Обсуждение статьи "Как построить советник, работающий автоматически (Часть 08): OnTradeTransaction"

 

Опубликована статья Как построить советник, работающий автоматически (Часть 08): OnTradeTransaction:

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

Теперь в наиболее полном виде у нас наконец-то появились дружественные друг другу класс C_Manager и советник. Они до такой степени дружны, что оба могут работать и следить за тем, чтобы не стать агрессивными или недружелюбными. Таким образом, поток сообщений между советником и классом C_Manager выглядит так, как показано на рисунке 02:

Рисунок 02

Рисунок 02 - Поток сообщений с новыми реализованными функциями

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

Глядя на рисунок 02, можно подумать, что код советника очень сложный, но он намного проще того, что многие люди считают необходимым для кода, реально используемого в советнике. Особенно в автоматизированном советнике. Помните следующее: на самом деле советник не генерирует никаких сделок, это всего лишь средство или инструмент для связи с торговым сервером. Таким образом, он действительно реагирует только на те триггеры, которые к нему применяются.

Автор: Daniel Jose

 

У меня проблема, которая ставит меня в тупик.

Спасибо за такую замечательную статью. У меня есть проблема, которая меня смущает. Я написал код для открытия позиции.

Он может вернуть правильное значение при запуске на демо-счете, но возвращает 0 при запуске на реальном счете, не могли бы вы мне подсказать, где проблема?

Моя цель - вернуть тикет сделки после открытия позиции.
//+------------------------------------------------------------------+
//|& nbsp; &nbsp ; Test1.mq5 |
//|& nbsp; Copyright 2023, MetaQuotes Ltd.|
//|& nbsp; &nbsp ; https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade\Trade.mqh>
CTrade    m_trade;
CDealInfo m_deal;
//+------------------------------------------------------------------+
//| Функции инициализации торговли советника & nbsp; &nbsp ; |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   EventSetMillisecondTimer(1);
   OpenPosition(ORDER_TYPE_BUY, 0.01, 123456);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| EA Quotation Function & nbsp; &nbsp ; ||
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
ulong OpenPosition(const ENUM_ORDER_TYPE type,
                   const double lot,
                   const long magic)
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   m_trade.SetExpertMagicNumber(magic);
   if(m_trade.PositionOpen(_Symbol, type, lot, (type == ORDER_TYPE_BUY) ? ask : bid, 0, 0))
     {
      ulong deal_ticket = m_trade.ResultDeal();
      if(HistoryDealSelect(deal_ticket))
         m_deal.Ticket(deal_ticket);
      string flag = (m_deal.DealType() == DEAL_TYPE_BUY) ? "buy" : "sell";
      printf(StringFormat("open deal ticket= #%%d order ticket= #%%d %%s %%.2f %%s at %%.%df", _Digits),
             deal_ticket, m_deal.Order(), flag, m_deal.Volume(), _Symbol, m_deal.Price());
      return(deal_ticket);
     }
   return(0);
  }


 
Yang Wang #:

Ола, Даниэль Хосе

Obrigado por dedicar um artigo tão maravilhoso. Estou com um problema que me confunde. Eu escrevi um código para abrir uma posição.

Ele pode retornar o valor correto quando é executado em conta demo, mas retorna 0 quando é executado em uma conta real, você pode me ajudar a informar onde est в чем проблема?

Моя цель - передать свой билет на работу после открытия вакансии.


Tente verificar o erro que está sendo retornado, adicionando a linha destacada logo abaixo.

ulong OpenPosition( const ENUM_ORDER_TYPE type,
                   const double lot,
                   const long magic)
  {
   double ask = SymbolInfoDouble ( _Symbol , SYMBOL_ASK );
   double bid = SymbolInfoDouble ( _Symbol , SYMBOL_BID );
   m_trade.SetExpertMagicNumber(magic);
   if (m_trade.PositionOpen( _Symbol , type, lot, (type == ORDER_TYPE_BUY ) ? ask : bid, 0 , 0 ))
     {
       ulong deal_ticket = m_trade.ResultDeal();
       if ( HistoryDealSelect (deal_ticket))
         m_deal.Ticket(deal_ticket);
       string flag = (m_deal.DealType() == DEAL_TYPE_BUY ) ? "buy" : "sell" ;
       printf ( StringFormat ( "open deal ticket= #%%d order ticket= #%%d %%s %%.2f %%s at %%.%df" , _Digits ),
             deal_ticket, m_deal.Order(), flag, m_deal.Volume(), _Symbol , m_deal.Price());
       return (deal_ticket);
     }
   Print("Error: ", m_trade.ResultRetcode());
   return ( 0 );
  }
 
Daniel Jose #:

Проверьте ошибку, которую вы отправляете на повтор, добавив к ней логотип, расположенный ниже.

Нет, моя цель - вернуть тикет сделки.

Он может возвращать тикет сделки позиции на демо-счете, но возвращать нулевое значение на реальном счете, это для чего?

ulong OpenPosition(const ENUM_ORDER_TYPE type,
                   const double lot,
                   const long magic)
  {
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   m_trade.SetExpertMagicNumber(magic);
   if(m_trade.PositionOpen(_Symbol, type, lot, (type == ORDER_TYPE_BUY) ? ask : bid, 0, 0))
      return(m_trade.ResultDeal());
   return(0);
  }
 
Yang Wang #:

Нет, моя цель - раздобыть миллиард долларов .

ele pode retornar ticket de negociação da posição na conta Demo, mas retornar valor zero na conta Real, isso é para quê?

A linha que mandei você adicionar NÃO É... вы повторяете. NÃO É para retornar nenhum valor ... ELA SERVE PARA VERIFICAR O ERRO que está dando ao tentar abrir uma posição ... вы не должны использовать восстановленную ценность... вы должны проверить ОШИБКУ на вкладке ошибок... Veja a DOCUMENTAÇÃO

 

Здравствуйте, я слежу за этой серией статей. Мое почтение. Это действительно полезно.

Я тестирую результат и после создания ордера удаляю его. Но советник больше не позволяет создать новый ордер.

Вот этот фрагмент кода:

 case TRADE_TRANSACTION_ORDER_DELETE :
                         if (trans.order == trans.position) (*manager).PendingToPosition();
                         else (*manager).UpdatePosition(trans.position); // ORDER?
                         break ;

Он использует trans.position, который в случае удаления ордера всегда равен 0. Таким образом, это значение передается в UpdatePosition. Его первое условие отправляет его непосредственно в оператор return.

Я попытался передать тикет trans.order, но код игнорирует его, потому что m_position имеет нулевые данные, поэтому тикет равен 0 (! = m_Position.Ticket):

После этого. Когда я пытаюсь создать новый заказ, m_ticketPending имеет значение первого заказа, поэтому он не создается.

EraseTicketPending не вызывается, потому что когда приходит TRADE_ACTION_REMOVE, это условие всегда ложно, так как request .request.TRADE_ACTION_REMOVE всегда ложно.

 if ((request.symbol == _Symbol ) && (result.retcode == TRADE_RETCODE_DONE ) && (request.magic == def_MAGIC_NUMBER))

всегда ложно, поскольку request.magic равен 0.

Я выясню, будет ли это исправлено в следующих главах.

С уважением,

 

Кажется, я нашел проблему.

Когда удаление ордера происходит из пользовательского интерфейса MT5, магическое число равно 0. Поэтому я изменил условие на

 if ((request.symbol == _Symbol ) && (result.retcode == TRADE_RETCODE_DONE ) && ((request.magic == def_MAGIC_NUMBER) || request.magic == 0 ))

и, кажется, все работает так, как ожидалось.

Файлы:
 
Yang Wang #:

Привет, Даниэль Хосе.

Спасибо за такую замечательную статью. У меня есть проблема, которая ставит меня в тупик. Я написал код для открытия позиции.

Он может вернуть правильное значение при запуске на демо-счете, но он возвращает 0 при запуске на реальном счете, не могли бы вы помочь мне подсказать, где проблема?

Моя цель - вернуть тикет сделки после открытия позиции.


Эта функция открывает позицию и возвращает DEAL TICKET:

