MT5 e velocidade em ação - página 20

 
fxsaber:

Pode haver uma opção mais rápida. Mas um passo para a esquerda na condição do que precisa ser contado, e a lógica pode ter que mudar consideravelmente. Não é fácil, em geral.

CHashMap<ulong, ulong> DealsIn;  // По PositionID возвращает DealIn.


Não é caching, é indexação. Aqui está o caching (parte do código):

class DealInfo {
public:
  datetime Closed;
  ulong Ticket;
  ulong Position;
  string Symbol;
  long Type;
  long Reason;
  double Volume;
  double Price;
  double Profit;
  double Swap;
  long Magic;
};

class DealsComparer : public IComparer<DealInfo*> {
  int Compare(DealInfo* x, DealInfo* y) {
    int res = (int)(x.Closed - y.Closed);
    if (res == 0) {
      res = (int)(x.Ticket - y.Ticket);
    }
    
    return res;
  }
};

CArrayList<DealInfo*> dealsHistory;

inline bool UpdateDeals(CArrayList<DealInfo*> &deals, datetime &lastUpdated) {
  DealInfo* dealsToAdd[];
  DealsComparer comparer;
  int toAdd = 0;
  DealInfo* deal;
  
  if (!HistorySelect(lastUpdated, TimeLocal() + 12*3600)) {
    return false;
  }

  for (int i = 0, total = HistoryDealsTotal(); i < total; i++) {
    DealInfo tmp;
    ulong ticket = HistoryDealGetTicket(i);
    if (ticket == 0) continue;
    
    datetime dt = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
    if (lastUpdated < dt) {
      lastUpdated = dt;
    }

    if (HistoryDealGetInteger(ticket, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;

    ulong reason = HistoryDealGetInteger(ticket, DEAL_REASON);

    tmp.Ticket = ticket;
    tmp.Closed = dt;
    int idx = deals.BinarySearch(&tmp, &comparer);
    if (idx >= 0 && deals.TryGetValue(idx, deal) && deal != nullptr && deal.Ticket == ticket)
      continue;

    deal = new DealInfo;
    deal.Ticket = ticket;
    deal.Closed = dt;
    deal.Position = HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
    deal.Symbol = HistoryDealGetString(ticket, DEAL_SYMBOL);
    deal.Type = HistoryDealGetInteger(ticket, DEAL_TYPE);
    deal.Reason = HistoryDealGetInteger(ticket, DEAL_REASON);
    deal.Volume = HistoryDealGetDouble(ticket, DEAL_VOLUME);
    deal.Price = HistoryDealGetDouble(ticket, DEAL_PRICE);
    deal.Swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
    deal.Profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
    deal.Magic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
    
    ArrayResize(dealsToAdd, toAdd + 1, total);
    dealsToAdd[toAdd++] = deal;
  }
  
  if (toAdd > 0) {
    deals.AddRange(dealsToAdd);
    deals.Sort(&comparer);
  }
  
  return (toAdd > 0);
}

O código foi escrito apressadamente e há muito para refinar, dado o freqüente ArrayResize, mas ele atualiza exatamente o cache, ordenado por Closed. Você quer pesquisar mais tarde, use seu próprio índice. Mas você só tem que atualizar uma pequena parte cada vez.
Não me lembro porque"12*3600" está lá, não acho que todos os acordos tenham sido feitos comigo.

 
Andrey Pogoreltsev:

Não é um cache, é um índice.

Por favor, leia atentamente.

Fórum sobre comércio, sistemas automatizados de comércio e testes de estratégia comercial

MT5 e Velocidade em Ação

fxsaber, 2020.08.28 21:10

Puro MQL5 é 100x mais lento que o cache parcial (somente HistorySelectByPosition).

Arquitetonicamente, faça o cache completo - você precisa pensar cuidadosamente. Há muitas armadilhas.

Aqui está o caching (parte do código):

O código foi escrito apressadamente e tem algumas coisas para melhorar considerando o ArrayResize freqüente, mas ele atualiza o cache ordenado por Closed. Você quer pesquisar mais tarde, use seu próprio índice. Mas você só tem que atualizar uma pequena parte cada vez.

Este é apenas um exemplo de uma poupança histórica sem os artifícios do mundo real. Mesmo no MT4Orders o caching parcial é feito com uma margem de cinco segundos.

Não me lembro porque"12*3600" está lá, não acho que todos os ofícios tenham sido emitidos para mim.

Sempre definir INT_MAX.

 
fxsaber:

Arquitetonicamente, fazer o caching completo requer muita reflexão. Há muitas armadilhas.

Não há realmente nada de complicado aí. Você pode fazer uma amostragem com horas para economizar, por exemplo, para todas as encomendas que possam aparecer na história retroativamente (o que é muito estranho, por sinal).

Este é apenas um exemplo de economia de histórico sem qualquer artifício em tempo real. Mesmo no MT4Orders o caching parcial é feito com uma margem de cinco segundos.

Sempre definir INT_MAX.

Eu não entendo o objetivo do turno. Como se houvesse um carimbo de horário atual, eu quero me relacionar com ele e é estranho que você tenha que especificar um horário que não existe. Eu meio que quero uma explicação lógica.

Meu cache, a propósito, funciona em contas reais com 10k+ negócios.

E os principais freios do código até agora são as funções de rede.

 
Só de passagem, pensei em resumir o que já foi sugerido aqui. Se você precisa de um desempenho super rápido a qualquer custo, ele é alcançado tradicionalmente - às custas do consumo de outros recursos, por exemplo, memória. Em outras palavras, não é necessário executar um loop em todo o histórico sempre que for necessário calcular algum valor agregado (não pode ser apenas a duração total), e calcular tudo antecipadamente na "matriz" (ou outra estrutura de dados otimizada) e depois apenas aumentar. E uma solicitação de um valor por identificador de posição (ou qualquer parâmetro, pelo qual a quebra é feita) degenerará em uma referência de célula de memória. Na forma mais geral - é algo como OLAP (embora minha implementação não tenha terminado a atualização on-line), mas para um problema particular pode ser feito e mais simples.
 

No último beta 2588, o recurso HistorySelect está muito bem armazenado em cache e quase sempre (exceto pela primeira vez) é gratuito.

É provável que façamos uma série de outras melhorias com o lançamento.



Como expliquei anteriormente, no MT5 não há custo extra na criação automática de instantâneos de mercado para cada EA antes de cada evento, como foi feito no MT4. Isto reduz os atrasos e permite que os robôs funcionem mais rapidamente. Cada desenvolvedor pergunta exatamente do que ele precisa.

Portanto, você deve entender claramente que a abordagem de "chamar a HistorySelect sobre toda a história, e depois fazer imediatamente outra seleção de HistorySelectByPosition" irá matar os esconderijos da história previamente criados. Este é um tiro no pé.


Após o lançamento, iniciaremos um grande trabalho para adicionar novas funções MQL5 mais eficientes e abrir estruturas de dados de pedidos/comércio nativas, para que possamos simplificar e acelerar algotrading.

 
Renat Fatkhullin:

No último 2588 beta, a função HistorySelect está muito bem armazenada em cache e quase sempre (exceto na primeira vez) é gratuita.

#include <fxsaber\Benchmark.mqh> // https://c.mql5.com/3/321/Benchmark.mqh

input int inAlertTime = 1; // Нижний порог в миллисекундах

#define _B2(A) _B(A, inAlertTime)
#define  ALERT(A) Alert(#A + " = " + (string)(A))

void OnInit()
{
  if (HistorySelect(0, INT_MAX))
  {
    ALERT(HistoryDealsTotal());
    ALERT(HistoryOrdersTotal());
  }
}

void OnTick()
{
  static int i = 0;
  
  ALERT(i++);
  
  _B2(HistorySelect(TimeCurrent(), INT_MAX));
  _B2(HistorySelect(0, INT_MAX));
}


Resultado.

2020.09.01 22:56:46.089 Test6 (EURAUD,M1)       Alert: HistoryDealsTotal() = 9435
2020.09.01 22:56:46.089 Test6 (EURAUD,M1)       Alert: HistoryOrdersTotal() = 12529
2020.09.01 22:56:46.575 Test6 (EURAUD,M1)       Alert: i++ = 0
2020.09.01 22:56:46.579 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:47.424 Test6 (EURAUD,M1)       Alert: i++ = 1
2020.09.01 22:56:47.428 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:47.765 Test6 (EURAUD,M1)       Alert: i++ = 2
2020.09.01 22:56:47.768 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:47.902 Test6 (EURAUD,M1)       Alert: i++ = 3
2020.09.01 22:56:47.906 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:48.453 Test6 (EURAUD,M1)       Alert: i++ = 4
2020.09.01 22:56:48.456 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 3 ms.
2020.09.01 22:56:48.516 Test6 (EURAUD,M1)       Alert: i++ = 5
2020.09.01 22:56:48.521 Test6 (EURAUD,M1)       Alert: Time[Test6.mq5 25: HistorySelect(0,INT_MAX)] = 4 ms.


Em cada tique há um problema.


ZY instalado Win10, LatencyMon mostra que tudo está bem.

 
No momento, vejo que em 99% dos casos somente o HistorySelect(0, INT_MAX) deve ser utilizado. Tente não usar outras opções.
 
Renat Fatkhullin:

Após o lançamento, iniciaremos muito trabalho para adicionar novas funções MQL5 mais eficientes e abrir estruturas de dados de ordem/ comércio nativas para que o comércio algorítmico possa ser simplificado e acelerado.

MqlDeal, MqlOrder e MqlPosition seria ótimo. Pode até se tornar mais simples.

 
fxsaber:
No momento, vejo que em 99% dos casos devemos usar apenas o HistorySelect(0, INT_MAX). Tente não utilizar as outras opções.

Se eu tenho centenas de milhares de pedidos em minha história, será também mais rápido do que fazer a história de última hora?

Então eu tenho que passar por toda essa história? Isso não faz sentido.

 
Dmi3:

Se eu tenho centenas de milhares de pedidos em minha história, é também mais rápido do que fazer a história de última hora?

Deixou apenas a variante 0-INT_MAX em robôs de combate. Parou de notar os atrasos.