English
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 18): Envelopes Trend Bounce Scalping - Kerninfrastruktur und Signalgenerierung (Teil I)

Automatisieren von Handelsstrategien in MQL5 (Teil 18): Envelopes Trend Bounce Scalping - Kerninfrastruktur und Signalgenerierung (Teil I)

MetaTrader 5Handel |
25 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem vorangegangenen Artikel (Teil 17) haben wir die Grid-Mart Scalping-Strategie mit einem dynamischen Dashboard für die Handelsüberwachung in Echtzeit automatisiert. In Teil 18 beginnen wir mit der Automatisierung der Envelopes Trend Bounce Scalping Strategie in MetaQuotes Language 5 (MQL5) und entwickeln die Kerninfrastruktur des Expert Advisors und die Logik der Signalerzeugung. Wir werden die folgenden Themen behandeln:

  1. Verstehen der Strategie
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende werden Sie eine solide Grundlage für das Scalping von Trendausschlägen haben, bereit für die Handelsausführung im nächsten Teil - lassen Sie uns einsteigen!


Verstehen der Strategie

Die Envelopes Trend Bounce Scalping Strategie verwendet den Envelopes Indikator, der obere und untere Bänder um einen gleitenden Durchschnitt mit einer festgelegten Abweichung (z.B. 0,1% bis 1,4%) erstellt, um Preisumkehrungen für das Scalping kleiner Gewinne zu identifizieren. Er generiert Kaufsignale, wenn der Kurs das untere Band in einem Aufwärtstrend berührt, und Verkaufssignale, wenn er das obere Band in einem Abwärtstrend berührt, bestätigt durch Trendfilter wie den Exponential Moving Average (EMA) mit einer Periodenlänge von 200 oder den Relative Strength Index (RSI) mit einer Periodenlänge von 8. Diese Strategie funktioniert gut in Trendmärkten, erfordert aber ein striktes Risikomanagement, um Fehlsignale in Schwankungsbreiten zu vermeiden, und wir werden uns darum kümmern.

Unser Implementierungsplan umfasst die Entwicklung eines Programms zur Automatisierung dieser Strategie durch die Initialisierung von Envelopes und Trendindikatoren, die Erkennung von Bounce-Signalen und die Einrichtung einer robusten Signalvalidierung. Wir werden modulare Funktionen verwenden, um Bandinteraktionen zu berechnen und Handelsgeschäfte zu filtern, um Präzision beim Hochfrequenz-Scalping zu gewährleisten. Risikokontrollen, wie z. B. die maximale Handelsfrequenz und die Bestätigung von Signalen, sorgen dafür, dass die Zuverlässigkeit unter allen Marktbedingungen erhalten bleibt. Kurz gesagt, hier ist eine Visualisierung dessen, was wir erreichen wollen.

BLAUPAUSE


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, öffnen Sie den MetaEditor, gehen zum Navigator, suchen den Ordner Experts, klicken Sie auf die Registerkarte „Neu“ und folgen Sie den Anweisungen, um die Datei zu erstellen. Sobald es erstellt ist, müssen wir in der Programmierumgebung einige globale Variablen und Eingaben deklarieren, die wir im gesamten Programm verwenden werden.

//+------------------------------------------------------------------+
//|                      Envelopes Trend Bounce Scalping Strategy EA |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+

#property copyright "Copyright 2025, Allan Munene Mutiiria."
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"
#property strict                                           //--- Enable strict compilation for MQL5 compatibility

//--- Include trade operations library
#include <Trade\Trade.mqh> //--- Import MQL5 trade functions for order execution

//--- Input parameters for user configuration
input string OrderComment = __FILE__;                     // Comment to orders
input int MagicNumber = 123456789;                        // Unique identifier for EA orders
double PipPointOverride = 0;                              //--- Override pip point value manually (0 for auto-detection)
input int MaxDeviationSlippage = 10;                      //--- Set maximum slippage in points for trades
bool AllowManualTPSLChanges = true;                       //--- Permit manual adjustment of TP and SL lines on chart
bool OneQuotePerBar = false;                              //--- Process only first tick per bar if true to limit trades
bool AlertOnError = false;                                //--- Trigger MetaTrader alerts for errors
bool NotificationOnError = false;                         //--- Send push notifications for errors
bool EmailOnError = true;                                 //--- Send email notifications for errors
bool DisplayOnChartError = true;                          //--- Show error messages on chart
bool DisplayOrderInfo = false;                            //--- Show order details on chart if enabled
ENUM_TIMEFRAMES DisplayOrderDuringTimeframe = PERIOD_M1;   //--- Set timeframe for order info display (default: M1)
input string CComment = __FILE__;                         //--- Add secondary comment (default: file name)

//--- Global variables for EA functionality
double PipPoint = 0.0001;                                 //--- Initialize pip point (default for 4-digit symbols)
uint OrderFillingType = -1;                               //--- Store order filling type (FOK, IOC, or Return)
uint AccountMarginMode = -1;                              //--- Store account margin mode (Netting or Hedging)
bool StopEA = false;                                      //--- Pause EA operations if true
double UnitsOneLot = 100000;                              //--- Define standard lot size (100,000 units for forex)
int IsDemoLiveOrVisualMode = false;                       //--- Flag demo, live, or visual backtest mode
string Error;                                             //--- Hold current error message
string ErrorPreviousQuote;                                //--- Hold previous quote's error message
string OrderInfoComment;                                  //--- Store order information comments

Wir beginnen mit der Einrichtung der Kerninfrastruktur für unser Programm und konzentrieren uns dabei auf Bibliotheken, Nutzereingaben und globale Variablen für die Signalerzeugung. Wir binden die Bibliothek „Trade.mqh“ mit der Anweisung #include für Handelsoperationen ein. Wir definieren Eingaben wie „OrderComment“, „MagicNumber“ (123456789), „PipPointOverride“, und „MaxDeviationSlippage“ (10), sowie die Booleschen Werte „AllowManualTPSLChanges“ (true), „EmailOnError“ (true) und „DisplayOrderDuringTimeframe“ („PERIOD_M1“).

Wir initialisieren Globals wie „PipPoint“ (0,0001), „OrderFillingType“, „AccountMarginMode“, „StopEA“ (false) und „UnitsOneLot“ (100.000), zusammen mit „Error“ und „OrderInfoComment“ für die Fehler- und Auftragsverfolgung und legen damit die Grundlage für die Einrichtung des Indikators. Wir können nun einige Konstanten und Enumeration definieren, die wir auch verwenden werden.

//--- Define constants for order types
#define OP_BUY 0                                          //--- Represent Buy order type
#define OP_SELL 1                                         //--- Represent Sell order type

//--- Define constants for market data retrieval
#define MODE_TIME 5                                       //--- Retrieve symbol time
#define MODE_BID 9                                        //--- Retrieve Bid price
#define MODE_ASK 10                                       //--- Retrieve Ask price
#define MODE_POINT 11                                     //--- Retrieve point size
#define MODE_DIGITS 12                                    //--- Retrieve digit count
#define MODE_SPREAD 13                                    //--- Retrieve spread
#define MODE_STOPLEVEL 14                                 //--- Retrieve stop level
#define MODE_LOTSIZE 15                                   //--- Retrieve lot size
#define MODE_TICKVALUE 16                                 //--- Retrieve tick value
#define MODE_TICKSIZE 17                                  //--- Retrieve tick size
#define MODE_SWAPLONG 18                                  //--- Retrieve swap long
#define MODE_SWAPSHORT 19                                 //--- Retrieve swap short
#define MODE_STARTING 20                                  //--- Unused, return 0
#define MODE_EXPIRATION 21                                //--- Unused, return 0
#define MODE_TRADEALLOWED 22                              //--- Unused, return 0
#define MODE_MINLOT 23                                    //--- Retrieve minimum lot
#define MODE_LOTSTEP 24                                   //--- Retrieve lot step
#define MODE_MAXLOT 25                                    //--- Retrieve maximum lot
#define MODE_SWAPTYPE 26                                  //--- Retrieve swap mode
#define MODE_PROFITCALCMODE 27                            //--- Retrieve profit calculation mode
#define MODE_MARGINCALCMODE 28                            //--- Unused, return 0
#define MODE_MARGININIT 29                                //--- Unused, return 0
#define MODE_MARGINMAINTENANCE 30                         //--- Unused, return 0
#define MODE_MARGINHEDGED 31                              //--- Unused, return 0
#define MODE_MARGINREQUIRED 32                            //--- Unused, return 0
#define MODE_FREEZELEVEL 33                               //--- Retrieve freeze level

//--- Define string conversion macros
#define CharToStr CharToString                            //--- Convert char to string
#define DoubleToStr DoubleToString                        //--- Convert double to string
#define StrToDouble StringToDouble                        //--- Convert string to double
#define StrToInteger (int)StringToInteger                 //--- Convert string to integer
#define StrToTime StringToTime                            //--- Convert string to datetime
#define TimeToStr TimeToString                            //--- Convert datetime to string
#define StringGetChar StringGetCharacter                  //--- Get character from string
#define StringSetChar StringSetCharacter                  //--- Set character in string

//--- Define enumerations for order grouping
enum ORDER_GROUP_TYPE { 
   Single=1,                                              //--- Group as single order
   SymbolOrderType=2,                                     //--- Group by symbol and order type
   Basket=3,                                              //--- Group all orders as a basket
   SymbolCode=4                                           //--- Group by symbol
};

//--- Define enumerations for profit calculation
enum ORDER_PROFIT_CALCULATION_TYPE { 
   Pips=1,                                                //--- Calculate profit in pips
   Money=2,                                               //--- Calculate profit in currency
   EquityPercentage=3                                     //--- Calculate profit as equity percentage
};

//--- Define enumerations for CRUD operations
enum CRUD { 
   NoAction=0,                                            //--- Perform no action
   Created=1,                                             //--- Create item
   Updated=2,                                             //--- Update item
   Deleted=3                                              //--- Delete item
};

Hier erweitern wir das Programm durch die Definition von Konstanten, Makros und Enumerationen, um die Auftragsabwicklung und den Datenabruf zu rationalisieren. Wir beginnen mit Konstanten wie „OP_BUY“ (0) und „OP_SELL“ (1), um Auftragstypen darzustellen, und einer Reihe von „MODE_“-Konstanten (z. B. „MODE_BID“ = 9, „MODE_ASK“ = 10), um Marktdaten wie Geld-/Briefkurse, Spreads und Losgrößen abzurufen. Wir definieren auch Makros zur Zeichenkettenkonvertierung, wie z. B. CharToString und DoubleToString, um die Konvertierung von Datentypen zu vereinfachen.

Als Nächstes erstellen wir die Enumeration „ORDER_GROUP_TYPE“ zur Kategorisierung von Aufträgen (z.B., „Single“ = 1, „SymbolOrderType“ = 2), die Enumeration „ORDER_PROFIT_CALCULATION_TYPE“ für Gewinnkennzahlen („Pips“ = 1, „Money“ = 2), und die Enumeration „CRUD“ für die Verwaltung von Operationen („Created“ = 1, „Updated“ = 2). Diese Definitionen gewährleisten eine einheitliche Datenverarbeitung und unterstützen die modulare Signalerzeugung für das Programm. Wir können dann einige Hilfsfunktionen wie folgt definieren.

//--- Copy single indicator buffer value
double CopyBufferOneValue(int handle, int index, int shift) {
   double buf[];                                          //--- Declare array for buffer data
   //--- Copy one value from indicator buffer
   if(CopyBuffer(handle, index, shift, 1, buf) > 0)
      return(buf[0]);                                     //--- Return buffer value
   return EMPTY_VALUE;                                    //--- Return EMPTY_VALUE on failure
}

//--- Retrieve current Ask price
double Ask_LibFunc() {
   MqlTick last_tick;                                     //--- Declare tick data structure
   SymbolInfoTick(_Symbol, last_tick);                    //--- Fetch latest tick for symbol
   return last_tick.ask;                                  //--- Return Ask price
}

//--- Retrieve current Bid price
double Bid_LibFunc() {
   MqlTick last_tick;                                     //--- Declare tick data structure
   SymbolInfoTick(_Symbol, last_tick);                    //--- Fetch latest tick for symbol
   return last_tick.bid;                                  //--- Return Bid price
}

//--- Retrieve account equity
double AccountEquity_LibFunc() {
   return AccountInfoDouble(ACCOUNT_EQUITY);              //--- Return current equity
}

//--- Retrieve account free margin
double AccountFreeMargin_LibFunc() {
   return AccountInfoDouble(ACCOUNT_MARGIN_FREE);         //--- Return free margin
}

//--- Retrieve market information for a symbol
double MarketInfo_LibFunc(string symbol, int type) {
   switch(type) {                                                           //--- Handle requested info type
   case MODE_LOW:
      return(SymbolInfoDouble(symbol, SYMBOL_LASTLOW));                     //--- Return last low price
   case MODE_HIGH:
      return(SymbolInfoDouble(symbol, SYMBOL_LASTHIGH));                    //--- Return last high price
   case MODE_TIME:
      return((double)SymbolInfoInteger(symbol, SYMBOL_TIME));               //--- Return symbol time
   case MODE_BID:
      return(Bid_LibFunc());                                                //--- Return Bid price
   case MODE_ASK:
      return(Ask_LibFunc());                                                //--- Return Ask price
   case MODE_POINT:
      return(SymbolInfoDouble(symbol, SYMBOL_POINT));                       //--- Return point size
   case MODE_DIGITS:
      return((double)SymbolInfoInteger(symbol, SYMBOL_DIGITS));             //--- Return digit count
   case MODE_SPREAD:
      return((double)SymbolInfoInteger(symbol, SYMBOL_SPREAD));             //--- Return spread
   case MODE_STOPLEVEL:
      return((double)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL));  //--- Return stop level
   case MODE_LOTSIZE:
      return(SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE));         //--- Return contract size
   case MODE_TICKVALUE:
      return(SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE));            //--- Return tick value
   case MODE_TICKSIZE:
      return(SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE));             //--- Return tick size
   case MODE_SWAPLONG:
      return(SymbolInfoDouble(symbol, SYMBOL_SWAP_LONG));                   //--- Return swap long
   case MODE_SWAPSHORT:
      return(SymbolInfoDouble(symbol, SYMBOL_SWAP_SHORT));                  //--- Return swap short
   case MODE_MINLOT:
      return(SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN));                  //--- Return minimum lot
   case MODE_LOTSTEP:
      return(SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP));                 //--- Return lot step
   case MODE_MAXLOT:
      return(SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX));                  //--- Return maximum lot
   case MODE_SWAPTYPE:
      return((double)SymbolInfoInteger(symbol, SYMBOL_SWAP_MODE));          //--- Return swap mode
   case MODE_PROFITCALCMODE:
      return((double)SymbolInfoInteger(symbol, SYMBOL_TRADE_CALC_MODE));    //--- Return profit calc mode
   case MODE_FREEZELEVEL:
      return((double)SymbolInfoInteger(symbol, SYMBOL_TRADE_FREEZE_LEVEL)); //--- Return freeze level
   default:
      return(0);                                         //--- Return 0 for unknown type
   }
   return(0);                                            //--- Ensure fallback return
}

Wir erstellen Hilfsfunktionen, die eine effiziente Datenabfrage für die Signalerzeugung ermöglichen. Wir erstellen die Funktion „CopyBufferOneValue“, die ein Indikatorhandle, einen Pufferindex und eine Verschiebung als Eingaben annimmt, einen einzelnen Wert aus einem Indikatorpuffer in das Array „buf“ kopiert und den Wert oder bei Fehlschlag EMPTY_VALUE zurückgibt. Diese Funktion ist für das Abrufen von präzisen Indikatordaten, wie z. B. die Werte der Bänder von Envelopes, von entscheidender Bedeutung.

Als Nächstes definieren wir die Funktionen „Ask_LibFunc“ und „Bid_LibFunc“, um den aktuellen Brief- bzw. Geldkurs abzurufen. Dabei verwenden wir die Struktur MqlTick und SymbolInfoTick, um die letzten Tick-Daten für das aktuelle Symbol zu erfassen. Wir implementieren auch die Funktionen „AccountEquity_LibFunc“ und „AccountFreeMargin_LibFunc“, um das Eigenkapital und die freie Marge des Kontos über AccountInfoDouble zurückzugeben und die Berechnungen des Risikomanagements zu unterstützen.

Schließlich erstellen wir die Funktion „MarketInfo_LibFunc“, die eine Switch-Anweisung mit „MODE_“-Konstanten (z. B. „MODE_BID“, „MODE_ASK“) verwendet, um verschiedene Symboleigenschaften wie Spread, Losgröße oder Swap-Sätze abzurufen, wobei bei nicht unterstützten Typen 0 zurückgegeben wird. Diese Funktionen bilden die Datengrundlage für die Generierung präziser Handelssignale. Nun können wir zur Definition von Klassen und Funktionen übergehen, in denen die Hauptlogik untergebracht werden soll.

//--- Define function interface
interface IFunction {
   double GetValue(int index);                            //--- Retrieve value at index
   void Evaluate();                                       //--- Execute function evaluation
   void Init();                                           //--- Initialize function
};

//--- Define base class for handling double values in a circular buffer
class DoubleFunction : public IFunction {
private:
   double _values[];                                      //--- Store array of historical values
   int _zeroIndex;                                        //--- Track current index in circular buffer

protected:
   int ValueCount;                                        //--- Define number of values to store

public:
   //--- Initialize the circular buffer
   void Init() {
      _zeroIndex = -1;                                    //--- Set initial index to -1
      ArrayResize(_values, ValueCount);                   //--- Resize array to hold ValueCount elements
      ArrayInitialize(_values, GetCurrentValue());        //--- Fill array with current value
   }

   //--- Update buffer with new value
   void Evaluate() {
      double currentValue = GetCurrentValue();            //--- Retrieve current value
      _zeroIndex = (_zeroIndex + 1) % ValueCount;         //--- Increment index, wrap around if needed
      _values[_zeroIndex] = currentValue;                 //--- Store new value at current index
   }

   //--- Retrieve value at specified index
   double GetValue(int requestIndex = 0) {
      int requiredIndex = (_zeroIndex + ValueCount - requestIndex) % ValueCount; //--- Calculate index for requested value
      return _values[requiredIndex];                      //--- Return value at calculated index
   }

   //--- Declare pure virtual method for getting current value
   virtual double GetCurrentValue() = 0;                  //--- Require derived classes to implement
};

//--- Define base class for Ask and Bid price functions
class AskBidFunction : public DoubleFunction {
public:
   //--- Initialize AskBidFunction
   void AskBidFunction() {
      ValueCount = 2;                                     //--- Set buffer to store 2 values
   }
};

//--- Define class for retrieving Ask price
class AskFunction : public AskBidFunction {
public:
   //--- Retrieve current Ask price
   double GetCurrentValue() {
      return Ask_LibFunc();                               //--- Return Ask price using utility function
   }
};

//--- Define class for retrieving Bid price
class BidFunction : public AskBidFunction {
public:
   //--- Retrieve current Bid price
   double GetCurrentValue() {
      return Bid_LibFunc();                               //--- Return Bid price using utility function
   }
};

//--- Declare global function pointers for Ask and Bid
IFunction *AskFunc;                                       //--- Point to Ask price function
IFunction *BidFunc;                                       //--- Point to Bid price function

//--- Retrieve order filling type for current symbol
uint GetFillingType() {
   uint fillingType = -1;                                 //--- Initialize filling type as invalid
   uint filling = (uint)SymbolInfoInteger(Symbol(), SYMBOL_FILLING_MODE); //--- Get symbol filling mode
   if ((filling & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) {
      fillingType = ORDER_FILLING_FOK;                    //--- Set Fill or Kill type
      Print("Filling type: FOK");                         //--- Log FOK filling type
   } else if ((filling & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) {
      fillingType = ORDER_FILLING_IOC;                    //--- Set Immediate or Cancel type
      Print("Filling type: IOC");                         //--- Log IOC filling type
   } else {
      fillingType = ORDER_FILLING_RETURN;                 //--- Set Return type as default
      Print("Filling type: RETURN");                      //--- Log Return filling type
   }
   return fillingType;                                    //--- Return determined filling type
}

//--- Retrieve trade execution mode for current symbol
uint GetExecutionType() {
   uint executionType = -1;                               //--- Initialize execution type as invalid
   uint execution = (uint)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_EXEMODE); //--- Get symbol execution mode
   if ((execution & SYMBOL_TRADE_EXECUTION_MARKET) == SYMBOL_TRADE_EXECUTION_MARKET) {
      executionType = SYMBOL_TRADE_EXECUTION_MARKET;      //--- Set Market execution mode
      Print("Deal execution mode: Market execution, deviation setting will be ignored."); //--- Log Market mode
   } else if ((execution & SYMBOL_TRADE_EXECUTION_INSTANT) == SYMBOL_TRADE_EXECUTION_INSTANT) {
      executionType = SYMBOL_TRADE_EXECUTION_INSTANT;     //--- Set Instant execution mode
      Print("Deal execution mode: Instant execution, deviation setting might be taken into account, depending on your broker."); //--- Log Instant mode
   } else if ((execution & SYMBOL_TRADE_EXECUTION_REQUEST) == SYMBOL_TRADE_EXECUTION_REQUEST) {
      executionType = SYMBOL_TRADE_EXECUTION_REQUEST;     //--- Set Request execution mode
      Print("Deal execution mode: Request execution, deviation setting might be taken into account, depending on your broker."); //--- Log Request mode
   } else if ((execution & SYMBOL_TRADE_EXECUTION_EXCHANGE) == SYMBOL_TRADE_EXECUTION_EXCHANGE) {
      executionType = SYMBOL_TRADE_EXECUTION_EXCHANGE;    //--- Set Exchange execution mode
      Print("Deal execution mode: Exchange execution, deviation setting will be ignored."); //--- Log Exchange mode
   }
   return executionType;                                  //--- Return determined execution type
}

//--- Retrieve account margin mode
uint GetAccountMarginMode() {
   uint marginMode = -1;                                  //--- Initialize margin mode as invalid
   marginMode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE); //--- Get account margin mode
   if (marginMode == ACCOUNT_MARGIN_MODE_RETAIL_NETTING) {
      Print("Account margin mode: Netting");              //--- Log Netting mode
   } else if (marginMode == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) {
      Print("Account margin mode: Hedging");              //--- Log Hedging mode
   } else if (marginMode == ACCOUNT_MARGIN_MODE_EXCHANGE) {
      Print("Account margin mode: Exchange");             //--- Log Exchange mode
   } else {
      Print("Unknown margin type");                       //--- Log unknown margin mode
   }
   return marginMode;                                     //--- Return determined margin mode
}

//--- Retrieve description for trade error code
string GetErrorDescription(int error_code) {
   string description = "";                               //--- Initialize empty description
   switch (error_code) {                                  //--- Match error code to description
   case 10004: description = "Requote"; break;            //--- Set Requote error
   case 10006: description = "Request rejected"; break;   //--- Set Request rejected error
   case 10007: description = "Request canceled by trader"; break; //--- Set Trader cancel error
   case 10008: description = "Order placed"; break;       //--- Set Order placed status
   case 10009: description = "Request completed"; break;  //--- Set Request completed status
   case 10010: description = "Only part of the request was completed"; break; //--- Set Partial completion error
   case 10011: description = "Request processing error"; break; //--- Set Processing error
   case 10012: description = "Request canceled by timeout"; break; //--- Set Timeout cancel error
   case 10013: description = "Invalid request"; break;    //--- Set Invalid request error
   case 10014: description = "Invalid volume in the request"; break; //--- Set Invalid volume error
   case 10015: description = "Invalid price in the request"; break; //--- Set Invalid price error
   case 10016: description = "Invalid stops in the request"; break; //--- Set Invalid stops error
   case 10017: description = "Trade is disabled"; break;  //--- Set Trade disabled error
   case 10018: description = "Market is closed"; break;   //--- Set Market closed error
   case 10019: description = "There is not enough money to complete the request"; break; //--- Set Insufficient funds error
   case 10020: description = "Prices changed"; break;     //--- Set Price change error
   case 10021: description = "There are no quotes to process the request"; break; //--- Set No quotes error
   case 10022: description = "Invalid order expiration date in the request"; break; //--- Set Invalid expiration error
   case 10023: description = "Order state changed"; break; //--- Set Order state change error
   case 10024: description = "Too frequent requests"; break; //--- Set Too frequent requests error
   case 10025: description = "No changes in request"; break; //--- Set No changes error
   case 10026: description = "Autotrading disabled by server"; break; //--- Set Server autotrading disabled error
   case 10027: description = "Autotrading disabled by client terminal"; break; //--- Set Client autotrading disabled error
   case 10028: description = "Request locked for processing"; break; //--- Set Request locked error
   case 10029: description = "Order or position frozen"; break; //--- Set Frozen order error
   case 10030: description = "Invalid order filling type"; break; //--- Set Invalid filling type error
   case 10031: description = "No connection with the trade server"; break; //--- Set No server connection error
   case 10032: description = "Operation is allowed only for live accounts"; break; //--- Set Live account only error
   case 10033: description = "The number of pending orders has reached the limit"; break; //--- Set Pending order limit error
   case 10034: description = "The volume of orders and positions for the symbol has reached the limit"; break; //--- Set Symbol volume limit error
   case 10035: description = "Incorrect or prohibited order type"; break; //--- Set Incorrect order type error
   case 10036: description = "Position with the specified POSITION_IDENTIFIER has already been closed"; break; //--- Set Position closed error
   case 10038: description = "A close volume exceeds the current position volume"; break; //--- Set Excessive close volume error
   case 10039: description = "A close order already exists for a specified position"; break; //--- Set Existing close order error
   case 10040: description = "The number of open positions simultaneously present on an account has reached the limit"; break; //--- Set Position limit error
   case 10041: description = "The pending order activation request is rejected, the order is canceled"; break; //--- Set Order activation rejected error
   case 10042: description = "The request is rejected, because the 'Only long positions are allowed' rule is set for the symbol"; break; //--- Set Long-only rule error
   case 10043: description = "The request is rejected, because the 'Only short positions are allowed' rule is set for the symbol"; break; //--- Set Short-only rule error
   case 10044: description = "The request is rejected, because the 'Only position closing is allowed' rule is set for the symbol"; break; //--- Set Close-only rule error
   case 10045: description = "The request is rejected, because 'Position closing is allowed only by FIFO rule' flag is set for the trading account"; break; //--- Set FIFO closing rule error
   case 10046: description = "The request is rejected, because the 'Opposite positions on a single symbol are disabled' rule is set for the trading account"; break; //--- Set Opposite positions disabled error
   default: description = "Unknown error code " + IntegerToString(error_code); break; //--- Set unknown error with code
   }
   return description;                                    //--- Return error description
}

//--- Set pip point value for current symbol
void SetPipPoint() {
   if (PipPointOverride != 0) {
      PipPoint = PipPointOverride;                        //--- Use manual override if specified
   } else {
      PipPoint = GetRealPipPoint(Symbol());               //--- Calculate pip point automatically
   }
   Print("Pip (forex)/ Point (indices): " + DoubleToStr(PipPoint, 5)); //--- Log calculated pip point
}

//--- Calculate real pip point based on symbol digits
double GetRealPipPoint(string Currency) {
   double calcPoint = 0;                                  //--- Initialize pip point value
   double calcDigits = Digits();                          //--- Get symbol's decimal digits
   Print("Number of digits after decimal point: " + DoubleToString(calcDigits)); //--- Log digit count
   if (calcDigits == 0) {
      calcPoint = 1;                                      //--- Set pip point to 1 for 0 digits
   } else if (calcDigits == 1) {
      calcPoint = 1;                                      //--- Set pip point to 1 for 1 digit
   } else if (calcDigits == 2) {
      calcPoint = 0.1;                                    //--- Set pip point to 0.1 for 2 digits
   } else if (calcDigits == 3) {
      calcPoint = 0.01;                                   //--- Set pip point to 0.01 for 3 digits
   } else if (calcDigits == 4 || calcDigits == 5) {
      calcPoint = 0.0001;                                 //--- Set pip point to 0.0001 for 4 or 5 digits
   }
   return calcPoint;                                      //--- Return calculated pip point
}

//--- Calculate required margin for an order
bool MarginRequired(ENUM_ORDER_TYPE type, double volume, double &marginRequired) {
   double price;                                          //--- Declare price variable
   if (type == ORDER_TYPE_BUY) {
      price = Ask_LibFunc();                              //--- Set price to Ask for Buy orders
   } else if (type == ORDER_TYPE_SELL) {
      price = Bid_LibFunc();                              //--- Set price to Bid for Sell orders
   } else {
      string message = "MarginRequired: Unsupported ENUM_ORDER_TYPE"; //--- Prepare error message
      HandleErrors(message);                              //--- Log unsupported order type error
      price = Ask_LibFunc();                              //--- Default to Ask price
   }
   if (!OrderCalcMargin(type, _Symbol, volume, price, marginRequired)) {
      HandleErrors(StringFormat("Couldn't calculate required margin, error: %d", GetLastError())); //--- Log margin calculation error
      return false;                                       //--- Return false on failure
   }
   return true;                                           //--- Return true on success
}

//--- Create horizontal line on chart for TP/SL visualization
bool HLineCreate(const long chart_ID = 0, const string name = "HLine", const int sub_window = 0,
                 double price = 0, const color clr = clrRed, const ENUM_LINE_STYLE style = STYLE_SOLID,
                 const int width = 1, const bool back = false, const bool selection = true,
                 const bool hidden = true, const long z_order = 0) {
   uint lineFindResult = ObjectFind(chart_ID, name);      //--- Check if line already exists
   if (lineFindResult != UINT_MAX) {
      Print("HLineCreate object already exists: " + name); //--- Log existing line error
      return false;                                       //--- Return false if line exists
   }
   if (!price) {
      price = Bid_LibFunc();                              //--- Default to Bid price if not specified
   }
   ResetLastError();                                      //--- Clear last error
   if (!ObjectCreate(chart_ID, name, OBJ_HLINE, sub_window, 0, price)) {
      Print(__FUNCTION__, ": failed to create a horizontal line! Error code = ", GetLastError()); //--- Log line creation error
      return false;                                       //--- Return false on failure
   }
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);  //--- Set line color
   ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style); //--- Set line style
   ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width); //--- Set line width
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);   //--- Set background rendering
   if (AllowManualTPSLChanges) {
      ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection); //--- Enable line selection
      ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);   //--- Set line as selected
   }
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden); //--- Hide line in object list
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order); //--- Set mouse click priority
   return true;                                           //--- Return true on success
}

//--- Move existing horizontal line on chart
bool HLineMove(const long chart_ID = 0, const string name = "HLine", double price = 0) {
   uint lineFindResult = ObjectFind(ChartID(), name);     //--- Check if line exists
   if (lineFindResult == UINT_MAX) {
      Print("HLineMove didn't find object: " + name);      //--- Log missing line error
      return false;                                       //--- Return false if line not found
   }
   if (!price) {
      price = SymbolInfoDouble(Symbol(), SYMBOL_BID);      //--- Default to Bid price if not specified
   }
   ResetLastError();                                      //--- Clear last error
   if (!ObjectMove(chart_ID, name, 0, 0, price)) {
      Print(__FUNCTION__, ": failed to move the horizontal line! Error code = ", GetLastError()); //--- Log line move error
      return false;                                       //--- Return false on failure
   }
   return true;                                           //--- Return true on success
}

//--- Delete chart object by name
bool AnyChartObjectDelete(const long chart_ID = 0, const string name = "") {
   uint lineFindResult = ObjectFind(ChartID(), name);     //--- Check if object exists
   if (lineFindResult == UINT_MAX) {
      return false;                                       //--- Return false if object not found
   }
   ResetLastError();                                      //--- Clear last error
   if (!ObjectDelete(chart_ID, name)) {
      Print(__FUNCTION__, ": failed to delete a horizontal line! Error code = ", GetLastError()); //--- Log deletion error
      return false;                                       //--- Return false on failure
   }
   return true;                                           //--- Return true on success
}

//--- Placeholder function for future use
int Dummy(string message) {
   return 0;                                              //--- Return 0 (no operation)
}

Hier entwickeln wir das Programm weiter, indem wir eine Schnittstelle, Klassen und Hilfsfunktionen zur Verwaltung von Kursdaten und Charts definieren. Wir erstellen die Schnittstelle „IFunction“ und geben die Methoden „GetValue“, „Evaluate“ und „Init“ an, um die Datenabfrage für Preisfunktionen zu standardisieren. Dann definieren wir die Klasse „DoubleFunction“, die von „IFunction“ erbt, um einen Ringpuffer mit dem Array „_values“ und der Variablen „_zeroIndex“ zu verwalten, indem wir „Init“ zum Einrichten des Puffers, „Evaluate“ zum Aktualisieren der Werte und „GetValue“ zum Abrufen historischer Daten, mit einer rein virtuellen Methode „GetCurrentValue“ für Unterklassen.

Wir leiten die Klasse „AskBidFunction“ von „DoubleFunction“ ab, setzen „ValueCount“ auf 2 für die Pufferung von Ask/Bid-Kursen und erstellen die Klassen „AskFunction“ und „BidFunction“, um aktuelle Preise über „Ask_LibFunc“ und „Bid_LibFunc“ in ihren „GetCurrentValue“-Methoden zurückzugeben. Für den Zugriff auf diese Funktionen werden globale Zeiger „AskFunc“ und „BidFunc“ deklariert. Wir implementieren auch die Funktionen „GetFillingType“, „GetExecutionType“ und „GetAccountMarginMode“, um maklerspezifische Einstellungen, Protokollierungsmodi wie ORDER_FILLING_FOK oder „ACCOUNT_MARGIN_MODE_RETAIL_HEDGING“ zu ermitteln. Die Funktion „GetErrorDescription“ ordnet Fehlercodes lesbaren Zeichenketten zu, um die Fehlersuche zu erleichtern.

Zusätzlich definieren wir „SetPipPoint“ und „GetRealPipPoint“, um die „PipPoint“-Variable basierend auf den Symbolziffern zu berechnen, „MarginRequired“, um den Margin-Bedarf mit „Ask_LibFunc“ oder „Bid_LibFunc“ zu berechnen, sowie „HLineCreate“, „HLineMove“ und „AnyChartObjectDelete“, um Chart-Linien für die TP/SL-Visualisierung zu verwalten, mit „Dummy“ als Platzhalter für die zukünftige Verwendung. Wir können nun die zu verwendenden Indikatoren deklarieren und müssen daher einige zusätzliche Eingaben für die dynamische Steuerung definieren (siehe unten).

//--- Input parameters for indicators
input int iMA_SMA8_ma_period = 14;                        //--- Set SMA period for trend filter (M30 timeframe)
input int iMA_SMA8_ma_shift = 2;                          //--- Set SMA shift for trend filter
input int iMA_SMA_4_ma_period = 9;                        //--- Set SMA period for reverse trend filter (M30 timeframe)
input int iMA_SMA_4_ma_shift = 0;                         //--- Set SMA shift for reverse trend filter
input int iMA_EMA200_ma_period = 200;                     //--- Set EMA period for long-term trend (M1 timeframe)
input int iMA_EMA200_ma_shift = 0;                        //--- Set EMA shift for long-term trend
input int iRSI_RSI_ma_period = 8;                         //--- Set RSI period for overbought/oversold signals (M1 timeframe)
input int iEnvelopes_ENV_LOW_ma_period = 95;              //--- Set Envelopes period for lower band (M1 timeframe)
input int iEnvelopes_ENV_LOW_ma_shift = 0;                //--- Set Envelopes shift for lower band
input double iEnvelopes_ENV_LOW_deviation = 1.4;          //--- Set Envelopes deviation for lower band (1.4%)
input int iEnvelopes_ENV_UPPER_ma_period = 150;           //--- Set Envelopes period for upper band (M1 timeframe)
input int iEnvelopes_ENV_UPPER_ma_shift = 0;              //--- Set Envelopes shift for upper band
input double iEnvelopes_ENV_UPPER_deviation = 0.1;        //--- Set Envelopes deviation for upper band (0.1%)

//--- Indicator handle declarations
int hd_iMA_SMA8;                                          //--- Store handle for 8-period SMA
//--- Retrieve 8-period SMA value
double fn_iMA_SMA8(string symbol, int shift) {
   int index = 0;                                         //--- Set buffer index to 0
   return CopyBufferOneValue(hd_iMA_SMA8, index, shift);  //--- Return SMA value at specified shift
}

int hd_iMA_EMA200;                                        //--- Store handle for 200-period EMA
//--- Retrieve 200-period EMA value
double fn_iMA_EMA200(string symbol, int shift) {
   int index = 0;                                         //--- Set buffer index to 0
   return CopyBufferOneValue(hd_iMA_EMA200, index, shift); //--- Return EMA value at specified shift
}

int hd_iRSI_RSI;                                          //--- Store handle for 8-period RSI
//--- Retrieve RSI value
double fn_iRSI_RSI(string symbol, int shift) {
   int index = 0;                                         //--- Set buffer index to 0
   return CopyBufferOneValue(hd_iRSI_RSI, index, shift);  //--- Return RSI value at specified shift
}

int hd_iEnvelopes_ENV_LOW;                                //--- Store handle for lower Envelopes band
//--- Retrieve lower Envelopes band value
double fn_iEnvelopes_ENV_LOW(string symbol, int mode, int shift) {
   int index = mode;                                      //--- Set buffer index to specified mode
   return CopyBufferOneValue(hd_iEnvelopes_ENV_LOW, index, shift); //--- Return lower Envelopes value
}

int hd_iEnvelopes_ENV_UPPER;                              //--- Store handle for upper Envelopes band
//--- Retrieve upper Envelopes band value
double fn_iEnvelopes_ENV_UPPER(string symbol, int mode, int shift) {
   int index = mode;                                      //--- Set buffer index to specified mode
   return CopyBufferOneValue(hd_iEnvelopes_ENV_UPPER, index, shift); //--- Return upper Envelopes value
}

int hd_iMA_SMA_4;                                         //--- Store handle for 4-period SMA
//--- Retrieve 4-period SMA value
double fn_iMA_SMA_4(string symbol, int shift) {
   int index = 0;                                         //--- Set buffer index to 0
   return CopyBufferOneValue(hd_iMA_SMA_4, index, shift); //--- Return SMA value at specified shift
}

Um die Indikatoreinstellungen zu konfigurieren, verwenden wir die „input“-Direktive, um die externen Nutzereingaben zu deklarieren und rufen dann die Funktion „CopyBufferOneValue“ auf, um die angegebenen Werte der Indikatoren zu erhalten. Wir können dann eine Klasse für die Auftragsverwaltung erstellen, um das Grundgerüst für die Infrastruktur zu schaffen.

//--- Commission variables
double CommissionAmountPerTrade = 0.0;                    //--- Set fixed commission per trade (default: 0)
double CommissionPercentagePerLot = 0.0;                  //--- Set commission percentage per lot (default: 0)
double CommissionAmountPerLot = 0.0;                      //--- Set fixed commission per lot (default: 0)
double TotalCommission = 0.0;                             //--- Track total commission for all trades
bool UseCommissionInProfitInPips = false;                 //--- Exclude commission from pip profit if false

//--- Define class for order close information
class OrderCloseInfo {
public:
   string ModuleCode;                                     //--- Store module identifier for close condition
   double Price;                                          //--- Store price for TP or SL
   int Percentage;                                        //--- Store percentage of order to close
   bool IsOld;                                            //--- Flag outdated close info

   //--- Default constructor
   void OrderCloseInfo() {}                               //--- Initialize empty close info

   //--- Copy constructor
   void OrderCloseInfo(OrderCloseInfo* ordercloseinfo) {
      ModuleCode = ordercloseinfo.ModuleCode;             //--- Copy module code
      Price = ordercloseinfo.Price;                       //--- Copy price
      Percentage = ordercloseinfo.Percentage;             //--- Copy percentage
      IsOld = ordercloseinfo.IsOld;                       //--- Copy old flag
   }

   //--- Check if Stop Loss is hit
   bool IsClosePriceSLHit(ENUM_ORDER_TYPE type, double ask, double bid) {
      switch (type) {
      case ORDER_TYPE_BUY:
         return bid <= Price;                             //--- Return true if Bid falls below SL for Buy
      case ORDER_TYPE_SELL:
         return ask >= Price;                             //--- Return true if Ask rises above SL for Sell
      }
      return false;                                       //--- Return false for invalid type
   }

   //--- Check if Take Profit is hit
   bool IsClosePriceTPHit(ENUM_ORDER_TYPE type, double ask, double bid) {
      switch (type) {
      case ORDER_TYPE_BUY:
         return bid >= Price;                             //--- Return true if Bid reaches TP for Buy
      case ORDER_TYPE_SELL:
         return ask <= Price;                             //--- Return true if Ask reaches TP for Sell
      }
      return false;                                       //--- Return false for invalid type
   }

   //--- Destructor
   void ~OrderCloseInfo() {}                              //--- Clean up close info
};

//--- Define class for managing order details
class Order {
public:
   ulong Ticket;                                          //--- Store unique order ticket
   ENUM_ORDER_TYPE Type;                                  //--- Store order type (Buy/Sell)
   ENUM_ORDER_STATE State;                                //--- Store order state (e.g., Filled)
   long MagicNumber;                                      //--- Store EA’s magic number
   double Lots;                                           //--- Store order volume in lots
   double OrderFilledLots;                                //--- Store filled volume
   datetime OpenTime;                                     //--- Store order open time
   double OpenPrice;                                      //--- Store order open price
   datetime CloseTime;                                    //--- Store order close time
   double ClosePrice;                                     //--- Store order close price
   double StopLoss;                                       //--- Store Stop Loss price
   double StopLossManual;                                 //--- Store manually set Stop Loss
   double TakeProfit;                                     //--- Store Take Profit price
   double TakeProfitManual;                               //--- Store manually set Take Profit
   datetime Expiration;                                   //--- Store order expiration time
   double CurrentProfitPips;                              //--- Store current profit in pips
   double HighestProfitPips;                              //--- Store highest profit in pips
   double LowestProfitPips;                               //--- Store lowest profit in pips
   string Comment;                                        //--- Store order comment
   uint TradeRetCode;                                     //--- Store trade result code
   ulong TradeDealTicket;                                 //--- Store deal ticket
   double TradePrice;                                     //--- Store trade price
   double TradeVolume;                                    //--- Store trade volume
   double Commission;                                     //--- Store commission cost
   double CommissionInPips;                               //--- Store commission in pips
   string SymbolCode;                                     //--- Store symbol code
   bool IsAwaitingDealExecution;                          //--- Flag pending deal execution
   OrderCloseInfo* CloseInfosTP[];                        //--- Store Take Profit close info
   OrderCloseInfo* CloseInfosSL[];                        //--- Store Stop Loss close info
   Order* ParentOrder;                                    //--- Store parent order for splits
   bool MustBeVisibleOnChart;                             //--- Flag chart visibility

   //--- Initialize order with visibility flag
   void Order(bool mustBeVisibleOnChart) {
      OrderFilledLots = 0.0;                              //--- Set filled lots to 0
      OpenPrice = 0.0;                                    //--- Set open price to 0
      ClosePrice = 0.0;                                   //--- Set close price to 0
      Commission = 0.0;                                   //--- Set commission to 0
      CommissionInPips = 0.0;                             //--- Set commission in pips to 0
      MustBeVisibleOnChart = mustBeVisibleOnChart;        //--- Set chart visibility flag
   }

   //--- Copy order details with visibility flag
   void Order(Order* order, bool mustBeVisibleOnChart) {
      Ticket = order.Ticket;                              //--- Copy ticket
      Type = order.Type;                                  //--- Copy order type
      State = order.State;                                //--- Copy order state
      MagicNumber = order.MagicNumber;                    //--- Copy magic number
      Lots = order.Lots;                                  //--- Copy lots
      OpenTime = order.OpenTime;                          //--- Copy open time
      OpenPrice = order.OpenPrice;                        //--- Copy open price
      CloseTime = order.CloseTime;                        //--- Copy close time
      ClosePrice = order.ClosePrice;                      //--- Copy close price
      StopLoss = order.StopLoss;                          //--- Copy Stop Loss
      StopLossManual = order.StopLossManual;              //--- Copy manual Stop Loss
      TakeProfit = order.TakeProfit;                      //--- Copy Take Profit
      TakeProfitManual = order.TakeProfitManual;          //--- Copy manual Take Profit
      Expiration = order.Expiration;                      //--- Copy expiration
      CurrentProfitPips = order.CurrentProfitPips;        //--- Copy current profit
      HighestProfitPips = order.HighestProfitPips;        //--- Copy highest profit
      LowestProfitPips = order.LowestProfitPips;          //--- Copy lowest profit
      Comment = order.Comment;                            //--- Copy comment
      TradeRetCode = order.TradeRetCode;                  //--- Copy trade result code
      TradeDealTicket = order.TradeDealTicket;            //--- Copy deal ticket
      TradePrice = order.TradePrice;                      //--- Copy trade price
      TradeVolume = order.TradeVolume;                    //--- Copy trade volume
      Commission = order.Commission;                      //--- Copy commission
      CommissionInPips = order.CommissionInPips;          //--- Copy commission in pips
      SymbolCode = order.SymbolCode;                      //--- Copy symbol code
      IsAwaitingDealExecution = order.IsAwaitingDealExecution; //--- Copy execution flag
      ParentOrder = order.ParentOrder;                    //--- Copy parent order
      MustBeVisibleOnChart = mustBeVisibleOnChart;        //--- Set visibility flag
   }

   //--- Split order into partial close
   Order* SplitOrder(int percentageToSplitOff) {
      Order* splittedOffPieceOfOrder = new Order(&this, true); //--- Create new order for split
      splittedOffPieceOfOrder.Lots = CalcVolumePartialClose(this.Lots, percentageToSplitOff); //--- Calculate split volume
      if (this.Lots - splittedOffPieceOfOrder.Lots < 1e-13) {
         splittedOffPieceOfOrder.MustBeVisibleOnChart = false; //--- Hide split if no volume remains
         splittedOffPieceOfOrder.Lots = 0;                   //--- Set split volume to 0
      } else {
         this.Lots = this.Lots - splittedOffPieceOfOrder.Lots; //--- Reduce original order volume
      }
      return splittedOffPieceOfOrder;                        //--- Return split order
   }

   //--- Calculate profit in pipettes
   double CalculateProfitPipettes() {
      double closePrice = GetClosePrice();                   //--- Get current close price
      switch (Type) {
      case ORDER_TYPE_BUY:
         return (closePrice - OpenPrice);                    //--- Return Buy profit in pipettes
      case ORDER_TYPE_SELL:
         return (OpenPrice - closePrice);                    //--- Return Sell profit in pipettes
      }
      return 0;                                              //--- Return 0 for invalid type
   }

   //--- Calculate profit in pips
   double CalculateProfitPips() {
      double pipettes = CalculateProfitPipettes();           //--- Get profit in pipettes
      double pips = pipettes / PipPoint;                     //--- Convert to pips
      if (UseCommissionInProfitInPips) {
         return pips - CommissionInPips;                     //--- Subtract commission if enabled
      }
      return pips;                                           //--- Return profit in pips
   }

   //--- Calculate profit in account currency
   double CalculateProfitCurrency() {
      double closePrice = GetClosePrice();                   //--- Get current close price
      switch (Type) {
      case OP_BUY:
         return (closePrice - OpenPrice) * (UnitsOneLot * TradeVolume) - Commission; //--- Return Buy profit
      case OP_SELL:
         return (OpenPrice - closePrice) * (UnitsOneLot * TradeVolume) - Commission; //--- Return Sell profit
      }
      return 0;                                              //--- Return 0 for invalid type
   }

   //--- Calculate profit as equity percentage
   double CalculateProfitEquityPercentage() {
      double closePrice = GetClosePrice();                   //--- Get current close price
      switch (Type) {
      case OP_BUY:
         return 100 * ((closePrice - OpenPrice) * (UnitsOneLot * TradeVolume) - Commission) / AccountEquity_LibFunc(); //--- Return Buy equity percentage
      case OP_SELL:
         return 100 * ((OpenPrice - closePrice) * (UnitsOneLot * TradeVolume) - Commission) / AccountEquity_LibFunc(); //--- Return Sell equity percentage
      }
      return 0;                                              //--- Return 0 for invalid type
   }

   //--- Calculate price difference in pips
   double CalculateValueDifferencePips(double value) {
      double divOpenPrice = 0.0;                             //--- Initialize price difference
      switch (Type) {
      case OP_BUY:
         divOpenPrice = (value - OpenPrice);                 //--- Calculate Buy difference
         break;
      case OP_SELL:
         divOpenPrice = (OpenPrice - value);                 //--- Calculate Sell difference
         break;
      }
      double pipsDivOpenPrice = divOpenPrice / PipPoint;     //--- Convert to pips
      return pipsDivOpenPrice;                               //--- Return difference in pips
   }

   //--- Retrieve realized profit in pips
   double GetProfitPips() {
      if (CloseTime > 0) {                                   //--- Check if order is closed
         switch (Type) {
         case ORDER_TYPE_BUY: {
            double pipettes = ClosePrice - OpenPrice;        //--- Calculate Buy pipettes
            return pipettes / PipPoint;                      //--- Return Buy profit in pips
         }
         case ORDER_TYPE_SELL: {
            double pipettes = OpenPrice - ClosePrice;        //--- Calculate Sell pipettes
            return pipettes / PipPoint;                      //--- Return Sell profit in pips
         }
         }
      }
      return 0;                                              //--- Return 0 if not closed
   }

   //--- Check if module has processed close info
   bool IsAlreadyProcessedByModule(string moduleCode, OrderCloseInfo* &closeInfos[]) {
      for (int i = 0; i < ArraySize(closeInfos); i++) {
         if (closeInfos[i].ModuleCode == moduleCode && closeInfos[i].IsOld) {
            return true;                                     //--- Return true if processed and old
         }
      }
      return false;                                          //--- Return false if not processed
   }

   //--- Check if module has active close info
   bool HasAValueAlreadyByModule(string moduleCode, OrderCloseInfo* &closeInfos[]) {
      for (int i = 0; i < ArraySize(closeInfos); i++) {
         if (closeInfos[i].ModuleCode == moduleCode && !closeInfos[i].IsOld) {
            return true;                                     //--- Return true if active
         }
      }
      return false;                                          //--- Return false if no active info
   }

   //--- Draw TP and SL lines on chart
   void Paint() {
      if (IsDemoLiveOrVisualMode) {                          //--- Check for demo/live/visual mode
         for (int i = 0; i < ArraySize(CloseInfosTP); i++) {
            if (CloseInfosTP[i].IsOld) continue;             //--- Skip outdated TP info
            PaintTPInfo(CloseInfosTP[i].Price);              //--- Draw TP line
         }
         for (int i = 0; i < ArraySize(CloseInfosSL); i++) {
            if (CloseInfosSL[i].IsOld) continue;             //--- Skip outdated SL info
            PaintSLInfo(CloseInfosSL[i].Price);              //--- Draw SL line
         }
      }
   }

   //--- Set Take Profit information
   bool SetTPInfo(string moduleCode, double price, int percentage) {
      uint result = SetCloseInfo(CloseInfosTP, moduleCode, price, percentage); //--- Update TP info
      if (result != NoAction) {
         if (IsDemoLiveOrVisualMode) {
            PaintTPInfo(price);                              //--- Draw TP line
         }
         return true;                                        //--- Return true on success
      }
      return false;                                          //--- Return false on no action
   }

   //--- Set Stop Loss information
   bool SetSLInfo(string moduleCode, double price, int percentage) {
      uint result = SetCloseInfo(CloseInfosSL, moduleCode, price, percentage); //--- Update SL info
      if (result != NoAction) {
         if (IsDemoLiveOrVisualMode) {
            PaintSLInfo(price);                              //--- Draw SL line
         }
         return true;                                        //--- Return true on success
      }
      return false;                                          //--- Return false on no action
   }

   //--- Retrieve closest Stop Loss price
   double GetClosestSL() {
      double closestSL = 0;                                  //--- Initialize closest SL
      for (int cli = 0; cli < ArraySize(CloseInfosSL); cli++) {
         if (CloseInfosSL[cli].IsOld) continue;              //--- Skip outdated SL
         if ((Type == ORDER_TYPE_BUY && (closestSL == 0 || CloseInfosSL[cli].Price > closestSL)) ||
             (Type == ORDER_TYPE_SELL && (closestSL == 0 || CloseInfosSL[cli].Price < closestSL))) {
            closestSL = CloseInfosSL[cli].Price;             //--- Update closest SL
         }
      }
      return closestSL;                                      //--- Return closest SL price
   }

   //--- Retrieve closest Take Profit price
   double GetClosestTP() {
      double closestTP = 0;                                  //--- Initialize closest TP
      for (int cli = 0; cli < ArraySize(CloseInfosTP); cli++) {
         if (CloseInfosTP[cli].IsOld) continue;              //--- Skip outdated TP
         if ((Type == ORDER_TYPE_BUY && (closestTP == 0 || CloseInfosTP[cli].Price < closestTP)) ||
             (Type == ORDER_TYPE_SELL && (closestTP == 0 || CloseInfosTP[cli].Price > closestTP))) {
            closestTP = CloseInfosTP[cli].Price;             //--- Update closest TP
         }
      }
      return closestTP;                                      //--- Return closest TP price
   }

   //--- Remove Stop Loss information
   bool RemoveSLInfo(string moduleCode) {
      RemoveCloseInfo(CloseInfosSL, moduleCode);             //--- Remove SL info
      if (IsDemoLiveOrVisualMode) {
         double newValue = NULL;                             //--- Initialize new SL value
         for (int i = 0; i < ArraySize(CloseInfosSL); i++) {
            if ((Type == OP_BUY && (newValue == NULL || CloseInfosSL[i].Price > newValue)) ||
                (Type == OP_SELL && (newValue == NULL || CloseInfosSL[i].Price < newValue))) {
               newValue = CloseInfosSL[i].Price;             //--- Update new SL value
            }
         }
         if (newValue == NULL) {
            AnyChartObjectDelete(ChartID(), IntegerToString(Ticket) + "_SL"); //--- Delete SL line
         } else {
            HLineMove(ChartID(), IntegerToString(Ticket) + "_SL", newValue); //--- Move SL line
         }
      }
      return true;                                           //--- Return true on success
   }

   //--- Remove Take Profit information
   bool RemoveTPInfo(string moduleCode) {
      RemoveCloseInfo(CloseInfosTP, moduleCode);             //--- Remove TP info
      if (IsDemoLiveOrVisualMode) {
         double newValue = NULL;                             //--- Initialize new TP value
         for (int i = 0; i < ArraySize(CloseInfosTP); i++) {
            if ((Type == OP_BUY && (newValue == NULL || CloseInfosTP[i].Price < newValue)) ||
                (Type == OP_SELL && (newValue == NULL || CloseInfosTP[i].Price > newValue))) {
               newValue = CloseInfosTP[i].Price;             //--- Update new TP value
            }
         }
         if (newValue == NULL) {
            AnyChartObjectDelete(ChartID(), IntegerToString(Ticket) + "_TP"); //--- Delete TP line
         } else {
            HLineMove(ChartID(), IntegerToString(Ticket) + "_TP", newValue); //--- Move TP line
         }
      }
      return true;                                           //--- Return true on success
   }

   //--- Set or update close info (TP or SL)
   CRUD SetCloseInfo(OrderCloseInfo* &closeInfos[], string moduleCode, double price, int percentage) {
      for (int i = 0; i < ArraySize(closeInfos); i++) {
         if (closeInfos[i].ModuleCode == moduleCode) {
            closeInfos[i].Price = price;                     //--- Update existing price
            return Updated;                                  //--- Return Updated status
         }
      }
      int newSize = ArraySize(closeInfos) + 1;              //--- Calculate new array size
      ArrayResize(closeInfos, newSize);                     //--- Resize close info array
      closeInfos[newSize-1] = new OrderCloseInfo();         //--- Create new close info
      closeInfos[newSize-1].Price = price;                  //--- Set price
      closeInfos[newSize-1].Percentage = percentage;        //--- Set percentage
      closeInfos[newSize-1].ModuleCode = moduleCode;        //--- Set module code
      return Created;                                       //--- Return Created status
   }

   //--- Remove close info (TP or SL)
   CRUD RemoveCloseInfo(OrderCloseInfo* &closeInfos[], string moduleCode) {
      int removedCount = 0;                                  //--- Track removed items
      int arraySize = ArraySize(closeInfos);                 //--- Get current array size
      for (int i = 0; i < arraySize; i++) {
         if (closeInfos[i].ModuleCode == moduleCode) {
            removedCount++;                                  //--- Increment removed count
            if (closeInfos[i] != NULL && CheckPointer(closeInfos[i]) == POINTER_DYNAMIC) {
               delete(closeInfos[i]);                        //--- Delete dynamic close info
            }
            continue;                                        //--- Skip to next item
         }
         closeInfos[i - removedCount] = closeInfos[i];       //--- Shift remaining items
      }
      ArrayResize(closeInfos, arraySize - removedCount);     //--- Resize array
      return Deleted;                                       //--- Return Deleted status
   }

   //--- Destructor for order cleanup
   void ~Order() {
      for (int i = 0; i < ArraySize(CloseInfosTP); i++) {
         if (CloseInfosTP[i] != NULL && CheckPointer(CloseInfosTP[i]) == POINTER_DYNAMIC) {
            delete(CloseInfosTP[i]);                         //--- Delete dynamic TP info
         }
      }
      for (int i = 0; i < ArraySize(CloseInfosSL); i++) {
         if (CloseInfosSL[i] != NULL && CheckPointer(CloseInfosSL[i]) == POINTER_DYNAMIC) {
            delete(CloseInfosSL[i]);                         //--- Delete dynamic SL info
         }
      }
      if (IsDemoLiveOrVisualMode && MustBeVisibleOnChart) {
         AnyChartObjectDelete(ChartID(), IntegerToString(Ticket) + "_TP"); //--- Delete TP line
         AnyChartObjectDelete(ChartID(), IntegerToString(Ticket) + "_SL"); //--- Delete SL line
      }
   }

private:
   //--- Retrieve close price for profit calculation
   double GetClosePrice() {
      if (ClosePrice > 1e-5) {
         return ClosePrice;                                  //--- Return stored close price if set
      } else if (Type == OP_BUY) {
         return SymbolInfoDouble(SymbolCode, SYMBOL_BID);    //--- Return Bid for Buy orders
      }
      return SymbolInfoDouble(SymbolCode, SYMBOL_ASK);       //--- Return Ask for Sell orders
   }

   //--- Calculate volume for partial close
   double CalcVolumePartialClose(double orderVolume, int percentage) {
      return RoundVolume(orderVolume * ((double)percentage / 100)); //--- Return rounded volume
   }

   //--- Round volume to broker specifications
   double RoundVolume(double volume) {
      string pair = Symbol();                                //--- Get current symbol
      double lotStep = MarketInfo_LibFunc(pair, MODE_LOTSTEP); //--- Get lot step
      double minLot = MarketInfo_LibFunc(pair, MODE_MINLOT); //--- Get minimum lot
      volume = MathRound(volume / lotStep) * lotStep;        //--- Round volume to lot step
      if (volume < minLot) volume = minLot;                  //--- Enforce minimum lot
      return volume;                                         //--- Return rounded volume
   }

   //--- Draw Stop Loss line on chart
   void PaintSLInfo(double value) {
      double currentValue;                                   //--- Declare current value
      if (ObjectGetDouble(ChartID(), IntegerToString(Ticket) + "_SL", OBJPROP_PRICE, 0, currentValue)) {
         if (Type == OP_BUY && value > currentValue) {
            HLineMove(ChartID(), IntegerToString(Ticket) + "_SL", value); //--- Move SL line for Buy
         } else if (Type == OP_SELL && value < currentValue) {
            HLineMove(ChartID(), IntegerToString(Ticket) + "_SL", value); //--- Move SL line for Sell
         }
      } else {
         HLineCreate(ChartID(), IntegerToString(Ticket) + "_SL", 0, value, clrRed); //--- Create red SL line
      }
   }

   //--- Draw Take Profit line on chart
   void PaintTPInfo(double value) {
      double currentValue;                                   //--- Declare current value
      if (ObjectGetDouble(ChartID(), IntegerToString(Ticket) + "_TP", OBJPROP_PRICE, 0, currentValue)) {
         if (Type == OP_BUY && value < currentValue) {
            HLineMove(ChartID(), IntegerToString(Ticket) + "_TP", value); //--- Move TP line for Buy
         } else if (Type == OP_SELL && value > currentValue) {
            HLineMove(ChartID(), IntegerToString(Ticket) + "_TP", value); //--- Move TP line for Sell
         }
      } else {
         HLineCreate(ChartID(), IntegerToString(Ticket) + "_TP", 0, value, clrGreen); //--- Create green TP line
      }
   }
};

Um die Kernlogik der Provisionsabwicklung und des Ordermanagements zu etablieren, definieren wir Provisionsvariablen wie „CommissionAmountPerTrade“ (0.0), „CommissionPercentagePerLot“ (0.0), „CommissionAmountPerLot“ (0.0), „TotalCommission“ (0.0), um die Handelskosten zu verfolgen, und „UseCommissionInProfitInPips“ (false), um Provisionen von Pip-Berechnungen auszuschließen und so eine genaue Gewinnverfolgung sicherzustellen.

Wir erstellen die Klasse „OrderCloseInfo“ zur Verwaltung von Handelsabschlussdetails mit den Variablen „ModuleCode“ für die Modulidentifikation, „Price“ für TP/SL-Levels, „Percentage“ für Teilabschlüsse und „IsOld“ zur Kennzeichnung veralteter Daten. Zu den Methoden gehören „IsClosePriceSLHit“ und „IsClosePriceTPHit“, um zu prüfen, ob Stop-Loss- oder Take-Profit-Levels für ORDER_TYPE_BUY- oder „ORDER_TYPE_SELL“-Aufträge erreicht werden, wobei „Ask“- und „Bid“-Kurse verwendet werden.

Anschließend definieren wir die Klasse „Order“, um Auftragsdetails zu kapseln, einschließlich Variablen wie „Ticket“, „Type“ (ENUM_ORDER_TYPE), „Status“ (ENUM_ORDER_STATE), „MagicNumber“, „Lots“, „OpenPrice“, „StopLoss“, „TakeProfit“ und „Commission“. Zu den wichtigsten Methoden gehören „Order“-Konstruktoren für die Initialisierung, „SplitOrder“ zur Behandlung von Teilabschlüssen, „CalculateProfitPips“ und „CalculateProfitCurrency“ für Gewinnberechnungen mit „PipPoint“ und „UnitsOneLot“ sowie „SetTPInfo“ und „SetSLInfo“ zur Aktualisierung der Arrays „CloseInfosTP“ und „CloseInfosSL“.

Die Methode „Paint“ zeichnet TP/SL-Linien mit „PaintTPInfo“ und „PaintSLInfo“, während „GetClosestSL“ und „GetClosestTP“ die nächstgelegenen Stop-Loss- und Take-Profit-Kurse abrufen. Die Methoden „SetCloseInfo“ und „RemoveCloseInfo“, die den Status „CRUD“ zurückgeben, verwalten TP/SL-Aktualisierungen, und private Methoden wie „GetClosePrice“ und „RoundVolume“ gewährleisten eine genaue Preis- und Volumenverarbeitung. Diese Strukturen unterstützen eine robuste Auftragsverwaltung für Scalping-Signale. Definieren wir also eine Funktion, um die gesammelten Aufträge zu sammeln und zu gruppieren, wie unten dargestellt.

//--- Define class for managing a collection of orders
class OrderCollection {
private:
   Order* _orders[];                                      //--- Store array of order pointers
   int _pointer;                                          //--- Track current iteration index
   int _size;                                             //--- Track number of orders

public:
   //--- Initialize empty order collection
   void OrderCollection() {
      _pointer = -1;                                      //--- Set initial pointer to -1
      _size = 0;                                          //--- Set initial size to 0
   }

   //--- Destructor to clean up orders
   void ~OrderCollection() {
      for (int i = 0; i < ArraySize(_orders); i++) {
         delete(_orders[i]);                              //--- Delete each order object
      }
   }

   //--- Add order to collection
   void Add(Order* item) {
      _size = _size + 1;                                  //--- Increment size
      ArrayResize(_orders, _size, 8);                     //--- Resize array with reserve capacity
      _orders[(_size - 1)] = item;                        //--- Store order at last index
   }

   //--- Remove order at specified index
   Order* Remove(int index) {
      Order* removed = NULL;                              //--- Initialize removed order as null
      if (index >= 0 && index < _size) {                  //--- Check valid index
         removed = _orders[index];                        //--- Store order to be removed
         for (int i = index; i < (_size - 1); i++) {
            _orders[i] = _orders[i + 1];                  //--- Shift orders left
         }
         ArrayResize(_orders, ArraySize(_orders) - 1, 8); //--- Reduce array size
         _size = _size - 1;                               //--- Decrement size
      }
      return removed;                                     //--- Return removed order or null
   }

   //--- Retrieve order at specified index
   Order* Get(int index) {
      if (index >= 0 && index < _size) {                  //--- Check valid index
         return _orders[index];                           //--- Return order at index
      }
      return NULL;                                        //--- Return null for invalid index
   }

   //--- Retrieve number of orders
   int Count() {
      return _size;                                       //--- Return current size
   }

   //--- Reset iterator to start
   void Rewind() {
      _pointer = -1;                                      //--- Set pointer to -1
   }

   //--- Move to next order
   Order* Next() {
      _pointer++;                                         //--- Increment pointer
      if (_pointer == _size) {                            //--- Check if at end
         Rewind();                                        //--- Reset pointer
         return NULL;                                     //--- Return null
      }
      return Current();                                   //--- Return current order
   }

   //--- Move to previous order
   Order* Prev() {
      _pointer--;                                         //--- Decrement pointer
      if (_pointer == -1) {                               //--- Check if before start
         return NULL;                                     //--- Return null
      }
      return Current();                                   //--- Return current order
   }

   //--- Check if more orders exist
   bool HasNext() {
      return (_pointer < (_size - 1));                    //--- Return true if pointer is before end
   }

   //--- Retrieve current order
   Order* Current() {
      return _orders[_pointer];                           //--- Return order at current pointer
   }

   //--- Retrieve current iterator index
   int Key() {
      return _pointer;                                    //--- Return current pointer
   }

   //--- Find index by order ticket
   int GetKeyByTicket(ulong ticket) {
      int keyFound = -1;                                  //--- Initialize found index as -1
      for (int i = 0; i < ArraySize(_orders); i++) {
         if (_orders[i].Ticket == ticket) {               //--- Check ticket match
            keyFound = i;                                 //--- Set found index
         }
      }
      return keyFound;                                    //--- Return found index or -1
   }
};

//--- Define class for managing order operations with broker
class OrderRepository {
private:
   //--- Retrieve order by ticket
   static Order* getByTicket(ulong ticket) {
      bool orderSelected = OrderSelect(ticket);           //--- Select order by ticket
      if (orderSelected) {                                //--- Check if selection succeeded
         Order* order = new Order(false);                 //--- Create new order object
         OrderRepository::fetchSelected(order);           //--- Populate order details
         return order;                                    //--- Return order object
      } else {
         return NULL;                                     //--- Return null if selection failed
      }
   }

   //--- Retrieve close time for historical order
   static datetime OrderCloseTime(ulong ticket) {
      return (datetime)(HistoryOrderGetInteger(ticket, ORDER_TIME_DONE_MSC) / 1000); //--- Return close time in seconds
   }

   //--- Retrieve close price for historical order
   static double OrderClosePrice(ulong ticket) {
      return HistoryOrderGetDouble(ticket, ORDER_PRICE_CURRENT); //--- Return close price
   }

   //--- Populate order details from selected order
   static void fetchSelected(Order& order) {
      COrderInfo orderInfo;                               //--- Declare order info object
      order.Ticket = orderInfo.Ticket();                  //--- Set order ticket
      order.Type = orderInfo.OrderType();                 //--- Set order type
      order.State = orderInfo.State();                    //--- Set order state
      order.MagicNumber = orderInfo.Magic();              //--- Set magic number
      order.Lots = orderInfo.VolumeInitial();             //--- Set initial volume
      order.OpenPrice = orderInfo.PriceOpen();            //--- Set open price
      order.StopLoss = orderInfo.StopLoss();              //--- Set Stop Loss
      order.TakeProfit = orderInfo.TakeProfit();          //--- Set Take Profit
      order.Expiration = orderInfo.TimeExpiration();      //--- Set expiration time
      order.Comment = orderInfo.Comment();                //--- Set comment
      order.OpenTime = orderInfo.TimeSetup();             //--- Set open time
      order.CloseTime = OrderCloseTime(order.Ticket); //--- Set close time
      order.SymbolCode = orderInfo.Symbol();              //--- Set symbol code
      order.TradeVolume = orderInfo.VolumeInitial();      //--- Set trade volume
      CalculateAndSetCommision(order);                    //--- Calculate and set commission
   }

   //--- Modify order’s Stop Loss or Take Profit
   static bool modify(ulong ticket, double stopLoss = NULL, double takeProfit = NULL) {
      CTrade trade;                                       //--- Declare trade object
      Order* order = OrderRepository::getByTicket(ticket); //--- Retrieve order by ticket
      double price = order.OpenPrice;                     //--- Set price to open price
      stopLoss = (stopLoss == NULL) ? order.StopLoss : stopLoss; //--- Use existing SL if null
      takeProfit = (takeProfit == NULL) ? order.TakeProfit : takeProfit; //--- Use existing TP if null
      datetime expiration = order.Expiration;             //--- Set expiration
      bool result = false;                                //--- Initialize result as false
      if (order.State == ORDER_STATE_PLACED) {            //--- Check if order is pending
         result = trade.OrderModify(ticket, price, stopLoss, takeProfit, ORDER_TIME_SPECIFIED, expiration, 0); //--- Modify pending order
      } else if (order.State == ORDER_STATE_FILLED) {     //--- Check if order is filled
         result = trade.PositionModify(ticket, stopLoss, takeProfit); //--- Modify position
      }
      if (CheckPointer(order) == POINTER_DYNAMIC) {        //--- Check if order is dynamic
         delete(order);                                   //--- Delete order object
      }
      return result;                                      //--- Return modification result
   }

public:
   //--- Retrieve open and pending orders
   static OrderCollection* GetOpenOrders(int magic = NULL, int type = NULL, string symbolCode = NULL) {
      OrderCollection* orders = new OrderCollection();    //--- Create new order collection
      //--- Process pending orders
      for (int orderIndex = 0; orderIndex < OrdersTotal(); orderIndex++) {
         bool orderSelected = OrderSelect(OrderGetTicket(orderIndex)); //--- Select order by index
         if (orderSelected) {                             //--- Check if selection succeeded
            Order* order = new Order(false);              //--- Create new order object
            OrderRepository::fetchSelected(order);        //--- Populate order details
            if ((magic == NULL || magic == order.MagicNumber) &&
                (type == NULL || type == order.Type) &&
                (symbolCode == NULL || symbolCode == order.SymbolCode)) { //--- Filter by magic, type, symbol
               orders.Add(order);                         //--- Add order to collection
            } else {
               if (CheckPointer(order) == POINTER_DYNAMIC) {
                  delete(order);                          //--- Delete unused order object
               }
            }
         }
      }
      //--- Process open positions (netting system)
      int total = PositionsTotal();                       //--- Get total positions
      for (int i = total - 1; i >= 0; i--) {
         ulong position_ticket = PositionGetTicket(i);    //--- Get position ticket
         string position_symbol = PositionGetString(POSITION_SYMBOL); //--- Get position symbol
         long position_magicNumber = PositionGetInteger(POSITION_MAGIC); //--- Get position magic number
         double volume = PositionGetDouble(POSITION_VOLUME); //--- Get position volume
         double open_price = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get open price
         datetime open_time = (datetime)PositionGetInteger(POSITION_TIME); //--- Get open time
         ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get position type
         if (position_magicNumber == MagicNumber) {       //--- Check matching magic number
            Order* order = new Order(false);              //--- Create new order object
            order.Ticket = position_ticket;               //--- Set order ticket
            if (positionType == POSITION_TYPE_BUY) {      //--- Check if Buy position
               order.Type = ORDER_TYPE_BUY;               //--- Set Buy type
            } else if (positionType == POSITION_TYPE_SELL) { //--- Check if Sell position
               order.Type = ORDER_TYPE_SELL;              //--- Set Sell type
            }
            order.Lots = volume;                          //--- Set order volume
            order.TradeVolume = volume;                   //--- Set trade volume
            order.OpenPrice = open_price;                 //--- Set open price
            order.OpenTime = open_time;                   //--- Set open time
            order.MagicNumber = position_magicNumber;     //--- Set magic number
            order.SymbolCode = position_symbol;           //--- Set symbol code
            if ((magic == NULL || magic == order.MagicNumber) &&
                (type == NULL || type == order.Type) &&
                (symbolCode == NULL || symbolCode == order.SymbolCode)) { //--- Filter by magic, type, symbol
               orders.Add(order);                         //--- Add order to collection
            } else {
               if (CheckPointer(order) == POINTER_DYNAMIC) {
                  order.Ticket = -1;                      //--- Invalidate ticket
                  delete(order);                          //--- Delete unused order object
               }
            }
         }
      }
      return orders;                                      //--- Return order collection
   }

   //--- Execute Buy order
   static ulong ExecuteOpenBuy(Order* order) {
      ulong orderTicket = ULONG_MAX;                      //--- Initialize ticket as invalid
      MqlTradeRequest request = {};                       //--- Declare trade request
      MqlTradeResult result = {};                         //--- Declare trade result
      request.action = TRADE_ACTION_DEAL;                 //--- Set action to deal
      request.symbol = Symbol();                          //--- Set symbol to current
      request.volume = order.Lots;                        //--- Set volume
      request.type = ORDER_TYPE_BUY;                      //--- Set Buy type
      request.price = Ask_LibFunc();                      //--- Set price to Ask
      request.deviation = MaxDeviationSlippage;           //--- Set maximum slippage
      request.magic = MagicNumber;                        //--- Set magic number
      request.comment = order.Comment;                    //--- Set order comment
      request.type_filling = (ENUM_ORDER_TYPE_FILLING)OrderFillingType; //--- Set filling type
      ResetLastError();                                   //--- Clear last error
      if (OrderSend(request, result)) {                   //--- Send trade request
         if (result.retcode == TRADE_RETCODE_DONE || result.retcode == TRADE_RETCODE_PLACED) { //--- Check success
            orderTicket = result.order;                   //--- Store order ticket
            order.Ticket = orderTicket;                   //--- Update order ticket
            order.IsAwaitingDealExecution = true;         //--- Flag awaiting execution
         } else {
            Print(StringFormat("OrderSend: retcode=%u", result.retcode)); //--- Log return code
         }
      } else {
         Print(StringFormat("OrderSend: error %d: %s", GetLastError(), GetErrorDescription(result.retcode))); //--- Log error
      }
      return orderTicket;                                 //--- Return order ticket
   }

   //--- Execute Sell order
   static ulong ExecuteOpenSell(Order* order) {
      ulong orderTicket = ULONG_MAX;                      //--- Initialize ticket as invalid
      MqlTradeRequest request = {};                       //--- Declare trade request
      MqlTradeResult result = {};                         //--- Declare trade result
      request.action = TRADE_ACTION_DEAL;                 //--- Set action to deal
      request.symbol = Symbol();                          //--- Set symbol to current
      request.volume = order.Lots;                        //--- Set volume
      request.type = ORDER_TYPE_SELL;                     //--- Set Sell type
      request.price = Bid_LibFunc();                      //--- Set price to Bid
      request.deviation = MaxDeviationSlippage;           //--- Set maximum slippage
      request.magic = MagicNumber;                        //--- Set magic number
      request.comment = order.Comment;                    //--- Set order comment
      request.type_filling = (ENUM_ORDER_TYPE_FILLING)OrderFillingType; //--- Set filling type
      ResetLastError();                                   //--- Clear last error
      if (OrderSend(request, result)) {                   //--- Send trade request
         if (result.retcode == TRADE_RETCODE_DONE || result.retcode == TRADE_RETCODE_PLACED) { //--- Check success
            orderTicket = result.order;                   //--- Store order ticket
            order.Ticket = orderTicket;                   //--- Update order ticket
            order.IsAwaitingDealExecution = true;         //--- Flag awaiting execution
         } else {
            Print(StringFormat("OrderSend: retcode=%u", result.retcode)); //--- Log return code
         }
      } else {
         Print(StringFormat("OrderSend: error %d: %s", GetLastError(), GetErrorDescription(result.retcode))); //--- Log error
      }
      return orderTicket;                                 //--- Return order ticket
   }

   //--- Close position (hedging accounts only)
   static bool ClosePosition(Order* order) {
      CPositionInfo m_position;                           //--- Declare position info object
      CTrade m_trade;                                     //--- Declare trade object
      bool foundPosition = false;                         //--- Initialize position found flag
      for (int i = PositionsTotal() - 1; i >= 0; i--) {   //--- Iterate positions
         if (m_position.SelectByIndex(i)) {               //--- Select position by index
            if (m_position.Ticket() == order.Ticket) {    //--- Check matching ticket
               foundPosition = true;                      //--- Set position found
               uint returnCode = 0;                       //--- Initialize return code
               if (m_trade.PositionClosePartial(order.Ticket, NormalizeDouble(order.Lots, 2), MaxDeviationSlippage)) { //--- Attempt partial close
                  returnCode = m_trade.ResultRetcode();   //--- Get return code
                  if (returnCode == TRADE_RETCODE_DONE || returnCode == TRADE_RETCODE_PLACED) { //--- Check success
                     ulong orderTicket = m_trade.ResultOrder(); //--- Get new order ticket
                     order.Ticket = orderTicket;          //--- Update order ticket
                     order.IsAwaitingDealExecution = true; //--- Flag awaiting execution
                     Print(StringFormat("Successfully created a close order (%d) by EA (%d). Awaiting execution.", orderTicket, MagicNumber)); //--- Log success
                     return true;                         //--- Return true
                  }
                  Print(StringFormat("Placing close order failed, Return code: %d", returnCode)); //--- Log failure
               }
            }
         }
      }
      return false;                                       //--- Return false if position not found
   }

