Wie ein Marktindikator für alle nicht standarten Charts geschrieben werden soll
Inhaltsverzeichnis
- Einführung
- 1. Der Indikator "IndCreateOffline", der ein autonomes Chart erstellen wird
- 1.1. Die Korrektur des "Kopfs" vom Indikator
- 1.2. Die Kontrolle des Typs vom Chart
- 1.3. Die Erstellungsfunktion des Titels von der History-Datei
- 1.4. Der erste Eintrag der History in die Datei *.hst
- 1.5. Die Aufnahme der Online-Notierungen
- 1.6. Die Korrektur der Funktion OnCalculate()
- 2. Der Inkikatorsstart auf einem autonomen Chart
- 2.1. Die Aktualisierung des autonomen Charts mit ChartSetSymbolPeriod()
- 2.2. Servicemodus
- 2.3. Das Kopieren des Indikators in ein autonomes Chart
- 2.4. Die Integration in MACD.mq4
- 2.5. Die ökonomische Neuberechnung des Indikators auf dem autonomen Chart
- 2.6. Das Laden der History. Das autonome Chart
- 3. Der Indikator, der Renko-Bars zeigt
- 3.1. Die Konstruktionsregeln der Range-Bars
- 3.2. Die Bildung der "Ziegel" auf historischen Daten
- 3.3. Die Online-Arbeit des Indikators
- 4. Der Indikator, der ein nicht standarten Symbol erstellen wird - das Dollar-Index USDx
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:
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.
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.
//--- 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:
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:
- es ist der erste Start des Indikators;
- 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.
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:
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.
in Abb. 5. Der vorherige Ziegel - bullish
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 = Open; High = Open; Close = High - Renko size; Low = 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":
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
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.