Bibliotecas: MT4Orders - página 82

 

Não consigo entender se é possível resolver esse problema com velocidade de teste sem usar as funções de linguagem do MT5. O problema surgiu há muito tempo, mas só agora consegui resolvê-lo.

Build 3802, __MT4ORDERS__ "2022.07.20"

Função condicional chamada no Expert Advisor em cada tick. Teste no testador de estratégia mt5.

	 datetime get_last_order_close_time () {
                datetime last_close_time=0;

                for ( int i = OrdersHistoryTotal() - 1; i >= 0; i-- ) {
                        if ( !OrderSelect ( i, SELECT_BY_POS, MODE_HISTORY )) {
                                continue;
                        }
         		 last_close_time=OrderCloseTime();
		        return last_close_time;
                }

                return last_close_time;
        }

Onúmero de negócios é de cerca de 26 mil. O tempo de teste é de cerca de 1 hora e 10 minutos.

Se removermos a função do código, o tempo de teste será de cerca de 17 minutos.

Reescrevi a mesma função usando a funcionalidade do MT5:

	 datetime get_last_order_close_timeMT5 () {
                datetime last_close_time=0;
                HistorySelect(0,TimeCurrent()); 

                for ( int i = HistoryDealsTotal() - 1; i >= 0; i-- ) {
                        
                        ulong ticket=HistoryDealGetTicket(i);
                        
                        if((ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT)
                        {
                           last_close_time=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
                           return last_close_time;
                        }                       
                }

                return last_close_time;
        }

O tempo de teste é de cerca de 18 minutos.

Reverto a biblioteca para a versão __MT4ORDERS__ "2020.01.12" e compilo o Expert Advisor no build 2980.

O tempo do Expert Advisor com a função get_last_order_close_time é de cerca de 20 minutos.

 
elavr #:

Não consigo ver se esse problema de velocidade de teste pode ser resolvido sem o uso dos recursos de idioma do MT5.

Em todos os três casos, o resultado final é o mesmo?
 
fxsaber #:
Em todos os três casos, o resultado final é o mesmo?

Sim, com certeza.

Isso começou a acontecer depois de outra atualização do terminal. Eu não alterei sua biblioteca e compilei o bot no terminal antigo para que ele funcionasse como antes.

 
elavr #:

Isso começou a acontecer depois de outra atualização do terminal. Não alterei sua biblioteca e compilei o bot no terminal antigo para que ele funcionasse como antes.

Fiz algumas pesquisas.


Expert Advisor.

#include <MT4Orders.mqh> // https://www.mql5.com/pt/code/16006

#define  VIRTUAL_SNAPSHOT_REFRESHTIME 1000
#include <fxsaber\Virtual\Virtual.mqh> // https://www.mql5.com/pt/code/22577

input int inMod = 5;
input int inRange = 0;
input bool inVirtual = false;

const bool Init = inVirtual ? VIRTUAL::SelectByHandle(VIRTUAL::Create()) : false;;

#define  Bid SymbolInfoDouble(_Symbol, SYMBOL_BID)
#define  Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)

ulong lOnTester = 0;

void OnTick()
{  
  static int i = 0;
  
  VIRTUAL::NewTick();
  
  if (!(i++ % inMod))
    OrderClose(OrderSend(_Symbol, OP_BUY, 0.1, Ask, 0, 0, 0), 0.1, Bid, 0);
    
// VIRTUAL::Snapshot(); 
  lOnTester += LastCloseTimeMQL4() % 100;
  
// lOnTester += LastCloseTimeMQL5() % 100;
}

double OnTester()
{
  if (HistorySelect(0, INT_MAX))
    Print(HistoryDealsTotal());
  
  return((double)lOnTester);
}

Em cada tick do inMod, uma posição é aberta e fechada. E, em cada tick, o OrderCloseTime é calculado.

OnTester serve como um critério para verificar a identidade dos resultados em diferentes configurações.


Funções correspondentes de trabalho com histórico.

datetime LastCloseTimeMQL4()
{  
  datetime Res = 0;
  
  for (int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) && (OrderType() <= OP_SELL))
    {
      Res = OrderCloseTime();
      
      break;
    }

  return(Res);
}

datetime LastCloseTimeMQL5()
{
  datetime Res = 0;
  
  if (HistorySelect(0, INT_MAX))
    for (int i = HistoryDealsTotal() - 1; i >= 0; i--)
    {
      const ulong Ticket = HistoryDealGetTicket(i);
      
      if (HistoryDealGetInteger(Ticket, DEAL_ENTRY) != DEAL_ENTRY_IN)
      {
        Res = (datetime)HistoryDealGetInteger(Ticket, DEAL_TIME);
        
        break;
      }
    }
    
  return(Res);
}

#define  MACROS(A, B)          \
  datetime A##_2()            \
  {                           \
    static datetime Res = 0;  \
    static int PrevTotal = 0; \
                              \
    const int Total = B;      \
                              \
    if (PrevTotal != Total)   \
    {                         \
      Res = A();              \
                              \
      PrevTotal = Total;      \
    }                         \
                              \
    return(Res);              \
  }

// LastCloseTimeMQL4_2
MACROS(LastCloseTimeMQL4, OrdersHistoryTotal())

// LastCloseTimeMQL5_2
MACROS(LastCloseTimeMQL5, HistorySelect(0, INT_MAX) ? HistoryDealsTotal() : 0)


Metodologia.

Uma única execução nas configurações como na fonte (inMod = 5).

2023.07.06 23:59:59   28179
final balance 99996992.20 pips
OnTester result 2761115
EURUSD,M1: 274413 ticks, 70443 bars generated. Environment synchronized in 0:00:00.021. Test passed in 0:00:03.712 (including ticks preprocessing 0:00:00.031).

28 mil transações, quase quatro segundos. Como precisamos medir o desempenho no modo de otimização, todas as leituras de desempenho a seguir foram feitas dessa forma.

shortest pass 0:00:02.875, longest pass 0:00:03.094, average pass 0:00:02.919

Com recompilação obrigatória antes da execução e apenas um agente habilitado para execução sequencial de passes.


Desempenho.

Tabela de desempenho (tempo de execução em milissegundos) para b3815 e b2958.

Construção MT5 LastCloseTimeMQL4 LastCloseTimeMQL5 LastCloseTimeMQL4_2 LastCloseTimeMQL4+VIRTUAL::Snapshot LastCloseTimeMQL4+VirtualTester
b3815 2875 113 708 732 45
b2958 2718 107 675 715 50

O MT4Orders de 20.07.2022 foi usado em todos os lugares.


Conclusões.

  • O OrderSelect é lento. Isso se deve ao fato de que o OrderSelect calcula absolutamente todos os dados da ordem selecionada a partir do histórico (o OrderPrint é gratuito). Essa é uma ótima combinação das funções HistoryOrderGet* e HistoryDealGet*, que lida com qualquer complexidade de situações de mercado e com um grande número de armadilhas. Dê uma olhada em MT4ORDERS::GetHistoryPositionData(). Essa é uma função única para o Terminal e para o Testador, mas é especialmente acelerada para o Testador, porque muitas armadilhas do Terminal não estão presentes no Testador. Exemplos de configurações: uma, duas, três.
  • Dessa forma, é necessário reduzir as chamadas de OrderSelect para ordens históricas. Isso pode ser feito das maneiras indicadas nas três colunas da direita da tabela.
  • O trabalho mais rápido (antes da MQL5 pura) é a negociação em um ambiente virtual. Eu recomendaria a otimização no ambiente virtual, e passes únicos - fora dele.
  • O desempenho do Tester b2958 e do b3815 é quase idêntico.
  • Não recomendo vincular a lógica do TS para o testador ao histórico de negociação com referência a ele. Por exemplo, você pode saber o OrderCloseTime do seu TS sem fazer referência ao histórico.
 
elavr #:

O tempo de teste é de aproximadamente 1 hora e 10 minutos.

Reverto a biblioteca para a versão __MT4ORDERS__ "2020.01.12" e compilo o Expert Advisor no build 2980.

O tempo de funcionamento do Expert Advisor com a função get_last_order_close_time é de cerca de 20 minutos.

Experimente o código acima em uma versão antiga da biblioteca (eu não a tenho).

Se houver alguma diferença no desempenho, envie-o para a PM.

 
Muito obrigado! Vou dar uma olhada na próxima semana!
 

É possível que haja um vazamento de memória ou um uso de memória maior do que o necessário? Talvez em this.tickets ou em this.amount (::ArrayResize) ou em outro lugar?

O tamanho das matrizes só aumenta com o tempo. Isso é obrigatório? É possível esvaziá-las ou talvez não carregar todas as negociações anteriores?

 
pcdeni #:

É possível que haja um vazamento de memória ou um uso de memória maior do que o necessário? Talvez em this.tickets ou em this.amount (::ArrayResize) ou em outro lugar?

O tamanho das matrizes só aumenta com o tempo. Isso é obrigatório? É possível esvaziá-las ou talvez não carregar todas as negociações anteriores?

A biblioteca não armazena totalmente em cache os dados do histórico. A falta de memória é possível apenas na teoria, mas não na prática.

Escreva um script que classifique todo o histórico de negociação usando a biblioteca e veja quanta memória é consumida.

 
fxsaber #:

A biblioteca não armazena totalmente em cache os dados do histórico. A falta de memória é possível apenas na teoria, mas não na prática.

Escreva um script que classifique todo o histórico de negociação usando a biblioteca e veja quanta memória é consumida.

No teste de estratégia, o EA está usando 25 GB por núcleo.

editar: é um teste de período de 1 ano, portanto, não é muito.
 
pcdeni #:

no teste de estratégia, o ea está usando 25 GB por núcleo.

editar: é um teste de período de 1 ano, portanto, não muito.

A biblioteca (MT4Orders.mqh) não consome essa quantidade de memória. Você pode executar, por exemplo, este EA para ver por si mesmo.

Provavelmente, você está trabalhando com indicadores de forma incorreta: você cria novos identificadores de indicadores, mas não exclui os antigos.