Here are the complete implementations for both MetaTrader 4 (MQL4) and MetaTrader 5 (MQL5).
Both Expert Advisors enforce the exact same behavioral restrictions:
Strictly No Averaging Down: Before placing an order, the EA scans open orders/positions. If any position with the EA's Magic Number is active on the symbol, execution halts immediately.
Dynamic 1% Risk Sizing: Lot size is calculated from current account equity and the ATR stop distance, then rounded down to the broker’s lot step so you never exceed your chosen risk limit.
Hard Stops on Entry: SL and TP brackets are sent with the market execution call directly to the broker server.
Never Average Down on a Losing Scalp
Re: Never Average Down on a Losing Scalp
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Never Average Down on a Losing Scalp
1. MetaTrader 4 (MQL4)
Save this as StrictRiskScalper.mq4 in your MQL4/Experts/ folder.
Save this as StrictRiskScalper.mq4 in your MQL4/Experts/ folder.
Code: Select all
//+------------------------------------------------------------------+
//| StrictRiskScalper.mq4 |
//| Copyright 2026, Strict Risk Scalper |
//+------------------------------------------------------------------+
#property copyright "Strict Risk Scalper"
#property link ""
#property version "1.00"
#property strict
// =========================================================================
// 1. INPUTS & RISK MANAGEMENT PARAMETERS
// =========================================================================
input group "=== Risk Management ==="
input double InpRiskPct = 1.0; // Risk Per Trade (%)
input double InpSlAtrMult = 1.5; // Stop Loss ATR Multiplier
input double InpRrRatio = 2.0; // Risk:Reward Ratio
input group "=== Strategy Setup ==="
input int InpFastEmaLength = 9; // Fast EMA Length
input int InpSlowEmaLength = 21; // Slow EMA Length
input int InpAtrPeriod = 14; // ATR Period
input group "=== Execution Settings ==="
input int InpMagicNumber = 101102; // Magic Number
input int InpSlippage = 3; // Max Slippage (Pips)
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Execute only once per newly closed candle
if(Time[0] == lastBarTime)
return;
// =========================================================================
// 2. ENFORCE "NO AVERAGING DOWN" RULE
// =========================================================================
if(!IsFlat())
return; // A position is already running. The thesis must play out.
// =========================================================================
// 3. SETUP CONDITIONS (Assessed on bar 1 and bar 2 to confirm bar close)
// =========================================================================
double fastEma1 = iMA(Symbol(), 0, InpFastEmaLength, 0, MODE_EMA, PRICE_CLOSE, 1);
double slowEma1 = iMA(Symbol(), 0, InpSlowEmaLength, 0, MODE_EMA, PRICE_CLOSE, 1);
double fastEma2 = iMA(Symbol(), 0, InpFastEmaLength, 0, MODE_EMA, PRICE_CLOSE, 2);
double slowEma2 = iMA(Symbol(), 0, InpSlowEmaLength, 0, MODE_EMA, PRICE_CLOSE, 2);
bool longSetup = (fastEma2 <= slowEma2) && (fastEma1 > slowEma1);
bool shortSetup = (fastEma2 >= slowEma2) && (fastEma1 < slowEma1);
if(!longSetup && !shortSetup)
return;
// =========================================================================
// 4. POSITION SIZING & STOPS
// =========================================================================
double atr = iATR(Symbol(), 0, InpAtrPeriod, 1);
double slDistance = atr * InpSlAtrMult;
double tpDistance = slDistance * InpRrRatio;
double lotSize = CalculateLotSize(slDistance);
if(lotSize <= 0)
return;
// =========================================================================
// 5. EXECUTION
// =========================================================================
int ticket = -1;
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
if(longSetup)
{
double askPrice = MarketInfo(Symbol(), MODE_ASK);
double slPrice = NormalizeDouble(askPrice - slDistance, digits);
double tpPrice = NormalizeDouble(askPrice + tpDistance, digits);
ticket = OrderSend(Symbol(), OP_BUY, lotSize, askPrice, InpSlippage, slPrice, tpPrice, "NoHopeScalper", InpMagicNumber, 0, clrBlue);
}
else if(shortSetup)
{
double bidPrice = MarketInfo(Symbol(), MODE_BID);
double slPrice = NormalizeDouble(bidPrice + slDistance, digits);
double tpPrice = NormalizeDouble(bidPrice - tpDistance, digits);
ticket = OrderSend(Symbol(), OP_SELL, lotSize, bidPrice, InpSlippage, slPrice, tpPrice, "NoHopeScalper", InpMagicNumber, 0, clrRed);
}
if(ticket > 0)
lastBarTime = Time[0]; // Lock execution to this bar
}
//+------------------------------------------------------------------+
//| Check if there are active positions for this pair and magic |
//+------------------------------------------------------------------+
bool IsFlat()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Calculate lot size to strictly lose X% on stop out |
//+------------------------------------------------------------------+
double CalculateLotSize(double slDistancePrice)
{
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
if(tickSize <= 0 || tickValue <= 0 || slDistancePrice <= 0)
return 0.0;
double riskAmount = AccountEquity() * (InpRiskPct / 100.0);
double lossPerLot = (slDistancePrice / tickSize) * tickValue;
if(lossPerLot <= 0)
return 0.0;
double rawLots = riskAmount / lossPerLot;
// Floor to the nearest lot step to guarantee we don't exceed the risk ceiling
double normalizedLots = MathFloor(rawLots / lotStep) * lotStep;
if(normalizedLots < minLot)
{
Print("Calculated lots (", rawLots, ") is below minimum allowed lot size (", minLot, ").");
return 0.0;
}
return MathMin(normalizedLots, maxLot);
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Never Average Down on a Losing Scalp
2. MetaTrader 5 (MQL5)
Save this as StrictRiskScalper.mq5 in your MQL5/Experts/ folder. MT5 uses indicator handles and the standard CTrade library.
Save this as StrictRiskScalper.mq5 in your MQL5/Experts/ folder. MT5 uses indicator handles and the standard CTrade library.
Code: Select all
//+------------------------------------------------------------------+
//| StrictRiskScalper.mq5 |
//| Copyright 2026, Strict Risk Scalper |
//+------------------------------------------------------------------+
#property copyright "Strict Risk Scalper"
#property link ""
#property version "1.00"
#include <Trade\Trade.mqh>
// =========================================================================
// 1. INPUTS & RISK MANAGEMENT PARAMETERS
// =========================================================================
input group "=== Risk Management ==="
input double InpRiskPct = 1.0; // Risk Per Trade (%)
input double InpSlAtrMult = 1.5; // Stop Loss ATR Multiplier
input double InpRrRatio = 2.0; // Risk:Reward Ratio
input group "=== Strategy Setup ==="
input int InpFastEmaLength = 9; // Fast EMA Length
input int InpSlowEmaLength = 21; // Slow EMA Length
input int InpAtrPeriod = 14; // ATR Period
input group "=== Execution Settings ==="
input ulong InpMagicNumber = 101102; // Magic Number
input ulong InpDeviation = 10; // Max Deviation (Points)
// Handles & Trade object
CTrade trade;
int handleFastEma;
int handleSlowEma;
int handleAtr;
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(InpDeviation);
handleFastEma = iMA(_Symbol, _Period, InpFastEmaLength, 0, MODE_EMA, PRICE_CLOSE);
handleSlowEma = iMA(_Symbol, _Period, InpSlowEmaLength, 0, MODE_EMA, PRICE_CLOSE);
handleAtr = iATR(_Symbol, _Period, InpAtrPeriod);
if(handleFastEma == INVALID_HANDLE || handleSlowEma == INVALID_HANDLE || handleAtr == INVALID_HANDLE)
{
Print("Error creating indicator handles.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(handleFastEma);
IndicatorRelease(handleSlowEma);
IndicatorRelease(handleAtr);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Execute only once per newly closed candle
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime == lastBarTime)
return;
// =========================================================================
// 2. ENFORCE "NO AVERAGING DOWN" RULE
// =========================================================================
if(!IsFlat())
return; // Position exists. No secondary additions allowed.
// =========================================================================
// 3. SETUP CONDITIONS (Assessed on bar 1 and bar 2 to confirm bar close)
// =========================================================================
double fastEma[2], slowEma[2], atr[1];
if(CopyBuffer(handleFastEma, 0, 1, 2, fastEma) < 2 ||
CopyBuffer(handleSlowEma, 0, 1, 2, slowEma) < 2 ||
CopyBuffer(handleAtr, 0, 1, 1, atr) < 1)
{
return;
}
// fastEma[1] is bar 1 (most recently closed), fastEma[0] is bar 2
bool longSetup = (fastEma[0] <= slowEma[0]) && (fastEma[1] > slowEma[1]);
bool shortSetup = (fastEma[0] >= slowEma[0]) && (fastEma[1] < slowEma[1]);
if(!longSetup && !shortSetup)
return;
// =========================================================================
// 4. POSITION SIZING & STOPS
// =========================================================================
double slDistance = atr[0] * InpSlAtrMult;
double tpDistance = slDistance * InpRrRatio;
double lotSize = CalculateLotSize(slDistance);
if(lotSize <= 0)
return;
// =========================================================================
// 5. EXECUTION
// =========================================================================
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
if(longSetup)
{
double askPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double slPrice = NormalizeDouble(askPrice - slDistance, digits);
double tpPrice = NormalizeDouble(askPrice + tpDistance, digits);
if(trade.Buy(lotSize, _Symbol, askPrice, slPrice, tpPrice, "NoHopeScalper"))
lastBarTime = currentBarTime;
}
else if(shortSetup)
{
double bidPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double slPrice = NormalizeDouble(bidPrice + slDistance, digits);
double tpPrice = NormalizeDouble(bidPrice - tpDistance, digits);
if(trade.Sell(lotSize, _Symbol, bidPrice, slPrice, tpPrice, "NoHopeScalper"))
lastBarTime = currentBarTime;
}
}
//+------------------------------------------------------------------+
//| Check if there are active positions for this pair and magic |
//+------------------------------------------------------------------+
bool IsFlat()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
{
return false;
}
}
}
return true;
}
//+------------------------------------------------------------------+
//| Calculate lot size to strictly lose X% on stop out |
//+------------------------------------------------------------------+
double CalculateLotSize(double slDistancePrice)
{
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(tickSize <= 0 || tickValue <= 0 || slDistancePrice <= 0)
return 0.0;
double riskAmount = AccountInfoDouble(ACCOUNT_EQUITY) * (InpRiskPct / 100.0);
double lossPerLot = (slDistancePrice / tickSize) * tickValue;
if(lossPerLot <= 0)
return 0.0;
double rawLots = riskAmount / lossPerLot;
// Floor to the nearest lot step to ensure we do not round up into excess risk
double normalizedLots = MathFloor(rawLots / lotStep) * lotStep;
if(normalizedLots < minLot)
{
Print("Calculated volume (", rawLots, ") is below broker minimum (", minLot, ").");
return 0.0;
}
return MathMin(normalizedLots, maxLot);
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Never Average Down on a Losing Scalp
Key Implementation Details:
MathFloor Sizing: Many default scripts use MathRound when fitting lot sizes to broker steps. This code deliberately uses MathFloor. If rounding pushed your position up, a stop-out would exceed your 1% limit. By rounding down, you are mathematically guaranteed to risk equal to or slightly less than 1%.
Bar Completion Guards: Both scripts sample bar 1 vs bar 2 to verify signal crossovers and track lastBarTime. They never trigger on intra-bar fakeouts or place multiple orders on the same candle.
Separation of Magic Numbers: Both scripts use distinct Magic Numbers (101102), ensuring they only manage their own trades and won't interfere with manual trades or other automated systems running in your terminal.
MathFloor Sizing: Many default scripts use MathRound when fitting lot sizes to broker steps. This code deliberately uses MathFloor. If rounding pushed your position up, a stop-out would exceed your 1% limit. By rounding down, you are mathematically guaranteed to risk equal to or slightly less than 1%.
Bar Completion Guards: Both scripts sample bar 1 vs bar 2 to verify signal crossovers and track lastBarTime. They never trigger on intra-bar fakeouts or place multiple orders on the same candle.
Separation of Magic Numbers: Both scripts use distinct Magic Numbers (101102), ensuring they only manage their own trades and won't interfere with manual trades or other automated systems running in your terminal.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.