English Русский 中文 Español 日本語 Português
Charts ohne "Gaps" (Lücken)

Charts ohne "Gaps" (Lücken)

MetaTrader 4Beispiele | 23 Dezember 2015, 10:03
774 0
Andrey Khatimlianskii
Andrey Khatimlianskii

Motivation


Das System des Charts-Aufbaus in MT4 ist so programmiert, dass nur die Bars angezeigt werden, in deren Zeitrahmen mindestens eine Preisänderung stattgefunden hat. Wenn es innerhalb von einer Minute keine Preisänderung gab, dann tritt im einminütigen Chart eine Gap aus einem Bar.


Programmier haben bewusst diese Art des Chart-Aufbaus eingesetzt, denn die meisten Benutzer, die diese Produkte verwenden, benutzen gern die Charts, die nur aktuelle Preise enthalten. Trotzdem gibt es auch Fans von Charts, die keine Gaps haben. Sie meinen, dass ein Bar trotz keiner Preisänderung angezeigt werden muss, bei dem der Open-Price mit dem Exit-Price des vorherigen Bars gleich ist. Dadurch werden im Chart keine Gaps über der Zeit-Skala auftreten, und 100 Bars im minütigen Chart werden immer dem Zeitraum aus 100 Minuten entsprechen. Während der aktuellen Realisierung können da andere Zahlen sein. Zum Beispiel über 100 Minuten können 98 Bars kommen, wenn es über 100 Minuten 2 Minuten lange keine Preisnotierungen geliefert wurden.


Zum Glück gibt es in MQL 4 alle nötige Werkzeuge, um solche Charts selber zu realisieren.


Realisierung


Am Anfang teilen wir die Aufgabe zu zwei Stufen:

  • Bearbeitung der History-Daten;

  • Erneuerung des letzten Bars.

In der ersten Stufe erstellen wir eine neue History-Datei mit "ALL" zum Symbol-Namen ("ALL" – "Alle", in unserem Kontext – "alle Bars")  und schreiben wir darin die History mit den hinzugefügten Bars.


Die ähnliche Aufgabe wird im Skript
"period_converter" erledigt, mit dem der Client-Terminal MT4 komplettiert ist. Das Skript erzeugt das Chart mit einer ungewöhnlichen Periode. Von seinem Beispiel lernen wir eben, wie wir mit einer History-Datei arbeiten müssen.


Bevor das Programm erstellt wird, muss man entscheiden, in welcher Form es gestaltet werden muss, in der Form von: einem Skript, Indikator oder Experten. Indikator ist für die Darstellung des Arrays-Inhaltes vorgesehen. Das brauchen wir nicht. Der grundlegende Unterschied zwischen einem Skript und einem Experten besteht nur in einem Punkt, - Das Skript wird direkt nach der Ausführung aus dem Chart entfernt. In dieser Stufe passt es noch, deshalb suchen wir ein Skript aus.


      Also, was haben wir eigentlich bekommen (AllMinutes_Step1.mq4):

#property show_inputs
 
//---- Aktivieren/Deaktivieren, Bars am Wochenende zu zeichnen
//---- Wenn == true, bleiben die Wochenende-Tage nicht gefüllt
//---- Wenn == false, werden die Wochenende-Tage mit Bars O=H=L=C gefüllt
extern bool  SkipWeekEnd = true;
 
int start()
  {
   int HistoryHandle = -1, pre_time, now_time, _PeriodSec;
   double  now_close, now_open, now_low, now_high, now_volume, pre_close;
 
   int    _GetLastError = 0, cnt_copy = 0, cnt_add = 0;
   int    temp[13];
 
   //---- Merken wir uns das Symbol und die Periode des Charts
   string _Symbol = Symbol();
   int _Period = Period();
   _PeriodSec = _Period * 60;
 
   //---- Öffnen wir die Datei, in der wir die History schreiben
   string file_name = StringConcatenate( "ALL", _Symbol, _Period, ".hst" );
   HistoryHandle = FileOpenHistory( file_name, FILE_BIN | FILE_WRITE );
   if(HistoryHandle < 0 )
     {
       _GetLastError = GetLastError();
       Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #", _GetLastError);
       return(-1);
     }
 
   //---- Schreiben Wir den Datei-Überschrift
   FileWriteInteger(HistoryHandle, 400, LONG_VALUE);
   FileWriteString(HistoryHandle, "Copyright © 2006, komposter", 64);
   FileWriteString(HistoryHandle, "ALL" + _Symbol, 12);
   FileWriteInteger(HistoryHandle, _Period, LONG_VALUE);
   FileWriteInteger(HistoryHandle, Digits, LONG_VALUE);
   FileWriteInteger(HistoryHandle, 0, LONG_VALUE);       //timesign
   FileWriteInteger(HistoryHandle, 0, LONG_VALUE);       //last_sync
   FileWriteArray(HistoryHandle, temp, 0, 13);
   //+-----------------------------------------------------------------+
   //| Bearbeiten die History                                          |
   //+-----------------------------------------------------------------+
   int bars = Bars;
   pre_time = Time[bars-1];
   for(int i = bars - 1; i >= 0; i--)
     {
       //---- Merken wir uns die Parameter des Bars  
       now_open = Open[i];
       now_high = High[i];
       now_low = Low[i];
       now_close = Close[i];
       now_volume = Volume[i];
       now_time = Time[i] / _PeriodSec;
       now_time *= _PeriodSec;
       //---- wenn einige Bars übersprungen wurden,
       while(now_time > pre_time + _PeriodSec)
         {
           pre_time += _PeriodSec;
           pre_time /= _PeriodSec;
           pre_time *= _PeriodSec;
 
           //---- wenn es nicht an den Wochenende-Tage ist,
           if(SkipWeekEnd)
             {
               if(TimeDayOfWeek(pre_time) <= 0 || 
                  TimeDayOfWeek(pre_time) > 5) 
                   continue; 
               if(TimeDayOfWeek(pre_time) == 5)
                 {
                   if(TimeHour(pre_time) == 23 || 
                      TimeHour(pre_time + _PeriodSec) == 23)
                     {
                       continue; 
                     }
                 }
             }
 
           //---- Schreiben Wir das übersprungene Bar in der Datei
           FileWriteInteger(HistoryHandle, pre_time, LONG_VALUE);
           FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle, 1, DOUBLE_VALUE);
           FileFlush(HistoryHandle);
           cnt_add ++;
         }
 
       //---- Schreiben Wir das neue Bar in der Datei
       FileWriteInteger(HistoryHandle, now_time, LONG_VALUE);
       FileWriteDouble(HistoryHandle, now_open, DOUBLE_VALUE);
       FileWriteDouble(HistoryHandle, now_low, DOUBLE_VALUE);
       FileWriteDouble(HistoryHandle, now_high, DOUBLE_VALUE);
       FileWriteDouble(HistoryHandle, now_close, DOUBLE_VALUE);
       FileWriteDouble(HistoryHandle, now_volume, DOUBLE_VALUE);
       FileFlush(HistoryHandle);
       cnt_copy ++;
 
       //---- Merken wir uns den Zeitwert und den Exit-Price  
       //---- des geschriebenen Bars
       pre_close = now_close;
       pre_time  = now_time / _PeriodSec;
       pre_time  *=_PeriodSec;
     }
 
   //---- Schließen wir die Datei
   FileClose( HistoryHandle );
 
   //---- Liefern wir die Statistik
   Print("< - - - ", _Symbol, _Period, ": es war ", cnt_copy, 
         " Bars, hinzugefügt ", cnt_add, " Bars - - - >");
   Print("< - - - Um die Ergebnisse zu sehen, öffnen Sie das Chart \"ALL", 
         _Symbol, _Period, "\" - - - >");
   return(0);
  }


Geben Sie Acht bei der Variable SkipWeekEnd. Wenn ihr Wert false ist, die Wochenende-Tage werden auch mit O=H=L=C Bars gefüllt (mit Bindestrichen).


Lassen Sie uns überprüfen, wie unser Skript funktioniert, - Schließen wir ihn einfach zum minütigen Chart GBPUSD an:


  

Nun lassen Sie uns das Chart ALLGBPUSD1 im Status "off-line" öffnen und vergleichen wir ihn mit dem Anfangschart:



 

Wie Sie sehen können, wurden im Chart noch ein paar übersprungenen Minuten hinzugefügt. Sie sind rot eingekreist. Das wollten wir bekommen, nicht wahr?


Nun, wenn wir ein vollständiges Chart haben, können wir uns mit seiner Erneuerung beschäftigen. Jetzt werden im Chart keine neue Preisnotierungen angezeigt, und von daher werden keine neue Gaps entstehen.


Nehmen wir wieder als Beispiel den Skript "period_converter". Die Aufgabe der Charts-Erneuerung wird da auch erledigt. Machen wir nur einen Unterschied: Fügen wir da einen Block hinzu, der die übersprungenen Bars füllt.  Da das Chart bei jedem Tick erneuert werden muss, tragen wir unseren ganzen Code in den Experten ein. Er wird mit jeder neuen Preisnotierung gestartet. Der Code vom ersten Teil platzieren wir in der Funktion init(), denn es muss nur einmal ausgeführt werden, und den ganzen neuen Teil platzieren wir in der Funktion start(), denn sie wird bei jedem Tick aufgerufen. Außerdem tragen wir die Datei-Schließung in deinit() ein, der Platz ist genau für sie.


Also, der Code des Experten (AllMinutes_Step2.mq4):

#include <WinUser32.mqh>
 
//---- Aktivieren/Deaktivieren, Bars am Wochenende zu zeichnen
//---- Wenn == true, bleiben die Wochenende-Tage nicht gefüllt
//---- Wenn == false, werden die Wochenende-Tage mit Bars O=H=L=C gefüllt
extern bool  SkipWeekEnd = true;
 
int  HistoryHandle = -1, hwnd = 0, last_fpos = 0, pre_time, now_time;
int  _Period, _PeriodSec;
double  now_close, now_open, now_low, now_high, now_volume;
double  pre_close, pre_open, pre_low, pre_high, pre_volume;
string  _Symbol;
 
int init()
  {
    int _GetLastError = 0, cnt_copy = 0, cnt_add = 0;
    int temp[13];
 
    //---- Merken wir uns das Symbol und die Periode des Charts
    _Symbol = Symbol();
    _Period = Period();
    _PeriodSec = _Period * 60;
    hwnd = 0;
 
    //---- Öffnen wir die Datei, in der wir die History schreiben
    string file_name = StringConcatenate("ALL", _Symbol, _Period, ".hst");
    HistoryHandle = FileOpenHistory( file_name, FILE_BIN | FILE_WRITE );
    if(HistoryHandle < 0)
      {
        _GetLastError = GetLastError();
        Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #", _GetLastError);
        return(-1);
      }
 
    //---- Schreiben Wir den Datei-Überschrift
    FileWriteInteger(HistoryHandle, 400, LONG_VALUE);
    FileWriteString(HistoryHandle, "Copyright © 2006, komposter", 64);
    FileWriteString(HistoryHandle, StringConcatenate("ALL", _Symbol), 12);
    FileWriteInteger(HistoryHandle, _Period, LONG_VALUE);
    FileWriteInteger(HistoryHandle, Digits, LONG_VALUE);
    FileWriteInteger(HistoryHandle, 0, LONG_VALUE);       //timesign
    FileWriteInteger(HistoryHandle, 0, LONG_VALUE);       //last_sync
    FileWriteArray(HistoryHandle, temp, 0, 13);
 
    //+-----------------------------------------------------------------+
    //| Bearbeiten die History                                          |
    //+-----------------------------------------------------------------+
    int bars = Bars;
    pre_time = Time[bars-1];
    for(int i = bars - 1; i >= 1; i--)
      {
        //---- Merken wir uns die Parameter des Bars  
        now_open = Open[i];
        now_high = High[i];
        now_low = Low[i];
        now_close = Close[i];
        now_volume = Volume[i];
        now_time = Time[i] / _PeriodSec;
        now_time *=_PeriodSec;
 
        //---- wenn einige Bars übersprungen wurden,
        while(now_time > pre_time + _PeriodSec)
          {
            pre_time += _PeriodSec;
            pre_time /= _PeriodSec;
            pre_time *= _PeriodSec;
 
            //---- wenn es nicht an den Wochenende-Tage ist,
            if(SkipWeekEnd)
              {
                if(TimeDayOfWeek(pre_time) <= 0 || TimeDayOfWeek(pre_time) > 5) 
                    continue; 
                if(TimeDayOfWeek(pre_time) == 5)
                  {
                    if(TimeHour(pre_time) == 23 || 
                       TimeHour(pre_time + _PeriodSec) == 23)
                        continue; 
                  }
              }
 
            //---- Schreiben Wir das übersprungene Bar in der Datei
            FileWriteInteger(HistoryHandle, pre_time, LONG_VALUE);
            FileWriteDouble(HistoryHandle, pre_close,DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle, 1, DOUBLE_VALUE);
            FileFlush(HistoryHandle );
            cnt_add ++;
          }
 
        //---- Schreiben Wir das neue Bar in der Datei
        FileWriteInteger(HistoryHandle, now_time, LONG_VALUE);
        FileWriteDouble(HistoryHandle, now_open, DOUBLE_VALUE);
        FileWriteDouble(HistoryHandle, now_low, DOUBLE_VALUE);
        FileWriteDouble(HistoryHandle, now_high, DOUBLE_VALUE);
        FileWriteDouble(HistoryHandle, now_close, DOUBLE_VALUE);
        FileWriteDouble(HistoryHandle, now_volume, DOUBLE_VALUE);
        FileFlush(HistoryHandle);
        cnt_copy ++;
 
        //---- Merken wir uns den Zeitwert und den Exit-Price 
        //---- des geschriebenen Bars
        pre_close = now_close;
        pre_time = now_time / _PeriodSec;
        pre_time *=_PeriodSec;
      }
 
    last_fpos = FileTell( HistoryHandle);
 
    //---- Liefern wir die Statistik
    Print("< - - - ", _Symbol, _Period, ": waren ", cnt_copy, " Bars, hinzugefügt ", cnt_add, " Bars - - - >");
    Print("< - - - Um die Ergebnisse zu sehen, öffnen Sie das Chart \"ALL", _Symbol, _Period, "\" - - - >");
 
    //---- Rufen wir die Funktion start auf, damit das 0-te Bar direkt angezeigt würde
    start();
    
    return(0);
   }
//----
int start()
   {
     //+---------------------------------------------------------------+
     //| Bearbeiten die kommenden Ticks                                 |
     //+---------------------------------------------------------------+
 
     //---- Platzieren wir den "Cursor" vor dem letzten Bar 
     //---- (Das muss man bei jedem Start machen, außer dem ersten Start)
      FileSeek(HistoryHandle, last_fpos, SEEK_SET);
 
     //---- Merken wir uns die Parameter des Bars  
     now_open = Open[0];
     now_high = High[0];
     now_low = Low[0];
     now_close = Close[0];
     now_volume = Volume[0];
     now_time = Time[0] / _PeriodSec;
     now_time *= _PeriodSec;
 
     //---- Wenn das Bar umformt ist, 
     if(now_time >= pre_time + _PeriodSec)
       {
         //---- Schreiben Wir das umformte Bar
         FileWriteInteger(HistoryHandle, pre_time, LONG_VALUE);
         FileWriteDouble(HistoryHandle, pre_open, DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle, pre_low, DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle, pre_high, DOUBLE_VALUE );
         FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle, pre_volume, DOUBLE_VALUE);
         FileFlush(HistoryHandle);
 
         //---- Merken wir uns den Platz in der Datei, bevor das 0-te Bar geschrieben wird
         last_fpos = FileTell(HistoryHandle);
       }
 
     //---- Wenn die übersprungene Bars entstehen,
     while(now_time > pre_time + _PeriodSec)
       {
         pre_time += _PeriodSec;
         pre_time /= _PeriodSec;
         pre_time *= _PeriodSec;
 
         //---- wenn es nicht an den Wochenende-Tage ist,
         if(SkipWeekEnd)
           {
             if(TimeDayOfWeek(pre_time) <= 0 || 
                TimeDayOfWeek(pre_time) > 5 ) 
                 continue; 
             if(TimeDayOfWeek(pre_time) == 5)
               {
                 if(TimeHour(pre_time) == 23 || 
                    TimeHour(pre_time + _PeriodSec) == 23)
                     continue; 
               }
           }
 
         //---- Schreiben Wir das übersprungene Bar in der Datei
         FileWriteInteger(HistoryHandle, pre_time, LONG_VALUE);
         FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle, pre_close, DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle, 1, DOUBLE_VALUE);
         FileFlush(HistoryHandle);
 
         //---- Merken wir uns den Platz in der Datei, bevor das 0-te Bar geschrieben wird
         last_fpos = FileTell(HistoryHandle);
       }
 
     //---- Schreiben Wir das aktuelle Bar
     FileWriteInteger(HistoryHandle, now_time, LONG_VALUE);
     FileWriteDouble(HistoryHandle, now_open, DOUBLE_VALUE);
     FileWriteDouble(HistoryHandle, now_low, DOUBLE_VALUE);
     FileWriteDouble(HistoryHandle, now_high, DOUBLE_VALUE);
     FileWriteDouble(HistoryHandle, now_close, DOUBLE_VALUE);
     FileWriteDouble(HistoryHandle, now_volume, DOUBLE_VALUE);
     FileFlush(HistoryHandle);
 
     //---- Merken wir uns die Parameter des geschriebenen Bars
     pre_open = now_open;
     pre_high = now_high;
     pre_low = now_low;
     pre_close = now_close;
     pre_volume = now_volume;
     pre_time = now_time / _PeriodSec;
     pre_time *= _PeriodSec;
 
     //---- Finden wir das Fenster, wohin wir aktuelle Preisnotierungen "senden" werden 
     if(hwnd == 0)
       {
         hwnd = WindowHandle(StringConcatenate( "ALL", _Symbol ), _Period);
         if(hwnd != 0) 
           { 
             Print("< - - - Charts ", 
                   "ALL" + _Symbol, _Period, " gefunden! - - - >"); 
           }
       }
     //---- und wenn Sie das Fenster gefunden haben, Erneuern Sie ihn
     if(hwnd != 0) 
       { 
         PostMessageA(hwnd, WM_COMMAND, 33324, 0); 
       }
   }
//----
int deinit()
  {
   if(HistoryHandle >= 0)
     {
       //---- Schließen wir die Datei
       FileClose(HistoryHandle);
       HistoryHandle = -1;
     }
   return(0);
  }
   

Ich muss direkt vorbehalten, dass der Erneuerungsprozess des Charts ziemlich aufwändig ist, weil das Terminal alle Bars lädt, die in der Datei geschrieben wurden. Wenn Bars in der Datei ziemlich viel sind, kann das Terminal merklich langsamer werden. Das ist von den Eigenschaften des Computers sehr abhängig, in dem das Client-Terminal MT4 installiert ist. Jedenfalls sind die Ressourcen nicht unerschöpflich. Deshalb lösen wir das Problem ganz einfach aus - wir reduzieren die Anzahl der Bars im Chart bis 10 000 ("Extras"-"Optionen"-"Diagramme", Max. Kerzen im Chart"). Jetzt starten wir den Terminal neu und schließen unseren Experten an:


  

Der Experte hat die History direkt "geflickt" und hat angefangen, auf neue Ticks zu warten. Nach 2 Minuten sahen die Charts so aus:

   

Wie Sie sehen können, es wurde im oberen Chart noch eine Minute hinzugefügt, als im unteren Chart noch ein übersprungenes Bar hinzugefügt wurde.

Das heißt, wir haben das gewünschte Ergebnis bekommen!

Zoomen


So ein Chart zu haben, ist klar gut, aber was soll man tun, wenn 10 Charts ohne übersprungenen Bars eröffnet werden müssen? Für jedes Chart noch ein "vorübergehendes" Chart eröffnen, in dem der Experte funktionieren wird, - ist aber keine beste Lösung. Es werden zusätzliche Ressourcen verbraucht, und bzw wird die Arbeit unbequemer sein.


Lassen Sie uns den Experten erstellen, der die jene Menge Charts bearbeiten kann. Das macht die Verwendung bequemer und das ist eine rationelle Lösung.

Also, was müssen wir in unserem Code ändern, damit er mit ein paar Charts arbeitet:

  • eine externe Variable hinzufügen, mit der man die Liste aus Charts ändern kann;
  • Alle Variablen mit den Arrays auswechseln, deren Anzahl der Elemente mit der Anzahl der bearbeitenden Charts gleich ist;
  • den ganzen Code in der Loop setzen, in der diese Charts sortiert werden;
  • den Erneuerungsblock in der unendlichen Loop einsetzen, nachdem Sie vom Einkommen der Preisnotierungen unabhängig werden. Wenn es in der Liste unterschiedliche Symbols sind, dann werden ihre Erneuerungszeit anders sein.

Das ist, was wir eigentlich bekommen müssen (AllMinutes.mq4):

#include <WinUser32.mqh>
 
//---- Die Charts-Liste, die bearbeitet werden müssen, ist durch die Komme 
getrennt (",")
extern string    ChartList = "EURUSD1,GBPUSD1";
//---- Aktivieren/Deaktivieren, Bars am Wochenende zu zeichnen
//---- Wenn == true, bleiben die Wochenende-Tage nicht gefüllt
//---- Wenn == false, werden die Wochenende-Tage mit Bars O=H=L=C gefüllt
extern bool     SkipWeekEnd = true;
//---- Die Periode, in der die Charts erneuert werden, in Millisekunden 
//---- Je höher der Wert ist, desto weniger Ressourcen der Experte 
//---- benutzt.
extern int   RefreshLuft = 1000;
 
int init() 
  { 
   start(); 
   return(0); 
  }
int start()
  {
   int _GetLastError = 0, cnt_copy = 0, cnt_add = 0, temp[13];
   int Charts = 0, pos = 0, curchar = 0, len = StringLen(ChartList);
   string cur_symbol = "", cur_period = "", file_name = "";
 
   string _Symbol[100]; int _Period[100], _PeriodSec[], _Bars[];
   int HistoryHandle[], hwnd[], last_fpos[], pre_time[], now_time[];
   double now_close[],now_open[],now_low[],now_high[],now_volume[];
   double pre_close[],pre_open[],pre_low[],pre_high[],pre_volume[];
 
   //---- Zahlen wir die Anzahl der Charts, die bearbeitet werden müssen
   while(pos <= len)
     {
       curchar = StringGetChar(ChartList, pos);
       if(curchar > 47 && curchar < 58)
           cur_period = cur_period + CharToStr(curchar); 
       else
         {
           if(curchar == ',' || pos == len)
             { 
               MarketInfo(cur_symbol, MODE_BID);
               if(GetLastError() == 4106)
                 {
                   Alert("Unbekanntes Symbol ", cur_symbol, "!!!");
                   return(-1);                
                 }
               if(iClose(cur_symbol, StrToInteger(cur_period), 0) <= 0)
                 {
                   Alert("unbekannte Periode ", cur_period, "!!!");
                   return(-1);                
                 }
 
               _Symbol[Charts] = cur_symbol; 
               _Period[Charts] = StrToInteger(cur_period);
               cur_symbol = ""; 
               cur_period = "";
 
               Charts ++;
             }
           else
               cur_symbol = cur_symbol + CharToStr(curchar); 
         }
       pos++;
     }
   Print("< - - - Gefunden ", Charts, " korrekte Charts. - - - >");
   ArrayResize(_Symbol, Charts); 
   ArrayResize(_Period, Charts);
   ArrayResize(HistoryHandle, Charts); 
   ArrayResize(hwnd, Charts);
   ArrayResize(last_fpos, Charts); 
   ArrayResize(pre_time, Charts);
   ArrayResize(now_time, Charts); 
   ArrayResize(now_close, Charts);
   ArrayResize(now_open, Charts); 
   ArrayResize(now_low, Charts);
   ArrayResize(now_high, Charts); 
   ArrayResize(now_volume, Charts);
   ArrayResize(pre_close, Charts); 
   ArrayResize(pre_open, Charts);
   ArrayResize(pre_low, Charts); 
   ArrayResize(pre_high, Charts);
   ArrayResize(pre_volume, Charts); 
   ArrayResize(_PeriodSec, Charts);
   ArrayResize(_Bars, Charts);
 
   for(int curChart = 0; curChart < Charts; curChart ++)
     {
       _PeriodSec[curChart] = _Period[curChart] * 60;
 
       //---- Öffnen wir die Datei, in der wir die History schreiben
       file_name = StringConcatenate("ALL", _Symbol[curChart], 
                                     _Period[curChart], ".hst");
       HistoryHandle[curChart] = FileOpenHistory(file_name, 
                                                 FILE_BIN | FILE_WRITE);
       if(HistoryHandle[curChart] < 0)
         {
           _GetLastError = GetLastError();
           Alert("FileOpenHistory( \"", file_name, "\", FILE_BIN | FILE_WRITE)", 
                 " - Error #", _GetLastError);
           continue;
         }
 
       //---- Schreiben Wir den Datei-Überschrift
       FileWriteInteger(HistoryHandle[curChart], 400, LONG_VALUE);
       FileWriteString(HistoryHandle[curChart], "Copyright © 2006, komposter", 64);
       FileWriteString(HistoryHandle[curChart], StringConcatenate("ALL", 
                       _Symbol[curChart]), 12);
       FileWriteInteger(HistoryHandle[curChart], _Period[curChart], LONG_VALUE );
       FileWriteInteger(HistoryHandle[curChart], MarketInfo( _Symbol[curChart], 
                        MODE_DIGITS ), LONG_VALUE);
       FileWriteInteger(HistoryHandle[curChart], 0, LONG_VALUE);    // timesign
       FileWriteInteger(HistoryHandle[curChart], 0, LONG_VALUE);    // last_sync
       FileWriteArray(HistoryHandle[curChart], temp, 0, 13);
 
       //+-----------------------------------------------------------+
       //| Bearbeiten die History                                       |
       //+-----------------------------------------------------------+
       _Bars[curChart] = iBars(_Symbol[curChart], _Period[curChart]);
       pre_time[curChart] = iTime(_Symbol[curChart], _Period[curChart], 
                                  _Bars[curChart] - 1);
       for(int i = _Bars[curChart] - 1; i >= 1; i--)
         {
           //---- Merken wir uns die Parameter des Bars  
           now_open[curChart] = iOpen(_Symbol[curChart], _Period[curChart], i);
           now_high[curChart] = iHigh(_Symbol[curChart], _Period[curChart], i);
           now_low[curChart] = iLow(_Symbol[curChart], _Period[curChart], i);
           now_close[curChart] = iClose(_Symbol[curChart], _Period[curChart], i);
           now_volume[curChart] = iVolume(_Symbol[curChart], _Period[curChart], i);
           now_time[curChart] = iTime(_Symbol[curChart], _Period[curChart], i) / 
                                      _PeriodSec[curChart];
           now_time[curChart] *= _PeriodSec[curChart];
 
           //---- wenn einige Bars übersprungen wurden,
           while(now_time[curChart] > pre_time[curChart] + _PeriodSec[curChart])
             {
               pre_time[curChart] += _PeriodSec[curChart];
               pre_time[curChart] /= _PeriodSec[curChart];
               pre_time[curChart] *= _PeriodSec[curChart];
 
               //---- wenn es nicht an den Wochenende-Tage ist,
               if(SkipWeekEnd )
                 {
                   if(TimeDayOfWeek(pre_time[curChart]) <= 0 || 
                      TimeDayOfWeek(pre_time[curChart]) > 5 ) 
                       continue; 
                   if(TimeDayOfWeek(pre_time[curChart]) == 5 )
                     {
                       if(TimeHour(pre_time[curChart]) == 23 || 
                          TimeHour(pre_time[curChart] + 
                          _PeriodSec[curChart])==23) 
                           continue; 
                     }
                 }
 
               //---- Schreiben Wir das übersprungene Bar in der Datei
               FileWriteInteger(HistoryHandle[curChart], pre_time[curChart],
                                LONG_VALUE);
               FileWriteDouble(HistoryHandle[curChart], pre_close[curChart], 
                               DOUBLE_VALUE);
               FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],
                               DOUBLE_VALUE);
               FileWriteDouble(HistoryHandle[curChart], pre_close[curChart], 
                               DOUBLE_VALUE);
               FileWriteDouble(HistoryHandle[curChart], pre_close[curChart], 
                               DOUBLE_VALUE);
               FileWriteDouble(HistoryHandle[curChart], 1, DOUBLE_VALUE);
               FileFlush(HistoryHandle[curChart]);
               cnt_add ++;
             }
 
           //---- Schreiben Wir das neue Bar in der Datei
           FileWriteInteger(HistoryHandle[curChart], now_time[curChart],
                            LONG_VALUE);
           FileWriteDouble(HistoryHandle[curChart], now_open[curChart], 
                           DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle[curChart], now_low[curChart],
                           DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle[curChart], now_high[curChart], 
                           DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle[curChart], now_close[curChart], 
                           DOUBLE_VALUE);
           FileWriteDouble(HistoryHandle[curChart], now_volume[curChart],
                           DOUBLE_VALUE);
           FileFlush(HistoryHandle[curChart]);
           cnt_copy ++;
 
           //---- Merken wir uns den Zeitwert und den Exit-Price  
           //---- des geschriebenen Bars
           pre_close[curChart] = now_close[curChart];
           pre_time[curChart] = now_time[curChart] / _PeriodSec[curChart];
           pre_time[curChart] *= _PeriodSec[curChart];
         }
 
       last_fpos[curChart] = FileTell(HistoryHandle[curChart]);
 
       //---- Liefern wir die Statistik
       Print("< - - - ", _Symbol[curChart], _Period[curChart], ": waren ", 
             cnt_copy," Bars, hinzugefügt ", cnt_add, " Bars - - - >");
       Print("< - - - Um die Ergebnisse zu sehen, öffnen Sie das Chart \"ALL", 
             _Symbol[curChart], _Period[curChart], "\" - - - >" );
 
     }
 
//+---------------------------------------------------------------+
//| Bearbeiten die kommenden Ticks                                 |
//+---------------------------------------------------------------+
  while(!IsStopped())
    {
      RefreshRates();
      for(curChart = 0; curChart < Charts; curChart ++)
        {
          //---- Platzieren wir den "Cursor" vor dem letzten Bar 
          //---- (Das muss man bei jedem Start machen, außer dem ersten Start)
          FileSeek(HistoryHandle[curChart], last_fpos[curChart], 
                   SEEK_SET );
 
          //---- Merken wir uns die Parameter des Bars  
          now_open[curChart] = iOpen(_Symbol[curChart], 
                                     _Period[curChart], 0);
          now_high[curChart] = iHigh(_Symbol[curChart], 
                                     _Period[curChart], 0);
          now_low[curChart] = iLow(_Symbol[curChart], 
                                   _Period[curChart], 0);
          now_close[curChart] = iClose(_Symbol[curChart], 
                                       _Period[curChart], 0);
          now_volume[curChart] = iVolume(_Symbol[curChart], 
                                         _Period[curChart], 0);
          now_time[curChart] = iTime(_Symbol[curChart], 
                                     _Period[curChart], 0) / 
                                     _PeriodSec[curChart];
          now_time[curChart] *= _PeriodSec[curChart];
 
          //---- Wenn das Bar umformt ist, 
          if(now_time[curChart] >= pre_time[curChart] + _PeriodSec[curChart])
            {
              //---- Schreiben Wir das umformte Bar
              FileWriteInteger(HistoryHandle[curChart], pre_time[curChart],
                               LONG_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 
                                  pre_open[curChart],DOUBLE_VALUE );
              FileWriteDouble(HistoryHandle[curChart], 
                              pre_low[curChart],DOUBLE_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 
                              pre_high[curChart],DOUBLE_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 
                              pre_close[curChart],DOUBLE_VALUE);
              FileWriteDouble(HistoryHandle[curChart],
                              pre_volume[curChart], DOUBLE_VALUE);
              FileFlush(HistoryHandle[curChart] );
 
              //---- Merken wir uns den Platz in der Datei, bevor das 0-te Bar geschrieben wird
              last_fpos[curChart] = FileTell(HistoryHandle[curChart]);
            }
 
          //---- Wenn die übersprungene Bars entstehen,
          while(now_time[curChart] > pre_time[curChart] + 
                _PeriodSec[curChart] )
            {
              pre_time[curChart] += _PeriodSec[curChart];
              pre_time[curChart] /= _PeriodSec[curChart];
              pre_time[curChart] *= _PeriodSec[curChart];
 
              //---- wenn es nicht an den Wochenende-Tage ist,
              if(SkipWeekEnd)
                {
                  if(TimeDayOfWeek(pre_time[curChart]) <= 0 || 
                     TimeDayOfWeek(pre_time[curChart]) > 5 ) 
                      continue; 
                  if(TimeDayOfWeek(pre_time[curChart]) == 5)
                    {
                      if(TimeHour(pre_time[curChart]) == 23 || 
                         TimeHour(pre_time[curChart]+_PeriodSec[curChart])==23) 
                          continue; 
                    }
                }
 
              //---- Schreiben Wir das übersprungene Bar in der Datei
              FileWriteInteger(HistoryHandle[curChart], pre_time[curChart],
                               LONG_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 
                              pre_close[curChart],DOUBLE_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 
                              pre_close[curChart],DOUBLE_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 
                              pre_close[curChart],DOUBLE_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 
                              pre_close[curChart],DOUBLE_VALUE);
              FileWriteDouble(HistoryHandle[curChart], 1,
                              DOUBLE_VALUE);
              FileFlush(HistoryHandle[curChart]);
 
              //---- Merken wir uns den Platz in der Datei, bevor das 0-te Bar geschrieben wird
              last_fpos[curChart] = FileTell( HistoryHandle[curChart] );
            }
 
          //---- Schreiben Wir das aktuelle Bar
          FileWriteInteger(HistoryHandle[curChart], now_time[curChart],
                           LONG_VALUE);
          FileWriteDouble(HistoryHandle[curChart], 
                              now_open[curChart],DOUBLE_VALUE);
          FileWriteDouble(HistoryHandle[curChart], 
                          now_low[curChart], DOUBLE_VALUE);
          FileWriteDouble(HistoryHandle[curChart], 
                          now_high[curChart],DOUBLE_VALUE);
          FileWriteDouble(HistoryHandle[curChart], 
                          now_close[curChart],DOUBLE_VALUE);
          FileWriteDouble(HistoryHandle[curChart], 
                          now_volume[curChart],DOUBLE_VALUE);
          FileFlush(HistoryHandle[curChart]);
 
          //---- Merken wir uns die Parameter des geschriebenen Bars
          pre_open[curChart] = now_open[curChart];
          pre_high[curChart] = now_high[curChart];
          pre_low[curChart] = now_low[curChart];
          pre_close[curChart] = now_close[curChart];
          pre_volume[curChart] = now_volume[curChart];
          pre_time[curChart] = now_time[curChart] / 
                                        _PeriodSec[curChart];
          pre_time[curChart] *= _PeriodSec[curChart];
 
          //---- Finden wir das Fenster, wohin wir aktuelle Preisnotierungen "senden" werden 
          if(hwnd[curChart] == 0)
            {
              hwnd[curChart] = WindowHandle(StringConcatenate( "ALL", 
                                            _Symbol[curChart]), 
                                            _Period[curChart]);
              if(hwnd[curChart] != 0) 
                  Print("< - - - Charts ", "ALL" + _Symbol[curChart], 
                        _Period[curChart], " gefunden! - - - >"); 
            }
          //---- und wenn Sie das Fenster gefunden haben, Erneuern Sie ihn
          if(hwnd[curChart] != 0) 
              PostMessageA(hwnd[curChart], WM_COMMAND, 33324, 0); 
        }
      Sleep(RefreshLuft);
    }
   for(curChart = 0; curChart < Charts; curChart ++)
    {
      if(HistoryHandle[curChart] >= 0)
        {
          //---- Schließen wir die Datei
          FileClose(HistoryHandle[curChart]);
          HistoryHandle[curChart] = -1;
        }
    }
   return(0);
  }
 

Jetzt versuchen wir den Experten im fünfminütigen Chart EURUSD mit dem Parameter ChartList zu starten, der "EURUSD1,GBPUSD1,EURGBP1" gleich ist, und wir Öffnen alle drei Charts im Status "off-line":

 



    Also, wir haben es geschafft.
    Alle 3 Charts werden gleichzeitig erneuert und falls es Gaps entstehen, werden sie "geflickt".

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

Beigefügte Dateien |
AllMinutes.mq4 (30.88 KB)
Informationsspeicherung und Informations-Darstellung Informationsspeicherung und Informations-Darstellung
Der Artikel ist den bequemen und praktischen Speichermethoden und der Informations-Darstellung gewidmet. Hier werden Alternativen zur Standard-Log-Datei des Terminals und der Funktion Comment() betrachtet.
Orders steuern ist einfach Orders steuern ist einfach
Der Artikel beschreibt die unterschiedlichen Arten der Kontrolle über den geöffneten Positionen und Pending Orders und soll bei der Erstellung eines Expertes helfen.
Die Synchronisation Experts,Skripts und Indikatoren Die Synchronisation Experts,Skripts und Indikatoren
Betrachtet wird die Notwendigkeit und allgemeine Grundlagen zum Aufbau des Programmkomplexes, der einen Experte, einen Skript und einen Indikator enthält.
Die Orders-Berechnung in einem großen Programm Die Orders-Berechnung in einem großen Programm
Betrachtet werden allgemeine Grundlagen der Orders-Berechnung in einem komplizierten Programm.