- MultiSignal Confluence EA v1.11 (stop loss bug fixed) - Harmonic Pattern Finder v2 (optimized) - Candlestick Pattern EA (fixed) - Pivot Fade EA v4 (fixed) - Bot series (10001, 10002, EnhancedEA) Performance: ~19% return in 2 months on 00k account
713 lines
27 KiB
Plaintext
Executable File
713 lines
27 KiB
Plaintext
Executable File
//+------------------------------------------------------------------+
|
|
//| CandlestickPatternEA.mq5 |
|
|
//| Copyright 2025, ART inc. |
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, ARTi"
|
|
#property link "https://www.abbeyroadtech.com"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
// Include necessary libraries
|
|
#include <Trade\Trade.mqh>
|
|
#include <Trade\SymbolInfo.mqh>
|
|
|
|
// Input Parameters for Trading
|
|
input double InpLotSize = 0.01; // Lot size
|
|
input int InpStopLoss = 100; // Stop Loss in points
|
|
input int InpTakeProfit = 200; // Take Profit in points
|
|
input bool InpUseHammerSignals = true; // Trade Hammer signals
|
|
input bool InpUsePinBarSignals = true; // Trade Pin Bar signals
|
|
input bool InpUseWickRejection = true; // Trade Wick Rejection signals
|
|
input bool InpUseTweezerTop = true; // Trade Tweezer Top signals
|
|
input bool InpUseShootingStar = true; // Trade Shooting Star signals
|
|
input int InpMaxPositions = 5; // Maximum open positions
|
|
input bool InpCloseOnOppositeSignal = true; // Close position on opposite signal
|
|
|
|
// Trailing Stop Parameters
|
|
input bool InpUseTrailingStop = true; // Use trailing stop
|
|
input int InpTrailingStart = 50; // Points of profit before trailing begins
|
|
input int InpTrailingStep = 10; // Trailing step in points
|
|
input int InpTrailingStop = 30; // Trailing stop distance in points
|
|
|
|
// Input Parameters for Moving Average Filter
|
|
input int InpFastMA = 50; // Fast MA period
|
|
input int InpSlowMA = 200; // Slow MA period
|
|
input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // MA method
|
|
input ENUM_APPLIED_PRICE InpMAPrice = PRICE_CLOSE; // Applied price
|
|
|
|
// Input Parameters for Indicator
|
|
input int InpCandlesToAnalyze = 300; // Number of candles to analyze
|
|
input double InpHammerRatio = 0.3; // Hammer body to wick ratio
|
|
input double InpPinBarRatio = 0.25; // Pin bar body to wick ratio
|
|
input double InpWickRejectionRatio = 0.4; // Wick rejection ratio
|
|
input double InpTweezerMaxDiff = 0.1; // Tweezer top max difference %
|
|
input double InpShootingStarRatio = 0.3; // Shooting star body to wick ratio
|
|
input int InpConfirmationCandles = 1; // Confirmation candles
|
|
|
|
// ATR Filter Parameters
|
|
input bool InpUseATRFilter = true; // Use ATR filter
|
|
input int InpATRPeriod = 14; // ATR period
|
|
input group "ATR Filter Mode"
|
|
input bool InpUseFixedATRValue = true; // Use fixed ATR value (vs percentage of peak)
|
|
input double InpMinATRValue = 0.0010; // Fixed: Minimum ATR value to trade (adjust for your pair)
|
|
input int InpATRLookbackPeriod = 50; // Period to find peak ATR value
|
|
input double InpATRPercentage = 30.0; // Percentage of peak ATR (30% = 0.3 * max ATR)
|
|
|
|
// Bollinger Band Width Filter Parameters
|
|
input bool InpUseBBWFilter = true; // Use Bollinger Band Width filter
|
|
input int InpBBPeriod = 20; // Bollinger Bands period
|
|
input double InpBBDeviation = 2.0; // Bollinger Bands deviation
|
|
input double InpMinBBWidth = 0.0020; // Minimum BB width to trade (as ratio)
|
|
|
|
// Global Variables
|
|
CTrade Trade; // Trading object
|
|
CSymbolInfo SymbolInfo; // Symbol info object
|
|
int FastMAHandle; // Fast MA indicator handle
|
|
int SlowMAHandle; // Slow MA indicator handle
|
|
int ATRHandle; // ATR indicator handle
|
|
int BBHandle; // Bollinger Bands indicator handle
|
|
int PatternIndicatorHandle; // Candlestick pattern indicator handle
|
|
bool isTradingAllowed = true; // Flag to control trading
|
|
datetime lastBarTime = 0; // Last processed bar time
|
|
|
|
// Order tracking
|
|
int totalBuyPositions = 0; // Track current buy positions
|
|
int totalSellPositions = 0; // Track current sell positions
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Initialize symbol info
|
|
if(!SymbolInfo.Name(_Symbol))
|
|
{
|
|
Print("Failed to initialize symbol info");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Initialize trade object
|
|
Trade.SetExpertMagicNumber(123456); // Set a magic number for this EA
|
|
|
|
// Initialize indicator handles
|
|
FastMAHandle = iMA(_Symbol, _Period, InpFastMA, 0, InpMAMethod, InpMAPrice);
|
|
SlowMAHandle = iMA(_Symbol, _Period, InpSlowMA, 0, InpMAMethod, InpMAPrice);
|
|
|
|
// Initialize volatility filter indicators
|
|
if(InpUseATRFilter)
|
|
ATRHandle = iATR(_Symbol, _Period, InpATRPeriod);
|
|
|
|
if(InpUseBBWFilter)
|
|
BBHandle = iBands(_Symbol, _Period, InpBBPeriod, InpBBDeviation, 0, PRICE_CLOSE);
|
|
|
|
// Initialize the custom candlestick pattern indicator
|
|
PatternIndicatorHandle = iCustom(_Symbol, _Period, "CandlePatternConfirmation",
|
|
InpCandlesToAnalyze, InpHammerRatio, InpPinBarRatio,
|
|
InpWickRejectionRatio, InpTweezerMaxDiff, InpShootingStarRatio, InpConfirmationCandles);
|
|
|
|
// Check if indicators were created successfully
|
|
if(FastMAHandle == INVALID_HANDLE || SlowMAHandle == INVALID_HANDLE || PatternIndicatorHandle == INVALID_HANDLE)
|
|
{
|
|
Print("Failed to create primary indicators: Error ", GetLastError());
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
if((InpUseATRFilter && ATRHandle == INVALID_HANDLE) || (InpUseBBWFilter && BBHandle == INVALID_HANDLE))
|
|
{
|
|
Print("Failed to create volatility filter indicators: Error ", GetLastError());
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Set lastBarTime to avoid immediate trading on EA start
|
|
lastBarTime = iTime(_Symbol, _Period, 0);
|
|
|
|
// Count currently open positions
|
|
CountOpenPositions();
|
|
|
|
// Log initialization info
|
|
string atrModeDesc = InpUseFixedATRValue ? "Fixed value: " + DoubleToString(InpMinATRValue, 5) :
|
|
"Dynamic: " + DoubleToString(InpATRPercentage, 1) + "% of peak over " +
|
|
IntegerToString(InpATRLookbackPeriod) + " bars";
|
|
|
|
Print("EA initialized with volatility filters: ATR = ", (InpUseATRFilter ? "ON (" + atrModeDesc + ")" : "OFF"),
|
|
", BBW = ", (InpUseBBWFilter ? "ON" : "OFF"));
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// Release indicator handles
|
|
if(FastMAHandle != INVALID_HANDLE) IndicatorRelease(FastMAHandle);
|
|
if(SlowMAHandle != INVALID_HANDLE) IndicatorRelease(SlowMAHandle);
|
|
if(PatternIndicatorHandle != INVALID_HANDLE) IndicatorRelease(PatternIndicatorHandle);
|
|
|
|
if(InpUseATRFilter && ATRHandle != INVALID_HANDLE) IndicatorRelease(ATRHandle);
|
|
if(InpUseBBWFilter && BBHandle != INVALID_HANDLE) IndicatorRelease(BBHandle);
|
|
|
|
Print("Expert removed. Reason: ", reason);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
// Check if there's a new bar
|
|
datetime currentBarTime = iTime(_Symbol, _Period, 0);
|
|
bool isNewBar = (currentBarTime != lastBarTime);
|
|
|
|
// Process trailing stops on every tick (not just new bars)
|
|
if(InpUseTrailingStop)
|
|
{
|
|
ManageTrailingStops();
|
|
}
|
|
|
|
// Only process signals on new bar
|
|
if(!isNewBar) return;
|
|
|
|
// Update lastBarTime
|
|
lastBarTime = currentBarTime;
|
|
|
|
// Check if trading is allowed
|
|
if(!isTradingAllowed) return;
|
|
|
|
// Count currently open positions
|
|
CountOpenPositions();
|
|
|
|
// Check if maximum positions reached
|
|
if(totalBuyPositions + totalSellPositions >= InpMaxPositions) return;
|
|
|
|
// Check volatility filters first
|
|
if(!CheckVolatilityFilters())
|
|
{
|
|
Print("Trade skipped due to low volatility or market indecision");
|
|
return;
|
|
}
|
|
|
|
// Check for MA filter
|
|
bool fastAboveSlow = false;
|
|
bool fastBelowSlow = false;
|
|
CheckMAFilter(fastAboveSlow, fastBelowSlow);
|
|
|
|
// Get candlestick pattern signals
|
|
bool hammerSignal = false;
|
|
bool pinBarSignal = false;
|
|
bool wickRejectionBullSignal = false;
|
|
bool wickRejectionBearSignal = false;
|
|
bool tweezerTopSignal = false;
|
|
bool shootingStarSignal = false;
|
|
|
|
CheckCandlestickPatterns(hammerSignal, pinBarSignal, wickRejectionBullSignal,
|
|
wickRejectionBearSignal, tweezerTopSignal, shootingStarSignal);
|
|
|
|
// Define candlestick pattern signals (without MA filter)
|
|
bool buyPatternSignal = (InpUseHammerSignals && hammerSignal) ||
|
|
(InpUseWickRejection && wickRejectionBullSignal);
|
|
|
|
bool sellPatternSignal = (InpUsePinBarSignals && pinBarSignal) ||
|
|
(InpUseWickRejection && wickRejectionBearSignal) ||
|
|
(InpUseTweezerTop && tweezerTopSignal) ||
|
|
(InpUseShootingStar && shootingStarSignal);
|
|
|
|
// Check for opposite candlestick pattern signals and close positions if needed
|
|
// This happens regardless of MA filter - based ONLY on candlestick patterns
|
|
if(sellPatternSignal && InpCloseOnOppositeSignal)
|
|
{
|
|
// Close buy positions on a sell pattern signal
|
|
CloseBuyPositions();
|
|
}
|
|
|
|
if(buyPatternSignal && InpCloseOnOppositeSignal)
|
|
{
|
|
// Close sell positions on a buy pattern signal
|
|
CloseSellPositions();
|
|
}
|
|
|
|
// For opening new positions, use both candlestick pattern AND MA filter
|
|
bool validBuySignal = buyPatternSignal && fastAboveSlow;
|
|
bool validSellSignal = sellPatternSignal && fastBelowSlow;
|
|
|
|
// Process buy signals
|
|
if(validBuySignal && (totalBuyPositions < InpMaxPositions))
|
|
{
|
|
// Open buy positions based on specific signals
|
|
if(InpUseHammerSignals && hammerSignal)
|
|
{
|
|
OpenBuyPosition("Hammer");
|
|
}
|
|
else if(InpUseWickRejection && wickRejectionBullSignal)
|
|
{
|
|
OpenBuyPosition("Wick Rejection Bullish");
|
|
}
|
|
}
|
|
|
|
// Process sell signals
|
|
if(validSellSignal && (totalSellPositions < InpMaxPositions))
|
|
{
|
|
// Open sell positions based on specific signals
|
|
if(InpUsePinBarSignals && pinBarSignal)
|
|
{
|
|
OpenSellPosition("Pin Bar");
|
|
}
|
|
else if(InpUseWickRejection && wickRejectionBearSignal)
|
|
{
|
|
OpenSellPosition("Wick Rejection Bearish");
|
|
}
|
|
else if(InpUseTweezerTop && tweezerTopSignal)
|
|
{
|
|
OpenSellPosition("Tweezer Top");
|
|
}
|
|
else if(InpUseShootingStar && shootingStarSignal)
|
|
{
|
|
OpenSellPosition("Shooting Star");
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check volatility filters to avoid trades during indecision |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckVolatilityFilters()
|
|
{
|
|
// Check ATR Filter
|
|
if(InpUseATRFilter)
|
|
{
|
|
double atrValues[];
|
|
double minRequiredATR = 0;
|
|
|
|
// Get current ATR value
|
|
if(CopyBuffer(ATRHandle, 0, 0, 1, atrValues) <= 0)
|
|
{
|
|
Print("Failed to copy current ATR value: Error ", GetLastError());
|
|
return false;
|
|
}
|
|
|
|
double currentATR = atrValues[0];
|
|
|
|
// Determine which ATR threshold to use
|
|
if(InpUseFixedATRValue)
|
|
{
|
|
// Use the fixed value directly
|
|
minRequiredATR = InpMinATRValue;
|
|
}
|
|
else
|
|
{
|
|
// Get historical ATR values for the lookback period
|
|
if(CopyBuffer(ATRHandle, 0, 0, InpATRLookbackPeriod, atrValues) <= 0)
|
|
{
|
|
Print("Failed to copy historical ATR values: Error ", GetLastError());
|
|
return false;
|
|
}
|
|
|
|
// Find the peak ATR value in the lookback period
|
|
double peakATR = 0;
|
|
for(int i = 0; i < InpATRLookbackPeriod; i++)
|
|
{
|
|
if(atrValues[i] > peakATR)
|
|
peakATR = atrValues[i];
|
|
}
|
|
|
|
// Calculate minimum required ATR as percentage of peak
|
|
minRequiredATR = peakATR * (InpATRPercentage / 100.0);
|
|
|
|
Print("Dynamic ATR Threshold: Peak ATR = ", DoubleToString(peakATR, 5),
|
|
", Required ", DoubleToString(InpATRPercentage, 1), "% = ",
|
|
DoubleToString(minRequiredATR, 5));
|
|
}
|
|
|
|
// If current ATR is below threshold, market volatility is too low
|
|
if(currentATR < minRequiredATR)
|
|
{
|
|
Print("ATR Filter: Current ATR (", DoubleToString(currentATR, 5),
|
|
") is below minimum threshold (", DoubleToString(minRequiredATR, 5), ")");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Print("ATR Filter PASSED: Current ATR (", DoubleToString(currentATR, 5),
|
|
") exceeds minimum threshold (", DoubleToString(minRequiredATR, 5), ")");
|
|
}
|
|
}
|
|
|
|
// Check Bollinger Band Width Filter
|
|
if(InpUseBBWFilter)
|
|
{
|
|
double upperBand[1], lowerBand[1], middleBand[1];
|
|
|
|
if(CopyBuffer(BBHandle, 1, 0, 1, upperBand) <= 0 ||
|
|
CopyBuffer(BBHandle, 2, 0, 1, lowerBand) <= 0 ||
|
|
CopyBuffer(BBHandle, 0, 0, 1, middleBand) <= 0)
|
|
{
|
|
Print("Failed to copy Bollinger Bands values: Error ", GetLastError());
|
|
return false;
|
|
}
|
|
|
|
// Calculate width as a ratio rather than raw points
|
|
double bbWidth = (upperBand[0] - lowerBand[0]) / middleBand[0];
|
|
|
|
// If BB width is below minimum threshold, market is in consolidation
|
|
if(bbWidth < InpMinBBWidth)
|
|
{
|
|
Print("BB Width Filter: Current BB width (", DoubleToString(bbWidth, 5),
|
|
") is below minimum threshold (", DoubleToString(InpMinBBWidth, 5), ")");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Print("BB Width Filter PASSED: Current width (", DoubleToString(bbWidth, 5),
|
|
") exceeds minimum threshold (", DoubleToString(InpMinBBWidth, 5), ")");
|
|
}
|
|
}
|
|
|
|
// All filters passed or are disabled
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Moving Average Filter |
|
|
//+------------------------------------------------------------------+
|
|
void CheckMAFilter(bool &fastAboveSlow, bool &fastBelowSlow)
|
|
{
|
|
// Get MA values
|
|
double fastMAValue[1] = {0};
|
|
double slowMAValue[1] = {0};
|
|
|
|
// Copy MA values
|
|
if(CopyBuffer(FastMAHandle, 0, 0, 1, fastMAValue) <= 0 ||
|
|
CopyBuffer(SlowMAHandle, 0, 0, 1, slowMAValue) <= 0)
|
|
{
|
|
Print("Failed to copy MA values: Error ", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Set filter flags
|
|
fastAboveSlow = (fastMAValue[0] > slowMAValue[0]);
|
|
fastBelowSlow = (fastMAValue[0] < slowMAValue[0]);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Candlestick Patterns |
|
|
//+------------------------------------------------------------------+
|
|
void CheckCandlestickPatterns(bool &hammerSignal, bool &pinBarSignal,
|
|
bool &wickRejectionBullSignal, bool &wickRejectionBearSignal,
|
|
bool &tweezerTopSignal, bool &shootingStarSignal)
|
|
{
|
|
// Arrays to store indicator values for each pattern
|
|
double hammerValues[1] = {0};
|
|
double pinBarValues[1] = {0};
|
|
double wickRejectionValues[1] = {0};
|
|
double tweezerTopValues[1] = {0};
|
|
double shootingStarValues[1] = {0};
|
|
|
|
// Get values from the pattern indicator
|
|
if(CopyBuffer(PatternIndicatorHandle, 0, 1, 1, hammerValues) <= 0 ||
|
|
CopyBuffer(PatternIndicatorHandle, 1, 1, 1, pinBarValues) <= 0 ||
|
|
CopyBuffer(PatternIndicatorHandle, 2, 1, 1, wickRejectionValues) <= 0 ||
|
|
CopyBuffer(PatternIndicatorHandle, 3, 1, 1, tweezerTopValues) <= 0 ||
|
|
CopyBuffer(PatternIndicatorHandle, 4, 1, 1, shootingStarValues) <= 0)
|
|
{
|
|
Print("Failed to copy pattern values: Error ", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Set signal flags
|
|
hammerSignal = (hammerValues[0] != EMPTY_VALUE);
|
|
pinBarSignal = (pinBarValues[0] != EMPTY_VALUE);
|
|
|
|
// For wick rejection, we need to determine if it's bullish or bearish
|
|
if(wickRejectionValues[0] != EMPTY_VALUE)
|
|
{
|
|
// Determine if bullish or bearish based on the position relative to the candle
|
|
double candleHigh = iHigh(_Symbol, _Period, 1);
|
|
wickRejectionBullSignal = (wickRejectionValues[0] < candleHigh);
|
|
wickRejectionBearSignal = (wickRejectionValues[0] > candleHigh);
|
|
}
|
|
else
|
|
{
|
|
wickRejectionBullSignal = false;
|
|
wickRejectionBearSignal = false;
|
|
}
|
|
|
|
tweezerTopSignal = (tweezerTopValues[0] != EMPTY_VALUE);
|
|
shootingStarSignal = (shootingStarValues[0] != EMPTY_VALUE);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Open a Buy position |
|
|
//+------------------------------------------------------------------+
|
|
void OpenBuyPosition(string signalType)
|
|
{
|
|
// Update symbol info
|
|
SymbolInfo.Refresh();
|
|
SymbolInfo.RefreshRates();
|
|
|
|
// Calculate position size
|
|
double lotSize = InpLotSize;
|
|
|
|
// Calculate stop loss and take profit levels
|
|
double stopLoss = SymbolInfo.Ask() - InpStopLoss * SymbolInfo.Point();
|
|
double takeProfit = SymbolInfo.Ask() + InpTakeProfit * SymbolInfo.Point();
|
|
|
|
// Execute buy order
|
|
if(!Trade.Buy(lotSize, _Symbol, SymbolInfo.Ask(), stopLoss, takeProfit, signalType))
|
|
{
|
|
Print("Buy order failed: Error ", Trade.ResultRetcode(), ", ", Trade.ResultRetcodeDescription());
|
|
}
|
|
else
|
|
{
|
|
Print("Buy order placed successfully: Signal = ", signalType);
|
|
totalBuyPositions++;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Open a Sell position |
|
|
//+------------------------------------------------------------------+
|
|
void OpenSellPosition(string signalType)
|
|
{
|
|
// Update symbol info
|
|
SymbolInfo.Refresh();
|
|
SymbolInfo.RefreshRates();
|
|
|
|
// Calculate position size
|
|
double lotSize = InpLotSize;
|
|
|
|
// Calculate stop loss and take profit levels
|
|
double stopLoss = SymbolInfo.Bid() + InpStopLoss * SymbolInfo.Point();
|
|
double takeProfit = SymbolInfo.Bid() - InpTakeProfit * SymbolInfo.Point();
|
|
|
|
// Execute sell order
|
|
if(!Trade.Sell(lotSize, _Symbol, SymbolInfo.Bid(), stopLoss, takeProfit, signalType))
|
|
{
|
|
Print("Sell order failed: Error ", Trade.ResultRetcode(), ", ", Trade.ResultRetcodeDescription());
|
|
}
|
|
else
|
|
{
|
|
Print("Sell order placed successfully: Signal = ", signalType);
|
|
totalSellPositions++;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close all Buy positions |
|
|
//+------------------------------------------------------------------+
|
|
void CloseBuyPositions()
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionSelectByTicket(PositionGetTicket(i)))
|
|
{
|
|
// Check if it's our EA's position
|
|
if(PositionGetInteger(POSITION_MAGIC) == Trade.RequestMagic())
|
|
{
|
|
// Check if it's a buy position
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
{
|
|
// Close the position
|
|
if(!Trade.PositionClose(PositionGetTicket(i)))
|
|
{
|
|
Print("Failed to close buy position: Error ", Trade.ResultRetcode(), ", ", Trade.ResultRetcodeDescription());
|
|
}
|
|
else
|
|
{
|
|
Print("Buy position closed on opposite signal");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset counter
|
|
totalBuyPositions = 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close all Sell positions |
|
|
//+------------------------------------------------------------------+
|
|
void CloseSellPositions()
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionSelectByTicket(PositionGetTicket(i)))
|
|
{
|
|
// Check if it's our EA's position
|
|
if(PositionGetInteger(POSITION_MAGIC) == Trade.RequestMagic())
|
|
{
|
|
// Check if it's a sell position
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
|
{
|
|
// Close the position
|
|
if(!Trade.PositionClose(PositionGetTicket(i)))
|
|
{
|
|
Print("Failed to close sell position: Error ", Trade.ResultRetcode(), ", ", Trade.ResultRetcodeDescription());
|
|
}
|
|
else
|
|
{
|
|
Print("Sell position closed on opposite signal");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset counter
|
|
totalSellPositions = 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close only losing positions by type |
|
|
//+------------------------------------------------------------------+
|
|
void CloseLosingPositions(ENUM_POSITION_TYPE posType)
|
|
{
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionSelectByTicket(PositionGetTicket(i)))
|
|
{
|
|
// Check if it's our EA's position and the correct type
|
|
if(PositionGetInteger(POSITION_MAGIC) == Trade.RequestMagic() &&
|
|
PositionGetInteger(POSITION_TYPE) == posType)
|
|
{
|
|
// Check if the position is losing
|
|
double posProfit = PositionGetDouble(POSITION_PROFIT);
|
|
|
|
if(posProfit < 0) // Only close if it's losing money
|
|
{
|
|
// Close the position
|
|
if(!Trade.PositionClose(PositionGetTicket(i)))
|
|
{
|
|
Print("Failed to close losing position: Error ", Trade.ResultRetcode(), ", ", Trade.ResultRetcodeDescription());
|
|
}
|
|
else
|
|
{
|
|
string posTypeStr = (posType == POSITION_TYPE_BUY) ? "Buy" : "Sell";
|
|
Print("Losing ", posTypeStr, " position closed on opposite signal. Profit: ", posProfit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset counters after closing positions
|
|
CountOpenPositions();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Count open positions |
|
|
//+------------------------------------------------------------------+
|
|
void CountOpenPositions()
|
|
{
|
|
totalBuyPositions = 0;
|
|
totalSellPositions = 0;
|
|
|
|
for(int i = 0; i < PositionsTotal(); i++)
|
|
{
|
|
if(PositionSelectByTicket(PositionGetTicket(i)))
|
|
{
|
|
// Check if it's our EA's position
|
|
if(PositionGetInteger(POSITION_MAGIC) == Trade.RequestMagic())
|
|
{
|
|
// Count by position type
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
totalBuyPositions++;
|
|
else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
|
totalSellPositions++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Manage trailing stops for all open positions |
|
|
//+------------------------------------------------------------------+
|
|
void ManageTrailingStops()
|
|
{
|
|
// Update symbol info
|
|
SymbolInfo.Refresh();
|
|
SymbolInfo.RefreshRates();
|
|
|
|
double ask = SymbolInfo.Ask();
|
|
double bid = SymbolInfo.Bid();
|
|
double point = SymbolInfo.Point();
|
|
|
|
// Process all open positions
|
|
for(int i = 0; i < PositionsTotal(); i++)
|
|
{
|
|
// Select position by ticket
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(!PositionSelectByTicket(ticket))
|
|
continue;
|
|
|
|
// Check if it's our EA's position
|
|
if(PositionGetInteger(POSITION_MAGIC) != Trade.RequestMagic())
|
|
continue;
|
|
|
|
// Get position details
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double currentSL = PositionGetDouble(POSITION_SL);
|
|
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
|
|
// Trailing logic for BUY positions
|
|
if(posType == POSITION_TYPE_BUY)
|
|
{
|
|
// Calculate profit in points
|
|
double profitPoints = (bid - openPrice) / point;
|
|
|
|
// Only trail if minimum profit is reached
|
|
if(profitPoints >= InpTrailingStart)
|
|
{
|
|
// Calculate new stop loss level
|
|
double newSL = bid - InpTrailingStop * point;
|
|
|
|
// Only modify if new SL is higher (better) than current one
|
|
// and at least one trailing step away from current SL
|
|
if(newSL > currentSL + InpTrailingStep * point)
|
|
{
|
|
if(Trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP)))
|
|
{
|
|
Print("Trailing stop adjusted for BUY position #", ticket,
|
|
" New stop loss: ", newSL,
|
|
" Previous stop loss: ", currentSL);
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to adjust trailing stop: Error ", Trade.ResultRetcode(), ", ", Trade.ResultRetcodeDescription());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Trailing logic for SELL positions
|
|
else if(posType == POSITION_TYPE_SELL)
|
|
{
|
|
// Calculate profit in points
|
|
double profitPoints = (openPrice - ask) / point;
|
|
|
|
// Only trail if minimum profit is reached
|
|
if(profitPoints >= InpTrailingStart)
|
|
{
|
|
// Calculate new stop loss level
|
|
double newSL = ask + InpTrailingStop * point;
|
|
|
|
// Only modify if new SL is lower (better) than current one
|
|
// and at least one trailing step away from current SL
|
|
if(currentSL == 0 || newSL < currentSL - InpTrailingStep * point)
|
|
{
|
|
if(Trade.PositionModify(ticket, newSL, PositionGetDouble(POSITION_TP)))
|
|
{
|
|
Print("Trailing stop adjusted for SELL position #", ticket,
|
|
" New stop loss: ", newSL,
|
|
" Previous stop loss: ", currentSL);
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to adjust trailing stop: Error ", Trade.ResultRetcode(), ", ", Trade.ResultRetcodeDescription());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |