Geschichten von Handelsrobotern: Ist weniger mehr?

Roman Zamozhnyy | 2 Juni, 2016

Bevor ich ein Problem lösen kann, muss ich mir erst einmal selbst eingestehen, dass es eins ist. Wenn ich glaube, die Lösung gefunden zu haben, muss ich beweisen, dass ich recht habe.

Ich kenne nur einen Weg, diesen Beweis anzutreten - mit meinem eigenen Geld.

Jesse Livermore


Vorbemerkung

Im Artikel Der letzt Kreuzzug haben wir eine ziemlich interessante, doch derzeit nicht oft eingesetzte Methode zur Anzeige von Marktinformationen untersucht - die Point-&Figure-Charts. Das angebotene Script konnte ein Chart zeichnen. Doch es hat noch keinen automatischen Handel vorgeschlagen. Jetzt können wir versuchen, den Handelsvorgang mit Hilfe des Point-&Figure-Charts zur Analyse und zur Entscheidung über die Richtung und des Handelsvolumens zu automatisieren.

Ich beschreibe hier nicht die grundlegenden Zeichnungsprinzipien, sehen Sie sich einfach ein typisches Chart an:

Copyright (c) 2012-2014 Roman Rich
Euro vs US Dollar, Box-20, Reverse-3


    1.4588 | \.....\.................................................................... | 1.4588
    1.4521 | X\....X\................................................................... | 1.4521
    1.4454 | XO\.\.XO\.................................................................. | 1.4454
    1.4388 | XOX\X\XO.\................................................................. | 1.4388
    1.4322 | XOXOXOXO..\................................................................ | 1.4322
    1.4256 | XOXOXOXO...\....\.......................................................... | 1.4256
    1.4191 | XOXO/OXO....\...X\......................................................... | 1.4191
    1.4125 | XOX/.O/O.....\..XO\........................................................ | 1.4125
    1.4060 | XO/../.O......\.XO.\....................................................... | 1.4060
    1.3996 | ./.....O.......\XO..\...................................................... | 1.3996
    1.3932 | .......OX.......XO...\....................................................X | 1.3932
    1.3868 | .......OXO..X.X.XOX...\.................................................X.X | 1.3868
    1.3804 | .......OXO..XOXOXOXOX..\..............................................X.XOX | 1.3804
    1.3740 | .......OXO..XOXOXOXOXO..\.................................\...........XOXOX | 1.3740
    1.3677 | .......OXOX.XO.O.OXOXO...\................................X\..........XOXOX | 1.3677
    1.3614 | .......OXOXOX....O.OXO....\...............................XO\.........XOXOX | 1.3614
    1.3552 | .......O.OXOX...../OXO.....\..............................XO.\........XOXOX | 1.3552
    1.3490 | .........OXOX..../.O.OX.....\.............................XO..\.......XOXO. | 1.3490
    1.3428 | .........OXOX.../....OXO.....\X.\.........................XO...\\...X.XOX.. | 1.3428
    1.3366 | .........O.OX../.....OXO......XOX\........................XO....X\..XOXOX.. | 1.3366
    1.3305 | ...........OX./......OXO....X.XOXO\.....................X.XO....XO\.XOXO... | 1.3305
    1.3243 | ...........OX/.......O.O....XOXOXOX\....................XOXO....XO.\XOX.../ | 1.3243
    1.3183 | ...........O/..........OX...XOXOXOXO\...................XOXOX.X.XOX.XOX../. | 1.3183
    1.3122 | .........../...........OXO..XOXOXOXO.\..........X...X.X.XOXOXOXOXOXOXO../.. | 1.3122
    1.3062 | .......................OXOX.XOXO.OXO..\.........XOX.XOXOXOXOXOXOXOXOX../... | 1.3062
    1.3002 | .......................O.OXOXO...O/O...\........XOXOXOXOXO.OXO.OXOXO../.... | 1.3002
    1.2942 | .........................OXOX..../.O....\.......XOXOXOXOX..OX..OXOX../..... | 1.2942
    1.2882 | .........................O.OX.../..O.....\......XOXO.OXO...OX..OXOX./...... | 1.2882
    1.2823 | ...........................OX../...OX.....\.....XO...OX.../OX..O/OX/....... | 1.2823
    1.2764 | ...........................OX./....OXO.....\....X....OX../.O.../.O/........ | 1.2764
    1.2706 | ...........................OX/.....OXO..X...\...X....O../......../......... | 1.2706
    1.2647 | ...........................O/......O.OX.XOX..\..X....../................... | 1.2647
    1.2589 | .........................../.........OXOXOXO..\.X...../.................... | 1.2589
    1.2531 | .....................................OXOXOXO...\X..../..................... | 1.2531
    1.2474 | .....................................OXO.OXO....X.../...................... | 1.2474
    1.2417 | .....................................OX..O.O..X.X../....................... | 1.2417
    1.2359 | .....................................OX....OX.XOX./........................ | 1.2359
    1.2303 | .....................................O.....OXOXOX/......................... | 1.2303
    1.2246 | ...........................................OXOXO/.......................... | 1.2246
    1.2190 | ...........................................OXO./........................... | 1.2190
    1.2134 | ...........................................OX.............................. | 1.2134
    1.2078 | ...........................................O............................... | 1.2078
    1.2023 | ........................................................................... | 1.2023

             222222222222222222222222222222222222222222222222222222222222222222222222222
             000000000000000000000000000000000000000000000000000000000000000000000000000
             111111111111111111111111111111111111111111111111111111111111111111111111111
             111111111111111111111111112222222222222222222222222222222333333333333333344
             ...........................................................................
             000000000001111111111111110000000000000000000000011111111000000000000011100
             788888899990000001111112221122233445566666677888900001222123444567778901213
             ...........................................................................
             200011211220111220011231220101212121201112222001100010001002123110112020231
             658801925683489071404504193396436668111288937260415979579417630739120547713
                                                                                        
             000100001012111111110111111100112010210001111101101101011111111101011101110
             910501876933613095500253237788652909250001557626626824655375907538165785367
             :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
             550433251023230204310404232105354323532031240033315125241340044324523153453
             000000000000000000000000000000000000000000000000000000000000000000000000000

Ich werde nicht anzweifeln, dass in diesem Chart ganz klar Handelsmöglichkeiten sichtbar sind - ich schlage nur vor, dass Sie zwei Handelshypothesen nachprüfen:

Handel mit dem Haupttrend — im Haussenmarkt kaufen und im Baissenmarkt verkaufen.

Stop Losses verwenden, die vor Ihrem Eintritt in den Markt festgelegt werden.

Lassen Sie Ihre Gewinne wachsen.

Schließen Sie Abschlüsse, die Verluste machen (gute Abschlüsse zeigen meist sofortigen Gewinn).


Wie wählt man das Volumen?

Achten Sie auf die o.g. Notierungen eines Meisters der Aktienspekulation: arbeiten Sie mit Stop-Orders. Ich steige am liebsten mit einem gewissen Volumen in den Markt ein, sodass der Verlust im Saldo, wenn die Stop Loss-Order ausgelöst wird, nur max. 1 % des Saldos beträgt, den ich akzeptieren kann (Ralph Vince nennt das in Die Mathematik der Geldverwaltung das optimale F). Das Verlustrisiko pro Abschluss ist ein Wert, der gut zu optimieren ist (opt_f im unten stehenden Code).

Daher besitzen wir eine Funktion, die eine Kauf/Verkauf-Order mit dem Volumenberechnungsmechanismus platziert, der vom annehmbaren Risiko pro Abschluss abhängt:

//+------------------------------------------------------------------+
//| The function places an order with a precalculated volume         |
//+------------------------------------------------------------------+
void PlaceOrder()
  {
//--- Variables for calculating the lot
   uint digits_2_lot=(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   double trade_risk=AccountInfoDouble(ACCOUNT_EQUITY)*opt_f;
   double one_tick_loss_min_lot=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE_LOSS)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
//--- Fill out the main fields of the request
   trade_request.magic=magic;
   trade_request.symbol=symbol;
   trade_request.action=TRADE_ACTION_PENDING;
   trade_request.tp=NULL;
   trade_request.comment=NULL;
   trade_request.type_filling=NULL;
   trade_request.stoplimit=NULL;
   trade_request.type_time=NULL;
   trade_request.expiration=NULL;
   if(is_const_lot==true)
     {
      order_vol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
     }
   else
     {
      order_vol=trade_risk/(MathAbs(trade_request.price-trade_request.sl)*MathPow(10,digits_2_lot)*one_tick_loss_min_lot)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
      order_vol=MathMax(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN));
      if(SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT)!=0) order_vol=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT));
      order_vol=NormalizeDouble(order_vol,(int)MathAbs(MathLog10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP))));
     }
//--- Place an order
   while(order_vol>0)
     {
      trade_request.volume=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX));
      if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
      order_vol=order_vol-SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
     };
   ticket=trade_result.order;
  };


Welches Optimierungskriterium soll man verwenden?

Tatsächlich gibt es nur zwei Optimierungskriterien: Minimierung der Inanspruchnahme auf einer gegebenen Renditeebene, oder Maximierung des Saldos für eine gegebene Ebene der Inanspruchnahme. Ich optimiere lieber das zweite Kriterium:

//+------------------------------------------------------------------+
//| Result of strategy run in the testing mode                       |
//+------------------------------------------------------------------+
double OnTester()
  {
   if(TesterStatistics(STAT_EQUITY_DDREL_PERCENT)>(risk*100))
      return(0);
   else
      return(NormalizeDouble(TesterStatistics(STAT_PROFIT),(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS)));
  };

wo das Risiko die Stufe der Inanspruchnahme ist, oberhalb derer die Strategie für nicht mehr akzeptabel ist. Dies ist auch eine der Variablen, die optimiert werden muss.


Wann sollte man re-optimieren?

Jemand re-optimiert Strategien in Zeitintervallen (z.B. einmal pro Woche, pro Monat, jemand anderer in Abschlussintervallen (nach 50, 100 Abschlüssen) und wieder ein anderer dann, wenn sich der Markt verändert. Die Notwendigkeit einer Re-Optimierung hängt einzig und allein von dem im vorausgegangenen Abschnitt gewählten Optimierungskriterium ab. Wenn die Inanspruchnahme unter den maximal zugelassenen Risikoparameter fällt, re-optimieren wir die Strategie. Ist alles gut, dann machen wir da gar nichts. Für mich ist eine Inanspruchnahme von mehr als 10% nicht mehr akzeptabel. Daher re-optimiere ich die Arbeitsweise des Systems, sobald es in seinen Abläufen eine größere Inanspruchnahme aufweist.


Kann man alles auf einmal optimieren?

Leider kann man im Strategietester keine externen Variablen für den Modus "Alle in der Marktbeobachtung gewählten Kürzel" optimieren. Daher wählen wir besser die Marktinstrumente zusammen mit anderen externen Variablen aus, die optimiert werden sollen. Das geht folgendermaßen:

//+------------------------------------------------------------------+
//| Enumeration of symbols                                           |
//+------------------------------------------------------------------+
enum  SYMBOLS
  {
   AA=1,
   AIG,
   AXP,
   BA,
   C,
   CAT,
   DD,
   DIS,
   GE,
   HD,
   HON,
   HPQ,
   IBM,
   IP,
   INTC,
   JNJ,
   JPM,
   KO,
   MCD,
   MMM,
   MO,
   MRK,
   MSFT,
   PFE,
   PG,
   QQQ,
   T,
   SPY,
   UTX,
   VZ,
   WMT,
   XOM
  };
//+------------------------------------------------------------------+
//| Symbol selection function                                        |
//+------------------------------------------------------------------+
void  SelectSymbol()
  {
   switch(selected_symbol)
     {
      case  1: symbol="#AA";   break;
      case  2: symbol="#AIG";  break;
      case  3: symbol="#AXP";  break;
      case  4: symbol="#BA";   break;
      case  5: symbol="#C";    break;
      case  6: symbol="#CAT";  break;
      case  7: symbol="#DD";   break;
      case  8: symbol="#DIS";  break;
      case  9: symbol="#GE";   break;
      case 10: symbol="#HD";   break;
      case 11: symbol="#HON";  break;
      case 12: symbol="#HPQ";  break;
      case 13: symbol="#IBM";  break;
      case 14: symbol="#IP";   break;
      case 15: symbol="#INTC"; break;
      case 16: symbol="#JNJ";  break;
      case 17: symbol="#JPM";  break;
      case 18: symbol="#KO";   break;
      case 19: symbol="#MCD";  break;
      case 20: symbol="#MMM";  break;
      case 21: symbol="#MO";   break;
      case 22: symbol="#MRK";  break;
      case 23: symbol="#MSFT"; break;
      case 24: symbol="#PFE";  break;
      case 25: symbol="#PG";   break;
      case 26: symbol="#QQQ";  break;
      case 27: symbol="#T";    break;
      case 28: symbol="#SPY";  break;
      case 29: symbol="#UTX";  break;
      case 30: symbol="#VZ";   break;
      case 31: symbol="#WMT";  break;
      case 32: symbol="#XOM";  break;
      default: symbol="#SPY";  break;
     };
  };

Ggf. können Sie die benötigten Instrumente in die Kürzelauswahlfunktion und die Aufzählung einfügen (die Funktion wird in OnInit() aufgerufen).


Welchen Roboter haben wir jetzt erzeugt?

Kursschwankungs-Handler:

//+------------------------------------------------------------------+
//| A typical ticks handler OnTick()                                 |
//|     Draw the chart only based on complete bars but first         |
//|     check if it is a new bar.                                    |
//|     If the bar is new and there is a position, check             |
//|     whether we need to move the stop loss,                       |
//|     if the bar is new and no position, check                     |
//|     if we have conditions for opening a deal.                    |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If the bar is new
   if(IsNewBar()==true)
     {
      RecalcIndicators();
      //--- Tester/optimizer mode?
      if((MQLInfoInteger(MQL_TESTER)==true) || (MQLInfoInteger(MQL_OPTIMIZATION)==true))
        {
         //--- Is it the testing period?
         if(cur_bar_time_dig[0]>begin_of_test)
           {
            //--- If there is an open position on the symbol
            if(PositionSelect(symbol)==true)
               //--- check if we need to move SL; if need, move it
               TrailCondition();
            //--- If there are no positions
            else
            //--- check if we need to open a position; if we need, open it
               TradeCondition();
           }
        }
      else
        {
         //--- if there is an oprn position on the symbol
         if(PositionSelect(symbol)==true)
            //--- check if we need to move SL; if need, move it
            TrailCondition();
         //--- If there are no positions
         else
         //---  check if we need to open a position; if we need, open it
            TradeCondition();
        }

     };
  };

Für Strategie 1: "Kaufen oberhalb der Stützebene mittels Verwendung einer Stop-Order oberhalb des Hochs der vorausgegangenen X-Spalte. Verkaufen unterhalb der Widerstandslinie mittels Verwendung einer Sell-Stop-Order unterhalb des Tiefs der vorausgegangenen O-Spalte und Einsatz eines Trailing Stop an der Pivot-Ebene|":

