Блог им. _sk_
local Candles = require("util.Candles")
local ds, error = Candles.create(classCode, secCode, timeframe, "indicativequote", timeout) if ds == nil then logger:error("Cannot create datasource for " .. secCode .. ": " .. error) else dataSources[secCode] = ds if ds:Size() == 0 then logger:error("No data for " .. secCode) ds:Close() dataSources[secCode] = nil else logger:info(ds:Size() .. " " .. timeframe .. "-min candles for " .. secCode .. " loaded.") end end
local HOUR = 3600 --- Перевод московское время -> нью-йоркское время -- @param dt таблица с датой и временем в Москве -- @return таблица с датой и временем в Нью-Йорке local function MSK_NY(dt) local yyyymmddhh = ((dt.year * 100 + dt.month) * 100 + dt.day) * 100 + dt.hour local diff = 0 if yyyymmddhh >= 2019110310 then diff = 8 * HOUR elseif yyyymmddhh >= 2019031009 then diff = 7 * HOUR elseif yyyymmddhh >= 2018110410 then diff = 8 * HOUR else diff = 7 * HOUR end return os.date("*t", os.time(dt) - diff) end --- Функция для фильтрации свечей основной торговой сессии. local accept = function(dt) local hhmm = dt.hour * 100 + dt.min return 930 <= hhmm and hhmm < 1600 end -- Запрос свечей. local candles = Candles.getCandles(ds, MSK_NY, accept, 30)
-- -- Формирование свечей по содержимому DataSource с фильтрацией и агрегированием. -- local Candles = {} local intervals = { [1] = INTERVAL_M1, [2] = INTERVAL_M2, [3] = INTERVAL_M3, [4] = INTERVAL_M4, [5] = INTERVAL_M5, [6] = INTERVAL_M6, [10] = INTERVAL_M10, [15] = INTERVAL_M15, [20] = INTERVAL_M20, [30] = INTERVAL_M30, [60] = INTERVAL_H1, } --- Создать объект DataSource и подождать, пока в нём появятся данные, но не более указанного количества секунд. -- @param classCode код классса -- @param secCode код инструмента -- @param timeframe таймфрейм в минутах (1, 2, 3, 4, 5, 6, 10, 15, 20, 30, 60) -- @param paramName имя параметра или nil, если нужно заказать обычные свечи -- @param timeout таймаут ожидания данных в секундах -- @return объект DataSource в случае успешного создания объекта, иначе nil, error local function create(classCode, secCode, timeframe, paramName, timeout) local ds, error if paramName == nil then ds, error = CreateDataSource(classCode, secCode, intervals[timeframe]) else ds, error = CreateDataSource(classCode, secCode, intervals[timeframe], paramName) end if ds == nil then return nil, error end ds:SetEmptyCallback() local deadline = os.time() + (timeout or 15) while os.time() < deadline and ds:Size() == 0 do sleep(10) end if ds:Size() == 0 then return nil, "No candles for " .. secCode end return ds end Candles.create = create local function equalDateTime(dt1, dt2) return type(dt1) == "table" and type(dt2) == "table" and dt1.year == dt2.year and dt1.month == dt2.month and dt1.day == dt2.day and dt1.hour == dt2.hour and dt1.min == dt2.min and dt1.sec == dt2.sec and (dt1.ms or 0) == (dt2.ms or 0) end --- Получить свечи из открытого объекта DataSource. -- @param ds объект DataSource -- @param maxSize максимальное количество запрашиваемых свечей или nil, если ограничение не нужно -- @return таблица с полями size (количество свечей) и массивами T, O, H, L, C, V, индексируемыми от 1 до size -- и содержащими параметры свечей local function getRawCandles(ds, maxSize) local candles = { size = 0, T = {}, O = {}, H = {}, L = {}, C = {}, V = {}, } if ds == nil or ds:Size() == 0 then return candles end table.ssort({ 0, 1 }, function(a, b) local dsSize = ds:Size() if maxSize == nil then maxSize = dsSize end local size, offset if dsSize <= maxSize then size, offset = dsSize, 0 else size, offset = maxSize, dsSize - maxSize end for i = 1, size do local j = i + offset candles.T[i] = ds:T(j) candles.O[i] = ds:O(j) candles.H[i] = ds:H(j) candles.L[i] = ds:L(j) candles.C[i] = ds:C(j) candles.V[i] = ds:V(j) end candles.size = size return true end) return candles end Candles.getRawCandles = getRawCandles --- Получить свечи из открытого DataSource. При необходимости можно отфильтровать свечи и агрегировать их -- согласно указанному таймфрейму. -- @param ds объект DataSource -- @param timeshift функция, преобразующая таблицу со временем, иначе время не преобразуется -- @param accept функция фильтрации в зависимости от уже преобразованного времени свечи, -- иначе будут использованы все свечи -- @param timeframe таймфрейм в минутах (1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60) -- или nil, если агрегация не нужна -- @param rawCandlesMaxSize максимальное количество свечей, запрашиваемых в функции getRawCandles, -- или nil, если запрашиваются все свечи -- @return таблица с полями size (количество свечей) и массивами T, O, H, L, C, V, индексируемыми от 1 до size -- и содержащими соответствущие параметры свечей local function getCandles(ds, timeshift, accept, timeframe, rawCandlesMaxSize) local rawCandles = getRawCandles(ds, rawCandlesMaxSize) if rawCandles.size == 0 then return rawCandles end if type(timeshift) ~= "function" then timeshift = nil end if type(accept) ~= "function" then accept = nil end if intervals[timeframe] == nil then timeframe = nil end if timeshift == nil and accept == nil and timeframe == nil then return rawCandles end local candles = { size = 0, T = {}, O = {}, H = {}, L = {}, C = {}, V = {}, } for i = 1, rawCandles.size do local dt = rawCandles.T[i] if timeshift ~= nil then dt = timeshift(dt) end if accept == nil or accept(dt) then dt.min = dt.min - (dt.min % timeframe) dt.sec = 0 if dt.ms then dt.ms = 0 end if dt.mcs then dt.mcs = 0 end local j = candles.size if j == 0 or not equalDateTime(candles.T[j], dt) then j = j + 1 candles.size = j candles.T[j] = dt candles.O[j] = rawCandles.O[i] candles.H[j] = rawCandles.H[i] candles.L[j] = rawCandles.L[i] candles.C[j] = rawCandles.C[i] candles.V[j] = rawCandles.V[i] else candles.H[j] = math.max(candles.H[j], rawCandles.H[i]) candles.L[j] = math.min(candles.L[j], rawCandles.L[i]) candles.C[j] = rawCandles.C[i] candles.V[j] = candles.V[j] + rawCandles.V[i] end end end return candles end Candles.getCandles = getCandles --- Урезать набор свечей, оставив не более чем указанное количество последних свечей. -- @param candles таблица с полями size (количество свечей) и массивами T, O, H, L, C, V, индексируемыми от 1 до size -- и содержащими соответствущие параметры свечей -- @param maxSize максимальное количество оставляемых свечей -- @return исходная таблица candles, если количество свечей меньше или равно maxSize, либо новая урезанная таблица -- размера maxSize local function truncate(candles, maxSize) if candles.size <= maxSize then return candles end local offset = candles.size - maxSize local T, O, H, L, C, V = {}, {}, {}, {}, {}, {} for i = 1, maxSize do T[i] = candles.T[i + offset] O[i] = candles.O[i + offset] H[i] = candles.H[i + offset] L[i] = candles.L[i + offset] C[i] = candles.C[i + offset] V[i] = candles.V[i + offset] end return { size = maxSize, T = T, O = O, H = H, L = L, C = C, V = V, } end Candles.truncate = truncate --- Сравнение таблиц с датой и временем. -- @param datetime1 первая таблица с датой и временем -- @param datetime2 вторая таблица с датой и временем -- @return -1, если datetime1 < datetime2; +1, если datetime1 > datetime2; 0, если datetime1 == datetime2. local function datetimeComparator(datetime1, datetime2) local d = datetime1.year - datetime2.year if d < 0 then return -1 elseif d > 0 then return 1 end d = datetime1.month - datetime2.month if d < 0 then return -1 elseif d > 0 then return 1 end d = datetime1.day - datetime2.day if d < 0 then return -1 elseif d > 0 then return 1 end d = datetime1.hour - datetime2.hour if d < 0 then return -1 elseif d > 0 then return 1 end d = datetime1.min - datetime2.min if d < 0 then return -1 elseif d > 0 then return 1 end d = datetime1.sec - datetime2.sec if d < 0 then return -1 elseif d > 0 then return 1 end d = (datetime1.ms or 0) - (datetime2.ms or 0) if d < 0 then return -1 elseif d > 0 then return 1 end d = (datetime1.count or 1) - (datetime2.count or 1) if d < 0 then return -1 elseif d > 0 then return 1 end return 0 end --- Найти индекс свечи в массиве по заданному моменту времени, используя двоичный поиск. -- @param candles таблица со свечными данными -- @param datetime таблица с искомыми датой и временем свечи -- @return true, index, если найдена свеча с таким временем, и false, index, если свеча не найдена; -- в случае, когда свечи нет, значение index определяет место вставки свечи в массив (от 0 до size + 1). local function searchCandleId(candles, datetime) local size = candles.size if size == 0 then return false, 1 end local b = size local T = candles.T local d = datetimeComparator(T[b], datetime) if d == 0 then return true, b elseif d < 0 then return false, b + 1 end local a = 1 d = datetimeComparator(T[a], datetime) if d == 0 then return true, a elseif d > 0 then return false, a - 1 end while true do local c = math.floor((a + b) / 2) if a == c then return false, b end d = datetimeComparator(T[c], datetime) if d < 0 then a = c elseif d > 0 then b = c else return true, c end end end Candles.searchCandleId = searchCandleId return Candles
Появляется ошибка