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

Roman Zamozhnyy | 4 Mai, 2016


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:

//+------------------------------------------------------------------+
//|  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:

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:

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.

  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.

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!