Универсальный шаблон экспертов
Введение
Многие трейдеры сталкивалось с проблемой написания своих экспертов. С чего начать? Как задать в коде советника тейк-профит, стоп-лосс или трейлинг-стоп? Как проверить стратегию на работоспособность? В данной статье рассматриваются основные функции для создания экспертов. Возможно для кого-нибудь код трейлинг-стопа окажется полезным.
Переменные в советнике
Какие же переменные нам нужны в каждом советнике? Как сделать так, чтобы тестировать его только по одному из его условий входа? Как сделать так, чтобы в тестере перебирались и параметры, заданные переменными логического типа (bool)? Во-первых, я задам общий код советника, который работает на всех видах валют; советую для скорости подстроить под Ваш тип валюты. Во-вторых, 0-выключено, 1-включено (мы же хотим все параметры перебирать в оптимизаторе? В готовом коде советую сменить этот параметр, если он редко оптимизируется). В-третьих, переменные уровней (стопов) мы задаем целыми числами, а в коде, где надо, превращаем в дробные. Мы же на разных валютах торгуем, а это универсальный шаблон! В-четвертых, если параметры стопов равны нулю, то данный вид стопа не работает.
Задание переменных
Теперь начнем задавать переменные. Вначале зададим те, которые подвержены оптимизации - внешние переменные.
extern double MaxLot; extern double TakeProfit; extern double TrailingStop; extern double StopLoss; extern double MinProfit; extern double ProfitPoints; extern int Slippage=0; extern int Condition1=1; extern double LotSpliter=1.0; extern int CloseByOtherSideCondition;
Переменная MaxLot задает максимальный лот, когда мы хотим ограничить максимально используемый лот (лот также ограничен сервером, но об этом мы поговорим далее).
TakeProfit, StopLoss и TrailingStop работают в нашем коде, когда они больше нуля.
MinProfit и ProfitPoints работают, когда ProfitPoints больше нуля по принципу: цена дошла до ProfitPoints и развернулась до MinProfit.
ConditionX включает условия входа.
LotSpliter - делитель лотов, использует только часть лотов из возможных, например, 0.1 включает лоты которые в 10 раз меньше, чем можно сделать ставку на весь депозит.
CloseByOtherSideCondition закрывает ордер при возникновении условий в обратном направлении.
Зададим внутренние переменные, которые будут рассмотрены по ходу описания эксперта.
double Lot; double PP=0; double slu,sld,a,b; double tp,sl;
Код инициализации
Теперь рассмотрим то, что можно рассчитать только при запуске советника, и использовать дальше в коде.
int init() { tp=TakeProfit; sl=StopLoss; return(0); }
Мы берем значения этих переменных на случай смены стоп-уровней. Также можно сразу расчитать лот, если мы будет торговать все время одинаковой размерностью, а избыток выводить (далее в статье рассмотрен расчет лота). Можно, например, также создать выводимый комментарий с описанием эксперта или авторским правом. Делается это так:
Comment("cloud trade \n v2.0.11");
"\n" - означает переход на следующую строку отображения.
Код каркаса
Теперь мы рассмотрим код, когда нет ни единого ордера:
if(OrdersTotal()==0) { preinit(); if(U()==1) { OrderBuy(); return(0); } if(U()==2) { OrderSell(); return(0); } return(0); }
Часть из этих функций мы рассмотрим позже, а пока принцип: проводим инициализацию параметров, проверяем, нет ли условий на вход, входим по условию.
Теперь рассмотрим код, когда уже есть один ордер:
if(OrderType()==OP_BUY) { if((slu)>PP) { PP=slu; } if(((slu)>0.001) && (OrderStopLoss()<(b-TrailingStop)) && (OrderOpenPrice()<(b-TrailingStop)) && (OrderProfit()>MathAbs(OrderSwap()))) { if(TrailingStop!=0) { OrderModify(OrderTicket(), 0, b-TrailingStop, 0, 0, 0); } } } if(OrderType()==OP_SELL) { if((sld)>PP) { PP=sld; } if(((sld)>0.001) && (OrderStopLoss()>(a+TrailingStop)) && (OrderOpenPrice()>(a+TrailingStop))) { if(TrailingStop!=0) { OrderModify(OrderTicket(), 0, a+TrailingStop, 0, 0, 0); } } } if(ProfitPoints!=0) { if(OrderType()==OP_BUY && PP>=ProfitPoints && (slu)<=MinProfit) { CloseOnlyOrder(OrderTicket()); return(0); } if(OrderType()==OP_SELL && PP>=ProfitPoints && (sld)<=MinProfit) { CloseOnlyOrder(OrderTicket()); return(0); } } if(CloseByOtherSideCondition==1) { if(OrderType()==OP_BUY && U()==2) { CloseOnlyOrder(OrderTicket()); return(0); } if(OrderType()==OP_SELL && U()==1) { CloseOnlyOrder(OrderTicket()); return(0); } }
Вначале мы выбираем единственный ордер для дальнейших операций над ним (позже мы разберем этот код в отдельном разделе статьи). Теперь мы обозначим цены переменными, чтобы не изменять ордер, например, по трелинг-стопу в ненужном направлении, по невыгодным новым ценам. Вначале мы проверяем ордер на возможность трейлинг-стопа, попутно собирая сведения для следующей функции - минимальный профит, применение которой описывалось ранее. Затем идет функция закрытия ордера при возникновении условия в обратном направлении и открытие в противоположную сторону.
Функции, рассматриваемые в статье
Теперь рассмотрим функции, призванные помочь нам укоротить код и объединить в блоки часто используемые команды, чтобы вызывать потом целый блок. Рассмотрим, как задавать условия и проверять их:
//+------------------------------------------------------------------+ //| возвращает сигнал на покупку или продажу | //+------------------------------------------------------------------+ int U() { if((U1()==2 && Condition1==1) || (U2()==2 && Condition2==1)){return(2);} if((U1()==1 && Condition1==1) || (U2()==1 && Condition2==1)){return(1);} return(0); } //+------------------------------------------------------------------+ //| возвращает сигнал на основании значений стохастика | //+------------------------------------------------------------------+ int U1() { if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing, Method,PriceUsing,MODE_SIGNAL,1)>=80) { if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,2) <=iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing, Method,PriceUsing,MODE_MAIN,2)) { if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,1) >=iStochastic(Symbol(),Period(), Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_MAIN,1)) { return(2); } } } if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,1)<=20) { if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_SIGNAL,2) >=iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_MAIN,2)) { if(iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing, Method,PriceUsing,MODE_SIGNAL,1) <=iStochastic(Symbol(),Period(),Kperiod,Dperiod,Slowing,Method,PriceUsing,MODE_MAIN,1)) { return(1); } } } return(0); } //+------------------------------------------------------------------+ //| выясним направление тренда с помощью фракталов | //+------------------------------------------------------------------+ int U2() { double fu=0,fd=0; int f=0,shift=2; while(f<2) { if(iFractals(Symbol(),Period(),MODE_UPPER,shift)>0) { fu=fu+1; f=f+1; } if(iFractals(Symbol(),Period(),MODE_LOWER,shift)>0) { fd=fd+1; f=f+1; } shift=shift+1; } if(fu==2){return(2);} if(fd==2){return(1);} return(0); }
Первая функция просто проверяет условия, две следующих задают условия. А теперь рассмотрим функцию, которая высчитывает уровни стопов, если они заданы неправильно, и определяет размер лота:
//+------------------------------------------------------------------+ //| предварительная инициализация переменных | //+------------------------------------------------------------------+ int preinit() { Lot=NormalizeDouble(MathFloor(LotSpliter*AccountBalance()*AccountLeverage() /Ask/MathPow(10,Digits+1)*10)/10,1); if(MaxLot>0 && Lot>MaxLot){Lot=MaxLot;} if(Lot>MarketInfo(Symbol(),MODE_MAXLOT)){Lot=MarketInfo(Symbol(),MODE_MAXLOT);} PP=0; StopLoss=sl; TakeProfit=tp; if(TakeProfit!=0 && TakeProfit<(MarketInfo(Symbol(),MODE_STOPLEVEL))) { TakeProfit=MarketInfo(Symbol(),MODE_STOPLEVEL); } if(StopLoss!=0 && StopLoss<(MarketInfo(Symbol(),MODE_STOPLEVEL))) { StopLoss=MarketInfo(Symbol(),MODE_STOPLEVEL); } return(0); }
Теперь зададим функции, открывающие ордеры в зависимости от установленных уровней стопов:
//+------------------------------------------------------------------+ //| возвращает true в случае успешного открытия Buy | //+------------------------------------------------------------------+ bool OrderBuy() { bool res=false; if(StopLoss!=0 && TakeProfit!=0) { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, NormalizeDouble(Ask-StopLoss,4), NormalizeDouble(Ask+TakeProfit,4), 0, 0, 0, 0); return(res); } if(StopLoss==0 && TakeProfit!=0) { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, 0, NormalizeDouble(Ask+TakeProfit,4), 0, 0, 0, 0); return(res); } if(StopLoss==0 && TakeProfit==0) { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, 0, 0, 0, 0, 0, 0); return(res); } if(StopLoss!=0 && TakeProfit==0) { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, NormalizeDouble(Ask-StopLoss,4), 0, 0, 0, 0, 0); return(res); } return(res); } //+------------------------------------------------------------------+ //| возвращает true в случае успешного открытия Sell | //+------------------------------------------------------------------+ bool OrderSell() { bool res=false; if(StopLoss!=0 && TakeProfit!=0) { res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage, NormalizeDouble(Bid+StopLoss,4), NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0); return(res); } if(StopLoss==0 && TakeProfit!=0) { res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage, NormalizeDouble(Bid+StopLoss,4), NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0); return(res); } if(StopLoss==0 && TakeProfit==0) { res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage, NormalizeDouble(Bid+StopLoss,4), NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0); return(res); } if(StopLoss!=0 && TakeProfit==0) { res=OrderSend(Symbol(), OP_SELL, NormalizeDouble(Lot,1), Bid, Slippage, NormalizeDouble(Bid+StopLoss,4), NormalizeDouble(Bid-TakeProfit,4), 0, 0, 0, 0); return(res); } return(res); }
Следующая функция закрывает ордер с указанным тикетом, размером и по указанной цене:
//+------------------------------------------------------------------+ //| возвращает true при успешном закрытии ордера с тикетом Ticket | //+------------------------------------------------------------------+ bool CloseOnlyOrder(int Ticket, double Lots ,double priceClose) { bool res=false; res=OrderClose(Ticket, Lots, priceClose, Slippage, 0); return(res);
Теперь рассмотрим функцию выбора ордера по номеру позиции для дальнейших действий над ним:
//+------------------------------------------------------------------+ //| возвращает true в случае успешного выбора ордера на позиции pos | //+------------------------------------------------------------------+ bool SelectOnlyOrder(int pos) { bool res=false; res=OrderSelect(pos,SELECT_BY_POS,MODE_TRADES); return(res); } //+------------------------------------------------------------------+
Несколько советов по написанию
Во-первых, задавайте опции в виде 0 и 1 вместо true и false. Это поможет Вам лучше и качественней оптимизировать советника. Во-вторых, не пренебрегайте стоп-лоссом, чтобы ограничить возможные убытки, когда рынок идет в ту сторону, которая противоречит условиям. В-третьих, не тестируйте экспертов без уровня стоп-лосса - это черевато быстрым проигрышем депозита. В-четвертых, используйте функции и блоки, которые помогают облегчить понимание кода.
Заключение
Экспертов создавать очень легко, а чтобы было еще легче, прикладываю эксперт, которого разбирал в статье.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Ребята, Вы все молодцы, но кто-то должен быть профессионалом, а имено ПРОФИ доводит начатое дело до конца. Если человек имеет хорошую идею, но из-за недостаточных знаний не может грамотно прокомментировать каждую строку или из-за обиды на стаю не довел и бросил - покажите пример сделайте из идеи конфетку и тогда другие пойдут правильным путем. Да и включите потом разжеванный шаблон в учебник. И ВООБЩЕ ПОРА СДЕЛАТЬ РАЗДЕЛ - ШАБЛОНЫ и ДОСКА ПОЧЕТА (с комментарием жюри) !!!! И СОЗДАТЬ РЕДАКЦИОННУЮ КОМИССИЮ ИЗ ЭКСПЕРТОВ ПО ЯЗЫКУ, КОТОРАЯ БУДЕТ РЕДАКТИРОВАТЬ ПОДОБНЫЕ СТАТЬИ С СОГЛАСИЯ АВТОРА. Ведь здесь все радеют за этот язык. А есть много авторов для которых он просто способ самовыражения, и напрягаться для других они не будут, бросили кость, а остальные пусть радуются, что типа хоть это они сделали бесплатно или разработки скачанные с других сайтов. Их разработки тоже надо доделывать для новичков при массовом обращении народа к членам комиссии. Кстати подобнАя комиссия СИЛЬНО ПОВЫСИТ ПОПУЛЯРНОСТЬ ЭТОГО САЙТА, - сюда начнут массово обращаться иностранцы и наши из тех, кто на пол-пути к овладения языком. Кто "За" - прошу голосовать: finansovyj-dozor@narod.ru. Здесь же (тот же адрес, но без "@"), буду выкладывать лучшие "ДОСКА ПОЧЕТА" (по моему мнению среднего клиента непрофессионала) "разжеванные" разработки или ссылки на них.
Если эта статья для новичков, то где тут ящик водки...???
.... так как бутылки явно не хватит... :)))
Непонятно каким образом определяется направление тренда в условии 2(int U2).
В цикле while (как я понял) осуществляется поиск 2х однонаправленных фракталов, цикл завершается когда эти фракталы найдены и управление передается дальше в:
... неясно как идентифицирутся тренд, если условие расположения фракталов относительно друг друга никак не задано.