Vermeidung von if-Statements und Conditional Breaks

 

Hallo Forum,

gerade bin ich dabei, mir die Erkenntnisse aus https://www.mql5.com/de/forum/351074 draufzuschaffen. Obwohl die Grundprinzipien einleuchten, hat es eine Weile gedauert, bis ich es überhaupt einigermaßen verstanden habe. Dann hatte ich erstmal andere Prioritäten und es ist ja doch ein Thema mit vielen Implikationen, das eine Versuchsreihe erfordert. Man muss hierbei das ganze Thema IF-Statements noch einmal von vorne lernen, nur eben mit boolschen Variablen und Funktionen.

Dieses Vorhaben zielt darauf ab, dass der Compiler den Code anders in Einsen und Nullen umsetzt und das Programm letztendlich schneller ausgeführt wird. Mir geht es erstmal darum, überhaupt einen einfachen EA zum Laufen zu kriegen, wo zum Beispiel die Handelsbedingungen und die Handelsausführung sämtlich aus boolschen Variablen und Funktionen bestehen, wodurch es scheinbar nicht zu Conditional Breaks kommt, wenn eine Bedingung nicht erfüllt wird, sondern es sich eher um eine Berechnung handelt, in der die boolschen Variablen alle miteinander malgenommen werden, bis die erste Null auftaucht. Dadurch wird der Term gleich Null und die Rechnung kann abgebrochen werden, da das Ergebnis des boolschen Ausdrucks somit feststeht und der Funktionsaufruf der CTrade wird nicht ausgeführt.

Ein Beispiel:

bool condition1=1*0*1*0*1=0=false

bool condition2=1*1*1*1*1=1=true

bool condition3=0*0*0*0*0=0=false

bool condition4=0*1*1*1*1=0=false

Der Zustand der Bedingungsvariable ist also immer gleich Null, sobald eine Null unter den Teilbedingungen ist. Zumindest hatte ich das so verstanden.
Soweit die Theorie. Eine Sache kam mir beim Ausprobieren jedoch komisch vor: Ersetze ich die &&-Zeichen (logisches UND) durch * passiert nicht das Selbe wie zuvor. Eigentlich sollte er nur handeln, wenn keine Position offen ist. Mit den Malzeichen feuert der EA jedoch bis zum Margin Stopout.

Hier ist mal eine Lösung, die macht was sie soll, ein MA EA mit SL und TP, der kauft und verkauft und maximal eine Position öffnen soll. Sie enthällt „&&“ zwischen den Bedingungen.

//+------------------------------------------------------------------+
//|MAFunktionsAufrufOhneIF01                                         |
//+------------------------------------------------------------------+

#property strict


#include <Trade\Trade.mqh>


const input double               InpLots=0.1;                  //Kontraktgröße
const input long                 InpMagicNumber=555666777;     //Magic Number


const input int                  InpAMA01Period=10;            // ATR period
const input int                  InpFastEMA01Period=2;         // MA Period
const input int                  InpSlowEMA01Period=20;        // RSI Period
const input int                  InpAMA01Shift=0;              // MA Shift
const input ENUM_APPLIED_PRICE   InpAMA01Price=PRICE_TYPICAL;  // MA Method


const int SL=1000, TP=3000;
double Ask, Bid;


bool buycondition=false;
bool sellcondition=false;


double    AMA01Array[];


int      AMA01Def;



CTrade Trade;


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()

  {
   Trade.SetExpertMagicNumber(InpMagicNumber);

   ArraySetAsSeries(AMA01Array,true);

   AMA01Def=iAMA(_Symbol,_Period,InpAMA01Period,InpFastEMA01Period,InpSlowEMA01Period,InpAMA01Shift,InpAMA01Price);

   ChartIndicatorAdd(0,0,AMA01Def);

//---
   return(INIT_SUCCEEDED);
  }


//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   IndicatorRelease(AMA01Def);
  }


//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   Print("******Positionen: ",PositionsTotal(),PositionsTotal()==0,"; buycondition: ",buycondition,"; sellcondition: ",sellcondition);

   CopyBuffer(AMA01Def,0,0,40,AMA01Array);

   GetPrices();

   TradingConditions();


   Comment(
      "\n Profit: ",PositionGetDouble(POSITION_PROFIT),
      "\n PositionSL: ",PositionGetDouble(POSITION_SL),
      "\n Bid : ",Bid,
      "\n Ask : ",Ask,
      "\n AMA01Array : ",AMA01Array[0],
      "\n Positionen: ",PositionsTotal()
   );
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+


void GetPrices()

  {
   Ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   Bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
  }



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void TradingConditions()
  {

   buycondition=(PositionsTotal()==0 && Ask>AMA01Array[0] && executeBuy(Ask,Bid,SL,TP));

   sellcondition=(PositionsTotal()==0 && Ask<AMA01Array[0] && executeSell(Ask,Bid,SL,TP));
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool executeBuy(const double ask, const double bid, const double sl, const double tp)
  {
   Trade.Buy(InpLots,_Symbol,ask,bid-sl*_Point,ask+tp*_Point,NULL);
   return(true);
   }
   
bool executeSell(const double ask, const double bid, const double sl, const double tp)
  {
   Trade.Sell(InpLots,_Symbol,bid,ask+sl*_Point,bid-tp*_Point,NULL);
   return(true);
   }


Bei der anderen Version werden die boolschen Variablen multipliziert.

void TradingConditions()
  {

   buycondition=(PositionsTotal()==0 * Ask>AMA01Array[0] * executeBuy(Ask,Bid,SL,TP));

   sellcondition=(PositionsTotal()==0 * Bid<AMA01Array[0] * executeSell(Ask,Bid,SL,TP));
  }

Mache ich etwas falsch oder ist das einfach die Grenze der Technik?

Gruß

 
Ganz einfach.
Die Multiplikation muss durchgeführt werden. Egal ob 0 oder 1.
Die boolsche Folge wird abgebrochen sowie eine Bedingung false ergibt.

Es macht keinen Unterschied im Sinne der Ausführung ob eine Multiplikation oder eine boolsche Evaluierung stattfindet. Es wird aber einen Unterschied in der Ausführungsfolge machen.

Bei einer Multiplikation muss bis zum Ende gerechnet werden.

Außerdem würde sonst jedes Zwischenergebnis mit einer zusätzlichen boolschen Anfrage geprüft werden müssen.

Immer einen Einzelschritt nach dem Anderen und fragen, was genau MUSS gemacht werden, bzw was genau verlange ich von der CPU. Welchen Befehl verarbeitet diese denn wirklich.

Hoffe das hilft.









 

Ich würde keine Lösung wählen,  die ich nicht nach 1 Jahr sofort wieder verstehen würde.

Warum verwendest Du nicht die einfachen und && und oder || ?

Der Unterschied wären nur Bruchteile von Millisekunden.

Hier ist etwas zur Algebra der Logik, Boolsche Algebra: https://studyflix.de/informatik/boolesche-algebra-977

Daran siehst Du && entspricht * (eine Null macht alles Null also falsch) und + dem || (eine 1 macht alles nicht Null, also wahr), aber * und + sind nicht wirklich logische Operatoren und stehen (imho) einem schnellen Verständnis entgegen.

Boolesche Algebra: Rechenregeln und Gesetze
Boolesche Algebra: Rechenregeln und Gesetze
  • studyflix.de
Gesetze und Rechenregeln der Booleschen Algebra ✅ Boolesche Logik: Grundlegende Gesetze ✅ Einfach und schnell erklärt ✅ Mit kostenlosem Video
 
Dominik Egert:
Ganz einfach.
Die Multiplikation muss durchgeführt werden. Egal ob 0 oder 1.
Die boolsche Folge wird abgebrochen sowie eine Bedingung false ergibt.

Es macht keinen Unterschied im Sinne der Ausführung ob eine Multiplikation oder eine boolsche Evaluierung stattfindet. Es wird aber einen Unterschied in der Ausführungsfolge machen.

Bei einer Multiplikation muss bis zum Ende gerechnet werden.

Außerdem würde sonst jedes Zwischenergebnis mit einer zusätzlichen boolschen Anfrage geprüft werden müssen.

Immer einen Einzelschritt nach dem Anderen und fragen, was genau MUSS gemacht werden, bzw was genau verlange ich von der CPU. Welchen Befehl verarbeitet diese denn wirklich.

Hoffe das hilft.


Das erklärt es wirklich gut. Danke sehr :)



Carl Schreiber:

Ich würde keine Lösung wählen,  die ich nicht nach 1 Jahr sofort wieder verstehen würde.

Warum verwendest Du nicht die einfachen und && und oder || ?

Der Unterschied wären nur Bruchteile von Millisekunden.

Hier ist etwas zur Algebra der Logik, Boolsche Algebra: https://studyflix.de/informatik/boolesche-algebra-977

Daran siehst Du && entspricht * (eine Null macht alles Null also falsch) und + dem || (eine 1 macht alles nicht Null, also wahr), aber * und + sind nicht wirklich logische Operatoren und stehen (imho) einem schnellen Verständnis entgegen.


Zugegeben, es sieht etwas kompliziert aus. Ich weiß noch nicht was es geschwindigkeitsmäßig bringt, habe es mir noch nicht im Vergleich anzeigen lassen Vermutlich hast Du diesbezüglich Recht..
Denke jedoch darüber nach, auch einmal größere verschachtelte If-Funktionen wie Trailing oder Anti-Crossover(man frägt ab ob sich zwei MAs für x Bars nicht gekreuzt haben) auf die Art zu verändern. Mal sehen. Ist jetzt aber auch lehrreich für mich weil ich schon Codes gesehen habe, wo stand

bool var=(!Funtion1()>=Funktion2())

Das habe ich damals ganz am Anfang nicht verstanden.

Jedenfalls ob nun && oder * mag wohl in der boolschen Algebra keinen Unterschied machen. Doch MQL-intern werden die Zeichen offenbar nicht genau gleich behandelt. Was zu beweisen war.
 
Nein, nein.. * oder && macht einen Unterschied.

Beispiel.

Return(check_up() || check_down());

Wenn du willst, dass immer beide Funktionen aufgerufen werden, dann klappt das so nicht.

Du musst das || durch ein + tauschen.

Im obigen Beispiel, wenn die erste Funktion true ergibt, wird die zweite nicht mehr gerufen.

Warum? Weil eine Oder-Bedingungen nicht mehr alterniert werden kann, wenn bereits ein True vorliegt.

Bei einer && Verknüpfung ist es ganz genauso, nur verdreht. Ist die erste Bedingung False, gibt es keine Notwendigkeit die zweite zu prüfen.

Genau mit diesem Mechanismus kann der Programmablauf ohne conditional Breaks gesteuert werden.

---

Wobei diese Art der Steuerung nur von Vorteil ist, wenn es sich um das selektive Zuweisen von Werten handelt.

Bei einem Funktionsaufruf ist der Vorteil eher kleiner. Zwar spart man den selektierenden Break in der Schleife, doch der Aufruf selber ist aufwendig.

Es kommt auf die Schleife selbst an, aus der diese Bedingung zum Aufruf der Funktion führt.

Also zB eine Schleife, die alle 100 Interationen eine Funktion aufrufen soll und danach die nächsten 100 Werte weiterverarbeiten soll, wird davon profitieren.

Eine Schleife, die bei jedem Durchlauf diese Funktion aufruft, jedoch nicht.

Boolsche Arithmetik ist sehr stark Kontextabhängig.

 
Dominik Egert:


Wobei diese Art der Steuerung nur von Vorteil ist, wenn es sich um das selektive Zuweisen von Werten handelt.

Achso, da sind wir wieder beim Auslesen von Indikatorwerten aus dem früheren Thread, wo es um Werte aus einem ZigZag-Buffer ging.


Dominik Egert:


Also zB eine Schleife, die alle 100 Interationen eine Funktion aufrufen soll und danach die nächsten 100 Werte weiterverarbeiten soll, wird davon profitieren.

Okay, schätze mal weil der Cache auf die Verarbeitung der nächsten 100 Werte eingerichtet wurde. Und dann wieder neu aufgebaut werden muss. Das ist übrigens auch noch von dem im OP verlinkten Thread bei mir hängengeblieben.


Also ich sehe schon, das wird ein eigenes Seitenprojekt und ich müsste mir die Unterschiede doch mal hiermit:

const ulong perf_start = GetMicrosecondCount();


/* Der zu testende Code hier */


printf("Time used: %llu microseconds.", GetMicrosecondCount() - perf_start);

verdeutlichen.