Projekt des Beraters - Seite 4

 
Vitaly Muzichenko:

Ich auch, aber ich bin schon vor langer Zeit zu dem Schluss gekommen, dass Code an Orten kompakt sein sollte, an denen er nie angeschaut, nie korrigiert wird und auch nie korrigiert werden wird.

Das Verstreuen von Benutzercode über die gesamte Umgebung bereitet zusätzliche Kopfschmerzen. Wenn Sie eine Datei in ein anderes Terminal ziehen oder freigeben wollen, müssen Sie mehrere Dateien ziehen und ablegen. Natürlich können Sie die Includniks auf alle Terminals übertragen, aber wenn Sie in einem Terminal etwas ändern oder hinzufügen, müssen alle durch ein neues Terminal ersetzt werden.

Die Expert Advisors und Indikatoren sind so klein, dass es keinen Sinn macht, sie vom Hauptteil des Programms zu trennen. Um genau zu sein, sie sind nicht klein, sie sind eine einzige Datei, es ist nicht wie bei einer Website mit 10 000 Seiten, wo man nicht ohne Klasse und Einfügungen auskommt. Außerdem gibt es jetzt Strukturen, die ausreichen, um einen kompakten und 100%ig funktionierenden Code zu schreiben.

Hier geht's los.... Kennen Sie sich mit symbolischen Links zu Ordnern aus?http://skesov.ru/sozdanie-simvolnoy-ssyilki-dlya-papki/

Ich habe alle meine Bibliotheken in einem Ordner, und in einer Reihe von Terminals, es gibt Dutzende von ihnen, in mql*\includes-Ordnern gibt es symbolische Links zu diesem echten Ordner. Nichts muss irgendwohin geschleppt werden.

Außerdem nutze ich den Speicherplatz aktiv. Wenn ich dort alles Wichtige aufbewahre, kann ich es in 5 Sekunden auf ein anderes Terminal herunterladen. Aber für Libs sind symbolische Links bequemer, immer volle Synchronisation.

Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
  • 2013.07.24
  • skesov.ru
Доброго времени суток! Сегодня рассмотрим интересную тему под названием «Символьные ссылки». Вариантов использования данного инструмента не так уж много. К примеру, если вы используете часть оперативной памяти как RAM-диск, можно перенести какую-либо игру или её часть (скажем папки с графикой) и создать символьную ссылку. Это значительно...
 
Alexey Navoykov:
Ich empfehle die Verwendung von symbolischen Links oder Junction Links für den MQL-Ordner. Alle Terminals befinden sich im selben Ordner.

Und es mit jemand anderem teilen?

 
Vitaly Muzichenko:

Mit jemandem teilen?

Nun, Sie müssen sich entscheiden, was Ihnen wichtiger ist: die Kontrolle oder die Fahrt). Ist Ihnen die Einfachheit des Austauschs mit anderen wichtiger als die Bequemlichkeit des Programmierens? Wenn Sie z.B. einen Fehler in einer Funktion gefunden haben, die von vielen Expert Advisors verwendet wird, dann müssen Sie in den Code aller Expert Advisors einsteigen und diese Funktion neu schreiben - stört Sie das als Programmierer nicht?

 
Vielen Dank an alle, die meine Frage erörtert haben.
Beschlossen, in Richtung OOP in mqlx für den Anfang zu suchen, halten Sie Ihre (wiederholbare) Funktionen in einer separaten Datei (en). Und seien Sie nicht zu faul zu kommentieren.

Und noch ein + für den symbolischen Link in Windows! Ich habe es unter Linux benutzt, aber unter Windows vergessen. Ich werde es ausprobieren müssen...
 
Alexey Navoykov:

Sie müssen sich entscheiden, was Ihnen wichtiger ist: die Kontrolle oder die Fahrt). Ist Ihnen die Einfachheit des Austauschs mit jemandem wichtiger als die Bequemlichkeit des Programmierens? Wenn Sie beispielsweise einen Fehler in einer Funktion entdeckt haben, die von vielen Expert Advisors verwendet wird, müssen Sie in den Code jedes einzelnen von ihnen gehen und diese Funktion neu schreiben - stört Sie das als Programmierer nicht?

Ich hatte so einen Fall nur einmal, vor etwa einem Monat, aber ich musste dort die Prüfung auf Marktoffenheit hinzufügen, es gibt alle Prüfungen außer dieser einen, und sie ist zum ersten Mal aufgetreten, seit ich sie benutze.

Wenn etwas hinzugefügt werden muss, füge ich es dem aktuellen Programm hinzu, und danach verwende ich die Datei als Vorlage für das nächste Programm. Das Ergebnis ist, dass in ein paar Jahren die Vorlage alles, na ja, oder fast alles, hat, so dass ein Bot von beliebiger Komplexität in einer halben Stunde geschrieben werden kann.

Der gesamte ausführbare Code passt in einen Bildschirm, obwohl die Datei etwas mehr als 4.000 Zeilen umfasst, aber ich schaue dort nur sehr selten nach, wenn dann nur das, was ich hinzufügen muss. Von Funktionen in Schleifen abgelehnt, verwendet nur zwei, eine auf der offenen sammelt Informationen, die zweite auf die Geschichte, und all dies in der Struktur am unteren Rand des Codes. Alles ist sehr einfach und nahe beieinander. Der Hauptcode ist kommentiert. Das Projekt lässt sich sehr leicht und schnell und ohne Verluste erweitern.

 
Alexey Volchanskiy:

Sieht gut aus, können wir auch TRACE_*** und ASSERT sehen?

Na ja... Für den Autor eines Meisterkurses über die Verführung von Frauen, den ich mit schwarzem Neid beneide, sind Sie willkommen.

Die Debug-Version wird in meinem Code automatisch aktiviert, wenn das entsprechende Systemmakro definiert ist. Wenn dies nicht der Fall ist, können Sie auch Asserts und Trace by Defines aktivieren:

#define _FORCETRACE 1
#define _FORCEASSERT 1

In diesem Fall werden - unabhängig von den Systemeinstellungen - Debug-Traces und Debug-Asserts erzeugt.

Ich habe eine Richtlinie zur Verbindung dieser Makros:

#include <MyLib\DebugOrRelease\DebugSupport.mqh>

Mit dieser Richtlinie werden alle erforderlichen Dateien und Definitionen verknüpft. Ich habe sie alle in einem separaten Ordner DebugOrRelease. Ich füge sie hier ein. (Der Code wurde vor langer Zeit geschrieben, meist "hastig", daher ist er nicht so schön wie die Schnittstellen und die Geschichtsklasse). Die Asserts und Traces selbst für die Debug-Version befinden sich in den Dateien AssertD und TraceD, die eigentlichen Funktionen sind PerformAssert() und PerformTrace().

Außerdem verwenden diese Dateien und Makros die globale Protokolldatei (wenn die Ausgabe in die Protokolldatei eingestellt ist), ich habe es schon einmal gepostet, aber noch einmal. Die Protokolldatei befindet sich in meinem Ordner "Common".

Dateien:
 
Andrey Kisselyov:

Gute Arbeit, gefällt mir, aber ich mag OOP nicht und versuche, darauf zu verzichten. Ich mag keine Prozessoren mit Stream-Splitting (z.B. 4 Kerne und 8 Threads). Es sollte klar sein, dass Splitting und jede Virtualisierung einen Leistungsverlust und einen Verlust an Rechenzeit für die Implementierung bedeutet, egal ob es sich um Stream-Splitting im Kernel oder Virtualisierung von Funktionen im Code handelt.

Kürze ist die Schwester des Talents, ich finde, das klingt besser.

Ich habe schon vor langer Zeit gelernt, dass die Wartbarkeit und Wiederverwendung von Code viel wichtiger ist als die Reduzierung der Leistung.

OOP - es hilft mir sehr, wenn ich nach einiger Zeit zum Code zurückkehre, um ihn zu ändern. Von der Wiederverwendung ganz zu schweigen.

Aber ich stimme zu, dass es bei weitem nicht immer notwendig ist , OOP zu verwenden.

Sagen wir, ich habe CDataProvider:pulic CDataProviderI Klasse - Datenanbieter, die Experten mit Zeitreihen, Indikatoren, Terminal und Umweltdaten bietet. In einem Expert Advisor kann es viele TS geben - jeder von ihnen würde Zeiger auf Zeitreihen und Indikatoren vom Datenanbieter erhalten (jeder TS müsste keine Zeitreihen erstellen - der Datenanbieter würde Zeiger auf die erforderlichen Zeitreihen bereitstellen, wenn sie bereits existieren, und würde nur Zeitreihen erstellen, die noch nicht erstellt wurden).

Wenn Sie einen Indikator vom Datenanbieter benötigen, füllen Sie die Struktur für die Indikatorbeschreibung aus und fordern dann einen Indikator vom Anbieter an, der auf diese Struktur verweist.

Dementsprechend sollte jeder Indikator innerhalb des Datenanbieters in der Lage sein, seine Struktur zu identifizieren (der Datenanbieter "kennt" nur die abstrakte Basisklasse der Struktur) und entsprechend das fertige Indikatorobjekt zu erstellen, das vom Datenanbieter erstellt wird.

Aber es ist unvernünftig, all diese Dinge nur deshalb zu machen, um eine neue Idee für einen Indikator zu überprüfen. Infolgedessen ist bei diesen neuen Indikatoren alles "hausgemacht", ohne OOP. Wenn ich jedoch sehe, dass ein Indikator für Expert Advisors nützlich ist, wird er "richtig" geschrieben - mit voller OOP-Unterstützung und Erstellung innerhalb eines Datenanbieters.

P.S.

Übrigens, im Fall von Indikatoren und Datenanbietern sehen wir den Vorteil der Virtualisierungsvererbung. Wir haben die Basisschnittstelle der Indikatorparameter CIndicatorParametersI, die Nachfolger dieser Schnittstelle sind die tatsächlichen Parameter des notwendigen Indikators. Bei der Abfrage des Indikators deklarieren wir diese Parameter und übergeben dem Datenanbieter einen Zeiger auf die abstrakte Schnittstelle. So weiß der Datenlieferant selbst nicht einmal, welcher Indikator angefordert wird - er wird in einer Funktion definiert, in der der Indikator entsprechend dem neuen Typ erstellt wird. Und nur dieser Indikator weiß, welche Parameter übergeben wurden, er holt sich die benötigten Parameter aus dem übergebenen Objekt.

Der Trick besteht darin, dass fast überall im Datenanbieter eine einfache Basisklasse von Parametern (oder Indikatoren) vorhanden ist - nur die einfachsten und gebräuchlichsten Funktionen der Basisschnittstellen sind für den Datenanbieter verfügbar. Dies vereinfacht die Änderung des Codes (wenn sie erforderlich ist) und führt nicht zu der Versuchung, den Code der Indikatoren des Datenanbieters zu "manipulieren". Wenn Sie einen Indikator ändern möchten, geschieht dies nur innerhalb des Indikators. Der Datenanbieter ist nur ein Speicher für Indikatoren, er kann höchstens einen neuen Indikator erstellen.

 
George Merts:

Übrigens macht es mich sehr nervös, wenn die Verschachtelung mehr als zwei Ebenen umfasst. Ich versuche, es nie so zu schreiben und den Code über Funktionen zu verteilen.

Und selbst wenn es zwei Verschachtelungsebenen gibt - nach jeder schließenden Klammer muss ich einen Kommentar schreiben, welcher Block damit geschlossen wird (z. B. doppelter Schleifenkopf).

Wie für Stil, hier ist mein Code für die Auswahleiner Geschichte Position für MT5 (von bestimmten Magier, Symbol, mit bestimmten Datum Bereich):

Die History-Klasse selbst ist ein Abkömmling der abstrakten Schnittstelle CTradeHistoryI:

Durch Auswahl der gewünschten Historie können Sie deren Komponenten (Positionen für MT5 oder Aufträge für MT4) neu berechnen und eine Schnittstelle zu jeder Komponente als abstrakte Schnittstelle erhalten:

Für MT4 gibt es entsprechende History-Klassen, die ebenfalls von diesen Interfaces erben - damit ist gleichzeitig der plattformübergreifende Charakter gegeben - ein EA muss nicht herausfinden, wo er arbeitet, die gesamte Arbeit mit der History wird über abstrakte Interfaces erledigt.


Das ist keine große Kritik:

class CTradePosComponentI: public CMyObject
{
...
}

Warum das Rad in Form von CMyObject neu erfinden, wenn es ein Standard-CObject gibt, das jeder versteht?

class class CTradeHistoryI: public CMyObject
{
// Расширенный интерфейс
   virtual void Sort(ESortTPCMode stmMode = STM_BY_OPEN_TIME_A) = 0;
}

Die Funktionalität von CObject und CArrayObj wird hier eindeutig übernommen. Warum? Die Schnellsortierung ist in Standard-Datencontainer eingebaut. Sie sollten sie verwenden.

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
}

Wenn eine Klasse eine Schnittstelle hat, sollte man ihren Konstruktor besser im geschützten Bereich verstecken. Dann kann das Objekt nicht direkt erstellt werden.

Einen leeren Konstruktor definieren? Ich weiß es nicht. Das würde ich nicht tun. Sie sollten den Destruktor besser nicht erwähnen, wenn Sie ihn nicht brauchen.

for(iI=0;iI<iHistoryDealsTotal; ++iI)
...

Nicht-Standard-Inkrement iI, Nicht-Standard-Iteration ++iI, iHistoryDealsTotal - irgendwo da draußen definiert, weit vor der Schleife. Einfacher und besser:

for(int i = 0; i < HistoryDealsTotal();i++)

Sie ist genauso schnell wie die vorherige Version, aber viel deutlicher.

virtual bool               IsTPCInUnloss() const { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };

Die Programmierer selbst scheinen gegen solche Texte zu sein, aber sie selbst schreiben sie irgendwo. Niemand will sich mit solchem Unsinn befassen. Was hat sie daran gehindert, es so zu schreiben?

virtual bool IsTPCInUnloss() const
{
   if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE)
      return(false);
   if(GetTPCType() == POSITION_TYPE_BUY)
   { 
      if(GetTPCStopLoss() >= GetTPCOpenPrice())
         return(true);
   } 
   else
   {
     if(GetTPCStopLoss() <= GetTPCOpenPrice())
        return(true);
   }; 
   return (false);
};

Und ';' am Ende von geschweiften Klammern - das ist veraltet, das sollte man nicht mehr machen.

Eine riesige Select-Methode besteht aus einer riesigen for-Schleife:

for(iI=0;iI<iHistoryDealsTotal; ++iI)
      {
      ulCurTicket = HistoryDealGetTicket(iI);
      
      if(ulCurTicket == 0)
         return(WRONG_VALUE);
      
      // Получим направление сделки   
      if(HistoryDealGetInteger(ulCurTicket,DEAL_ENTRY,lCurEntry)!=true)
         {
         TRACE_INTEGER("Не удалось получить направление сделки ! Тикет: ",ulCurTicket);
         continue;
         };
      
      // Проверим направление сделки
      if(lCurEntry != DEAL_ENTRY_OUT)
         continue;
      
      // Получим магик сделки
      if(HistoryDealGetInteger(ulCurTicket,DEAL_MAGIC,lCurMagic)!=true)
         {
         TRACE_INTEGER("Не удалось получить магик сделки ! Тикет: ",ulCurTicket);
         continue;
         };
         
      // Проверим магик
      if(ulMagic != NULL && lCurMagic != ulMagic)
         {
         //TRACE_INTEGER("Сделка не подходит ! Имеет неверный магик ! Magic сделки: ",lCurMagic);
         //TRACE_INTEGER("Требуемый Magic : ",ulMagic);
         continue;
         };
      ...
}

Natürlich sollten alle Überprüfungen, ob die Transaktion mit dem aktuellen Expert Advisor übereinstimmt, in einer separaten Methode implementiert werden, zum Beispiel so:

for(iI=0;iI<iHistoryDealsTotal; ++iI)
{
   if(!CheckDeal(iI))
      continue;
   ...
}

Und im Allgemeinen sollte select in 3-4 weitere Methoden aufgeteilt werden, damit klar wird, was in ihm vorgeht.

George Merts
Der Expert Advisor selbst besteht aus fünf Zeilen. In dieser Datei wird das Objekt der Teilefabrik des EA selbst deklariert und die Einschlüsse werden aufgenommen.

Factory ist ein sehr umstrittenes Muster. Es ist gut, es zu benutzen, aber ich empfehle nicht, alles über eine Fabrik zu machen.

George Merts
Und selbst wenn es zwei Verschachtelungsebenen gibt, ist es obligatorisch, nach jeder schließenden Klammer einen Kommentar zu schreiben, der angibt, welchen Block sie verdeckt (z. B. einen doppelten Schleifenkopf).

Das ist der Grund, warum Sie es in Klammern auf die hässliche Art von MQL schreiben. Wenn Sie es so schreiben würden:

if(OrderSelect())
{
   ...
}

Sie würden immer sehen, welche Klammer welchen Codeblock abschließt.

Es gäbe noch ein Dutzend anderer Warnungen in Ihrem Code. Natürlich ist der Code nicht perfekt, aber man kann den Geschmack des Autors für Schönheit spüren :))

 
Vasiliy Sokolov:

Eine kleine Kritik:

О. Das ist die Art von Diskussion, die ich liebe. Also.

Warum das Rad in Form von CMyObject neu erfinden, wenn es ein Standard-CObject gibt, das jeder versteht?

Die Funktionalität von CObject und CArrayObj wird hier offensichtlich übernommen. Warum? Die Schnellsortierung ist in Standard-Datencontainer eingebaut. Verwenden Sie sie.

CMyObject ist ein Erbe des Standard-CObject, alle Listen und Arrays in meinem Code sind Nachkommen von CArray (und anderen Standardbibliothek-Arrays). Ich verwende fast nie standardmäßige array[]-Arrays.

Und natürlich werden beim Sortieren und Arbeiten mit Listen die grundlegenden Funktionen von CObject verwendet.

Und der Unterschied zwischen ihnen ist folgender: Ein Standard-CObject ist "ein Listen- oder sortiertes Array-Objekt". Ein CMyObject ist ein CObject, das einen bestimmten Typ hat und einen Wert enthält, der bei seiner Erstellung angegeben wurde. Ich brauchte dieses Objekt wegen der weit verbreiteten Reduktion von Objekten auf eine abstrakte Basisklasse - um durch einen Zeiger zu verstehen, auf welches Objekt "eigentlich" gezeigt wird. Der Typ CMyObject wird durch eben diese Funktion SetMyObjectType () festgelegt. Diese Funktion muss notwendigerweise in den Konstruktoren eines jeden von CMyObject abgeleiteten Objekts aufgerufen werden, um der Klasse, zu der das Objekt gehört, einen Bezeichner zuzuweisen.

Außerdem gibt es die Funktion SetUDCreationValue(), die bei der Erstellung einen benutzerdefinierten Wert setzt. Selten verwendet. Sie wird benötigt, um verschiedene Objekte der gleichen Klasse zu unterscheiden.

Wenn die Klasse Schnittstelle - sein Konstruktor ist besser, in den geschützten Abschnitt zu verstecken. Dann kann das Objekt nicht direkt erstellt werden.

Geschützter Konstruktor Ja, ich denke, das ist für Schnittstellen sinnvoll, ich wusste nicht, dass das möglich ist.

Einen leeren Konstruktor definieren? Ich weiß es nicht. Das würde ich nicht tun. Es ist besser, den Destruktor nicht zu erwähnen, wenn man ihn nicht braucht.

Es ist ein "verfluchtes Erbe der Vergangenheit". Wir haben einmal ein ziemlich großes Projekt geschrieben, und wenn wir dort keinen leeren Destruktor definierten, dauerte es aus irgendeinem Grund ziemlich lange, die Objekte zu entfernen. Seitdem mache ich das immer spontan. Im Allgemeinen muss auch der Destruktor virtuell sein.

Nicht-Standard-Inkrement iI, Nicht-Standard-Iteration ++iI, iHistoryDealsTotal - irgendwo da draußen definiert, weit vor der Schleife. Es ist besser, es einfacher zu halten:

Sie sind nicht einverstanden. Inkrement ist ganz normal, - i, nur standardisierte Notation - zuerst mit einem kleinen Buchstaben ist sein Typ Integer, und dann mit einem großen Buchstaben ist sein Name I.

Sie scheinen gegen solche Blätter zu sein, aber Sie schreiben es irgendwo. Niemand hat Lust, solchen Unsinn zu analysieren. Was hat Sie davon abgehalten, es so zu schreiben?

In diesem Fall musste ich zwischen der "Sichtbarkeit" der Klasse und der Schönheit der Funktion wählen. Ich habe "Sichtbarkeit" gewählt. Die Schönheit hat gelitten.

Die Methode giant Select besteht aus einer riesigen for-Schleife:

Natürlich sollten alle Überprüfungen, ob die Transaktion mit dem aktuellen Expert Advisor übereinstimmt, in einer separaten Methode erfolgen, z.B. so:

Und im Allgemeinen muss select in 3-4 weitere Methoden aufgeteilt werden, damit klar wird, was darin vor sich geht.

Ich stimme zu. Hier ist im Prinzip genau dieser Kreislauf "gewachsen", anfangs war er nicht so groß.

Allerdings ist es nicht immer bequem, kleinere Prüfungen in private Funktionen zu implementieren, da diese Prüfungen im Code nicht immer nachvollziehbar sind.

Factory ist ein sehr umstrittenes Muster. Die Nutzung ist in Ordnung, aber ich empfehle nicht, alles über eine Fabrik abzuwickeln.

Jetzt erinnere ich mich nicht mehr, es gab mehrere Varianten des Aufbaus des Expert Advisors. Ich habe bei der "Expert Advisor parts factory" angehalten. Im Prinzip handelt es sich nicht mehr um ein reines klassisches "Fabrik"-Muster. Ursprünglich sollte es ein "klassisches" Muster sein, aber jetzt ist es eher ein "Konstruktor-Konzentrator" von Expert Advisor-Teilen. Und sie ist auch für die Entnahme dieser Teile zuständig, was nicht typisch für eine Fabrik ist. Aber der Name blieb.

Deshalb schreiben Sie es in Klammern in der hässlichen Art von MQL. Wenn Sie es so schreiben würden:

Sie würden immer sehen, welche Klammer welchen Codeblock abschließt.

Warum "die hässliche Art"?

Der Schleifenkopf, dann die öffnende Klammer mit Einrückung, dann der ganze Block mit der gleichen Einrückung und schließlich die schließende Klammer - ebenfalls mit Einrückung.

Was ist Ihrer Meinung nach besser?

 
Gregory Kovalenko:

Ich muss bei 2 offenen Aufträgen Gewinne erzielen. Die letzte offene Order nenne ichOrderProfit2 und die nächste Order Order -OrderProfit1.

Die erste Ordnung wird zuerst geöffnet, dann die 2. Ordnung, so dass die 1. Ordnung in der Schleife aufgerufen wird 2)

Wo liegt der Fehler?

Sie gehen nur die Aufträge durch. Nirgends wird geprüft, wer der Erste und wer der Zweite ist.

Sie müssen eine Überprüfung der Öffnungszeit eingeben. So können Sie zwischen dem Auftrag, der früher geöffnet wurde, und dem Auftrag, der später geöffnet wurde, unterscheiden. Vielleicht können sie auch gleichzeitig geöffnet werden.