Der Markt und die Physik seiner globalen Muster

17 Februar 2021, 08:47
Evgeniy Ilin
0
186

Einführung

In diesem Artikel werden wir versuchen zu verstehen, wie die Marktphysik für den automatisierten Handel genutzt werden kann. Die Sprache der Mathematik impliziert den Übergang von der Abstraktheit und Unsicherheit zur Vorhersage. Dies ermöglicht es, mit klaren Formeln oder Kriterien zu arbeiten, anstatt mit einigen ungefähren und vagen Werten, um die Qualität der erstellten Systeme zu verbessern. Ich werde keine Theorien oder Muster erfinden, sondern nur bekannte Fakten verwenden und diese Fakten schrittweise in die Sprache der mathematischen Analyse übersetzen. Die Marktphysik ist ohne Mathematik unmöglich, denn die Signale, die wir erzeugen, sind mathematische Substanz. Viele Menschen versuchen, verschiedene Theorien und Formeln zu erstellen, ohne jegliche statistische Analyse oder mit sehr begrenzten Statistiken, die oft nicht genug für solche kühnen Schlussfolgerungen sind. Die Praxis allein ist das Kriterium der Wahrheit. Zuerst werde ich versuchen, ein wenig zu reflektieren, und dann, basierend auf diesen Überlegungen, einen Expert Advisor (EA) erstellen. Im Anschluss daran wird der EA getestet.


Der Preis und was er bietet

Jeder Marktkontext impliziert die Existenz von verschiedenen Produkten. Auf dem Währungsmarkt ist das Produkt die Währung. Eine Währung bedeutet das Recht, ein bestimmtes Produkt oder eine bestimmte Information zu besitzen, die weltweit als Benchmark festgelegt wird. Betrachten Sie zum Beispiel das EURUSD-Paar und den aktuellen Wert seines Charts. Der aktuelle Chartwert bedeutet USD/EUR = CurrentPrice. Oder: USD = EUR*CurrentPrice, das bedeutet die Menge an Dollar, die in einem Euro enthalten ist. Mit anderen Worten, der Wert zeigt das Verhältnis des Gewichts der einzelnen Währungen an, wobei natürlich angenommen wird, dass es für jede Währung ein gemeinsames Äquivalent gibt, d.h. eine gemeinsame Ware oder etwas anderes. Der Preis wird im Orderbuch gebildet, und die Dynamik des Orderbuchs bestimmt die Preisbewegung. Es sollte immer bedacht werden, dass wir nie alle Faktoren der Preisbildung berücksichtigen können. Zum Beispiel, FORTS ist mit FOREX verbunden und beide Märkte beeinflussen sich gegenseitig. Ich bin kein Experte auf diesem Gebiet, aber ich bin in der Lage zu verstehen, dass alles miteinander verbunden ist und dass je mehr Datenkanäle, desto besser. Es scheint mir, dass es besser ist, nicht so tief ins Detail zu gehen, sondern sich auf einfache Dinge zu konzentrieren, die den Preis bewegen.

Jede Abhängigkeit kann als eine Funktion von vielen Variablen dargestellt werden, wie jeder Kurs. Anfänglich ist der Preis:

  • P=P(t)

Mit anderen Worten: Der Preis ist eine Funktion der Zeit. Die Form der Funktion kann nicht für jedes Währungspaar oder jedes andere Instrument zuverlässig ermittelt werden, da dies unendlich viel Zeit in Anspruch nehmen würde. Aber diese Darstellung gibt uns nichts. Der Preis ist jedoch von doppelter Natur, da er sowohl eine vorhersagbare als auch eine zufällige Komponente hat. Der vorhersagbare Teil ist nicht die Funktion selbst, sondern ihre erste Zeitableitung. Es macht keinen Sinn, diese Funktion als einige Terme darzustellen, da dies keinen Zweck für den Handel hat. Wenn wir aber ihre erste Ableitung betrachten, ergibt sich folgendes:

  • P'(t)=Pa'(t)+Pu'(t)

Hier spiegelt der erste Term den Teil wider, der auf irgendeine Weise mit Hilfe der mathematischen Analyse analysiert werden kann, der zweite ist ein unvorhersehbarer Teil. Basierend auf dieser Formel können wir sagen, dass es nicht möglich ist, die Größe und Richtung der Bewegung mit einer 100%igen Genauigkeit vorherzusagen. Es ist nicht nötig, darüber nachzudenken, was der letzte Term sein wird, da wir ihn nicht bestimmen können. Aber wir können den ersten Term bestimmen. Es ist anzunehmen, dass dieser Term in einer anderen Form dargestellt werden kann, wobei zu berücksichtigen ist, dass die Wertfunktion diskret ist und wir keine Differentialoperationen anwenden können. Aber stattdessen können wir die durchschnittliche Ableitung über die Zeit "st" nehmen. Auf den Preis angewandt, ist dies die Dauer eines Balkens; auf Ticks angewandt, ist es die minimale Zeit zwischen zwei Ticks.

  • PaM(t)=(Pa(t)-Pa(t-st))/st - die durchschnittliche Preisbewegung (Zeitableitung) über einen festen Zeitraum
  • Ma(t)=Ma(P(t),P(t-st) + ... + P(t-N*st), D(t),D(t-st) + ... + D(t-N*st),U[1](),U[2](t) + ... + U[N](t) )
  • P(t[i]) - alte Kurswerte (Balken- oder Tickdaten)
  • D(t[i]) - alte Kurswerte auf anderen Währungspaaren
  • U[i](t) - andere unbekannte oder bekannte Werte, die den Markt beeinflussen
  • Ma(t) - mathematische Erwartung des PaM(t) Wertes zu einem bestimmten Zeitpunkt 

Mit anderen Worten, wir gehen davon aus, dass der vorhersehbare Teil des Preises von vorherigen Balken oder Ticks sowie von den Preisdaten anderer Währungspaare, Daten von anderen Börsen und Weltereignissen abhängen kann. Es ist jedoch zu verstehen, dass auch dieser Teil des Preises nicht mit einer 100%igen Genauigkeit vorhergesagt werden kann, sondern wir können nur einige seiner Eigenschaften berechnen. Eine solche Charakteristik kann nur eine Wahrscheinlichkeit oder Parameter einer Zufallsvariablen sein, wie die mathematische Erwartung, Varianz, Standardabweichung und andere Größen der Wahrscheinlichkeitstheorie. Das Arbeiten mit mathematischen Erwartungen kann ausreichen, um den Handel profitabel zu machen. Nachdem wir uns Zeit genommen und sorgfältig nachgedacht haben, können wir zu dem Schluss kommen, dass der Markt nicht nur mit dieser Logik analysiert werden kann. Die Sache ist die, dass der vorhersehbare Teil des Preises sich auf der Grundlage der Aktivität der Marktteilnehmer entwickelt. Wir können verschiedene Marktparameter verwerfen, mit Ausnahme der Faktoren, die von den Marktteilnehmern selbst geschaffen werden. Das alles führt natürlich zu einer Verringerung der Zuverlässigkeit unserer Analysemethode, aber das Modell wird dadurch stark vereinfacht. Dabei gilt: Je kleiner der Wert "st" ist, desto genauer beschreiben unsere Formeln den Markt.

  • VMa(t)=VMa(P(t),P(t-st) + ... + P(t-N*st))
  • VMa(t)=VBuy(t)-VSell(t)
  • VMa(t) - Gesamtvolumen
  • VBuy(t) - Volumina der offenen Kaufaufträge
  • VSell(t) - Volumina der offenen Verkaufsaufträge

Die obige Funktion beschreibt die Differenz der Volumina aller aktuell offenen Kauf- und Verkaufspositionen. Ein Teil dieser Positionen kompensiert sich gegenseitig, während die restlichen Positionen unabhängig sind. Da die Positionen offen sind, symbolisieren sie ein Versprechen, sie nach einer gewissen Zeit zu schließen. Wir alle wissen, dass Kaufen den Preis nach oben und Verkaufen den Preis nach unten bewegt. Der einzige Weg, um zu wissen, wohin der Preis gehen wird, besteht darin, das Volumen der offenen Positionen zu messen und die Richtung dieser Positionen abzuschätzen, wobei nur offene Marktaufträge berücksichtigt werden.

Die Wellennatur des Marktes ist eigentlich mit dieser einfachen Tatsache verbunden. Dies ist nur ein spezieller Fall eines allgemeineren Prozesses von Schwankungen der Positionsvolumina oder der Aktionen von Bullen und Bären.

