English Русский 中文 Español 日本語 Português
preview
Entwicklung eines Qualitätsfaktors für Expert Advisors

Entwicklung eines Qualitätsfaktors für Expert Advisors

MetaTrader 5Beispiele | 1 Dezember 2023, 10:43
254 0
Ricardo Rodrigues Lucca
Ricardo Rodrigues Lucca

Einführung

In diesem Artikel sehen wir uns an, wie Sie eine Qualitätsbewertung entwickeln, die Ihr Expert Advisor im Strategietester anzeigen kann. In Abbildung 1 unten sehen Sie, dass der Wert „OnTester result“ 1,0639375 beträgt, was ein Beispiel für die Qualität des ausgeführten Systems ist. In diesem Artikel werden wir zwei mögliche Ansätze zur Messung der Systemqualität kennenlernen und sehen, wie man beide Werte protokolliert, da wir nur einen von ihnen zurückgeben können.

Abbildung 1: Hervorgehobenes Feld „OnTester result“.


Start eines Handelsmodells und Aufbau eines EA

Bevor man sich mit dem Faktor Systemqualität befasst, ist es notwendig, ein Basissystem zu erstellen, das in den Tests verwendet werden soll. Wir haben uns für ein einfaches System entschieden: Wir wählen eine Zufallszahl und eröffnen, wenn sie gerade ist, eine Kaufposition; andernfalls gehen wir eine Verkaufsposition ein, da die Zahl ungerade ist.

Um die Auslosung festzuhalten, verwenden wir die Funktion MathRand(), die eine Zahl zwischen 0 (Null) und 32767 liefert. Um das System ausgewogener zu gestalten, werden wir außerdem zwei ergänzende Regeln hinzufügen. Mit diesen drei Regeln werden wir versuchen, ein zuverlässigeres System zu gewährleisten. Hier sind sie:

  • Wenn wir nicht in einer Position sind, generieren wir eine Zufallszahl
    • Wenn die Zahl 0 oder 32767 ist, tun wir nichts.
    • Wenn die Zahl gerade ist, kaufen wir den Vermögenswert in der Menge, die der Mindestlosgröße entspricht.
    • Wenn die Zahl ungerade ist, verkaufen wir den Vermögenswert in der Menge, die der Mindestlosgröße entspricht.
  • Wenn wir in einer Position sind, verschieben wir das Stopp-Level in Richtung jeder neuen Kerze, die höher ist als die vorherige in Richtung der Bewegung.
    • Das verwendete Stop-Level basiert auf dem 1-Perioden-ATR-Indikator, der mit einem 8 EMA normalisiert wird. Außerdem wird er sich am weitesten von den beiden für die Analyse verwendeten Kerzen entfernt befinden
  • Liegt die Zeit außerhalb des Bereichs von 11:00 bis 16:00 Uhr, dürfen wir keine Position eröffnen, und um 16:30 Uhr muss die Position geschlossen werden.

Nachfolgend finden Sie den Code, der diese Regeln umsetzt.

//--- Indicator ATR(1) with EMA(8) used for the stop level...
int ind_atr = iATR(_Symbol, PERIOD_CURRENT, 1);
int ind_ema = iMA(_Symbol, PERIOD_CURRENT, 8, 0, MODE_EMA, ind_atr);
//--- Define a variable that indicates that we have a deal...
bool tem_tick = false;
//--- An auxiliary variable for opening a position
#include<Trade/Trade.mqh>
#include<Trade/SymbolInfo.mqh>
CTrade negocios;
CSymbolInfo info;
//--- Define in OnInit() the use of the timer every second
//--- and start CTrade
int OnInit()
  {
//--- Set the fill type to keep a pending order
//--- until it is fully filled
   negocios.SetTypeFilling(ORDER_FILLING_RETURN);
//--- Leave the fixed deviation at it is not used on B3 exchange
   negocios.SetDeviationInPoints(5);
//--- Define the symbol in CSymbolInfo...
   info.Name(_Symbol);
//--- Set the timer...
   EventSetTimer(1);
//--- Set the base of the random number to have equal tests...
   MathSrand(0xDEAD);
   return(INIT_SUCCEEDED);
  }
//--- Since we set a timer, we need to destroy it in OnDeInit().
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//--- The OnTick function only informs us that we have a new deal
void OnTick()
  {
   tem_tick = true;
  }
//+------------------------------------------------------------------+
//| Expert Advisor main function                                     |
//+------------------------------------------------------------------+
void OnTimer()
  {
   MqlRates cotacao[];
   bool fechar_tudo = false;
   bool negocios_autorizados = false;
//--- Do we have a new trade?
   if(tem_tick == false)
      return ;
//--- To check, return information of the last 3 candlesticks....
   if(CopyRates(_Symbol, PERIOD_CURRENT, 0, 3, cotacao) != 3)
      return ;
//--- Is there a new candlestick since the last check?
   if(tem_vela_nova(cotacao[2]) == false)
      return ;
//--- Get data from the trade window and closing...
   negocios_autorizados = esta_na_janela_de_negocios(cotacao[2], fechar_tudo);
//--- If we are going to close everything and if there is a position, close it...
   if(fechar_tudo)
     {
      negocios.PositionClose(_Symbol);
      return ;
     }
//--- if we are not closing everything, move stop level if there is a position...
   if(arruma_stop_em_posicoes(cotacao))
      return ;
   if (negocios_autorizados == false) // are we outside the trading window?
      return ;
//--- We are in the trading window, try to open a new position!
   int sorteio = MathRand();
//--- Entry rule 1.1
   if(sorteio == 0 || sorteio == 32767)
      return ;
   if(MathMod(sorteio, 2) == 0)  // Draw rule 1.2 -- even number - Buy
     {
     negocios.Buy(info.LotsMin(), _Symbol);
     }
   else // Draw rule 1.3 -- odd number - Sell
     {
     negocios.Sell(info.LotsMin(), _Symbol);
     }
  }
//--- Check if we have a new candlestick...
bool tem_vela_nova(const MqlRates &rate)
  {
   static datetime vela_anterior = 0;
   datetime vela_atual = rate.time;
   if(vela_atual != vela_anterior) // is time different from the saved one?
     {
      vela_anterior = vela_atual;
      return true;
     }
   return false;
  }
//--- Check if the time is n the trade period to close positions...
bool esta_na_janela_de_negocios(const MqlRates &rate, bool &close_positions)
  {
   MqlDateTime mdt;
   bool ret = false;
   close_positions = true;
   if(TimeToStruct(rate.time, mdt))
     {
      if(mdt.hour >= 11 && mdt.hour < 16)
        {
         ret = true;
         close_positions = false;
        }
      else
        {
         if(mdt.hour == 16)
            close_positions = (mdt.min >= 30);
        }
     }
   return ret;
  }
//---
bool arruma_stop_em_posicoes(const MqlRates &cotacoes[])
  {
   if(PositionsTotal()) // Is there a position?
     {
      double offset[1] = { 0 };
      if(CopyBuffer(ind_ema, 0, 1, 1, offset) == 1 // EMA successfully copied?
         && PositionSelect(_Symbol))  // Select the existing position!
        {
         ENUM_POSITION_TYPE tipo = (ENUM_POSITION_TYPE) PositionGetInteger(POSITION_TYPE);
         double SL = PositionGetDouble(POSITION_SL);
         double TP = info.NormalizePrice(PositionGetDouble(POSITION_TP));
         if(tipo == POSITION_TYPE_BUY)
           {
            if (cotacoes[1].high > cotacoes[0].high)
               {
                  double sl = MathMin(cotacoes[0].low, cotacoes[1].low) - offset[0];
                  info.NormalizePrice(sl);
                  if (sl > SL)
                     {
                        negocios.PositionModify(_Symbol, sl, TP);
                     }
               }
           }
         else // tipo == POSITION_TYPE_SELL
           {
           if (cotacoes[1].low < cotacoes[0].low)
               {
                  double sl = MathMax(cotacoes[0].high, cotacoes[1].high) + offset[0];
                  info.NormalizePrice(sl);
                  if (SL == 0 || (sl > 0 && sl < SL))
                     {
                        negocios.PositionModify(_Symbol, sl, TP);
                     }
               }
           }
        }
      return true;
     }
   // there was no position
   return false;
  }

Betrachten wir kurz den obigen Code. Wir verwenden den aus der ATR berechneten Durchschnitt, um die Größe der Stopps zu bestimmen, die an den Kerzengrenzen platziert werden, wenn wir eine Kerze finden, die die vorherige übersteigt. Dies geschieht mit der Funktion arruma_stop_em_posicoes. Immer wenn true zurückgegeben wird, gibt es eine Position und wir sollten im Hauptcode in OnTimer nicht fortfahren. Ich verwende diese Funktion anstelle von OnTick, weil ich nicht für jeden durchgeführten Handel eine große Funktion benötige. Die Funktion sollte bei jeder neuen Kerze des definierten Zeitraums ausgeführt werden. In OnTick zeigt der auf true gesetzte Wert einen früheren Handel an. Dies ist notwendig, da der Strategietester sonst in Zeiten, in denen der Markt geschlossen ist, Pausen einlegen würde, da er die Funktion auch ohne vorherigen Handel ausführen würde.

Bis zu diesem Zeitpunkt ist alles streng nach dem festgelegten Plan verlaufen, einschließlich der beiden festgelegten Fenster. Das erste ist das Zeitfenster für die Eröffnung von Handelsgeschäften, das zwischen 11:00 und 4:00 Uhr liegt. Das zweite ist das Verwaltungsfenster, das es dem Algorithmus ermöglicht, die offenen Positionen zu verwalten, indem er die Stopps bis 16:30 Uhr verschiebt – zu diesem Zeitpunkt sollte er alle Handelsgeschäfte des Tages schließen.

Bitte beachten Sie, dass, wenn wir diesen EA jetzt handeln, das Ergebnis von „OnTester“ Null sein wird, wie in Abbildung 2 zu sehen, da wir keine Berechnungsfunktion für diesen Wert vorgesehen haben.

Abbildung 2: EA ausgeführt auf USDJPY, H1 im OHLC-Modus 1 Minute im Zeitraum vom 2023-01-01 bis 2023-05-19


Über den Faktor Qualität

Um die Anzeige des Wertes von „OnTester“ zu ermöglichen, müssen wir die Funktion OnTester definieren, die einen Wert vom Typ double zurückgibt. Mit dem nachstehenden Code erhalten wir das in Abbildung 3 dargestellte Ergebnis.

EA ausgeführt auf USDJPY, H1 im OHLC-Modus 1 Minute im Zeitraum vom 2023-01-01 bis 2023-05-19.

Abbildung 3: EA ausgeführt auf USDJPY, H1 im OHLC-Modus 1 Minute im Zeitraum vom 2023-01-01 bis 2023-05-19.

Der folgende Code sollte am Ende des vorherigen Codes eingefügt werden. Hier wird das durchschnittliche Risiko-Ertrags-Verhältnis der Handelsgeschäfte berechnet: Dieses Verhältnis wird in der Regel als die erhaltene Rendite ausgedrückt, da das Risiko als konstant angenommen wird und gleich 1 ist. So kann das Risiko-Rendite-Verhältnis als 1:1,23 oder einfach als 1,23 interpretiert werden, ein anderes Beispiel könnte 0,43 sein. Im ersten Beispiel gewinnen wir für jeden riskierten 1 Dollar 1,23 Dollar, während wir im zweiten Beispiel für jeden riskierten 1 Dollar 0,43 Dollar erhalten, also verlieren. Wenn also die Rendite 1 oder nahe daran ist, bedeutet dies, dass wir kostendeckend arbeiten, und wenn sie darüber liegt, bedeutet dies, dass wir gewinnen.

Da die Statistiken keinen durchschnittlichen Gewinn oder Verlust ausweisen, verwenden wir den Bruttowert, normiert durch die Anzahl der Handelsgeschäfte auf jeder Seite (Kauf oder Verkauf). Bei der Rückgabe der Werte von ausgeführten Handelsgeschäften zur Verwendung wird 1 hinzugefügt. Auf diese Weise wird das Programm nicht wegen einer Division durch Null während der Berechnung abgebrochen, wenn es keine gewinnbringenden Handelsgeschäfte oder keine Verlustgeschäfte gibt. Um zu vermeiden, dass eine große Anzahl von Ziffern angezeigt wird, wie in Abbildung 1, in der wir mehr als 5 Ziffern hatten, haben wir NormalizeDouble verwendet, um das Ergebnis mit nur zwei Ziffern anzuzeigen.

