English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Dr. Handel oder: Wie ich lernte, mir keine Sorgen mehr zu machen und einen autodidakten Expert Advisor erstellte

Dr. Handel oder: Wie ich lernte, mir keine Sorgen mehr zu machen und einen autodidakten Expert Advisor erstellte

MetaTrader 5Handel | 4 Mai 2016, 15:39
931 0
Roman Zamozhnyy
Roman Zamozhnyy

Konzept

Nach der Erstellung eines Expert Advisors werden wir den eingebauten Strategie-Tester benutzen, um die besten Parameter auszuwählen. Nachdem wir diese ausgewählt haben, lassen wir den Expert Advisor laufen, und sobald eine wesentliche Veränderung stattgefunden hat, wird der Expert Advisor angehalten und wieder und wieder mit dem Strategie-Tester optimiert.

Können wir dem Expert Advisor die neu-optimierte Entscheidung oder die Neu-Optimierung als Prozess zuweisen, ohne seine Arbeit zu unterbrechen?

Eine der Lösungen für dieses Problem wurde von Quantum in seinem Artikel "Adaptive Trading Systems and Their Use in MetaTrader5 Terminal" (Adaptive Handelssysteme und wie Sie im MetaTrader5-Terminal verwendet werden) besprochen. Er behandelt die Verwendung eines echten Handelssystems neben ein paar (zahlenmäßig unbegrenzten) virtuellen Handelsstrategien, aus denen die Strategie gewählt wurde, die bis jetzt den höchsten Gewinn einbrachte. Die Entscheidung, die Handelsstrategie zu ändern, wurde getroffen, nachdem ein bestimmter festgesetzter Wert übertroffen wurde.

Ich schlage die Verwendung des Codes eines genetischen Algorithmus (GA) vor, der von Joo in seinem Artikel "Genetische Algorithmen - Leicht gemacht!" beschrieben wird. Werfen wir einen Blick auf die Implementierung eines solchen Expert Advisors (eines der unteren Beispiele ist ein Expert Advisor, der zur Teilnahme bei der Automated Trading Championship 2011 vorgeschlagen wurde).


Produkte im Werden

Wir müssen also als Erstes definieren, was ein Expert Advisor tun soll. Er soll natürlich handeln, und zwar mit der ausgewählten Strategie. Zweitens soll er entscheiden, ob es an der Zeit für eine Neu-Optimierung ist (das heißt, eine neue Optimierung der Input-Parameter). Und drittens, eine Neu-Optimierung mit dem GA. Zu Beginn werden wir die einfachste Neu-Optimierung begutachten - es gibt eine Strategie, wir wählen nur die Parameter aus. Dann werden wir sehen ob wir mit dem GA eine andere Strategie in einer sich verändernden Marktumgebung auswählen können, und wenn, wie man das am besten macht.

Um außerdem eine Simulation in der Fitnessfunktion zu vereinfachen, handeln wir nur mit vollständigen Balken in einem Instrument. Es wird keine neuen Positionen oder Glattstellungen geben. Wer es bevorzugt, Fixed Stops und Takes und Trailing Stops zu verwenden, soll sich an den Artikel "Tick Generation Algorithm in MetaTrader5 Strategy Tester" (Der Algorithmus für Tick-Generierungen im MetaTrader5-Strategietester) wenden, um Stop Loss und Take Profit Überprüfungen in der Fitnessfunktion zu machen. Darauf werde ich noch näher eingehen.

In der Fitnessfunktion habe ich einen Testmodus simuliert, der im Tester als "Nur Preise öffnen" bekannt ist. ABER! Das heißt nicht, dass es der einzig mögliche Test ist, der Simulation in der Fitnessfunktion ausführen kann. Menschen mit mehr Skrupeln wollen vielleicht einen Fitnessfunktions-Test mit dem Modus "Jeder Tick" implementieren. Um das Rad an dieser Stelle nicht neu erfinden zu müssen, oder auch jeden "Tick", möchte ich Ihre Aufmerksamkeit auf einen bereits vorhandenen Algorithmus lenken, der von MetaQuotes entwickelt wurde. Nach der Lektüre dieses Artikels ist man also in der Lage, den "Jeder Tick"-Modus in der Fitnessfunktion zu simulieren, was eine notwendige Bedingung für eine korrekte Simulation von Stops und Takes in der Fitnessfunktion ist.

Bevor wir zum Hauptpunkt kommen (der Strategie-Implementierung), werden wir kurz die Formsachen besprechen und Hilfsfunktionen implementieren, die das Öffnen eines neuen Balkens sowie das Öffnen und Abschließen von Positionen definieren:

//+------------------------------------------------------------------+
//| Define whether a new bar has opened                             |
//+------------------------------------------------------------------+
bool isNewBars()
  {
   CopyTime(s,tf,0,1,curBT);
   TimeToStruct(curBT[0],curT);
   if(tf==PERIOD_M1||
      tf==PERIOD_M2||
      tf==PERIOD_M3||
      tf==PERIOD_M4||
      tf==PERIOD_M5||
      tf==PERIOD_M6||
      tf==PERIOD_M10||
      tf==PERIOD_M12||
      tf==PERIOD_M15||
      tf==PERIOD_M20||
      tf==PERIOD_M30)
      if(curT.min!=prevT.min)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_H1||
      tf==PERIOD_H2||
      tf==PERIOD_H3||
      tf==PERIOD_H4||
      tf==PERIOD_H6||
      tf==PERIOD_H8||
      tf==PERIOD_M12)
      if(curT.hour!=prevT.hour)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_D1||
      tf==PERIOD_W1)
      if(curT.day!=prevT.day)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_MN1)
      if(curT.mon!=prevT.mon)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   return(false);
  }
//+------------------------------------------------------------------+
//|  ClosePosition                                                   |
//+------------------------------------------------------------------+
void ClosePosition()
  {
   request.action=TRADE_ACTION_DEAL;
   request.symbol=PositionGetSymbol(0);
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) request.type=ORDER_TYPE_SELL; 
   else request.type=ORDER_TYPE_BUY;
   request.type_filling=ORDER_FILLING_FOK;
   if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
      SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      request.sl=NULL;
      request.tp=NULL;
      request.deviation=100;
     }
   while(PositionsTotal()>0)
     {
      request.volume=NormalizeDouble(MathMin(PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(PositionGetSymbol(0),SYMBOL_VOLUME_MAX)),2);
      if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
     }
  }
//+------------------------------------------------------------------+
//|  OpenPosition                                                    |
//+------------------------------------------------------------------+
void OpenPosition()
  {
   double vol;
   request.action=TRADE_ACTION_DEAL;
   request.symbol=s;
   request.type_filling=ORDER_FILLING_FOK;
   if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
      SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      request.sl=NULL;
      request.tp=NULL;
      request.deviation=100;
     }
   vol=MathFloor(AccountInfoDouble(ACCOUNT_FREEMARGIN)*optF*AccountInfoInteger(ACCOUNT_LEVERAGE)
       /(SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP)))*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP);
   vol=MathMax(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MIN));
   vol=MathMin(vol,GetPossibleLots()*0.95);
   if(SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)!=0) vol=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)),2);
   request.volume=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2);
   while(PositionSelect(s)==false)
     {
      if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); 
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
      PositionSelect(s);
     }
   while(PositionGetDouble(POSITION_VOLUME)<vol)
     {
      request.volume=NormalizeDouble(MathMin(vol-PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2);
      if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
      PositionSelect(s);
     }
  }
//+------------------------------------------------------------------+

Man kann drei wesentliche Parameter in der Funktion "Öffne Position" erkennen: s und optF-Variablen und GetPossibleLots() Funktionsruf:

  • s - Handelsinstrument, eine der Variablen, die von GA optimiert werden
  • optF - Teil der Anzahlung, die zum Handeln verwendet wird (eine andere Variable, die von GA optimiert wird) 
  • GetPossibleLots() Funktion - gibt den Teil der Anzahlung zurück, der zum Handeln benutzt wird:
//+------------------------------------------------------------------+
//|  GetPossibleLots                                                 |
//+------------------------------------------------------------------+
double GetPossibleLots()
  {
   request.volume=1.0;
   if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
   else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
   OrderCheck(request,check);
   return(NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)/check.margin,2));
  }

In einem kleinen Exkurs stellen wir zwei weitere Funktionen vor, die bei allen Expert Advisors vorkommen und in Phase Zwei essentiell sind:

//+------------------------------------------------------------------+
//|  InitRelDD                                                       |
//+------------------------------------------------------------------+
void InitRelDD()
  {
   ulong DealTicket;
   double curBalance;
   prevBT[0]=D'2000.01.01 00:00:00';
   TimeToStruct(prevBT[0],prevT);
   curBalance=AccountInfoDouble(ACCOUNT_BALANCE);
   maxBalance=curBalance;
   HistorySelect(D'2000.01.01 00:00:00',TimeCurrent());
   for(int i=HistoryDealsTotal();i>0;i--)
     {
      DealTicket=HistoryDealGetTicket(i);
      curBalance=curBalance+HistoryDealGetDouble(DealTicket,DEAL_PROFIT);
      if(curBalance>maxBalance) maxBalance=curBalance;
     }
  }
//+------------------------------------------------------------------+
//|  GetRelDD                                                        |
//+------------------------------------------------------------------+
double GetRelDD()
  {
   if(AccountInfoDouble(ACCOUNT_BALANCE)>maxBalance) maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);
   return((maxBalance-AccountInfoDouble(ACCOUNT_BALANCE))/maxBalance);
  }

Was können wir hier sehen? Die erste Funktion bestimmt die größtmögliche Account-Bilanz, die zweite Funktion berechnet den relativen aktuellen Wertverlust des Accounts. Ihre Eigenheiten werden in Phase zwei detailliert beschrieben.

