Advertisement IC Markets

The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

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: The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

Post by PTScalper »

1. MetaTrader 4 (MQL4)

Save this file as RiskContainer.mq4 in your MQL4/Experts folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                                RiskContainer.mq4 |
//|                                  "The House Plan & Scalp Bricks" |
//+------------------------------------------------------------------+
#property copyright "Risk Container"
#property link      ""
#property version   "1.00"
#property strict

//--- 1. THE CONTAINER INPUTS
input string   InpSessionHeader     = "=== The Container (Risk/Time) ==="; // --- Risk Settings ---
input string   InpSessionStart      = "09:30";   // Session Start (Server HH:MM)
input string   InpSessionEnd        = "11:30";   // Session End (Server HH:MM)
input double   InpMaxDailyLossPct   = 2.0;       // Max Daily Loss (%)
input double   InpDailyGoalPct      = 4.0;       // Good Enough Goal (%)
input double   InpLotSize           = 0.1;       // Fixed Lot Size
input int      InpMagicNumber       = 991122;    // EA Magic Number
input int      InpSlippage          = 3;         // Max Slippage (points)

//--- 2. THE BRICKS INPUTS
input string   InpBricksHeader      = "=== The Bricks (Scalp Logic) ===";  // --- Scalp Settings ---
input int      InpFastEMA           = 9;         // Fast EMA Period
input int      InpSlowEMA           = 21;        // Slow EMA Period

//--- Internal State Variables
datetime g_currentDay;
double   g_startOfDayEquity;
bool     g_tradingHalted;
datetime g_lastBarTime;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   g_currentDay        = TimeDay(TimeCurrent());
   g_startOfDayEquity  = AccountEquity();
   g_tradingHalted     = false;
   g_lastBarTime       = 0;
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   ManageDailyRollover();
   EnforceContainerLimits();
   UpdateDashboard();
   
   // Check bar close to execute the Bricks (avoids intra-bar false signals)
   if(Time[0] != g_lastBarTime)
   {
      g_lastBarTime = Time[0];
      ExecuteBricks();
   }
}

//+------------------------------------------------------------------+
//| Container: Daily Rollover Reset                                  |
//+------------------------------------------------------------------+
void ManageDailyRollover()
{
   if(TimeDay(TimeCurrent()) != g_currentDay)
   {
      g_currentDay       = TimeDay(TimeCurrent());
      g_startOfDayEquity = AccountEquity();
      g_tradingHalted    = false;
      Print("[Container] New trading day initialized. Equity baseline: ", g_startOfDayEquity);
   }
}

//+------------------------------------------------------------------+
//| Container: Enforce Hard Floor, Goal Ceiling, and Time Windows    |
//+------------------------------------------------------------------+
void EnforceContainerLimits()
{
   double dailyPnLPct = ((AccountEquity() - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;
   
   // 1. Hard Floor / Hard Ceiling Checks
   if(!g_tradingHalted)
   {
      if(dailyPnLPct <= -InpMaxDailyLossPct)
      {
         Print("[Container] Hard floor hit (", DoubleToString(dailyPnLPct, 2), "%). Liquidating positions.");
         g_tradingHalted = true;
         CloseAllPositions();
      }
      else if(dailyPnLPct >= InpDailyGoalPct)
      {
         Print("[Container] Goal reached (", DoubleToString(dailyPnLPct, 2), "%). Desk closed.");
         g_tradingHalted = true;
         CloseAllPositions();
      }
   }
   
   // 2. Session Time Enforcement
   if(!IsInSession() && CountBotPositions() > 0)
   {
      Print("[Container] End of trading window. Session liquidated.");
      CloseAllPositions();
   }
}

//+------------------------------------------------------------------+
//| Container: Session Filter Validator                              |
//+------------------------------------------------------------------+
bool IsInSession()
{
   datetime now = TimeCurrent();
   int currentSeconds = TimeHour(now) * 3600 + TimeMinute(now) * 60 + TimeSeconds(now);
   
   int startSeconds = ParseTimeToSeconds(InpSessionStart);
   int endSeconds   = ParseTimeToSeconds(InpSessionEnd);
   
   if(startSeconds <= endSeconds)
      return (currentSeconds >= startSeconds && currentSeconds <= endSeconds);
   else
      return (currentSeconds >= startSeconds || currentSeconds <= endSeconds); // Overnight span
}

int ParseTimeToSeconds(string timeStr)
{
   string parts[];
   ushort u_sep = StringGetCharacter(":", 0);
   int count = StringSplit(timeStr, u_sep, parts);
   if(count >= 2)
      return (int)StringToInteger(parts[0]) * 3600 + (int)StringToInteger(parts[1]) * 60;
   return 0;
}

//+------------------------------------------------------------------+
//| Bricks: M1/M5 Scalp Execution                                    |
//+------------------------------------------------------------------+
void ExecuteBricks()
{
   if(g_tradingHalted || !IsInSession())
      return;
      
   double fastPrev = iMA(Symbol(), 0, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE, 2);
   double fastCurr = iMA(Symbol(), 0, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   double slowPrev = iMA(Symbol(), 0, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE, 2);
   double slowCurr = iMA(Symbol(), 0, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   bool longSignal  = (fastPrev <= slowPrev && fastCurr > slowCurr);
   bool shortSignal = (fastPrev >= slowPrev && fastCurr < slowCurr);
   
   if(longSignal)
   {
      ClosePositionsByType(OP_SELL);
      if(CountBotPositions() == 0)
         OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, InpSlippage, 0, 0, "RiskContainer", InpMagicNumber, 0, clrBlue);
   }
   else if(shortSignal)
   {
      ClosePositionsByType(OP_BUY);
      if(CountBotPositions() == 0)
         OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, InpSlippage, 0, 0, "RiskContainer", InpMagicNumber, 0, clrRed);
   }
}

//+------------------------------------------------------------------+
//| Order Liquidation Helpers                                        |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
         {
            if(OrderType() == OP_BUY)
               OrderClose(OrderTicket(), OrderLots(), Bid, InpSlippage, clrGray);
            else if(OrderType() == OP_SELL)
               OrderClose(OrderTicket(), OrderLots(), Ask, InpSlippage, clrGray);
         }
      }
   }
}

void ClosePositionsByType(int type)
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber && OrderType() == type)
         {
            double price = (type == OP_BUY) ? Bid : Ask;
            OrderClose(OrderTicket(), OrderLots(), price, InpSlippage, clrGray);
         }
      }
   }
}

int CountBotPositions()
{
   int count = 0;
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
            count++;
      }
   }
   return count;
}

//+------------------------------------------------------------------+
//| On-Chart HUD                                                     |
//+------------------------------------------------------------------+
void UpdateDashboard()
{
   double dailyPnLPct = ((AccountEquity() - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;
   
   string status = "ACTIVE";
   if(g_tradingHalted)
   {
      if(dailyPnLPct >= InpDailyGoalPct) status = "GOAL REACHED (DESK CLOSED)";
      else status = "MAX LOSS HIT (DESK CLOSED)";
   }
   else if(!IsInSession())
   {
      status = "OUT OF SESSION";
   }
   
   string dash = StringConcatenate(
      "-----------------------------------------\n",
      " THE RISK CONTAINER (MT4)\n",
      " Daily Baseline: $", DoubleToString(g_startOfDayEquity, 2), "\n",
      " Current PnL: ", DoubleToString(dailyPnLPct, 2), "%\n",
      " Status: ", status, "\n",
      "-----------------------------------------"
   );
   
   Comment(dash);
}
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: The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

Post by PTScalper »

2. MetaTrader 5 (MQL5)

Save this file as RiskContainer.mq5 in your MQL5/Experts folder. It uses standard library handles (CTrade) to ensure clean execution and modern position tracking.

Code: Select all

//+------------------------------------------------------------------+
//|                                                RiskContainer.mq5 |
//|                                  "The House Plan & Scalp Bricks" |
//+------------------------------------------------------------------+
#property copyright "Risk Container"
#property link      ""
#property version   "1.00"

#include <Trade\Trade.mqh>

//--- 1. THE CONTAINER INPUTS
input group "=== The Container (Risk/Time) ==="
input string   InpSessionStart      = "09:30";   // Session Start (Server HH:MM)
input string   InpSessionEnd        = "11:30";   // Session End (Server HH:MM)
input double   InpMaxDailyLossPct   = 2.0;       // Max Daily Loss (%)
input double   InpDailyGoalPct      = 4.0;       // Good Enough Goal (%)
input double   InpLotSize           = 0.1;       // Fixed Lot Size
input ulong    InpMagicNumber       = 991122;    // EA Magic Number
input ulong    InpSlippage          = 3;         // Max Slippage (points)

//--- 2. THE BRICKS INPUTS
input group "=== The Bricks (Scalp Logic) ==="
input int      InpFastEMA           = 9;         // Fast EMA Period
input int      InpSlowEMA           = 21;        // Slow EMA Period

//--- Internal State Variables
CTrade   trade;
int      fastEmaHandle;
int      slowEmaHandle;
datetime g_currentDay;
double   g_startOfDayEquity;
bool     g_tradingHalted;
datetime g_lastBarTime;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   trade.SetExpertMagicNumber(InpMagicNumber);
   trade.SetDeviationInPoints(InpSlippage);

   fastEmaHandle = iMA(_Symbol, _Period, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE);
   slowEmaHandle = iMA(_Symbol, _Period, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE);

   if(fastEmaHandle == INVALID_HANDLE || slowEmaHandle == INVALID_HANDLE)
   {
      Print("[Error] Failed to initialize indicator handles.");
      return(INIT_FAILED);
   }

   MqlDateTime dt;
   TimeCurrent(dt);
   g_currentDay        = dt.day;
   g_startOfDayEquity  = AccountInfoDouble(ACCOUNT_EQUITY);
   g_tradingHalted     = false;
   g_lastBarTime       = 0;

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   IndicatorRelease(fastEmaHandle);
   IndicatorRelease(slowEmaHandle);
   Comment("");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   ManageDailyRollover();
   EnforceContainerLimits();
   UpdateDashboard();

   datetime currentBarTime = iTime(_Symbol, _Period, 0);
   if(currentBarTime != g_lastBarTime)
   {
      g_lastBarTime = currentBarTime;
      ExecuteBricks();
   }
}

//+------------------------------------------------------------------+
//| Container: Daily Rollover Reset                                  |
//+------------------------------------------------------------------+
void ManageDailyRollover()
{
   MqlDateTime dt;
   TimeCurrent(dt);

   if(dt.day != g_currentDay)
   {
      g_currentDay       = dt.day;
      g_startOfDayEquity = AccountInfoDouble(ACCOUNT_EQUITY);
      g_tradingHalted    = false;
      Print("[Container] New session day initialized. Baseline Equity: ", g_startOfDayEquity);
   }
}

//+------------------------------------------------------------------+
//| Container: Risk Limits & Session Control                         |
//+------------------------------------------------------------------+
void EnforceContainerLimits()
{
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double dailyPnLPct   = ((currentEquity - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;

   // 1. Hard Floor / Hard Ceiling Checks
   if(!g_tradingHalted)
   {
      if(dailyPnLPct <= -InpMaxDailyLossPct)
      {
         Print("[Container] Hard floor hit (", DoubleToString(dailyPnLPct, 2), "%). Halting.");
         g_tradingHalted = true;
         CloseAllPositions();
      }
      else if(dailyPnLPct >= InpDailyGoalPct)
      {
         Print("[Container] Goal reached (", DoubleToString(dailyPnLPct, 2), "%). Desk closed.");
         g_tradingHalted = true;
         CloseAllPositions();
      }
   }

   // 2. Session Time Enforcement
   if(!IsInSession() && CountBotPositions() > 0)
   {
      Print("[Container] Out of window. Auto-liquidating session.");
      CloseAllPositions();
   }
}

//+------------------------------------------------------------------+
//| Container: Session Time Gate                                     |
//+------------------------------------------------------------------+
bool IsInSession()
{
   MqlDateTime dt;
   TimeCurrent(dt);
   int currentSeconds = dt.hour * 3600 + dt.min * 60 + dt.sec;

   int startSeconds = ParseTimeToSeconds(InpSessionStart);
   int endSeconds   = ParseTimeToSeconds(InpSessionEnd);

   if(startSeconds <= endSeconds)
      return (currentSeconds >= startSeconds && currentSeconds <= endSeconds);
   else
      return (currentSeconds >= startSeconds || currentSeconds <= endSeconds);
}

int ParseTimeToSeconds(string timeStr)
{
   string parts[];
   ushort u_sep = StringGetCharacter(":", 0);
   int count = StringSplit(timeStr, u_sep, parts);
   if(count >= 2)
      return (int)StringToInteger(parts[0]) * 3600 + (int)StringToInteger(parts[1]) * 60;
   return 0;
}

//+------------------------------------------------------------------+
//| Bricks: Execution Engine                                         |
//+------------------------------------------------------------------+
void ExecuteBricks()
{
   if(g_tradingHalted || !IsInSession())
      return;

   double fastEma[];
   double slowEma[];
   ArraySetAsSeries(fastEma, true);
   ArraySetAsSeries(slowEma, true);

   if(CopyBuffer(fastEmaHandle, 0, 1, 2, fastEma) < 2 || CopyBuffer(slowEmaHandle, 0, 1, 2, slowEma) < 2)
      return;

   // fastEma[0] = Bar 1 (closed), fastEma[1] = Bar 2
   bool longSignal  = (fastEma[1] <= slowEma[1] && fastEma[0] > slowEma[0]);
   bool shortSignal = (fastEma[1] >= slowEma[1] && fastEma[0] < slowEma[0]);

   if(longSignal)
   {
      ClosePositionsByType(POSITION_TYPE_SELL);
      if(CountBotPositions() == 0)
         trade.Buy(InpLotSize, _Symbol, SymbolInfoDouble(_Symbol, SYMBOL_ASK), 0, 0, "RiskContainer");
   }
   else if(shortSignal)
   {
      ClosePositionsByType(POSITION_TYPE_BUY);
      if(CountBotPositions() == 0)
         trade.Sell(InpLotSize, _Symbol, SymbolInfoDouble(_Symbol, SYMBOL_BID), 0, 0, "RiskContainer");
   }
}

//+------------------------------------------------------------------+
//| Liquidation Utilities                                            |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0)
      {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
            trade.PositionClose(ticket);
      }
   }
}

void ClosePositionsByType(ENUM_POSITION_TYPE posType)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0)
      {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol && 
            PositionGetInteger(POSITION_MAGIC) == InpMagicNumber && 
            PositionGetInteger(POSITION_TYPE) == posType)
         {
            trade.PositionClose(ticket);
         }
      }
   }
}

