English Русский 中文 Español 日本語 Português
Wie ein Marktindikator für alle nicht standarten Charts geschrieben werden soll

Wie ein Marktindikator für alle nicht standarten Charts geschrieben werden soll

MetaTrader 4Beispiele | 24 Mai 2016, 16:26
1 653 0
Vladimir Karputov
Vladimir Karputov

Inhaltsverzeichnis

 

Von japanischen Kerzen bis Renko-Charts

Bis heute ist die populärste Form von Charts unter den Händlern - ist japanische Kerzen, welche die Einschätzung der aktuellen Situation auf dem Markt leichter machen. Die Kerzen-Charts bieten eine gute und klare Vorstellung über die Preisentwicklung in der Zeit an, in der eine Kerze gefasst wird. Aber einige Händler finden als Nachteil das, dass die Charts eine Zeit-Komponente enthalten, und bevorzugen, nur mit Preisschwankungen zu tun zu haben. So entstanden Charts wie "Tic-Tac-Toe", "Renko", "Kagi", "Range Bars", Equivolume Charts usw. 

Mit Hilfe der Offline-Charts in der Programmiersprache MQL4 und nach einer wenigen Erfahrung können Sie all diese Charts in MetaTrader 4 bekommen. Es gibt eine Möglichkeit, ihre Charts mit eigenen synthetischen Instrumenten (die kein Broker hat oder es gibt nicht in der Natur) und nicht standarden Timeframes zu erstellen, die in der Plattform nicht zu Verfügung stehen. Die meisten Entwickler verwenden dafür die DLL-Aufrufe und komplizierte Systeme. In diesem Artikel zeigen wir Ihnen, wie Indikatoren wie "two in one" von beliebiger Komplexität erstellt werden können,  die nicht nur keine DLL-Kenntnisse erfordern, sondern können auch leicht im Markt in der Form vom Produkt veröffentlicht werden, da sie völlig unabhängig und schon bereite Anwendungen sind.

Die Beispiele für diesen Artikel können Sie finden und in Form von kostenlosen Anwendungen auf dem Markt herunterladen:

  • USDx Chart MT4 - Dieser Indikator baut ein autonomes Chart, in dem statt der gewöhnlichen Bars und Kerzen, ein Dollar-Index gezeichnet wird. 
  • Renko Chart MT4 - Dieser Indikator baut ein autonomes Renko-Chart, in dem alle Bars in Form von Renko "Ziegel" gebaut werden. Die "Kerzen" selbst haben keine Schaten, und die Größe eines.

Der Indikator, der ein autonomos Chart des nicht standarten Symbols und/oder der Periode erstellt, hat keine Notwendigkeit, die DLL aufzurufen - alles wird mit MQL4 gelöst. Dafür verwenden Sie das folgende Arbeitsschema: der gleiche Indikator kann sowie auf einem Online-Chart, auch auf einem Offline-Chart arbeiten. Dabei wird dieser Indikator seine Funktionalität ändern, je nachdem, wo es funktioniert: auf einem Online-Chart oder auf einem Offline-Chart.

Auf einem Online-Chart funktioniert der Indikator in Servicemodus: sammelt und komponiert Notierungen, erstellt ein autonomes Chart (sowie einer standarden auch einer nicht standarden Periode), führt seine Aktualisierung durch. Auf einem autonomen Chart funktioniert dieser Indikator genauso wie alle andere - analysiert die Notierungen und baut verschiedene Objekte und Figuren. Beachten Sie, dass alle Beispiele im Artikel auf einem neuen Regelskript PeriodConverter.mq4 basiert sind.


1. Der Indikator "IndCreateOffline", der ein autonomes Chart erstellen wird

Wir nennen es der Indikator IndCreateOffline. Der Indikator ist nur ein Eingangsparameter, der für die Periode des autonomen Charts verantwortlich ist. Lassen Sie uns darüber etwas unten sprechen. Der Indikator IndCreateOffline wird nur eine Aufgabe erfüllen — die Datei *.hst zu erstellen und ein autonomes Chart eröffnen.

Im Indikator werden nur zwei Hauptfunktionen ausgeführt. Die erste wird einmal verwendet - in der wird die Datei *.hst erstellt und wird der Titel der Datei gestaltet. Die zweite Funktion ist für die Aufnahme der Notierungen in der Datei *.hst.

1.1. Die Korrektur des "Kopfs" vom Indikator

Auch wenn man es direkt ungern tut, müssen Sie zuerst die Datei-Beschreibung hinzufügen.  Im Laufe der Zeit wird es helfen, sich daran zu erinnern, was der Zweck unseres Indikators war.

#property version   "1.00"
#property description "The indicator creates an offline chart"
#property strict
#property indicator_chart_window

Natürlich ist es theoretisch möglich, ein autonomes Chart mit jeder Periode zu erstellen. Allerdings werden wir die Fantasie der Händler beschränken, falls jemand sich plötzlich für die Periode von 10000000 interessiert. Wir führen eine Auflistung ein, in der Sie alle vier Optionen wählen können - zwei, drei, vier, oder sechs Minuten:

#property indicator_chart_window
//+------------------------------------------------------------------+
//| Enumerations of periods offline chart                            |
//+------------------------------------------------------------------+
enum ENUM_OFF_TIMEFRAMES
  {
   M2=2,                      // period M2
   M3=3,                      // period M3
   M4=4,                      // period M4
   M6=6,                      // period M6
  };
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |

Der nächste Schritt - die Zugabe der globalen Variablen zum Kopf (nicht mit den globalen Variablen des Terminals verwechseln):

   M6=6,                      // period M6
  };
//--- input parameter
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     crash=false;         // false -> error in the code
int      HandleHistory=-1;    // handle for the opened "*.hst" file
datetime time0;               //
ulong    last_fpos=0;         //
long     last_volume=0;       //
int      periodseconds;       //
int      i_period;            //
MqlRates rate;                //
long     ChartOffID=-1;       // ID of the offline chart
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |

1.2. Die Kontrolle des Typs vom Chart

Unser Indikator IndCreateOffline  sollte nur auf dem Online-Chart ausgeführt werden, weil es die einzige Möglichkeit ist, die Richtigkeit der Daten zu gewährleisten. Die Art des Charts, auf dem der Indikator eingesetzt ist, können Sie mit Hilfe der Eigenschaften CHART_IS_OFFLINE bestimmen. Lassen Sie uns die Funktion IsOffline nach OnCalculate() hinzufügen, die der Typ des Charts zurückliefert:

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+ 
//| The function checks offline mode of the chart                    | 
//+------------------------------------------------------------------+ 
bool IsOffline(const long chart_ID=0)
  {
   bool offline=ChartGetInteger(chart_ID,CHART_IS_OFFLINE);
   return(offline);
  }
//+------------------------------------------------------------------+

Den Aufruf der Funktion IsOffline führen wir in OnInit() durch:

int OnInit()
  {
   if(!IsOffline(ChartID()) && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//---
   return(INIT_SUCCEEDED);
  }

Beachten Sie, wenn der Indikator auf einem Online-Chart (IsOffline(ChartID())==false) ist, deren Periode nicht gleich PERIOD_M1 ist, dann wird der Variable crash  in diesem Fall der true zugeordnet. Was bringt es uns: bei crash==true wird der Indikator im Online-Chart bleiben, aber es wird nichts machen. In diesem Fall erhalten wir im Titel "Experten" eine Nachricht:

IndCreateOffline EURUSD,H1: The period on the online chart must be "M1"!

Allerdings wird der Indikator auf dem Online-Chart bleiben und wartet so lange, bis der Benutzer die Periode um PERIOD_M1 ersetzt.

Warum ist die Periode PERIOD_M1 für uns so wichtig? Hier sin zwei Momente wichtig.

Moment №1: Die Bildung der letzten Periode des autonomen Charts. Betrachten wir das Beispiel des Skripts PeriodConverter.mq4,  wo die endgültige Periode des autonomen Charts berechnet wird:

#property show_inputs
input int InpPeriodMultiplier=3; // Period multiplier factor
int       ExtHandle=-1;
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   datetime time0;
   ulong    last_fpos=0;
   long     last_volume=0;
   int      i,start_pos,periodseconds;
   int      cnt=0;
//---- History header
   int      file_version=401;
   string   c_copyright;
   string   c_symbol=Symbol();
   int      i_period=Period()*InpPeriodMultiplier;
   int      i_digits=Digits;
   int      i_unused[13];
   MqlRates rate;
//---  

Bei diesen Eingangsparametern wird die Periode des Online-Charts, zu dem der Skript hinzugefügt wird, der Periode "PERIOD_M3" gleich sein. Beim Wert  InpPeriodMultiplier=3 rechnen wir mit einem autonomen Chart mit einer Periode 3. In Wirklichkeit erhalten wir eine Periode vom autonomen Chart, das gleich 9 ist:

   i_period=Period()*InpPeriodMultiplier=3*3=9

Nach dieser Weise, um die Periode 3 zu bekommen, muss man ein Online-Chart mit der Periode PERIOD_M1 verwenden. 

Moment №2: Die Einträge der History in die Datei. Bei der Bildung der History-Datei werden Daten aus Arrays-Timeframes Open[], Low[], High[], Volume[], Time[] verwendet. Sie verwenden alle die Daten des aktuellen Charts nach aktueller Periode. Und was kann genauer sein als jede künstliche Bildung der Periode, die auf der Grundlage von Daten aus dem Chart mit der Periode "PERIOD_M1" ist?  richtig: es ist nur das Chart mit der Periode PERIOD_M1. 

Die Änderungen des Indikators, der oben beschrieben wurde, kann man in der Datei IndCreateOfflineStep1.mq4 sehen.

1.3. Die Erstellungsfunktion des Titels von der History-Datei

Die Erstellungsfunktion des Titels von der History-Datei nennen wir  CreateHeader():

bool CreateHeader(
   const ENUM_OFF_TIMEFRAMES offline_period // period of offline chart
   );

Parameter

offline_period

[in]  Die Periode des autonomen Charts. 

Rückgabewert

true, wenn die History-Datei erfolgreich erstellt wurde, und false — falls ein Fehler auftritt.

Eine vollständige Liste der Funktionen:

//+------------------------------------------------------------------+ 
//| The function checks offline mode of the chart                    | 
//+------------------------------------------------------------------+ 
bool IsOffline(const long chart_ID=0)
  {
   bool offline=ChartGetInteger(chart_ID,CHART_IS_OFFLINE);
   return(offline);
  }
//+------------------------------------------------------------------+
//| Create history header                                            |
//+------------------------------------------------------------------+
bool CreateHeader(const ENUM_OFF_TIMEFRAMES offline_period)
  {
//---- History header
   int      file_version=401;
   string   c_copyright;
   string   c_symbol=Symbol();
   i_period=Period()*offline_period;
   int      i_digits=Digits;
   int      i_unused[13];
//---  
   ResetLastError();
   HandleHistory=FileOpenHistory(c_symbol+(string)i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_ANSI);
   if(HandleHistory<0)
     {
      Print("Error open ",c_symbol+(string)i_period,".hst file ",GetLastError());
      return(false);
     }
   c_copyright="(C)opyright 2003, MetaQuotes Software Corp.";
   ArrayInitialize(i_unused,0);
//--- write history file header
   FileWriteInteger(HandleHistory,file_version,LONG_VALUE);
   FileWriteString(HandleHistory,c_copyright,64);
   FileWriteString(HandleHistory,c_symbol,12);
   FileWriteInteger(HandleHistory,i_period,LONG_VALUE);
   FileWriteInteger(HandleHistory,i_digits,LONG_VALUE);
   FileWriteInteger(HandleHistory,0,LONG_VALUE);
   FileWriteInteger(HandleHistory,0,LONG_VALUE);
   FileWriteArray(HandleHistory,i_unused,0,13);
   return(true);
  }
//+------------------------------------------------------------------+

Neben der Erstellung der History-Datei "*.hst" und der Erstellung des Titels der Datei (die einigen ersten Zeilen) werden in der Funktion CreateHeader() die HandleHistory der erstellten Datei in der Variable HandleHistory gespeichert.

1.4. Der erste Eintrag der History in die Datei *.hst

Nachdem Sie die Datei * .hst erstellen und füllen seinen Titel aus, muss man die erste Notierungen-History in die Datei durchführen - das heißt, man muss die ganze Datei mit der History ausfüllen, die im Moment aktuell ist. Für den ersten Eintrag in der History-Datei wird die Funktion FirstWriteHistory() verantwortlich sein (man kann es im Indikator gucken).

Wann tritt eine Situation "der erste Eintrag der History" in unserem Indikator auf? Es ist logisch, anzunehmen, dass dies beim ersten Laden des Indikators geschieht.

Beim ersten Laden kann (und sollte) der Indikator nach der Wert der Variable prev_calculated kontrolliert werden. Die Bedeutung prev_calculated == 0 zeigt das, was eben beim ersten Laden passiert. Aber zur gleichen Zeit prev_calculated==0 kann immer noch bedeuten, dass das Laden nicht der erste ist, aber es wurde die History geladen. Was soll man beim Laden der History tun, werden wir bei der Korrektur des Codes OnCalculate() besprechen.

1.5. Die Aufnahme der Online-Notierungen

Nach der Erstellung und Ausfüllung des Titels der Datei *.hst und dem ersten Eintrag der History kann man mit Aufnahmen der Online-Notierungen anfangen. Dafür ist die Funktion CollectTicks() verantwortlich (man kann es im Indikator gucken).

Die Änderungen des Indikators, der oben beschrieben wurde, kann man in der Datei IndCreateOfflineStep2.mq4 sehen. 

1.6. Die Korrektur der Funktion OnCalculate()

Führen wir die Variable first_start ein. Sie wird den Wert true nach dem ersten Start gespeichert haben. Anderes gesagt, bei first_start==true werden wir wissen, dass unser Indikator noch nicht die Datei *.hst erstellt hat.

//--- input parameter
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     first_start=true;    // true -> it's first start
bool     crash=false;         // false -> error in the code

Der Arbeitsalgorithmus der Funktion OnCalculate() in unserem Indikator:

algorithm

in Abb. 1. Der Algorithmus der Funktion OnCalculate() 

Die endgültige Version des Indikators kann man in der Datei IndCreateOffline.mq4 sehen. 

 

2. Der Inkikatorsstart auf einem autonomen Chart

2.1. Die Aktualisierung des autonomen Charts mit ChartSetSymbolPeriod() 

Wichtiger Hinweis: Für die Aktualisierung des autonomen Charts, muss man statt ChartRedraw() ChartSetSymbolPeriod() mit den aktuellen Parametern aufrufen. Der Aufruf ChartSetSymbolPeriod() wird in der Funktion CollectTicks() mit Intervallen nicht häufiger als einmal in 3 Sekunden ausgeführt.

Sie sollten auch eine Einschränkung der Arbeit eines autonomen Charts berücksichtigen: Der Indikator, der zum autonomen Chart hinzugefügt ist, wird bei jeder seinen Aktualisierung OnCalculate() prev_calculated==0 erhalten. Man muss sich diese Besonderheit merken. Im Folgenden wird ein Verfahren gezeigt, dass diese Besonderheit berücksichtigt. 

2.2. Wartungsmodus 

Was wir wollen: der gleiche Indikator muss genauso gut sowie auf einem Online-Chart, auch auf einem Offline-Chart funktionieren. Dabei ändert sich das Verhalten des Indikators, je nachdem, auf welchem Chart - online oder offline es ist. Wenn der Indikator auf einem online Chart ist, ist seine Funktionalität sehr ähnlich mit dem Indikator  IndCreateOffline.mq4, der wir oben betrachtet haben. Aber im autonomen Modus beginnt er so arbeiten wie ein normaler Indikator.

Also, Lassen Sie uns unseren Indikator IndMACDDoubleDuty nennen — der wird auf Basis des oben betrachteten IndCreateOffline.mq4  gebaut und des standarten Indikators MACD.mq4. Bereiten Sie bitte einen Entwurf des künftigen Indikators vor: in MetaEditor'e öffnen Sie die Datei IndCreateOffline.mq4, weiter das Menü "Datei" -> "Speichern unter..." und Schreiben Sie den Namen des Indikators IndMACDDoubleDuty.mq4

Fügen wir direkt die Beschreibung des Indikators hinzu - unser Indikator erstellt nun ein autonomes Chart, und hat einen doppelten Zweck:

//+------------------------------------------------------------------+
//|                                            IndMACDDoubleDuty.mq4 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "The indicator creates an offline chart."
#property description "May work on an online chart and off-line graph."
#property strict
#property indicator_chart_window

Im "Servicemodus" wird der Indikator funktionieren, wenn er mit einer Periode PERIOD_M1 auf dem Online-Chart ausgeführt wurde. In diesem Modus führt er die folgenden Funktionen aus:

  • erstellt de Datei *.hst  füllt ihn aus;
  • Öffnet ein autonomes Chart basierennd aud der Datei *.hst ;
  • Bei dem Empfang der Notierungen schreibt die Hystory in der Datei*.hst und aktualisiert das autonome Chart.
Für die Ausfüllung, in welchem Modus der Indikator funkioniert, fügen wir die Variable mode_offline ein:
//--- input parameter
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     first_start=true;    // true -> it's first start
bool     crash=false;         // false -> error in the code
bool     mode_offline=true;   // true -> on the offline chart
int      HandleHistory=-1;    // handle for the opened "*.hst" file
datetime time0;               //

Dementsprechend ändert sich OnInit():

int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//---
   return(INIT_SUCCEEDED);
  }

