Page 1 of 3
Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 8:29 pm
by LondonScalper
Tilt script after a slipped stop -- written when calm, used when not.
Slipped stops feel personal. My old pattern: immediate re-entry, wider stop, a quiet lecture to the platform. That is how a one-R event becomes three.
Recovery script (sticky)
1. Flatten any impulse add-on.
2. Five-minute timer -- stand up, leave the mouse.
3. Tag slip_tilt and screenshot the fill.
4. Next ticket only if the checklist is still true; size down once.
5. If I break the script -- day throttle or done.
The script is not profound. It is short enough to follow when I am angry. Long essays do not get read in tilt. Practising the stand-up on small annoyances makes it available on real ones.
What is on your post-slip checklist, if you have one?
I review tilt scripts monthly the same way I review hotkeys. If step two is unrealistic (cannot leave the room), rewrite it. A script you cannot execute is decoration. Pair it with a hard size step-down so even a partial follow-through still reduces damage.
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:31 pm
by FTtrader
Great post. Slipped stops are the ultimate test of an emotional baseline. A normal stop-out is just a business expense; a slipped stop feels like the market reaching into your pocket. Your point about rewriting the script if you can't execute it is spot on. A trading plan that requires robotic perfection from a frustrated human is doomed to fail.
Here is my post-slip checklist, focused heavily on raw price action and resetting the structural narrative:
The Physical Reset: Like yours, hands off the mouse. If I can’t leave the room, I switch the monitor input or minimize the platform for a strict 5 minutes.
The "Sweep vs. Shift" Check: Once calm, I zoom out to the 15-minute and Daily charts. Was the slippage the result of a violent liquidity sweep, or did market structure actually shift against me? If the higher timeframe narrative is broken, the bias is dead. No re-entry.
Execution Audit: I log the exact slippage in pips/ticks. If the broker is consistently slipping me during standard, non-news conditions, that is a structural business issue, not a market one.
Risk Throttle: Next setup must be half-size. Earning back the right to trade full size requires executing a clean, emotionless trade first, regardless of the outcome.
Hard Daily Stop: If I feel the urge to "revenge" the slip rather than trade the structure, the platform gets closed for the day.
Decorating a monitor with rules doesn't work if the rules don't respect human nature. Practicing the stand-up on the small cuts is exactly how you survive the deep ones. Thanks for sharing.
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:32 pm
by FTtrader
Pine Script: Tilt Recovery Throttle
Since discretionary traders can't have a script physically block them from clicking "Buy" on a manual broker, the next best thing is visual enforcement.
This Pine Script acts as a visual throttle. When your strategy logs a losing trade (or if you use it alongside your manual execution tracking), it triggers a mandatory cooldown timer. During this time, it blocks automated strategy entries and paints the chart background red, serving as a glaring visual reminder to step away.
Code: Select all
//@version=5
strategy("Tilt Recovery Throttle", overlay=true, initial_capital=1000)
// --- User Inputs ---
cooldownPeriod = input.int(5, title="Cooldown Period (Minutes)", minval=1, tooltip="Time to block new entries after a loss")
enableVisuals = input.bool(true, title="Paint Background During Cooldown")
// --- State Variables ---
var int lastLossTime = na
var int currentLosses = strategy.losstrades
// --- Detect New Losses ---
// If the number of losing trades increases, record the exact time of the loss
if strategy.losstrades > currentLosses
lastLossTime := time
currentLosses := strategy.losstrades
// --- Cooldown Logic ---
// Convert minutes to milliseconds and check if current bar time is within the cooldown window
inCooldown = not na(lastLossTime) and (time < lastLossTime + (cooldownPeriod * 60 * 1000))
// --- Visual Feedback ---
// Paint the background red to visually enforce the "hands off the mouse" rule
bgcolor(inCooldown and enableVisuals ? color.new(color.red, 90) : na, title="Tilt Cooldown Active")
// --- Example Trading Logic (For Demonstration) ---
// Replace this with your actual price action / structural conditions
longCondition = ta.crossover(ta.sma(close, 9), ta.sma(close, 21))
shortCondition = ta.crossunder(ta.sma(close, 9), ta.sma(close, 21))
// --- Entry Execution ---
// Entries are STRICTLY blocked if inCooldown is true
if longCondition and not inCooldown
strategy.entry("Long", strategy.long)
if shortCondition and not inCooldown
strategy.entry("Short", strategy.short)
// Example Stop Loss / Take Profit to generate trades
strategy.exit("Exit Long", from_entry="Long", loss=150, profit=300)
strategy.exit("Exit Short", from_entry="Short", loss=150, profit=300)
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:32 pm
by FTtrader
How to use this in your workflow:
The Visual Anchor: Even if you trade manually, you can link the lastLossTime trigger to an alert or a manual toggle. When the background turns red, it acts as your "5-minute timer" step. You do not touch the mouse until the chart returns to its normal color.
Customization: If you only want the throttle to trigger after a slipped stop (e.g., a loss greater than a specific monetary or pip threshold), you can modify the detection logic to check strategy.closedtrades.profit(strategy.closedtrades - 1) < -maxAcceptableLoss.
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:33 pm
by FTtrader
If your script requires
willpower, it will fail during tilt. Offload the discipline to the system.
What metric are you using to determine if a stop was legitimately "slipped" versus just poorly placed in a high-volatility zone?
Pine Script v5: Dynamic Slippage Circuit Breaker
To make this professional, standard timers are insufficient. A pro-level script should mathematically detect the difference between a normal stop-out and a slipped stop-out, triggering the lockout specifically when execution quality fails.
This script calculates your expected risk versus your actual fill. If the negative slippage exceeds your defined tolerance, it triggers a visual circuit breaker and halts strategy execution, outputting the diagnostic data to a clean dashboard.
Code: Select all
//@version=5
strategy("Slippage Circuit Breaker Framework", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=2)
// --- Professional Execution Inputs ---
grp_risk = "Execution & Risk Parameters"
slipToleranceTicks = input.int(10, "Slippage Tolerance (Ticks)", group=grp_risk, tooltip="Max acceptable slippage before triggering lockout")
lockoutMinutes = input.int(15, "Lockout Duration (Minutes)", group=grp_risk)
showDashboard = input.bool(true, "Show Execution Dashboard", group=grp_risk)
// --- State Variables ---
var float expectedStopPrice = na
var int lockoutEndTime = na
var int totalSlippedStops = 0
var float lastSlipAmount = 0.0
// --- Circuit Breaker Logic ---
bool inLockout = not na(lockoutEndTime) and time < lockoutEndTime
if inLockout[1] and not inLockout
// Reset expected stop once lockout clears to avoid state leakage
expectedStopPrice := na
// --- Slippage Detection Engine ---
// Triggered on the bar immediately following a closed trade
if strategy.closedtrades > nz(strategy.closedtrades[1])
float actualExitPrice = strategy.closedtrades.exit_price(strategy.closedtrades - 1)
float tradeDirection = strategy.closedtrades.size(strategy.closedtrades - 1)
float entryPrice = strategy.closedtrades.entry_price(strategy.closedtrades - 1)
// Calculate if it was a losing trade
bool wasLoss = (tradeDirection > 0 and actualExitPrice < entryPrice) or (tradeDirection < 0 and actualExitPrice > entryPrice)
if wasLoss and not na(expectedStopPrice)
// Calculate slippage in ticks
float slipDistance = math.abs(expectedStopPrice - actualExitPrice)
float slipTicks = slipDistance / syminfo.mintick
// If slippage exceeds tolerance, engage the circuit breaker
if slipTicks > slipToleranceTicks
lockoutEndTime := time + (lockoutMinutes * 60 * 1000)
totalSlippedStops += 1
lastSlipAmount := slipTicks
// Log the execution failure for auditing
log.warning("CIRCUIT BREAKER TRIGGERED: Slipped {0} ticks. Expected: {1}, Filled: {2}", str.tostring(slipTicks), str.tostring(expectedStopPrice), str.tostring(actualExitPrice))
// --- Visual & Structural Warning ---
// Paints the chart background a dark crimson during the lockout phase
bgcolor(inLockout ? color.new(#8b0000, 85) : na, title="Circuit Breaker Active")
// --- Price Action Execution (Example Framework) ---
// Using raw price action structure (e.g., simplistic engulfing for structural shifts)
bullishEngulfing = close > open[1] and close[1] < open[1]
bearishEngulfing = close < open[1] and close[1] > open[1]
// Execution logic strictly blocked by the circuit breaker state
if bullishEngulfing and not inLockout and strategy.position_size == 0
strategy.entry("Long", strategy.long)
expectedStopPrice := low - (5 * syminfo.mintick) // Setting structural expectation
strategy.exit("Exit Long", from_entry="Long", stop=expectedStopPrice, limit=close + (close - expectedStopPrice) * 2)
if bearishEngulfing and not inLockout and strategy.position_size == 0
strategy.entry("Short", strategy.short)
expectedStopPrice := high + (5 * syminfo.mintick) // Setting structural expectation
strategy.exit("Exit Short", from_entry="Short", stop=expectedStopPrice, limit=close - (expectedStopPrice - close) * 2)
// --- Execution Dashboard ---
var table diagTable = table.new(position.top_right, 2, 4, border_width=1, border_color=color.gray, frame_color=color.gray, frame_width=1)
if showDashboard and barstate.islast
table.cell(diagTable, 0, 0, "System Status", text_color=color.white, bgcolor=color.black)
table.cell(diagTable, 1, 0, inLockout ? "LOCKED" : "ACTIVE", text_color=inLockout ? color.red : color.green, bgcolor=color.black)
table.cell(diagTable, 0, 1, "Slippage Events", text_color=color.white, bgcolor=color.black)
table.cell(diagTable, 1, 1, str.tostring(totalSlippedStops), text_color=color.white, bgcolor=color.black)
table.cell(diagTable, 0, 2, "Last Slip (Ticks)", text_color=color.white, bgcolor=color.black)
table.cell(diagTable, 1, 2, str.tostring(math.round(lastSlipAmount, 1)), text_color=color.white, bgcolor=color.black)
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:35 pm
by FTtrader
Here is an ultra-professional, systems-architecture approach followed by a quantitative Pine Script module.
This version strips out retail trading psychology and replaces it with institutional execution logic, state management, and statistical variance. It also aligns perfectly with a raw price-action methodology on higher timeframes.
Here is my post-slip protocol, built around state management and structural validation:
Algorithmic Circuit Breaker (State: LOCKED): The stand-up rule is good, but a hard-coded lockout is better. The moment negative slippage exceeds my statistical baseline, my execution logic hard-locks for 15 minutes. No manual bypass. The UI turns red, and order routing is severed.
Structural Validation (15m & Daily): I trade raw price action. During the lockout, I isolate the 15-minute and Daily structure. I need to determine: was the slip a targeted liquidity sweep hunting a local extreme, or a fundamental shift in market structure? If the higher-timeframe structure is broken, the bias is dead. The trade is over.
Execution Audit (Latency vs. Liquidity): I analyze the tick data and spread at the exact millisecond of the fill. Symmetrical slippage during news is normal. Asymmetrical slippage (heavy slip on stops, no positive slip on limits) during standard liquidity is a toxic broker profile. If it's a routing issue, I don't adjust my trading; I change my broker.
Programmatic Risk Throttle (State: RECOVERY): When the system unlocks, the next execution is programmatically capped at 50% risk. I do not get access to full leverage again until a structurally sound, emotionless setup is executed and closed.
Session Termination: If there is any physiological urge to aggressively fade the sweep just to "reclaim" the lost capital, the session is manually terminated.
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:35 pm
by FTtrader
A script you cannot execute is just decoration. Offload the discipline to the system so you don't have to rely on your emotions to enforce your rules.
Pine Script v5: Quantitative Slippage & State Management Module
To make this truly professional, we move away from hardcoded tick tolerances. This script acts as a state machine. It uses arrays to track your rolling slippage history, calculating the mean and standard deviation. If a slipped stop falls outside of the normal statistical variance (e.g., > 2 Standard Deviations), it triggers an algorithmic lockdown.
It includes no lagging indicators, leaving the entry logic completely open for your raw price action setups.
Code: Select all
//@version=5
strategy("Institutional Execution State Machine", overlay=true, initial_capital=10000, margin_long=100, margin_short=100, calc_on_every_tick=true)
// --- System Parameters ---
grp_risk = "Quantitative Risk & State Parameters"
lockoutMinutes = input.int(15, "Lockout Duration (Minutes)", group=grp_risk)
stdDevLimit = input.float(2.0, "Slippage Z-Score Limit", tooltip="Trigger lockout if slip exceeds this many standard deviations from the mean", group=grp_risk)
minSlipTicks = input.int(5, "Minimum Slip to Track (Ticks)", group=grp_risk)
// --- State Machine ---
var int STATE_ACTIVE = 1
var int STATE_LOCKED = 2
var int STATE_RECOVERY = 3
var int currentState = STATE_ACTIVE
var int lockoutEndTime = na
var float expectedStop = na
// --- Statistical Arrays (Tracking Execution Quality) ---
var float[] slipHistory = array.new_float(0)
var int maxArraySize = 50
// --- Circuit Breaker & State Logic ---
if currentState == STATE_LOCKED
if time >= lockoutEndTime
currentState := STATE_RECOVERY
expectedStop := na
// --- Execution & Slippage Engine ---
// Triggered exactly once per closed trade to audit the fill
if strategy.closedtrades > nz(strategy.closedtrades[1])
float actualExitPrice = strategy.closedtrades.exit_price(strategy.closedtrades - 1)
float tradeDir = strategy.closedtrades.size(strategy.closedtrades - 1)
float entryPrice = strategy.closedtrades.entry_price(strategy.closedtrades - 1)
bool isLoss = (tradeDir > 0 and actualExitPrice < entryPrice) or (tradeDir < 0 and actualExitPrice > entryPrice)
if isLoss and not na(expectedStop)
float slipDistance = math.abs(expectedStop - actualExitPrice)
float slipTicks = slipDistance / syminfo.mintick
if slipTicks > minSlipTicks
// Add to statistical model
array.push(slipHistory, slipTicks)
if array.size(slipHistory) > maxArraySize
array.shift(slipHistory)
// Calculate Variance (Z-Score)
float meanSlip = array.avg(slipHistory)
float stdDev = array.stdev(slipHistory)
float zScore = stdDev > 0 ? (slipTicks - meanSlip) / stdDev : 0
// Hard Lockout Trigger
if zScore > stdDevLimit or array.size(slipHistory) < 5 // Failsafe for early data
currentState := STATE_LOCKED
lockoutEndTime := time + (lockoutMinutes * 60 * 1000)
log.error("SYSTEM LOCKED: Anomalous Slippage Detected. Slip: {0} ticks, Z-Score: {1}", str.tostring(slipTicks), str.tostring(math.round(zScore, 2)))
// --- Chart UI & State Visualization ---
color stateColor = currentState == STATE_ACTIVE ? color.new(color.gray, 100) :
currentState == STATE_LOCKED ? color.new(#8b0000, 85) :
color.new(#b8860b, 90) // Golden tint for Recovery state
bgcolor(stateColor, title="Execution State")
// --- Structural Dashboard ---
var table sysTable = table.new(position.bottom_right, 2, 4, border_width=1, border_color=color.new(color.gray, 50), frame_color=color.new(color.gray, 50))
if barstate.islast
table.cell(sysTable, 0, 0, "SYSTEM STATE", text_color=color.gray, text_size=size.small, bgcolor=color.black)
string stateTxt = currentState == STATE_ACTIVE ? "ACTIVE (FULL RISK)" : currentState == STATE_LOCKED ? "LOCKED (NO EXECUTION)" : "RECOVERY (HALF RISK)"
color stateTxtColor = currentState == STATE_ACTIVE ? color.teal : currentState == STATE_LOCKED ? color.red : color.orange
table.cell(sysTable, 1, 0, stateTxt, text_color=stateTxtColor, text_size=size.small, bgcolor=color.black)
float currentMean = array.size(slipHistory) > 0 ? array.avg(slipHistory) : 0
table.cell(sysTable, 0, 1, "Avg Slip (Ticks)", text_color=color.gray, text_size=size.small, bgcolor=color.black)
table.cell(sysTable, 1, 1, str.tostring(math.round(currentMean, 1)), text_color=color.white, text_size=size.small, bgcolor=color.black)
// --- Price Action Execution Framework (Placeholder) ---
// *Insert your 15m / Daily raw price action or liquidity sweep logic here*
bool validLongSetup = ta.crossover(close, open) // Replace with actual PA trigger
bool validShortSetup = ta.crossunder(close, open) // Replace with actual PA trigger
// Position Sizing based on State Machine
float riskMultiplier = currentState == STATE_RECOVERY ? 0.5 : 1.0
if validLongSetup and currentState != STATE_LOCKED and strategy.position_size == 0
strategy.entry("PA_Long", strategy.long, qty=strategy.equity * 0.01 * riskMultiplier / close)
expectedStop := low - (2 * syminfo.mintick)
strategy.exit("Exit_L", from_entry="PA_Long", stop=expectedStop)
// Successful execution in Recovery resets state to Active
if currentState == STATE_RECOVERY
currentState := STATE_ACTIVE
if validShortSetup and currentState != STATE_LOCKED and strategy.position_size == 0
strategy.entry("PA_Short", strategy.short, qty=strategy.equity * 0.01 * riskMultiplier / close)
expectedStop := high + (2 * syminfo.mintick)
strategy.exit("Exit_S", from_entry="PA_Short", stop=expectedStop)
if currentState == STATE_RECOVERY
currentState := STATE_ACTIVE
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:36 pm
by FTtrader
Moving this logic from Pine Script to MetaQuotes environments (MQL4/MQL5) is where it transitions from theoretical analysis to a production-grade execution engine. In MetaTrader, we don't need to guess the expected stop loss; we can pull the exact server-side OrderStopLoss() and compare it directly to the execution price provided by the broker's liquidity provider.
Below are the architectural frameworks for both MQL5 and MQL4. They are structured as Expert Advisor (EA) modules built around a state machine. You can plug your raw price action logic directly into the execution blocks.
MQL5: Institutional Execution Architecture
MQL5 is event-driven. We can use the OnTradeTransaction() or poll HistorySelect() in OnTick() to audit fills. For reliability across different broker data feeds, polling the last closed deal is highly robust.
Code: Select all
//+------------------------------------------------------------------+
//| Institutional_CircuitBreaker.mq5|
//| Quantitative Slippage State Machine |
//+------------------------------------------------------------------+
#property copyright "Execution Architecture"
#property version "1.00"
#include <Math\Stat\Math.mqh>
//--- Enums for State Machine
enum ENUM_SYS_STATE {
STATE_ACTIVE, // Full Risk
STATE_LOCKED, // Hard Lockout
STATE_RECOVERY // Reduced Risk
};
//--- Inputs
input int LockoutMinutes = 15; // Lockout Duration (Minutes)
input double StdDevLimit = 2.0; // Slippage Z-Score Limit
input int MinSlipPoints = 10; // Minimum Slip to Track (Points)
input int MaxHistorySize = 50; // Statistical Array Size
input ulong MagicNumber = 12345; // EA Magic Number
//--- Global State Variables
ENUM_SYS_STATE currentState = STATE_ACTIVE;
datetime lockoutEndTime = 0;
ulong lastProcessedDeal = 0;
//--- Statistical Arrays
double slipHistory[];
int slipCount = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
ArrayResize(slipHistory, MaxHistorySize);
ArrayInitialize(slipHistory, 0.0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| State Management & Execution Audit |
//+------------------------------------------------------------------+
void OnTick() {
// 1. Manage Time-Based State Transitions
if (currentState == STATE_LOCKED && TimeCurrent() >= lockoutEndTime) {
currentState = STATE_RECOVERY;
Print("SYSTEM UNLOCKED: Entering Recovery State (Half Risk).");
}
// 2. Audit Execution History for Slippage
AuditLastExecution();
// 3. Update UI Dashboard
UpdateDashboard();
// 4. Execution Framework (Insert PA Logic Here)
if (currentState != STATE_LOCKED) {
// bool validLongSetup = ... (e.g. 15m engulfing, liquidity sweep)
// bool validShortSetup = ...
// double riskMultiplier = (currentState == STATE_RECOVERY) ? 0.5 : 1.0;
// if(validLongSetup) { ExecuteOrder(ORDER_TYPE_BUY, riskMultiplier); }
// if(successful_trade_closed && currentState == STATE_RECOVERY) { currentState = STATE_ACTIVE; }
}
}
//+------------------------------------------------------------------+
//| Audit the last closed deal for anomalous slippage |
//+------------------------------------------------------------------+
void AuditLastExecution() {
HistorySelect(0, TimeCurrent());
int totalDeals = HistoryDealsTotal();
if (totalDeals == 0) return;
ulong dealTicket = HistoryDealGetTicket(totalDeals - 1);
// Process only new exits
if (dealTicket != lastProcessedDeal && HistoryDealGetInteger(dealTicket, DEAL_ENTRY) == DEAL_ENTRY_OUT) {
lastProcessedDeal = dealTicket;
if (HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != MagicNumber) return;
double exitPrice = HistoryDealGetDouble(dealTicket, DEAL_PRICE);
double stopLoss = HistoryDealGetDouble(dealTicket, DEAL_SL); // Gets SL at time of execution
// If SL was hit (or exit was extremely close to SL)
if (stopLoss > 0 && MathAbs(exitPrice - stopLoss) > 0) {
double slipDistance = MathAbs(exitPrice - stopLoss);
double slipPoints = slipDistance / _Point;
if (slipPoints > MinSlipPoints) {
UpdateStatisticalModel(slipPoints);
}
}
}
}
//+------------------------------------------------------------------+
//| Update Rolling Array and Calculate Variance |
//+------------------------------------------------------------------+
void UpdateStatisticalModel(double newSlip) {
// FIFO Shift
if (slipCount >= MaxHistorySize) {
for (int i = 0; i < MaxHistorySize - 1; i++) {
slipHistory[i] = slipHistory[i + 1];
}
slipCount = MaxHistorySize - 1;
}
slipHistory[slipCount] = newSlip;
slipCount++;
if (slipCount > 5) { // Need baseline data
double mean, variance, stdDev;
mean = MathMean(slipHistory);
stdDev = MathStandardDeviation(slipHistory);
double zScore = (stdDev > 0) ? (newSlip - mean) / stdDev : 0.0;
if (zScore > StdDevLimit) {
currentState = STATE_LOCKED;
lockoutEndTime = TimeCurrent() + (LockoutMinutes * 60);
PrintFormat("CIRCUIT BREAKER: Slipped %.1f points. Z-Score: %.2f. System Locked.", newSlip, zScore);
}
}
}
//+------------------------------------------------------------------+
//| Simple Chart UI |
//+------------------------------------------------------------------+
void UpdateDashboard() {
string stateTxt = (currentState == STATE_ACTIVE) ? "ACTIVE (FULL RISK)" :
(currentState == STATE_LOCKED) ? "LOCKED (NO EXECUTION)" : "RECOVERY (HALF RISK)";
Comment("--- EXECUTION ARCHITECTURE ---\n",
"State: ", stateTxt, "\n",
"Data Points: ", slipCount, "\n",
"Time to Unlock: ", (currentState == STATE_LOCKED) ? IntegerToString((lockoutEndTime - TimeCurrent())/60) + " min" : "N/A");
}
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:37 pm
by FTtrader
MQL4: Legacy EA Adaptation
MQL4 handles historical orders differently (pulling from OrderSelect in MODE_HISTORY). It also lacks the native MathStat libraries, so the statistical functions are built out manually to keep the module fully self-contained.
Code: Select all
//+------------------------------------------------------------------+
//| Institutional_CircuitBreaker.mq4|
//| Quantitative Slippage State Machine |
//+------------------------------------------------------------------+
#property copyright "Execution Architecture"
#property version "1.00"
#property strict
//--- Enums for State Machine
enum ENUM_SYS_STATE {
STATE_ACTIVE, // Full Risk
STATE_LOCKED, // Hard Lockout
STATE_RECOVERY // Reduced Risk
};
//--- Inputs
input int LockoutMinutes = 15;
input double StdDevLimit = 2.0;
input int MinSlipPoints = 10;
input int MaxHistorySize = 50;
input int MagicNumber = 12345;
//--- Global State Variables
ENUM_SYS_STATE currentState = STATE_ACTIVE;
datetime lockoutEndTime = 0;
int lastProcessedTicket = 0;
//--- Statistical Arrays
double slipHistory[];
int slipCount = 0;
//+------------------------------------------------------------------+
int OnInit() {
ArrayResize(slipHistory, MaxHistorySize);
ArrayInitialize(slipHistory, 0.0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnTick() {
// 1. Manage State
if (currentState == STATE_LOCKED && TimeCurrent() >= lockoutEndTime) {
currentState = STATE_RECOVERY;
Print("SYSTEM UNLOCKED: Entering Recovery State.");
}
// 2. Audit History
AuditLastExecution();
// 3. UI
string stateTxt = (currentState == STATE_ACTIVE) ? "ACTIVE" : (currentState == STATE_LOCKED) ? "LOCKED" : "RECOVERY";
Comment("System State: ", stateTxt, "\nSlip Count: ", slipCount);
// 4. Execution logic goes here (blocked if currentState == STATE_LOCKED)
}
//+------------------------------------------------------------------+
void AuditLastExecution() {
int totalHistory = OrdersHistoryTotal();
if (totalHistory == 0) return;
// Grab the most recently closed order
if (OrderSelect(totalHistory - 1, SELECT_BY_POS, MODE_HISTORY)) {
int ticket = OrderTicket();
if (ticket != lastProcessedTicket && OrderMagicNumber() == MagicNumber) {
lastProcessedTicket = ticket;
double exitPrice = OrderClosePrice();
double stopLoss = OrderStopLoss();
// Check if closed at a loss, near the Stop Loss
if (stopLoss > 0 && OrderProfit() < 0) {
double slipDistance = MathAbs(exitPrice - stopLoss);
double slipPoints = slipDistance / Point;
// Exclude manual closures by ensuring the exit price crossed the SL threshold
bool wasStoppedOut = (OrderType() == OP_BUY && exitPrice <= stopLoss) ||
(OrderType() == OP_SELL && exitPrice >= stopLoss);
if (wasStoppedOut && slipPoints > MinSlipPoints) {
UpdateStatisticalModel(slipPoints);
}
}
}
}
}
//+------------------------------------------------------------------+
void UpdateStatisticalModel(double newSlip) {
if (slipCount >= MaxHistorySize) {
for (int i = 0; i < MaxHistorySize - 1; i++) {
slipHistory[i] = slipHistory[i + 1];
}
slipCount = MaxHistorySize - 1;
}
slipHistory[slipCount] = newSlip;
slipCount++;
if (slipCount > 5) {
double mean = GetMean(slipHistory, slipCount);
double stdDev = GetStdDev(slipHistory, slipCount, mean);
double zScore = (stdDev > 0) ? (newSlip - mean) / stdDev : 0.0;
if (zScore > StdDevLimit) {
currentState = STATE_LOCKED;
lockoutEndTime = TimeCurrent() + (LockoutMinutes * 60);
PrintFormat("CIRCUIT BREAKER: Slipped %.1f pts. Z-Score: %.2f. System Locked.", newSlip, zScore);
}
}
}
//+------------------------------------------------------------------+
//| Math Helpers for MQL4 |
//+------------------------------------------------------------------+
double GetMean(double &arr[], int size) {
double sum = 0;
for(int i = 0; i < size; i++) sum += arr[i];
return (size > 0) ? sum / size : 0.0;
}
double GetStdDev(double &arr[], int size, double mean) {
double sum_sq = 0;
for(int i = 0; i < size; i++) sum_sq += MathPow(arr[i] - mean, 2);
return (size > 1) ? MathSqrt(sum_sq / size) : 0.0;
}
Re: Tilt after a slipped stop: recovery script
Posted: Mon Sep 14, 2026 9:37 pm
by FTtrader
Integration Workflow
To fuse this with your custom setups, simply drop your signal logic into OnTick(). Wrap your OrderSend() (MQL4) or OrderSendAsync() (MQL5) functions in a strict conditional check: if(currentState != STATE_LOCKED).
For the state reset (moving from RECOVERY back to ACTIVE), you would add a check inside AuditLastExecution(): if the most recently closed trade was profitable, and the system is currently in STATE_RECOVERY, reassign it back to STATE_ACTIVE to restore full leverage.