Weniger Code, mehr Action... einen EA schreiben

 

Ich werde versuchen (oder wir werden es versuchen, falls jemand daran interessiert ist), einen Rahmen für Expert Advisors zu schaffen. Möglichst nur für einfache Dinge geeignet und die keine substantiellen Kenntnisse vom angewandten Programmierer erfordern.

Im Gegensatz zu der lokal üblichen Praxis wird der Entwurf von oben nach unten durchgeführt. Mit anderen Worten: Zuerst schreiben wir die Haupt- (und einzige) Datei des EA für den Endbenutzer, entfernen alles, was offensichtlich unnötig ist, und schreiben eine Bibliothek, die dies ermöglicht. Dann fügen wir einen neuen Anwendungsfall hinzu und aktualisieren die Bibliothek.

Als Gegenthese und Polemik gegenüber https://www.mql5.com/ru/articles/5654 und den Beratern von Herrn Karputow

Bevor man überhaupt weitermacht, muss man natürlich die "Parteilinie" und den "Zielkommunismus" festlegen:

- 100 Zeilen genügen dem Benutzer, um die Strategie umzusetzen. (abgesehen von Kommentaren, Eingaben und anderen #Eigenschaften).

- Die Anzahl der neuen "Entitäten" (Funktionen/Klassen/Methoden/Konstanten) sollte auf ein Minimum reduziert werden.

- Die Bibliothek muss eine zählbare Anzahl von Dateien enthalten.

- Potenziell geeignet für GUI

- erweiterbar durch Plugins

Erreichbare Ziele ? schwer, aber im Prinzip JA. Es gibt einige Möglichkeiten, einige Ideen. Aber es gibt noch keine fertige Lösung :-)

Der nächste Schritt ist die Organisation der Datenerfassung durch den Experten. Und zwar so, dass sie für den Benutzer leicht zu beschreiben sind, aber gleichzeitig gibt es noch "Anhaltspunkte" für Metadaten und weitere Erweiterungen. (Zukünftig sollte der GUI-Teil, zumindest der Debugging-Teil, durch den weiteren Code implementiert werden)


Als Initiator habe ich einen einfachen Anwendungsfall geschaffen - wir handeln bei der Kreuzung von zwei MA

Dementsprechend sieht der Eingabeteil wie folgt aus

/** ------- ПАРАМЕТРЫ СОВЕТНИКА ------
**/
input ENUM_APPLIED_PRICE FAST_MA_PRICE=PRICE_CLOSE;
input ENUM_MA_METHOD FAST_MA_METHOD=MODE_EMA;
input int FAST_MA_PERIOD=14;
input int FAST_MA_SHIFT=0;

input ENUM_APPLIED_PRICE SLOW_MA_PRICE=PRICE_CLOSE;
input ENUM_MA_METHOD SLOW_MA_METHOD=MODE_SMA;
input int SLOW_MA_PERIOD=54;
input int SLOW_MA_SHIFT=0;

Als nächstes muss der Benutzer beschreiben, welche Daten er durch die Eingabe erhält. Führen Sie sie zumindest auf:

// просто перечисляем идентификаторы данных
// всех которые нужны для принятия решений
enum ENUM_SERIES {
   FAST_MA,       // id. значений FAST_MA
   SLOW_MA,       // id. значений SLOW_MA
   TOTAL_SERIES   // последний элемент перечисления = кол-во элементов
};

und erklären Sie, wie es diese Daten berechnet/empfängt (es ist lang, gilt aber für beide Terminals).

/// вычисление данных
/// эксперт будет обращаться к функции каждый раз когда ему необходимы данные
/// управление кешированием и очерёдность(взаимозависимость) вычислений лежит на верхнем уровне
/// @arg ea - эксперт
/// @arg id - ид.серии данных
/// @arg shift - сдвиг в серии
/// @arg data[] - кешированные результаты
/// @return double - конкретное значение для [id][shift] или EMPTY_VALUE если не может быть вычилено
/// если данные могут быть кешированы, они должны быть сохраненны в массиве data
double GetData(EA *ea,int id,int shift,double &data[])
{
#ifdef __MQL4__
   // для 4-ки всё просто - по идентификаторам серии и бара получить данные
   switch ((ENUM_SERIES)id) {
      case FAST_MA:
         return data[shift]=iMA(ea.Symbol,ea.Period,FAST_MA_PERIOD,0,FAST_MA_METHOD,FAST_MA_PRICE,shift);
      case SLOW_MA:
         return data[shift]=iMA(ea.Symbol,ea.Period,SLOW_MA_PERIOD,0,SLOW_MA_METHOD,SLOW_MA_PRICE,shift);
   }
   return EMPTY_VALUE;
#else
   // для 5-ки несколко сложнее (и кстати не проверено) - надо ещё заводить хендлы стандартных индикаторов
   // и проводить (возможно)лишнее копирование
   static d_fast_ma=0;
   static d_slow_ma=0;
   if (d_fast_ma==0) d_fast_ma=iMA(ea.Symbol,ea.Period,FAST_MA_PERIOD,0,FAST_MA_METHOD,FAST_MA_PRICE,shift);
   if (d_slow_ma==0) d_slow_ma=iMA(ea.Symbol,ea.Period,SLOW_MA_PERIOD,0,SLOW_MA_METHOD,SLOW_MA_PRICE,shift);  
   double tmp[1];
   switch((ENUM_SERIES)id) {
      case FAST_MA: CopyBuffer(d_fast_ma,0,shift,1,tmp); return data[shift]=tmp[0];
      case SLOW_MA: CopyBuffer(d_slow_ma,0,shift,1,tmp); return data[shift]=tmp[0];
   }
   return EMPTY_VALUE;
#endif
}

und beschreiben schließlich das Handelssignal:

/// генерация(вычисление) сигнала при открытии бара
/// @arg ea - эксперт
/// @arg shift - номер бара в таймсерии. Типично будет 0
/// @return int - сигнал OP_BUY или OP_SELL или -1 если сигнала нет 
int SignalOfCross(EA *ea,int shift)
{
   if (FAST_MA_PRICE!=PRICE_OPEN || SLOW_MA_PRICE!=PRICE_OPEN) shift++;
   if (ea.CrossedUp(FAST_MA,SLOW_MA,shift)) {
      return OP_BUY;
   }
   if (ea.CrossedDn(FAST_MA,SLOW_MA,shift)) {
      return OP_SELL;
   }
   return -1;
}

DAS IST IM GRUNDE ALLES. Das reicht aus, um einen EA zu implementieren, und der Benutzer muss natürlich nicht tonnenweise Dokumentation lesen. Der Benutzer benötigt nur ein Grundwissen über MQL. Alles andere sollte von der Bibliothek (oder hier, ein schickes Wort - Engine) erledigt werden. Es sollte für den Benutzer entwickelt werden und nicht dafür, dass der Benutzer eine weitere mehrbändige API erlernen muss.

Übrigens, hier ist OnInit :

int OnInit()
{
   ea = new EA();
   // настраиваем таймфрейм "по умолчанию"
   //   символ и период - текущие
   //   TOTAL_SERIES наборов данных и кешируем по 30 в каждом
   //   для получения данных служит GetData
   ea.SetupTimeframe(_Symbol,_Period,TOTAL_SERIES,30,GetData);
   // настраиваем сигнал "по умолчанию"
   ea.SetupSignal(SignalOfCross);
   // ------ настройки завершены ------
   // остальная часть одинакова для всех советников
   int ret;
   if ((ret=ea.OnInit())!=INIT_SUCCEEDED) {
      return ret;
   }
   EventSetTimer(60);

   return(INIT_SUCCEEDED);
}
  

..

Библиотека для простого и быстрого создания программ для MetaTrader (Часть I). Концепция, организация данных, первые результаты
Библиотека для простого и быстрого создания программ для MetaTrader (Часть I). Концепция, организация данных, первые результаты
  • www.mql5.com
Разбирая огромное количество торговых стратегий, множество заказов на изготовление программ для терминалов MT5 и MT4, просматривая огромное разнообразие различных сайтов по тематике скриптов, индикаторов и роботов для MetaTrader, я пришёл к выводу, что всё это многообразие в подавляющем своём большинстве строится на фактически одних и тех же...
 
Bitte geben Sie alle Ihre Codes korrekt ein: Es ist unmöglich, diese graue Tristesse zu betrachten. Es gibt einen klaren Aktionsplan: Klicken Sie auf die Schaltfläche im Editor, fügen Sie den Code in das entsprechende Feld ein. Aber Sie fügen stur eine Leinwand mit Text ein und versuchen dann, den Stil "Code" auf diese Leinwand anzuwenden
 
Vladimir Karputov:
Bitte geben Sie alle Ihre Codes korrekt ein: Es ist unmöglich, in diese graue Tristesse zu schauen. Es gibt einen klaren Aktionsplan: Klicken Sie auf die Schaltfläche im Editor, fügen Sie den Code in das entsprechende Feld ein. Sie fügen stur eine Leinwand mit Text ein und versuchen dann, den Stil "Code" auf diese Leinwand anzuwenden

Er (der Code) wird nicht korrekt eingefügt. Ein Fragment kann noch bewältigt werden, aber ein bisschen mehr ist schon eine Qual...

Ich meine, es liegt nicht an mir - es liegt an den Webmastern. Wie sie das im Jahr 2019 bei der Fülle der Mittel geschafft haben - ein Rätsel :-)

Aber damit es in Zukunft mehr Spaß macht, werde ich mehr oder weniger große Beiträge zuerst im Wiki schreiben und sie dann hier einfügen und den Umfang des veröffentlichten Codes reduzieren.

 
Maxim Kuznetsov:

Er (der Code) wird nicht korrekt eingefügt. Ein Fragment kann noch bewältigt werden, aber ein bisschen mehr ist schon eine Qual...

Ich meine, es liegt nicht an mir - es liegt an den Webmastern. Wie sie das im Jahr 2019 bei der Fülle der Mittel geschafft haben, ist ein Rätsel :-)

Aber um in Zukunft mehr Spaß zu haben, werden mehr oder weniger große Beiträge zuerst ins Wiki geschrieben und dann per Copy-Paste hierher übertragen, um den Umfang des veröffentlichten Codes zu reduzieren.

Offenbar müssen Sie den Codeblock jetzt kopieren und in Notepad einfügen

Kopieren Sie dann aus Notepad und fügen Sie wieder ein, aber erstellen Sie vorher einen neuen Block für den "neuen" Code

 

Ich habe eine Grundvorlage für den EA erstellt und die Strategiedatei separat erstellt.

Glauben Sie, dass es für den Benutzer einfach ist?

Sie benötigen trotzdem ein Minimum an Programmierkenntnissen!

Und keine "Hilfe"-Anweisungen, Videos, nichts rettet Sie.

Der Benutzer muss dann kostenlos Strategien in die Pedale treten.

Und sie werden die Hilfe nicht lesen.

 
Vitaly Muzichenko:

Anscheinend sollten Sie nun den Codeblock kopieren und in Notepad einfügen

Kopieren Sie dann aus dem Notizblock und fügen Sie ihn wieder ein, aber erstellen Sie vorher einen neuen Block für den "neuen" Code

Es hat ein bisschen Mühe gekostet, aber es hat irgendwie "gefärbt". :-)

Ein Kieselstein (oder eher ein Kieselstein) für die oben erwähnten Webmaster: Beim Einfügen in den Inline-Editor, oder besser gesagt beim ersten "Einfärben", beißt der Editor ungewollt einen Teil des Codes heraus, den er nicht mag. Insbesondere wurde willkürlich "if ((ret=ea.OnInit())!=INIT_SECEED) {..}" bearbeitet. Offenbar glaubt der Highlight-Algorithmus, dass OnInit der einzige ist und in der Klasse nicht überladen werden kann.

 
Handelt es sich um Pseudocode oder ist er bereits anwendungsreif? Die FunktionGetData kann keine "Statik" für Indikator-"Handles"(d_fast_ma,d_slow_ma) verwenden, da der Benutzer ein weiteres Paar "Handles" für die Filterung oder etwas anderes mit anderen Parametern (z. B. für ein anderes Symbol) verwenden wird. Man sollte entweder "Handles" in einem Objekt zwischenspeichern oder (wenn man nicht auf Effizienz achtet) "Handles" jedes Mal wieder holen - sie sollten vom Terminal selbst zwischengespeichert werden, wenn die Parameter gleich sind.
 
Maxim Kuznetsov:

Natürlich muss man, bevor man überhaupt weitermacht, die "Parteilinie" und den "Zielkommunismus" festlegen:

1) Der Benutzer benötigt nur 100 Zeilen, um die Strategie umzusetzen. (abgesehen von Kommentaren, Eingaben und anderen #Eigenschaften).

2) Die Anzahl der neuen "Entitäten" (Funktionen/Klassen/Methoden/Konstanten) sollte minimiert werden.

3) Die Bibliothek muss eine zählbare Anzahl von Dateien enthalten.

4) potentiell geeignet für GUI

5) erweiterbar durch Plugins


3) Welchen Unterschied macht es, wie viele Dateien eine Bibliothek enthält? 1, 10, 100, 1000? Alles sollte für die Bequemlichkeit des Entwicklers gemacht werden. Wenn es ihm nichts ausmacht, alles in kleinen Ordnern abzulegen, ist das kein Problem. Wenn er/sie daran gewöhnt ist, alles in einer Datei zu kodieren, ist das kein Problem. Im Großen und Ganzen ist es nicht schwierig, eine Megadatei aus einer Reihe unterschiedlicher automatisierter Mittel zusammenzustellen. Ich würde also nicht auf diesem Punkt beharren.

4) GUI nutzbar - es ist nicht ganz klar, wie es ist. Die Bibliothek ist eine isolierte Schicht von Geschäftslogik in einer ausgereiften Weise. Diese Schicht sollte in keiner Weise von der externen grafischen Benutzeroberfläche abhängen. Die grafische Benutzeroberfläche sollte von dieser Schicht abhängen.

 
Maxim Kuznetsov:
if (d_fast_ma==0) d_fast_ma=iMA(ea.Symbol,ea.Period,FAST_MA_PERIOD,0,FAST_MA_METHOD,FAST_MA_PRICE,shift);
if (d_slow_ma==0) d_slow_ma=iMA(ea.Symbol,ea.Period,SLOW_MA_PERIOD,0,SLOW_MA_METHOD,SLOW_MA_PRICE,shift);  


Ihr Ansatz ist von vornherein rein verfahrenstechnisch: Was ich sehe, erzähle ich auch. Was ist, wenn der Durchschnitt nicht auf der Grundlage der Preise, sondern z. B. auf der Grundlage des Volumens oder eines anderen Indikators berechnet werden muss? Müssen wir den Benutzer noch einmal umschreiben? Die Berechnung des Durchschnitts ist ein Algorithmus. Sie wollen den Algorithmus auf die Daten anwenden. Sie vermischen den Algorithmus mit den Daten, anstatt sie gleich zu trennen (wie dieser Pseudocode):

int period_ma = 12;
int shift = 3;
double fast_ma = SMA(Close, period, shift);

Oh, ich habe den Serienindex vergessen. - Es sollte wirklich keine geben. Sie müssen alles in einem Ringpuffer berechnen, indem Sie Algorithmen zu Pipelines kombinieren.

 
Maxim Kuznetsov:

Als Gegenthese und Polemik zu https://www.mql5.com/ru/articles/5654 und den Beratern von Herrn Karputow

Meiner ist zu Unrecht in Vergessenheit geraten. Und vergeblich, es gibt eine Menge davon.

 
Vasiliy Sokolov:

Ihr Ansatz ist von vornherein rein verfahrenstechnisch: Was ich sehe, erzähle ich auch. Was ist, wenn der Durchschnitt nicht auf der Grundlage der Preise, sondern z. B. auf der Grundlage des Volumens oder eines anderen Indikators berechnet werden muss? Müssen wir den Benutzer noch einmal umschreiben? Die Berechnung des Durchschnitts ist ein Algorithmus. Sie wollen den Algorithmus auf die Daten anwenden. Sie vermischen den Algorithmus mit den Daten, anstatt sie gleich zu trennen (wie dieser Pseudocode):

Ups, ich habe den Serienindex vergessen. - Es sollte wirklich keine geben. Sie müssen alles in einem Ringpuffer berechnen, indem Sie Algorithmen zu Pipelines kombinieren.

Ein großer Teil von allem wird um dieser Tatsache willen getan. So können Sie alles berechnen und der Expert Advisor kann die Abläufe und Zusammenhänge selbst herausfinden. Ich habe einmal einen Spreadsheet-Rechner a la micro-excel auf MT4 gemacht, und die Organisation der Berechnungen basiert auf diesem Modell.

Der Use-Case-Stil ist grundsätzlich verfahrensorientiert, da er am weitesten verbreitet ist. Potenzielle Benutzer(unerfahrene Programmierer) schreiben auf diese Weise.