English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Kontrolle der Saldo-Gefällekurve während der Arbeit eines Expert Advisors

Kontrolle der Saldo-Gefällekurve während der Arbeit eines Expert Advisors

MetaTrader 5Handel | 9 März 2016, 09:51
731 0
Dmitriy Skub
Dmitriy Skub

Einleitung

Dieser Beitrag beschreibt einen Ansatz, mit dem die Leistung eines Expert Advisors durch Erzeugung eines Feedbacks gesteigert werden kann. Dieses Feedback beruht ganz spezifisch auf der Messung der Saldo-Gefällekurve. Die Kontrolle des Gefälles wird automatisch durch die Regulierung des Arbeitsvolumens ausgeführt. Ein Expert Advisor kann auf folgende Arten Handel betreiben: mit einem reduzierten Volumen, mit einer Arbeitsmenge in Posten (entsprechend der anfangs angepassten Menge) und mit einem Zwischenvolumen. Die Arbeitsweisen werden automatisch abgewechselt.

In der Feedback-Kette werden unterschiedliche Regulierungscharakteristika verwendet: schrittweise, schrittweise mit Hsyterese und linear. Damit kann das Kontrollsystem der Saldo-Gefällekurve an die Chakateristika eines bestimmten Systems angepasst werden.

Der Hauptgedanke ist die Automatisierung des Entscheidungsprozesses für einen Händler während der Beobachtung seines eigenen Handelssystems. Und es ist schon sinnvoll, die Risiken während ungünstiger Arbeitsperioden zu begrenzen. Wenn man dann wieder die normale Arbeitsweise erreicht, können die Risiken wieder auf ihr ursprüngliches Niveau zurückgebracht werden.

Klar ist dieses System kein Allheilmittel und es macht aus einem verlustreichen Expert Advisor sicherlich keinen rentablen. Es ist eigentlich eher ein Zusatz zum Money Management (Geldverwaltung) des Expert Advisors, das ihn vor beträchtlichen Verlusten in einem Konto schützt.

Dieser Beitrag umfasst eine Library, mit deren Hilfe diese Funktion in den Code jedes Expert Advisors eingebettet werden kann.


Arbeitsprinzip

Sehen wir uns nun das Arbeitsprinzip des Systems genauer an, das die Saldo-Gefällekurve kontrolliert. Nehmen wir an, wir haben einen handelnden Expert Advisor, dessen hypothetische Saldokurve so aussieht:

Arbeitsprinzip des Systems zur Kontrolle der Saldo-Gefällekurve

Abb. 1 Arbeitsprinzip des Systems zur Kontrolle der Saldo-Gefällekurve

Die ursprüngliche Saldokurve für den Expert Advisor, der ein konstantes Volumen an Handelsoperationen verwendet, ist oben gezeigt. Geschlossene Handel sind als rote Punkte dargestellt. Verbinden wir diese Punkte mit einer Kurve, die die Veränderung des Saldos des Expert Advisors während des Handels (dicke schwarze Linie) widerspiegelt.

Jetzt verfolgen wir die Neigung des Gefälles dieser Linie in einem Stück bis zur Zeitachse (die dünnen blauen Linien). Genauer gesagt: wir berechnen vor Eröffnung jedes Handels durch ein Signal die Gefälleneigung durch zwei zuvor geschlossene Handel (d.h. zwei Handel, um es nicht zu kompliziert zu machen). Wird die Gefälleneigung geringer als der angegebene Wert, beginnt unser Kontrollsystem seine Arbeit - es fährt das Volumen entsprechend des kalkulierten Neigungswerts und der angegebenen Regulierungsfunktion herunter.

Auf diese Weise nimmt das Volumen, sobald der Handel eine erfolglose Periode erreicht, von Vmax auf Vmin innerhalb der Т3...Т5 Handelsperiode ab. Nachdem der Handel an Punkt Т5 mit einem angegebenen Mindestvolumen ausgeführt ist - in der Funktion der Ablehnung des Handelsvolumens. Sobald die Rentabilität des Expert Advisors wiederhergestellt ist und die Neigung der Saldo-Gefällekurve den spezifizierten Wert übersteigt, beginnt das Volumen wieder zuzunehmen. Dies geschieht innerhalb des Т8...Т10 Intervalls. Nach Punkt Т10, stellt sich das Volumen der Handelsoperationen wieder auf den ursprünglichen Zustand Vmax ein.

Die in Relation auf so eine Regulierung entstandene Saldokurve ist im unteren Teil von Abb. 1 zu sehen. Sie erkennen hier gut, dass die ursprüngliche Inanspruchnahme von B1 zu B2 abnahm und von B1 zu B2* größer (???) wurde. Sie erkennen ebenfalls, dass der Gewinn innerhalb der Periode der Wiederherstellung des Maximalvolumens Т8...Т10 leicht abnahm - das ist die andere Seite der Medaille.

Grün hebt den Teil der Saldokurve hervor, als der Handel mit einem angegebenen Mindestvolumen durchgeführt wurde. Gelb bezeichnet die Teile des Übergangs von Maximal- zu Minimalvolumen und zurück. Hier sind mehrere Varianten an Übergängen möglich:

  • schrittweise - Volumen ändert sich in separaten Schritten von maximal zu minimal und zurück;
  • linear - Volumen ändert sich linear, je nach Neigung des Gefälles der Saldokurve innerhalb eines geregelten Intervalls;
  • schrittweise mit Hysterese - Übergang von Maximal- zu Minimalvolumen und zurück wird bei Differenzwerten der Gefälleneigung ausgeführt;

Zeigen wir das am besten in Bildern:

Arten der Regulierungsmerkmale

Abb. 2 Arten der Regulierungsmerkmale

Regulierungsmerkmale wirken sich auf die Frequenzen des Kontrollsystems aus - die Verzögerung von Aktivierung/Deaktivierung und den Übergang von Maximal- und Minimalvolumen und zurück. Es ist daher ratsam, ein Merkmal auf experimenteller Basis zu wählen, wenn man optimale Testergebnisse erhalten möchte.

Wir erweitern das Handelssystem durch das Feedback, auf Grundlage der Gefälleneigung der Saldokurve. Beachten Sie, dass eine derartige Regulierung des Volumens nur für die Systeme angebracht ist, die Volumen nicht als Teil des Handelssystems an sich besitzen. Wird z.B. das Martingale-Prinzip verwendet, können Sie dieses System nicht direkt ohne Änderungen im ursprünglichen Expert Advisor verwenden.

Zudem dürfen wir dabei nicht die folgenden wichtigen Punkte vergessen:

  • die Effektivität der Verwaltung des Gefälles der Saldolinie hängt direkt vom Verhältnis des Arbeitsvolumens im normalen Betrieb zum Volumen im Volumenablehnungsmodus ab. Je größer dieses Verhältnis wird, umso effektiver ist die Verwaltung. Deshalb sollte das ursprüngliche Arbeitsvolumen erheblich größer sein als das minimal mögliche.
  • die Durchschnittsperiode des Wandels von Ansteigen und Abfallen des Saldos des Expert Advisors sollte erheblich größer sein als die Reaktionszeit des Kontrollsystems. Andernfalls wir das System das Gefälle der Saldokurve nicht regulieren können. Je größer das Verhältnis von Durchschnittsperiode zu Reaktionszeit ist, umso effektiver ist das System. Diese Voraussetzung betrifft fast jedes System automatischer Regulierung.


Implementierung in MQL5 mit Hilfe des Objekt-orientierten Programmierens

Schreiben wir jetzt eine Library, die den oben beschriebenen Ansatz umsetzen kann. Dazu bedienen wir uns eines neuen Features in MQL5 - dem Objekt-orientierten Ansatz. Mit seiner Hilfe können wir unsere Library kinderleicht entwickeln und zukünftig erweitern, ohne große Teile des Codes komplett von vorne noch einmal schreiben zu müssen.


TradeSymbol Klasse

Da das Testen in mehreren Währungen in der neuen MetaTrader 5 Plattform implementiert ist, brauchen wir eine Klasse, die in sich selbst die gesamte Arbeit mit jedem Arbeitssymbol verkapselt. Denn dann können wir diese Library in Expert Advisors mit mehreren Währungen nutzen. Diese Klasse hat mit dem Kontrollsystem direkt nichts zu tun, sie ist vielmehr eine Hilfe und wird daher für Abläufe mit dem Arbeitssymbol verwendet.

//---------------------------------------------------------------------
//  Operations with work symbol:
//---------------------------------------------------------------------
class TradeSymbol
{
private:
  string  trade_symbol;                          // work symbol

private:
  double  min_trade_volume;                      // minimum allowed volume for trade operations
  double  max_trade_volume;                      // maximum allowed volume for trade operations
  double  min_trade_volume_step;                 // minimum change of volume
  double  max_total_volume;                      // maximum change of volume
  double  symbol_point;                          // size of one point
  double  symbol_tick_size;                      // minimum change of price
  int     symbol_digits;                        // number of digits after decimal point

protected:

public:
  void    RefreshSymbolInfo( );                  // refresh market information about the work symbol
  void    SetTradeSymbol( string _symbol );      // set/change work symbol
  string  GetTradeSymbol( );                     // get work symbol
  double  GetMaxTotalLots( );                    // get maximum cumulative volume
  double  GetPoints( double _delta );            // get change of price in points

public:
  double  NormalizeLots( double _requied_lot );  // get normalized trade volume
  double  NormalizePrice( double _org_price );   // get normalized price with consideration of step of change of quote

public:
  void    TradeSymbol( );                       // constructor
  void    ~TradeSymbol( );                      // destructor
};

Die Struktur für diese Klasse ist sehr einfach. Ihr Zweck ist das Erhalten, Speichern und Verarbeiten der aktuellen Marktinformation nach einem angegebenen Symbol. Die Hauptmethoden sind TradeSymbol::RefreshSymbolInfo, TradeSymbol::NormalizeLots, TradeSymbol::NormalizePrice. Doch schauen wir sie uns nacheinander an.

DieTradeSymbol::RefreshSymbolInfo Methode dient zur Aktualisierung der Marktinfromation nper Arbeitssymbol.

//---------------------------------------------------------------------
//  Refresh market information by work symbol:
//---------------------------------------------------------------------
void
TradeSymbol::RefreshSymbolInfo( )
{
//  If a work symbol is not set, don't do anything:
  if( GetTradeSymbol( ) == NULL )
  {
    return;
  }

//  Calculate parameters necessary for normalization of volume:
  min_trade_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_MIN );
  max_trade_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_MAX );
  min_trade_volume_step = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_STEP );

  max_total_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_LIMIT );

  symbol_point = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_POINT );
  symbol_tick_size = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_TRADE_TICK_SIZE );
  symbol_digits = ( int )SymbolInfoInteger( GetTradeSymbol( ), SYMBOL_DIGITS );
}

Bitte beachten Sie hier einen wichtigen Punkt, der in einigen Methoden verwendet wird. Da die aktuelle Realisierung von MQL5 den Einsatz eines Constructors mit Parametern nicht gestattet, müssen Sie die folgende Methode zum primären einrichten der Arbeitssymbole aufrufen:

void    SetTradeSymbol( string _symbol );      // set/change work symbol

Mit Hilfe der TradeSymbol::NormalizeLots Methode erhält man ein korrektes und normalisiertes Volumen. Wir wissen, dass die Größe einer Position nicht geringer sein darf als der vom Makler erlaubte kleinste Minimalwert. Der Makler legt auch die Mindestschritt der Veränderung einer Position fest, der immer wieder unterschiedlich sein kann. Diese Methode liefert uns den den nächsten Wert des Volumens von unten und

prüft auch, ob das Volumen der vermeintlichen Position den vom Makler erlaubten Maximalwert überschreitet.

//---------------------------------------------------------------------
//  Get normalized trade volume:
//---------------------------------------------------------------------
//  - input necessary volume;
//  - output is normalized volume;
//---------------------------------------------------------------------
double
TradeSymbol::NormalizeLots( double _requied_lots )
{
  double   lots, koeff;
  int      nmbr;

//  If a work symbol is not set, don't do anything:
  if( GetTradeSymbol( ) == NULL )
  {
    return( 0.0 );
  }

  if( this.min_trade_volume_step > 0.0 )
  {
    koeff = 1.0 / min_trade_volume_step;
    nmbr = ( int )MathLog10( koeff );
  }
  else
  {
    koeff = 1.0 / min_trade_volume;
    nmbr = 2;
  }
  lots = MathFloor( _requied_lots * koeff ) / koeff;

//  Lower limit of volume:
  if( lots < min_trade_volume )
  {
    lots = min_trade_volume;
  }

//  Upper limit of volume:
  if( lots > max_trade_volume )
  {
    lots = max_trade_volume;
  }

  lots = NormalizeDouble( lots, nmbr );
  return( lots );
}

Mit Hilfe der TradeSymbol::NormalizePrice Methode erhält man einen korrekten und normalisierten Preis. Da die Anzahl der bedeutenden Ziffern nach de Dezimalstelle (Exaktheit des Preises) für ein gegebenes Symbol festgelegt werden muss, müssen wir den Preis abschneiden. Darüber hinaus haben einige Symbole (z.B. Futures) einen Mindestschritt der Preisveränderung, die größer als ein Punkt ist. Deswegen müssen wir die Werte des Preises als Multiple des minimalen Ermessens anlegen.

//---------------------------------------------------------------------
//  Normalization of price with consideration of step of price change:
//---------------------------------------------------------------------
double
TradeSymbol::NormalizePrice( double _org_price )
{
//  Minimal step of quote change in points:
  double  min_price_step = NormalizeDouble( symbol_tick_size / symbol_point, 0 );

  double  norm_price = NormalizeDouble( NormalizeDouble(( NormalizeDouble( _org_price / symbol_point, 0 )) / min_price_step, 0 ) * min_price_step * symbol_point, symbol_digits );
  return( norm_price );
}

Der notwendige, nicht normalisierte Preis wird in die Funktion eingegeben und liefert den normalisierten Preis, der am nächsten zum erforderlichen liegt.

Der Zweck der anderen Methoden ist sehr klar in den Kommentaren beschrieben, sodass wir hier darauf nicht extra eingehen müssen.


TBalanceHistory Klasse

Diese Klasse dient zur Arbeit mit der Saldo-History eines Accounts, was ja aus ihrem Namen schon hervorgeht.. Sie ist zudem eine Basisklasse für weitere, unten beschriebene Klassen. Ihr Hauptzweck ist der Zugriff auf die Handels-History eines Expert Advisors. Die History können Sie zusätzlich nach Arbeitssymbol, nach "magischer Zahl", nach dem Beginn der Kontrolle des Expert Advisors oder nach all drei Elementen gleichzeitig filtern.

//---------------------------------------------------------------------
//  Operations with balance history:
//---------------------------------------------------------------------
class TBalanceHistory
{
private:
  long      current_magic;            // value of "magic number" when accessing the history of deals ( 0 - any number )
  long      current_type;             // type of deals ( -1 - all )
  int       current_limit_history;   // limit of depth of history ( 0 - all history )
  datetime   monitoring_begin_date;   // date of start of monitoring history of deals
  int       real_trades;             // number of actual trades already performed

protected:
  TradeSymbol  trade_symbol;          // operations with work symbol

protected:
//  "Raw" arrays:
  double    org_datetime_array[ ];                                                                                                                                                      // date/time of trade
  double    org_result_array[ ];                                                                                                                                                                // result of trade

//  Arrays with data grouped by time:
  double    group_datetime_array[ ];                                                                                                                                            // date/time of trade
  double    group_result_array[ ];                                                                                                                                                      // result of trade

  double    last_result_array[ ];     // array for storing results of last trades ( points on the Y axis )
  double    last_datetime_array[ ];   // array for storing time of last trades ( points on the X axis )

private:
  void      SortMasterSlaveArray( double& _m[ ], double& _s[ ] );  // synchronous ascending sorting of two arrays

public:
  void      SetTradeSymbol( string _symbol );                      // set/change work symbol
  string    GetTradeSymbol( );                                    // get work symbol
  void      RefreshSymbolInfo( );                                 // refresh market information by work symbol
  void      SetMonitoringBeginDate( datetime _dt );                // set date of start of monitoring
  datetime  GetMonitoringBeginDate( );                            // get date of start of monitoring
  void      SetFiltrParams( long _magic, long _type = -1, int _limit = 0 );// set parameters of filtration of deals

public:
// Get results of last trades:
  int       GetTradeResultsArray( int _max_trades );

public:
  void      TBalanceHistory( );       // constructor
  void      ~TBalanceHistory( );      // destructor
};

Die Filtereinstellungen für das Lesen der Ergebnisse der letzten Handel und der History werden mit Hilfe derTBalanceHistory::SetFiltrParams Methode gemacht. Sie hat die folgenden Eingabeparameter:

  • _magic - "magische Zahl" der Handel, die aus der History gelesen werden sollten. Wird der Wert "0" angegeben, werden Handel mit jeder "magischen Zahl" gelesen.
  • _type - Art der Abschlüsse, die gelesen werden sollten. Sie kann die folgenden Werte haben - DEAL_TYPE_BUY (nur zum Lesen von langen Handel), DEAL_TYPE_SELL (nur zum Lesen von kurzen Handel) und -1 (zum Lesen von sowohl kurzen als auch langen Handel).
  • _limit - begrenzt die Tiefe der analysierten History der Handel. Ist _limit = "0", wird die komplettem verfügbare History analysiert.

Standardmäßig werden bei der Erzeugung des Objekts der TBalanceHistory Klasse die folgenden Werte gesetzt: _magic = 0, _type = -1, _limit = 0.

Die Hauptmethode dieser Klasse ist TBalanceHistory::GetTradeResultsArray. Sie dient zum Füllen von Klassen-Mitglieds-Arrays last_result_array and last_datetime_array mit den Ergebnissen der letzten Handel. Die Methode hat die folgenden Eingabeparameter:

  • _max_trades - Maximalzahl der Handel, die aus der History gelesen und in die Ausgabe-Arrays geschrieben werden sollten. Da wir mindestens zwei Punkte zur Berechnung der Neigung des Gefälles brauchen, sollte dieser Wert niemals < 2 sein. Ist dieser Wert = "0" wir die komplette, verfügbare History der Handel gelesen. Praktischerweise sollte die Anzahl der zur Berechnung des Gefälles der Saldokurve erforderlichen Punkte hier angegeben werden.
//---------------------------------------------------------------------
//  Reads the results of last (by time) trades to arrays:
//---------------------------------------------------------------------
//  - returns the number of actually read trades but not more than specified;
//---------------------------------------------------------------------
int
TBalanceHistory::GetTradeResultsArray( int _max_trades )
{
  int       index, limit, count;
  long      deal_type, deal_magic, deal_entry;
  datetime   deal_close_time, current_time;
  ulong     deal_ticket;                        // ticket of deal
  double    trade_result;
  string    symbol, deal_symbol;

  real_trades = 0;

//  Number of trades should be no less than two:
  if( _max_trades < 2 )
  {
    return( 0 );
  }

//  If a work symbol is not specified, don't do anything:
  symbol = trade_symbol.GetTradeSymbol( );
  if( symbol == NULL )
  {
    return( 0 );
  }

//  Request the history of deals and orders from the specified time to the current moment:
  if( HistorySelect( monitoring_begin_date, TimeCurrent( )) != true )
  {
    return( 0 );
  }

//  Calculate number of trades:
  count = HistoryDealsTotal( );

//  If there are less trades in the history than it is necessary, then exit:
  if( count < _max_trades )
  {
    return( 0 );
  }

//  If there are more trades in the history than it is necessary, then limit them:
  if( current_limit_history > 0 && count > current_limit_history )
  {
    limit = count - current_limit_history;
  }
  else
  {
    limit = 0;
  }

//  If needed, adjust dimension of "raw" arrays by the specified number of trades:
  if(( ArraySize( org_datetime_array )) != ( count - limit ))
  {
    ArrayResize( org_datetime_array, count - limit );
    ArrayResize( org_result_array, count - limit );
  }

//  Fill the "raw" array with trades from history base:
  real_trades = 0;
  for( index = count - 1; index >= limit; index-- )
  {
    deal_ticket = HistoryDealGetTicket( index );

//  If those are not closed deals, don't go further:
    deal_entry = HistoryDealGetInteger( deal_ticket, DEAL_ENTRY );
    if( deal_entry != DEAL_ENTRY_OUT )
    {
      continue;
    }

//  Check "magic number" of deal if necessary:
    deal_magic = HistoryDealGetInteger( deal_ticket, DEAL_MAGIC );
    if( current_magic != 0 && deal_magic != current_magic )
    {
      continue;
    }

//  Check symbol of deal:
    deal_symbol = HistoryDealGetString( deal_ticket, DEAL_SYMBOL );
    if( symbol != deal_symbol )
    {
      continue;
    }
                
//  Check type of deal if necessary:
    deal_type = HistoryDealGetInteger( deal_ticket, DEAL_TYPE );
    if( current_type != -1 && deal_type != current_type )
    {
      continue;
    }
    else if( current_type == -1 && ( deal_type != DEAL_TYPE_BUY && deal_type != DEAL_TYPE_SELL ))
    {
      continue;
    }
                
//  Check time of closing of deal:
    deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME );
    if( deal_close_time < monitoring_begin_date )
    {
      continue;
    }

//  So, we can read another trade:
    org_datetime_array[ real_trades ] = deal_close_time / 60;
    org_result_array[ real_trades ] = HistoryDealGetDouble( deal_ticket, DEAL_PROFIT ) / HistoryDealGetDouble( deal_ticket, DEAL_VOLUME );
    real_trades++;
  }

//  if there are less trades than necessary, return:
  if( real_trades < _max_trades )
  {
    return( 0 );
  }

  count = real_trades;

//  Sort the "raw" array by date/time of closing the order:
  SortMasterSlaveArray( org_datetime_array, org_result_array );

// If necessary, adjust dimension of group arrays for the specified number of points:
  if(( ArraySize( group_datetime_array )) != count )
  {
    ArrayResize( group_datetime_array, count );
    ArrayResize( group_result_array, count );
  }
  ArrayInitialize( group_datetime_array, 0.0 );
  ArrayInitialize( group_result_array, 0.0 );

//  Fill the output array with grouped data ( group by the identity of date/time of position closing ):
  for( index = 0; index < count; index++ )
  {
//  Get another trade:
    deal_close_time = ( datetime )org_datetime_array[ index ];
    trade_result = org_result_array[ index ];

//  Now check if the same time already exists in the output array:
    current_time = ( datetime )group_datetime_array[ real_trades ];
    if( current_time > 0 && MathAbs( current_time - deal_close_time ) > 0.0 )
    {
      real_trades++;                      // move the pointer to the next element
      group_result_array[ real_trades ] = trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
    else
    {
      group_result_array[ real_trades ] += trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
  }
  real_trades++;                          // now this is the number of unique elements

//  If there are less trades than necessary, exit:
  if( real_trades < _max_trades )
  {
    return( 0 );
  }

  if( ArraySize( last_result_array ) != _max_trades )
  {
    ArrayResize( last_result_array, _max_trades );
    ArrayResize( last_datetime_array, _max_trades );
  }

//  Write the accumulated data to the output arrays with reversed indexation:
  for( index = 0; index < _max_trades; index++ )
  {
    last_result_array[ _max_trades - 1 - index ] = group_result_array[ index ];
    last_datetime_array[ _max_trades - 1 - index ] = group_datetime_array[ index ];
  }

//  In the output array replace the results of single trades with the accumulating total:
  for( index = 1; index < _max_trades; index++ )
  {
    last_result_array[ index ] += last_result_array[ index - 1 ];
  }

  return( _max_trades );
}

Die obligatorischen Prüfungen werden zu Anfang ausgeführt - ob das Arbeitssymbol angegeben ist und die Eingabeparameter korrekt sind.

Dann lesen wir die History der Abschlüsse und Orders vom angegebene Datum bis zum aktuellen Zeitpunkt. Dies findet im folgenden Teil des Codes statt:

//  Request the history of deals and orders from the specified time to the current moment:
  if( HistorySelect( monitoring_begin_date, TimeCurrent( )) != true )
  {
    return( 0 );
  }

//  Calculate number of trades:
  count = HistoryDealsTotal( );

//  If there are less trades in the history than it is necessary, then exit:
  if( count < _max_trades )
  {
    return( 0 );
  }

Darüber hinaus wird die Gesamtanzahl der Abschlüsse in der History überprüft. Ist sie geringer als festgelegt, dann machen weitere Handlungen keinen Sinn mehr. Sobald die "rohen" Arrays vorbereitet sind, wird der Zyklus ihrer Befüllung mit der Information aus der History der Handel ausgeführt. Dies geschieht folgendermaßen:

//  Fill the "raw" array from the base of history of trades:
  real_trades = 0;
  for( index = count - 1; index >= limit; index-- )
  {
    deal_ticket = HistoryDealGetTicket( index );

//  If the trades are not closed, don't go further:
    deal_entry = HistoryDealGetInteger( deal_ticket, DEAL_ENTRY );
    if( deal_entry != DEAL_ENTRY_OUT )
    {
      continue;
    }

//  Check "magic number" of deal if necessary:
    deal_magic = HistoryDealGetInteger( deal_ticket, DEAL_MAGIC );
    if( _magic != 0 && deal_magic != _magic )
    {
      continue;
    }

//  Check symbols of deal:
    deal_symbol = HistoryDealGetString( deal_ticket, DEAL_SYMBOL );
    if( symbol != deal_symbol )
    {
      continue;
    }
                
//  Check type of deal if necessary:
    deal_type = HistoryDealGetInteger( deal_ticket, DEAL_TYPE );
    if( _type != -1 && deal_type != _type )
    {
      continue;
    }
    else if( _type == -1 && ( deal_type != DEAL_TYPE_BUY && deal_type != DEAL_TYPE_SELL ))
    {
      continue;
    }
                
//  Check time of closing of deal:
    deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME );
    if( deal_close_time < monitoring_begin_date )
    {
      continue;
    }

//  So, we can rad another trade:
    org_datetime_array[ real_trades ] = deal_close_time / 60;
    org_result_array[ real_trades ] = HistoryDealGetDouble( deal_ticket, DEAL_PROFIT ) / HistoryDealGetDouble( deal_ticket, DEAL_VOLUME );
    real_trades++;
  }

//  If there are less trades than necessary, exit:
  if( real_trades < _max_trades )
  {
    return( 0 );
  }

Zu Beginn wird das Ticket des Abschlusses aus der History mit Hilfe der HistoryDealGetTicket Funktion gelesen - weiteres Lesen der Abschlussdetails erfolgt dann mittels des erhaltenen Tickets. Da uns nur geschlossene Handel interessieren (wir analysieren ja den Saldo), wird zunächst die Art des Abschlusses geprüft. dies geschieht mittels Aufruf der HistoryDealGetInteger Funktion mit dem DEAL_ENTRY Parameter. Wenn die Funktion DEAL_ENTRY_OUT liefert, bedeutet das das Schließen einer Position

Anschließend werden die "magische Zahl" des Abschlusses, die Art des Abschlusses (wenn der Eingabeparameter der Methode angegeben ist) und das Symbol des Abschlusses geprüft. Wenn alle Parameter des Abschlusses die Voraussetzungen erfüllen, wird der letzte Parameter geprüft: die Zeit wann der Abschluss geschlossen wurde. Dies geschieht folgendermaßen:

//  Check the time of closing of deal:
    deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME );
    if( deal_close_time < monitoring_begin_date )
    {
      continue;
    }

Das Datum/Uhrzeit des Abschlusses wird mit dem gegebenen Datum/Uhrzeit des Beginns der Kontrolle der History verglichen. Ist Datum/Uhrzeit des Abschlusses jünger als das angegebene, dann lesen wir unseren Handel in das Array - lesen der Ergebnisse des Handels in Punkten und die Uhrzeit des Handels in Minuten (hier also die Zeit wann der geschlossen wurde). Danach erhöht sich der Zähler der gelesenen Abschlüsse real_trades und der Zyklus läuft weiter.

Sobald die "rohen" Arrays mit der notwendigen Menge Information befüllt sind, sollten wir das Array sortieren, wo die Zeit wann die Abschlüsse geschlossen wurden, gespeichert ist, sortieren. Zugleich müssen wir darauf achten, dass die Uhrzeit des Schließens im org_datetime_array Array und die Ergebnisse der Abschlüsse im org_result_array Array miteinander korrespondieren. Dies geschieht mit Hilfe der speziell geschriebenen Methode:

TBalanceHistory::SortMasterSlaveArray( double& _master[ ], double& _slave[ ] ). Der erste Parameter ist: _master - das Array, das aufsteigend sortiert wird Der zweite Parameter ist: _slave - das Array, dessen Elemente synchron mit den Elementen des ersten Arrays verschoben werden sollten. Sortieren erfolgt via der "Blasenmethode".

Nach all den oben beschriebenen Abläufen haben wir zwei Arrays mit Uhrzeit und Ergebnissen der Abschlüsse, die nach Zeit sortiert sind. Da nur ein Punkt auf der Saldokurve (der Punkt auf der Y-Achse) jedem zeitlichen Moment entsprechen kann (der Punkt auf der X-Achse), müssen wir die Elemente des Arrays mit der gleichen Abschlusszeit (falls vorhanden) gruppieren. Dies leistet der folgende Teil des Codes:

//  Fill the output array with grouped data ( group by identity of date/time of closing of position ):
  real_trades = 0;
  for( index = 0; index < count; index++ )
  {
//  Get another trade:
    deal_close_time = ( datetime )org_datetime_array[ index ];
    trade_result = org_result_array[ index ];

//  Now check, if the same time already exists in the output array:
    current_time = ( datetime )group_datetime_array[ real_trades ];
    if( current_time > 0 && MathAbs( current_time - deal_close_time ) > 0.0 )
    {
      real_trades++;                      // move the pointer to the next element
      group_result_array[ real_trades ] = trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
    else
    {
      group_result_array[ real_trades ] += trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
  }
  real_trades++;                          // now this is the number of unique elements

Praktischerweise werden alle Handel mit der "gleichen" Abschlusszeit hier zusammengefasst. Die Ergebnisse werden in die TBalanceHistory::group_datetime_array (Abschlusszeit) nd TBalanceHistory::group_result_array (Ergebnisse der Handel) Arrays geschrieben. Somit erhalten wir zwei sortierte Arrays mit einmaligen Elementen. Die Identität der Zeit wird in diesem Fall innerhalb einer Minute betrachtet. Diese Umwandlung kann grafisch veranschaulicht werden:

Abschlüsse mit der gleichen Uhrzeit gruppieren

Abb. 3 Abschlüsse mit der gleichen Uhrzeit gruppieren

Alle Abschlüsse innerhalb einer Minute (linker Teil der Abb.) werden in ein Array gruppiert, mit Aufrunden der Zeit und Zusammenfassung der Ergebnisse (rechter Teil der Abb.). Dadurch können das "Auseinanderklaffen" der Abschlusszeit von Abschlüssen ausgeglichen und die Stabilität der Regulierung verbessert werden.

Danach müssen Sie bei den erhaltenen Arrays noch zwei weitere Umwandlungen vollziehen. Die Reihenfolge der Elemente umkehren, sodass der früheste Abschluss dem "0"-Element entspricht und die Ergebnisse von einzelnen Handel durch das kumulative Gesamtergebnis, also dem Saldo, ersetzen. Dies geschieht mittels der folgenden Code-Fragments:

//  Write the accumulated data into output arrays with reversed indexation:
  for( index = 0; index < _max_trades; index++ )
  {
    last_result_array[ _max_trades - 1 - index ] = group_result_array[ index ];
    last_datetime_array[ _max_trades - 1 - index ] = group_datetime_array[ index ];
  }

//  Replace the results of single trades with the cumulative total in the output array:
  for( index = 1; index < _max_trades; index++ )
  {
    last_result_array[ index ] += last_result_array[ index - 1 ];
  }


TBalanceSlope Klasse

Diese Klasse dient zur Ausführung von Vorgängen an der Saldokurve eines Accounts. Sie wird von der TBalanceHistory Klasse "hervorgebracht" und erbt all ihre geschützten und public Daten und Methoden. Sehen wir uns ihre Struktur mal genauer an:

//---------------------------------------------------------------------
//  Operations with the balance curve:
//---------------------------------------------------------------------
class TBalanceSlope : public TBalanceHistory
{
private:
  double    current_slope;               // current angle of slope of the balance curve
  int       slope_count_points;          // number of points ( trades ) for calculation of slope angle
        
private:
  double    LR_koeff_A, LR_koeff_B;      // rates for the equation of the straight-line regression
  double    LR_points_array[ ];          // array of point of the straight-line regression

private:
  void      CalcLR( double& X[ ], double& Y[ ] );  // calculate the equation of the straight-line regression

public:
  void      SetSlopePoints( int _number );        // set the number of points for calculation of angle of slope
  double    CalcSlope( );                         // calculate the slope angle

public:
  void      TBalanceSlope( );                     // constructor
  void      ~TBalanceSlope( );                    // destructor
};

Wir legen die Gefälleneigung der Saldokurve mittels der Gefälleneigung der Linie der linearen Regression fest, die für die angegebene Menge an Punkten (Handel) der Saldokurve gezeichnet wird. Also müssen wir zuerst die Gleichung der geradlinigen Regression der folgenden Form berechnen: A*x + B. Dies macht die folgende Methode für uns:

//---------------------------------------------------------------------
//  Calculate the equation of the straight-line regression:
//---------------------------------------------------------------------
//  input parameters:
//    X[ ] - arras of values of number series on the X axis;
//    Y[ ] - arras of values of number series on the Y axis;
//---------------------------------------------------------------------
void
TBalanceSlope::CalcLR( double& X[ ], double& Y[ ] )
{
  double    mo_X = 0, mo_Y = 0, var_0 = 0, var_1 = 0;
  int       i;
  int       size = ArraySize( X );
  double    nmb = ( double )size;

//  If the number of points is less than two, the curve cannot be calculated:
  if( size < 2 )
  {
    return;
  }

  for( i = 0; i < size; i++ )
  {
    mo_X += X[ i ];
    mo_Y += Y[ i ];
  }
  mo_X /= nmb;
  mo_Y /= nmb;

  for( i = 0; i < size; i++ )
  {
    var_0 += ( X[ i ] - mo_X ) * ( Y[ i ] - mo_Y );
    var_1 += ( X[ i ] - mo_X ) * ( X[ i ] - mo_X );
  }

//  Value of the A coefficient:
  if( var_1 != 0.0 )
  {
    LR_koeff_A = var_0 / var_1;
  }
  else
  {
    LR_koeff_A = 0.0;
  }

//  Value of the B coefficient:
  LR_koeff_B = mo_Y - LR_koeff_A * mo_X;

//  Fill the array of points that lie on the regression line:
  ArrayResize( LR_points_array, size );
  for( i = 0; i < size; i++ )
  {
    LR_points_array[ i ] = LR_koeff_A * X[ i ] + LR_koeff_B;
  }
}

Wir verwenden hier die Methode der kleinsten Quadrate zur Berechnung des Mindestfehlers der Position der Regressionslinie in Bezug auf die ursprünglichen Daten. Das Array, das die Y-Koordinaten, die auf der berechneten Linie liegen, speichert, wird auch befüllt. Es wird im Moment nicht gebraucht und kommt erst bei späterer Entwicklung zum Einsatz.

Die Hauptmethode, die in der gegebenen Klasse verwenet wird, ist TBalanceSlope::CalcSlope. Sie liefert die Gefälleneigung der Saldokurve, die nach der angegebene Menge der letzten Handel berechnet wird. Das sieht dann so aus:

//---------------------------------------------------------------------
//  Calculate slope angle:
//---------------------------------------------------------------------
double
TBalanceSlope::CalcSlope( )
{
//  Get result of trading from the history of trades:
  int      nmb = GetTradeResultsArray( slope_count_points );
  if( nmb < slope_count_points )
  {
    return( 0.0 );
  }

//  Calculate the regression line by the results of last trades:
  CalcLR( last_datetime_array, last_result_array );
  current_slope = LR_koeff_A;

  return( current_slope );
}

Zuerst wird die angegebene Menge der letzten Punkte der Saldokurve analysiert. Dies geschieht durch Aufruf der Methode der Basisklasse TBalanceSlope::GetTradeResultsArray. Ist die Menge der gelesenen Punkt nicht kleiner als angegeben, wird die Regressionslinie berechnet. Und zwar mit Hilfe der TBalanceSlope::CalcLR Methode. Die last_result_array und last_datetime_array Arrays, die zu der Basisklasse gehören und die im vorigen Schritt befüllt wurden, werden als Argumente herangezogen.

Die restlichen Methoden sind einfach und müssen nicht im einzelnen beschrieben werden.


TBalanceSlopeControl Klasse

Diese Basisklasse verwaltet das Gefälle der Saldokurve durch Veränderung des Arbeitsvolumens. Sie wird von der TBalanceSlope Klasse "hervorgebracht" und erbt all ihre geschützten und public Daten und Methoden. Ihr einziger Zweck ist die Berechnung des aktuellen Arbeitsvolumens je nach der aktuellen Neigung des Gefälles der Saldokurve. Sehen wir sie uns mal genauer an:

//---------------------------------------------------------------------
//  Managing slope of the balance curve:
//---------------------------------------------------------------------
enum LotsState
{
  LOTS_NORMAL = 1,            // mode of trading with normal volume
  LOTS_REJECTED = -1,         // mode of trading with lowered volume
  LOTS_INTERMEDIATE = 0,      // mode of trading with intermediate volume
};
//---------------------------------------------------------------------
class TBalanceSlopeControl : public TBalanceSlope
{
private:
  double    min_slope;          // slope angle that corresponds to the mode of volume rejection
  double    max_slope;          // slope angle that corresponds to the mode of normal volume
  double    centr_slope;        // slope angle that corresponds to the mode of volume switching without hysteresis

private:
  ControlType  control_type;    // type of the regulation function

private:
  double    rejected_lots;      // volume in the rejection mode
  double    normal_lots;        // volume in the normal mode
  double    intermed_lots;      // volume in the intermediate mode

private:
  LotsState current_lots_state; // current mode of volume

public:
  void      SetControlType( ControlType _control );  // set type of the regulation characteristic
  void      SetControlParams( double _min_slope, double _max_slope, double _centr_slope );

public:
  double    CalcTradeLots( double _min_lots, double _max_lots );  // get trade volume

protected:
  double    CalcIntermediateLots( double _min_lots, double _max_lots, double _slope );

public:
  void      TBalanceSlopeControl( );   // constructor
  void      ~TBalanceSlopeControl( );  // destructor
};

Bevor wir das aktuelle Volumen berechnen, müssen wir zuerst die initialen Parameter setzen. Das geschieht durch Aufruf der folgenden Methoden:

void      SetControlType( ControlType _control );  // set type of the regulation characteristic

Input parameter_control - dies ist der Type des Regulierungsmerkmals. Er kann den folgenden Wert haben:

  • STEP_WITH_HYSTERESISH - schrittweise mit Hysterese-Regulierungsmerkmal;
  • STEP_WITHOUT_HYSTERESIS - schrittweise ohne Hysterese-Regulierungsmerkmal;
  • LINEAR - lineares Regulierungsmerkmal;
  • NON_LINEAR - nicht-lineares Regulierungsmerkmal (in dieser Version nicht implementiert);
void      SetControlParams( double _min_slope, double _max_slope, double _centr_slope );

Eingabeparameter sind wie folgt:

  • _min_slope - Gefälleneigung der Saldokurve, die Handel mit Minimalvolumen entspricht;
  • _max_slope - Gefälleneigung der Saldokurve, die Handel mit Maximalvolumen entspricht;
  • _centr_slope - Gefälleneigung der Saldokurve, die dem schrittweisen Regulierungsmerkmal ohne Hysterese entspricht;

Das Volumen wird mit Hilfe folgender Methode berechnet:

//---------------------------------------------------------------------
//  Get trade volume:
//---------------------------------------------------------------------
double
TBalanceSlopeControl::CalcTradeLots( double _min_lots, double _max_lots )
{
//  Try to calculate slope of the balance curve:
  double    current_slope = CalcSlope( );

//  If the specified amount of trades is not accumulated yet, trade with minimal volume:
  if( GetRealTrades( ) < GetSlopePoints( ))
  {
    current_lots_state = LOTS_REJECTED;
    rejected_lots = trade_symbol.NormalizeLots( _min_lots );
    return( rejected_lots );
  }

//  If the regulation function is stepped without hysteresis:
  if( control_type == STEP_WITHOUT_HYSTERESIS )
  {
    if( current_slope < centr_slope )
    {
      current_lots_state = LOTS_REJECTED;
      rejected_lots = trade_symbol.NormalizeLots( _min_lots );
      return( rejected_lots );
    }
    else
    {
      current_lots_state = LOTS_NORMAL;
      normal_lots = trade_symbol.NormalizeLots( _max_lots );
      return( normal_lots );
    }
  }

//  If the slope of linear regression for the balance curve is less than the allowed one:
  if( current_slope < min_slope )
  {
    current_lots_state = LOTS_REJECTED;
    rejected_lots = trade_symbol.NormalizeLots( _min_lots );
    return( rejected_lots );
  }

//  If the slope of linear regression for the balance curve is greater than specified:
  if( current_slope > max_slope )
  {
    current_lots_state = LOTS_NORMAL;
    normal_lots = trade_symbol.NormalizeLots( _max_lots );
    return( normal_lots );
  }

//  The slope of linear regression for the balance curve is within specified borders (intermediate state):
  current_lots_state = LOTS_INTERMEDIATE;

//  Calculate the value of intermediate volume:
  intermed_lots = CalcIntermediateLots( _min_lots, _max_lots, current_slope );
  intermed_lots = trade_symbol.NormalizeLots( intermed_lots );

  return( intermed_lots );
}

Die hauptsächlichen Punkte der Implementierung der TBalanceSlopeControl::CalcTradeLots Methode sind:

  • Bis die angegebene Mindestmenge an Handel angehäuft ist, mit Minimalvolumen handeln. Ist ja logisch, da man nicht weiß, in welcher Periode (rentabel oder nicht) sich der Expert Advisor, so direkt nachdem Sie ihn fürs Handeln eingerichtet haben, gerade befindet.
  • Wird als Regulierungsfunktion die schrittweise Funktion ohne Hysterese angewendet, dann sollten Sie zur Einrichtung der Neigung des Wechsels zwischen den Handelsmodi via derTBalanceSlopeControl::SetControlParams Methode ausschließlich den Parameter _centr_slope verwenden. Die Parameter _min_slope und _max_slope werden ignoriert. Dies wird gemacht, um eine korrekte Optimierung durch diesen Parameter imMetaTrader 5 Strategie-Tester zu erreichen.

Je nach berechneter Neigung des Gefälles wird Handel mit minimalem, maximalem oder dazwischenliegendem Volumen ausgeführt. Dazwischenliegendes Volumen wird mittels der einfachen Methode - TBalanceSlopeControl::CalcdazwischenliegendLots berechnet. Diese Methode ist geschützt und wird innerhalb der Klasse verwendet. Ihr Code steht unten:

//---------------------------------------------------------------------
//  Calculation of intermediate volume:
//---------------------------------------------------------------------
double
TBalanceSlopeControl::CalcIntermediateLots( double _min_lots, double _max_lots, double _slope )
{
  double    lots;

//  If the regulation function is stepped with hysteresis:
  if( control_type == STEP_WITH_HYSTERESISH )
  {
    if( current_lots_state == LOTS_REJECTED && _slope > min_slope && _slope < max_slope )
    {
      lots = _min_lots;
    }
    else if( current_lots_state == LOTS_NORMAL && _slope > min_slope && _slope < max_slope )
    {
      lots = _max_lots;
    }
  }
//  If the regulation function is linear:
  else if( control_type == LINEAR )
  {
    double  a = ( _max_lots - _min_lots ) / ( max_slope - min_slope );
    double  b = normal_lots - a * .max_slope;
    lots = a * _slope + b;
  }
//  If the regulation function is non-linear ( not implemented yet ):
  else if( control_type == NON_LINEAR )
  {
    lots = _min_lots;
  }
//  If the regulation function is unknown:
  else
  {
    lots = _min_lots;
  }

  return( lots );
}

Andere Methoden dieser Klasse müssen nicht extra beschrieben werden.

Beispiel einer Einbettung des Systems in einen Expert Advisor

Betrachten wir uns nun Schritt für Schritt, wie das System zur Kontrolle des Gefälles der Saldokurve in einen Expert Advisor implementiert wird.

Schritt 1 - Hinzufügen der Anweisung die entwickelte Library mit dem Expert Advisor zu verknüpfen:

#include  <BalanceSlopeControl.mqh>

Schritt 2 - Hinzufügen der externen Variablen zur Errichtung von Parametern des Systems zur Kontrolle des Gefälles der Saldolinie in den Expert Advisor:

//---------------------------------------------------------------------
//  Parameters of the system of controlling the slope of the balance curve;
//---------------------------------------------------------------------
enum SetLogic 
{
  No = 0,
  Yes = 1,
};
//---------------------------------------------------------------------
input SetLogic     UseAutoBalanceControl = No;
//---------------------------------------------------------------------
input ControlType  BalanceControlType = STEP_WITHOUT_HYSTERESIS;
//---------------------------------------------------------------------
//  Amount of last trades for calculation of LR of the balance curve:
input int          TradesNumberToCalcLR = 3;
//---------------------------------------------------------------------
//  Slope of LR to decrease the volume to minimum:
input double       LRKoeffForRejectLots = -0.030;
//---------------------------------------------------------------------
//  Slope of LR to restore the normal mode of trading:
input double       LRKoeffForRestoreLots = 0.050;
//---------------------------------------------------------------------
//  Slope of LR to work in the intermediate mode:
input double       LRKoeffForIntermedLots = -0.020;
//---------------------------------------------------------------------
//  Decrease the initial volume to the specified value when the LR is inclined down
input double       RejectedLots = 0.10;
//---------------------------------------------------------------------
//  Normal work volume in the mode of MM with fixed volume:
input double       NormalLots = 1.0;

Schritt 3 - Hinzufügen des Objekts vom Typ TBalanceSlopeControl in den Expert Advisor:

TBalanceSlopeControl  BalanceControl;

Diese Deklarierung kann am Anfang des Expert Advisors, vor der Festlegung der Funktionen, gemacht werden.

Schritt 4 - Hinzufügen des Codes zur Initialisierung des Systems zur Kontrolle des Gefälles der Saldokurve in die OnInit Funktion des Expert Advisors:

//  Adjust our system of controlling the slope of the balance curve:
  BalanceControl.SetTradeSymbol( Symbol( ));
  BalanceControl.SetControlType( BalanceControlType );
  BalanceControl.SetControlParams( LRKoeffForRejectLots, LRKoeffForRestoreLots, LRKoeffForIntermedLots );
  BalanceControl.SetSlopePoints( TradesNumberToCalcLR );
  BalanceControl.SetFiltrParams( 0, -1, 0 );
  BalanceControl.SetMonitoringBeginDate( 0 );

Schritt 5 - Hinzufügen des Aufrufs der Methode zur Aktualisierung der aktuellen Marktinformation in die OnTick Funktion des Expert Advisors:

//  Refresh market information:
  BalanceControl.RefreshSymbolInfo( );

Der Aufruf dieser Methode kann ganz am Anfang der OnTick Funktion hinzugefügt werden, oder nach der Prüfung, ob ein neuer Balken ankommt (für Expert Advisors, die über so eine Prüfung verfügen).

Schritt 6 - Hinzufügen des Codes zur Berechnung des aktuellen Volumens vor dem Code, wo Positions eröffnet werden:

if( UseAutoBalanceControl == Yes )
  {
    current_lots = BalanceControl.CalcTradeLots( RejectedLots, NormalLots );
  }
  else
  {
    current_lots = NormalLots;
  }

Wird ein Money Management System im Expert Advisor verwendet, sollten Sie statt NormalLots die TBalanceSlopeControl::CalcTradeLots Methode schreiben -das aktuelle Volumen, das vom MM-System des Expert Advisors berechnet wird.

Ein Test des Expert Advisor BSCS-TestExpert.mq5 mit dem oben beschriebenen, eingebauten System ist im Anhang dieses Beitrags zu finden. Das Arbeitsprinzip beruht auf der Überschneidung der Ebenen des CCI Indikators. Dieser Expert Advisor ist für Testzwecke entwickelt und eignet sich nicht für die Arbeit an echten Accounts Wir testen ihn im H4 Zeitrahmen (01.07.2008 - 01.09.2010) auf EURUSD.

Analysieren wir nun das Arbeitsergebnis dieses EAs. Der Chart der Saldoveränderung bei aktiviertem System zur Kontrolle des Gefälles ist unten abgebildet. Um ihn zu sehen, müssen wir für den UseAutoBalanceControl externen Parameter den Nein Wert setzen.

Ursprünglicher Chart der Saldoveränderung

Abb. 4 Ursprünglicher Chart der Saldoveränderung

Setzen wir jetzt für denUseAutoBalanceControl externen Parameter den Ja Wert und testen den Expert Advisor. Sie erhalten den Chart bei aktiviertem System zur Kontrolle des Saldogefälles.

Chart der Saldoveränderung bei aktiviertem Kontrollsystem

Abb. 5 Chart der Saldoveränderung bei aktiviertem Kontrollsystem

Sie sehen, das die meisten Perioden im oberen Chart (Abb. 4)so aussehen, als wären sie abgeschnitten, und dass sie im unteren Chart flacher sind (Abb. 5). Das ist das Ergebnis der Arbeit unseres Systems. Sie können die hauptsächlichen Parameter der Arbeit des Expert Advisors miteinander vergleichen:

Parameter UseAutoBalanceControl = Nein UseAutoBalanceControl = Ja
Insgesamter Nettogewinn: 18.378,00 17.261,73
Gewinnfaktor: 1,47 1,81
Korrekturfaktor: 2,66 3,74
Erwartete Auszahlung: 117,81 110,65
Absolute Inanspruchnahme des Saldos: 1.310.50 131,05
Absolute Inanspruchnahme von Eigenkapital: 1.390,50 514,85
Maximale Inanspruchnahme des Saldos: 5.569,50 (5,04%) 3.762,15 (3,35%)
Maximale Inanspruchnahme von Eigenkapital: 6.899,50 (6,19%) 4.609,60 (4,08%)

Die besten Parameter unter den miteinander verglichenen sind in Grün unterlegt. Gewinn und erwartete Auszahlung haben leicht abgenommen - dies ist die andere Seite de Regulierung, die infolge der Verzögerungen beim Wechsel zwischen den Stati des Arbeitsvolumens auftritt. Doch insgesamt gesehen, stellt dies eine Verbesserung der Raten in der Arbeit des Expert Advisors dar. Vor allem bei der Inanspruchnahme und dem Gewinnfaktor.


Fazit

Für mich zeigen sich mehrere Wege, dieses System zu verbessern:
  • Der Einsatz von virtuellem Handel, sobald der Expert Advisor in eine ungünstige Arbeitsperiode eintritt. Denn dann spielt das normale Arbeitsvolumen keine Rolle mehr. Eine Abnahme der Inanspruchnahme wird dadurch gestattet.
  • Der Einsatz von komplexeren Algorithmen zur Festlegung der aktuellen Arbeitsstati des Expert Advisors (rentabel oder nicht). Wir können z.B. die Anwendung eines Neuronennetzes für eine derartige Analyse probieren. Doch hierfür ist noch zusätzliche genaue Untersuchung nötig

In diesem Beitrag haben wir das Arbeitsprinzip und Arbeitsergebnis des Systems behandelt, mit dessen Hilfe die Qualitätsmerkmale eines Expert Advisors verbessert werden können. Der gemeinsame Einsatz mit dem Money Management-System erlaubt in manchen Fällen, einen Anstieg der Rentabilität ohne zugleich ansteigendes Risiko.

Ich erinnere Sie hier noch einmal: es gibt kein Hilfssystem was auch immer, mit dem ein verlustreicher Expert Advisor im Handumdrehen zu einem gewinnbringenden wird.

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

Beigefügte Dateien |
Analyse von Kerzenmustern Analyse von Kerzenmustern
Die Konstruktion japanischer Kerzendiagramme und die Analyse von Kerzenmustern sind ein erstaunlicher Bereich der technischen Analyse. Der Vorteil von Kerzen ist, dass sie Daten auf eine Art darstellen, dank der Sie die Dynamiken innerhalb der Daten verfolgen können. In diesem Beitrag analysieren wir Arten von Kerzen, klassifizieren Kerzenmuster und stellen einen Indikator vor, der Kerzenmuster bestimmen kann.
Leitfaden zum Schreiben einer DLL für MQL5 in Delphi Leitfaden zum Schreiben einer DLL für MQL5 in Delphi
Dieser Beitrag befasst sich mit dem Mechanismus zur Erstellung eines DLL-Moduls mithilfe der beliebten Programmiersprache ObjectPascal innerhalb einer Delphi-Programmierumgebung. Die Inhalte dieses Beitrags richten sich mit der Einbindung äußerer DLL-Module vorrangig an Neueinsteiger in der Programmierung, die mit Problemen kämpfen, die die Grenzen der eingebetteten Programmiersprache MQL5 sprengen.
Erstellen eines Expert Advisors, der mit verschiedenen Instrumenten handelt Erstellen eines Expert Advisors, der mit verschiedenen Instrumenten handelt
Das Konzept der Diversifizierung von Vermögenswerten auf Finanzmärkten ist ziemlich alt und war für Neueinsteiger im Handel immer interessant. In diesem Beitrag stellt der Verfasser eine äußerst einfache Vorgehensweise für die Erstellung eines Expert Advisors vor, der mit mehreren Währungen handelt, um diese Strömung von Handelsstrategien vorzustellen.
Wie man rasch einen Expert Advisor für den Automatisierten Trading-Wettbewerb 2010 erzeugt Wie man rasch einen Expert Advisor für den Automatisierten Trading-Wettbewerb 2010 erzeugt
Zur Entwicklung eines Expert Advisors zur Teilnahme am Automatisierten Trading-Wettbewerb 2010, nehmen wir ein Template eines fertigen Expert Advisors her. Selbst noch unerfahrene MQL5 Programmierer können diese Aufgabe bewältigen, da ja für die Strategien die grundlegenden Klassen, Funktionen und Templates schon entwickelt sind. Daher genügt es, nur ein bisschen Code zur Implementierung Ihres Trading-Konzepts zu schreiben.