Робот.

Простейший, вариант робота работающего через Quik API с возможностью добавления к уже открытой позиции


Про код робота

Прежде чем использовать этого или подобного робота пожалуйста опробуйте и разберитесь с работой простейшего робота. Отличие этого варианта робота в том, что он позволяет совершать сделки на покупку в то время, когда уже открыта длинная позиция и сделки на продажу без покрытия (шорт) когда уже открыта короткая позиция. Таким образом можно доливаться к уже существующей позиции.

Рассмотрим систему которую я взял для примера.

В системе есть обычный вход в позицию. Buy1 и Short1 которые открывают первоначальную позицию только тогда, когда открытых позиций нет.
Выходы из позиции Sell1 и Cover1 которые безусловно полностью закрывают открытые на этот момент соответственно длинные и короткие позиции.
Кроме того, есть сигналы на доливку Buy2 и Short2 которые позволяют добавляться к уже открытой позиции.
Размер доливки точно такой, как и открываемая первоначально позиция.
Саму систему я оставил как и в простейшем варианте (напоминаю, что система взята просто для примера) и к ней добавился блок доливок
Buy2 = Cross(MA(C, 2), MA(C, 5)); Short2 = Cross(MA(C, 5), MA(C, 3));

Обращаю внимание, что сигналы доливок работают только тогда, когда позиция уже открыта!!!

Важно не забыть сдвинуть сигналы доливок на следующий бар, чтобы они, как и основные сигналы, срабатывали только после закрытия сигнального бара.
Buy2 = Ref(Buy2, -1); Short2 = Ref(Short2, -1);


Соответственно немного изменились и настройки торгуемых бумаг.
if(Name() == "LKOH") { //Символ как он называется в Ами SecCode = "LKOH"; Class = "EQBR"; Lots = 5; TickS = 0.1; // Минимальный шаг цены торгуемой бумаги Otstup = 20; // Заявка будет выставлена хуже текущей цены на Otstup рублей LimB = 20; // Максимальное число лотов позиции когда еще возможна доливка на покупку LimS = 20; // Максимальное число лотов позиции когда еще возможна доливка на шорт } else if(Name() == "SBER") { //Символ как он называется в Ами SecCode = "SBER"; Class = "EQBR"; Lots = 2; TickS = 0.01; // Минимальный шаг цены торгуемой бумаги Otstup = 1; // Заявка будет выставлена хуже текущей цены на Otstup рублей LimB = 12; // Максимальное число лотов позиции когда еще возможна доливка на покупку LimS = 6; // Максимальное число лотов позиции когда еще возможна доливка на шорт }

Как видите добавилось 2 параметра LimB и LimS. Эти параметры огранивают размер уже открытой позиции, когда еще разрешена доливка.

Теперь важно решить вопрос повторяемости сигнала. Робот сканирует рынок каждую секунду (или реже, зависит от настроек АА). Т.е. робот видит возникший сигнал каждую секунду пока не появится следующий бар. Обычные сигналы фильтруются размером текущей позиции. Т.е. если появился сигнал на открытие позиции и текущая позиция рана 0, то сигнал отправляется на сервер и в случае положительного ответа сервера записывается размер открытой позиции. При следующем сканировании сигнал на открытие будет проигнорирован т.к. размер текущей позиции уже не равен 0.
С сигналами на доливку такой трюк не пройдет т.к. размер позиции уже не равен 0, а сигнал отрабатывать надо. Таким образом один сигнал на доливку будет отправляться на сервер с периодичностью один раз в секунду до тех пор, пока размер суммарной позиции не упрется в лимит LimB или LimS.
Для того, чтобы избежать такого поведения я придумал записывать индекс бара на котором исполнена последняя доливка и при исполнении сигнала доливки проверяем не на этом ли баре была исполнена последняя доливка. Если на этом, то сигнал не исполняем. Индекс бара состоит из номера дня недели и времени бара. Соответственно теоретически может возникнуть ситуация когда предыдущая доливка была на этом же баре прошлой недели и других сделок по этой бумаге не было. В таком случае очередная доливка будет пропущена.
SdID = AS_READ_PARAM("Quik_Robot", Name(), "BarID"); // читаем индекс бара на котором последняя доливка BarID = LastValue(Ref(DayOfWeek()*1000000 + TimeNum(), -1)); // индекс предпоследнего бара ....

Немного изменилась и главная процедура робота orders(). В ней появились строки записи размера позиции при доливке и запись индекса бара
if(StrToNum(order) > 100) { iz = "исполнена"; coloriz = colorGreen; str = str + " заяка исполнена" + " ответ сервера=" + order; if(pos > 0 AND bs == "S") // закрытие лонга { AS_WRITE_PARAM("Quik_Robot", Name(), "pos", 0); AS_WRITE_PARAM("Quik_Robot", Name(), "BarID", 0); } else if(pos == 0 AND bs == "S") // шорт { AS_WRITE_PARAM("Quik_Robot", Name(), "pos", -1*Lot); AS_WRITE_PARAM("Quik_Robot", Name(), "BarID", 0); } else if(pos < 0 AND bs == "B") // закрытие шорта { AS_WRITE_PARAM("Quik_Robot", Name(), "pos", 0); AS_WRITE_PARAM("Quik_Robot", Name(), "BarID", 0); } else if(pos == 0 AND bs == "B") // лонг { AS_WRITE_PARAM("Quik_Robot", Name(), "pos", Lot); AS_WRITE_PARAM("Quik_Robot", Name(), "BarID", 0); } else if(pos > 0 AND bs == "B") // доливка к покупке { AS_WRITE_PARAM("Quik_Robot", Name(), "pos", pos + Lot); AS_WRITE_PARAM("Quik_Robot", Name(), "BarID", BarID); } else if(pos < 0 AND bs == "S") // доливка к шорту { AS_WRITE_PARAM("Quik_Robot", Name(), "pos", pos - Lot); AS_WRITE_PARAM("Quik_Robot", Name(), "BarID", BarID); } str = str + " позиция на момент исполнения " + pos; } else { iz = "не исполнена"; coloriz = colorRed; str = str + " заяка НЕ исполнена" + " ответ сервера=" + order; }

Их предназначение правильно записывать размер позиции при доливке и индекс бара BarID в файл Quik_Robot.

И последнее значимое изменение это отслеживание сигналов системы и их отправка в процедуру orders().
if (TimeFrame == Interval() AND Permit_Ticker) { if(Buy1[BarCount-1] AND pos == 0) { str = str + " Buy"; sd = "Buy"; orders("B", round(C[BarCount-1]) + Otstup, Lots); AS_WRITE_FILE("log.quik", str); } else if(Sell1[BarCount-1] AND pos > 0) { str = str + " Sell"; sd = "Sell"; orders("S", round(C[BarCount-1]) - Otstup, abs(pos)); AS_WRITE_FILE("log.quik", str); } else if(Short1[BarCount-1] AND pos == 0) { str = str + " Short"; sd = "Short"; orders("S", round(C[BarCount-1]) - Otstup, Lots); AS_WRITE_FILE("log.quik", str); } else if(Cover1[BarCount-1] AND pos < 0) { str = str + " Cover"; sd = "Cover"; orders("B", round(C[BarCount-1]) + Otstup, abs(pos)); AS_WRITE_FILE("log.quik", str); } else if(Buy2[BarCount-1] AND pos > 0 AND pos < LimB AND BarID != SdID) { str = str + " Buy (доливка)"; sd = "BuyIn"; orders("B", round(C[BarCount-1]) + Otstup, Lots); AS_WRITE_FILE("log.quik", str); } else if(Short2[BarCount-1] AND pos < 0 AND abs(pos) < LimS AND BarID != SdID) { str = str + " Short (доливка)"; sd = "ShortIn"; orders("S", round(C[BarCount-1]) - Otstup, Lots); AS_WRITE_FILE("log.quik", str); } } else { iz = "не исполнена"; coloriz = colorRed; str = str + " заяка НЕ исполнена" + " ответ сервера=" + order; }
Обратите внимание на проверку индекса бара при обработке сигналов доливки AND BarID != SdID

Все.
Забирайте код целиком.
СКАЧАТЬ КОД


Принимаются предложения по доработке. Задать вопросы по работе кода и предложения по расширению функционала можно на форуме.


Удачи.



Удачи.