Datenzugriff organisieren

In diesem Abschnitt werden die Fragen behandelt, die mit Erfassung, Aufbewahren und Anforderung der Preisdaten (Timeserien) verbunden sind.

Datenerfassung vom Handelsserver

Bevor Preisdaten im Terminal MetaTrader 5 zugaenglich sind, muessen sie erfasst und verarbeitet werden. Für Datenerfassung ist der Anschluss an den Handelsserver MetaTrader 5 erforderlich. Daten werden vom Server erfasst auf Anfrage des Terminals in der Form der gepackten Blocks der Minutenbars.

Der Mechanismus der Serverreferenz hängt nicht davon ab, wie die Anfrage initiiert wurde - vom Benutzer bei der Navigation durch Chart oder vom Programm in der Sprache MQL5.

Aufbewahren der Zwischendaten

Die vom Server erfassten Daten werden verpackt und im spezialen Zwischenformat HCC aufbewahren. Daten für jedes Symbol werden in dem gesonderten Verzeichnis Terminalverzeichnis\bases\Name_Servers\history\Name_Symbols geschrieben . Z.B. Daten für Symbol EURUSD vom Handelsserver MetaQuotes-Demo werden sich im VerzeichnisKatalog_Terminals\bases\MetaQuotes-Demo\history\EURUSD\ befinden.

Daten werden in die Dateien mit der Endung .hcc geschrieben, jede Datei bewahrt Daten der Minutenbars für ein Jahr. ZB. due Datei 2009.hcc hat im Verzeichnis EURUSD Minutenbars für Symbol EURUSD für das Jahr 2009. Diese Daten werden für Vorbereitung der Preisdaten für alle Timeframes verwendet und sind nicht für den direkten Zugang bestimmt.

Erhaltung der Daten des notwendigen Timeframes aus den Zwischendaten

Dateien im Format HCC sind Quelle für Bauen der Preisdaten für angeforderte Timeframes im Format HC. Daten im Format HC sind Timeserien, die für den schnellen Zugang am meisten maximal sind. Sie werden nur auf Anfrage des Charts oder des mql5-Programms erzeugt, das Volumen ist nicht mehr als Parameterwert "Max bars in charts", und sie sind für weitere Verwendung in Dateien mit Endung hc aufbewahren.

für Ressourcenökonomie werden Daten des Timeframes gespeichert und aufbewahren im Operativspeicher wenn notwendig, beim langen Fehlen der Aufrufe werden sie aus dem Operativspeicher befreit und in der Datei gespeichert. Für jedes Timeframe werden Daten vorbereiten unabhängig davon, ob es schon fertige Daten für andere Timeframes gibt. Die Regeln des Zusammenstellens und Zugaenglichkeit der Daten sind für alle Timeframes gleich. D.h. Abgesehen davon, dass die Einheit der aufbewahrenen Daten im HCC Format Minutenbar ist, bedeutet das Vorhandensein der Daten im HCC Format nicht das Vorhandensein und Zugaenglichkeit der Daten des Timeframes M1 im HC Format in derselbenn Anzahl.    

Erhalten neuer Daten vom Server ruft automatischen Update der verwendeten Preisdaten im HC Format für alle timeframes und Nachkalkulation aller Anzeiger, die die Daten explizit verwenden als Eingabedaten für Berechnung.  

Parameter "Max bars in chart"

Parameter "Max bars in charts" beschraenkt die Anzahl der Bars im Format HC, die für Charts, Anzeiger und mql5-Programme zugaenglich sind. Diese Einschraenkung gilt für Daten aller Timeframes und ist in erster Linie für Sparen der Ressourcen bestimmt.

Bei der Einstellung der grossen Werte dieses Parameters muss an sich daran erinnern, dass wenn die Geschichte der Preisdaten für kleine Timeframes tief ist, kann der Speicher für Aufbewahren der Zeitreihen und Puffer hunderte Megabytes sein und Einschraenkungen des Operativspeichers für Programm des Client-Terminals erreichen (2GB für 32-Bit  MS Windows-Anwendungen).

Veränderung des Parameters "Max bars in charts" tritt in Kraft nach Wiederanlauf des Client-Terminals. Die Veränderung dieses Parameters selbst führt weder zum Anruf des Servers, um zusaetzliche Daten zu bekommen,  noch zur Bildung zusaetzlicher Bars der Zeitreihen. Anforderung der zusaetzlichen Preisdaten und Erneuerung der Zeitreihen unter Beachtung der neuen Einschraenkung erfolgen entweder beim Scrolling des Charts zum Bereich mit den fehlenden Daten oder bei der Anforderung der fehlenden Daten vom mql5-Programm.

Das Volumen der vom Server angeforderten Daten entspricht der erforderlichen Anzahl der Bars des vorgegebenen Timeframes unter Beachtung vom Parameter "Max bars in charts". Einschraenkung, vorgegeben vom Parameter ist nicht strikt, und in einigen Faellen kann die Anzahl der zugaenglichen Daten für Timeframe bisschen groesser sein, als der laufende Wert des Parameters.

Zugaenglichkeit der Daten

Anwesenheit der Daten im HCC Format oder  im fertigen für Verwendung HC Format bedeutet nicht, dass diese Daten für Darstellung auf dem Chart oder für Verwendung in mql5-Programmen vollstaendig zugaenglich sind.

Beim Zugang zu Preisdaten oder zu Indikatorwerten aus mql5-Programmen muss man behalten, dass ihrew Zugaenglichkeit für bestimmten Zeitpunket oder seit dem betimmten Zeitpunkt nicht garantiert wird. Das ist damit verbunden, dass zu Zwecken der  Ressourcenökonomie wird in  MetaTrader 5 nicht die ganze Kopie der erforderlichen Daten für  mql5-Programm aufbewahren, sondern es wird ein direkter Zugang zur Datenbank des Terminals gegeben.

Preisgeschichte für alle Timeframes wird aus allgemeinen Daten des HCC Formats gebildet und jedes Update der Daten vom Server führt zum Update der Daten für alle Timeframes und Umbeechnung der Indikatoren. Deswegen kann der Datenzugang zurückgewiesen werden auch wenn diese Daten vor einem Moment zugaenglich waren.  

Synchronisierung der Terminaldaten und der Serverdaten #

Da ein mql5-Programm  Daten für jedes Symbol und Timeframe aufrufen kann, ist es moeglich,dass Daten der erforderlichen Serie im Terminal noch nicht formiert sind oder Preisdaten mit dem Handelsserver noch nicht synchronisiert sind. In diesem Fall ist Wartezeit schwer vorherzusagen.

Algorithmen, die Wartezeit Zyklen verwenden sind keine beste Entscheidung. der einzige Ausschluss ist in diesem Fall Scripts, denn sie keine andere Algorithmenwahl haben wegen des Fehlens der Ereignisverarbitung. Für Benutzerindikatoren sind solche Algorithmen, wie auch andere Wartezeitzyklen strikt nicht empfohlen, denn sie führen zur Beendigung der Berechnung aller Indikatoren und einer anderen Verarbeitung der Preisdaten für das Symbol.

für Experten und Benutzerindikatoren ist es besser Ereignismodel der Verarbeitung zu verwenden. Wenn es bei der Verarbeitung des Ereignisses OnTick() oder OnCalculate() unmoeglich war, alle erforderlichen Daten der Angeforderten Zeitreihe zu bekommen, muss man Ereignisbearbeiter verlassen und vertrauen auf Datenzugang beim naechsten Aufruf des Bearbeiters.

Beispiel eines Scripts für Laden der Geschichte

Betrachten wir ein Beispiel des Scripts, das Anforderung erfuellt, Geschichte vom Handelsserver für das angegebene Symbol zu erhalten. Script ist dafür bestimmt, das erforderliche Instrument im Chart ablaufen zu lassen, Timeframe ist nicht von Bedeutung, denn, es wurde schon erwaehnt, alle Preisdaten kommen vom Handelsserver als verpackte Minutendaten, aus denen danach jede vorbestimmte Zeitreihe gebildet wird.

Schreiben wir alle Handlungen bezueglich Datenerhaltung als eine gesonderte Funktion CheckLoadHistory(symbol, timeframe, start_date):

int CheckLoadHistory(string symbol,ENUM_TIMEFRAMES period,datetime start_date)
  {
  }

Funktion ist CheckLoadHistory() ist als universelle Funktion gedacht, die aus jedem Programm (Expert, Script oder Indikator) aufgerufen werden kann und darum muss sie drei Eingabeparameter haben: Symbolname, Periode und Anfangsdatum, von dem wir Preisgeschichte brauchen.

Geben wir in den Kode der Funktion alle notwendigen Pruefungen ein, bevor wir die fehlende Geschichte fordern. Erstens, überzeugenwir uns, dass Symbolname und Periodenwert korrekt sind:

   if(symbol==NULL || symbol==""symbol=Symbol();
   if(period==PERIOD_CURRENT)     period=Period();

Dann überzeugen wir uns davon, dass das angegebene Symbol im Fenster MarketWatch zugaenglich ist, d.h. die Geschichte des angegebenen Symbols auch beim Senden der Anforderung  zum Handelsserver zugaenglich ist. Wenn solches Symbol in MarketWatch fehlt fuegen wir das zu durch die Funktion SymbolSelect().

   if(!SymbolInfoInteger(symbol,SYMBOL_SELECT))
     {
      if(GetLastError()==ERR_MARKET_UNKNOWN_SYMBOLreturn(-1);
      SymbolSelect(symbol,true);
     }

Jetzt ist es notwendig, Anfangsdatum der zugaenglichen Geschichte für das angegebene Paar Symbol/Periode zu erhalten. Der Wert des Eingabeparameters  startdate, der der Funktion CheckLoadHistory() übertragen wurde, kann in Interval der schon zugaenglichen Geschichte gelangen und dann ist keine Anforderung zum Handelsserver erforderlich. Für Erhaltung des ersten Datums für Symbol-Periode ist zum jetzigen Zeitpunkt die Funktion SeriesInfoInteger() mit dem Modifikator SERIES_FIRSTDATE bestimmt.

   SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date);
   if(first_date>0 && first_date<=start_date) return(1);

Die naechste Pruefung ist die Pruefung des Programmtyps, aus dem die Funktion aufgerufen wird. Erinnern wir daran, dass Senden der Anforderung, Zeitreihe mit derselben Periode wie beim Indikator zu update ist gar nicht erwuenschenswert. Unerwuenschtheit der Datenanforderung für das selbe Symbol-Periode, wie beim Indikator ist dadurch bedingt, dass Update der historischen Daten in demselben Thread erfolgt, wo Indikator arbeitet. Darum ist clinch hoechstwarscheinlich. Für die Pruefung verwenden wir die Funktion MQL5InfoInteger() mit dem Modifikator MQL5_PROGRAM_TYPE.

   if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol)
      return(-4);

Wenn wir alle Pruefungen erfolgreich durchgelaufen haben, machen wir den letzten Versuch ohne Aufruf des Handelsservers. Zuerst erfahren wir über das Anfangsdatum, für das Minutendaten in  HCC Format zugaenglich sind. Rufen wir diesen Wert durch die Funktion SeriesInfoInteger() mit dem Modiofikator SERIES_TERMINAL_FIRSTDATE auf und vergleichen ihn mitdem Parameterwert  start_date.

   if(SeriesInfoInteger(symbol,PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_date))
     {
      //--- there is loaded data to build timeseries
      if(first_date>0)
        {
         //--- force timeseries build
         CopyTime(symbol,period,first_date+PeriodSeconds(period),1,times);
         //--- check date
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            if(first_date>0 && first_date<=start_date) return(2);
        }
     }

Wenn nach aller Pruefungen Durchführungsthread im Koerpder der Funktion CheckLoadHistory() noch bleibt, dann bedeutet es, es ist notwendig, die fehlenden Preisdaten vom Handelsserver anzufordern. Vor allem erfahren wir über den Wert "Max bars in chart" durch die Funktion TerminalInfoInteger():

  int max_bars=TerminalInfoInteger(TERMINAL_MAXBARS);

Diese Funktion brauchen wir, um eAnforderung von extra Daten vorzubeugen. Dann klaeren wir das erste Datum in der Geschichte des Symbol sauf dem Handelsserver auf (unabhängig von der Periode) durch die schon bekannte Funktion SeriesInfoInteger() mit dem Modifikator SERIES_SERVER_FIRSTDATE.

   datetime first_server_date=0;
   while(!SeriesInfoInteger(symbol,PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date) && !IsStopped())
      Sleep(5);

Da eine Anforderung eine asynchrone Operation ist, wird die Funktion mit der kleinen Verzoegerung 5 Milisekuden aufgerufen bis die Variable  first_server_date den Wert erhaelt oder Zyklusdurchführung vom Benutzer unterbrochen wird (IsStopped(), in diesem Fall gibt den Wert true zurück). Geben wir den korrekten Wert des Anfangsdatums an, von dem wir Preisdaten vom Handelsserver anfordern.

   if(first_server_date>start_date) start_date=first_server_date;
   if(first_date>0 && first_date<first_server_date)
      Print("Warning: first server date ",first_server_date,
            " for ",symbol," does not match to first series date ",first_date);

Wenn das Anfangsdatum first_server_date auf dem Server niedriger als Anfangsdatum first_date für Symbol in HCC Format ist, wird im Journal die entsprechende Nachricht ausgegeben werden.

Jetzt sind wir bereit, Anforderung beim Handelsserver zu machen, um fehlende Preisdaten zu bekommen. Anforderung machen wir in Form eines Zyklus und fangen wir an, ihren Koerper auszufuellen:  

   while(!IsStopped())
     {
      //1. warten auf Synchronisierung zwischen umgebildeten Zeitreihe und Zwischengeschichte in HCC Format
      //2. laufende Anzahl der Bars in dieser Zeitreihe erhalten 
      //   Wenn bars groesser ist, als Max_bars_in_chart, können wir verlassen, die Arbeit ist zu Ende
      //3. Erhalten wir Anfangsdatum first_date in der umgebildeten Zeitreihe und vergleichen es mit dem Wert 
      //   start_date wenn first_date kleiner ist als start_date, können wir verlassen, Arbeit ist zu Ende
      //4. Fordern wir vom Handelsserver einen neuen Teil der Geschichte - 100 Bars von der letzten 
      //   zugaenglichen Bar mit Nr bars
     }

Die ersten drei Punkte werden durch schon bekannte Methoden realisiert.

   while(!IsStopped())
     {
      //--- 1.Warten darauf, bis Umbildung der Zeitreihe beendet wird 
      while(!SeriesInfoInteger(symbol,period,SERIES_SYNCHRONIZED) && !IsStopped())
         Sleep(5);
      //--- 2.fordern wir an, wieviel Bars wir jetzt haben 
      int bars=Bars(symbol,period);
      if(bars>0)
        {
         //--- Anzahl der Bars ist mehr, als man auf dem Chart zeigen kann, verlassen wir das
         if(bars>=max_bars) return(-2); 
         //--- 3. erfahren wir über das Anfangsdatum in der Zeitreihe
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            // Anfangsdatum ist früher als angefordert, Aufgabe ist erfuellt 
            if(first_date>0 && first_date<=start_date) return(0);
        }
      //4. Fordern wir vom Handelsservereinen neuen Teil der Geschichte von 
      //   der letzten zugaenglichen Bar mit der Nummer bars an - 100 Bars  
     }

Es bleibt der letzter vierte Punkt - unmittelbare Anfirderung der Geschichte. Wir können nicht direkt Serveraufrufen, aber jede Copy-Funktion beim Fehlen der Geschichte in HCC Format initiiert automatisch das Senden dieser Anforderung vom Terminal zum Handelsserver. Da die Zeit des ersten Anfangsdatums in der Variable first_date als das einfachste und natuerlichste Kriterium um Grad der Erfuellung der Anforderung  einzuschaetzen, wird es am leichtesten sein, die Funktion CopyTime() zu verwenden.

Beim Aufruf der Funktionen, durch die Kopieren jeder Daten von Zeitreihen erfolgt, muss man bemerken, dass Parameter start (Barnummer, mit der Kopieren der Preisdaten beginnt werden muss) immer innerhalb der zugaenglichen Geschichte des Terminlas sein muss. wenn wir nur 100 Bars haben, ist es sinnlos, 300 Bars zu kopieren versuchen, angefangen mit der Bar mit Index 500. Eine solche Anforderung wird als fehlerhafte angenommen werden und wird nicht verarbeitet, d.h. keine Geschichte vom Handelsserver wird geladen.

Eben darum werden wir 100 Bars kopieren, angefangen mit der Bar mit Index. Das garantiert sanfte Ladung der Geschichte vom Handelsserver, dabei wird mehr als angeforderte 100 Bars ausgeladen werden, Server gibt die Geschichte mit überschuss zurück.

   int copied=CopyTime(symbol,period,bars,100,times);

Nach Kopieren muss man die Anzahl der kopiertenElemente analysieren, wenn der Versuch erfolglos geworden ist, wird der Wert der Variable copied gleich Null sein und der Wert des Counters  fail_cnt wird um  1 steigen. Nach 100 erfolglosen Versuche wird die Funktionsarbeit unterbrochen.

int fail_cnt=0;
...
   int copied=CopyTime(symbol,period,bars,100,times);
   if(copied>0)
     {
      //--- pruefen wir die Daten 
      if(times[0]<=start_date)  return(0);  // der kopierte Wert ist kleiner, fertig 
      if(bars+copied>=max_bars) return(-2); // Anzahl der Bars ist mehr, als auf dem Chart 
                                            // gezeigt werden kann, fertig
      fail_cnt=0;
     }
   else
     {
      //--- nicht mehr als 100 erfolglose Versuche nacheinander 
      fail_cnt++;
      if(fail_cnt>=100) return(-5);
      Sleep(10);
     }
 

So, ist in der Funktion nicht nur korrekte Verarbeitung der laufenden Situation zu jedem Zeitpunkt der Durchführung organisiert, sondern wird auch Beendigungskode zurückgegeben, den wir nach Aufruf der Funktion CheckLoadHistory() für die Erhaltung der zusaetzlichen Information verarbeiten können. ZB. auf dieser Weise:

   int res=CheckLoadHistory(InpLoadedSymbol,InpLoadedPeriod,InpStartDate);
   switch(res)
     {
      case -1 : Print("Unbekanntes Symbol",InpLoadedSymbol);                                          break;
      case -2 : Print("Angeforderte Anzahl der Bars ist mehr, als es auf Chart gezeigt werden kann"); break;
      case -3 : Print("Durchführung wurde vom Benutzer unterbrochen");                                break;
      case -4 : Print("Indikator muss nicht eigene Namen laden");                                     break;
      case -5 : Print("Laden ist nicht erfolgreich");                                                 break;
      case  0 : Print("alle Daten sind geladen");                                                     break;
      case  1 : Print("Anzahl der zugaenglichen Daten in der Zeitreihe ist genug");                   break;
      case  2 : Print("Zeitreihe ist aus vorhandenen Daten des Terminals gebildet");                  break;
      default : Print("Ergebnis der Durchführung ist nicht bestimmt");
     }

Der vollstaendige Funktionskode ist in Script angegeben, das richtiges Organisieren des Zuganges zu jeden Daten mit der Verarbeitung des Ergebnisses der Anforderung demonstriert.

Kode:

//+------------------------------------------------------------------+
//|                                              TestLoadHistory.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.02"
#property script_show_inputs
//--- input parameters
input string          InpLoadedSymbol="NZDUSD";   // Symbol to be load
input ENUM_TIMEFRAMES InpLoadedPeriod=PERIOD_H1;  // Period to be load
input datetime        InpStartDate=D'2006.01.01'; // Start date
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print("Start load",InpLoadedSymbol+","+GetPeriodName(InpLoadedPeriod),"from",InpStartDate);
//---
   int res=CheckLoadHistory(InpLoadedSymbol,InpLoadedPeriod,InpStartDate);
   switch(res)
     {
      case -1 : Print("Unknown symbol ",InpLoadedSymbol);             break;
      case -2 : Print("Requested bars more than max bars in chart"); break;
      case -3 : Print("Program was stopped");                        break;
      case -4 : Print("Indicator shouldn't load its own data");      break;
      case -5 : Print("Load failed");                                break;
      case  0 : Print("Loaded OK");                                  break;
      case  1 : Print("Loaded previously");                          break;
      case  2 : Print("Loaded previously and built");                break;
      default : Print("Unknown result");
     }
//---
   datetime first_date;
   SeriesInfoInteger(InpLoadedSymbol,InpLoadedPeriod,SERIES_FIRSTDATE,first_date);
   int bars=Bars(InpLoadedSymbol,InpLoadedPeriod);
   Print("First date",first_date,"-",bars,"bars");
//---
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CheckLoadHistory(string symbol,ENUM_TIMEFRAMES period,datetime start_date)
  {
   datetime first_date=0;
   datetime times[100];
//--- check symbol & period
   if(symbol==NULL || symbol==""symbol=Symbol();
   if(period==PERIOD_CURRENT)     period=Period();
//--- check if symbol is selected in the MarketWatch
   if(!SymbolInfoInteger(symbol,SYMBOL_SELECT))
     {
      if(GetLastError()==ERR_MARKET_UNKNOWN_SYMBOLreturn(-1);
      SymbolSelect(symbol,true);
     }
//--- check if data is present
   SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date);
   if(first_date>0 && first_date<=start_date) return(1);
//--- don't ask for load of its own data if it is an indicator
   if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol)
      return(-4);
//--- second attempt
   if(SeriesInfoInteger(symbol,PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_date))
     {
      //--- there is loaded data to build timeseries
      if(first_date>0)
        {
         //--- force timeseries build
         CopyTime(symbol,period,first_date+PeriodSeconds(period),1,times);
         //--- check date
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            if(first_date>0 && first_date<=start_date) return(2);
        }
     }
//--- max bars in chart from terminal options
   int max_bars=TerminalInfoInteger(TERMINAL_MAXBARS);
//--- load symbol history info
   datetime first_server_date=0;
   while(!SeriesInfoInteger(symbol,PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date) && !IsStopped())
      Sleep(5);
//--- fix start date for loading
   if(first_server_date>start_date) start_date=first_server_date;
   if(first_date>0 && first_date<first_server_date)
      Print("Warning: first server date ",first_server_date,
            " for ",symbol," does not match to first series date ",first_date);
//--- load data step by step
   int fail_cnt=0;
   while(!IsStopped())
     {
      //--- wait for timeseries build
      while(!SeriesInfoInteger(symbol,period,SERIES_SYNCHRONIZED) && !IsStopped())
         Sleep(5);
      //--- ask for built bars
      int bars=Bars(symbol,period);
      if(bars>0)
        {
         if(bars>=max_bars) return(-2);
         //--- ask for first date
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            if(first_date>0 && first_date<=start_date) return(0);
        }
      //--- copying of next part forces data loading
      int copied=CopyTime(symbol,period,bars,100,times);
      if(copied>0)
        {
         //--- check for data
         if(times[0]<=start_date)  return(0);
         if(bars+copied>=max_bars) return(-2);
         fail_cnt=0;
        }
      else
        {
         //--- no more than 100 failed attempts
         fail_cnt++;
         if(fail_cnt>=100) return(-5);
         Sleep(10);
        }
     }
//--- stopped
   return(-3);
  }
//+------------------------------------------------------------------+
//| gibt Zeilenwert der Periode zurück                               |
//+------------------------------------------------------------------+
string GetPeriodName(ENUM_TIMEFRAMES period)
  {
   if(period==PERIOD_CURRENTperiod=Period();
//---
   switch(period)
     {
      case PERIOD_M1:  return("M1");
      case PERIOD_M2:  return("M2");
      case PERIOD_M3:  return("M3");
      case PERIOD_M4:  return("M4");
      case PERIOD_M5:  return("M5");
      case PERIOD_M6:  return("M6");
      case PERIOD_M10return("M10");
      case PERIOD_M12return("M12");
      case PERIOD_M15return("M15");
      case PERIOD_M20return("M20");
      case PERIOD_M30return("M30");
      case PERIOD_H1:  return("H1");
      case PERIOD_H2:  return("H2");
      case PERIOD_H3:  return("H3");
      case PERIOD_H4:  return("H4");
      case PERIOD_H6:  return("H6");
      case PERIOD_H8:  return("H8");
      case PERIOD_H12return("H12");
      case PERIOD_D1:  return("Daily");
      case PERIOD_W1:  return("Weekly");
      case PERIOD_MN1return("Monthly");
     }
//---
   return("unknown period");
  }