int CountBotPositions()
{
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0)
      {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
            count++;
      }
   }
   return count;
}

//+------------------------------------------------------------------+
//| On-Chart HUD                                                     |
//+------------------------------------------------------------------+
void UpdateDashboard()
{
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double dailyPnLPct   = ((currentEquity - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;

   string status = "ACTIVE";
   if(g_tradingHalted)
   {
      if(dailyPnLPct >= InpDailyGoalPct) status = "GOAL REACHED (DESK CLOSED)";
      else status = "MAX LOSS HIT (DESK CLOSED)";
   }
   else if(!IsInSession())
   {
      status = "OUT OF SESSION";
   }

   string dash = StringFormat(
      "-----------------------------------------\n"
      " THE RISK CONTAINER (MT5)\n"
      " Daily Baseline: $%.2f\n"
      " Current PnL: %.2f%%\n"
      " Status: %s\n"
      "-----------------------------------------",
      g_startOfDayEquity, dailyPnLPct, status
   );

   Comment(dash);
}
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: The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

Post by PTScalper »

Important Setup Notes

Timezone Offset: Both scripts use broker server time (TimeCurrent()), not your PC’s local clock. If your broker runs on GMT+2/GMT+3 (standard for London/NY close charts), verify what 09:30 corresponds to on your broker's Market Watch clock.

Coexistence with Manual Trades: Both scripts identify trades exclusively via InpMagicNumber = 991122. If you swing trade other setups or take manual trades outside the bot, the container will leave those untouched when liquidating.

Execution Boundary: The risk limits check AccountEquity() on every tick, meaning floating drawdowns will trip the circuit breaker mid-candle if necessary. The scalping signals (ExecuteBricks()) only trigger on bar close (Time[0] != g_lastBarTime) to eliminate false intra-bar repainting crosses.
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: The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

Post by PTScalper »

To upgrade the "Risk Container" from a basic retail script to a professional desk-level engine, we must transition from static rules to dynamic lifecycle management. A professional envelope doesn't just cut you off when you lose; it protects your daily profits, sizes positions based on market volatility, and manages the trade while it is open.

Here is the MetaTrader 5 (MQL5) Pro Version. It introduces four institutional-grade upgrades:

High-Watermark Daily Drawdown: Instead of tracking loss from the day's starting balance, it tracks your peak equity of the day. If you are up 3% in the morning, the max loss trails up with you. You cannot give back the whole day.

Volatility-Adjusted Sizing (ATR): Fixed lots are replaced by percentage-based risk. The EA calculates the stop loss distance using Average True Range (ATR) and sizes the lot so a loss equals exactly X% of your account.

Trade Lifecycle (Breakeven & Trail): Once a scalp hits 1R (Risk), the stop loss is automatically moved to breakeven to secure the capital.

Market Defenses: Built-in spread filters block entries during volatile widening, and a "Friday Flat" timer forcefully liquidates the book before the weekend gap.
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: The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

Post by PTScalper »

The MT5 Pro Risk Container

Code: Select all

//+------------------------------------------------------------------+
//|                                             RiskContainerPro.mq5 |
//|                             "Pro Desk Envelope & Scalp Bricks"   |
//+------------------------------------------------------------------+
#property copyright "Pro Risk Container"
#property version   "2.00"

#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>

//--- 1. THE ENVELOPE (Risk, Sizing & Time)
input group "=== The Envelope (Risk & Protection) ==="
input double   InpRiskPerTradePct   = 0.5;       // Risk Per Trade (%)
input double   InpMaxDailyDrawdown  = 2.0;       // Max DD from Daily Peak (%)
input double   InpDailyGoalPct      = 4.0;       // Hard Ceiling Goal (%)
input int      InpMaxSpreadPts      = 15;        // Max Spread (Points) to Enter
input bool     InpUseBreakeven      = true;      // Move SL to Breakeven at 1R?

input group "=== Session Windows ==="
input string   InpSessionStart      = "09:30";   // Session Start (HH:MM)
input string   InpSessionEnd        = "11:30";   // Session End (HH:MM)
input bool     InpCloseOnFriday     = true;      // Flatten Book on Friday?
input int      InpFridayCloseHour   = 20;        // Friday Auto-Close Hour (Server)

//--- 2. THE BRICKS (Logic & ATR)
input group "=== The Bricks (Scalp Logic) ==="
input int      InpFastEMA           = 9;         // Fast EMA Period
input int      InpSlowEMA           = 21;        // Slow EMA Period
input int      InpATRPeriod         = 14;        // ATR Period (for SL/TP/Sizing)
input double   InpATRStopMult       = 1.5;       // SL ATR Multiplier
input double   InpATRRewardMult     = 3.0;       // TP ATR Multiplier
input ulong    InpMagicNumber       = 991133;    // EA Magic Number

//--- Objects
CTrade         trade;
CSymbolInfo    symInfo;
CPositionInfo  posInfo;

//--- Handles & State
int            fastEmaHandle, slowEmaHandle, atrHandle;
datetime       g_currentDay;
double         g_startOfDayEquity;
double         g_dailyPeakEquity;
bool           g_tradingHalted;
datetime       g_lastBarTime;

//+------------------------------------------------------------------+
int OnInit()
{
   trade.SetExpertMagicNumber(InpMagicNumber);
   trade.SetDeviationInPoints(5);
   symInfo.Name(_Symbol);

   fastEmaHandle = iMA(_Symbol, _Period, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE);
   slowEmaHandle = iMA(_Symbol, _Period, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE);
   atrHandle     = iATR(_Symbol, _Period, InpATRPeriod);

   if(fastEmaHandle == INVALID_HANDLE || slowEmaHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE)
   {
      Print("[Error] Indicator initialization failed.");
      return(INIT_FAILED);
   }

   ResetDailyState();
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   IndicatorRelease(fastEmaHandle);
   IndicatorRelease(slowEmaHandle);
   IndicatorRelease(atrHandle);
   Comment("");
}

//+------------------------------------------------------------------+
void OnTick()
{
   symInfo.RefreshRates();
   
   ManageDailyRollover();
   EnforceProContainerLimits();
   ManageOpenTrades();
   UpdateProDashboard();

   datetime currentBarTime = iTime(_Symbol, _Period, 0);
   if(currentBarTime != g_lastBarTime)
   {
      g_lastBarTime = currentBarTime;
      ExecuteBricks();
   }
}

//+------------------------------------------------------------------+
void ResetDailyState()
{
   MqlDateTime dt;
   TimeCurrent(dt);
   g_currentDay        = dt.day;
   g_startOfDayEquity  = AccountInfoDouble(ACCOUNT_EQUITY);
   g_dailyPeakEquity   = g_startOfDayEquity;
   g_tradingHalted     = false;
}

//+------------------------------------------------------------------+
void ManageDailyRollover()
{
   MqlDateTime dt;
   TimeCurrent(dt);
   if(dt.day != g_currentDay)
   {
      ResetDailyState();
      Print("[Container] New session. Baseline/Peak reset to: ", g_startOfDayEquity);
   }
}

//+------------------------------------------------------------------+
void EnforceProContainerLimits()
{
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   
   // Track High Watermark
   if(currentEquity > g_dailyPeakEquity) 
      g_dailyPeakEquity = currentEquity;

   double ddFromPeakPct = ((g_dailyPeakEquity - currentEquity) / g_dailyPeakEquity) * 100.0;
   double dailyPnLPct   = ((currentEquity - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;

   // 1. Hard Floor (Trailing from Peak) & Hard Ceiling
   if(!g_tradingHalted)
   {
      if(ddFromPeakPct >= InpMaxDailyDrawdown)
      {
         PrintFormat("[Container] Peak Drawdown hit (%.2f%%). Halting.", ddFromPeakPct);
         g_tradingHalted = true;
         CloseAllPositions();
      }
      else if(dailyPnLPct >= InpDailyGoalPct)
      {
         PrintFormat("[Container] Goal reached (%.2f%%). Desk closed.", dailyPnLPct);
         g_tradingHalted = true;
         CloseAllPositions();
      }
   }

   // 2. Session Time & Friday Flat Check
   MqlDateTime dt;
   TimeCurrent(dt);
   
   bool isFridayClose = (InpCloseOnFriday && dt.day_of_week == 5 && dt.hour >= InpFridayCloseHour);
   
   if((!IsInSession() || isFridayClose) && CountBotPositions() > 0)
   {
      Print("[Container] Out of window or Friday Close. Auto-liquidating.");
      CloseAllPositions();
   }
}

//+------------------------------------------------------------------+
void ExecuteBricks()
{
   if(g_tradingHalted || !IsInSession()) return;

   // Spread Filter
   if(symInfo.Spread() > InpMaxSpreadPts) return;

   double fastEma[], slowEma[], atr[];
   ArraySetAsSeries(fastEma, true); ArraySetAsSeries(slowEma, true); ArraySetAsSeries(atr, true);

   if(CopyBuffer(fastEmaHandle, 0, 1, 2, fastEma) < 2 || 
      CopyBuffer(slowEmaHandle, 0, 1, 2, slowEma) < 2 || 
      CopyBuffer(atrHandle, 0, 1, 1, atr) < 1) return;

   bool longSignal  = (fastEma[1] <= slowEma[1] && fastEma[0] > slowEma[0]);
   bool shortSignal = (fastEma[1] >= slowEma[1] && fastEma[0] < slowEma[0]);

   if(longSignal && CountBotPositions() == 0)
   {
      double slDist = atr[0] * InpATRStopMult;
      double tpDist = atr[0] * InpATRRewardMult;
      double sl = symInfo.Ask() - slDist;
      double tp = symInfo.Ask() + tpDist;
      double lots = CalculatePositionSize(slDist);
      
      trade.Buy(lots, _Symbol, symInfo.Ask(), sl, tp, "RiskContainer_Long");
   }
   else if(shortSignal && CountBotPositions() == 0)
   {
      double slDist = atr[0] * InpATRStopMult;
      double tpDist = atr[0] * InpATRRewardMult;
      double sl = symInfo.Bid() + slDist;
      double tp = symInfo.Bid() - tpDist;
      double lots = CalculatePositionSize(slDist);
      
      trade.Sell(lots, _Symbol, symInfo.Bid(), sl, tp, "RiskContainer_Short");
   }
}

//+------------------------------------------------------------------+
//| Lifecycle: Move SL to Breakeven at 1R                            |
//+------------------------------------------------------------------+
void ManageOpenTrades()
{
   if(!InpUseBreakeven) return;

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == InpMagicNumber && posInfo.Symbol() == _Symbol)
      {
         double openPrice = posInfo.PriceOpen();
         double currentSL = posInfo.StopLoss();
         double slDistanceInitial = MathAbs(openPrice - currentSL); // Assumes SL was set at entry
         
         if(posInfo.PositionType() == POSITION_TYPE_BUY)
         {
            // If current price is 1R in profit, and SL is not already at/above BE
            if(symInfo.Bid() > openPrice + slDistanceInitial && currentSL < openPrice)
            {
               trade.PositionModify(posInfo.Ticket(), openPrice, posInfo.TakeProfit());
               Print("[Lifecycle] Long Stop moved to Breakeven.");
            }
         }
         else if(posInfo.PositionType() == POSITION_TYPE_SELL)
         {
            // If current price is 1R in profit, and SL is not already at/below BE
            if(symInfo.Ask() < openPrice - slDistanceInitial && (currentSL > openPrice || currentSL == 0))
            {
               trade.PositionModify(posInfo.Ticket(), openPrice, posInfo.TakeProfit());
               Print("[Lifecycle] Short Stop moved to Breakeven.");
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
double CalculatePositionSize(double slDistance)
{
   double riskAmount = AccountInfoDouble(ACCOUNT_EQUITY) * (InpRiskPerTradePct / 100.0);
   double tickValue  = symInfo.TickValue();
   double tickSize   = symInfo.TickSize();
   
   if(slDistance == 0 || tickValue == 0 || tickSize == 0) return symInfo.LotsMin();
   
   double ticks = slDistance / tickSize;
   double lots  = riskAmount / (ticks * tickValue);
   
   // Normalize to broker limits
   double step = symInfo.LotsStep();
   lots = MathFloor(lots / step) * step;
   
   if(lots < symInfo.LotsMin()) lots = symInfo.LotsMin();
   if(lots > symInfo.LotsMax()) lots = symInfo.LotsMax();
   
   return lots;
}

//+------------------------------------------------------------------+
bool IsInSession()
{
   MqlDateTime dt;
   TimeCurrent(dt);
   int currentSecs = dt.hour * 3600 + dt.min * 60 + dt.sec;
   
   string partsStart[], partsEnd[];
   StringSplit(InpSessionStart, ':', partsStart);
   StringSplit(InpSessionEnd, ':', partsEnd);
   
   int startSecs = (int)StringToInteger(partsStart[0]) * 3600 + (int)StringToInteger(partsStart[1]) * 60;
   int endSecs   = (int)StringToInteger(partsEnd[0]) * 3600 + (int)StringToInteger(partsEnd[1]) * 60;
   
   if(startSecs <= endSecs) return (currentSecs >= startSecs && currentSecs <= endSecs);
   else return (currentSecs >= startSecs || currentSecs <= endSecs);
}

void CloseAllPositions()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
         trade.PositionClose(ticket);
   }
}

int CountBotPositions()
{
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
         count++;
   }
   return count;
}

//+------------------------------------------------------------------+
void UpdateProDashboard()
{
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   double dailyPnLPct   = ((currentEquity - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;
   double ddFromPeakPct = ((g_dailyPeakEquity - currentEquity) / g_dailyPeakEquity) * 100.0;

   string status = "ACTIVE";
   if(g_tradingHalted)
   {
      if(dailyPnLPct >= InpDailyGoalPct) status = "GOAL REACHED";
      else status = "MAX DRAWDOWN HIT";
   }
   else if(!IsInSession()) status = "OUT OF SESSION";

   string dash = StringFormat(
      "=== PRO RISK CONTAINER (MT5) ===\n"
      "Daily Start Equity: $%.2f\n"
      "Daily Peak Equity: $%.2f\n"
      "Current PnL: %.2f%%\n"
      "Drawdown from Peak: %.2f%% (Max: %.2f%%)\n"
      "System Status: %s\n"
      "Spread: %d pts (Max: %d)",
      g_startOfDayEquity, g_dailyPeakEquity, dailyPnLPct, 
      ddFromPeakPct, InpMaxDailyDrawdown, status, 
      symInfo.Spread(), InpMaxSpreadPts
   );

   Comment(dash);
}
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: The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

Post by PTScalper »

Here is the exact MetaTrader 4 (MQL4) Pro Version. It translates the institutional-grade features—dynamic high-watermark drawdown, ATR position sizing, 1R breakeven stops, and market defenses—into MQL4's strict syntax.

The MT4 Pro Risk Container

Save this file as RiskContainerPro.mq4 in your MQL4/Experts folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                             RiskContainerPro.mq4 |
//|                             "Pro Desk Envelope & Scalp Bricks"   |
//+------------------------------------------------------------------+
#property copyright "Pro Risk Container"
#property version   "2.00"
#property strict

//--- 1. THE ENVELOPE (Risk, Sizing & Time)
input string   InpEnvHeader         = "=== The Envelope (Risk & Protection) ===";
input double   InpRiskPerTradePct   = 0.5;       // Risk Per Trade (%)
input double   InpMaxDailyDrawdown  = 2.0;       // Max DD from Daily Peak (%)
input double   InpDailyGoalPct      = 4.0;       // Hard Ceiling Goal (%)
input int      InpMaxSpreadPts      = 15;        // Max Spread (Points) to Enter
input bool     InpUseBreakeven      = true;      // Move SL to Breakeven at 1R?

input string   InpTimeHeader        = "=== Session Windows ===";
input string   InpSessionStart      = "09:30";   // Session Start (Server HH:MM)
input string   InpSessionEnd        = "11:30";   // Session End (Server HH:MM)
input bool     InpCloseOnFriday     = true;      // Flatten Book on Friday?
input int      InpFridayCloseHour   = 20;        // Friday Auto-Close Hour (Server)

//--- 2. THE BRICKS (Logic & ATR)
input string   InpBrickHeader       = "=== The Bricks (Scalp Logic) ===";
input int      InpFastEMA           = 9;         // Fast EMA Period
input int      InpSlowEMA           = 21;        // Slow EMA Period
input int      InpATRPeriod         = 14;        // ATR Period (for SL/TP/Sizing)
input double   InpATRStopMult       = 1.5;       // SL ATR Multiplier
input double   InpATRRewardMult     = 3.0;       // TP ATR Multiplier
input int      InpMagicNumber       = 991133;    // EA Magic Number
input int      InpSlippage          = 3;         // Max Slippage (Points)

//--- Internal State Variables
datetime g_currentDay;
double   g_startOfDayEquity;
double   g_dailyPeakEquity;
bool     g_tradingHalted;
datetime g_lastBarTime;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   ResetDailyState();
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Comment("");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   ManageDailyRollover();
   EnforceProContainerLimits();
   ManageOpenTrades();
   UpdateProDashboard();

   // Trigger logic only on bar closes to avoid mid-candle repainting
   if(Time[0] != g_lastBarTime)
   {
      g_lastBarTime = Time[0];
      ExecuteBricks();
   }
}

//+------------------------------------------------------------------+
//| State Management                                                 |
//+------------------------------------------------------------------+
void ResetDailyState()
{
   g_currentDay       = TimeDay(TimeCurrent());
   g_startOfDayEquity = AccountEquity();
   g_dailyPeakEquity  = g_startOfDayEquity;
   g_tradingHalted    = false;
}

void ManageDailyRollover()
{
   if(TimeDay(TimeCurrent()) != g_currentDay)
   {
      ResetDailyState();
      Print("[Container] New session day initialized. Baseline Equity: ", g_startOfDayEquity);
   }
}

//+------------------------------------------------------------------+
//| Enforce Risk Floor, Ceiling & Time Gates                         |
//+------------------------------------------------------------------+
void EnforceProContainerLimits()
{
   double currentEquity = AccountEquity();
   
   // Update the Daily High Watermark
   if(currentEquity > g_dailyPeakEquity) 
      g_dailyPeakEquity = currentEquity;

   double ddFromPeakPct = ((g_dailyPeakEquity - currentEquity) / g_dailyPeakEquity) * 100.0;
   double dailyPnLPct   = ((currentEquity - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;

   // 1. Hard Floor (Trailing from Peak) & Hard Ceiling
   if(!g_tradingHalted)
   {
      if(ddFromPeakPct >= InpMaxDailyDrawdown)
      {
         PrintFormat("[Container] Peak Drawdown hit (%.2f%%). Halting.", ddFromPeakPct);
         g_tradingHalted = true;
         CloseAllPositions();
      }
      else if(dailyPnLPct >= InpDailyGoalPct)
      {
         PrintFormat("[Container] Goal reached (%.2f%%). Desk closed.", dailyPnLPct);
         g_tradingHalted = true;
         CloseAllPositions();
      }
   }

   // 2. Session Time & Friday Flat Check
   bool isFridayClose = (InpCloseOnFriday && TimeDayOfWeek(TimeCurrent()) == 5 && TimeHour(TimeCurrent()) >= InpFridayCloseHour);
   
   if((!IsInSession() || isFridayClose) && CountBotPositions() > 0)
   {
      Print("[Container] Out of window or Friday Close. Auto-liquidating.");
      CloseAllPositions();
   }
}

//+------------------------------------------------------------------+
//| Bricks Execution (ATR Adjusted)                                  |
//+------------------------------------------------------------------+
void ExecuteBricks()
{
   if(g_tradingHalted || !IsInSession()) return;

   // Market Defense: Spread Filter
   int spread = (int)MarketInfo(Symbol(), MODE_SPREAD);
   if(spread > InpMaxSpreadPts) return;

   // Logic Arrays (Check previous closed bar [1] vs [2])
   double fastPrev = iMA(Symbol(), 0, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE, 2);
   double fastCurr = iMA(Symbol(), 0, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   double slowPrev = iMA(Symbol(), 0, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE, 2);
   double slowCurr = iMA(Symbol(), 0, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   double atr      = iATR(Symbol(), 0, InpATRPeriod, 1);

   bool longSignal  = (fastPrev <= slowPrev && fastCurr > slowCurr);
   bool shortSignal = (fastPrev >= slowPrev && fastCurr < slowCurr);

   if(longSignal && CountBotPositions() == 0)
   {
      double slDist = atr * InpATRStopMult;
      double tpDist = atr * InpATRRewardMult;
      double sl = Ask - slDist;
      double tp = Ask + tpDist;
      double lots = CalculatePositionSize(slDist);
      
      OrderSend(Symbol(), OP_BUY, lots, Ask, InpSlippage, sl, tp, "ProContainer_Long", InpMagicNumber, 0, clrBlue);
   }
   else if(shortSignal && CountBotPositions() == 0)
   {
      double slDist = atr * InpATRStopMult;
      double tpDist = atr * InpATRRewardMult;
      double sl = Bid + slDist;
      double tp = Bid - tpDist;
      double lots = CalculatePositionSize(slDist);
      
      OrderSend(Symbol(), OP_SELL, lots, Bid, InpSlippage, sl, tp, "ProContainer_Short", InpMagicNumber, 0, clrRed);
   }
}

//+------------------------------------------------------------------+
//| Trade Lifecycle (Breakeven Trailing)                             |
//+------------------------------------------------------------------+
void ManageOpenTrades()
{
   if(!InpUseBreakeven) return;

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
         {
            double openPrice = OrderOpenPrice();
            double currentSL = OrderStopLoss();
            double tpPrice   = OrderTakeProfit();
            
            // Protect against divide by zero or trades without SL
            if(currentSL == 0) continue; 
            
            double slDistanceInitial = MathAbs(openPrice - currentSL);
            
            if(OrderType() == OP_BUY)
            {
               // If price is 1R in profit, move SL to breakeven (if not already there)
               if(Bid > openPrice + slDistanceInitial && currentSL < openPrice)
               {
                  bool res = OrderModify(OrderTicket(), openPrice, openPrice, tpPrice, 0, clrBlue);
                  if(res) Print("[Lifecycle] Long Stop moved to Breakeven.");
               }
            }
            else if(OrderType() == OP_SELL)
            {
               if(Ask < openPrice - slDistanceInitial && currentSL > openPrice)
               {
                  bool res = OrderModify(OrderTicket(), openPrice, openPrice, tpPrice, 0, clrRed);
                  if(res) Print("[Lifecycle] Short Stop moved to Breakeven.");
               }
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Auto Lot Sizing (Risk %)                                         |
//+------------------------------------------------------------------+
double CalculatePositionSize(double slDistance)
{
   double riskAmount = AccountEquity() * (InpRiskPerTradePct / 100.0);
   double tickValue  = MarketInfo(Symbol(), MODE_TICKVALUE);
   double tickSize   = MarketInfo(Symbol(), MODE_TICKSIZE);
   
   if(slDistance == 0 || tickValue == 0 || tickSize == 0) return MarketInfo(Symbol(), MODE_MINLOT);
   
   // Calculate cost of the stop loss per 1.0 standard lot
   double points = slDistance / tickSize;
   double riskPerStandardLot = points * tickValue;
   
   if(riskPerStandardLot == 0) return MarketInfo(Symbol(), MODE_MINLOT);
   
   double lots = riskAmount / riskPerStandardLot;
   
   // Normalize sizing for broker lot steps limits
   double minLot  = MarketInfo(Symbol(), MODE_MINLOT);
   double maxLot  = MarketInfo(Symbol(), MODE_MAXLOT);
   double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
   
   lots = MathFloor(lots / lotStep) * lotStep;
   
   if(lots < minLot) lots = minLot;
   if(lots > maxLot) lots = maxLot;
   
   return lots;
}

//+------------------------------------------------------------------+
//| Environment / Time Checks                                        |
//+------------------------------------------------------------------+
bool IsInSession()
{
   datetime now = TimeCurrent();
   int currentSeconds = TimeHour(now) * 3600 + TimeMinute(now) * 60 + TimeSeconds(now);
   
   int startSeconds = ParseTimeToSeconds(InpSessionStart);
   int endSeconds   = ParseTimeToSeconds(InpSessionEnd);
   
   if(startSeconds <= endSeconds) return (currentSeconds >= startSeconds && currentSeconds <= endSeconds);
   else return (currentSeconds >= startSeconds || currentSeconds <= endSeconds);
}

int ParseTimeToSeconds(string timeStr)
{
   string parts[];
   ushort u_sep = StringGetCharacter(":", 0);
   int count = StringSplit(timeStr, u_sep, parts);
   if(count >= 2) return (int)StringToInteger(parts[0]) * 3600 + (int)StringToInteger(parts[1]) * 60;
   return 0;
}

void CloseAllPositions()
{
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
         {
            if(OrderType() == OP_BUY) OrderClose(OrderTicket(), OrderLots(), Bid, InpSlippage, clrGray);
            else if(OrderType() == OP_SELL) OrderClose(OrderTicket(), OrderLots(), Ask, InpSlippage, clrGray);
         }
      }
   }
}

int CountBotPositions()
{
   int count = 0;
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber) count++;
      }
   }
   return count;
}

//+------------------------------------------------------------------+
//| Dashboard HUD                                                    |
//+------------------------------------------------------------------+
void UpdateProDashboard()
{
   double currentEquity = AccountEquity();
   double dailyPnLPct   = ((currentEquity - g_startOfDayEquity) / g_startOfDayEquity) * 100.0;
   double ddFromPeakPct = ((g_dailyPeakEquity - currentEquity) / g_dailyPeakEquity) * 100.0;

   string status = "ACTIVE";
   if(g_tradingHalted)
   {
      if(dailyPnLPct >= InpDailyGoalPct) status = "GOAL REACHED";
      else status = "MAX DRAWDOWN HIT";
   }
   else if(!IsInSession()) status = "OUT OF SESSION";

   string dash = StringConcatenate(
      "=== PRO RISK CONTAINER (MT4) ===\n",
      "Daily Start Equity: $", DoubleToString(g_startOfDayEquity, 2), "\n",
      "Daily Peak Equity: $", DoubleToString(g_dailyPeakEquity, 2), "\n",
      "Current PnL: ", DoubleToString(dailyPnLPct, 2), "%\n",
      "Drawdown from Peak: ", DoubleToString(ddFromPeakPct, 2), "% (Max: ", DoubleToString(InpMaxDailyDrawdown, 2), "%)\n",
      "System Status: ", status, "\n",
      "Spread: ", IntegerToString((int)MarketInfo(Symbol(), MODE_SPREAD)), " pts"
   );

   Comment(dash);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
LondonScalper
Posts: 770
Joined: Sat Sep 05, 2026 7:54 am

Re: The Scalper’s Paradox: Why We Win the 1-Minute Battles but Lose the War (and our accounts)

Post by LondonScalper »

Scheduled, and the date is in the diary before the quarter starts. Waiting until it felt off meant my reviews happened on the worst days and redesigned everything at once.

The container code is tidy. Two things I'd look at before trusting it. The goal-reached switch treats the daily target as a ceiling, and I don't cap winning days; I cap what I risk after a big win by halving size, which keeps the good days open without handing profit back to boredom. And strategy.risk.max_intraday_loss only governs the strategy's own trades, so in Pine it's a backtest discipline rather than a live one unless something enforces it at the broker.

The cBot is closer to what I'd run, since it watches equity rather than balance. Just make sure the daily reset follows the broker's rollover and not midnight UTC, or the first hour after rollover gets booked to the wrong day.
Post Reply