English 日本語
preview
Automatisieren von Handelsstrategien in MQL5 (Teil 26): Aufbau eines Pin Bar Averaging Systems für den Handel mit mehreren Positionen

Automatisieren von Handelsstrategien in MQL5 (Teil 26): Aufbau eines Pin Bar Averaging Systems für den Handel mit mehreren Positionen

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

Einführung

In unserem vorangegangenen Artikel (Teil 25) haben wir ein Trendlinien-Handelssystem in MetaQuotes Language 5 (MQL5) entwickelt, das die Anpassung durch die kleinsten Quadrate verwendet, um Unterstützungs- und Widerstandstrendlinien zu erkennen, und automatische Handelsgeschäfte basierend auf Preisberührungen mit visuellem Feedback generiert. In Teil 26 erstellen wir ein Programm für Pin Bars, das diese Kerzenmuster identifiziert, um Handelsgeschäfte zu initiieren, und mehrere Positionen durch eine Mittelungsstrategie verwaltet, die Trailing-Stops, Breakeven-Anpassungen und ein Dashboard für die Echtzeitüberwachung beinhaltet. Wir werden die folgenden Themen behandeln:

  1. Verstehen des Systems Pin Bar Averaging
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende haben Sie eine leistungsstarke MQL5-Strategie für den Pin-Bar-Handel, die Sie nur noch anpassen müssen – legen wir los!


Verstehen des Systems Pin Bar Averaging

Wir bauen ein automatisiertes Handelssystem auf, das die Kerzenmuster der Pin-Bars nutzt. Dabei handelt es sich um Kerzen-Formationen, die durch einen langen Docht und einen kleinen Körper gekennzeichnet sind und oft starke Kursumkehrungen auf wichtigen Marktniveaus signalisieren. Pin-Bar-Strategien sind beim Handel sehr beliebt, da sie Momente identifizieren, in denen der Preis zurückgewiesen wird, und somit Einstiegspunkte mit hoher Wahrscheinlichkeit für den Handel bieten, insbesondere in Kombination mit Unterstützungs- und Widerstandsebenen. Hier finden Sie eine Visualisierung einiger gängiger Formationen.

PIN BAR SYSTEM

Unser Ansatz wird sich darauf konzentrieren, diese Pin-Bar innerhalb des aktuellen Zeitrahmens aufzuspüren und eine Durchschnittsstrategie zu verwenden, um zusätzliche Positionen zu eröffnen, wenn sich der Markt gegen den ursprünglichen Handel bewegt. Dies zielt darauf ab, das Gesamtergebnis des Handels zu verbessern und gleichzeitig das Risiko durch den Einsatz von Trailing-Stops und Breakeven-Anpassungen zu steuern. Um dies zu erreichen, identifizieren wir zunächst Pin-Bars in Bezug auf ein Unterstützungs- oder Widerstandsniveau, das sich aus dem Schluss der vorherigen H4-Kerze ableitet, um sicherzustellen, dass die Handelsgeschäfte auf wichtige Marktzonen ausgerichtet sind.

Dann werden wir einen Mittelwertbildungsmechanismus einführen, um Positionen in vordefinierten Preisintervallen hinzuzufügen und so die Flexibilität unter volatilen Bedingungen zu erhöhen. Schließlich werden wir ein Dashboard zur Anzeige von Echtzeit-Handelsmetriken einbauen und visuelle Indikatoren wie Linien zur Markierung von Schlüsselebenen verwenden, um sicherzustellen, dass wir unsere Strategie effektiv überwachen und anpassen können. Schauen Sie sich an, was wir erreichen wollen, und dann können wir mit der Umsetzung beginnen.

STRATEGIE


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, öffnen wir den MetaEditor, gehen zum Navigator, suchen den Ordner der Indikatoren, klicken auf die Registerkarte „Neu“ und folgen den Anweisungen, um die Datei zu erstellen. Sobald das Programm erstellt ist, werden wir in der Programmierumgebung damit beginnen, einige Eingaben und globale Variablen zu deklarieren, die das Programm dynamischer machen werden.

//+------------------------------------------------------------------+
//|                                      a. Pin Bar Averaging EA.mq5 |
//|                           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

#include <Trade\Trade.mqh>                         //--- Include Trade library for trading operations
CTrade obj_Trade;                                  //--- Instantiate trade object

//+------------------------------------------------------------------+
//| Trading signal enumeration                                       |
//+------------------------------------------------------------------+
enum EnableTradingBySignal {                       //--- Define trading signal enum
   ENABLED  = 1,                                   // Enable trading signals
   DISABLED = 0                                    // Disable trading signals
};

//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input bool   useSignalMode = DISABLED;             // Set signal mode (ENABLED/DISABLED)
input int    orderDistancePips = 50;               // Set order distance (pips)
input double lotMultiplier = 1;                    // Set lot size multiplier
input bool   useRSIFilter = false;                 // Enable RSI filter
input int    magicNumber = 123456789;              // Set magic number
input double initialLotSize = 0.01;                // Set initial lot size
input int    compoundPercent = 2;                  // Set compounding percent (0 for fixed lots)
input int    maxOrders = 5;                        // Set maximum orders
input double stopLossPips = 400;                   // Set stop loss (pips)
input double takeProfitPips = 200;                 // Set take profit (pips)
input bool   useAutoTakeProfit = true;             // Enable auto take profit
input bool   useTrailingStop = true;               // Enable trailing stop
input double trailingStartPips = 15;               // Set trailing start (pips)
input double breakevenPips = 10;                   // Set breakeven (pips)
input string orderComment = "Forex_Algo_Trader";   // Set order comment
input color  lineColor = clrBlue;                  // Set line color
input int    lineWidth = 2;                        // Set line width

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
bool   isTradingAllowed();                         //--- Declare trading allowed check
double slBreakevenMinus = 0;                       //--- Initialize breakeven minus
double normalizedPoint;                            //--- Declare normalized point
ulong  currentTicket = 0;                          //--- Initialize current ticket
double buyCount, currentBuyLot, totalBuyLots;      //--- Declare buy metrics
double sellCount, currentSellLot, totalSellLots;   //--- Declare sell metrics
double totalSum, totalSwap;                        //--- Declare total sum and swap
double buyProfit, sellProfit, totalOperations;     //--- Declare profit and operations
double buyWeightedSum, sellWeightedSum;            //--- Declare weighted sums
double buyBreakEvenPrice, sellBreakEvenPrice;      //--- Declare breakeven prices
double minBuyLot, minSellLot;                      //--- Declare minimum lot sizes
double maxSellPrice, minBuyPrice;                  //--- Declare price extremes

Um den Grundstein für Pin Bar Averaging System in MQL5 zu legen und um den Handel auf der Basis von Pin-Bar-Mustern mit einem robusten Positionsmanagementsystem zu automatisieren, binden wir zunächst die Bibliothek „<Trade\Trade.mqh>“ ein und instanziieren „obj_Trade“ als CTrade-Objekt, um Handelsoperationen wie das Öffnen und Schließen von Positionen zu behandeln. Dann definieren wir die Enumeration „EnableTradingBySignal“ mit „ENABLED“ (1) und „DISABLED“ (0), um zu steuern, ob Handelssignale für das Positionsmanagement verwendet werden. Als Nächstes richten wir Eingabeparameter ein, um den EA anzupassen: ein Boolesches An und Aus, um den Signalmodus umzuschalten, den Auftragsabstand in Pips, den Multiplikator für die Losgröße, den RSI-Filter, die magische Zahl für die Handelsidentifizierung, die anfängliche Losgröße, den Prozentsatz für die Aufzinsung (0 für feste Lots), die maximalen Aufträge, den Stop-Loss und den Take-Profit in Pips, die Kippschalter für den automatischen Take-Profit und den Trailing-Stop, den Trailing-Start und den Breakeven in Pips, den Auftragskommentar sowie die Linienfarbe und -breite für die visuellen Indikatoren.

Zum Schluss deklarieren wir die globalen Variablen: die Funktion „isTradingAllowed“ zur Überprüfung der Handelsbedingungen, „slBreakevenMinus“ initialisiert mit 0 für Stop-Loss-Anpassungen, „normalizedPoint“ für die Preisskalierung, „currentTicket“ zur Verfolgung von Handelsgeschäften, Zählern und Summen wie „buyCount“, „currentBuyLot“, „totalBuyLots“, „sellCount“, „currentSellLot“, „totalSellLots“, „totalSum“, „totalSwap“, „buyProfit“, „sellProfit“, „totalOperations“, „buyWeightedSum“, „sellWeightedSum“, „buyBreakEvenPrice“, „sellBreakEvenPrice“, „minBuyLot“, „minSellLot“, „maxSellPrice“ und „minBuyPrice“ für Positionsmetriken, die das Kerngerüst des EA für die Pinbar-Erkennung und Mittelwertbildung bilden. Wir können nun mit der Initialisierung des Programms fortfahren, da die meiste Arbeit in der Tick-basierten Produktion erledigt wird.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   normalizedPoint = _Point;                       //--- Initialize point value
   if (_Digits == 5 || _Digits == 3) {             //--- Check for 5 or 3 digit symbols
      normalizedPoint *= 10;                       //--- Adjust point value
   }
   ChartSetInteger(0, CHART_SHOW_GRID, false);     //--- Disable chart grid
   obj_Trade.SetExpertMagicNumber(magicNumber);    //--- Set magic number for trade object
   obj_Trade.SetTypeFilling(ORDER_FILLING_IOC);    //--- Set order filling type
   return(INIT_SUCCEEDED);                         //--- Return success
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   ObjectsDeleteAll(0);                            //--- Delete all chart objects
   ChartRedraw(0);                                 //--- Redraw chart
}

Zunächst initialisieren wir in OnInit „normalizedPoint“ auf _Point und passen ihn an, indem wir ihn bei 5- oder 3-stelligen Symbolen mit _Digits mit 10 multiplizieren, um genaue Preisberechnungen zu gewährleisten, deaktivieren das Chart-Gitter mit ChartSetInteger und setzen CHART_SHOW_GRID auf false, um eine sauberere Anzeige zu erhalten, konfigurieren „obj_Trade“ mit „SetExpertMagicNumber“ unter Verwendung von „magicNumber“ für die Trade-Identifikation, setzen wir den Füllungstyp der Aufträge auf „ORDER_FILLING_IOC“ mit „SetTypeFilling“ und geben Sie „INIT_SUCCEEDED“ zurück, um die erfolgreiche Einrichtung zu bestätigen. Dann gehen wir zu OnDeinit über, wo wir alle Chart-Objekte mit ObjectsDeleteAll entfernen, um visuelle Elemente wie das Dashboard und die Linien, die wir später definieren werden, zu löschen. Wir haben dies nur getan, damit wir sicher sein können, das Chart bereits zu bereinigen, und rufen ChartRedraw auf, um das Chart zu aktualisieren und einen sauberen Ausgang zu gewährleisten. Bevor wir uns in die komplexe Handelslogik vertiefen, wollen wir einige Hilfsfunktionen definieren, die wir benötigen, um das Programm dynamisch und leicht zu pflegen zu machen.

//+------------------------------------------------------------------+
//| Count total trades                                               |
//+------------------------------------------------------------------+
int CountTrades() {
   int positionCount = 0;                         //--- Initialize position count
   for (int trade = PositionsTotal() - 1; trade >= 0; trade--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(trade);    //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL || PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check trade type
         positionCount++;                         //--- Increment position count
      }
   }
   return(positionCount);                         //--- Return total count
}

//+------------------------------------------------------------------+
//| Count buy trades                                                 |
//+------------------------------------------------------------------+
int CountTradesBuy() {
   int buyPositionCount = 0;                      //--- Initialize buy position count
   for (int trade = PositionsTotal() - 1; trade >= 0; trade--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(trade);    //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
         buyPositionCount++;                      //--- Increment buy count
      }
   }
   return(buyPositionCount);                      //--- Return buy count
}

//+------------------------------------------------------------------+
//| Count sell trades                                                |
//+------------------------------------------------------------------+
int CountTradesSell() {
   int sellPositionCount = 0;                     //--- Initialize sell position count
   for (int trade = PositionsTotal() - 1; trade >= 0; trade--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(trade);    //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
         sellPositionCount++;                     //--- Increment sell count
      }
   }
   return(sellPositionCount);                     //--- Return sell count
}

//+------------------------------------------------------------------+
//| Normalize price                                                  |
//+------------------------------------------------------------------+
double NormalizePrice(double price) {
   return(NormalizeDouble(price, _Digits));       //--- Normalize price to symbol digits
}

//+------------------------------------------------------------------+
//| Get lot digit for normalization                                  |
//+------------------------------------------------------------------+
int fnGetLotDigit() {
   double lotStepValue = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP); //--- Get lot step value
   if (lotStepValue == 1) return(0);              //--- Return 0 for step 1
   if (lotStepValue == 0.1) return(1);            //--- Return 1 for step 0.1
   if (lotStepValue == 0.01) return(2);           //--- Return 2 for step 0.01
   if (lotStepValue == 0.001) return(3);          //--- Return 3 for step 0.001
   if (lotStepValue == 0.0001) return(4);         //--- Return 4 for step 0.0001
   return(1);                                     //--- Default to 1
}

//+------------------------------------------------------------------+
//| Check buy orders for specific magic number                       |
//+------------------------------------------------------------------+
int CheckBuyOrders(int magic) {
   int buyOrderCount = 0;                         //--- Initialize buy order count
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);         //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic
      if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
            buyOrderCount++;                      //--- Increment buy count
            break;                                //--- Exit loop
         }
      }
   }
   return(buyOrderCount);                         //--- Return buy order count
}

//+------------------------------------------------------------------+
//| Check sell orders for specific magic number                      |
//+------------------------------------------------------------------+
int CheckSellOrders(int magic) {
   int sellOrderCount = 0;                         //--- Initialize sell order count
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);         //--- Get position ticket
      if (ticket == 0) continue;                   //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic
      if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
            sellOrderCount++;                      //--- Increment sell count
            break;                                 //--- Exit loop
         }
      }
   }
   return(sellOrderCount);                         //--- Return sell order count
}

//+------------------------------------------------------------------+
//| Check total buy orders                                           |
//+------------------------------------------------------------------+
int CheckTotalBuyOrders(int magic) {
   int totalBuyOrderCount = 0;                      //--- Initialize total buy order count
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);          //--- Get position ticket
      if (ticket == 0) continue;                    //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic
      if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
            totalBuyOrderCount++;                   //--- Increment buy count
         }
      }
   }
   return(totalBuyOrderCount);                      //--- Return total buy count
}

//+------------------------------------------------------------------+
//| Check total sell orders                                          |
//+------------------------------------------------------------------+
int CheckTotalSellOrders(int magic) {
   int totalSellOrderCount = 0;                      //--- Initialize total sell order count
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);           //--- Get position ticket
      if (ticket == 0) continue;                     //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic
      if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
            totalSellOrderCount++;                   //--- Increment sell count
         }
      }
   }
   return(totalSellOrderCount);                      //--- Return total sell count
}

//+------------------------------------------------------------------+
//| Check market buy orders                                          |
//+------------------------------------------------------------------+
int CheckMarketBuyOrders() {
   int marketBuyCount = 0;                        //--- Initialize market buy count
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);         //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching magic
      if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
            marketBuyCount++;                     //--- Increment buy count
         }
      }
   }
   return(marketBuyCount);                        //--- Return market buy count
}

//+------------------------------------------------------------------+
//| Check market sell orders                                         |
//+------------------------------------------------------------------+
int CheckMarketSellOrders() {
   int marketSellCount = 0;                       //--- Initialize market sell count
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);         //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching magic
      if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
            marketSellCount++;                    //--- Increment sell count
         }
      }
   }
   return(marketSellCount);                       //--- Return market sell count
}

//+------------------------------------------------------------------+
//| Close all buy positions                                          |
//+------------------------------------------------------------------+
void CloseBuy() {
   while (CheckMarketBuyOrders() > 0) {           //--- Check buy orders exist
      for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
         ulong ticket = PositionGetTicket(i);      //--- Get position ticket
         if (ticket == 0) continue;               //--- Skip invalid tickets
         if (PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check symbol and magic
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
               obj_Trade.PositionClose(ticket);   //--- Close position
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Close all sell positions                                         |
//+------------------------------------------------------------------+
void CloseSell() {
   while (CheckMarketSellOrders() > 0) {          //--- Check sell orders exist
      for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
         ulong ticket = PositionGetTicket(i);      //--- Get position ticket
         if (ticket == 0) continue;               //--- Skip invalid tickets
         if (PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check symbol and magic
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
               obj_Trade.PositionClose(ticket);   //--- Close position
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Calculate lot size                                               |
//+------------------------------------------------------------------+
double GetLots() {
   double calculatedLot;                          //--- Initialize calculated lot
   double minLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); //--- Get minimum lot
   double maxLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX); //--- Get maximum lot
   if (compoundPercent != 0) {                    //--- Check compounding
      calculatedLot = NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE) * compoundPercent / 100 / 10000, fnGetLotDigit()); //--- Calculate compounded lot
      if (calculatedLot < minLot) calculatedLot = minLot; //--- Enforce minimum lot
      if (calculatedLot > maxLot) calculatedLot = maxLot; //--- Enforce maximum lot
   } else {
      calculatedLot = initialLotSize;             //--- Use fixed lot size
   }
   return(calculatedLot);                         //--- Return calculated lot
}

//+------------------------------------------------------------------+
//| Check account free margin                                        |
//+------------------------------------------------------------------+
double AccountFreeMarginCheck(string symbol, int orderType, double volume) {
   double marginRequired = 0.0;                   //--- Initialize margin required
   double price = orderType == ORDER_TYPE_BUY ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID); //--- Get price
   double calculatedMargin;                       //--- Declare calculated margin
   bool success = OrderCalcMargin(orderType == ORDER_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, symbol, volume, price, calculatedMargin); //--- Calculate margin
   if (success) marginRequired = calculatedMargin; //--- Set margin if successful
   return AccountInfoDouble(ACCOUNT_MARGIN_FREE) - marginRequired; //--- Return free margin
}

//+------------------------------------------------------------------+
//| Check if trading is allowed                                      |
//+------------------------------------------------------------------+
bool isTradingAllowed() {
   bool isAllowed = false;                        //--- Initialize allowed flag
   return(true);                                  //--- Return true
}

Hier implementieren wir Hilfsfunktionen für das Programm, um das Zählen der Handelsgeschäfte, das Schließen von Positionen, die Berechnung der Losgröße, die Überprüfung der Margin und die Handelsberechtigungen zu verwalten und eine robuste Handelsabwicklung zu gewährleisten. Zunächst erstellen wir Funktionen zum Zählen von Handelsgeschäften: “CountTrades“ zählt die Gesamtpositionen, indem es durch PositionsTotal iteriert, mit PositionGetTicket auf gültige Tickets prüft, „Symbol“ und „magicNumber“ abgleicht und „positionCount“ für Kauf- oder Verkaufspositionen; „CountTradesBuy“ und „CountTradesSell“ zählen Kauf- bzw. Verkaufspositionen und filtern nach POSITION_TYPE_BUY oder „POSITION_TYPE_SELL“; „CheckBuyOrders“ und „CheckSellOrders“ erkennen mindestens eine Kauf- oder Verkaufsposition mit einer bestimmten magischen Zahl und brechen nach der ersten Übereinstimmung ab; „CheckTotalBuyOrders“ und „CheckTotalSellOrders“ zählen alle Kauf- oder Verkaufspositionen mit einer magischen Zahl; und „CheckMarketBuyOrders“ und „CheckMarketSellOrders“ zählen Kauf- oder Verkaufspositionen mit der magischen Zahl.

Dann implementieren wir „NormalizePrice“, um die Preise mit NormalizeDouble auf _Digits zu normalisieren, und „fnGetLotDigit“, um die entsprechende Dezimalpräzision für Losgrößen auf der Grundlage von SYMBOL_VOLUME_STEP zurückzugeben (z. B. 0 für 1, 1 für 0,1). Als Nächstes entwickeln wir „CloseBuy“ und „CloseSell“, um alle Kauf- oder Verkaufspositionen zu schließen, indem wir die Positionen in einer Schleife durchgehen, „Symbol“ und „magicNumber“ überprüfen und „obj_Trade.PositionClose“ verwenden, bis „CheckMarketBuyOrders“ oder „CheckMarketSellOrders“ 0 ergibt. Zuletzt implementieren wir „GetLots“ zur Berechnung der Losgröße auf der Grundlage von „compoundPercent“ (Normalisierung von „AccountInfoDouble (ACCOUNT_BALANCE) * compoundPercent / 100 / 10000“ mit „fnGetLotDigit“, eingeschränkt durch SYMBOL_VOLUME_MIN und „SYMBOL_VOLUME_MAX“) oder „initialLotSize“, und „AccountFreeMarginCheck“, um die verfügbare Marge zu berechnen, indem die erforderliche Marge mit OrderCalcMargin für den gegebenen Auftragstyp und das gegebene Volumen berechnet wird, und „isTradingAllowed“ als Platzhalter, der true zurückgibt. Für die Visualisierung benötigen wir Funktionen zum Zeichnen von Linien und Beschriftungen im Chart.

//+------------------------------------------------------------------+
//| Draw support/resistance line                                     |
//+------------------------------------------------------------------+
void MakeLine(double price) {
   string name = "level";                         //--- Set line name
   if (ObjectFind(0, name) != -1) {               //--- Check if line exists
      ObjectMove(0, name, 0, iTime(Symbol(), PERIOD_CURRENT, 0), price); //--- Move line
      return;                                     //--- Exit function
   }
   ObjectCreate(0, name, OBJ_HLINE, 0, 0, price); //--- Create horizontal line
   ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); //--- Set color
   ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); //--- Set style
   ObjectSetInteger(0, name, OBJPROP_WIDTH, lineWidth); //--- Set width
   ObjectSetInteger(0, name, OBJPROP_BACK, true); //--- Set to background
}

//+------------------------------------------------------------------+
//| Create dashboard label                                           |
//+------------------------------------------------------------------+
void LABEL(string labelName, string fontName, int fontSize, int xPosition, int yPosition, color textColor, int corner, string labelText) {
   if (ObjectFind(0, labelName) < 0) {            //--- Check if label exists
      ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0); //--- Create label
   }
   ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); //--- Set label text
   ObjectSetString(0, labelName, OBJPROP_FONT, fontName); //--- Set font
   ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, fontSize); //--- Set font size
   ObjectSetInteger(0, labelName, OBJPROP_COLOR, textColor); //--- Set text color
   ObjectSetInteger(0, labelName, OBJPROP_CORNER, corner); //--- Set corner
   ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, xPosition); //--- Set x position
   ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, yPosition); //--- Set y position
}

Um visuelle Elemente für das Programm zu erstellen, entwickeln wir die Funktion „MakeLine“, die eine horizontale Linie bei einem bestimmten „Preis“ zeichnet, um ein Unterstützungs- oder Widerstandsniveau zu markieren, indem wir ihren Namen auf „level“ setzen. Sie prüft mit ObjectFind, ob sie existiert, verschiebt sie mit ObjectMove auf die aktuelle Barzeit von iTime, wenn sie gefunden wurde, oder erstellt sie mit ObjectCreate als OBJ_HLINE, und setzt „OBJPROP_COLOR“ auf „lineColor“, OBJPROP_STYLE auf „STYLE_SOLID“, „OBJPROP_WIDTH“ auf „lineWidth“ und „OBJPROP_BACK“ auf „true“ mit ObjectSetInteger für die Hintergrundplatzierung.

Anschließend wird die Funktion „LABEL“ implementiert, die Dashboard-Etiketten erstellt oder aktualisiert, indem sie prüft, ob „labelName“ existiert, andernfalls ein OBJ_LABEL mit „ObjectCreate“ erstellt und die Eigenschaften mit „ObjectSetString“ für „OBJPROP_TEXT“ auf „labelText“ und „OBJPROP_FONT“ auf „fontName“, und „ObjectSetInteger“ für „OBJPROP_FONTSIZE“ auf „fontSize“, „OBJPROP_COLOR“ auf „textColor“, OBJPROP_CORNER auf „Ecke“, „OBJPROP_XDISTANCE“ auf „xPosition“ und „OBJPROP_YDISTANCE“ auf die y-Position. Wir können dann Indikator-Nutzenfunktionen definieren, die wir verwenden werden.

//+------------------------------------------------------------------+
//| Calculate ATR indicator                                          |
//+------------------------------------------------------------------+
double MyiATR(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift) {
   int handle = iATR(symbol, timeframe, period);  //--- Create ATR handle
   if (handle == INVALID_HANDLE) return 0;        //--- Check invalid handle
   double buffer[1];                              //--- Declare buffer
   if (CopyBuffer(handle, 0, shift, 1, buffer) != 1) buffer[0] = 0; //--- Copy ATR value
   IndicatorRelease(handle);                      //--- Release handle
   return buffer[0];                              //--- Return ATR value
}