Fügen wir die Änderungen in OnCalculate() ein:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(crash)
      return(rates_total);

   if(!mode_offline) // work in the online chart 
     {
      if(prev_calculated==0 && first_start) // first start
        {
         .
         .
         .
         first_start=false;
        }
      //---
      CollectTicks();
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

In dieser Phase wird der Indikator beim Hinzufügen zum Online-Chart mit einer Periode PERIOD_M1 in den "Servicemodus" umgestellt und erstellt ein autonomes Chart.

Die Änderungen des Indikators, der oben beschrieben wurde, kann man in der Datei IndMACDDoubleDutyStep1.mq4 sehen. 

2.3. Das Kopieren des Indikators in ein autonomes Chart

Zum Indikator IndMACDDoubleDuty  hinzufügen neue Funktionen: jetzt im "Servicemodus" - muss der Indikator seine Kopie zum erstellten autonomen Chart übergeben. Dabei hilft uns solche Funktionen: ChartSaveTemplate und ChartApplyTemplate. Jetzt wird der Algorithmus OnCalcalculate() so aussehen:

algorithm_2

in Abb. 2. Der Algorithmus der Funktion OnCalculate()  

Lassen Sie uns im Code OnCalculate() eine zusätzliche Funktion hinzufügen:

         else
            Print(__FUNCTION__,"Opening offline chart id=",ChartOffID);
         ResetLastError();
         if(!ChartSaveTemplate(0,"IndMACDDoubleDuty"))
           {
            Print("Error save template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
         ResetLastError();
         if(!ChartApplyTemplate(ChartOffID,"IndMACDDoubleDuty"))
           {
            Print("Error apply template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
        }
      //---
      if(prev_calculated==0 && !first_start) // a deeper history downloaded or history blanks filled

Nun wird unser Indikator beim Hinzufügen zum Online-Chart mit einer Periode PERIOD_M1 in den "Servicemodus" umgestellt und erstellt ein autonomes Chart.

Die Änderungen des Indikators, der oben beschrieben wurde, kann man in der Datei IndMACDDoubleDutyStep2.mq4 sehen.  

2.4. Die Integration in MACD.mq4

Unser Indikator stellt sich schon selbst auf auf dem autonomen Chart, aber zeigt immer noch nichts an, und berechnet nichts. Korrigieren wir es: Lassen Sie unser Indikator in standarte MACD.mq4 integrieren. 

Setzen wir zuerst die Eingangsparameter des Indikators MACD.mq4 MACD.mq4 in unserem Code ein:

#property strict

#include <MovingAverages.mqh>

//--- MACD indicator settings
#property  indicator_separate_window
#property  indicator_buffers 2
#property  indicator_color1  Silver
#property  indicator_color2  Red
#property  indicator_width1  2
//--- indicator parameters
input int InpFastEMA=12;   // Fast EMA Period
input int InpSlowEMA=26;   // Slow EMA Period
input int InpSignalSMA=9;  // Signal SMA Period
//--- indicator buffers
double    ExtMacdBuffer[];
double    ExtSignalBuffer[];
//--- right input parameters flag
bool      ExtParameters=false;
//+------------------------------------------------------------------+
//| Enumerations of periods offline chart                            |
//+------------------------------------------------------------------+

Dann fügen wir den Code in OnInit() ein. Es sollte angemerkt werden, dass die Parameter-Initialisierung des MACD-Indikators unter allen Bedingungen laufen soll - sowohl in Offline und Online-Charts, als auch in dem Fall, wenn die Periode des Online-Charts sich von PERIOD_M1 unterscheidet:

int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//--- init MACD indicator
   IndicatorDigits(Digits+1);
//--- drawing settings
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexStyle(1,DRAW_LINE);
   SetIndexDrawBegin(1,InpSignalSMA);
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtMacdBuffer);
   SetIndexBuffer(1,ExtSignalBuffer);
//--- name for DataWindow and indicator subwindow label
   IndicatorShortName("MACD("+IntegerToString(InpFastEMA)+","+IntegerToString(InpSlowEMA)+","+IntegerToString(InpSignalSMA)+")");
   SetIndexLabel(0,"MACD");
   SetIndexLabel(1,"Signal");
//--- check for input parameters
   if(InpFastEMA<=1 || InpSlowEMA<=1 || InpSignalSMA<=1 || InpFastEMA>=InpSlowEMA)
     {
      Print("Wrong input parameters");
      ExtParameters=false;
      return(INIT_FAILED);
     }
   else
      ExtParameters=true;
//---
   return(INIT_SUCCEEDED);
  }

Im nächsten Schritt ändern wir etwas den Code am Anfang des OnCalculate(). Wenn wir auf dem Online-Chart sind und seine Periode nicht PERIOD_M1 gleich ist, müssen wir die Berechnung der MACD-Parameter ermöglichen. Es war so:

const long &volume[],
                const int &spread[])
  {
//---
   if(crash)
      return(rates_total);

   if(!mode_offline) // work in the online chart 
     {
      if(prev_calculated==0 && first_start) // first start
        {

 und es wird so:

const long &volume[],
                const int &spread[])
  {
//---
   if(!mode_offline && !crash) // work in the online chart 
     {
      if(prev_calculated==0 && first_start) // first start
        {

Dann fügen Sie den Berechnungscode der Parameter des MACD-Indikators am Ende OnCalculate() ein:

         FirstWriteHistory(ExtOffPeriod);
         first_start=false;
        }
      //---
      CollectTicks();
     }
//---
   int i,limit;
//---
   if(rates_total<=InpSignalSMA || !ExtParameters)
      return(0);
//--- last counted bar will be recounted
   limit=rates_total-prev_calculated;
   if(prev_calculated>0)
      limit++;
//--- macd counted in the 1-st buffer
   for(i=0; i<limit; i++)
      ExtMacdBuffer[i]=iMA(NULL,0,InpFastEMA,0,MODE_EMA,PRICE_CLOSE,i)-
                       iMA(NULL,0,InpSlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//--- signal line counted in the 2-nd buffer
   SimpleMAOnBuffer(rates_total,prev_calculated,0,InpSignalSMA,ExtMacdBuffer,ExtSignalBuffer);
//--- return value of prev_calculated for next call
   return(rates_total);
  }

2.5. Die ökonomische Neuberechnung des Indikators auf dem autonomen Chart

Ich möchte Sie an die Nuance erinnern, die am Anfang des 2. Abschnitts beschrieben wurde:

Sie sollten auch eine Einschränkung der Arbeit eines autonomen Charts berücksichtigen: Der Indikator, der zum autonomen Chart hinzugefügt ist, wird bei jeder seinen Aktualisierung OnCalculate() prev_calculated==0 erhalten. Man muss sich diese Besonderheit merken. Im Folgenden wird ein Verfahren gezeigt, dass diese Besonderheit berücksichtigt. 

Und noch eine Erinnerung daran: dass der Wert prev_calculated==0 in OnCalculate() zwei Situationen bedeuten kann:

  1. es ist der erste Start des Indikators;
  2. oder die History wurde geladen.

In beiden Fällen muss der Indikator alle Bars auf dem Chart neuberechnen. Wenn es notwendig ist, nur einmal durchzuführen (beim Laden) - ist das normal. Aber auf dem autonomen Chart werden wir prev_calculated == 0 bei jeder Aktualisierung erhalten (etwa alle 2-3 Sekunden), und der Indikator berechnet alle Bars neu. Das ist ein sehr verschwenderischer Verbrauch der Ressourcen. Von daher benutzen wir einen kleinen Trick: Wenn der Indikator auf dem autonomen Chart ist, wird er die Anzahl der Bars (variable rates_total) und die Zeit des ganz rechten Bars auf dem Chart vergleichen und speichern.

Schritt 1: Am Anfang OnCalculate() erklären wir zwei statischen Variablen  und eine pseudo-Variable:

                const long &volume[],
                const int &spread[])
  {
//---
   static int static_rates_total=0;
   static datetime static_time_close=0;
   int pseudo_prev_calculated=prev_calculated;
//---
   if(!mode_offline && !crash) // work in the online chart 
     {

Schritt 2: Im Codeblock der Werteberechnung des Indikators ersetzen wir die Variable prev_calculated um pseudo_prev_calculated:

      CollectTicks();
     }
//---
   int i,limit;
//---
   if(rates_total<=InpSignalSMA || !ExtParameters)
      return(0);
//--- last counted bar will be recounted
   limit=rates_total-pseudo_prev_calculated;
   if(pseudo_prev_calculated>0)
      limit++;
//--- macd counted in the 1-st buffer
   for(i=0; i<limit; i++)
      ExtMacdBuffer[i]=iMA(NULL,0,InpFastEMA,0,MODE_EMA,PRICE_CLOSE,i)-
                       iMA(NULL,0,InpSlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//--- signal line counted in the 2-nd buffer
   SimpleMAOnBuffer(rates_total,pseudo_prev_calculated,0,InpSignalSMA,ExtMacdBuffer,ExtSignalBuffer);
//---
   if(mode_offline) // work in the offline chart 

Schritt 3: Hier werden wir den Wert für die Pseudo-Variable berechnen, wenn der Indikator auf einem autonomen Chart arbeitet. 

      CollectTicks();
     }
//---
   if(mode_offline) // work in the offline chart 
     {
      if(time[0]>static_time_close) // new bar
        {
         if(static_time_close==0)
            pseudo_prev_calculated=0;
         else // search bar at which time[0]==static_time_close
           {
            for(int i=0;i<rates_total;i++)
              {
               if(time[i]==static_time_close)
                 {
                  pseudo_prev_calculated=rates_total-i;
                  break;
                 }
              }
           }
        }
      else
        {
         pseudo_prev_calculated=rates_total;
        }
      //---
      static_rates_total=rates_total;
      static_time_close=time[0];
     }
//---
   int i,limit;

Jetzt berechnet unser Indikator seine Werte auf einem autonomen Chart sparsam. Außerdem verarbeitet er in diesem Modus die Kommunikationsabrüche korrekt: er berechnet nach dem Bruch nur neuhinzugefügte Bars.

2.6. Das Laden der History. Das autonome Chart

Man muss nur entscheiden, was man tun soll, wenn ein Laden der History auf dem Online-Chart mit unserem Indikator stattgefunden hat(das prev_calculated == 0). Ich schlage vor, diese Situation durch die globalen Variablen zu lösen. Der Arbeitsalgorithmus ist in Folge: wenn wir prev_calculated==0 auf dem Online-Chart erhalten (egal, ob es der erste Start war, oder es ist nur das Laden der History), erstellen wir einfach eine globalr Variable. Der Indikator auf dem autonomen Chart überprüft das Vorhandensein dieser globalen Variable bei jeder Aktualisierung: wenn sie da ist (dann heisst es, auf dem Online.Chart gab es prev_calculated==0), dann wird der Indikator neuberechnet und er löscht die globale Variable.

Wir fügen zum Kopf des Indikators die Variable hinzu, in der der Name der zukünftigen globalen Variable vom Terminal gespeichert wird und in OnInit () erzeugen wir den Namen:

MqlRates rate;                //
long     ChartOffID=-1;       // ID of the offline chart
string   NameGlVariable="";   // name global variable
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
   NameGlVariable=Symbol()+(string)ExtOffPeriod;
//--- init MACD indicator
   IndicatorDigits(Digits+1);
//--- drawing settings

Fügen Wir den Code der globalen Variable des Terminals hinzu, wenn die Bedingung prev_calculated == 0 erfüllt ist und der Indikator auf dem Online-Chart ist:

         if(!ChartApplyTemplate(ChartOffID,"IndMACDDoubleDuty"))
           {
            Print("Error apply template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
         //---
         ResetLastError();
         if(GlobalVariableSet(NameGlVariable,0.0)==0) // creates a new global variable
           {
            Print("Failed to creates a new global variable ",GetLastError());
           }
        }
      //---
      if(prev_calculated==0 && !first_start) // a deeper history downloaded or history blanks filled
        {
         Print("a deeper history downloaded or history blanks filled. first_start=",first_start);
         if(CreateHeader(ExtOffPeriod))
            first_start=false;
         else
           {
            crash=true;
            return(rates_total);
           }
         //---
         FirstWriteHistory(ExtOffPeriod);
         first_start=false;
         //---
         ResetLastError();
         if(GlobalVariableSet(NameGlVariable,0.0)==0) // creates a new global variable
           {
            Print("Failed to creates a new global variable ",GetLastError());
           }
        }
      //---
      CollectTicks();

Und eine letzte Änderung: Die Überprüfung, ob eine globale Variable des Indikators auf dem autonomen Chart ist:

      else
        {
         pseudo_prev_calculated=rates_total;
        }
      //---
      if(GlobalVariableCheck(NameGlVariable))
        {
         pseudo_prev_calculated=0;
         GlobalVariableDel(NameGlVariable);
        }
      //---
      Print("rates_total=",rates_total,"; prev_calculated=",
            prev_calculated,"; pseudo_prev_calculated=",pseudo_prev_calculated);
      static_rates_total=rates_total;
      static_time_close=time[0];
     }
//---
   int i,limit;

Die endgültige Version des Indikators mit den letzten Änderungen: IndMACDDoubleDuty.mq4.  

 

3. Der Indikator, der Renko-Bars zeigt

Die Renko-Bars werden wir nach gleicher Technologie bauen, die in den Punkten  1 verwendet wurden. Der Indikator "IndCreateOffline", der ein autonomes Chart erstellen wird  und 2. Der Indikatorsstart auf einem autonomen Chart. Das heißt, schließlich erhalten wir ein autonomes Chart, aber in diesem Fall wird die Histoty-Datei *.hst die gleichen Bars enthalten — man nennt sie noch Ziegel. Die Größe der Ziegel wird in Einstellungen des Indikators eingestellt und wird in Punkten gemessen.

Renko-Bars  

in Abb. 3. Renko-Bars 

Bevor der Bau der Bars beginnt, muss man ein paar Regeln der Bildung der History-Datei *.hst für den Bau der Renko-Bars berücksichtigen.

3.1. Die Konstruktionsregeln der Range-Bars

Die Regel1: Der OHLC-Eintrag in der History-Datei *.hst muss korrekt sein, besonderes betrifft es High und Low. Ansonsten zeigt das Terminal den unkorrekten Eintrag an. Das Beispiel für einen korrekten und fehlerhaften Eintrag in der History-Datei des aufwärtigen Bars  *.hst:

Das Beispiel für einen korrekten und fehlerhaften Eintrag  

in Abb. 4. Das Beispiel für einen korrekten und fehlerhaften Eintrag 

Die Regel 2: obwohl wir Range-Bars bauen, die keine Timing haben, erfordert selbst die Format der History-Datei  *.hst  den Parameter time - das ist die Anfangszeit der Periode. Deshalb muss der Parameter time immer geschrieben werden.

Die Regel 3: Der Parameter time in allen Bars muss unterschiedlich sein. Wenn wir für alle Bars den gleichen Parameter time schreiben, wird ein solcher Eintrag falsch, und das Terminal zeigt einfach kein Chart. Aber es gibt da auch eine angenehme Besonderheit: Der Parameter time darf man im Intervall von einer Sekunde schreiben. Zum Beispiel schreiben wir ein Bar mit dem Parameter time=2016.02.10 09:08:00, und das nächste — mit dem Parameter time=2016.02.10 09:08:01.

3.2. Die Bildung der "Ziegel" auf historischen Daten

Diese Methode beansprucht nicht auf den perfekten Algorithmus zu sein. Ich wählte den vereinfachten Ansatz, da der Hauptzweck dieses Artikels ist - zu zeigen, wie die History-Datei *.hst gebildet werden soll. Im Indikator "IndRange.mq4", wemm die Ziegel auf der Grundlage der History gezeichnet werden, werden auch die Werte Hoch [i] und Low [i] der aktuellen Periode analysiert. Das heißt, wenn der Indikator "IndRange.mq4" zum Chart mit der Periode M5 hinzugefügt wird, wird der Indikator "IndRange.mq4" beim ersten Start die History nach der aktuellen Periode M5 analysiert. 

Natürlich können Sie den Zeichnungsalgorithmus basierend auf der HIstory aktualisieren und dabei die Preisbewegung am jüngsten Timeframe - M1 berücksichtigen. Das allgemeine Schema der Arbeit:

  • wenn High[i] mehr um einen Ziegel ist, als high des vorherigen Ziegels, dann höher des vorherigen Ziegels werden ein oder mehrere Ziegel gezeichnet;
  • wenn Low[i] weniger um einen Ziegel ist, als low des vorherigen Ziegels, dann höher des vorherigen Ziegels werden ein oder mehrere Ziegel gezeichnet.

Der vorherige Ziegel - bullish

in Abb. 5. Der vorherige Ziegel - bullish     


 Der vorherige Ziegel - bärisch

in Abb. 6. Der vorherige Ziegel - bärisch

Ein interessantes Detail: Die Koordinaten der Bars (Open, High, Low, Close, Time und Volumen) sind in der Struktur rate gespeichert, die in dem "Kopf" des Indikators erklärt ist

MqlRates rate;                   //

und diese Struktur wird in jedem Eintrag in der History-Datei *.hst nicht auf 0 zurückgesetzt, sondern wird nur neugeschrieben. Deswegen ist einfach, die Algorithmen zu realisieren, die in Abb. 5 und in Abb. 6.dargestellt sind, und man kann die allgemeine Formel geben:

  • Wenn High[i] mehr um einen Ziegel ist als high des vorherigen Ziegels (dabei ist es nicht wichtig, ob der vorherige Ziegel bärisch oder bullisch war), Koordinaten für das neue Bar werden durch das folgende Schema berechnet:  

Open = High; Low = Open; Close = Low + Renko size; High = Close

 

  • Wenn Low[i] weniger um einen Ziegel ist als low des vorherigen Ziegels (dabei ist es nicht wichtig, ob der vorherige Ziegel bärisch oder bullisch war), Koordinaten für das neue Bar werden durch das folgende Schema berechnet:  

Low = OpenHigh = OpenClose = High - Renko sizeLow = Close

Und so sieht diese Formel im Indikator "IndRange.mq4", in der Funktion FirstWriteHistory() aus:

//+------------------------------------------------------------------+
//| First Write History                                              |
//+------------------------------------------------------------------+
bool FirstWriteHistory(const int offline_period)
  {
   int      i,start_pos;
   int      cnt=0;
//--- write history file
   periodseconds=offline_period*60;
   start_pos=Bars-1;
   rate.open=Open[start_pos];
   rate.close=Close[start_pos];
   rate.low=Low[start_pos];
   rate.high=High[start_pos];
   rate.tick_volume=(long)Volume[start_pos];
   rate.spread=0;
   rate.real_volume=0;
//--- normalize open time
   rate.time=D'1980.07.19 12:30:27';
   for(i=start_pos-1; i>=0; i--)
     {
      if(IsStopped())
         break;
      while((High[i]-rate.high)>SizeRenko*Point())
        {
         rate.time+=1;
         rate.open=rate.high;
         rate.low=rate.open;
         rate.close=NormalizeDouble(rate.low+SizeRenko*Point(),Digits);
         rate.high=rate.close;
         last_fpos=FileTell(HandleHistory);
         uint byteswritten=FileWriteStruct(HandleHistory,rate);
         //--- check the number of bytes written 
         if(byteswritten==0)
            PrintFormat("Error read data. Error code=%d",GetLastError());
         else
            cnt++;
        }
      while((Low[i]-rate.low)<-SizeRenko*Point())
        {
         rate.time+=1;
         rate.open=rate.low;
         rate.high=rate.open;
         rate.close=NormalizeDouble(rate.high-SizeRenko*Point(),Digits);
         rate.low=rate.close;
         last_fpos=FileTell(HandleHistory);
         uint byteswritten=FileWriteStruct(HandleHistory,rate);
         //--- check the number of bytes written 
         if(byteswritten==0)
            PrintFormat("Error read data. Error code=%d",GetLastError());
         else
            cnt++;
        }
     }
   FileFlush(HandleHistory);
   PrintFormat("%d record(s) written",cnt);
   return(true);
  }

 

Der Indikator "IndRange.mq4" bildet immer einen Namen der History-Datei *.hst nach der folgenden Regel: "Der Name des aktuellen Symbols"+"7"+".hst". Zum Beispiel für das Symbol "EURUSD" wird die History-Datei den Namen"EURUSD7.hst" haben.

3.3. Die Online-Arbeit des Indikators

Bei der Online-Arbeit des Indikators statt den Preis High[i] muss der Preis Close[0] analysiert werden - Der Schlusskurs auf dem nullwertigen Bar (ganz rechts):

//+------------------------------------------------------------------+
//| Collect Ticks                                                    |
//+------------------------------------------------------------------+
bool CollectTicks()
  {
   static datetime last_time;//=TimeLocal()-5;
   long     chart_id=0;
   datetime cur_time=TimeLocal();
//---
   while((Close[0]-rate.high)>SizeRenko*Point())
     {
      rate.time+=1;
      rate.open=rate.high;
      rate.low=rate.open;
      rate.close=NormalizeDouble(rate.low+SizeRenko*Point(),Digits);
      rate.high=rate.close;
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- check the number of bytes written  
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
     }
   while((Close[0]-rate.low)<-SizeRenko*Point())
     {
      rate.time+=1;
      rate.open=rate.low;
      rate.high=rate.open;
      rate.close=NormalizeDouble(rate.high-SizeRenko*Point(),Digits);
      rate.low=rate.close;
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- check the number of bytes written 
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
     }
//--- refresh window not frequently than 1 time in 2 seconds
   if(cur_time-last_time>=3)
     {
      FileFlush(HandleHistory);
      ChartSetSymbolPeriod(ChartOffID,Symbol(),i_period);
      last_time=cur_time;
     }
   return(true);
  }


4. Der Indikator, der ein nicht standartes Symbol erstellen wird - das Dollar-Index USDx

Die wichtige Hinweise für den Algorithmus des Indikators "IndUSDx.mq4": Der Indikator des Dollar-Index beansprucht nicht auf die absolute Haltung der Berechnungsformel des Index, weil für uns jetzt hauptsächlich wichtig ist - zu zeigen, wie man die HIstory-Datei  *.hst  nach dem nicht standarten Symbol gebildet wird. Als Ergebnis wird ein offline-Chart erhalten und die Bars werden den berechneten Dollar-Index anzeigen. Der Indikator "IndUSDx.mq4" erstellt auch ein autonomes Chart nur einmal: entweder beim ersten Anschluss des Indikators zum Chart, oder nach einer Ersetzung des Charts.

Die Formel zur Berechnung des Dollar-Index und der Satz der Symbole sind auf dem Code basierend: Der einfache Indikator des Dollar-Index

Für die Berechnungsformel des Dollar-Index muss man die Daten Time, Open und Close nach Symbolen "EURUSD", "GBPUSD", "USDCHF", "USDJPY", "AUDUSD", "USDCAD" und "NZDUSD" erhalten. Für die Bequemlichkeit der Speicherung und den Zugriff auf Daten wird die OHLS-Struktur (es wird im "Kopf" des Indikators erklärt) eingegeben:

//--- structuts
struct   OHLS
  {
   datetime          ohls_time[];
   double            ohls_open[];
   double            ohls_close[];
  };

In der OHLS-Struktur werden als Elemente die Arrays für die Speicherung  Time, Open und Close wein. Unter der Beschreibung der Struktur wurden direkt mehrere Objekte - die Struktur OHLS erklärt, in den die berechneten Datensymbole gespeichert sind:

//--- structuts
struct   OHLS
  {
   datetime          ohls_time[];
   double            ohls_open[];
   double            ohls_close[];
  };
OHLS     OHLS_EURUSD,OHLS_GBPUSD,OHLS_USDCHF,OHLS_USDJPY,OHLS_AUDUSD,OHLS_USDCAD,OHLS_NZDUSD;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()

In OnInit() wird die Funktion SelectSymbols() aufgerufen:

//+------------------------------------------------------------------+
//| Select Symbols                                                   |
//+------------------------------------------------------------------+
bool SelectSymbols()
  {
   bool rezult=true;
   string arr_symbols[7]={"EURUSD","GBPUSD","USDCHF","USDJPY","AUDUSD","USDCAD","NZDUSD"};
   for(int i=0;i<ArraySize(arr_symbols);i++)
      rezult+=SymbolSelect(arr_symbols[i],true);
//---
   return(rezult);
  }

Die Funktion SelectSymbols(), mit SymbolSelect, wählt die Symbole aus, die in der Formel des Dollar-Index teilnehmen, im Fenster MarketWatch.

In OnCalculate() wird beim ersten Start die Funktion CopyCloseSymbols() aufgerufen. Hier werden die Daten nach berechneten Symbolen aufgerufen und werden Strukturen der Symbole ausgefüllt:

//+------------------------------------------------------------------+
//| CopyClose Symbols                                                |
//+------------------------------------------------------------------+
bool CopyCloseSymbols(const int rates)
  {
   int copied=0;
   int copy_time=0,copy_open=0,copy_close=0;
   copy_time=CopyTime("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_open);
   copy_close=CopyClose("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"EURUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("GBPUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("GBPUSD",Period(),0,rates,OHLS_GBPUSD.ohls_open);
   copy_close=CopyClose("GBPUSD",Period(),0,rates,OHLS_GBPUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"GBPUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDCHF",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDCHF",Period(),0,rates,OHLS_USDCHF.ohls_open);
   copy_close=CopyClose("USDCHF",Period(),0,rates,OHLS_USDCHF.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDCHF\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDJPY",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDJPY",Period(),0,rates,OHLS_USDJPY.ohls_open);
   copy_close=CopyClose("USDJPY",Period(),0,rates,OHLS_USDJPY.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDJPY\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("AUDUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("AUDUSD",Period(),0,rates,OHLS_AUDUSD.ohls_open);
   copy_close=CopyClose("AUDUSD",Period(),0,rates,OHLS_AUDUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"AUDUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDCAD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDCAD",Period(),0,rates,OHLS_USDCAD.ohls_open);
   copy_close=CopyClose("USDCAD",Period(),0,rates,OHLS_USDCAD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDCAD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("NZDUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("NZDUSD",Period(),0,rates,OHLS_NZDUSD.ohls_open);
   copy_close=CopyClose("NZDUSD",Period(),0,rates,OHLS_NZDUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"NZDUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }
//---
   return(true);
  }

Wenn in der Funktion CopyCloseSymbols () die History des Symbols weniger heruntergeladenen ist, als der vorgegebene Wert, dann wird eine Nachricht mit dem Symbolnamen und dem tatsächlichen Wert der heruntergeladenen History des Symbols angezeigt.

Bei erfolgreicher Ausfüllung der Strukturen wird die grundlegende Funktionalität der Funktion  FirstWriteHistory() aufgerufen, welche die History-Datei *.hst ausfüllt:

//+------------------------------------------------------------------+
//| First Write History                                              |
//+------------------------------------------------------------------+
bool FirstWriteHistory(const int rates)
  {
   int      i;
   int      cnt=0;
   rate.tick_volume=0;
   rate.spread=0;
   rate.real_volume=0;
   for(i=0;i<rates;i++)
     {
      rate.time=OHLS_EURUSD.ohls_time[i];
      rate.open=(100*MathPow(OHLS_EURUSD.ohls_open[i],0.125)+100*MathPow(OHLS_GBPUSD.ohls_open[i],0.125)+
                 100*MathPow(OHLS_USDCHF.ohls_open[i],0.125)+100*MathPow(OHLS_USDJPY.ohls_open[i],0.125)+
                 100*MathPow(OHLS_AUDUSD.ohls_open[i],0.125)+100*MathPow(OHLS_USDCAD.ohls_open[i],0.125)+
                 100*MathPow(OHLS_NZDUSD.ohls_open[i],0.125))/8.0;

      rate.close=(100*MathPow(OHLS_EURUSD.ohls_close[i],0.125)+100*MathPow(OHLS_GBPUSD.ohls_close[i],0.125)+
                  100*MathPow(OHLS_USDCHF.ohls_close[i],0.125)+100*MathPow(OHLS_USDJPY.ohls_close[i],0.125)+
                  100*MathPow(OHLS_AUDUSD.ohls_close[i],0.125)+100*MathPow(OHLS_USDCAD.ohls_close[i],0.125)+
                  100*MathPow(OHLS_NZDUSD.ohls_close[i],0.125))/8.0;

      if(rate.open>rate.close)
        {
         rate.high=rate.open;
         rate.low=rate.close;
        }
      else
        {
         rate.high=rate.close;
         rate.low=rate.open;
        }
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- check the number of bytes written 
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
      else
         cnt++;
     }
   FileFlush(HandleHistory);
   PrintFormat("%d record(s) written",cnt);
   return(true);
  }

Das Ergebnis der Arbeit des Indikators "IndUSDx.mq4": 

 Der Indikatorор "IndUSDx.mq4

in Abb. 7. Der Indikatorор "IndUSDx.mq4 

  

Fazit

Es wurde festgestellt, dass auf autonome Charts, sowie wie auf Online-Charts kosteneffiziente Neuberechnung der Indikatoren durchgeführt werden können. Allerdings muss man dafür Änderungen im Code des Indikators einfügen, um die Besonderheiten der Aktualisierung des autonomen Charts zu rechnen, nämlich zu berücksichtigen, dass alle Indikatoren in OnCalculate() den Wert prev_calculate == 0 erhalten.

Der Artikel zeigt auch, wie die History-Datei  *.hst gebildet werden soll und dadurch ändern sich radikal die Parameter der angezeigten Bars auf dem Chart. 


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

Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1) Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1)
Dieser Artikel ist der Anfang einer weiteren Serie über die Entwicklung von grafischen Interfaces. Zur Zeit gibt es keine einzige Bibliothek, die es einem ermöglichen würde, einfach und schnell qualitative graphische Interfaces innerhalb einer MQL- Anwendung zu erzeugen. Damit meine ich graphische Interfaces, wie wir sie auch von anderen Betriebssystemen her kennen.
Die Verwendung der Fuzzy-Logik im Trading mit Hilfe von MQL4 Die Verwendung der Fuzzy-Logik im Trading mit Hilfe von MQL4
Der Artikel enthält Beispiele für die Fuzzy-Logik-Theorie im Trading mit Hilfe von MQL4. Es wird die Entwicklung eines Indikators und eines EAs durch die FuzzyNet Bibliothek für MQL4 beschrieben.
Die Fehlerverarbeitung und Protokollierung in MQL5 Die Fehlerverarbeitung und Protokollierung in MQL5
In diesem Artikel werden die meisten Fragen bezüglich der Fehlerverarbeitung im Programm betrachtet. Außerdem betrachten wir Protokollierung und werden ein Beispiel der Log-Realisierung mit MQL5 darstellen.
Bewertung der Effektivität von Handelssystemen durch eine Analyse ihrer Komponenten Bewertung der Effektivität von Handelssystemen durch eine Analyse ihrer Komponenten
In diesem Artikel wird die Effektivität von Handelssystemen durch eine Effektivitätsanalyse ihrer eigenen Komponenten geforscht. Jede Analyse, egal ob es eine grafische Analyse ist, die basierend auf Indikatoren oder auf eine andere Analyse ist, ist eine der wichtigsten Komponenten für das erfolgreiche Handeln auf den Finanzmärkten. Dieser Artikel ist zu einem gewissen Grad eine Forschung von einigen einfachen und unabhängigen Handelssystemen, enthält ihre Wirksamkeitsanalyse und Nützlichkeitsanalyse bei einer gemeinsamen Anwendung.