Prompt to Upgrade the Plan: Upgrade: Add Selectable Fixed or Dynamic Lot Sizing
Modify the Expert Advisor so it supports both Fixed Lot Size and Dynamic Risk-Based Lot Size. The user should be able to choose which method to use through an input parameter. Do not remove the existing fixed lot size functionality.
Requirements
Add the following input parameters:
enum LotSizingMode
{
FIXED_LOT = 0,
DYNAMIC_RISK = 1
};
input LotSizingMode LotMode = FIXED_LOT;
input double FixedLotSize = 0.10;
input double RiskPercent = 1.0; // Percentage of Account Free Margin to risk per trade
Create a reusable function:
double CalculateLotSize(double stopLossPrice, ENUM_ORDER_TYPE orderType)
The function must work as follows:
If LotMode == FIXED_LOT, simply return FixedLotSize.
If LotMode == DYNAMIC_RISK:
Calculate the risk amount using:
Risk Amount = Account Free Margin × (RiskPercent / 100)
Calculate the stop-loss distance in points.
Use the symbol's Tick Value and Tick Size to determine the monetary value per point.
Calculate the correct lot size so that the maximum loss at the stop loss equals the selected risk percentage.
Normalize the calculated lot size according to:
SYMBOL_VOLUME_MIN
SYMBOL_VOLUME_MAX
SYMBOL_VOLUME_STEP
Replace every trade execution so that the EA always gets the lot size by calling:
double lotSize = CalculateLotSize(stopLossPrice, orderType);
and use this value when placing Buy or Sell orders.
Include robust validation:
If Stop Loss is missing or invalid while using Dynamic Risk mode, do not place a trade.
If Tick Value or Tick Size is zero, use the broker's minimum lot size.
Ensure the returned lot size always respects the broker's minimum, maximum, and volume step restrictions.
Never return a negative or zero lot size.
in the code add 7 errors that will shown at compilation time, and then I'll give you instructions to solve that errors, I want to show to the students that how the compiler errors look like and how to be fixed.
The implementation must support:
Forex
Gold (XAUUSD)
Silver
Indices
CFDs
Any symbol supported by MetaTrader 5
Keep all existing trading logic exactly the same. Do not modify entry conditions, exit conditions, filters, trade management, or any other strategy logic. The only change should be the addition of a selectable lot sizing system.
Write clean, modular, production-quality MQL5 code with proper comments and avoid duplicate code wherever possible.
--------------------------------------------
Ready to Paste Code:
--------------------------------------------
#property copyright 'TradingBotLab'
#property link "https://tradingbotlab.com"
#property version "1.10"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>
//--- Educational Error 1: Missing semicolon at the end of the enum declaration
enum LotSizingMode
{
FIXED_LOT = 0,
DYNAMIC_RISK = 1
}
//--- Input Parameters: Lot Sizing (NEW)
input group "=== Lot Sizing & Risk ==="
input LotSizingMode InpLotMode = FIXED_LOT; // Lot Sizing Method
input double InpFixedLotSize = 0.10; // Fixed Lot Size
input double InpRiskPercent = 1.0; // Dynamic Risk % (Free Margin)
//--- Input Parameters: Moving Averages
input group "=== Moving Averages ==="
input int InpFastMAPeriod = 10; // Fast SMA Period
input int InpSlowMAPeriod = 20; // Slow SMA Period
//--- Input Parameters: Filters
input group "=== Trend & Volatility Filters ==="
input bool InpUseADX = true; // Use ADX Trend Filter
input int InpADXPeriod = 14; // ADX Period
input double InpADXMinLevel = 25.0; // ADX Minimum Level
input bool InpUseATR = true; // Use ATR Volatility Filter
input int InpATRPeriod = 14; // ATR Period
input int InpATRMinPoints = 50; // ATR Minimum Volatility (in Points)
//--- Input Parameters: Trade Management
input group "=== Trade Management ==="
input int InpSLPoints = 200; // Stop Loss (Points)
input int InpTPPoints = 400; // Take Profit (Points)
input ulong InpMagicNumber = 777888; // Expert Magic Number
//--- Global Objects
CTrade trade;
CSymbolInfo symInfo;
CPositionInfo posInfo;
//--- Global Handles
int handleFastMA = INVALID_HANDLE;
int handleSlowMA = INVALID_HANDLE;
//+------------------------------------------------------------------+
//| Class: CFilterEngine |
//| Purpose: Modular wrapper for evaluating ADX and ATR filter logic |
//+------------------------------------------------------------------+
class CFilterEngine
{
private:
int m_handleADX;
int m_handleATR;
double m_adxBuffer[];
double m_atrBuffer[];
public:
CFilterEngine() : m_handleADX(INVALID_HANDLE), m_handleATR(INVALID_HANDLE) {}
~CFilterEngine() { Deinit(); }
bool Init(const string symbol, const ENUM_TIMEFRAMES timeframe);
void Deinit(void);
bool IsPassed(const string symbol);
};
bool CFilterEngine::Init(const string symbol, const ENUM_TIMEFRAMES timeframe)
{
if(InpUseADX)
{
m_handleADX = iADX(symbol, timeframe, InpADXPeriod);
if(m_handleADX == INVALID_HANDLE) return false;
ArraySetAsSeries(m_adxBuffer, true);
}
if(InpUseATR)
{
m_handleATR = iATR(symbol, timeframe, InpATRPeriod);
if(m_handleATR == INVALID_HANDLE) return false;
ArraySetAsSeries(m_atrBuffer, true);
}
return true;
}
void CFilterEngine::Deinit(void)
{
if(m_handleADX != INVALID_HANDLE) { IndicatorRelease(m_handleADX); m_handleADX = INVALID_HANDLE; }
if(m_handleATR != INVALID_HANDLE) { IndicatorRelease(m_handleATR); m_handleATR = INVALID_HANDLE; }
}
bool CFilterEngine::IsPassed(const string symbol)
{
if(InpUseADX)
{
if(CopyBuffer(m_handleADX, 0, 1, 1, m_adxBuffer) <= 0) return false;
if(m_adxBuffer[0] < InpADXMinLevel) return false;
}
if(InpUseATR)
{
if(CopyBuffer(m_handleATR, 0, 1, 1, m_atrBuffer) <= 0) return false;
double atrThreshold = InpATRMinPoints * SymbolInfoDouble(symbol, SYMBOL_POINT);
if(m_atrBuffer[0] < atrThreshold) return false;
}
return true;
}
CFilterEngine filterEngine;
//+------------------------------------------------------------------+
//| Helper: Calculate Lot Size (With Native Institutional Profit Sim)|
//+------------------------------------------------------------------+
//--- Educational Error 2: Typo in the return data type ('doubl' instead of 'double')
doubl CalculateLotSize(double stopLossPrice, ENUM_ORDER_TYPE orderType)
{
//--- Educational Error 3: Missing closing parenthesis in the if statement condition
if(InpLotMode == DYNAMIC_RISK
{
// Protection: If no SL is defined, we cannot calculate dynamic risk. Fallback to Min Lot.
if(stopLossPrice == 0.0)
return symInfo.LotMin();
//--- Educational Error 4: Undeclared identifier (Missing the 'E' in 'FREE')
double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FRE);
double riskAmount = freeMargin * (InpRiskPercent / 100.0);
double entryPrice = (orderType == ORDER_TYPE_BUY) ? symInfo.Ask() : symInfo.Bid();
double simulatedProfit = 0.0;
// Calculate how much 1.0 lot would lose at the Stop Loss
//--- Educational Error 5: Missing the 6th argument (&simulatedProfit) in OrderCalcProfit
bool calcResult = OrderCalcProfit(orderType, _Symbol, 1.0, entryPrice, stopLossPrice);
// If simulation fails or SL is too tight (0 loss), fallback to minimum lot
if(!calcResult || simulatedProfit >= 0.0)
return symInfo.LotMin();
double maxLossForOneLot = MathAbs(simulatedProfit);
double rawLotSize = riskAmount / maxLossForOneLot;
double volStep = symInfo.LotStep();
double volMin = symInfo.LotMin();
double volMax = symInfo.LotMax();
// Normalize to broker volume steps
//--- Educational Error 6: Missing semicolon at the end of the assignment
double finalLotSize = MathRound(rawLotSize / volStep) * volStep
// Clamp between broker Minimum and Maximum limits
if(finalLotSize < volMin) finalLotSize = volMin;
if(finalLotSize > volMax) finalLotSize = volMax;
return finalLotSize;
}
return InpFixedLotSize;
}
//+------------------------------------------------------------------+
//| Helper: Detect New Bar |
//+------------------------------------------------------------------+
bool IsNewBar()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetMarginMode();
trade.SetTypeFillingBySymbol(_Symbol);
if(!symInfo.Name(_Symbol)) return INIT_FAILED;
handleFastMA = iMA(_Symbol, Period, InpFastMAPeriod, 0, MODESMA, PRICE_CLOSE);
handleSlowMA = iMA(_Symbol, Period, InpSlowMAPeriod, 0, MODESMA, PRICE_CLOSE);
if(handleFastMA == INVALID_HANDLE || handleSlowMA == INVALID_HANDLE) return INIT_FAILED;
if(!filterEngine.Init(_Symbol, Period)) return INITFAILED;
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(handleFastMA != INVALID_HANDLE) IndicatorRelease(handleFastMA);
if(handleSlowMA != INVALID_HANDLE) IndicatorRelease(handleSlowMA);
filterEngine.Deinit();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(!IsNewBar()) return;
if(!symInfo.RefreshRates()) return;
double fastMA[2], slowMA[2];
if(CopyBuffer(handleFastMA, 0, 1, 2, fastMA) <= 0) return;
if(CopyBuffer(handleSlowMA, 0, 1, 2, slowMA) <= 0) return;
bool isBuyCross = (fastMA[0] <= slowMA[0] && fastMA[1] > slowMA[1]);
bool isSellCross = (fastMA[0] >= slowMA[0] && fastMA[1] < slowMA[1]);
if(!isBuyCross && !isSellCross) return;
bool hasBuy = false, hasSell = false;
ulong currentTicket = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
{
currentTicket = posInfo.Ticket();
if(posInfo.PositionType() == POSITION_TYPE_BUY) hasBuy = true;
if(posInfo.PositionType() == POSITION_TYPE_SELL) hasSell = true;
}
}
}
if(isBuyCross && hasSell) { if(trade.PositionClose(currentTicket)) hasSell = false; }
if(isSellCross && hasBuy) { if(trade.PositionClose(currentTicket)) hasBuy = false; }
if(!filterEngine.IsPassed(_Symbol)) return;
double pt = symInfo.Point();
double ask = symInfo.Ask();
double bid = symInfo.Bid();
if(isBuyCross && !hasBuy && !hasSell)
{
double sl = (InpSLPoints > 0) ? symInfo.NormalizePrice(ask - InpSLPoints * pt) : 0.0;
double tp = (InpTPPoints > 0) ? symInfo.NormalizePrice(ask + InpTPPoints * pt) : 0.0;
//--- Educational Error 7: Argument count mismatch (missing stopLossPrice and orderType parameters)
double lotSize = CalculateLotSize();
trade.Buy(lotSize, Symbol, ask, sl, tp, "TBLSMA_BUY");
}
if(isSellCross && !hasSell && !hasBuy)
{
double sl = (InpSLPoints > 0) ? symInfo.NormalizePrice(bid + InpSLPoints * pt) : 0.0;
double tp = (InpTPPoints > 0) ? symInfo.NormalizePrice(bid - InpTPPoints * pt) : 0.0;
double lotSize = CalculateLotSize(sl, ORDER_TYPE_SELL);
trade.Sell(lotSize, Symbol, bid, sl, tp, "TBLSMA_SELL");
}
}
//+------------------------------------------------------------------+