English 中文 Español Deutsch 日本語 Português
Универсальный шаблон экспертов

Универсальный шаблон экспертов

MetaTrader 4Торговые системы | 13 августа 2007, 16:50
5 468 16
Вадим Андреевич
Вадим Андреевич

Введение

Многие трейдеры сталкивалось с проблемой написания своих экспертов. С чего начать? Как задать в коде советника тейк-профит, стоп-лосс или трейлинг-стоп? Как проверить стратегию на работоспособность? В данной статье рассматриваются основные функции для создания экспертов. Возможно для кого-нибудь код трейлинг-стопа окажется полезным.

Переменные в советнике

Какие же переменные нам нужны в каждом советнике? Как сделать так, чтобы тестировать его только по одному из его условий входа? Как сделать так, чтобы в тестере перебирались и параметры, заданные переменными логического типа (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. Это поможет Вам лучше и качественней оптимизировать советника. Во-вторых, не пренебрегайте стоп-лоссом, чтобы ограничить возможные убытки, когда рынок идет в ту сторону, которая противоречит условиям. В-третьих, не тестируйте экспертов без уровня стоп-лосса - это черевато быстрым проигрышем депозита. В-четвертых, используйте функции и блоки, которые помогают облегчить понимание кода.

Заключение

Экспертов создавать очень легко, а чтобы было еще легче, прикладываю эксперт, которого разбирал в статье.

Прикрепленные файлы |
template.mq4 (8.07 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (16)
Леонид
Леонид | 10 мар. 2008 в 21:33

Ребята, Вы все молодцы, но кто-то должен быть профессионалом, а имено ПРОФИ доводит начатое дело до конца. Если человек имеет хорошую идею, но из-за недостаточных знаний не может грамотно прокомментировать каждую строку или из-за обиды на стаю не довел и бросил - покажите пример сделайте из идеи конфетку и тогда другие пойдут правильным путем. Да и включите потом разжеванный шаблон в учебник. И ВООБЩЕ ПОРА СДЕЛАТЬ РАЗДЕЛ - ШАБЛОНЫ и ДОСКА ПОЧЕТА (с комментарием жюри) !!!! И СОЗДАТЬ РЕДАКЦИОННУЮ КОМИССИЮ ИЗ ЭКСПЕРТОВ ПО ЯЗЫКУ, КОТОРАЯ БУДЕТ РЕДАКТИРОВАТЬ ПОДОБНЫЕ СТАТЬИ С СОГЛАСИЯ АВТОРА. Ведь здесь все радеют за этот язык. А есть много авторов для которых он просто способ самовыражения, и напрягаться для других они не будут,  бросили кость, а остальные пусть радуются, что типа хоть это они сделали бесплатно или разработки скачанные с других сайтов. Их разработки тоже надо доделывать для новичков при массовом обращении народа к членам комиссии. Кстати подобнАя комиссия СИЛЬНО ПОВЫСИТ ПОПУЛЯРНОСТЬ ЭТОГО САЙТА- сюда начнут массово обращаться иностранцы и наши из тех, кто на пол-пути к овладения языком. Кто "За" - прошу голосовать: finansovyj-dozor@narod.ru. Здесь же (тот же адрес, но без "@"), буду выкладывать лучшие  "ДОСКА ПОЧЕТА" (по моему мнению среднего клиента непрофессионала) "разжеванные" разработки или ссылки на них. 

[Удален] | 19 мар. 2008 в 22:08
МДЯ-а-а-а-а.....
Если эта статья для новичков, то где тут ящик водки...??? 
.... так как бутылки явно не хватит...    :)))
[Удален] | 2 июн. 2008 в 17:56
А куда лошадь запрягать?
[Удален] | 18 июн. 2008 в 10:05
Ушел читать учебник дальше....
[Удален] | 22 окт. 2009 в 15:55

Непонятно каким образом определяется направление тренда в условии 2(int U2).

В цикле while (как я понял) осуществляется поиск 2х однонаправленных фракталов, цикл завершается когда эти фракталы найдены и управление передается дальше в:

if(fu==2){return(2);}
if(fd==2){return(1);}
return(0);
... неясно как идентифицирутся тренд, если условие расположения фракталов относительно друг друга никак не задано.
Математика в трейдинге. Оценка результатов торговых сделок Математика в трейдинге. Оценка результатов торговых сделок
Все мы слышали фразу "Никакая полученная прибыль в прошлом не гарантирует успешных результатов в будущем". Но необходимость оценки торговых систем тем не менее является актуальной. В этой статье мы рассмотрим некоторые простые и удобные методики оценки торговых результатов.
Язык MQL4 для "чайников". Технические индикаторы и встроенные функции Язык MQL4 для "чайников". Технические индикаторы и встроенные функции
Это третья статья из цикла "Язык MQL4 для 'чайников'". Сейчас мы будем разбираться, как использовать встроенные функции и функции для работы с техническими индикаторами. Последние будут жизненно необходимы при разработке в дальнейшем ваших советников и индикаторов. Кроме того, мы на простом примере посмотрим, как можно отслеживать торговые сигналы для входа в рынок, что бы вы поняли, как правильно использовать индикаторы. А в конце статьи вы узнаете кое-что новенькое и интересное про сам язык.
Практическое применение кластерных индикаторов на рынке FOREX Практическое применение кластерных индикаторов на рынке FOREX
Кластерные индикаторы – это набор индикаторов, разделяющих валютные пары на отдельные валюты. Индикаторы позволяют следить за колебаниями валют относительно друг друга, определять потенциал зарождения новых валютных трендов, получать торговые сигналы и сопровождать среднесрочные и долгосрочные позиции.
Как реализовать свой критерий оптимизации Как реализовать свой критерий оптимизации
Для стандртного эксперта Moving Average реализован пример оптимизации по критерию прибыль/просадка с выводом результатов в файл