//+------------------------------------------------------------------+
//| Function checks trade conditions for opening a deal              |
//+------------------------------------------------------------------+
void TradeCondition()
  {
   if(order_col_number!=column_count)
      //--- Are there any orders on the symbol?
     {
      if(OrdersTotal()>0)
        {
         //--- Delete them!
         for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++)
           {
            ticket=OrderGetTicket(loc_count_1);
            if(!OrderSelect(ticket)) Print("Failed to select order #",ticket);
            if(OrderGetString(ORDER_SYMBOL)==symbol)
              {
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
              };
           };
         order_col_number=column_count;
         return;
        }
      else
        {
         order_col_number=column_count;
         return;
        }
     }
   else
      if((MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK)) && 
         (pnf[column_count-1].column_type=='X') && 
         (pnf[column_count-1].max_column_price<=pnf[column_count-3].max_column_price))
        {
         //--- Conditions for BUY met; let's see if there are any pending Buy orders for the symbol with the price we need?
         trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price+double_box),digit_2_orders);
         trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price-(reverse-1)*double_box),digit_2_orders);
         trade_request.type=ORDER_TYPE_BUY_STOP;
         if(OrderSelect(ticket)==false)
            //--- No pending orders - place an order
           {
            PlaceOrder();
            order_col_number=column_count;
           }
         else
         //--- If there is a pending order
           {
            //--- what is the type and price of the pending order?
            if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || 
               ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
              {
               //--- The wrong type or the price differs - close the order
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
               //--- open with the desired price
               PlaceOrder();
               order_col_number=column_count;
              };
           };
         return;
        }
   else
      if((MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK)) && 
         (pnf[column_count-1].column_type=='O') && 
         (pnf[column_count-1].min_column_price>=pnf[column_count-3].min_column_price))
        {
         //--- Conditions for SELL met; let's see if there are any pending Sell orders for the symbol with the price we need?
         trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price-double_box),digit_2_orders);
         trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price+(reverse-1)*double_box),digit_2_orders);
         trade_request.type=ORDER_TYPE_SELL_STOP;
         if(OrderSelect(ticket)==false)
            //--- No pending orders, place an order
           {
            PlaceOrder();
            order_col_number=column_count;
           }
         else
         //--- or there is a pending order
           {
            //--- what is the type and price of the pending order?
            if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || 
               ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
              {
               //--- The wrong type or the price differs - close the order
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
               //--- and open with the desired price
               PlaceOrder();
               order_col_number=column_count;
              };
           };
         return;
        }
   else
      return;
  };
//+------------------------------------------------------------------+
//| The function checks conditions for moving Stop Loss              |
//+------------------------------------------------------------------+
void TrailCondition()
  {
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box),digit_2_orders);
   else
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box),digit_2_orders);
   if(PositionGetDouble(POSITION_SL)!=trade_request.sl)
      PlaceTrailOrder();
  };

Für Strategie 2: "Kaufen am Durchbruchspunkt der Widerstandslinie; verkaufen am Durchbruchspunkt der Stützlinie; einen Stop-Loss an der Pivot-Ebene und einen Trailing Stop an der Trendlinie setzen":