//+------------------------------------------------------------------+
//| Check bullish engulfing pattern                                  |
//+------------------------------------------------------------------+
bool BullishEngulfingExists() {
   if (iOpen(Symbol(), PERIOD_CURRENT, 1) <= iClose(Symbol(), PERIOD_CURRENT, 2) && iClose(Symbol(), PERIOD_CURRENT, 1) >= iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) >= 10 * _Point && iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1) >= 10 * _Point) { //--- Check bullish engulfing conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check bullish harami pattern                                     |
//+------------------------------------------------------------------+
bool BullishHaramiExists() {
   if (iClose(Symbol(), PERIOD_CURRENT, 2) < iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 1) < iClose(Symbol(), PERIOD_CURRENT, 1) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) > MyiATR(Symbol(), PERIOD_CURRENT, 14, 2) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) > 4 * (iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1))) { //--- Check bullish harami conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check doji at bottom pattern                                     |
//+------------------------------------------------------------------+
bool DojiAtBottomExists() {
   if (iOpen(Symbol(), PERIOD_CURRENT, 3) - iClose(Symbol(), PERIOD_CURRENT, 3) >= 8 * _Point && MathAbs(iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2)) <= 1 * _Point && iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1) >= 8 * _Point) { //--- Check doji at bottom conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check doji at top pattern                                        |
//+------------------------------------------------------------------+
bool DojiAtTopExists() {
   if (iClose(Symbol(), PERIOD_CURRENT, 3) - iOpen(Symbol(), PERIOD_CURRENT, 3) >= 8 * _Point && MathAbs(iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2)) <= 1 * _Point && iOpen(Symbol(), PERIOD_CURRENT, 1) - iClose(Symbol(), PERIOD_CURRENT, 1) >= 8 * _Point) { //--- Check doji at top conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check bearish harami pattern                                     |
//+------------------------------------------------------------------+
bool BearishHaramiExists() {
   if (iClose(Symbol(), PERIOD_CURRENT, 2) > iClose(Symbol(), PERIOD_CURRENT, 1) && iOpen(Symbol(), PERIOD_CURRENT, 2) < iOpen(Symbol(), PERIOD_CURRENT, 1) && iClose(Symbol(), PERIOD_CURRENT, 2) > iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 1) > iClose(Symbol(), PERIOD_CURRENT, 1) && iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2) > MyiATR(Symbol(), PERIOD_CURRENT, 14, 2) && iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2) > 4 * (iOpen(Symbol(), PERIOD_CURRENT, 1) - iClose(Symbol(), PERIOD_CURRENT, 1))) { //--- Check bearish harami conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check long up candle pattern                                     |
//+------------------------------------------------------------------+
bool LongUpCandleExists() {
   if (iOpen(Symbol(), PERIOD_CURRENT, 2) < iClose(Symbol(), PERIOD_CURRENT, 2) && iHigh(Symbol(), PERIOD_CURRENT, 2) - iLow(Symbol(), PERIOD_CURRENT, 2) >= 40 * _Point && iHigh(Symbol(), PERIOD_CURRENT, 2) - iLow(Symbol(), PERIOD_CURRENT, 2) > 2.5 * MyiATR(Symbol(), PERIOD_CURRENT, 14, 2) && iClose(Symbol(), PERIOD_CURRENT, 1) < iOpen(Symbol(), PERIOD_CURRENT, 1) && iOpen(Symbol(), PERIOD_CURRENT, 1) - iClose(Symbol(), PERIOD_CURRENT, 1) > 10 * _Point) { //--- Check long up candle conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check long down candle pattern                                   |
//+------------------------------------------------------------------+
bool LongDownCandleExists() {
   if (iOpen(Symbol(), PERIOD_CURRENT, 1) > iClose(Symbol(), PERIOD_CURRENT, 1) && iHigh(Symbol(), PERIOD_CURRENT, 1) - iLow(Symbol(), PERIOD_CURRENT, 1) >= 40 * _Point && iHigh(Symbol(), PERIOD_CURRENT, 1) - iLow(Symbol(), PERIOD_CURRENT, 1) > 2.5 * MyiATR(Symbol(), PERIOD_CURRENT, 14, 1)) { //--- Check long down candle conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check bearish engulfing pattern                                  |
//+------------------------------------------------------------------+
bool BearishEngulfingExists() {
   if (iOpen(Symbol(), PERIOD_CURRENT, 1) >= iClose(Symbol(), PERIOD_CURRENT, 2) && iClose(Symbol(), PERIOD_CURRENT, 1) <= iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) >= 10 * _Point && iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1) >= 10 * _Point) { //--- Check bearish engulfing conditions
      return(true);                               //--- Return true
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Calculate average range over 4 days                              |
//+------------------------------------------------------------------+
double AveRange4() {
   double rangeSum = 0;                           //--- Initialize range sum
   int count = 0;                                 //--- Initialize count
   int index = 1;                                 //--- Initialize index
   while (count < 4) {                            //--- Loop until 4 days
      MqlDateTime dateTime;                       //--- Declare datetime structure
      TimeToStruct(iTime(Symbol(), PERIOD_CURRENT, index), dateTime); //--- Convert time
      if (dateTime.day_of_week != 0) {            //--- Check non-Sunday
         rangeSum += iHigh(Symbol(), PERIOD_CURRENT, index) - iLow(Symbol(), PERIOD_CURRENT, index); //--- Add range
         count++;                                 //--- Increment count
      }
      index++;                                    //--- Increment index
   }
   return(rangeSum / 4.0);                        //--- Return average range
}

//+------------------------------------------------------------------+
//| Check buy pinbar                                                 |
//+------------------------------------------------------------------+
bool IsBuyPinbar() {
   double currentOpen, currentClose, currentHigh, currentLow; //--- Declare current candle variables
   double previousHigh, previousLow, previousClose, previousOpen; //--- Declare previous candle variables
   double currentRange, previousRange, currentHigherPart, currentHigherPart1; //--- Declare range variables
   currentOpen = iOpen(Symbol(), PERIOD_CURRENT, 1); //--- Get current open
   currentClose = iClose(Symbol(), PERIOD_CURRENT, 1); //--- Get current close
   currentHigh = iHigh(Symbol(), PERIOD_CURRENT, 0); //--- Get current high
   currentLow = iLow(Symbol(), PERIOD_CURRENT, 1); //--- Get current low
   previousOpen = iOpen(Symbol(), PERIOD_CURRENT, 2); //--- Get previous open
   previousClose = iClose(Symbol(), PERIOD_CURRENT, 2); //--- Get previous close
   previousHigh = iHigh(Symbol(), PERIOD_CURRENT, 2); //--- Get previous high
   previousLow = iLow(Symbol(), PERIOD_CURRENT, 2); //--- Get previous low
   currentRange = currentHigh - currentLow;       //--- Calculate current range
   previousRange = previousHigh - previousLow;    //--- Calculate previous range
   currentHigherPart = currentHigh - currentRange * 0.4; //--- Calculate higher part
   currentHigherPart1 = currentHigh - currentRange * 0.4; //--- Calculate higher part
   double averageDailyRange = AveRange4();        //--- Get average daily range
   if ((currentClose > currentHigherPart1 && currentOpen > currentHigherPart) && //--- Check close/open in higher third
       (currentRange > averageDailyRange * 0.5) && //--- Check pinbar size
       (currentLow + currentRange * 0.25 < previousLow)) { //--- Check nose length
      double lowArray[3];                         //--- Declare low array
      CopyLow(Symbol(), PERIOD_CURRENT, 3, 3, lowArray); //--- Copy low prices
      int minIndex = ArrayMinimum(lowArray);      //--- Find minimum low index
      if (lowArray[minIndex] > currentLow) return(true); //--- Confirm buy pinbar
   }
   return(false);                                 //--- Return false
}

//+------------------------------------------------------------------+
//| Check sell pinbar                                                |
//+------------------------------------------------------------------+
bool IsSellPinbar() {
   double currentOpen, currentClose, currentHigh, currentLow; //--- Declare current candle variables
   double previousHigh, previousLow, previousClose, previousOpen; //--- Declare previous candle variables
   double currentRange, previousRange, currentLowerPart, currentLowerPart1; //--- Declare range variables
   currentOpen = iOpen(Symbol(), PERIOD_CURRENT, 1); //--- Get current open
   currentClose = iClose(Symbol(), PERIOD_CURRENT, 1); //--- Get current close
   currentHigh = iHigh(Symbol(), PERIOD_CURRENT, 1); //--- Get current high
   currentLow = iLow(Symbol(), PERIOD_CURRENT, 1); //--- Get current low
   previousOpen = iOpen(Symbol(), PERIOD_CURRENT, 2); //--- Get previous open
   previousClose = iClose(Symbol(), PERIOD_CURRENT, 2); //--- Get previous close
   previousHigh = iHigh(Symbol(), PERIOD_CURRENT, 2); //--- Get previous high
   previousLow = iLow(Symbol(), PERIOD_CURRENT, 2); //--- Get previous low
   currentRange = currentHigh - currentLow;       //--- Calculate current range
   previousRange = previousHigh - previousLow;    //--- Calculate previous range
   currentLowerPart = currentLow + currentRange * 0.4; //--- Calculate lower part
   currentLowerPart1 = currentLow + currentRange * 0.4; //--- Calculate lower part
   double averageDailyRange = AveRange4();        //--- Get average daily range
   if ((currentClose < currentLowerPart1 && currentOpen < currentLowerPart) && //--- Check close/open in lower third
       (currentRange > averageDailyRange * 0.5) && //--- Check pinbar size
       (currentHigh - currentRange * 0.25 > previousHigh)) { //--- Check nose length
      double highArray[3];                        //--- Declare high array
      CopyHigh(Symbol(), PERIOD_CURRENT, 3, 3, highArray); //--- Copy high prices
      int maxIndex = ArrayMaximum(highArray);     //--- Find maximum high index
      if (highArray[maxIndex] < currentHigh) return(true); //--- Confirm sell pinbar
   }
   return(false);                                 //--- Return false
}

Hier implementieren wir Funktionen zur Erkennung von Kerzen-Mustern und zur Berechnung der Average True Range (ATR) für unser System. Zunächst erstellen wir die Funktion „MyiATR“, die die ATR berechnet, indem sie ein Handle mit der Funktion iATR für das angegebene Symbol, den Zeitrahmen und die Periode erstellt, 0 zurückgibt, wenn das Handle ungültig ist, den ATR-Wert mit CopyBuffer in einen Puffer kopiert, das Handle mit IndicatorRelease freigibt und den ATR-Wert zurückgibt.

Anschließend werden Funktionen zur Erkennung von Kerzen-Mustern implementiert: “BullishEngulfingExists“ prüft, ob die aktuelle Kerze die vorhergehende Abwärtskerze mit signifikanten Körpergrößen verschlingt; „BullishHaramiExists“ identifiziert eine kleine Aufwärtskerze innerhalb einer größeren Abwärtskerze unter Verwendung von „MyiATR“ zum Größenvergleich; „DojiAtBottomExists“ erkennt einen Doji zwischen einer Ab- und Aufwärtskerze für ein Morgenstern-Muster; „DojiAtTopExists“ identifiziert einen Doji zwischen einer Auf- und Abwärtskerze für ein Abendsternmuster; „BearishHaramiExists“ prüft auf eine kleine Abwärtskerze innerhalb einer größeren Aufwärtskerze; „LongUpCandleExists“ bestätigt eine starke Aufwärtskerze gefolgt von einer Abwärtskerze unter Verwendung der ATR; „LongDownCandleExists“ erkennt eine starke Abwärtskerze; und „BearishEngulfingExists“ verifiziert eine Abwärtskerze, die eine Aufwärtskerze umschließt.

Zuletzt implementieren wir „IsBuyPinbar“ und „IsSellPinbar“, die Pinbars identifizieren, indem sie prüfen, ob der Schluss- und der Eröffnungswert der aktuellen Kerze im oberen oder unteren Drittel ihrer Spanne liegen, die Spanne die Hälfte der durchschnittlichen täglichen Spanne von „AveRange4“ (Durchschnitt der Hoch-Tief-Spanne über vier Tage, die keine Sonntage sind) übersteigt und die Spitze des Pinbars über den Tiefst- oder Höchststand der vorherigen Kerze hinausgeht. Dies wird durch den Vergleich der jüngsten Tiefst- oder Höchststände mit den Funktionen CopyLow oder CopyHigh und „ArrayMinimum“ oder ArrayMaximum bestätigt. Dann können wir einige Funktionen definieren, um den Signaltyp für Anzeigezwecke und den gewichteten Preis für die Positionsverwaltung zu erhalten.

//+------------------------------------------------------------------+
//| Analyze candlestick patterns                                     |
//+------------------------------------------------------------------+
string CandleStick_Analyzer() {
   string candlePattern, comment1 = "", comment2 = "", comment3 = ""; //--- Initialize pattern strings
   string comment4 = "", comment5 = "", comment6 = "", comment7 = ""; //--- Initialize pattern strings
   string comment8 = "", comment9 = "";                               //--- Initialize pattern strings
   if (BullishEngulfingExists()) comment1 = " Bullish Engulfing ";    //--- Check bullish engulfing
   if (BullishHaramiExists()) comment2 = " Bullish Harami ";          //--- Check bullish harami
   if (LongUpCandleExists()) comment3 = " Bullish LongUp ";           //--- Check long up candle
   if (DojiAtBottomExists()) comment4 = " MorningStar Doji ";         //--- Check morning star doji
   if (DojiAtTopExists()) comment5 = " EveningStar Doji ";            //--- Check evening star doji
   if (BearishHaramiExists()) comment6 = " Bearish Harami ";          //--- Check bearish harami
   if (BearishEngulfingExists()) comment7 = " Bearish Engulfing ";    //--- Check bearish engulfing
   if (LongDownCandleExists()) comment8 = " Bearish LongDown ";       //--- Check long down candle
   candlePattern = comment1 + comment2 + comment3 + comment4 + comment5 + comment6 + comment7 + comment8 + comment9; //--- Combine patterns
   return(candlePattern);                                             //--- Return combined pattern
}

//+------------------------------------------------------------------+
//| Calculate average price for order type                           |
//+------------------------------------------------------------------+
double rata_price(int orderType) {
   double totalVolume = 0;                        //--- Initialize total volume
   double weightedOpenSum = 0;                    //--- Initialize weighted open sum
   double averagePrice = 0;                       //--- Initialize average price
   for (int positionIndex = 0; positionIndex < PositionsTotal(); positionIndex++) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber && (PositionGetInteger(POSITION_TYPE) == orderType)) { //--- Check position match
         totalVolume += PositionGetDouble(POSITION_VOLUME); //--- Add volume
         weightedOpenSum += (PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN)); //--- Add weighted open
      }
   }
   if (totalVolume != 0) {                        //--- Check non-zero volume
      averagePrice = weightedOpenSum / totalVolume; //--- Calculate average price
   }
   return(averagePrice);                          //--- Return average price
}

Für ein verbessertes Positionsmanagement erstellen wir die Funktion „CandleStick_Analyzer“, die String-Variablen wie „comment1“ bis „comment9“ als leer initialisiert, mit Funktionen wie „BullishEngulfingExists“, die wir bereits definiert haben, auf Kerzen-Muster prüft, den entsprechenden Variablen beschreibende Strings zuweist (z. B., „ Bullish Engulfing „) den entsprechenden Variablen zu, wenn Muster erkannt werden, und verkettet sie in „candlePattern“, um eine kombinierte Zeichenfolge der erkannten Muster für die Dashboard-Anzeige zurückzugeben.

Anschließend implementieren wir die Funktion „rata_price“, die den gewichteten Durchschnittspreis für einen bestimmten „OrderType“ (Kauf oder Verkauf) berechnet, indem wir „totalVolume“ und „weightedOpenSum“ auf 0 initialisiert, durch „PositionsTotal“ iteriert, um POSITION_VOLUME und das Produkt aus „POSITION_VOLUME“ und POSITION_PRICE_OPEN für Positionen zu summieren, die „Symbol“, „magicNumber“ und „orderType“ unter Verwendung von „PositionGetTicket“, PositionGetString und „PositionGetInteger“ und Berechnung von „averagePrice“ als „weightedOpenSum / totalVolume“. Wenn „totalVolume“ ungleich Null ist, wird das Ergebnis zurückgegeben, was eine kritische Musteranalyse für Handelssignale und genaue Durchschnittspreisberechnungen für die Durchschnittsbildung und Take-Profit-Anpassungen ermöglicht. Für die Positionen müssen wir zunächst ihre Kennzahlen erhalten. Lassen Sie uns dafür eine Logik definieren.

//+------------------------------------------------------------------+
//| Calculate position metrics                                       |
//+------------------------------------------------------------------+
void calculatePositionMetrics() {
   buyCount = 0;                                  //--- Reset buy count
   currentBuyLot = 0;                             //--- Reset current buy lot
   totalBuyLots = 0;                              //--- Reset total buy lots
   sellCount = 0;                                 //--- Reset sell count
   currentSellLot = 0;                            //--- Reset current sell lot
   totalSellLots = 0;                             //--- Reset total sell lots
   totalSum = 0;                                  //--- Reset total sum
   totalSwap = 0;                                 //--- Reset total swap
   buyProfit = 0;                                 //--- Reset buy profit
   sellProfit = 0;                                //--- Reset sell profit
   buyWeightedSum = 0;                            //--- Reset buy weighted sum
   sellWeightedSum = 0;                           //--- Reset sell weighted sum
   buyBreakEvenPrice = 0;                         //--- Reset buy breakeven price
   sellBreakEvenPrice = 0;                        //--- Reset sell breakeven price
   minBuyLot = 9999;                              //--- Initialize min buy lot
   minSellLot = 9999;                             //--- Initialize min sell lot
   maxSellPrice = 0;                              //--- Initialize max sell price
   minBuyPrice = 999999999;                       //--- Initialize min buy price
   for (int i = 0; i < PositionsTotal(); i++) {   //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);        //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; //--- Skip non-matching symbols
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
         buyCount++;                              //--- Increment buy count
         totalOperations++;                       //--- Increment total operations
         currentBuyLot = PositionGetDouble(POSITION_VOLUME); //--- Set current buy lot
         buyProfit += PositionGetDouble(POSITION_PROFIT); //--- Add buy profit
         totalBuyLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total buy lots
         minBuyLot = MathMin(minBuyLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min buy lot
         buyWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price
         minBuyPrice = MathMin(minBuyPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update min buy price
      }
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
         sellCount++;                             //--- Increment sell count
         totalOperations++;                       //--- Increment total operations
         currentSellLot = PositionGetDouble(POSITION_VOLUME); //--- Set current sell lot
         sellProfit += PositionGetDouble(POSITION_PROFIT); //--- Add sell profit
         totalSellLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total sell lots
         minSellLot = MathMin(minSellLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min sell lot
         sellWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price
         maxSellPrice = MathMax(maxSellPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update max sell price
      }
   }
   if (totalBuyLots > 0) {                        //--- Check buy lots
      buyBreakEvenPrice = buyWeightedSum / totalBuyLots; //--- Calculate buy breakeven
   }
   if (totalSellLots > 0) {                       //--- Check sell lots
      sellBreakEvenPrice = sellWeightedSum / totalSellLots; //--- Calculate sell breakeven
   }
}

Zur Berechnung der für die effiziente Verwaltung mehrerer Positionen wichtigen Metriken wird die Funktion „calculatePositionMetrics“ eingesetzt. Zunächst setzen wir die Schlüsselvariablen auf Null oder ihren jeweiligen Ausgangswert zurück, um eine genaue Verfolgung zu ermöglichen. Dann wird mit PositionsTotal durch alle Positionen iteriert, das Ticket jeder Position mit PositionGetTicket abgerufen, ungültige Tickets oder nicht übereinstimmende Symbole mit PositionGetString übersprungen und für Kaufpositionen (POSITION_TYPE_BUY) „buyCount“ und „totalOperations“ erhöht, „currentBuyLot“ gesetzt, Hinzufügen von „POSITION_PROFIT“ zu „buyProfit“ und „POSITION_VOLUME“ zu „totalBuyLots“, Aktualisieren von „minBuyLot“ mit MathMin, Hinzufügen des gewichteten offenen Preises zu „buyWeightedSum“ und Aktualisieren von „minBuyPrice“; für Verkaufspositionen (POSITION_TYPE_SELL) führen wir ähnliche Aktualisierungen für Verkaufsmetriken durch. Schließlich berechnen wir den „buyBreakEvenPrice“ als „buyWeightedSum / totalBuyLots“, wenn „totalBuyLots“ positiv ist, und den „sellBreakEvenPrice“ als „sellWeightedSum / totalSellLots“ wenn „totalSellLots“ positiv ist, was gewichtete Durchschnittspreise für das Breakeven-Management liefert und eine genaue Verfolgung der Positionsmetriken für die Mittelwertbildung und Risikokontrolle gewährleistet. Mit diesen Funktionen sind wir bereit, die Logik der Positionseröffnung zu beginnen. Wir werden dies in OnTick tun.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   static datetime previousBarTime = 0;           //--- Store previous bar time
   if (previousBarTime != iTime(Symbol(), PERIOD_CURRENT, 0)) { //--- Check new bar
      previousBarTime = iTime(Symbol(), PERIOD_CURRENT, 0); //--- Update previous bar time
      ChartRedraw(0);                             //--- Redraw chart
   } else {
      return;                                     //--- Exit if not new bar
   }
   if (iVolume(Symbol(), PERIOD_H4, 0) > iVolume(Symbol(), PERIOD_H4, 1)) return; //--- Exit if volume increased
   double supportResistanceLevel = NormalizeDouble(iClose(Symbol(), PERIOD_H4, 1), _Digits); //--- Get support/resistance level
   ObjectDelete(0, "level");                      //--- Delete existing level line
   MakeLine(supportResistanceLevel);              //--- Draw support/resistance line
   if (SymbolInfoInteger(Symbol(), SYMBOL_SPREAD) > 150) return; //--- Exit if spread too high
   int totalBuyPositions = 0;                     //--- Initialize buy positions count
   int totalSellPositions = 0;                    //--- Initialize sell positions count
   for (int i = 0; i < PositionsTotal(); i++) {   //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);        //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
         totalBuyPositions++;                     //--- Increment buy count
      }
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
         totalSellPositions++;                    //--- Increment sell count
      }
   }
}

In OnTick implementieren wir die anfängliche Logik für das Pin-Bar-Averaging-System, um Handelsentscheidungen und visuelle Aktualisierungen bei jedem neuen Balken zu verwalten. Zunächst wird geprüft, ob ein neuer Balken vorliegt, indem „previousBarTime“ (statisch, initialisiert auf 0) mit der aktuellen Balkenzeit von iTime für das aktuelle Symbol und die Periode bei Shift 0 verglichen wird, „previousBarTime“ aktualisiert wird und ChartRedraw aufgerufen wird, wenn ein neuer Balken erkannt wird, oder beendet wird, wenn nicht.

Wir steigen aus, wenn das Volumen des aktuellen H4-Balkens aus iVolume das Volumen des vorherigen Balkens übersteigt, um Perioden mit hoher Volatilität zu vermeiden. Als Nächstes berechnen wir das Unterstützungs-/Widerstandsniveau als normalisierten Schlusskurs des vorherigen H4-Balkens mit iClose und NormalizeDouble, löschen eine eventuell vorhandene „level“-Linie mit ObjectDelete und zeichnen eine neue horizontale Linie mit „MakeLine“ auf diesem Niveau. Zuletzt prüfen wir, ob der Spread aus SymbolInfoInteger 150 Punkte übersteigt, und beenden den Handel, wenn er zu hoch ist. Dann zählen wir die offenen Positionen, indem wir durch PositionsTotal iterieren und „PositionGetTicket“ verwenden, um Tickets zu erhalten, wobei wir ungültige oder nicht übereinstimmende Symbol- und „magicNumber“-Positionen überspringen und „totalBuyPositions“ oder „totalSellPositions“ für Kauf- oder Verkaufspositionen erhöhen, die mit der Funktion PositionGetInteger identifiziert wurden. Diese anfängliche Einrichtung stellt sicher, dass der EA nur bei neuen Bars mit günstigen Bedingungen handelt und eine aktualisierte visuelle Referenz beibehält. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

MARKIERUNG DES WIDERSTANDSNIVEAUS

Aus dem Bild ist ersichtlich, dass wir die Unterstützungs- und Widerstandsniveaus auf dem Chart dynamisch markieren. Nun müssen wir dazu übergehen, die Positionen dynamisch hinzuzufügen.

if (CheckMarketBuyOrders() < 70 && CheckMarketSellOrders() < 70) { //--- Check order limits
   if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == DISABLED) { //--- Check buy condition
      if (IsBuyPinbar() && totalBuyPositions < maxOrders && (isTradingAllowed() || totalBuyPositions > 0)) { //--- Check buy pinbar and limits
         double buyStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossPips * normalizedPoint, _Digits); //--- Calculate buy stop loss
         double buyTakeProfit = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + takeProfitPips * normalizedPoint, _Digits); //--- Calculate buy take profit
         if (AccountFreeMarginCheck(Symbol(), ORDER_TYPE_BUY, GetLots()) > 0) { //--- Check margin
            obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_ASK), buyStopLoss, buyTakeProfit, orderComment); //--- Open buy position
            if (useAutoTakeProfit) {             //--- Check auto take profit
               ModifyTP(ORDER_TYPE_BUY, rata_price(ORDER_TYPE_BUY) + takeProfitPips * normalizedPoint); //--- Modify take profit
            }
            CloseSell();                         //--- Close sell positions
         }
      }
   }
   if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == DISABLED) { //--- Check sell condition
      if (IsSellPinbar() && totalSellPositions < maxOrders && (isTradingAllowed() || totalSellPositions > 0)) { //--- Check sell pinbar and limits
         double sellStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) + stopLossPips * normalizedPoint, _Digits); //--- Calculate sell stop loss
         double sellTakeProfit = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - takeProfitPips * normalizedPoint, _Digits); //--- Calculate sell take profit
         if (AccountFreeMarginCheck(Symbol(), ORDER_TYPE_SELL, GetLots()) > 0) { //--- Check margin
            obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_BID), sellStopLoss, sellTakeProfit, orderComment); //--- Open sell position
            if (useAutoTakeProfit) {             //--- Check auto take profit
               ModifyTP(ORDER_TYPE_SELL, rata_price(ORDER_TYPE_SELL) - takeProfitPips * normalizedPoint); //--- Modify take profit
            }
            CloseBuy();                          //--- Close buy positions
         }
      }
   }
}
if (CountTrades() == 0) {                       //--- Check no trades
   if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == ENABLED) { //--- Check buy signal mode
      if (IsBuyPinbar() && CountTrades() < maxOrders) { //--- Check buy pinbar and limit
         obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_ASK), SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_ASK) + (takeProfitPips * normalizedPoint), orderComment); //--- Open buy position
      }
   }
}
if (CountTrades() == 0) {                       //--- Check no trades
   if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == ENABLED) { //--- Check sell signal mode
      if (IsSellPinbar() && CountTrades() < maxOrders) { //--- Check sell pinbar and limit
         obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_BID), SymbolInfoDouble(_Symbol, SYMBOL_BID) + stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_BID) - (takeProfitPips * normalizedPoint), orderComment); //--- Open sell position
      }
   }
}

