MT5 e la velocità in azione - pagina 20

 
fxsaber:

Potrebbe esserci un'opzione più veloce. Ma un passo a sinistra nella condizione di ciò che deve essere contato, e la logica potrebbe dover cambiare notevolmente. Non è facile, in generale.

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


Non è caching, è indicizzazione. Ecco il caching (parte del codice):

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);
}

Il codice è stato scritto frettolosamente e c'è molto da perfezionare, dato il frequente ArrayResize, ma aggiorna esattamente la cache, ordinata per Closed. Se vuoi cercare in seguito, usa il tuo indice. Ma dovete aggiornare solo una piccola parte ogni volta.
Non ricordo perché"12*3600" sia lì, non credo che tutte le offerte mi siano state rilasciate.

 
Andrey Pogoreltsev:

Non è una cache, è un indice.

Si prega di leggere attentamente.

Forum sul trading, sistemi di trading automatico e test di strategie di trading

MT5 e la velocità in azione

fxsaber, 2020.08.28 21:10

Il puro MQL5 è 100 volte più lento del caching parziale (solo HistorySelectByPosition).

Architettonicamente fare il caching completo - è necessario pensare attentamente. Ci sono molte insidie.

Ecco il caching (parte del codice):

Il codice è stato scritto frettolosamente e ha alcune cose da migliorare considerando le frequenti ArrayResize, ma aggiorna la cache ordinata per Closed. Se vuoi cercare in seguito, usa il tuo indice. Ma dovete aggiornare solo una piccola parte ogni volta.

Questo è solo un esempio di salvataggio della storia a testa alta senza gli espedienti del mondo reale. Anche in MT4Orders il caching parziale è fatto con un margine di cinque secondi...

Non ricordo perché c'è"12*3600", non credo che tutti gli scambi siano stati rilasciati a me.

Imposta sempre INT_MAX.

 
fxsaber:

Architettonicamente, fare il caching completo richiede un sacco di pensieri. Ci sono molte insidie.

Non c'è davvero niente di complicato. Si può fare un campionamento con ore di anticipo, per esempio, per tutti gli ordini che potrebbero apparire nella storia in modo retroattivo (il che è molto strano, tra l'altro).

Questo è solo un esempio di salvataggio della storia a testa alta, senza espedienti in tempo reale. Anche in MT4Orders il caching parziale è fatto con un margine di cinque secondi...

Imposta sempre INT_MAX.

Non capisco il senso dello spostamento. Come se ci fosse un timestamp attuale, voglio ottenere relativo ad esso ed è strano che si debba specificare un tempo che non esiste. Vorrei una spiegazione logica.

La mia cache, tra l'altro, funziona su conti reali con 10k+ scambi.

E i principali freni nel codice finora sono le funzioni di rete.

 
Di passaggio, ho pensato di riassumere ciò che è già stato accennato qui. Se avete bisogno di prestazioni superveloci ad ogni costo, si ottiene tradizionalmente - a spese del consumo di altre risorse, ad esempio la memoria. In altre parole, non è necessario eseguire un ciclo attraverso l'intera storia ogni volta quando è richiesto di calcolare qualche valore aggregato (può essere non solo la durata totale), e calcolare tutto in anticipo nella "matrice" (o altra struttura dati ottimizzata) e poi solo aumentare. E una richiesta di un valore per identificatore di posizione (o qualsiasi parametro, in base al quale viene fatta la scomposizione) degenera in un riferimento di cella di memoria. Nella forma più generale - è qualcosa come OLAP (anche se la mia implementazione non ha finito l'aggiornamento online), ma per un problema particolare può essere fatto e più semplice.
 

Nell'ultima beta di 2588, la funzione HistorySelect è molto ben memorizzata e quasi sempre (tranne la prima volta) è libera.

Probabilmente faremo una serie di altri miglioramenti per il rilascio.



Come ho spiegato prima, in MT5 non c'è nessun costo aggiuntivo nella creazione automatica di istantanee di mercato per ogni EA prima di ogni evento, come veniva fatto in MT4. Questo riduce i ritardi e permette ai robot di correre più velocemente. Ogni sviluppatore chiede esattamente ciò di cui ha bisogno.

Quindi, dovreste capire chiaramente che l'approccio di "chiamare HistorySelect su tutta la storia, e poi fare immediatamente un'altra selezione di HistorySelectByPosition" ucciderà le cache della storia create in precedenza. Questo è un colpo al piede.


Dopo il rilascio inizieremo un grande lavoro sull'aggiunta di nuove funzioni MQL5 più efficienti e strutture dati native aperte per ordini e compravendite, in modo da poter semplificare e velocizzare l'algotrading.

 
Renat Fatkhullin:

Nell'ultima beta 2588, la funzione HistorySelect è molto ben memorizzata e quasi sempre (tranne la prima volta) è libera.

#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));
}


Risultato.

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.


Su ogni zecca c'è un problema.


ZY ha installato Win10, LatencyMon mostra che tutto è a posto.

 
Al momento vedo che nel 99% dei casi si dovrebbe usare solo HistorySelect(0, INT_MAX). Cercate di non usare altre opzioni.
 
Renat Fatkhullin:

Dopo il rilascio inizieremo un sacco di lavoro per aggiungere nuove funzioni MQL5 più efficienti e aprire le strutture dati native di ordini e transazioni in modo che il trading algoritmico possa essere semplificato e accelerato.

MqlDeal, MqlOrder e MqlPosition sarebbero ottimi. Potrebbe anche diventare più semplice.

 
fxsaber:
Al momento, vedo che nel 99% dei casi dovremmo usare solo HistorySelect(0, INT_MAX). Cerca di non usare le altre opzioni.

Se ho centinaia di migliaia di ordini nella mia storia, sarà anche più veloce che prendere la storia dell'ultimo minuto?

Quindi devo passare attraverso tutta questa storia? Non ha senso.

 
Dmi3:

Se ho centinaia di migliaia di ordini nella mia storia, è anche più veloce che prendere la storia dell'ultimo minuto?

Ha lasciato solo la variante 0-INT_MAX nei robot da combattimento. Ho smesso di notare i ritardi.