Wenn man mit Balken handelt, kann man auch die Tatsache berücksichtigen, dass es 4 Preise innerhalb eines Balkens gibt, was uns bessere Formeln geben kann. Mehr Daten bedeuten eine genauere Analyse, deshalb ist es wichtig, alle Preisdaten zu berücksichtigen. Ich mag es jedoch nicht, jeden Tick zu zählen, da dies die Algorithmen um das Zehnfache verlangsamen würde. Außerdem können die Tick-Daten bei verschiedenen Brokern unterschiedlich sein. Im Gegensatz dazu sind die Bar-Open- und Close-Preise bei den meisten Brokern fast identisch. Lassen Sie uns die Volumenfunktion so abändern, dass alle Preisdaten berücksichtigt werden:

  • VMa(t)=VMa(O(t),O(t-st) +...+ O(t-N*st) + C(t),C(t-st) + C(t-N*st),H(t),H(t-st)...H(t-N*st),L(t),L(t-st)...L(t-N*st))

Wir könnten dieser Funktion weitere Variablen hinzufügen, wie z. B. Uhrzeit, Wochentage, Monate und Wochen, aber das würde eine Menge Funktionen erzeugen, die an bestimmte Marktbereiche gebunden sind, während unser Zweck darin besteht, die allgemeine Marktphysik zu bestimmen. Wir werden wissen, dass sie nicht gebrochen werden kann und somit kann sie so lange verwendet werden, wie der Markt existiert. Ein weiterer Vorteil der Formel ist ihre Mehrwährungsfähigkeit.

In der Praxis macht die Verwendung dieser Darstellungsart keinen Sinn, da wir genau wissen müssen, wie und auf Basis welcher Daten wir diese Funktion aufbauen. Wir können nicht einfach die Form dieser Funktion schreiben und die Abhängigkeiten bestimmen. Aber diese Ausdrücke können uns helfen, ein erstes Verständnis dafür zu gewinnen, wie wir analysieren und zu den folgenden Annahmen übergehen können. Jeder Satz von logischen Bedingungen kann letztendlich als eine solche Funktion dargestellt werden. Umgekehrt kann die Funktion selbst in eine Menge von Bedingungen umgewandelt werden. Es spielt keine Rolle, welche Form wir verwenden. Es ist nur wichtig, sie zu verstehen. Jeder Algorithmus kann auf eine einzige Formel reduziert werden. Manchmal ist es einfacher, Signale als Bedingungen oder ein System von Bedingungen zu beschreiben, als eine superkomplexe Funktion zu bauen. Eine andere große Frage ist, wie man eine solche Funktion baut.

In einem realen Handelssystem können wir nicht die gesamte Historie auf einmal analysieren, sondern nur eine bestimmte Zeitspanne. Es gibt 4 mögliche Ansätze für eine solche Analyse. Ich werde Namen für sie erstellen und erklären:

  • Formeln (Indikatoren oder deren Funktionen)
  • Simulation
  • Allgemeine Mathematik
  • Arten des maschinellen Lernens

Die erste Option geht davon aus, dass wir einen bestimmten Wert oder einen Satz von Werten verwenden. Ein Beispiel ist ein Indikator oder unsere eigene Formel. Der Vorteil dieses Ansatzes ist die Verfügbarkeit eines großen Toolkits in den MetaTrader 4/5 Terminals. Darüber hinaus gibt es eine Menge von Indikatoren, die auf populären Markttheorien basieren und im Markt und im Internet verfügbar sind. Der Nachteil dieses Ansatzes ist, dass wir in den meisten Fällen nicht verstehen werden, worauf der Indikator basiert. Selbst wenn wir es verstehen, kann dieses Verständnis keinen Wert haben.

Bei der zweiten Option verwenden wir nicht die Daten, die wir nicht verstehen oder die keinen Nutzen haben können. Stattdessen können wir versuchen, die Aufträge auf dem Markt zu simulieren, und so werden wir wissen, dass unser System bis zu einem gewissen Grad in der Lage sein wird, zu beschreiben, wie viele Positionen in der einen Richtung offen sind und wie viele in der anderen. Diese Informationen können die notwendigen Prognosen erstellen, die eine feine Beschreibung des Marktes in einer globalen Perspektive ermöglichen. Dies ist die einzige Alternative zum maschinellen Lernen.

Mathematik bedeutet das Verständnis einiger grundlegender Gesetze oder die Kenntnis bestimmter mathematischer Prinzipien, die es erlauben, jeden Kurs zu nutzen, unabhängig von der aktuellen Marktsituation. Tatsache ist, dass jede Funktion, auch diskrete, bestimmte Eigenschaften hat, die ausgenutzt werden können. Natürlich gehen wir hier davon aus, dass die Abhängigkeit nicht chaotisch ist (in unserem Fall ist Forex nicht chaotisch, also kann jeder Kurs ausgenutzt werden). Im nächsten Artikel werden wir ein solches Prinzip analysieren, das fast jeder kennt. Aber wissen und anwenden können sind zwei verschiedene Dinge. Der Vorteil dieses Ansatzes ist, dass wir uns, wenn wir es schaffen, ein erfolgreiches System zu bauen, keine Gedanken darüber machen müssen, wie es sich in Zukunft verhalten wird. 

Der vierte Ansatz ist der fortschrittlichste, da maschinelles Lernen das Beste aus allen Daten machen kann. Je mehr Rechenleistung man hat, desto höher ist die Qualität der Analyse. Der Nachteil dieses Ansatzes ist, dass er nicht dabei hilft, die Marktphysik zu verstehen. Der Vorteil ist die Einfachheit des Ansatzes, die maximale Qualität der Ergebnisse mit einem Minimum an Zeitaufwand. Aber dieser Ansatz ist im Rahmen dieses Artikels nicht anwendbar.


Über Muster

Der Handelsalltag bringt eine Fülle von Begriffen mit sich, die jeweils eine unterschiedliche Bedeutung haben. Einige Begriffe werden sehr häufig verwendet, obwohl nicht alle Händler ihren eigentlichen Zweck verstehen. Einer dieser Begriffe ist das Muster. Ich werde versuchen, es in der Sprache der Mathematik zu erklären. Ein Muster ist immer an einen bestimmten Zeitraum und ein bestimmtes Währungspaar und eine bestimmte Chart-Periode gebunden. Einige Muster sind stark. Solche Muster können währungsübergreifender oder globaler Natur sein. Ein ideales Muster ist der Gral. Es gibt einige Aussagen, die auf jedes Muster angewendet werden können:

  • Die Existenz einer Formel oder eines Satzes von Bedingungen, die das Muster symbolisieren
  • Minimale Testwerte im Strategietester oder Performance-Werte auf einem Demo- oder Realkonto
  • Einstufung eines Musters in Bezug auf die Performance für alle Währungspaare und Chartperioden
  • Der Zeitraum der Historie, in dem das Muster gefunden wurde
  • Das Zeitintervall in der Zukunft, in dem das Muster funktionsfähig bleibt
  • Die zweite Zeitspanne in der Zukunft, die auf die erste folgt, während der das ursprüngliche Muster einige Parameter beibehält oder sie invertiert

Wenn Sie jede Eigenschaft sorgfältig lesen, können Sie verstehen, dass ein Muster eine Formel oder eine Reihe von Bedingungen ist, die die Preisbewegung in ausgewählten Intervallen am genauesten beschreiben. Ein Muster kann sich als zufällig herausstellen, besonders wenn es auf einer zu kleinen Periode gefunden wird oder wenn das entsprechende System überoptimistische Werte produziert. Es ist sehr wichtig zu verstehen, dass beim Testen eines Systems auf kurzen Perioden die Chancen, globale Muster zu finden, gegen Null tendieren. Dies hängt mit dem Stichprobenumfang zusammen. Je kleiner die Stichprobe ist, desto höher ist die Zufälligkeit der Ergebnisse. 

Wir haben bestimmt, was ein Muster ist. Aber wie kann man Muster effektiv nutzen? Das hängt davon ab, wie dieses Muster gefunden wurde und von seiner Qualität. Wenn wir die Analysemethoden, die die Rechenleistung nutzen, nicht berücksichtigen, dann kommen wir zur Analytik. Meiner Meinung nach kann die Analytik nicht mit jeder Art von maschineller Analyse konkurrieren - selbst ein gutes Team von Analysten kann nicht die Daten verarbeiten, die eine Maschine verarbeiten kann. Wie auch immer, der Prozess, globale Muster zu finden, erfordert Rechenleistung. Nun, abgesehen von dem Fall, dass man offensichtliche Dinge mit eigenen Augen gesehen und ihre Physik verstanden hat.


Den einfachsten Positionssimulator schreiben

Um zu versuchen, globale Muster zu finden, wäre es interessant, einen Expert Advisor zu entwickeln, der die Stimmung der Marktteilnehmer beschreiben kann. Dazu habe ich mich entschlossen, einen Simulator von Marktpositionen zu erstellen. Die Positionen werden innerhalb der Balken in der Nähe des Marktrandes simuliert. Es wird notwendig sein, davon auszugehen, dass die Marktteilnehmer unterschiedlich sind und auch die Gewichtung ihrer Orders unterschiedlich ist. Gleichzeitig soll dies in einer einfachen Form dargestellt werden. Wenn ein einfacher Prototyp Gewinn zeigt, dann können seine Prinzipien weiter verwendet werden.

Die Logik wird bedingt in 3 separate Simulationen und jede mögliche gemischte Kombination von ihnen aufgeteilt:

  • Simulation von Stop-Orders
  • Simulation von Limit-Orders
  • Simulation von Market-Orders
  • Beliebige mögliche Kombinationen

Die folgende Logik wird für die Platzierung von Orders verwendet:

Order-Grid-Logik

Diese Raster (grid) werden bei jedem neuen Balken platziert, um die Stimmung eines Teils der Marktteilnehmer zu simulieren. Der Status der alten Order Raster wird auf der Grundlage des neuen Balkens, der im Chart erscheint, aktualisiert. Dieser Ansatz ist nicht sehr genau, aber die Tick-by-Tick-Simulation würde zu endlosen Berechnungen führen. Außerdem traue ich den Ticks nicht ganz.

Es gibt zwei Arten der Verteilung der relativen Volumina, mit Abschwächung und gleichmäßig, aber nur für Stop- und Limit-Orders. Market-Orders haben eine gleichmäßige Verteilung. Es wird auch möglich sein, die Arten der Verteilung zu erweitern, wenn dies in der Perspektive betrachtet wird. Hier ist die Illustration:

Relative Volumina Füllungstypen

Hier ist die Länge der Linie, die die Order symbolisiert, proportional zum Volumen der gleichen Order. Ich denke, solche Darstellungen werden für jeden einfach und verständlich sein.

In diesem Fall kann alles mit Hilfe eines objektorientierten Ansatzes gemacht werden. Beginnen wir mit der Beschreibung von nummerierten Listen:

enum CLOSE_MODE// how to close orders
   {
   CLOSE_FAST,// fast
   CLOSE_QUALITY// wait for an opposite signal
   };

enum WORK_MODE// operation mode
   {
   MODE_SIMPLE,// slow mode
   MODE_FAST// fast mode
   };

enum ENUM_GRID_WEIGHT//weight fill type for limit and stop orders
   {
   WEIGHT_DECREASE,// with attenuation if moving from the price
   WEIGHT_SAME// the same for the entire grid
   };

enum ENUM_STATUS_ORDER// statuses of orders or positions
   {
   STATUS_VIRTUAL,// stop or limit order
   STATUS_MARKET,// market order
   STATUS_ABORTED// canceled stop or limit order
   };

Der Simulator arbeitet in zwei Modi, langsam und schnell. Der langsame Modus wird hauptsächlich für die Startanalyse benötigt. Bei der Startanalyse wird die Berechnung in den ersten "n" marktnahen Kerzen durchgeführt. Im schnellen Modus wird die Berechnung nur für die neu erschienene Kerze durchgeführt. Aber der einfache Ansatz erwies sich als nicht ausreichend. Es wurde eine zusätzliche Funktionalität benötigt, um die Geschwindigkeit des Algorithmus zu erhöhen. Eine ziemlich große Berechnung wird bei der Initialisierung des Expert Advisors durchgeführt. Aber dann brauchen wir nur die Simulation für eine neue Kerze bei jeder Kerze zu aktualisieren. Es wird zwei Arten der Volumenverteilung geben, für Limit- und Stop-Orders, abhängig von der Entfernung zum aktuellen Marktpreis, der Open[i] für jeden Bar ist. Das liegt daran, dass bei jedem Balken ein Raster von Stop- und Limit-Orders geöffnet wird, mit unterschiedlichen Verteilungen und Gewichtungen. Nach einiger Zeit werden die Stop- und Limit-Orders zu Markt-Orders. Wenn der Preis während der angegebenen Zeit nicht den gewünschten Preis erreicht, werden die Stop- und Limit-Orders storniert.

Beginnen wir damit, diese Simulation von einfach bis komplex aufzubauen, indem wir nach und nach alles zusammensetzen. Definieren wir zunächst, was eine Order ist:

struct Order// structure symbolizing a player's order
   {
   public:
   double WantedPrice;// desired open price
   int BarsExpirationOpen;// If the order remains for certain number of bars, the player can't wait any more and cancels the order
   int BarsExpirationClose;//If this is a market order and the player does not want to wait, he closes the position
   double UpPriceToClose;//The total upward price movement at which the player closes the order (points)
   double LowPriceToClose;//The total downward price movement at which the player closes the order 
   double VolumeAlpha;// current volume equivalent [0...1]
   double VolumeStart;// starting volume equivalent [0...1]
   int IndexMarket;// the index of the bar on which the virtual market turned into market
   ENUM_STATUS_ORDER Status;// order status
   Order(ENUM_STATUS_ORDER S)// constructor that creates a certain order
      {
      Status=S;
      }
   };

Es gibt nicht viele Parameter, und jeder Parameter ist für den allgemeinen Algorithmus wichtig. Viele der Felder sind auf jede Order anwendbar, während einige von ihnen nur für Limit- oder Stop-Orders gelten. Zum Beispiel ist der gewünschte Preis der Eröffnungskurs für eine Markt-Order, während er genau der gewünschte Preis für Limit- und Stop-Orders ist.

Der obere und der untere Schlusskurs dienen als Stop-Level. Gleichzeitig nehmen wir an, dass die Raster-Order nicht ganz eine Order ist und diese Order ein ganzes Bündel von Orders enthält, während sie nur zu einer Order verschmelzen, die mit einem bestimmten Volumen zu einem bestimmten Preis eröffnet wird. Die Variablen des Startvolumens und des aktuellen Volumens sagen uns, wie wichtig die Orders auf einem bestimmten Niveau eines bestimmten Balkens sind.

Das Startvolumen ist das Volumen zum Zeitpunkt der Ordereröffnung. Das aktuelle Volumen ist das Volumen bei der weiteren Entwicklung der Ereignisse. Wichtig ist das Verhältnis der Volumina der Kauf- und Verkaufsorders und nicht die Gewinne aus bestimmten Orders. Auf der Grundlage dieser Überlegungen werden Handelssignale generiert. Natürlich könnten wir auch andere Signale generieren, aber das erfordert andere Überlegungen. Beachten Sie auch, dass die Orders nicht geschlossen werden, wenn sie ein bestimmtes Niveau erreichen, sondern das Schließen wird schrittweise bei jedem Takt erfolgen, um die reale Entwicklung der Ereignisse so gut wie möglich zu simulieren.

Als Nächstes müssen wir die Speicherung für jeden Balken definieren. Der Balken wird die Orders speichern, die an diesem Balken geöffnet werden:

class OrderBox// Order box of a specific bar
   {
   public:
   Order BuyStopOrders[];
   Order BuyLimitOrders[];
   Order BuyMarketOrders[];
   Order SellStopOrders[];
   Order SellLimitOrders[];
   Order SellMarketOrders[];
   
   OrderBox(int OrdersToOneBar)
      {
      ArrayResize(BuyStopOrders,OrdersToOneBar);
      ArrayResize(BuyLimitOrders,OrdersToOneBar);
      ArrayResize(BuyMarketOrders,OrdersToOneBar);
      ArrayResize(SellStopOrders,OrdersToOneBar);
      ArrayResize(SellLimitOrders,OrdersToOneBar);
      ArrayResize(SellMarketOrders,OrdersToOneBar);      
      for ( int i=0; i<ArraySize(BuyStopOrders); i++ )// Set types for all orders
         {
         BuyStopOrders[i]=Order(STATUS_VIRTUAL);
         BuyLimitOrders[i]=Order(STATUS_VIRTUAL);
         BuyMarketOrders[i]=Order(STATUS_MARKET);
         SellStopOrders[i]=Order(STATUS_VIRTUAL);
         SellLimitOrders[i]=Order(STATUS_VIRTUAL);
         SellMarketOrders[i]=Order(STATUS_MARKET);         
         }
      }
   };

Hier ist alles ganz einfach. Sechs Arten von Orders, die durch Arrays beschrieben werden. Dies geschieht, um Verwechslungen zu vermeiden. Die Klasse wird in ihrer reinen Form nicht verwendet werden, sie ist nur ein Baustein in der Konstruktion.

Danach deklarieren wir den gemeinsamen Speicher aller Balken als ein Objekt, von dem später vererbt werden soll. Die Technik ist hier recht einfach.

class BarBox// Storage for all orders
   {
   protected:
   OrderBox BarOrders[];
   
   BarBox(int OrdersToOneBar,int BarsTotal)
      {
      ArrayResize(BarOrders,BarsTotal);
      for ( int i=0; i<ArraySize(BarOrders); i++ )// Set types for all orders
         {
         BarOrders[i]=OrderBox(OrdersToOneBar);
         }
      }   
   };

Dies ist nur ein Speicher mit den Daten der Balken (Orders) und nichts weiter. Bis hierhin ist alles ganz einfach. Im weiteren Verlauf werden die Dinge etwas komplizierter.

Nachdem wir einen geeigneten Datenspeicher für Orders festgelegt haben, müssen wir bestimmen, wie und nach welchen Regeln Orders erstellt werden, welche Bedeutung bestimmte Order-Typen haben usw. Zu diesem Zweck habe ich die folgende Klasse erstellt:

class PositionGenerator:public BarBox// Inherit class from the box to avoid the need to include it as an internal member and to avoid multiple references
   {
   protected:
   double VolumeAlphaStop;// importance of volumes of STOP orders
   double VolumeAlphaLimit;// importance of volumes of LIMIT orders
   double VolumeAlphaMarket;// importance of volumes of MARKET orders
   double HalfCorridorLimitStop;// step of the corridor of Limit and Stop orders in points
   int ExpirationOpenLimit;// after how many bars the volumes of the grid of limit orders for opening will completely attenuate
   int ExpirationOpenStop;// after how many bars the volumes of the grid of stop orders for opening will completely attenuate
   int ExpirationClose;// after how many bars the volumes of orders for closing will completely attenuate
   int ProfitPointsCorridorPart;// half corridor size for the profit of all orders
   int LossPointsCorridorPart;// half corridor size for the loss of all orders
   int OrdersToOneBar;// orders of one type per 1 bar
   ENUM_GRID_WEIGHT WeightStopLimitFillingType;
   
   PositionGenerator( ENUM_GRID_WEIGHT WeightStopLimitFillingType0
                     ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0
                     ,int ExpirationOpenLimit0,int ExpirationOpenStop0
                     ,int ExpirationClose0
                     ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0
                     ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) 
                     : BarBox(OrdersToOneBar0,BarsTotal0)
      {
      VolumeAlphaStop=VolumeAlphaStop0;
      VolumeAlphaLimit=VolumeAlphaLimit0;
      VolumeAlphaMarket=VolumeAlphaMarket0;
      OrdersToOneBar=OrdersToOneBar0;
      HalfCorridorLimitStop=double(HalfCorridorLimitStop0)/double(OrdersToOneBar);
      ExpirationOpenLimit=ExpirationOpenLimit0;
      ExpirationOpenStop=ExpirationOpenStop0;
      ExpirationClose=ExpirationClose0;
      ProfitPointsCorridorPart=ProfitPointsCorridorPart0;
      LossPointsCorridorPart=LossPointsCorridorPart0;
      OrdersToOneBar=OrdersToOneBar0;
      WeightStopLimitFillingType=WeightStopLimitFillingType0;
      }
   private:
   
   double CalcVolumeDecrease(double TypeWeight,int i,int size)// attenuation volume
      {
      if ( size > 1 )
         {
         double K=1.0/(1.0-size);
         double C=1.0;
         return TypeWeight*K*i+C;
         }
      else return 0.0;
      }
      
   double CalcVolumeSimple(double TypeWeight)// equal volume
      {
      return TypeWeight;
      }
   
   void RebuildStops()// rebuild stop orders
      {
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
         {
         for ( int i=0; i<size; i++ )// reset all
            {
            BarOrders[j].BuyStopOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
            BarOrders[j].BuyStopOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
            BarOrders[j].BuyStopOrders[i].VolumeStart=BarOrders[j].BuyStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].BuyStopOrders[i].UpPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
            BarOrders[j].BuyStopOrders[i].LowPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
            BarOrders[j].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
            BarOrders[j].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;
       
            BarOrders[j].SellStopOrders[i].Status=STATUS_VIRTUAL;
            BarOrders[j].SellStopOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
            BarOrders[j].SellStopOrders[i].VolumeStart=BarOrders[j].SellStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].SellStopOrders[i].UpPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
            BarOrders[j].SellStopOrders[i].LowPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
            BarOrders[j].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
            BarOrders[j].SellStopOrders[i].BarsExpirationClose=ExpirationClose;                    
            }         
         }      
      }
      
   void RebuildLimits()// rebuild limit orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
         {
         for ( int i=0; i<size; i++ )// reset all
            {
            BarOrders[j].BuyLimitOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
            BarOrders[j].BuyLimitOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
            BarOrders[j].BuyLimitOrders[i].VolumeStart=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].BuyLimitOrders[i].UpPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
            BarOrders[j].BuyLimitOrders[i].LowPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
            BarOrders[j].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
            BarOrders[j].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;            
       
            BarOrders[j].SellLimitOrders[i].Status=STATUS_VIRTUAL;
            BarOrders[j].SellLimitOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
            BarOrders[j].SellLimitOrders[i].VolumeStart=BarOrders[j].SellLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].SellLimitOrders[i].UpPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
            BarOrders[j].SellLimitOrders[i].LowPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
            BarOrders[j].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
            BarOrders[j].SellLimitOrders[i].BarsExpirationClose=ExpirationClose;
            }         
         }      
      }
      
   void RebuildMarkets()// rebuild market orders
      {
      int size=ArraySize(BarOrders[0].BuyMarketOrders);
      double MarketStep;
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         MarketStep=(High[j+1]-Low[j+1])/double(OrdersToOneBar);
            
         for ( int i=0; i<size; i++ )// reset all
            {
            BarOrders[j].BuyMarketOrders[i].Status=STATUS_MARKET;// reset status to initial
            BarOrders[j].BuyMarketOrders[i].WantedPrice=Low[j+1]+MarketStep*i;// prices of the order grid
            BarOrders[j].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
            BarOrders[j].BuyMarketOrders[i].VolumeStart=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].BuyMarketOrders[i].UpPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
            BarOrders[j].BuyMarketOrders[i].LowPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
            BarOrders[j].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;
               
            BarOrders[j].SellMarketOrders[i].Status=STATUS_MARKET;
            BarOrders[j].SellMarketOrders[i].WantedPrice=High[j+1]-MarketStep*i;// prices of the order grid
            BarOrders[j].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
            BarOrders[j].SellMarketOrders[i].VolumeStart=BarOrders[j].SellMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].SellMarketOrders[i].UpPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
            BarOrders[j].SellMarketOrders[i].LowPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
            BarOrders[j].SellMarketOrders[i].BarsExpirationClose=ExpirationClose;
            } 
         }      
      }

   ///// Fast methods
   void RebuildStopsFast()// rebuild stop orders
      {
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )// shift orders
            {
            BarOrders[j].BuyStopOrders[i]=BarOrders[j-1].BuyStopOrders[i];
            BarOrders[j].SellStopOrders[i]=BarOrders[j-1].SellStopOrders[i];
            BarOrders[j].SellStopOrders[i].IndexMarket++;
            }
         }
         
      for ( int i=0; i<size; i++ )// create a new grid at a new bar
         {
         BarOrders[0].BuyStopOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
         BarOrders[0].BuyStopOrders[i].WantedPrice=Close[1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
         BarOrders[0].BuyStopOrders[i].VolumeStart=BarOrders[0].BuyStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].BuyStopOrders[i].UpPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//upper border to close
         BarOrders[0].BuyStopOrders[i].LowPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
         BarOrders[0].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
         BarOrders[0].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;
       
         BarOrders[0].SellStopOrders[i].Status=STATUS_VIRTUAL;
         BarOrders[0].SellStopOrders[i].WantedPrice=Close[1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
         BarOrders[0].SellStopOrders[i].VolumeStart=BarOrders[0].SellStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].SellStopOrders[i].UpPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
         BarOrders[0].SellStopOrders[i].LowPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
         BarOrders[0].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
         BarOrders[0].SellStopOrders[i].BarsExpirationClose=ExpirationClose;                    
         }               
      }
      
   void RebuildLimitsFast()// rebuild limit orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )// shift orders
            {
            BarOrders[j].BuyLimitOrders[i]=BarOrders[j-1].BuyLimitOrders[i];
            BarOrders[j].SellLimitOrders[i]=BarOrders[j-1].SellLimitOrders[i];
            BarOrders[j].SellLimitOrders[i].IndexMarket++;
            }         
         }
      
      for ( int i=0; i<size; i++ )// create a new grid at a new bar
         {
         BarOrders[0].BuyLimitOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
         BarOrders[0].BuyLimitOrders[i].WantedPrice=Open[1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
         BarOrders[0].BuyLimitOrders[i].VolumeStart=BarOrders[0].BuyLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].BuyLimitOrders[i].UpPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
         BarOrders[0].BuyLimitOrders[i].LowPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
         BarOrders[0].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
         BarOrders[0].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;            
       
         BarOrders[0].SellLimitOrders[i].Status=STATUS_VIRTUAL;
         BarOrders[0].SellLimitOrders[i].WantedPrice=Open[1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
         BarOrders[0].SellLimitOrders[i].VolumeStart=BarOrders[0].SellLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].SellLimitOrders[i].UpPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
         BarOrders[0].SellLimitOrders[i].LowPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower order to close
         BarOrders[0].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
         BarOrders[0].SellLimitOrders[i].BarsExpirationClose=ExpirationClose;
         }        
      }
      
   void RebuildMarketsFast()// rebuild market orders
      {
      int size=ArraySize(BarOrders[0].BuyMarketOrders);
      double MarketStep;
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )// shift orders
            {
            BarOrders[j].BuyMarketOrders[i]=BarOrders[j-1].BuyMarketOrders[i];
            BarOrders[j].SellMarketOrders[i]=BarOrders[j-1].SellMarketOrders[i];
            }         
         }
      MarketStep=(High[1]-Low[1])/double(OrdersToOneBar);
      for ( int i=0; i<size; i++ )// create a new grid at a new bar
         {
         BarOrders[0].BuyMarketOrders[i].Status=STATUS_MARKET;// reset status to initial
         BarOrders[0].BuyMarketOrders[i].WantedPrice=Low[1]+MarketStep*i;// prices of the order grid
         BarOrders[0].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
         BarOrders[0].BuyMarketOrders[i].VolumeStart=BarOrders[0].BuyMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].BuyMarketOrders[i].UpPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
         BarOrders[0].BuyMarketOrders[i].LowPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
         BarOrders[0].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;
               
         BarOrders[0].SellMarketOrders[i].Status=STATUS_MARKET;
         BarOrders[0].SellMarketOrders[i].WantedPrice=High[1]-MarketStep*i;// prices of the order grid
         BarOrders[0].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
         BarOrders[0].SellMarketOrders[i].VolumeStart=BarOrders[0].SellMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].SellMarketOrders[i].UpPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
         BarOrders[0].SellMarketOrders[i].LowPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
         BarOrders[0].SellMarketOrders[i].BarsExpirationClose=ExpirationClose;
         }         
      }   
   
   protected:
   void CreateNewOrders()// create new orders at each candlestick
      {
      if ( VolumeAlphaStop != 0.0 ) RebuildStops();
      if ( VolumeAlphaLimit != 0.0 ) RebuildLimits();
      if ( VolumeAlphaMarket != 0.0 ) RebuildMarkets();
      }
   
   void CreateNewOrdersFast()//
      {
      if ( VolumeAlphaStop != 0.0 ) RebuildStopsFast();
      if ( VolumeAlphaLimit != 0.0 ) RebuildLimitsFast();
      if ( VolumeAlphaMarket != 0.0 ) RebuildMarketsFast();      
      }
   
   public:   
   virtual void Update()// state updating function (will be expanded in child classes)
      {
      CreateNewOrders();
      }
      
   virtual void UpdateFast()// fast state update
      {
      CreateNewOrdersFast();
      }      
   };

Tatsächlich erzeugt diese Klasse nur eine Implementierung der Methoden Update() und UpdateFast(), die ähnlich sind, mit dem einzigen Unterschied, dass letztere viel schneller ist. Diese Methoden legen bei jedem Takt neue Orders an und löschen alte, wodurch die Daten für die nächste Klasse vorbereitet werden, die den Lebenszyklus der Order simulieren wird. Alle erforderlichen Parameter der Order werden in dieser Klasse zugewiesen, einschließlich Typ, offene Preise, Volumen und andere wichtige Parameter, die für den weiteren Betrieb benötigt werden.

Die nächste Klasse implementiert den Prozess der Order-Simulation sowie die Berechnungen der für den Handel notwendigen Parameter, auf deren Grundlage Signale generiert werden:

class Simulation:public PositionGenerator // then assemble a simulator of positions (inherited from the position generator)
   {// market parameter calculations will also performed in this class
   protected:
   double BuyPercent;// percent of open Buy positions
   double SellPercent;// percent of open Sell positions
   double StartVolume;// starting total volume of open Buy positions (the same for Buys and Sells)
   double RelativeVolume;// relative volume
   double SummVolumeBuy;// total volume for Buys
   double SummVolumeSell;// total volume for Sells
   
   public:   
   Simulation( ENUM_GRID_WEIGHT WeightStopLimitFillingType0
                     ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0
                     ,int ExpirationOpenLimit0,int ExpirationOpenStop0
                     ,int ExpirationClose0
                     ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0
                     ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) 
   :PositionGenerator(WeightStopLimitFillingType0
                     ,HalfCorridorLimitStop0,OrdersToOneBar0,BarsTotal0
                     ,ExpirationOpenLimit0,ExpirationOpenStop0
                     ,ExpirationClose0
                     ,ProfitPointsCorridorPart0,LossPointsCorridorPart0
                     ,VolumeAlphaStop0,VolumeAlphaLimit0,VolumeAlphaMarket0) 
      {
      CreateNewOrders();
      CalculateStartVolume();// calculate starting volumes
      UpdateVirtual();// first update virtual orders to process part of them as market orders in the next function
      UpdateMarket();// now update the state of all market orders
      CalculateCurrentVolume();// calculate current volumes of all open orders
      CalculatePercent();// calculate the percentage of positions
      CalculateRelativeVolume();// calculate relative volume
      }

   double GetBuyPercent()// get the percentage of open Buy deals
      {
      return BuyPercent;
      }
      
   double GetSellPercent()// get the percentage of open Sell deals
      {
      return SellPercent;
      }
      
   double GetRelativeVolume()// get relative volume
      {
      return RelativeVolume;
      }

   virtual void Update() override
      {
      PositionGenerator::Update();// call everything that was before
      UpdateVirtual();// first update virtual orders to process part of them as market orders in the next function
      UpdateMarket();// now update the state of all market orders
      CalculateCurrentVolume();// calculate current volumes of all open orders
      CalculatePercent();// calculate the percentage of positions
      CalculateRelativeVolume();// calculate relative volume
      }
      
   virtual void UpdateFast() override
      {
      PositionGenerator::UpdateFast();// call everything that was before
      UpdateVirtualFast();// first update virtual orders to process part of them as market orders in the next function
      UpdateMarketFast();// now update the state of all market orders
      CalculateCurrentVolume();// calculate current volumes of all open orders
      CalculatePercent();// calculate the percentage of positions
      CalculateRelativeVolume();// calculate relative volume
      }   
      
   private:
   
   void UpdateVirtual()// update the status of virtual orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int i=SizeBarOrders; i>0; i-- )// update the state of limit orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[i] )// if the order is virtual and is inside a candlestick, then it turns into a market one
                     {
                     BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].BuyLimitOrders[k].IndexMarket = i;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[i] )// the same
                     {
                     BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].SellLimitOrders[k].IndexMarket = i;
                     } 
                 
                  /////// Check for interest expiration of limit players
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
                     BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                        }
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
                     BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;                  
                        }
                     } 
                  }
               }         
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int i=SizeBarOrders; i>0; i-- )// update the state of limit orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].SellStopOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[i] )// the same
                     {
                     BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].SellStopOrders[k].IndexMarket = i;
                     }
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[i] )// the same
                     {
                     BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].BuyStopOrders[k].IndexMarket = i;
                     }
                  
                  /////// Check for interest expiration of stop and limit players
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
                     BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;                  
                        } 
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen  )
                     BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;                     
                        }
                     }                                                                       
                  }
               }         
            }
         }               
      }
      
   void UpdateMarket()// update the status of market orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  // Block for closing when prices change
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);// with profit
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);// with loss
                        }
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);//with profit
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);//with loss
                        }
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                     } 
                  // End of lock for closing when prices change
               
                  // Block for closing when time changes******************************************************
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                     } 
                  }
               }         
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  // Block for closing when prices change
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);// with profit
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);// with loss
                        }
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);//with profit
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);//with loss
                        }
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                     }
               
                  // End of lock for closing when prices change
               
                  // Block for closing when time changes******************************************************
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                     }
                  }
               }         
            }
         }
         
      if ( VolumeAlphaMarket != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  // Block for closing when prices change
                  /// For obviously market positions
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);// with profit
                     BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);// with loss
                     }
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                              
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);// with profit
                     BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);// with loss
                     }
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;                  
                  // End of lock for closing when prices change
               
                  // Block for closing when time changes******************************************************
              
                  /// For obviously market positions
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;               
                  //
                  }
               }         
            }
         }
      }
      
   /// fast methods****   
   void UpdateVirtualFast()// update the status of virtual orders
      {
      int SizeBarOrders=ArraySize(BarOrders);
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[1] )// if the order is virtual and is inside a candlestick, then it turns into a market one
                  {
                  BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].BuyLimitOrders[k].IndexMarket = 1;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[1] )// the same
                  {
                  BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].SellLimitOrders[k].IndexMarket = 1;
                  } 
                  
               /////// Check for interest expiration of limit players
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
                  BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
                  BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;                  
                     }
                  } 
               }
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {       
         for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].SellStopOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[1] )// the same
                  {
                  BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].SellStopOrders[k].IndexMarket = 1;
                  }
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[1] )// the same
                  {
                  BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].BuyStopOrders[k].IndexMarket = 1;
                  }
                  
               /////// Check for interest expiration of stop players
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
                  BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;                  
                     } 
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen  )
                  BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;                     
                     }
                  }                                                                       
               }
            }
         }         
      }
      
   void UpdateMarketFast()// update the status of market orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               // Block for closing when prices change
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);// with profit
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);// with loss
                     }
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);// with profit
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);// with loss
                     }
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                  } 
               // End of lock for closing when prices change
               
               // Block for closing when time changes******************************************************
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                  } 
               //
               }
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               // Block for closing when prices change
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);// with profit
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);// with loss
                     }
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);// with profit
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);// with loss
                     }
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                  }
               
               // End of lock for closing when prices change
               
               // Block for closing when time changes******************************************************
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                  }
               //
               }
            }
         }
         
       if ( VolumeAlphaMarket != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )// update the state of all candlesticks preceding this one
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               /// For obviously market positions
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                  {
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);// with profit
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);// with loss
                  }
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                              
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                  {
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);// with profit
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);// with loss
                  }
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;                  
               // End of lock for closing when prices change
               
               // Block for closing when time changes******************************************************
             
               /// For obviously market positions
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
               BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
               BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;               
               //
               }
            }
         }                          
      }      
   ///******
      
   void CalculateStartVolume()// calculate the starting total volume of all positions (relative to it we will estimate market fullness)
      {
      StartVolume=0;
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyStopOrders[i].VolumeStart;
               }
            }        
         }
         
      if ( VolumeAlphaLimit != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyLimitOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyLimitOrders[i].VolumeStart;
               }         
            }
         }
         
      if ( VolumeAlphaMarket != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyMarketOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyMarketOrders[i].VolumeStart;
               }         
            }
         }         
      }
      
   void CalculateCurrentVolume()// calculate the current total volume of all positions
      {
      SummVolumeBuy=0;
      SummVolumeSell=0;
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               if ( BarOrders[j].BuyStopOrders[i].Status == STATUS_MARKET )
               SummVolumeBuy+=BarOrders[j].BuyStopOrders[i].VolumeAlpha;
               if ( BarOrders[j].SellStopOrders[i].Status == STATUS_MARKET )
               SummVolumeSell+=BarOrders[j].SellStopOrders[i].VolumeAlpha;            
               }         
            }
         }
      
      if ( VolumeAlphaLimit != 0.0 )
         {   
         size=ArraySize(BarOrders[0].BuyLimitOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               if ( BarOrders[j].BuyLimitOrders[i].Status == STATUS_MARKET )
               SummVolumeBuy+=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;
               if ( BarOrders[j].SellLimitOrders[i].Status == STATUS_MARKET )
               SummVolumeSell+=BarOrders[j].SellLimitOrders[i].VolumeAlpha;            
               }         
            }
         }
      
      if ( VolumeAlphaMarket != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyMarketOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               SummVolumeBuy+=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;
               SummVolumeSell+=BarOrders[j].SellMarketOrders[i].VolumeAlpha;
               }         
            }
         }         
      }
      
   void CalculatePercent()// calculate the percentage of Buys and Sells relative to all positions
      {
      if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) BuyPercent=100.0*SummVolumeBuy/(SummVolumeBuy+SummVolumeSell);
      else BuyPercent=50;
      if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) SellPercent=100.0*SummVolumeSell/(SummVolumeBuy+SummVolumeSell);
      else SellPercent=50;
      }      
      
   void CalculateRelativeVolume()// calculate relative volumes of Buys and Sells (calculate only uncovered part of positions)
      {
      if ( SummVolumeBuy >= SummVolumeSell ) RelativeVolume=(SummVolumeBuy-SummVolumeSell)/StartVolume;
      else RelativeVolume=(SummVolumeSell-SummVolumeBuy)/StartVolume;
      }                   
   
   };

Der gesamte Code ist sowohl für MetaTrader 4 als auch für MetaTrader 5 gültig. Diese Klassen können in beiden Plattformen kompiliert werden. Natürlich sollten Sie in MetaTrader 5 vordefinierte Arrays im Voraus implementieren, genau wie in MQL4. Ich werde diesen Code hier nicht besprechen. Sie können ihn im angehängten Quellcode überprüfen. Mein Code ist nicht sehr originell. Alles, was wir jetzt noch tun müssen, ist die Implementierung der Variablen, die für den Handel verantwortlich sind, und der entsprechenden Funktionalität. Expert Advisors für beide Terminals sind unten angehängt.

Der Code ist sehr ressourcenintensiv, deshalb habe ich eine langsame Logikimplementierung für den Start der Analyse und eine schnelle Implementierung für die Arbeit auf Balken organisiert. Alle meine Expert Advisors arbeiten auf Balken, um die Abhängigkeit von der künstlichen Tick-Generierung und andere unerwünschte Folgen des Every-Tick-Tests zu vermeiden. Natürlich könnte man auf langsame Funktionen komplett verzichten. Aber in diesem Fall hätte es keine Startanalyse gegeben. Außerdem mag ich es nicht, Funktionalität außerhalb des Klassenkörpers zu implementieren, da dies meiner Meinung nach die Integrität des Bildes ruiniert.

Der letzte Klassenkonstruktor nimmt die folgenden Parameter entgegen, die Eingaben sein werden. Wenn Sie möchten, können Sie viele solcher Simulationen durchführen, da es sich nur um eine Klasseninstanz handelt:

input bool bPrintE=false;// print market parameters
input CLOSE_MODE CloseModeE=CLOSE_FAST;// order closing mode
input WORK_MODE ModeE=MODE_SIMPLE;// simulation mode
input ENUM_GRID_WEIGHT WeightFillingE=WEIGHT_SAME;// weight distribution type
input double LimitVolumeE=0.5;// significance of limit orders
input double StopVolumeE=0.5;// significance of stop orders
input double MarketVolume=0.5;// significance of market orders
input int ExpirationBars=100;// bars for full expiration of open orders
input int ExpirationOpenStopBars=1000;// patience of a stop player in bars, after which the order is canceled
input int ExpirationOpenLimitBars=1000;// patience of a limit player in bars, after which the order is canceled
input int ProfitPointsCloseE=200;// points to close with profit
input int LossPointsCloseE=400;// points to close with loss
input int HalfCorridorE=500;// half-corridor for limit and stop orders
input int OrdersToOneBarE=50;// orders for half-grid per 1 bar
input int BarsE=250;// bars for analysis
input double MinPercentE=60;// minimum superiority of one trading side in percentage 
input double MaxPercentE=80;// maximum percentage
input double MinRelativeVolumeE=0.0001;// minimum market filling [0...1]
input double MaxRelativeVolumeE=1.00;// maximum market filling [0...1]

Ich werde hier keine Variablen angeben, die sich auf den Handel beziehen, da alles einfach und klar ist. 

Hier ist meine Funktion, in der der Handel implementiert ist:

void Trade()
   {
   if ( Area0 == NULL )
      {
      CalcAllMQL5Values();
      Area0 = new Simulation(WeightFillingE,HalfCorridorE,OrdersToOneBarE,BarsE
       ,ExpirationOpenLimitBars,ExpirationOpenStopBars,ExpirationBars,ProfitPointsCloseE,LossPointsCloseE
       ,StopVolumeE,LimitVolumeE,MarketVolume);      
      }
   
   switch(ModeE)
      {
      case MODE_SIMPLE:
         Area0.Update();// update simulation
      case MODE_FAST:
         Area0.UpdateFast();// fast update simulation
      }
   
   if (bPrintE)
      {
      Print("BuyPercent= ",Area0.GetBuyPercent());
      Print("SellPercent= ",Area0.GetSellPercent());
      Print("RelativeVolume= ",Area0.GetRelativeVolume());
      }
      
   if ( CloseModeE == CLOSE_FAST && Area0.GetBuyPercent() > 50.0 )
      {
      if ( !bInvert ) CloseBuyF();
      else CloseSellF();
      }
      
   if ( CloseModeE == CLOSE_FAST && Area0.GetSellPercent() > 50.0 )
      {
      if ( !bInvert ) CloseSellF();
      else CloseBuyF();
      }      
      
   if ( Area0.GetBuyPercent() > MinPercentE && Area0.GetBuyPercent() < MaxPercentE 
   && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
      {
      if ( !bInvert )
         {
         CloseBuyF();
         SellF();
         }
      else
         {
         CloseSellF();
         BuyF();
         }   
      }
      
   if ( Area0.GetSellPercent() > MinPercentE && Area0.GetSellPercent() < MaxPercentE 
   && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
      {
      if ( !bInvert )    
         {
         CloseSellF();
         BuyF();
         }  
      else
         {
         CloseBuyF();
         SellF();
         }
      }
   }

Wenn gewünscht, ist es möglich, bessere Handelsbedingungen und eine kompliziertere Funktion zu machen, aber bis jetzt sehe ich keinen Sinn darin. Ich versuche immer, den Handel vom logischen Teil zu trennen. Die Logik ist im Objekt implementiert. Das Simulationsobjekt wird dynamisch in der Handelsfunktion beim ersten Trigger erzeugt; es wird bei der Deinitialisierung gelöscht. Das liegt daran, dass es in MQL5 keine vordefinierten Arrays gibt und diese künstlich erzeugt werden müssen, um den Klassenbetrieb ähnlich wie in MQL4 zu gewährleisten.


Wie kann man funktionierende Einstellungen finden?

Aus meiner Erfahrung heraus kann ich sagen, dass es besser ist, die Einstellungen manuell zu wählen. Darüber hinaus schlage ich vor, den Spread in der ersten Phase für Expert Advisors zu ignorieren, die auf niedrigen Zeitrahmen laufen und eine kleine mathematische Erwartung haben - das wird Ihnen helfen, die Performance-Anzeichen nicht gleich am Anfang zu verpassen. MetaTrader 4 ist für diesen Zweck sehr gut geeignet. Auf eine erfolgreiche Suche folgen immer Überarbeitungen, Bearbeitungen usw., um die endgültige mathematische Erwartung und den Gewinnfaktor (Signalstärke) zu erhöhen. Auch die Struktur der Eingabedaten sollte im Idealfall unabhängige Betriebsarten ermöglichen. Mit anderen Worten, eine Einstellung sollte den unabhängigsten Effekt auf die Systemleistung haben, unabhängig vom Wert der anderen Einstellungen. Dieser Ansatz kann das Gesamtsignal durch ausgewogene Einstellungen verbessern, die alle gefundenen Signale miteinander kombinieren. Eine solche Implementierung ist nicht immer möglich, aber in unserem Fall ist sie anwendbar, da wir jeden Order-Typ separat analysieren.


Testen des Expert Advisors

Dieser Expert Advisor wurde ohne jegliche Vorbereitung, von Grund auf, geschrieben. Dieser EA-Code ist also neu für mich. Ich hatte einen ähnlichen EA, aber der war viel einfacher und hatte eine ganz andere Logik. Ich möchte dies betonen, denn der Zweck des Artikels war zu zeigen, dass jede Idee, die einige korrekte Grundlagen hat, auch wenn sie die Marktphysik nicht vollständig beschreibt, eine sehr hohe Chance hat, eine gewisse Performance zu zeigen. Wenn das System grundlegende Performance-Fähigkeiten zeigt, können wir es testen, herausfinden, was in diesem System funktioniert und auf die nächste Stufe des Marktverständnisses gehen. Die nächste Stufe setzt qualitativ bessere Expert Advisors voraus, die Sie selbst generieren werden.

Beim Testen des Expert Advisors musste ich mehrere Tage lang geduldig Handelsparameter auswählen. Alles war langweilig und langwierig, aber ich habe es geschafft, funktionierende Einstellungen zu finden. Natürlich sind diese Einstellungen sehr schwach und die Simulation wird mit nur 1 Order-Typ durchgeführt, aber das wurde absichtlich gemacht. Denn es ist besser, separat zu analysieren, wie ein bestimmter Order-Typ ein Signal beeinflusst, und dann zu versuchen, andere Order-Typen zu simulieren. Hier ist mein Ergebnis in MetaTrader 4:

EURUSD 2010.01.01 -2020.11.01

Ich habe zunächst eine Version für MetaTrader 4 erstellt und mit dem kleinstmöglichen Spread getestet. Tatsache ist, dass wir jeden Tick sehen müssen, um nach Mustern zu suchen, vor allem auf niedrigeren Zeitrahmen. Wenn wir die Version für MetaTrader 5 testen, können wir das wegen der Spreads, die MetaTester 5 nach eigenem Ermessen einstellen kann, nicht sehen.

Dies ist ein perfektes Tool für Stresstests und zur Bewertung der realen Systemleistung. Es sollte in der letzten Phase verwendet werden, um das System auf realen Ticks zu testen. In unserem Fall ist es besser, mit dem Testen des Systems im MetaTrader 4 zu beginnen. Ich empfehle jedem, diesen Ansatz zu verwenden, denn wenn Sie sofort mit dem Testen in der fünften Version beginnen, können Sie eine Menge hervorragender Einstellungsmöglichkeiten verlieren, die als Grundlage für qualitativ bessere Einstellungen dienen können.

Ich empfehle die Optimierung aus vielen Gründen nicht, aber der Hauptgrund ist, dass es sich um eine einfache Iteration von Parametern handelt. Sie werden nicht verstehen können, wie es funktioniert, wenn Sie die Parameter nicht manuell ausprobieren. Wenn Sie ein altes Radio nehmen, Elektromotoren an seine Griffe anschließen und sie starten, werden Sie höchstwahrscheinlich keinen Radiosender finden. Das Gleiche passiert hier. Selbst wenn Sie es schaffen, etwas zu erwischen, wird es schwer zu verstehen sein, was es ist.

Ein weiterer sehr wichtiger Aspekt ist die Anzahl der Orders. Je mehr Orders im Verhältnis zur Anzahl der Bars geöffnet werden, desto stärker ist die gefundene Physik, und Sie sollten sich nicht um ihre Leistung in der Zukunft sorgen. Denken Sie auch daran, dass die gefundenen Muster innerhalb des Spreads liegen können, was das System unbrauchbar machen kann, wenn diese Momente unkontrolliert gelassen werden!

An dieser Stelle brauchen wir den Tester des MetaTrader 5. MetaTrader 5 erlaubt das Testen einer Strategie mit echten Ticks. Leider gibt es reale Ticks nur für einen relativ kurzen Zeitraum für alle Währungspaare und Instrumente. Ich werde die Systemversion für MetaTrader 5 mit realen Ticks und sehr strengen Spread-Vorgaben testen, um zu sehen, ob das System im Jahr 2020 funktioniert. Aber zuerst werde ich das System im "Every tick"-Modus für den bisher verwendeten Zeitraum testen:

Dieser Testmodus ist nicht so gut wie derjenige mit echten Ticks. Es ist jedoch offensichtlich, dass hier nur ein sehr kleiner Teil der Signale aus dem Anfangsergebnis geblieben ist. Trotzdem gibt es Signale, die den Spread abdecken und sogar einen kleinen Gewinn bringen. Auch bin ich mir nicht sicher über die Spreads, die der Tester aus der tiefen Historie nimmt. In diesem Test war die Losgröße 0,01, was bedeutet, dass die mathematische Erwartung 5 Punkte beträgt, was sogar höher ist als die des ursprünglichen Tests, obwohl der Chart nicht sehr gut aussieht. Diesen Daten kann immer noch vertraut werden, da eine riesige Stichprobe von 100.000 Positionen im ursprünglichen Test dahinter steht.

Lassen Sie uns nun einen Blick auf das letzte Jahr werfen:

In diesem Test habe ich das Lot auf 0,1 gesetzt, somit liegt die mathematische Erwartung bei 23,4 Punkten, was recht gut ist, wenn man bedenkt, dass der anfängliche Test auf dem MetaTrader 4 eine Erwartung von nur 3 Punkten hatte. Die Erwartung kann in Zukunft sinken, aber nicht um viele Punkte. Es wird also immer noch für einen ausgeglichenen Handel reichen.

Der Expert Advisor für beide Terminals ist im Anhang unten verfügbar. Sie können die Einstellungen ändern und versuchen, funktionierende Parameter für Limit und für Market Orders zu finden. Dann können Sie Sets kombinieren und einige Durchschnittsparameter einstellen. Leider hatte ich nicht genug Zeit, um das Beste daraus zu machen, also gibt es noch Raum für weitere Aktionen.

Bitte beachten Sie natürlich, dass es sich nicht um einen fertigen EA handelt, den Sie einfach auf einem Chart starten und genießen können. Testen Sie die Simulation mit anderen Arten von Orders, und kombinieren Sie dann die Einstellungen und wiederholen Sie diese beiden Testzyklen, bis der EA zuverlässige Handelsergebnisse zeigt. Eventuell können Sie einige Filter einführen oder die Algorithmen selbst anpassen. Wie aus den Tests ersichtlich ist, sind weitere Verbesserungen möglich. Sie können also das beigefügte Programm verwenden und es verfeinern, um stabile Ergebnisse zu erzielen. Außerdem ist es mir nicht gelungen, geeignete Einstellungen für höhere Zeitrahmen zu finden. Der EA funktioniert am besten auf M5, aber vielleicht können Sie andere Zeitrahmen finden. Außerdem hatte ich keine Zeit zu prüfen, wie sich das Set auf anderen Währungspaaren verhält. Normalerweise bedeuten solche flachen Linien jedoch, dass der EA auch auf anderen Währungspaaren funktionieren sollte. Ich werde versuchen, den EA weiter zu verfeinern, wenn ich Zeit habe. Beim Schreiben dieses Artikels habe ich einige Unzulänglichkeiten und Fehler im EA gefunden, es gibt also noch eine Menge zu tun.


Schlussfolgerung

Der Simulator hat die guten Erwartungen bezüglich dieser Analysemethode bewiesen. Nachdem man die ersten Ergebnisse erhalten hat, ist es an der Zeit, darüber nachzudenken, wie man sie verbessern kann, wie man den Algorithmus modernisieren und ihn besser, schneller und variabler machen kann. Der größte Vorteil dieses Ansatzes ist seine maximale Einfachheit. Ich spreche nicht von der logischen Implementierung, sondern von der zugrunde liegenden Logik dieser Methode aus physikalischer Sicht.

Wir können nicht alle Faktoren berücksichtigen, die die Preisbewegung beeinflussen. Aber selbst wenn es mindestens einen solchen Faktor gibt (auch wenn wir ihn nicht zuverlässig beschreiben können), dann wird selbst seine ungenaue Codebeschreibung bestimmte Signale erzeugen. Diese Signale mögen nicht von sehr hoher Qualität sein, aber sie werden ausreichen, um einen gewissen Gewinn aus dieser Idee zu erzielen. Versuchen Sie nicht, eine ideale Formel zu finden, das ist unmöglich, weil man nie alle Faktoren, die den Markt beeinflussen, berücksichtigen kann.

Außerdem müssen Sie nicht genau meinen Ansatz verwenden. Der Zweck dieses Artikels war es, eine Marktphysik zu nutzen, die meiner Meinung nach den Markt zumindest teilweise beschreibt, und einen Expert Advisor zu schreiben, der diese Idee beweisen kann. Als Ergebnis habe ich einen Expert Advisor entwickelt, der gezeigt hat, dass solche Annahmen als Basis für ein funktionierendes System dienen können.

Berücksichtigen Sie auch die Tatsache, dass der Code kleinere und größere Fehler und Bugs hat. Auch in dieser Form funktioniert der EA. Sie sollten ihn nur noch verfeinern. Danach werde ich eine andere Art der Multi-Asset-Marktanalyse vorstellen, die einfacher und effektiver ist und die jeder kennt. Wieder werden wir einen Expert Advisor erstellen.

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/8411

Beigefügte Dateien |
Simulation.zip (27.31 KB)
Neuronale Netze leicht gemacht (Teil 9): Dokumentation der Arbeit Neuronale Netze leicht gemacht (Teil 9): Dokumentation der Arbeit

Wir haben schon einen langen Weg hinter uns und der Code in unserer Bibliothek wird immer umfangreicher. Das macht es schwierig, den Überblick über alle Verbindungen und Abhängigkeiten zu behalten. Daher schlage ich vor, eine Dokumentation für den früher erstellten Code zu erstellen und diese mit jedem neuen Schritt zu aktualisieren. Eine gut vorbereitete Dokumentation wird uns helfen, die Integrität unserer Arbeit zu erkennen.

Entwicklung eines selbstanpassenden Algorithmus (Teil I): Finden eines Grundmusters Entwicklung eines selbstanpassenden Algorithmus (Teil I): Finden eines Grundmusters

In der kommenden Artikelserie werde ich die Entwicklung von selbstanpassenden Algorithmen unter Berücksichtigung der meisten Marktfaktoren demonstrieren, sowie zeigen, wie man diese Situationen systematisiert, in Logik beschreibt und in seiner Handelsaktivität berücksichtigt. Ich werde mit einem sehr einfachen Algorithmus beginnen, der sich nach und nach die Theorie aneignet und sich zu einem sehr komplexen Projekt entwickelt.

Über das Finden von zeitlicher Mustern im Devisenmarkt mit dem CatBoost-Algorithmus Über das Finden von zeitlicher Mustern im Devisenmarkt mit dem CatBoost-Algorithmus

Der Artikel befasst sich mit dem Erstellen von Machine-Learning-Modellen mit Zeitfiltern und diskutiert die Effektivität dieses Ansatzes. Der menschliche Faktor kann nun eliminiert werden, indem das Modell einfach angewiesen wird, zu einer bestimmten Stunde an einem bestimmten Wochentag zu handeln. Die Mustersuche kann durch einen separaten Algorithmus bereitgestellt werden.

Brute-Force-Ansatz zur Mustersuche (Teil III): Neue Horizonte Brute-Force-Ansatz zur Mustersuche (Teil III): Neue Horizonte

Dieser Artikel bietet eine Fortsetzung des Brute-Force-Themas und führt neue Möglichkeiten der Marktanalyse in den Programmalgorithmus ein, wodurch die Geschwindigkeit der Analyse beschleunigt und die Qualität der Ergebnisse verbessert wird. Neue Ergänzungen ermöglichen die qualitativ hochwertigste Ansicht von globalen Mustern innerhalb dieses Ansatzes.