Wir setzen die Implementierung der Tick-Funktion fort und fügen eine Logik zur Eröffnung neuer Positionen auf der Grundlage von Pin-Bar-Signalen und Marktbedingungen hinzu. Zunächst prüfen wir mit „CheckMarketBuyOrders“ und „CheckMarketSellOrders“, ob die offenen Kauf- und Verkaufsaufträge unter 70 liegen, um sicherzustellen, dass der EA keine praktischen Grenzen überschreitet. Dann, wenn „useSignalMode“ „DISABLED“ ist, werden Kaufbedingungen ausgewertet: wenn „supportResistanceLevel“ den aktuellen Eröffnungskurs von iOpen übersteigt, ein Kauf der Pin-Bar mit „IsBuyPinbar“ erkannt wird, „totalBuyPositions“ unter „maxOrders“ liegt, und der Handel über „isTradingAllowed“ erlaubt ist oder bereits Käufe existieren, berechnen wir „buyStopLoss“ und „buyTakeProfit“ unter Verwendung von SymbolInfoDouble mit „stopLossPips“ und „takeProfitPips“ angepasst durch „normalizedPoint“, überprüfen die Margin mit „AccountFreeMarginCheck“, eröffnen eine Kaufposition mit „obj_Trade.PositionOpen“ unter Verwendung von „GetLots“. Wir modifizieren Take-Profit mit „ModifyTP“, wenn „useAutoTakeProfit“ wahr ist, und schließen Verkaufspositionen mit „CloseSell“; eine ähnliche Logik gilt für Verkaufsbedingungen, wenn „supportResistanceLevel“ unter dem Eröffnungskurs liegt, mit „IsSellPinbar“.

Als Nächstes, wenn keine Handelsgeschäfte existieren („CountTrades“ ist 0) und „useSignalMode“ ist „ENABLED“, eröffnen wir eine Kaufposition auf einem Buy-Pin-Bar mit „IsBuyPinbar“ und „CountTrades“ unter „maxOrders“, mit „obj_Trade.PositionOpen“ mit berechneten Stop-Loss und Take-Profit, und ähnlich für Verkaufspositionen mit „IsSellPinbar“, um sicherzustellen, dass der EA Positionen basierend auf Pin-Bar-Signalen auf Schlüssel-Levels mit angemessenem Risikomanagement eröffnet. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

BESTÄTIGTES SIGNAL

Da wir nun Signale bestätigen und Positionen eröffnen, müssen wir die Signale verwalten. Dafür werden wir einige Funktionen definieren.

//+------------------------------------------------------------------+
//| Update stop loss and take profit                                 |
//+------------------------------------------------------------------+
void updateStopLossTakeProfit() {
   for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);           //--- Get position ticket
      if (ticket == 0) continue;                     //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; //--- Skip non-matching symbols
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
         double buyTakeProfitLevel = (buyBreakEvenPrice + takeProfitPips * _Point) * (takeProfitPips > 0); //--- Calculate buy take profit
         double buyStopLossLevel = PositionGetDouble(POSITION_SL); //--- Get current stop loss
         if (slBreakevenMinus > 0) {                 //--- Check breakeven adjustment
            buyStopLossLevel = (buyBreakEvenPrice - slBreakevenMinus * _Point); //--- Set breakeven stop loss
         }
         if (buyCount == 1) {                        //--- Check single buy position
            buyTakeProfitLevel = NormalizePrice(PositionGetDouble(POSITION_PRICE_OPEN) + takeProfitPips * _Point) * (takeProfitPips > 0); //--- Set take profit
            if (laterUseSL > 0) {                    //--- Check unused stop loss
               buyStopLossLevel = (PositionGetDouble(POSITION_PRICE_OPEN) - laterUseSL * _Point); //--- Set stop loss
            }
         }
         buyTakeProfitLevel = NormalizePrice(buyTakeProfitLevel); //--- Normalize take profit
         buyStopLossLevel = NormalizePrice(buyStopLossLevel); //--- Normalize stop loss
         if (SymbolInfoDouble(_Symbol, SYMBOL_BID) >= buyTakeProfitLevel && buyTakeProfitLevel > 0) { //--- Check take profit hit
            obj_Trade.PositionClose(ticket);         //--- Close position
         }
         if (SymbolInfoDouble(_Symbol, SYMBOL_BID) <= buyStopLossLevel) { //--- Check stop loss hit
            obj_Trade.PositionClose(ticket);         //--- Close position
         }
         if (NormalizePrice(PositionGetDouble(POSITION_TP)) != buyTakeProfitLevel || NormalizePrice(PositionGetDouble(POSITION_SL)) != buyStopLossLevel) { //--- Check modification needed
            obj_Trade.PositionModify(ticket, buyStopLossLevel, buyTakeProfitLevel); //--- Modify position
         }
      }
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
         double sellTakeProfitLevel = (sellBreakEvenPrice - takeProfitPips * _Point) * (takeProfitPips > 0); //--- Calculate sell take profit
         double sellStopLossLevel = PositionGetDouble(POSITION_SL); //--- Get current stop loss
         if (slBreakevenMinus > 0) {                //--- Check breakeven adjustment
            sellStopLossLevel = (sellBreakEvenPrice + slBreakevenMinus * _Point); //--- Set breakeven stop loss
         }
         if (sellCount == 1) {                      //--- Check single sell position
            sellTakeProfitLevel = (PositionGetDouble(POSITION_PRICE_OPEN) - takeProfitPips * _Point) * (takeProfitPips > 0); //--- Set take profit
            if (laterUseSL > 0) {                   //--- Check unused stop loss
               sellStopLossLevel = (PositionGetDouble(POSITION_PRICE_OPEN) + laterUseSL * _Point); //--- Set stop loss
            }
         }
         sellTakeProfitLevel = NormalizePrice(sellTakeProfitLevel); //--- Normalize take profit
         sellStopLossLevel = NormalizePrice(sellStopLossLevel); //--- Normalize stop loss
         if (SymbolInfoDouble(_Symbol, SYMBOL_ASK) <= sellTakeProfitLevel) { //--- Check take profit hit
            obj_Trade.PositionClose(ticket);        //--- Close position
         }
         if (SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= sellStopLossLevel && sellStopLossLevel > 0) { //--- Check stop loss hit
            obj_Trade.PositionClose(ticket);        //--- Close position
         }
         if (NormalizePrice(PositionGetDouble(POSITION_TP)) != sellTakeProfitLevel || NormalizePrice(PositionGetDouble(POSITION_SL)) != sellStopLossLevel) { //--- Check modification needed
            obj_Trade.PositionModify(ticket, sellStopLossLevel, sellTakeProfitLevel); //--- Modify position
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Add averaging order                                              |
//+------------------------------------------------------------------+
void addAveragingOrder() {
   int positionIndex = 0;                         //--- Initialize position index
   double lastOpenPrice = 0;                      //--- Initialize last open price
   double lastLotSize = 0;                        //--- Initialize last lot size
   bool isLastBuy = false;                        //--- Initialize buy flag
   int totalBuyPositions = 0;                     //--- Initialize buy positions count
   int totalSellPositions = 0;                    //--- Initialize sell positions count
   long currentSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); //--- Get current spread
   double supportResistanceLevel = iClose(Symbol(), PERIOD_H4, 1); //--- Get support/resistance level
   for (positionIndex = 0; positionIndex < PositionsTotal(); positionIndex++) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check buy position
         if (lastOpenPrice == 0) {                //--- Check initial price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price
         }
         if (lastOpenPrice > PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check lower price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price
         }
         if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot
            lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size
         }
         isLastBuy = true;                        //--- Set buy flag
         totalBuyPositions++;                     //--- Increment buy count
      }
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check sell position
         if (lastOpenPrice == 0) {                //--- Check initial price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price
         }
         if (lastOpenPrice < PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check higher price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price
         }
         if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot
            lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size
         }
         isLastBuy = false;                       //--- Clear buy flag
         totalSellPositions++;                    //--- Increment sell count
      }
   }
   if (isLastBuy) {                               //--- Check buy position
      if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check buy condition
         if (IsBuyPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_BID) <= lastOpenPrice - (orderDistancePips * _Point)) { //--- Check buy pinbar and distance
            obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_ASK), SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_ASK) + (takeProfitPips * normalizedPoint), orderComment); //--- Open buy position
            isLastBuy = false;                    //--- Clear buy flag
            return;                               //--- Exit function
         }
      }
   } else if (!isLastBuy) {                       //--- Check sell position
      if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check sell condition
         if (IsSellPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= lastOpenPrice + (orderDistancePips * _Point)) { //--- Check sell pinbar and distance
            obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_BID), SymbolInfoDouble(_Symbol, SYMBOL_BID) + stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_BID) - (takeProfitPips * normalizedPoint), orderComment); //--- Open sell position
            return;                               //--- Exit function
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Add averaging order with auto take profit                        |
//+------------------------------------------------------------------+
void addAveragingOrderWithAutoTP() {
   int positionIndex = 0;                         //--- Initialize position index
   double lastOpenPrice = 0;                      //--- Initialize last open price
   double lastLotSize = 0;                        //--- Initialize last lot size
   bool isLastBuy = false;                        //--- Initialize buy flag
   int totalBuyPositions = 0;                     //--- Initialize buy positions count
   int totalSellPositions = 0;                    //--- Initialize sell positions count
   long currentSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); //--- Get current spread
   double supportResistanceLevel = iClose(Symbol(), PERIOD_H4, 1); //--- Get support/resistance level
   for (positionIndex = 0; positionIndex < PositionsTotal(); positionIndex++) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check buy position
         if (lastOpenPrice == 0) {                //--- Check initial price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price
         }
         if (lastOpenPrice > PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check lower price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price
         }
         if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot
            lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size
         }
         isLastBuy = true;                        //--- Set buy flag
         totalBuyPositions++;                     //--- Increment buy count
      }
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check sell position
         if (lastOpenPrice == 0) {                //--- Check initial price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price
         }
         if (lastOpenPrice < PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check higher price
            lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price
         }
         if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot
            lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size
         }
         isLastBuy = false;                       //--- Clear buy flag
         totalSellPositions++;                    //--- Increment sell count
      }
   }
   if (isLastBuy) {                               //--- Check buy position
      if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check buy condition
         if (IsBuyPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_BID) <= lastOpenPrice - (orderDistancePips * _Point)) { //--- Check buy pinbar and distance
            obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_ASK), 0, 0, orderComment); //--- Open buy position
            calculatePositionMetrics();           //--- Calculate position metrics
            updateStopLossTakeProfit();           //--- Update stop loss and take profit
            isLastBuy = false;                    //--- Clear buy flag
            return;                               //--- Exit function
         }
      }
   } else if (!isLastBuy) {                       //--- Check sell position
      if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check sell condition
         if (IsSellPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= lastOpenPrice + (orderDistancePips * _Point)) { //--- Check sell pinbar and distance
            obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_BID), 0, 0, orderComment); //--- Open sell position
            calculatePositionMetrics();           //--- Calculate position metrics
            updateStopLossTakeProfit();           //--- Update stop loss and take profit
            return;                               //--- Exit function
         }
      }
   }
}

