Разбираясь в работе Quik и функциональных возможностях Lua, иногда можно запутаться, и тогда трезвый взгляд со стороны помогает всё расставить по местам. Для меня таким взглядом всегда был Nikolay. И сейчас я бы не стал вмешиваться в эту дискуссию, если бы не ответы!
Да, это возможно! В Lua действительно можно запустить другой Lua-скрипт с помощью функций dofile или loadfile. Вы можете создать основной скрипт, который будет проверять, запущен ли другой скрипт, и если нет — запускать его. Все это и так явно.
Но почему бы не пойти дальше? Хочу озвучить другой подход.
Давайте забудем про все языки (Си, C#, да и про Fortran с Basic я забыл ещё со студенческих времён). Что у нас есть?
API Quik: разработчики любезно предоставили нам возможность через Lua получать данные от брокера и отправлять приказы (заявки) брокеру.
Функционал Lua: вот здесь начинается самое интересное!
Представим, что поток main — это основной поток (другого ничего нет), в котором всё крутится (в этом легко убедиться, запустив его без задержек). Функционал Lua предоставляет лёгкие потоки, которые работают внутри основного потока — они называются корутины.
Что они делают? Любые программы, блоки, модули, функции — всё это можно запускать в корутинах! Причём не только последовательно, но и асинхронно.
Таким образом, функция main(), будучи запущенной, превращается в сложнейшую программу, способную не только явно но и в фоновом режиме выполнять что угодно: получать данные, проводить расчёты, подключать и останавливать модули и многое другое.
К чему я так подробно рассказываю? Напишите в своей программе модуль на Lua (это та же таблица) который хотите запустить, и используйте его где угодно.
Это открывает огромные возможности для создания гибких и мощных решений.
Коллбек как источник данных, для асинхронной обработки данных. Довел вариант до рабочего, в качестве источника данных OnAllTrade, получаем таблицу и переносим расчеты, не в маин, в сопрограмму. Такой подход не блокирует выполнение других операций программы, если количество сделок большое, или сделка обрабатывается долго, которые запущены в процессе. Производительность и нагрузки просто супер. Задание паузы между итерациями обработки позволяет контролировать частоту обработки данных, что полезно для предотвращения перегрузки системы, а также задает временные промежутки для накопления данных в очередь. Этот подход позволяет не только улучшить структуру кода, но и легко расширять систему с новыми метриками и функциональностью. Каждый компонент изолирован и отвечает только за свою часть. Легко добавлять или менять логику внутри каждого модуля, не влияя на другие части системы. А также, можно добавлять новые метрики или алгоритмы, расширяя существующие возможности самой программы. Собраны метрики и алгоритмы в отдельные модули. Каждый модуль может экспортировать только необходимые функции и данные, позволяя другим частям системы использовать их через интерфейсы, что должно помочь избежать излишней зависимости между компонентами. Метрики можно групировать по стратегиям. Система принятия решений будет интегрировать метрики, анализировать их с использованием различных стратегий и на основе результатов принимать решение о торговых действиях. Основными компонентами такой системы принятия решений будут: 1. Метрики – используем различные метрики для анализа данных. 2. Стратегии – каждая стратегия (скальпинг, трендовая торговля, арбитраж) будет использовать различные метрики для принятия решений. 3. Алгоритм принятия решений – будет выбирать, какая стратегия должна быть использована в текущих условиях рынка и какое действие (купить/продать) должно быть выполнено. 4. Система обработки данных – будет собирать данные (например, цены и объемы), передавать их в соответствующие стратегии и использовать метрики для анализа. В моем случае придется, переосмыслить архитектуру основной программы - добавить универсальности, но и наконец то, выхожу на однотипную структуру данных для такой программы, под гордым названием с большой буквы Робот. Пока, Всем хорошей торговли!
Не знаю почему не получается сворачивать окно у меня. Подскажите последовательность нажатия конок? Хотел предложить на обсуждение еще один вариант, обработки сделок уже т. всех сделок с использованием очереди. По факту даже два варианта. 1) обрабатываем в маин. 2) асинхронный. Как раз перерабатываю, чтобы модуль добавить в программу.
Касаемо обработки собственных сделок, была а возможно и есть программа написана на qpl, называется Калькулятор уровней позиции: (q_calc) автор Николай Морошкин. На ней учился переписывая на луа логику. Выкладываю как возможный пример обработки сделок, просто в нем есть четкая логика, на ошибки нужно проверять.
Код
function detect_position()
--[[ Проверки статуса заявок (1-й и 2-й биты flags):
"Активна": bit.band(order.flags, 1) > 0
"Неактивна" (исполнена либо снята): bit.band(order.flags, 1) == 0
"Исполнена": bit.band(order.flags, 1) == 0 и bit.band(order.flags, 2) == 0
"Снята": bit.band(order.flags, 1) == 0 и bit.band(order.flags, 2) > 0
Нас интересуют два флага:
OF_ACTIVE = 0x01
OF_KILL = 0x02
--]]
local ord = "orders"
local max_numorder = getNumberOf("orders")
-- Отслеживаем количество ордеров
Log:trace('detect_position: отслеживаю размер массива '
.. ' getNumberOf =' .. tostring(max_numorder)
.. ' index =' .. tostring(max_numorder-1)
.. ' / o_index = ' .. tostring(o_index)
.. ' save_needed =' .. tostring(save_needed))
-- Функция для поиска ордеров
local function myFind(A, C, S, F)
return (A == ACCOUNT and C == CLASSCODE and S == SECCODE
and bit.band(F, 0x2) == 0 and bit.band(F, 0x1) == 0)
end
-- Поиск ордеров с нужными фильтрами
local iItems = SearchItems("orders", 0, max_numorder - 1, myFind, "account,class_code,sec_code,flags")
-- Проверка на пустой результат
if not iItems or #iItems == 0 then
Log:trace('ERROR: SearchItems вернул пустой результат')
return false
end
Log:trace('detect_position: всего элементов ' .. tostring(#iItems) .. ' последний ордер: ' .. tostring(iItems[#iItems]))
-- Если еще не инициализировано
if initialized == 0 then
initialized = -1
return
end
-- Обработка ордеров
for i = o_index, #iItems do
local order = getItem("orders", iItems[i])
if not order or type(order) ~= "table" then return false end
-- Определение флагов состояния ордера
local OF_ACTIVE = bit.band(order.flags, 1) > 0
local NOT_ACTIVE = bit.band(order.flags, 1) == 0
local OF_FILL = bit.band(order.flags, 1) == 0 and bit.band(order.flags, 2) == 0
local OF_KILL = bit.band(order.flags, 1) == 0 and bit.band(order.flags, 2) > 0
if order.account == ACCOUNT and order.class_code == CLASSCODE and order.sec_code == SECCODE
and (OF_FILL or OF_ACTIVE) and not Orders[order.order_num] then
Orders[order.order_num] = os.time() -- Запоминаем время
-- Получаем информацию о заказе
local order_n = order.order_num
local order_dt = order.datetime
local order_d = get_date(order_dt) -- Дата
local order_t = get_time(order_dt) -- Время
last_tradedate = order_d
local order_b = order.balance
local order_q = order.qty - order_b
local order_p = order.price
local order_op = bit.band(order.flags, 0x4) == 0 and 'BUY' or 'SELL'
-- Логирование активных ордеров
if OF_ACTIVE then
Log:trace(NAME_OF_STRATEGY .. ' ' .. i .. ' ACTIVE лимитная заявка по ' .. order.sec_code
.. ' number: ' .. tostring(order_n)
.. ' ' .. tostring(order_d)
.. ' ' .. tostring(order_t)
.. ' ' .. tostring(order_op)
.. ' ' .. tostring(order_q) .. ' (' .. tostring(order_b) .. ')'
.. ' ' .. tostring(order_p))
end
-- Обработка исполненных ордеров
if OF_FILL then
Log:trace(NAME_OF_STRATEGY .. ' ' .. i .. ' Исполнена лимитная заявка по ' .. order.sec_code
.. ' ' .. tostring(order_d)
.. ' ' .. tostring(order_t)
.. ' ' .. tostring(order_op)
.. ' ' .. tostring(order_q) .. ' (' .. tostring(order_b) .. ')'
.. ' ' .. tostring(order_p)
.. ' number: ' .. tostring(order_n))
local trade_q = 0
local trade_p = 0
local exchange_comission = 0
-- Поиск сделок, соответствующих ордеру
for trades_i = 0, getNumberOf("trades") - 1 do
local trade = getItem("trades", trades_i)
if trade.order_num == order_n then
last_order_id = order_n
local trade_d = get_date(trade.datetime)
local trade_t = get_time(trade.datetime)
local qq = trade.qty
local pp = trade.price
trade_q = trade_q + qq
trade_p = trade_p + (qq * pp)
exchange_comission = exchange_comission + trade.exchange_comission
end
end
if trade_q ~= 0 then
trade_p = trade_p / trade_q
exchange_comission = exchange_comission / trade_q
end
-- Проверка на рассинхронизацию заявок и сделок
if order_q ~= trade_q then
Log:trace("Рассинхронизация заявок и сделок: " .. tostring(order_n)
.. ", " .. tostring(order_q) .. "/" .. tostring(trade_q))
message("Рассинхронизация заявок и сделок: " .. tostring(order_n)
.. ", " .. tostring(order_q) .. "/" .. tostring(trade_q))
end
-- Запись в журнал
Log:trace('Получаю тек. позицию: '
.. " " .. tostring(pos.dir)
.. ' ' .. tostring(pos.qty)
.. ' ' .. tostring(pos.op)
.. ' ' .. tostring(pos.cap)
.. ' ' .. tostring(pos.be))
Log:trace('Получаю изменения в позиции: '
.. ' ' .. tostring(qcap)
.. ' order_op=' .. tostring(order_op)
.. ' order_q=' .. tostring(order_q)
.. ' order_b=' .. tostring(order_b)
.. ' order_p=' .. tostring(order_p)
.. ' / trade_q=' .. tostring(trade_q)
.. ' trade_p=' .. tostring(trade_p))
-- Управление позицией
local log_action = ""
if pos.dir == "n" then
pos.dir, pos.qty, pos.op, pos.cap, pos.be, pos.open = set_position(order_op, trade_q, trade_p)
write_journal(order_op, qcap, trade_q, trade_p, trade_datetime)
log_action = "Открытие позиции " .. tostring(pos.dir) .. ' ' .. tostring(pos.qty) .. ' ' .. tostring(pos.op)
Log:trace(i .. ' ' .. log_action)
save_needed = 1
elseif (pos.dir == "L" and order_op == "BUY") or (pos.dir == "S" and order_op == "SELL") then
pos.op = pos.op * pos.qty + trade_p * trade_q
pos.qty = pos.qty + trade_q
pos.op = pos.op / pos.qty
write_journal(order_op, -1, trade_q, trade_p, trade_datetime)
log_action = "Наращивание позиции " .. tostring(pos.dir) .. tostring(pos.qty)
Log:trace(i .. ' ' .. log_action)
save_needed = 1
else
if trade_q == pos.qty then
pos.dir, pos.qty, pos.op, pos.cap, pos.be, pos.open = "n", 0, 0, 0, 0, bar.open
write_journal(order_op, 0, trade_q, trade_p, trade_datetime)
log_action = "Закрытие позиции " .. tostring(pos.dir) .. ' ' .. tostring(pos.qty)
Log:trace(i .. ' ' .. log_action)
end
save_needed = 1
end
end
end
end
end
nikolz, Вы опять правы, мои предположения и понимание не есть "истина в последней инстанции", и я не отношу себя к "гуру", бывает ошибаюсь. Мне просто не понятно, что на специализированном форуме проходится рассуждать про объективные вещи. Да по мере присутствия на бирже учусь и набираюсь опыта, им делюсь и обсуждаю.
Цитата
nikolz написал: Вы полагаете что БКС гоняется за тиками и поэтому Вы его называете ММ?
Вы зачем ставите все "с ног на голову"? Ведь это Ваше утверждение.
Цитата
nikolz написал: Я утверждаю, что понятие (кличка) "MM" связано лишь с оказанием платных услуг биржи. Это никак не связано ни со следами, ни с размером ни с чем другим .
ММ это прежде всего коммерческий проект, целью которого является извлечение прибыли, как и любой другой коммерческой организации. Обладая значительными средствами, набирают большие позиции которые, оставляя следы на графике, а значить могут повести рынок за собой или держать его в диапазоне. Это интересует трейдера, отвечая на вопрос в какую сторону держать позицию, и уж совсем не интересно сколько заработал ММ. Именно это поведение как крупного игра их отличает. Вы можете даже конкретные алгоритмы найти в сети. Обладая дополнительной информацией они строят свои торговые стратегии, свой бизнес.
"Что написано пиром, то не вырубить топором". Касаемо моего подхода, читайте выше, там есть и формализация понятия о чем я рассуждаю, а рассуждаю я о контрагентах в сделках, как их можно формализовать в алгоритмических стратегиях.
nikolz, Все я прекрасно понял. Смею предположить, Ваши торговые стратегии, видимо не выходят за пределы технологий "скальпинга", отсюда и кругозор. Выше Вы опубликовали участников заключивших договора, Вам наименования организаций не о чем не говорят? Именно они гоняются за тиками? Вы просто посмотрите на ликвидность в стаканах, и на финансовые возможности компаний обеспечивающих эту ликвидность. Что делать с остальными финансами? Могу предположить, что под скапинг выделяется не большая доля активного капитала на котором и крутятся HFT-алгоритмы, выдели 1% а что делать с 99% средств? Кстати, на эту же проблему эффективности, натолкнулся у себя в своих среднесрочных стратегиях, суть в следующем: есть 100% торговый капитал, по правилам риск менеджмента активный не более < 25%, что означает > 75% т. капитала лежит просто в обеспечении сделки, не плохо бы часть покрутить в скальперских стратегиях. Касаемо придумок все за долго до меня, стратегии опубликованы, на классику жанра указал выше. И никаких кличек, терминология применяется разная но это для упрощения в пониманиях, сегодня модная есть "смарт мани", там своя, у классиков своя, Суть одна. Касаемо следов не только на ценовых графиках отслеживаются, но часто в стакане, такой объем можно видеть, так как рассчитываемся по определенным правилам для данного эмитента то есть алгоритмический. Такие объемы определяются, и от них строятся разные стратегии. Платная услуга биржи, это не признак ММ, это один из источников дохода ММ. Даже мне раньше прилетало от биржи.
nikolz, Все это верно, это не предмет обсуждения. ММ это прежде всего коммерческий проект, целью которого является извлечение прибыли, как и любой другой коммерческой организации. Обладая значительными средствами, набирают большие позиции которые, оставляя следы на графике, а значить могут повести рынок за собой или держать его в диапазоне. Это интересует трейдера, отвечая на вопрос в какую сторону держать позицию, и уж совсем не интересно сколько заработал ММ. Именно это поведение как крупного игра их отличает. Вы можете даже конкретные алгоритмы найти в сети. Обладая дополнительной информацией они строят свои торговые стратегии, свой бизнес.
nikolz, На форуме существует некая путаница в понятиях, и Вы не первый кто рассуждая о Маркет - мейкерах, упрощает их деятельность или сравнивает их с некими злодеями Крупными игроками занимающихся манипуляциями рынков. На фондовых биржах это крупные инвестиционные банки (например, Goldman Sachs, Morgan Stanley) как Вы думаете их интересуют сделки в один тик? На разных рынках по разному, задачи и техники принципиально одни. Отслеживая сделки на рынке в целях выявления крупных сделок для лучшего понимания ситуации и реализации в системах принятия решений, делю игроков условно на 3 категории: 1) Крупный игрок 2) Маркет-мейкер 3) Ретейл. Различие в поведении в торгах! имелось в виду именно это, поэтому подробно подчернил технический аспект. Касаемо ММ, хотя это специализированная участник, компания или и.банк, обязанные соблюдать строгие регуляторные требования, они тоже совершают ошибки. Давайте представим ситуацию набрана огромная позиция, а рынок пошел против, что на весах, штраф если заметят, или манипуляция во спасение добра. И таких ситуаций множество. И это только моя точка зрения в основе которой, методологии Ричарда Вайкоффа (Wyckoff) с его композитным игроком, решения как считать каждый принимает сам.
Да, использую в обработке собственных сделок комбинированный подход. Ну про это говорили. Вот коллбек которым пользуюсь, на форуме он широко обсуждался, сто лет пользуюсь не заглядывая.
Код
--[[ Trade CALLBACK ]]-- -- Вызов колбэка OnTrade, который использует self для контроля и записи данных
--function OnTrade(trade) tradeManager:OnTrade(trade) end
function OnTrade(trade)
local key = trade.trans_id
local i=tonumber(sdelka[0]);
if working
and key and key>0
and (i==0 and key~=sdelka.id) or (i>0 and sdelka[i][1]~=key)
--and trade.sec_code==symbol --and trade.class_code==class
then
i=i+1;
sdelka[0]=i;
sdelka[i]={};
sdelka[i][0]=sdelka[0];
sdelka[i][1]=trade.trans_id;
sdelka[i][2]=get_date(trade.datetime)
sdelka[i][3]=get_time(trade.datetime)
sdelka[i][4]="B"; if bit.band(trade.flags,4)~=0 then sdelka[i][4]="S"; end;
local dir = sdelka[i][4]=="B" and 1 or sdelka[i][4]=="S" and -1 or 0;
sdelka[i][5]=trade.qty*dir;
sdelka[i][6]=trade.price;
----- comission
sdelka[i][7]=trade.clearing_comission+trade.exchange_comission+trade.tech_center_comission;
sdelka[i][8]=trade.order_num;
sdelka[i][9]=trade.trade_num;
sdelka[i][10]=trade.sec_code;
sdelka.id=trade.trans_id;
Log:info("OnTrade! sdelka "..i .."; "
.."; trans_id="..sdelka[i][1]
.."; "..sdelka[i][2]
.."; "..sdelka[i][3]
.."; "..sdelka[i][4]
.."; "..sdelka[i][5]
.."; "..sdelka[i][6]
.."; comission="..sdelka[i][7]
.."; onum="..sdelka[i][8]
.."; tnum="..sdelka[i][9]
.."; "..sdelka[i][10]
.."\n"
);
--local a,v; for a,v in pairs(sdelka[i) do Log:trace( 'OnTrade! sdelka['..i..']['..tostring(a)..'] = '.. tostring(v) ) end;
end
end;
Всем добрый день, хочу просто добавить фундамент, в о сознание цели. Знание сила!
Коллбеки (или обратные вызовы) — это механизм, при котором одна функция передается как параметр в другую функцию, и она вызывается (или "вызвана") внутри этой функции, когда наступает определенное событие или условие. В контексте торгового алгоритма и работы с терминалом QUIK, коллбеки используются для получения данных (например, сделок) и их обработки в реальном времени. В очередь ставятся данные полученные от них, для уменьшения нагрузки.
Кто выступает контрагентом в сделке, и что нужно учитывать в скальпинге и подобных стратегиях.
1) Рынки МосБиржи, если рассматриваете ее, управляются маркет-мейкерами. Основные алгоритмы для обеспечения максимальной эффективности которые, можно даже сказать, обязан использовать маркет-мейкер: a) Алгоритмы выставления двустороних котировок; b) Алгоритмы управления позицией; c) Арбитражные алгоритмы; d) Алгоритмы управления рисками; e) Алгоритмы HFT (High-Frequency Trading). Работают не со стаканом а с книгой ордеров. Принцип работы HFT-алгоритмов (High-Frequency Trading): Осуществляют тысячи сделок в секунду. Мониторят микро изменения цен и мгновенно реагируют на рыночные дисбалансы. Технически: Используются FPGA (платы программируемой логики) для минимизации задержек. Торговля осуществляется максимально близко к серверам биржи (co-location), используются прямые подключения к биржевым серверам через высокоскоростные сети. Современные алгоритмы маркет-мейкеров работают с задержкой менее 1 миллисекунды. А в кризисных ситуациях может выйти из рынка.
2) На российском рынке сейчас преобладают частные инвесторы (МосБиржа), и этому есть простые объяснения. Вот из оценки брокера, доля частных инвесторов в объеме торгов акциями на ноябрь 2024г. - 76% за 2023г. -40%.
Разрабатывая архитектуру своей алгоритмической торговой программы, на мой взгляд, это нужно учитывать, особенно если стратегии скальперские. Луа в рамках QUIK, прекрасно справляется с торговыми задачами. Корутинный подход (асинхронность) прекрасно решает задачу Event и Wait озвученные nikolz, для этого, один из подходов создается своя таблица событий (Event).
-- Функция для вычисления кумулятивной функции распределения нормального распределения (N(x))
function norm_cdf(x)
local t = 1 / (1 + 0.2316419 * math.abs(x))
local d = 0.3989422804014337 * math.exp(-0.5 * x * x)
local prob = d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429)))))
if x >= 0 then
return 1 - prob
else
return prob
end
end
-- Основная функция для расчета цены опциона по модели Блэка-Шоулза
function black_scholes_call_price(S, K, T, r, sigma)
local d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
local d2 = d1 - sigma * math.sqrt(T)
return S * norm_cdf(d1) - K * math.exp(-r * T) * norm_cdf(d2)
end
-- Функция для нахождения подразумеваемой волатильности для одного опциона
function implied_volatility(S, K, T, r, market_price)
local low = 0.0001
local high = 2.0
local tolerance = 0.0001
local sigma = (low + high) / 2
local price = black_scholes_call_price(S, K, T, r, sigma)
-- Итерационный метод поиска
while math.abs(price - market_price) > tolerance do
if price < market_price then
low = sigma
else
high = sigma
end
sigma = (low + high) / 2
price = black_scholes_call_price(S, K, T, r, sigma)
end
return sigma
end
-- Функция для нахождения подразумеваемых волатильностей для серии опционов
function implied_volatilities_for_series(S, r, options)
local ivs = {}
for i, option in ipairs(options) do
local iv = implied_volatility(S, option.K, option.T, r, option.market_price)
table.insert(ivs, {K = option.K, T = option.T, implied_volatility = iv})
end
return ivs
end
-- Пример использования
local S = 100 -- Текущая цена актива
local r = 0.05 -- Безрисковая процентная ставка (5%)
-- Список опционов с параметрами: Цена исполнения (K), Время до экспирации (T в годах), Рыночная цена опциона (market_price)
local options = {
{K = 95, T = 30 / 365, market_price = 6},
{K = 100, T = 30 / 365, market_price = 5},
{K = 105, T = 30 / 365, market_price = 3.5},
{K = 110, T = 60 / 365, market_price = 4.5}
}
-- Вычисляем подразумеваемую волатильность для каждого опциона
local ivs = implied_volatilities_for_series(S, r, options)
-- Выводим результаты
for _, iv in ipairs(ivs) do
print(string.format("Цена исполнения: %.2f, Время до экспирации: %.2f лет, Подразумеваемая волатильность: %.4f%%",
iv.K, iv.T, iv.implied_volatility * 100))
end
Код
Что получилось:
Подразумеваемая волатильность: 42.0108
Цена исполнения: 95.00, Время до экспирации: 0.08 лет, Подразумеваемая волатильность: 21.5971%
Цена исполнения: 100.00, Время до экспирации: 0.08 лет, Подразумеваемая волатильность: 42.0108%
Цена исполнения: 105.00, Время до экспирации: 0.08 лет, Подразумеваемая волатильность: 46.9254%
Цена исполнения: 110.00, Время до экспирации: 0.16 лет, Подразумеваемая волатильность: 48.9867%
-- Функция для вычисления кумулятивной функции распределения нормального распределения (N(x))
function norm_cdf(x)
local t = 1 / (1 + 0.2316419 * math.abs(x))
local d = 0.3989422804014337 * math.exp(-0.5 * x * x)
local prob = d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429) )))
if x >= 0 then
return 1 - prob
else
return prob
end
end
-- Основная функция для расчета цены опциона по модели Блэка-Шоулза
function black_scholes_call_price(S, K, T, r, sigma)
local d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
local d2 = d1 - sigma * math.sqrt(T)
return S * norm_cdf(d1) - K * math.exp(-r * T) * norm_cdf(d2)
end
-- Функция для нахождения подразумеваемой волатильности
function implied_volatility(S, K, T, r, market_price)
local low = 0.0001
local high = 2.0
local tolerance = 0.0001
local sigma = (low + high) / 2
local price = black_scholes_call_price(S, K, T, r, sigma)
-- Итерационный метод поиска
while math.abs(price - market_price) > tolerance do
if price < market_price then
low = sigma
else
high = sigma
end
sigma = (low + high) / 2
price = black_scholes_call_price(S, K, T, r, sigma)
end
return sigma
end
-- Для примера использования
local S = 100 -- Текущая цена актива
local K = 100 -- Цена исполнения
local T = 30 / 365 -- Время до экспирации (в годах)
local r = 0.05 -- Безрисковая процентная ставка (5%)
local market_price = 5 -- Рыночная цена опциона call
local iv = implied_volatility(S, K, T, r, market_price)
print(string.format("Подразумеваемая волатильность: %.4f", iv * 100))
Да действительно, есть особенности в самой функции math.floor, углубляться не стал, вернулся к варианту изложенному Kolossi, с двумя правилами, сделал просто более читаемым для себя. Спасибо за обсуждение. Казалось бы простая задача округления числа, а вариантов собралось с десяток.
Acaw написал: В блоке MAIN у меня создается источник данных (ДС):ds["NLMK"] = CreateDataSource("TQBR", "NLMK", INTERVAL_D1)Далее мне нужно в функции обращаться к нему, но не напрямую, а присвоив его локальному дс:local dsf = ds["NLMK"], т.е. чтобы можно обращаться через dsf:C(i).
В Lua, поведение относительно памяти зависит от того, является ли это совершенно новой таблицей или ссылкой на существующую таблицу. 1) Под новую таблицу, Lua выделит новую память. Предыдущая таблица, на которую ссылается пользователь, станет доступной для сборки мусора. Утечки памяти нет. 2) Если это ссылка (или псевдоним) на таблицу, которая уже существует в программе, то она просто укажет на эту существующую таблицу. Для самой таблицы не выделяется новая память. Тем не менее, предыдущая таблица, на которую ссылается пользователь, все равно будет удалена сборщиком мусора, если на нее больше не ссылаются нигде. Короче говоря, в целом безопасен и эффективен. Сборка мусора Lua отвечает за управление памятью, предотвращая утечки памяти в большинстве случаев.
Добрый день! Решил разбавить свой портфель разными активами, остановил выбор на ОФЗ, дописал под них свой класс. В общем не чего особенного, ОФЗ котируют трех типов поэтому в основу легло три стратегии, которые при одной и той же ситуации ведут себя по разному. Но столкнулся с проблемой автоматического обновления показателей инфляции и ключевой ставки, то есть с необходимость подключить внешние данные? Возможно уже решали подобную задачу или есть идеи. В основу создания универсального класса положена простая идея, класс который способен давать однозначные рекомендации по облигациям с указанием, на каких показателях основывается рекомендация. Сейчас это выглядит примерно так
Код
-- Вывод рекомендаций
function Bond:print_recommendations(inflation_rate, expected_rate_change)
local strategies = self:recommend_strategy(inflation_rate, expected_rate_change)
print("Рекомендации для облигации:", self.name)
for _, strategy in ipairs(strategies) do
print(string.format("- Стратегия: %s", strategy.strategy))
print(string.format(" Применима: %s", strategy.applicable and "Да" or "Нет"))
print(string.format(" Рекомендация: %s", strategy.message))
end
end
-- Пример использования
local myBond = Bond:new("ОФЗ 26207", "ОФЗ-ПД", 1000, 0.07, 950, 5)
local inflation_rate = 0.06 -- Текущая инфляция 6%
local expected_rate_change = -0.01 -- Ожидаемое снижение ставки на 1%
myBond:print_recommendations(inflation_rate, expected_rate_change)
Это ответ
Код
modules\Bond.lua" 1
Рекомендации для облигации: ОФЗ 26207
- Стратегия: Сохранение капитала
Применима: Да
Рекомендация: Облигация сохраняет капитал при текущей инфляции.
- Стратегия: Стабильный доход
Применима: Да
Рекомендация: ОФЗ-ПД подходят для стабильного дохода благодаря фиксированным купонам.
- Стратегия: Заработок на ставках
Применима: Нет
Рекомендация: Для заработка на ставках подходят долгосрочные ОФЗ-ПД.
>Exit code: 0 Time: 0.8326
function round(num, idp)
-- Если num некорректное, вернуть как есть
if not num or type(num) ~= "number" then return num end
-- Если idp не указан, использовать 0 (округление до целого числа)
idp = idp or 0
local mult = 10^idp
-- Округление для любого числа
local rounded = math.floor(num * mult + 0.5) / mult
-- Если число целое, убрать .0
if rounded == math.floor(rounded) then
return math.floor(rounded)
end
return rounded
end
Добрый день, Дмитрий, тема озвученная Вами далеко не тривиальна и дело здесь не в жлобстве. Пробежался по постам, тему обсуждали опытные пользователи, но мне показалось не до конца вникая в проблематику. В своих стратегиях я также получаю и анализирую данные с разных тайм фреймов. Под натерпелся. Каждый раз собирая новую торговую стратегию, говорю себе что последний раз переделываю структуру данных торговой системы, и эта структура будет универсальной и использоваться во всех стратегиях. Именно задача организации структуры данных для простого, универсального, удобного использования является фундаментальной, и будет всегда актуальна. Касаемо озвученной задачи:
Цитата
Дмитрий написал: надо выгружать в файл данные OHLC и некоторых индикаторов по всем доступным инструментам QUIK по всем доступным ТФ, для каждого ТФ свой файлт.е. ТФ 15м,30м,1час = 3 файлика в формате O,H,L,C,индикатор1,индикатор2,индикатор31 бар = 1 строчка файлика
Могу сказать как я ее решаю, правда я отказался писать в файл и очень давно.
Вы просто выведите таблицу ds2 и посмотрите что получите. А там будут методы и служебная информация. Функции для получения значений по индексу и дополнительному параметру. Так что не хранит ничего кроме этих методов. Если Ваш код подписку не закрывает, то и пере подписываться не нужно.
Acaw, Вы поймите, ds2 это не место хранения данных, это таблица методов получения этих данных. ds2 = CreateDataSource("SPBFUT", "RIU3", INTERVAL_M1) это подписка на получение данных по коду класса по коду тикера и определённому тайм фрейму. Сами данные на сервере и обновляются не зависимо от Вашего подключения.
О чудеса чудесные или Создание и Использование хук-функции. Что такое хуки простыми словами? Хуки — это технология перехвата вызовов функций в чужих процессах, в моем случае механизм, позволяющий зарегистрировать действия, которые должны быть выполнены при завершении программы. Идея состоит в том, чтобы установить определенный код, который выполнится автоматически перед завершением работы робота или программы, независимо от того, завершилась ли программа корректно или аварийно. Это механизм позволяет регистрировать действия, которые будут выполнены в конце работы программы, когда все процессы будут завершены. Это полезно для "корректного завершения" программы — закрытия всех активных ордеров или выполнения другой важной логики. Вынос в отдельную функцию позволяет аккуратно и централизованно завершать работу с ордерами, упрощая управление состояниями. В моем случае это просто анонимная функция.
Скальперские стратегии точно не для квик, таблица текущих торгов обновляется 1раз в секунду. Допустим для сетки получаю значения из стакана и ТТТоргов и они отличаются. Но Вы не расстраивайтесь возможен же компромисс, в место 35 оставьте 5 - 7 графиков для детализации, остальные через канал или еще варианты попробуйте. Так же если подключите на кабеле, то тянуть будет лучше, так как скорость и пропускная способность почти одно и тоже.
Свойства графика / диаграмма / ставим метку Оставлять трендовые линии .... Здесь нужно сказать что линии будет оставлять по все тикерам, по этому я пользуюсь в основном индикаторами они адаптируются.
Лена, Дело тут не только в памяти компьютера, дело еще в пропускной способности Вашего канала связи, система работает как сервер / клиент, а у Вас стаканы информация в которых обновляется не пойми как, да и графики тяжело отрисовываются. Свою подобную ситуацию решил, следующим образом настроил одну вкладку с таблицей текущих торгов практически с со всеми тикерами и 4 графика разных тайм фреймах, на один даже тиковая информация выводится + стакан + таблица обезличенных сделок, и так по каждому рынку. переключение идет через создание внутреннего канала (якоря на графиках и таблицах). Так все работает. Вы еще можете проверить скорость связавшись с провайдером. Удачи.
Рычажные весы как символ баланса (смысл который автор закладывает)
Рычажные весы смысла и бессмыслицы отображают баланс между этими двумя крайностями. Статическая уравновешенность этих весов отражает статический баланс между порядком и хаосом. В рамках этого баланса действует закон сохранения, который гласит, что изменение одного элемента компенсируется изменением другого: "Что от одного тела убудет, то присовокупится к другому".
Баланс (S1 / S2 = 1): Смысл и бессмыслица уравновешены, что отражает статическую гармонию системы.
Дисбаланс (S1 / S2 > 1): Преобладание смысла над бессмыслицей, что символизирует порядок и структурированность.
Дисбаланс (S1 / S2 < 1): Преобладание бессмыслицы, указывающее на рост энтропии и хаоса.
Таким образом, рычажные весы смысла и бессмыслицы являются мощной метафорой для описания динамических процессов самоорганизации и деградации в системах, отражающих вечную борьбу между порядком и хаосом, смыслом и бессмыслицей.
"Смесь бульдога с носорогом" или на стыке предметов. На основе идеи двойственности, можно создать класс для управления инвестиционным портфелем, который учитывает баланс между доходами и обязательствами, активами и пассивами, а также принцип двойственности - "Всякой твари по паре". Двойственные отношения через базисные экспоненциальные функции и их связь с множественными концепциями, включая спиральность, фазовые сдвиги и принципы симметрии. Этот подход не только затрагивает математические аспекты, такие как ряды Тейлора для экспоненциальных функций, но и более широкие концепции, относящиеся к философии, физике, и даже управлению. Этот пример класса опирается на концепции представленные М.И. Беляевым, его задача показать как помочь инвестору управлять портфелем через балансировку пар, нахождение баланса между активами, обязательствами, доходами и расходами, это своего рода такие математические весы.
Пояснения к классу: 1. Фактор двойственности. Этот фактор отражает дисбаланс между активами и обязательствами. Когда активов больше, чем обязательств, этот фактор увеличивается, показывая, что инвестор находится в более устойчивой позиции. Если обязательств больше, то фактор будет отражать "долговую нагрузку". 2. Метод `balanceDuality. Он рассчитывает баланс между активами и обязательствами, регулируя фактор двойственности в зависимости от текущего состояния портфеля. 3. Методы для добавления и удаления активов и обязательств. После каждой операции система автоматически пересчитывает баланс. 4. Самонормировка. Вдохновленный идеей о "самонормировке экспоненциальных функций", портфель сам настраивается через фактор двойственности, который балансирует активы и обязательства.
Этот пример, подсвечивает как элегантно, автоматически проходит Самонормировка.
Код
-- Класс для управления портфелем инвестора на основе двойственного подхода
local DualityPortfolioManager = {}
DualityPortfolioManager.__index = DualityPortfolioManager
-- Конструктор
function DualityPortfolioManager:new()
local instance = {
assets = {}, -- Активы
liabilities = {}, -- Обязательства
income = 0, -- Доходы
expenses = 0, -- Расходы
dualityFactor = 1 -- Фактор двойственности (влияет на баланс активов и обязательств)
}
setmetatable(instance, DualityPortfolioManager)
return instance
end
-- Метод для добавления актива
function DualityPortfolioManager:addAsset(name, value)
self.assets[name] = (self.assets[name] or 0) + value
self:balanceDuality()
end
-- Метод для удаления актива
function DualityPortfolioManager:removeAsset(name, value)
if self.assets[name] then
self.assets[name] = self.assets[name] - value
if self.assets[name] <= 0 then
self.assets[name] = nil
end
end
self:balanceDuality()
end
-- Метод для добавления обязательства
function DualityPortfolioManager:addLiability(name, value)
self.liabilities[name] = (self.liabilities[name] or 0) + value
self:balanceDuality()
end
-- Метод для удаления обязательства
function DualityPortfolioManager:removeLiability(name, value)
if self.liabilities[name] then
self.liabilities[name] = self.liabilities[name] - value
if self.liabilities[name] <= 0 then
self.liabilities[name] = nil
end
end
self:balanceDuality()
end
-- Метод для добавления дохода
function DualityPortfolioManager:addIncome(value)
self.income = self.income + value
end
-- Метод для добавления расхода
function DualityPortfolioManager:addExpense(value)
self.expenses = self.expenses + value
end
-- Метод для баланса двойственности (самонормировка)
function DualityPortfolioManager:balanceDuality()
-- Рассчитываем соотношение активов и обязательств
local totalAssets = self:getTotal(self.assets)
local totalLiabilities = self:getTotal(self.liabilities)
-- Применяем фактор двойственности для поддержания баланса
if totalAssets > totalLiabilities then
self.dualityFactor = totalAssets / totalLiabilities
elseif totalLiabilities > totalAssets then
self.dualityFactor = totalLiabilities / totalAssets
else
self.dualityFactor = 1
end
end
-- Метод для получения общей стоимости активов или обязательств
function DualityPortfolioManager:getTotal(t)
local total = 0
for _, value in pairs(t) do
total = total + value
end
return total
end
-- Метод для расчета чистой стоимости портфеля
function DualityPortfolioManager:calculateNetWorth()
local totalAssets = self:getTotal(self.assets)
local totalLiabilities = self:getTotal(self.liabilities)
-- Рассчитываем с учетом фактора двойственности
return (totalAssets - totalLiabilities) * self.dualityFactor
end
-- Метод для получения общей информации о портфеле
function DualityPortfolioManager:getPortfolioSummary()
return {
assets = self.assets,
liabilities = self.liabilities,
income = self.income,
expenses = self.expenses,
netWorth = self:calculateNetWorth(),
dualityFactor = self.dualityFactor
}
end
-- Пример использования класса
local investor = DualityPortfolioManager:new()
investor:addAsset("Stock A", 10000)
investor:addAsset("Real Estate", 25000)
investor:addLiability("Mortgage", 15000)
investor:addIncome(5000)
investor:addExpense(2000)
local summary = investor:getPortfolioSummary()
for k, v in pairs(summary) do
if type(v) == "table" then
print(k .. ":")
for asset, value in pairs(v) do
print(" " .. asset .. ": " .. value)
end
else
print(k .. ": " .. v)
end
end
Дальнейшее развитие концепции М.И. Беляева. (Прикладной характер Структурного Анализа рыночных отношений).
Если рассматривать рынок как сложную систему, применяя структурный анализ с учетом иерархических и мульти двойственных отношений, можно организовать рынок в виде сети взаимодействий различных элементов (агентов) и факторов. Основной монадой рыночных отношений, является взаимосвязь спроса и предложения, выраженная в ценах. Как можно построить структурный анализ рынка, учитывая его сложность. Иерархическая структура рынка. Рынок можно рассматривать как многоуровневую систему, где разные уровни отражают различные аспекты его функционирования. Например: а) Макроуровень — глобальные экономические и политические факторы (центральные банки, международные соглашения, денежно-кредитная политика, торговые блоки). б) Мезоуровень — отрасли и сектора экономики, которые взаимодействуют друг с другом через спрос и предложение, производственные и логистические цепочки. с) Микроуровень — отдельные участники рынка: компании, покупатели, продавцы, биржи, банки и т.д. Каждый уровень этой иерархии взаимодействует с соседними уровнями через механизмы координации и субординации.
Начнем с микроуровеня. Биржевой анализ. На финансовых рынках структурный анализ может помочь понять, как взаимодействие участников рынка (инвесторов, маркет-мейкеров, регуляторов) на различных уровнях иерархии определяет динамику цен акций, облигаций и деривативов. При анализе финансовых рынков на микроуровне важно рассматривать рыночные структуры и отношения между различными участниками, такими как инвесторы, маркет-мейкеры и регуляторы, и как их действия влияют на динамику цен финансовых инструментов (акций, облигаций, деривативов). Микроуровневый биржевой анализ в этом контексте помогает понять краткосрочные ценовые колебания, ликвидность и распределение капитала на рынке.
Микроуровень под микроскоп. Микроструктура. Применим концепцию структурного анализа в комбинации с монадой рыночных отношений, которая включает взаимодействие таких параметров, как цена, количество заявок, открытый интерес, и других данных, поступающих с биржи. На микроуровне структурный анализ позволяет рассматривать рынок как сложную систему, состоящую из взаимосвязанных элементов, каждый из которых оказывает влияние на общий рыночный баланс.
Монада рыночных отношений. Монада в этом контексте — это абстрактное представление взаимосвязанного взаимодействия между всеми рыночными параметрами, которые формируют динамику актива. Монада рыночных отношений включает: Цена актива (в виде баров) — отражает баланс спроса и предложения за определенный период. Количество заявок на покупку и продажу — индикатор текущего рыночного настроения и ликвидности. Открытый интерес — объем открытых позиций, отражающий интерес участников к активу. Объем торгов — подтверждение силы движения цены. Волатильность — оценка уровня неопределенности и риска. Объём ликвидности по ценовым уровням (Level 2) — распределение ликвидности на разных уровнях цены. Эти элементы можно рассматривать как взаимосвязанные части единой системы, где каждый параметр влияет на другой. Их совместное изучение может привести к более глубокой оценке текущей рыночной ситуации.
Если рассматривать монаду рыночных отношений как двойственные отношения между покупателями и продавцами, можно интерпретировать ее как систему, стремящуюся к равновесию, где две противоположные силы находятся в постоянном взаимодействии. В нашем случае это спрос и предложение, отраженные через рыночные данные. Основная задача в этом контексте — двигаться от микроуровневого анализа данных к нахождению баланса, который можно назвать монадой или состоянием рынка. Спин монады в данном случае можно представить как динамическое смещение рыночного баланса, которое отражает, в чью сторону идет движение: к покупателям (росту) или к продавцам (падению).
Движение от микроуровня к монаде (алгоритм). 1. Исходные рыночные данные (нижний уровень). Мы начинаем с анализа микроуровневых данных, таких как:
Заявки на покупку и продажу — количество и объем. Цены сделок — отражение фактического взаимодействия между покупателями и продавцами. Открытый интерес — количество открытых позиций (как фьючерсов, так и опционов). Объемы торгов — подтверждение активности на рынке. Ликвидность — плотность заявок на разных уровнях цены. Каждый из этих показателей может быть рассмотрен с точки зрения "весов" или структурных долей покупателей и продавцов.
2. Оценка относительной силы покупателей и продавцов (средний уровень) На этом этапе происходит расчет весов каждой из сторон. Например, Баланс заявок на покупку и продажу: если на рынке преобладают покупатели, то их доля может быть, скажем, 0.6, а продавцов 0.4. Этот баланс может быть выражен как, Впокупатели=0.6, Впродавцы=0.4
Аналогично мы можем рассчитать и другие пропорции:
Объемы сделок: насколько активны покупатели и продавцы в конкретный момент. Открытый интерес: сколько позиций принадлежит каждой из сторон. Ликвидность на уровнях: где сосредоточена ликвидность и что это говорит о готовности покупателей и продавцов действовать на определенных уровнях цен. Эти весовые коэффициенты могут динамически меняться в зависимости от рыночных условий.
3. Поиск баланса или монады (верхний уровень) После того как мы оценили силу участников (покупателей и продавцов) через микроуровневые данные, мы можем определить, как эти отношения формируют общую структуру рынка и стремятся к состоянию баланса. Этот баланс можно представить как целостную систему — монаду: M = Bпокупатели + Bпродавцы = 1 Здесь B — это доли участия каждой стороны. Например, если Покупатели занимают 60% рынка (0.6), а Продавцы — 40% (0.4), то монада равна 1, что означает, что все силы учтены. Таким образом, монада рыночных отношений — это итоговая оценка, показывающая, как сильно смещены силы в сторону покупателей или продавцов.
4. Движение от обратного (снизу вверх) Когда мы говорим о движении от обратного, мы имеем в виду постепенное построение монады, начиная с самого низкого уровня данных. Мы берем микроуровневые показатели (баланс заявок, объемы, ликвидность и т.д.), оцениваем их относительные силы, а затем объединяем эти оценки для получения целостного представления о состоянии рынка.
Если покупатели активны, их доля возрастает, и рыночные данные показывают это через: Превышение объема заявок на покупку. Увеличение объема сделок по покупкам. Рост открытого интереса в сторону длинных позиций. Если же продавцы доминируют, эти показатели начинают смещаться в их пользу: Превышение объема заявок на продажу. Снижение объемов покупок. Увеличение коротких позиций.
5. Применение структурного анализа для баланса. Построение монады из рыночных данных и оценка баланса покупателей и продавцов через структурный анализ дает нам возможность понимать, где рынок находится относительно равновесия.
Если баланс смещен в сторону покупателей (например, 0.7 покупатели и 0.3 продавцы), это может говорить о бычьем настроении, и трейдеры могут ожидать продолжения роста цен. Если баланс смещен в сторону продавцов (например, 0.4 покупатели и 0.6 продавцы), это может указывать на медвежьи настроения, и цены, скорее всего, пойдут вниз.
Заключение Поиск монады рыночных отношений через микроуровневые данные — это по сути построение целостной картины рынка, начиная с отдельных элементов и заканчивая общей структурой взаимодействия покупателей и продавцов. Это движение "снизу вверх" — от микроуровневого анализа данных к глобальному пониманию баланса на рынке — позволяет принимать более точные торговые решения, основанные на реальном положении дел. Спин монады в данном случае можно представить как динамическое смещение рыночного баланса, которое отражает, в чью сторону идет движение: к покупателям (росту) или к продавцам (падению).
Стояла задача оценить финансово хозяйственную деятельность инвестора, ничего лучше придумать не мог и взял за основу модели, финансово хозяйственную деятельность предприятия. Покопался, нашел подход описанный М. Беляев "Милогия", собрал ккласс на Lua для управления портфелем инвестора, основанного на предложенном подходе. Этот класс будет включать методы для добавления и удаления активов, расчета общей стоимости портфеля, а также получения информации о доходах и обязательствах. Создается новый портфель, добавляются активы и обязательства, рассчитываются доходы и расходы, после чего выводится сводка о состоянии портфеля.
Этот класс можно расширять, добавляя новые методы и функциональность, например, для анализа доходности активов, отслеживания динамики портфеля или интеграции с внешними API для получения рыночных данных. Создается новый портфель, добавляются активы и обязательства, рассчитываются доходы и расходы, после чего выводится сводка о состоянии портфеля. Концепция бухгалтерского учета, перенесена на учет и анализ финансово-хозяйственной деятельности (ФХД) инвестора, применяя аналогию с "весами монады" для управления активами и пассивами. Но главное, это универсальность анализа структуры. Все тут не расскажешь, но как классно все легло по образу и подобию на деятельность Инвестора. Нет предала совершенству.
Добрый день nikolz, ну вот, а то можно подумать что что то случилось? Не знаю флуд это или Ваши заблуждения, Вы по прежнему не читаете сообщения, кроме своих. Чисто из уважения к Вашему возрасту в сотый уже раз повторю, тема давно разобрана по винтикам на данном сайте и не только, вот ссылка, где очень подробно простым языком разыскано о сопрограммах https://fingercomp.gitlab.io/lua-coroutines/#code-1.3. От себя лишь добавлю если еще и этого не достаточно, создайте корутину и возьмите type(). Логика Вашего высказывания тоже не выдерживает критики, Вы привели цитату, ткните пальцем, где есть слово "ОС", или "параллельно". Сопрограмма нужна для создания асинхронности исполнения кода, нет другого варианта в луа уйти от примитивов. Вы писали что пользуетесь таблицей состояний для прерываний. Так воспользуйтесь еще раз, добавьте состояние которое будет отслеживать допустим подключение к серверу, оберните в корутину, и попробуйте, это пример элементарной асинхронности исполнения кода. Я не рассуждаю об ОС, их потоках языках от личных от луа и прочем, меня интересует эффективное использование прикладной программы на луа. На деюсь не чем Вас не обидел.
Другой важный аспект. Учёт свойства вложенности в колебательных процессах и определение локальных максимумов и минимумов на различных временных масштабах — это ключ к более точному анализу временного ряда. Также важно учитывать лаг, который смещает фазы колебаний, чему виной часто, сам мат. аппарат применяемый в расчетах. Уже важно само понимание того, что отображение цены за интервал времени в формате свечи, это тоже отражение колебаний, которые являются следствием закона спроса и предложения, заложенного в ценообразовании на бирже. Колебательный процесс приводит к пониманию возможности применения со всей строгостью фазаво - частотных характеристик в описании этих данных. Точное определение фазы - вот наша цель! И здесь нужно вспомнить задачу Коши, зная функциональный закон и определяя начальные условия, можем формировать прогноз системы любой природы. Тема обширна и наверно выходит за рамки данного форума, но не сказать про нее нельзя. Если кто то заинтересовался могу предложить алгоритм подобного подхода, он отличается от академических, но на мой взгляд так же строг и значительно прост в понимании.
Нечетка логика (библиотека LuaFuzzy), хорошо подходит для оценки различных рыночных условий, особенно удобно получать зоны: перекупленность / перепроданность, накопление / распределение, сопротивление / поддержка, зоны высокой ликвидности. Все это можно получить используя выше предоставленную библиотеку нечеткой логики, сохраняя высокую степень автоматизации.
Вот простой алгоритм ее применения, подход следующий, 1) определяем тренды разных интервалов, 2) рассчитываем стандартное отклонение, приняв его за волатильность, 3) используем правило 3 сигм при составлении нечетких правил и выводов.
Использование стандартного отклонения для оценки волатильности и правило трех сигм поможет более точно определить границы для зон. Установив зависимости Волатильность = sigma = стандартное отклонение цен. Тогда Зоны: Высокая волатильность: если цена выходит за пределы ±3 * sigma (верояное событие) Умеренная волатильность: если цена находится в пределах ± 3 * sigma.
Это дает нам четкие критерии для определения трендов при формирования нечетких правил. Метод еще и универсален так как данные нормированы.
Чуть поясню о чем здесь. В QUIK есть два подхода обработки фьючерсных позиций: 1. OnFuturesClientHolding` через обратный вызов и 2. getFuturesHolding. На первый взгляд очень соблазнительно выглядит 1 вариант, получил автоматически и забыл, про производительность можно и вообще не вспоминать. Но есть одно НО, к существенному недостатку, приводит обработка данных в неожиданный момент, что усложняет синхронизацию с другими процессами. У себя в программах контролирую момент прихода, по времени, это важно так как всегда есть две координаты {time,price}, поэтому предпочитаю пользоваться 2 вариантом. Ситуация которая заставила задуматься описана выше, чтоб избегать ее применил корутину. Корутинный подход снижает нагрузку и предоставляет больше контроля над частотой обновлений, требуется явный вызов функции. Получаем гибкость в управлении запросами и возможностью контролировать нагрузку на систему, запрашиваем там где требуется и когда требуется. Задача - данные по запросу, например, при анализе состояния позиций в определенные моменты. Корутинный подход позволяет не обращать внимания на nil, не блокировать основной поток, программа продолжает выполнение, а данные обновим при следующим запросе. Чем и как пользоваться каждый решает для себя сам, я лишь озвучил подход.
В продолжение темы хочется "пропеть пару дифирамб - корутинам" хотя бы и в собственном исполнении. Реализация сопрограмм в луа, на мой взгляд на сколько простая на столько классная. Мало одной модульности нужен еще хороший процессинг. Что позволяет делать корутина, как и функции делить программу по функциональности, на отдельно исполняемые кусочки. Но в отличии от функции исполнение идет в собственном потоке, низко затратам, без использования задержек и блокировки основного потока, может приостанавливать исполнение и возобновлять впоследствии. Подход, в котором каждая стратегия работает в своей корутине, позволяет молниеносно обрабатывать ее, практически не влияя на производительность. Асинхронность позволяет расставлять приоритеты при принятии решений. Как то странно, на ветке посвященной программированию на луа, совсем, ну почти совсем это не обсуждается. А ведь сколько полезных вещей можно написать упрощая себе и продлевая программе жизнь. Например вот уже из букваря, реализованных у себя. Итераторы, Конечный автомат, Обработка ошибок, Многопоточность, и даже рисование линий (и видимо можно и объектов ведь рисуют анимации). Вот тут все классно "разжевано" https://fingercomp.gitlab.io/lua-coroutines/#code-1.3
Мало словить проблему, нужно найти ее решение. Решение оказалось на столько элегантным настолько и простым. Асинхронный подход - вызов функций qlua в сопрограммах, элегантно решет данную проблему. В чем суть, вот из "букваря". "В асинхронных программах ключевой элемент — event loop. Ивент луп — это цикл в программе, который ждёт событий и вызывает обработчиков." "Плюс корутины в том, что она умеет останавливаться несколько раз. С корутинами, Ивент луп получил событие A и вызвал корутину - обработчик. Корутина же отправила HTTP-запрос и уснула. Теперь ивент луп её разбудит, когда получит событие B, что соединение установлено. И корутина продолжит работу с того момента в коде, как уснула, что очень удобно. А пока она спит, могут работать другие корутины." Кому стало интересно, лучше почитать здесь, все просто "разжевано", нужно только "скушать". https://fingercomp.gitlab.io/lua-coroutines/#code-1.3 Всем, хорошего кода!
glotov_pa@mail.ru, Вы привели пример из MetaTrader "The indicator corresponds to the ADX indicator in MetaTrader". Это не одно и тоже. Вам нужен вариант для квик, можно найти на этом сайте. Вот вариант от разработчиков, должен выводить все линии
Код
------------------------------------------------------------------
----Moving Average SMA, MMA, EMA, WMA, SMMA, VMA
------------------------------------------------------------------
local SMA,MMA,EMA,WMA,SMMA,VMA = "SMA","MMA","EMA","WMA","SMMA","VMA"
local OPEN,HIGH,LOW,CLOSE,VOLUME,MEDIAN,TYPICAL,WEIGHTED,DIFFERENCE,ANY = "O","H","L","C","V","M","T","W","D","A"
--[[Simple Moving Average (SMA)
SMA = sum(Pi) / n]]
local function F_SMA()
local sum = {}
local it = {p=0, l=0}
return function (I, P, VT, ds)
if I == 1 then
sum = {}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={p=I, l=it.l+1} end
local Ip,Ipp,Ippp = Squeeze(it.l,P),Squeeze(it.l-1,P),Squeeze(it.l-P,P)
sum[Ip] = (sum[Ipp] or 0) + GetValueEX(it.p,VT,ds)
if it.l >= P then
return (sum[Ip] - (sum[Ippp] or 0)) / P
end
end
return nil
end
end
--[[Modified Moving Average (MMA)
MMA = (MMAi-1*(n-1) + Pi) / n]]
local function F_MMA()
local sum = {}
local tmp = {pp=nil, p=nil}
local it = {p=0, l=0}
return function(I, P, VT, ds)
if I == 1 then
sum = {}
tmp = {pp=nil, p=nil}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then
it = {p=I, l=it.l+1}
tmp.pp = tmp.p
end
local Ip,Ipp,Ippp = Squeeze(it.l,P),Squeeze(it.l-1,P),Squeeze(it.l-P,P)
if it.l <= P + 1 then
sum[Ip] = (sum[Ipp] or 0) + GetValueEX(it.p,VT,ds)
if (it.l == P) or (it.l == P + 1) then
tmp.p = (sum[Ip] - (sum[Ippp] or 0)) / P
end
else
tmp.p = (tmp.pp*(P-1) + GetValueEX(it.p,VT,ds)) / P
end
if it.l >= P then
return tmp.p
end
end
return nil
end
end
--[[Exponential Moving Average (EMA)
EMAi = (EMAi-1*(n-1)+2*Pi) / (n+1)]]
local function F_EMA()
local tmp = {pp=nil, p=nil}
local it = {p=0, l=0}
return function(I, P, VT, ds)
if I == 1 then
tmp = {pp=nil, p=nil}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then
it = {p=I, l=it.l+1}
tmp.pp = tmp.p
end
if it.l == 1 then
tmp.p = GetValueEX(it.p,VT,ds)
else
tmp.p = (tmp.pp*(P-1) + 2*GetValueEX(it.p,VT,ds)) / (P+1)
end
if it.l >= P then
return tmp.p
end
end
return nil
end
end
--[[William Moving Average (WMA)
( Previous WILLMA * ( Period - 1 ) + Data ) / Period]]
local function F_WMA()
local tmp = {pp=nil, p=nil}
local it = {p=0, l=0}
return function(I, P, VT, ds)
if I == 1 then
tmp = {pp=nil, p=nil}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then
it={p=I, l=it.l+1}
tmp.pp = tmp.p
end
if it.l == 1 then
tmp.p = GetValueEX(it.p,VT,ds)
else
tmp.p = (tmp.pp * (P-1) + GetValueEX(it.p,VT,ds)) / P
end
if it.l >= P then
return tmp.p
end
end
return nil
end
end
--[[Volume Adjusted Moving Average (VMA)
VMA = sum(Pi*Vi) / sum(Vi)]]
local function F_VMA()
local sum = {}
local sum2 = {}
local it = {p=0, l=0}
return function(I, P, VT, ds)
if I == 1 then
sum = {}
sum2 = {}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={p=I, l=it.l+1} end
local Ip,Ipp,Ippp = Squeeze(it.l,P),Squeeze(it.l-1,P),Squeeze(it.l-P,P)
sum[Ip] = (sum[Ipp] or 0) + GetValueEX(it.p,VT,ds) * GetValueEX(it.p,VOLUME,ds)
sum2[Ip] = (sum2[Ipp] or 0) + GetValueEX(it.p,VOLUME,ds)
if it.l >= P then
return (sum[Ip] - (sum[Ippp] or 0)) / (sum2[Ip] - (sum2[Ippp] or 0))
end
end
return nil
end
end
--[[Smoothed Moving Average (SMMA)
SMMAi = (sum(Pi) - SMMAi-1 + Pi) / n]]
local function F_SMMA()
local sum = {}
local sum2 = {}
local it = {p=0, l=0}
return function(I, P, VT, ds)
if I == 1 then
sum = {}
sum2 = {}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={p=I, l=it.l+1} end
local Ip,Ipp,Ippp = Squeeze(it.l,P),Squeeze(it.l-1,P),Squeeze(it.l-P,P)
sum[Ip] = (sum[Ipp] or 0) + GetValueEX(it.p,VT,ds)
if it.l >= P then
if it.l == P then
sum2[Ip] = (sum[Ip] - (sum[Ippp] or 0)) / P
else
sum2[Ip] = ((sum[Ip] - (sum[Ippp] or 0)) - (sum2[Ipp] or 0)+ GetValueEX(it.p,VT,ds)) / P
end
return sum2[Ip]
end
end
return nil
end
end
local function CandleExist(I,ds)
return (type(C)=="function" and C(I)~=nil) or
(type(ds)=="table" and (ds[I]~=nil or (type(ds.Size)=="function" and (I>0) and (I<=ds:Size()))))
end
local function Squeeze(I,P)
return math.fmod(I-1,P+1)
end
local function ConvertValue(T,...)
local function r(V, R)
if R and string.upper(R)== "ON" then R=0 end
if V and tonumber(R) then
if V >= 0 then return math.floor(V * 10^R + 0.5) / 10^R
else return math.ceil(V * 10^R - 0.5) / 10^R end
else return V end
end
local arg = {...}
arg.n = select('#', ...)
if arg.n > 0 then
for i = 1, arg.n do
arg[i]=arg[i] and r(arg[i] * ((T and T.Multiply) or 1), (T and T.Round) or "off")
end
return unpack(arg)
else return nil end
end
local function GetValueEX(I,VT,ds)
VT=(VT and string.upper(string.sub(VT,1,1))) or ANY
if VT == OPEN then --Open
return (O and O(I)) or (ds and ds:O(I))
elseif VT == HIGH then --High
return (H and H(I)) or (ds and ds:H(I))
elseif VT == LOW then --Low
return (L and L(I)) or (ds and ds:L(I))
elseif VT == CLOSE then --Close
return (C and C(I)) or (ds and ds:C(I))
elseif VT == VOLUME then --Volume
return (V and V(I)) or (ds and ds:V(I))
elseif VT == MEDIAN then --Median
return ((GetValueEX(I,HIGH,ds) + GetValueEX(I,LOW,ds)) / 2)
elseif VT == TYPICAL then --Typical
return ((GetValueEX(I,MEDIAN,ds) * 2 + GetValueEX(I,CLOSE,ds))/3)
elseif VT == WEIGHTED then --Weighted
return ((GetValueEX(I,TYPICAL,ds) * 3 + GetValueEX(I,OPEN,ds))/4)
elseif VT == DIFFERENCE then --Difference
return (GetValueEX(I,HIGH,ds) - GetValueEX(I,LOW,ds))
else --Any
return (ds and ds[I])
end
return nil
end
local function MA() --Moving Average ("MA")
local T_MA = {[SMA]=F_SMA(),[MMA]=F_MMA(),[EMA]=F_EMA(),[VMA]=F_VMA(),[SMMA]=F_SMMA(),[WMA]=F_WMA()}
return function (I, Fsettings, ds)
local Fsettings=(Fsettings or {})
local P = (Fsettings.Period or 14)
if (P > 0) then
return T_MA[string.upper(Fsettings.Metod or EMA)](I, P, (Fsettings.VType or CLOSE), ds)
end
return nil
end
end
local function TR() --True Range ("TR")
local it = {pp=0, p=0, l=0}
return function (I, ds)
if I == 1 then
it = {pp=0, p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={pp=it.p, p=I, l=it.l+1} end
if it.l == 1 then
return math.abs(GetValueEX(it.p,DIFFERENCE, ds))
else
return math.max(math.abs(GetValueEX(it.p,DIFFERENCE, ds)),
math.abs(GetValueEX(it.p,HIGH,ds) - GetValueEX(it.pp,CLOSE,ds)),
math.abs(GetValueEX(it.pp,CLOSE,ds)-GetValueEX(it.p,LOW,ds)))
end
end
return nil
end
end
function ADX() --Average Directional Movement Index ("ADX")
local pDI_MA=MA()
local mDI_MA=MA()
local ADX_MA=MA()
local f_TR=TR()
local it = {pp=0, p=0, l=0}
return function (I, Fsettings, ds)
local Fsettings=(Fsettings or {})
local P = (Fsettings.Period or 14)
local M = (Fsettings.Metod or EMA)
if (P>0) then
if I == 1 then
if (M==VMA) then M=SMA end
it = {pp=0, p=0, l=0}
end
local i_TR = f_TR(I,ds)
if CandleExist(I,ds) then
if I~=it.p then it={pp=it.p, p=I, l=it.l+1} end
if it.l > 1 then
if GetValueEX(it.p,HIGH,ds) > GetValueEX(it.pp,HIGH,ds) then
pDM = math.abs(GetValueEX(it.p,HIGH,ds)-GetValueEX(it.pp,HIGH,ds))
else pDM = 0 end
if GetValueEX(it.p,LOW,ds) < GetValueEX(it.pp,LOW,ds) then
mDM = math.abs(GetValueEX(it.pp,LOW,ds)-GetValueEX(it.p,LOW,ds))
else mDM = 0 end
if pDM > mDM then mDM=0 end
if mDM > pDM then pDM=0 end
if pDM == mDM then pDM=0 mDM = 0 end
if i_TR~=0 then pSDI = pDM / i_TR * 100 else pSDI = 0 end
if i_TR~=0 then mSDI = mDM / i_TR * 100 else mSDI = 0 end
local pDI = pDI_MA(it.l-1, {Period=P, Metod=M, VType=ANY}, {[it.l-1] = pSDI})
local mDI = mDI_MA(it.l-1, {Period=P, Metod=M, VType=ANY}, {[it.l-1] = mSDI})
if it.l>P and pDI and mDI then
return ADX_MA(it.l-P, {Period=P, Metod=M, VType=ANY}, {[it.l-P]=math.abs(pDI-mDI)/(pDI+mDI)*100}),pDI,mDI
else
return nil,pDI,mDI
end
end
end
end
return nil,nil,nil
end
end
Settings = {
Name = "*ADX (Average Directional Movement Index)",
Period = 14,
Metod = "EMA", --(SMA, MMA, EMA, WMA, SMMA, VMA)
line = {{
Name = "Horizontal line",
Type = TYPE_LINE,
Color = RGB(140, 140, 140)
},
{
Name = "ADX",
Type = TYPE_LINE,
Color = RGB(0, 162, 232)
},
{
Name = "ADX +DI",
Type = TYPE_LINE,
Color = RGB(0, 206, 0)
},
{
Name = "ADX -DI",
Type = TYPE_LINE,
Color = RGB(221, 44, 44)
}
},
Round = "off",
Multiply = 1,
Horizontal_line="off"
}
local func
function Init()
func = ADX()
return #Settings.line
end
function OnCalculate(Index)
return tonumber(Settings.Horizontal_line),ConvertValue(Settings,func(Index, Settings))
end
nikolz написал: Относительно задержки процессора. Я использую Event, об этом писал и вы знаете. В результате реакция на колбек запаздывает не более, чем на 0.00001 сек, а загрузка процессора 3-7%.
Дело тут не в задержке а в инициализации. Я использую задержки но в данном случае зачем? Сколько не задерживай получишь нил, пока не инициализируешь. И запроса здесь одного мало. И хотелось бы что то услышать от разработчиков это задумка такая или оплошность. И как поступать в алгоритме при смене тикера?
VPM написал: local holdings = nil --getFuturesHolding(self.firmid, self.account, symbol, self.type)
Хоть к теме и не относится, но что-то часто такие ошибки у Вас в коде...
А так -- ну да, getFuturesHolding даёт nil, если не было ордеров по тикеру с прошлого клиринга и одновременно если нет позиции. Наверняка это деталь реализации для какого-нибудь ускорения...
Это заглушка для проверки кода в среде луа (без квик), в квик просто нужно раскомментировать строку.
Так и я про это, как без этих выкрутасов можно понять как работает getFuturesHolding? И где здесь вообще логика? Сделал запрос вернулась таблица, нет запросов зачистили память. Обломали полумесячный тест.
nikolz, Трудно представить что тут можно по другому local holdings = getFuturesHolding(self.firmid, self.account, symbol, self.type)
Для тех кто будет пробовать нужно добавит задержку в код, чтоб не подвешивать процессор. sleep(1000)
Код
function main()
--local type = 0
local firmid = 'SPBFUT'
local account = 'SPBFUT00hkv'
local symbol_names = {'NGU4' , 'BRQ4' , 'GDU4', 'NGU4' , 'NGX4' ,}
working = true
message("Robot started")
local mm = MoneyManagement:new( firmid, account )-- , type
while working do
for _, symbol in ipairs(symbol_names) do
mm:FuturesHolding(symbol)
end
sleep(1000)
end
end
В моем случае ошибка - nil получена по 3 тикерам.
Ошибка функции getFuturesHolding, возвращает = nil; self.firmid = SPBFUT; self.account = SPBFUT00hkv; symbol = BRQ4; self.type = 0 number Ошибка функции getFuturesHolding, возвращает = nil; self.firmid = SPBFUT; self.account = SPBFUT00hkv; symbol = GDU4; self.type = 0 number Ошибка функции getFuturesHolding, возвращает = nil; self.firmid = SPBFUT; self.account = SPBFUT00hkv; symbol = NGX4; self.type = 0 number
Вот и возникает вопрос а что делаю не так как описано в инструкции?
local MoneyManagement = {}
MoneyManagement.__index = MoneyManagement
function MoneyManagement:new(firmid, account )
local self = setmetatable({}, MoneyManagement)
self.firmid = firmid
self.account = account
self.type = 0
self.holdings = {}
return self
end
-- Function to get Futures Holding
function MoneyManagement:FuturesHolding(symbol)
local holdings = nil --getFuturesHolding(self.firmid, self.account, symbol, self.type)
if holdings then
self.holdings = holdings
self.startbuy = holdings.startbuy or 0
self.startsell = holdings.startsell or 0
self.startnet = holdings.startnet or 0
self.todaybuy = holdings.todaybuy or 0
self.todaysell = holdings.todaysell or 0
self.totalnet = holdings.totalnet or 0
self.openbuys = holdings.openbuys or 0
self.opensells = holdings.opensells or 0
self.lused = holdings.cbplused or 0
self.lplanned = holdings.cbplplanned or 0
self.varmargin = holdings.varmargin or 0
self.avrposnprice = holdings.avrposnprice or 0
self.positionvalue = holdings.positionvalue or 0
self.real_varmargin = holdings.real_varmargin or 0
self.total_varmargin = holdings.total_varmargin or 0
local session_status = {
[0] = 'не определено',
[1] = 'основная сессия',
[2] = 'начался промклиринг',
[3] = 'завершился промклиринг',
[4] = 'начался основной клиринг',
[5] = 'основной клиринг: новая сессия назначена',
[6] = 'завершился основной клиринг',
[7] = 'завершилась вечерняя сессия'
}
self.session_status = session_status[ math.floor(holdings.session_status)]
if self.session_status ~= 'основная сессия' then
message('Актуальный статус торговой сессии getFuturesHolding: ' .. holdings.session_status .. ' ' .. self.session_status)
end
else
message("Ошибка функции getFuturesHolding, возвращает = " .. tostring(holdings) ..
"; self.firmid = " .. tostring(self.firmid) ..
"; self.account = " .. tostring(self.account) ..
"; symbol = " .. tostring(symbol) ..
"; self.type = " .. tostring(self.type)..' '.. type(self.type))
end
end
local working = nil
function OnStop(stop_flag)
message(' OnStop: '..'stop_flag = '..tostring(stop_flag) )
working = false
end
--[[ MAIN LOOP ]]--
function main()
--local type = 0
local firmid = ''
local account = ''
local symbol_names = {'NGU4' , 'BRQ4' , 'GDU4', 'NGU4' , 'NGX4' ,}
working = true
message("Robot started")
local mm = MoneyManagement:new( firmid, account )-- , type
while working do
for _, symbol in ipairs(symbol_names) do
mm:FuturesHolding(symbol)
end
end
end
nikolz, Не понял что мешает организовать свой поток маин? Вот накидал на скорую руку, попробуйте так поставив свои данные.
Код
local MoneyManagement = {}
MoneyManagement.__index = MoneyManagement
function MoneyManagement:new()
local self = setmetatable({}, WindowCoordinates)
-- поставить свои данные
self.firmid = ''
self.account = ''
self.type = 0
return self
end
-- Function to get Futures Holding
function MoneyManagement:FuturesHolding(symbol)
local holdings = getFuturesHolding(self.firmid, self.account, symbol, self.type)
if holdings then
self.holdings = holdings
self.startbuy = holdings.startbuy or 0
self.startsell = holdings.startsell or 0
self.startnet = holdings.startnet or 0
self.todaybuy = holdings.todaybuy or 0
self.todaysell = holdings.todaysell or 0
self.totalnet = holdings.totalnet or 0
self.openbuys = holdings.openbuys or 0
self.opensells = holdings.opensells or 0
self.lused = holdings.cbplused or 0
self.lplanned = holdings.cbplplanned or 0
self.varmargin = holdings.varmargin or 0
self.avrposnprice = holdings.avrposnprice or 0
self.positionvalue = holdings.positionvalue or 0
self.real_varmargin = holdings.real_varmargin or 0
self.total_varmargin = holdings.total_varmargin or 0
--self.session_status = math.floor(holdings.session_status) or 0
local session_status = {
[0] = 'не определено',
[1] = 'основная сессия',
[2] = 'начался промклиринг',
[3] = 'завершился промклиринг',
[4] = 'начался основной клиринг',
[5] = 'основной клиринг: новая сессия назначена',
[6] = 'завершился основной клиринг',
[7] = 'завершилась вечерняя сессия'
}
self.session_status = session_status[ math.floor(holdings.session_status)]
if getFuturesHolding ~= 'основная сессия' then
Log:trace('Актуальный статус торговой сессии getFuturesHolding: ' .. holdings.session_status .. ' ' .. self.session_status)
end
else
Log:info("Ошибка функции getFuturesHolding, возвращает = " .. tostring(holdings) ..
"; self.firmid = " .. tostring(self.firmid) ..
"; self.account = " .. tostring(self.account) ..
"; symbol = " .. tostring(symbol) ..
"; self.type = " .. tostring(self.type)..' '.. type(self.type))
end
end
local working = nil
function OnStop(stop_flag)
message(' OnStop: '..'stop_flag = '..tostring(stop_flag) )
working = false
end
--[[ MAIN LOOP ]]--
function main()
local symbol_names = {
'NGU4'
, 'BRQ4'
, 'GDU4'
,
}
working = true
message("Robot started")
local mm = MoneyManagement:new()
while working do
for _, symbol in ipairs(symbol_names) do
mm:FuturesHolding(symbol)
end
end
end
nikolz, Функция вызова обрабатывается в потоке маин, работает правильно и со стороны пользователя защищена проверкой на nil,
Цитата
В случае ошибки функция возвращает «nil».
На этот случай дополнительно выводится сообщение и проверяемся корректность ввода пользователем входных параметров. В случае описанном нет ошибки на стороне пользователя (все введено корректно изменился тикер)! Ошибка возникает на стороне функции (Разработчиков), так как возвращает nil то есть - ошибку (читаем документацию)! Вот я и пытаюсь получить комментарий Разработчиков, понять где не точность в функции или документации? Описание говорит что должна вернуться таблица.
nikolz, Выше я показал функцию вызова и ответ на нее. Все прекрасно работало на старом тикере. Сбой происходит в момент ввода нового тикера. Ответ на вызов local holdings = nil? при этом все входящие данные верны, более того изменился только код тикера.
Цитата
Ошибка функции getFuturesHolding, возвращает = nil; self.firmid = SPBFUT; self.account = SPBFUT00hkv; symbol = NGU4; self.type = 0 number
Функция возвращает таблицу Lua, не может быть здесь отсутствие самой таблицы, могут отсутствовать поля этой таблицы, но не сама таблица?
Вот из справки
Цитата
Функция предназначена для получения информации по фьючерсным позициям.
Формат вызова:
TABLE getFuturesHolding(STRING firmid, STRING trdaccid, STRING sec_code, NUMBER type)
Функция возвращает таблицу Lua с параметрами Таблицы «Позиции по клиентским счетам».
В случае ошибки функция возвращает «nil».
Я и допустил что при обновлении тикера не проходит какая - то инициализация, так как таблица отражает владение, то и активировал просто ордер по этому тикеру, сразу функция заработала нормально. Об этом мое сообщение.
funduk, Да заметил спасибо, но это дописано после, хотел уменьшить частоту вывода. Исправил
Код
if self.session_status ~= 'основная сессия' then
Log:trace('Актуальный статус торговой сессии getFuturesHolding: ' .. holdings.session_status .. ' ' .. self.session_status)
end
Ошибка функции getFuturesHolding, возвращает = nil; self.firmid = SPBFUT; self.account = SPBFUT00hkv; symbol = NGU4; self.type = 0 number
Все параметры введены правильно, не должна выводить local holdings = nil? Выводит nil а правильно сказать ни чего не выводит по данному инструменту! Не понимает пока не обновил таблицу "состояние счета", то есть прогнал пустой ордер чтоб отобразился тикер. Ну почему, Все за что не возьмешься, через одно место? Программе столько лет, все уже должно идеально работать. Вот сама функция, что не так?
Код
-- Function to get Futures Holding
function MoneyManagement:FuturesHolding(symbol)
local holdings = getFuturesHolding(self.firmid, self.account, symbol, self.type)
if holdings then
self.holdings = holdings
self.startbuy = holdings.startbuy or 0
self.startsell = holdings.startsell or 0
self.startnet = holdings.startnet or 0
self.todaybuy = holdings.todaybuy or 0
self.todaysell = holdings.todaysell or 0
self.totalnet = holdings.totalnet or 0
self.openbuys = holdings.openbuys or 0
self.opensells = holdings.opensells or 0
self.lused = holdings.cbplused or 0
self.lplanned = holdings.cbplplanned or 0
self.varmargin = holdings.varmargin or 0
self.avrposnprice = holdings.avrposnprice or 0
self.positionvalue = holdings.positionvalue or 0
self.real_varmargin = holdings.real_varmargin or 0
self.total_varmargin = holdings.total_varmargin or 0
--self.session_status = math.floor(holdings.session_status) or 0
local session_status = {
[0] = 'не определено',
[1] = 'основная сессия',
[2] = 'начался промклиринг',
[3] = 'завершился промклиринг',
[4] = 'начался основной клиринг',
[5] = 'основной клиринг: новая сессия назначена',
[6] = 'завершился основной клиринг',
[7] = 'завершилась вечерняя сессия'
}
self.session_status = session_status[ math.floor(holdings.session_status)]
if getFuturesHolding ~= 'основная сессия' then
Log:trace('Актуальный статус торговой сессии getFuturesHolding: ' .. holdings.session_status .. ' ' .. self.session_status)
end
else
Log:info("Ошибка функции getFuturesHolding, возвращает = " .. tostring(holdings) ..
"; self.firmid = " .. tostring(self.firmid) ..
"; self.account = " .. tostring(self.account) ..
"; symbol = " .. tostring(symbol) ..
"; self.type = " .. tostring(self.type)..' '.. type(self.type))
end
end
Ирина Никонова, Данный скрипт (утилита) сохраняет сделки за торговую сессию, ровно столько сколько хранит их Квик, ТОРГОВЫЙ день "деюро" иное, можно для понимания ознакомиться на сайте биржи. Если квик отключился то и сделок нет. Когда подключаетесь то сохраненные за сессию сделки подтянутся и сохранятся. Все что нужно подключаться в режиме торговой сессии. И тут BlaZed, прав Ваша проблема скрывает два момента технический и программирования. Для решения каждой нужен отдельный специалист.