Система принятия решений и/или Нечеткая логика(FuzzyLogic)

Страницы: Пред. 1 ... 13 14 15 16 17 След.
RSS
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
А если такой вариант рассмотреть, создать специальный модуль или глобальную таблицу, которая будет управлять всеми зависимостями. Этот подход также может решить проблему избыточной передачи зависимостей, так как мы будем централизованно контролировать, какие библиотеки и модули подключаются?
 
Цитата
VPM написал:
а) dofile:
* каждый вызов → заново читает файл
* заново выполняет код
* выполняется в глобальном окружении?
* нет кэширования?
* нет изоляции
"Пять копеек" от Роберту Иерузалимски:
Цитата
Для простых задач dofile удобна, поскольку выполняет всю работу
за один вызов. Однако loadfile более гибкая. В случае ошибки loadfile
возвращает nil и сообщение об ошибке, что позволяет нам обработать
ошибку  более  подходящими  для  нас  способами.  Более  того,  если  нам
нужно  выполнить  файл  несколько  раз,  то  мы  можем  один  раз  вызвать
loadfile  и  несколько  раз  вызвать  возвращенную  им  функцию.  Этот
подход гораздо дешевле, чем несколько раз вызывать dofile, поскольку
файл компилируется лишь один раз.
Всё пройдет. Но это не точно.
 
Ziveleos,  Роберту Иерузалимски сравнивает dofile / loadfile. Мы же обсуждаем архитектуру production-системы. Это разные уровни задачи.

* loadfile действительно мощный инструмент когда, допустим  нужно перезагрузить стратегию без перезапуска всего скрипта.
* require — все таки правильно для модульной архитектуры.

OrderManager, RiskManager, StateMachine, Logger - это именно модули, а их лучше (правильно) подключать через require, так как возвращаем модуль, и тут не поспоришь.

Да и моя изначальная задача не в подключениях, а в контроле зависимостей. Но в любом случае, Вы подсветили полную картину подключений.
 
Цитата
VPM написал:
Да, спасибо за вариант, про собственный флаг, даже не задумывался, взял на вооружение!
   Этот вариант наглядный, но не самый эффективный и гибкий. Сам я использую для управления, всего двумя нужными мне коллбеками: OnCleanUp и OnTransReply их переопределение, то есть присвоение переменным коллбеков тех функций, которые мне нужны для обработки соответствующих событий.
   Например, если я не хочу (по как то причине) обрабатывать события OnTransReply, то выполняется  OnTransRe ply = function()  end
   Для продолжения обработки значение переменной OnTransReply  заменяется на нужную мне функцию обработки (запись в очередь параметров событий).
 
TGB,  Мне вообще не понятно, зачем ими управлять, ведь это событие? Ну если первый способ, еще логичен включение как точки входа. То остальное зачем?
 
Цитата
VPM написал:
То остальное зачем?
   Согласен. В вашем конкретном случае достаточно первого варианта.
 
Цитата
VPM написал:
* loadfile действительно мощный инструмент когда, допустим  нужно перезагрузить стратегию без перезапуска всего скрипта.
* require — все таки правильно для модульной архитектуры.
Перезагрузить можно и модуль загруженный require, достаточно стереть запись о нем из package.loaded.
Цитата
VPM написал:
Но в любом случае, Вы подсветили полную картину подключений.
Да ничего я не подсвечивал.
Просто Вы сравнили dofile и require, а я напомнил про loadfile, тем более, что обе эти функции используют loadfile для загрузки и компиляции файла.
Всё пройдет. Но это не точно.
 
Да нет Ziveleos, . Вы подметили суть. Сравнивать dofile и require можно долго, но без loadfile картина была неполной. И это важное уточнение. Вы не просто "напомнили", а добавили критически важный слой в понимание темы.  

dofile и require — это высокоуровневые обёртки над мощным механизмом loadfile. По аналогии из механики: loadfile — это рычаг, а dofile и require — просто разные насадки на этот рычаг для разных задач.

Внесу пояснения, чтобы зафиксировать этот "полный цикл":

1.  Фундамент — loadfile - это базовая функция. Она делает самое главное (и самое тяжелое) — читает файл с диска, компилирует его в байт-код и возвращает результат в виде *функции*. Но она её не выполняет.

2.  Утилитарный уровень — dofile - это просто удобная обёртка. Она сделала всю работу за нас, но лишила нас контроля. Это "выстрелил и забыл".

3.  Архитектурный уровень — require - Это уже не просто утилита, а менеджер зависимостей. Он использует loadfile для загрузки, и добавляет поверх этого кэширование.

А вот механика про перезагрузку модуля, загруженного через require, — это вообще высший пилотаж. Встречается для горячей перезагрузки кода в сложных приложениях (играх). Этот подход позволяет обновлять логику стратегии, не теряя состояние всей программы, что критически важно для долгоиграющих процессов.

Если есть свой пример для торговых приложений приведите?
 
"Умный в гору не пойдет, Умный гору обойдет"

Особенности QUIK или ровно то место, где ломается вся торговая система, основанная на событийной модели!

Ключевая проблема QUIK (которую поймал).  Факты:
* brokerref:
 > может отсутствовать в OnTrade;
 > может быть обрезан;
 > может не совпасть;
* trans_id  > вообще может не приходит в OnTrade;
* order_num  >  единственный стабильный идентификатор заявки.

Попытка использовать getOrderByNumber, вообще оказалась критической дырой (опасный костыль)

local order_info = getOrderByNumber(trade.class_code, trade.trade_num)  Это НЕ гарантируется QUIK! Почему? trade_num / order_num
1) нет гарантии наличия связи;
2) может вернуть nil или не тот ордер.

Правильное решение нашлось. НЕ искать order_num через trade_num. Вместо этого, использовать OnOrder как источник истины.


Производственный конвейер (Production pipeline)
1. В OnTransReply trans_id > сохраняем (pending);
2. В OnOrder order_num < связываем с trans_id;
А вот где рождается настоящая связь!
3. В OnTrade trade.order_num > находим ордер.

Следовательно, правильная модель связывания следующая Strategy > OrderExecutor > QUIK:
QUIK:  trans_id > order_num > trade_num
OnTrade:  trade > order_num > НАШ ORDER

Связка через order_num — это не просто улучшение, это - > единственный надёжный способ устранить position drift в QUIK, зафиксирую это как архитектурный стандарт!
 
Не открою большого секрета, что QUIK создавался и написан под брокерские команды. Отличительной чертой которых, является наличие узко специализированных специалистов, в то время как частному инвестору приходиться со всем справляться самому. В этом есть и преимущество, так и недостатки. Основной - это сложность построения инженерных торговых стратегий. Язык выбранный для автоматизации, на мой не профессиональный взгляд выбран оптимально, подходит тем и другим. Но вот реализация? Но этому собственно и посвящен настоящий сайт (а что тут еще есть?). Документация выложенная для пользователя, ну мягко говоря написана не на "русском языке".

Мое долгое вступление веду к тому, что для "частника" решившего заняться автоматизацией торгового процесса, или даже построением своей торговой системы, очень важна архитектура программы! Если задачей является, не просто "Кнопка срубить бабло", а инженерная стратегия арбитража или хедж - фонда, то процедурный стиль плохо подходит. Остается модульный стиль с четким разделением ответственности, и едиными стандартами данных на выходах. Это просто позволяет не переписывать код, а использовать одни и те - же уровни. Следовательно, можем создавать модульную математическую библиотеку внутри Lua - мозг системы.

QUIK накладывает свои особенности, а у кого их нет, такова действительность. В своем подходе остановился на HFT исполнении и событийной модели, (опросы в качестве уточнений и восстановления). Конечно это не чистый HFT (ни для соревнований с фондами типа "Atto Trading Technologies LLC", которые уже говорят о наносекундах), у нас "QUIK latency" - высокий и нет прямого доступа к бирже. Но ряд подходов широко не обсуждаемых, можно использовать. Главным образом появляется возможность универсального движка, к которому можно цепляться как инвестиционными стратегиями, так HFT конечно в рамках QUIK. Этот подход требует и особой надежности системы (коду нужно крутиться продолжительное время). Отладка, вот где проявляется то как устроен QUIK, что он возвращает, а документацией "Зачитаешься!". Вот и у меня, который раз виден финиш, и на новый круг в забеге.  :smile: Всем хорошего код!
 
Цитата
VPM написал:
Правильное решение нашлось. НЕ искать order_num через trade_num. Вместо этого, использовать OnOrder как источник истины.Производственный конвейер (Production pipeline) 1. В OnTransReply trans_id > сохраняем (pending); 2. В OnOrder order_num   находим ордер. Следовательно, правильная модель связывания следующая Strategy > OrderExecutor > QUIK: QUIK:  trans_id > order_num > trade_num OnTrade:  trade > order_num > НАШ ORDER Связка через order_num — это не просто улучшение, это - > единственный надёжный способ устранить position drift в QUIK, зафиксирую это как архитектурный стандарт!
  Ну и зачем так сложно? Вы хотите завести пользователей в болото :smile: ?
  Когда это можно реализовать проверкой (с использованием trans_id) в таблицах order, stop_order. И эту проверку надо выполнять  только, когда изменяется количество записей в них. Единственным коллбеком, который имеет смысл, при этом, обрабатывать, фильтруя по  созданным trans_id, это OnTransReply, так как в нем может быть информация о причине отказа выставления заявки.
 
