Wie kann man Objekte dynamisch erstellen? (Einige OOP-Sachen)

 

Jetzt kommt etwas OOP-Zeug.

Die Idee für das Programm:

* Ich zeichne eine Trendlinie in den Chart und nenne sie "beep" - wenn der Kurs diese Linie das nächste Mal überquert, erhalte ich einen "beep".

* Ich zeichne eine Trendlinie ein und nenne sie "buy" - wenn der Kurs diese Linie das nächste Mal überschreitet, werde ich eine Long-Position eingehen.

Ich habe bereits ein Objekt namens "CSmartLine" geschrieben, das in der Lage ist, zu piepen und zu kaufen und zu verkaufen und zu schließen und ... (bis jetzt kein Kaffee-Service).

In meinem EA habe ich drei Codezeilen:

CSmartLinie mSmartLinie1;    // Create one object of class CSmartLine  


void OnTick()
      mSmartLinie1.CheckForAction(); // check for crossing prices

void OnChartEvent()
      if(id == CHARTEVENT_OBJECT_CLICK  ||
         id == CHARTEVENT_OBJECT_DRAG   ||
         id == CHARTEVENT_OBJECT_CHANGE ||
         id == CHARTEVENT_OBJECT_CREATE)
         if (sparam == "beep" || sparam == "buy" || sparam == "sell" || sparam == "close")
            {
            mSmartLinie1.CheckForAction(sparam);  // activation and tracking changes
            return;
            };

Das funktioniert bisher gut.

Nun ...... Ich möchte eine beliebige Anzahl von Smart-Trend-Linien in den Chart zeichnen.

Angenommen, das Objekt hat die Chance, den Namen der Linie zu ändern (z.B. in "SmartLine_x"), um zu zeigen, dass die Linie von ihm kontrolliert wird.

Jedes Mal, wenn der EA eine neue Linie erkennt, sollte er ein neues Objekt der Klasse "CSmartLine" erstellen.

Der Code könnte so aussehen

OnChartEvent()
      if (sparam = "beep")
             mSmartLine2 = new CSmartLine;

OnTick()
      if(mSmartLine2 != Null)
             mSmartLine2.CheckForAction();

Aber wie .... hmmm ....

Vielleicht sollte "mSmartLine" ein Array von Zeigern sein? Wenn ja, welcher Typ?

Das Handbuch zeigt an dieser Stelle ein Beispiel, das ich nicht nachvollziehen kann.


Wenn die Trendlinie verschwindet (weil der Benutzer sie z.B. aus dem Chart gelöscht hat) .

Der Code sollte lauten.

      delete mSmartLine2;
      mSmartLine2=NULL;

Da es das Objekt selbst ist, das das Verschwinden der Linie erkennt, sollte es das Objekt sein, das sich selbst löscht, anstatt den EA zu bitten, es zu löschen.

WIllbur

 

Hier kann man einfach verwenden

ObjectCreate(0,"mSmartLinie"+IntegerToString(X),OBJ_HLINE,0,0,0);

Wo ein einfaches

X++;

die ganze Zahl X erhöhen wird, um

"mSmartLinie0"
"mSmartLinie1"
"mSmartLinie2"
"mSmartLinie3"

Und usw.

 
Marco hat Recht.

Ihr Problem besteht darin, dass, wenn ein "Beep"-Objekt existiert, ein neues nicht erstellt wird.
Wie Marco erwähnte
plus
Fügen Sie die Befehle auf das erste Zeichen des Objektnamens nach einer
erkennbaren Zeichenfolge

Bsp:

A->Beep
B->Kaufen
C->Verkaufen
Folge -> SMRTLN_

Das erste Auftreten eines Signaltons wird also als "SMRTLN_A_"+TotalBeepCount bezeichnet.
 

In der OOP gibt es gängige Entwurfsmuster für die Speicherung von Objekten und deren Iteration. Sie werden als Container, Sets, Collections, Maps, Vectors (und andere ähnliche Namen) bezeichnet, aber afaik wird keines mit der MQL-Basisdistribution ausgeliefert. Sie müssen sie selbst programmieren oder sie in der Codebasis finden.

C++ Programming/Code/Design Patterns - Wikibooks, open books for an open world
C++ Programming/Code/Design Patterns - Wikibooks, open books for an open world
  • en.wikibooks.org
A design pattern is neither a static solution, nor is it an algorithm. A pattern is a way to describe and address by name (mostly a simplistic description of its goal), a repeatable solution or approach to a common design problem, that is, a common way to solve a generic problem (how generic or complex, depends on how restricted the target goal...
 

Ich glaube, ich habe es verstanden - das sind die magischen Linien (hoffe ich):

CSmartLine *ArrayOfSmartLineS[10]; // An array of pointers to CSmartLine object


On ChartEvent

           //--- Create another object
           CSmartLine*mSmartLine = new CSmartLine();
           // Make the new object the owner of the new trend line
           mSmartLine.SetName(sparam);
           //--- Place the pointer value in an Array
           ArrayOfSmartLineS[NoLines]=mSmartLine;

Im Handbuch wird die Form "CSmartLine*mSmartLine = new CSmartLine();" nur beim Erzeugen der ersten Instanz dieser Klasse verwendet.
Beim nächsten Mal ist es nur noch "mSmartLine = new CSmartLine();"
Das ist in meinem Testframe nicht möglich (Kompilierungsfehler). ? ? ?


Ich habe einen einfachen Test-Frame mit sehr geringer Funktionalität geschrieben, aber der Test für die dynamische Erstellung von Objekten.

Immer wenn der EA eine Trendlinie mit dem Namen "beep" erkennt, erstellt er ein "SmartLine"-Objekt, das die Linie umbenennt und sie fortan kontrolliert.

//+------------------------------------------------------------------+
//| Test frame OOP                                                   |
//+------------------------------------------------------------------+
class CSmartLine
{
protected:
string   iName;
double   iRate;

public:
   void   SetName(string xName)
            {
            for(int i=0;i<99;i++)
               {
               iName = "SmartLine_"+IntegerToString(i);
               if(ObjectFind(0,iName) < 0) break; // find unused name
               }
            ObjectSetString(0,xName,OBJPROP_NAME,0,iName); // rename trend line
            // --- Get rate
            iRate = ObjectGetDouble(0,iName,OBJPROP_PRICE,0);
            // signal identification of the line
               Sleep(300); PlaySound("ok.wav");
               ObjectSetInteger(0,iName,OBJPROP_WIDTH,4); ChartRedraw();
               Sleep(300);
               ObjectSetInteger(0,iName,OBJPROP_WIDTH,1); ChartRedraw();
            //
            };
           
   string GetName(void) {return(iName);}
  
   void   checkForChange(string xName)
            {
            if(xName != iName) return;
            // Check whether the line has been moved
            // get the new position
                        // --- Get rate
            iRate = ObjectGetDouble(0,iName,OBJPROP_PRICE,0);
            MessageBox("New rate: "+iName+" = "+DoubleToString(iRate,5));
            };
   void   checkForAction(double iAsk)
            {
            if(MathAbs(100 * (iRate - iAsk)/iAsk) < 0.005)
              {
              MessageBox("it's hit me "+iName+
              "\n myRate "+DoubleToString(iRate,5)+
              "\n actAsk "+DoubleToString(iAsk, 5)+
              "\n actDiff "+DoubleToString(100 * (iRate - iAsk)/iAsk,5) );
              }
            // Calculation whether the price hits the line
            // action: beep, buy, sell, close
            };         

};

//################# E N D - - - C S m a r t L i n e ##########################

//################# B E G I N of E A program ##################################

//--- Declare an array of object pointers of type CSmartLine

      CSmartLine *ArrayOfSmartLineS[10]; // An array of pointers to CSmartLine object

int   NoLines=0;

//----------------------------------------------------------------------------
void OnInit(void)
{
// --- do I need this?
   for(int i=0;i<10;i++)
     ArrayOfSmartLineS[i]=NULL;     

//--- delete all old trend lines
    ObjectsDeleteAll(0,"SmartLine",-1);

}
//+--------------------------------------------------------------------------
void OnChartEvent(const int id,        
                  const long& lparam,  
                  const double& dparam,
                  const string& sparam) 
{
      if(id == CHARTEVENT_OBJECT_CLICK  ||
         id == CHARTEVENT_OBJECT_DRAG   ||
         id == CHARTEVENT_OBJECT_CHANGE ||
         id == CHARTEVENT_OBJECT_CREATE)
         {
         if(sparam == "beep" || sparam == "buy" || sparam == "sell") 
           {
           //--- Create another object
           CSmartLine*mSmartLine = new CSmartLine();
           // Make to new object the owner of the new line
           mSmartLine.SetName(sparam);
           //--- file the pointer value in the array[0]
           ArrayOfSmartLineS[NoLines]=mSmartLine;
           //--- ask the new object for it's line name
           MessageBox("new object: " + ArrayOfSmartLineS[NoLines].GetName());
           //
           NoLines++;
           };
         if(StringSubstr(sparam,0,10) == "SmartLine_")
           {
           for(int i=0;i<10;i++)  // Ask all exsisting objects to pick up the change if concerns
             {
             if(ArrayOfSmartLineS[i] != NULL)
                ArrayOfSmartLineS[i].checkForChange(sparam);
             }
           }

         }
}
//----------------------------------------------------------------------------
void OnTick(void)
{
      MqlTick last_tick;
  
      SymbolInfoTick(_Symbol,last_tick);
     
      for(int i=0;i<10;i++)  // Ask all exsisting objects
             {
             if(ArrayOfSmartLineS[i] != NULL)
                ArrayOfSmartLineS[i].checkForAction(last_tick.ask);
             }
}
//+------------------------------------------------------------------+
void OnDeinit(const int xReason)
{
      if(xReason == REASON_RECOMPILE   ||
         xReason == REASON_CHARTCHANGE ||
         xReason == REASON_PARAMETERS  ||
         xReason == REASON_ACCOUNT)    return;
     
//--- We must delete all created dynamic objects
      for(int i=0;i<10;i++)
         {
         //--- We can delete only the objects with pointers of POINTER_DYNAMIC type
         if(CheckPointer(ArrayOfSmartLineS[i])==POINTER_DYNAMIC)
            {
            //--- Notify of deletion
            MessageBox("Deleting object "+IntegerToString(i)+" named "+ArrayOfSmartLineS[i].GetName());
            //--- Delete an object by its pointer
            delete ArrayOfSmartLineS[i];
            ArrayOfSmartLineS[i] = NULL;
            }
         }   // Loop i=0;i<10;i++
  }

 

@Marco

Ich bin mir nicht sicher, ob ich Ihre Idee verstanden habe.

Meinst Du, ich sollte mit den Standard-Grafikobjekten arbeiten, eine Klasse schreiben, die alles davon erbt und die Funktionalität auf den Bereich erweitert, den ich für meine SmartLines plane?!?

Ich habe über diese Idee nachgedacht und frage mich, ob MT5 es erlaubt, vom Benutzer erstellte Grafikobjekte in einem Chart zu erstellen (über die normale Schnittstelle).

Neben diesem Problem müssen meine SmartLine-Objekte ausgelöst werden, wenn der Preis Chancen und ich habe keine Ahnung, wie man dieses Problem zu umgehen.

Haben Sie Erfahrung auf diesem Gebiet?

Willbur

 
Willbur:

@Marco

Ich bin mir nicht sicher, ob ich Ihre Idee verstanden habe.

Meinst du, ich soll mit den Standard-Grafikobjekten gehen, eine Klasse schreiben, die alles davon erbt und die Funktionalität auf den Bereich erweitert, den ich für meine SmartLines plane?!?

Ich habe über diese Idee nachgedacht und frage mich, ob MT5 es erlaubt, vom Benutzer erstellte grafische Objekte in einem Chart zu erstellen (über die normale Schnittstelle).

Abgesehen von diesem Problem müssen meine SmartLine-Objekte ausgelöst werden, wenn sich der Preis ändert und ich habe keine Ahnung, wie ich dieses Problem umgehen kann.

Haben Sie Erfahrung in diesem Bereich?

Willbur

Das war nur ein Beispiel, wie man es machen kann.

Wann immer ich ein GUI oder eine grafische Benutzeroberfläche baue, baue ich Kontroll-Frameworks in einer Schleife.

Das bedeutet, dass die Schaltflächen einfach als B0,B1,B2,B3,B4,B5 und usw. erstellt werden, was in Schaltfläche 0 Schaltfläche 1 Schaltfläche 2 und usw. übersetzt wird.

Und ich verwende immer IntegerToString(), um die numerische Ganzzahl zum Objektnamen hinzuzufügen.

Wenn Sie den Trigger wollen, können Sie auch verwenden:

if((Ask+Bid)/2>ObjectGetDouble(0,"object name",OBJPROP_PRICE)
 {
  // Trigger 1
 }

else if((Ask+Bid)/2<ObjectGetDouble(0,"object name",OBJPROP_PRICE)
 {
  // Trigger 2
 }

Oder eine Variation, da dies nur als Anregung dienen soll.

Im Grunde genommen nehmen Sie also einen Preis Ask oder Bid oder Median und vergleichen ihn mit dem Double, auf dem sich Ihre Hline gerade befindet.

Die einzige Grenze ist Ihre eigene Vorstellungskraft.

 

Marco, Du bist noch in Deinem EA-Code. Oder doch?

Das ist nicht das, wovon ich spreche.

Die Idee war, das vorgegebene Grafikobjekt um Funktionalität zu erweitern, indem ich ein eigenes Objekt schreibe, das vom ursprünglichen Grafikobjekt erbt und es um die Funktionen "Kaufen" und "Verkaufen" erweitert.

Wenn Sie diesen Weg gehen, sollte mein SmartLine-Objekt im MT5-Menü neben der Trendlinie, den Pfeilen, dem Textobjekt und all diesen Dingen erscheinen.

Dabei kann man es wie die anderen Grafikobjekte mit der Maus anwählen und in den Chart einfügen.

Wenn MT5 das zulässt, dann bleibt nur noch die Frage zu klären, wie das Objekt durch das Terminalprogramm bei einer Kursänderung ausgelöst werden kann.

Keines der existierenden Grafikobjekte kann auf Preisänderungen reagieren (soweit ich weiß).

Willbur

 

Ich möchte nicht sagen, dass Sie es völlig falsch machen, aber Sie tun es, denn dies ist Strukturprogrammierung und nicht OOP. Der große Unterschied ist die Macht der Vererbung und Überladung. Übrigens kann man nicht wirklich von echten grafischen Objekten erben, aber man kann alles als Codeobjekt darstellen und von diesem Objekt aus auf eine Linie oder was auch immer verweisen. Das ist die Art und Weise, wie es normalerweise in jeder Klasse gemacht wird, egal ob es eine MFC- oder eine MQL-Klasse ist, es ist alles das Gleiche.

Wenn Ihre Zeilen Objekte sind, dann behandeln Sie sie als solche. Handeln Sie nicht mit Arrays außerhalb, tun Sie es innerhalb einer Klasse Sammlung und arbeiten Sie mit Zeigern. Werfen Sie einen Blick auf CWndContainer und machen Sie sich ein Bild davon. Diese Klasse ist ein Container, der hauptsächlich Zeiger-Arrays für CWnd-Objekte verwaltet. Gehen Sie einen Schritt weiter, Ihre Struktur sollte sein:

CObject als Basis für jedes Objekt

CPriceTimeObjects als Basis für jedes preis-/zeitbasierte Objekt, wie z.B. Linien, leitet sich von CObject ab. Es steuert die Erstellung, hält Zeit und Preis und ruft eine OnCreate() auf, die vom nächsten Erben verwendet werden kann. Es verfügt auch über eine Tick-Funktion, die ein virtuelles OnTick() aufruft, das dann von den Nachfolgern überladen wird.

CTrendLine als Basis für Trendlinien erbt von CPriceTimeObjects und behandelt OnCreate, wobei es die endgültige Linie mit der Funktion ObjectCreate erstellt. Sie sollte auch einen OnTick()-Handler haben, um auf Tick-Ereignisse zu reagieren, da sie preisabhängig sein soll, soweit ich das verstanden habe.

Daneben gibt es eine Containerklasse, die ein Zeigerarray verwaltet, das alle gewünschten CTimePriceObject-Objekte enthält. Sie erbt selbst auch von CTimePriceObject und übergibt OnTick() an ihre "Kinder". Der Container hat auch eine Funktion, die das OnChartEvent() verarbeitet, um Linien hinzuzufügen oder zu entfernen. Er sollte auch eine Scan-Funktion haben, um alle existierenden Objekte für den Fall zu scannen, dass der Experte hinzugefügt wurde, nachdem Linien erstellt wurden. Darüber hinaus behandelt es das überladene OnTick() von CTimePrice, durchläuft das dortige Array, fragt jedes CTrendLine-Objekt, ob es sich irgendwie verantwortlich fühlt, zu reagieren, indem es die Tick-Funktion jedes Kindobjekts aufruft, die von einem virtuellen OnTick behandelt wird. Warum das Ganze noch einmal? Weil die CTrendLine diese Funktion auch von CTimePrice überlädt und somit diese Klasse auch von weiteren Erben mit weiteren Funktionen geerbt werden kann.

Ihr Code sollte später wie folgt aussehen:

CTPContainer Container;

::OnChartEvent(...)

container.ChartEvent(id, lparam, dparam, sparam) //.. führt zu OnCreate() und OnDelete() bei jedem CTrendLineObject. Der Container entscheidet, was zu tun ist, nicht Ihr EA.

::OnTick()

container.Tick() // .. führt zu OnTick() bei jedem CTrendLine-"Kind"-Objekt

und so weiter.

Das ist eine klare OOP-Basis, die leicht um nützliche Funktionen erweitert werden kann, ohne jemals wieder einen EA selbst zu berühren, der diese Klassen verwendet.

 
Ich möchte nicht sagen, dass du es völlig falsch machst, aber du tust es... <br / translate="no">

Wow ... danke für die Lektion.

Irgendwie hört es sich an, als hättest du den richtigen Ansatz.

Ich werde versuchen, meinen Code in diese Richtung zu ändern, bevor ich mit ihm zurückkomme.

Willbur

 

Wenn Sie konsequent und auf Anhieb objektorientiert programmieren, werden Sie es nie bereuen. Der Anfang mag schwieriger sein als der normale Top-Down-Weg, aber Sie werden in der Lage sein, auf einem viel höheren Niveau zu entwickeln, das mehr Leistung, mehr Möglichkeiten, mehr Flexibilität und leichtere Kompatibilität mit zukünftigen Änderungen beinhaltet.

MQL ist großartig, aber ohne OOP werden Sie nie herausfinden, wie viel es wirklich ist.

Wenn Sie irgendwelche Fragen haben, posten Sie sie einfach und ich werde versuchen zu helfen.

Grund der Beschwerde: