Здравствуйте, Направление вычисляется по номерам заявок покупателя и продавца (в потоке параметры id_ord_buy и id_ord_sell соответственно), у кого больше то направление и берется.
Павел Bosco написал: Ну хорошо, ну ведь если пришла сделка от 11:00:00, вы можете сказать что все свечки от 10 часов уже не обновятся.
Ну да, ровно это и было сказано, разве нет?
Цитата
Sergey Gorokhov написал: т.е. уже случится сделка приведшая к появлению новой свечки и/или пошла первая секунда новой свечи).
Цитата
Павел Bosco написал: А он почему-то даже после открытия свечи в 11:00 может обновить и предыдущую. Насколько я знаю..
Так не должно быть, есть логи/скриншоты/доказательства? Или это Ваши домыслы? если так то пожалуйста не надо говорить то чего нет.
Цитата
Павел Bosco написал: В идеале хотелось бы иметь какой-то дополнительные евент, наподобие OnUpdateCallBack, но OnCandleCompleteCallbackпусть квик сначала вызывает OnCandleCompleteCallback, а потом начинает рисовать новую свечу у себя. и потом вызывает OnUpdateCallBack
Как уже было сказано и еще раз повторим, нет никакой возможности узнать что свеча сформирована ДО того как появится новая свеча. В нашем примере 11:00:00.000 это УЖЕ новая свеча (и тут не важно была сделка или нет) "а потом начинает рисовать новую свечу" это значит вызвать OnCandleCompleteCallback в 10:59:59.999 и при этом точно на все 100% знать что новых сделок не будет, а этого никто не знает и не может знать в принципе.
Вы можете (на выбор): 1) смотреть время (по нашему примеру в 11:00:00.000 делать вывод что старая свеча сформирована). 2) смотреть сделки (можно совместно с п.1) 3) смотреть index в событии SetUpdateCallback (можно совместно с п.2)
Павел Bosco написал: Имею в виду, в datasource поседняя свеча всё время меняется, как узнать что она уже "точно сформировалась"?
А как? допустим сейчас 10:59:59.999 и прошла сделка, как узнать что это последняя сделка на свечке? Вот и мы не знаем, а вдруг будет еще 10 таких же сделок, и все в 10:59:59.999? Никто Вам не скажет сколько точно будет сделок в одной свече. В связи с чем, "точно" узнать что свечка сформировалась можно только и только в момент когда уже появится новая свеча (т.е. уже случится сделка приведшая к появлению новой свечки и/или пошла первая секунда новой свечи).
Nikolay, В том то и дело, что Вы привели не полную версию кода, в связи с чем о многом приходится догадываться. Выводить значения всех линий вовсе не обязательно. ниже пример который явно это показывает:
Скрытый текст
Код
Settings = {
Name = "*TEST",
line = {{Name = "line 1", Color = RGB(255, 0, 0)},
{Name = "line 2", Color = RGB(0, 255, 0)}
}
}
function Init()
return 2
end
function OnCalculate(Index)
if (Index >= 10) and (Index < 20) then
return 4320
elseif (Index >= 20) then
return 4320, 4325
end
end
Ирина написал: Индекс конечного элемента, заданный переменной или функцией, вычисляется 1 раз перед поиском, или его изменение во время работы SearchItems будет учтено?
Один раз перед поиском.
Цитата
Ирина написал: Откуда можно узнать, как индексируется таблица, возвращаемая SearchItems?
Данные индексируются ровно в том порядке как поступили с биржи.
Цитата
Ирина написал: И ещё. Последовательность перебора элементов от стартового до конечного соблюдается или в хаотичном порядке?
Евгений Петров, К сожалению, основываясь только лишь на симптомах, мы не можем быть уверены что проблема о которой Вы говорите это ровно та же проблема с которой столкнулся автор топика. В связи с чем нам требуется анализ. И только после проведения анализа мы сможем подтвердить что это действительно та же самая проблема. Причина в том что история знает не мало случаев, когда при совпадении симптомов, источник проблемы был разным и по факту это были разные проблемы. А значит, проигнорировав анализ по одному похожему случаю, мы допускаем риск что ошибка будет упущена из виду. А это недопустимо. Таков наш регламент и просим отнестись к нему с пониманием. В связи с чем, конкретно по данной ветке форума, мы не можем ничего подтвердить до тех пор пока не будет подтверждение после анализа.
Евгений Петров, Здравствуйте, Да по данной теме мы зафиксировали ошибку, которая пока еще не исправлена. При выходе обновления с исправлением, об этом будет отдельное уведомление в данной ветке форума.
Здравствуйте, В QUIK есть механизм динамического импорта транзакций, суть в том что внешняя программа пишет в файл транзакцию. Терминал QUIK считывает файл и отправляет транзакцию в систему. Следующий способ, через язык программирования QLUA или QPILE. Еще есть возможность отправки транзакций через библиотеку Trans2Quik.DLL.
Не понял, я невнятно сформулировал вопрос или эта тема табу. Будет либо последовательное выполнение запросов либо параллельное (при наличии свободных процессорных ядер разумеется). "Ничего" быть не может раз уж скрипты вообще работают. Я разумеется могу рано или поздно протестировать сам, но был уверен что поддержка знает ответ. Странно както, при все уважении....
"Ничего не будет" означает что в описанном сценарии скрипты не будут друг другу мешать. Вы можете самостоятельно протестировать и убедиться в этом
Imersio Arrigo написал: Где-то тут Сергей Горохов выкладывал скрипты с примером расчётов на луа.
Да было дело, речь про скрипт Greek.lua
Скрытый текст
Код
--[[
Что делает:
По Таблице текущих параметров строит таблицу опционов с расcчитанными греками
Формулы взяты отсюда:
http://en.wikipedia.org/wiki/Black%96Scholes
http://en.wikipedia.org/wiki/Greeks_%28finance%29
Как использовать:
откройте Таблицу Текущих Параметров (меню Таблицы -> Текущая Таблица)
Укажите настройки, после чего, запустите скрипт (меню Таблицы - Lua - Доступные скрипты)
чтобы сохранить в CSV файл, текущее состояние таблицы, нужно нажать комбинацию клавиш Ctrl+S файл сохраняется в папку со скриптом, с именем HHHMMDD.csv
]]
-------------------------------НАСТРОЙКИ-------------------------------
RiskFree=0/100 --безрисковая ставка %, Указывается вручную
BaseClassCode = "SPBFUT" --Класс базового актива
ClassCode = "SPBOPT" --Класс опционов
--Список базовых активов, через запятую, по которым отображать опционы:
BaseSecList = "RIU7" --getClassSecurities(BaseClassCode) --все сразу
--Список опционов через запятую:
SecList = getClassSecurities(ClassCode) --все сразу
INTERVAL = 1000 --Интервал обновления таблицы
doLogging=false --включает запись в файл, формата csv.
log_file=getScriptPath() .. "\\Greek.csv" --путь к csv файлу
-----------------------------------------------------------------------
-------------------------------ТО ЧТО НИЖЕ, ТРОГАТЬ НЕ НАДО------------------------------------------------------------------
--Параметры таблицы
tbl = {
["caption"]="Greek",
[1]="Название",
[2]="Код опциона",
[3]="Тип опциона",
[4]="Баз. актив",
[5]="Расчетная цена",
[6]="Страйк",
[7]="Волатильность",
[8]="До исполнения",
[9]="Дельта",
[10]="Гамма(%)",
[11]="Тэта",
[12]="Вега",
[13]="Ро",
["t_id"]=0
}
abTable = {}
BaseCol = {}
Sec2row = {}
file = nil
Sep = ";"
YearLen=365.0 --Число дней в году
WORK = true
CALC = false
G_ROW = -1
if (BaseSecList == "") or (BaseSecList == nil) then
BaseSecList = getClassSecurities(BaseClassCode)
end
if (SecList == "") or (SecList == nil) then
SecList = getClassSecurities(ClassCode)
end
-------------------------------ФУНКЦИИ------------------------------------------------------------------
function Logging(str) --Пишет лог
if file~=nil and doLogging then
file:write(str .. "\n")
file:flush()
end
end
function N(x) --Нормальное среднее
if (x > 10) then
return 1
elseif (x < -10) then
return 0
else
local t = 1 / (1 + 0.2316419 * math.abs(x))
local p = 0.3989423 * math.exp(-0.5 * x * x) * t * ((((1.330274 * t - 1.821256) * t + 1.781478) * t - 0.3565638) * t + 0.3193815)
if x > 0 then
p=1-p
end
return p
end
end
function pN(x) --производная от функции нормального среднего
return math.exp(-0.5 * x * x) / math.sqrt(2 * math.pi)
end
function Greek(tmpParam)
local b = tmpParam.volatility / 100 --"b" волатильность доходности (квадратный корень из дисперсии) базисной акции.
local S = tmpParam.settleprice --"S" текущая цена базисной акции;
local Tt = tmpParam.DAYS_TO_MAT_DATE / YearLen --"T-t" время до истечения срока опциона (период опциона);
local K = tmpParam.strike --"K" цена исполнения опциона;
local r = RiskFree --"r" безрисковая процентная ставка;
local d1 = (math.log(S / K) + (r + b * b * 0.5) * Tt) / (b * math.sqrt(Tt))
local d2 = d1-(b * math.sqrt(Tt))
local Delta = 0
local Gamma = 0
local Theta = 0
local Vega = 0
local Rho = 0
local e = math.exp(-1 * r * Tt)
Gamma = pN(d1) / (S * b * math.sqrt(Tt))
Vega = S * e * pN(d1) * math.sqrt(Tt)
Theta = (-1 * S * b * e * pN(d1)) / (2 * math.sqrt(Tt))
if tmpParam.Optiontype == "Call" then
Delta = e * N(d1)
Theta = Theta - (r * K * e * N(d2)) + r * S * e * N(d1)
----Theta = Theta - (r * K * e * N(d2))
Rho = K * Tt * e * N(d2)
else
Delta = -1 * e * N(-1*d1)
Theta = Theta + (r * K * e * N(-1 * d2)) - r * S * e * N(-1 * d1)
----Theta = Theta + (r * K * e * N(-1 * d2))
Rho = -1 * K * Tt * e * N(-1 * d2)
end
return {
["Delta"] = Delta,
["Gamma"] = 100 * Gamma,
["Theta"] = Theta / YearLen,
["Vega"] = Vega / 100,
["Rho"] = Rho / 100
}
end
function GetRow(ID,row) --возвращает строку таблицы
local rows, col = GetTableSize(ID)
local result = ""
if rows~=nil and row<=rows then
for i=1,col do
result=result..GetCell(ID,row,i).image .. Sep
end
end
return result
end
function CSV(T) --пишет таблицу в csv файл
function FTEXT(V) --ПРОВЕРЯЕМ КОРРЕКТНОСТЬ КОЛИЧЕСТВА СИМОЛОВ ПЕРЕМЕННОЙ
V=tostring(V)
if (string.len(V)==1) or (string.len(V)==5) then
V="0".. V
end
return V
end
local temp = os.date("*t")
local Fname =getScriptPath() .. "\\" .. FTEXT(temp.year) .. FTEXT(temp.month) .. FTEXT(temp.day) .. ".csv"
CSVFile = io.open(Fname, "w+")
if CSVFile~=nil then
local rows, col = GetTableSize(T.t_id)
for i=1,col do --расставляем заголовки
CSVFile:write(T[i] .. Sep)
end
CSVFile:write("\n")
for i=1,rows do --пишем таблицу
CSVFile:write(GetRow(T.t_id,i).."\n")
end
CSVFile:flush()
CSVFile:close()
message("Файл успешно сохранен:\n"..Fname, 1)
else
message("Ошибка при сохранении файла:\n"..Fname, 3)
end
end
function round(num, idp) --округляет до указанного количества знаков
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
function comma_value(n) --ставит разделители в числах
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
return left..(num:reverse():gsub('(%d%d%d)','%1 '):reverse())..right
end
function CreateDataSourceEX(Class,Sec,Par)
local ds,err = CreateDataSource(Class, Sec, INTERVAL_TICK, Par)
if ds==nil then
message("Ошибка при получении параметра "..Par..":\n"..err, 3)
return false
else
ds:SetEmptyCallback()
while (ds:Size()==0) and (WORK) do
sleep(100)
end
return true
end
end
function Stop()
if doLogging then
file:close()
end
WORK = false
end
function Calculate(row,do_calc)
if (row~=nil) and (row>=0) and (do_calc) then
local T=BaseCol[row]
local tmpParam ={
["Optiontype"] = T.Optiontype,
["settleprice"] = getParamEx(BaseClassCode,T.Optionbase,"settleprice").param_value+0,
["strike"] = getParamEx(ClassCode,T.SecCode,"strike").param_value+0,
["volatility"] = getParamEx(ClassCode,T.SecCode,"volatility").param_value+0,
["DAYS_TO_MAT_DATE"] = T.DAYS_TO_MAT_DATE
}
local tmpGreek = Greek(tmpParam)
SetCell(tbl.t_id, row, 5, comma_value(tmpParam.settleprice), tmpParam.settleprice) -- "Расчетная цена",
SetCell(tbl.t_id, row, 6, comma_value(tmpParam.strike), tmpParam.strike) --"Страйк",
SetCell(tbl.t_id, row, 7, tostring(tmpParam.volatility), tmpParam.volatility) -- "Волатильность",
SetCell(tbl.t_id, row, 8, tostring(tmpParam.DAYS_TO_MAT_DATE), tmpParam.DAYS_TO_MAT_DATE) --"До исполнения",
SetCell(tbl.t_id, row, 9, tostring(round(tmpGreek.Delta,2)), tmpGreek.Delta) --"Дельта",
SetCell(tbl.t_id, row, 10, tostring(round(tmpGreek.Gamma,4)), tmpGreek.Gamma) -- "Гамма(%)",
SetCell(tbl.t_id, row, 11, tostring(round(tmpGreek.Theta,2)), tmpGreek.Theta) -- "Тэта",
SetCell(tbl.t_id, row, 12, tostring(round(tmpGreek.Vega,2)), tmpGreek.Vega) -- "Вега",
SetCell(tbl.t_id, row, 13, tostring(round(tmpGreek.Rho,2)), tmpGreek.Rho) -- "Ро",
if doLogging then
Logging(os.date().. Sep .. GetRow(tbl.t_id,row))
end
end
return false
end
-------------------------------Колбэки------------------------------------------------------------------
function f_cb(t_id,msg,par1,par2) --событие на нажатие клавиш
if (msg==QTABLE_CHAR) and (par2==19) then --сохранить в CSV файл текущее состояние таблицы нужно нажать комбинацию клавиш Ctrl+S
CSV(tbl)
end
if (msg==QTABLE_CLOSE) then --закрытие окна
Stop()
end
if (msg==QTABLE_VKEY) and (par2==116) then --функция принудительного обновления таблицы при нажатии клавиши Ctrl+F5
for SecCode in string.gmatch(SecList, "([^,]+)") do --перебираем опционы по очереди.
Calculate(Sec2row[SecCode],true)
Highlight(tbl.t_id, Sec2row[SecCode], QTABLE_NO_INDEX, RGB(255,0,0), QTABLE_DEFAULT_COLOR, INTERVAL)
end
end
end
function OnStop()
Stop()
DestroyTable(tbl.t_id)
end
function OnInit()
local STR = ""
if doLogging then
file = io.open(log_file, "w+")
end
tbl.t_id = AllocTable()
for i=1,table.maxn(tbl) do --добавляем колонки
if i<=4 then
AddColumn(tbl.t_id, i, tbl[i], true, QTABLE_CACHED_STRING_TYPE, string.len(tbl[i])*2)
else
AddColumn(tbl.t_id, i, tbl[i], true, QTABLE_DOUBLE_TYPE, 10)
end
if doLogging then
STR=STR..tbl[i]..Sep
end
end
Logging("Дата Время".. Sep .. STR)
CreateWindow(tbl.t_id)
SetWindowCaption(tbl.t_id,tbl.caption)
SetTableNotificationCallback(tbl.t_id, f_cb)
end
function OnParam(class, sec)
if (class==ClassCode) and (WORK) and (string.find(SecList,sec)~=nil) then
G_ROW = Sec2row[sec]
if (G_ROW~=nil) and (G_ROW>=0) then
Highlight(tbl.t_id, G_ROW, QTABLE_NO_INDEX, RGB(255,0,0), QTABLE_DEFAULT_COLOR, INTERVAL)
CALC=true
end
end
end
function main()
WORK = false
CALC=true
for SecCode in string.gmatch(SecList, "([^,]+)") do --перебираем опционы по очереди.
local Optionbase=getParamEx(ClassCode,SecCode,"optionbase").param_image
local Optiontype=getParamEx(ClassCode,SecCode,"optiontype").param_image
if (string.find(BaseSecList,Optionbase)~=nil) then
local row = InsertRow(tbl.t_id,-1)
local T={
["Name"] = getSecurityInfo(ClassCode,SecCode).name,
["SecCode"] = SecCode,
["Optiontype"] = Optiontype,
["Optionbase"] = Optionbase,
["DAYS_TO_MAT_DATE"] = getParamEx(ClassCode,SecCode,"DAYS_TO_MAT_DATE").param_value+0
}
BaseCol[row]=T
--заполняем статичные параметры
Sec2row[SecCode]=row
SetCell(tbl.t_id, row, 1, BaseCol[row].Name) -- "Название опциона",
SetCell(tbl.t_id, row, 2, BaseCol[row].SecCode) --"Код опциона",
SetCell(tbl.t_id, row, 3, BaseCol[row].Optiontype) -- "Тип опциона",
SetCell(tbl.t_id, row, 4, BaseCol[row].Optionbase) --"Баз. актив",
--заказ данных
CreateDataSourceEX(BaseClassCode,T.Optionbase,"settleprice")
CreateDataSourceEX(ClassCode,T.SecCode,"strike")
CreateDataSourceEX(ClassCode,T.SecCode,"volatility")
--заполняем динамичные параметры
CALC=Calculate(row,true)
end
end
WORK = true
while WORK do
CALC=Calculate(G_ROW,CALC)
sleep(INTERVAL)
end
end
Здравствуйте, На свечках нет информации о покупке либо продаже. Только цены. В связи с чем с графика цены и объема Вы не сможете получить эту информацию, ее просто там нет. Однако, эта информация есть в таблице обезличенных сделок. Но так как в ней информация отображается только за текущий день, то за прошлые даты Вы ее получить не сможете. Вариант решения, хранить эти данные самостоятельно.
Mikhail Ran написал: Вопрос, наверное, к Sergey Gorokhov, так как он вроде писал индикаторы.
Вы ошибаетесь. Лично я только переписал индикаторы на LUA, а в терминале они появились за долго до моего появления. Но всё равно спасибо. Вы смотрите русскую wiki, а в английской формула отклонения почему-то другая https://en.wikipedia.org/wiki/Average_absolute_deviation и как быть, где правильно?
BlackBoar написал: И еще вопрос по близкой теме. Глубину получения стаканов можно менять в настройках квик. А из скрипта луа есть аналогичная функциональность? И если нет можно ли выразить пожелание ее добавить?
Из скрипта Lua вообще нет никакой штатной возможности менять какие-либо настройки терминала.
BlackBoar написал: Апдейт, поэкспериментировал разворачивать цикл чтобы было меньше итераций самого цикла. Лучший результат для стакана 10х10 где-то 55-65 микросекунд. Когда внутри цикла больше 100 строк кода похоже растут накладные расходы самого луа-движка, дальнейшее разворачивание уже ухудшает результат. Вопрос тот же, цифры уже похожи на то из чего можно исходить?
Боюсь что на этот вопрос нет ответа. Т.к. нет такого эталонного образца "то что должно быть"
Ваше пожелание зарегистрировано. Мы постараемся рассмотреть его и сообщить Вам результаты анализа. Впоследствии, по результатам анализа, будет приниматься решение о реализации пожелания в будущих версиях ПО.
BlackBoar написал: Перечитывал в очередной раз "Руководство пользователя QLua", сложилось такое впечатление что в талицах которые возвращает API QUIK ключи (названия параметров) всегда имеют строковый тип. Подскажите пожалуйста так ли это или я где-то что-то пропустил?
BlackBoar написал: Подскажите пожалуйста, порректно ли вообще измерять время на вызов функций квик подобным способом. Или "дерганье" их миллион раз подряд вызывает какие-то аномалии в работе квик?
"дерганье" миллион раз не должно вызывать никаких аномалий. Другой вопрос в том что каждая итерация цикла может выполняться раз в 15.625 мс, почитать можно например тут: https://habrahabr.ru/company/intel/blog/186998/
Антон (band) написал: Цитата Imersio Arrigo написал: Так и есть. Только счётчик уменьшается по ds:Close() .похоже нет, раз
Цитата Sergey Gorokhov написал:Соответственно и отказ от подписки в одном скрипте приведет к отказу во всем терминале и во всех скриптах.
значит ds:close может нарушить работу других скриптов.
звучит как какая-то дичь. не должно такого быть чтобы отказ в одном скрипте приводил к отказу по всему остальному терминалу.
имхо должно быть так: заказ/перезаказ в рамках одного скрипта не порождает больше потоков данных, если подписка на эту сущность уже есть, просто увеличивается счетчик. Соответственно отписка счётчик уменьшает, и если он равен нулю, то тогда и только тогда подписка физически прекращается.
предлагаю проверить это экспериментально. Например двумями скриптами.
Прошу прощения, произошла ошибка. На самом деле это не так и отказ от подписки в одном скрипте НЕ приведет к отказу от подписки в других.
Здравствуйте, Не понятно как оно вообще у Вас работает. Стандартная функция GET_CANDLE имеет гораздо больше параметров. Видимо у Вас не родная функция, а самописная. Рекомендуем посмотреть наш пример, возможно в нем найдете ответ.
Скрытый текст
Код
PORTFOLIO_EX GetDayCandles;
DESCRIPTION Сохранение свечей графиков ряда параметров набора инструментов за текущую торговую сессию (в таблицу и в файл);
CLIENTS_LIST ALL_CLIENTS;
FIRMS_LIST ALL_FIRMS;
USE_CASE_SENSITIVE_CONSTANTS
PROGRAM
ClassCodeList = "SPBFUT" ' коды классов бумаг, по которым будет осуществляться вывод
SecCodeList = "RIZ2" ' коды бумаг, по которым будет осуществляться вывод
Params = "" ' параметры, по которым будут строиться свечи
GraphType = "PRICE" ' тип графика
Intervals = "1,2,3,4,5,6,10,15,20,30,60" ' интервал графика: 1 - минутный, 5 - 5-минутный и пр.
Separator = "." ' разделитель дробной части
OutPath = "c:\" ' путь к директории с файлом свечей
razn=3 ' разница с Москвой в часах
start_time="10:00:00" ' 10 часов 00 минут утра по Москве - время начала текущей торговой сессии "HHMMSS"
'=============================ФУНКЦИИ===============================
'ВЫЧИСЛЯЕТ ЧАСТНОЕ ОТ ДЕЛЕНИЯ
'DOUBLE div(DOUBLE Value, DOUBLE Value)
'Для работы нужны функции: никаких
func div(result,y_)
result=FLOOR(result/y_)
end func
'ВЫЧИСЛЯЕТ ОСТАТОК ОТ ДЕЛЕНИЯ
'DOUBLE mod(DOUBLE Value, DOUBLE Value)
'Для работы нужны функции: никаких
func mod(result,y_)
result=result-FLOOR(result/y_)*y_
end func
'ПРОВЕРЯЕМ ВХОЖДЕНИЕ pattern в СТРОКЕ list
'DOUBLE is_in_list(STRING Value, STRING Value)
'Для работы нужны функции: никаких
FUNC is_in_list(list, pattern)
result = 0
if 0 + find(list, 0, pattern) = -1
result = 0
else
result = 1
end if
END FUNC
'ПЕРЕВОДИМ ВРЕМЯ ИЗ ФОРМАТА HHMMSS в HH:MM:SS
'STRING HHMMSS2Time(STRING Value)
'Для работы нужны функции: никаких
FUNC HHMMSS2Time(result)
result=""&result
if LEN(result)=5
result="0"&result
end if
if LEN(result)=6
result = substr(result, 0, 2) & ":" & substr(result, 2, 2) & ":" & substr(result, 4, 2)
end if
END FUNC
'ПЕРЕВОДИМ ВРЕМЯ ИЗ ФОРМАТА HH:MM:SS в HHMMSS
'STRING Time2HHMMSS(STRING Value)
'Для работы нужны функции: FTEXT
Func Time2HHMMSS(result)
result=FTEXT(SUBSTR(result,0,FIND(result,0,":")))& _
FTEXT(SUBSTR(result,FIND(result,0,":")+1,FIND(result,FIND(result,0,":")+1,":")-FIND(result,0,":")-1)) & _
FTEXT(SUBSTR(result,FIND(result,FIND(result,0,":")+1,":")+1,len(result)-FIND(result,FIND(result,0,":")+1,":")-1))
result=FTEXT(result)
end Func
'ПРОВЕРЯЕМ КОРРЕКТНОСТЬ КОЛИЧЕСТВА СИМОЛОВ ПЕРЕМЕННОЙ
'STRING FTEXT(STRING Value)
'Для работы нужны функции: никаких
func FTEXT(result)
result=""&result
if (len(result)=1) OR (len(result)=5)
result="0"&result
end if
end func
'ЗАМЕНЯЕМ 0 ПУСТОЙ СТРОКОЙ
'STRING REPLACE_0(STRING Value)
'Для работы нужны функции: никаких
func REPLACE_0(result)
if result+0=0
result=""
end if
end func
'ПЕРЕВОДИМ НОРМАЛЬНОЕ ВРЕМЯ HHMMSS В СЕКУНДЫ
'DOUBLE Time2Sec(STRING Value)
'Для работы нужны функции: Time2HHMMSS, FTEXT
func Time2Sec(result)
result=Time2HHMMSS(result)
result=SUBSTR(result,0,2)*3600+SUBSTR(result,2,2)*60+SUBSTR(result,4,2)
end func
'ПАУЗА В СЕКУНДАХ
'DOUBLE Sleep(DOUBLE Value)
'Для работы нужны функции: никаких
func Sleep(result)
time_begin = get_value(GET_DATETIME(), "HOUR")*3600+get_value(GET_DATETIME(), "HOUR")*60+get_value(GET_DATETIME(), "SEC")
for i_ from 0 to 2
if (get_value(GET_DATETIME(), "HOUR")*3600+get_value(GET_DATETIME(), "HOUR")*60+get_value(GET_DATETIME(), "SEC") - time_begin) >= result
BREAK
end if
i_=i_-1
end for
end func
'ПЕРЕВОДИМ СЕКУНДЫ В НОРМАЛЬНОЕ ВРЕМЯ HH:MM:SS
'STRING Sec2Time(DOUBLE Value)
'Для работы нужны функции: FTEXT, HHMMSS2Time
func Sec2Time(result)
result=FTEXT(floor(result/3600))& _
FTEXT(floor((result-(floor(result/3600))*3600)/60))& _
FTEXT((result-(floor(result/3600))*3600)-(floor((result-(floor(result/3600))*3600)/60)) * 60)
result=HHMMSS2Time(result)
end func
'ПЕРЕВОДИТ ДАТУ В ФОРМАТЕ YYYYMMDD или DD.MM.YYYY В КОЛИЧЕСТВО ДНЕЙ НАЧИНАЯ С 1970.01.01
'DOUBLE date2day(STRING Value)
'Для работы нужны функции: никаких
func date2day(YYYYMMDD_)
if (SUBSTR (YYYYMMDD_, 2, 1)=",") or (SUBSTR (YYYYMMDD_, 2, 1)=".")
YYYYMMDD_=SUBSTR(YYYYMMDD_,6,4)&SUBSTR(YYYYMMDD_,3,2)&SUBSTR(YYYYMMDD_,0,2)
end if
result=SUBSTR(YYYYMMDD_,0,4)*365+floor((SUBSTR(YYYYMMDD_,0,4)-1)/4)-719542
result=result+substr("031059090120151181212243273304334365",SUBSTR(YYYYMMDD_,4,2)*3-6,3)
if (SUBSTR(YYYYMMDD_,0,4)-floor(SUBSTR(YYYYMMDD_,0,4)/4)*4=0) AND (SUBSTR(YYYYMMDD_,4,2)+0>2)
result=result+1
end if
result=(result+SUBSTR(YYYYMMDD_,6,2)-1)
end func
'ПЕРЕВОДИМ МАССИВ get_datetime() В НОРМАЛЬНОЕ ВРЕМЯ HHMMSS
'STRING Map2HHMMSS(MAP Name)
'Для работы нужны функции: FTEXT
FUNC Map2HHMMSS(result)
result=FTEXT(get_value(result, "HOUR"))& _
FTEXT(get_value(result, "MIN"))& _
FTEXT(get_value(result, "SEC"))
END FUNC
'ВЫВОДИТ ИЗ МАССИВА get_datetime() ИЛИ get_trade_date() ДАТУ В ФОРМАТЕ YYYYMMDD
'STRING Map2YYYYMMDD(MAP Name)
'Для работы нужны функции: FTEXT
FUNC Map2YYYYMMDD(result)
result=FTEXT(get_value(result, "YEAR"))& _
FTEXT(get_value(result, "MONTH"))& _
FTEXT(get_value(result, "DAY"))
END FUNC
'ПЕРЕВОДИТ ДАТУ В ФОРМАТЕ YYYYMMDD В ФОРМАТ DD.MM.YYYY
'STRING Map2YYYYMMDD(STRING Value)
'Для работы нужны функции: FTEXT
FUNC YYYYMMDD2Date(result)
result=SUBSTR(result,6,2)&"."&SUBSTR(result,4,2)&"."&SUBSTR(result,0,4)
END FUNC
'УСТРАНЯЕТ В get_datetime() РАЗНИЦУ ВО ВРЕМЕНИ
'MAP Fix_get_datetime(MAP Name, DOUBLE Value)
'Для работы нужны функции: FTEXT
FUNC Fix_get_datetime(result, time_zone)
if get_value(result, "HOUR")+0>=time_zone+0
result=SET_VALUE(result, "HOUR",(get_value(result, "HOUR")-time_zone))
result=SET_VALUE(result, "DATETIME",SUBSTR(get_value(result, "DATETIME"),0,11)& _
FTEXT(SUBSTR(get_value(result, "DATETIME"),11,2)-time_zone)& _
SUBSTR(get_value(result, "DATETIME"),13,10))
end if
END FUNC
'ЗАМЕНА РАЗДЕЛИТЕЛЯ ДРОБНОЙ ЧАСТИ
'STRING replace_decimal_symbol(STRING Value, STRING Value)
'Для работы нужны функции: никаких
func replace_decimal_symbol(string, dchar)
result=""
string=""&string
for rdc_x from 0 to len(string)-1
if (substr(string, rdc_x, 1) = ".") OR (substr(string, rdc_x, 1)=",")
result=result & dchar
else
result=result & substr(string, rdc_x, 1)
end if
end for
end func
'ЗАМЕНА В СТРОКЕ string ОДНОГО СИМВОЛА dchar_in ДРУГИМ dchar_out
'STRING replace_symbol(STRING Value, STRING Value, STRING Value)
'Для работы нужны функции: никаких
func replace_symbol(string, dchar_in, dchar_out)
result=""
string=""&string
for rdc_x from 0 to len(string)-1
if substr(string, rdc_x, 1) = dchar_in
result=result & dchar_out
else
result=result & substr(string, rdc_x, 1)
end if
end for
end func
'=============================ФУНКЦИИ===============================
NEW_GLOBAL("Counter", 1) ' счетчик записей в массив выходных данных (таблицы)
NEW_GLOBAL("Date", "0") ' дата текущей торговой сессии QUIK
NEW_GLOBAL("CommonTime", time2sec(start_time))
NEW_GLOBAL("OutFile", "")
if Date = "0"
Date = Map2YYYYMMDD(GET_TRADE_DATE()) ' Преобразовали текущую дату в принятый для вызова GET_CANDLE формат
OutFile = OutPath & YYYYMMDD2Date(Date) & ".txt" ' Сформировали имя файла данных со свечами
CLEAR_FILE(OutFile) 'Очистили файл
WRITELN(OutFile, "<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>") ' Вывели заголовок
DELETE_ALL_ITEMS()
CurIntervals = CREATE_MAP() ' Создаем массив счетчиков интервалов
CurIntervals = SET_VALUE(CurIntervals, "1", 1)
CurIntervals = SET_VALUE(CurIntervals, "2", 0)
CurIntervals = SET_VALUE(CurIntervals, "3", 0)
CurIntervals = SET_VALUE(CurIntervals, "4", 0)
CurIntervals = SET_VALUE(CurIntervals, "5", 0)
CurIntervals = SET_VALUE(CurIntervals, "6", 0)
CurIntervals = SET_VALUE(CurIntervals, "10", 0)
CurIntervals = SET_VALUE(CurIntervals, "15", 0)
CurIntervals = SET_VALUE(CurIntervals, "20", 0)
CurIntervals = SET_VALUE(CurIntervals, "30", 0)
CurIntervals = SET_VALUE(CurIntervals, "60", 0)
end if
CurTime = Fix_get_datetime(GET_DATETIME(),razn)
CurTimeInSec = time2sec(Map2HHMMSS(CurTime)) ' Преобразовали текущее время в количество секунд
'CurTimeInSec = time2sec(GET_INFO_PARAM("SERVERTIME"))
if (CurTimeInSec - CommonTime) >= 60 ' Текущее время более 10.00 (время московское) и с момента последнего опроса данных прошло не менее минуты
for i from 0 to floor((CurTimeInSec - CommonTime)/ 60)
for Interval in Intervals
CurInterval = GET_VALUE(CurIntervals, Interval)
if (0 + CurInterval) = (0 + Interval)
if (0 + Interval) <> 1
CurTime = sec2time(CommonTime - CurInterval * 60)
else
CurTime = sec2time(CommonTime)
end if
for ClassCode in ClassCodeList
for SecCode in SecCodeList
if LEN(Params)=0
Params=" "
END IF
for Param in Params
Candle = GET_CANDLE(ClassCode, SecCode, Param, Interval, GraphType, Date, Time2HHMMSS(CurTime))
Open = replace_decimal_symbol(GET_VALUE(Candle, "OPEN"),Separator)
if Open+0 <> 0 ' Если цена открытия интервала равна нулю, свечу не выводим
Close = replace_decimal_symbol(GET_VALUE(Candle, "Close"),Separator)
High = replace_decimal_symbol(GET_VALUE(Candle, "High"),Separator)
Low = replace_decimal_symbol(GET_VALUE(Candle, "Low"),Separator)
Volume = replace_decimal_symbol(GET_VALUE(Candle, "Volume"),Separator)
Candle = SET_VALUE(Candle, "Security", SecCode)
Candle = SET_VALUE(Candle, "ClassCode", ClassCode)
Candle = SET_VALUE(Candle, "Param", Param)
Candle = SET_VALUE(Candle, "Date", YYYYMMDD2Date(Date))
Candle = SET_VALUE(Candle, "Time", CurTime)
Candle = SET_VALUE(Candle, "Interval", Interval)
ADD_ITEM(Counter, Candle)
WRITELN(OutFile, SecCode & " [" & ClassCode & "]," & Interval & "," & Date & "," & Time2HHMMSS(CurTime) & "," & Open & "," & High & "," & Low & "," & Close & "," & Volume)
'Counter = Counter + 1
end if
end for
end for
end for
CurIntervals = SET_VALUE(CurIntervals, Interval, 1) ' скинули интервальный счетчик
else
CurIntervals = SET_VALUE(CurIntervals, Interval, (CurInterval + 1)) ' накинули интервальный счетчик
end if
end for
CommonTime = CommonTime + 60 ' накинули время
end for
end if
END_PROGRAM
PARAMETER Security;
PARAMETER_TITLE Код инструмента;
PARAMETER_DESCRIPTION Код инструмента;
PARAMETER_TYPE String(12);
END
PARAMETER ClassCode;
PARAMETER_TITLE Код класса;
PARAMETER_DESCRIPTION Код класса;
PARAMETER_TYPE String(12);
END
PARAMETER Param;
PARAMETER_TITLE Параметр;
PARAMETER_DESCRIPTION Параметр;
PARAMETER_TYPE String(12);
END
PARAMETER Date;
PARAMETER_TITLE Дата;
PARAMETER_DESCRIPTION Дата интервала;
PARAMETER_TYPE String(10);
END
PARAMETER Time;
PARAMETER_TITLE Время;
PARAMETER_DESCRIPTION Время интервала;
PARAMETER_TYPE String(10);
END
PARAMETER Interval;
PARAMETER_TITLE Интервал;
PARAMETER_DESCRIPTION Интервал;
PARAMETER_TYPE String(2);
END
PARAMETER Open;
PARAMETER_TITLE Открытие;
PARAMETER_DESCRIPTION Цена открытия интервала;
PARAMETER_TYPE Numeric(20,4);
END
PARAMETER Close;
PARAMETER_TITLE Закрытие;
PARAMETER_DESCRIPTION Цена закрытия интервала;
PARAMETER_TYPE Numeric(20,4);
END
PARAMETER High;
PARAMETER_TITLE Максимум;
PARAMETER_DESCRIPTION Максимальная цена интервала;
PARAMETER_TYPE Numeric(20,4);
END
PARAMETER Low;
PARAMETER_TITLE Минимум;
PARAMETER_DESCRIPTION Минимальная цена интервала;
PARAMETER_TYPE Numeric(20,4);
END
PARAMETER Volume;
PARAMETER_TITLE Объем;
PARAMETER_DESCRIPTION Суммарный объем сделок в интервале;
PARAMETER_TYPE Numeric(20,4);
END
END_PORTFOLIO_EX
Антон (band) написал: Т.е. если я хочу проверить открытые подписки я должен пройтись по списку инструментов. создать для каждого ds = createdatasource(c_code, s_code, interval_tick)что не приведет к заказу данных(допустим он закрыт для этой бумаги в терминале)а дальше проверить открыта ли уже подписка в терминале я не могу. но могу закрыть подписку вызвав close, так? и терминал перестанет данные получать даже если подписка была открыта другими скриптами и они с этими данными работают?
Как уже говорилось, повторная подписка не означает повторный заказ данных. Если одна подписка уже активирована (не важно как) то вторая такая же не приведет к созданию еще одного потока. Соответственно и отказ от подписки в одном скрипте приведет к отказу во всем терминале и во всех скриптах.
Цитата
Антон (band) написал: ну хоть на счет проверки подписки тиковых данных согласны? про ТТП и проверку заказа на параметр? или вы считаете раз у вас так контроль подписки работает то и данные функции не нужны. если я хочу удалить подписку то удаляю. если заказать то заказываю. без всяких проверок открыта она уже или нет тк. терминал не ведет контроль того кто эти подписки заказывал и используются ли они до сих пор. правильно понимаю?
Не понятно с чем надо согласиться, Вы же сами говорите что проверка не нужна.
Антон (band) написал: Для SetUpdateCallback пока не понимаю что будет означать index т.к. все примеры для интервалов больше тика. это номер эл-та в таблице AllTrades, а не номер свечки как в документации написано? (меня только тиковые данные интересуют сейчас)
Именно номер свечки. Вот что на тиковом графике будет под номером 1 то и вернет функция. При том что эта запись в AllTrades может быть вообще где-нибудь по середине таблицы.
Цитата
Антон (band) написал: ds удаляется а подписка получается остается?
ds это источник, а мы говорим про подписку на данные. Это немного разные вещи. источник можно удалить или присвоить nil, подписка на данные от этого не закроется. Если Вы удалите источник то не сможете выполнить отписку через Close
Цитата
Антон (band) написал: разве не логично удалять такие подписки.
Нет не логично, от куда терминал знает нужна скрипту эта подписка или нет? может он ее использует через OnAllTrade или вообще скрипту она понадобится как-нибудь потом. Скрипт должен сам сообщить терминалу когда надо закрыть подписку и для этого есть Close.
Цитата
Антон (band) написал: собсно это в дополнение к проверки на подписку. для таких моментов она и нужна. раз мы можем получать данные хотя никто их не обрабатывает. просто забыли закрыть ds. или не сохранили его в глобальной таблице луа. (что я имел ввиду под удалением).
Нет не нужна т.к. повторный заказ через CreateDataSource не приводит к дублированию информации. Т.е. для отписки по удаленному ds вы можете повторно вызвать CreateDataSource, а потом Close. А вообще, удаление ds это плохой тон и таких моментов лучше избегать.
bazuce написал: Допустил ошибку в первом пункте, да и темы тоже.
Заявка отправляется с типом IOC. Это несколько меняет логику, получается по такому типу заявок можно получить флаг исполнения в сообщении получаемом при обработки OnOrder только при полном исполнении?
Что в первом, что во втором примере заявка исполнилась только частично
Заявка с типом IOC может исполниться частично, после чего сама снимается. В Вашем случае, она была частично исполнена после чего снята, это Вы и видите. Частичное исполнение должно быть видно в параметре ext_order_status, проверьте этот момент. Однако ошибка про кросс курсы, в данном случае не понятна. Как уже было сказано и еще раз повторим, про нее может ответить только Ваш брокер. У нас нет совершенно никакой возможности определить причины ее возникновения. Вероятная причина уже была озвучена, но без брокера это лишь догадки, не более.
Trans2QuikAPI_1.3_x64 + QUIK 7.9.1.1 = TRANS2QUIK_QUIK_TERMINAL_NOT_FOUND, Не удается добиться подключения к QUIK в варианте Trans2QuikAPI_1.3_x64 + QUIK 7.9.1.1
Антон написал: 1) как то туда(в CreateDataSource) есть возможность сразу все параметры передать которые я хочу получать?
Вы можете заказать данные и потом брать из AllTrade то что Вам надо. При этом не обязательно использовать именно OnAllTrade, можно например получать данные из самого источника (Функции O, H, L, C, V, T) или через свой колбек (SetUpdateCallback) или через SearchItems (к слову в ней можно назначить функцию со своими параметрами)
Цитата
Антон написал: 2) как проверять включена ли загрузка этого параметра? для стакана есть IsSubscribed_Level_II_Quotes. для обезличенных сделок не нашел.
К сожалению такой функции нет.
Цитата
Антон написал: 3) как убрать подписку на какой-либо параметр[ы] обезличенной сделки (или бумагу целиком в частном случае если для параметров не возможно)
Отдельно отказаться от некоторых параметров нельзя. в случае CreateDataSource, закрыть поток можно через функцию Close.
Цитата
Антон написал: 4) нужно ли хранить таблицу ds полученную от CreateDataSource. если ее удалить перестанет ли идти подписка?касаемо параметров бумаги (таблица текущих параметров)
что значит "удалить"? Пока не вызовите Close подписка не остановится.
Цитата
Антон написал: 5) почему нету функции isParamRequest (узнать заказан ли нужный мне параметр). Подписка/отписка же есть. или я ее тоже не нашел в документации. как проверять подписку?
bazuce написал: 1. Насколько я понимаю, флаги в данном случае заполнились неверно? И причина описанная в reject_reason тоже невалидная? Как правильно определить что заявка с типом FOK была исполнена при обработке OnOrder ?
Не понятно зачем гадать. Флаги описаны в документации QLUA.chm -Описание битовых флагов --Флаги для таблиц Заявки, Заявки на внебиржевые сделки, Сделки, Сделки для исполнения
Там написано
Цитата
бит 0 (0x1) Заявка активна, иначе – не активна бит 1 (0x2) Заявка снята. Если флаг не установлен и значение бита «0» равно «0», то заявка исполнена
у Вас флаги flags=286. В битовом представлении это число 100011110 Видим что 0x1 равен 0, а 0x2 равен 1. Согласно документации это значит что заявка снята. И на скриншоте она у Вас снята. И по reject_reason она снята. Почему Вы думаете что она исполнена совсем не понятно. Посмотрите сами в таблице заявок, колонка Статус
Цитата
bazuce написал: 2. Почему по бирже СПБ приходят два одинаковых callback в OnOrder ?
Они не одинаковые. Посмотрите параметры uid и trans_id Двойная отправка происходит как раз из-за обновления этих полей.
Цитата
bazuce написал: 3. Зачем так много вызовов OnTrade?
В отличии от OnOrder, тут другая история, Вы столкнулись с известной ошибкой повторной рассылки OnTrade. Данная ошибка нам известна и к сожалению, пока не исправлена, но обязательно будет.
Цитата
bazuce написал: 4. Почему причина снятия заявки теперь reject_reason="Вследствие возможной кросс-сделки"?
"кросс сделка" это потенциальная сделка с самим собой. Возможно в момент срабатывания заявки у Вас была активна другая заявка противоположного направления? В любом случае, как Вы на нее наткнулись подскажет только брокер.
Ирина написал: Так вот, бит 8 очень похож на него. В таком случае, об активации заявки в полном объеме по нему не узнаешь.
Все правильно. Только не понятно чего Вы хотите. Если смотреть исполнение связанной заявки тогда надо смотреть таблицу заявок а не стоп заявок. Если смотреть исполнение стоп заявки то как говорилось это бит 2
Ирина написал: Речь о той же смене даты, когда срок заявки "по исполнению" истек, а связанная была исполнена. Ну ясно уже - снимется...
тут возникла путаница в терминологии. Правильно так: Стоп по исполнению, активируется когда связанная заявка исполняется или частично исполнится (зависит от галки "Частичное исполнение заявки учитывается"), после активации стоп будет ждать заданного в нем условия. Если условие наступит то стоп заявка исполнится и отправит заявку на биржу. Если не исполнится, то по истечении срока жизни он стоп снимется.
Ирина написал: Бит 8? При полностью исполненной исходной заявке он тоже устанавливается? Спасибо.
В документации четко сказано в каком случае ставится бит
Цитата
Устанавливается в случае стоп-заявки типа тейк-профита по заявке, в случае когда исходная заявка частично исполнена и по выставленной тейк-профит заявке на исполненную часть заявки выполнилось условие активации
Если интересует момент исполнения стоп заявки, то это уже бит 2
Цитата
Ирина написал: Значит, этот момент интересует, момент снятия. Так снимутся, не взирая на начавшийся расчет?
Если срок действия кончился, то стоп снимается, не зависимо идет расчет или нет.
Ирина написал: Другой вопрос: что происходит с "тейк-профитом по заявке" или простым "тейк-профитом" со сроком действия "сегодня", находящимся в процессе расчета min/max, при наступлении клиринга?
Ничего не произойдет. Стоп заявки со сроком сегодня, проверяются по астрономическому "Сегодня", а не по расписанию торгов на бирже.
Немного поправим ответ, если говорить про стоп "по исполнению" то он снимется, при снятии связанной заявки.
Ирина написал: Вопрос: можно ли значение этого параметра получить через Lua? (не нашла) Или как отличить заявку "по исполнению" в состоянии при "живой" лимитке-условии и исполненной средствами Lua?
Посмотрите параметр flags
Цитата
Ирина написал: Другой вопрос: что происходит с "тейк-профитом по заявке" или простым "тейк-профитом" со сроком действия "сегодня", находящимся в процессе расчета min/max, при наступлении клиринга?
Ничего не произойдет. Стоп заявки со сроком сегодня, проверяются по астрономическому "Сегодня", а не по расписанию торгов на бирже.