   //--- Retrieve recently closed orders
   static OrderCollection* GetLastClosedOrders(datetime startDatetime = NULL) {
      OrderCollection* lastClosedOrders = new OrderCollection(); //--- Create new order collection
      long positionIds[];                                 //--- Store position IDs
      if (HistorySelect(0, TimeCurrent())) {              //--- Select trade history
         for (int i = HistoryDealsTotal() - 1; i >= 0; i--) { //--- Iterate deals
            ulong dealId = HistoryDealGetTicket(i);       //--- Get deal ticket
            long magicNumber = HistoryDealGetInteger(dealId, DEAL_MAGIC); //--- Get deal magic number
            string symbol = HistoryDealGetString(dealId, DEAL_SYMBOL); //--- Get deal symbol
            if ((magicNumber != MagicNumber && magicNumber != 0) || symbol != Symbol()) { //--- Filter by magic and symbol
               continue;                                  //--- Skip non-matching deals
            }
            if (HistoryDealGetInteger(dealId, DEAL_ENTRY) == DEAL_ENTRY_OUT) { //--- Check if deal is close
               datetime closetime = (datetime)HistoryDealGetInteger(dealId, DEAL_TIME); //--- Get close time
               if (startDatetime > closetime) {           //--- Check if before start time
                  break;                                  //--- Exit loop
               }
               long positionId = HistoryDealGetInteger(dealId, DEAL_POSITION_ID); //--- Get position ID
               for (int pi = 0; pi < ArraySize(positionIds); pi++) { //--- Check existing IDs
                  if (positionIds[pi] == positionId) {    //--- Skip duplicates
                     continue;
                  }
               }
               int size = ArraySize(positionIds);         //--- Get current ID array size
               ArrayResize(positionIds, size + 1);        //--- Add new ID
               positionIds[size] = positionId;            //--- Store position ID
            }
         }
      }
      for (int i = 0; i < ArraySize(positionIds); i++) {  //--- Process each position
         if (HistorySelectByPosition(positionIds[i])) {    //--- Select position history
            Order* order = new Order(false);              //--- Create new order object
            double currentOutVolume = 0;                  //--- Track closed volume
            for (int j = 0; j < HistoryDealsTotal(); j++) { //--- Iterate deals
               ulong ticket = HistoryDealGetTicket(j);    //--- Get deal ticket
               if (HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_IN) { //--- Check if open deal
                  datetime openTime = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME); //--- Get open time
                  double openPrice = HistoryDealGetDouble(ticket, DEAL_PRICE); //--- Get open price
                  double lots = HistoryDealGetDouble(ticket, DEAL_VOLUME); //--- Get volume
                  if (order.Ticket == 0) {                //--- Check if first deal
                     order.Ticket = HistoryDealGetInteger(ticket, DEAL_ORDER); //--- Set order ticket
                     long dealType = HistoryDealGetInteger(ticket, DEAL_TYPE); //--- Get deal type
                     if (dealType == ORDER_TYPE_BUY) {    //--- Check if Buy
                        order.Type = ORDER_TYPE_SELL;     //--- Set Sell type (reversed for close)
                     } else if (dealType == ORDER_TYPE_SELL) { //--- Check if Sell
                        order.Type = ORDER_TYPE_BUY;      //--- Set Buy type (reversed for close)
                     } else {
                        Alert("Unknown order.Type in GetLastClosedOrder"); //--- Log unknown type
                     }
                     order.OpenTime = openTime;           //--- Set open time
                     order.OpenPrice = openPrice;         //--- Set open price
                     order.Lots = lots;                   //--- Set volume
                  } else {
                     double averagePrice = ((order.OpenPrice * order.Lots) + (openPrice * lots)) / (order.Lots + lots); //--- Calculate average price
                     order.Lots = order.Lots + lots;      //--- Add volume
                     order.OpenPrice = averagePrice;      //--- Update open price
                  }
               } else if (HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT) { //--- Check if close deal
                  double dealLots = HistoryDealGetDouble(ticket, DEAL_VOLUME); //--- Get close volume
                  double dealClosePrice = HistoryDealGetDouble(ticket, DEAL_PRICE); //--- Get close price
                  if (order.CloseTime == 0) {          //--- Check if first close
                     order.CloseTime = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME); //--- Set close time
                     order.ClosePrice = dealClosePrice; //--- Set close price
                     currentOutVolume = dealLots;       //--- Set initial close volume
                  } else {
                     double averagePrice = ((order.ClosePrice * currentOutVolume) + (dealClosePrice * dealLots)) / (currentOutVolume + dealLots); //--- Calculate average close price
                     order.CloseTime = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME); //--- Update close time
                     order.ClosePrice = averagePrice;   //--- Update close price
                     currentOutVolume += dealLots;      //--- Add close volume
                  }
               }
            }
            lastClosedOrders.Add(order);                 //--- Add order to collection
         }
      }
      return lastClosedOrders;                         //--- Return closed orders collection
   }

   //--- Open order (Buy or Sell)
   static bool OpenOrder(Order* order) {
      double price = NULL;                                //--- Initialize price
      ulong ticketId = -1;                                //--- Initialize ticket
      switch (order.Type) {
      case ORDER_TYPE_BUY:
         ticketId = ExecuteOpenBuy(order);                //--- Execute Buy order
         break;
      case ORDER_TYPE_SELL:
         ticketId = ExecuteOpenSell(order);               //--- Execute Sell order
         break;
      }
      bool success = ticketId != ULONG_MAX;               //--- Check if order was opened
      if (success) {
         Print(StringFormat("Successfully opened an order (%d) by EA (%d)", ticketId, MagicNumber)); //--- Log success
      }
      return success;                                     //--- Return true if successful
   }

   //--- Calculate and set commission for order
   static void CalculateAndSetCommision(Order& order) {
      order.Commission = 0.0;                             //--- Initialize commission
      order.CommissionInPips = 0.0;                       //--- Initialize commission in pips
      order.Commission = 2.0 * CommissionAmountPerTrade + //--- Add roundtrip fixed commission
                         CommissionPercentagePerLot * order.Lots * UnitsOneLot + //--- Add percentage commission
                         CommissionAmountPerLot * order.Lots; //--- Add per-lot commission
      if (order.Lots > 1.0e-5 && order.Commission > 1.0e-5) { //--- Check valid volume and commission
         order.CommissionInPips = order.Commission / (order.Lots * UnitsOneLot * PipPoint); //--- Calculate commission in pips
      }
   }
};

Wir definieren die Klasse „OrderCollection“, um eine Sammlung von „Order“-Objekten zu verwalten, wobei wir das Array „_orders“, „_pointer“ für die Iteration und „_size“ für die Anzahl der Bestellungen verwenden. Zu den Methoden gehören „OrderCollection“ für die Initialisierung, „Add“ zum Hinzufügen von Bestellungen, „Remove“ zum Löschen von Bestellungen bei einem Index, „Get“ zum Abrufen von Bestellungen, „Count“ für die Größe und Iteratormethoden wie „Rewind“, „Next“, „Prev“, „HasNext“, „Current“, „Key“ und „GetKeyByTicket“ zum Navigieren und Auffinden von Aufträgen nach Ticket.

Außerdem erstellen wir die Klasse „OrderRepository“, um die Interaktionen mit Brokern zu handhaben, mit privaten Methoden wie „getByTicket“ zum Abrufen von Aufträgen über OrderSelect, „OrderCloseTime“ und „OrderClosePrice“ um historische Auftragsdaten abzurufen, „fetchSelected“, um „Order“-Details über „COrderInfo“ aufzufüllen, und „modify“, um Stop-Loss/Take-Profit über „CTrade“ zu aktualisieren. Zu den öffentlichen Methoden gehört „GetOpenOrders“ zur Erfassung offener und schwebender Aufträge, gefiltert nach „MagicNumber“, „Type“ oder „SymbolCode“, wobei sowohl schwebende Aufträge als auch Netting-Positionen behandelt werden.

Die Funktionen „ExecuteOpenBuy“ und „ExecuteOpenSell“ senden Handelsanfragen mit MqlTradeRequest und MqlTradeResult und setzen „ORDER_TYPE_BUY“ oder ORDER_TYPE_SELL, „Ask_LibFunc“ oder „Bid_LibFunc“ Preise und „OrderFillingType“, während „ClosePosition“ Absicherungspositionen mit „CPositionInfo“ und „CTrade“ schließt. Die Funktion „GetLastClosedOrders“ ruft die zuletzt geschlossenen Aufträge ab, indem sie die Geschäftshistorie mit HistorySelect analysiert, und „CalculateAndSetCommission“ berechnet die Provisionen mit „CommissionAmountPerTrade“, „CommissionPercentagePerLot“ und „CommissionAmountPerLot“. Als Nächstes können wir die Aufträge gruppieren und sie effizient hacken.

//--- Define class for grouping order tickets
class OrderGroupData {
public:
   ulong OrderTicketIds[];                                //--- Store array of order ticket IDs

   //--- Default constructor
   void OrderGroupData() {}                               //--- Initialize empty group

   //--- Copy constructor
   void OrderGroupData(OrderGroupData* ordergroupdata) {} //--- Initialize from existing group (empty)

   //--- Add ticket to group
   void Add(ulong ticketId) {
      int size = ArraySize(OrderTicketIds);               //--- Get current array size
      ArrayResize(OrderTicketIds, size + 1);              //--- Resize array
      OrderTicketIds[size] = ticketId;                    //--- Store ticket at last index
   }

   //--- Remove ticket from group
   void Remove(ulong ticketId) {
      int size = ArraySize(OrderTicketIds);               //--- Get current array size
      int counter = 0;                                    //--- Track new array position
      int counterFound = 0;                               //--- Track removed tickets
      for (int i = 0; i < size; i++) {
         if (OrderTicketIds[i] == ticketId) {             //--- Check matching ticket
            counterFound++;                               //--- Increment found count
            continue;                                     //--- Skip to next
         } else {
            OrderTicketIds[counter] = OrderTicketIds[i];  //--- Shift ticket
            counter++;                                    //--- Increment new position
         }
      }
      if (counterFound > 0) {                             //--- Check if tickets were removed
         ArrayResize(OrderTicketIds, counter);            //--- Resize array to new size
      }
   }

   //--- Destructor
   void ~OrderGroupData() {}                              //--- Clean up group
};

//--- Define class for hash map entry
class OrderGroupHashEntry {
public:
   string _key;                                           //--- Store entry key
   OrderGroupData* _val;                                  //--- Store order group data
   OrderGroupHashEntry* _next;                            //--- Point to next entry

   //--- Default constructor
   OrderGroupHashEntry() {
      _key = NULL;                                        //--- Set key to null
      _val = NULL;                                        //--- Set value to null
      _next = NULL;                                       //--- Set next to null
   }

   //--- Constructor with key and value
   OrderGroupHashEntry(string key, OrderGroupData* val) {
      _key = key;                                         //--- Set key
      _val = val;                                         //--- Set value
      _next = NULL;                                       //--- Set next to null
   }

   //--- Destructor
   ~OrderGroupHashEntry() {
      if (_val != NULL && CheckPointer(_val) == POINTER_DYNAMIC) { //--- Check if value is dynamic
         delete(_val);                                    //--- Delete value
      }
   }
};

//--- Define class for hash map of order groups
class OrderGroupHashMap {
private:
   uint _hashSlots;                                       //--- Store number of hash slots
   int _resizeThreshold;                                  //--- Store resize threshold
   int _hashEntryCount;                                   //--- Track number of entries
   OrderGroupHashEntry* _buckets[];                       //--- Store hash buckets
   bool _adoptValues;                                     //--- Flag value adoption
   uint _foundIndex;                                      //--- Store found index
   OrderGroupHashEntry* _foundEntry;                      //--- Store found entry
   OrderGroupHashEntry* _foundPrev;                       //--- Store previous entry

   //--- Initialize hash map
   void init(uint size, bool adoptValues) {
      _hashSlots = 0;                                     //--- Set initial slots to 0
      _hashEntryCount = 0;                                //--- Set initial entry count to 0
      _adoptValues = adoptValues;                         //--- Set value adoption flag
      rehash(size);                                       //--- Resize hash map
   }

   //--- Calculate hash for key
   uint hash(string s) {
      uchar c[];                                          //--- Declare character array
      uint h = 0;                                         //--- Initialize hash
      if (s != NULL) {                                    //--- Check if key is valid
         h = 5381;                                        //--- Set initial hash value
         int n = StringToCharArray(s, c);                 //--- Convert string to chars
         for (int i = 0; i < n; i++) {
            h = ((h << 5) + h) + c[i];                    //--- Update hash
         }
      }
      return h % _hashSlots;                              //--- Return hash modulo slots
   }

   //--- Find entry by key
   bool find(string keyName) {
      bool found = false;                                 //--- Initialize found flag
      _foundPrev = NULL;                                  //--- Set previous to null
      _foundIndex = hash(keyName);                        //--- Calculate hash index
      if (_foundIndex <= _hashSlots) {                    //--- Check valid index
         for (OrderGroupHashEntry* e = _buckets[_foundIndex]; e != NULL; e = e._next) { //--- Iterate bucket
            if (e._key == keyName) {                      //--- Check key match
               _foundEntry = e;                           //--- Store found entry
               found = true;                              //--- Set found flag
               break;                                     //--- Exit loop
            }
            _foundPrev = e;                               //--- Update previous
         }
      }
      return found;                                       //--- Return found status
   }

   //--- Retrieve number of slots
   uint getSlots() {
      return _hashSlots;                                  //--- Return slot count
   }

   //--- Resize hash map
   bool rehash(uint newSize) {
      bool ret = false;                                   //--- Initialize return flag
      OrderGroupHashEntry* oldTable[];                    //--- Declare old table
      uint oldSize = _hashSlots;                          //--- Store current size
      if (newSize <= getSlots()) {                        //--- Check if resize is needed
         ret = false;                                     //--- Set failure
      } else if (ArrayResize(_buckets, newSize) != newSize) { //--- Resize buckets
         ret = false;                                     //--- Set failure
      } else if (ArrayResize(oldTable, oldSize) != oldSize) { //--- Resize old table
         ret = false;                                     //--- Set failure
      } else {
         uint i = 0;                                      //--- Initialize index
         for (i = 0; i < oldSize; i++) {                  //--- Copy buckets
            oldTable[i] = _buckets[i];                    //--- Store old bucket
         }
         for (i = 0; i < newSize; i++) {                  //--- Clear new buckets
            _buckets[i] = NULL;                           //--- Set to null
         }
         _hashSlots = newSize;                            //--- Update slot count
         _resizeThreshold = (int)_hashSlots / 4 * 3;      //--- Set resize threshold
         for (uint oldHashCode = 0; oldHashCode < oldSize; oldHashCode++) { //--- Rehash entries
            OrderGroupHashEntry* next = NULL;             //--- Initialize next
            for (OrderGroupHashEntry* e = oldTable[oldHashCode]; e != NULL; e = next) { //--- Iterate old bucket
               next = e._next;                            //--- Store next entry
               uint newHashCode = hash(e._key);           //--- Calculate new hash
               e._next = _buckets[newHashCode];           //--- Link to new bucket
               _buckets[newHashCode] = e;                 //--- Store in new bucket
            }
            oldTable[oldHashCode] = NULL;                 //--- Clear old bucket
         }
         ret = true;                                      //--- Set success
      }
      return ret;                                         //--- Return resize result
   }

public:
   //--- Default constructor
   OrderGroupHashMap() {
      init(13, false);                                    //--- Initialize with 13 slots, no adoption
   }

   //--- Constructor with adoption flag
   OrderGroupHashMap(bool adoptValues) {
      init(13, adoptValues);                              //--- Initialize with 13 slots
   }

   //--- Constructor with size
   OrderGroupHashMap(int size) {
      init(size, false);                                  //--- Initialize with specified size, no adoption
   }

   //--- Constructor with size and adoption
   OrderGroupHashMap(int size, bool adoptValues) {
      init(size, adoptValues);                            //--- Initialize with size and adoption
   }

   //--- Destructor
   ~OrderGroupHashMap() {
      for (uint i = 0; i < _hashSlots; i++) {            //--- Iterate buckets
         OrderGroupHashEntry* nextEntry = NULL;           //--- Initialize next
         for (OrderGroupHashEntry* entry = _buckets[i]; entry != NULL; entry = nextEntry) { //--- Iterate entries
            nextEntry = entry._next;                      //--- Store next entry
            if (_adoptValues && entry._val != NULL && CheckPointer(entry._val) == POINTER_DYNAMIC) { //--- Check if value is dynamic
               delete entry._val;                         //--- Delete value
            }
            delete entry;                                 //--- Delete entry
         }
         _buckets[i] = NULL;                              //--- Clear bucket
      }
   }

   //--- Check if key exists
   bool ContainsKey(string keyName) {
      return find(keyName);                               //--- Return true if key found
   }

   //--- Retrieve group data by key
   OrderGroupData* Get(string keyName) {
      OrderGroupData* obj = NULL;                         //--- Initialize return object
      if (find(keyName)) {                                //--- Check if key exists
         obj = _foundEntry._val;                          //--- Set return object
      }
      return obj;                                         //--- Return group data or null
   }

   //--- Retrieve all group data
   void GetAllData(OrderGroupData* &data[]) {
      for (uint i = 0; i < _hashSlots; i++) {            //--- Iterate buckets
         OrderGroupHashEntry* nextEntry = NULL;           //--- Initialize next
         for (OrderGroupHashEntry* entry = _buckets[i]; entry != NULL; entry = nextEntry) { //--- Iterate entries
            if (entry._val != NULL) {                     //--- Check valid value
               int size = ArraySize(data);                //--- Get current array size
               ArrayResize(data, size + 1);               //--- Resize array
               data[size] = entry._val;                   //--- Store value
               nextEntry = entry._next;                   //--- Move to next
            }
         }
      }
   }

   //--- Store or update group data
   OrderGroupData* Put(string keyName, OrderGroupData* obj) {
      OrderGroupData* ret = NULL;                         //--- Initialize return value
      if (find(keyName)) {                                //--- Check if key exists
         ret = _foundEntry._val;                          //--- Store existing value
         if (_adoptValues && _foundEntry._val != NULL && CheckPointer(_foundEntry._val) == POINTER_DYNAMIC) { //--- Check if value is dynamic
            delete _foundEntry._val;                      //--- Delete existing value
         }
         _foundEntry._val = obj;                          //--- Update value
      } else {
         OrderGroupHashEntry* e = new OrderGroupHashEntry(keyName, obj); //--- Create new entry
         OrderGroupHashEntry* first = _buckets[_foundIndex]; //--- Get current bucket head
         e._next = first;                                 //--- Link new entry
         _buckets[_foundIndex] = e;                       //--- Store new entry
         _hashEntryCount++;                               //--- Increment entry count
         if (_hashEntryCount > _resizeThreshold) {        //--- Check if resize needed
            rehash(_hashSlots / 2 * 3);                   //--- Resize hash map
         }
      }
      return ret;                                         //--- Return previous value or null
   }

   //--- Delete entry by key
   bool Delete(string keyName) {
      bool found = false;                                 //--- Initialize found flag
      if (find(keyName)) {                                //--- Check if key exists
         OrderGroupHashEntry* next = _foundEntry._next;   //--- Store next entry
         if (_foundPrev != NULL) {                        //--- Check if previous exists
            _foundPrev._next = next;                      //--- Update previous link
         } else {
            _buckets[_foundIndex] = next;                 //--- Update bucket head
         }
         if (_adoptValues && _foundEntry._val != NULL && CheckPointer(_foundEntry._val) == POINTER_DYNAMIC) { //--- Check if value is dynamic
            delete _foundEntry._val;                      //--- Delete value
         }
         delete _foundEntry;                              //--- Delete entry
         _hashEntryCount--;                               //--- Decrement entry count
         found = true;                                    //--- Set found flag
      }
      return found;                                       //--- Return true if deleted
   }

   //--- Delete multiple keys
   int DeleteKeys(const string& keys[]) {
      int count = 0;                                      //--- Initialize delete count
      for (int i = 0; i < ArraySize(keys); i++) {         //--- Iterate keys
         if (Delete(keys[i])) {                           //--- Attempt to delete key
            count++;                                      //--- Increment count
         }
      }
      return count;                                       //--- Return number of deleted keys
   }

   //--- Delete all keys except specified
   int DeleteKeysExcept(const string& keys[]) {
      int index = 0, count = 0;                           //--- Initialize index and count
      string hashedKeys[];                                //--- Declare hashed keys array
      ArrayResize(hashedKeys, _hashEntryCount);           //--- Resize to entry count
      for (uint i = 0; i < _hashSlots; i++) {            //--- Iterate buckets
         OrderGroupHashEntry* nextEntry = NULL;           //--- Initialize next
         for (OrderGroupHashEntry* entry = _buckets[i]; entry != NULL; entry = nextEntry) { //--- Iterate entries
            nextEntry = entry._next;                      //--- Store next
            if (entry._key != NULL) {                     //--- Check valid key
               hashedKeys[index] = entry._key;            //--- Store key
               index++;                                   //--- Increment index
            }
         }
      }
      for (int i = 0; i < ArraySize(hashedKeys); i++) {   //--- Iterate hashed keys
         bool keep = false;                               //--- Initialize keep flag
         for (int j = 0; j < ArraySize(keys); j++) {      //--- Check against keep keys
            if (hashedKeys[i] == keys[j]) {               //--- Check match
               keep = true;                               //--- Set keep flag
               break;                                     //--- Exit loop
            }
         }
         if (!keep) {                                     //--- Check if key should be deleted
            if (Delete(hashedKeys[i])) {                  //--- Attempt to delete
               count++;                                   //--- Increment count
            }
         }
      }
      return count;                                       //--- Return number of deleted keys
   }
};

Um Auftragstickets effizient zu verwalten und zu gruppieren und so eine organisierte Abwicklung des Handels zu unterstützen, definieren wir die Klasse „OrderGroupData“, um Auftragsticket-IDs im Array „OrderTicketIds“ zu speichern, mit Methoden wie „OrderGroupData“ für die Initialisierung, „Add“ zum Hinzufügen von Ticket-IDs, „Remove“ zum Löschen bestimmter Tickets durch Verschieben der verbleibenden Einträge und einen Destruktor „~OrderGroupData“ für die Bereinigung, um eine dynamische Ticketverwaltung zu gewährleisten.

Als Nächstes erstellen wir die Klasse „OrderGroupHashEntry“ zur Darstellung von Einträgen in einer Hash-Map, die „_key“ für den Eintragsbezeichner, „_val“ zur Aufnahme eines „OrderGroupData“-Objekts und „_next“ zur Verknüpfung von Einträgen im Falle von Kollisionen enthält. Seine Konstruktoren „OrderGroupHashEntry“ initialisieren die Einträge, und der Destruktor „~OrderGroupHashEntry“ gibt bei Bedarf dynamische „OrderGroupData“-Objekte frei.

Anschließend implementieren wir die Klasse „OrderGroupHashMap“, um Bestellgruppen mithilfe einer Hashtabelle zu verwalten, mit den privaten Variablen „_hashSlots“ für die Anzahl der Eimer, „_resizeThreshold“ für Auslöser zur Größenänderung, „_hashEntryCount“ für die Verfolgung von Einträgen und „_buckets“ zum Speichern von „OrderGroupHashEntry“-Arrays. Die private Methode „init“ richtet die Hash-Map ein, „hash“ berechnet einen Hash-Wert für die Schlüssel, „find“ findet Einträge nach Schlüssel und „rehash“ verändert die Größe der Tabelle.

Zu den öffentlichen Methoden gehören die Konstruktoren „OrderGroupHashMap“ für verschiedene Initialisierungsoptionen, „ContainsKey“ zur Überprüfung des Vorhandenseins von Schlüsseln, „Get“ zum Abrufen von „OrderGroupData“, „GetAllData“ zum Sammeln aller Gruppen, „Put“ zum Hinzufügen oder Aktualisieren von Einträgen, „Delete“ zum Entfernen eines Schlüssels, „DeleteKeys“ für Mehrfachlöschungen und „DeleteKeysExcept“ zum Entfernen aller Schlüssel außer den angegebenen. Der Destruktor „~OrderGroupHashMap“ sorgt für eine ordnungsgemäße Bereinigung. Wir müssen nun Handelszustände verwalten und benötigen dafür eine zusätzliche Klasse.

//--- Declare global array to track recent order results
int LastOrderResults[];                                   //--- Store outcomes of recent trades (1 for profit, 0 for loss)

//--- Define class for managing trading state and orders
class Wallet {
private:
   int _openedBuyOrderCount;                              //--- Track number of open Buy orders
   int _openedSellOrderCount;                             //--- Track number of open Sell orders
   ulong _closedOrderCount;                               //--- Track total closed orders
   int _lastOrderResultSize;                              //--- Store size of LastOrderResults array
   ENUM_TIMEFRAMES _lastOrderResultByTimeframe;           //--- Store timeframe for tracking closed orders
   datetime _lastBarStartTime;                            //--- Store start time of last bar
   OrderCollection* _openOrders;                          //--- Store currently open orders
   OrderGroupHashMap* _openOrdersSymbolType;              //--- Store open orders grouped by symbol and type
   OrderGroupHashMap* _openOrdersSymbol;                  //--- Store open orders grouped by symbol
   OrderCollection* _pendingOpenOrders;                   //--- Store pending open orders
   OrderCollection* _pendingCloseOrders;                  //--- Store pending close orders
   Order* _mostRecentOpenOrder;                           //--- Store most recently opened order
   Order* _mostRecentClosedOrder;                         //--- Store most recently closed order
   OrderCollection* _recentClosedOrders;                  //--- Store recently closed orders

public:
   //--- Initialize wallet
   void Wallet() {
      _openedBuyOrderCount = 0;                           //--- Set Buy order count to 0
      _openedSellOrderCount = 0;                          //--- Set Sell order count to 0
      _closedOrderCount = 0;                              //--- Set closed order count to 0
      _lastOrderResultSize = 0;                           //--- Set result size to 0
      _lastOrderResultByTimeframe = NULL;                 //--- Set timeframe to null
      _lastBarStartTime = NULL;                           //--- Set bar start time to null
      _pendingOpenOrders = new OrderCollection();         //--- Create pending open orders collection
      _pendingCloseOrders = new OrderCollection();        //--- Create pending close orders collection
      _recentClosedOrders = new OrderCollection();        //--- Create recent closed orders collection
      _openOrdersSymbolType = NULL;                       //--- Set symbol-type group to null
      _openOrdersSymbol = NULL;                           //--- Set symbol group to null
      _openOrders = new OrderCollection();                //--- Create open orders collection
      _mostRecentOpenOrder = NULL;                        //--- Set recent open order to null
      _mostRecentClosedOrder = NULL;                      //--- Set recent closed order to null
   }

   //--- Destructor to clean up wallet
   void ~Wallet() {
      delete(_pendingOpenOrders);                         //--- Delete pending open orders
      delete(_pendingCloseOrders);                        //--- Delete pending close orders
      delete(_recentClosedOrders);                        //--- Delete recent closed orders
      if (_openOrders != NULL) {                          //--- Check if open orders exist
         delete(_openOrders);                             //--- Delete open orders
      }
      if (_mostRecentOpenOrder != NULL) {                 //--- Check if recent open order exists
         delete(_mostRecentOpenOrder);                    //--- Delete recent open order
      }
      if (_mostRecentClosedOrder != NULL) {               //--- Check if recent closed order exists
         delete(_mostRecentClosedOrder);                  //--- Delete recent closed order
      }
      if (_openOrdersSymbolType != NULL) {                //--- Check if symbol-type group exists
         delete(_openOrdersSymbolType);                   //--- Delete symbol-type group
      }
      if (_openOrdersSymbol != NULL) {                    //--- Check if symbol group exists
         delete(_openOrdersSymbol);                       //--- Delete symbol group
      }
   }

   //--- Handle new tick event
   void HandleTick() {
      if (_lastOrderResultByTimeframe != NULL) {          //--- Check if timeframe is set
         datetime newBarStartTime = iTime(_Symbol, _lastOrderResultByTimeframe, 0); //--- Get current bar start
         if (_lastBarStartTime == newBarStartTime) {      //--- Check if same bar
            return;                                       //--- Exit if no new bar
         } else {
            _lastBarStartTime = newBarStartTime;          //--- Update bar start time
            for (int i = 0; i < _recentClosedOrders.Count(); i++) { //--- Iterate closed orders
               Order* order = _recentClosedOrders.Get(i); //--- Get closed order
               if (CheckPointer(order) != POINTER_INVALID && CheckPointer(order) == POINTER_DYNAMIC) { //--- Check dynamic pointer
                  delete(order);                             //--- Delete order
               }
               _recentClosedOrders.Remove(i);             //--- Remove order
            }
            PrintOrderChanges();                          //--- Log order changes
         }
      }
   }

   //--- Set size of order results array
   void SetLastOrderResultsSize(int size) {
      if (size > _lastOrderResultSize) {                  //--- Check if size increased
         ArrayResize(LastOrderResults, size);             //--- Resize results array
         ArrayInitialize(LastOrderResults, 1);            //--- Initialize with 1 (assume profit)
         _lastOrderResultSize = size;                     //--- Update result size
      }
   }

   //--- Set timeframe for tracking closed orders
   void SetLastClosedOrdersByTimeframe(ENUM_TIMEFRAMES timeframe) {
      if (_lastOrderResultByTimeframe != NULL && timeframe <= _lastOrderResultByTimeframe) { //--- Check if timeframe is valid
         return;                                          //--- Exit if no change needed
      }
      _lastOrderResultByTimeframe = timeframe;            //--- Set new timeframe
      _lastBarStartTime = iTime(_Symbol, _lastOrderResultByTimeframe, 0); //--- Set bar start time
   }

   //--- Retrieve recent closed orders
   OrderCollection* GetRecentClosedOrders() {
      return _recentClosedOrders;                         //--- Return closed orders collection
   }

   //--- Activate order grouping types
   void ActivateOrderGroups(ORDER_GROUP_TYPE &groupTypes[]) {
      for (int i = 0; i < ArrayRange(groupTypes, 0); i++) { //--- Iterate group types
         if (groupTypes[i] == SymbolOrderType && _openOrdersSymbolType == NULL) { //--- Check symbol-type grouping
            _openOrdersSymbolType = new OrderGroupHashMap(); //--- Create symbol-type hash map
         } else if (groupTypes[i] == SymbolCode && _openOrdersSymbol == NULL) { //--- Check symbol grouping
            _openOrdersSymbol = new OrderGroupHashMap();  //--- Create symbol hash map
         }
      }
   }

   //--- Retrieve open orders
   OrderCollection* GetOpenOrders() {
      if (_openOrders == NULL) {                          //--- Check if orders are loaded
         LoadOrdersFromBroker();                          //--- Load orders from broker
      }
      return _openOrders;                                 //--- Return open orders collection
   }

   //--- Retrieve open order by ticket
   Order* GetOpenOrder(ulong ticketId) {
      int index = _openOrders.GetKeyByTicket(ticketId);   //--- Find order index by ticket
      if (index == -1) {                                  //--- Check if not found
         return NULL;                                     //--- Return null
      }
      return _openOrders.Get(index);                      //--- Return order at index
   }

   //--- Retrieve grouped orders by symbol and type
   void GetOpenOrdersSymbolOrderType(OrderGroupData* &data[]) {
      _openOrdersSymbolType.GetAllData(data);             //--- Populate data with grouped orders
   }

   //--- Retrieve grouped orders by symbol
   void GetOpenOrdersSymbol(OrderGroupData* &data[]) {
      _openOrdersSymbol.GetAllData(data);                 //--- Populate data with grouped orders
   }

   //--- Retrieve pending open orders
   OrderCollection* GetPendingOpenOrders() {
      return _pendingOpenOrders;                          //--- Return pending open orders
   }

   //--- Retrieve pending close orders
   OrderCollection* GetPendingCloseOrders() {
      return _pendingCloseOrders;                         //--- Return pending close orders
   }

   //--- Reset pending orders
   void ResetPendingOrders() {
      delete(_pendingOpenOrders);                         //--- Delete existing pending open orders
      delete(_pendingCloseOrders);                        //--- Delete existing pending close orders
      _pendingOpenOrders = new OrderCollection();         //--- Create new pending open orders
      _pendingCloseOrders = new OrderCollection();        //--- Create new pending close orders
      Print("Wallet has " + IntegerToString(_pendingOpenOrders.Count()) + " pending open orders now."); //--- Log open orders count
      Print("Wallet has " + IntegerToString(_pendingCloseOrders.Count()) + " pending close orders now."); //--- Log close orders count
   }

   //--- Check if orders are being opened
   bool AreOrdersBeingOpened() {
      for (int i = _pendingOpenOrders.Count() - 1; i >= 0; i--) { //--- Iterate pending open orders
         if (_pendingOpenOrders.Get(i).IsAwaitingDealExecution) { //--- Check execution status
            return true;                                     //--- Return true if awaiting execution
         }
      }
      return false;                                       //--- Return false if no orders pending
   }

   //--- Check if orders are being closed
   bool AreOrdersBeingClosed() {
      for (int i = _pendingCloseOrders.Count() - 1; i >= 0; i--) { //--- Iterate pending close orders
         if (_pendingCloseOrders.Get(i).IsAwaitingDealExecution) { //--- Check execution status
            return true;                                     //--- Return true if awaiting execution
         }
      }
      return false;                                       //--- Return false if no orders pending
   }

   //--- Reset open orders
   void ResetOpenOrders() {
      _openedBuyOrderCount = 0;                           //--- Reset Buy order count
      _openedSellOrderCount = 0;                          //--- Reset Sell order count
      if (_openOrders != NULL) {                          //--- Check if open orders exist
         delete(_openOrders);                             //--- Delete open orders
         _openOrders = new OrderCollection();             //--- Create new open orders
      }
      if (_openOrdersSymbol != NULL) {                    //--- Check if symbol group exists
         delete(_openOrdersSymbol);                       //--- Delete symbol group
         _openOrdersSymbol = new OrderGroupHashMap();     //--- Create new symbol group
      }
      if (_openOrdersSymbolType != NULL) {                //--- Check if symbol-type group exists
         delete(_openOrdersSymbolType);                   //--- Delete symbol-type group
         _openOrdersSymbolType = new OrderGroupHashMap(); //--- Create new symbol-type group
      }
   }

   //--- Retrieve most recent open order
   Order* GetMostRecentOpenOrder() {
      return _mostRecentOpenOrder;                        //--- Return recent open order
   }

   //--- Retrieve most recent closed order
   Order* GetMostRecentClosedOrder() {
      return _mostRecentClosedOrder;                      //--- Return recent closed order
   }

   //--- Load orders from broker
   void LoadOrdersFromBroker() {
      OrderCollection* brokerOrders = OrderRepository::GetOpenOrders(MagicNumber, NULL, Symbol()); //--- Retrieve open orders
      for (int i = 0; i < brokerOrders.Count(); i++) {    //--- Iterate broker orders
         Order* openOrder = brokerOrders.Get(i);          //--- Get open order
         AddOrderToOpenOrderCollections(openOrder);       //--- Add to collections
         SetMostRecentOpenOrClosedOrder(openOrder);       //--- Update recent order
         CountAddedOrder(openOrder);                      //--- Update order counts
      }
      OrderCollection* lastClosedOrders = OrderRepository::GetLastClosedOrders(_lastBarStartTime); //--- Retrieve closed orders
      for (int i = 0; i < lastClosedOrders.Count(); i++) { //--- Iterate closed orders
         Order* closedOrder = lastClosedOrders.Get(i);    //--- Get closed order
         _recentClosedOrders.Add(new Order(closedOrder, true)); //--- Add to recent closed orders
         SetMostRecentOpenOrClosedOrder(closedOrder);     //--- Update recent order
      }
      delete(lastClosedOrders);                           //--- Delete closed orders collection
      delete(brokerOrders);                               //--- Delete broker orders collection
      PrintOrderChanges();                                //--- Log order changes
      Print("Wallet has " + IntegerToString(GetOpenedOrderCount()) + " orders now."); //--- Log total open orders
   }

   //--- Move pending open order to open status
   void SetPendingOpenOrderToOpen(Order* justOpenedOrder) {
      bool success = false;                               //--- Initialize success flag
      int key = _pendingOpenOrders.GetKeyByTicket(justOpenedOrder.Ticket); //--- Find order by ticket
      if (key != -1) {                                    //--- Check if order found
         if (_mostRecentOpenOrder != NULL && CheckPointer(_mostRecentOpenOrder) != POINTER_INVALID && CheckPointer(_mostRecentOpenOrder) == POINTER_DYNAMIC) { //--- Check existing recent order
            delete(_mostRecentOpenOrder);                 //--- Delete recent open order
         }
         _mostRecentOpenOrder = new Order(justOpenedOrder, false); //--- Set new recent open order
         AddOrderToOpenOrderCollections(justOpenedOrder); //--- Add to collections
         CountAddedOrder(justOpenedOrder);                //--- Update order counts
         delete(justOpenedOrder);                         //--- Delete input order
         _pendingOpenOrders.Remove(key);                  //--- Remove from pending
         success = true;                                  //--- Set success flag
      }
      if (success) {                                      //--- Check if successful
         PrintOrderChanges();                             //--- Log order changes
      } else {
         Alert("Couldn't move pending open order to opened orders for ticketid: " + IntegerToString(justOpenedOrder.Ticket)); //--- Log failure
      }
   }

   //--- Cancel pending open order
   bool CancelPendingOpenOrder(Order* justOpenedOrder) {
      int key = _pendingOpenOrders.GetKeyByTicket(justOpenedOrder.Ticket); //--- Find order by ticket
      if (key != -1) {                                    //--- Check if order found
         delete(justOpenedOrder);                         //--- Delete order
         _pendingOpenOrders.Remove(key);                  //--- Remove from pending
      } else {
         Alert("Couldn't cancel pending open order for ticketid: " + IntegerToString(justOpenedOrder.Ticket)); //--- Log failure
      }
      PrintOrderChanges();                                //--- Log order changes
      return key != -1;                                   //--- Return true if canceled
   }

   //--- Move all open orders to pending close
   void SetAllOpenOrdersToPendingClose() {
      bool success = false;                               //--- Initialize success flag
      for (int i = _openOrders.Count() - 1; i >= 0; i--) { //--- Iterate open orders
         Order* order = _openOrders.Get(i);               //--- Get open order
         if (MoveOpenOrderToPendingCloseOrders(order)) {  //--- Move to pending close
            success = true;                               //--- Set success flag
         }
      }
      if (success) {                                      //--- Check if changes made
         PrintOrderChanges();                             //--- Log order changes
      }
   }

   //--- Move single open order to pending close
   bool SetOpenOrderToPendingClose(Order* orderToClose) {
      bool success = MoveOpenOrderToPendingCloseOrders(orderToClose); //--- Move to pending close
      if (success) {                                      //--- Check if successful
         PrintOrderChanges();                             //--- Log order changes
         return true;                                     //--- Return true
      }
      Alert("Couldn't move open order to pendingclose orders for ticketid: " + IntegerToString(orderToClose.Ticket)); //--- Log failure
      return false;                                       //--- Return false
   }

   //--- Add order to pending close
   bool AddPendingCloseOrder(Order* orderToClose) {
      _pendingCloseOrders.Add(new Order(orderToClose, false)); //--- Add new order to pending close
      if (CheckPointer(orderToClose) != POINTER_INVALID && CheckPointer(orderToClose) == POINTER_DYNAMIC) { //--- Check dynamic pointer
         delete(orderToClose);                            //--- Delete input order
      }
      PrintOrderChanges();                                //--- Log order changes
      return true;                                        //--- Return true
   }

   //--- Move pending close order to closed status
   bool SetPendingCloseOrderToClosed(Order* justClosedOrder) {
      int key = _pendingCloseOrders.GetKeyByTicket(justClosedOrder.Ticket); //--- Find order by ticket
      if (key != -1) {                                    //--- Check if order found
         if (_lastOrderResultSize > 0) {                  //--- Check if results tracking enabled
            for (int i = ArraySize(LastOrderResults) - 1; i > 0; i--) { //--- Shift results
               LastOrderResults[i] = LastOrderResults[i - 1]; //--- Move previous result
            }
            LastOrderResults[0] = justClosedOrder.CalculateProfitPips() > 0 ? 1 : 0; //--- Set result (1 for profit, 0 for loss)
         }
         if (_mostRecentClosedOrder != NULL && CheckPointer(_mostRecentClosedOrder) != POINTER_INVALID && CheckPointer(_mostRecentClosedOrder) == POINTER_DYNAMIC) { //--- Check existing recent closed order
            delete(_mostRecentClosedOrder);               //--- Delete recent closed order
         }
         _mostRecentClosedOrder = new Order(justClosedOrder, false); //--- Set new recent closed order
         _recentClosedOrders.Add(new Order(justClosedOrder, true)); //--- Add to recent closed orders
         _pendingCloseOrders.Remove(key);                 //--- Remove from pending close
         delete(justClosedOrder);                         //--- Delete input order
         _closedOrderCount++;                             //--- Increment closed count
         PrintOrderChanges();                             //--- Log order changes
         return true;                                     //--- Return true
      }
      Alert("Couldn't move open order to removed order for ticketid: " + IntegerToString(justClosedOrder.Ticket)); //--- Log failure
      return false;                                       //--- Return false
   }