//+------------------------------------------------------------------+
//|& nbsp; &nbsp ; Test1.mq5 |
//|& nbsp; Copyright 2023, MetaQuotes Ltd.|
//|& nbsp; &nbsp ; https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
CTrade      m_trade;
CDealInfo   m_deal;
CSymbolInfo m_symbol;
input double TakeProfit = 0;
input double StopLoss   = 0;
input string PositionComment = "";
double m_point;
//+------------------------------------------------------------------+
//| Функции инициализации торговли советника & nbsp; &nbsp ; |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   EventSetMillisecondTimer(1);
   if(!m_symbol.Name(_Symbol)) // задает имя символа
      return(INIT_FAILED);
   RefreshRates();
   m_trade.SetMarginMode();
   m_trade.SetTypeFillingBySymbol(m_symbol.Name());
   m_trade.SetDeviationInPoints(INT_MAX);
   int adjust = (m_symbol.Digits() == 3 || m_symbol.Digits() == 5) ? 10 : 1;
   m_point = m_symbol.Point() * adjust;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| EA Quotation Function & nbsp; &nbsp ; ||
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
void OnTimer()
  {
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
bool RefreshRates(void)
  {
//--- частота обновления
   if(!m_symbol.RefreshRates())
     {
      Print("RefreshRates error");
      return(false);
     }
//--- защита от возврата значения "ноль".
   if(m_symbol.Ask() == 0 || m_symbol.Bid() == 0)
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|& nbsp; &nbsp nbsp; &nbsp; &nbsp; &nbsp nbsp; |
//+------------------------------------------------------------------+
ulong OpenPosition(const ENUM_ORDER_TYPE type,
                   const double volume,
                   const long magic)
  {
   ulong deal_ticket = 0;
   double sl = 0;
   double tp = 0;
   m_trade.SetExpertMagicNumber(magic);
   RefreshRates();
   double ask = m_symbol.Ask();
   double bid = m_symbol.Bid();
   switch(type)
     {
      case ORDER_TYPE_BUY:
         if(TakeProfit > 0)
            tp = m_symbol.NormalizePrice(ask + TakeProfit * m_point);
         if(StopLoss > 0)
            sl = m_symbol.NormalizePrice(bid - StopLoss * m_point);
         break;
      case ORDER_TYPE_SELL:
         if(TakeProfit > 0)
            tp = m_symbol.NormalizePrice(bid - TakeProfit * m_point);
         if(StopLoss > 0)
            sl = m_symbol.NormalizePrice(ask + StopLoss * m_point);
         break;
     }
   static datetime from_date = iTime(m_symbol.Name(), PERIOD_D1, 0);
   ulong time = GetTickCount64();
   if(m_trade.PositionOpen(m_symbol.Name(), type, volume, (type == ORDER_TYPE_BUY) ? ask : bid, sl, tp, PositionComment))
     {
      ulong open_execution = GetTickCount64() - time;
      do
        {
         ulong order_ticket = m_trade.ResultOrder();
         HistorySelect(from_date, TimeTradeServer());
         int total = HistoryDealsTotal();
         for(int i = total - 1; i >= 0; i--)
           {
            if(!m_deal.SelectByIndex(i))
               continue;
            if(m_deal.Symbol() != m_symbol.Name())
               continue;
            if(m_deal.Entry() != DEAL_ENTRY_IN)
               continue;
            if(m_deal.Order() != order_ticket)
               continue;
            printf(StringFormat("open  #%%I64u %%s %%.2f %%s at %%.%df done in %%I64u ms", m_symbol.Digits()),
                   m_deal.Order(), (type == ORDER_TYPE_BUY) ? "buy" : "sell", m_deal.Volume(), m_deal.Symbol(), m_deal.Price(), open_execution);
            from_date = m_deal.Time();
            deal_ticket = m_deal.Ticket();
            break;
           }
        }
      while(deal_ticket == 0);
     }
   return(deal_ticket);
  }
//+------------------------------------------------------------------+
 
OnTradeTransaction - это здорово, но у меня есть реальная проблема.
Я начал использовать его для отлова событий (открытие позиции, закрытие, модификация), как я делал это раньше в MT4 (постоянно проверяя позиции, чтобы угадать, что произошло). В этом аспекте подход MT5 очень чист.
НО ЧТО ПРОИСХОДИТ, ЕСЛИ советник падает или выключается в течение часа: он, конечно, не получит никаких событий, но когда он перезапустится, он не получит событий, которые он испортил в течение 1 часа. Поэтому, чтобы угадать, что произошло, ему придется действовать старым способом MT4, анализируя позиции, чтобы угадать, что произошло. Чтобы решить эту проблему, я должен сохранить 2 способа обнаружения событий: способ MT5 и способ MT4 в качестве резервного.
 
OnTradeTransaction занимает до 5 секунд для обнаружения событий по отложенным ордерам (модификация и удаление). Это нормально?
Для рыночных позиций это происходит мгновенно.
 
Gad Benisty OnTradeTransaction - это здорово, но у меня есть реальная проблема. Я начал использовать его для отлова событий (открытие позиции, закрытие, изменение), как я делал раньше в MT4 (постоянно проверяя позиции, чтобы угадать, что произошло). В этом аспекте подход MT5 очень чист. НО ЧТО ПРОИСХОДИТ, ЕСЛИ советник падает или выключается в течение часа: он, конечно, не получит никаких событий, но когда он перезапустится, он не получит событий, которые он испортил в течение 1 часа. Поэтому, чтобы угадать, что произошло, ему придется действовать старым способом MT4, анализируя позиции, чтобы угадать, что произошло. Чтобы решить эту проблему, я должен сохранить 2 способа обнаружения событий: способ MT5 и способ MT4 в качестве резервного.

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

Автоматический перевод применен модератором