Hier implementieren wir die Funktionen „updateStopLossTakeProfit“ und „addAveragingOrder“ sowie „addAveragingOrderWithAutoTP“, um Stop-Loss-, Take-Profit- und Averaging-Handelsgeschäfte zu verwalten und dynamische Positionsanpassungen zu gewährleisten. Zunächst entwickeln wir die Funktion „updateStopLossTakeProfit“, die alle Positionen iterativ durchläuft. Für Kaufpositionen (POSITION_TYPE_BUY) berechnen wir „buyTakeProfitLevel“ auf der Grundlage von „buyBreakEvenPrice“ plus „takeProfitPips * _Point“, wenn „takeProfitPips“ positiv ist, ermitteln wir den aktuellen Stop-Loss mit PositionGetDouble und passen ihn an „buyBreakEvenPrice – slBreakevenMinus * _Point“ an wenn „slBreakevenMinus“ positiv ist, oder für Einzelpositionen („buyCount == 1“), Take-Profit und Stop-Loss auf Basis von POSITION_PRICE_OPEN, korrigiert um Take-Profit und Stop-Loss, Normalisierung beider Werte mit „NormalizePrice“, Schließen der Positionen mit „obj_Trade.PositionClose“, wenn der Geldkurs den Take-Profit oder den Stop-Loss erreicht, und ändern die Positionen mit „obj_Trade.PositionModify“, wenn die Niveaus abweichen; eine ähnliche Logik gilt für Verkaufspositionen mit „sellBreakEvenPrice“ und dem Briefkurs.

Anschließend implementieren wir die Funktion „addAveragingOrder“, die die letzte Position verfolgt, indem sie durch PositionsTotal iteriert, „lastOpenPrice“ auf den niedrigsten Kauf- oder höchsten Verkaufspreis und „lastLotSize“ auf das größte Volumen aktualisiert und „isLastBuy“ entsprechend setzt. Für Käufe, wenn „supportResistanceLevel“ den aktuellen Eröffnungskurs übersteigt und ein Buy-Pin-Bar mit „IsBuyPinbar“ erkannt wird und der Geldkurs um „orderDistancePips * _Point“ unter „lastOpenPrice“ liegt, eröffnen wir eine Kaufposition mit „obj_Trade.PositionOpen“ eine Kaufposition mit einer Lot-Größe von „lastLotSize * lotMultiplier“, normalisiert durch „fnGetLotDigit“, mit berechneten Stop-Loss und Take-Profit, und löschen „isLastBuy“; bei Verkäufen prüfen wir, ob der Briefkurs um „orderDistancePips * _Point“ über dem „lastOpenPrice“ liegt, und eröffnen auf ähnliche Weise eine Verkaufsposition.

Zuletzt implementieren wir „addAveragingOrderWithAutoTP“, das der gleichen Logik wie „addAveragingOrder“ folgt, aber Positionen ohne anfänglichen StopLoss oder TakeProfit (auf 0 gesetzt) eröffnet, „calculatePositionMetrics“ auf, um Metriken wie „buyBreakEvenPrice“ zu aktualisieren, und ruft „updateStopLossTakeProfit“ auf, um Breakeven-basierte Niveaus festzulegen und dynamische Anpassungen für einen Durchschnitt der Handelsgeschäfte zu gewährleisten. Wir können diese Funktionen nun in der Tick-Logik aufrufen, damit die Logik wirksam wird.

if (useSignalMode == ENABLED && CountTradesBuy() >= 1 && CountTradesBuy() < maxOrders && useAutoTakeProfit == false) { //--- Check buy averaging
   addAveragingOrder();                        //--- Add buy averaging order
}
if (useSignalMode == ENABLED && CountTradesSell() >= 1 && CountTradesSell() < maxOrders && useAutoTakeProfit == false) { //--- Check sell averaging
   addAveragingOrder();                        //--- Add sell averaging order
}
if (useSignalMode == ENABLED && CountTradesBuy() >= 1 && CountTradesBuy() < maxOrders && useAutoTakeProfit == true) { //--- Check buy averaging with auto TP
   addAveragingOrderWithAutoTP();              //--- Add buy averaging order with auto TP
}
if (useSignalMode == ENABLED && CountTradesSell() >= 1 && CountTradesSell() < maxOrders && useAutoTakeProfit == true) { //--- Check sell averaging with auto TP
   addAveragingOrderWithAutoTP();              //--- Add sell averaging order with auto TP
}

Wir vervollständigen die Tick-Logik-Implementierung, indem wir eine Logik zur Handhabung des Durchschnitts der Handelsgeschäfte unter bestimmten Bedingungen hinzufügen und so die Fähigkeit des EAs verbessern, dynamisch in Positionen zu skalieren. Wenn „useSignalMode“ „ENABLED“ ist, prüfen wir zunächst, ob mindestens eine Kaufposition mit „CountTradesBuy“ vorhanden ist und die Anzahl der Kaufpositionen unter „maxOrders“ liegt. Wenn „useAutoTakeProfit“ falsch ist, rufen wir „addAveragingOrder“ auf, um eine zusätzliche Kaufposition auf der Grundlage der Pin-Bar-Erkennung und der Preisdistanzkriterien zu eröffnen, wobei eine multiplizierte Losgröße verwendet wird.

Anschließend wenden wir dieselbe Logik für Verkaufspositionen an, indem wir „CountTradesSell“ überprüfen und „addAveragingOrder“ aufrufen, wenn „useAutoTakeProfit“ falsch ist, um eine Verkaufsposition unter ähnlichen Bedingungen hinzuzufügen. Als Nächstes rufen wir für Kaufpositionen, bei denen „useAutoTakeProfit“ wahr ist, „addAveragingOrderWithAutoTP“ auf, um eine Kaufposition ohne anfänglichen Stop-Loss oder Take-Profit zu eröffnen, gefolgt von einer Aktualisierung der Metriken und einer Anpassung der Breakeven-basierten Levels. Zuletzt wiederholen wir dies für Verkaufspositionen, wenn „useAutoTakeProfit“ wahr ist, und rufen „addAveragingOrderWithAutoTP“ auf, um eine Verkaufsposition mit dynamischen Stop-Loss- und Take-Profit-Anpassungen hinzuzufügen. Diese Logik stellt sicher, dass der EA im Signalmodus effektiv Durchschnittsgeschäfte verwaltet und sich an Marktbewegungen anpasst. Nach der Kompilierung erhalten wir folgendes Ergebnis.

MITTELWERTBILDUNG PROBE

Nachdem wir nun die Option der Mittelwertbildung hinzugefügt haben, bleibt nur noch das Hinzufügen einer Trailing-Stop-Logik für das Risikomanagement. Die Logik muss für eine präzise Risikokontrolle bei jedem Tick ausgeführt werden, daher fügen wir die Logik außerhalb der Balkenbeschränkungslogik hinzu.

double setPointValue = normalizedPoint;         //--- Set point value for calculations
if (useTrailingStop && trailingStartPips > 0 && breakevenPips < trailingStartPips) { //--- Check trailing stop conditions
   double averageBuyPrice = rata_price(ORDER_TYPE_BUY); //--- Calculate average buy price
   double trailingReference = 0;                //--- Initialize trailing reference
   for (int iTrade = 0; iTrade < PositionsTotal(); iTrade++) { //--- Iterate through positions
      ulong ticket = PositionGetTicket(iTrade); //--- Get position ticket
      if (ticket == 0) continue;                //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check buy position
         if (useAutoTakeProfit) {               //--- Check auto take profit
            trailingReference = averageBuyPrice; //--- Use average buy price
         } else {                               //--- Use open price
            trailingReference = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set open price
         }
         if (SymbolInfoDouble(_Symbol, SYMBOL_BID) - trailingReference > trailingStartPips * setPointValue) { //--- Check trailing condition
            if (SymbolInfoDouble(_Symbol, SYMBOL_BID) - ((trailingStartPips - breakevenPips) * setPointValue) > PositionGetDouble(POSITION_SL)) { //--- Check stop loss adjustment
               obj_Trade.PositionModify(ticket, SymbolInfoDouble(_Symbol, SYMBOL_BID) - ((trailingStartPips - breakevenPips) * setPointValue), PositionGetDouble(POSITION_TP)); //--- Modify position
            }
         }
      }
   }
   double averageSellPrice = rata_price(ORDER_TYPE_SELL); //--- Calculate average sell price
   for (int iTrade2 = 0; iTrade2 < PositionsTotal(); iTrade2++) { //--- Iterate through positions
      ulong ticket2 = PositionGetTicket(iTrade2); //--- Get position ticket
      if (ticket2 == 0) continue;               //--- Skip invalid tickets
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check sell position
         if (useAutoTakeProfit) {               //--- Check auto take profit
            trailingReference = averageSellPrice; //--- Use average sell price
         } else {                               //--- Use open price
            trailingReference = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set open price
         }
         if (trailingReference - SymbolInfoDouble(_Symbol, SYMBOL_ASK) > trailingStartPips * setPointValue) { //--- Check trailing condition
            if (SymbolInfoDouble(_Symbol, SYMBOL_ASK) + ((trailingStartPips - breakevenPips) * setPointValue) < PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0) { //--- Check stop loss adjustment
               obj_Trade.PositionModify(ticket2, SymbolInfoDouble(_Symbol, SYMBOL_ASK) + ((trailingStartPips - breakevenPips) * setPointValue), PositionGetDouble(POSITION_TP)); //--- Modify position
            }
         }
      }
   }
}

