English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Tracing, Debugging und strukturelle Analyse von Quellcodes

Tracing, Debugging und strukturelle Analyse von Quellcodes

MetaTrader 5Beispiele | 7 April 2016, 15:02
597 0
---
---

Einleitung

Dieser Beitrag beschreibt eine der Methoden zur Erstellung eines Aufrufs-Stacks während der Ausführung. Die folgenden Funktionen werden in diesem Beitrag beschrieben:

  • Erstellung der Struktur verwendeter Klassen, Funktionen und Dateien.
  • Erstellung des Aufrufs-Stacks unter Beibehaltung aller vorherigen Stacks. Die Reihenfolge ihrer Aufrufe.
  • Ansicht des Zustands der Watch-Parameter während der Ausführung.
  • Schrittweise Ausführung des Codes.
  • Gruppierung und Sortierung erhaltener Stacks, Abruf von "extremen" Informationen.


Grundprinzipien der Entwicklung

Als Methode für die Darstellung der Struktur wird eine herkömmliche Herangehensweise genutzt: die Darstellung in Form einer Baumstruktur. Zu diesem Zweck benötigen wir zwei Informationsklassen. CNode – ein "Knoten" zum Schreiben aller Informationen über ein Stack. CTreeCtrl – ein "Baum", der alle Knoten verarbeitet. Und CTraceCtrl, der Tracer selbst, für die Verarbeitung der Bäume.

Die Klassen werden gemäß der folgenden Hierarchie implementiert:

Die Klassen CNodeBase und CTreeBase beschreiben grundlegende Eigenschaften und Methoden der Arbeit mit Knoten und Bäumen.

Die vererbte Klasse CNode erweitert die Grundfunktionalität von CNodeBase und die Klasse CTreeBase arbeitet mit der abgeleiteten Klasse CNode. Dies geschieht, da die Klasse CNodeBase den anderen Standardknoten übergeordnet ist und als unabhängige Klasse für die praktische Umsetzung der Hierarchie und Vererbung isoliert ist.

Im Gegensatz zu CTreeNode aus der Standardbibliothek beinhaltet die Klasse CNodeBase ein Array aus Pointern zu Knoten, sodass die Menge der "Zweige" aus diesem Knoten unbegrenzt ist.

Die Klassen CNodeBase und CNode

class CNode; // forward declaration
//------------------------------------------------------------------    class CNodeBase
class CNodeBase
  {
public:
   CNode   *m_next[]; // list of nodes it points to
   CNode   *m_prev; // parent node
   int     m_id; // unique number
   string  m_text; // text

public:
          CNodeBase() { m_id=0; m_text=""; } // constructor
          ~CNodeBase(); // destructor
  };

//------------------------------------------------------------------    class CNode
class CNode : public CNodeBase
  {
public:
   bool    m_expand; // expanded
   bool    m_check; // marked with a dot
   bool    m_select; // highlighted

   //--- run-time information
   int     m_uses; // number of calls of the node
   long    m_tick; // time spent in the node
   long    m_tick0; // time of entering the node
   datetime    m_last; // time of entering the node
   tagWatch   m_watch[]; // list of name/value parameters
   bool    m_break; // debug-pause

   //--- parameters of the call
   string   m_file; // file name
   int      m_line; // number of row in the file
   string   m_class; // class name
   string   m_func; // function name
   string   m_prop; // add. information

public:
           CNode(); // constructor
           ~CNode(); // destructor
   void    AddWatch(string watch,string val);
  };

Sie finden die Umsetzung aller Klassen in den angehängten Dateien. In diesem Beitrag werden nur ihre Kopfzeilen und wichtige Funktionen gezeigt.

Gemäß der akzeptierten Klassifizierung stellt CTreeBase ein orientiertes azyklisches Diagramm dar. Die abgeleitete Klasse CTreeCtrlnutzt CNode und bedient ihre gesamte Funktionalität: Hinzufügen, Ändern und Löschen der Knoten von CNode.

CTreeCtrlund CNode können problemlos die entsprechenden Klassen der Standardbibliothek ersetzen, da sie eine etwas weiter gefasste Funktionalität bieten.

Die Klassen CTreeBase und CTreeCtrl

