Visuelle Optimierung von Indikator und Signal Rentabilität

Sergey Kravchuk | 4 Mai, 2016

Dieser Artikel ist eine Fortsetzung und Weiterentwicklung meines vorherigen Artikels "Visuelles Testen der Rentabilität der Indikatoren und Benachrichtigungen". Nachdem ich einige Interaktivität zu dem Parameter Änderungsprozess hinzugefügt und die Studienziele überarbeitet habe, schaffte ich es ein neues Werkzeug zu erhalten, das nicht nur die potentiellen Handelsergebnisse auf Grundlage der verwendeten Signale zeigt, sondern Ihnen ermöglicht sofort ein Layout von Trades, Kontostand-Chart und Endergebnissen des Handels zu erhalten, durch verschieben virtueller Schieberegler, die als Steuerung für Signal-Parameterwerte in de Hauptchart fungieren.



Einführung

Zunächst ein Wort der Warnung an alle Gral-Jäger und Nörgler da draußen: Genau wie das in dem vorherigen Artikel beschriebene Toll, ist dieses, in keiner Weise ein Zauberstab, der Ihnen helfen wird Verluste insgesamt zu vermeiden und nur Gewinne zu generieren. Es ist einfach ein Werkzeug, das es Ihnen ermöglicht Berechnungen schnell durchzuführen und die Berechnungsergebnisse in einer komfortablen Form visuell anzuzeigen. Die Indikator Zeichnungen sollten eher als Nahrung für die Gedanken der Trader über Handelstaktiken und Signale betrachtet werden. Dieser Indikator kann trotzdem ein sehr praktisches Tool in kurzfristigem (insbesondere Intraday) Trading werden.

Die Idee den alten Indikator zu überarbeiten und ihm eine neue Form zu geben, entstand aus einem unbefangenen und deutlich aufrichtigem Kommentar, der zusammen mit der Beschreibung von einem Expert Advisor in einem unserer Forum Threads gepostet wurde: "Er funktioniert gut seit 2009. Ich bin zufrieden. Zur Vereinfachung der Signalüberwachung, habe ich..." Der Expert Advisor hat wirklich gutes Trading ausgeführt auf dem von dem Autor angegebenen Symbol und Zeitrahmen. Aber jeder Versuch die Parameter zu ändern, würde in einem unverhohlenen Killer enden, der die gesamte Einlage fast sofort verliert. Und obwohl der Autor des Expert Advisor empfohlen hat ihn nicht für Live-Trading zu verwenden, saß der Gedanke in meinem Kopf fest - was ist zu tun, um nicht nur die potenziellen Handelsergebnisse zu überprüfen, sondern auch das Handelsverhalten, die Stabilität der Ergebnisse, wenn man es möchte. Ich wollte ihn schnell machen und in die Lage bringen die Nutzlosigkeit einiger Signale und Handelstaktiken visuell zu demonstrieren. Zusätzlich spukte ein anderer Gedanke durch meinen Kopf: der Expert Advisor könnte einen anständigen Gewinn mit den bestehenden Parametern erreichen. Ich dachte, dass wenn ich manuell so traden könnte, wäre ich sehr zufrieden mit den Ergebnissen. Was, wenn die Ergebnisse weiter verbessert werden könnten? Allgemein gesagt, das Problem "in sich selbst" interessant zu sein, und ich begann nach Möglichkeit zu suchen, um es zu lösen.



Aufgabenstellung

Es war klar, dass die Lösung auf meinem Indikator aus dem Artikel "Visuelles Testen der Rentabilität von Indikatoren und Benachrichtigungen" basieren musste, was bedeutete, das der Hauptteil der Arbeit bereits getan war und sie nur ein Upgrade benötigte. Was fehlte der vorherigen Umsetzung? Offensichtlich war es ein komfortabler Mechanismus der Parameterauswahl. Es wäre sehr mühsam ständig die Indikator-Parameter prüfen zu müssen, Änderungen zu machen und dann die resultierenden Zahlen und Reihen von Ordern mit historischen Daten zu analysieren. Das ist verständlich - Der ursprüngliche Indikator war gedacht um fertige Signale zu überprüfen, während in diesem Fall etwas anderes erforderlich war. Was konnte das sein? Diejenigen von Ihnen, dies den Strategietester in MT aktiv verwenden, müssen eine sehr klare Vorstellung davon haben. Um Handelsergebnisse zu bewerten, benötigen wir, vor allem, Kontostand-Diagramme und die Möglichkeit die urprüngliche Parameter für die Berechnung von Signalwerten zu ändern.



Kontostand-Diagramm

