{"id":17466099,"url":"https://github.com/m-kus/mpipe","last_synced_at":"2025-07-25T06:40:34.852Z","repository":{"id":98725866,"uuid":"169857342","full_name":"m-kus/mpipe","owner":"m-kus","description":"Build a trading system out of functional modules","archived":false,"fork":false,"pushed_at":"2019-02-09T13:00:59.000Z","size":73,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-02-02T09:26:13.406Z","etag":null,"topics":["algotrading","execution","pipeline","trading-bot"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/m-kus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-09T10:54:38.000Z","updated_at":"2019-05-03T05:46:15.000Z","dependencies_parsed_at":"2023-05-24T23:45:17.797Z","dependency_job_id":null,"html_url":"https://github.com/m-kus/mpipe","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-kus%2Fmpipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-kus%2Fmpipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-kus%2Fmpipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-kus%2Fmpipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/m-kus","download_url":"https://codeload.github.com/m-kus/mpipe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245999318,"owners_count":20707554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algotrading","execution","pipeline","trading-bot"],"created_at":"2024-10-18T13:08:03.092Z","updated_at":"2025-03-28T08:43:40.710Z","avatar_url":"https://github.com/m-kus.png","language":"C++","readme":"# MPipe\n\n[![Build Status](https://travis-ci.org/m-kus/mpipe.svg?branch=master)](https://travis-ci.org/m-kus/mpipe)\n[![Made With](https://img.shields.io/badge/made%20with-cpp-red.svg?)](https://www.python.org)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nЭто библиотека на C++ для быстрого создания торговых систем из кубиков - модулей `Module`. \nОни ничего не знают друг про друга и общаются с помощью большой структуры - состояния `State`.\nЕсть готовые модули, функционал которых покрывает большинство задач, однако можно легко реализовывать свои, на C++ или Python (без потери производительности). \nПроцесс создания приложения заключается в инициализации необходимых модулей и их объединения в конвейеры - пайпы `Pipe`.\n\n# Быстрый старт\n\nСобираем проект скриптом, забираем артифакты:\n* MQL5, для работы с метатрейдером, файлы надо аналогично разложить по папочкам\n* Python inplace, для быстрой отладки, надо закинуть в папку с вашим скриптом\n* Python wheel, можно установить так: `pip install \u003cwheel_name\u003e.whl`\n\n## Хочу написать страту на питоне\n\nИдем по порядку, сперва нужно создать инструменты, которые му будем использовать.\nОбычно они есть в библиотеке предопределенных инструментов `SecurityStore`, если нет - пните меня или добавьте сами.\n\n```python\nfrom pype import *\nss = SecurityStore()\n```\n\nБудем строить индикатор и торговать по сишке:\n\n```python\nsi = ss.Create('Si', settings=\"trades,finam,book1\", leg_factor=1)\n```\n\nleg_factor это на что мы будем домножать размер и направление ордера (он может быть 0, если мы не хотим его торговать), флаги означают, что мы хотим получать сделки, исторические данные, стакан глубиной 1.\n\nУра! Теперь можно создать состояние.\n\n```python\nstate = State([si])\n```\n\nНам нужны исторические данные с Финама, таймфрейм 1 минута, с предыдущего торгового дня, закрытия свечи:\n\n```python\nfinam = Finam(\n    timeframe=FinamTimeFrame.Min1,\n    day_shift=1,\n    bar_open=False)\n```\n\nКотировки c минимальной частотой тика 5 секунд:\n\n```python\nmcast = Mcast([], quartz_period=5000)\n```\n\nОграничение по времени торговли:\n\n* Начинаем торговать в 7\n* Не открывать позицию после 10\n* Принудительное закрытие в 11\n\n```python\nhours = ModHours([\n\tModHoursRule(\"19:00:00\", Side.All, Action.Open),\n    ModHoursRule(\"22:00:00\", Side.All, Action.Close),        \n    ModHoursRule(\"23:00:00\", Side.All, Action.ForceClose)\n])\n```\n\nХотим, чтобы позиция закрывалась по времени через час (этот модуль генерирует ордеры, нужно ставить его перед другими модулями, создающими ордеры):\n\n```python\ntclose = ModTimeClose(timeout=3600000)\n```\n\nТеперь модуль, который будет по сигналу создавать торговые ордеры:\n\n```python\nordgen = ModOrderGen(lot_size=1)\n```\n\nДалее цепочка фильтров по объему и цене:\n\n* Не больше, чем позволяет ограничение по позе\n* Не больше, чем есть в стакане, но не меньше заданного количества\n* По лучшей цене + один пункт вглубь\n\n```python\nposlmt = ModPosLimit(max_pos=1)\nmarket_px = ModMarketPx(min_qty=1, slippage=-1)\n```\n\nНаконец, торговый шлюз! Возьмем для начала виртуальное исполнение:\n\n```python\nexecutor = ModVirtExec(\n    trade_timeout=1000, \n    cancel_timeout=3000, \n    trades_file='trades.csv', \n    random_unfill=False\n)\n```\n\nА теперь самое главное, сама стратегия. Мы должны обработать котировки и передать торговый сигнал.\n\n```python\nclass Strategy(Module):\n    \n    def __init__(self):\n        super(Strategy, self).__init__()\n        \n    def Mutate(self, state: State)\n    \tpass\n```\n\nЭто базовый каркас, теперь начнем его наполнять. Для примера возьмем простой алгоритм: покупаем ниже скользящей средней, продаем выше. Нам потребуется индикатор:\n\n```python\nself.rol_mean = RollingMean(window=15, timeframe=60000)  # 15 минут\n```\n\nКаждый вызов `Mutate` это тик, обновляем индикатор:\n\n```python\nif state.securities[0].trd_ts \u003e 0:  # если цена последней сделки валидная\n\tself.rol_mean.Update(state.signal.ts, state.securities[0].trd_px)  # то обновляем\n```\n\nСама логика:\n\n```python\nsi = state.securities[0]\nif self.rol_mean.Good():  # прогрелся, значение валидное\n    mean = self.rol_mean.calculate()\n    if si.bid \u003e mean + 100:\n        state.signal.action = Action.Open\n        state.signal.side = Side.Sell\n    elif si.ask \u003c mean - 100:\n        state.signal.action = Action.Open\n        state.signal.side = Side.Buy\n```\n\nОтлично! Надо все это теперь соединить вместе и запустить.\n\n```python\nimport time\n\nstrategy = Strategy()\n\nexecutor.Start(state, [])\nfinam.Start(state, [strategy])  # запускаем в начале, чтобы прогреть индикатор\nmcast.Start(state, [strategy, tclose, hours, ordgen, poslmt, market_px, executor])\n\ntry:\n    while True:\n        time.sleep(1)\nexcept KeyboardInterrupt:\n    pass\n\nexecutor.Stop()\nmcast.Stop()\n```\n\nОбязательно надо останавливать активные модули в конце!\n\n## Хочу прикрутить сигналы из метака\n\nОбщение будет реализовано с помощью 0mq сокетов, для этого есть специальный модуль. В нем из сокета считывается бинарная структура `Signal`:\n\n```c++\nstruct Signal\n{\n  \tint64_t ts;  // время сигнала в миллисекундах\n  \tSide side;  // (int32_t) направление ордеров: покупка, продажа\n\tAction action;  // (int32_t) действие: открытие, закрытие, пропуск\n};\n```\n\nВарианты:\n\n- Открыть лонг `ActionOpen` `SideBuy`\n- Открыть шорт `ActionOpen` `SideSell`\n- Закрыть лонг `ActionClose` `SideSell`\n- Закрыть шорт `ActionClose` `SideBuy`\n- Закрыть позицию `ActionClose` `SideAll`\n- Ничего не делать `ActionBypass` `SideAll`\n\nСпециальные случаи:\n\n- Аварийное завершение пайпа `ActionTerminate`\n- Принудительное закрытие позиции `ForceClose` (по времени, в конце торгового дня)\n\nВнутри модуля идет проверка, насколько старый сигнал записан в память. \nЕсли значение больше допустимого таймаута, то считается, что сигнала нет (`ActionBypass`).\n\nНа стороне метатрейдера нужно писать в сокет соответствующие значения (не забываем разложить нужные фалы по папочкам):\n\n```c++\n#include \u003cModZmqSignalPub.mqh\u003e\n\ninput string forwarder_tx = \"tcp://127.0.0.1:42001\";\nModZmqSignalPub pub;\n\nint OnInit()\n{\n   if (pub.Stat(forwarder_tx) != 0)\n      return INIT_FAILED;\n         \n   return INIT_SUCCEEDED;\n}\n\nvoid OnTick()\n{\n  \tif (OpenLong())\n    {\n      \tpub.SendSignal(ActionOpen, SideBuy);\n    }\n  \telse\n    {\n      \tpub.SendSignal(ActionBypass, SideAll);\n    }   \t\t\n}\n```\n\n\n\n## Хочу торговать синтетик\n\nВ случае синтетической позиции, у нас появляется несколько \"ног\". Соотношение между ними регулируется с помощью коэффициента `leg_factor`. Например, для торговли фьючерсным спредом значения будут 1 и -1.\n\nТакже появится несколько дополнительных модулей:\n\n* Модуль, выравнивающий размер ордеров `ModEqualizer`\n* Модуль, выравнивающий позицию, в случае неисполнения части ордеров `ModBalancer`, он работает только если нет висящих неисполненных ордеров\n\n```python\nequalizer = ModMcastEqualizer()\nbalancer = ModMcastBalancer()\n\nexecutor.Start(state, [balancer, poslmt, market_px, executor])  # вызывается на каждую сделку\nmcast.Start(state, [strategy, ..., poslmt, market_px, equalizer, executor]) # перед исполнением\n```\n\n\n\n## Хочу протестировать страту\n\nНет проблем, берем модуль Финама, задаем в нем количество торговых дней и запускаем боевой пайп. \nЕдинственное, нужно заменить исполнение `ModMarketPx` на `ModTrdPx`:\n\n```python\ntrdpx = ModTrdPx(spread_pt=2)\n\nfinam.Start(state, [strategy, ..., trdpx, executor])\n```\n\n\n\n## Часто возникающие проблемы\n\n#### Кастомный модуль падает при вызове Mutate\n\n* Проверьте наличие в конструкторе  `super(\u003cYourModuleName\u003e, self).__init__()`\n* Оберните тело функции в `try except`\n\n#### Не торгует :(\n\n* Убедитесь, что в сигнале не нулевой таймстамп `ts`\n* `leg_factor` должен быть отличным от нуля\n\n# Как все работает\n\n## Модули\n\nМодули — объекты, генерирующие (активные) или преобразующие (пассивные) состояние `State`. \nПайп представляет собой список модулей, которые выполняются слева направо. \nПайп есть у каждого активного модуля (может быть пустой).\nКогда активный модуль принимает решение о запуске пайпа, он ждет, пока освободится состояние, блокирует его (чтобы одновременно выполнялся только один пайп) и запускает обработку сигнала.\n\nОчень важно, чтобы активный модуль не блокировал основной поток приложения. Исключение делается только для загрузки исторических данных и тестирования.\n\nПассивные модули должны реализовывать метод `Mutate` , принимающий в качестве аргумента указатель на состояние `State`.\n\nЗапуск и остановка активного модуля осуществляется с помощью методов `Start` (передается указатель на состояние и пайп) и `Stop` соответственно.\nМодуль может переопределить методы `StartImpl` и `Stop` при необходимости.\nДля запуска пайпа нужно получить состояние с помощью  `GetState`, далее заблокировать его, вызвать `ExecutePipe` и разблокировать состояние.\n\n\n\n### Пишем пассивный модуль\n\nС++\n\n```c++\nstruct PassiveModule : public Module\n{\nprivate:  \n  \tint64_t int_param_;\n  \tstd::string string_param_;\n \npublic:  \n  \tPassiveModule(int64_t some_int_param,  // все параметры можно передать в конструкторе\n                  const std::string\u0026 some_string_param)\n      \t: int_param_(some_int_param)\n    \t, string_param_(some_string_param)\n  \t{\n  \t}\n  \n\tvoid Mutate(State* state) override\n\t{\t\n\t\tstate-\u003esignal.action = Action::Open;\n    \tstate-\u003esignal.side = Side::Buy;\n\t}\n};\n```\n\nPython\n\n```python\nclass PassiveModule(Module):\n    \n    def __init__(self, some_param, another_param):        \n        super(PassiveModule, self).__init__()  # без этого не будет работать!\n        self.param = some_param\n        self.another_param = another_param\n\n\tdef Mutate(self, state: State):  # не забываем про self\n\t\tstate.signal.action = Action.Open\n    \tstate.signal.side = Side.Buy\n```\n\n\n\n### Пишем активный модуль\n\nВ С++ для блокировки состояние можно использовать удобный класс `ScopedState`, который работает в пределах видимости. Или можно использовать методы примитива синхронизации:\n\n* `lock` ждет освобождения ресурса и блокирует ресурс\n* `unlock` освобождает ресурс\n* `try_lock` пытается захватить ресурс, при неудаче возвращает _false_ иначе _true_\n\nС++\n\n```c++\nclass ActiveModule : public Module\n{\nprivate:\n  \tint64_t int_param_;\n  \n\tvoid SomeCallbackFromThread(int64_t ts)  // все время в миллисекундах\n\t{\n\t\tScopedState state(GetState());  // удобный класс для контекстной блокировки \n\t\tExecutePipe(ts, true)  // сброс сигнала выставлен в true\n\t}\n\t\npublic:  \n  \tActiveModule(int64_t int_param)\n    \t: int_param_(int_param)\n    {\n\t}\n\n\tvoid StartImpl() override\n\t{\t\n\t\tStartSomeThreadWithParam(int_param_);\n\t}\n\n\tvoid Stop() override\n\t{\t\n\t\tStopThread();\n\t}\n};\n```\n\nPython\n\n```python\nclass ActiveModule(Module):\n\n\tdef __init__(self):        \n        super(ActiveModule, self).__init__()\n\n\tdef StartImpl(self):\n\t\tstartSomeThreadProc()\n\n\tdef Stop(self):\n\t\tstopThreadProc()\n\n\tdef callbackFromFuckingThread(self, ts: int):\n\t\tstate = self.GetState()\n\t\tstate.lock()\n\t\tself.ExecutePipe(ts, reset=true)\n\t\tstate.unlock()\n```\n\n\nАктивный модуль должен обязательно передать в пайп метку времени —  ее будут использовать все последующие модули. Это может быть текущее время, серверное время или прошлое, при работе с историческими данными.\n\nПримеры запуска активного модуля:\n\nС++\n\n```c++\nactive_module.Start(\u0026state, { \u0026mod1, \u0026mod2, \u0026mod3 })\n```\n\nPython\n\n```python\nactiveModule.Start(state, [mod1, mod2, mod3])\n```\n\n**ВАЖНО!** \nОборачивайте, пожалуйста, тела методов в питоне в try except, иначе можно очень долго отлавливать неявный баг.\n\n## Состояние\n\nСостояние — это структура, в которой содержится вся информация, необходимая для общения между модулями:\n\n* Список инструментов `Security`\n* Текущий торговый сигнал `Signal`\n* Текущие торговые ордера `Order`\n\nСостояние создается один раз в одном экземпляре и используется на протяжении всей работы программы.\nСостояние инициализируется списком предварительно созданных инструментов.\n\n### Инструмент\n\nСтруктура описывающая параметры и состояние торгуемого на какой-либо площадке инструмента. Состоит из нескольких секций. \nПервая секция содержит неизменяемые параметры, необходимые для идетификации инструмента в различных системах:\n\n* `full_code` полный код контракта, например Si-12.16\n* `class_code` идентификатор сессии, например CETS (валюта), SPBFUT (фьючерсы)\n* `px_decimals` количество знаков после запятой в цене\n* `px_step` шаг цены в пунктах\n* `qty_decimals` количество знаков после запятой в объеме\n* `qty_step` шаг объема в пунктах\n\nСтакан заявок:\n\n- `book_ts` время последнего изменения стакана (в мс); 0 означает, что стакан невалидный\n- `bid_px` лучшая цена на покупку (в пунктах) \n- `bid_qty` объем по лучшей цене на покупку\n- `ask_px` лучшая цена на продажу (в пунктах) \n- `ask_qty` объем по лучшей цене на продажу\n- `min_px` минимальная цена в стакане (в пунктах) \n- `max_px` максимальная цена в стакане (в пунктах) \n- `book` массив объемов по указанной цене (индексы - цены в пунктах)\n\nПоследняя сделка:\n\n- `trd_ts` время последней сделки (в мс)\n- `trd_px` цена последней сделки (в пунктах) \n- `trd_qty`объем последней сделки\n\nПозиция:\n\n- `pos` позиция со знаком (+1 лонг, -1 шорт)\n- `pos_buy_limit` суммарный объем висящих лимиток на покупку\n- `pos_sell_limit` суммарный объем висящих лимиток на продажу\n- `pos_buy_chunks` разбивка лонга по времени\n- `pos_sell_chunks` разбивка шорта по времени\n\nНастройки:\n\n- `settings` строка с перечисленными потоками данных для данного иструмента\n- `leg_factor` коэффициент ноги, имеет знак (1 общее направление, -1 обратное направление); значение 0 означает неторговый инструмент\n\nРасширения:\n\n- `extensions` словарь значений вещественного типа, где ключ - идентификатор, уникальный для каждого расширения стакана или сделок\n\n#### Настройки \n\nСтрока подписки на рыночные данные может содержать следующие значения (разделитель не важен, регистр нижний):\n\n- `book1` подписка на лучшие цены\n- `book5` подписка на стакан глубины 5\n- `book20` подписка на стакан глубины 20\n- `book50` подписка на стакан глубины 50\n- `book` подписка на полный стакан\n- `trades` подписка на сделки\n- `finam` подписка на прогрев\n- `zmq` подписка на котировки по 0mq\n\n### Сигнал\n\nТоговый сигнал — структура, содержащая время, действие и направление торгового решения.\nВременная метка сигналу присваивается активным модулем при запуске конвейера.\nВ сигнале может быть сколь угодно много торгуемых ног.\n\nНапраление `Side` имеет для удобства представление в виде знака. Т.о. становятся удобными операции инвертирования направления и модификации позиции.\n\n* 1 `Buy` \n* -1 `Sell` \n* 0 `All` \n\nДействия `Action` отсортированы в порядке наибольшего приоритета, это сделано для возможности фильтрации с помощью правил.\n\n* 0 `Terminate` \n* 1 `ForceClose` \n* 2 `Bypass` \n* 3 `Close`\n* 4 `Open`\n\n### Ордер\n\nСодержит информацию для торгового приказа: направление, количество, цену в десятичных пунктах, время постановки в миллисекундах и указатель на соответствующий инструмент.\n\nЧтобы получить цену (объем) в виде числа с плавающей точкой, надо цену (объем) ордера поделить на 10 в степени количество знаков после запятой у данного инструмента: \n\n```python\norder.px / 10 ** order.security.px_decimals\norder.qty / 10 ** order.security.qty_decimals\n```\n\nОрдер может находиться в следующих состояниях:\n\n* `Pending` ордер передан в обработку, но факт обработки не подтвержден (на примере zmq, ордер отсылается паблишером, до получения пакета со статусом `OrderStatus::Placed`)\n* `Placed` ордер обработан (лимитка еще может быть не выставлена)\n* `Filled` ордер частично или полностью исполнен\n* `Canceled` ордер отвергнут системой или отменен, полностью или частично\n\nДля удобства класс содержит методы, изменяющие состояние ордера:\n\n* `Add`  обновляет метку времени и присваивает один из статусов `Pending` или `Placed`; позиция не изменяется, но модифицируются значения `pending_buy_limit` или `pending_sell_limit`. \n* `Fill`  вызывается при сделке, передаются время, объем и цена сделки; Происходит модификация позиции, текущих лимиток, а также разбивки позиции по времени `PositionChunk`. Это механизм для закрытия по времени FIFO. При каждой сделке \"кусочки\" позиции правильно схлопываются и объединяются так, что их сумма всегда равна текущей позиции.\n* `Cancel` отмена ордер\n\n### Кастомные поля в состоянии\n\nВ С++ нужно отнаследоваться от State, далее во всех местах кастовать указатели:\n\n```c++\nvoid Mutate(State* state) override\n{\n\tExtendedState* state_ex = dynamic_cast\u003cExtendedState*\u003e(state);\n}\n```\n\nВ питоне поддерживаются динамические аттрибуты:\n\n```python\ndef Mutate(self, state: State):\n    print(state.some_new_var)\n\tstate.another_extended_var = 42\n```\n\n\n\n### Предопределенные инструменты\n\nКласс `SecurityStore` хранит список предопределенных инструментов. Создать экземпляр можно как по полному коду, так и по сокращению (удобно при перестановке контрактов перед экспирацией).\nИспользуется метод `Create`, в который передается символьный ключ, а также настройки инструмента.\nКласс также умеет правильно выбирать нужную экспирацию фьючерса на Московской бирже по символу, например Si.\n\n```python\nss = SecurityStore()\nsi_sec = ss.Create(key='Si', settings=\"book,finam\", leg_factor=1)\n```\n\nПример сокращений и полных кодов предопределенных инструментов\n\n|  Полный код  | Сокращение |\n| :----------: | :--------: |\n|   ED-12.16   |     ED     |\n|   Si-12.16   |     Si     |\n|   BR-1.17    |     BR     |\n|  RTS-12.16   |    RTS     |\n| USD000UTSTOM | USDRUB_TOM |\n\n\n\n\nБиблиотека модулей\n==================\n\n## Источник котировок `ModMcast`\n\nВнутри представляет собой набор стримов, соответствующих потокам FAST, и набор расширений-обработчиков.\nСтандартные расширения `ModMcastBook` (собирает стакан из обновлений) и `ModMcastTrades` (записывает последнюю сделку) активируются при наличии флагов `Flags::BookAggregate` и `Flags::Trades` соответственно.\nМодуль самостоятельно выбирает стрим и обработчик для каждого инструмента состояния, на основе параметров `flags` и `session_id`.\n\nДля правильной обработки котировок, следует понимать, в какой момент данные валидные.\nЗапуску конвейера предшествуют следующие события:\n\n1. В FAST поток приходит новое сообщение\n2. Декодировано первое обновление для одного из инструментов\n3. Состояние блокируется\n4. Применяются все последующие обновления\n5. Очередь сообщений пуста\n6. Запуск конвейера\n7. Для каждого инструмента, если время последнего обновления (`trd_ts`, `book_ts`) не равно 0, то данные валидные \n8. Разблокировка состояния\n\nВ конце работы обязательно вызвать `Stop`.\nХорошей практикой будет запоминать в модуле стратегии время последнего обновления инструмента, чтобы пересчитывать только то, что изменилось.\n\n### Расширения\n\nДля каждого стрима на каждый инструмент создается обработчик, на который навешивается некоторое количество расширений. Расширения бывают встроенные (стакан, сделки), под их состояние заведены специальные переменные `book_*`  и `trd_*` и внешние, чьи значения хранятся в словаре `extensions`.\n\n##### Низкоуровневые структуры\n\n```c++\nenum class FastUpdateAction\n{\n\tNew = 0,\n\tChange = 1,\n\tDelete = 2\n};\n\nenum class FastEntryType  // может быть расширена по запросу\n{\n\tBid = '0',\n\tAsk = '1',\n\tTrade = '2',\n\tEmpty = 'J', \n};\n\nstruct FastBookUpdate\n{\n\tFastUpdateAction action;\n\tFastEntryType type;\n\tint64_t ts;  // время в миллисекундах\n\tuint64_t level;  // для ФОРТС: уровень в стакане, отсчет с 1\n\tuint64_t rpt_seq;  // номер инкрементального обновления, должен увеличиваться на 1\n\tint64_t px;  // цена в пунктах\n\tint64_t qty;\n};\n\nclass FastBook  // приведу только несколько методов, которые могут пригодиться\n{\npublic:\n  \n\tbool Online();  // стакан валидный\n  \tvoid RecoverFree();  // говорим, что не нужна процедура восстановления в случае пропуска\n};\n```\n\n\n\nРасширение должно реализовывать интерфейс `ModMcastExtension`:\n\n* `bool OnlineImpl(FastBook* book)` по умолчанию true, если хотя бы одно раширение возвратило false, пайп не исполнится\n* `void ResetImpl(FastBook* book)` вызывается при начальной инициализации, при всех очистках стакана и клирингах\n* `void UpdateImpl(FastBook* book, FastBookUpdate\u0026 update)` вызывается на каждое инкрементальное обновление, стакан еще не готов\n* `void UpdateEndImpl(FastBook* book)` вызывается в конце тика, в этотм момент стакан уже собран, можно его использовать\n\n```python\nmcast = ModMcast([])\n```\n\n### Параметры конструктора\n\n|    Название    |            Тип            | Описание                              |\n| :------------: | :-----------------------: | :------------------------------------ |\n| **extensions** | _List[ModMcastExtension]_ | список расширений (может быть пустой) |\n\nЗависит от:\n\n* `state.security.isin`\n* `state.security.instr_id`\n* `state.security.session_id`\n* `state.security.book_depth`\n\nМодифицирует:\n\n* `state.signal.*`\n* `state.security.book_*`\n* `state.security.trd_*`\n\n## Источник сделок `ModFinam`\n\nЗагружает сделки с выбранным таймфреймом и заданной глубиной (в днях). Выбирает инструменты по параметрам `flags` содержащим `FlagBoost`. Использует `finam_market` и `finam_quote`.\nСледует вызывать в самом начале, блокирует состояние на весь период работы функции `Start`.\n\n```python\nfinam = ModFinam(\n    timeframe=FinamTimeFrame.Min1,\n    day_shift=1,\n    bar_open=True\n)\n```\n\n### Параметры конструктора\n\n|   Название    |       Тип        | Описание                                 |\n| :-----------: | :--------------: | :--------------------------------------- |\n| **timeframe** | _FinamTimeFrame_ | таймфрейм из перечисления `FinamTimeFrame` |\n| **day_shift** |     _Int64_      | глубина истории в торговых днях          |\n| **bar_open**  |      _Bool_      | если истина, то берется цена открытия бара, иначе - закрытия |\n\nЗависит от:\n\n* `state.security.flags`\n* `state.security.finam_*`\n\nМодифицирует:\n\n* `state.signal.ts`\n* `state.security.trd_*`\n\n## Тайм менеджер `ModHours`\n\nХранит список торговых правил `ModHoursRule`, привязанных к определенному времени суток.\nКаждое правило состоит из следующих полей:\n\n* Время применения, строка `HH:MM:SS`\n* Направление `Side`\n* Действие `Action`\n\nЕсли приоритет действия сигнала меньше текущего правила, то назначается действие соответствующее правилу.\nНапример, сигнал `Open`, а правило `ForceClose` для `All`, тогда итоговый сигнал будет `ForceClose`.\n\nМодуль также имеет метод `ForceClose` для принудительного закрытия позиций. \nПараметр `min_period` задает минимальный перид в миллисекундах, в течение которого не будет применено следующее правило.\n\n```python\nhours = ModHours([\n\tModHoursRule(\"11:00:00\", Side.All, Action.Open),\n\tModHoursRule(\"22:00:00\", Side.All, Action.Close),        \n\tModHoursRule(\"23:00:00\", Side.All, Action.ForceClose)\n])\n```\n\n### Параметры конструктора\n\n| Название  |         Тип          | Описание     |\n| :-------: | :------------------: | :----------- |\n| **rules** | _List(ModHoursRule)_ | Набор правил |\n\n\nЗависит от:\n\n* `state.signal.ts`\n* `state.signal.side`\n* `state.signal.action`\n\nМодифицирует:\n\n* `state.signal.side`\n* `state.signal.action`\n\n## Генератор ордеров `ModOrderGen`\n\nПреобразует сигнал в набор ордеров, в соответствии с коэффициентами ног. Если ордера в сигнале уже есть, то ничего не делает.\nДля случаев `Close` и `ForceClose` с направлением `All` выбирает направление закрытия позиции по текущему инструменту.\n\n```python\norder_gen = ModOrderGen(lot_size=50)\n```\n\n### Параметры конструктора\n\n|   Название   |   Тип   | Описание              |\n| :----------: | :-----: | :-------------------- |\n| **lot_size** | _Int64_ | Общий лот для ордеров |\n\nЗависит от:\n\n* `state.signal.action`\n* `state.orders.empty`\n\nМодифицирует:\n\n* `state.orders`\n* `state.signal.order.qty`\n* `state.signal.order.side`\n* `state.signal.order.security` (указатель)\n\n## Менеджер позиций `ModPosLimit`\n\nОграничивает объем ордеров в соответствии с лимитами по позиции и открытым ордерам.\n\n```python\npos_limit = ModPosLimit(max_pos=100)\n```\n\n### Параметры конструктора\n\n|  Название   |   Тип   | Описание                    |\n| :---------: | :-----: | :-------------------------- |\n| **max_pos** | _Int64_ | Максимальный размер позиции |\n\nЗависит от:\n\n* `state.signal.action`\n* `state.signal.order.side`\n* `state.signal.order.qty`\n* `state.signal.order.security.leg_factor`\n* `state.signal.order.security.pos`\n* `state.signal.order.security.pos_buy_limit`\n* `state.signal.order.security.pos_sell_limit`\n\nМодифицирует:\n\n* `state.signal.order.qty`\n\n## Исполнение по маркету `ModMarketPx`\n\nНазначает цену для ордеров в сигнале. Также корректирует объем исходя из текущей ликвидности и минимально допустимого лота.\n\n```python\nmarket_px = ModMarketPx(min_qty=1, slippage=100)\n```\n\n### Параметры конструктора\n\n|   Название   |   Тип   | Описание                   |\n| :----------: | :-----: | :------------------------- |\n| **min_qty**  | _Int64_ | Минимальный размер позиции |\n| **slippage** | _Int64_ |                            |\n\nЗависит от:\n\n* `state.signal.order.side`\n* `state.signal.order.qty`\n* `state.signal.order.security.leg_factor`\n* `state.signal.order.security.bid`\n* `state.signal.order.security.ask`\n* `state.signal.order.security.book`\n\nМодифицирует:\n\n* `state.signal.order.qty`\n* `state.signal.order.px`\n\n## Уравнитель `ModEqualizer`\n\nВыравнивает объем в ордерах в соответствии с коэффициентом ног.\n\n```python\nequalizer = ModEqualizer()\n```\n\nЗависит от:\n\n* `state.signal.order.qty`\n* `state.signal.order.security.leg_factor`\n\nМодифицирует:\n\n* `state.signal.order.qty`\n\n## Балансировщик `ModBalancer`\n\nИспользуется только для синтетических инструментов. \nОбнаруживает разбалансировку синтетической позиции и генерирует соответствующие ордеры, с учетом предыдущего сигнала.\nРаботает только если нет неисполненных лимиток.\n\n```python\nbalancer = ModBalancer()\n```\nЗависит от:\n\n* `state.signal.action`\n* `state.signal.order.side`\n* `state.signal.order.qty`\n* `state.signal.order.security.leg_factor`\n* `state.signal.order.security.pos`\n* `state.signal.order.security.pos_buy_limit`\n* `state.signal.order.security.pos_sell_limit`\n\nМодифицирует:\n\n* `state.orders`\n* `state.signal.order.qty`\n* `state.signal.order.side`\n* `state.signal.order.security` (указатель)\n\n## Исполнение по сделкам `ModTrdPx`\n\nПодходит для тестирования по сделкам. Назначает ордерам цену последней сделки плюс (минус) заданный в пунктах спред.\n\n```python\ntrdPx = ModTrdPx(spread_pt=2)\n```\n\n### Параметры конструктора\n\n|   Название    |   Тип   | Описание        |\n| :-----------: | :-----: | :-------------- |\n| **spread_pt** | _Int64_ | Спред в пунктах |\n\nЗависит от:\n\n* `state.signal.order.side`\n* `state.signal.order.security.trd_px`\n\nМодифицирует:\n\n* `state.signal.order.px`\n\n## Закрытие по времени `ModTimeClose`\n\nРаботает раз в минуту, суммирует \"протухшие кусочки\" позиции по каждому инструменту, и делает из них ордера, перезаписывая имеющиеся.\nДелает сигнал `ForceClose`.\n\n```python\ntime_close = ModVirtExec(timeout=1000*60)\n```\n\n### Параметры конструктора\n\n|  Название   |   Тип   | Описание                 |\n| :---------: | :-----: | :----------------------- |\n| **timeout** | _Int64_ | время жизни позиции в мс |\n\n\nЗависит от:\n\n* `state.signal.ts`\n* `state.signal.order.security.leg_factor`\n* `state.signal.order.security.pos`\n* `state.signal.order.security.pos_buy_chunks`\n* `state.signal.order.security.pos_sell_chunks`\n\nМодифицирует:\n\n* `state.signal.action`\n* `state.orders`\n* `state.signal.order.qty`\n* `state.signal.order.side`\n* `state.signal.order.security` (указатель)\n\n## Трейдер `ModVirtExec`\n\nВиртуальная торговля, с рандомным исполнением (заявка может исполниться, а может повиснуть). Журналирует все сделки в файл.\n\n```python\nvirt_exec = ModVirtExec(\n    trade_timeout=1000, \n    cancel_timeout=3000, \n    trades_file='trades.csv', \n    random_unfill=False\n)\n```\n\n### Параметры конструктора\n\n|      Название      |   Тип    | Описание                                 |\n| :----------------: | :------: | :--------------------------------------- |\n| **trade_timeout**  | _Int64_  | отправление ордеров не чаще чем раз в промежуток (в мс) |\n| **cancel_timeout** | _Int64_  | время до отмены ордера (в мс)            |\n|  **trades_file**   | _String_ | путь к выходному файлу со сделками       |\n| **random_unfill**  |  _Bool_  | случайно не исполняет ордера (имитирует висящие лимитки) |\n\nЗависит от:\n\n* `state.signal.ts`\n* `state.signal.action`\n* `state.signal.order.*`\n\nМодифицирует:\n\n* `state.signal.*`\n* `state.signal.order.security.pos_*`\n\n## Трейдер `ModPlazaExec`\n\nКоннектор к плазе.\n\n```python\nplaza = ModPlazaExec(trade_timeout=1000, cancel_timeout=3000, comment='strategyName', client_code='CODE', port=1234)\n```\n\n### Параметры конструктора\n\n|      Название      |   Тип    | Описание                                 |\n| :----------------: | :------: | :--------------------------------------- |\n| **trade_timeout**  | _Int64_  | отправление ордеров не чаще чем раз в промежуток (в мс) |\n| **cancel_timeout** | _Int64_  | время до отмены ордера (в мс)            |\n|    **comment**     | _String_ | Комментарий к выставляемым ордерам       |\n|  **client_code**   | _String_ | зависит от счета                         |\n|      **port**      | _Short_  | зависит от инстанса роутера, по умолчанию 4001 |\n\nЗависит от:\n\n* `state.signal.ts`\n* `state.signal.action`\n* `state.signal.order.*`\n\nМодифицирует:\n\n* `state.signal.*`\n* `state.signal.order.security.pos_*`\n\n\n## Точка восстановления позиции `ModRestorePoint`\n\nСохраняет текущую позицию при отсутствии висящих лимиток и загружает при старте приложения.\n\n```python\nrestore = ModRestorePoint('restore_point.csv')\n```\n\n### Параметры конструктора\n\n| Название |   Тип    | Описание                             |\n| :------: | :------: | :----------------------------------- |\n| **path** | _String_ | путь к файлу с точкой восстановления |\n\nЗависит от:\n\n- `state.security.class_code`\n- `state.security.full_code`\n- `state.security.pos_buy_limit`\n- `state.security.pos_sell_limit`\n- `state.security.leg_factor`\n\nМодифицирует:\n\n- `state.security.pos`\n\n## ZeroMQ-based модули\n\nВсе взаимодействие построено по схеме PUB-SUB через посредника. Решается проблема обнаружения, а также временного отключения одной из сторон. Адреса посредника на M1:\n\n* `tcp://127.0.0.1:42001` для отправки сообщений\n* `tcp://127.0.0.1:42002` для получения сообщений\n\n### Получение сигналов `ModZmqSignalSub`\n\n```python\nsig_sub = ModZmqSignalSub(name='signal_name', forwarder_rx=\"tcp://127.0.0.1:42002\")\n```\n\n#### Параметры конструктора\n\n|     Название     |   Тип    | Описание                                 |\n| :--------------: | :------: | :--------------------------------------- |\n|     **name**     | _String_ | уникальное имя сигнала (используется как топик сообщения) |\n| **forwarder_rx** | *String* | zmq адрес получения                      |\n\nМодифицирует:\n\n- `state.signal`\n\n### Отправка сигналов `ModZmqSignalPub`\n\n```python\nsig_pub = ModZmqSignalPub(name='signal_name', forwarder_tx=\"tcp://127.0.0.1:42001\")\n```\n\n#### Параметры конструктора\n\n|     Название     |   Тип    | Описание                                 |\n| :--------------: | :------: | :--------------------------------------- |\n|     **name**     | _String_ | уникальное имя сигнала (используется как топик сообщения) |\n| **forwarder_tx** | *String* | zmq адрес отправки                       |\n\nЗависит от:\n\n- `state.signal`\n\n### Получение котировок `ModZmqSecuritySub`\n\nПодписывается на инструменты с флагом `zmq`, топик `SEC_\u003cClassCode\u003e_\u003cFullCode\u003e`. Запускает конвеер на каждый тик.\n\n```python\nsec_sub = ModZmqSecuritySub(forwarder_rx=\"tcp://127.0.0.1:42002\")\nsec_sub.Start(state, [])\nsec_sub.Stop()\n```\n\n#### Параметры конструктора\n\n|     Название     |   Тип    | Описание            |\n| :--------------: | :------: | :------------------ |\n| **forwarder_rx** | *String* | zmq адрес получения |\n\nЗависит от:\n\n- `state.securities.settings`\n- `state.securities.class_code`\n- `state.securities.full_code`\n\nМодифицирует:\n\n* `state.securities.book_*`\n* `state.securities.trd_*`\n* `state.signal.ts`\n\n### Отправка котировок `ModZmqSecurityPub`\n\nДля всех инструментов с флагом `zmq` рассылаются обновления с  топиком `SEC_\u003cClassCode\u003e_\u003cFullCode\u003e`.\n\n```python\nsec_sub = ModZmqSecurityPub(forwarder_tx=\"tcp://127.0.0.1:42001\")\n```\n\n#### Параметры конструктора\n\n|     Название     |   Тип    | Описание           |\n| :--------------: | :------: | :----------------- |\n| **forwarder_tx** | *String* | zmq адрес отправки |\n\nЗависит от:\n\n- `state.securities.settings`\n- `state.securities.class_code`\n- `state.securities.full_code`\n\n\n- `state.securities.book_*`\n- `state.securities.trd_*`\n\n### Исполнение `ModZmqExec`\n\nРеализует торговлю по алгоритму, аналогичному `ModVirtExec`: постановка лимиток, отмена через таймаут. Дополнительный параметр `placed_timeout` введен для обработки случая, когда принимающая сторона недоступна, если через заданный промежуток времени не пришло подтверждение со статусом `OrderStatus::Placed`, считается, что ордер отвергнут.\n\nБлагодаря дизайну PUB-SUB можно исполнять ордера сразу на нескольких удаленный экзекьюторах, например реализовывать арбитражные стратегии.\n\n```python\nexecutor = ModZmqExec(\n    comment=\"test\",\n    place_timeout=5000,\n    cancel_timeout=3000,\n    forwarder_rx=\"tcp://127.0.0.1:42002\",\n\tforwarder_tx=\"tcp://127.0.0.1:42001\"\n)\nexecutor.Start(state, [])\nexecutor.Stop()\n```\n\n#### Параметры конструктора\n\n|      Название      |    Тип    | Описание                             |\n| :----------------: | :-------: | :----------------------------------- |\n|    **comment**     | *String*  | комментарий к ордерам                |\n| **place_timeout**  | *Integer* | таймаут ответа о постановке ордера   |\n| **cancel_timeout** | *Integer* | таймаут отмены неисполненных лимиток |\n|  **forwarder_rx**  | *String*  | zmq адрес получателя                 |\n|  **forwarder_tx**  | *String*  | zmq адрес отправителя                |\n\nЗависит от:\n\n- `state.orders`\n- `state.signal.ts`\n- `state.securities.full_code`\n\n\n- Модифицирует:\n  - `state.signal.*`\n  - `state.signal.order.security.pos_*`\n\n## Инкрементальные индикаторы\n\nБазовый класс `RollingBase` для имплеметации индикаторов со скользящим окном. \nДоступные для переопределения методы: `OnPush`, `OnPop`, `calculate`.\n\nСтандартные индикаторы (параметры: `window`, `timeframe`):\n\n* `RollingMean`\n* `RollingStd`\n* `RollingMin`\n* `RollingMax`\n* `RollingShift`\n* `RSI`\n\nИспользование:\n\n1. Проинициализировать (в конструкторе).\n2. Обновлять внутреннее значение на каждой итерации с помощью `Update` (принимает `ts` время в мс и `value` вещественное число).\n3. Когда индикатор полностью \"прогреется\", метод `Good` будет возвращать истину.\n4. Получить конечное значение можно с помощью `calculate`. (У некоторых индикаторов есть вспомогательные методы, например `mean` у `RollingStd`, или `previous` у `RollingMinMax`).\n\n```python\n\toil_rsi = RSI(window, timeframe)\n\toil_sec = state.securities[1]\n\toil_rsi.Update(state.signal.ts, oil_sec.trd_px)\n\tif oil_rsi.Good():\n\t\trsi = oil_rsi.calculate()\n\t\tprint('RSI: %.f', rsi)\n```\n\n\n\n\nУстановка\n==================\n\n### Откуда качать\n\n* cgate ftp://ftp.moex.com/pub/FORTS/Plaza2/CGate/ (x64)\n* libzmq https://github.com/zeromq/libzmq\n* conda https://www.continuum.io/downloads (x64)\n* pybind11 https://github.com/pybind/pybind11.git\n\n### Прописать переменные окружения\n\n* `%CGATE_HOME%` (C:\\Moscow Exchange\\SpectraCGate2)\n* `%CPP_DEPS%` (C:\\deps)\n* `%CONDA_HOME%` (C:\\Anaconda3)\n\n### Проверить зависимости в `%PATH%` (приложения будут искать здесь библиотеки)\n\n* `%CGATE_HOME%\\bin`\n* `%CONDA_HOME%`\n* `%CONDA_HOME%\\Library\\bin`\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm-kus%2Fmpipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fm-kus%2Fmpipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm-kus%2Fmpipe/lists"}