Advertisement IC Markets

Never Average Down on a Losing Scalp

Master exponential money management, position sizing calculators, strict daily stop-loss limits, and overcoming FOMO on micro-timeframes.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Never Average Down on a Losing Scalp

Post by PTScalper »

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.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Never Average Down on a Losing Scalp

Post by PTScalper »

1. MetaTrader 4 (MQL4)

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.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Never Average Down on a Losing Scalp

Post by PTScalper »

2. MetaTrader 5 (MQL5)

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.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Never Average Down on a Losing Scalp

Post by PTScalper »

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.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply