Иногда CreateDataSource возвращает неверные данные. Демонстрационный скрипт:
Скрытый текст
Код
class, sec_code = "SPBFUT", "SiH1"
local run = true
function OnStop()
run = nil
return 500
end
function main()
local Volume -- Дневной объём
local ds_D1 = assert(CreateDataSource(class, sec_code, INTERVAL_D1))
ds_D1:SetUpdateCallback(function (index)
if not run then return end
Volume = ds_D1:V(index)
end)
sleep(1000)
local Day = os.date("*t").day
local CreateDS = true
local ds_M1, Index
local function cb(index)
if not run then return end
if Index and index > Index then
run = false return -- Ждём закрытия свечи, на которой обнаружена ошибка
end
if math.floor(os.time() / 60) ~= math.floor(os.time(ds_M1:T(index)) / 60) then return end -- Время свечи не совпадает с текущим
local volume = 0 -- Суммарный объём за текущий день
for i = index, 1, -1 do
local day = ds_M1:T(i).day
if day < Day then break
elseif day == Day then
volume = volume + ds_M1:V(i)
end
end
if volume == Volume then
CreateDS = true -- Если ошибки нет, открываем новый DS
elseif Index == nil then
Index = index -- Если есть ошибка, запомним номер последней свечи
message(string.format("[ERROR] %s: index: %u: %s, %s", sec_code, Index, Volume, volume), 3)
end
end
while run do
if CreateDS then
if ds_M1 then
ds_M1:Close()
sleep(2000)
end
if run then
ds_M1 = assert(CreateDataSource(class, sec_code, INTERVAL_M1))
ds_M1:SetUpdateCallback(cb)
CreateDS = false
end
else sleep(1) end
end
ds_D1:Close()
if Index then
local function canle(ds, index)
local t = ds:T(index)
return string.format("%02u:%02u:%02u: %s; %s; %s; %s; %s", t.hour, t.min, t.sec, ds:O(index), ds:H(index), ds:L(index), ds:C(index), ds:V(index))
end
local s = string.format("%s: index: %u\n%s", sec_code, Index, canle(ds_M1, Index)) -- Выводим свечу, на которой обнаружена ошибка
ds_M1:Close()
sleep(100)
ds_M1 = assert(CreateDataSource(class, sec_code, INTERVAL_M1))
repeat sleep(1000) until ds_M1:Size() > Index
message(s .. "\n" .. canle(ds_M1, Index), 2) -- Сравниваем со свечой с тем же индексом из другого DS
end
ds_M1:Close()
end
Графики должны быть закрыты. Можно запустить несколько скриптов с разными инструментами. Через некоторое время будет сообщение вида:
Скрытый текст
Возможно, большое количество свечей повышает вероятность ошибки. Но это не точно.
Надо делать так, как надо. А как не надо - делать не надо.
Исполнение кода занимает некоторое время, команда sleep принудительно останавливает процесс. Между чтениями данных из базы данных образуется задержка, которая может быть достаточно большой чтобы выполнилась одна или более транзакций.
Ваш код очень тяжело читается, возможно содержит неочевидные ошибки. Что именно он должен делать, если словами?
Артем написал: Между чтениями данных из базы данных образуется задержка, которая может быть достаточно большой чтобы выполнилась одна или более транзакций.
Транзакция в прошлом? В окне сообщений есть и время свечи и время самого сообщения.
Цитата
Артем написал: Что именно он должен делать, если словами?
Показывать что
Цитата
Старатель написал: Иногда CreateDataSource возвращает неверные данные
Сравнивается дневной объём на D1 и M1. При несовпадении запоминается индекс крайней свечи M1 на момент создания DS (обычно эта свеча содержит ошибку). По закрытию свечи M1 выводится её содержимое. В message - одна и та же свеча из разных DataSource, открытых с небольшой (100 мс) временной разницей.
Надо делать так, как надо. А как не надо - делать не надо.
Дабы предупредить спекуляции на другие темы: Обычно DataSource обновляются синхронно в порядке их создания. И, если нет разрывов соединения или потерь пакетов, это позволяет в тестовом скрипте сравнивать данные из DataSource разных таймфреймов.
Надо делать так, как надо. А как не надо - делать не надо.
Артем написал: Между чтениями данных из базы данных образуется задержка, которая может быть достаточно большой чтобы выполнилась одна или более транзакций.
Транзакция в прошлом? В окне сообщений есть и время свечи и время самого сообщения.
В асинхронных фреймворках еще и не такое бывает.
Вроде как свечи обновляются срезами, разные срезы = расхождение в данных.
Насколько я понимаю, время свечки пересчитывается по локальным часам. Может ли быть такое, что сделка, попадающая точно на границу свечек, может попасть в предыдущую свечку а не в следующую при обработке в колбеке? При этом такой эффект не будет наблюдаться при загрузке архива свечек с сервера. Попробуйте вывести также содержимое соседних свечек.
Проделал следующее: Сразу после первого сообщения об ошибке до закрытия свечи открыл минутный график. Через пару минут разорвал соединение с сервером. На свече объём 4984
Скрытый текст
Затем переключился на другой таймфрейм и обратно на минутный. Объём поменялся на 4982
Скрытый текст
Саппорт, вас интересуют баг-репорты?
Надо делать так, как надо. А как не надо - делать не надо.
Вот скрипт попроще, который довольно быстро воспроизводит эту проблему:
Скрытый текст
Код
class_code, sec_code = "SPBFUT", "SiH1"
local run = true
local logFile
function OnStop()
run = nil
end
local function WriteLog(message)
local t = os.sysdate()
local timestamp = string.format("%02u:%02u:%02u.%03u", t.hour, t.min, t.sec, t.ms)
logFile:write(timestamp .. " " .. message .. "\n")
logFile:flush()
end
function main()
logFile = io.open("ds_test.log", "a+")
WriteLog("subscribing for D1")
local dsd = CreateDataSource(class_code, sec_code, INTERVAL_D1)
while dsd:Size() == 0 do
sleep(100)
end
sleep(200)
WriteLog("D1 filled: " .. dsd:Size())
local count = 10
while run do
WriteLog("subscribing for H2 & H4")
local ds2 = CreateDataSource(class_code, sec_code, INTERVAL_H2)
local ds4 = CreateDataSource(class_code, sec_code, INTERVAL_H4)
while ds2:Size() == 0 or ds4:Size() == 0 do
sleep(100)
end
sleep(200)
WriteLog("H2 filled: " .. ds2:Size())
WriteLog("H4 filled: " .. ds4:Size())
local vol2, vol4
repeat
local vol4 = ds4:V(ds4:Size())
local vol2 = ds2:V(ds2:Size()) + (ds4:T(ds4:Size()).hour == ds2:T(ds2:Size()).hour and 0 or ds2:V(ds2:Size() - 1))
if vol4 == vol2 then
WriteLog("V4 = " .. vol4 .. " V2 = " .. vol2)
break
end
WriteLog("error: V4 = " .. vol4 .. " V2 = " .. vol2 .. " difference = " .. (vol4 - vol2))
sleep(1000)
count = count - 1
if count == 0 then
run = false
end
until not run
WriteLog("closing H2 & H4")
ds2:Close()
ds2 = nil
ds4:Close()
ds4 = nil
end
dsd:Close()
logFile:close()
logFile = nil
end
1. Свечи считаются на сервере, и хранятся там ГОДАМИ, т.е. это НЕИЗМЕННАЯ величина, посчитанная ОДИН раз. В том числе, их объёмы, временные отметки и прочая "японская" хрень. А раз так - были там "разрывы соединения или потери пакетов" или нет - какая разница?
2. Все эти данные нужны только для прогнозирования, которое по определению имеет ВЕРОЯТНОСТНЫЙ характер. Следовательно, особая точность в этих значениях не нужна (и даже вредна). А потому сама проблема, вынесенная в заголовок, не имеет значения: ну, возвращает "иногда" - и пущай себе возвращает!
3. Более тяжёлые свечи строятся из более лёгких по простейшему и однозначному алгоритму. Следовательно, если нет ошибок в младших значениях свечей, их не может быть и в старших. И как при этом можно перепутать последнюю свечу с предпоследней - убей, не понимаю! И при чём тут "сделки, попадающие точно на границу свечек"? Они ведь УЖЕ ПОСЧИТАНЫ! ЕЩЕ НА СЕРВЕРЕ ПОСЧИТАНЫ! ОТКУДА, может взяться "обработка в колбеке"?
4. Шаг влево, шаг вправо карается расстрелом: "должен быть закрыт график по тестируемому таймфрейму". А это с какого бодуна? А уж если так - кто назвал эту софтину "асинхронным фреймворком!?
P.S. Лично я с самого начала считал свои свечки сам (по 9 таймфреймам, начиная с 15-секундного) и по всем тикерам, за которыми следит скрипт (таковых несколько сотен). И это чуть ли не единственное место в скрипте, которое, будучи однажды написано, прекрасно работало и работает до сих пор без сбоев. Ах, да - я же считаю даже не свечи (кому они нужны?), а динамику их изменения (скорость движения), да ещё и в дискретной шкале: курс может а) стоять на месте (это я использую, кстати, для отслеживания ситуаций, когда торги по данному инструменту не ведутся) б) медленно ползти в) нормально идти г) быстро бежать д) лететь сломя голову - всего 9 состояний (с учётом направления). Просто, надёжно (все эти ошибки, приведённые в этой ветке практически автоматом исчезнут!), наглядно (куда там графику до такого уровня наглядности!), и безо всякого CreateDataSource вообще! Всем рекомендую.
Описанная в данном инциденте ошибка была исправлена в версии 9.2 терминала QUIK. Рекомендуем обновить версию программы. Приносим извинения за причиненные неудобства.