Files
mql-trading-bots/Bot10001.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

595 lines
44 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.
//+------------------------------------------------------------------+
//| Bot10001.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; // (Removed)
//--- 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 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
// (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 = 30; //RSI over bought
input int RSIOverbought = 70; //RSI over sold
//--- Pin Bar Filter
input bool UsePinBarFilter = true;
//--- Control for Previous Day Trade Restriction
input bool RequirePreviousDayTrade = true;
//--- ATR-Based Stop Loss
input bool UseATRStopLoss = true; // Enable ATR-Based Stop-Loss
input bool UseATRTakeProfit = true;
input int ATRMultiplier = 2; // ATR Multiplier for Stop-Loss
input int ATRPeriod = 14; // ATR Period for Calculation
//--- Global variables (Pivot Points)
static datetime TradeDate = 0; // Tracks the current day
static bool OrdersPlaced = false;
/***** 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);
//--- Get current market prices
double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);
MqlTick lastTick;
SymbolInfoTick(_Symbol, lastTick);
//--- Execute trading logic
ExecuteTradingLogic(FastMA, SlowMA, currentClose, lastTick );
ApplyTrailingStop();
}
//+------------------------------------------------------------------+
//| 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;
}
//+------------------------------------------------------------------+
//| Calculate ATR-Based Stop Loss |
//+------------------------------------------------------------------+
double CalculateATRStopLoss()
{
if (!UseATRStopLoss) return StopLossPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Use Fixed SL if ATR is disabled
int atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod);
if (atrHandle == INVALID_HANDLE)
{
Print("Failed to create ATR handle");
return StopLossPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Fallback to Fixed SL
}
double atrBuffer[1];
if (CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) <= 0)
{
Print("Failed to retrieve ATR values");
return StopLossPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Fallback to Fixed SL
}
IndicatorRelease(atrHandle);
return atrBuffer[0] * ATRMultiplier;
}
//--- ATR-Based Take Profit Calculation
//+------------------------------------------------------------------+
double CalculateATRTakeProfit()
{
if (!UseATRTakeProfit) return TakeProfitPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod);
if (atrHandle == INVALID_HANDLE)
return TakeProfitPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double atrBuffer[1];
if (CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) <= 0)
return TakeProfitPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
IndicatorRelease(atrHandle);
return atrBuffer[0] * ATRMultiplier;
}
//+------------------------------------------------------------------+
//| 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 )
{
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
// Only check on new bar formation
if(lastBar == currentBar)
return;
lastBar = currentBar;
//bool rsiBuyCondition = IsRSIOversold();
//bool rsiSellCondition = IsRSIOverbought();
double stopLoss = CalculateATRStopLoss(); // Get dynamic stop-loss;
double takeProfit = CalculateATRTakeProfit();
// 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);
bool maBuyCondition = (fastMA > slowMA) && (PositionSelect(_Symbol) == false);
if( maBuyCondition && ( isStrongBullishCandle) && !IsRSIOverbought() && !IsTradeOpenForSymbol() )
{
double tpPrice = closePrice + takeProfit;
double slPrice = closePrice - takeProfit;
double lot = CalculateLotSizeFromRisk(closePrice, closePrice - slPrice);
//ValidateStopLevels(slPrice, tpPrice, closePrice, ORDER_TYPE_BUY);
ExecuteBuyOrder(lot, closePrice, slPrice, tpPrice, Slippage);
}
bool maSellCondition = (fastMA < slowMA) && (PositionSelect(_Symbol) == false);
if( maSellCondition && ( isStrongBearishCandle) && !IsRSIOversold() && !IsTradeOpenForSymbol() )
{
double tpPrice = closePrice - takeProfit;
double slPrice = closePrice + takeProfit;
double lot = CalculateLotSizeFromRisk(closePrice, closePrice + slPrice);
//ValidateStopLevels(slPrice, tpPrice, closePrice, ORDER_TYPE_SELL);
ExecuteSellOrder(lot, closePrice, slPrice, tpPrice, 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);
}
}
}
}
}
//+------------------------------------------------------------------+
/*****=====================================================================
* 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;
}
//+------------------------------------------------------------------+