Dies stellte sich als die einfachste Aufgabe heraus. Da alle Vorgänge in dem Chart gezeichnet werden und die Ergebnisse von jedem von ihnen bekannt sind, ist alles was wir brauchen sie alle zu erhalten und als laufende Gesamtsumme summieren. Ein Zunahme (oder Verringerung) Diagramm kann in einem separaten Fenster angezeigt werden. Um die Bedürfnisse derjenigen zu erfüllen, die die Ergebnisse in Geldwert oder Punkten ausgedrückt analysieren wollen, sollten wir einen geeigneten Parameter zur Verfügung stellen.



Dreieckige Slider Emulation

Es war viel schwieriger die automatisierte Trading-orientierte Plattform an die Aufgaben, die Interaktion mit dem Trader erfordern, anzupassen. Die Standard-Ressourcen ermöglichten mir nur eine Abfolge von Handlungen: Auswahl eines Indikators, Aufruf seiner Eigenschaften, Änderung der Parameter und Neuberechnung. Ein solcher Durchlauf über mindestens ein Dutzend Varianten benötigt sehr viel Zeit. Zum Zeitpunkt, wenn Sie die Ergebnisse der zehnten Variante erhalten, haben Sie die Ergebnisse der ersten schon komplett vergessen.

Da die Standard-GUI-Elemente des Betriebssystems (Schaltflächen Kontrollkästchen, Kombiboxen, Schieberegler, usw.) nicht verfügbar sind für die Verwendung in Indikatoren und EAs (es sei denn, Sie verwenden in einer anderen Programmiersprache geschriebene DLL-Bibliotheken), musste ich nach einem adäquaten Ersatz suchen. Ich habe beschlossen ein Dreieck anzupassen, ein Objekt, das einfach zu jedem Chart hinzugefügt werden kann, für die Verwendung eines Standard-Sliders, der seinen Wert von einem angegebenen Minimum bis zu einem angegebenen Maximum ändern würde. Wenn zwei seiner Eckpunkte vertikal (oder horizontal) angeordnet sind, während der dritte zwischen ihnen verschoben wird, können wir ein recht angemessenes lineares Modell eines Sliders erhalten. Eine vertikale Position eines solchen Sliders ermöglicht es einen glatteren (kontinuierlichen) Fluss der der Werte zu erhalten. Um in der Lage zu sein, gleichzeitig mehrere Parameter zu ändern, müssen wir die Möglichkeit mit mehreren verschiedenen Dreiecken separat zu arbeiten.

Eine spezielle Funktion wurde für die Arbeit mit Dreieck-Slidern geschrieben:

double ParamValue(int ParamNo, string ParamName, double ParamValue, double vMin, double vMax, color clr)
{
  double Triangle[3],vCur, WMax, WMin; datetime tt1, tt2;

  // if there is no triangle, create it
  if(ObjectFind(ParamName) < 0)
  {
    // determine the chart boundaries in the current scale vertically
    WMax = WindowPriceMax();  WMin = WindowPriceMin();

    // calculate the coordinates of points by time...
    tt1 = Time[0] + Period()*60*ParamNo*20; tt2 = tt1 + Period()*60*20;

    // ... and "price"
    vCur = WMin + (ParamValue - vMin) * (WMax - WMin) / (vMax - vMin);

    // create an object and fill it with the color specified in the parameters
    ObjectCreate(ParamName,OBJ_TRIANGLE, 0, tt1,WMax, tt2,vCur, tt1,WMin);
    ObjectSet(ParamName,OBJPROP_COLOR,clr);
  }

  // the triangle exists - get its coordinates
  Triangle[0] = ObjectGet(ParamName,OBJPROP_PRICE1);
  Triangle[1] = ObjectGet(ParamName,OBJPROP_PRICE2);
  Triangle[2] = ObjectGet(ParamName,OBJPROP_PRICE3);

  // arrange the vertices in the order of "increase"
  ArraySort(Triangle);

  // convert the midpoint coordinate to the scale of real values between vMin and vMax
  vCur = vMin + (Triangle[1] - Triangle[0]) / (Triangle[2] - Triangle[0]) * (vMax - vMin);

  // write the value to the object comment
  ObjectSetText(ParamName,DoubleToStr(vCur,2));

  // return the value to the main module
  return(vCur);

}

Die Kommentare bieten eine ziemlich detaillierte Beschreibung des Funktionsalgorithmus, also beschränken wir uns auf die Erklärung des Zwecks der Funktion-Parameter.

ParamNo - Anzahl der verwendeten Parameter (er bestimmt wie das Dreieck sich relativ zu jedem anderen an der Zeitachse bewegt). Sich können anschließend Ihre Positionen und Größen ändern, wenn erforderlich.

