Пример MQL5. Простой советник. Проверка размера бара. Покупка/продажа - страница 2

 

Шаг пятый. Подготовка переменных к проверке размера бара.

Немного перефразируем известное высказывание: "Что бы продать что-то ненужное, нужно купить что-то ненужное!":

 

в фразу: "Что бы проверить размер бара, сначала нужно размер бара задать!". Размер бара задавать будем во входящих параметрах - при помощи Input переменной:

#include <Trade\SymbolInfo.mqh>  
CSymbolInfo    m_symbol;                     // symbol info object
//--- input parameters
input ushort   InpSizeOfBar=10;              // size of bar (in pips)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

Обратите внимание: всё, что касается размера бара, уровней TakeProfit и StopLoss задаём при помощи

ushort

Беззнаковым типом short является тип ushort, который также имеет размер 2 байта. Минимальное значение равно 0, максимальное значение 65 535.

Во-первых - этим отсекаются возможные ошибки задания отрицательных значений, во-вторых - это привычное измерение в пунктах/пипсах (здесь не будет рассуждать, что первее было пипсы или пункты - просто запомните: размер бара должен одинаково работать на любом символе - и на "пятизнаке" и на символах у которых после запятой ноль знаков).

 

Типичное сравнение размера бара будет таким: цена закрытия бара (1.03456) минус цена открытия бара (1.0317). Например:

Close - Open

Результат равен 0.00239. "Пятизнак" хорош при техническом анализе, но сейчас это лишнее, по старой доброй шкале "четырёхзнака" размер бара составляет 23 пункта (это без округления). Как-же нам согласовать эти три величины:

  • заданный размер бара "InpSizeOfBar=10"
  • реальную величину "0.00239" и
  • размер бара 23 пункта?

Поступаем так: вводим переменную double "ExtSizeOfBar" 

//--- input parameters
input ushort   InpSizeOfBar=10;              // size of bar (in pips)
//--- parameters
double         ExtSizeOfBar=0.0;             //
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()

В этой переменной мы будем (после некоторых вычислений в OnInit()) хранить заданный размер бара уже в ценах текущего финансового инструмента.

Для этого в OnInit() проверим количество знаков после десятичной точки у данного финансового инструмента и в зависимости от этого скорректируем нашу переменную "ExtSizeOfBar":

   m_symbol.Refresh();
//--- tuning for 3 or 5 digits
   int digits_adjust=1;
   if(m_symbol.Digits()==3 || m_symbol.Digits()==5)
      digits_adjust=10;
   ExtSizeOfBar=InpSizeOfBar*m_symbol.Point()*digits_adjust;

//---
   return(INIT_SUCCEEDED);
  }

Что означает эта запись: если у финансового инструмента количество знаков после десятичной точки (метод Digits) равно 3 или 5 (то есть если в терминале используется "пятизнак"), то коэффициент "digits_adjust" равен 10. На примере "EURUSD":

   ExtSizeOfBar=InpSizeOfBar*m_symbol.Point()*digits_adjust => 

ExtSizeOfBar=10*0.00001*10=0.0010

Таким образом ExtSizeOfBar=0.0010 - что соответствует 10 пунктам/пипсам на "четырёхзнаке". Теперь можно будет в программе сравнивать разницу цен закрытия и открытия бара и нашу переменную "ExtSizeOfBar".

 

Сохраним версию файла 1.04 в Хранилище. 

Файлы:
 

Шаг шестой: момент рождения нового бара.

Нам нужно на каждом тике знать время открытия бара. Делать это можно при помощи такой простой функции:

//+------------------------------------------------------------------+
//| Get Time for specified bar index                                 |
//+------------------------------------------------------------------+
datetime iTime(const int index,string symbol=NULL,ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT)
  {
   if(symbol==NULL)
      symbol=Symbol();
   if(timeframe==0)
      timeframe=Period();
   datetime Time[1];
   datetime time=0;
   int copied=CopyTime(symbol,timeframe,index,1,Time);
   if(copied>0) time=Time[0];
   return(time);
  }

добавьте эту функцию в самый конец программы.

У текущего бара время открытия бара будет больше, чем у предыдущего. Значит в OnTick() нам нужно сравнивать время открытия бара со временем открытия бара на предыдущем тике. Как только время открытия бара станет больше, чем время сохранённое на предыдущем баре - значит родился новый бар :).

А вот хранить время нам поможет статическая переменная объявленная в OnTick(). Чем удобна статическая переменная - она сохраняет своё значение при последующем входе в процедуру/функцию. Пропишем объявление статической переменной:

void OnTick()
  {
//---
   static datetime prev_time=0;
   if(prev_time<iTime(0))
     {
      //--- new bar

     }
   prev_time=iTime(0);                       // time on the previous tick

//---
   if(!RefreshRates())

Введём ещё одну переменную - флаг "новый бар" - при заходе в OnTick() этот флаг будет инициализироваться значением "false", а если мы поймали новый бар, то присвоим этому флагу значение "true":

void OnTick()
  {
//---
   static datetime prev_time=0;
   bool new_bar=false;                       // flag "new bar"
   if(prev_time<iTime(0))
     {
      //--- new bar
      new_bar=true;
     }
   prev_time=iTime(0);                       // time on the previous tick
//---
   if(!RefreshRates())

Теперь мы на каждом тике будем знать - мы поймали новый бар или это текущий бар.


Как обычно увеличиваем версию файла и сохраняем в Хранилище. Версия файла 1.05. 

Файлы:
 

Шаг седьмой: получение цены открытия бара и цены закрытия бара.

Для получения цен открытия и закрытия бара есть такие простые функции:

//+------------------------------------------------------------------+
//| Get Open for specified bar index                                 |
//+------------------------------------------------------------------+
double iOpen(const int index,string symbol=NULL,ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT)
  {
   if(symbol==NULL)
      symbol=Symbol();
   if(timeframe==0)
      timeframe=Period();
   double Open[1];
   double open=0;
   int copied=CopyOpen(symbol,timeframe,index,1,Open);
   if(copied>0) open=Open[0];
   return(open);
  }
//+------------------------------------------------------------------+
//| Get Close for specified bar index                                |
//+------------------------------------------------------------------+
double iClose(const int index,string symbol=NULL,ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT)
  {
   if(symbol==NULL)
      symbol=Symbol();
   if(timeframe==0)
      timeframe=Period();
   double Close[1];
   double close=0;
   int copied=CopyClose(symbol,timeframe,index,1,Close);
   if(copied>0) close=Close[0];
   return(close);
  }

Запишите эти функции в самый конец программы. Изменяем версию на 1.06 и сохраняем в Хранилище.
 

Файлы:
 

Шаг восемь: подготовка к торговле.

Напомню задачи советника:

  • проверка результатов открытия позиции - если позиция открыта, то ставим флаг и выходим до момента рождения нового бара
  • на новом баре снова начинаем работать на каждом тике и проверять размер свечи и как только размер бара станет равным или превысит заданный размер то: если свеча бычья и у нас открыта позиция Buy - значит ещё покупка, если свеча медвежья и у нас открыта позиция Buy - значит закрываем позицию/позиции Buy; если свеча медвежья и у нас открыта позиция Sell - значит ещё продажа, если свеча бычья и у нас открыта позиция Sell - значит закрываем позицию/позиции Sell.

Первым делом нужен сам флаг разрешения торговли. Объявим переменную bool "TradeIsAllowed" в "шапке" программы (эта область называется область объявления глобальных переменных ПРОГРАММЫ - не путайте с глобальными переменными терминала):

//--- parameters
double         ExtSizeOfBar=0.0;             //
bool           TradeIsAllowed=true;          //
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()

и в OnInit() также будет принудительно инициализировать эту переменную:

   if(m_symbol.Digits()==3 || m_symbol.Digits()==5)
      digits_adjust=10;
   ExtSizeOfBar=InpSizeOfBar*m_symbol.Point()*digits_adjust;
//---
   TradeIsAllowed=true;

//---
   return(INIT_SUCCEEDED);
  }

Теперь в OnTick() можно прописать проверку: если торговля запрещена и мы находимся не в момент рождения бара - то выходим:

   Comment("Bid=",DoubleToString(m_symbol.Bid(),m_symbol.Digits()),"\n",
           ",Ask=",DoubleToString(m_symbol.Ask(),m_symbol.Digits()));
//---
   if(!TradeIsAllowed && !new_bar)
      return;

   TradeIsAllowed=true;
  }