double OnTester()
  {
//--- Average profit
   double lucro_medio=TesterStatistics(STAT_GROSS_PROFIT)/(TesterStatistics(STAT_PROFIT_TRADES)+1);
//--- Average loss
   double prejuizo_medio=-TesterStatistics(STAT_GROSS_LOSS)/(TesterStatistics(STAT_LOSS_TRADES)+1); 
//--- Risk calculation: profitability to be returned
   double rr_medio = lucro_medio / prejuizo_medio;
//---
   return NormalizeDouble(rr_medio, 2);
  }

Die Funktion OnTester muss in jedem Expert Advisor vorhanden sein, damit der Wert im Bericht angezeigt wird. Um die Arbeit, die mit dem Kopieren mehrerer Codezeilen verbunden ist, zu minimieren, werden wir die Funktion in eine separate Datei verschieben. Auf diese Weise brauchen wir jedes Mal nur eine einzige Zeile zu kopieren. Dies geschieht wie folgt:

#include "ARTICLE_METRICS.mq5"

Auf diese Weise haben wir einen prägnanten Code! In der angegebenen Datei wird die Funktion durch eine Definition festgelegt. Falls wir include verwenden wollen, können wir so den Namen der einzubindenden Funktion einfach ändern und mögliche Fehler durch Duplizierung vermeiden, falls die Funktion OnTester bereits definiert ist. Wir können uns dies also als einen Mechanismus vorstellen, um OnTester den Vorzug zu geben, der direkt in den EA-Code eingefügt wird. Wenn wir sie mit include verwenden wollen, kommentieren wir einfach die OnTester-Funktion im EA-Code aus und kommentieren die entsprechende Makrodefinition aus. Wir werden später darauf zurückkommen.

Zu Beginn sieht die Datei ARTICLE_METRICS.mq5 wie folgt aus:

//--- Risk calculation: average return on operation
double rr_medio()
  {
//--- Average profit
   double lucro_medio=TesterStatistics(STAT_GROSS_PROFIT)/(TesterStatistics(STAT_PROFIT_TRADES)+1);
//--- Average loss
   double prejuizo_medio=-TesterStatistics(STAT_GROSS_LOSS)/(TesterStatistics(STAT_LOSS_TRADES)+1); 
//--- Risk calculation: profitability to be returned
   double rr_medio = lucro_medio / prejuizo_medio;
//---
   return NormalizeDouble(rr_medio, 2);
  }

//+------------------------------------------------------------------+
//| OnTester                                                         |
//+------------------------------------------------------------------+
#ifndef SQN_TESTER_ON_TESTER
#define SQN_TESTER_ON_TESTER OnTester
#endif
double SQN_TESTER_ON_TESTER()
  {
   return rr_medio();
  }

Bitte beachten Sie, dass der korrekte Dateiname die Erweiterung „mqh“ haben muss. Da ich jedoch vorhabe, die Datei im Verzeichnis Experts zu speichern, habe ich die Code-Erweiterung absichtlich weggelassen. Es liegt in Ihrem Ermessen.


Erste Version der Qualitätsberechnung

Unsere erste Version der Qualitätsberechnung basiert auf dem von Sunny Harris entwickelten Ansatz mit dem Titel CPC Index. Dieser Ansatz verwendet drei Kennzahlen, die miteinander multipliziert werden: durchschnittliches Risiko, Erfolgsquote und Gewinnverhältnis. Wir werden sie jedoch dahingehend ändern, dass sie nicht den Gewinnfaktor verwendet, sondern den niedrigsten Wert zwischen dem Gewinnfaktor und dem Erholungsfaktor. Obwohl man sich, wenn man den Unterschied zwischen den beiden betrachtet, für den Erholungsfaktor entscheiden sollte, habe ich es vorgezogen, ihn so zu belassen, weil er bereits zu einer Verbesserung der Berechnung führt.

Der folgende Code implementiert den im vorigen Absatz erwähnten Ansatz. Wir müssen sie nur unter OnTester aufrufen. Bitte beachten Sie, dass wir hier nicht 1 zur Anzahl der Abschlüsse hinzugefügt haben, da der angegebene Wert generisch ist und wir davon ausgehen, dass es mindestens 1 Abschluss zu bewerten gibt.

