Структуры. Научите пожалуйста. - страница 3

 
Petr Zharuk #:
Да, поддерживаю, ассоциативно понятнее. 
Осталось осознать применение в нашей с вами сфере )

Ну насчёт применения, выше уже более менее расписали.

Лучше всего объяснить на каком нибудь примере.

Возьмём для примера ордера.  Например нам надо в конце тестирования советника распечатать какие то статистические данные по всем ордерам.

И мы по какой то причине не хотим обращаться к базовым функциям, не важно по каким причинам.

Мы можем создать свой массив структур и записывать туда всю инфу по ордерам, в конце тестирования просто отправить этот массив структур на печать.

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

Допустим нас интересует время выставления ордера, цена по которой выставлялся ордер и направление. Структура будет выглядеть примерно так.

struct Ord
  {
   datetime          time;
   double            prise;
   ENUM_ORDER_TYPE   type;
  };
Ord  m_Ord[];

По мере появления ордеров, советник увеличивает размер массива и записывает в него нужные данные.

ArrayResize(m_Ord, 1);
m_Ord[0].prise = 1.0023;

В конце тестирования вызываем

ArrayPrint(m_Ord);

И всё у нас красиво распечатается в лог)

 

Любой пример с пользовательской структурой вызывает вопрос: А зачем это всё заполнять, если и так всё можно получить и использовать. Но вот пример на функции которая заполняет структуру или даже массив структур более нагляден.

Например структура тиков

     MqlTick tick;
     SymbolInfoTick(_Symbol, tick);
// Дальше если нужна цена Bid 
     tick.bid;
// Если нужна цена Ask
     tick.ask;

Альтернативный вариант

SymbolInfoDouble(_Symbol, SYMBOL_BID);
// или 
SymbolInfoDouble(_Symbol, SYMBOL_ASK);

Это хорошо, если нужна только одна цена, или Bid или Ask, но если нужны обе цены, то во втором случае надо вызывать две функции.

И таких примеров масса. Вот к примеру ещё структура

struct MqlRates 
  { 
   datetime time;         // время начала периода 
   double   open;         // цена открытия 
   double   high;         // наивысшая цена за период 
   double   low;          // наименьшая цена за период 
   double   close;        // цена закрытия 
   long     tick_volume;  // тиковый объем 
   int      spread;       // спред 
   long     real_volume;  // биржевой объем 
  };

За один вызов одной функции можно получить огромный массив содержащий OHLC баров в истории.

 
Давненько уже показывал пример как использовать структуры при сборе информации по позициям и ордерам, огромный плюс что мы получаем всю нужную информацию в одном объекте, + всегда можно дописать нужные поля. При работе с сеточниками и т.д. получается невероятно удобно и быстро работать. Без кучи дополнительных функций.
Получение информации по открытым позициям.
Получение информации по открытым позициям.
  • 2018.03.13
  • www.mql5.com
Друзья, доброго времени суток. Написал функцию для получения информации по открытым позициям...
 

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

Все пишут примеры использования структур. Вот пример их НЕ использования:

void ReOpen()
 {
  bool result;
  //--
  for(int a=0;a<OrdersTotal();a++)
   if(OrderSelect(a,0,0))
    if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
     {
      if(OrderType()==OP_BUY && OrderComment()=="1")
       {        
        if(TOOCOnComment("2")==0)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus1*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus1_LotSize,Ask,0,0,0,"2",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("3")==0 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus2*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus2_LotSize,Ask,0,0,0,"3",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("4")==0 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus3*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus3_LotSize,Ask,0,0,0,"4",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("5")==0 && TOOCOnComment("4")==1 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus4*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus4_LotSize,Ask,0,0,0,"5",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("6")==0 && TOOCOnComment("5")==1 && TOOCOnComment("4")==1 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus5*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus5_LotSize,Ask,0,0,0,"6",MagicNumber,0,Aqua);
         }
       }
       
      if(OrderType()==OP_SELL && OrderComment()=="1")
       {        
        if(TOOCOnComment("2")==0)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(Bid-OrderOpenPrice()>=OpenAgainAtMinus1*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_SELL,Minus1_LotSize,Bid,0,0,0,"2",MagicNumber,0,Red);
         }
        if(TOOCOnComment("3")==0 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(Bid-OrderOpenPrice()>=OpenAgainAtMinus2*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_SELL,Minus2_LotSize,Bid,0,0,0,"3",MagicNumber,0,Red);
         }
        if(TOOCOnComment("4")==0 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(Bid-OrderOpenPrice()>=OpenAgainAtMinus3*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_SELL,Minus3_LotSize,Bid,0,0,0,"4",MagicNumber,0,Red);
         }
        if(TOOCOnComment("5")==0 && TOOCOnComment("4")==1 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(Bid-OrderOpenPrice()>=OpenAgainAtMinus4*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_SELL,Minus4_LotSize,Bid,0,0,0,"5",MagicNumber,0,Red);
         }
        if(TOOCOnComment("6")==0 && TOOCOnComment("5")==1 && TOOCOnComment("4")==1 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(Bid-OrderOpenPrice()>=OpenAgainAtMinus5*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_SELL,Minus5_LotSize,Bid,0,0,0,"6",MagicNumber,0,Red);
         }
       }
       
     }
  return;
 }

int TOOCOnComment(string TradeComment)
 {
  int tooc;
  for(int a=0;a<OrdersTotal();a++) 
   if(OrderSelect(a,0,0))
    if(OrderSymbol()==Symbol() && OrderComment()==TradeComment && OrderMagicNumber()==MagicNumber)
     tooc++;
  return(tooc);
 }

Обратите внимание на тело TOOCOnComment() и на то, как, где и сколько раз она вызывается. Это просто треш. За 7 лет я ничего ужаснее не видел.

Интересно было бы посчитать количество вызовов OrderSelect() за 1 тик, когда в рынке 5 ордеров.

Советник написан в октябре 2019 года...

Я больше не хочу модифицировать чужие разработки...

Структуры. Научите пожалуйста.
Структуры. Научите пожалуйста.
  • 2022.11.18
  • www.mql5.com
Доброго времени коллеги! Не нашел в статьях разбора что такое Структуры. В документации мало информации...
 
Vladislav Boyko #:

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

Все пишут примеры использования структур. Вот пример их НЕ использования:

Обратите внимание на тело TOOCOnComment() и на то, как, где и сколько раз она вызывается. Это просто треш. За 7 лет я ничего ужаснее не видел.

Интересно было бы посчитать количество вызовов OrderSelect() за 1 тик, когда в рынке 5 ордеров.

Советник написан в октябре 2019 года...

Я больше не хочу модифицировать чужие разработки...

там же для разных условий

другой через switch бы оформил.

 
Vladislav Boyko #:

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

Все пишут примеры использования структур. Вот пример их НЕ использования:

Обратите внимание на тело TOOCOnComment() и на то, как, где и сколько раз она вызывается. Это просто треш. За 7 лет я ничего ужаснее не видел.

Интересно было бы посчитать количество вызовов OrderSelect() за 1 тик, когда в рынке 5 ордеров.

Советник написан в октябре 2019 года...

Я больше не хочу модифицировать чужие разработки...

обработка идет где? в списке терминала или сервера?

идите назад в школу, сэр.

 
lynxntech #:

там же для разных условий

другой через switch бы оформил.

Суть в том, что в цикле перебора ордеров 15 раз вызывается функция, которая опять же перебирает ордера в цикле!
 
lynxntech #:

обработка идет где? в списке терминала или сервера?

идите назад в школу, сэр.

В рынке сетка из 5ти ордеров. В таком случае эта гениальная функция (выделил красным) вызывается 9 раз только за одну итерацию цикла. Внутри каждого вызова, как минимум 5 OrderSelect(). 45 вызовов OrderSelect() за одну итерацию цикла, когда 5 ордеров в рынке. Цикла, который перед каждой итерацией делает что?.. Делает OrderSelect()

for(int a=0;a<OrdersTotal();a++)
   if(OrderSelect(a,0,0))
    if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
     {
      if(OrderType()==OP_BUY && OrderComment()=="1")
       {        
        if(TOOCOnComment("2")==0)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus1*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus1_LotSize,Ask,0,0,0,"2",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("3")==0 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus2*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus2_LotSize,Ask,0,0,0,"3",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("4")==0 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus3*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus3_LotSize,Ask,0,0,0,"4",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("5")==0 && TOOCOnComment("4")==1 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus4*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus4_LotSize,Ask,0,0,0,"5",MagicNumber,0,Aqua);
         }
        if(TOOCOnComment("6")==0 && TOOCOnComment("5")==1 && TOOCOnComment("4")==1 && TOOCOnComment("3")==1 && TOOCOnComment("2")==1)   
         { 
          result=OrderSelect(a,0,0);
          RefreshRates();
          
          if(OrderOpenPrice()-Ask>=OpenAgainAtMinus5*Point && MarketInfo(Symbol(),MODE_SPREAD)<=MaxSpread_Entry)
           result=OrderSend(Symbol(),OP_BUY,Minus5_LotSize,Ask,0,0,0,"6",MagicNumber,0,Aqua);
         }
       }

По вашему здесь все нормально? Даже не говоря о рациональности и производительности, код масштабируемый и его можно дополнять новыми возможностями? Я вас правильно понимаю?

В какой школе отучат думать, что это одноразовая неподдерживаемая портянка, которая непригодна к модификации?

P.S. Это кусок, который добавляет ордера сетки. Есть еще ему подобные, которые модифицируют и закрывают ордера. И все они расположены внутри отдельного цикла по ордерам:

for(int a=0;a<OrdersTotal();a++)
   if(OrderSelect(a,0,0))
    if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
      {
       //...
 
Sergey Gridnev #:
Суть в том, что в цикле перебора ордеров 15 раз вызывается функция, которая опять же перебирает ордера в цикле!

кто-то в цикле обрабатывает на каждом тике(очевидно новичок), у меня через индикатор шпион.

бывало и такое, что в индикатор шпионе проходил цикл по всем символам), статься №648 известный автор, это было уже давно.