//+------------------------------------------------------------------+
//| Refreshes the symbol quotes data                                 |

в противном случае - разрешаем торговлю

И если размер бара больше заданного и в зависимости от того, какой бар - бычий или медвежий - покупаем или продаём:

   if(!TradeIsAllowed && !new_bar)
      return;
   TradeIsAllowed=true;
//--- calculate the size of bar
   double open=iOpen(0);
   double close=iClose(0);
   if(close>open && close-open>ExtSizeOfBar) // Buy
     {

     }
   if(open>close && open-close>ExtSizeOfBar) // Sell
     {

     }

  }
//+------------------------------------------------------------------+
//| Refreshes the symbol quotes data                                 |


Сохраняем версию 1.07. 

Файлы:
 

Шаг девятьCTrade - класс для упрощенного доступа к торговым функциям.

Класс CTrade

Класс CTrade является классом для упрощенного доступа к торговым функциям.

Описание

Класс CTrade обеспечивает упрощенный доступ к торговым функциям.

Заголовок

   #include <Trade\Trade.mqh>

Из всего многообразия будем использовать такие методы:

Установка параметров

 

SetExpertMagicNumber

Устанавливает идентификатор эксперта

Дополнительные методы

 

Buy

Открывает длинную позицию с заданными параметрами

Sell

Открывает короткую позицию с заданными параметрами

Доступ к результатам исполнения последнего запроса

 

ResultRetcode

Получает код результата выполнения запроса

ResultRetcodeDescription

Получает код результата выполнения запроса как строку

ResultDeal

Получает тикет сделки

 

И, сначала, нужно прописать использование класса CTrade: 

#property description "If the size of bar is more, than the set parameter - that BUY OR SELL"
#include <Trade\SymbolInfo.mqh>  
#include <Trade\Trade.mqh>
CSymbolInfo    m_symbol;                     // symbol info object
CTrade         m_trade;                      // trading object
//--- input parameters

Для установки идентификатора, введём входную переменную "InpMagic":

//--- input parameters
input ushort   InpSizeOfBar=10;              // size of bar (in pips)
input ulong    InpMagic=15489;               // magic number
//--- parameters
double         ExtSizeOfBar=0.0;             //

и в OnInit() установим идентификатор эксперта:

