В статье рассматривается механика внешних (серверных) стоп-ордеров в OsEngine: как поддержка включается в коннекторе, какие коннекторы её имеют, как устроен жизненный цикл внешнего стопа и как он хранится внутри позиции.
До этого момента все стопы внутри платформы были внутренние. Теперь у нас появился механизм для выставления внешних. Пока в тестовом режиме и не для всех коннекторов. После бета-тестирования и как устоится – данный вид стопов будет проброшен по большинству популярных коннекторов в OsEngine.
Внутренний стоп-приказ хранится внутри OsEngine: движок сам отслеживает цену последней сделки и отправляет ордер на рынок только после достижения цены активации.
Внешний стоп-приказ сразу отправляется на биржу и хранится там. За ценой активации следит биржа и сама порождает заявку при касании уровня. Основные следствия:
стоп срабатывает, даже когда OsEngine выключен или потерял связь с сервером;
исключается задержка между касанием уровня и выставлением заявки — активацию выполняет биржа;
стоп виден в терминале брокера как обычный стоп-ордер.
Каждый коннектор в OsEngine декларирует свои возможности через интерфейс IServerPermission. За внешние стопы отвечает флаг StopOrdersIsSupport:
public bool StopOrdersIsSupport
{
get { return true; } // коннектор поддерживает внешние стопы
}
Если флаг включён, коннектор маршрутизирует ордера с типом StopLimit и StopMarket не в обычный торговый поток, а в специализированный API стоп-ордеров биржи. Если флаг выключен, все методы выставления внешних стопов автоматически откатываются на внутренние — код робота при этом не меняется.
На уровне ядра признаком внешнего стопа являются два значения перечисления OrderPriceType — StopLimit и StopMarket — в сочетании с полем StopPrice (цена активации) у ордера.
Внешние стоп-ордера, на момент выхода поста, поддерживает коннектор Т-Инвестиции. Реализация построена на gRPC-сервисе StopOrdersService Tinkoff Invest API:
PostStopOrder — выставление стоп-ордера (stop-limit и stop-market);На остальных коннекторах флаг StopOrdersIsSupport выключен, и внешние стопы прозрачно работают как внутренние. Архитектура открыта: чтобы подключить внешние стопы на новом коннекторе, достаточно включить флаг и обработать типы StopLimit / StopMarket при отправке, отзыве и приёме статусов ордеров.
Жизненный цикл внешнего стопа:
1) Робот или пользователь выставляет стоп-ордер (из кода — методы *OnServer, из интерфейса — чекбокс «Серверный стоп ордер» в окнах открытия, закрытия и доливки позиции).
2) Ордер уходит на биржу и переходит в статус Active. Он хранится на бирже, OsEngine следит за его статусом через поток событий.
3) Цена касается уровня активации — биржа активирует стоп и порождает дочернюю заявку: Limit для stop-limit, Market для stop-market.
4) Сам стоп-ордер завершает жизнь и приходит в робота со статусом Cancel — это штатное поведение, а не отмена пользователем.
5) Исполнение приходит на дочерней заявке: сделки (MyTrade) привязываются к ней.
Дополнительные свойства механики:
Позиция в OsEngine хранит два списка ордеров: OpenOrders (ордера открытия и доливки) и CloseOrders (ордера закрытия). Внешний стоп — обычный объект Order в одном из этих списков, с характерным набором полей:
Ордера в списках лежат в хронологическом порядке: сначала стоп, после активации в конец списка дописывается дочерняя заявка. Типичное состояние позиции после срабатывания стопа на вход и выставления стопа на выход:
Типичное состояние позиции после срабатывания стопа на вход и выставления стопа на выход выглядит так. В списке OpenOrders лежат два ордера: первым идёт стоп-лимит на вход в статусе Cancel — биржа активировала его и погасила; следом — исполненная дочерняя лимитная заявка в статусе Done. Дочерняя заявка ссылается на стоп через поле ParentOrderNumberMarket, а стоп — на дочернюю заявку через поле ChildOrderNumberMarket. В списке CloseOrders в этот момент находится один активный стоп-лимит на закрытие позиции.
Связывание выполняет ядро журнала (PositionController): когда от сервера приходит ордер с заполненным ParentOrderNumberMarket, ядро находит стоп-родителя в позициях и прикрепляет дочерний ордер к той же позиции. Поэтому открытие и закрытие позиции внешними стопами корректно отражается в журнале, на графике и в расчёте прибыли без участия робота.
Поля StopPrice, ParentOrderNumberMarket и ChildOrderNumberMarket сериализуются вместе с ордером, поэтому связи «стоп — дочерняя заявка» переживают перезапуск приложения.
Все методы относятся к торговой вкладке робота BotTabSimple.
Открытие позиции:
Доливка к открытой позиции:
Закрытие позиции:
Отмена ордеров:
На серверах без поддержки и в тестере все эти методы автоматически откатываются на внутренние стопы, поэтому один и тот же робот работает везде без изменений кода. Проверить поддержку можно свойством вкладки ServerIsSupportStopOrders.
Готовый пример — робот ServerStopOrdersSample (папка Robots/TechSamples): выставляет внешний стоп-лимит на покупку, ожидает активации заданное число свечей, перевыставляет ордер по актуальной цене и после открытия позиции сопровождает её внешним стопом на закрытие.
Удачных алгоритмов!
Комментарии открыты для друзей!

https://smart-lab.ru/company/os_engine/blog/1024149.php
Скачать OsEngine: https://github.com/AlexWan/OsEngine
Официальная поддержка OsEngine в MAX: https://max
Канал Научный трейдинг в MAX: https://max.ru/
Поддержка OsEngine в Телеграм: https://t.me/osengine_official_support
Канал Научный трейдинг (Bad Quant) в Телеграм: https://t.me/bad_quant