//+------------------------------------------------------------------+
//| The function checks trade conditions for opening a deal          |
//+------------------------------------------------------------------+
void TradeCondition()
  {
   if(order_col_number!=column_count)
      //--- Are there any orders for the symbol?
     {
      if(OrdersTotal()>0)
        {
         //--- Delete them!
         for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++)
           {
            ticket=OrderGetTicket(loc_count_1);
            if(!OrderSelect(ticket)) Print("Failed to select order #",ticket);
            if(OrderGetString(ORDER_SYMBOL)==symbol)
              {
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
              };
           };
         order_col_number=column_count;
         return;
        }
      else
        {
         order_col_number=column_count;
         return;
        }
     }
   else
   if(MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK))
     {
      //--- Conditions for BUY met; let's see if there are any pending Buy orders for the symbol with the price we need?
      trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price),digit_2_orders);
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price-(reverse-1)*double_box),digit_2_orders);
      trade_request.type=ORDER_TYPE_BUY_STOP;
      if(OrderSelect(ticket)==false)
         //--- No pending orders - place an order
        {
         PlaceOrder();
         order_col_number=column_count;
        }
      else
      //--- or there is a pending order
        {
         //--- what is the type and price of the pending order?
         if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || 
            ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
           {
            //--- The wrong type or the price differs - close the order
            trade_request.order=ticket;
            trade_request.action=TRADE_ACTION_REMOVE;
            if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
            //--- open with the desired price
            PlaceOrder();
            order_col_number=column_count;
           };
        };
      return;
     }
   else
   if(MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK))
     {
      //--- Conditions for SELL met; let's see if there are any pending Sell orders for the symbol with the price we need?
      trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price),digit_2_orders);
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price+(reverse-1)*double_box),digit_2_orders);
      trade_request.type=ORDER_TYPE_SELL_STOP;
      if(OrderSelect(ticket)==false)
         //--- No pending orders - place an order
        {
         PlaceOrder();
         order_col_number=column_count;
        }
      else
      //--- If there is a pending order
        {
         //--- what is the type and price of the pending order?
         if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || 
            ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
           {
            //--- The wrong type or the price differs - close the order
            trade_request.order=ticket;
            trade_request.action=TRADE_ACTION_REMOVE;
            if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
            //--- open with the desired price
            PlaceOrder();
            order_col_number=column_count;
           };
        };
      return;
     }
   else
      return;
  };
//+------------------------------------------------------------------+
//| The function checks conditions for moving Stop Loss              |
//+------------------------------------------------------------------+
void TrailCondition()
  {
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
      trade_request.sl=NormalizeDouble(MathMax(SymbolInfoDouble(symbol,SYMBOL_ASK),MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box)),digit_2_orders);
   else
      trade_request.sl=NormalizeDouble(MathMin(SymbolInfoDouble(symbol,SYMBOL_BID),MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box)),digit_2_orders);
   if(PositionGetDouble(POSITION_SL)!=trade_request.sl)
      PlaceTrailOrder();
  };

Lieber Leser, hier muss ich Sie auf einige Punkte hinweisen.

Der Code für die Hilfsfunktionen findet sich in den Anhängen weiter unten. Der Code für das Chart ist zu umfangreich, daher spare ich ihn mir hier. Ich habe ihn stattdessen samt Kommentaren ebenfalls unten angehängt


Ergebnisse des EA-Handels

In den Dateien symbol_list_1.mhq und smybol_list_2.mhq habe ich zwei Sets Marktkürzel zur Optimierung vorbereitet. Sie umfassen Währungspaare und Aktien-CFDs des Dow Index.

Einrichtungsfenster:

Testoptionen

Im ersten Fall sieht das Einrichtungsfenster im Strategietester folgendermaßen aus:

Optimierungsparameter

Achten Sie auf die Startzeile des Tests. Der Roboter verlangt mind. ein paar Chartspalten für eine Analyse und Entscheidungsfindung. Und wenn Sie die Boxgröße auf = 50 Preisänderungen oder mehr einstellen, ist oft eine History von 1 Jahr selbst für eine Spalte nicht ausreichend. Daher sollten Sie für Charts mit Boxgröße von 50 Preisänderungen oder mehr aus dem Start des Roboterbetriebs den Zeitraum von ca. 3 Jahren oder mehr verwenden und den Start des Roboterbetriebs in den Teststart-Parametern im Einrichtungsfenster einstellen. In unserem Beispiel müssen Sie für einen Test mit der Boxgröße 100 Preisänderungen seit 01.01.2012 in der Registerkarte "Einstellungen" den Zeitraum seit 01.01.2009 und in der Registerkarte "Parameter" den Zeitraum seit 01.01.2012 einstellen.

