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);
}