Accettazione di ordini SL/TP

 

Questo ramo si occuperà degli ordini che vengono creati come risultato dell'attivazione dei livelli SL/TP delle posizioni aperte.

Ho scritto una funzione complicata ma utile per ottenere un tick che serve come trigger per un ordine SL/TP specificato.

#define  SEARCH_TICK(A, B)                             \
{                                                     \
  if (!(Ticks[Pos].##A  B Price) && ((Pos <= 1) ||     \
      (!StopLevel && (Ticks[Pos - 1].##A == Price)))) \
    Tick = Ticks[Pos];                                \
  else                                                \
  {                                                   \
    while ((Pos >= 0) && !(Ticks[Pos].##A  B Price))   \
      Tick = Ticks[Pos--];                            \
                                                      \
    if (!Tick.time)                                   \
    {                                                 \
      while ((Pos >= 0) && (Ticks[Pos].##A  B Price))  \
        Pos--;                                        \
                                                      \
      while ((Pos >= 0) && !(Ticks[Pos].##A  B Price)) \
        Tick = Ticks[Pos--];                          \
    }                                                 \
  }                                                   \
}

// Получение тика, который акцептировал TP/SL-ордер.
bool GetAcceptedTick( const ulong Ticket, MqlTick &Tick, const bool PrintFlag = false )
{
  bool Res = false;

  if (!IsStopped() && HistoryOrderSelect2(Ticket))
  {
    const ENUM_ORDER_REASON Reason = (ENUM_ORDER_REASON)HistoryOrderGetInteger(Ticket, ORDER_REASON);

    if ((Reason == ORDER_REASON_TP) || (Reason == ORDER_REASON_SL))
    {
      const long CreateTime = HistoryOrderGetInteger(Ticket, ORDER_TIME_SETUP_MSC);
      const long DoneTime = HistoryOrderGetInteger(Ticket, ORDER_TIME_DONE_MSC);
      const string Symb = HistoryOrderGetString(Ticket, ORDER_SYMBOL);
      const ENUM_ORDER_STATE State = (ENUM_ORDER_STATE)HistoryOrderGetInteger(Ticket, ORDER_STATE);
      const ENUM_ORDER_TYPE Type = (ENUM_ORDER_TYPE)HistoryOrderGetInteger(Ticket, ORDER_TYPE);
      const double Price = HistoryOrderGetDouble(Ticket, ORDER_PRICE_OPEN);

      const ulong TicketOpen = HistoryOrderGetInteger(Ticket, ORDER_POSITION_ID);

      if (SymbolInfoInteger(Symb, SYMBOL_EXIST) && TicketOpen && HistoryOrderSelect2(TicketOpen))
      {
      #define  TOSTRING(A) ", " + #A + " = " + (string)(A)
        const int digits = (int)SymbolInfoInteger(Symb, SYMBOL_DIGITS);

        const int StopLevel = (int)SymbolInfoInteger(Symb, SYMBOL_TRADE_STOPS_LEVEL);
        long PositionCreated = HistoryOrderGetInteger(TicketOpen, ORDER_TIME_DONE_MSC);
        
        // Условие может сработать при частичном исполнении.
        if ((PositionCreated >= CreateTime) && HistorySelectByPosition(TicketOpen) && HistoryDealsTotal())
        {
          PositionCreated = HistoryDealGetInteger(HistoryDealGetTicket(0), DEAL_TIME_MSC);
          
          HistoryOrderSelect(TicketOpen); // Не HistoryOrderSelect2, т.к. нужно HistoryOrdersTotal() <= 1.
        }

      #define  HOUR (3600 * 1000)
        long From = MathMax(PositionCreated,    // Время открытия позиции
                            CreateTime - HOUR); // Час - просто с запасом.

        MqlTick Ticks[];

        ResetLastError();
        int Pos = CopyTicksRange(Symb, Ticks, COPY_TICKS_INFO, From, CreateTime) - 1;

        if ((Pos < 0) && !_LastError && (From == PositionCreated))
          Pos = CopyTicksRange(Symb, Ticks, COPY_TICKS_INFO, From -= HOUR, CreateTime) - 1;

        if (Pos >= 0)
        {
          const MqlTick LastTick = Ticks[Pos];

          Tick.time = 0;

          if (Type == ORDER_TYPE_BUY)
          {
            if (Reason == ORDER_REASON_TP)
              SEARCH_TICK(ask, >)
            else
              SEARCH_TICK(ask, <)
          }
          else if (Reason == ORDER_REASON_TP)
            SEARCH_TICK(bid, <)
          else
            SEARCH_TICK(bid, >)

          if (!(Res = /*(Pos >= 0) && */ Tick.time))
            Alert(__FUNCSIG__ + ": Error!"); // Ошибка при расчетах
          else if (PrintFlag) // Выводим найденный тик.
          {
            Print("Last Tick " + TickToString(LastTick, digits));

            Print("Accepted Tick " + TickToString(Tick, digits));
            Print("Accepted Length = " + (string)(CreateTime - Tick.time_msc) + " ms.");
          }
        }
        else // В случае ошибки CopyTicks - сообщаем.
          Alert(__FUNCSIG__ + ": CopyTicksRange(" + Symb + ", " + TimeToString(From) + ", " +
                                                TimeToString(CreateTime) + ") = " + (string)(Pos + 1) + TOSTRING(_LastError));

        if (PrintFlag || !Res) // Распечатываем данные ордера.
          Print("Order " + (string)Ticket + " " + EnumToString(Type) + " " + Symb + " " + TimeToString(CreateTime) + " " +
                           DoubleToString(Price, digits) + " " + EnumToString(Reason) + " " + EnumToString(State) + " " +
                           TimeToString(DoneTime) + ", Position " + (string)TicketOpen + " created " +
                           TimeToString(PositionCreated) + TOSTRING(StopLevel) + "\n");
      }
    }
  }

  return(Res);
}

// Преобразование времени в миллисекундах в строку.
string TimeToString( const long time, const int FlagTime = TIME_DATE | TIME_SECONDS)
{
  return(TimeToString((datetime)time / 1000, FlagTime) + "." + IntegerToString(time % 1000, 3, '0'));
}

// Преобразование тика в строку.
string TickToString( const MqlTick &Tick, const int digits )
{
  return(TimeToString(Tick.time_msc) + " " + DoubleToString(Tick.bid, digits) + " " + DoubleToString(Tick.ask, digits));
}

// Правильный выбор исторического ордера.
bool HistoryOrderSelect2( const ulong Ticket)
{
  return(((HistoryOrderGetInteger(Ticket, ORDER_TICKET) == Ticket) || HistoryOrderSelect(Ticket)));
}


L'applicazione di questa grande funzione è la ragione per la creazione di questo ramo. Sono sicuro di non aver preso tutti i bug nel codice, ma per il bene della storia e per capire che è davvero difficile, ho dato un elenco completo.

 

Indagando sull'esecuzione degli ordini TP, ho notato che alcuni ordini TP sono stati creati con un ritardo significativo rispetto ai tick che li hanno accettati.

Il debriefing ha mostrato che questa situazione si ripete non solo su diversi broker, ma anche nella situazione in cui il trading viene fatto sul terminale, che è sulla stessa macchina del Trading Server. Cioè con un ping molto basso e l'unico account di trading per il Trading Server.



Scrivere la funzioneGetAcceptedTick ci ha permesso di indagare a fondo il problema e di dimostrarlo in modo costruttivo.


Script.

Quindi, il seguente script si trova nel trailer.

// Скрипт выводит самое длительное или конкретное акцептирование SL/TP-ордера.
#property script_show_inputs

input datetime inFrom = D'2020.01.01'; // С какого времени проверять ордера
input ulong inTicket = 0;              // Отдельно проверяемый тикет

// Возвращает самый медленный TP/SL-ордер с определенной даты.
ulong GetSlowestOrder( const datetime From );

// Распечатывает подробности акцепта SL/TP-ордера.
void PrintOrder( const ulong MaxTicket );

void OnStart()
{
  Print("\n\nStart " + MQLInfoString(MQL_PROGRAM_NAME) + TOSTRING(inFrom) + TOSTRING(inTicket) + "\n");
  
  PrintOrder(inTicket ? inTicket : GetSlowestOrder(inFrom));
}


Il risultato dell'esecuzione su MQ-Demo.

Total Orders (from 2020.09.01 00:00:00) = 58493, calculated = 439
Calculation time = 00:00:11.328, Performance = 38.0 orders/sec.

ServerName: MetaQuotes-Demo

Last Tick 2020.09.30 19:07:32.917 1.80181 1.80205
Accepted Tick 2020.09.30 19:07:32.716 1.80178 1.80202
Accepted Length = 357 ms.
Order 726444166 ORDER_TYPE_BUY GBPAUD 2020.09.30 19:07:33.073 1.80206 ORDER_REASON_TP ORDER_STATE_FILLED 2020.09.30 19:07:33.082, Position created 2020.09.30 17:21:17.933, StopLevel = 0

Orders (2) before 726444166 with PositionID = 725926764:
------------------------
Checked Orders = 0
------------------------


Lo script sostiene di aver trovato un ordine TP e un tick che è stato l'innesco della sua creazione (evidenziato in colore nel testo). Sembrerebbe che se il prezzo ha raggiunto il livello TP di una posizione aperta sul server di trading (specialmente sulla demo), l'ordine TP corrispondente dovrebbe essere creato (non necessariamente eseguito) immediatamente. Tuttavia, in questa situazione, non è successo immediatamente, ma dopo 357 millisecondi!


Lasciatemi dire subito che anche un ritardo di un millisecondo non è troppo poco. Essere nel tempo è un verbo significativo in algotrading.


Verifica.

Non fidiamoci ciecamente dello script e controlliamo questa situazione manualmente. Quindi, ecco il nostro ordine.


Il corrispondente tick di accettazione trovato dallo script può essere visto qui.


La freccia mostra che tra i tick è nato l'ordine TP. Gli screenshot mostrano chiaramente che lo script era giusto e la creazione dell'ordine TP è avvenuta con un enorme ritardo da parte del server commerciale.


Linea di fondo.

Ora c'è uno strumento che mostra i valori di ritardo sul lato del Trade Server quando si fa trading tramite i livelli TP/SL. Al momento sono enormi. E questo è sicuramente un grave svantaggio della piattaforma che deve essere corretto.

Sfortunatamente, è impossibile registrare l'accettazione degli ordini in sospeso, poiché questa informazione non è disponibile sul lato del terminale. Ma quasi invariabilmente, la presenza di ritardi significativi sul lato dell'ordine TP/SL non può che influenzare i ritardi nell'esecuzione degli ordini. Poiché la causa sembra essere della stessa natura.


Tutto sommato, la piattaforma MT5 è attualmente in ritardo del 100% proprio in queste situazioni. E richiede correzioni fino a quando non c'è zero lag.


Vi incoraggio a condividere i risultati dei vostri conti. Contribuire a migliorare MT5!

File:
 
fxsaber:

Incoraggiarti a condividere i risultati dei tuoi conti. Aiutate a migliorare MT5!

Total Orders (from 2020.11.01 00:00:00) = 21725, calculated = 10465
Calculation time = 00:04:33.609, Performance = 38.0 orders/sec.

ServerName: RannForex-Server

Last Tick 2020.11.16 00:34:35.201 104.630 104.640
Accepted Tick 2020.11.16 00:34:06.309 104.627 104.639
Accepted Length = 28894 ms.
Order 1715452 ORDER_TYPE_SELL USDJPY 2020.11.16 00:34:35.203 104.627 ORDER_REASON_TP ORDER_STATE_REJECTED 2020.11.16 00:34:35.217, Position created 2020.11.16 00:33:51.196, StopLevel = 0

Orders (4) before 1715452 with PositionID = 1715287:
-----------------------
Last Tick 2020.11.16 00:34:06.309 104.627 104.639
Accepted Tick 2020.11.16 00:34:06.309 104.627 104.639
Accepted Length = 3 ms.
Order 1715425 ORDER_TYPE_SELL USDJPY 2020.11.16 00:34:06.312 104.625 ORDER_REASON_TP ORDER_STATE_REJECTED 2020.11.16 00:34:06.327, Position created 2020.11.16 00:33:51.196, StopLevel = 0

Checked Orders = 1
------------------------

28 secondi di ritardo! Probabilmente è meglio contattare già il broker in queste situazioni.

 
2020.11.25 02:42:17.718 CheckOrders (EURUSD,H1) ServerName: ICMarkets-MT5
2020.11.25 02:42:17.718 CheckOrders (EURUSD,H1) 
2020.11.25 02:42:17.718 CheckOrders (EURUSD,H1) Last Tick 2020.11.24 23:00:49.327 1.33569 1.33570
2020.11.25 02:42:17.718 CheckOrders (EURUSD,H1) Accepted Tick 2020.11.24 23:00:49.327 1.33569 1.33570
2020.11.25 02:42:17.718 CheckOrders (EURUSD,H1) Accepted Length = 7 ms.
2020.11.25 02:42:17.718 CheckOrders (EURUSD,H1) Order 106887648 ORDER_TYPE_BUY GBPUSD 2020.11.24 23:00:49.334 1.33572 ORDER_REASON_TP ORDER_STATE_FILLED 2020.11.24 23:00:49.830, Position created 2020.11.24 22:57:47.071, StopLevel = 0
2020.11.25 02:42:17.718 CheckOrders (EURUSD,H1) 
2020.11.25 02:42:17.719 CheckOrders (EURUSD,H1) Orders (2) before 106887648 with PositionID = 106886713:
2020.11.25 02:42:17.719 CheckOrders (EURUSD,H1) ------------------------
2020.11.25 02:42:17.719 CheckOrders (EURUSD,H1) Checked Orders = 0
2020.11.25 02:42:17.719 CheckOrders (EURUSD,H1) ------------------------
2020.11.25 02:47:22.624 CheckOrders (EURUSD,H1) ServerName: ICMarkets-MT5
2020.11.25 02:47:22.624 CheckOrders (EURUSD,H1) 
2020.11.25 02:47:22.633 CheckOrders (EURUSD,H1) Last Tick 2020.11.18 12:44:37.354 1.18748 1.18748
2020.11.25 02:47:22.633 CheckOrders (EURUSD,H1) Accepted Tick 2020.11.18 12:44:37.354 1.18748 1.18748
2020.11.25 02:47:22.633 CheckOrders (EURUSD,H1) Accepted Length = 17 ms.
2020.11.25 02:47:22.633 CheckOrders (EURUSD,H1) Order 105637485 ORDER_TYPE_SELL EURUSD 2020.11.18 12:44:37.371 1.18749 ORDER_REASON_SL ORDER_STATE_FILLED 2020.11.18 12:44:37.476, Position created 2020.11.17 22:24:15.116, StopLevel = 0
2020.11.25 02:47:22.633 CheckOrders (EURUSD,H1) 
2020.11.25 02:47:22.634 CheckOrders (EURUSD,H1) Orders (2) before 105637485 with PositionID = 105516718:
2020.11.25 02:47:22.634 CheckOrders (EURUSD,H1) ------------------------
2020.11.25 02:47:22.634 CheckOrders (EURUSD,H1) Checked Orders = 0
2020.11.25 02:47:22.634 CheckOrders (EURUSD,H1) ------------------------
 
2020.11.25 02:50:58.687 CheckOrders (EURUSD,H1) ServerName: OctaFX-Real
2020.11.25 02:50:58.687 CheckOrders (EURUSD,H1) 
2020.11.25 02:50:58.687 CheckOrders (EURUSD,H1) Last Tick 2020.11.23 18:14:35.081 1.18108 1.18115
2020.11.25 02:50:58.687 CheckOrders (EURUSD,H1) Accepted Tick 2020.11.23 18:14:35.081 1.18108 1.18115
2020.11.25 02:50:58.687 CheckOrders (EURUSD,H1) Accepted Length = 11 ms.
2020.11.25 02:50:58.687 CheckOrders (EURUSD,H1) Order 8950107 ORDER_TYPE_SELL EURUSD 2020.11.23 18:14:35.092 1.18105 ORDER_REASON_TP ORDER_STATE_FILLED 2020.11.23 18:14:35.104, Position created 2020.11.23 18:11:38.678, StopLevel = 20
2020.11.25 02:50:58.687 CheckOrders (EURUSD,H1) 
2020.11.25 02:50:58.688 CheckOrders (EURUSD,H1) Orders (2) before 8950107 with PositionID = 8950014:
2020.11.25 02:50:58.688 CheckOrders (EURUSD,H1) ------------------------
2020.11.25 02:50:58.688 CheckOrders (EURUSD,H1) Checked Orders = 0
2020.11.25 02:50:58.688 CheckOrders (EURUSD,H1) ------------------------
 
2020.11.25 02:54:37.912 CheckOrders (EURUSD,H1) ServerName: Pepperstone-MT5-Live01
2020.11.25 02:54:37.912 CheckOrders (EURUSD,H1) 
2020.11.25 02:54:37.934 CheckOrders (EURUSD,H1) Last Tick 2020.09.03 01:00:02.426 106.199 106.199
2020.11.25 02:54:37.934 CheckOrders (EURUSD,H1) Accepted Tick 2020.09.03 01:00:02.426 106.199 106.199
2020.11.25 02:54:37.934 CheckOrders (EURUSD,H1) Accepted Length = 4 ms.
2020.11.25 02:54:37.934 CheckOrders (EURUSD,H1) Order 18982771 ORDER_TYPE_SELL USDJPY 2020.09.03 01:00:02.430 106.191 ORDER_REASON_TP ORDER_STATE_FILLED 2020.09.03 01:00:02.466, Position created 2020.09.02 22:57:47.081, StopLevel = 0
2020.11.25 02:54:37.934 CheckOrders (EURUSD,H1) 
2020.11.25 02:54:37.935 CheckOrders (EURUSD,H1) Orders (2) before 18982771 with PositionID = 18975080:
2020.11.25 02:54:37.935 CheckOrders (EURUSD,H1) ------------------------
2020.11.25 02:54:37.935 CheckOrders (EURUSD,H1) Checked Orders = 0
2020.11.25 02:54:37.935 CheckOrders (EURUSD,H1) ------------------------
 

Un'altra esplosione di cervello da Fxsaber. Non so nemmeno cosa dire.

Penso che l'unica soluzione praticabile sia trovare il collo di bottiglia insieme al broker e, se possibile, provare a risolverlo.

per una correzione interna, dovrete

- resistere a una raffica di critiche da parte degli sviluppatori e dimostrare che il lag si è verificato (tenendo conto di cose che non si possono sapere, per esempio, l'hardware del server del broker)

- convincerti che è fondamentale

- aspettare una correzione

- spingendo zelantemente il broker ad aggiornare all'ultima build, che può essere molto più difficile di tutti gli elementi precedenti.

 
e l'abusato MT non è per gli HFT )
 
ServerName: RannForex-Server
Accepted Length = 28894 ms.

Qui c'è un forte sospetto della tecnologia molto cool del broker per cui fai trading.

Qualcosa sta rallentando nei plugin di elaborazione dei limiti personalizzati.

 
Andrey Khatimlianskii:

Qui è dove c'è un forte sospetto della tecnologia molto cool del broker per il quale si sta facendo trading.

... I server del broker possono essere qualsiasi cosa tu voglia, o può essere un link a un thread sui ritardi nella sezione scambi, possiamo solo indovinare.

 
Andrei Trukhanovich:

Per una correzione interna, dovrete

- Dovrete subire una raffica di critiche da parte degli sviluppatori e dimostrare che il lag ha luogo (considerando le sfumature che potreste non conoscere, per esempio, l'hardware della parte server del broker).

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

Accettazione di ordini SL/TP

fxsaber, 2020.11.25 00:47

Risultato dell'esecuzione su MQ-Demo.

Total Orders (from 2020.09.01 00:00:00) = 58493, calculated = 439
Calculation time = 00:00:11.328, Performance = 38.0 orders/sec.

ServerName: MetaQuotes-Demo


È stato ripetutamente affermato in un altro thread che anche il Terminale rallenta a causa di un enorme numero di fattori. Di conseguenza, il Trading Server, molto più complesso, è destinato a rallentare ancora di più. Spero ancora che l'ottimizzazione algoritmica sia ancora possibile. Anche un ritardo di 5 ms è già molto brutto. Figuriamoci centinaia di millisecondi.