--

add

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

 
Vladislav Boyko #:

В рынке сетка из 5ти ордеров. В таком случае эта гениальная функция (выделил красным) вызывается 9 раз только за одну итерацию цикла. Внутри каждого вызова, как минимум 5 OrderSelect(). 45 вызовов OrderSelect() за одну итерацию цикла, когда 5 ордеров в рынке. Цикла, который перед каждой итерацией делает что?.. Делает OrderSelect()

По вашему здесь все нормально? Даже не говоря о рациональности и производительности, код масштабируемый и его можно дополнять новыми возможностями? Я вас правильно понимаю?

В какой школе отучат думать, что это одноразовая неподдерживаемая портянка, которая непригодна к модификации?

P.S. Это кусок, который добавляет ордера сетки. Есть еще ему подобные, которые модифицируют и закрывают ордера. И все они расположены внутри отдельного цикла по ордерам:

При каждом выполнении OnTick() нужна информация об открытых в данный момент ордерах. Так помести ее в структуру.

Какой-нибудь такой шаблон для начала:

struct STRUCT_MARKET_ORDER
  {
   double             stopLoss;
   double             takeProfit;
   double             price;
   double             volume;
   datetime           time;
   int                type;
   int                ticket;
  };

struct STRUCT_MARKET
  {
   /* Массив ордеров. В основном используется при модификации и закрытии
      (если разнонаправленные ордера могут существовать одновременно, можно разбить их на 2 массива: buy[] и sell[])*/
   STRUCT_MARKET_ORDER orders[];
   // -------
   int                 ordersNumber; // Количество ордеров. Мне так удобней, что бы не использовать каждый раз ArraySize()
  };

Дальше думаешь, какая именно нужна будет информация об открытых сейчас ордерах. Например, для открытия новых ордеров сетки нужна верхняя и нижняя цены открытия ордеров, номер последнего шага и (как в том примере) признак наличия существования сигнального ордера. Добавляешь нужные переменные в структуру:

struct STRUCT_MARKET
  {
   STRUCT_MARKET_ORDER orders[];
   double              upperPrice;       // Цена открытия самого верхнего ордера
   double              lowerPrice;       // Цена открытия самого нижнего ордера
   int                 lastStep;         // Номер последнего шага (самый большой)
   int                 ordersNumber;
   bool                signalOrderExist; // Признак наличия существования сигнального ордера
  };

Один раз проходишь в цикле по всем ордерам и заполняешь эту структуру. Это ведь реализуемо, правда? Все. Дальше просто пихаешь уже подготовленные данные в условия для открытия новых ордеров сетки.

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

Открываем первый ордер по сигналу индикатора? Пожалуйста:

STRUCT_MARKET market;

void openOnSignal()
  {
   if(market.ordersNumber > 0)
      return;
   // Можем проверять сигнал от индикатора
  }

Можно даже сделать еще более явно:

STRUCT_MARKET market;

void blockTrade()
  {
   if(market.ordersNumber > 0)
      addOrdersOfGrid();
   else openOnSignal();
  }

Добавляем закрытие и модификацию (тут я уже вставил немного упрощенный пример из реального кода):

void blockTrade()
  {
   // Убеждаемся, что в процессе выполнения не возникло ошибок
   if(!rates_IsActual || !history.isActual || !market.isActual)
      return;
   // Если есть ордера...
   if(market.ordersNumber > 0)
     {
      // Есть сигнал на закрытие - будем закрывать
      if(exitSignal.dir == EXIT_BUY)
        {
         if(market.dir == ENTRY_BUY)
           {
            Print("Exit buy signal from ", SIGNAL_SOURCE_DESCRIPT[exitSignal.source], ". Closing...");
            ordersClose(market.orders);
            return;
           }
        }
      else if(exitSignal.dir == EXIT_SELL)
        {
         if(market.dir == ENTRY_SELL)
           {
            Print("Exit sell signal from ", SIGNAL_SOURCE_DESCRIPT[exitSignal.source], ". Closing...");
            ordersClose(market.orders);
            return;
           }
        }
      // После закрытия мы выходим. Если оказались здесь - есть ордера и закрывать их не нужно
      // В таком случае можно проверить что со стопами и тейками - безубытки, трейлинги и т. д.
      blockModify();
     }
   // А если ордеров нет - проверяем условия для открытия первого
   else blockEntryOnSignal();
  }

Что касается history и exitSignal - это тоже заранее подготовленные, аккуратно упакованные в структуры данные.


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

for(int a=0;a<OrdersTotal();a++) 
   if(OrderSelect(a,0,0))

превратив код в одноразовую портянку.


А пример кода, который подорвал мне сраку, я привел для демонстрации того, к чему приводит подход "начни писать, дальше разберешься"/"аппетит приходит во время еды"