Может ли чайник написать робота? Что два байта переслать! Глава 4.

26 января 2020, 18:32
Programmer96
Programmer96
9
43

Функции

   Весь код советника может быть размещен в OnTick()
Зачастую так и делают. Но это применимо только к очень простым работам. Если советник не аналог одноклеточной амёбы, то вскоре вы поймете что ваш сплошной код нужно разбить на куски. Иначе опа.  Обособленные куски кода выполняющие стереотипную задачу, это и есть Функции.
_______________________________________________________________________________________________________________________

Содержание 

  • Введение
  • Первый шаг
  • Основы языка
  • Переменные 
  • Функции
  • Учимся читать
  • Обучение методом "Ломаем/строим". Модификация чужого кода
  • Работа над ошибками
  • Как найти "иголку" в стоге кода функции которую якобы можно использовать "как есть"?
  • Графический интерфейс
  • Пишем простой советник с нуля
  • Пишем сеточный советник с Мартингейлом
  • Перечень вложений
      Любая большая задача может быть разбита на подзадачи, которые можно представить в виде обособленных кусков кода. Функции служат для записи кода этих подзадач.
     Ниже опубликованы примеры двух реально работающих функций, которые можно использовать "как есть".

       Пример функции открывающей сделки:
     int SleepPage=50; // Допустимое проскальзывание
     double minLot=MarketInfo(Symbol(),MODE_MINLOT);
     double StopLevel=MarketInfo(Symbol(),MODE_STOPLEVEL);
       double stLev=StopLevel*Point;  
    // выше объявлены и инициализированы переменные которые доллжны быть в советнике
    
    // -------------------------- Функция ---------------------------
    int open(double lot=0,double tpVal=0,double slVal=0, int mag=0, string comment=" ",int type=0) {
    //-------------
     double tp=0, sl=0, price=0, opnPrice=0;
     int ordNumber=0, err=0;      
    // ------------------------ открыть Buy ------------------------
       if(type==1) {  
          if(slVal>0) sl=NormalizeDouble(slVal,Digits); else sl=0; 
          if(tpVal>0) tp=NormalizeDouble(tpVal,Digits); else tp=0;
             if(stLev>0) { // Проверка по стоп-уровням
                if(sl>0 && Bid-sl<stLev) sl=0; // Обнулить sl Но сделка откроется
                if(tp>0 && tp-Bid<stLev) tp=0; // Обнулить tp
             } //-- if(stLev>0)
          if(lot>=minLot) { 
             RefreshRates(); // Получить свежие цены
                price=NormalizeDouble(Ask,Digits); // Нормализованная цена открытия
             ordNumber=OrderSend(Symbol(),OP_BUY,lot,price,SleepPage,sl,tp,comment,mag,0,clrBlue);
               //Открыть сделку  
             if(ordNumber>0) return(ordNumber); // вернуть номер сделки и выйти из функции 
                else { // иначе
                   err=GetLastError(); // Получить номер ошибки
                     Print("_2_ Can't oppen Buy, Error: ",err); // Сообщить трейдеру номер ошибки 
                         return(0); // вернуть ноль - Cделка Не Открыта - и выйти из функции
                } //-- else
          } else return(0); 
       } //-- if(type==1)
    // ------------------------ открыть SELL ------------------------
       if(type==2) {   ResetLastError();
          if(slVal>0) sl=NormalizeDouble(slVal,Digits); else sl=0; 
          if(tpVal>0) tp=NormalizeDouble(tpVal,Digits); else tp=0;
             if(stLev>0) { 
                if(sl>0 && sl-Ask<stLev) sl=0; 
                if(tp>0 && Ask-tp<stLev) tp=0; 
             } //-- if(stLev>0)
          if(lot>=minLot) { 
             RefreshRates();
                price=NormalizeDouble(Bid,Digits);
             ordNumber=OrderSend(Symbol(),OP_SELL,lot,price,SleepPage,sl,tp,comment,mag,0,clrBlue); 
                if(ordNumber>0) return(ordNumber);
                else {      
                   err=GetLastError(); 
                      Print("_2_ Can't oppen Sell. Error: ",err);
                         return(0);
                } //-- else
          }  else return(0); 
       } //-- if(type==2)
    //-------   
       return(0); // Если дошло до этой строки - сделка не открыта, вернуть ноль
     } //-- int open
    
    

       Все, что находится перед первой фигурной скобкой, составляет заголовок определения функции, а то, что находится между фигурными скобками, является телом функции.
       Заголовок функции включает в себя описание типа возвращаемого значения, имени функции и объявления переменных, принимающих передаваемые функции параметры.  Количество параметров, передаваемых в функцию, ограничено и не может превышать 64.

    Функция может вызываться из других частей программы столько раз, сколько необходимо.  Если не написать функцию рисующую кнопки, то один и тот же код вам придется повторить столько раз, сколько кнопок нужно нарисовать.

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

      Если функция не возвращает никакого значения,  то она должна иметь тип void.
       Пример функции void рисующей на графике кнопки:
    void Button(string name="",int Hd=0,int Vd=0,int H=0,int V=0,string txt="",bool state=0) {
          ObjectCreate(0,name,OBJ_BUTTON,0,0,0); // Создает кнопку. 
          ObjectSetInteger(0,name,OBJPROP_XDISTANCE,Hd); //Левый край кнопки. Пикселей справа    
          ObjectSetInteger(0,name,OBJPROP_YDISTANCE,Vd); //Верх кнопки. Пикселей сверху 
          ObjectSetInteger(0,name,OBJPROP_XSIZE,H); // Ширина кнопки    
          ObjectSetInteger(0,name,OBJPROP_YSIZE,V); // Высота кнопки   
          ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_RIGHT_UPPER); //Угол графика. Можно изменить 
          ObjectSetString (0,name,OBJPROP_TEXT,txt); // Текст на теле кнопки
          ObjectSetString (0,name,OBJPROP_FONT,"Arial");   // Тип шрифта
          ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);     // Размер шрифта
          ObjectSetInteger(0,name,OBJPROP_COLOR,clrBlack); // Цвет шрифта 
          ObjectSetInteger(0,name,OBJPROP_BGCOLOR,clrSilver); // Цвет кнопки
          ObjectSetInteger(0,name,OBJPROP_STATE,state); //Статус. Отжата false Нажата true   
     } //-- void Button
       Свойства OBJPROP_CORNER, OBJPROP_FONTSIZE и другие прописанные константами можно задать через переменные, если это нужно, и изменять при вызове функции.
      Можно добавить другие свойства кнопки - их есть ;-)

       Практические занятия
    Теория была выше. А сейчас практика - пишем советник. В нем будет всего лишь две показанные выше функции, но при всем при этом он будет способен открывать сделки по рынку, устанавливать ТП и СЛ, если они заданы.
        Проверять на демо-счете или в тестере!!! На реале не рекомендую. После проверки можно.

    Код советника:   
    //+------------------------------------------------------------------+
    //|                                                   MySecondBot.mq4 |
    //+------------------------------------------------------------------+
    #property copyright "V.Temchenko Skype:vasily.temchenko" //Вставьте в эту и другие  
    #property link      "Mailto:tvp.prog.96@gmail.com" //аналогичные строки свои данные.
    #property version   "1.00"
    #property strict 
    #property description "This is my second robot." //текст можно писать на любом языке
    #property description "It is simple but works."
    #property description "In a month I can create a commercial version."
    #property description  "Have questions?"; 
    #property description  "Ask on Skype or here: https://www.mql5.com/ru/users/programmer96"; 
    #property description "or by Viber: +38098 800-6-800"
    #property icon         "\\Images\\force64.ico"; //строка рисует лого. 
    //Замените force64.ico своей иконкой. Если нет - закройте строку комментом или удалите 
    //рисунок name.ico должен находиться в папке ....84FBF\MQL4\Images  
    //-----------------------------------
    input double Lots       =0.01;
    input int TakeProfit    =650;
    input int StopLoss      =450;
    //---------
     int SleepPage=50; // Допустимое проскальзывание 
     int Magic=123;
     double StopLevel=0, stLev=0, minLot=0;
    //---------------------------
    
    int OnInit() {
    //---
       minLot=MarketInfo(Symbol(),MODE_MINLOT);
       StopLevel=MarketInfo(Symbol(),MODE_STOPLEVEL);
          stLev=StopLevel*Point; 
    //------
       if(ObjectFind("OpenBuy")<0) { // Если на графике не найдена кнопка ОткрытьБай
         //-------------- Форматируем график. Подробно об этом - читайте доку "Операции с графиками"
          ChartSetInteger(0,CHART_SHOW_GRID,0,0); 
          ChartSetInteger(0,CHART_COLOR_CHART_UP,White);
          ChartSetInteger(0,CHART_MODE,1);
          ChartSetInteger(0,CHART_SHOW_OHLC,0,0);
          ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,White);
          ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,LightSeaGreen);
          ChartSetInteger(0,CHART_SCALE,2); ChartSetInteger(0,CHART_SHIFT,true); 
          ChartSetDouble (0,CHART_SHIFT_SIZE,35);
         //---------------  Вызов функции рисующей кнопки ----------------
             Button("OpenBuy"    ,144, 20,70,15,"Open Buy",false); 
             Button("OpenSell"   , 73, 20,70,15,"Open Sell",false); 
       } //--
       
    //---
       return(INIT_SUCCEEDED);
     } //-- OnInit()
    
    //+------------------------------------------------------------------+
    void OnTick() {
     int answer=0;
     double tp=0, sl=0;
    //---
       if(ObjectGetInteger(0,"OpenBuy",OBJPROP_STATE)==true) { // Если кнопка нажата
          if(TakeProfit>0) tp=Ask+TakeProfit*Point; else tp=0; // Расчитать цену тейка
          if(StopLoss>0) sl=Ask-StopLoss*Point; else sl=0;  // Рассчитать цену СЛ
             answer=open(Lots,tp,sl,Magic,"Manualy open buy-deal",1); 
             // Вызвать функцию открытия buy-сделки (type==1)
                if(answer>0) Print("___ Открыта бай-сделка #",answer); 
                   else Print("___ Ошибка. Не могу открыть бай-сделку.");
          ObjectSetInteger(0,"OpenBuy",OBJPROP_STATE,false); // Отжать кнопку
       } //-- if(ObjectGetInteger(0,"OpenBuy"
    //--------
       if(ObjectGetInteger(0,"OpenSell",OBJPROP_STATE)==true) { 
          if(TakeProfit>0) tp=Bid-TakeProfit*Point; else tp=0; 
          if(StopLoss>0) sl=Bid+StopLoss*Point; else sl=0;   
             answer=open(Lots,tp,sl,Magic,"Manualy open sell-deal",2); 
             // Вызвать функцию открытия sell-сделки (type==2)
                if(answer>0) Print("___ Открыта селл-сделка #",answer); 
                   else Print("___ Ошибка. Не могу открыть селл-сделку.");
          ObjectSetInteger(0,"OpenSell",OBJPROP_STATE,false); 
       } //-- if(ObjectGetInteger(0,"OpenSell"
       
    //------   
     } //-- OnTick()



    После вставки в редактор и компиляции этого кода, компилятор скажет вам что вы неправы и выдаст список ошибок.
    Я здесь не стану говорить как их исправить, надеюсь что сами сообразите как удовлетворить компилятор ;)
    Если сделаете с первого раза - ваша оценка 10 баллов.
    Если сообразите что нужно сделать до того как как нажмете кнопку "Компилировать" - ваша оценка 12 баллов.

        Получилось? Компиляция прошла без ошибок? Поздравляю!
    При установке на график этого "советника" вы увидите:







        Нажатием кнопки OpenBuy вы откроете сделку с заданными в меню параметрами. То же самое для OpenSell.
    ___________________________________________________________________________________________________________

       Выше показаны примеры пользовательских функций.  И написал я о них далеко не все.
    В советнике всегда есть как минимум одна обязательная стандартная функция OnTick() и может использоваться множество других не обязательных стандартных функций. Знать абсолютно все стандартные функции не нужно, но десяток-другой нужно знать обязательно. Поэтому даю
    Задание на самостоятельную работу - изучить тему "Функции"

       В Документации найдете  ее здесь.
       В учебнике Ковалева найдете  Функции здесь.

    Отнеситесь серьезно - это очень важная тема, без ее знания нормальный советник вам не написать.
     

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

       Букварь публикуется "с листа".
    Это значит что правки неизбежны. Изучив главу 15 (например) не поленитесь пролистать все предыдущие. Высока вероятность  того что там появилась новая важная информация или поправлены ошибки.



       Если это кому то интересно, то ниже ссылка на все мои посты опубликованные в блоге начиная с августа 2014
                      Ранее опубликованные посты
       Не все в них так, то таких которые категорически нужно удалить не обнаружил (пока не обнаружил)

    Поделитесь с друзьями: