English Русский 中文 Español 日本語 Português
preview
Visualisierung der Handelsgeschäfte auf dem Chart (Teil 2): Grafische Anzeige der Daten

Visualisierung der Handelsgeschäfte auf dem Chart (Teil 2): Grafische Anzeige der Daten

MetaTrader 5Handelssysteme | 28 November 2024, 09:45
186 2
Aleksandr Seredin
Aleksandr Seredin

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

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

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 des Skriptvorgangs mit den angezeigten Handelsgeschäftsdaten

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

Beigefügte Dateien |
DealsPrintScreen.mq5 (104.52 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Alexander Piechotta
Alexander Piechotta | 1 Dez. 2024 in 15:17
Ein tolle Idee und erstklassige Erklärung zum verstehen. Danke.
Aleksandr Seredin
Aleksandr Seredin | 1 Dez. 2024 in 17:01
Alexander Piechotta #:
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! :)

Algorithmus einer chemischen Reaktionsoptimierung (CRO) (Teil I): Prozesschemie in der Optimierung Algorithmus einer chemischen Reaktionsoptimierung (CRO) (Teil I): Prozesschemie in der Optimierung
Im ersten Teil dieses Artikels werden wir in die Welt der chemischen Reaktionen eintauchen und einen neuen Ansatz zur Optimierung entdecken! Die chemische Reaktionsoptimierung (CRO) nutzt Prinzipien, die sich aus den Gesetzen der Thermodynamik ableiten, um effiziente Ergebnisse zu erzielen. Wir werden die Geheimnisse der Zersetzung, der Synthese und anderer chemischer Prozesse lüften, die die Grundlage für diese innovative Methode bilden.
Entwicklung eines Replay Systems (Teil 53): Die Dinge werden kompliziert (V) Entwicklung eines Replay Systems (Teil 53): Die Dinge werden kompliziert (V)
In diesem Artikel behandeln wir ein wichtiges Thema, das nur wenige Menschen verstehen: Nutzerdefinierte Ereignisse. Gefahren. Vor- und Nachteile dieser Elemente. Dieses Thema ist der Schlüssel für diejenigen, die professionelle Programmierer in MQL5 oder einer anderen Sprache werden wollen. Hier werden wir uns auf MQL5 und MetaTrader 5 konzentrieren.
Neuronale Netze leicht gemacht (Teil 93): Adaptive Vorhersage im Frequenz- und Zeitbereich (letzter Teil) Neuronale Netze leicht gemacht (Teil 93): Adaptive Vorhersage im Frequenz- und Zeitbereich (letzter Teil)
In diesem Artikel setzen wir die Umsetzung der Ansätze des ATFNet-Modells fort, das die Ergebnisse von 2 Blöcken (Frequenz und Zeit) innerhalb der Zeitreihenprognose adaptiv kombiniert.
Entwicklung eines Replay Systems (Teil 52): Die Dinge werden kompliziert (IV) Entwicklung eines Replay Systems (Teil 52): Die Dinge werden kompliziert (IV)
In diesem Artikel werden wir den Mauszeiger ändern, um die Interaktion mit dem Kontrollindikator zu ermöglichen und einen zuverlässigen und stabilen Betrieb zu gewährleisten.