//--- Calculating CPC Index by Sunny Harris
double CPCIndex()
  {
   double taxa_acerto=TesterStatistics(STAT_PROFIT_TRADES)/TesterStatistics(STAT_TRADES);
   double fator=MathMin(TesterStatistics(STAT_PROFIT_FACTOR), TesterStatistics(STAT_RECOVERY_FACTOR));
   return NormalizeDouble(fator * taxa_acerto * rr_medio(), 5);
  }


Zweite Version der Qualitätsberechnung

Der zweite Qualitätsfaktor, über den wir sprechen werden, heißt System Quality Index (SQN, Systemqualitätsindex). Sie wurde von Van Tharp geschaffen. Wir berechnen die in jedem Monat getätigten Handelsgeschäfte und erhalten einen einfachen Durchschnitt für alle Monate der Simulation. SQN unterscheidet sich von dem im vorigen Abschnitt beschriebenen Ansatz, weil er die Stabilität des Handelssystems hervorheben will.

Ein wichtiges Merkmal von SQN ist, dass es Systeme bestraft, die ausgeprägte Spitzen aufweisen. Wenn das System also eine Reihe von kleinen Handelsgeschäften und ein großes Handelsgeschäft hat, wird letzteres bestraft. Das heißt, wenn wir ein System mit kleinen Verlusten und einem großen Gewinn haben, wird dieser Gewinn benachteiligt. Das Gegenteil (kleine Gewinne und ein großer Verlust) wird ebenfalls bestraft. Letzteres ist für diejenigen, die handeln, am schlimmsten.

Denken Sie daran, dass der Handel ein langfristiges Rennen ist, und konzentrieren Sie sich immer darauf, Ihrem System zu folgen! Nicht auf das Geld, das Sie am Ende des Monats verdienen werden, da es große Schwankungen geben kann.

//--- standard deviation of executed trades based on results in money
double dp_por_negocio(uint primeiro_negocio, uint ultimo_negocio,
                      double media_dos_resultados, double quantidade_negocios)
  {
   ulong ticket=0;
   double dp=0.0;
   for(uint i=primeiro_negocio; i < ultimo_negocio; i++)
     {
      //--- try to get deals ticket
      if((ticket=HistoryDealGetTicket(i))>0)
        {
         //--- get deals properties
         double profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);
         //--- create price object
         if(profit!=0)
           {
            dp += MathPow(profit - media_dos_resultados, 2.0);
           }
        }
     }
   return MathSqrt(dp / quantidade_negocios);
  }

//--- Calculation of System Quality Number, SQN, by Van Tharp
double sqn(uint primeiro_negocio, uint ultimo_negocio,
           double lucro_acumulado, double quantidade_negocios)
  {
   double lucro_medio = lucro_acumulado / quantidade_negocios;
   double dp = dp_por_negocio(primeiro_negocio, ultimo_negocio,
                              lucro_medio, quantidade_negocios);
   if(dp == 0.0)
     {
      // Because the standard deviation returned a value of zero, which we didn't expect
      // we change it to average_benefit, since there is no deviation, which
      // brings the system closer to result 1.
      dp = lucro_medio;
     }
//--- The number of trades here will be limited to 100, so that the result will not be
//--- maximized due to the large number of trades.
   double res = (lucro_medio / dp) * MathSqrt(MathMin(100, quantidade_negocios));
   return NormalizeDouble(res, 2);
  }

//--- returns if a new month is found
bool eh_um_novo_mes(datetime timestamp, int &mes_anterior)
  {
   MqlDateTime mdt;
   TimeToStruct(timestamp, mdt);
   if(mes_anterior < 0)
     {
      mes_anterior=mdt.mon;
     }
   if(mes_anterior != mdt.mon)
     {
      mes_anterior = mdt.mon;
      return true;
     }
   return false;
  }

//--- Monthly SQN
double sqn_mes(void)
  {
   double sqn_acumulado = 0.0;
   double lucro_acumulado = 0.0;
   double quantidade_negocios = 0.0;
   int sqn_n = 0;
   int mes = -1;
   uint primeiro_negocio = 0;
   uint total_negocios;
//--- request the history of trades
   if(HistorySelect(0,TimeCurrent()) == false)
      return 0.0;
   total_negocios = HistoryDealsTotal();
//--- the average for each month is calculated for each trade
   for(uint i=primeiro_negocio; i < total_negocios; i++)
     {
      ulong    ticket=0;
      //--- Select the required ticket to pick up data
      if((ticket=HistoryDealGetTicket(i))>0)
        {
         datetime time = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
         double   lucro = HistoryDealGetDouble(ticket,DEAL_PROFIT);
         if(lucro == 0)
           {
            //--- If there is no result, move on to the next trade.
            continue;
           }
         if(eh_um_novo_mes(time, mes))
           {
            //--- If we have trades, then we calculate sqn, otherwise it will be equal to zero...
            if(quantidade_negocios>0)
              {
               sqn_acumulado += sqn(primeiro_negocio, i, lucro_acumulado,
                                    quantidade_negocios);
              }
            //--- The calculated amount sqns is always updated!
            sqn_n++;
            primeiro_negocio=i;
            lucro_acumulado = 0.0;
            quantidade_negocios = 0;
           }
         lucro_acumulado += lucro;
         quantidade_negocios++;
        }
     }
//--- when exiting "for", we can have undesired result
   if(quantidade_negocios>0)
     {
      sqn_acumulado += sqn(primeiro_negocio, total_negocios,
                           lucro_acumulado, quantidade_negocios);
      sqn_n++;
     }
//--- take the simple average of sqns
   return NormalizeDouble(sqn_acumulado / sqn_n, 2);
  }

Schauen wir uns den Code an:

Mit der ersten Funktion wird die Standardabweichung einer Reihe von Handelsgeschäften berechnet. Hier folgen wir der Empfehlung von Van Tharp und beziehen alle Handelsgeschäfte in die Berechnung der Standardabweichung ein. In der endgültigen Formel (in der Funktion unten) begrenzen wir jedoch die Anzahl der Abschlüsse auf 100. Dadurch wird das Ergebnis nicht durch die Anzahl der Abschlüsse verzerrt, was es praktischer und aussagekräftiger macht.

Schließlich gibt es noch die Funktion sqn_mes, die prüft, ob es sich um einen neuen Monat handelt, und die einige Daten sammelt, die für die oben genannten Funktionen benötigt werden. Am Ende dieser Funktion wird die durchschnittliche monatliche SQN für den Zeitraum, in dem die Simulation durchgeführt wurde, berechnet. Diese kurze Erklärung soll einen Überblick über den Code und den Zweck der einzelnen Funktionen geben. Wenn Sie diesen Ansatz verfolgen, können Sie die SQN-Berechnung besser verstehen.

Die Funktion OnTester kann alle drei Werte ausdrucken und in der Registerkarte „Tester“ abgefragt oder in einer Datei gespeichert werden, oder wir können sogar einen Wert multipliziert mit dem anderen zurückgeben, damit er im Bericht erscheint, wie unten zu sehen ist.
double SQN_TESTER_ON_TESTER()
  {
   PrintFormat("%G,%G,%G", rr_medio(), CPCIndex(), sqn_mes());
   return NormalizeDouble(sqn_mes() * CPCIndex(), 5);
  }


Bevor wir fertig sind

Bevor wir diesen Artikel abschließen, wollen wir noch einmal auf das Thema „Include“ zurückkommen, um zu sehen, wie man den Fehler doppelter Funktionen vermeiden kann. Nehmen wir an, wir haben den Code eines Expert Advisors mit der Funktion OnTester und möchten die angegebene Datei mit include laden. Sie wird in etwa so aussehen wie unten (ignorieren Sie den Inhalt von OnTester in diesem Beispiel).

//+------------------------------------------------------------------+
double OnTester()
  {
   return __LINE__;
  }
//+------------------------------------------------------------------+
#include "ARTICLE_METRICS.mq5"

Dieser Code führt zu einem Fehler von doppelten Funktionen, da sowohl der EA in unserem Code als auch die Include-Datei eine Funktion mit demselben Namen OnTester haben. Wir können jedoch zwei Definitionen verwenden, um eine davon umzubenennen und einen Mechanismus zum Aktivieren oder Deaktivieren der zu verwendenden Funktion zu simulieren. Siehe das folgende Beispiel.

//+------------------------------------------------------------------+
#define OnTester disable
//#define SQN_TESTER_ON_TESTER disable
double OnTester()
  {
   return __LINE__;
  }
#undef OnTester
//+------------------------------------------------------------------+
#include "ARTICLE_METRICS.mq5"

