
Visualisierung der Handelsgeschäfte auf dem Chart (Teil 2): Grafische Anzeige der Daten
Einführung
In diesem Artikel vervollständigen wir das Skript zur Visualisierung von Handelsgeschäften (deals) auf dem Chart, mit dessen Implementierung wir in „Visualisierung der Handelsgeschäfte auf dem Chart (Teil 1): Auswahl eines Zeitraums für die Analyse“ begonnen haben. Wir werden den Code schreiben, um Daten für ein einzelnes, vom Nutzer ausgewähltes Handelsgeschäft auszuwählen und die erforderlichen Datenobjekte auf dem Chart zu zeichnen, die wir dann als Druckbild der entsprechenden Charts in einer Datei speichern werden. Das Skript wird es uns ermöglichen, viel Zeit bei der technischen Arbeit im Zusammenhang mit der Erstellung von Handelsgeschäftsdiagrammen sowie bei der Speicherung in Druckbildschirmen für die nachträgliche Analyse zu sparen. Wer keine Zeit für die Zusammenstellung der Projekte aufwenden möchte, kann eine fertige Version des Skripts vom Markt herunterladen.
Auswahl der Daten für ein Handelsgeschäft
Im Gegensatz zur Auswahl von Daten zu Handelsgeschäften für einen bestimmten Zeitraum vereinfacht die Auswahl von Daten zu einem einzelnen Handelsgeschäft die Umsetzung des Falles der historischen Auftragsauswahl erheblich. Der Hauptunterschied besteht darin, dass wir anstelle der vordefinierten Terminalfunktion HistorySelect() die Methode HistorySelectByPosition() verwenden, um historische Daten abzufragen. Die Parameter der Methode sollten POSITION_IDENTIFIER erhalten, die wir im MetaTrader 5-Terminal finden können (Ansicht -> Werkzeugleiste -> Kontohistorie -> Spalte Ticket). Der Wert ist über die globale Eingabevariable inp_d_ticket an das Skript zu übergeben.
In allen anderen Aspekten wiederholt die Logik des Falls Select_one_deal vollständig die Implementierung der Logik des vorherigen Falls und wird im folgenden Code vollständig dargestellt, wobei die gleichen Informationen für die Nutzer eingefügt werden.
//--- if one deal is needed case Select_one_deal: res = MessageBox("You have selected analysis of one deal. Continue?","",MB_OKCANCEL); // informed in the message if(res == IDCANCEL) // if interrupted by user { printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); // informed in the journal return; // interrupted } MessageBox("Please press 'Ok' and wait for the next message until script will be done."); // informed in the message //--- select by one position if(HistorySelectByPosition(inp_d_ticket)) // select position by id { int total = HistoryDealsTotal(); // total deals if(total <= 0) // if nothing found { printf("%s - %d -> Deal was not found.",__FUNCTION__,__LINE__); // notify MessageBox("Deal was not found with this tiket: "+IntegerToString(inp_d_ticket)+". Script is done."); // informed in the message return; } for(int i=0; i<total; i++) // iterate through the number of deals { //--- try to get deals ticket if((ticket=HistoryDealGetTicket(i))>0) // took the deal number { //--- get deals properties position_id = HistoryDealGetInteger(ticket,DEAL_POSITION_ID); // took the main id entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);// entry or exit? if(entry == DEAL_ENTRY_IN) // if this is an entry { open = HistoryDealGetDouble(ticket,DEAL_PRICE); // take open price time_open =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME); // take open time symbol=HistoryDealGetString(ticket,DEAL_SYMBOL); // take symbol stop_loss = HistoryDealGetDouble(ticket,DEAL_SL); // take Stop Loss take_profit = HistoryDealGetDouble(ticket,DEAL_TP); // take Take Profit //--- magic = (int)HistoryDealGetInteger(ticket,DEAL_MAGIC); // take Magic comment=HistoryDealGetString(ticket,DEAL_COMMENT); // take comment externalID=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID); // take external id volume = HistoryDealGetDouble(ticket,DEAL_VOLUME); // take volume commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION); // take commission value } if(entry == DEAL_ENTRY_OUT) // if this is an exit { close = HistoryDealGetDouble(ticket,DEAL_PRICE); // take close price time_close =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);// take close time //--- reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); // take reason swap = HistoryDealGetDouble(ticket,DEAL_SWAP); // take swap profit = HistoryDealGetDouble(ticket,DEAL_PROFIT); // take profit fee = HistoryDealGetDouble(ticket,DEAL_FEE); // take fee } //--- enter data into the main storage //--- check if there is such id if(Find(PositionID,position_id)==-1) // if there is no such deal, { //--- change the dimensions of the arrays ArrayResize(arr_time_open,ArraySize(arr_time_open)+1); // open time ArrayResize(arr_time_close,ArraySize(arr_time_close)+1); // close time ArrayResize(arr_symbol,ArraySize(arr_symbol)+1); // symbols ArrayResize(arr_stop_loss,ArraySize(arr_stop_loss)+1); // stop levels ArrayResize(arr_take_profit,ArraySize(arr_take_profit)+1);// profits ArrayResize(arr_open,ArraySize(arr_open)+1); // entries ArrayResize(arr_close,ArraySize(arr_close)+1); // exits ArrayResize(PositionID,ArraySize(PositionID)+1); // position id //--- ArrayResize(arr_magic,ArraySize(arr_magic)+1); // Magic ArrayResize(arr_extermalID,ArraySize(arr_extermalID)+1); // external id ArrayResize(arr_comment,ArraySize(arr_comment)+1); // comment ArrayResize(arr_volume,ArraySize(arr_volume)+1); // volume ArrayResize(arr_commission,ArraySize(arr_commission)+1); // commission ArrayResize(arr_reason,ArraySize(arr_reason)+1); // reason ArrayResize(arr_swap,ArraySize(arr_swap)+1); // swap ArrayResize(arr_profit,ArraySize(arr_profit)+1); // profit ArrayResize(arr_fee,ArraySize(arr_fee)+1); // fee PositionID[ArraySize(arr_time_open)-1]=position_id; // id if(entry == DEAL_ENTRY_IN) // if this is an entry { arr_time_open[ ArraySize(arr_time_open)-1] = time_open; // deal time arr_symbol[ ArraySize(arr_symbol)-1] = symbol; // instrument symbol arr_stop_loss[ ArraySize(arr_stop_loss)-1] = stop_loss; // deal stop loss arr_take_profit[ ArraySize(arr_take_profit)-1] = take_profit; // deal take profit arr_open[ ArraySize(arr_open)-1] = open; // open price //--- arr_magic[ ArraySize(arr_magic)-1] = magic; // Magic arr_comment[ ArraySize(arr_comment)-1] = comment; // comment arr_extermalID[ ArraySize(arr_extermalID)-1] = externalID; // external id arr_volume[ ArraySize(arr_volume)-1] = volume; // volume arr_commission[ ArraySize(arr_commission)-1] = commission; // commission } if(entry == DEAL_ENTRY_OUT) // if this is an exit { arr_time_close[ ArraySize(arr_time_close)-1] = time_close; // close time arr_close[ ArraySize(arr_close)-1] = close; // close prices //--- arr_reason[ ArraySize(arr_reason)-1] = reason; // reason arr_swap[ ArraySize(arr_swap)-1] = swap; // swap arr_profit[ ArraySize(arr_profit)-1] = profit; // profit arr_fee[ ArraySize(arr_fee)-1] = fee; // fee } } else { int index = Find(PositionID,position_id); // if there was a record already, if(entry == DEAL_ENTRY_IN) // if this was an entry { arr_time_open[index] = time_open; // deal time arr_symbol[index] = symbol; // symbol arr_stop_loss[index] = stop_loss; // deal stop loss arr_take_profit[index] = take_profit; // deal take profit arr_open[index] = open; // open price //--- arr_magic[index] = magic; // Magic arr_comment[index] = comment; // comment arr_extermalID[index] = externalID; // external id arr_volume[index] = volume; // volume arr_commission[index] = commission; // commission } if(entry == DEAL_ENTRY_OUT) // if this is an exit { arr_time_close[index] = time_close; // deal close time arr_close[index] = close; // deal close price //--- arr_reason[index] = reason; // reason arr_swap[index] = swap; // swap arr_profit[index] = profit; // profit arr_fee[index] = fee; // fee } } } } } else { printf("%s - %d -> Error of selecting history deals: %d",__FUNCTION__,__LINE__,GetLastError()); // informed in the journal printf("%s - %d -> Deal was not found.",__FUNCTION__,__LINE__); // informed in the journal MessageBox("Deal was not found with this tiket: "+IntegerToString(inp_d_ticket)+". Script is done."); // informed in the message return; } break;
Nachdem nun beide Optionen beschrieben wurden und alle Speicher während der Programmausführung mit den erforderlichen Daten gefüllt wurden, können wir mit der Anzeige dieser Daten auf den Terminalkarten beginnen.
Anzeige der benötigten Charts
Um Handelsgeschäfte auf dem Chart zu platzieren, müssen wir zunächst auf der Programmebene ein neues Fenster mit dem gewünschten Symbol öffnen, die notwendigen Ansichts-Einstellungen vornehmen, einschließlich einer individuellen Verschiebung des Einzugs nach rechts, damit das gesamte Handelsgeschäft gut sichtbar ist, und eine vordefinierte Funktion aufrufen, die einen Druckbildschirm im gewünschten Ordner speichert.
Zunächst deklarieren wir die lokalen Variablen, die wir benötigen, um das Fenster des gewünschten Charts zu öffnen. Die Variable „bars“ speichert den Offset-Wert für das Chart auf der rechten Seite, die Variablen „chart_width“ und „chart_height“ speichern die entsprechenden Größen für die Speicherung, und das Handle des neuen Charts wird, wenn es geöffnet wird, in der Variablen „handle“ gespeichert, um in Zukunft auf das Chart zugreifen zu können.
//--- data collected, moving on to printing int bars = -1; // number of bars in a shift int chart_width = -1; // chart width int chart_height =-1; // chart height long handle =-1; // chart handle
Bevor wir eine Anfrage zum Öffnen neuer Symbolfenster starten, sollten wir die Gültigkeit dieser Symbole aus der Historie abfragen. Diese Überprüfung ist unbedingt erforderlich, um den Fehler zu vermeiden, ein „nicht existierendes Symbol“ auf dem Konto zu eröffnen. Ich denke, es ist notwendig, hier zu erklären, woher ein „nicht existierendes Symbol“ kommen kann, wenn es in der Handelsgeschichte gespeichert wurde, was bedeutet, dass es einmal existierte.
Zunächst einmal kann dies mit den Broker-Kontotypen zusammenhängen. Heutzutage bieten die meisten Broker den Händlern mehrere Kontooptionen an, um ihre Nutzung so profitabel und bequem wie möglich zu gestalten, was die verwendeten Handelsstrategien betrifft. Einige Konten erheben eine Provision für die Eröffnung von Handelsgeschäften, haben aber einen sehr niedrigen Spread, während andere Konten einen hohen Spread, aber keine Gebühr pro Handelsgeschäft haben. Daher zahlen Händler, die mittelfristig handeln, möglicherweise keine Provision für ein Handelsgeschäft, während die Größe des Spreads beim mittelfristigen Handel nicht so wichtig ist. Umgekehrt würden Händler, die intraday mit kleinen Impulsen handeln, lieber eine Provision für die Eröffnung eines Handelsgeschäfts zahlen, als einen Verlust hinzunehmen, nur weil sich der Spread „plötzlich“ ausgeweitet hat. In der Regel fassen Makler solche Konditionen in Kontotypen zusammen, z. B. Standard, Gold, Platin, ESN, und geben für jedes Konto einen Symbolnamen an. Im Falle des EURUSD-Paares auf einem Standardkonto könnte das Symbol auf einem anderen Kontotyp je nach Broker wie EURUSDb, EURUSDz oder EURUSD_i aussehen.
Außerdem können sich die Symbolnamen je nach Verfallsdatum bestimmter Instrumente, die nicht mit dem Handel von Währungspaaren auf dem Forex zusammenhängen, ändern, aber wir werden diesen Punkt hier nicht im Detail betrachten, da der Artikel immer noch speziell den Währungspaaren gewidmet ist.
Eine weitere Bedingung für die Notwendigkeit, die Gültigkeit der Symbole zu überprüfen, ist das rein technische Fehlen eines Abonnements für die erforderlichen Instrumente im Fenster der Marktübersicht des Terminals. Selbst wenn ein Symbolname auf dem autorisierten Konto existiert, aber nicht im Kontextmenü des Terminals (Ansicht -> Marktübersicht) ausgewählt ist, können wir den entsprechenden Chart nicht öffnen, was zu einem Fehler der aufrufenden Funktion führt.
Wir beginnen mit der Implementierung der Prüfung, indem wir eine Schleife für die Iteration über jedes Werkzeug in unserem Speicher einrichten, wie unten gezeigt.
for(int i=0; i<ArraySize(arr_symbol); i++) // iterate through all deal symbols
Um die Gültigkeit eines in unserem Container gespeicherten Symbols zu überprüfen, werden wir die vordefinierte Terminalfunktion SymbolSelect() verwenden. Der erste Parameter, den wir übergeben, ist der Symbolname im Format string. Dies ist ein Symbol, dessen Gültigkeit wir überprüfen wollen. An zweiter Stelle steht der logische Wert von „true“. Die Übergabe von „true“ als zweiter Parameter bedeutet, dass das angegebene Instrument, wenn es gültig, aber nicht in der „Marktübersicht“ ausgewählt ist, dort automatisch ausgewählt werden soll. Die vollständige Prüflogik sieht wie folgt aus.
//--- check for symbol availability for(int i=0; i<ArraySize(arr_symbol); i++) // iterate through all deal symbols { if(!SymbolSelect(arr_symbol[i],true)) // check if the symbol is in the book and add if not { printf("%s - %d -> Failed to add a symbol %s to the marketbook. Error: %d", __FUNCTION__,__LINE__,arr_symbol[i],GetLastError()); // informed in the journal MessageBox("Failed to add a symbol to the marketbook: "+arr_symbol[i]+ ". Please select 'show all' in the your market book and try again. Script is done."); // informed in the message return; // if failed, abort } }
Wenn also die Gültigkeitsprüfung des Symbols nicht bestanden wird, brechen wir die Programmausführung mit entsprechenden Hinweisen für den Nutzer ab. Wenn alle Gültigkeitsprüfungen bestanden sind, können wir die erforderlichen Symbolkarten direkt im Terminal öffnen.
Zunächst stellen wir die Hilfsvariable deal_close_date des Datentyps MqlDateTime bereit, mit deren Hilfe wir alle gespeicherten Charts bequem in die entsprechenden Zeitspannenordner sortieren können. Für die explizite Reduktion des Datentyps datetime auf den Datentyp MqlDateTime in unserem Speicher verwenden wir die vordefinierte Terminalfunktion TimeToStruct(), wie unten dargestellt.
MqlDateTime deal_close_date; // deal closure date in the structure TimeToStruct(arr_time_close[i],deal_close_date); // pass date to the structure
Die Charts sind entsprechend den nutzerdefinierten Daten in den Variablen main_graph, addition_graph, addition_graph_2 und addition_graph_3 zu zeichnen. Wenn die Variable den Wert der Enumeration PERIOD_CURRENT enthält, wird kein Chart gezeichnet. Wenn ein bestimmter Wert in die Variable eingegeben wird (z. B. PERIOD_D1), wird dieses Chart zum Zeichnen verwendet. Wir werden diese Prüfung für alle eingegebenen Variablen in der folgenden Form durchführen (die Hauptvariable ist unten als Beispiel dargestellt):
//--- check the main one if(main_graph != PERIOD_CURRENT) // if the main one selected
Das Zeichnen jedes Charts beginnt mit dem Öffnen eines neuen Charts mit dem gewünschten Symbol. Das SymbolChart wird mit der vordefinierten Terminalfunktion ChartOpen() geöffnet, wobei das gewünschte Symbol und der Zeitrahmen aus dem Speicher übergeben werden, wie unten dargestellt.
//--- open the required chart handle = ChartOpen(arr_symbol[i],main_graph); // open the necessary symbol chart
Sobald das Chart geöffnet ist, wenden wir alle oben erwähnten Standard-Nutzereinstellungen auf es an. Dazu verwenden wir die vordefinierte Terminalfunktion ChartApplyTemplate(), die uns dabei sehr hilft und es uns erspart, den Code selbst zu schreiben. Die Parameter der Funktion ChartApplyTemplate() erhalten das Handle des Charts, das durch den Aufruf der Funktion ChartOpen() erhalten wurde, sowie den Namen der Vorlage, die vom Nutzer für den Zeitrahmen des Handelsgeschäfts im Format dailyHistorytemp angegeben wurde. Der Code für den Aufruf der Anwendungsfunktion der Vorlage ist unten dargestellt.
ChartApplyTemplate(handle,main_template); // apply template
Machen wir hier einen kleinen Exkurs für diejenigen, die bisher noch keine Templates im MetaTrader 5 Terminal verwendet haben. Wenn wir eine „hässliche“ Vorlage verwenden, kann sich der gespeicherte Druckbildschirm des Handelsgeschäfts als „irritierend“ oder sogar „nutzlos“ erweisen. Folgen Sie diesen Schritten, um Ihre eigene dailyHistorytemp-Vorlage zu erstellen:
- Öffnen Sie den Chart eines beliebigen Symbols über Datei - Neuer Chart.
- Sobald der Chart geöffnet ist, drücken Sie F8, um das Fenster mit den Eigenschaften zu öffnen, z. B. „PropertiesGBPAUD,Daily“.
- Das Fenster mit den Eigenschaften enthält mehrere Registerkarten: Allgemein, Zeigen und Farben. Nehmen Sie auf jeder Seite die Einstellungen vor, die Ihnen am vertrautesten sind, z. B. für ein TagesChart, und klicken Sie auf OK. Die Einzelheiten finden Sie hier - Einstellungen (offizielle Terminal-Hilfe).
- Nachdem Sie auf OK geklickt haben, wird das Fenster Eigenschaften geschlossen und das Chart nimmt die von Ihnen gewünschte Form an.
- Wählen Sie nun im Kontextmenü Charts - Vorlagen - Vorlage speichern. Das Fenster zum Speichern der Vorlage wird angezeigt und das Fenster zum Speichern der Vorlage wird angezeigt. Geben Sie dailyHistorytemp.tpl in das Feld „Dateiname“ ein und klicken Sie auf Speichern.
- Danach befindet sich im Terminalordner ..MQL5\Profiles\Templates die Datei dailyHistorytemp.tpl, die Sie im Skript verwenden können. Wichtig ist, dass der Name der Vorlage ohne die Erweiterung .tpl in das Skript eingegeben wird.
Kommen wir nun zurück zu unserem Code. Sobald die gewünschte Vorlage angewendet wurde, müssen wir eine kleine Verzögerung in der Codeausführung einbauen, um dem Chart Zeit zu geben, in der gewünschten Qualität zu laden. Andernfalls kann es sein, dass das Chart nicht korrekt angezeigt wird, weil es zu lange dauert, bis die erforderlichen historischen Kursdaten in das Terminal geladen sind. Wenn Sie z. B. ein Chart längere Zeit nicht geöffnet haben, braucht das Terminal Zeit, um es korrekt anzuzeigen. Wir werden die Zeitverzögerung durch die vordefinierte Terminalfunktion Sleep() bekannt geben, wie unten gezeigt.
Sleep(2000); // wait for the chart to load
Als Verzögerung verwenden wir einen Wert von 2000 Millisekunden oder 2 Sekunden, der rein aus der Praxis stammt, damit das Chart garantiert Zeit zum Laden hat und die Ausführung des Skripts bei einer großen Anzahl von Handelsgeschäften nicht zu langen Minuten führt. Sie können diesen Wert selbständig in die Skripteinstellungen eingeben, um den Prozess zu beschleunigen oder zu verlangsamen, je nach der Leistung Ihrer Geräte oder Ihrer Internetverbindung. Wie die Praxis zeigt, sind zwei Sekunden in den meisten Fällen ausreichend.
Jetzt müssen wir das Scrollen der Charts zu den neuesten Balkenwerten deaktivieren, da wir die Historie analysieren und nicht ständig neue Ticks benötigen, um unser Chart nach rechts zu verschieben. Dazu wird die Eigenschaft CHART_AUTOSCROLL des erforderlichen Charts über die vordefinierte Funktion ChartSetInteger() auf „false“ gesetzt, wie unten gezeigt.
ChartSetInteger(handle,CHART_AUTOSCROLL,false); // disable auto scroll
Da der automatische Bildlauf nun deaktiviert ist, müssen wir zunächst die Anzahl der Balken im Chart des entsprechenden Zeitrahmens nach links zählen, um das Chart in Richtung Historie für den Zeitraum des Abschlusses des betreffenden Handelsgeschäfts zu verschieben. Wir können den Wert über die vordefinierte Terminalfunktion iBarShift() erhalten, indem wir das Symbol, den Zeitrahmen des Charts und den Zeitpunkt des Handelsgeschäftsabschlusses als Parameter übergeben, da wir das gesamte Handelsgeschäft von Anfang bis Ende auf dem Druckbildschirm sehen wollen. Im Parameter „exact“ wird „false“ übergeben, falls die Geschichte wirklich tief ist. In diesem Fall ist dies jedoch nicht so entscheidend für unsere Implementierung. Der vollständige Methodenaufruf mit den Parametern ist unten dargestellt.
bars = iBarShift(arr_symbol[i],main_graph,arr_time_close[i],false); // get the shift for the deal time
Sobald wir wissen, welche Chartverschiebung wir brauchen, können wir genau den Zeitraum anzeigen, der das gewünschte Handelsgeschäft in der Geschichte erfasst. Mit der vordefinierten Terminalvariable ChartNavigate() können wir das Chart in die gewünschte Richtung verschieben, indem wir ihr die folgenden Parameter übergeben, wie unten gezeigt.
ChartNavigate(handle,CHART_CURRENT_POS,-bars+bars_from_right_main); // shifted the chart with a custom margin
Um das Chart zu verschieben, haben wir das Handle des Charts, den Wert von CHART_CURRENT_POS der aktuellen Position der Enumeration ENUM_CHART_POSITION sowie die Verschiebung des Deals, die wir zuvor in der Variablen „bars“ erhalten haben, mit dem vom Nutzer eingegebenen Offset übergeben, um das Potential für Preisbewegungen nach dem Verlassen der Position zu bewerten.
Rufen wir nach den beschriebenen Chart-Transformationen vorsichtshalber die Methode ChartRedraw() auf und beginnen mit dem Zeichnen zusätzlicher Daten auf dem Chart, um historische Handelsgeschäfte zu analysieren.
Um Elemente und Linien des nutzerdefinierten Informationspanels zu zeichnen, die Positionseröffnungen und -schließungen sowie Stop-Loss- und Take-Profit-Levels anzeigen, werden wir die entsprechenden nutzerdefinierten Funktionen paintDeal() und paintPanel() verwenden. Wir werden sie selbst definieren, basierend auf den Standard-Verhaltensmustern für die Arbeit mit Terminal-Charts, wobei paintDeal() Linien mit Eröffnungs- und Schlusskursen sowie Take-Profit und Stop-Loss zeichnen wird, während die Methode paintPanel() eine Tabelle mit den vollständigen Handelsinformationen in der Ecke des Bildschirms enthält.
Die detaillierte Definition der Methoden wird im nächsten Abschnitt gegeben. Hier geben wir einfach an, dass die Methoden in diesem Codesegment aufgerufen werden sollen. Dies geschieht auch unter dem Gesichtspunkt, dass Sie nicht unbedingt die im vorliegenden Artikel beschriebene Implementierung verwenden müssen, um diese beiden Gruppen von Elementen zu zeichnen. Sie können sie selbst umdefinieren, wobei die gewünschte Signatur erhalten bleibt. Die Umsetzung dieser Methoden im vorliegenden Artikel ist ein Beispiel für das optimale Verhältnis von Schönheit und Informationsgehalt von Grafiken zum Zeitpunkt der Erstellung des Codes. Hier geht es vor allem darum, die Position der Methodenaufrufe im Hauptcode beizubehalten.
//--- draw the deal paintDeal(handle,PositionID[i],arr_stop_loss[i],arr_take_profit[i],arr_open[i],arr_close[i],arr_time_open[i],arr_time_close[i]); //--- draw the information panel paintPanel(handle,PositionID[i],arr_stop_loss[i],arr_take_profit[i],arr_open[i], arr_close[i],arr_time_open[i],arr_time_close[i],arr_magic[i],arr_comment[i], arr_extermalID[i],arr_volume[i],arr_commission[i],arr_reason[i],arr_swap[i], arr_profit[i],arr_fee[i],arr_symbol[i],(int)SymbolInfoInteger(arr_symbol[i],SYMBOL_DIGITS));
Nachdem die Methoden die Handelslinien und das Informationsfeld auf dem Chart gezeichnet haben, können wir mit der Implementierung des Speicherns eines Druckbildes von allem, was auf dem aktuellen Chart passiert ist, fortfahren. Dazu bestimmen wir zunächst die zukünftigen Abmessungen des Druckbildschirms in Breite und Höhe, indem wir diese Daten einfach mit der neu definierten Terminalfunktion ChartSetInteger() aus dem geöffneten Chart abfragen, wie unten gezeigt.
//--- get data by screen size chart_width = (int) ChartGetInteger(handle,CHART_WIDTH_IN_PIXELS); // look at the chart width chart_height = (int) ChartGetInteger(handle,CHART_HEIGHT_IN_PIXELS); // look at the chart height
Wir haben die Werte der Enumeration ENUM_CHART_PROPERTY_INTEGER für die Breite, CHART_WIDTH_IN_PIXELS, und die Höhe, CHART_HEIGHT_IN_PIXELS, als die entsprechenden Parameter für die Darstellung des Charts übergeben.
Nachdem wir die Daten der Größen erhalten haben, müssen wir einen Pfad zum Speichern des Chart-Bilds im Standardordner des Terminals erstellen. Um zu verhindern, dass der EA alle Dateien in einem Ordner ablegt, sondern sie stattdessen zur Bequemlichkeit des Nutzers sortiert, automatisieren wir diesen Prozess durch den Dateinamen in der nächsten Zeichenfolge.
string name_main_screen = brok_name+"/"+ IntegerToString(account_num)+"/"+ IntegerToString(deal_close_date.year)+"-"+IntegerToString(deal_close_date.mon)+ "-"+IntegerToString(deal_close_date.day)+"/"+ IntegerToString(PositionID[i])+"/"+ EnumToString(main_graph)+IntegerToString(PositionID[i])+".png"; // assign the name
Die grafische Struktur der Sortierung von Dateien in Ordnern in einem Standardverzeichnis ist in Abbildung 1 dargestellt.
Abbildung 1. Struktur der Verzeichnisadressen der gespeicherten Chart-Bilder nach Handelsgeschäften
Wie wir sehen können, werden die Chart-Dateien nach Brokernamen, Kontonummer, Jahr, Monat und Tag der Ausführung sortiert, sodass der Nutzer das gewünschte Handelsgeschäft leicht finden kann, ohne den Dateinamen in einer allgemeinen Liste suchen zu müssen. Die verschiedenen Zeitrahmen befinden sich in dem Ordner mit der entsprechenden Positionsnummer des Terminals.
Wir speichern die Informationen direkt, indem wir die vordefinierte Terminalfunktion ChartScreenShot() aufrufen und ihr das Handle des gewünschten Charts, die zuvor ermittelten Druckbildgrößen, die den Chartgrößen entsprechen, und auch den Namen der Datei, die die gesamte Struktur der Ordneradressen enthält, als Parameter übergeben, wie in Abbildung 1 und im nachstehenden Code gezeigt.
ChartScreenShot(handle,name_main_screen,chart_width,chart_height,ALIGN_LEFT); // make a screenshot
Wenn die in der Hierarchie angegebenen Ordner nicht im Standard-Terminalordner vorhanden sind, erstellt das Terminal sie automatisch ohne Nutzereingriff.
Nach dem Speichern der Datei können wir das Chart schließen, um die Ansicht des Terminals nicht zu überladen, insbesondere wenn der Download eine große Anzahl von historischen Handelsgeschäften für das Konto enthält. Wir schließen das Chart mit der vordefinierten Terminalfunktion ChartClose() und übergeben ihr das Handle des gewünschten Charts, um nichts Unnötiges zu schließen. Der Funktionsaufruf ist unten dargestellt.
ChartClose(handle); // closed the chart
Wir wiederholen diesen Vorgang in ähnlicher Weise für alle vom Nutzer in den Eingaben angegebenen Zeiträume. Um unser Skript zu vervollständigen, müssen wir nun das Verhalten der Methoden paintDeal() und paintPanel() außerhalb des Hauptprogrammcodes definieren.
Zeichnen von Datenobjekten auf Charts
Für die bequeme Platzierung von Informationen auf dem Druckbildschirm müssen wir nur zwei Methoden neu definieren, die bestimmen, wie genau die vom Nutzer benötigten Daten gezeichnet werden.
Beginnen wir mit einer Beschreibung der Methode paintDeal(). Sein Ziel ist es, Grafiken für eine Position zu zeichnen, die mit der Position des Eröffnungs- und Schlusskurses, des Stop-Loss und der Take-Profit-Position verbunden sind. Dazu deklarieren wir die Methodenbeschreibung mit der folgenden Signatur außerhalb des Hauptcodeteils:
void paintDeal(long handlE, ulong tickeT, double stop_losS, double take_profiT, double opeN, double closE, datetime timE, datetime time_closE)
Die folgenden Werte werden in den Methodenparametern angegeben: handlE - Handle des Charts, auf dem wir zeichnen werden, tickeT - Deal Ticket, stop_losS - Preis des Stop-Loss, falls vorhanden, take_profiT - Take-Profit, falls vorhanden, open price - opeN und close price - closE, deal open time - timE und deal close time - time_closE.
Beginnen wir mit dem Namen des Objekts, der einem eindeutigen Namen entspricht, der nicht wiederholt werden sollte. Deshalb werden wir in den Namen ein Merkmal einbauen, dass dieses Objekt einer Haltestelle in Form von „name_sl_“ entspricht. Um den Namen eindeutig zu machen, fügen wir auch die Ticketnummer des Handelsgeschäfts hinzu, wie unten gezeigt.
string name_sl = "name_sl_"+IntegerToString(tickeT); // assign the name
Jetzt können wir das eigentliche Grafikobjekt mit der vordefinierten Terminalfunktion ObjectCreate() erstellen, die das Stop-Loss-Niveau nach historischer Position im Chart zeichnet. Die übergebenen Parameter sind das Chart-Handle und der eindeutige Name aus der Variable name_sl. Wir geben den Wert OBJ_ARROW_LEFT_PRICE als Objekttyp an, d. h. das linke Preisetikett aus der Enumeration ENUM_OBJECT sowie den tatsächlichen Preiswert und den Zeitpunkt, zu dem das Etikett im Chart platziert wurde, wie unten dargestellt.
ObjectCreate(handlE,name_sl,OBJ_ARROW_LEFT_PRICE,0,timE,stop_losS); // create the left label object
Nachdem das Objekt nun erstellt wurde, können wir die Werte für die Felder OBJPROP_COLOR und OBJPROP_TIMEFRAMES festlegen. Wir setzen OBJPROP_COLOR auf clrRed, da Stop-Loss in der Regel rot gefärbt ist, während OBJPROP_TIMEFRAMES auf OBJ_ALL_PERIODS gesetzt wird, um alle Zeitrahmen anzuzeigen. Allerdings ist die zweite Bedingung bei dieser Umsetzung nicht entscheidend. Im Allgemeinen sieht der Stop-Loss-Zeichnungsblock wie folgt aus.
//--- draw stop loss string name_sl = "name_sl_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_sl,OBJ_ARROW_LEFT_PRICE,0,timE,stop_losS); // create the left label object ObjectSetInteger(handlE,name_sl,OBJPROP_COLOR,clrRed); // add color ObjectSetInteger(handlE,name_sl,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
Nach dem Zeichnen von jedem Block rufen wir die Methode ChartRedraw() auf.
Das Zeichnen des Take-Profit-Blocks erfolgt ähnlich wie das Zeichnen des Stop-Loss-Blocks, mit den folgenden Ausnahmen. Zunächst fügen wir dem eindeutigen Objektnamen „name_tp_“ plus das Handelsgeschäftsticket hinzu und setzen die Farbe aus der grünen Palette, die der traditionellen Bezeichnung des erhaltenen Gewinns entspricht, über die Farbe clrLawnGreen. Ansonsten ist die Logik ähnlich wie beim Stop-Loss-Block und wird hier vollständig dargestellt.
//--- draw take profit string name_tp = "name_tp_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_tp,OBJ_ARROW_LEFT_PRICE,0,timE,take_profiT); // create the left label object ObjectSetInteger(handlE,name_tp,OBJPROP_COLOR,clrLawnGreen); // add color ObjectSetInteger(handlE,name_tp,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
Kommen wir nun zur Umsetzung der Zeichnung des Einstiegspreises über das linke Preisschild. Der Unterschied zu den vorangegangenen Blöcken besteht vor allem in der eindeutigen Bezeichnung des Objekts. Wir fügen am Anfang „name_open_“ ein. Ein weiterer Unterschied ist die Linienfarbe clrWhiteSmoke, damit sie im Chart nicht zu sehr hervorsticht, aber ansonsten ist alles gleich geblieben.
//--- draw entry price string name_open = "name_open_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_open,OBJ_ARROW_LEFT_PRICE,0,timE,opeN); // create the left label object ObjectSetInteger(handlE,name_open,OBJPROP_COLOR,clrWhiteSmoke); // add color ObjectSetInteger(handlE,name_open,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
Die Linie, die den Eröffnungs- und den Schlusskurs des Handelsgeschäfts verbindet, wird in derselben Farbe angezeigt. Der Linientyp wird anders sein. Bei der Erstellung eines Objekts im Parameter der Methode ObjectCreate() übergeben wir den Wert OBJ_TREND der Enumeration ENUM_OBJECT als dritten Parameter, um eine Trendlinie zu erstellen. Um die Trendlinie im Chart korrekt zu positionieren, müssen wir zusätzliche Parameter für die Position von zwei Punkten angeben, wobei jeder Punkt zwei Attribute hat: Preis und Zeit. Zu diesem Zweck werden die Eröffnungs- und Schlusskurse opeN und closE zusammen mit den Schluss- und Eröffnungszeiten in den Variablen timE und time_closE an die nachfolgenden Parameter übergeben, wie unten dargestellt.
//--- deal line string name_deal = "name_deal_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_deal,OBJ_TREND,0,timE,opeN,time_closE,closE); // create the left label object ObjectSetInteger(handlE,name_deal,OBJPROP_COLOR,clrWhiteSmoke); // add color ObjectSetInteger(handlE,name_deal,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
Um das Handelsgeschäft vollständig im Chart darzustellen, müssen wir noch die Preiskennzeichnungen für das Schließen des Handelsgeschäfts zeichnen. Um dies zu erreichen, werden wir das richtige Preisetikett verwenden, sodass die Informationen auf dem Druckbildschirm visuell ansprechender dargestellt werden. Um das rechte Etikett zu zeichnen, sollte die Methode ObjectCreate() als dritten Parameter den Wert OBJ_ARROW_RIGHT_PRICE erhalten, also das rechte Preisetikett aus der Enumeration ENUM_OBJECT. Für den Rest der Zeichnung benötigen wir nur den Preis und die Zeit, die wir über die entsprechenden Variablen time_closE,closE wie unten gezeigt übergeben.
//--- draw exit price string name_close = "name_close"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_close,OBJ_ARROW_RIGHT_PRICE,0,time_closE,closE);// create the left label object ObjectSetInteger(handlE,name_close,OBJPROP_COLOR,clrWhiteSmoke); // add color ObjectSetInteger(handlE,name_close,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
Damit ist die Beschreibung unserer nutzerdefinierten Methode paintDeal() zum Zeichnen der Ein- und Ausstiegslinien der Position abgeschlossen. Nun können wir mit der Beschreibung der Methode paintPanel() fortfahren, mit der das Panel mit den vollständigen Handelsgeschäftsinformationen gezeichnet wird.
Die Beschreibung der Methode zum Zeichnen des Panels erfordert eine komplexere Struktur von Methoden, die für das Zeichnen von Textbeschriftungen zuständig sind, wie z. B. Textbeschriftungen vom Typ OBJ_LABEL, die Enumeration ENUM_OBJECT und das Objekt OBJ_RECTANGLE_LABEL für die Erstellung und Gestaltung grafischer Nutzeroberflächen. Deklarieren wir die entsprechenden nutzerdefinierten Methoden namens LabelCreate() zum Erstellen von Textetiketten und RectLabelCreate() zum Erstellen eines rechteckigen Etiketts. Wir beginnen die Beschreibung mit Hilfsmethoden und gehen dann zur Beschreibung der Hauptmethode paintPanel() über, in der wir Hilfsmethoden verwenden.
Im Allgemeinen wird die Struktur unserer Skriptmethoden wie in Abbildung 2 aussehen.
Abbildung 2. Struktur der nutzerdefinierten Methoden zum Zeichnen von Grafiken
Deklaration der Methode LabelCreate() mit der folgenden Signatur als Parameter:
bool LabelCreate(const long chart_ID=0, // chart ID const string name="Label", // label name const int sub_window=0, // subwindow number const long x=0, // X coordinate const long y=0, // Y coordinate const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // chart corner for anchoring const string text="Label", // text const string font="Arial", // font const int font_size=10, // font size const color clr=clrRed, // color const double angle=0.0, // text angle const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // anchor type const bool back=false, // in the background const bool selection=false, // select to move const bool hidden=true, // hidden in the list of objects const long z_order=0) // priority for clicking with a mouse
Der Parameter chart_ID erhält das Handle des Charts, auf dem das Objekt gezeichnet werden soll: „name“ ist ein eindeutiger Objektname, während der Wert 0 des Parameters sub_window bedeutet, dass das Objekt im Hauptfenster des Charts gezeichnet werden soll. Die Koordinaten der linken oberen Ecke des Objekts werden über die Parameter X und Y übergeben. Wir können die Bindung der Objektecke an das Chart von der standardmäßigen linken Ecke ändern, indem wir den entsprechenden Wert an den Parameter „corner“ übergeben, aber wir werden den Standardwert von ANCHOR_LEFT_UPPER dort lassen. Wir übergeben den String-Wert der anzuzeigenden Informationen im Parameter „text“. Die Art der Anzeige, wie Schriftart und -größe, Farbe und Winkel, wird in den entsprechenden Parametern „font“, „font_size“, „clr“ und „angle“ übergeben. Außerdem wird unser Objekt in der Liste der Objekte für den Nutzer verborgen und nicht mit der Maus auswählbar sein, indem wir die Parameter „selection“ und „hidden“ verwenden. Der Parameter z_order ist für die Prioritätsreihenfolge der Mausklicks verantwortlich.
Beginnen wir die Methodenbeschreibung mit dem Zurücksetzen der Fehlervariablen, damit es möglich ist, das Ergebnis der Erstellung eines Objekts in der Zukunft durch die vordefinierte Terminalfunktion ResetLastError() korrekt zu kontrollieren. Das Ergebnis der Objekterstellung vom Typ OBJ_LABEL wird über den logischen Operator „if“ beim Aufruf der Funktion ObjectCreate() gehandhabt, wie unten dargestellt. Wenn das Objekt nicht erstellt wird, informieren wir den Nutzer darüber im EA-Protokoll und unterbrechen die Methodenausführung wie üblich über die Return-Anweisung.
//--- reset the error value ResetLastError(); //--- create a text label if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0)) { Print(__FUNCTION__, ": failed to create the text label! Error code = ",GetLastError()); return(false); }
Wenn das Objekt erfolgreich erstellt wurde, initialisieren wir die Eigenschaftsfelder des Objekts mit den vordefinierten Terminalfunktionen ObjectSetInteger(), ObjectSetString() und ObjectSetDouble(), um ihm das gewünschte Aussehen zu verleihen. Wir verwenden die Funktion ObjectSetInteger(), um die Werte der entsprechenden Koordinaten, den Winkel der Objektverankerung, die Schriftgröße, die Methode der Objektverankerung, die Farbe, den Anzeigemodus sowie die Eigenschaften im Zusammenhang mit der Sichtbarkeit des Objekts für den Nutzer festzulegen. Die Werte für den Schriftwinkel setzen wir mit dem Befehl ObjectSetDouble(), während wir mit dem Befehl ObjectSetString() den Inhalt des übergebenen Textes und den Schrifttyp für die Anzeige festlegen. Die vollständige Implementierung des Methodenkörpers wird im Folgenden dargestellt.
//--- reset the error value ResetLastError(); //--- create a text label if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0)) { Print(__FUNCTION__, ": failed to create the text label! Error code = ",GetLastError()); return(false); } //--- set label coordinates ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); //--- set the chart's corner, relative to which point coordinates are defined ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); //--- set the text ObjectSetString(chart_ID,name,OBJPROP_TEXT,text); //--- set the text font ObjectSetString(chart_ID,name,OBJPROP_FONT,font); //--- set font size ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size); //--- set the text angle ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle); //--- set anchor type ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor); //--- set the color ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); //--- display in the foreground (false) or background (true) ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); //--- enable (true) or disable (false) the mode of moving the label by mouse ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); //--- hide (true) or display (false) graphical object name in the object list ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); //--- set the priority for receiving the event of a mouse click on the chart ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); //--- successful execution return(true);
Deklaration der Methode RectLabelCreate() mit der folgenden Signatur als Parameter für die Objekterstellung:
bool RectLabelCreate(const long chart_ID=0, // chart ID const string name="RectLabel", // label name const int sub_window=0, // subwindow number const int x=19, // X coordinate const int y=19, // Y coordinate const int width=150, // width const int height=20, // height const color back_clr=C'236,233,216', // background color const ENUM_BORDER_TYPE border=BORDER_SUNKEN, // border type const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // chart corner for anchoring const color clr=clrRed, // flat border color (Flat) const ENUM_LINE_STYLE style=STYLE_SOLID, // flat border style const int line_width=1, // flat border width const bool back=true, // 'true' in the background const bool selection=false, // select to move const bool hidden=true, // hidden in the list of objects const long z_order=0) // priority for clicking with a mouse
Die Parameter der Methode RectLabelCreate() sind den Parametern der zuvor deklarierten Methode LabelCreate() sehr ähnlich, mit Ausnahme zusätzlicher Einstellungen für den rechteckigen Etikettenrahmen, der als Hintergrund für die Anzeige der Daten des vorherigen Methodenobjekts dienen wird. Zusätzliche Parameter zur Konfiguration des Objektrahmens: „border“ - Typ des Rahmens, definiert durch die Enumeration ENUM_BORDER_TYPE mit dem Standardwert BORDER_SUNKEN, „style“ - Stil des Rahmens, definiert durch die Enumeration ENUM_LINE_STYLE mit dem Standardwert STYLE_SOLID und „line_width“ - Breite der Rahmenlinie als Integer-Wert.
Die Definition des Methodenkörpers sieht ähnlich aus wie die vorherige und besteht ebenfalls aus zwei globalen Abschnitten: der Erstellung des Objekts und der Definition seiner Eigenschaften durch die entsprechenden vordefinierten Terminalmethoden, wie unten dargestellt.
//--- reset the error value ResetLastError(); // reset error //--- create a rectangle label if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0)) // create object { //--- set label coordinates ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); // assign x coordinate ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); // assign y coordinate //--- set label size ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); // width ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height); // height //--- set the background color ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr); // background color //--- set border type ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border); // border type //--- set the chart corner, relative to which point coordinates are defined ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); // anchor corner //--- set flat border color (in Flat mode) ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); // frame //--- set flat border line style ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); // style //--- set flat border width ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width); // width //--- display in the foreground (false) or background (true) ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); // default is background //--- enable (true) or disable (false) the mode of moving the label by mouse ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); // is it possible to select ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); // //--- hide (true) or display (false) graphical object name in the object list ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); // is it visible in the list //--- set the priority for receiving the event of a mouse click on the chart ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); // no events //--- successful execution } return(true);
Nachdem nun alle Hilfsmethoden beschrieben wurden, wollen wir den Hauptteil der Methode paintPanel() definieren, mit der das gesamte Panel gezeichnet wird. Die Eingaben enthalten die Felder, die erforderlich sind, um dem Nutzer die vollständigen Handelsgeschäftsinformationen anzuzeigen (siehe unten).
void paintPanel(long handlE, ulong tickeT, double stop_losS, double take_profiT, double opeN, double closE, datetime timE, datetime time_closE, int magiC, string commenT, string externalIDD, double volumE, double commissioN, ENUM_DEAL_REASON reasoN, double swaP, double profiT, double feE, string symboL, int digitS )
Wie bei den vorherigen Methoden ist der erste Parameter für die Definition des Handles des Charts verantwortlich, auf dem alle mit dem Informationspanel verbundenen Objekte erstellt werden. Alle anderen Parameter wiederholen die Felder des historischen Handelsgeschäftsobjekts.
Wir beginnen die Implementierung der Methode zum Zeichnen der Datentafel, indem wir die Variablen für die Speicherung der Größe der Tafel sowie die Koordinaten für die Verankerung der Spalten mit dem Namen der angezeigten Informationen und den zuvor erhaltenen Werten definieren, wie unten gezeigt.
int height=20, max_height =0, max_width = 0; // column height and max values for indent int x_column[2] = {10, 130}; // columns X coordinates int y_column[17]; // Y coordinates
Die Variable „height“ speichert einen statischen Wert von 20 als Höhe jeder Spalte, um sicherzustellen, dass jede Zeile gleichmäßig gezeichnet wird, und die Werte max_height und max_width speichern die Maximalwerte jeder Spalte, um eine gleichmäßige Zeichnung zu gewährleisten. Die erforderlichen Koordinaten entlang der X- und Y-Achse werden in den Arrays x_column[] bzw. y_column[] gespeichert.
Nun müssen wir zwei Arrays deklarieren, die die Zeilenwerte für die Anzeige der Kopfspalte und der Wertspalte speichern. Wir deklarieren die Kopfspalte über das Datenarray vom Typ String, wie im folgenden Code gezeigt.
string column_1[17] = { "Symbol", "Position ID", "External ID", "Magic", "Comment", "Reason", "Open", "Close", "Time open", "Time close", "Stop loss", "Take profit", "Volume", "Commission", "Swap", "Profit", "Fee" };
Alle Array-Werte werden statisch deklariert und initialisiert, da sich das Panel nicht ändern wird und die Daten immer in derselben Reihenfolge angezeigt werden. Dies dürfte sich als praktisch erweisen, wenn man sich an die Anzeige von Informationen über verschiedene Angebote gewöhnen möchte. Es ist möglich, eine Funktion zu implementieren, die es ermöglicht, Daten aus dem Panel auszuschließen, die keine Werte enthalten oder gleich Null sind, aber das wäre für die Augen bei der schnellen Suche nach Informationen unangenehm. Es ist immer noch vertrauter, in einem bekannten Anzeigemuster nach Informationen zu suchen, als sich jedes Mal die Spaltenwerte anzusehen.
In derselben Datensequenz deklarieren wir ein zweites Array, das bereits die Werte der im obigen Array deklarierten Spalten enthalten wird. Die Beschreibung der Array-Deklaration ist wie folgt:
string column_2[17] = { symboL, IntegerToString(tickeT), externalIDD, IntegerToString(magiC), commenT, EnumToString(reasoN), DoubleToString(opeN,digitS), DoubleToString(closE,digitS), TimeToString(timE), TimeToString(time_closE), DoubleToString(stop_losS,digitS), DoubleToString(take_profiT,digitS), DoubleToString(volumE,2), DoubleToString(commissioN,2), DoubleToString(swaP,2), DoubleToString(profiT,2), DoubleToString(feE,2) };
Das Array wird lokal, auf Methodenebene, deklariert, wobei die Felder gleichzeitig direkt aus den Methodenparametern mit den entsprechenden vordefinierten Terminalfunktionen initialisiert werden.
Nun, da wir die Container mit den erforderlichen Daten deklariert haben, müssen wir die Ankerwerte der einzelnen Zellkoordinaten berechnen, wobei wir die Suche nach dem Höchstwert in jeder Zelle berücksichtigen. Wir können dies mit folgendem Code umsetzen:
int count_rows = 1; for(int i=0; i<ArraySize(y_column); i++) { y_column[i] = height * count_rows; max_height = y_column[i]; count_rows++; int width_curr = StringLen(column_2[i]); if(width_curr>max_width) { max_width = width_curr; } } max_width = max_width*10; max_width += x_column[1]; max_width += x_column[0];
Hier werden die Ankerkoordinaten jedes Objekts ermittelt, indem die Anzahl der Zeilen in einer Schleife durchlaufen und mit einem festen Y-Höhenwert multipliziert wird. Außerdem wird jeder Wert mit dem Wert für die maximale Breite verglichen, um die X-Koordinate zu erhalten.
Sobald wir alle Werte mit ihren Koordinaten haben, können wir mit dem Zeichnen der Informationen beginnen, indem wir die zuvor deklarierte nutzerdefinierte Methode LabelCreate() verwenden, die wir zyklisch aufrufen, je nach Anzahl der anzuzeigenden Zeilen, wie unten gezeigt wird.
color back_Color = clrWhiteSmoke; color font_Color = clrBlueViolet; for(int i=0; i<ArraySize(column_1); i++) { //--- draw 1 string name_1 = column_1[i]+"_1_"+IntegerToString(tickeT); LabelCreate(handlE,name_1,0,x_column[0],y_column[i],CORNER_LEFT_UPPER,column_1[i],"Arial",10,font_Color,0,ANCHOR_LEFT_UPPER,false); //--- draw 2 string name_2 = column_1[i]+"_2_"+IntegerToString(tickeT); LabelCreate(handlE,name_2,0,x_column[1],y_column[i],CORNER_LEFT_UPPER,column_2[i],"Arial",10,font_Color,0,ANCHOR_LEFT_UPPER,false); }
Am Ende der Methode müssen wir nur noch den Hintergrund mit der zuvor deklarierten und beschriebenen nutzerdefinierten Methode RectLabelCreate() auf diese Werte zeichnen und das angezeigte Chart aktualisieren, wie unten gezeigt.
//--- draw the background RectLabelCreate(handlE,"RectLabel",0,1,height,max_width,max_height,back_Color); ChartRedraw(handlE);
Damit ist die Beschreibung aller Methoden abgeschlossen. Das Projekt ist zur Montage und Nutzung bereit.
Das Ergebnis ist, dass die Chart-Datei nach Verwendung des Skripts wie in Abbildung 3 dargestellt aussieht.
Abbildung 3. Das Ergebnis der Skriptoperation mit den angezeigten Handelsgeschäftsdaten
Wie wir sehen können, werden alle Informationen über das Handelsgeschäft in einer allgemeinen Form in einem einzigen Chart dargestellt, was die Analyse und Bewertung der vom Nutzer durchgeführten Handelsoperationen erleichtert. Das Skript ordnet diese Dateien in den entsprechenden Ordnern an, sodass der Nutzer jederzeit die gewünschten Informationen zu jeder Handelsoperation in der Kontohistorie finden kann.
Schlussfolgerung
Mit diesem Artikel haben wir das Schreiben eines Skripts zur automatischen Visualisierung von Handelsgeschäften in einem Chart abgeschlossen. Mit dieser Lösung können Sie Ihren Handel deutlich verbessern, indem Sie mögliche Fehler bei der Wahl des Einstiegspunktes korrigieren und die mathematische Erwartung Ihrer gesamten Strategie durch die Wahl der richtigen Symbole und der erwarteten Kursimpulslage erhöhen. Die Verwendung des Skripts ermöglicht Ihnen eine erhebliche Zeitersparnis bei der Vorbereitung von Chart-Dateien, die Sie stattdessen für die Analyse und die Suche nach neuen Handelsideen verwenden können. Das Wichtigste ist, dass sich der Markt ständig verändert, und um einen stabilen Betrieb zu gewährleisten, müssen Sie ständig in Kontakt bleiben und die Veränderungen überwachen. Dieses Tool kann Ihnen dabei helfen. Ich wünsche Ihnen viel Erfolg bei Ihrer Arbeit. Bitte hinterlassen Sie Ihr Feedback in den Kommentaren.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/14961





- 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.
Ein tolle Idee und erstklassige Erklärung zum verstehen. Danke.
Vielen Dank für Ihren Kommentar. Ich würde mich freuen, wenn Ihnen das bei Ihrer Arbeit hilft. Servus! :)