   //--- Retrieve total open order count
   int GetOpenedOrderCount() {
      return _openedBuyOrderCount + _openedSellOrderCount; //--- Return sum of Buy and Sell orders
   }

   //--- Retrieve closed order count
   ulong GetClosedOrderCount() {
      return _closedOrderCount;                           //--- Return closed order count
   }

private:
   //--- Add order to open order collections
   void AddOrderToOpenOrderCollections(Order* order) {
      Order* newOpenOrder = new Order(order, true);       //--- Create new order with visibility
      _openOrders.Add(newOpenOrder);                      //--- Add to open orders
      if (IsSymbolOrderTypeOrderGroupActivated()) {       //--- Check symbol-type grouping
         string key = GetOrderGroupSymbolOrderTypeKey(order); //--- Get symbol-type key
         OrderGroupData* orderGroupData = _openOrdersSymbolType.Get(key); //--- Retrieve group data
         if (orderGroupData == NULL) {                    //--- Check if group exists
            orderGroupData = new OrderGroupData();        //--- Create new group
         }
         orderGroupData.Add(newOpenOrder.Ticket);         //--- Add order ticket
         _openOrdersSymbolType.Put(key, orderGroupData);  //--- Store group data
      }
      if (IsSymbolOrderGroupActivated()) {                //--- Check symbol grouping
         string key = GetOrderGroupSymbolKey(order);      //--- Get symbol key
         OrderGroupData* orderGroupData = _openOrdersSymbol.Get(key); //--- Retrieve group data
         if (orderGroupData == NULL) {                    //--- Check if group exists
            orderGroupData = new OrderGroupData();        //--- Create new group
         }
         orderGroupData.Add(newOpenOrder.Ticket);         //--- Add order ticket
         _openOrdersSymbol.Put(key, orderGroupData);      //--- Store group data
      }
      PrintOrderChanges();                                //--- Log order changes
   }

   //--- Remove order from open order collections
   bool RemoveOrderFromOpenOrderCollections(Order* order) {
      int key = GetOpenOrders().GetKeyByTicket(order.Ticket); //--- Find order by ticket
      if (key != -1) {                                    //--- Check if order found
         GetOpenOrders().Remove(key);                     //--- Remove from open orders
         if (_openOrdersSymbolType != NULL) {             //--- Check symbol-type grouping
            string symbolOrderTypeKey = GetOrderGroupSymbolOrderTypeKey(order); //--- Get symbol-type key
            OrderGroupData* symbolOrderTypeGroupData = _openOrdersSymbolType.Get(symbolOrderTypeKey); //--- Retrieve group
            symbolOrderTypeGroupData.Remove(order.Ticket); //--- Remove ticket
         }
         if (_openOrdersSymbol != NULL) {                 //--- Check symbol grouping
            string symbolKey = GetOrderGroupSymbolKey(order); //--- Get symbol key
            OrderGroupData* symbolGroupData = _openOrdersSymbol.Get(symbolKey); //--- Retrieve group
            symbolGroupData.Remove(order.Ticket);         //--- Remove ticket
         }
      }
      return key != -1;                                   //--- Return true if removed
   }

   //--- Update most recent open or closed order
   void SetMostRecentOpenOrClosedOrder(Order* order) {
      if (order.CloseTime == 0) {                         //--- Check if open order
         if (_mostRecentOpenOrder == NULL) {              //--- Check if no recent open order
            _mostRecentOpenOrder = new Order(order, false); //--- Set new recent open order
         } else if (_mostRecentOpenOrder.OpenTime < order.OpenTime) { //--- Check if newer
            delete(_mostRecentOpenOrder);                 //--- Delete existing
            _mostRecentOpenOrder = new Order(order, false); //--- Set new recent open order
         }
      } else {                                            //--- Handle closed order
         if (_mostRecentClosedOrder == NULL) {            //--- Check if no recent closed order
            _mostRecentClosedOrder = new Order(order, false); //--- Set new recent closed order
         } else if (_mostRecentClosedOrder.CloseTime < order.CloseTime) { //--- Check if newer
            delete(_mostRecentClosedOrder);               //--- Delete existing
            _mostRecentClosedOrder = new Order(order, false); //--- Set new recent closed order
         }
      }
   }

   //--- Move open order to pending close
   bool MoveOpenOrderToPendingCloseOrders(Order* orderToClose) {
      if (RemoveOrderFromOpenOrderCollections(orderToClose)) { //--- Remove from open collections
         CountRemovedOrder(orderToClose);                 //--- Update order counts
         _pendingCloseOrders.Add(new Order(orderToClose, false)); //--- Add to pending close
         if (orderToClose.OpenTime == _mostRecentOpenOrder.OpenTime) { //--- Check if recent open order
            if (CheckPointer(_mostRecentOpenOrder) != POINTER_INVALID && CheckPointer(_mostRecentOpenOrder) == POINTER_DYNAMIC) { //--- Check dynamic pointer
               delete(_mostRecentOpenOrder);              //--- Delete recent open order
            }
            _mostRecentOpenOrder = NULL;                  //--- Clear recent open order
            Order* newMostRecentOpenOrder = GetLastOpenOrder(); //--- Get new recent open order
            if (newMostRecentOpenOrder != NULL) {         //--- Check if new order exists
               SetMostRecentOpenOrClosedOrder(newMostRecentOpenOrder); //--- Update recent order
            }
         }
         if (CheckPointer(orderToClose) != POINTER_INVALID && CheckPointer(orderToClose) == POINTER_DYNAMIC) { //--- Check dynamic pointer
            delete(orderToClose);                         //--- Delete input order
         }
         return true;                                     //--- Return true
      }
      return false;                                       //--- Return false if failed
   }

   //--- Retrieve last open order
   Order* GetLastOpenOrder() {
      Order* order = NULL;                                //--- Initialize order
      for (int i = _openOrders.Count() - 1; i >= 0; i--) { //--- Iterate open orders
         return _openOrders.Get(i);                       //--- Return last order
      }
      return NULL;                                        //--- Return null if none
   }

   //--- Generate key for symbol and order type
   string GetOrderGroupSymbolOrderTypeKey(Order* order) {
      return order.SymbolCode + IntegerToString(order.Type); //--- Combine symbol and type
   }

   //--- Generate key for symbol
   string GetOrderGroupSymbolKey(Order* order) {
      return order.SymbolCode;                            //--- Return symbol code
   }

   //--- Check if symbol-type grouping is active
   bool IsSymbolOrderTypeOrderGroupActivated() {
      return _openOrdersSymbolType != NULL;               //--- Return true if active
   }

   //--- Check if symbol grouping is active
   bool IsSymbolOrderGroupActivated() {
      return _openOrdersSymbol != NULL;                   //--- Return true if active
   }

   //--- Increment order count for added order
   void CountAddedOrder(Order* order) {
      if (order.Type == ORDER_TYPE_BUY) {                 //--- Check if Buy order
         _openedBuyOrderCount++;                          //--- Increment Buy count
      } else if (order.Type == ORDER_TYPE_SELL) {         //--- Check if Sell order
         _openedSellOrderCount++;                         //--- Increment Sell count
      }
   }

   //--- Decrement order count for removed order
   void CountRemovedOrder(Order* order) {
      if (order.Type == ORDER_TYPE_BUY) {                 //--- Check if Buy order
         _openedBuyOrderCount--;                          //--- Decrement Buy count
      } else if (order.Type == ORDER_TYPE_SELL) {         //--- Check if Sell order
         _openedSellOrderCount--;                         //--- Decrement Sell count
      }
   }

   //--- Log order state changes
   void PrintOrderChanges() {
      if (DisplayOrderInfo && IsDemoLiveOrVisualMode) {   //--- Check if display enabled
         string comment = "\n     ------------------------------------------------------------"; //--- Start comment
         comment += "\n      :: Pending open orders: " + IntegerToString(_pendingOpenOrders.Count()); //--- Add pending open count
         comment += "\n      :: Open orders: " + IntegerToString(_openedBuyOrderCount) + " (Buy), " + IntegerToString(_openedSellOrderCount) + " (Sell)"; //--- Add open counts
         comment += "\n      :: Pending close orders: " + IntegerToString(_pendingCloseOrders.Count()); //--- Add pending close count
         comment += "\n      :: Recently closed orders: " + IntegerToString(_recentClosedOrders.Count()); //--- Add closed count
         OrderInfoComment = comment;                      //--- Store comment
      }
   }
};

Hier implementieren wir die Klasse „Wallet“ zur Verwaltung des Handelsstatus und der Aufträge. Wir deklarieren das globale Array „LastOrderResults“, um die Ergebnisse des Handels zu speichern (1 für Gewinn, 0 für Verlust). Die Klasse „Wallet“ verwendet private Variablen wie „_openedBuyOrderCount“, „_openedSellOrderCount“, „_closedOrderCount“, „_lastOrderResultSize“, „_lastOrderResultByTimeframe“ (ENUM_TIMEFRAMES) und „_lastBarStartTime“, um die Anzahl der Orders und das Timing zu verfolgen, zusammen mit Zeigern auf „OrderCollection“ Objekte („_openOrders“, „_pendingOpenOrders“, „_pendingCloseOrders“, „_recentClosedOrders“) und „OrderGroupHashMap“ Objekte („_openOrdersSymbolType“, „_openOrdersSymbol“) für die Gruppierung, sowie „_mostRecentOpenOrder“ und „_mostRecentClosedOrder“ für die jüngsten Aufträge.

Wir definieren den Konstruktor von „Wallet“, um die Zähler auf 0 zu initialisieren, neue „OrderCollection“-Instanzen zu erstellen und die Zeiger auf Null zu setzen, wobei der Destruktor des „~Wallet“ alle dynamischen Objekte aufräumt. Die Methode „HandleTick“ aktualisiert „_recentClosedOrders“ bei neuen Bars unter Verwendung der iTime, während „SetLastOrderResultsSize“ die Größe von „LastOrderResults“ ändert und „SetLastClosedOrdersByTimeframe“ „_lastOrderResultByTimeframe“ festlegt. Öffentliche Methoden wie „GetRecentClosedOrders“, „GetOpenOrders“, „GetOpenOrder“, „GetPendingOpenOrders“, und „GetPendingCloseOrders“ Auftragssammlungen abrufen, wobei „ActivateOrderGroups“ die Gruppierung nach „ORDER_GROUP_TYPE“-Werten („SymbolOrderType“, „SymbolCode“) ermöglicht.

Die Methoden „ResetPendingOrders“ und „ResetOpenOrders“ reinitialisieren Sammlungen, und „AreOrdersBeingOpened“ und „AreOrdersBeingClosed“ überprüfen den Status der anstehenden Ausführung. Wir implementieren „LoadOrdersFromBroker“, um die Aufträge über „OrderRepository::GetOpenOrders“ und „GetLastClosedOrders“, „SetPendingOpenOrderToOpen“ und „CancelPendingOpenOrder“ zur Verwaltung ausstehender Eröffnungen, „SetAllOpenOrdersToPendingClose“ und „SetOpenOrderToPendingClose“ für Abschlüsse, und „SetPendingCloseOrderToClosed“ zur Aktualisierung von „LastOrderResults“ und „_closedOrderCount“.

Private Methoden wie „AddOrderToOpenOrderCollections“, „RemoveOrderFromOpenOrderCollections“, „SetMostRecentOpenOrClosedOrder“, „GetOrderGroupSymbolOrderTypeKey“, und „PrintOrderChanges“ unterstützen die Gruppierung von Aufträgen und die Protokollierung in „OrderInfoComment“. Diese Klasse wird die Auftragsverwaltung für Scalping zentralisieren. Um den aktuellen Fortschritt zu sehen, definieren wir eine Basisklasse für die Verwaltung des Starts und können sie bei der Initialisierung aufrufen, um den Meilenstein zu visualisieren.

//--- Define enumeration for trade actions
enum TradeAction {
   UnknownAction = 0,                                     //--- Represent unknown action
   OpenBuyAction = 1,                                     //--- Represent open Buy action
   OpenSellAction = 2,                                    //--- Represent open Sell action
   CloseBuyAction = 3,                                    //--- Represent close Buy action
   CloseSellAction = 4                                    //--- Represent close Sell action
};

//--- Define interface for trader
interface ITrader {
   void HandleTick();                                     //--- Handle tick event
   void Init();                                           //--- Initialize trader
   Wallet* GetWallet();                                   //--- Retrieve wallet
};

//--- Declare global trader pointer
ITrader *_ea;                                             //--- Store EA instance

//--- Define main Expert Advisor class
class EA : public ITrader {
private:
   bool _firstTick;                                       //--- Track first tick
   Wallet* _wallet;                                       //--- Store wallet

public:
   //--- Initialize EA
   void EA() {
      _firstTick = true;                                  //--- Set first tick flag
      _wallet = new Wallet();                             //--- Create wallet
      _wallet.SetLastClosedOrdersByTimeframe(DisplayOrderDuringTimeframe); //--- Set closed orders timeframe
   }

   //--- Destructor to clean up EA
   void ~EA() {
      delete(_wallet);                                    //--- Delete wallet
   }