TGB,  Согласен, Вы правы, можно проще, мой предыдущий подход излишне сложен. Но это стремление к максимальной отказоустойчивости и скорости.

Если использовать OnTransReply как единственный источник информации об отправке ордера, а для получения order_num и статуса исполнения периодически опрашивать таблицу orders (или stop_orders), фильтруя по trans_id. OnOrder и OnTrade в такой схеме не нужны, если задача позволяет мириться с небольшой задержкой (до 1 секунды) между исполнением и реакцией системы.
Это менее подвержено ошибкам, связанным с "out-of-order" событиями и наверняка проще. Для HFT это не подходит, но для обычной алготорговли – вполне!

Тогда Упрощённый алгоритм (без OnOrder/OnTrade)  будет выглядеть:
OnTransReply – только логируем успех/ошибку отправки. При успехе запоминаем trans_id как pending.
Периодический опрос таблицы orders (например, раз в 0.5-1 секунду) – выбираем заявки с trans_id из нашего списка pending. Получаем order_num, статус, исполненный объём.
Обновление позиций – при обнаружении изменений в balance (исполненная часть) вызываем PositionManager:update_position(...).
OnTrade не нужен – вся информация о сделках есть в таблице orders (поля balance, qty).
 
Цитата
VPM написал:
Периодический опрос таблицы orders (например, раз в 0.5-1 секунду) – выбираем заявки с trans_id из нашего списка pending. Получаем order_num, статус, исполненный объём.Обновление позиций – при обнаружении изменений в balance (исполненная часть) вызываем PositionManager:update_position(...).OnTrade не нужен – вся информация о сделках есть в таблице orders (поля balance, qty).
Опрос делается не по времени, а если изменился размер таблицы,
т е запоминаете размер, в main на каждом цикле сравниваете с текущим,
если изменился то опрашиваете.
 
Цитата
VPM написал:
вся информация о сделках есть в таблице orders (поля balance, qty)
Это не вся информация. Сделки по конкретному ордеру необходимы чтобы:

- узнать время сделок.
- узнать цену и объем сделок

Торговля одним лотом - это замечательно, но совсем не показательно. Также есть ордера "по рынку" и цену исполнения такого ордера Вы не узнаете из самого ордера.

После того как прочитали, что состояние ордера изменилось - просто вызываете поиск сделок по номеру ордера.
 
До кучи ..
Например ,создаю таблицу активных заявок по каждому инструменту и обновляю ее по колбеку OnOrder и не использую таблицу заявок терминала ВООБЩЕ. Что существенно быстрее работает. Особенно если у вас HFT который может выставлять и снимать сотни заявок
 
Тогда нужен правильный опробованный алгоритм, и архитектура этой polling-модели?

Но ведь QUIK скорее event-driven система, причем асинхронная, недетерминированная система? Следовательно, единственный способ сделать её детерминированной — это FSM + event pipeline.

Как у меня сейчас, главный тезис:
 * Таблицы QUIK — это snapshot состояния;
 * Callback            — это история событий.
 
Отсюда и единственно возможная архитектура и родилась к которой пришёл (и почему она сложная):
 * OnTransReply > intent
 * OnOrder          > связь trans_id > order_num
 * OnTrade          > факт исполнения
! это минимально достаточная модель, не "overengineering", включающая только те функции, которые необходимы для решения конкретной задачи.

Почему идея упростить, не отвечает данной постановки задачи? Потому что решаем задачу, "восстановления детерминированной истории исполнения, из не детерминированного потока событий QUIK"?

А проверка таблиц (polling) полезна и необходимо использовать в задачах:

1. Recovery / reconciliation. if exchange_position ~= internal_position then trigger_recovery() end
2. Watchdog. if order висит слишком долго > проверить таблицу
3. Fail-safe. если callback потерян > подтянуть через snapshot

Что не так делаю?
 
Цитата
nikolz написал:
не использую таблицу заявок терминала ВООБЩЕ.
 То есть, у вас, до сих пор, в роботе не реализовано нормальное восстановление продолжения его работы при его перезапуске по любой причине?
 Например, ваш робот подал заявку и тут же упал (почему бы нет?). Вы его смогли починить через 1 час и запустили. И он, не используя таблицу заявок терминала ВООБЩЕ,