Nun geht es um die Expert Advisor an sich. Da wir lediglich Anfänger sind, werden wir keinen Expert Advisor für die Wahl einer Strategie bekommen, sondern einfach zwei Expert Advisor mit den folgenden Strategien implementieren:
  • einer handelt mit Schnittmengen gleitender Durchschnitte (Golden Cross - wir kaufen ein Werkzeug, Death Cross - wir verkaufen);
  • der andere ist ein einfaches, neuronales Netzwerk, das Preisänderungen im Bereich von [0..1] über die letzten fünf Handelssessions erhält.

Die Arbeit eines selbstoptimierenden Expert Advisors kann wie folgt algorithmisch dargestellt werden:

  1. Die Installation von Variablen, die vom Expert Advisor verwendet werden: definieren und initialisieren Sie Indikatorenpuffer oder richten sie die neuronale Netzwerk-Topologie ein (Anzahl der Schichten/Neuronen in einer Schicht; ein einfaches neuronales Netzwerk, in dem die Anzahl von Neuronen in allen Schichten gleich ist, dient als Beispiel) und stellen Sie den Arbeits-Zeitrahmen. Der vermutlich wichtigste Schritt: wir rufen die Genetische Optimierunges-Funktion auf, die ihrerseits die wichtigste Funktion aufruft - die Fitnessfunktion (ab hier FF).

    WICHTIG. Es gibt eine neue FF für jede Handels-Strategie, das heißt, sie wird jedes Mal neu erstellt. Die FF für einen einzelnen gleitenden Durchschnitt unterscheidet sich also vollkommen von der FF für zwei gleitende Durchschnitte und sie unterscheidet sich erheblich von der FF für neuronale Netzwerke.

    Das FF-Performance-Ergebnis meiner Expert Advisor ist die größtmögliche Bilanz unter der Bedingung, dass der relative Wertverlust nicht den kritischen Wert überschritten hat, der als externe Variable festgelegt wurde (in unserem Beispiel - 0,5). In anderen Worten, wenn der nächste GA-Lauf eine Bilanz von 100.000 ergibt, während der relative Wertverlust -0,6 ist, ist die FF=0,0. In Ihrem Fall kann das FF-Ergebnis vollkommen andere Kriterien bedingen.

    Sammeln Sie die Ergebnisse der Performance des Genetischen-Algorithmus: für die Schnittmenge von gleitenden Durchschnitten werden es gleitende Durchschnittsperioden sein, im Fall eines neuronalen Netzwerkes wird es Synaptic Weight sein, und das gemeinsame Ergebnis für beide (und für meine anderen Expert Advisors) wird ein Instrument sein, das bis zur nächsten Neu-Optimierung gehandelt wird und das wir bereits kennen: opfF. Das heißt, ein Teil der Anzahlung kann zum Handeln verwendet werden. Sie können gerne optimierte Parameter zu Ihrer FF hinzufügen, Sie können auch den Zeitrahmen aussuchen oder andere Parameter.

    Der letzte Schritt der Initialisierung ist es, den größtmöglichen Account-Bilanzwert herauszufinden. Warum ist das wichtig? Weil das der Startpunkt für die Entscheidung einer Neu-Optimierung ist.

    WICHTIG. Wie wird die Entscheidung einer Neu-Optimierung getroffen: wenn der relative BILANZ-Wertverlust einen bestimmten Wert als externe Variabel erreicht (in unserem Beispiel 0,2), müssen wir neu-optimieren. Um mit einem Expert Advisor eine Neu-Optimierung an jedem Balken zu machen, bis man den kritischen Wertverlust erreicht hat, muss man den maximalen Bilanz-Wert mit einem aktuellen Wert ersetzen.

    Sie, lieber Leser, haben vielleicht andere Kriterien für die Implementierung einer Neu-Optimierung.

  2. Handel im Fortschritt.

  3. Nach jeder abgeschlossenen Position vergewissern wir uns, dass der Bilanz-Wertverlust den kritischen Wert erreicht hat. Wenn der kritische Wert erreicht wurde, lassen wir den GA laufen und sammeln seine Performance-Ergebnisse (das ist Neu-Optimierung!).

  4. Nun warten wir, entweder auf einen Anruf des Devisenmarkt-Direktors, der uns bittet, die Welt nicht bankrott gehen zu lassen, oder, was häufiger der Fall ist, auf einen Stop Out, Margin Call, den Rettungswagen...

Unten finden Sie eine programmierte Implementierung des oben Besprochenen für den Expert Advisor, der gleitende Durchschnittsstrategien und neuronale Netzwerke anwendet, alles als Quellcode.

Dieser Code wird nach GPL-Lizenzregeln und -bedingungen bereitgestellt.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   tf=Period();
//---for bar-to-bar test...
   prevBT[0]=D'2001.01.01';
//---... long ago
   TimeToStruct(prevBT[0],prevT);
//--- historical depth (should be set since the optimisation is based on historical data)
   depth=10000;
//--- copies at a time (should be set since the optimisation is based on historical data)
   count=2;
   ArrayResize(LongBuffer,count);
   ArrayResize(ShortBuffer,count);
   ArrayInitialize(LongBuffer,0);
   ArrayInitialize(ShortBuffer,0);
//--- calling the neural network genetic optimisation function
   GA();
//--- getting the optimised neural network parameters and other variables
   GetTrainResults();
//--- getting the account drawdown
   InitRelDD();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(isNewBars()==true)
     {
      bool trig=false;
      CopyBuffer(MAshort,0,0,count,ShortBuffer);
      CopyBuffer(MAlong,0,0,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         if(PositionsTotal()>0)
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               ClosePosition();
               trig=true;
              }
           }
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         if(PositionsTotal()>0)
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               ClosePosition();
               trig=true;
              }
           }
        }
      if(trig==true)
        {
         //--- if the account drawdown has exceeded the allowable value:
         if(GetRelDD()>maxDD)
           {
            //--- calling the neural network genetic optimisation function
            GA();
            //--- getting the optimised neural network parameters and other variables
            GetTrainResults();
            //--- readings of the drawdown will from now on be based on the current balance instead of the maximum balance
            maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);
           }
        }
      CopyBuffer(MAshort,0,0,count,ShortBuffer);
      CopyBuffer(MAlong,0,0,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         request.type=ORDER_TYPE_SELL;
         OpenPosition();
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         request.type=ORDER_TYPE_BUY;
         OpenPosition();
        }
     };
  }
//+------------------------------------------------------------------+
//| Preparing and calling the genetic optimizer                      |
//+------------------------------------------------------------------+
void GA()
  {
//--- number of genes (equal to the number of optimised variables), 
//--- all of them should be specified in the FitnessFunction())
   GeneCount      =OptParamCount+2;    
//--- number of chromosomes in a colony
   ChromosomeCount=GeneCount*11;
//--- minimum search range
   RangeMinimum   =0.0;
//--- maximum search range
   RangeMaximum   =1.0;
//--- search pitch
   Precision      =0.0001;
//--- 1 is a minimum, anything else is a maximum
   OptimizeMethod =2;                                                 
   ArrayResize(Chromosome,GeneCount+1);
   ArrayInitialize(Chromosome,0);
//--- number of epochs without any improvement
   Epoch          =100;                                               
//--- ratio of replication, natural mutation, artificial mutation, gene borrowing, 
//--- crossingover, interval boundary displacement ratio, every gene mutation probabilty, %
   UGA(100.0,1.0,1.0,1.0,1.0,0.5,1.0);                                
  }