Wir implementieren die Trailing-Stop-Logik, indem wir zunächst „setPointValue“ auf „normalizedPoint“ setzen, um konsistente Preisberechnungen zu ermöglichen, und prüfen, ob „useTrailingStop“ wahr ist, „trailingStartPips“ positiv ist und „breakevenPips“ kleiner ist als „trailingStartPips“, um gültige Trailing-Bedingungen zu gewährleisten. Dann fahren wir fort, Kaufpositionen zu behandeln, indem wir „averageBuyPrice“ unter Verwendung von „rata_price“ für ORDER_TYPE_BUY berechnen, durch alle Positionen iterieren, um gültige Kaufpositionstickets zu erhalten, die mit „Symbol“ und „magicNumber“ übereinstimmen, wobei „trailingReference“ auf „averageBuyPrice“ gesetzt wird, wenn „useAutoTakeProfit“ true ist, oder andernfalls auf „POSITION_PRICE_OPEN“, und der Stop-Loss mit „obj_Trade.PositionModify“ auf SYMBOL_BID – (trailingStartPips – breakevenPips) * setPointValue“, wenn der Geldkurs „trailingReference“ um „trailingStartPips * setPointValue“ übersteigt und der neue Stop-Loss höher ist als der aktuelle.

Als Nächstes wenden wir eine ähnliche Logik für Verkaufspositionen an, indem wir „averageSellPrice“ mit „rata_price“ für „ORDER_TYPE_SELL“ berechnen, durch die Positionen iterieren, „trailingReference“ auf „averageSellPrice“ setzen oder POSITION_PRICE_OPEN, und ändern des Stop-Loss auf „SYMBOL_ASK + (trailingStartPips - breakevenPips) * setPointValue“, wenn der Briefkurs um „SYMBOL_ASK + (trailingStartPips - breakevenPips) * setPointValue“ liegt und der neue Stop-Loss niedriger oder nicht gesetzt ist. Zuletzt stellen wir sicher, dass die Änderungen den bestehenden Take-Profit über „PositionGetDouble(POSITION_TP)“ beibehalten und rufen ChartRedraw in der übergeordneten Funktion auf, um den Chart zu aktualisieren. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Vor dem Trailing-Stop:

VOR TRAILING-STOP

Nach Trailing-Stop:

NACH TRAILING-STOP

Nachdem wir nun die Logik der Positionsverwaltung fertiggestellt haben, können wir ein Dashboard erstellen, um die Kontometriken zu visualisieren. Auch dafür verwenden wir eine Funktion, die die Verwaltung erleichtert.

//+------------------------------------------------------------------+
//| Display dashboard information                                    |
//+------------------------------------------------------------------+
void Display_Info() {
   buyCount = 0;                                  //--- Reset buy count
   currentBuyLot = 0;                             //--- Reset current buy lot
   totalBuyLots = 0;                              //--- Reset total buy lots
   sellCount = 0;                                 //--- Reset sell count
   currentSellLot = 0;                            //--- Reset current sell lot
   totalSellLots = 0;                             //--- Reset total sell lots
   totalSum = 0;                                  //--- Reset total sum
   totalSwap = 0;                                 //--- Reset total swap
   buyProfit = 0;                                 //--- Reset buy profit
   sellProfit = 0;                                //--- Reset sell profit
   buyWeightedSum = 0;                            //--- Reset buy weighted sum
   sellWeightedSum = 0;                           //--- Reset sell weighted sum
   buyBreakEvenPrice = 0;                         //--- Reset buy breakeven price
   sellBreakEvenPrice = 0;                        //--- Reset sell breakeven price
   minBuyLot = 9999;                              //--- Initialize min buy lot
   minSellLot = 9999;                             //--- Initialize min sell lot
   maxSellPrice = 0;                              //--- Initialize max sell price
   minBuyPrice = 999999999;                       //--- Initialize min buy price
   for (int i = 0; i < PositionsTotal(); i++) {   //--- Iterate through positions
      ulong ticket = PositionGetTicket(i);        //--- Get position ticket
      if (ticket == 0) continue;                  //--- Skip invalid tickets
      if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; //--- Skip non-matching symbols
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position
         buyCount++;                              //--- Increment buy count
         totalOperations++;                       //--- Increment total operations
         currentBuyLot = PositionGetDouble(POSITION_VOLUME); //--- Set current buy lot
         buyProfit += PositionGetDouble(POSITION_PROFIT); //--- Add buy profit
         totalBuyLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total buy lots
         minBuyLot = MathMin(minBuyLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min buy lot
         buyWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price
         minBuyPrice = MathMin(minBuyPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update min buy price
      }
      if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position
         sellCount++;                             //--- Increment sell count
         totalOperations++;                       //--- Increment total operations
         currentSellLot = PositionGetDouble(POSITION_VOLUME); //--- Set current sell lot
         sellProfit += PositionGetDouble(POSITION_PROFIT); //--- Add sell profit
         totalSellLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total sell lots
         minSellLot = MathMin(minSellLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min sell lot
         sellWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price
         maxSellPrice = MathMax(maxSellPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update max sell price
      }
   }
   if (totalBuyLots > 0) {                        //--- Check buy lots
      buyBreakEvenPrice = buyWeightedSum / totalBuyLots; //--- Calculate buy breakeven
   }
   if (totalSellLots > 0) {                       //--- Check sell lots
      sellBreakEvenPrice = sellWeightedSum / totalSellLots; //--- Calculate sell breakeven
   }
   int minutesRemaining, secondsRemaining;        //--- Declare time variables
   minutesRemaining = (int)(PeriodSeconds() - (TimeCurrent() - iTime(Symbol(), PERIOD_CURRENT, 0))); //--- Calculate remaining time
   secondsRemaining = minutesRemaining % 60;      //--- Calculate seconds
   minutesRemaining = minutesRemaining / 60;      //--- Calculate minutes
   long currentSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); //--- Get current spread
   string spreadPrefix = "", minutesPrefix = "", secondsPrefix = ""; //--- Initialize prefixes
   if (currentSpread < 10) spreadPrefix = "..";   //--- Set spread prefix for single digit
   else if (currentSpread < 100) spreadPrefix = "."; //--- Set spread prefix for double digit
   if (minutesRemaining < 10) minutesPrefix = "0"; //--- Set minutes prefix
   if (secondsRemaining < 10) secondsPrefix = "0"; //--- Set seconds prefix
   int blinkingColorIndex;                        //--- Declare blinking color index
   color equityColor = clrGreen;                  //--- Initialize equity color
   if (AccountInfoDouble(ACCOUNT_EQUITY) - AccountInfoDouble(ACCOUNT_BALANCE) < 0.0) { //--- Check negative equity
      equityColor = clrRed;                       //--- Set equity color to red
   }
   color profitColor = (buyProfit + sellProfit >= 0) ? clrGreen : clrRed; //--- Set profit color
   MqlDateTime currentDateTime;                   //--- Declare datetime structure
   TimeToStruct(TimeCurrent(), currentDateTime);  //--- Convert current time
   if (currentDateTime.sec >= 0 && currentDateTime.sec < 10) { //--- Check first 10 seconds
      blinkingColorIndex = clrRed;                //--- Set red color
   }
   if (currentDateTime.sec >= 10 && currentDateTime.sec < 20) { //--- Check next 10 seconds
      blinkingColorIndex = clrOrange;             //--- Set orange color
   }
   if (currentDateTime.sec >= 20 && currentDateTime.sec < 30) { //--- Check next 10 seconds
      blinkingColorIndex = clrBlue;               //--- Set blue color
   }
   if (currentDateTime.sec >= 30 && currentDateTime.sec < 40) { //--- Check next 10 seconds
      blinkingColorIndex = clrDodgerBlue;         //--- Set dodger blue color
   }
   if (currentDateTime.sec >= 40 && currentDateTime.sec < 50) { //--- Check next 10 seconds
      blinkingColorIndex = clrYellow;             //--- Set yellow color
   }
   if (currentDateTime.sec >= 50 && currentDateTime.sec <= 59) { //--- Check last 10 seconds
      blinkingColorIndex = clrYellow;             //--- Set yellow color
   }
   if (ObjectFind(0, "DashboardBG") < 0) {        //--- Check dashboard background
      ObjectCreate(0, "DashboardBG", OBJ_RECTANGLE_LABEL, 0, 0, 0); //--- Create dashboard background
      ObjectSetInteger(0, "DashboardBG", OBJPROP_CORNER, 0); //--- Set corner
      ObjectSetInteger(0, "DashboardBG", OBJPROP_XDISTANCE, 100); //--- Set x distance
      ObjectSetInteger(0, "DashboardBG", OBJPROP_YDISTANCE, 20); //--- Set y distance
      ObjectSetInteger(0, "DashboardBG", OBJPROP_XSIZE, 260); //--- Set width
      ObjectSetInteger(0, "DashboardBG", OBJPROP_YSIZE, 300); //--- Set height
      ObjectSetInteger(0, "DashboardBG", OBJPROP_BGCOLOR, clrLightGray); //--- Set background color
      ObjectSetInteger(0, "DashboardBG", OBJPROP_BORDER_TYPE, BORDER_FLAT); //--- Set border type
      ObjectSetInteger(0, "DashboardBG", OBJPROP_COLOR, clrBlack); //--- Set border color
      ObjectSetInteger(0, "DashboardBG", OBJPROP_BACK, false); //--- Set to foreground
   }
   if (ObjectFind(0, "CLOSE ALL") < 0) {          //--- Check close all button
      ObjectCreate(0, "CLOSE ALL", OBJ_BUTTON, 0, 0, 0); //--- Create close all button
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_CORNER, 0); //--- Set corner
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_XDISTANCE, 110); //--- Set x distance
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_YDISTANCE, 280); //--- Set y distance
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_XSIZE, 240); //--- Set width
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_YSIZE, 25); //--- Set height
      ObjectSetString(0, "CLOSE ALL", OBJPROP_TEXT, "Close All Positions"); //--- Set button text
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_COLOR, clrWhite); //--- Set text color
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_BGCOLOR, clrRed); //--- Set background color
      ObjectSetInteger(0, "CLOSE ALL", OBJPROP_BORDER_COLOR, clrBlack); //--- Set border color
   }
   string headerText = "Pin Bar Averaging EA";    //--- Set header text
   LABEL("Header", "Impact", 20, 110, 20, clrNavy, 0, headerText); //--- Create header label
   string copyrightText = "Copyright 2025, Allan Munene Mutiiria"; //--- Set copyright text
   LABEL("Copyright", "Arial", 9, 110, 55, clrBlack, 0, copyrightText); //--- Create copyright label
   string linkText = "https://t.me/Forex_Algo_Trader"; //--- Set link text
   LABEL("Link", "Arial", 9, 110, 70, clrBlue, 0, linkText); //--- Create link label
   string accountHeader = "Account Information";  //--- Set account header
   LABEL("AccountHeader", "Arial Bold", 10, 110, 90, clrBlack, 0, accountHeader); //--- Create account header label
   string balanceText = "Balance: " + DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2); //--- Set balance text
   LABEL("Balance", "Arial", 9, 120, 105, clrBlack, 0, balanceText); //--- Create balance label
   string equityText = "Equity: " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2); //--- Set equity text
   LABEL("Equity", "Arial", 9, 120, 120, equityColor, 0, equityText); //--- Create equity label
   string marginText = "Free Margin: " + DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_FREE), 2); //--- Set margin text
   LABEL("Margin", "Arial", 9, 120, 135, clrBlack, 0, marginText); //--- Create margin label
   string profitText = "Open Profit: " + DoubleToString(buyProfit + sellProfit, 2); //--- Set profit text
   LABEL("Profit", "Arial", 9, 120, 150, profitColor, 0, profitText); //--- Create profit label
   string positionsText = "Buy Positions: " + IntegerToString((int)buyCount) + " Sell Positions: " + IntegerToString((int)sellCount); //--- Set positions text
   LABEL("Positions", "Arial", 9, 120, 165, clrBlack, 0, positionsText); //--- Create positions label
   string buyBEText = "Buy Break Even: " + (buyCount > 0 ? DoubleToString(buyBreakEvenPrice, _Digits) : "-"); //--- Set buy breakeven text
   LABEL("BuyBE", "Arial", 9, 120, 180, clrBlack, 0, buyBEText); //--- Create buy breakeven label
   string sellBEText = "Sell Break Even: " + (sellCount > 0 ? DoubleToString(sellBreakEvenPrice, _Digits) : "-"); //--- Set sell breakeven text
   LABEL("SellBE", "Arial", 9, 120, 195, clrBlack, 0, sellBEText); //--- Create sell breakeven label
   string spreadText = "Spread: " + spreadPrefix + IntegerToString((int)currentSpread) + " points"; //--- Set spread text
   LABEL("Spread", "Arial", 9, 120, 210, clrBlack, 0, spreadText); //--- Create spread label
   string timeText = "Time to next bar: " + minutesPrefix + IntegerToString(minutesRemaining) + ":" + secondsPrefix + IntegerToString(secondsRemaining); //--- Set time text
   LABEL("Time", "Arial", 9, 120, 225, clrBlack, 0, timeText); //--- Create time label
   string pinbarText;                             //--- Declare pinbar text
   if (IsBuyPinbar()) pinbarText = "Buy Pinbar";  //--- Check buy pinbar
   else if (IsSellPinbar()) pinbarText = "Sell Pinbar"; //--- Check sell pinbar
   else pinbarText = "None";                      //--- Set no pinbar
   LABEL("Pinbar", "Arial", 9, 120, 240, clrBlack, 0, "Pinbar Signal: " + pinbarText); //--- Create pinbar label
   string patternText = "Candle Pattern: " + CandleStick_Analyzer(); //--- Set candlestick pattern text
   LABEL("Pattern", "Arial", 9, 120, 255, clrBlack, 0, patternText); //--- Create pattern label
}