Der falsche Wert des Parameters "Minimumposten handeln?" gibt an, das die Postengröße vom Saldo und der Variablen "Risiko pro Handel in %" abhängt (im vorliegenden Fall 1% pro Handel, doch kann dies auch noch optimiert werden). "Max Inanspruchnahme in %" ist das Optimierungskriterium in der OnTester()-Funktion. In diesem Beitrag verwende ich nur zwei Variablen für Optimierung: Kürzel und Boxgröße in Preisänderungen.

Optimierungsperiode: 2012-2013. Der EA läuft am besten auf dem EURUSD-Chart, das dieses Kürzel die beste Kursschwankungsabdeckung liefert. Die unten stehende Tabelle enthält einen kompletten Bericht zum Testen auf verschiedenen Währungspaaren mit Boxgröße 10, auf Basis der ersten Strategie:

Bestanden Ergebnis Gewinn Erwartete Auszahlung Gewinn-Faktor Rückfluss-Faktor Sharpe-Ratio Angepasst Eigenkapital, Max % Handel ausgewähltes_Kürzel Feld;
0,00 0,00 -1/002,12 -18,91 0,54 -0,79 -0,24 0,00 12,67 53,00 AUDCAD 10,00
1,00 886,56 886,56 14,53 1,40 1,52 0,13 886,56 5,76 61,00 AUDCHF 10,00
2,00 0,00 -1/451,63 -10,60 0,77 -0,70 -0,09 0,00 19,92 137,00 AUDJPY 10,00
3,00 -647,66 -647,66 -17,50 0,57 -0,68 -0,24 -647,66 9,46 37,00 AUDNZD 10,00
4,00 -269,22 -269,22 -3,17 0,92 -0,26 -0,03 -269,22 9,78 85,00 AUDUSD 10,00
5,00 0,00 -811,44 -13,52 0,72 -0,64 -0,14 0,00 12,20 60,00 CADCHF 10,00
6,00 0,00 1/686,34 16,53 1,36 1,17 0,12 0,00 11,78 102,00 CHFJPY 10,00
7,00 356,68 356,68 5,66 1,13 0,40 0,06 356,68 8,04 63,00 EURAUD 10,00
8,00 0,00 -1/437,91 -25,68 0,53 -0,92 -0,25 0,00 15,47 56,00 EURCAD 10,00
9,00 0,00 -886,66 -46,67 0,34 -0,74 -0,46 0,00 11,56 19,00 EURCHF 10,00
10,00 0,00 -789,59 -21,93 0,54 -0,75 -0,26 0,00 10,34 36,00 EURGBP 10,00
11,00 0,00 3/074,86 28,47 1,62 1,72 0,20 0,00 12,67 108,00 EURJPY 10,00
12,00 0,00 -1/621,85 -19,78 0,55 -0,97 -0,25 0,00 16,75 82,00 EURNZD 10,00
13,00 152,73 152,73 2,88 1,07 0,21 0,03 152,73 6,90 53,00 EURUSD 10,00
14,00 0,00 -1/058,85 -14,50 0,65 -0,66 -0,16 0,00 15,87 73,00 GBPAUD 10,00
15,00 0,00 -1/343,47 -25,35 0,43 -0,64 -0,34 0,00 20,90 53,00 GBPCAD 10,00
16,00 0,00 -2/607,22 -44,19 0,27 -0,95 -0,59 0,00 27,15 59,00 GBPCHF 10,00
17,00 0,00 1/160,54 11,72 1,27 0,81 0,10 0,00 12,30 99,00 GBPJPY 10,00
18,00 0,00 -1/249,91 -14,70 0,69 -0,85 -0,15 0,00 14,41 85,00 GBPNZD 10,00
19,00 208,94 208,94 5,36 1,12 0,25 0,05 208,94 7,81 39,00 GBPUSD 10,00
20,00 0,00 -2/137,68 -21,17 0,53 -0,79 -0,24 0,00 25,62 101,00 NZDUSD 10,00
21,00 0,00 -1/766,80 -38,41 0,30 -0,97 -0,53 0,00 18,10 46,00 USDCAD 10,00
22,00 -824,69 -824,69 -11,95 0,73 -0,90 -0,13 -824,69 9,11 69,00 USDCHF 10,00
23,00 2/166,53 2/166,53 26,10 1,58 2,40 0,18 2/166,53 7,13 83,00 USDJPY 10,00