//+------------------------------------------------------------------+
//| Fitness function for neural network genetic optimizer:           | 
//| selecting a pair, optF, synapse weights;                         |
//| anything can be optimised but it is necessary                    |
//| to carefully monitor the number of genes                         |
//+------------------------------------------------------------------+
void FitnessFunction(int chromos)
  {
   int    b;
//--- is there an open position?
   bool   trig=false;
//--- direction of an open position
   string dir="";
//--- opening price
   double OpenPrice=0;
//--- intermediary between a gene colony and optimised parameters
   int    z;
//--- current balance
   double t=cap;
//--- maximum balance
   double maxt=t;
//--- absolute drawdown
   double aDD=0;
//--- relative drawdown
   double rDD=0.000001;
//--- fitness function proper
   double ff=0;
//--- GA is selecting a pair
   z=(int)MathRound(Colony[GeneCount-1][chromos]*12);
   switch(z)
     {
      case  0: {s="AUDUSD"; break;};
      case  1: {s="AUDUSD"; break;};
      case  2: {s="EURAUD"; break;};
      case  3: {s="EURCHF"; break;};
      case  4: {s="EURGBP"; break;};
      case  5: {s="EURJPY"; break;};
      case  6: {s="EURUSD"; break;};
      case  7: {s="GBPCHF"; break;};
      case  8: {s="GBPJPY"; break;};
      case  9: {s="GBPUSD"; break;};
      case 10: {s="USDCAD"; break;};
      case 11: {s="USDCHF"; break;};
      case 12: {s="USDJPY"; break;};
      default: {s="EURUSD"; break;};
     }
   MAshort=iMA(s,tf,(int)MathRound(Colony[1][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   MAlong =iMA(s,tf,(int)MathRound(Colony[2][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS));
   
//--- GA is selecting the optimal F
   optF=Colony[GeneCount][chromos];                                   
   
   leverage=AccountInfoInteger(ACCOUNT_LEVERAGE);
   contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE);
   b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth);
   
//--- for a neural network using historical data - where the data is copied from
   for(from=b;from>=1;from--) 
     {
      CopyBuffer(MAshort,0,from,count,ShortBuffer);
      CopyBuffer(MAlong,0,from,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         if(trig==false)
           {
            CopyOpen(s,tf,from,count,o);
            OpenPrice=o[1];
            dir="SELL";
            trig=true;
           }
         else
           {
            if(dir=="BUY")
              {
               CopyOpen(s,tf,from,count,o);
               if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0;
               if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
               if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt;
               OpenPrice=o[1];
               dir="SELL";
               trig=true;
              }
           }
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         if(trig==false)
           {
            CopyOpen(s,tf,from,count,o);
            OpenPrice=o[1];
            dir="BUY";
            trig=true;
           }
         else
           {
            if(dir=="SELL")
              {
               CopyOpen(s,tf,from,count,o);
               if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0;
               if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
               if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt;
               OpenPrice=o[1];
               dir="BUY";
               trig=true;
              }
           }
        }
     }
   if(rDD<=trainDD) ff=t; else ff=0.0;
   AmountStartsFF++;
   Colony[0][chromos]=ff;
  }

//+---------------------------------------------------------------------+
//| getting the optimized neural network parameters and other variables |
//| should always be equal to the number of genes                       |
//+---------------------------------------------------------------------+
void GetTrainResults()
  {
//---  intermediary between a gene colony and optimised parameters
   int z;                                                             
   MAshort=iMA(s,tf,(int)MathRound(Chromosome[1]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   MAlong =iMA(s,tf,(int)MathRound(Chromosome[2]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   CopyBuffer(MAshort,0,from,count,ShortBuffer);
   CopyBuffer(MAlong,0,from,count,LongBuffer);
//--- save the best pair
   z=(int)MathRound(Chromosome[GeneCount-1]*12);                      
   switch(z)
     {
      case  0: {s="AUDUSD"; break;};
      case  1: {s="AUDUSD"; break;};
      case  2: {s="EURAUD"; break;};
      case  3: {s="EURCHF"; break;};
      case  4: {s="EURGBP"; break;};
      case  5: {s="EURJPY"; break;};
      case  6: {s="EURUSD"; break;};
      case  7: {s="GBPCHF"; break;};
      case  8: {s="GBPJPY"; break;};
      case  9: {s="GBPUSD"; break;};
      case 10: {s="USDCAD"; break;};
      case 11: {s="USDCHF"; break;};
      case 12: {s="USDJPY"; break;};
      default: {s="EURUSD"; break;};
     }
//--- saving the best optimal F
   optF=Chromosome[GeneCount];                                        
  }
//+------------------------------------------------------------------+

Schauen wir uns nun die Hauptfunktion des Algorithmus an - die Fitnessfunktion.

Die Idee hinter einem selbstoptimierenden Expert Advisors basiert auf der Simulation des Handelprozesses (wie im Standard-Tester von MetaQuotes) in einem gewissen Zeitraum in der Fitnessfunktion, die einen Input von optimierten Variablen vom Genetischen Algorithmus bekommt (GA-Funktion ()). Im Fall des Algorithmus, der auf die Schnittmenge von gleitenden Durchschnitten basiert, beinhalten die optimierten Variablen:

  • Instrument (am Devisenmarkt: ein Währungspaar): dies ist ein typischer Expert Advisor mit vielen Währungen, und der Genetische Algorithmus wählt ein Instrument (da der Code vom Expert Advisor, der für mehrere Wettkämpfe vorgeschlagen wurde, genommen wurde, sind seine Paare die gleichen, wie die Währungspaare im Wettkampf; generell kann jedes Instrument von einem Makler sein).
    Beachten Sie, dass der Expert Advisor im Testmodus leider die Paarliste nicht vom MarketWatch-Fenster bekommen kann (hier wird es nochmal klargestellt dank unserer MetaQuote-User No way!). Wenn Sie demzufolge den Expert Advisor separat für Devisenmarkt und Shares laufen lassen wollen, definieren sie Ihre eigenen Instrumente in der FF und der GetTrainResults()-Funktion.
  • der Teil der Anzahlung, der zum Handeln benutzt wird;
  • Zeitraum für zwei gleitende Durchschnittswerte;

Die unteren Beispiele zeigen eine Variante eines Expert Advisors zum Ausprobieren!

Realer Handelscode kann wesentlich vereinfacht werden wenn man die Liste der Instrumente vom MarketWatch-Fenster nimmt.

Um dies mit der FF und der GetTrainResults()-Funktion mit den Kommentaren "//--- GA is selecting a pair" and "//--- saving the best pair" zu erledigen, schreiben sie einfach:

//--- GA is selecting a pair
  z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1));
  s=SymbolName(z,true);

Zu Beginn der FF haben wir also die Variablen für die Simulation des verlaufbasierten Handelns definiert und initialisiert In der nächsten Phase sammeln wir verschiedene Werte von optimierten Variablen des Genetischen Algorithmus, zum Beispiel von dieser Zeile: "optF=Colony[GeneCount][chromos]; der Teil des Anzahlungswertes wird von GA in die FF transferiert.

Außerdem überprüfen wir die zur Verfügung stehende Anzahl von Balken im Verlauf, beginnend entweder mit derm10.000sten Balken oder dem ersten vorhandenen, wir simulieren den Prozess "Erhalt von Quotes" im "Open Price only"-Modus und treffen Handelsentscheidungen.

  • Kopieren Sie die Werte von gleitenden Durchschnitten zu Puffern;
  • Überprüfen Sie, ob es ein Death Cross ist.
  • Wenn es ein Death Cross ist und keine offenen Positionen (if(trig==false)), öffnen Sie eine virtuelle SELL-Position (erinnern Sie sich aber an den Öffnungspreis und die Richtung);
  • Wenn es ein Death Cross ist und eine offene BUY-Position (if(dir=="BUY")) - nehmen sie den Balken-Öffnungspreis und passen Sie auf, dass die drei wichtigen Zeilen wie folgt aussehen:
  1. Simulieren Sie das Schließen einer Position und eine Veränderung der Bilanz: die aktuelle Bilanz wird durch den aktuellen Bilanzwert erhöht, der mit dem Teil der gehandelten Anzahlung multipliziert wird, die mit der Differenz zwischen dem Öffnungs- und Schließ-Preis multipliziert wird, und nochmal mit dem PIP-Preis multipliziert.
  2. Überprüfen Sie, ob die aktuelle Bilanz ihr Maximum im Verlauf der Handelssimulation erreicht; falls nicht, kalkulieren Sie den maximalen finanziellen Wertverlust.
  3. Konvertieren Sie den zuvor kalkulierten finanziellen Wertverlust in Geld beim relativen Bilanz-Wertverlust.
  • Öffnen Sie eine virtuelle SELL-Position (aber denken Sie an den offenen Preis und die Richtung):
  • Machen Sie ähnliche Überprüfungen und Kalkulationen für das Golden Cross.

Nach einem vollständigen Durchlauf des Verlaufs und der Simulation des virtuellen Handelns, kalkulieren Sie den endgültigen FF-Wert. Wenn der kalkulierte relative Bilanz-Wertverlust weniger ist als der für den Test verwendete, setzen Sie FF=balance, sonst FF=0. Der genetische Algorithmus zielt auf die Maximalisierung der Fitness Funktion ab.

Der genetische Algorithmus wird die Werte finden, die die Bilanz am kleinsten relativen Wertverlust maximieren, indem er den Instrumenten, Teilanzahlungen und gleitenden Durchschnitten verschiedene Werte gibt (das Minimum wird vom User gesetzt).


Fazit

Es ist einfach, einen autodidakten Expert Advisor zu erstellen; das Schwierige ist dann, den Input herauszufinden (das Wichtige ist die Idee, die Implementierung ist dann nur eine technische Problemstellung).

Auf die Frage von Pessimisten: "Funktioniert das?", habe ich die Antwort: "Das tut es." Optimisten muss ich aber warnen: Dies ist nicht der heilige Gral.

Was ist der wesentliche Unterschied zwischen der hier vorgeschlagenen Methode und Quantums Methode? Das kann am besten mit einem Vergleich der Expert Advisor anhand von MAs exemplifiziert werden.

  1. Die Entscheidung, gleitende Mittelwerte in adaptiven Handelssystem zu verwenden, sollte vor der Kompilierung und stark verschlüsselt gemacht werden, und diese Option ist nur bei einer limitierten Anzahl an Versionen möglich. Wir fällen keine Entscheidungen über die Perioden vor der Kompilierung in genetisch optimierten Expert Advisors. Diese Entscheidung wird nur von GAs getroffen, und die Anzahl an Varianten ist nur vom gesunden Menschenverstand limitiert.
  2. Virtuelles Handeln in adaptiven Handelssystemen passiert sukzessive. Bei genetisch optimierten Expert Advisors ist dies oft nicht der Fall - und wenn, dann nur bei einer Neu-Optimierung. Computerleistung aufgrund der steigenden Anzahl an Strategien, Parametern und Instrumenten könnte ein limitierende Faktor für ein adaptives Handelssystem sein.


Anhang

Diese Ergebnisse bekommen wir, wenn das neuronale Netzwerk im Tester ohne jegliche Optimierung läuft und auf den täglichen Grafiken ab dem 01.01.2010 beruht.

Strategy Tester-Prüfbericht
MetaQuotes-Demo (Build 523)
Expert Advisor: ANN-Beispiel
Kürzel: EURUSD
Zeitraum: Täglich (vom 01. 01. 2010 - 30. 09. 2011)
Eingangsparameter: trainDD=0.9
maxDD=0.1
Makler: Alpari NZ Limited
Währung: USD
Anfangseinlage: 10.000,00
Leverage 1.100.
Ergebnisse
Verlaufsqualität: 100%
Balken: 454 Kursänderungen (Ticks): 2554879
Gesamter Nettogewinn: -9.094,49 Bruttogewinn: 29.401,09 Bruttoverlust: -38.495,58
Gewinnfaktor: 0,76 Erwartete Auszahlung: -20,53 Margin Level 732,30%
Korrekturfaktor: -0,76 Sharpe-Ratio: -0,06 OnTester Ergebnis: 0
Wertverlust des Kontos:
Gesamter Wertverlust des Kontos: 9.102,56 Maximaler Wertverlust des Kontos: 11.464,70 (92,74%) Relativer Wertverlust des Kontos: 92,74% (11.464,70)
Wertverlust des Kapitals:
Gesamter Wertverlust des Kapitals: 9.176,99 Maximaler Wertverlust des Kapitals: 11.904,00 (93,53%) Relativer Wertverlust des Kapitals: 93,53% (11.904,00)
Abschlüsse insgesamt: 443 Kurzfristige (short) Abschlüsse (mit Gewinn %): 7 (14,29%) Langfristige (long) Abschlüsse (mit Gewinn %): 436 (53,44%)
Abschlüsse insgesamt: 886 Mit Gewinn (in % aller Abschlüsse) 234 (52,82%) Mit Verlust (in % aller Abschlüsse) 209 (47,18%)
Größter Abschluss mit Gewinn: 1.095,57 Größter Abschluss mit Verlust: -1.438,85
Durchschnittlicher Abschluss mit Gewinn: 125,65 Durchschnittlicher Abschluss mit Verlust: -184,19
Maximal aufeinanderfolgende Gewinne 8 (397,45) Maximal aufeinanderfolgende Verluste 8 (-1.431,44)
Maximal aufeinanderfolgende Gewinne (Anzahl Gewinne) 1.095,57 (1) Maximal aufeinanderfolgende Verluste (Anzahl Verluste) -3.433,21 (6)
Durchschnittlich ununterbrochene Gewinne: 2 Durchschnittlich ununterbrochene Verluste: 2

unten: drei Varianten der Neu-Optimierung:

zuerst...

Zeit Deals Symbol Typ Direction Volumen Kurs Order Swap Profit Balance
01.01.2010 00:00 1   Balance         0,00 10.000,00 10.000,00
01.04.2010 00:00 2 AUDUSD Buy in 0,90 0,89977 2 0,00 0,00 10.000,00
05.01.2010 00:00 3 AUDUSD Sell out 0,90 0,91188 3 5,67 1.089,90 11.095,57
05.01.2010 00:00 4 AUDUSD Buy in 0,99 0,91220 4 0,00 0,00 11.095,57
06.01.2010 00:00 5 AUDUSD Sell out 0,99 0,91157 5 6,24 -62,37 11.039,44
2010,01,06 00:00 6 AUDUSD Buy in 0,99 0,91190 6 0,00 0,00 11.039,44
07.01.2010 00:00 7 AUDUSD Sell out 0,99 0,91924 7 18,71 726,66 11.784,81


zweitens...

Zeit Deals Symbol Typ Direction Volumen Kurs Order Provision Swap Profit Balance
19.05.2010 00:00 189 AUDUSD Sell out 0,36 0,86110 189 0,00 2,27 -595,44 4.221,30
19.05.2010 00:00 190 EURAUD Sell in 0,30 1,41280 190 0,00 0,00 0,00 4.221,30
20.05.2010 00:00 191 EURAUD Buy out 0,30 1,46207 191 0,00 7,43 -1.273,26 2.955,47
20.05.2010 00:00 192 AUDUSD Buy in 0,21 0,84983 192 0,00 0,00 0,00 2.955,47


drittens...

Zeit Deals Symbol Typ Direction Volumen Kurs Order Swap Profit Balance
16.06.2010 00:00 230 GBPCHF Buy in 0,06 1,67872 230 0,00 0,00 2.128,80
17.06.2010 00:00 231 GBPCHF Sell out 0,06 1,66547 231 0,13 -70,25 2.058,68
2010,06,17 00:00 232 GBPCHF Buy in 0,06 1,66635 232 0,00 0,00 2.058,68
2010,06,18 00:00 233 GBPCHF Sell out 0,06 1,64705 233 0,04 -104,14 1.954,58
2010,06,18 00:00 234 AUDUSD Buy in 0,09 0,86741 234 0,00 0,00 1.954,58
2010,06,21 00:00 235 AUDUSD Sell out 0,09 0,87184 235 0,57 39,87 1.995,02
21.06.2010 00:00 236 AUDUSD Buy in 0,09 0,88105 236 0,00 0,00 1.995,02
22.06.2010 00:00 237 AUDUSD Sell out 0,09 0,87606 237 0,57 -44,91 1.950,68
2010,06,22 00:00 238 AUDUSD Buy in 0,09 0,87637 238 0,00 0,00 1.950,68
23.06.2010 00:00 239 AUDUSD Sell out 0,09 0,87140 239 0,57 -44,73 1.906,52
2010,06,23 00:00 240 AUDUSD Buy in 0,08 0,87197 240 0,00 0,00 1.906,52
24.06.2010 00:00 241 AUDUSD Sell out 0,08 0,87385 241 1,51 15,04 1.923,07
2010,06,24 00:00 242 AUDUSD Buy in 0,08 0,87413 242 0,00 0,00 1.923,07
2010,06,25 00:00 243 AUDUSD Sell out 0,08 0,86632 243 0,50 -62,48 1.861,09
2010,06,25 00:00 244 AUDUSD Buy in 0,08 0,86663 244 0,00 0,00 1.861,09
28.06.2010 00:00 245 AUDUSD Sell out 0,08 0,87375 245 0,50 56,96 1.918,55
2010,06,28 00:00 246 AUDUSD Buy in 0,08 0,87415 246 0,00 0,00 1.918,55
29.06.2010 00:00 247 AUDUSD Sell out 0,08 0,87140 247 0,50 -22,00 1.897,05
2010,06,29 00:00 248 AUDUSD Buy in 0,08 0,87173 248 0,00 0,00 1.897,05
01.07.2010 00:00 249 AUDUSD Sell out 0,08 0,84053 249 2,01 -249,60 1.649,46
07.01.2010 00:00 250 EURGBP Sell in 0,07 0,81841 250 0,00 0,00 1.649,46
02.07.2010 00:00 251 EURGBP Buy out 0,07 0,82535 251 -0,04 -73,69 1.575,73
02.07.2010 00:00 252 EURGBP Sell in 0,07 0,82498 252 0,00 0,00 1.575,73
05.07.2010 00:00 253 EURGBP Buy out 0,07 0,82676 253 -0,04 -18,93 1.556,76
05.07.2010 00:00 254 EURGBP Sell in 0,06 0,82604 254 0,00 0,00 1.556,76
06.07.2010 00:00 255 EURGBP Buy out 0,06 0,82862 255 -0,04 -23,43 1.533,29


P.S. Hausaufgabe: Wählen Sie nicht nur die Parameter eines bestimmten Systems, sondern auch das System, das am besten zum Markt in einem bestimmten Moment passt!


Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/334

Beigefügte Dateien |
anntrainlib.mqh (9.76 KB)
matrainlib.mqh (8.94 KB)
ugalib.mqh (33.26 KB)
annexample.mq5 (4.32 KB)
maexample.mq5 (4.22 KB)
musthavelib.mqh (8.14 KB)
Die Alles oder Nichts - Strategie am Devisenmarkt Die Alles oder Nichts - Strategie am Devisenmarkt
In diesem Artikel wird die Erstellung einer einfachen Handelsstrategie beschrieben, die nach dem "Alles oder Nichts"-Spielprinzip funktioniert. Wir wollen hier keinen gewinnbringenden Expert Advisor erstellen, sondern den Ersteinsatz mehrere Male mit der höchsten Wahrscheinlichkeit vergrößern. Kann man den Jackpot am Devisenmarkt gewinnen, ohne dass man etwas über technische Analysen weiß oder Indikatoren verwendet?
Hinzufügen von neuen UI-Sprachen zur MetaTrader5-Plattform Hinzufügen von neuen UI-Sprachen zur MetaTrader5-Plattform
Die Benutzerschnittstelle der MetaTrader5-Plattform wird in mehrere Sprachen übersetzt. Keine Sorge, wenn Ihre Sprache nicht unter den unterstützten aufscheint. Mit dem kostenlosen Paket "MetaTrader-5-MultiLanguage" von MetaQuotes Software Corp. können Sie ganz einfach eine Übersetzung durchführen. In diesem Artikel werden wir an einigen Bespielen zeigen, wie man eine neue UI-Sprache zur MetaTrader5-Plattform hinzufügen kann.
Wie man in wenigen Minuten einen MQL5-Expert-Advisor mit dem Expert-Advisor-Baum erstellt: Teil eins Wie man in wenigen Minuten einen MQL5-Expert-Advisor mit dem Expert-Advisor-Baum erstellt: Teil eins
Der EA-Baum ist das erste Programm, mit dem man anhand von 'drag and drop' einen Expert Advisor in MetaTrader-MQL5 erstellen kann. Mit einer sehr benutzerfreundlichen, graphischen Benutzerschnittstelle können sie komplexe Expert Advisor in MQL5 erstellen. In diesem Programm entwirft man Expert Advisor, indem man Felder miteinander verbindet. Die Felder können MQL5-Funktionen enthalten, technische Indikatoren, benutzerdefinierte Indikatoren oder Werte. Mit dem "Baum aus Feldern" generiert das Programm den MQL5-Code des Expert Advisors.
Wie man mit einem UML-Werkzeug einen Expert Advisor entwickelt Wie man mit einem UML-Werkzeug einen Expert Advisor entwickelt
In diesem Artikel wird die Erstellung von Expert Advisors mittels UML (Unified Modeling Language) beschrieben, einer Modellierungssprache, die für die bildliche Modellierung von objektorientierten Softwaresystemen verwendet wird. Der Hauptvorteil dieses Ansatzes ist die Verbildlichung des Modellierungsprozesses. In diesem Artikel wird auch ein Beispiel vorgestellt, das die Modellierung von Struktur und Eigenschaften eines Expert Advisors mit dem Programm "Software Ideas Modeler" zeigt.