ParamName - Parameter-Name, der einzigartig sein muss um zwischen den Dreiecken zu unterscheiden und aussagekräftig für die Anzeige im Tooltip.

ParamValue - der Anfangsparameterwert (er wird verwendet um den mittleren Eckpunkt korrekt zwischen dem minimalen Wert vMin und dem maximalen Wert vMax zu platzieren, die in der Optimierung verwendet werden).

?lr - Farbe des Dreiecks.

Der folgende Code wird für die Arbeit mit Dreiecken verwendet:

MAPeriod  = ParamValue(0, SliderPrefix+"MA Period", MAPeriod, 5, 35, Blue);

RSIPeriod = ParamValue(1, SliderPrefix+"RSI Period", RSIPeriod, 2, 25, Red);

RSILevel  = ParamValue(2, SliderPrefix+"RSI Level", RSILevel, 5, 95, Orange);

Die erhaltenen Werte werden wieter verwendet zur Berechnung der entsprechenden Signale.

Um die Entwicklung von Indikatoren und Debuggen zu veranschaulichen, habe einen von den Nutzer Helen (HI_Line_E_RSI_MA.mq4) vorgeschlagenen Indikator verwendet, der mich im Grunde inspiriert hat all dies zu machen. Der Signalberechnung-Block muss neu geschrieben werden, um für die Verwendung innerhalb meines Indikators geeignet zu sein, obwohl der verbliebene Code im Wesentlichen der gleiche ist:

// fill signal arrays with values and count their number
  for(i=DisplayBars;i>=0;i--)
  {

    double t1=iRSI(NULL,0,RSIPeriod,MAPrice,i+1);
    double t11=iRSI(NULL,0,RSIPeriod,MAPrice,i+2);

    double t2=iMA(NULL,0,MAPeriod,0,MAMode,MAPrice,i+1);
    double t3=iMA(NULL,0,MAPeriod*3,0,MAMode,MAPrice,i+1);

    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel&&t2>t3) {sBuy[i]=1; sBuyCnt++;}
    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)) {sCloseBuy[i]=1; sBuyCloseCnt++;}

    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)&&t2<t3) {sSell[i]=1; sSellCnt++; }
    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel) {sCloseSell[i]=1; sSellCloseCnt++;}
  }

Ich habe beschlossen keinen vierten Parameter zu verwenden - das Zeitraum-Vergrößerungsverhältnis für den Erhalt des langen MA - bis jetzt. Also verwende ich einfach den MA der der Mal länger ist als der wichtigste (MAPeriod*3). Wenn Sie möchten, können Sie ihn einfach auf eigene Faust implementieren.

Eine weitere Ergänzung zu dem ursprünglichen Indikator ist der Block der Kontostand-Kurve Berechnung und Anzeige.

// plot the balance chart
// get profit values one by one and add them up 
i2 = DisplayBars; bBallance[i2] = 0; 
for(i=DisplayBars-1;i>=0;i--) { if(bBallance[i] != 0) { bBallance[i2] = bBallance[i2+1] + bBallance[i]; i2--;  } }
double multiplier; if(ProfitInPoints) multiplier = 1; else multiplier = ToCurrency;
for(i=0;i<DisplayBars-2;i++) bBallance[i] = bBallance[i+i2+1] * multiplier;
SetIndexStyle(0,DRAW_HISTOGRAM,STYLE_SOLID,ProfitWidth,Blue);

Die erste Schleife fügt die früheren Betriebsgewinnwerte kumulativ hinzu, während die zweite sie in einem einzelnen "Stapel" bekommt, ohne Leerräume zwischen ihnen. Als Randbemerkung, es gibt daher keinen Grund nach der Korrespondenz zwischen dem oberen und unteren Chart zu suchen: vertikale Linien von öffnenden und schließenden Ordern stehen in keiner Weise in Zusammenhang mit dem Chart (weshalb sie standardmäßig deaktiviert sind).

Als Ergebnis ist der Text des Indikators wie folgt:

/*///——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

    IndicatorOptimizer.mq4 
  
    Visual testing of profitability of indicators and alerts
    
    Copyright ɠ2009, Sergey Kravchuk, http://forextools.com.ua

/*///——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
#property copyright "Copyright ɠ2006-2009, Sergey Kravchuk. http://forextools.com.ua"
#property link      "http://forextools.com.ua"

#property indicator_separate_window
#property indicator_buffers 1
#property indicator_level1 0

// parameters for displaying chart elements
extern bool   ShowZZ          = true;           // whether to draw ZZ
extern bool   ShowMARKERS     = false;           // whether to draw vertical marker lines
extern int    ProfitWidth     = 1;
extern bool   ProfitInPoints  = true;

