Verarbeitung von Ereignissen in MQL5: Unmittelbare Änderung des Zeitraums für den gleitenden Durchschnitt

Sceptic Philozoff | 12 Februar, 2016

Einleitung

Verarbeitung von Ereignissen in MQL5: Unmittelbare Änderung des Zeitraums für den gleitenden Durchschnitt Dieser kurze Beitrag widmet sich einem der neuen Features von MQL5 der MetaTrader 5 Plattform, die von MetaQuotes Software Corp. entwickelt wurde. Vielleicht kommt dieser Beitrag ein bisschen verspätet (hätte eigentlich im September/Oktober 2009 schon erscheinen sollen - das wäre rechtzeitig gewesen), doch gab es zu diesem Thema noch keine ähnlichen Beiträge. Und zudem gab es damals noch keine solchen Möglichkeiten, Ereignisse in Indikatoren zu verarbeiten.

Also stellen wir uns vor, wir haben irgendeinen Kurs-Indikator auf ein Chart angewendet (in unserem ist es der Gleitender Durchschnitt, MA), und wir wollen seine ausgleichenden Zeitraum ändern. Die MT4 Plattform bot uns dazu die folgenden Optionen:

Und wir alle wissen ja, dass der Wunsch nach möglichst wenig Anstrengung der größte Treiber für Fortschritt ist. Doch dank der neuen MT5 Plattform, die die Verarbeitung von Indikatoren-Ereignissen, die vom Benutzer initiiert wurden, gestattet, können wir die o.g. Möglichketien alle vergessen und Indikator-Parameter mit einem einzigen Tastendruck verändern. Dieser Beitrag zeigt wie die technische Implementierung dieser Problemlösung funktioniert.

Ausgabenzuweisung und Problem

Der in unserem Beispiel verwendete Indikator-Quellcode kommt zusammen mit dem Client-Terminal. Die unveränderte Quellcode-Datei (Custom Moving Average.mq5) ist am Ende des Beitrags angehängt.

Wir werden hier nicht den Quellcode und seine Veränderungen, verglichen mit seinem MQL4 Original analysieren. An einigen Stellen hat er sich in der Tat erheblich geändert, aber das ist nicht immer sichtbar. Entsprechende Erklärungen in Bezug auf die Restrukturierung des Grundlagenteils der Berechnung findet man in Foren und der Online-Hilfe.

Doch der Hauptteil des Indikator in MQL4 ist dennoch unverändert geblieben. Mindestens 80% aller Änderungen im Code, mit denen das Problem gelöst werden soll, wurden auf Basis der Idee der Rechnerfunktionen des Indikators als die "Black Boxes" gemacht.

Ein Beispiel dessen, was wir erreichen wollen, sieht so aus. Angenommen, wir haben diesen Indikator auf ein Chart angewandt, und er zeigt zu einem bestimmten Moment einen exponentiellen MA (EMA) mit Null Verschiebung und Zeitraum 10. Unser Ziel ist den ausgleichenden Zeitraum eines einfachen MA (SMA) um 3 zu erhöhen (also auf 13) und um 5 Balken nach rechts zu verschieben . Die dazu erforderlichen Handlungen sehen folgendermaßen aus:

Die erste und offensichtlichste Lösung ist, die OnChartEvent() Funktion in den Indikator-Code einzufügen und den Handler für den Tastendruck zu schreiben. Gemäß der Liste der Veränderungen im MetaTrader 4, baut der Client-Terminal 245  und 246 https://www.mql5.com/en/forum/53/page1/#comment_2655,

MetaTrader 5 Client-Terminal baut 245 und 246

MQL5: Wurde zur Möglichkeit des Ereignis-Handling durch angepasste Indikatoren hinzugefügt, ähnlich wie bei Expert Advisors.

Jetzt bereitet uns die Ergänzung neuer Ereignis-Handler im Indikator keine Probleme mehr. Doch dafür müssen wir seinen Code leicht verändern.

Erstens hat sich in MQL5 der Status der externen Parameter des Indikators verändert: die kann man nicht im Code ändern. Das geht nur im Dialogfeld Eigenschaften im Client-Terminal. Muss man sie jedoch doch einmal dringend dort ändern, kann man diese Einschränkung leicht umgehen: einfach die Werte der externen Parameter in die neuen globalen Variablen des Indikators kopieren und, wenn diese neuen Variablen tatsächlich die externen Parameter des Indikators sind, werden alle Berechnungen ausgeführt. Andererseits verliert man in dem Fall die Umsetzbarkeit der externen Parameter, deren Werte Benutzer nur irreführen können. Wir brauchen diese Parameter jetzt einfach nicht.

Also gibt es keine externen (Eingabe) Parameter im Indikator. Die Variablen, die nun die Funktion der externen Parameter übernehmen sind jetzt die Terminal Globalen Variablen, kurz TGVs. Möchten Sie die TGVs sehen, die für die ehemaligen externen Parameter des Indikators verantwortlich sind, drücken Sie einfach im Terminal F3. Für mich schlicht der einfachste Weg, die Indikatorparameter zu kontrollieren.

Zweitens (und das ist wichtig), bei jeder Änderung der externen Parameter des Indikators müssen wir all seine Werte in der gesamten History neue berechnen, abermals und von Anfang an. Anders ausgedrückt: wir müssen Berechnungen durchführen, die sonst nur beim allerersten Start des Indikators gemacht werden. Die Optimierung der Indikator-Berechnungen bleibt zwar, wird jedoch jetzt etwas subtiler.

Unten stehen einige Teile des Codes der ersten Version des modifizierten Indikators. Der komplette Code findet sich im Anhang dieses Beitrags.

 

"Standardversion": Beschreibung der Änderungen im Standard-Quellcode des Indikators

Externe Parameter sind nicht mehr extern, sondern einfach globale Variablen

Alle externen Parameter des Indikators haben ihren Eingabe-Modifikator verloren. Eigentlich hätte ich sie gar nicht global machen können, doch ich habe das aus Gewohnheit dennoch getan:

int              MA_Period   = 13;
int              MA_Shift    =  0;
ENUM_MA_METHOD   MA_Method   =  0;
int              Updated     =  0;     /// Indicates whether the indicator has updated after changing it's values

Die ersten drei Optionen - der Zeitraum, die Verschiebung und die Art des MA sowie die vierte - Aktualisiert - sind für die Berechnungsoptimierung bei der Veränderung der MA Paramater zuständig. Dies wird einige Zeilen weiter unten erklärt. 

Codes für virtuelle Tasten

Geben Sie nun die Codes für die virtuellen Tasten ein:

#define KEY_UP             38
#define KEY_DOWN           40
#define KEY_NUMLOCK_DOWN   98
#define KEY_NUMLOCK_UP    104
#define KEY_TAB             9

Das sind die Codes für die "Bild nach oben" und "Bild nach unten" Tasten und ähnliche Tasten auf dem Zahlenfeld (Taste "8" und "2"), sowie die TAB-Taste. Die gleichen Codes (mit anderen Namen der VK_XXX Konstanten) gibt es tatsächlich in der Datei <MT5dir>\MQL5\Include\VirtualKeys.mqh, doch in unserem Beispiel habe ich sie nicht angerührt.


Kleine Code-Korrektur in der Funktion, die den linear gewichteten gleitenden Durchschnitt (LWMA) berechnet

Ich habe in der CalculateLWMA() Funktion eine kleine Verbesserung vorgenommen: In der Originalversion war die weightsum Variable mit Hilfe des statischen Modifikators deklariert. Und der einzig naheliegende Grund, warum die Entwickler dies getan haben, war die Notwendigkeit, sie beim ersten Aufruf dieser Funktion vorab berechnen zu können. Im weiteren Code bleibt diese Variable unverändert. Und das ist der Originalcode der Funktion, in dem alle Teile, die mit Berechnung und Verwendung von weightsum zusammenhängen, mit Kommentaren versehen sind:

void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int              i,limit;
   static int     weightsum;                       // <-- using weightsum
   double               sum;
//--- first calculation or number of bars was changed
   if(prev_calculated==0)                          // <-- using weightsum
     {
      weightsum=0;                                 // <-- using  weightsum
      limit=InpMAPeriod+begin;                     // <-- using weightsum
      //--- set empty value for first limit bars
      for(i=0;i<limit;i++) ExtLineBuffer[i]=0.0;
      //--- calculate first visible value
      double firstValue=0;
      for(i=begin;i<limit;i++)                     // <-- using weightsum
        {
         int k=i-begin+1;                          // <-- using weightsum
         weightsum+=k;                             // <-- using weightsum
         firstValue+=k*price[i];
        }
      firstValue/=(double)weightsum;
      ExtLineBuffer[limit-1]=firstValue;
     }
   else limit=prev_calculated-1;
//--- main loop
   for(i=limit;i<rates_total;i++)
     {
      sum=0;
      for(int j=0;j<InpMAPeriod;j++) sum+=(InpMAPeriod-j)*price[i-j];
      ExtLineBuffer[i]=sum/weightsum;              // <-- using weightsum
      }
//---
  }

Zuvor funktionierte diese Version ziemlich gut, doch als ich "Indikator + Advisor" beide zusammen ablaufen ließ (wird am Ende dieses Beitrags angesprochen), kam es in genau dieser Art MA zu Problemen. Das größte Problem wurde durch die o.g. Umstände verursacht, d.h. weightsum was die statische Variable: diese Variable stieg konstant an, da ja bei jeder spontanen Veränderung des MA Parameters, ihre Neuberechnung von Anfang an notwendig war.

Der einfachste Weg, den weightsum Wert direkt und sofort zu berechnen (ist gleich der Summe aller ganzen Zahlen von 1 bis zum MA Zeitraum - zu diesem Zweck git es eine einfache Formel für die Summe der arithmetischen Progression), und zugleich seinen Status als einen statischen Wert abzulehnen - was ich getan habe. Also anstatt wie vorhin weightsum mit Hilfe des statischen Modifikators zu deklarieren, machen wir das jetzt ohne ihn, initialisieren ihn nur mit dem Wert "korrekt" und entfernen so die ursprüngliche Schleife der "Variablenhäufung".

int weightsum = MA_Period *( MA_Period + 1 ) / 2;

So, jetzt funktioniert alles korrekt.


OnCalculate() Funktion als Handler

Ich musste in der OnCalculate() Funktion eine ganze Menge ändern, und deswegen gebe ich ihren Code hier komplett an.

int OnCalculate(const int rates_total,
                const int prev_calculated,            /// Mathemat: full recalculation!
                const int begin,                      /// Mathemat: full recalculation!
                const double &price[])
  {
//--- check for bars count
   if(rates_total<MA_Period-1+begin)
      return(0);// not enough bars for calculation
//--- first calculation or number of bars was changed
   if(prev_calculated==0)
      ArrayInitialize(LineBuffer,0);
//--- sets first bar from what index will be draw
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,MA_Period-1+begin);

//--- calculation (Mthmt - optimized by Mathemat)

   if( GlobalVariableGet( "Updated" ) == 1 )
   {
      if(MA_Method==MODE_EMA)  CalculateEMA(       rates_total,prev_calculated,begin,price);
      if(MA_Method==MODE_LWMA) CalculateLWMA_Mthmt(rates_total,prev_calculated,begin,price);
      if(MA_Method==MODE_SMMA) CalculateSmoothedMA(rates_total,prev_calculated,begin,price);
      if(MA_Method==MODE_SMA)  CalculateSimpleMA(  rates_total,prev_calculated,begin,price);
   }
   else
   {
      OnInit( );                 /// Mthmt
      if(MA_Method==MODE_EMA)  CalculateEMA(       rates_total,0,0,price);
      if(MA_Method==MODE_LWMA) CalculateLWMA_Mthmt(rates_total,0,0,price);
      if(MA_Method==MODE_SMMA) CalculateSmoothedMA(rates_total,0,0,price);
      if(MA_Method==MODE_SMA)  CalculateSimpleMA(  rates_total,0,0,price);
      GlobalVariableSet( "Updated", 1 );
      Updated = 1;
   }
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Die primäre Änderung hängt damit zusammen, dass wir ja eine Indikatorberechnung "komplett von Null aus" machen müssen: denn es ist ja klar, dass, wenn der MA Zeitraum durch Tastatureingaben von 13 auf 14 verändert wurde, alle vorherigen Optimierungen seiner Berechnungen nutzlos geworden sind und wir den MA erneut berechnen müssen. Das passiert, wenn die Aktualisierte Variable den Wert 0 besitzt (TGV hat sich durch Drücken des Hotkeys verändert, doch die Preisänderung (Tick), die den Indikator neu zeichnet, ist noch nicht passiert).

Zusätzlich müssen wir jedoch auch zuvor explizit die OnInit() Funktion aufrufen, da wir den Kurznamen des Indikators, der angezeigt wird, wenn der Cursor über der Zeile steht,verändern mussten. Nach der ersten MA-Berechnung wir der Aktualisierte TGV auf 1 gesetzt, was den Weg zur optimierten Indikatorberechnung freimacht - bis man wieder keine Indikatorparameter spontan verändern möchte.


OnChartEvent() Handler

Unten steht ein einfacher Code des OnChartEvent() Handlers:

void OnChartEvent( const int          id,
                   const long    &lparam,
                   const double  &dparam,
                   const string  &sparam )
{
   if( id == CHARTEVENT_KEYDOWN )
      switch( lparam )
      {
         case( KEY_TAB          ):  changeTerminalGlobalVar( "MA_Method",  1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         
         case( KEY_UP           ):  changeTerminalGlobalVar( "MA_Period",  1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         case( KEY_DOWN         ):  changeTerminalGlobalVar( "MA_Period", -1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         
         case( KEY_NUMLOCK_UP   ):  changeTerminalGlobalVar( "MA_Shift",   1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
         case( KEY_NUMLOCK_DOWN ):  changeTerminalGlobalVar( "MA_Shift",  -1 ); 
                                    GlobalVariableSet( "Updated",  0 );
                                    Updated = 0;
                                    break;
      }
      
      return;
}//+------------------------------------------------------------------+      

Der Handler funktioniert so: Man drückt den Hotkey, dessen virtueller Code festgelegt ist, sodass dann die changeTerminalGlobalVar() Zusatzfunktion gestartet wird und den gewünschten TGV korrekt verändert. Danach wird die Flagge Aktualisiert auf Null zurückgesetzt, und wartet auf die Preisänderung, die OnCalculate() startet und den Indikator "von Anfang an" neu zeichnet.


Zusatzfunktion, die den TGV "korrekt" verändert

Und schließlich der im OnChartEvent() Handler verwendete Code der changeTerminalGlobalVar() Funktion:

void changeTerminalGlobalVar( string name, int dir = 0 )
{
   int var = GlobalVariableGet( name );
   int newparam = var + dir;
   if( name == "MA_Period" )
   {
      if( newparam > 0 )       /// Possible period is valid for MA
      {
         GlobalVariableSet( name, newparam ); 
         MA_Period = newparam;     /// Don't forget to change the global variable    
      }   
      else                       /// we do not change the period, because MA period is equal to 1 minimum
      {   
         GlobalVariableSet( name, 1 );     
         MA_Period = 1;     /// Don't forget to change the global variable 
      }   
   }   
      
   if( name == "MA_Method" )    /// Here when you call the 'dir' it is always equal to 1, the dir value is not important    
   {
      newparam = ( var + 1 ) % 4;
      GlobalVariableSet( name, newparam );  
      MA_Method = newparam;
   }      

   if( name == "MA_Shift" )
   {
      GlobalVariableSet( name, newparam );     
      MA_Shift = newparam;
   }      

   ChartRedraw( );
   return;
}//+------------------------------------------------------------------+

Der Hauptzweck dieser Funktion ist, die korrekte Berechnung neuer MA Parameter unter Berücksichtigung "physikalischer Einschränkungen". Offensichtlich können wir den MA-Zeitraum nicht kleiner als 1 machen, die MA Verschiebung kann zufällig sein, doch der MA-Typ ist die Zahl von 0 - 3, entsprechend einer abhängigen Mitgliedsnummer in der ENUM_MA_METHOD Aufzählung.

 

Prüfung. Funktioniert, kriegt aber nur "einen Dreier". Was tun?

Okay, wenden wir unseren Indikator auf den Chart an und drücken zuerst einmal sporadisch die Hotkeys, die die MA-Parameter verändern. Prima, alles funktioniert korrekt. Doch eines ist unangenehm: die TGVs ändern sich sofort (das kann man durch Aufruf des TGV mit Hilfe der F3 Taste herausfinden), doch die MAs zeichnen sich nicht sofort neu, sondern nur beim Auftreten einer Kursänderung. Bei einer Sitzung bei vollem Tagesgeschäft mit einem aktiven Kursänderungs-Flow bemerkt man die Verzögerung kaum. Doch wenn das nachts passiert, wenn alles ruhig ist, dann warten wir auf die neue Zeichnung durchaus einige Minuten. Also was ist los?

Mn sagt ja, man kriegt nur das zurück was man schreibt. Vor dem Aufbau von 245 in den Indikatoren gab es nur einen "Einstiegspunkt" - die OnCalculate() Funktion. Ich spreche natürlich nicht von den OnInit() und OnDeinit() Funktionen, die die allerersten Berechnungen, Initialisierungen und den Abschluss des Indikators liefern. Jetzt haben wir mehrere Einstiegspunkte und alle sind mit den neuen Timer und ChartEvent Ereignissen verknüpft.

Doch die neuen Handler machen nur das, was sie umfassen und hängen formal nicht mit dem OnCalculate() Handler zusammen. Was machen wir also mit unserem "außerirdischen" OnChartEvent() Handler, damit er "richtig" funktioniert, also die Neuzeichnung des MA sofort erlaubt?

Es gibt hier im Allgemeinen verschieden Möglichkeiten diese Vorassuetzung zu implementieren:

 

"Matryoshka" funktioniert!

Und es zeigt sich, dass wir das Wichtige bereits gemacht haben. Unten steht ein sehr einfacher Code der Funktion. Sie können einfach ihren Aufruf vor dem return Operator des OnChartEvent() Handlers einfügen.

int OnCalculate_Void()
{
   const int rates_total = Bars( _Symbol, PERIOD_CURRENT );
   CopyClose( _Symbol, PERIOD_CURRENT, 0, rates_total, _price );
   OnCalculate( rates_total, 0, 0, _price );
   return( 1 );
}//+------------------------------------------------------------------+

Nach der Erstellung des Indikators und seiner Anwendung auf den Chart, sehen wir, dass der Code generell schnell und unabhängig von ankommenden Kursänderungen funktioniert.

Der Nachteil dieser Implementierung ist, dass exakt die Schlusskurse in das price[] Array kopiert werden. Ggf. kann die CopyClose() Funktion mit dem was wir wollen ersetzt werden, indem im "Einstellungen"-Tab des Dialogfensters der Indikatoreigenschaften "Anwenden auf" eingerichtet wird. Ist der aktuelle Kurs einfach (Open, High, Low, Close), haben wir bereits die entsprechende CopyXXXX() Funktion. Bei komplizierteren Kursen (durchschnittlich, typisch oder gewichtet) müssen wir das Array anders berechnen.

Ich bin mir nicht sicher, ob wir die CopyClose() Funktion, die die ganze History des Arrays kopiert, nicht doch brauchen. Doch andererseits ist diese Funktion schnell genug, wenn die History nicht zu überladen ist. Eine Prüfung des Indikators auf EURUSD H1 mit History vor 1999 (ca. 700.000 Balken) ergab, dass der Indikator mit Berechnungen handelt und keinerlei Verlangsamung zeigt. Bei solchen Histories liegen mögliche Verlangsamungen nicht an der CopyXXXX() Funktion, sondern eher daran, dass man kompliziertere Indikator-Neuberechnungen vom Anfang der History braucht (das ist obligatorisch).


Einige Erkenntnisse und Schlussbemerkung

Was ist nun besser - eine Indikatordateioder das"Indikator + Advisor" Pärchen?

Die Antwort ist gar nicht so einfach. Einerseits ist es gut, wenn man eine Indikatordatei hat, da alle Funktionen, einschließlich Ereignis-Handler, sich an einem Ort befinden.

Andererseits nehmen wir mal an, dass 3 oder 4 Indikatoren zusammen mit einem Expert Advisor auf ein Chart angewandt werden- was gar nicht so selten vorkommt. Und nehmen wir mal weiter an, dass jeder Indikator seinen eigenen Ereignis-Handler hat, zusätzlich zu Standard OnCalculate(). Zur Vermeidung von Verwirrungen bei der Verarbeitung von Ereignissen in diesem "Durcheinander", ist es sinnvoller alle Ereignis-Handler, die nicht in den Indikatoren zugelassen sind, an einem Platz zu haben - dem Expert Advisor.

Lange haben Software-Entwickler dazu tendiert, uns die Möglichkeit der Verarbeitung von Ereignissen im Indikator anzubieten: von den nicht öffentlichen Beta-Versionen 09.09.09 (wo der Indikator als "reine Berechnungs- & mathematische Einheit" angesehen wird und nicht durch irgendwelche Features verunreinigt werden darf, die die Berechnungsgeschwindigkeit hemmen) sind genau 5 Monate vergangen. Und genauso muss die "Reinheit des Gedankens" leiden - und jetzt kommen wirklich einige voll chaotische Programmierer-Spinnereien auf den Markt. Doch die Ausgewogenheit liegt immer zwischen einem reinen, jedoch eingeschränkten Gedanken und einer nicht so sauberen, jedoch leistungsstärkeren Fähigkeit.

Im September/Oktober 2009, als die Herstellungszahl der MT5 Beta-Version noch keine 200 erreicht hatte, habe ich einen Code für das "Expert Adviser + Indikator" Pärchen geschrieben und auch Fehler beseitigt, mit dem man MA-Parameter spontan verwalten konnte - jedoch mit "einem Dreier" als Note: er wurde nur nach Kursänderungseingang aktualisiert, aber nicht sofort. Damals war das Pärchen die einzig mögliche Lösung, und jetzt ist es eher unwahrscheinlich, dass sich noch jemand dafür interessiert.

Damals wusste ich nicht, wie ich die Indikator-Funktionalität auf einen "Zweier" bringen sollte, d.h. so wie in der jüngsten Version präsentiert. Ich freue mich daher jetzt sehr, allen Interessierten jetzt eine bequemere Lösung anbieten zu können.


"Matryoshka" Videovorführung

Im Anhang finden Sie mein Kurzvideo, das zeigt, wie unsere Lösung funktioniert. Die weiche Änderung der MA-Kurve (nur der Zeitraum ändert sich - zuerst wächst er an, dann nimmt er ab) ist irgendwie schon verblüffend. Das ist Matryoshka (ähnlich wie die berühmten russischen Babuschkas).


Solche Tricks sind natürlich nur sinnvoll, wenn die Neuberechnung des Indikators an sich "von Null an" nicht zu viel Zeit in Anspruch nimmt. Der in diesem Indikator enthaltene, einfache MA erfüllt alle Voraussetzungen


Noch ein gerissener Aspekt

Sie erinnern sich, dass die ehemaligen externen Parameter des Indikators jetzt die globalen Variablen des Terminals sind (TGV), die Sie durch Drücken von F3 aufrufen können? Angenommen, Sie haben das Dialogfenster Globale Variablen geöffnet und einen der TGVs geändert (z.B. den MA Zeitraum. Dann rechnen Sie damit, dass diese Veränderung sofort im Indikator auf dem Chart abgebildet wird.

Im Terminal befindet sich aktuell kein Ereignis, das der vom Benutzer durchgeführten Bearbeitung der TGV entspricht (z.B. ChartEvent_GLOBALVAR_ENDEDIT). Ich denke, dass wir auch nicht die TGV-Veränderung im Dialogfenster Globale Variablen deaktivieren können. Deshalb können wir hier nicht mit irgendeinem Ereignis außer der Kursänderung rechnen. Was passiert aber in Echt?

Wenn Sie keine Taste Ihrer Tastatur drücken, dann ist die Aktualisierung selbst bei der nächsten Kursänderung "falsch": die Aktualisierte Variable wurde nicht auf "0" gesetzt und daher wird nur die "optimierte" Berechnung des Indikators ausgeführt (entsprechend dem vorherigen Wert der geänderten TGV). Um hier die Gerechtigkeit wieder herzustellen, kann ich nur zu einem Schritt raten: nach der Bearbeitung von TGV sollten Sie mindestens einmal den Hotkey drücken, der die TGV verändert, Aktualisiert auf "0" setzt und eine komplette Neuberechnung des Indikators in Gang bringt.

Jeder mögliche Benutzer und Entwickler sollte stets daran denken.


Angehängte Dateien: Quellcode und Video

Zu guter Letzt hänge ich die Quellcode-Dateien an. Erklärung:

  1. Custom Moving Average.mq5 - MA-Quellcode-Datei, die bei MT5 mit dabei ist.
  2. MyMA_ind_with_ChartEvent.mq5 - erste Implementierung ("Note 3"): Indikator-Aktualisierungen nur nach ankommender Kursänderung.
  3. MyMA_ind_with_ChartEvent_Matryoshka.mq5 - zweite Version (vielleicht "Note 2"): der Indikator aktualisiert sich sofort, ohne auf die Ankunft einer Kursänderung zu warten.