2/029,87 -10/213,52




13,40 1/659,00

Hier ist die tabellarische Zusammenfassung für verschiedene Kürzel und Boxgrößenwerte:

Strategie Kürzel Boxgröße Handel Eigenkapital, Max % Gewinn Ergebnis Erwarteter Saldo
1 Währungen 10 1/659 September -10/214 2/030 2/030
1 Währungen 20 400 5 1/638 2/484 2/484
1 Aktien 50 350 60 7/599 7/599 15/199
1 Aktien 100 81 2 4/415 4/415 17/659
2 Währungen 10 338 20 -4/055 138 138
2 Währungen 20 116 8 4/687 3/986 3/986
2 Aktien 50 65 6 6/770 9/244 9/244
2 Aktien 100 12 1 -332 -332 -5/315

Was sehen wir?

Es gibt Narren, die es immer falsch machen.

Und es gibt Narren in der Wall Street, die glauben, man sollte immer handeln.

Es gibt niemanden auf der ganzen Welt, der immer alle notwendigen Informationen besitzt, um Aktien zu kaufen oder zu verkaufen und dies auch noch vernünftig machen zu können.

Die Folgerung daraus mag Sie verwundern: je weniger Sie handeln, umso größer ist die Wahrscheinlichkeit, dass Ihr Depot ansteigt. Hätten wir vor zwei Jahren unseren EA Aktien mit Boxgröße 100 und einem Risiko pro Handel von 1% handeln lassen, hätte der EA bislang nur 81 Abschlüsse gemacht (~ 1,25 Abschlüsse pro Kürzel pro Jahr) und wäre unser Depot um 44% angestiegen, mit einer durchschnittlichen Kapitalinanspruchnahme von etwas mehr als 2%. Da wir eine mögliche Inanspruchnahme von 10% hätten akzeptieren können, hätten wir pro Abschluss 4% riskieren können und unser Depot wäre allein in diesen zwei Jahren um 177% angestiegen - also 90% pro Jahr in US Dollar!!


Nachwort

Der Kurs ist niemals zu hoch, um zu kaufen und niemals zu niedrig, um zu verkaufen.

Nicht Denken führt zum großen Geld. Sondern sitzen.

Die oben beschriebenen Strategien können verändert werden und zeigen bei einer Inanspruchnahme von max. 10% eine noch größere Rendite. Halten Sie sich beim Handel zurück, versuchen Sie lieber einen Makler zu finden, der nicht nur ein "Standard-Set" an Kürzeln von zwei Dutzend Währungspaaren und drei Dutzend Aktien anbietet, sondern mind. drei- bis vierhundert dieser Kürzel (Aktien, Termingeschäfte). Die Wahrscheinlichkeit, dass diese Kürzel miteinander korrelieren ist größer und Ihr Depot ist sicherer. Noch eine letzte Bemerkung - Aktien liefern bessere Ergebnisse als Währungspaare.


PS (Werbung in eigener Sache)

Mein PnF Chartist Script steht im Market zur Verfügung. Es zeichnet mit Hilfe von Notierungen von MT4, MT5 oder Yahoo Finance, Point- & Figure-Charts in Textdateien. Verwenden Sie es für eine visuelle Suche nach Kursmustern, denn nichts ist ein besserer Tester/Optimierer als Ihr eigenes Gehirn. Sobald Sie auf Muster stoßen, verwenden Sie EA-Templates aus diesem Beitrag, die Ihre Ideen beweisen werden.