Торговый алгоритм.Открытие позиции происходит выставлением пары лучших заявок на покупку и продажу и ожидание их исполнения. При этом заявки удерживаются лучшими, но не могут сблизиться более чем на определенную величину (чтобы биржевая комиссия не перекрыла прибыль от сделки). После исполнения одно из заявок вторая снимается. На открывшуюся позицию выставляется тейк-профит и стоп-лосс. Тейк-профит удерживается на границе спреда (при этом его величина не может быть меньше чем изначально заданная). Величина стоп-лосса линейно меняется со временем, что позволяет уменьшить время простоя робота. После закрытия «перезапуск алгоритма», т.е. опять выставляется пара заявок.
Данный алгоритм представляет скорее не торговую стратегию, а ее часть. И для более успешной работы шаблон можно дополнить, добавив фильтры волатийности и тренда. Либо в зависимости от тренда задавать направление позиции (лонг или шорт).Реализация алгоритма в RobotLab.Рассмотрим реализацию описанного выше алгоритма, с помощь визуального редактора роботов RobotLab. Готовая схема включает в себя следующие логические блоки:

Инициализация.В данном блоке значения внешних настроек (аргументов) присваивается внутренним переменным. Вообще говоря, делать это не обязательно, но русские названия из нескольких слов загромождают выражения в конструкторе, а если использовать короткие названия аргументов, то их смысл будет малопонятен в форме настроек.
Описание переменных:
Account - в данной переменной будет храниться значение счета, с которого будет производиться торговля.
Instrument - в данной переменной будет храниться значение инструмента, по которому будут открываться и закрываться позиции.
MinSpread - в данной переменной будет храниться значение минимального расстояния спреда.
TakeProfit - в данной переменной будет храниться значение тейк-профита
Lots - в данной переменной будет храниться значение кол-ва лотов, которым будет производиться торговля.
StopLimit - в данной переменной будет храниться конечное значение стоп-лоса.
StartStopLevel - в данной переменной будет храниться начальное значение уровня стоп-лоса.
StopPeriod - в данной переменной будет храниться значение промежутка времени, за которое будет изменяться стоп-лосс с начального до конечного.
PriceStep - в данной переменной будет храниться значение шага цены (пипс).
Запуск спредера.В данном блоке мы выставляем заявки открывающие позицию.
Для выставления их на нужные ценовые уровни получается стакан (OrderBook) и берутся цены лучше покупки и продажи. Далее проверяется размер спреда, если он больше чем величина: «Максимальное_схождение» + 2 шага цены, то для цены выставления заявок берутся цены бида и аска ± шаг цены инструмента (т.е. наши заявки будут гарантировано лучшими и первыми исполнятся в случае появления на бирже рыночной заявки). Если же это не так, то за цены заявок берется середина спреда ± половина «Максимального_схождения». Далее по вычисленным ценам (sellPrice и buyPrice) выставляется пара заявок OrderBuy и OrderSell. После выставления заявок мы записываем в переменную OpenTime текущее время, которое потребуется для вычисления величины стопа.
Удержание заявок лучшими.В данном блоке проверяется являются ли наши заявки лучшими. А также происходит их перевыставление в случае необходимости.
Для проверки нам необходимо запросить стакан и вычесть из него свои заявки. Вычитаение своих заявок проводится в блоксхеме «Границы спреда без своих заявок».
Рассмотрим логику ее работы на примере покупки:Если цена лучшей заявки совпадает с ценой заявки OrderBuy, то вычитаем оставшееся число лотов OrderBuy из объема заявок по лучшей цене, в случае нулевого результата наша заявка будет являться лучшей (и лучшая цена без учета нашей заявки – вторая строчка стакана (OrderBook.Buy(1).Price)), в случае ненулевого – нет (лучшая цена – цена нашей заявки или первая строчка стакана (OrderBook.Buy(0).Price)).
Эта проверка осуществляется с помощью следующего логического выражения в условии: ((OrderBook.Buy(0).Volume - OrderBuy.RemainingLots) = 0) and ((OrderBook.Buy(0).Price = OrderBuy.Price))
После определения лучших цен покупки и продажи (BestBuy и BestSell) необходимо вычислить величину ближе, которой заявки не могут подойти друг к другу. Эта необходимость обусловлена наличием двух параметров отвечающих за сближение заявок: минимального схождения (для случая пары активных заявок) и тейк-профита (для случая открытой позиции). Значение вычисленной величины хранится в переменной MinProfit.
Перед перемещением заявок желательно проверить, успели они исполниться за время вычисления границ спреда, если успели, управление из блок-схемы удержания переходит дальше, если нет, начинается проверка перемещения и перемещение заявок.
Далее с помощью 2х условий мы определяем, какие заявки исполнены, а какие нет и, проверяем необходимость перемещения для каждой из заявок.
Рассмотрим логику перемещения заявок на примере перемещения заявки OrderBuy.1) Условием: (OrderBuy.Price = (OrderSell.Price - MinProfit)) and (OrderBuy.Price <= BestBuy) мы отсеиваем ситуации, при которых наша заявка не лучшая, но в то же время выставлена на расстоянии MinProfit от заявки на продажу (т.е. если мы переместим заявку еще ближе, то получим убыток, либо слишком маленькую прибыль).
2) Вычисляем новую цену заявки BuyPrice как минимальную из (BestBuy + PriceStep) (лучшая цена покупки) и (OrderSell.Price - MinProfit) (цена дороже которой мы покупать не хотим). Кроме схемы которая представлена в шаблоне получить минимальное значение из двух можно методом Math.Min((BestBuy + PriceStep),(OrderSell.Price - MinProfit)).
3) После вычисления цены BuyPrice мы сравниваем ее с текущей ценой заявки OrderBuy.Price, если они совпадают, то ничего не делаем, если отличаются, то перевыстваляем заявку OrderBuy.
4) Для первыставления заявки необходимо: подать запрос на снятие заявки, дождаться получением заявки статуса исполнена (OrderBuy.IsFilled = true) или снята (OrderBuy.Status = 3), в случае исполнения в процессе снятия ничего не делать, в случае снятия – выставить новую заявку OrderBuy по цене BuyPrice на оставшееся число лотов в старой заявке (OrderVolume = OrderBuy.RemainingLots).
Проверка исполнения заявок.После перемещения заявки (перед проверкой на стоп) надо проверить исполнены ли обе заявки или нет, если исполнены, то перейти к блоку Запуск Спредера, если нет продолжить работу.
Вычисление Стопа.Так как после того, как открыли позицию, мы не знаем сколько потребуется времени на её закрытие, мы используем уменьшение уровня стопа. Чем дольше мы находимся в открытой позиции, тем больше уровень стопа подтягивается к конечному уровню, заданному в настройках.

Закрытие по Стопу.Перед закрытием мы проверяем, не покинула ли цена коридора, в котором работал спредер, границы которого задаются уровнем стоп-лосса, а цена вычисляется по середине спреда. Если покинула – обнуляемся, если нет – переходим в схему удержания заявок после небольшой задержки, необходимой для снижения нагрузки на CPU. Вообще задержки в несколько миллисекунд лучше помещать как можно чаще, если это не идет в ущерб работы алгоритма.
