Блог им. _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
Появляется ошибка