Page 3 of 4
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:34 pm
by PTScalper
The MT5 Implementation (MQL5)
MT5 requires you to explicitly subscribe to the DOM using MarketBookAdd(). The logic is identical: retrieve the MqlBookInfo array, separate the asks, and compute the true cost of execution for your specific lot size.
Code: Select all
#property copyright "Macro DOM Filter"
#property version "1.00"
input double InpMaxEffectiveSpreadPips = 1.5;
input double InpTargetVolumeLots = 10.0;
int OnInit()
{
// Subscribe to Level 2 Data
if(!MarketBookAdd(_Symbol)) {
Print("DOM not supported by this broker/feed.");
return INIT_FAILED;
}
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
MarketBookRelease(_Symbol);
}
bool IsBookDeepEnoughForLong()
{
MqlBookInfo book[];
if(!MarketBookGet(_Symbol, book)) return false;
double targetVolume = InpTargetVolumeLots;
double accumulatedVolume = 0;
double weightedPriceSum = 0;
double topBid = 0;
// Find top bid for spread calculation
for(int i = 0; i < ArraySize(book); i++) {
if(book[i].type == BOOK_TYPE_BUY) {
topBid = book[i].price;
break;
}
}
// Calculate VWAP across Ask tiers
for(int i = 0; i < ArraySize(book); i++)
{
if(book[i].type == BOOK_TYPE_SELL)
{
double volumeToTake = MathMin(book[i].volume, targetVolume - accumulatedVolume);
accumulatedVolume += volumeToTake;
weightedPriceSum += book[i].price * volumeToTake;
if(accumulatedVolume >= targetVolume) break;
}
}
if(accumulatedVolume < targetVolume || topBid == 0) return false;
double vwapAsk = weightedPriceSum / targetVolume;
double effectiveSpread = (vwapAsk - topBid) / _Point / 10.0; // Assuming 5-digit pricing
return effectiveSpread <= InpMaxEffectiveSpreadPips;
}
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:34 pm
by PTScalper
The Bottom Line
If your strategy is pushing serious volume, never trust Level 1 spread. A 50-lot position executed via market order on a 0.2 pip Level 1 spread can easily suffer 3 pips of VWAP slippage during a CPI digest. Computing the true DOM absorption cost guarantees that your script only fires when institutional liquidity has genuinely returned to the book.
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:36 pm
by PTScalper
When you trade the structural digest of a CPI or NFP print, the retail infrastructure itself becomes your biggest bottleneck.
Here is why the shift to direct FIX 4.4 connectivity fundamentally changes the execution landscape.
1. Eliminating the "Middleware Tax
"When you fire a market order from an MT5 Expert Advisor or a cTrader bot, it does not go straight to the market. It must survive a gauntlet of network hops:Your Terminal ➔ Broker's MT5/cTrader ServerMT5 Server ➔ MT5 Gateway (which translates platform logic into FIX messages). MT5 Gateway ➔ Liquidity Bridge (like oneZero or PrimeXM), where the broker's risk plugins (A-book/B-book routing, spread markups) are applied. Liquidity Bridge ➔ Prime Broker's Matching Engine
Even on a highly optimized, co-located VPS, this middleware chain introduces 30–150 milliseconds of processing latency. In a liquidity vacuum following a macro print, 50 milliseconds is an eternity. Prices can gap 15 pips before your order even leaves the bridge.
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:40 pm
by PTScalper
2. The FIX API Advantage
By writing your execution engine in a lower-level language (like C++, C#, or Rust) and communicating directly via the Financial Information eXchange (FIX 4.4) protocol, you strip away the UI and the broker's platform servers entirely.
Sub-Millisecond Latency: If you co-locate your execution server in the exact Equinix data center as your Prime Broker (typically LD4 in London or NY4 in New York) and use a physical fiber cross-connect, your round-trip execution drops to the 1–5 millisecond range.
Raw DOM Transparency: You receive the unthrottled, unconflated Level II order book directly from the LP aggregator. This allows the VWAP calculations we discussed earlier to be perfectly accurate because you are reading the actual market matching engine, not a broker's filtered price feed.
Institutional Order Types: FIX allows you to send native Fill or Kill (FOK) and Immediate or Cancel (IOC) orders. If the liquidity isn't there at your exact requested price, the order is instantly killed by the matching engine, structurally preventing catastrophic slippage. Retail platforms often emulate these locally, which is dangerously slow.
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:40 pm
by PTScalper
3. The Infrastructure Reality Check
The tradeoff for this speed is that you become your own IT department and risk manager.
Session Management: FIX is a raw TCP protocol. You have to programmatically manage logon sequences, heartbeats, sequence number syncing, and emergency disconnect recovery.
The Capital Barrier: True direct market access (DMA) via a Prime of Prime usually requires a minimum account size of $50,000 to $100,000+, alongside monthly volume minimums and data center cross-connect fees.
If your volume and strategy justify the capital requirements, abandoning the retail platforms and plugging directly into the FIX bridge is the only way to guarantee you aren't the one providing liquidity to faster algorithms during a news print.
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:44 pm
by PTScalper
To elevate this from a retail template to an institutional execution framework, we need to strip out static assumptions and build a deterministic state machine.
An institutional model does not trade a fixed "half size." It dynamically sizes the position based on live account equity and a volatility-adjusted stop distance. Furthermore, we must migrate to Pine Script v6, which introduces strict type safety, native enumerations (enum), and real-time top-of-book variables (bid and ask) that finally allow us to stop using high and low as spread proxies.
Here is the institutional architecture. It utilizes User-Defined Types (UDTs) to store the market structure, an Enum to lock the state machine, and a dynamic risk model for position sizing.
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:44 pm
by PTScalper
Institutional v6 Framework
Code: Select all
//@version=6
strategy("Institutional Macro Engine v6", overlay=true, calc_on_every_tick=true, margin_long=100, margin_short=100)
// =========================================================================
// 1. INPUTS & RISK PARAMETERS
// =========================================================================
var grpTime = "Macro Event Target"
newsHour = input.int(8, "Hour (Exchange Time)", group=grpTime, minval=0, maxval=23)
newsMinute = input.int(30, "Minute", group=grpTime, minval=0, maxval=59)
var grpRisk = "Risk Management Engine"
riskPct = input.float(1.0, "Risk Per Trade (%)", group=grpRisk, step=0.1)
atrPeriod = input.int(14, "ATR Period (Stop Sizing)", group=grpRisk)
atrMult = input.float(1.5, "ATR Stop Multiplier", group=grpRisk)
maxSpread = input.float(1.5, "Max Spread (Pips)", group=grpRisk)
var grpExec = "Execution States"
preMins = input.int(10, "Kill-Switch Mins Before", group=grpExec)
digestMins = input.int(15, "Digest Window (Mins)", group=grpExec)
ttlMins = input.int(45, "Time-To-Live (Mins)", group=grpExec, tooltip="Cut trade if stagnant")
// =========================================================================
// 2. ENUMS & DATA STRUCTURES (v6 Features)
// =========================================================================
// Enums provide type-safe control over the state machine
enum MarketState
Active
PreNewsVacuum
Digest
PostNewsWindow
// UDT to encapsulate the structural impulse data
type MacroStructure
float impHigh
float impLow
float fib50
float fib618
bool isLocked
// =========================================================================
// 3. STATE MACHINE & TIME ENGINE
// =========================================================================
currMins = hour(time) * 60 + minute(time)
newsMins = newsHour * 60 + newsMinute
MarketState state = MarketState.Active
if (currMins >= (newsMins - preMins)) and (currMins < newsMins)
state := MarketState.PreNewsVacuum
else if (currMins >= newsMins) and (currMins < (newsMins + digestMins))
state := MarketState.Digest
else if (currMins >= (newsMins + digestMins)) and (currMins < (newsMins + 120))
state := MarketState.PostNewsWindow
// =========================================================================
// 4. LIQUIDITY & SPREAD VALIDATION
// =========================================================================
// Pine v6 introduces bid/ask. We use these in realtime; fallback to high/low for historical backtesting
float currentSpreadPips = barstate.isrealtime ? (ask - bid) / syminfo.mintick / 10 : (high - low) / syminfo.mintick / 10
// Strict booleans in v6: variables must evaluate to true/false, never 'na'
bool isSpreadTight = na(currentSpreadPips) ? false : (currentSpreadPips <= maxSpread)
// =========================================================================
// 5. STRUCTURAL IMPULSE MAPPING
// =========================================================================
var MacroStructure struct = MacroStructure.new(na, na, na, na, false)
var int tradeStartTime = na
if state == MarketState.Digest
// Reset structure on first tick of digest
if struct.isLocked
struct := MacroStructure.new(high, low, na, na, false)
else
struct.impHigh := math.max(nz(struct.impHigh, high), high)
struct.impLow := math.min(nz(struct.impLow, low), low)
else if state == MarketState.PostNewsWindow and not struct.isLocked
// Lock the structure and calculate institutional discount levels
struct.fib50 := struct.impHigh - ((struct.impHigh - struct.impLow) * 0.5)
struct.fib618 := struct.impHigh - ((struct.impHigh - struct.impLow) * 0.618)
struct.isLocked := true
// =========================================================================
// 6. VOLATILITY-ADJUSTED RISK MODEL
// =========================================================================
float currATR = ta.atr(atrPeriod)
float stopDist = currATR * atrMult
// Dynamic Sizing: (Account Equity * Risk %) / (Stop Distance in Account Currency)
float riskAmount = strategy.equity * (riskPct / 100)
float pointValue = syminfo.pointvalue
float lotSize = pointValue > 0 and stopDist > 0 ? (riskAmount / (stopDist / syminfo.mintick * pointValue)) : 0
// =========================================================================
// 7. EXECUTION & TIME EXITS
// =========================================================================
if state == MarketState.PreNewsVacuum
strategy.cancel_all()
strategy.close_all(comment="KILL: Vacuum")
// Entry Engine
if state == MarketState.PostNewsWindow and struct.isLocked and strategy.position_size == 0
// Setup: Price testing the 50%-61.8% discount array
bool inDiscountZone = close > struct.fib618 and low <= struct.fib50
if inDiscountZone and isSpreadTight and lotSize > 0
strategy.entry("Macro_Cont", strategy.long, qty=lotSize)
strategy.exit("Macro_Risk", "Macro_Cont", stop=close - stopDist, limit=struct.impHigh)
tradeStartTime := currMins
// Time-To-Live (TTL) Hard Exit
if strategy.position_size != 0
if (currMins - tradeStartTime) >= ttlMins
strategy.close("Macro_Cont", comment="TTL: Stagnant Flow")
// =========================================================================
// 8. VISUAL DIAGNOSTICS & DASHBOARD
// =========================================================================
bgcolor(state == MarketState.PreNewsVacuum ? color.new(color.red, 90) : na, title="Vacuum Zone")
bgcolor(state == MarketState.Digest ? color.new(color.orange, 90) : na, title="Digest Zone")
plot(struct.isLocked and state == MarketState.PostNewsWindow ? struct.impHigh : na, color=color.new(color.green, 50), style=plot.style_linebr, title="Impulse High")
plot(struct.isLocked and state == MarketState.PostNewsWindow ? struct.impLow : na, color=color.new(color.red, 50), style=plot.style_linebr, title="Impulse Low")
plot(struct.isLocked and state == MarketState.PostNewsWindow ? struct.fib50 : na, color=color.new(color.blue, 0), style=plot.style_cross, title="50% Retrace")
// v6 Professional Text Formatting on Dashboard
var table dash = table.new(position.bottom_right, 2, 2, border_width = 1)
if barstate.islast
table.cell(dash, 0, 0, "Market State:", text_color=color.gray)
table.cell(dash, 1, 0, str.tostring(state), text_color=color.white, text_formatting=text.format_bold)
table.cell(dash, 0, 1, "Top-of-Book Spread:", text_color=color.gray)
table.cell(dash, 1, 1, str.tostring(currentSpreadPips, "#.#") + " Pips", text_color=isSpreadTight ? color.green : color.red)
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:45 pm
by PTScalper
Architectural UpgradesReal-time Book Analysis:
Because Pine Script v6 grants access to the highest price an active buyer is willing to pay (bid) and the lowest an active seller will accept (ask), we no longer have to use High/Low spreads. The script reads the raw top-of-book feed in real-time to validate liquidity before firing.
Deterministic Enums: The MarketState Enum replaces messy integer state variables. This ensures compile-time safety so the execution engine can only exist in mathematically predefined boundaries.
Volatility Sizing: Instead of arbitrary "half size" logic, the framework checks the ATR immediately preceding the print, calculates the necessary stop distance to avoid market noise, and dynamically sizes the contract count so you only ever risk exactly 1.0% of account equity.
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:46 pm
by PTScalper
Integrating a multi-timeframe (MTF) regime filter into a lower-timeframe execution engine introduces a specific structural hazard: Data Bleed.
If you simply request the current 4-hour trend during the news digest (e.g., the 08:30 to 08:45 window), the massive volatility of the news print itself will instantaneously skew the 4-hour moving averages. You will end up reading a trend direction that was artificially created by the news spike, rather than the true underlying regime that existed before the release.
To build an institutional-grade filter in Pine Script v6, we must achieve two things:
Type-Safe Regime Classification: Define an enum to strictly categorize the higher timeframe (HTF) state.
State Isolation (Non-Repainting): Use request.security() combined with a historical offset [1] and barmerge.lookahead_on to fetch the 4-hour state exactly as it closed prior to the current bar. This guarantees the macro spike does not corrupt your trend logic.
Here is the modular addition to the v6 framework.
Re: News-based scalping: how do you trade NFP/CPI releases?
Posted: Mon Sep 07, 2026 9:46 pm
by PTScalper
The MTF Regime Architecture
First, we define the enum and the calculation function. Place this near the top of your script, right after your inputs.
Code: Select all
// =========================================================================
// MTF REGIME FILTER (4-Hour Trend)
// =========================================================================
var grpHtf = "Higher Timeframe Filter"
htfRes = input.timeframe("240", "Regime Timeframe (e.g., 240 = 4H)", group=grpHtf)
emaFastLen = input.int(20, "HTF Fast EMA", group=grpHtf)
emaSlowLen = input.int(50, "HTF Slow EMA", group=grpHtf)
// Type-safe enum for the trend regime
enum Regime
Bullish
Bearish
Neutral
// Function to calculate the trend structurally
getHtfRegime() =>
float fastEMA = ta.ema(close, emaFastLen)
float slowEMA = ta.ema(close, emaSlowLen)
Regime currentRegime = Regime.Neutral
if close > fastEMA and fastEMA > slowEMA
currentRegime := Regime.Bullish
else if close < fastEMA and fastEMA < slowEMA
currentRegime := Regime.Bearish
currentRegime
// Fetch the LAST fully closed HTF bar's state.
// Using [1] with lookahead_on is the canonical way to prevent repainting
// and ensure the news spike itself doesn't skew the filter.
Regime htfTrend = request.security(syminfo.tickerid, htfRes, getHtfRegime()[1], lookahead=barmerge.lookahead_on)