//chart plotting dates
extern datetime DateStart     = D'1.01.1970';
extern datetime DateEnd       = D'31.12.2037';

//profit calculation parameters
extern double LotForCalc      = 0.05;           // lot size for profit calculation

// RSI indicator parameters
extern int    RSIPeriod       = 8;              // RSI calculation period
extern int    RSILevel        = 73;             // RSI calculation level

// MA indicator parameters
extern int    MAPeriod        = 20;             // ?? calculation period
extern int    MAMode          = 3;              // 0-MODE_SMA 1-MODE_EMA 2-MODE_SMMA 3-MODE_LWMA
extern int    MAPrice         = 6;              // 0-Close 1-Open 2-High 3-Low 4-(H+L)/2 5-(H+L+C)/3 6-(H+L+2*C)/4


// MACD indicator parameters
extern double MACDOpenLevel   = 3;              
extern double MACDCloseLevel  = 2;
extern double MATrendPeriod   = 26;

// colors of Buy lines
extern color  ColorProfitBuy  = Blue;           // line color for profitable Buys 
extern color  ColorLossBuy    = Red;            // line color for unprofitable Buys
extern color  ColorZeroBuy    = Gray;           // line color for zero-profit Buys

// colors of Sell lines
extern color  ColorProfitSell = Blue;           // line color for profitable Sells
extern color  ColorLossSell   = Red;            // line color for unprofitable Sells
extern color  ColorZeroSell   = Gray;           // line color for zero-profit Sells

// colors of signal lines
extern color  ColorBuy        = CornflowerBlue; // Buy signal line color
extern color  ColorSell       = HotPink;        // Sell signal line color
extern color  ColorClose      = Gainsboro;      // line color of the signal for closing

//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
double sBuy[],sCloseBuy[],sSell[],sCloseSell[]; // arrays for signals
int sBuyCnt,sSellCnt,sBuyCloseCnt,sSellCloseCnt;// signal counters
int i,DisplayBars;
double bBallance[]; // for the balance on operations
int IndicatorWindowNo;  // indicator window number
//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// service codes
#define MrakerPrefix "IO_"
#define SliderPrefix "SL_"
#define OP_CLOSE_BUY  678
#define OP_CLOSE_SELL 876
//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
int init()   
{ 
  SetIndexBuffer(0,bBallance);
  IndicatorShortName("IndicatorOptimizer");
  
  return(0); 
}
int deinit() 
{ 
  ClearMarkers(); 
  ClearSliders();
  return(0); 
}

double ParamValue(int ParamNo, string ParamName, double ParamValue, double vMin, double vMax, color clr)
{
  double Triangle[3],vCur, WMax, WMin; datetime tt1, tt2; 

  // if there is no triangle, create it
  if(ObjectFind(ParamName) < 0)
  {
    // determine the chart boundaries in the current scale vertically
    WMax = WindowPriceMax();  WMin = WindowPriceMin();

    // calculate the coordinates of points by time...
    tt1 = Time[0] + Period()*60*ParamNo*20; tt2 = tt1 + Period()*60*20;
    // ... and "price"
    vCur = WMin + (ParamValue - vMin) * (WMax - WMin) / (vMax - vMin);
    // create an object and fill it with the color specified in the parameters
    ObjectCreate(ParamName,OBJ_TRIANGLE, 0, tt1,WMax, tt2,vCur, tt1,WMin);
    ObjectSet(ParamName,OBJPROP_COLOR,clr);
  }
  // the triangle exists - get its coordinates
  Triangle[0] = ObjectGet(ParamName,OBJPROP_PRICE1);
  Triangle[1] = ObjectGet(ParamName,OBJPROP_PRICE2);
  Triangle[2] = ObjectGet(ParamName,OBJPROP_PRICE3);
  // arrange the vertices in the order of "increase"
  ArraySort(Triangle);
  // convert the midpoint coordinate to the scale of real values between vMin and vMax
  vCur = vMin + (Triangle[1] - Triangle[0]) / (Triangle[2] - Triangle[0]) * (vMax - vMin);
  // write the value to the object comment
  ObjectSetText(ParamName,DoubleToStr(vCur,2)); 
  // return the value to the main module
  return(vCur); 
}


