- 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
841 lines
63 KiB
Plaintext
Executable File
841 lines
63 KiB
Plaintext
Executable File
//+------------------------------------------------------------------+
|
||
//| Bot10002.mq5 |
|
||
//| Copyright 2024, MetaQuotes Ltd. |
|
||
//| https://www.abbeyroadtech.com |
|
||
//+------------------------------------------------------------------+
|
||
#property copyright "Copyright 2024, MetaQuotes Ltd."
|
||
#property link "https://www.abbeyroadtech.com"
|
||
#property version "1.00"
|
||
#property strict
|
||
|
||
#include <Trade/Trade.mqh>
|
||
CTrade trade;
|
||
|
||
//--- Trading Parameters
|
||
// Removed fixed lot size input in favor of risk-based volume calculation
|
||
input double LotSize = 0.1; // LotSize
|
||
|
||
|
||
//--- Risk management: risk per trade as a percentage of account equity
|
||
// Note: A value of 0.01 means 0.01% of the equity.
|
||
input double RiskPerTrade = 0.5;
|
||
|
||
input int StopLossPips = 150; // Stop loss in pips
|
||
input int TakeProfitPips = 300; // Take profit in pips
|
||
input int Slippage = 10; // Allowed slippage in points
|
||
|
||
//--- Input parameters for Moving Averages
|
||
input int FastMAPeriod = 10; // Fast Moving Average period
|
||
input int SlowMAPeriod = 20; // Slow Moving Average period
|
||
|
||
//--- Input parameters for Pivot Points
|
||
input bool UsePivotPoints = true; // Enable/Disable Pivot Points
|
||
|
||
//--- Input parameters for Harmonic Patterns
|
||
input bool UseHarmonicPatterns = true; // Enable/Disable Harmonic Patterns
|
||
input int SwingLookback = 100; // Bars for swing detection
|
||
input double PatternTolerance = 0.05; // Allowed ratio variance
|
||
|
||
//--- Input parameters for Trailing Stop
|
||
input int TrailingStart = 50; // Profit in points to start trailing
|
||
input int TrailingDistance = 30; // Distance to maintain from current price
|
||
input int TrailingStep = 10; // Step in points to move the stop loss
|
||
|
||
input double StopLossFactor = 1.0; // Multiplier for (R2 - R1) or (S1 - S2)
|
||
input double TakeProfitFactor = 1.0; // Multiplier for take-profit
|
||
// (RiskPerTrade already declared above)
|
||
|
||
input long MagicNumber = 98765; // Magic number for this EA
|
||
|
||
/***** Day-of-Week Logic *****/
|
||
input bool TradeMonday = true;
|
||
input bool TradeTuesday = true;
|
||
input bool TradeWednesday = true;
|
||
input bool TradeThursday = true;
|
||
input bool TradeFriday = true;
|
||
input bool TradeSaturday = false;
|
||
input bool TradeSunday = false;
|
||
|
||
/************** RSI Tuning ****************/
|
||
input int RSIOversold = 40; //RSI over bought
|
||
input int RSIOverbought = 60; //RSI over sold
|
||
|
||
//--- Pin Bar Filter
|
||
input bool UsePinBarFilter = true;
|
||
|
||
//--- Control for Previous Day Trade Restriction
|
||
input bool RequirePreviousDayTrade = true;
|
||
|
||
|
||
//--- Global variables (Pivot Points)
|
||
static datetime TradeDate = 0; // Tracks the current day
|
||
static bool OrdersPlaced = false;
|
||
double PivotPoint, S1, S2, R1, R2; // Pivot Points and support/resistance
|
||
|
||
// Define ENUM_SWING_TYPE
|
||
enum ENUM_SWING_TYPE
|
||
{
|
||
SWING_MODE_LOW = 0, // Start with low swing points
|
||
SWING_MODE_HIGH = 1 // Start with high swing points
|
||
};
|
||
|
||
/***** For multiple-trade trailing: track best price per position ticket *****/
|
||
/**
|
||
We'll keep two parallel static arrays:
|
||
- posTicketArray[] holds position tickets
|
||
- bestPriceArray[] holds the best price so far for that ticket
|
||
(highest for BUY, lowest for SELL).
|
||
We only store up to some arbitrary max positions (e.g. 100).
|
||
Each time we see a new position, we add if not found.
|
||
Each time a position is closed, we remove it from the arrays.
|
||
*/
|
||
#define MAX_POSITIONS 100
|
||
|
||
static ulong posTicketArray[MAX_POSITIONS];
|
||
static double bestPriceArray[MAX_POSITIONS];
|
||
static int posCount = 0; // How many valid entries we have
|
||
|
||
bool IsTradingDay();
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Expert initialization function |
|
||
//+------------------------------------------------------------------+
|
||
int OnInit()
|
||
{
|
||
Print("Expert Advisor initialized (no Fibonacci).");
|
||
return(INIT_SUCCEEDED);
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Expert deinitialization function |
|
||
//+------------------------------------------------------------------+
|
||
void OnDeinit(const int reason)
|
||
{
|
||
Print("Expert Advisor deinitialized.");
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Expert tick function |
|
||
//+------------------------------------------------------------------+
|
||
void OnTick()
|
||
{
|
||
//--- Calculate Moving Averages (MQL5 style)
|
||
double FastMA = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
|
||
double SlowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
|
||
|
||
//--- Calculate Pivot Points
|
||
if(UsePivotPoints)
|
||
CalculatePivotPoints();
|
||
|
||
//--- Detect Harmonic Patterns
|
||
bool bullishSignal = false, bearishSignal = false;
|
||
if(UseHarmonicPatterns)
|
||
{
|
||
bullishSignal = DetectBullishPatterns();
|
||
bearishSignal = DetectBearishPatterns();
|
||
}
|
||
|
||
//--- Get current market prices
|
||
double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);
|
||
MqlTick lastTick;
|
||
SymbolInfoTick(_Symbol, lastTick);
|
||
|
||
// Check for new D1 bar
|
||
datetime today = iTime(_Symbol, PERIOD_D1, 0);
|
||
if(today != TradeDate)
|
||
{
|
||
TradeDate = today;
|
||
OrdersPlaced = false;
|
||
CleanUpClosedPositions();
|
||
}
|
||
|
||
//--- Execute trading logic
|
||
ExecuteTradingLogic(FastMA, SlowMA, currentClose, lastTick, bullishSignal, bearishSignal);
|
||
|
||
ApplyTrailingStop();
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Calculate Pivot Points |
|
||
//+------------------------------------------------------------------+
|
||
void CalculatePivotPoints()
|
||
{
|
||
MqlRates daily[];
|
||
// Copy exactly 1 bar from the previous D1 candle
|
||
if(CopyRates(_Symbol, PERIOD_D1, 1, 1, daily) < 1)
|
||
{
|
||
Print("Failed to copy daily rates for pivot calculation.");
|
||
return;
|
||
}
|
||
|
||
PivotPoint = (daily[0].high + daily[0].low + daily[0].close) / 3.0;
|
||
S1 = (2.0 * PivotPoint) - daily[0].high;
|
||
R1 = (2.0 * PivotPoint) - daily[0].low;
|
||
S2 = PivotPoint - (daily[0].high - daily[0].low);
|
||
R2 = PivotPoint + (daily[0].high - daily[0].low);
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Harmonic Pattern Detection System |
|
||
//+------------------------------------------------------------------+
|
||
bool DetectBullishPatterns()
|
||
{
|
||
double X, A, B, C, D;
|
||
if(!GetRecentSwings(X, A, B, C, D, SWING_MODE_LOW))
|
||
return false;
|
||
|
||
return CheckGartley(X, A, B, C, D, true) ||
|
||
CheckButterfly(X, A, B, C, D, true) ||
|
||
CheckBat(X, A, B, C, D, true);
|
||
}
|
||
|
||
bool DetectBearishPatterns()
|
||
{
|
||
double X, A, B, C, D;
|
||
if(!GetRecentSwings(X, A, B, C, D, SWING_MODE_HIGH))
|
||
return false;
|
||
|
||
return CheckGartley(X, A, B, C, D, false) ||
|
||
CheckButterfly(X, A, B, C, D, false) ||
|
||
CheckBat(X, A, B, C, D, false);
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Swing Point Detection |
|
||
//+------------------------------------------------------------------+
|
||
bool GetRecentSwings(double &X, double &A, double &B, double &C, double &D, ENUM_SWING_TYPE startType)
|
||
{
|
||
int swings[5];
|
||
int direction = (int)startType; // Initialize direction based on startType
|
||
|
||
for(int i = 0; i < 5; i++)
|
||
{
|
||
int swingIndex = -1;
|
||
|
||
if(direction == SWING_MODE_HIGH)
|
||
swingIndex = iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, SwingLookback, 0);
|
||
else
|
||
swingIndex = iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, SwingLookback, 0);
|
||
|
||
if(swingIndex == -1)
|
||
return false;
|
||
|
||
swings[i] = swingIndex;
|
||
direction = (direction == SWING_MODE_HIGH) ? SWING_MODE_LOW : SWING_MODE_HIGH;
|
||
}
|
||
|
||
X = (startType == SWING_MODE_LOW)
|
||
? iLow(_Symbol, PERIOD_CURRENT, swings[0])
|
||
: iHigh(_Symbol, PERIOD_CURRENT, swings[0]);
|
||
A = (startType == SWING_MODE_LOW)
|
||
? iHigh(_Symbol, PERIOD_CURRENT, swings[1])
|
||
: iLow(_Symbol, PERIOD_CURRENT, swings[1]);
|
||
B = (startType == SWING_MODE_LOW)
|
||
? iLow(_Symbol, PERIOD_CURRENT, swings[2])
|
||
: iHigh(_Symbol, PERIOD_CURRENT, swings[2]);
|
||
C = (startType == SWING_MODE_LOW)
|
||
? iHigh(_Symbol, PERIOD_CURRENT, swings[3])
|
||
: iLow(_Symbol, PERIOD_CURRENT, swings[3]);
|
||
D = (startType == SWING_MODE_LOW)
|
||
? iLow(_Symbol, PERIOD_CURRENT, swings[4])
|
||
: iHigh(_Symbol, PERIOD_CURRENT, swings[4]);
|
||
|
||
return true;
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Pattern Validation Functions |
|
||
//+------------------------------------------------------------------+
|
||
bool CheckGartley(double X, double A, double B, double C, double D, bool bullish)
|
||
{
|
||
double XA = MathAbs(A - X);
|
||
double AB = MathAbs(B - A);
|
||
double BC = MathAbs(C - B);
|
||
double CD = MathAbs(D - C);
|
||
|
||
bool validAB = CheckRatio(AB/XA, 0.618);
|
||
bool validBC = CheckRatio(BC/AB, 0.382) || CheckRatio(BC/AB, 0.886);
|
||
bool validCD = CheckRatio(CD/BC, 1.272) || CheckRatio(CD/BC, 1.618);
|
||
|
||
if(bullish)
|
||
return (validAB && validBC && validCD && D > X);
|
||
return (validAB && validBC && validCD && D < X);
|
||
}
|
||
|
||
bool CheckButterfly(double X, double A, double B, double C, double D, bool bullish)
|
||
{
|
||
double XA = MathAbs(A - X);
|
||
double AB = MathAbs(B - A);
|
||
double BC = MathAbs(C - B);
|
||
double CD = MathAbs(D - C);
|
||
|
||
bool validAB = CheckRatio(AB/XA, 0.786);
|
||
bool validBC = CheckRatio(BC/AB, 0.382) || CheckRatio(BC/AB, 0.886);
|
||
bool validCD = CheckRatio(CD/BC, 1.618) || CheckRatio(CD/BC, 2.24);
|
||
|
||
if(bullish)
|
||
return (validAB && validBC && validCD && D > X);
|
||
return (validAB && validBC && validCD && D < X);
|
||
}
|
||
|
||
bool CheckBat(double X, double A, double B, double C, double D, bool bullish)
|
||
{
|
||
double XA = MathAbs(A - X);
|
||
double AB = MathAbs(B - A);
|
||
double BC = MathAbs(C - B);
|
||
double CD = MathAbs(D - C);
|
||
|
||
bool validAB = CheckRatio(AB/XA, 0.382) || CheckRatio(AB/XA, 0.5);
|
||
bool validBC = CheckRatio(BC/AB, 0.382) || CheckRatio(BC/AB, 0.886);
|
||
bool validCD = CheckRatio(CD/BC, 1.618) || CheckRatio(CD/BC, 2.618);
|
||
bool validXD = CheckRatio(MathAbs(D - X)/XA, 0.886);
|
||
|
||
if(bullish)
|
||
return (validAB && validBC && validCD && validXD && D > X );
|
||
return (validAB && validBC && validCD && validXD && D < X);
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Ratio Validation Helper |
|
||
//+------------------------------------------------------------------+
|
||
bool CheckRatio(double ratio, double target)
|
||
{
|
||
return (ratio >= target*(1 - PatternTolerance) &&
|
||
ratio <= target*(1 + PatternTolerance));
|
||
}
|
||
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Calculate RSI |
|
||
//+------------------------------------------------------------------+
|
||
bool IsRSIOversold()
|
||
{
|
||
int RSIPeriod = 14;
|
||
double rsi[];
|
||
if(CopyBuffer(iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE), 0, 0, 1, rsi) <= 0)
|
||
{
|
||
Print("Failed to retrieve RSI values");
|
||
return false;
|
||
}
|
||
return rsi[0] < RSIOversold;
|
||
}
|
||
|
||
bool IsRSIOverbought()
|
||
{
|
||
int RSIPeriod = 14;
|
||
double rsi[];
|
||
if(CopyBuffer(iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE), 0, 0, 1, rsi) <= 0)
|
||
{
|
||
Print("Failed to retrieve RSI values");
|
||
return false;
|
||
}
|
||
return rsi[0] > RSIOverbought;
|
||
}
|
||
|
||
double valueRSI()
|
||
{
|
||
int RSIPeriod = 14;
|
||
double rsi[];
|
||
if(CopyBuffer(iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE), 0, 0, 1, rsi) <= 0)
|
||
{
|
||
Print("Failed to retrieve RSI values");
|
||
return false;
|
||
}
|
||
return rsi[0] ;
|
||
}
|
||
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Calculate ATR-based stop-loss |
|
||
//+------------------------------------------------------------------+
|
||
double CalculateATRStopLoss()
|
||
{
|
||
int ATRPeriod = 14;
|
||
double atrValue;
|
||
int atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod);
|
||
double atrThreshold = 0.0100; // Set based on historical analysis
|
||
if (atrHandle == INVALID_HANDLE)
|
||
{
|
||
Print("Failed to create ATR handle");
|
||
return 0;
|
||
}
|
||
|
||
|
||
double atrBuffer[];
|
||
if (CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) <= 0)
|
||
{
|
||
Print("Failed to retrieve ATR values");
|
||
return 0;
|
||
}
|
||
|
||
if (atrValue > atrThreshold)
|
||
{
|
||
Print("Skip trading volatility is too high");
|
||
return 0; // Skip trading if volatility is too high
|
||
}
|
||
atrValue = atrBuffer[0];
|
||
IndicatorRelease(atrHandle);
|
||
return MathMax(atrValue * 2, SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10);
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Ensure Stop Loss and Take Profit meet broker constraints |
|
||
//+------------------------------------------------------------------+
|
||
void ValidateStopLevels(double &stopLoss, double &takeProfit, double entryPrice, ENUM_ORDER_TYPE orderType)
|
||
{
|
||
double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
if (minStopLevel == 0)
|
||
minStopLevel = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10; // Default fallback
|
||
|
||
if (orderType == ORDER_TYPE_BUY)
|
||
{
|
||
if (stopLoss > 0 && (entryPrice - stopLoss) < minStopLevel) stopLoss = entryPrice - minStopLevel;
|
||
if (takeProfit > 0 && (takeProfit - entryPrice) < minStopLevel) takeProfit = entryPrice + minStopLevel;
|
||
}
|
||
else if (orderType == ORDER_TYPE_SELL)
|
||
{
|
||
if (stopLoss > 0 && (stopLoss - entryPrice) < minStopLevel) stopLoss = entryPrice + minStopLevel;
|
||
if (takeProfit > 0 && (entryPrice - takeProfit) < minStopLevel) takeProfit = entryPrice - minStopLevel;
|
||
}
|
||
|
||
// Ensure stopLoss and takeProfit are not equal to entryPrice
|
||
if (stopLoss == entryPrice) stopLoss += minStopLevel;
|
||
if (takeProfit == entryPrice) takeProfit -= minStopLevel;
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Enhanced Trading Logic with ATR Stop-Loss, RSI Filter, and Stop Validation |
|
||
//+------------------------------------------------------------------+
|
||
void ExecuteTradingLogic(double fastMA, double slowMA, double closePrice,
|
||
MqlTick &tick, bool bullishPattern, bool bearishPattern)
|
||
{
|
||
static datetime lastBar = 0;
|
||
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||
|
||
// Only check on new bar formation
|
||
if(lastBar == currentBar)
|
||
return;
|
||
lastBar = currentBar;
|
||
|
||
|
||
|
||
|
||
bool pivotCondition = UsePivotPoints ? (closePrice > PivotPoint) : true;
|
||
bool pivotBearCondition= UsePivotPoints ? (closePrice < PivotPoint) : true;
|
||
//bool bullishHarmonicCondition = UseHarmonicPatterns ? bullishPattern : true;
|
||
//bool bearishHarmonicCondition = UseHarmonicPatterns ? bearishPattern : true;
|
||
//bool rsiBuyCondition = IsRSIOversold();
|
||
//bool rsiSellCondition = IsRSIOverbought();
|
||
|
||
double stopLoss = StopLossPips;
|
||
double takeProfit = TakeProfitPips; //closePrice + (TakeProfitPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT));
|
||
|
||
// Get candle data
|
||
double openPrice = iOpen(_Symbol, PERIOD_CURRENT, 1);
|
||
double pclosePrice = iClose(_Symbol, PERIOD_CURRENT, 1);
|
||
double highPrice = iHigh(_Symbol, PERIOD_CURRENT, 1);
|
||
double lowPrice = iLow(_Symbol, PERIOD_CURRENT, 1);
|
||
//bool isPinedBar = IsPinBar(openPrice, pclosePrice,highPrice, lowPrice);
|
||
//bool isBearishPinBar = IsBearishPinBar(openPrice, pclosePrice,highPrice, lowPrice);
|
||
//bool isBullishPinBar = IsBullishPinBar(openPrice, pclosePrice,highPrice, lowPrice);
|
||
//bool isDoji = IsDoji(openPrice, pclosePrice,highPrice, lowPrice);
|
||
bool isStrongBullishCandle = IsStrongBullishCandle(openPrice, pclosePrice,highPrice, lowPrice);
|
||
bool isStrongBearishCandle = IsStrongBearishCandle(openPrice, pclosePrice,highPrice, lowPrice);
|
||
|
||
double openPrice2 = iOpen(_Symbol, PERIOD_CURRENT, 2);
|
||
double pclosePrice2 = iClose(_Symbol, PERIOD_CURRENT, 2);
|
||
double highPrice2 = iHigh(_Symbol, PERIOD_CURRENT, 2);
|
||
double lowPrice2 = iLow(_Symbol, PERIOD_CURRENT, 2);
|
||
//bool isPinedBar0 = IsPinBar(openPrice, pclosePrice,highPrice, lowPrice);
|
||
//bool isBearishPinBar0 = IsBearishPinBar(openPrice, pclosePrice,highPrice, lowPrice);
|
||
//bool isBullishPinBar0 = IsBullishPinBar(openPrice, pclosePrice,highPrice, lowPrice);
|
||
//bool isDoji0 = IsDoji(openPrice, pclosePrice,highPrice, lowPrice);
|
||
bool isStrongBullishCandle0 = IsStrongBullishCandle(openPrice2, pclosePrice2,highPrice2, lowPrice2);
|
||
bool isStrongBearishCandle0 = IsStrongBearishCandle(openPrice2, pclosePrice2,highPrice2, lowPrice2);
|
||
|
||
bool maBuyCondition = (fastMA > slowMA) && (PositionSelect(_Symbol) == false);
|
||
if( maBuyCondition && isStrongBullishCandle && isStrongBullishCandle0 && !IsRSIOverbought() && (!RequirePreviousDayTrade || !IsTradeOpenForSymbol() || IsPreviousDayTradeOpen()))
|
||
{
|
||
double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
if (minStopLevel == 0)
|
||
minStopLevel = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10; // Default fallback
|
||
double askPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
double tpPrice = askPrice + minStopLevel;
|
||
double slPrice = askPrice - (stopLoss * SymbolInfoDouble(_Symbol, SYMBOL_POINT));
|
||
double lot = LotSize;// CalculateLotSizeFromRisk(askPrice, askPrice - slPrice);
|
||
ValidateStopLevels(slPrice, takeProfit, askPrice, ORDER_TYPE_BUY);
|
||
|
||
ExecuteBuyOrder(lot, closePrice, slPrice, takeProfit, Slippage);
|
||
}
|
||
|
||
bool maSellCondition = (fastMA < slowMA) && (PositionSelect(_Symbol) == false);
|
||
if( maSellCondition && isStrongBearishCandle && isStrongBearishCandle0 && !IsRSIOversold() && valueRSI() < 50 && (!RequirePreviousDayTrade || !IsTradeOpenForSymbol() || IsPreviousDayTradeOpen()) )
|
||
{
|
||
double lot = LotSize;//CalculateLotSizeFromRisk(closePrice, closePrice + stopLoss);
|
||
double slPrice = closePrice + (stopLoss * SymbolInfoDouble(_Symbol, SYMBOL_POINT));
|
||
ValidateStopLevels(slPrice, takeProfit, closePrice, ORDER_TYPE_SELL);
|
||
ExecuteSellOrder(lot, closePrice, slPrice, takeProfit, Slippage);
|
||
}
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Check if an open trade is from the previous day |
|
||
//+------------------------------------------------------------------+
|
||
bool IsPreviousDayTradeOpen()
|
||
{
|
||
datetime currentTime = TimeCurrent();
|
||
MqlDateTime nowStruct;
|
||
TimeToStruct(currentTime, nowStruct);
|
||
|
||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
{
|
||
if(PositionGetSymbol(i) == _Symbol)
|
||
{
|
||
datetime openTime = PositionGetInteger(POSITION_TIME);
|
||
MqlDateTime openStruct;
|
||
TimeToStruct(openTime, openStruct);
|
||
|
||
if(openStruct.day != nowStruct.day)
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Check if there are any open trades for the current symbol |
|
||
//+------------------------------------------------------------------+
|
||
bool IsTradeOpenForSymbol()
|
||
{
|
||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
{
|
||
if(PositionGetSymbol(i) == _Symbol)
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Candle Pattern Detection |
|
||
//+------------------------------------------------------------------+
|
||
bool IsBullishPinBar(double open, double close, double high, double low)
|
||
{
|
||
double body = MathAbs(open - close);
|
||
double lowerWick = MathMin(open, close) - low;
|
||
return (lowerWick > body * 2) && body < (high - low) * 0.3;
|
||
}
|
||
|
||
bool IsBearishPinBar(double open, double close, double high, double low)
|
||
{
|
||
double body = MathAbs(open - close);
|
||
double upperWick = high - MathMax(open, close);
|
||
return (upperWick > body * 2) && body < (high - low) * 0.3;
|
||
}
|
||
|
||
bool IsDoji(double open, double close, double high, double low)
|
||
{
|
||
double body = MathAbs(open - close);
|
||
return (body < (high - low) * 0.1);
|
||
}
|
||
|
||
bool IsStrongBullishCandle(double open, double close, double high, double low)
|
||
{
|
||
double body = MathAbs(open - close);
|
||
return (close > open && body > (high - low) * 0.6);
|
||
}
|
||
|
||
bool IsStrongBearishCandle(double open, double close, double high, double low)
|
||
{
|
||
double body = MathAbs(open - close);
|
||
return (open > close && body > (high - low) * 0.6);
|
||
}
|
||
bool IsPinBar(double open, double close, double high, double low)
|
||
{
|
||
double body = MathAbs(open - close);
|
||
double upperWick = high - MathMax(open, close);
|
||
double lowerWick = MathMin(open, close) - low;
|
||
return (upperWick > body * 2 || lowerWick > body * 2) && body < (high - low) * 0.3;
|
||
}
|
||
|
||
double GetVolumeProfileLevel(ENUM_TIMEFRAMES timeframe, int lookbackPeriod)
|
||
{
|
||
// Calculate the start time for the lookback period
|
||
datetime start_time = TimeCurrent() - lookbackPeriod * PeriodSeconds(timeframe);
|
||
|
||
double total_volume = 0; // Total volume over the lookback period
|
||
double price_level = 0; // Weighted sum of price levels
|
||
|
||
for(int i = 0; i < lookbackPeriod; i++)
|
||
{
|
||
// Get the volume and closing price for the current bar
|
||
double bar_volume = iVolume(_Symbol, timeframe, i);
|
||
double bar_price = iClose(_Symbol, timeframe, i);
|
||
|
||
// Add to the total volume and weighted price level
|
||
total_volume += bar_volume;
|
||
price_level += bar_volume * bar_price;
|
||
}
|
||
|
||
// Avoid division by zero
|
||
if(total_volume == 0)
|
||
{
|
||
Print("Error: Total volume is zero. Cannot calculate Point of Control (POC).");
|
||
return 0; // Return 0 or handle the error as needed
|
||
}
|
||
|
||
// Calculate and return the Point of Control (POC)
|
||
return price_level / total_volume;
|
||
}
|
||
|
||
//--- Input parameters for ADX
|
||
input int ADXPeriod = 14; // Period for ADX calculation
|
||
|
||
// Function to calculate ADX
|
||
double GetADX(ENUM_TIMEFRAMES timeframe)
|
||
{
|
||
int adx_handle = iADX(_Symbol, timeframe, ADXPeriod);
|
||
if(adx_handle == INVALID_HANDLE)
|
||
{
|
||
Print("Error creating ADX handle: ", GetLastError());
|
||
return 0;
|
||
}
|
||
|
||
double adx_value[1]; // Array to store the ADX value
|
||
if(CopyBuffer(adx_handle, 0, 0, 1, adx_value) <= 0) // Copy the main ADX line
|
||
{
|
||
Print("Error copying ADX buffer: ", GetLastError());
|
||
return 0;
|
||
}
|
||
|
||
return adx_value[0]; // Return the latest ADX value
|
||
}
|
||
|
||
double CalculateDynamicTakeProfit(double atrValue, double multiplier)
|
||
{
|
||
return atrValue * multiplier * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Calculate lot size based on risk from entry to stop-loss |
|
||
//+------------------------------------------------------------------+
|
||
double CalculateLotSizeFromRisk(double entryPrice, double stopLoss)
|
||
{
|
||
// Calculate the risk money (a percentage of account equity)
|
||
double riskMoney = AccountInfoDouble(ACCOUNT_EQUITY) * (RiskPerTrade / 100.0);
|
||
|
||
// Calculate the risk in points (using the symbol’s point size)
|
||
double riskPoints = MathAbs(entryPrice - stopLoss) / SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
|
||
// Obtain tick value and tick size to determine pip value per lot.
|
||
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
||
// Here we assume one pip equals one Symbol POINT in value terms.
|
||
double pipValue = (tickValue / tickSize) * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
|
||
// Calculate volume so that: riskMoney = lot * (riskPoints * pipValue)
|
||
double lot = riskMoney / (riskPoints * pipValue);
|
||
return NormalizeDouble(lot, 2);
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Order Execution Helpers |
|
||
//+------------------------------------------------------------------+
|
||
void ExecuteBuyOrder(double volume, double entryPrice, double stopLoss, double takeProfit, double slippage)
|
||
{
|
||
MqlTradeRequest request;
|
||
MqlTradeResult result;
|
||
ZeroMemory(request);
|
||
ZeroMemory(result);
|
||
|
||
// Set up the trade request
|
||
request.action = TRADE_ACTION_DEAL; // Immediate order execution
|
||
request.symbol = _Symbol; // Current symbol
|
||
request.volume = volume; // Calculated volume based on risk
|
||
request.type = ORDER_TYPE_BUY; // Buy order
|
||
request.price = entryPrice; // Entry price
|
||
request.sl = stopLoss; // Stop loss
|
||
request.tp = takeProfit; // Take profit
|
||
request.deviation = (uint)slippage; // Allowed slippage
|
||
request.magic = MagicNumber; // Use the magic number
|
||
|
||
// Send the order
|
||
if(!OrderSend(request, result))
|
||
Print("Buy Order Failed: ", GetLastError());
|
||
else
|
||
Print("Buy Order Executed Successfully");
|
||
}
|
||
|
||
void ExecuteSellOrder(double volume, double entryPrice, double stopLoss, double takeProfit, double slippage)
|
||
{
|
||
MqlTradeRequest request;
|
||
MqlTradeResult result;
|
||
ZeroMemory(request);
|
||
ZeroMemory(result);
|
||
|
||
// Set up the trade request
|
||
request.action = TRADE_ACTION_DEAL; // Immediate order execution
|
||
request.symbol = _Symbol; // Current symbol
|
||
request.volume = volume; // Calculated volume based on risk
|
||
request.type = ORDER_TYPE_SELL; // Sell order
|
||
request.price = entryPrice; // Entry price
|
||
request.sl = stopLoss; // Stop loss
|
||
request.tp = takeProfit; // Take profit
|
||
request.deviation = (uint)slippage; // Allowed slippage
|
||
request.magic = MagicNumber; // Magic number
|
||
|
||
// Send the order
|
||
if(!OrderSend(request, result))
|
||
Print("Sell Order Failed: ", GetLastError());
|
||
else
|
||
Print("Sell Order Executed Successfully");
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Apply trailing stop to open positions |
|
||
//+------------------------------------------------------------------+
|
||
void ApplyTrailingStop()
|
||
{
|
||
double bidPrice, askPrice;
|
||
MqlTick lastTick;
|
||
if(SymbolInfoTick(_Symbol, lastTick))
|
||
{
|
||
bidPrice = lastTick.bid;
|
||
askPrice = lastTick.ask;
|
||
}
|
||
|
||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
{
|
||
if(PositionGetSymbol(i) == _Symbol)
|
||
{
|
||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||
double stopLoss = PositionGetDouble(POSITION_SL);
|
||
double takeProfit = PositionGetDouble(POSITION_TP);
|
||
double currentPrice= (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? bidPrice : askPrice;
|
||
double profitPoints= (currentPrice - openPrice) / SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
||
profitPoints = -profitPoints;
|
||
|
||
if(profitPoints >= TrailingStart)
|
||
{
|
||
double newStopLoss = currentPrice - TrailingDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
||
newStopLoss = currentPrice + TrailingDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
|
||
if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && newStopLoss > stopLoss + TrailingStep * SymbolInfoDouble(_Symbol, SYMBOL_POINT)) ||
|
||
(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && newStopLoss < stopLoss - TrailingStep * SymbolInfoDouble(_Symbol, SYMBOL_POINT)))
|
||
{
|
||
trade.PositionModify(PositionGetInteger(POSITION_TICKET), newStopLoss, takeProfit);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Place Fade Orders if aligned with MA |
|
||
//+------------------------------------------------------------------+
|
||
void PlaceFadeOrders()
|
||
{
|
||
double entryPriceSell = R2;
|
||
double entryPriceBuy = S2;
|
||
double slSell = R2 + ((R2 - R1) * StopLossFactor);
|
||
double slBuy = S2 - ((S1 - S2) * StopLossFactor);
|
||
double tpSell = (R1 * TakeProfitFactor);
|
||
double tpBuy = (S1 * TakeProfitFactor);
|
||
|
||
// For fade orders, calculate volume based on the risk between entry and stop loss.
|
||
if(FastMAPeriod > SlowMAPeriod && entryPriceSell > PivotPoint)
|
||
{
|
||
double lotSell = CalculateLotSizeFromRisk(entryPriceSell, slSell);
|
||
PlaceLimitOrder(ORDER_TYPE_SELL_LIMIT, entryPriceSell, slSell, tpSell, lotSell);
|
||
}
|
||
|
||
if(FastMAPeriod < SlowMAPeriod && entryPriceBuy < PivotPoint)
|
||
{
|
||
double lotBuy = CalculateLotSizeFromRisk(entryPriceBuy, slBuy);
|
||
PlaceLimitOrder(ORDER_TYPE_BUY_LIMIT, entryPriceBuy, slBuy, tpBuy, lotBuy);
|
||
}
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
//| Place Limit Order Helper |
|
||
//+------------------------------------------------------------------+
|
||
void PlaceLimitOrder(ENUM_ORDER_TYPE orderType, double entryPrice, double slPrice, double tpPrice, double volume)
|
||
{
|
||
MqlTradeRequest request;
|
||
MqlTradeResult result;
|
||
ZeroMemory(request);
|
||
ZeroMemory(result);
|
||
|
||
request.action = TRADE_ACTION_PENDING;
|
||
request.symbol = _Symbol;
|
||
request.magic = MagicNumber;
|
||
request.volume = volume;
|
||
request.type = orderType;
|
||
request.price = entryPrice;
|
||
request.sl = slPrice;
|
||
request.tp = tpPrice;
|
||
|
||
if(!OrderSend(request, result))
|
||
{
|
||
Print("OrderSend failed: ", GetLastError());
|
||
}
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
/*****=====================================================================
|
||
* Cleanup any closed positions from the best price array
|
||
*======================================================================*****/
|
||
void CleanUpClosedPositions()
|
||
{
|
||
// Loop through our array of known tickets and remove closed ones
|
||
for(int i = posCount - 1; i >= 0; i--)
|
||
{
|
||
ulong storedTicket = posTicketArray[i];
|
||
bool found = false;
|
||
|
||
uint totalPos = PositionsTotal();
|
||
for(uint p = 0; p < totalPos; p++)
|
||
{
|
||
if(PositionGetTicket(p) == storedTicket)
|
||
{
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(!found)
|
||
{
|
||
for(int j = i; j < posCount - 1; j++)
|
||
{
|
||
posTicketArray[j] = posTicketArray[j+1];
|
||
bestPriceArray[j] = bestPriceArray[j+1];
|
||
}
|
||
posCount--;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*****=====================================================================
|
||
* IsTradingDay()
|
||
*======================================================================*****/
|
||
bool IsTradingDay()
|
||
{
|
||
MqlDateTime mt;
|
||
TimeToStruct(TimeCurrent(), mt);
|
||
int dow = mt.day_of_week; // 0=Sun,1=Mon,...,6=Sat
|
||
switch(dow)
|
||
{
|
||
case 0: return TradeSunday;
|
||
case 1: return TradeMonday;
|
||
case 2: return TradeTuesday;
|
||
case 3: return TradeWednesday;
|
||
case 4: return TradeThursday;
|
||
case 5: return TradeFriday;
|
||
case 6: return TradeSaturday;
|
||
}
|
||
return false;
|
||
}
|
||
//+------------------------------------------------------------------+
|