Discussão do artigo "Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte X)" - página 3

 
Alexey Viktorov:

Artem, diga-me qual é a função da seção de código destacada.

Como esse código pode ser executado se o cronômetro não estiver ativado?

Mas se essa seção de código for excluída, as mensagens de evento não serão impressas. Mas tudo funciona com ela.

E eu gostaria de poder obter um tíquete, preços e talvez algumas outras propriedades de posições e ordens junto com a mensagem do evento.

Acho que você alterou o comentário. Em OnTimer(), o EA é verificado, não no testador:

/************************Expert tick function************************/
void OnTick()
{
//--- Inicialização do último evento de negociação
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Se estiver trabalhando no testador
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
     }
//--- Se o último evento de negociação foi alterado
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
      Comment("last_event: ",EnumToString(last_event));
      Print(__FUNCTION__, " last_event: ",EnumToString(last_event));
      //engine.ResetLastTradeEvent();
      //Print("last_event: ",EnumToString(last_event));
     }

}
/***************************Timer function***************************/
void OnTimer()
{
//--- Inicialização do último evento de negociação
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Se o trabalho não for  в тестере
   if(!MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
     }
}
/*******************************************************************/

E compare os dois manipuladores: em OnTick() o cronômetro da biblioteca é iniciado apenas no testador, e em OnTimer() o cronômetro da biblioteca é iniciado apenas não no testador - porque na MQL4 o cronômetro no testador não funciona, e nós trabalhamos com ticks no testador.

Vou dar um exemplo agora.

 
Artyom Trishkin:

Acho que você alterou o comentário.

O comentário mudou por conta própria, não mexi nele. ))))))

Eu apenas copiei em um lugar e colei em lugares diferentes e não prestei atenção. Mas depois coloquei uma negação na forma de ! e não é importante, não mexi.

Mas a pergunta continua sem resposta: se o cronômetro não é iniciado em OnInit(), qual é o objetivo do manipulador OnTimer() e por que o código nele é executado pelo menos uma vez.

Basicamente, recebi as mensagens no registro. Resta obter as propriedades dos itens. Tipo, tíquete, preços e hora de abertura, fechamento e modificação.

 
Alexey Viktorov:

E eu gostaria de poder obter tíquetes, preços e talvez algumas outras propriedades de posições e ordens junto com a mensagem do evento.

No testador, não é possível obter o tíquete da ordem do último evento em OnChartEvent() - parâmetro lparam. Em dparam, o preço é armazenado. Em sparam - símbolo.

Para obter dados no testador, você deve usar o código de evento que obtém com engine.LastTradeEvent() - porque tudo depende do evento - se for uma modificação, você precisará obter uma lista de modificações e, se for uma alteração no número de ordens, você precisará obter listas dessas novas ordens.

Precisamos adicionar o CEngine para retornar os valores necessários ao programa. Ainda não cheguei ao ponto de enviar informações ao programa - ainda estou descrevendo a preparação dos dados necessários. Em artigos futuros, chegaremos à classe que dá acesso fácil a todos os dados necessários. E agora, se precisar urgentemente, você precisa adicionar ao CEngine o retorno da lista da coleção de eventos da classe - a própria classe tem o recebimento dessa lista, aqui estão elas - todas as listas na seção pública CEventCollection:

public:
//--- Seleciona eventos da coleção com horários que variam de begin_time a end_time
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- Retorna a lista completa da coleção de eventos "como está"
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_events;                                           }
//--- Retorna uma lista com as propriedades selecionadas (1) integer, (2) real e (3) string que satisfazem o critério que está sendo comparado
   CArrayObj        *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }

Todos os eventos são armazenados na lista m_list_events, e esses métodos retornam a lista completa ou filtrada por um determinado critério.

Para obter o último evento, basta criar no CEngine um retorno dessa lista para o programa e, no programa, obter o evento desejado da lista.

Tudo isso será automatizado em breve - ainda não está na fila.

Se você ainda precisar fazer uma muleta, a discussão será em particular. Isso não vale a pena aqui - não se aplica à biblioteca, pois ela está em desenvolvimento, e mais trabalho normal e adequado será feito para colocar todos e quaisquer eventos necessários no programa.

 
Alexey Viktorov:

O comentário mudou por conta própria, não mexi nele. )))))

Eu apenas copiei em um lugar e colei em lugares diferentes e não prestei atenção. Mas depois coloquei uma negação na forma de ! e não mexi no que era importante.

Mas a pergunta continua sem resposta: se o cronômetro não é iniciado em OnInit(), qual é o objetivo do manipulador OnTimer() e por que o código nele é executado pelo menos uma vez.

Basicamente, recebi as mensagens no registro. Resta obter as propriedades dos itens. Tipo, tíquete, preços e hora de abertura, fechamento e modificação.

Explique o que você quer dizer? Você está falando sobre a criação de um cronômetro? Ele é criado no construtor do CEngine:

//+------------------------------------------------------------------+
//| Construtor CEngine|
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT)
  {
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
         ::Print(DFUN,"Falha ao criar o cronômetro. Erro: ","Could not create timer. Error: ",(string)::GetLastError());
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
         ::Print(DFUN,"Falha ao criar o cronômetro. Erro: ","Could not create timer. Error: ",(string)::GetLastError());
   #endif 
  }
//+------------------------------------------------------------------+
 
Artyom Trishkin:

Não no testador, você pode obter o tíquete do pedido do último evento em OnChartEvent() - parâmetro lparam. Em dparam, o preço é armazenado. Em sparam - símbolo.

Eu já o encontrei, obrigado. E, ao adquirir um ingresso, você pode obter tudo o que precisa. Exceto por qual preço houve uma modificação. Ou fazer uma verdadeira muleta para saber o preço antes da modificação. Em princípio, isso ainda não é muito necessário.

 
Alexey Viktorov:

Basicamente, obtive as mensagens de registro. Só preciso obter as propriedades das posições. Tipo, tíquete, preços e horário de abertura, fechamento e modificação.

No entanto, para uma solução rápida, adicione um retorno de lista de eventos à seção pública do CEngine:

public:
   //--- Retorna uma lista de posições de mercado (1), (2) ordens pendentes e (3) ordens de mercado
   CArrayObj*           GetListMarketPosition(void);
   CArrayObj*           GetListMarketPendings(void);
   CArrayObj*           GetListMarketOrders(void);
   //--- Retorna a lista de histórico de (1) ordens, (2) ordens pendentes excluídas, (3) negociações, (4) todas as ordens a mercado de uma posição por seu identificador
   CArrayObj*           GetListHistoryOrders(void);
   CArrayObj*           GetListHistoryPendings(void);
   CArrayObj*           GetListDeals(void);
   CArrayObj*           GetListAllOrdersByPosID(const ulong position_id);
//--- Retorna uma lista de eventos
   CArrayObj*           GetListAllEvents(void)                          { return this.m_events.GetList();      }
//--- Reinicia o último evento de negociação
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Retorna (1) último evento de negociação, (2) sinalizador de conta de hedge, (3) sinalizador de operação do testador
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;      }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;              }
   bool                 IsTester(void)                            const { return this.m_is_tester;             }
//--- Cria um contador de cronômetro
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Temporizador
   void                 OnTimer(void);
//--- Construtor/Destrutor
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

No EA, adicione este código:

//+------------------------------------------------------------------+
//| Função de tique de especialista|
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Inicialização do último evento de negociação
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Se estiver trabalhando no testador
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- Se o último evento de negociação foi alterado
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
      Comment("\nlast_event: ",EnumToString(last_event));
      CArrayObj* list=engine.GetListAllEvents();
      if(list!=NULL)
        {
         if(list.Total()>0)
           {
            CEvent* event=list.At(list.Total()-1);
            if(event!=NULL)
              {
               event.Print();
              }
           }
        }
     }
//--- Se o sinalizador de finalização estiver definido
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+

E o último evento será impresso no registro

 
Artyom Trishkin:

Explique o que você quer dizer? Você está falando sobre a criação de um cronômetro? Ele é criado no construtor do CEngine:

Bem, você teve que percorrer toda a biblioteca para isso. )))

 
Alexey Viktorov:

Eu já encontrei isso, obrigado. E se você conseguir uma passagem, poderá obter tudo o que precisa. Exceto pelo preço da modificação. Ou faça uma muleta de verdade para saber o preço antes da modificação. Eu realmente não preciso disso ainda.

Eu já lhe dei o código - há tudo lá, e o preço antes da modificação também.

 
Alexey Viktorov:

Bem, você teve que percorrer toda a biblioteca para isso. )))

Não. Basta ler os artigos

 

E foi isso que aconteceu:

Enquanto eu estava executando esse código na demonstração, uma ordem de limite foi definida e excluída

443342388 2019.05.27 14:54:10 buy limit 0.01 eurusd 1.11835 0.00000 0.00000 2019.05.27 15:01:14 1.11972 cancelled 

E, de repente, durante a próxima simulação, uma posição foi modificada, uma posição foi aberta e uma posição foi fechada. Mas de onde veio o registro da exclusão de uma ordem excluída há muito tempo?

2019.05.27 18:34:11.903 00 EURUSD,H1: OnChartEvent: id=1002, event=TRADE_EVENT_PENDING_ORDER_REMOVED, lparam=443342388, dparam=1.11835, sparam=EURUSD
2019.05.27 18:34:11.903 00 EURUSD,H1: OnChartEvent: id=1024, event=TRADE_EVENT_POSITION_CLOSED, lparam=443417294, dparam=1.11933, sparam=EURUSD
2019.05.27 18:34:11.903 00 EURUSD,H1: - Отложенный ордер удалён: 2019.05.27 14:54:10.000 -
EURUSD Удалён 0.01 Buy Limit #443342388  по цене 1.11835
2019.05.27 18:34:11.903 00 EURUSD,H1: - Позиция закрыта: 2019.05.27 18:33:02.000 -
EURUSD Закрыт Sell #443417294  по цене 1.11912, профит -0.21 USD
2019.05.27 18:33:02.755 00 EURUSD,H1: OnChartEvent: id=1022, event=TRADE_EVENT_POSITION_OPENED, lparam=443417294, dparam=1.11912, sparam=EURUSD
2019.05.27 18:33:02.755 00 EURUSD,H1: - Позиция открыта: 2019.05.27 18:33:02.000 -
EURUSD Открыт 0.01 Sell #443417294 [0.01 Market order Sell #443417294]  по цене 1.11912
2019.05.27 18:29:21.913 00 EURUSD,H1: OnChartEvent: id=1050, event=TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT, lparam=443218277, dparam=1.12218, sparam=EURUSD
2019.05.27 18:29:21.913 00 EURUSD,H1: - Модифицирован TakeProfit позиции: 2019.05.27 18:27:45.000 -
EURUSD Buy #443218277:  модифицирован TakeProfit: [1.12240 --> 1.12218]