//------------------------------------------------------------------    class CTreeBase
class CTreeBase
  {
public:
   CNode   *m_root; // first node of the tree
   int     m_maxid;    // counter of ID

   //--- base functions
public:
           CTreeBase(); // constructor
           ~CTreeBase(); // destructor
   void    Clear(CNode *root=NULL); // deletion of all nodes after a specified one
   CNode   *FindNode(int id,CNode *root=NULL); // search of a node by its ID starting from a specified node
   CNode   *FindNode(string txt,CNode *root=NULL); // search of a node by txt starting from a specified node
   int     GetID(string txt,CNode *root=NULL); // getting ID for a specified Text, the search starts from a specified node
   int     GetMaxID(CNode *root=NULL); // getting maximal ID in the tree
   int     AddNode(int id,string text,CNode *root=NULL); // adding a node to the list, search is performed by ID starting from a specified node
   int     AddNode(string txt,string text,CNode *root=NULL); // adding a node to the list, search is performed by text starting from a specified node
   int     AddNode(CNode *root,string text); // adding a node under root
  };

//------------------------------------------------------------------    class CTreeCtrl
class CTreeCtrl : public CTreeBase
  {
   //--- base functions
public:
          CTreeCtrl() { m_root.m_file="__base__"; m_root.m_line=0; 
                        m_root.m_func="__base__"; m_root.m_class="__base__"; } // constructor
          ~CTreeCtrl() { delete m_root; m_maxid=0; } // destructor
   void    Reset(CNode *root=NULL); // reset the state of all nodes
   void    SetDataBy(int mode,int id,string text,CNode *root=NULL); // changing text for a specified ID, search is started from a specified node
   string  GetDataBy(int mode,int id,CNode *root=NULL); // getting text for a specified ID, search is started from a specified node

   //--- processing state
public:
   bool    IsExpand(int id,CNode *root=NULL); // getting the m_expand property for a specified ID, search is started from a specified node
   bool    ExpandIt(int id,bool state,CNode *root=NULL); // change the m_expand state, search is started from a specified node
   void    ExpandBy(int mode,CNode *node,bool state,CNode *root=NULL); // expand node of a specified node

   bool    IsCheck(int id,CNode *root=NULL); // getting the m_check property for a specified ID, search is started from a specified node
   bool    CheckIt(int id,bool state,CNode *root=NULL); // change the m_check state to a required one starting from a specified node
   void    CheckBy(int mode,CNode *node,bool state,CNode *root=NULL); // mark the whole tree

   bool    IsSelect(int id,CNode *root=NULL); // getting the m_select property for a specified ID, search is started from a specified node
   bool    SelectIt(int id,bool state,CNode *root=NULL); // change the m_select state to a required one starting from a specified node
   void    SelectBy(int mode,CNode *node,bool state,CNode *root=NULL); // highlight the whole tree

   bool    IsBreak(int id,CNode *root=NULL); // getting the m_break property for a specified ID, search is started from a specified node
   bool    BreakIt(int id,bool state,CNode *root=NULL); // change the m_break state, search is started from a specified node
   void    BreakBy(int mode,CNode *node,bool state,CNode *root=NULL); // set only for a selected one 

   //--- operations with nodes
public:
   void    SortBy(int mode,bool ascend,CNode *root=NULL); // sorting by a property
   void    GroupBy(int mode,CTreeCtrl *atree,CNode *node=NULL); // grouping by a property
  };

Die Architektur endet mit zwei Klassen: CTraceCtrl, deren einzige Instanz für direktes Tracing verwendet wird, enthält drei Instanzen der Klasse CTreeCtrlfür die Erstellung der erforderlichen Funktionsstruktur und einen temporären Container, die Klasse CIn. Dabei handelt es sich um eine reine Hilfsklasse, die zum Hinzufügen neuer Knoten zu CTraceCtrl genutzt wird.

Die Klassen CTraceCtrl und CIn

class CTraceView; // provisional declaration
//------------------------------------------------------------------    class CTraceCtrl
class CTraceCtrl
  {
public:
   CTreeCtrl   *m_stack; // object of graph
   CTreeCtrl   *m_info; // object of graph
   CTreeCtrl   *m_file; // grouping by files
   CTreeCtrl   *m_class; // grouping by classes
   CTraceView  *m_traceview; // pointer to displaying of class

   CNode   *m_cur; // pointer to the current node
           CTraceCtrl() { Create(); Reset(); } // tracer created
           ~CTraceCtrl() { delete m_stack; delete m_info; delete m_file; delete m_class; } // tracer deleted
   void    Create(); // tracer created
   void    In(string afile,int aline,string aname,int aid); // entering a specified node 
   void    Out(int aid); // exit from a specified node
   bool    StepBack(); // exit from a node one step higher (going to the parent)
   void    Reset() { m_cur=m_stack.m_root; m_stack.Reset(); m_file.Reset(); m_class.Reset(); } // resetting all nodes
   void    Clear() { m_cur=m_stack.m_root; m_stack.Clear(); m_file.Clear(); m_class.Clear(); } // resetting all nodes

public:
   void    AddWatch(string name,string val); // checking the debug mode for a node
   void    Break(); // pause for a node
  };

//------------------------------------------------------------------    CIn
class CIn
  {
public:
   void In(string afile,int aline,string afunc)
     {
      if(NIL(m_trace)) return; // exit if there is no graph
      if(NIL(m_trace.m_tree)) return;
      if(NIL(m_trace.m_tree.m_root)) return;
      if(NIL(m_trace.m_cur)) m_trace.m_cur=m_trace.m_tree.m_root;
      m_trace.In(afile,aline,afunc,-1); // entering the next one
     }
   void ~CIn() { if(!NIL(m_trace)) m_trace.Out(-1); } // exiting higher
  };


Modell der Arbeit der Klasse CIn

Diese Klasse ist für die Erstellung des Stack-Baums verantwortlich.

Die Konstruktion des Diagramms geschieht schrittweise in zwei Etappen mithilfe zweier CTraceCtrl-Funktionen:

void In(string afile, int aline, string aname, int aid); // entering a specified node
void Out(int aid);  // exit before a specified node

In anderen Worten: Um einen Baum zu bilden, erfolgen kontinuierliche Aufrufe von In-Out-In-Out-In-In-Out-Out usw.

Das Paar In-Out funktioniert folgendermaßen:

1. Eintritt in einen Block (Funktion, Zyklus, Bedingung usw.), d. h. gleich nach der Klammer "{".
Beim Eintritt in den Block wird eine neue Instanz von CIn erstellt und erhält die aktuelle CTraceCtrl, die bereits mit vorherigen Knoten gestartet wurde. Die Funktion CTraceCtrl::In wird in CIn aufgerufen und erstellt einen neuen Knoten im Stack. Der Knoten wird unter dem aktuellen Knoten CTraceCtrl::m_cur erstellt. Alle aktuellen Informationen über den Eintritt werden hineingeschrieben: Dateiname, Zeilennummer, Klassenname, Funktionen, aktuelle Zeit usw.

2. Austritt aus dem Block beim Treffen auf die Klammer "}".
Beim Austritt aus dem Block ruft MQL automatisch den Destruktor CIn::~CIn auf. Im Destruktor wird CTraceCtrl::Out aufgerufen. Der Pointer des aktuellen Knotens CTraceCtrl::m_cur steigt eine Ebene höher im Baum. Dabei wird der Destruktor nicht für den neuen Knoten aufgerufen, der Knoten bleibt im Baum.

Schema des Aufbaus eines Stacks


Der Aufbau des Aufrufs-Stacks in Form eines Baums mit Eintragung aller Informationen über einen Aufruf erfolgt mithilfe des Containers CIn.



Makros zum Vereinfachen von Aufrufen

Um das Neuschreiben der langen Codezeilen für die Erstellung des Objekts CIn und den Eintritt eines Knotens in Ihrem Code zu vermeiden, besteht die bequeme Möglichkeit, dies durch den Aufruf eines Makros zu ersetzen:
#define _IN    CIn _in; _in.In(__FILE__, __LINE__, __FUNCTION__)
Wie Sie sehen können, wird das Objekt CIn erstellt und anschließend treten wir in den Knoten ein.


Da MQL eine Warnung ausgibt, wenn die Namen von lokalen Variablen die gleichen sind wie die der globalen Variablen, ist es besser (genauer und klarer), 3-4 analoge Definitionen mit anderen Namen von Variablen in der folgenden Form festzulegen:

#define _IN1    CIn _in1; _in1.In(__FILE__, __LINE__, __FUNCTION__)
#define _IN2    CIn _in2; _in2.In(__FILE__, __LINE__, __FUNCTION__)
#define _IN3    CIn _in3; _in3.In(__FILE__, __LINE__, __FUNCTION__)
Wenn Sie tiefer gelegene Sub-Blöcke betreten, verwenden Sie die nächsten Makros _INx.
bool CSampleExpert::InitCheckParameters(int digits_adjust)
  { _IN;
//--- initial data checks
   if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel())
     { _IN1;
      printf("Take Profit must be greater than %d",m_symbol.StopsLevel());

Mit dem Auftreten von Makros in Build 411 können Sie die Übergabe von Parametern mithilfe von #define uneingeschränkt nutzen.

Deshalb finden Sie in der Klasse CTraceCtrl die folgende Makro-Definition:

#define NIL(p)    (CheckPointer(p)==POINTER_INVALID)

Sie ermöglicht es, die Überprüfung der Gültigkeit des Pointers zu verkürzen.

Beispielsweise wird die Zeile:

if (CheckPointer(m_tree))==POINTER_INVALID || CheckPointer(m_cur))==POINTER_INVALID) return;  

durch eine kürzere Variante ersetzt:

if (NIL(m_tree) || NIL(m_cur)) return;


Vorbereitung Ihrer Dateien für Tracing

Um das Stack zu kontrollieren und abzurufen, müssen Sie drei Schritte durchführen.

1. Die erforderlichen Dateien hinzufügen
#include <Trace.mqh>

Die gesamte Standardbibliothek basiert derzeit auf der Klasse CObject. Wenn sie also auch in Ihren Dateien als Basisklasse verwendet wird, genügt es, Trace.mqh nur zu Object.mqh hinzuzufügen.

2. Die _IN-Makros in den erforderlichen Blöcken platzieren (Sie können suchen und ersetzen)

Beispiel der Nutzung des _IN-Makros:
bool CSampleExpert::InitCheckParameters(int digits_adjust)
  { _IN;
//--- initial data checks
   if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel())
     { _IN1;
      printf("Take Profit must be greater than %d",m_symbol.StopsLevel());


3. Fügen Sie in den Funktionen OnInit, OnTime und OnDeinit, die das Hauptmodul des Programms darstellen, jeweils die Erstellung, Modifizierung bzw. Löschung des globalen Objekts CTraceCtrlein. Unten aufgeführt sehen Sie den nutzungsfertigen Code für die Einfügung:

Einbetten des Tracers im Hauptcode

//------------------------------------------------------------------    OnInit
int OnInit()
  {
   //****************
   m_traceview= new CTraceView; // created displaying of the graph
   m_trace= new CTraceCtrl; // created the graph
   m_traceview.m_trace=m_trace; // attached the graph
   m_trace.m_traceview=m_traceview; // attached displaying of the graph
   m_traceview.Create(ChartID()); // created chart
   //****************
   // remaining part of your code…
   return(0);
  }
//------------------------------------------------------------------    OnDeinit
void OnDeinit(const int reason)
  {
   //****************
   delete m_traceview;
   delete m_trace;
   //****************
   // remaining part of your code…
  }
//------------------------------------------------------------------    OnTimer
void OnTimer()
  {
   //****************
   if (m_traceview.IsOpenView(m_traceview.m_chart)) m_traceview.OnTimer();
   else { m_traceview.Deinit(); m_traceview.Create(ChartID()); } // if the window is accidentally closed
   //****************
   // remaining part of your code…
  }
//------------------------------------------------------------------    OnChartEvent
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   //****************
   m_traceview.OnChartEvent(id, lparam, dparam, sparam);
   //****************
   // remaining part of your code…
  }


Klassen der Darstellung des Tracings

Das Stack ist also eingerichtet. Betrachten wir nun die Darstellung der erhaltenen Informationen.

Zu diesem Zweck müssen wir zwei Klassen erstellen. CTreeView für die Anzeige des Baums und CTraceView für die Steuerung der Darstellung von Bäumen und zusätzlichen Informationen über das Stack. Beide Klassen werden von der Basisklasse CView abgeleitet.

Die Klassen CTreeView und CTraceView

//------------------------------------------------------------------    class CTreeView
class CTreeView: public CView
  {
   //--- basic functions
public:
           CTreeView(); // constructor
           ~CTreeView(); // destructor
   void    Attach(CTreeCtrl *atree); // attached the tree object for displaying it
   void    Create(long chart,string name,int wnd,color clr,color bgclr,color selclr,
                    int x,int y,int dx,int dy,int corn=0,int fontsize=8,string font="Arial");

   //--- functions of processing of state
public:
   CTreeCtrl        *m_tree; // pointer to the tree object to be displayed
   int     m_sid; // last selected object (for highlighting)
   int     OnClick(string name); // processing the event of clicking on an object

   //--- functions of displaying
public:
   int     m_ndx, m_ndy; // size of margins from button for drawing
   int     m_bdx, m_bdy; // size of button of nodes
   CScrollView       m_scroll;
   bool    m_bProperty; // show properties near the node

   void    Draw(); // refresh the view
   void    DrawTree(CNode *first,int xpos,int &ypos,int &up,int &dn); // redraw
   void    DeleteView(CNode *root=NULL,bool delparent=true); // delete all displayed elements starting from a specified node
  };

//------------------------------------------------------------------    class CTreeView
class CTraceView: public CView
  {
   //--- base functions
public:
           CTraceView() { }; // constructor
           ~CTraceView() { Deinit(); } // destructor
   void    Deinit(); // full deinitialization of representation
   void    Create(long chart); // create and activate the representation

   //--- function of processing of state
public:
   int     m_hagent; // handler of the indicator-agent for sending messages
   CTraceCtrl   *m_trace; // pointer to created tracer
   CTreeView    *m_viewstack; // tree for displaying the stack
   CTreeView    *m_viewinfo; // tree for displaying of node properties
   CTreeView    *m_viewfile; // tree for displaying of the stack with grouping by files
   CTreeView    *m_viewclass; // tree for displaying of stack with grouping by classes
   void    OnTimer(); // handler of timer
   void    OnChartEvent(const int,const long&,const double&,const string&); // handler of event

   //--- functions of displaying
public:
   void    Draw(); // refresh objects
   void    DeleteView(); // delete the view

   void    UpdateInfoTree(CNode *node,bool bclear); // displaying the window of detailed information about a node
   string  TimeSeparate(long time); // special function for transformation of time into string
  };

Wir haben uns für die Anzeige des Stacks in einem separaten Unterfenster als optimale Variante entschieden.

In anderen Worten: Wenn die Klasse CTraceView in der Funktion CTraceView::Create erstellt wird, wird das Diagrammfenster erstellt und alle Objekte werden darin eingezeichnet, obwohl die Klasse CTraceView in einem Expert Advisor in einem anderen Fenster erstellt wird und arbeitet. Dies soll verhindern, dass die Arbeit des Quellcodes des getraceten Programms und die Anzeige ihrer eigenen Informationen im Diagramm durch die riesige Menge von Informationen behindert werden.

Doch um die Interaktion zwischen zwei Fenstern zu ermöglichen, müssen wir einen Indikator zum Fenster hinzufügen, das alle Ereignisse des Benutzers an das Basisfenster mit dem getraceten Programm senden wird.

Der Indikator wird ebenfalls in der Funktion CTraceView::Create erstellt. Er hat nur einen externen Parameter: die ID des Diagramms, an das er die Ereignisse senden soll.

Der Indikator TraceAgent

#property indicator_chart_window
input long cid=0; // чарт получателя
//------------------------------------------------------------------    OnCalculate
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double& price[])
{ return(rates_total); }
//------------------------------------------------------------------    OnChartEvent
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
    EventChartCustom(cid, (ushort)id, lparam, dparam, sparam);
  }

Als Ergebnis erhalten wir eine hinreichend strukturierte Darstellung des Stacks.

Im Baum TRACE auf der linken Seite wird das ursprüngliche Stack angezeigt.

Darunter befindet sich das Fenster INFO mit detaillierten Informationen über den ausgewählten Knoten (in diesem Beispiel CTraceView::OnChartEvent). Zwei benachbarte Fenster mit Bäumen zeigen das gleiche Stack, doch es ist nach Klassen (Baum CLASS in der Mitte) und nach Dateien (Baum FILE auf der rechten Seite) gruppiert.

Die Klassen- und Dateibäume haben einen eingebetteten Mechanismus zur Synchronisierung mit dem Hauptbaum des Stracks sowie bequeme Kontrollmöglichkeiten. Wenn Sie beispielsweise auf einen Klassennamen im Klassenbaum klicken, werden alle Funktionen dieser Klasse im Stack-Baum und im Dateibaum ausgewählt. Auf die gleiche Weise werden alle Funktionen und Klassen in einer Datei ausgewählt, wenn Sie auf einen Dateinamen klicken.

Dieser Mechanismus ermöglicht die schnelle Auswahl und Ansicht der erforderlichen Gruppen von Funktionen.

Möglichkeiten der Arbeit mit dem Stack

  • Hinzufügen von Watch-Parametern

Wie Sie bereits bemerkt haben, enthalten die Parameter des Knotens CNode das Struktur-Array tagWatch. Es wird nur für die bequeme Darstellung von Informationen erstellt. Es enthält einen benannten Wert einer Variable oder eines Ausdrucks.

Struktur eines Watch-Werts

//------------------------------------------------------------------    struct tagWatch
struct tagWatch
{
    string m_name;     // name
    string m_val;    // value
};

Um einen neuen Watch-Wert zum aktuellen Knoten hinzuzufügen, müssen Sie die Funktion CTrace::AddWatch aufrufen und das Makro _WATCH benutzen.

#define _WATCH(w, v)         if (!NIL(m_trace) && !NIL(m_trace.m_cur)) m_trace.m_cur.AddWatch(w, string(v));


Die spezielle Einschränkung hinzugefügter Werte (die gleiche wie bei Knoten) kontrolliert die Einzigartigkeit von Namen. Das bedeutet, dass der Name eines Watch-Werts auf Einzigartigkeit geprüft wird, bevor er zum Array CNode::m_watch[] hinzugefügt wird. Falls das Array einen Wert mit dem gleichen Namen enthält, wird der neue nicht hinzugefügt, aber der Wert des bestehenden wird aktualisiert.

Alle nachverfolgten Watch-Werte werden im Informationsfenster angezeigt.

  • Schrittweise Ausführung des Codes

Eine weitere bequeme Funktion, die von MQL5 bereitgestellt wird, ist die Einrichtung einer erzwungenen Pause im Code während seiner Ausführung.

Die Pause wird mithilfe der einfachen Endlosschleife while (true) umgesetzt. Das Praktische an MQL5 ist in diesem Fall die Verarbeitung des Ereignisses des Verlassens dieser Schleife durch Klicken auf den roten Kontroll-Button. Um einen Unterbrechungspunkt während der Ausführung einzurichten, nutzen Sie die Funktion CTrace::Break.


Funktion für die Einrichtung von Unterbrechungspunkten

//------------------------------------------------------------------    Break
void CTraceCtrl::Break() // checking the debug mode of a node
  {
   if(NIL(m_traceview)) return; // check of validity
   m_stack.BreakBy(TG_ALL,NULL,false); // removed the m_break flags from all nodes
   m_cur.m_break=true; // activated only at the current one
   m_traceview.m_viewstack.m_sid=m_cur.m_id; // moved selection to it
   m_stack.ExpandBy(TG_UP,m_cur,true,m_cur); // expand parent node if they are closed
   m_traceview.Draw(); // drew everything
   string name=m_traceview.m_viewstack.m_name+string(m_cur.m_id)+".dbg"; // got name of the BREAK button
   bool state=ObjectGetInteger(m_traceview.m_chart,name,OBJPROP_STATE);
   while(!state) // button is not pressed, execute the loop
     {
      Sleep(1000); // made a pause
      state=ObjectGetInteger(m_traceview.m_chart,name,OBJPROP_STATE);  // check its state
      if(!m_traceview.IsOpenView()) break; // if the window is closed, exit
      m_traceview.Draw(); // drew possible changes
     }
   m_cur.m_break=false; // removed the flag
   m_traceview.Draw(); // drew the update
  }

Wird ein solcher Unterbrechungspunkt erreicht, werden die Stack-Bäume synchronisiert, um die Funktion anzuzeigen, die dieses Makro aufgerufen hat. Wenn ein Knoten geschlossen wird, wird der übergeordnete Knoten erweitert, um sie anzuzeigen. Und falls erforderlich, scrollt der Baum nach oben oder unten, um den Knoten in den Anzeigebereich zu bringen.

Klicken Sie zum Verlassen von CTraceCtrl::Break auf den roten Button neben dem Namen des Knotens.

Fazit

Nun verfügen wir über ein spannendes "Spielzeug". Beim Schreiben dieses Beitrags habe ich viele Varianten der Arbeit mit CTraceCtrl ausprobiert und mich davon überzeugt, dass MQL5 einzigartige Möglichkeiten der Steuerung von Expert Advisors und der Einrichtung ihrer Arbeit bietet. Alle Funktionen, die für die Entwicklung des Tracers verwendet wurden, sind in MQL4 nicht verfügbar, was die Vorteile von MQL5 und der breit gefächerten Möglichkeiten dieser Sprache abermals betont.

Im angehängten Code finden Sie alle in diesem Beitrag beschriebenen Klassen zusammen mit Service-Bibliotheken (in ihrem erforderlichen Mindestumfang, da sie hier nicht das Ziel sind). Zusätzlich habe ich das vorgefertigte Beispiel angehängt – aktualisierte Dateien der Standardbibliothek mit implementierten _IN-Makros. Alle Experimente wurden mit dem Expert Advisor durchgeführt, der im Standardpaket von MetaTrader 5 enthalten ist: MACD Sample.mq5.

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

Beigefügte Dateien |
mql5.zip (24.67 KB)
Statistische Schätzungen Statistische Schätzungen
Die Schätzung der statistischen Parameter einer Sequenz ist sehr wichtig, weil die meisten mathematischen Modelle und Methoden auf unterschiedlichen Annahmen basieren, beispielsweise dem Normalverteilungsgesetz oder dem Streuungswert oder anderen Parametern. Beim Analysieren und Prognostizieren von Zeitreihen brauchen wir deshalb ein einfaches und bequemes Werkzeug, das es uns ermöglicht, die wichtigsten statistischen Parameter schnell und deutlich zu schätzen. Dieser Beitrag beschreibt kurz die einfachsten statistischen Parameter einer zufälligen Sequenz und mehrere Methoden für die visuelle Analyse. Er liefert die Umsetzung dieser Methoden in MQL5 und die Methoden der Visualisierung des Ergebnisses der Berechnung mithilfe der Anwendung Gnuplot.
Statistische Verteilungen von Wahrscheinlichkeiten in MQL5 Statistische Verteilungen von Wahrscheinlichkeiten in MQL5
Dieser Beitrag behandelt Verteilungen von Wahrscheinlichkeiten (normal, lognormal, binomial, logistisch, Cauchy-Verteilung, Studentsche t-Verteilung, Laplace-Verteilung, Poisson-Verteilung, Secans-Hyperbolicus-Verteilung, Beta- und Gamma-Verteilung) zuf&auml;lliger Statistiken in der angewandten Statistik. Er nennt ebenfalls Klassen f&uuml;r den Umgang mit diesen Verteilungen.
Verwendung von selbstorganisierenden Karten (Kohonenkarten) in MetaTrader 5 Verwendung von selbstorganisierenden Karten (Kohonenkarten) in MetaTrader 5
Einer der interessantesten Aspekte von selbstorganisierenden Karten (Kohonenkarten) ist, dass sie ohne Beaufsichtigung lernen, Daten zu klassifizieren. Im einfachsten Fall erstellen sie eine Ähnlichkeitskarte von Eingabedaten (Clustering). SOM-Karten können für die Klassifizierung und Visualisierung von hochdimensionalen Daten genutzt werden. In diesem Beitrag werden wir mehrere einfache Anwendungsbeispiele von Kohonenkarten betrachten.
3 Methoden zur Beschleunigung von Indikatoren anhand des Beispiels der linearen Regression 3 Methoden zur Beschleunigung von Indikatoren anhand des Beispiels der linearen Regression
Dieser Beitrag behandelt die Methoden zur Optimierung der Berechnungsalgorithmen von Indikatoren. Jeder wird eine Methode finden, der seine/ihre Anforderungen am besten erfüllt. Drei Methoden werden hier beschrieben. Eine ist ziemlich simpel, die zweite erfordert solide mathematische Kenntnisse und die dritte benötigt einen gewissen Scharfsinn. Für die Realisierung der meisten beschriebenen Methoden werden Indikatoren oder Designmerkmale des MetaTrader 5 Terminals verwendet. Die Methoden sind weitestgehend universell einsatzfähig und können nicht nur für die Beschleunigung der Berechnung der linearen Regression verwendet werden, sondern auch für viele andere Indikatoren.