Page 1 of 2
A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:26 pm
by LondonScalper
Flat-before-news discipline -- practical version, not a purity contest.
I flat (or never open) ahead of tier-1 releases that move my pairs. The edge I trade is not the first spike; it is post-chaos structure when spreads behave again. Sitting through the print "because I am already in" has cost more than the missed continuation ever paid.
How I run it
1. Blackout window written the night before (e.g. T-10 to T+10, longer for FOMC).
2. Working orders cancelled before the window -- not hoped away.
3. Platform alerts for calendar, plus a sticky so I do not rely on memory.
4. Re-entry only after spread and tape look normal and a fresh checklist passes. The release itself is not a signal.
Medium events are case-by-case by pair. Tier-1 is not. If I break the blackout, it is a process strike even if the ticket wins.
Where do you draw the line between flat-always and "I can scalp around it"?
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:48 pm
by FTtrader
LondonScalper wrote: Mon Sep 14, 2026 9:26 pm
Flat-before-news discipline -- practical version, not a purity contest.
I flat (or never open) ahead of tier-1 releases that move my pairs. The edge I trade is not the first spike; it is post-chaos structure when spreads behave again. Sitting through the print "because I am already in" has cost more than the missed continuation ever paid.
How I run it
1. Blackout window written the night before (e.g. T-10 to T+10, longer for FOMC).
2. Working orders cancelled before the window -- not hoped away.
3. Platform alerts for calendar, plus a sticky so I do not rely on memory.
4. Re-entry only after spread and tape look normal
and a fresh checklist passes. The release itself is not a signal.
Medium events are case-by-case by pair. Tier-1 is not. If I break the blackout, it is a process strike even if the ticket wins.
Where do you draw the line between flat-always and "I can scalp around it"?
Hello LondonScalper, traders, scalpers.
The distinction between a "purity contest" and practical survival is exactly right.
Tier-1 news destroys market microstructure. The moment those prints hit (CPI, NFP, FOMC), the algorithms pull their liquidity, the order book thins out to nothing, and the resulting spread widening practically guarantees devastating slippage. Sitting through that isn't trading; it's flipping a coin and paying a premium for the privilege.
Especially when scalping the 1-minute or 5-minute charts, the real edge isn't in guessing the direction of the initial spike. It's waiting for those inevitable liquidity sweeps to clear out the early entrants and stop-losses, letting the spread normalize, and then stepping in to trade the actual post-news market structure.
To answer your question about where to draw the line: The line is drawn at liquidity and structural risk.
If an event has a historical track record of gapping the spread and clearing the book, it is a hard flat—no exceptions. For medium-tier events, I can scalp around them only if the price action setup is exceptionally clean and my structural invalidation point (stop loss) is placed safely outside the anticipated volatility noise. If those conditions aren't met, I treat it like Tier-1.
And I completely agree on the process strike. A winning trade built on a broken blackout rule is just positive reinforcement for a habit that will eventually blow up the account. Discipline pays more than the lucky spike ever will.
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:48 pm
by FTtrader
Pine Script: Tier-1 News Blackout Enforcer
This Pine Script translates your manual checklist into a visual and automated chart tool. It handles Point 1 (setting the window), Point 3 (platform alerts), and visually reminds you of Point 4 (waiting for post-chaos structure).
Code: Select all
//@version=5
indicator("News Blackout Enforcer", overlay=true)
// =========================================================================
// INPUTS: Define your blackout window the night before
// =========================================================================
grp_time = "Blackout Window Settings"
// Format: HH:MM-HH:MM (Exchange Timezone)
// Example: 14:20-14:40 for a 14:30 news release (T-10 to T+10)
blackout_session = input.session("14:20-14:40", title="Blackout Window", group=grp_time)
grp_visual = "Visuals & Alerts"
alert_msg_start = input.string("CANCEL WORKING ORDERS - Entering News Blackout", title="Start Alert Message", group=grp_visual)
alert_msg_end = input.string("News passed. Wait for spread/tape to normalize.", title="End Alert Message", group=grp_visual)
box_color = input.color(color.new(color.red, 85), title="Blackout Zone Color", group=grp_visual)
// =========================================================================
// LOGIC & CALCULATIONS
// =========================================================================
// Check if the current bar falls inside the defined blackout session
in_blackout = time(timeframe.period, blackout_session) != 0
// Detect the exact bar where the blackout starts and ends
blackout_start = in_blackout and not in_blackout[1]
blackout_end = not in_blackout and in_blackout[1]
// =========================================================================
// VISUALS
// =========================================================================
// Paint the chart background red during the blackout window so you don't rely on memory
bgcolor(in_blackout ? box_color : na, title="Blackout Background")
// Optional: Plot a subtle visual marker when the zone ends to signal "check spreads"
plotshape(blackout_end, style=shape.flag, location=location.belowbar, color=color.new(color.blue, 30), size=size.small, title="Structure Watch", text="WATCH\nSPREAD")
// =========================================================================
// ALERTS
// =========================================================================
// Fire platform alerts to keep you disciplined
if blackout_start
alert(alert_msg_start, alert.freq_once_per_bar)
if blackout_end
alert(alert_msg_end, alert.freq_once_per_bar)
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:48 pm
by FTtrader
How to use it in your workflow:
The Night Before: Drop the indicator on your chart and update the Blackout Window input with your T-10 to T+10 timeframe (e.g., 14:20-14:40 for a 14:30 EST release). Ensure the time aligns with your broker/chart timezone.
The Warning: When the chart enters the red zone, the script fires the CANCEL WORKING ORDERS alert to your platform/phone.
The Re-entry: When the red background disappears, a small blue flag prints below the bar, and you receive the second alert reminding you to verify the spread and tape before hunting for the next setup.
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:50 pm
by FTtrader
Pro Pine Script: Advanced News Blackout Enforcer
This version is built with cleaner architecture. It supports up to three separate news events per day, uses precise background states, and includes a "Volatility Normalization" visual to remind you to wait for the post-chaos structure to settle before hunting for a setup.
Code: Select all
//@version=5
indicator("Advanced News Blackout Enforcer", overlay=true, max_labels_count=50, max_boxes_count=50)
// =========================================================================
// UI & INPUTS
// =========================================================================
var G_T1 = "Event 1 (e.g., London Open / Early News)"
e1_active = input.bool(true, title="Enable Event 1", group=G_T1, inline="E1")
e1_time = input.session("14:20-14:40", title="Window", group=G_T1, inline="E1", tooltip="Format: HH:MM-HH:MM (Chart Timezone)")
var G_T2 = "Event 2 (e.g., NY Open / Tier-1 News)"
e2_active = input.bool(false, title="Enable Event 2", group=G_T2, inline="E2")
e2_time = input.session("19:50-20:20", title="Window", group=G_T2, inline="E2")
var G_T3 = "Event 3 (e.g., Late FOMC)"
e3_active = input.bool(false, title="Enable Event 3", group=G_T3, inline="E3")
e3_time = input.session("20:00-20:30", title="Window", group=G_T3, inline="E3")
var G_VIS = "Aesthetics & Alerts"
color_blackout = input.color(color.new(color.red, 85), title="Blackout Zone Color", group=G_VIS)
color_recovery = input.color(color.new(color.gray, 90), title="Recovery Zone (Post-News)", group=G_VIS)
alert_msg = input.string("BLACKOUT ENTRY: Cancel Working Orders", title="Alert Message", group=G_VIS)
// =========================================================================
// TIME STATE MACHINE
// =========================================================================
// Helper function to evaluate if current bar is within a specific session
is_in_session(sess) => time(timeframe.period, sess) != 0
// Determine active blackout states
in_e1 = e1_active and is_in_session(e1_time)
in_e2 = e2_active and is_in_session(e2_time)
in_e3 = e3_active and is_in_session(e3_time)
// Master blackout flag
bool is_blackout = in_e1 or in_e2 or in_e3
// Detect state changes for alerts and labels
bool blackout_start = is_blackout and not is_blackout[1]
bool blackout_end = not is_blackout and is_blackout[1]
// =========================================================================
// VOLATILITY NORMALIZATION (POST-NEWS TAPE CHECK)
// =========================================================================
// Keeps a subtle gray background for exactly 3 bars after the blackout ends
// as a visual reminder to verify spread/tape normalization before entering.
var int recovery_counter = 0
if blackout_end
recovery_counter := 3
else if recovery_counter > 0
recovery_counter -= 1
bool is_recovery = recovery_counter > 0
// =========================================================================
// RENDER
// =========================================================================
// Background painting for structural zones
bgcolor(is_blackout ? color_blackout : na, title="Blackout Zone Active")
bgcolor(is_recovery ? color_recovery : na, title="Spread Recovery Watch")
// Visual flags at boundaries
if blackout_start
label.new(bar_index, high, text="⚠️ BLACKOUT", style=label.style_label_down, color=color.new(color.red, 20), textcolor=color.white, size=size.small)
if blackout_end
label.new(bar_index, low, text="WATCH SPREAD", style=label.style_label_up, color=color.new(color.gray, 20), textcolor=color.white, size=size.small)
// =========================================================================
// ALERTS
// =========================================================================
if blackout_start
alert(alert_msg, alert.freq_once_per_bar)
if blackout_end
alert("Blackout cleared. Verify DOM liquidity and spread before executing.", alert.freq_once_per_bar)
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:50 pm
by FTtrader
Key Upgrades in the Code:
Multi-Event Handling: You can now program up to three separate events for the session (e.g., early CPI, NY Open, and afternoon FOMC) without having to swap out indicators.
State Machine Logic: The code properly handles state transitions (blackout_start / blackout_end), ensuring alerts only fire exactly when the boundaries are crossed.
Post-News Recovery Visualizer: Added a custom integer counter that shades the immediate 3 bars after the blackout ends in a subtle gray. This acts as a mechanical reminder for Point #4 in your checklist—forcing you to wait for the tape and spread to normalize rather than firing a market order the second the blackout window expires.
Cleaner Labels: Replaced the basic shape plots with dynamic label.new arrays that print precise warnings at the top and bottom of the structural boundaries.
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:52 pm
by FTtrader
Porting this logic to MetaTrader requires shifting from TradingView's background color model to drawing background rectangles (OBJ_RECTANGLE). Since terminal performance is critical when monitoring order book liquidity and executing rapid scalps, these indicators are gated to only calculate the time bounds once per bar, preventing tick-level lag on your execution terminal.
Here are the production-ready versions for both MQL4 and MQL5.
MQL4: Advanced News Blackout Enforcer
Save this as News_Blackout_Enforcer.mq4 in your MQL4\Indicators folder.
Code: Select all
//+------------------------------------------------------------------+
//| News_Blackout_Enforcer.mq4 |
//+------------------------------------------------------------------+
#property copyright "Pro Version"
#property link ""
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
// --- Inputs ---
input bool Enable_Event1 = true; // Enable Event 1
input string E1_Start = "14:20"; // E1 Start (HH:MM)
input string E1_End = "14:40"; // E1 End (HH:MM)
input bool Enable_Event2 = false; // Enable Event 2
input string E2_Start = "19:50"; // E2 Start (HH:MM)
input string E2_End = "20:20"; // E2 End (HH:MM)
input bool Enable_Event3 = false; // Enable Event 3
input string E3_Start = "20:00"; // E3 Start (HH:MM)
input string E3_End = "20:30"; // E3 End (HH:MM)
input color ClrBlackout = clrDarkRed; // Blackout Zone Color
input color ClrRecovery = clrDimGray; // Recovery Zone Color
input int RecoveryBars = 3; // Post-News Recovery Bars
// --- Global Variables ---
datetime lastBarTime = 0;
datetime lastAlertStart = 0;
datetime lastAlertEnd = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit() {
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// Only run logic once per bar to save CPU cycles for scalping execution
if(Time[0] == lastBarTime) return(rates_total);
lastBarTime = Time[0];
datetime currentServerTime = TimeCurrent();
string todayStr = TimeToStr(currentServerTime, TIME_DATE);
CheckAndDrawEvent(1, Enable_Event1, todayStr, E1_Start, E1_End, currentServerTime);
CheckAndDrawEvent(2, Enable_Event2, todayStr, E2_Start, E2_End, currentServerTime);
CheckAndDrawEvent(3, Enable_Event3, todayStr, E3_Start, E3_End, currentServerTime);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Core Logic: Parse time, draw zones, and fire alerts |
//+------------------------------------------------------------------+
void CheckAndDrawEvent(int eventId, bool isEnabled, string dateStr, string startStr, string endStr, datetime currentTime) {
if(!isEnabled) return;
datetime startTime = StringToTime(dateStr + " " + startStr);
datetime endTime = StringToTime(dateStr + " " + endStr);
datetime recoveryTime = endTime + (PeriodSeconds() * RecoveryBars);
// Draw Blackout Rectangle
string objNameBlackout = "News_Blackout_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(objNameBlackout) < 0) {
ObjectCreate(0, objNameBlackout, OBJ_RECTANGLE, 0, startTime, 999999, endTime, 0);
ObjectSetInteger(0, objNameBlackout, OBJPROP_COLOR, ClrBlackout);
ObjectSetInteger(0, objNameBlackout, OBJPROP_BACK, true);
ObjectSetInteger(0, objNameBlackout, OBJPROP_FILL, true);
}
// Draw Recovery Rectangle
string objNameRecovery = "News_Recovery_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(objNameRecovery) < 0) {
ObjectCreate(0, objNameRecovery, OBJ_RECTANGLE, 0, endTime, 999999, recoveryTime, 0);
ObjectSetInteger(0, objNameRecovery, OBJPROP_COLOR, ClrRecovery);
ObjectSetInteger(0, objNameRecovery, OBJPROP_BACK, true);
ObjectSetInteger(0, objNameRecovery, OBJPROP_FILL, true);
}
// Alert Logic: Entering Blackout
if(currentTime >= startTime && currentTime < endTime) {
if(lastAlertStart != startTime) {
Alert("BLACKOUT ENTRY: Cancel Working Orders (Event " + IntegerToString(eventId) + ")");
lastAlertStart = startTime;
}
}
// Alert Logic: Exiting Blackout
if(currentTime >= endTime && currentTime < recoveryTime) {
if(lastAlertEnd != endTime) {
Alert("BLACKOUT CLEARED: Verify DOM liquidity and spread (Event " + IntegerToString(eventId) + ")");
lastAlertEnd = endTime;
}
}
}
//+------------------------------------------------------------------+
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:52 pm
by FTtrader
MQL5: Advanced News Blackout Enforcer
Save this as News_Blackout_Enforcer.mq5 in your MQL5\Indicators folder. The structure is adapted to MQL5's specific time array handling and object property enumerations.
Code: Select all
//+------------------------------------------------------------------+
//| News_Blackout_Enforcer.mq5 |
//+------------------------------------------------------------------+
#property copyright "Pro Version"
#property link ""
#property version "1.00"
#property indicator_chart_window
#property indicator_plots 0
// --- Inputs ---
input bool Enable_Event1 = true; // Enable Event 1
input string E1_Start = "14:20"; // E1 Start (HH:MM)
input string E1_End = "14:40"; // E1 End (HH:MM)
input bool Enable_Event2 = false; // Enable Event 2
input string E2_Start = "19:50"; // E2 Start (HH:MM)
input string E2_End = "20:20"; // E2 End (HH:MM)
input bool Enable_Event3 = false; // Enable Event 3
input string E3_Start = "20:00"; // E3 Start (HH:MM)
input string E3_End = "20:30"; // E3 End (HH:MM)
input color ClrBlackout = clrDarkRed; // Blackout Zone Color
input color ClrRecovery = clrDimGray; // Recovery Zone Color
input int RecoveryBars = 3; // Post-News Recovery Bars
// --- Global Variables ---
datetime lastBarTime = 0;
datetime lastAlertStart = 0;
datetime lastAlertEnd = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit() {
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total == 0) return 0;
// Only run once per bar
if(time[rates_total - 1] == lastBarTime) return(rates_total);
lastBarTime = time[rates_total - 1];
datetime currentServerTime = TimeCurrent();
string todayStr = TimeToString(currentServerTime, TIME_DATE);
CheckAndDrawEvent(1, Enable_Event1, todayStr, E1_Start, E1_End, currentServerTime);
CheckAndDrawEvent(2, Enable_Event2, todayStr, E2_Start, E2_End, currentServerTime);
CheckAndDrawEvent(3, Enable_Event3, todayStr, E3_Start, E3_End, currentServerTime);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Core Logic: Parse time, draw zones, and fire alerts |
//+------------------------------------------------------------------+
void CheckAndDrawEvent(int eventId, bool isEnabled, string dateStr, string startStr, string endStr, datetime currentTime) {
if(!isEnabled) return;
datetime startTime = StringToTime(dateStr + " " + startStr);
datetime endTime = StringToTime(dateStr + " " + endStr);
datetime recoveryTime = endTime + (PeriodSeconds() * RecoveryBars);
long chartID = 0;
// Draw Blackout Rectangle
string objNameBlackout = "News_Blackout_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(chartID, objNameBlackout) < 0) {
ObjectCreate(chartID, objNameBlackout, OBJ_RECTANGLE, 0, startTime, 999999, endTime, 0);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_COLOR, ClrBlackout);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_BACK, true);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_FILL, true);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_HIDDEN, true); // Hide from object list
}
// Draw Recovery Rectangle
string objNameRecovery = "News_Recovery_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(chartID, objNameRecovery) < 0) {
ObjectCreate(chartID, objNameRecovery, OBJ_RECTANGLE, 0, endTime, 999999, recoveryTime, 0);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_COLOR, ClrRecovery);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_BACK, true);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_FILL, true);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_HIDDEN, true);
}
// Alert Logic: Entering Blackout
if(currentTime >= startTime && currentTime < endTime) {
if(lastAlertStart != startTime) {
Alert("BLACKOUT ENTRY: Cancel Working Orders (Event " + IntegerToString(eventId) + ")");
lastAlertStart = startTime;
}
}
// Alert Logic: Exiting Blackout
if(currentTime >= endTime && currentTime < recoveryTime) {
if(lastAlertEnd != endTime) {
Alert("BLACKOUT CLEARED: Verify DOM liquidity and spread (Event " + IntegerToString(eventId) + ")");
lastAlertEnd = endTime;
}
}
}
//+------------------------------------------------------------------+
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:53 pm
by FTtrader
Here are the updated MQL4 and MQL5 scripts modified to include automated order cancellation. When the blackout window begins, the script will loop through all open pending orders for the current chart symbol and issue delete requests for every limit and stop order, enforcing your rule mechanically before the liquidity void hits.
MQL4: Automated Order-Cancellation Blackout Enforcer
Save this as News_Blackout_AutoCancel.mq4 in your MQL4\Indicators folder.
Code: Select all
//+------------------------------------------------------------------+
//| News_Blackout_AutoCancel.mq4 |
//+------------------------------------------------------------------+
#property copyright "Pro Version"
#property link ""
#property version "2.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
// --- Inputs ---
input bool Enable_Event1 = true; // Enable Event 1
input string E1_Start = "14:20"; // E1 Start (HH:MM)
input string E1_End = "14:40"; // E1 End (HH:MM)
input bool Enable_Event2 = false; // Enable Event 2
input string E2_Start = "19:50"; // E2 Start (HH:MM)
input string E2_End = "20:20"; // E2 End (HH:MM)
input bool Enable_Event3 = false; // Enable Event 3
input string E3_Start = "20:00"; // E3 Start (HH:MM)
input string E3_End = "20:30"; // E3 End (HH:MM)
input color ClrBlackout = clrDarkRed; // Blackout Zone Color
input color ClrRecovery = clrDimGray; // Recovery Zone Color
input int RecoveryBars = 3; // Post-News Recovery Bars
// --- Global Variables ---
datetime lastBarTime = 0;
datetime lastAlertStart = 0;
datetime lastAlertEnd = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit() {
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(Time[0] == lastBarTime) return(rates_total);
lastBarTime = Time[0];
datetime currentServerTime = TimeCurrent();
string todayStr = TimeToStr(currentServerTime, TIME_DATE);
CheckAndExecute(1, Enable_Event1, todayStr, E1_Start, E1_End, currentServerTime);
CheckAndExecute(2, Enable_Event2, todayStr, E2_Start, E2_End, currentServerTime);
CheckAndExecute(3, Enable_Event3, todayStr, E3_Start, E3_End, currentServerTime);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Core Logic: Parse time, draw zones, cancel orders & trigger |
//+------------------------------------------------------------------+
void CheckAndExecute(int eventId, bool isEnabled, string dateStr, string startStr, string endStr, datetime currentTime) {
if(!isEnabled) return;
datetime startTime = StringToTime(dateStr + " " + startStr);
datetime endTime = StringToTime(dateStr + " " + endStr);
datetime recoveryTime = endTime + (PeriodSeconds() * RecoveryBars);
// Draw Blackout Rectangle
string objNameBlackout = "News_Blackout_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(objNameBlackout) < 0) {
ObjectCreate(0, objNameBlackout, OBJ_RECTANGLE, 0, startTime, 999999, endTime, 0);
ObjectSetInteger(0, objNameBlackout, OBJPROP_COLOR, ClrBlackout);
ObjectSetInteger(0, objNameBlackout, OBJPROP_BACK, true);
ObjectSetInteger(0, objNameBlackout, OBJPROP_FILL, true);
}
// Draw Recovery Rectangle
string objNameRecovery = "News_Recovery_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(objNameRecovery) < 0) {
ObjectCreate(0, objNameRecovery, OBJ_RECTANGLE, 0, endTime, 999999, recoveryTime, 0);
ObjectSetInteger(0, objNameRecovery, OBJPROP_COLOR, ClrRecovery);
ObjectSetInteger(0, objNameRecovery, OBJPROP_BACK, true);
ObjectSetInteger(0, objNameRecovery, OBJPROP_FILL, true);
}
// Action Logic: Entering Blackout (Auto-Cancel Pending Orders)
if(currentTime >= startTime && currentTime < endTime) {
if(lastAlertStart != startTime) {
CancelPendingOrders(Symbol());
Alert("BLACKOUT ENTRY: Working orders cancelled (Event " + IntegerToString(eventId) + ")");
lastAlertStart = startTime;
}
}
// Action Logic: Exiting Blackout
if(currentTime >= endTime && currentTime < recoveryTime) {
if(lastAlertEnd != endTime) {
Alert("BLACKOUT CLEARED: Verify DOM liquidity and spread (Event " + IntegerToString(eventId) + ")");
lastAlertEnd = endTime;
}
}
}
//+------------------------------------------------------------------+
//| Helper: Loop through pool and delete pending orders for symbol |
//+------------------------------------------------------------------+
void CancelPendingOrders(string symbol) {
int total = OrdersTotal();
for(int i = total - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == symbol) {
int type = OrderType();
// Check if order is a pending limit or stop order
if(type > 1 && type < 6) {
bool res = OrderDelete(OrderTicket());
if(!res) {
Print("Failed to delete pending order #", OrderTicket(), ". Error: ", GetLastError());
}
}
}
}
}
}
//+------------------------------------------------------------------+
Re: A practical guide to flat-before-news discipline
Posted: Mon Sep 14, 2026 9:53 pm
by FTtrader
MQL5: Automated Order-Cancellation Blackout Enforcer
Save this as News_Blackout_AutoCancel.mq5 in your MQL5\Indicators folder.
Code: Select all
//+------------------------------------------------------------------+
//| News_Blackout_AutoCancel.mq5 |
//+------------------------------------------------------------------+
#property copyright "Pro Version"
#property link ""
#property version "2.00"
#property indicator_chart_window
#property indicator_plots 0
#include <Trade\Trade.mqh>
CTrade trade;
// --- Inputs ---
input bool Enable_Event1 = true; // Enable Event 1
input string E1_Start = "14:20"; // E1 Start (HH:MM)
input string E1_End = "14:40"; // E1 End (HH:MM)
input bool Enable_Event2 = false; // Enable Event 2
input string E2_Start = "19:50"; // E2 Start (HH:MM)
input string E2_End = "20:20"; // E2 End (HH:MM)
input bool Enable_Event3 = false; // Enable Event 3
input string E3_Start = "20:00"; // E3 Start (HH:MM)
input string E3_End = "20:30"; // E3 End (HH:MM)
input color ClrBlackout = clrDarkRed; // Blackout Zone Color
input color ClrRecovery = clrDimGray; // Recovery Zone Color
input int RecoveryBars = 3; // Post-News Recovery Bars
// --- Global Variables ---
datetime lastBarTime = 0;
datetime lastAlertStart = 0;
datetime lastAlertEnd = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit() {
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total == 0) return 0;
if(time[rates_total - 1] == lastBarTime) return(rates_total);
lastBarTime = time[rates_total - 1];
datetime currentServerTime = TimeCurrent();
string todayStr = TimeToString(currentServerTime, TIME_DATE);
CheckAndExecute(1, Enable_Event1, todayStr, E1_Start, E1_End, currentServerTime);
CheckAndExecute(2, Enable_Event2, todayStr, E2_Start, E2_End, currentServerTime);
CheckAndExecute(3, Enable_Event3, todayStr, E3_Start, E3_End, currentServerTime);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Core Logic: Parse time, draw zones, cancel orders & trigger |
//+------------------------------------------------------------------+
void CheckAndExecute(int eventId, bool isEnabled, string dateStr, string startStr, string endStr, datetime currentTime) {
if(!isEnabled) return;
datetime startTime = StringToTime(dateStr + " " + startStr);
datetime endTime = StringToTime(dateStr + " " + endStr);
datetime recoveryTime = endTime + (PeriodSeconds() * RecoveryBars);
long chartID = 0;
// Draw Blackout Rectangle
string objNameBlackout = "News_Blackout_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(chartID, objNameBlackout) < 0) {
ObjectCreate(chartID, objNameBlackout, OBJ_RECTANGLE, 0, startTime, 999999, endTime, 0);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_COLOR, ClrBlackout);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_BACK, true);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_FILL, true);
ObjectSetInteger(chartID, objNameBlackout, OBJPROP_HIDDEN, true);
}
// Draw Recovery Rectangle
string objNameRecovery = "News_Recovery_" + IntegerToString(eventId) + "_" + dateStr;
if(ObjectFind(chartID, objNameRecovery) < 0) {
ObjectCreate(chartID, objNameRecovery, OBJ_RECTANGLE, 0, endTime, 999999, recoveryTime, 0);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_COLOR, ClrRecovery);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_BACK, true);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_FILL, true);
ObjectSetInteger(chartID, objNameRecovery, OBJPROP_HIDDEN, true);
}
// Action Logic: Entering Blackout (Auto-Cancel Pending Orders)
if(currentTime >= startTime && currentTime < endTime) {
if(lastAlertStart != startTime) {
CancelPendingOrders(_Symbol);
Alert("BLACKOUT ENTRY: Working orders cancelled (Event " + IntegerToString(eventId) + ")");
lastAlertStart = startTime;
}
}
// Action Logic: Exiting Blackout
if(currentTime >= endTime && currentTime < recoveryTime) {
if(lastAlertEnd != endTime) {
Alert("BLACKOUT CLEARED: Verify DOM liquidity and spread (Event " + IntegerToString(eventId) + ")");
lastAlertEnd = endTime;
}
}
}
//+------------------------------------------------------------------+
//| Helper: Loop through orders and delete pending orders for symbol |
//+------------------------------------------------------------------+
void CancelPendingOrders(string targetSymbol) {
int total = OrdersTotal();
for(int i = total - 1; i >= 0; i--) {
ulong ticket = OrderGetTicket(i);
if(ticket > 0) {
string symbol = OrderGetString(ORDER_SYMBOL);
if(symbol == targetSymbol) {
// Request deletion via trade class
trade.OrderDelete(ticket);
}
}
}
}
//+------------------------------------------------------------------+