Wir implementieren die Funktion „Display_Info“, um ein umfassendes Dashboard für die Überwachung des Handels in Echtzeit zu erstellen. Zunächst setzen wir Schlüsselmetriken wie „buyCount“ und andere auf ihre jeweiligen Initialisierungswerte zurück, dann durchlaufen wir alle Positionen, um diese Metriken für Kauf- und Verkaufspositionen, die mit „Symbol“ übereinstimmen, zu aktualisieren, indem wir die Anzahl erhöhen, Gewinne, Volumina, gewichtete offene Preise summieren und Min-/Max-Preise verfolgen sowie gegebenenfalls Breakeven-Preise berechnen.

Anschließend wird die verbleibende Zeit bis zum nächsten Balken mit PeriodSeconds minus der Differenz zwischen aktueller Zeit und iTime berechnet, in „minutesRemaining“ und „secondsRemaining“ umgewandelt und Formatierungspräfixe für die Spread- und Zeitanzeige gesetzt. Als Nächstes bestimmen wir „equityColor“ (grün oder rot auf der Basis von Kapital vs. Saldo) und „profitColor“ (grün oder rot auf der Basis des Gesamtgewinns) und setzen einen blinkenden Farbindex auf der Basis der aktuellen Sekunde für den visuellen Effekt. Zuletzt erstellen wir einen Dashboard-Hintergrund mit ObjectCreate als OBJ_RECTANGLE_LABEL, wenn „DashboardBG“ nicht existiert, eine „CLOSE ALL“-Schaltfläche als „OBJ_BUTTON“ und mehrere Labels mit der Funktion „LABEL“ Funktion, um den Titel des EA, das Copyright, den Link, die Kontoinformationen (Saldo, Eigenkapital, freie Marge, Gewinn), die Anzahl der Positionen, die Break-Even-Preise, den Spread, die Zeit bis zum nächsten Balken, das Pin-Bar-Signal und die Kerzen-Muster von „CandleStick_Analyzer“ anzuzeigen und so eine klare und dynamische Visualisierung der Handelsinformationen zu gewährleisten. Für die Schaltfläche implementieren wir die Logik in der Ereignisbehandlung von OnChartEvent.

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
   if (id == CHARTEVENT_OBJECT_CLICK) {           //--- Check object click event
      if (sparam == "CLOSE ALL") {                //--- Check close all button
         ObjectSetInteger(0, "CLOSE ALL", OBJPROP_STATE, false); //--- Reset button state
         for (int positionIndex = PositionsTotal() - 1; positionIndex >= 0; positionIndex--) { //--- Iterate through positions
            ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket
            if (ticket == 0) continue;            //--- Skip invalid tickets
            if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol
               obj_Trade.PositionClose(ticket);   //--- Close position
            }
         }
      }
   }
}

In der Funktion OnChartEvent wird geprüft, ob die „id“ des Ereignisses CHARTEVENT_OBJECT_CLICK ist, um Objektklicks im Chart zu erkennen. Dann überprüfen wir, ob das angeklickte Objekt „sparam“ die Schaltfläche „ALLE SCHLIESSEN“ ist, und wenn ja, setzen wir den Zustand der Schaltfläche mit ObjectSetInteger mit „OBJPROP_STATE“ auf false zurück. Als Nächstes werden alle Positionen durchlaufen, das Ticket jeder Position mit PositionGetTicket abgerufen, ungültige Tickets übersprungen und mit der Funktion PositionGetString geprüft, ob das Symbol der Position mit dem aktuellen „Symbol“ übereinstimmt. Bei übereinstimmenden Positionen schließen wir diese mit „obj_Trade.PositionClose“, um den Befehl des Nutzers zum Schließen aller Positionen auszuführen und sicherzustellen, dass die Schaltfläche „Close All Positions“ des Dashboards eine reaktionsschnelle Möglichkeit zur manuellen Verwaltung offener Geschäfte bietet. Nach dem Aufruf der Funktion in OnTick und der Kompilierung erhalten wir das folgende Ergebnis.

KKOMPLETTES SYSTEM PIN BAR AVERAGING

Aus dem Bild können wir ersehen, dass es das Unterstützungs- oder Widerstandsniveau, die offenen und durchschnittlichen Positionen, die Nachverfolgung der Positionen und die Visualisierung der Kontometadaten in einem Panel erkennen und visualisieren kann und somit unsere Ziele erreicht. Bleiben nur noch die Backtests des Programms, und das wird im nächsten Abschnitt behandelt.


Backtests

Nach einem gründlichen Backtest erhalten wir folgende Ergebnisse.

Backtest-Grafik:

GRAPH

Bericht des Backtests:

BERICHT


Schlussfolgerung

Abschließend haben wir ein Pin-Bar Averaging System in MQL5 entwickelt, das das Kerzenmuster der Pin Bar nutzt, um Handelsgeschäfte zu initiieren und mehrere Positionen durch eine Averaging-Strategie zu verwalten, die mit Trailing-Stops, Breakeven-Anpassungen und einem dynamischen Dashboard für die Echtzeitüberwachung erweitert wurde. Durch modulare Komponenten wie die Funktionen „CandleStick_Analyzer“ und „addAveragingOrder“ bietet dieses Programm einen disziplinierten Ansatz für den Umkehrhandel mit anpassbarer Risikokontrolle.

Haftungsausschluss: Dieser Artikel ist nur für Bildungszwecke gedacht. Der Handel ist mit erheblichen finanziellen Risiken verbunden, und die Volatilität der Märkte kann zu Verlusten führen. Gründliche Backtests und sorgfältiges Risikomanagement sind entscheidend, bevor Sie dieses Programm auf den Live-Märkten einsetzen.

Indem Sie die vorgestellten Konzepte und Implementierungen nutzen, können Sie dieses Pin-Bar-System an Ihren Handelsstil anpassen und Ihre algorithmischen Strategien verbessern. Viel Spaß beim Handeln! 

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

Beigefügte Dateien |
Entwicklung des Price Action Analysis Toolkit (Teil 37): Sentiment Tilt Meter Entwicklung des Price Action Analysis Toolkit (Teil 37): Sentiment Tilt Meter
Die Marktstimmung ist eine der am meisten übersehenen, aber dennoch mächtigen Kräfte, die die Kursentwicklung beeinflussen. Während sich die meisten Händler auf nachlaufende Indikatoren oder Vermutungen verlassen, verwandelt der Sentiment Tilt Meter (STM) EA rohe Marktdaten in klare, visuelle Hinweise, die in Echtzeit anzeigen, ob der Markt nach oben oder unten tendiert oder neutral bleibt. Dies erleichtert die Bestätigung von Geschäften, die Vermeidung von Fehleinstiegen und eine bessere Zeitplanung der Marktteilnahme.
MetaTrader Tick-Info-Zugang von MQL5-Diensten zur Python-Anwendung über Sockets MetaTrader Tick-Info-Zugang von MQL5-Diensten zur Python-Anwendung über Sockets
Manchmal ist nicht alles in der MQL5-Sprache programmierbar. Und selbst wenn es möglich wäre, bestehende fortgeschrittene Bibliotheken in MQL5 zu konvertieren, wäre dies sehr zeitaufwändig. Dieser Artikel versucht zu zeigen, dass wir die Abhängigkeit vom Windows-Betriebssystem umgehen können, indem wir Tick-Informationen wie Bid, Ask und Time mit MetaTrader-Diensten über Sockets an eine Python-Anwendung übertragen.
Aufbau eines Handelssystems (Teil 2): Die Wissenschaft der Positionsbestimmung Aufbau eines Handelssystems (Teil 2): Die Wissenschaft der Positionsbestimmung
Selbst bei einem System mit positiver Erwartungshaltung entscheidet die Positionsgröße darüber, ob Sie Erfolg haben oder zusammenbrechen. Das ist der Dreh- und Angelpunkt des Risikomanagements – die Umsetzung statistischer Erkenntnisse in reale Ergebnisse bei gleichzeitigem Schutz Ihres Kapitals.
Entwicklung des Price Action Analysis Toolkit (Teil 36): Direkter Python-Zugang zu MetaTrader 5 Market Streams freischalten Entwicklung des Price Action Analysis Toolkit (Teil 36): Direkter Python-Zugang zu MetaTrader 5 Market Streams freischalten
Schöpfen Sie das volle Potenzial Ihres MetaTrader 5 Terminals aus, indem Sie das datenwissenschaftliche Ökosystem von Python und die offizielle MetaTrader 5 Client-Bibliothek nutzen. Dieser Artikel zeigt, wie man Live-Tick- und Minutenbalken-Daten direkt in den Parquet-Speicher authentifiziert und streamt, mit Ta und Prophet ein ausgefeiltes Feature-Engineering durchführt und ein zeitabhängiges Gradient-Boosting-Modell trainiert. Anschließend setzen wir einen leichtgewichtigen Flask-Dienst ein, um Handelssignale in Echtzeit zu liefern. Egal, ob Sie ein hybrides Quant-Framework aufbauen oder Ihren EA mit maschinellem Lernen erweitern, Sie erhalten eine robuste Ende-zu-Ende-Pipeline für den datengesteuerten algorithmischen Handel an die Hand.