//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
int start() 
{ 
  double Profit=0,P1,P2; int CntProfit=0,CntLoose=0,i1,i2;
  // coefficient for conversion of points to the deposit currency
  double ToCurrency = MarketInfo(Symbol(),MODE_TICKVALUE)*LotForCalc;    
  
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // delete all markers in case the indicator is redrawn
  ClearMarkers(); 
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // prepare signal counters
  sBuyCnt=0; sSellCnt=0; sBuyCloseCnt=0; sSellCloseCnt=0; 
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // allocate memory for signal arrays and zero out their values
  DisplayBars=Bars; // number of bars displayed
  ArrayResize(sBuy,DisplayBars);  ArrayInitialize(sBuy,0);
  ArrayResize(sSell,DisplayBars); ArrayInitialize(sSell,0);
  ArrayResize(sCloseBuy,DisplayBars);  ArrayInitialize(sCloseBuy,0);
  ArrayResize(sCloseSell,DisplayBars); ArrayInitialize(sCloseSell,0);

  // prepare balance calculation
  ArrayInitialize(bBallance,0); 
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // creating (if necessary) and getting parameters of the control triangle
  MAPeriod  = ParamValue(0, SliderPrefix+"MA Period", MAPeriod, 5, 35, Blue);
  RSIPeriod = ParamValue(1, SliderPrefix+"RSI Period", RSIPeriod, 2, 25, Red);
  RSILevel  = ParamValue(2, SliderPrefix+"RSI Level", RSILevel, 5, 95, Orange);
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


  // fill signal arrays with values and count their number
  for(i=DisplayBars;i>=0;i--) 
  {
    double t1=iRSI(NULL,0,RSIPeriod,MAPrice,i+1);
    double t11=iRSI(NULL,0,RSIPeriod,MAPrice,i+2);

    double t2=iMA(NULL,0,MAPeriod,0,MAMode,MAPrice,i+1);

    double t3=iMA(NULL,0,MAPeriod*3,0,MAMode,MAPrice,i+1);

    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel&&t2>t3) {sBuy[i]=1; sBuyCnt++;}
    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)) {sCloseBuy[i]=1; sBuyCloseCnt++;}

    if (t1<(100-RSILevel)&&t11>(100-RSILevel)&&t1<t11&&t1<(100-RSILevel)&&t2<t3) {sSell[i]=1; sSellCnt++; } 
    if (t1>RSILevel&&t11<RSILevel&&t1>t11&&t1>RSILevel) {sCloseSell[i]=1; sSellCloseCnt++;}
  }

 
        //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // delete repeated signals leaving only the first ones, on the left of the chart
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy[i]==sBuy[i+1]) sBuy[i]=0;
    if(sSell[i]==sSell[i+1]) sSell[i]=0;
    if(sCloseBuy[i]==sCloseBuy[i+1]) sCloseBuy[i]=0;
    if(sCloseSell[i]==sCloseSell[i+1]) sCloseSell[i]=0;
  }
  // delete signals outside the specified date range
  for(i=0;i<DisplayBars;i++) 
  {
    if(Time[i]<DateStart || DateEnd<Time[i]) { sBuy[i]=0; sSell[i]=0; sCloseBuy[i]=0; sCloseSell[i]=0; }
  }
  // add forced closing at the range limit
  if(DateEnd<=Time[0]) { i=iBarShift(Symbol(),Period(),DateEnd); sBuy[i]=0; sSell[i]=0; sCloseBuy[i]=1; sCloseSell[i]=1; }
  if(DateEnd >Time[0]) { sCloseBuy[0]=1; sCloseSell[0]=1; }
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // count the number of signals
  for(i=0;i<DisplayBars;i++) 
  {
    if(sBuy [i]!=0) sBuyCnt++;  if(sCloseBuy [i]!=0) sBuyCloseCnt++;
    if(sSell[i]!=0) sSellCnt++; if(sCloseSell[i]!=0) sSellCloseCnt++;
  }
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // set markers, draw ZZ and calculate profit
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // process Buys
  for(i=DisplayBars-1;i>=0;i--) // get the points
  {
    // find the next point of opening and save its position and price
    for(i1=i;i1>=0;i1--) if(sBuy[i1]!=0) break; 

    // find the next Buy closing point and save its position and price
    for(i2=i1-1;i2>=0;i2--) if(sCloseBuy[i2]!=0) break;
    if(i2<0) i2=0; // for the last non-closed position, calculate closing on the current price
    i=i2; // new bar to continue the search for points of opening

    // determine prices for drawing 
    P1=Open[i1]; P2=Open[i2];  
    
    P1/=Point; P2/=Point; // convert prices to points
    
    // determine the profit and fill the corresponding buffer
    if(i1>=0) 
    { 
      Profit=Profit+P2-P1; // get the cumulative profit
      if(P2-P1>=0) CntProfit++; else CntLoose++; // count the number of orders
      DrawLine(i1,i2,OP_BUY); // draw the order line
      PlaceMarker(i1,OP_BUY); 
      PlaceMarker(i2,OP_CLOSE_BUY); 
      
      bBallance[i2] += P2-P1;
    }
  }
  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // process sells
  for(i=DisplayBars-1;i>=0;i--) // get the points
  {
    // find the next point of opening and save its position and price
    for(i1=i;i1>=0;i1--) if(sSell[i1]!=0) break; 

    // find the next Buy closing point and save its position and price
    for(i2=i1-1;i2>=0;i2--) if(sCloseSell[i2]!=0) break;
    if(i2<0) i2=0; // for the last non-closed position, calculate closing on the current price
    i=i2; // new bar to continue the search for points of opening

    // determine prices for drawing depending on the Optimizm parameter
    P1=Open[i1]; P2=Open[i2]; 
    
    P1/=Point; P2/=Point; // convert prices to points
    
    // if both points exist, determine the profit and fill the corresponding buffer
    if(i1>=0) 
    { 
      Profit=Profit+P1-P2; // get the cumulative profit
      if(P1-P2>=0) CntProfit++; else CntLoose++; // count the number of orders
      DrawLine(i1,i2,OP_SELL); // draw the order line
      PlaceMarker(i1,OP_SELL); 
      PlaceMarker(i2,OP_CLOSE_SELL);
      
      bBallance[i2] += P1-P2;
    }
  }

  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // plot the balance chart
  // get profit values one by one and add them up 
  i2 = DisplayBars; bBallance[i2] = 0; 
  for(i=DisplayBars-1;i>=0;i--) { if(bBallance[i] != 0) { bBallance[i2] = bBallance[i2+1] + bBallance[i]; i2--;  } }
  double multiplier; if(ProfitInPoints) multiplier = 1; else multiplier = ToCurrency;
  for(i=0;i<DisplayBars-2;i++) bBallance[i] = bBallance[i+i2+1] * multiplier;
  SetIndexStyle(0,DRAW_HISTOGRAM,STYLE_SOLID,ProfitWidth,Blue);

  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // calculation of the final values for the comment  
  int Cnt=CntProfit+CntLoose; // total number of operations
  string msg="Period: "+TimeToStr(MathMax(DateStart,Time[Bars-1]))+" - "+TimeToStr(MathMin(DateEnd,Time[0]))+"\n\n";
  
  msg=msg + 
  "RSI period: " + RSIPeriod + "\n" +
  "RSI level: " + RSILevel + "\n" +
  "?? period: " + MAPeriod + "\n\n" +
  sBuyCnt+" Buys and "+sBuyCloseCnt+" their closings\n"+
  sSellCnt+" Sells and "+sSellCloseCnt+" their closings\n\n";
  
  // total time in days
  int TotalDays = (MathMin(DateEnd,Time[0])-MathMax(DateStart,Time[Bars-1]))/60/60/24; //convert seconds to days
  if(TotalDays<=0) TotalDays=1; // to avoid zero divide for an incomplete day
  
  if(Cnt==0) msg=msg+("No operation");
  else msg=msg+
  (
    DoubleToStr(Profit,0)+" points for "+Cnt+" operations over "+TotalDays+" days\n"+
    DoubleToStr(Profit/Cnt,1)+" points per operation ("+
    DoubleToStr(Profit/TotalDays,1)+" per day)\n\n"+
    "When trading the lot "+DoubleToStr(LotForCalc,2)+" get in "+AccountCurrency()+":\n"+
    DoubleToStr(Profit*ToCurrency,0)+" total, "+
    DoubleToStr(Profit/Cnt*ToCurrency,1)+" per operation ("+
    DoubleToStr(Profit/TotalDays*ToCurrency,1)+" per day)\n\n"+
    CntProfit+" profitable ("+DoubleToStr(((CntProfit)*1.0/Cnt*1.0)*100.0,1)+"%)\n"+
    CntLoose+" losing ("+DoubleToStr(((CntLoose)*1.0/Cnt*1.0)*100.0,1)+"%)"
  );
  Comment(msg);
  
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————



//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// deleting all objects from our chart
void ClearMarkers() 
{ 
  for(int i=ObjectsTotal()-1;i>=0;i--) if(StringFind(ObjectName(i),MrakerPrefix)==0) ObjectDelete(ObjectName(i)); 
}
void ClearSliders() 
{ 
  for(int i=ObjectsTotal()-1;i>=0;i--) if(StringFind(ObjectName(i),SliderPrefix)==0) ObjectDelete(ObjectName(i)); 
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// setting a vertical line - the op_type operation marker
void PlaceMarker(int i, int op_type)
{
  if(!ShowMARKERS) return; // displaying markers is disabled

  color MarkerColor; string MarkName; 

  // __ so that the closing line is drawn below the opening line by sorting 
  if(op_type==OP_CLOSE_SELL) { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__SELL_"+i; }
  if(op_type==OP_CLOSE_BUY)  { MarkerColor=ColorClose; MarkName=MrakerPrefix+"__BUY_"+i;  }  
  if(op_type==OP_SELL)       { MarkerColor=ColorSell;  MarkName=MrakerPrefix+"_SELL_"+i;  }
  if(op_type==OP_BUY)        { MarkerColor=ColorBuy;   MarkName=MrakerPrefix+"_BUY_"+i;   }

  ObjectCreate(MarkName,OBJ_VLINE,0,Time[i],0); 
  ObjectSet(MarkName,OBJPROP_WIDTH,1); 
  if(op_type==OP_CLOSE_BUY || op_type==OP_CLOSE_SELL) ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID); 
  else ObjectSet(MarkName,OBJPROP_STYLE,STYLE_DOT);
  ObjectSet(MarkName,OBJPROP_BACK,True);  

  ObjectSet(MarkName,OBJPROP_COLOR,MarkerColor);
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// setting the line on the chart for the op_type operation type using the Optimizm parameter values
void DrawLine(int i1,int i2, int op_type)
{
  if(!ShowZZ) return; // displaying ZZ is disabled
  
  color ?urColor;
  string MarkName=MrakerPrefix+"_"+i1+"_"+i2;
  double P1,P2;
  
  // determine prices for drawing depending on the Optimizm parameter
  P1=Open[i1]; P2=Open[i2];  

  ObjectCreate(MarkName,OBJ_TREND,0,Time[i1],P1,Time[i2],P2);
  
  ObjectSet(MarkName,OBJPROP_RAY,False); // draw segments instead of lines
  ObjectSet(MarkName,OBJPROP_BACK,False);
  ObjectSet(MarkName,OBJPROP_STYLE,STYLE_SOLID);
  ObjectSet(MarkName,OBJPROP_WIDTH,2);

  // set the line color depending on profitability of the operation
  if(op_type==OP_BUY) 
  { 
    if(P1 <P2) ?urColor = ColorProfitBuy;
    if(P1==P2) ?urColor = ColorZeroBuy;
    if(P1 >P2) ?urColor = ColorLossBuy;
  }
  if(op_type==OP_SELL) 
  { 
    if(P1 >P2) ?urColor = ColorProfitSell;
    if(P1==P2) ?urColor = ColorZeroSell;
    if(P1 <P2) ?urColor = ColorLossSell;
  }
  ObjectSet(MarkName,OBJPROP_COLOR,?urColor);
}
//——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

Die ausführliche Beschreibung des Algorithmus und dessen Betrieb ist in meinem vorherigen Artikel zu finden. Es muss hier angemerkt werden, dass ich den Workaround für den Optimierung-Parameter entfernt habe. In dieser Version des Indikators, werden Signale für den aktuellen Balken basierend auf Werten der vorherigen, vollständig abgeschlossenen Balken berechnet. Dies legt den klassischen Ansatz zum Trading basierend auf der Balkenanalyse nahe: nachdem die Ergebnisse eines frisch abgeschlossenen Balken analysiert wurden, öffnen Sie eine Position am Anfang des aktuellen. Es ermöglicht uns festzustellen, dass die erhaltenen Berechnungsergebnisse sehr zuverlässig sind.



Optimierer in Aktion

Nach dem Kompilieren des Indikators und dem Hinzufügen zu dem Chart, können wir fortfahren zur Analyse. Dieser Vorgang kann in Einzelheiten und ausführlich beschrieben werden, aber es wird alles sehr deutlich, wenn Sie eine kleine, den Live-Betrieb des Indikators veranschaulichende Animation ansehen.

Wir müssen hier eine wichtige Sache beachten: Der Indikator berechnet seine Werte bei jedem Tick neu, wodurch wir nach jedem Verschieben des Dreieck-Sliders auf den neuen Kurs warten sollten oder das Chart manuell aktualisieren, oder den Tick-Emulator verwenden.

Arbeiten mit dem Optimierer wendet sich nun in ein eigenartiges Spiel mit ihm, bekannt als "was wäre wenn?". Sie wählen einfach einen Dreieck-Slider und beginnen deren Mittelpunkt auf/ab zu bewegen. Die Horizontale Position des Dreieck-Vertex spielt keine Rolle, wichtig ist nur die vertikale Position der Eckpunkte. Somit bewegen Sie die Dreieck-Mittelpunkte und versuchen zu erfassen, zu was diese Bewegung führt. Ihre Intuition und Ihre praktische Erfahrung werden Sie sicherlich auffordern, was Sie wo bewegen. Nun, wen Ihnen die Erfahrung fehlt, geben Sie der Versuch-und-Fehler Methode eine Chance: versuchen Sie, suchen Sie nach Varianten, die geeignet wären und später traden Sie auf Grundlage der ermittelten Parameter.



Wie der Indikator verwendet wird. Teil eins: Was NICHT gemacht wird

Dieser Indikator, genau wie mein vorheriger Indikator, sollte verwendet werden, solange Sie verstehen was Sie machen und welche Ergebnisse Sie erhalten. Sie können den Signalberechnung-Block mit Ihrem eigenen ersetzen und die Anzahl der verwendeten Signale steigern oder verringern. Aber wenn Sie die Ergebnisse ansehen, denken Sie immer daran, dass wenn Sie den Indikator auf EURUSD M30 optimiert haben und er zeigt eine gute Gewinnkurve, bedeutet dies überhaupt nicht, dass die Ergebnisse auch hervorragend sind, wenn Sie ein anderes Währungspaar handeln oder einen anderen Zeitrahmen oder beides. Um zu verstehen, warum das so ist, werfen Sie einfach einen Blick auf M1 und D1 Charts. Der Unterschied in der Natur des Balkenwechsels ist so offensichtlich, dass es keiner weiteren Kommentare bedarf.

Der Unterschied zwischen Währungspaaren ist nicht so klar, kann aber auch leicht erkannt werden, beispielsweise mit der in meinem Artikel "Marktdiagnosen durch Impulse" beschriebenen Technik. Die in diesem Artikel bereitgestellten Screenshots werden veranschaulichen, warum Sie nicht die gleichen Ergebnisse auf allen Währungspaaren erwarten sollten, auch wenn die Signalberechnung-Block Einstellungen die gleichen sind.



Wie der Indikator verwendet wird. Teil Zwei: Was gemacht werden MUSS

Es ist jedoch nicht so schlimm wie es aussieht. Wie manche Philosophen sagen, "Nachteile sind die Erweiterung unserer Vorzüge". Wenn wir die Fehler sehen, aber noch die Vorteile zu erkennen sind, bedeutet es nicht, dass es keine gibt. Werfen Sie einen Blick auf das D1 Chart mit einem ziemlich langen stetigen Aufwärtstrend. Denken Sie wirklich, dass der auf den Anfang dieses Zeitraums optimierte Indikator keinen Gewinn in der Mitte dieses Trends erzielt?!

Wenn Sie beschließen dieses Tool im Trading zu verwenden, so wie ich es machen werde, muss er von Zeit zu Zeit einfach auf ein bestimmtes Währungspaar und Zeitrahmen angepasst werden. Sie können den von Helen vorgeschlagenen Expert Advisor nehmen und ihn im automatisierten Trading versuchen, die Parameter zu den Optimierungsergebnissen hin und wieder anpassen, insbesondere weil der Indikator es ermöglicht diesen Vorgang mühelos viele Male am Tag auszuführen, und damit auch auf Minuten Charts (M5, M15, M30).



Fazit

Der Markt ist grundsätzlich nicht vorhersehbar. Jedes 100% zuverlässige und bestätigte Signal kann sich mit dem Blinzeln eines Auges in das Gegenteil drehen, durch eine wichtige Pressemitteilung. Bei der Diskussion über den Artikel, werden Trading-Asse vielleicht versuchen Sie zu überzeugen, dass Sie niemals wissen wohin der Kurs sich bewegt und Sie folglich auch keine stabilen Gewinne erzielen können, wenn Sie nicht Dinge wie fundamentale Signale berücksichtigen oder wenn Sie keine Handelsvolumen-Daten haben. Nun, es ist deren Meinung, das ist wie sie traden und Erfolg haben. Man kann diesen Ansatz verwenden, wenn man mehr als tausend Dollar verwaltet.

Ich glaube, dass für kleine Einlagen dieser Indikator eines der einfachsten und zuverlässigsten Tools werden kann, wenn Sie möchten. Sie werden niemals ein "volles Bild" der Kursbewegung erhalten, also ist es nutzlos nach Umkehrpunkten zu suchen und zu versuchen jeden Cent aus dem Markt zu pressen. Ein langer Trend über Tage und Wochen, wird wahrscheinlich einige allgemeine Muster haben, und wenn Sie ihren Anfang erkennen und Ihre System zur Signalberechnung entsprechend optimieren, werden Sie in der Lage sein erfolgreich auf den optimierten Parametern zu verdienen, für einen, wahrscheinlich, nicht so kurzen Zeitraum. Aber auch wenn der "Wind sich dreht", werden Sie es nicht mehr als großes Problem ansehen, da der Optimierungsvorgang jetzt viel weniger anstrengend ist als er es war, bevor Sie begonnen haben den Indikator zu verwenden.