int OnInit()
  {
//---
   m_trade.SetExpertMagicNumber(InpMagic);   // sets magic number

//---
   m_symbol.Name(Symbol());                  // sets symbol name
   if(!RefreshRates())

Magic поможет нам, когда нужно будет закрывать все позиции - только при наличии Magic мы сможем закрыть свои позиции.

Переходим в блок открытия позиции:

   if(close>open && close-open>ExtSizeOfBar) // Buy
     {
      double lot=m_symbol.LotsMin();
      double price=m_symbol.Ask();
      if(m_trade.Buy(lot,NULL,price)) // successful check of the structures
         if(m_trade.ResultDeal()!=0) // deal ticket if the deal is executed
            TradeIsAllowed=false;            // trade is forbidden
     }

Если метод Buy вернул true (успешное окончание работы метода) и если при этом тикет сделки не равен нулю - значит торговая операция успешна и прекращаем отслеживать размер бара до момента рождения нового бара.

Для Sell код аналогичный:

   if(open>close && open-close>ExtSizeOfBar) // Sell
     {
      double lot=m_symbol.LotsMin();
      double price=m_symbol.Bid();
      if(m_trade.Sell(lot,NULL,price)) // successful check of the structures
         if(m_trade.ResultDeal()!=0) // deal ticket if the deal is executed
            TradeIsAllowed=false;            // trade is forbidden
     }

Наш советник уже работает: анализирует на каждом тике размер бара и при удачном размещении позиции прекращает анализировать до рождения нового бара. Осталось совсем немного - перед открытием позиции проверять: не нужно ли закрыть противоположные позиции.

Сохраним в Хранилище файл под версией 1.08. 

Файлы:
 
Создаваемая поделка по-моему победитель в номинации "Самая бесполезная фигня года"
Уж что-нибудь полезное бы писал для обучения...
 
Vitaly Muzichenko:

А вот тут действительно покажите как можно шире, как написать советник, не используя сторонние файлы в виде стандартной библиотеки, и прочей шелухи.

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

Поддерживаю.

Карпутовская манера во всех своих пояснениях и в своих кодах для кодабазы -- использовать стандартную библиотеку (СБ) -- вообще не способствует пониманию и освоению языка новичками. 

Удивительно, что Карпутов это не может или не хочет никак понять.

Поэтому, как по мне, пояснения на СБ -- это гарантированный "мартышкин труд".

p.s. Как заметил по форуму -- если под его пояснением дать нормальное пояснение без СБ -- то на пояснение Карпутова вообще никто внимания не обращает. 

 
Andrey F. Zelinsky:

Поддерживаю.

Карпутовская манера во всех своих пояснениях и в своих кодах для кодабазы -- использовать стандартную библиотеку (СБ) -- вообще не способствует пониманию и освоению языка новичками. 

Удивительно, что Карпутов это не может или не хочет никак понять.

Поэтому, как по мне, пояснения на СБ -- это гарантированный "мартышкин труд".

p.s. Как заметил по форуму -- если под его пояснением дать нормальное пояснение без СБ -- то на пояснение Карпутова вообще никто внимания не обращает. 

На самом деле мне очень интересно не просто смотреть такие ветки, а именно изучать. Но изучить то здесь особо нечего, применяется СБ, поэтому что-то понять с изложенного крайне сложно, да и не интересно вовсе.

Так-же вопрос к комментариям, если уж хотите донести что либо, тогда это явно ничего не донесёт:

   if(open>close && open-close>ExtSizeOfBar) // Sell
     {
      double lot=m_symbol.LotsMin();
      double price=m_symbol.Bid();
      if(m_trade.Sell(lot,NULL,price))
         if(m_trade.ResultDeal()!=0)
            TradeIsAllowed=false;
     }

А вот это донесёт, и в нём легко понять, за что отвечает часть кода:

static double     lastDnPrcMACD_extrValue=0;       // значение прошлого нижнего экстремума цены для MACD
static datetime   lastDnPrcMACD_extrTime=0;        // время прошлого нижнего экстремума цены для MACD
    //--- если внизу есть экстремум MACD (на покупку)
      if(extrMACD==OP_BUY) {
       //--- если центральная точка экстремума MACD ниже ноля, найдём экстремумы и дивергенции
       if(macd_b<0) {
         FindAndSetDivergence(symbol,periodForWork,OP_BUY,macd_b,bar_b,wnd_macd,time_b,time,i,leftMACD_tUP,leftMACD_UP,leftMACD_tDN,leftMACD_DN);
       }
         lastDnMACD_extrValue=macd_b;  // сохраним значение "последнего" нижнего экстремума MACD для последующего сравнения с верхним экстремумом MACD (только для поиска экстремумов)
      }
      //--- если сверху есть экстремум MACD (на продажу)
    if(extrMACD==OP_SELL) {
     //--- если центральная точка экстремума MACD выше ноля, найдём экстремумы и дивергенции
    if(macd_b>0) {

Может всё-же имеет смысл комментировать код, чтоб было понятнее, о чём эта часть кода. Ну и конечно не использовать СБ, а показать практичным кодом все открытия, переборы и закрытия позиций, и если можно - ордеров.  

 
Vitaly Muzichenko:

Может всё-же имеет смысл комментировать код, чтоб было понятнее, о чём эта часть кода. Ну и конечно не использовать СБ, а показать практичным кодом все открытия, переборы и закрытия позиций, и если можно - ордеров.  

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

 
Vladimir Karputov:

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

Код не читаем. Абсолютно. Это просто постоянные обращения к "чёрному ящику". Если не знать что содержит в себе этот "чёрный ящик", и как он, ящик этот, работает, и что там делается внутри и даётся на выход, то о каком понимании может идти речь?

Вот ты сам сможешь написать нормальный класс, который всё это будет делать? Написать, а потом объяснить "на пальцах" новичкам. Так, чтобы они поняли алгоритм. Понимаешь? АЛГОРИТМ. А не запрос к таинственному "нечто" и ответ от этого таинственного "нечто" потом использовать. Вот тогда они будут понимать что творится в их программах. И уже тогда они сами, без твоего навязывания решат переходить им к использованию СБ, или написать своё. Или наследоваться от классов СБ.

А так ... всё в воду...

Причина обращения: