Annahme von SL/TP-Aufträgen

 

Dieser Zweig befasst sich mit Aufträgen, die als Ergebnis der Auslösung von SL/TP-Levels offener Positionen erstellt werden.

Ich habe eine komplizierte, aber nützliche Funktion geschrieben, um einen Tick zu erhalten, der als Auslöser für einen bestimmten SL/TP-Auftrag dient.

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


Die Anwendung dieser großartigen Funktion ist der Grund für die Schaffung dieses Zweigs. Ich bin sicher, dass ich nicht alle Fehler im Code gefunden habe, aber um der Geschichte willen und um zu verstehen, dass es wirklich schwierig ist, habe ich eine vollständige Liste erstellt.

 

Bei der Untersuchung der Ausführung von TP-Aufträgen ist mir aufgefallen, dass einige TP-Aufträge mit einer erheblichen Verzögerung gegenüber den Ticks, die sie angenommen haben, erstellt wurden.

Die Nachbesprechung ergab, dass sich diese Situation nicht nur bei verschiedenen Brokern wiederholt, sondern auch dann, wenn der Handel über das Terminal erfolgt, das sich auf demselben Rechner wie der Handelsserver befindet. D.h. mit einem sehr niedrigen Ping und dem einzigen Handelskonto für den Handelsserver.



Das Schreiben der FunktionGetAcceptedTick ermöglichte es uns, das Problem gründlich zu untersuchen und konstruktiv zu demonstrieren.


Skript.

Das folgende Skript befindet sich also im Anhänger.

// Скрипт выводит самое длительное или конкретное акцептирование 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));
}


Das Ergebnis der Ausführung auf der 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
------------------------


Das Skript behauptet, einen TP-Auftrag und einen Tick gefunden zu haben, der der Auslöser für seine Erstellung war (im Text farblich hervorgehoben). Es scheint, dass, wenn der Preis das TP-Niveau einer offenen Position auf dem Handelsserver (insbesondere auf dem Demoserver) erreicht hat, die entsprechende TP-Order sofort erstellt (nicht unbedingt ausgeführt) werden sollte. In diesem Fall geschah dies jedoch nicht sofort, sondern erst nach 357 Millisekunden!


Ich möchte vorausschicken, dass selbst eine Verzögerung von einer Millisekunde nicht zu wenig ist. In der Zeit zu sein ist ein bedeutungsvolles Verb im Algotrading.


Überprüfung.

Wir sollten dem Skript nicht blind vertrauen und die Situation manuell überprüfen. Also, hier ist unsere Bestellung.


Der entsprechende akzeptierende Tick, den das Skript gefunden hat, ist hier zu sehen.


Der Pfeil zeigt an, dass der TP-Auftrag zwischen den Ticks entstanden ist. Die Screenshots zeigen deutlich, dass das Skript richtig war und die Erstellung der TP-Order mit einer großen Verzögerung auf der Seite des Handelsservers erfolgte.


Unterm Strich.

Jetzt gibt es ein Tool, das die Verzögerungswerte auf der Trade Server Seite anzeigt, wenn über TP/SL Level gehandelt wird. Im Moment sind sie enorm. Und das ist definitiv ein schwerwiegender Nachteil der Plattform, der korrigiert werden muss.

Leider ist es nicht möglich, die Annahme von schwebenden Aufträgen zu erkennen, da diese Information auf der Terminalseite nicht verfügbar ist. Doch fast immer wirkt sich das Vorhandensein signifikanter Lags auf der TP/SL-Auftragsseite zwangsläufig auf die Lags bei der Auftragsausführung aus. Denn die Ursache scheint dieselbe zu sein.


Alles in allem hinkt die MT5-Plattform gerade in diesen Situationen 100% hinterher. Und erfordert Korrekturen, bis es keine Verzögerung mehr gibt.


Ich möchte Sie ermutigen, die Ergebnisse Ihrer Konten mitzuteilen. Tragen Sie zur Verbesserung von MT5 bei!

Dateien:
 
fxsaber:

Sie werden ermutigt, die Ergebnisse Ihrer Konten mitzuteilen. Helfen Sie mit, MT5 zu verbessern!

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 Sekunden Verzögerung! Wahrscheinlich ist es am besten, wenn Sie sich in diesen Fällen bereits an den Makler wenden.

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

Ein weiterer Geistesblitz von fxsaber. Ich weiß nicht einmal, was ich sagen soll.

Ich denke, die einzige praktikable Lösung besteht darin, gemeinsam mit dem Makler den Engpass zu finden und, wenn möglich, zu beheben.

für eine hausinterne Lösung, müssen Sie

- einer Flut von Kritik seitens der Entwickler standhalten und beweisen, dass die Verzögerung aufgetreten ist (unter Berücksichtigung von Dingen, die Sie vielleicht nicht wissen, z. B. die Serverhardware des Brokers)

- Sie davon überzeugen, dass es entscheidend ist

- auf eine Lösung warten

- eifriges Drängen des Brokers auf ein Upgrade auf die neueste Version, was viel schwieriger sein kann als alle vorherigen Punkte.

 
und das abgedroschene MT ist nicht für HFTs )
 
ServerName: RannForex-Server
Accepted Length = 28894 ms.

Hier liegt der Verdacht nahe, dass die Technologie des Brokers, für den Sie handeln, sehr cool ist.

Irgendetwas verlangsamt die benutzerdefinierten Grenzwertverarbeitungs-Plugins.

 
Andrey Khatimlianskii:

Hier besteht ein starker Verdacht auf die sehr coole Technologie des Brokers, für den Sie handeln.

... Die Server des Brokers können alles sein, was Sie wollen, oder es kann ein Link zu einem Thread über Verzögerungen im Austauschbereich sein, wir können nur raten.

 
Andrei Trukhanovich:

Für eine interne Reparatur müssen Sie

- Sie müssen eine Flut von Kritik von den Entwicklern zu leiden und zu beweisen, dass die Verzögerung stattfindet (in Anbetracht der Nuancen können Sie nicht wissen, zum Beispiel, die Hardware des Servers Teil des Brokers).

Forum zum Thema Handel, automatisierte Handelssysteme und Testen von Handelsstrategien

Annahme von SL/TP-Aufträgen

fxsaber, 2020.11.25 00:47

Ergebnis der Ausführung auf 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


In einem anderen Thread wurde wiederholt darauf hingewiesen, dass selbst das Terminal durch eine Vielzahl von Faktoren verlangsamt wird. Dies hat zur Folge, dass der viel komplexere Trading Server noch langsamer wird. Ich hoffe nach wie vor, dass eine algorithmische Optimierung noch möglich ist. Selbst eine Verzögerung von 5 ms ist schon sehr schlecht. Geschweige denn Hunderte von Millisekunden.

Grund der Beschwerde: