Files
mql-trading-bots/Bot10002.mq5
Garfield 74308b38e7 Initial commit: MQL Trading Bots
- 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
2026-03-21 18:39:48 -04:00

841 lines
63 KiB
Plaintext
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| 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 symbols 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;
}
//+------------------------------------------------------------------+