Данная статья является продолжением ранее начатого цикла о создании торгового эксперта (см. 61 и 63 номера журнала ForTrader.org). К сожалению, как меня, так и многих читателей, работа в данном направлении была приостановлена на несколько месяцев. Причин для того было достаточно много, но главное, что я снова возвращаюсь к этой работе.
Итоги проделанной работы
Тот короткий период тестирования, который имел место быть, смог продемонстрировать некоторую живучесть идеи. Прибыль составила незначительную величину, однако более важно то, что не был получен убыток. В этой статье я постараюсь предпринять некоторые действия для улучшения результативности работы программы. Но для начала я попробую уточнить некоторые непонятные места в коде, а также исправить незначительные ошибки.
Ошибки и неясные места в коде экспрта
Строка 18 кода:
Pause_Of_Trade_Try = 2; //пауза между попытками открытия ордера (в секундах)
Здесь пауза указывается не в секундах, а в десятых долях секунды, т.е. 2 = 0.2 секунды.
Строки 118..119 (и аналогичный код далее в роботе):
return(OrderTicket());
break; //выходим из цикла
Здесь может быть не совсем понятно, для чего стоит break, ведь мы уже вышли из цикла, вернув значение функции, а значит, этот оператор не выполняется. Это скорее просто моя привычка, везде, где нужно выходить из цикла, ставить оператор break. Но, если мне не изменяет память, я сталкивался с языками программирования, где цикл продолжает выполняться без break, даже после return. Ну и все программисты знают, что, к примеру, в switch происходит «проваливание» на следующий оператор, если не поставить breal в языке C. Ставить его конкретно здесь совсем не обязательно.
Строка 169 (и тело функции sl):
double sl(int sl_value, int type, string symb=»NONE», int rmode=1) {
Здесь может быть не совсем понятно, зачем нужен параметр rmode и почему в зависимости от его значения, функция считает цены SL для покупок либо от цены Ask (при значении 1), либо от цены Bid (при значении 2), и для продаж либо от Bid, либо от Ask, соответственно.
Дело в том, что часто возникают споры, от какой же цены нужно считать цену StopLoss. Есть мнение, что при получении стопа, мы должны получать ровно то значение убытка, которое ждём (т.е. ставим в параметре SL=100 и получаем по стопу 100 пунктов убытка). А есть мнение, что нужно считать стоп так, чтобы захватывать спред, это актуально, когда спред не позволит выставить стоп на указанном расстоянии. Во втором случае мы получаем стоп, равный 100+<спред> пунктов.
Я чаще использую первый вариант, и именно для такого варианта и работает функция при rmode=1 (т.е. по умолчанию). Введением этого параметра я демонстрирую, что в курсе обеих точек зрения, что в целом не навязываю ни одну из них (если вы сторонник второго варианта, просто поменяйте 1 на 2 в коде), хотя лично я использую в своей работе именно первый вариант.
Остальные участки кода мне видятся достаточно логичными и «прозрачными», но, скорее всего, в перспективе я буду пояснять и их.
Нововведения в торговый эксперт
Первый результат, хоть и за короткое время, получен. Говорить о смене/оптимизации алгоритма поиска торгового сигнала пока рано. А потому добавим пару новых функций. Первая из них – трейлинг стоп. Для этого я возьму ставшую уже стандартной функцию для многих своих роботов, и немного изменю её (чтобы было можно использовать одновременно на разных символах). Функция имеет несколько входных параметров, которые в коде представлены как:
//+———————————————————————-+
//+ трейлинг-стоп +
extern bool UseTrailing = true; //включение/выключение T-SL
extern int TrailingStop = 50; // Фиксированный размер трала
extern int TrailingStep = 1; // Шаг трала
//+ трейлинг-стоп +
//+———————————————————————-+
Функция для работы на текущем символе выглядит так:
//+——————————————————————————————————————-+
//| трейлинг стоп лосс |
void T_SL() {
if(!UseTrailing) return;
int i = 0;
for(i=0; i<OrdersTotal(); i++) {
if(!(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))) continue;
if(OrderSymbol() != Symbol() || OrderMagicNumber()!=Magic_Number) continue;
if(OrderType()==OP_BUY) {
if(NormalizeDouble(Bid-OrderOpenPrice(),Digits)>NormalizeDouble(TrailingStop*Point,Digits)) {
if(NormalizeDouble(OrderStopLoss(),Digits)<NormalizeDouble(Bid-(TrailingStop+TrailingStep-1)*Point,Digits))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Bid-TrailingStop*Point,Digits), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(Bid-OrderOpenPrice(),Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_BUY)
if(OrderType()==OP_SELL) {
if(NormalizeDouble(OrderOpenPrice()-Ask,Digits)>NormalizeDouble(TrailingStop*Point,Digits)) {
if(NormalizeDouble(OrderStopLoss(),Digits)>NormalizeDouble(Ask+(TrailingStop+TrailingStep-1)*Point,Digits))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Ask+TrailingStop*Point,Digits), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(OrderOpenPrice()-Ask,Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_SELL)
} //end for(i=0; i<OrdersTotal(); i++)
} //end void T_SL()
//| трейлинг стоп лосс |
//+——————————————————————————————————————-+
Для нашего робота её нужно привести к виду, при котором она сможет обрабатывать все сделки (с заданным магическим числом) всех инструментов:
//+——————————————————————————————————————-+
//| трейлинг стоп лосс |
void T_SL() {
if(!UseTrailing) return;
string symb=»»;
int i = 0;
double ask=0.0, bid=0.0, dig=0.0, pp=0.0;
for(i=0; i<OrdersTotal(); i++) {
if(!(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))) continue;
if(OrderMagicNumber()!=Magic_Number) continue;
symb=OrderSymbol();
pp=MarketInfo(symb,MODE_POINT);
dig=MarketInfo(symb,MODE_DIGITS);
ask=MarketInfo(symb,MODE_ASK);
bid=MarketInfo(symb,MODE_BID);
if(OrderType()==OP_BUY) {
if(NormalizeDouble(bid-OrderOpenPrice(),dig)>NormalizeDouble(TrailingStop*pp,dig)) {
if(NormalizeDouble(OrderStopLoss(),dig)<NormalizeDouble(bid-(TrailingStop+TrailingStep-1)*pp,dig))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(bid-TrailingStop*pp,dig), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(Bid-OrderOpenPrice(),Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_BUY)
if(OrderType()==OP_SELL) {
if(NormalizeDouble(OrderOpenPrice()-ask,dig)>NormalizeDouble(TrailingStop*pp,dig)) {
if(NormalizeDouble(OrderStopLoss(),dig)>NormalizeDouble(ask+(TrailingStop+TrailingStep-1)*pp,dig))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(ask+TrailingStop*pp,dig), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(OrderOpenPrice()-Ask,Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_SELL)
} //end for(i=0; i<OrdersTotal(); i++)
} //end void T_SL()
//| трейлинг стоп лосс |
//+——————————————————————————————————————-+
После этого остаётся только поместить вызов функции внутрь функции start() (см. в конечном варианте).
Вторая функция, которая может оказаться полезной, и которую я решил включить в робота – подача звукового сигнала при открытии сделки торговым экспертом. Это будет совсем маленькое дополнение, так что описывать подробно ее не буду, посмотреть полный код робота вы можете здесь.
Заключение
В следующей версии эксперта появятся более интересные, нежели сегодня, функции. Они будут нацелены на контроль рисков и управление объёмами торговых операций, ну а пока последим. Но нам покажет эта версия.