ждет появления OnOrder?
----------
   
Цитата
VPM написал:
Для HFT это не подходит,
   
Цитата
nikolz написал:
Особенно если у вас HFT который может выставлять и снимать сотни заявок
   Только сумасшедший решиться использовать QUIK для HFT.
 
Цитата
VPM написал:
* Callback            — это история событий.
   Вы историк или торговец :smile: ?
   Для принятия решений в роботе вам недостаточно текущего состояния вашего счета (которое отображается в его таблицах), а также текущих котировок и их истории (в ds)?
 
Цитата
VPM написал:
Но ведь QUIK скорее event-driven система
Это не так. Это просто попытка сделать как было модно, лет 15 назад. Использовать колбеки или нет - это ваше дело. Сейчас разработчики утверждают, что колбеки гарантированы, и можете их использовать, не думая, что пропустите. Но это не избавляет от необходимости опрашивать таблицы, при старте, очистке таблиц брокером, актуализации состояния и др. Так что в любом случае будет и контур колбеков (если они используются), и контур опроса таблиц. Причем без первого можно работать, а вот без второго - уже никак.
 
TGB,  В данный момент обсуждаю задачу как инженер разработчик, и не понимаю Ваш сарказм? Причем здесь "сумасшествие" если вопрос стоит об универсальном, оптимальном движке? Собственно как Вы любите. Возможно я не очень четко описал проблематику, или Вы у nikolz,  научились читать только то что Вам нужно. Еще раз. Ряд торговых задач решается только с HFT подходом даже в рамках QUIK (свечи из DS не нужны) именно текущее состояние восстанавливаем. Другие свечные стратегии тоже могут использовать движок.
Цитата
TGB написал:
Для принятия решений в роботе вам недостаточно текущего состояния вашего счета (которое отображается в его таблицах)
Ну конечно не достаточно, а виной тому асинхронный приход событий и заполнение таблиц.
Ну например: Критическая проблема. Out-of-order ты вообще не контролируешь?
QUIK:
OnTrade → раньше OnOrder
модель: таблица → уже обновлена

!ты даже не узнаешь, что был out-of-order. Такое может быть?

Или вот еще:  Дедупликация невозможна.
* В event-driven модели: processed_trades[trade_num]
* В polling-модели:  просто читаешь итоговое состояние
не возможно:
1) отфильтровать дубли;
2) гарантировать,  обеспечить (idempotency) , чтобы многократное выполнение одной операции приводило к тому же результату, что и первое, не меняя состояние системы повторно.
Может быть?
 
Nikolay,  Но ведь в любом случае ОПРОС будет более затратной операцией, чем реакция на событие. А значит нужна как минимум оптимизация процесса? nikolz,
Цитата
nikolz написал:
Опрос делается не по времени, а если изменился размер таблицы, т е запоминаете размер, в main на каждом цикле сравниваете с текущим, если изменился то опрашиваете.
Цитата
nikolz написал:
Например ,создаю таблицу активных заявок по каждому инструменту и обновляю ее по колбеку OnOrder и не использую таблицу заявок терминала ВООБЩЕ. Что существенно быстрее работает.
Что то типа такого?
 
Цитата
VPM написал:
Ряд торговых задач решается только с HFT подходом даже в рамках QUIK
   Из интернета: "Высокочастотный трейдинг (HFT — High-Frequency Trading) — это вид алгоритмической торговли, использующий мощные компьютеры и сверхскоростные алгоритмы для совершения тысяч операций за доли секунды."
   Вы считаете HFT торговлей в QUIK подачу заявок с ограничением < 50 заявок в секунду и с получением данных с биржи с задержкой > 1 секунды?
 
TGB,  Речь идет о подходах используемых HFT — High-Frequency Trading, а не технологиях.  
Цитата
VPM написал:
QUIK накладывает свои особенности, а у кого их нет, такова действительность. В своем подходе остановился на HFT исполнении и событийной модели, (опросы в качестве уточнений и восстановления). Конечно это не чистый HFT (ни для соревнований с фондами типа "Atto Trading Technologies LLC", которые уже говорят о наносекундах), у нас "QUIK latency" - высокий и нет прямого доступа к бирже. Но ряд подходов широко не обсуждаемых, можно использовать. Главным образом появляется возможность универсального движка, к которому можно цепляться как инвестиционными стратегиями, так HFT конечно в рамках QUIK. Этот подход требует и особой надежности системы (коду нужно крутиться продолжительное время)
 
Цитата
VPM написал:
остановился на HFT исполнении
   Это массовая подача заявок с ограничением < 50 заявок в секунду? И до этого вы бы не додумались, если бы вы не прочитали про HFT?
 
Идея (упрощённая модель) не использовать OnOrder / OnTrade ориентироваться на:
а) OnTransReply;
б) периодический просмотр таблиц orders / stop_orders;
с) фильтр по TRANS_ID.
- звучит вроде логично, но это polling-модель, не учитывает ряд критических проблем.

Попробую их формализовать. Это ровно то место, где ломается вся система!

Критическая проблема №1 "trans_id не живёт дальше заявки".
-------------------------
Ты опираешься на: trans_id > order > состояние
Но в реальности:  OnTrade НЕ содержит trans_id
значит: ты не можешь связать сделку с заявкой через trans_id?

Критическая проблема №2 "Таблицы QUIK - поток событий".
-------------------------
Ты предлагаешь: проверять orders / stop_orders при изменении количества записей. Проблема:

1) Нет гарантии атомарности?
T0 - появился order
T1 - сразу прошёл trade
T2 - ты прочитал таблицу
! ты увидишь: order уже частично исполнен НО не увидишь последовательность событий?

2) Ты теряешь событийную причинность?
FSM требует: ACK > TRADE > PARTIAL > FILLED
А таблица даёт тебе: balance уменьшился
! ты не знаешь:
* сколько было трейдов
* в каком порядке
* были ли дубли
* был ли cancel между ними

Критическая проблема №3 "Дедупликация невозможна"
-------------------------
В event-driven модели:processed_trades[trade_num]
В polling-модели: ты просто читаешь итоговое состояние
! ты не можешь:
* отфильтровать дубли
* гарантировать idempotency?

Критическая проблема №4 "Out-of-order ты вообще не контролируешь"?
-------------------------
QUIK: OnTrade > раньше OnOrder
Модель: таблица > уже обновлена
! ты даже не узнаешь, что был out-of-order?

Критическая проблема №5 "частичное исполнение (Partial fills) ломают модель"
-------------------------
Реальность: order 100
> trade 30
> trade 20
> trade 50
Таблица: balance = 0
! ты НЕ знаешь, было 3 сделки или 1 или 10?

Критическая проблема №6 "отмена/замена (Cancel/Replace) становится некорректным"
-------------------------
cancel > trade > cancel confirm
Polling увидит: order исчез
! ты НЕ узнаешь: был ли fill перед cancel сколько именно исполнилось?
 
Этих проблем нет. Ощущение, что просто ищется любое, чтобы оправдать колбеки.
 
Nikolay,  У меня ломается система, пытаюсь установить причины и довести событийную модель "до ума". Возможно и перестарался с проблематикой надежности, но уже "дую на воду".  И почему бы не колбеки использовать? В любом случае основные проблемы: асинхронность прихода и их потеря установленны.  

А "Hybrid model: Event-driven (основа) + Snapshot reconciliation (страховка)", мне пока представляется наиболее правильной для использования в QUIK?
 
Цитата
TGB написал:
Это массовая подача заявок с ограничением < 50 заявок в секунду? И до этого вы бы не додумались, если бы вы не прочитали про HFT?
А что это за ограничения? У брокера? Но даже на этой скорости исполнения заявок 1000 мс / 50 = 20 мс. нужно четко корректное исполнение. Что не так то?
 
Цитата
VPM написал:
У меня ломается система, пытаюсь установить причины и довести событийную модель "до ума".
  Это результат применения ваших "подходов".
  Цитирую себя: "Меньше дергаешься, реже падаешь".
 
Она ломается, т.к. есть попытка организовать алгоритм, основанный на последовательности. Но т.к. порядок прихода колбеков, да и в целом приход данных с сервера брокера - не имеет определенного порядка.

Да, сделки могут прийти первыми, до ордера. И что это ломает.

Вы просто выполняете свой алгоритм:

Отправили транзакцию.
Ждем появления ордера по номеру транзакции. Хоть по колбеку, хоть сканируя появления записи в таблице ордеров.
Получили ордер. Смотрим на его состояние. И здесь опять не важно как: колбеком или чтением данных с "датчика".
Если состояние изменилось, то переходите к шагу для текущего состояния.
Исполнен - смотрим на сделки. Можно в лоб прочитать таблицу сделок через SearchItems (номер ордера же уже есть). А можно накапливать буфер прошедших сделок, и уже читать из этого буфера. Т.о. не важно пришла ли сделка первой или второй.
Не активен - проверяем какой баланс, и если отличается от количества, то еще необходимо получить сделки до снятия.

Т.о. алгоритм - это State Machine, конечный автомат. Вы просто переходите между состояниями, проверяя данные. Как эти данные получены - не важно.
Если же алгоритм сильно зависит от порядка получения данных, то это плохо. В клиент-серверном приложении точно, когда OnOrder может прийти через минуты после OnTrade. В алгоритме, который просто проверяет данные, он просто будет ждать пока не будет получен весь набор данных, влияющих на переход в следующее состояние.
 
Цитата
VPM написал:
А что это за ограничения? У брокера? Но даже на этой скорости исполнения заявок 1000 мс / 50 = 20 мс. нужно четко корректное исполнение. Что не так то?
Да, у брокера. Причем у большинства это не 50, а 30.
 
Цитата
TGB написал:
Цитата
nikolz написал:
не использую таблицу заявок терминала ВООБЩЕ.
  То есть, у вас, до сих пор, в роботе не реализовано нормальное восстановление продолжения его работы при его перезапуске по любой причине?
 Например, ваш робот подал заявку и тут же упал (почему бы нет?). Вы его смогли починить через 1 час и запустили. И он, не используя таблицу заявок терминала ВООБЩЕ,
ждет появления OnOrder?
----------
   
Цитата
Прикольно, Я понял что Вы пошутили.
При старте и перезапуске таблицы обрабатывает даже буратино.
Полагаю, что  вопрос не о старте терминала, а о работе внутри торгового дня и особенно когда рынок штормит.
 
Цитата
TGB написал:
  Цитирую себя: "Меньше дергаешься, реже падаешь".
На это Ваше "открытие" Алиса отвечает:  
---------------------
Не удалось найти информацию о связи между уменьшением непроизвольных движений и снижением риска падений
 
Цитата
nikolz написал:
Алиса отвечает: Не удалось найти информацию о связи между уменьшением непроизвольных движений и снижением риска падений
   Плохо ищет:
Обзор от ИИ
Уменьшение непроизвольных движений (дерганий) напрямую связано со снижением частоты падений, улучшением координации и стабилизацией нервной системы.
 
Видимо Алиса не знакома с Теорией решения изобретательских задач  (ТРИЗ). Где базовая логика начинается с:   "Любая сложная задача - это Противоречие❗" :smile:
 
Заглянул в материальную часть. То, что  по сути описал Nikolay,  - это  модель State-based FSM (pull + buffer), а не event-driven FSM. Нужно более детально разобрать отличие? Так как мой первоначальный замысел и реализация все таки крутится в парадигме event-driven FSM.

Предложенная модель:
1. Отправили транзакцию
2. Ждём появления order (любой источник)
3. Смотрим состояние order
4. При необходимости подтягиваем trades
5. Двигаем FSM
И главное, не важно, в каком порядке пришли данные!

Переформулирую принцип данной модели: "FSM НЕ должен зависеть от порядка событий !НО должен потреблять события как источник истины"?

Что смущает? Заявлено в подходе "можно просто прочитать таблицу сделок через SearchItems”  Когда делаешь: SearchItems("trades", ...)  получаешь текущее состояние таблицы НО НЕ полную историю изменений?

Что при этом теряешь:
1.  Не знаешь - уже учитывал этот trade или нет
2.  Дельту. было  fill = 30 стало fill = 50, не знаешь - это +20? или пересчёт? или дубликат?
3. Частичные исполнения в динамике. (Для FSM важно  PARTIAL > PARTIAL > FILLED, а snapshot даёт balance = X)

А что с подходом Event-driven (моя текущая)
+ точная история (trade_num)
+ идемпотентность
+ корректный partial fill

- нужно решать out-of-order?

Что не так?
 
Цитата
VPM написал:
event-driven FSM

готовые реализации FSM для Lua:
  • MoonAgents — модуль для событийно-ориентированного параллельного программирования, вдохновлённый моделью параллелизма ITU-T SDL. Позволяет реализовывать системы из взаимодействующих конечных автоматов («агентов»). github.com
  • lua-fsm — простая реализация конечного автомата, основанная на идее javascript-finite-state-machine. Позволяет создавать машины состояний с методами для каждого события. github.com
  • StateQ — полностью типизированная реализация FSM в Luau с поддержкой асинхронных переходов через очередь событий. github.com
 
Цитата
VPM написал:
Что смущает? Заявлено в подходе "можно просто прочитать таблицу сделок через SearchItems”  Когда делаешь: SearchItems("trades", ...)  получаешь текущее состояние таблицы НО НЕ полную историю изменений?
Меня смущает этот вопрос. Really?

Мы же про сделки говорим. Вы получите все сделки ордера. Это и есть ВСЯ история сделок. Сделка - это законченное, неделимое целое с точки зрения таблицы Квик. Чем будет отличаться полученное если это через колбеки или через таблицу сделок? Или здесь вопрос в времени запроса? Хотя и оно будет одинаковым до долей. Содержимое-то не изменится.

Что же касается остального - все подходы по учету дублей такие же. Сделку прочитали - записали, то зачем её ещё раз учитывать, если второй раз читаете? Точно также как с колбеком - пришел раз, второй уже не учитываешь.

Частичные исполнения в динамике - да также. Увидел, что баланс изменился - посмотри сделки.

Мы здесь разбираем пример работы с ордерами. Но сделки, на самом деле, учитываются независимо, через накопление в буфер. Сделка необходима для расчета своей позиции скрипта, независимой от другого скрипта.
Поэтому сделки контролируются просто постоянно. А уже при проверке ордера мы обращаемся к источнику сделок. Если же позиция контролируется не через сделки, то да, источник - сама таблица сделок.
 
Nikolay,  То что таблица trades это фактический журнал сделок, я понимаю.
Почему я акцентировал внимание. Я о той грани, что отделяет статику от динамики. В статике да Вы все правильно описали, согласен целиком, а в динамике — нет?

Расхождение возникает не в полноте истории данных, а в семантике доступа их получения, в моменте её доступности.

1) Мы получаем: МНОЖЕСТВО сделок (set)
2) Мы получаем: ПОТОК сделок (stream)
Смыслы для программирования можно свести к следущим:
  Подход 1. Pull (SearchItems)
 Подход 2: Push (OnTrade)
А это фундаментально разные модели.

Если для модели, (как Вы и предлагаете) порядок НЕ важен значит таблицы (set) достаточно не поспоришь. Но ведь реальная проблема не в истории сделок, а в моменте наблюдения.

Формально, SearchItems даёт полную историю, НО не гарантирует, что она уже полная в момент запроса?  Ну например гонка терминал / сервер (может быть?)
T0: сделка произошла на бирже
T1: мы взываем SearchItems
T2: QUIK обновил таблицу trades, мы НЕ увидели эту сделку.

В то время как OnTrade просто доставка события.
 
Nikolay,  Кажется уловил Вашу идею, все дело в буффере. Именно он опять возвращает в событийную модель через опрос + буффер, вместо callbacks + буффер?
И решаются вопросы тайминга?
 
nikolz,  Спасибо за демонстрации подходов, на мой взгляд нет большой надобности в наших задачах выстраивать какие - то изыски? У меня все просто для задачи  executor: FSM, event queue, orphan buffer, recovery, net-order support. Сейчас такой вариант:

Код
-- ============================================================
-- 1. Order State Machine (FSM)
-- ============================================================
local OrderState = {}
OrderState.__index = OrderState

function OrderState:new(trans_id, params)
    local o = {
        state = "NEW",          -- NEW, SENT, ACK, PARTIAL, FILLED, CANCEL_PENDING, CANCELLED, REJECTED
        trans_id = trans_id,
        order_num = nil,
        strategy = params.strategy,
        sec = params.sec,
        side = params.side,
        qty = params.qty,
        price = params.price,
        filled = 0,
        created_at = os.time(),
        last_update = os.time(),
        is_net = params.is_net or false,
    }
    setmetatable(o, self)
    return o
end

function OrderState:on_sent()
    self.state = "SENT"
    self.last_update = os.time()
end

function OrderState:on_ack(order_num)
    self.order_num = order_num
    self.state = "ACK"
    self.last_update = os.time()
end

function OrderState:on_trade(trade_qty)
    if self.state == "CANCELLED" then
        self.logger:warn("Trade after cancel, still processing")
    end
    -- обновление filled
    if self.filled >= self.qty then
        self.state = "FILLED"
    else
        if self.state ~= "CANCELLED" then
            self.state = "PARTIAL"
        end
    end
end

function OrderState:on_cancel()
    if self.state == "FILLED" then return end
    if self:is_active() then
        self.state = "CANCEL_PENDING"
    end
end

function OrderState:on_cancelled()
    self.state = "CANCELLED"
    self.last_update = os.time()
end

function OrderState:on_reject()
    self.state = "REJECTED"
    self.last_update = os.time()
end

function OrderState:on_timeout()
    if self:is_active() then
        self.state = "CANCELLED"
        self.last_update = os.time()
    end
end

function OrderState:is_active()
    return self.state == "SENT" or self.state == "ACK" or self.state == "PARTIAL"
end
 
Цитата
VPM написал:
Nikolay,  Кажется уловил Вашу идею, все дело в буффере. Именно он опять возвращает в событийную модель через опрос + буффер, вместо callbacks + буффер?
И решаются вопросы тайминга?
Можно и так сказать. Вы можете просто ждать колбека. А можете постоянно опрашивать таблицу. Именно, что постоянно. Естественно через проверку числа записей в таблице как триггер.
На вопрос - это же накладно. Ответ - большую часть времени скрипт ничего не делает, так что проверить число записей в таблице - это можно сказать что ничего.

Т.к. я привык к такой модели: есть датчик, получаем данные с него постоянным опросом, то это вполне естественное поведение и здесь. Кому-то это может показаться "устаревшим", "неудобным" и т.д. Но колбеки - это просто абстракция над точно такой же моделью поведения, просто скрытой от глаз. Да, раз они есть, их можно использовать. Но - это уже вопрос привычки.
 
А можете не большой пример кода опроса показать, через  SearchItems? Что то у нас с ним  ни как отношения не сложатся. Уж больно мудрёная функция какая - то.
 
Так реализация банальна. Запоминаете число записей в таблице при прошлом запросе. При новом проверяете число записей и если оно изменилось, значит есть новая сделка. А дальше уже можете просто перебрать с прошлого индекса до нового или вызвать поиск тоже с прошлого индекса. Ничего необычного. Может даже назначит функцию колбек на новую запись и будет вам свой колбек, а не системный. Причем его никогда не пропустите, т.к. при старте скрипта опросите таблицу.
 
А чем хуже тогда этот вариант?

Код
function main()
    local last_trade_count = getNumberOf("trades") -- Запоминаем кол-во сделок при старте
    
    while is_run do
        local current_count = getNumberOf("trades")
        
        if current_count > last_trade_count then
            -- Проходим по всем новым сделкам
            for i = last_trade_count, current_count - 1 do
                local trade = getItem("trades", i)
                message("Новая сделка (Pull): " .. tostring(trade.sec_code) .. " по цене " .. trade.price)
            end
            last_trade_count = current_count
        end
        
        sleep(30) -- Пауза  мл.секунд, чтобы не вешать процессор
    end
end
 
Да Усек, "Функция SearchItems — это встроенный в QUIK «движок» быстрого поиска. Главная её фишка в том, что она перебирает строки таблицы на уровне C++, что в десятки раз быстрее, чем обычный цикл for в Lua."
 
Что же это получается, 30 - 50 сделок в секунду с поиском их при помощи SearchItems в потоке QUIK, ну допустим в 10 раз быстрее чем сделает Lua. Здесь главная задача остается не перегружать основной поток. Так как я уже задумался про обработку таблицы всех сделок этим способом. То нужны критерии нагрузок?
 
Цитата
VPM написал:
Так как я уже задумался про обработку таблицы всех сделок этим способом
Таблицу обезличенных сделок, как разбор стакана, делать только через main. Через колбеки - это плохая затея.

Если же говорить о нагрузке, то вызов getNumberOf("trades") хоть раз в 2 млс - это не то, о чём стоит говорить. Стоит думать о том, какой цикл выполнения функции main. Если это 100 млс, для примера, то ручной разбор сделок будет неотличим от колбека, если говорить о временной составляющей. Даже скорее быстрее, что я показал в одной из веток при анализе одного брокера.

Если же цикл main секунда и более, то уже стоит думать о сокращении этого времени, т.к. колбек хоть может и придёт, но действия по этому колбеку всё равно будет в main, а он медленный.
 
Может я не все детали уловил, есть же вариант асинхронной (корутины) для  обработки таблиц таких загруженных, как таблицу обезличенных сделок?  А фильтровать параметры через SearchItems? Ну как вариант. Если много инструментов мне думается важны предельно допустимые нагрузки на QUIK? Можно все повесить. А скорость цикл main она просчитывается 1000 мс / (30 - 50 сделок). Можно как стандарт брать.
 
В моем нынешнем варианте (который пытаюсь довести "до ума") скорость цикла main делаю адаптивной к загрузке очереди.

Код
local qsize = get_queue_size(ctx.queue)
            if qsize > 10000 then sleep(1)
            elseif qsize > 1000 then sleep(5)
            elseif qsize > 100 then sleep(10)
            else sleep(20) end
       Но пока очередь не переполнялась то и выводов нет. Размер очереди задаю изначально. Но это так пока эксперименты?
Страницы: Пред. 1 ... 13 14 15 16 17 След.
Читают тему
Наверх