In diesem neuen Format gibt es keinen Fehler durch eine doppelte vorhandene Funktion, da die Definition den Namen der Funktion im EA-Code von OnTester in disable ändert. Wenn wir nun die erste Definition auskommentieren und die zweite auskommentieren, wird die Funktion in der Datei ARTICLE_METRICS in disable umbenannt, und die Funktion in der Datei Expert Advisor heißt weiterhin OnTester.

Dieser Ansatz scheint eine recht einfache Möglichkeit zu sein, zwischen beiden Funktionen zu wechseln, ohne mehrere Codezeilen auskommentieren zu müssen. Auch wenn es etwas invasiver ist, glaube ich, dass es für den Nutzer in Frage kommt. Der Nutzer sollte sich auch überlegen, ob es notwendig ist, die Funktion im EA beizubehalten, da es bereits eine in der eingeschlossenen Datei gibt, was zu Verwirrung führen könnte.


Schlussfolgerung

Wir sind am Ende dieses Artikels angelangt, in dem wir ein Modell eines Expert Advisors vorgestellt haben, der nach dem Zufallsprinzip arbeitet. Wir haben ihn als Beispiel für die Berechnung des Qualitätsfaktors verwendet. Wir haben zwei mögliche Berechnungen in Betracht gezogen: Van Tharp und Sunny Harris. Darüber hinaus wurde ein einführender Faktor vorgestellt, der das Verhältnis zwischen Risiko und Rendite nutzt. Wir haben auch gezeigt, wie die Verwendung von „Includes“ den Wechsel zwischen verschiedenen verfügbaren Funktionen erleichtern kann.

Wenn Sie Fragen haben oder einen Fehler gefunden haben, kommentieren Sie bitte den Artikel. Die besprochenen Codes sowohl für den Expert Advisor als auch für die Metrikdatei finden Sie in der beigefügten Zip-Datei.

Verwenden Sie einen anderen Qualitätsmaßstab? Teilen Sie es durch einen Kommentar hier! Vielen Dank, dass Sie diesen Artikel gelesen haben.


Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/11373

Beigefügte Dateien |
ARTICLE.zip (4.73 KB)
ARTICLE_METRICS.mq5 (10.33 KB)
ARTICLE_MT5.mq5 (11.87 KB)
Elastische Netzregression mit Koordinatenabstieg in MQL5 Elastische Netzregression mit Koordinatenabstieg in MQL5
In diesem Artikel untersuchen wir die praktische Umsetzung der elastischen Netzregression, um die Überanpassung zu minimieren und gleichzeitig automatisch nützliche Prädiktoren von solchen zu trennen, die wenig prognostische Kraft haben.
Developing a Replay System — Market simulation (Part 13): Die Geburt des SIMULATORS (III) Developing a Replay System — Market simulation (Part 13): Die Geburt des SIMULATORS (III)
Hier werden wir einige Elemente im Zusammenhang mit der Arbeit im nächsten Artikel vereinfachen. Ich erkläre auch, wie Sie sich vorstellen können, was der Simulator in Bezug auf die Zufälligkeit erzeugt.
Entwicklung eines Replay Systems — Marktsimulation (Teil 14): Die Geburt des SIMULATORS (IV) Entwicklung eines Replay Systems — Marktsimulation (Teil 14): Die Geburt des SIMULATORS (IV)
In diesem Artikel werden wir die Entwicklungsphase des Simulators fortsetzen. Diesmal werden wir sehen, wie wir eine Bewegung vom Typ RANDOM WALK effektiv erstellen können. Diese Art von Bewegung ist sehr interessant, denn sie bildet die Grundlage für alles, was auf dem Kapitalmarkt geschieht. Darüber hinaus werden wir beginnen, einige Konzepte zu verstehen, die für die Durchführung von Marktanalysen grundlegend sind.
Die diskrete Hartley-Transformation Die diskrete Hartley-Transformation
In diesem Artikel werden wir eine der Methoden der Spektralanalyse und Signalverarbeitung betrachten - die diskrete Hartley-Transformation. Es ermöglicht die Filterung von Signalen, die Analyse ihres Spektrums und vieles mehr. Die Möglichkeiten der DHT stehen denen der diskreten Fourier-Transformation in nichts nach. Im Gegensatz zur DFT werden bei der DHT jedoch nur reelle Zahlen verwendet, was die Umsetzung in der Praxis erleichtert, und die Ergebnisse der Anwendung sind anschaulicher.