   //--- Initialize EA components
   void Init() {
      IsDemoLiveOrVisualMode = !MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE); //--- Set mode flag
      UnitsOneLot = MarketInfo_LibFunc(Symbol(), MODE_LOTSIZE); //--- Set lot size
      _wallet.LoadOrdersFromBroker();                     //--- Load orders from broker
   }

   //--- Handle tick event
   void HandleTick() {
      if (MQLInfoInteger(MQL_TESTER) == 0) {              //--- Check if not in tester
         SyncOrders();                                    //--- Synchronize orders
      }
      if (AllowManualTPSLChanges) {                       //--- Check if manual TP/SL allowed
         SyncManualTPSLChanges();                         //--- Synchronize manual TP/SL
      }
      AskFunc.Evaluate();                                 //--- Update Ask price
      BidFunc.Evaluate();                                 //--- Update Bid price
      UpdateOrders();                                     //--- Update order profits
      if (!StopEA) {                                      //--- Check if EA not stopped
         _wallet.HandleTick();                            //--- Handle wallet tick
         if (ExecutePendingCloseOrders()) {               //--- Execute close orders
            if (!ExecutePendingOpenOrders()) {            //--- Execute open orders
               HandleErrors(StringFormat("Open (all) order(s) failed. Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
            }
         } else {
            HandleErrors(StringFormat("Close (all) order(s) failed! Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
         }
      } else {
         if (ExecutePendingCloseOrders()) {               //--- Execute close orders
            _wallet.SetAllOpenOrdersToPendingClose();     //--- Move open orders to pending close
         } else {
            HandleErrors(StringFormat("Close (all) order(s) failed! Please check EA %d and look at the Journal and Expert tab.", MagicNumber)); //--- Log error
         }
      }
      if (_firstTick) {                                   //--- Check if first tick
         _firstTick = false;                              //--- Clear first tick flag
      }
   }

   //--- Retrieve wallet
   Wallet* GetWallet() {
      return _wallet;                                     //--- Return wallet
   }

private:
   //--- Synchronize orders with broker
   void SyncOrders() {
      OrderCollection* currentOpenOrders = OrderRepository::GetOpenOrders(MagicNumber, NULL, Symbol()); //--- Retrieve open orders
      if (currentOpenOrders.Count() != (_wallet.GetOpenOrders().Count() + _wallet.GetPendingCloseOrders().Count())) { //--- Check order mismatch
         Print("(Manual) orderchanges detected" + " (found in MT: " + IntegerToString(currentOpenOrders.Count()) + " and in wallet: " + IntegerToString(_wallet.GetOpenOrders().Count()) + "), resetting EA, loading open orders."); //--- Log mismatch
         _wallet.ResetOpenOrders();                       //--- Reset open orders
         _wallet.ResetPendingOrders();                    //--- Reset pending orders
         _wallet.LoadOrdersFromBroker();                  //--- Reload orders
      }
      delete(currentOpenOrders);                          //--- Delete orders collection
   }

   //--- Synchronize manual TP/SL changes
   void SyncManualTPSLChanges() {
      _wallet.GetOpenOrders().Rewind();                   //--- Reset orders iterator
      while (_wallet.GetOpenOrders().HasNext()) {         //--- Iterate orders
         Order* order = _wallet.GetOpenOrders().Next();   //--- Get order
         uint lineFindResult = ObjectFind(ChartID(), IntegerToString(order.Ticket) + "_SL"); //--- Find SL line
         if (lineFindResult != UINT_MAX) {                //--- Check if SL line exists
            double currentPosition = ObjectGetDouble(ChartID(), IntegerToString(order.Ticket) + "_SL", OBJPROP_PRICE); //--- Get SL position
            if ((order.StopLossManual == 0 && currentPosition != order.GetClosestSL()) || //--- Check manual SL change
                (order.StopLossManual != 0 && currentPosition != order.StopLossManual)) { //--- Check manual SL mismatch
               order.StopLossManual = currentPosition;       //--- Update manual SL
            }
         }
         lineFindResult = ObjectFind(ChartID(), IntegerToString(order.Ticket) + "_TP"); //--- Find TP line
         if (lineFindResult != UINT_MAX) {                //--- Check if TP line exists
            double currentPosition = ObjectGetDouble(ChartID(), IntegerToString(order.Ticket) + "_TP", OBJPROP_PRICE); //--- Get TP position
            if ((order.TakeProfitManual == 0 && currentPosition != order.GetClosestTP()) || //--- Check manual TP change
                (order.TakeProfitManual != 0 && currentPosition != order.TakeProfitManual)) { //--- Check manual TP mismatch
               order.TakeProfitManual = currentPosition;     //--- Update manual TP
            }
         }
      }
   }

   //--- Update order profits
   void UpdateOrders() {
      _wallet.GetOpenOrders().Rewind();                   //--- Reset orders iterator
      while (_wallet.GetOpenOrders().HasNext()) {         //--- Iterate orders
         Order* order = _wallet.GetOpenOrders().Next();   //--- Get order
         double pipsProfit = order.CalculateProfitPips(); //--- Calculate profit
         order.CurrentProfitPips = pipsProfit;            //--- Update current profit
         if (pipsProfit < order.LowestProfitPips) {       //--- Check if lowest profit
            order.LowestProfitPips = pipsProfit;          //--- Update lowest profit
         } else if (pipsProfit > order.HighestProfitPips) { //--- Check if highest profit
            order.HighestProfitPips = pipsProfit;         //--- Update highest profit
         }
      }
   }

   //--- Execute pending close orders
   bool ExecutePendingCloseOrders() {
      OrderCollection* pendingCloseOrders = _wallet.GetPendingCloseOrders(); //--- Retrieve pending close orders
      int ordersToCloseCount = pendingCloseOrders.Count(); //--- Get count
      if (ordersToCloseCount == 0) {                      //--- Check if no orders
         return true;                                     //--- Return true
      }
      if (_wallet.AreOrdersBeingOpened()) {               //--- Check if orders being opened
         return true;                                     //--- Return true
      }
      int ordersCloseSuccessCount = 0;                    //--- Initialize success count
      for (int i = ordersToCloseCount - 1; i >= 0; i--) { //--- Iterate orders
         Order* pendingCloseOrder = pendingCloseOrders.Get(i); //--- Get order
         if (pendingCloseOrder.IsAwaitingDealExecution) { //--- Check if awaiting execution
            ordersCloseSuccessCount++;                    //--- Increment success count
            continue;                                     //--- Move to next
         }
         bool success;                                    //--- Declare success flag
         if (AccountMarginMode == ACCOUNT_MARGIN_MODE_RETAIL_NETTING) { //--- Check netting mode
            Order* reversedOrder = new Order(pendingCloseOrder, false); //--- Create reversed order
            reversedOrder.Type = pendingCloseOrder.Type == ORDER_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; //--- Set opposite type
            success = OrderRepository::OpenOrder(reversedOrder); //--- Open reversed order
            if (success) {                                //--- Check if successful
               pendingCloseOrder.Ticket = reversedOrder.Ticket; //--- Update ticket
            }
            delete(reversedOrder);                        //--- Delete reversed order
         } else {
            success = OrderRepository::ClosePosition(pendingCloseOrder); //--- Close position
         }
         if (success) {                                   //--- Check if successful
            ordersCloseSuccessCount++;                    //--- Increment success count
         }
      }
      return ordersCloseSuccessCount == ordersToCloseCount; //--- Return true if all successful
   }

   //--- Execute pending open orders
   bool ExecutePendingOpenOrders() {
      OrderCollection* pendingOpenOrders = _wallet.GetPendingOpenOrders(); //--- Retrieve pending open orders
      int ordersToOpenCount = pendingOpenOrders.Count(); //--- Get count
      if (ordersToOpenCount == 0) {                       //--- Check if no orders
         return true;                                     //--- Return true
      }
      int ordersOpenSuccessCount = 0;                     //--- Initialize success count
      for (int i = ordersToOpenCount - 1; i >= 0; i--) { //--- Iterate orders
         Order* order = pendingOpenOrders.Get(i);         //--- Get order
         if (order.IsAwaitingDealExecution) {             //--- Check if awaiting execution
            ordersOpenSuccessCount++;                     //--- Increment success count
            continue;                                     //--- Move to next
         }
         bool isTradeContextFree = false;                 //--- Initialize trade context flag
         double StartWaitingTime = GetTickCount();        //--- Start timer
         while (true) {                                   //--- Wait for trade context
            if (MQL5InfoInteger(MQL5_TRADE_ALLOWED)) {    //--- Check if trade allowed
               isTradeContextFree = true;                 //--- Set trade context free
               break;                                     //--- Exit loop
            }
            int MaxWaiting_sec = 10;                      //--- Set max wait time
            if (IsStopped()) {                            //--- Check if EA stopped
               HandleErrors("The expert was stopped by a user action."); //--- Log error
               break;                                     //--- Exit loop
            }
            if (GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) { //--- Check if timeout
               HandleErrors(StringFormat("The (%d seconds) waiting time exceeded. Trade not allowed: EA disabled, market closed or trade context still not free.", MaxWaiting_sec)); //--- Log error
               break;                                     //--- Exit loop
            }
            Sleep(100);                                   //--- Wait briefly
         }
         if (!isTradeContextFree) {                       //--- Check if trade context not free
            if (!_wallet.CancelPendingOpenOrder(order)) { //--- Attempt to cancel order
               HandleErrors("Failed to cancel an order (because it couldn't open). Please see the Journal and Expert tab in Metatrader for more information."); //--- Log error
            }
            continue;                                     //--- Move to next
         }
         bool success = OrderRepository::OpenOrder(order); //--- Open order
         if (success) {                                   //--- Check if successful
            ordersOpenSuccessCount++;                     //--- Increment success count
         } else {
            if (!_wallet.CancelPendingOpenOrder(order)) { //--- Attempt to cancel order
               HandleErrors("Failed to cancel an order (because it couldn't open). Please see the Journal and Expert tab in Metatrader for more information."); //--- Log error
            }
         }
      }
      return ordersOpenSuccessCount == ordersToOpenCount; //--- Return true if all successful
   }
};

Hier schließen wir den Kern der Infrastruktur ab, indem wir Handelsaktionen und die Hauptlogik des Expert Advisors definieren. Wir erstellen die Enumeration „TradeAction“, um Handelsoperationen zu kategorisieren, einschließlich „UnknownAction“ (0), „OpenBuyAction“ (1), „OpenSellAction“ (2), „CloseBuyAction“ (3) und „CloseSellAction“ (4), um einen klaren Rahmen für das Handelsmanagement zu schaffen. Wir werden dies später verwenden. Dann definieren wir die Schnittstelle „ITrader“ mit den Methoden „HandleTick“, „Init“ und „GetWallet“, um die EA-Funktionalität zu standardisieren, und deklarieren einen globalen Zeiger „_ea“ vom Typ „ITrader“, um die EA-Instanz zu speichern.

Wir implementieren die Klasse „EA“, die von „ITrader“ erbt, mit privaten Variablen „_firstTick“, um die ersten Ticks zu verfolgen, und „_wallet“, um die „Wallet“-Instanz zu verwalten. Der „EA“-Konstruktor initialisiert „_firstTick“ auf true und erstellt eine neue „Wallet“, deren Zeitrahmen über „SetLastClosedOrdersByTimeframe“ mit „DisplayOrderDuringTimeframe“ festgelegt wird. Der Destruktor „~EA“ räumt „_wallet“ auf. Die Methode „Init“ setzt „IsDemoLiveOrVisualMode“ über MQLInfoInteger, weist „UnitsOneLot“ über „MarketInfo_LibFunc“ zu und ruft „_wallet.LoadOrdersFromBroker“ auf.

Die Methode „HandleTick“ verwaltet Ticks durch den Aufruf von „SyncOrders“ (außer im Testermodus), „SyncManualTPSLChanges“, wenn „AllowManualTPSLChanges“ wahr ist, die Aktualisierung von „AskFunc“ und „BidFunc“ und den Aufruf von „UpdateOrders“ und „_wallet.HandleTick“. Es führt „ExecutePendingCloseOrders“ und „ExecutePendingOpenOrders“ aus, sofern „StopEA“ nicht wahr ist, protokolliert Fehler über „HandleErrors“, falls erforderlich, und löscht „_firstTick“.

Zu den privaten Methoden gehören „SyncOrders“ zur Synchronisierung von Aufträgen mit „OrderRepository::GetOpenOrders“, „SyncManualTPSLChanges“, um manuelle TP/SL über ObjectFind und ObjectGetDouble zu aktualisieren, „UpdateOrders“, um Gewinnmetriken mit „CalculateProfitPips“ zu aktualisieren, „ExecutePendingCloseOrders“, um Positionen mit „OrderRepository::ClosePosition“ oder „OpenOrder“ für das Netting und „ExecutePendingOpenOrders“ zum Öffnen von Aufträgen mit „OrderRepository::OpenOrder“, zur Sicherstellung des Handelskontexts über „MQL5InfoInteger“ und zur Behandlung von Stornierungen. Wir können dies nun in OnInit aufrufen.

//--- Set up chart appearance
void SetupChart() {
   ChartSetInteger(ChartID(), CHART_FOREGROUND, 0, false); //--- Set chart foreground to background
}

//--- Initialize Expert Advisor
int OnInit() {
   ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite); //--- Set chart background to white
   ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrRed); //--- Set bearish candles to red
   ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrGreen); //--- Set bullish candles to green
   ChartSetInteger(0, CHART_COLOR_ASK, clrDarkRed); //--- Set Ask line to dark red
   ChartSetInteger(0, CHART_COLOR_BID, clrDarkGreen); //--- Set Bid line to dark green
   ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrRed); //--- Set downward movement to red
   ChartSetInteger(0, CHART_COLOR_CHART_UP, clrGreen); //--- Set upward movement to green
   ChartSetInteger(0, CHART_COLOR_GRID, clrLightGray); //--- Set grid to light gray
   ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack); //--- Set axis and text to black
   ChartSetInteger(0, CHART_COLOR_LAST, clrBlack); //--- Set last price line to black
   OrderFillingType = GetFillingType();                //--- Retrieve order filling type
   if ((int)OrderFillingType == -1) {                  //--- Check if invalid
      HandleErrors("Unsupported filling type " + IntegerToString((int)OrderFillingType)); //--- Log error
      return (INIT_FAILED);                            //--- Return failure
   }
   GetExecutionType();                                 //--- Retrieve execution type
   AccountMarginMode = GetAccountMarginMode();         //--- Retrieve margin mode
   SetPipPoint();                                      //--- Set pip point
   if (PipPoint == 0) {                                //--- Check if invalid
      HandleErrors("Couldn't find correct pip/point for symbol."); //--- Log error
      return (INIT_FAILED);                            //--- Return failure
   }
   AskFunc = new AskFunction();                        //--- Create Ask function
   AskFunc.Init();                                     //--- Initialize Ask function
   BidFunc = new BidFunction();                        //--- Create Bid function
   BidFunc.Init();                                     //--- Initialize Bid function
   OrderInfoComment = "";                              //--- Initialize order comment
   _ea = new EA();                                     //--- Create EA instance
   _ea.Init();                                         //--- Initialize EA
   SetupChart();                                       //--- Set up chart
   hd_iMA_SMA8 = iMA(NULL, PERIOD_M30, iMA_SMA8_ma_period, iMA_SMA8_ma_shift, MODE_SMA, PRICE_CLOSE); //--- Initialize 8-period SMA
   if (hd_iMA_SMA8 < 0) {                              //--- Check if failed
      HandleErrors(StringFormat("Could not find indicator 'iMA'. Error: %d", GetLastError())); //--- Log error
      return -1;                                       //--- Return failure
   }
   hd_iMA_EMA200 = iMA(NULL, PERIOD_M1, iMA_EMA200_ma_period, iMA_EMA200_ma_shift, MODE_EMA, PRICE_CLOSE); //--- Initialize 200-period EMA
   if (hd_iMA_EMA200 < 0) {                            //--- Check if failed
      HandleErrors(StringFormat("Could not find indicator 'iMA'. Error: %d", GetLastError())); //--- Log error
      return -1;                                       //--- Return failure
   }
   hd_iRSI_RSI = iRSI(NULL, PERIOD_M1, iRSI_RSI_ma_period, PRICE_CLOSE); //--- Initialize RSI
   if (hd_iRSI_RSI < 0) {                              //--- Check if failed
      HandleErrors(StringFormat("Could not find indicator 'iRSI'. Error: %d", GetLastError())); //--- Log error
      return -1;                                       //--- Return failure
   }
   hd_iEnvelopes_ENV_LOW = iEnvelopes(NULL, PERIOD_M1, iEnvelopes_ENV_LOW_ma_period, iEnvelopes_ENV_LOW_ma_shift, MODE_SMA, PRICE_CLOSE, iEnvelopes_ENV_LOW_deviation); //--- Initialize lower Envelopes
   if (hd_iEnvelopes_ENV_LOW < 0) {                    //--- Check if failed
      HandleErrors(StringFormat("Could not find indicator 'iEnvelopes'. Error: %d", GetLastError())); //--- Log error
      return -1;                                       //--- Return failure
   }
   hd_iEnvelopes_ENV_UPPER = iEnvelopes(NULL, PERIOD_M1, iEnvelopes_ENV_UPPER_ma_period, iEnvelopes_ENV_UPPER_ma_shift, MODE_SMA, PRICE_CLOSE, iEnvelopes_ENV_UPPER_deviation); //--- Initialize upper Envelopes
   if (hd_iEnvelopes_ENV_UPPER < 0) {                  //--- Check if failed
      HandleErrors(StringFormat("Could not find indicator 'iEnvelopes'. Error: %d", GetLastError())); //--- Log error
      return -1;                                       //--- Return failure
   }
   hd_iMA_SMA_4 = iMA(NULL, PERIOD_M30, iMA_SMA_4_ma_period, iMA_SMA_4_ma_shift, MODE_SMA, PRICE_CLOSE); //--- Initialize 4-period SMA
   if (hd_iMA_SMA_4 < 0) {                             //--- Check if failed
      HandleErrors(StringFormat("Could not find indicator 'iMA'. Error: %d", GetLastError())); //--- Log error
      return -1;                                       //--- Return failure
   }
   return (INIT_SUCCEEDED);                            //--- Return success
}



//--- Handle errors
void HandleErrors(string errorMessage) {
   Print(errorMessage);                                //--- Log error
   if (Error != NULL || errorMessage == ErrorPreviousQuote) { //--- Check existing or repeated error
      return;                                          //--- Exit
   }
   if (AlertOnError) Alert(errorMessage);              //--- Trigger alert if enabled
   if (NotificationOnError) SendNotification(StringFormat("Error by EA (%d) %s", MagicNumber, errorMessage)); //--- Send notification if enabled
   if (EmailOnError) SendMail(StringFormat("Error by EA (%d)", MagicNumber), errorMessage); //--- Send email if enabled
   Error = errorMessage;                               //--- Set current error
   ErrorPreviousQuote = Error;                         //--- Set previous error
}

Zur Initialisierung des Programms definieren wir die Funktion „SetupChart“, um das Erscheinungsbild des Charts zu konfigurieren, indem wir CHART_FOREGROUND mit ChartSetInteger auf false setzen und so sicherstellen, dass der Hintergrund des Charts aus Gründen der Übersichtlichkeit Vorrang hat. Wir implementieren die Funktion OnInit, um den EA zu initialisieren, und beginnen mit der Anpassung des Charts über „ChartSetInteger“, um die Farben einzustellen: „CHART_COLOR_BACKGROUND“ auf weiß, CHART_COLOR_CANDLE_BEAR auf rot, CHART_COLOR_CANDLE_BULL auf grün, CHART_COLOR_ASK“ auf dunkelrot, CHART_COLOR_BID“ auf dunkelgrün und andere zur optischen Unterscheidung.

Wir rufen „GetFillingType“ auf, um „OrderFillingType“ zu setzen, und geben INIT_FAILED zurück, wenn es ungültig ist, und rufen „GetExecutionType“ und „GetAccountMarginMode“ auf, um die Handelsmodi zu konfigurieren. Die Funktion „SetPipPoint“ setzt „PipPoint“, mit einer Fehlerprüfung, und wir instanziieren „AskFunc“ und „BidFunc“ als „AskFunction“- und „BidFunction“-Objekte, indem wir ihre „Init“-Methoden aufrufen.

Wir erstellen die Instanz „_ea“ der Klasse „EA“, initialisieren sie mit „Init“ und rufen „SetupChart“ auf. Wir initialisieren Indikator-Handles mit „iMA“ für „hd_iMA_SMA8“ (M30, 14-Perioden-SMA), „hd_iMA_EMA200“ (M1, 200-Perioden-EMA), „hd_iMA_SMA_4“ (M30, 9-Perioden-SMA), „iRSI“ für „hd_iRSI_RSI“ (M1, 8-Perioden), und „iEnvelopes“ für „hd_iEnvelopes_ENV_LOW“ (M1, 95-Perioden, 1.4% Abweichung) und „hd_iEnvelopes_ENV_UPPER“ (M1, 150-Periode, 0,1% Abweichung), wobei bei einem Fehler mit „HandleErrors“ -1 zurückgegeben wird.

Wir definieren die Funktion „HandleErrors“, um Fehler über Print zu protokollieren, Duplikate mit „Error“ und „ErrorPreviousQuote“ zu überspringen und Benachrichtigungen über „Alert“, „SendNotification“ oder „SendMail“ basierend auf „AlertOnError“, „NotificationOnError“ und „EmailOnError“, wobei „Error“ und „ErrorPreviousQuote“ aktualisiert werden. Damit ist das Programm für die Verarbeitung der Kernlogik bereit, und wenn wir es ausführen, erhalten wir das folgende Eingabeergebnis.

INPUT SEKTION

Aus der Visualisierung ist ersichtlich, dass der Nutzer Eingaben zur Programmsteuerung machen kann. Wenn wir das Programm ausführen, erhalten wir das folgende Ergebnis.

BESTÄTIGUNG DER INITIALISIERUNG

Aus dem Bild geht hervor, dass wir das Programm erfolgreich initialisiert haben und es bereit ist, Bestellungen entgegenzunehmen. Damit haben wir die Kerninfrastruktur definiert, die das Programm initialisiert. Bleibt nur noch das Backtest des Programms, um sicherzustellen, dass es korrekt startet, was im nächsten Abschnitt behandelt wird.


Backtests

Wir haben den Backtest in eine animierte Graphics Interchange Format (GIF)-Datei kompiliert, um die Initialisierungslogik des Programms darzustellen.

BACKTEST


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir erfolgreich den Grundstein für die Automatisierung der Envelopes Trend Bounce Scalping-Strategie in MQL5 gelegt haben, indem wir eine robuste Expert Advisor-Infrastruktur und einen Rahmen für die Signalgenerierung geschaffen haben. Wir haben wesentliche Komponenten konfiguriert, darunter die Initialisierung von Indikatoren, Klassen für die Auftragsverwaltung und die Fehlerbehandlung, um präzise Skalierungsoperationen zu unterstützen. Diese Grundlage bildet die Basis für die Implementierung der Handelsausführung und des dynamischen Managements im nächsten Teil, was uns einem vollautomatischen Handelsprogramm näher bringt. Bleiben Sie am Ball.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/18269

Vom Neuling zum Experten: Autogeometrisches Analysesystem Vom Neuling zum Experten: Autogeometrisches Analysesystem
Geometrische Muster bieten Händlern eine prägnante Methode zur Interpretation von Kursbewegungen. Viele Analysten zeichnen Trendlinien, Rechtecke und andere Formen mit der Hand und treffen ihre Handelsentscheidungen dann auf der Grundlage der von ihnen gesehenen Formationen. In diesem Artikel untersuchen wir eine automatisierte Alternative: die Nutzung von MQL5 zur Erkennung und Analyse der gängigsten geometrischen Muster. Wir schlüsseln die Methodik auf, erörtern Details der Implementierung und zeigen auf, wie die automatische Mustererkennung die Markteinblicke eines Händlers schärfen kann.
Entwicklung des Price Action Analysis Toolkit (Teil 26): Pin Bar, Engulfing Patterns und RSI Divergence (Multi-Pattern) Tool Entwicklung des Price Action Analysis Toolkit (Teil 26): Pin Bar, Engulfing Patterns und RSI Divergence (Multi-Pattern) Tool
Im Einklang mit unserem Ziel, praktische Tools zu Preis-Aktionen zu entwickeln, untersucht dieser Artikel die Erstellung eines EA, der die Muster von Pin-Bars und Engulfing erkennt und die RSI-Divergenz als Bestätigungsauslöser verwendet, bevor er Handelssignale erzeugt.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 67): Verwendung von TRIX-Mustern und der Williams Percent Range MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 67): Verwendung von TRIX-Mustern und der Williams Percent Range
Der Triple Exponential Moving Average Oscillator (TRIX) und der Williams Percentage Range Oscillator sind ein weiteres Paar von Indikatoren, die in Verbindung mit einem MQL5 Expert Advisor verwendet werden können. Dieses Indikatorpaar ist, wie die anderen, die wir kürzlich behandelt haben, ebenfalls komplementär, da der TRIX den Trend definiert, während die Williams Percent Range die Unterstützungs- und Widerstandsniveaus bestätigt. Wie immer verwenden wir den MQL5-Assistenten, um das Potenzial dieser beiden zu testen.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 66): Verwendung von FrAMA-Mustern und des Force Index mit dem Punktprodukt-Kernel MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 66): Verwendung von FrAMA-Mustern und des Force Index mit dem Punktprodukt-Kernel
Der FrAMA-Indikator und der Force Index Oscillator sind Trend- und Volumeninstrumente, die bei der Entwicklung eines Expert Advisors kombiniert werden können. Wir knüpfen an unseren letzten Artikel an, in dem dieses Paar vorgestellt wurde, und betrachten die Anwendbarkeit des maschinellen Lernens auf dieses Paar. Wir verwenden ein neuronales Faltungsnetzwerk, das den Punkt-Produkt-Kernel bei der Erstellung von Prognosen mit den Eingaben dieser Indikatoren verwendet. Dies geschieht in einer nutzerdefinierten Signalklassendatei, die mit dem MQL5-Assistenten arbeitet, um einen Expert Advisor zusammenzustellen.