Advertisement IC Markets

Post-central bank speech review template for scalpers

Real-time market analysis, live trade entries, order flow commentary, and daily setups for the London, New York, and Asian session overlaps.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

The Pine Script (v5)

Code: Select all

//@version=5
strategy("Pro News Pullback Scalper", overlay=true, initial_capital=10000, default_qty_type=strategy.cash, commission_type=strategy.commission.cash_per_order, commission_value=1)

// =========================================================================
// 1. TIME & REACTION SETTINGS
// =========================================================================
grp_time = "1. Session & Reaction"
news_session = input.session("14:00-14:15", title="News Chaos Window", group=grp_time, tooltip="Exchange time. The 'sit on your hands' window.")
box_color_bull = input.color(color.new(color.teal, 85), title="Bullish Reaction Color", group=grp_time)
box_color_bear = input.color(color.new(color.maroon, 85), title="Bearish Reaction Color", group=grp_time)

// =========================================================================
// 2. TRIGGER & EXPIRATION SETTINGS
// =========================================================================
grp_trig = "2. Trigger & Filters"
ema_len = input.int(9, title="Pullback EMA", group=grp_trig)
max_wait_bars = input.int(30, title="Max Bars to Wait for Pullback", group=grp_trig, tooltip="If the pullback doesn't happen within this many bars, cancel the setup.")
max_sl_pips = input.float(30.0, title="Max Allowed SL (Pips/Points)", group=grp_trig, tooltip="Prevents taking trades in insane, untradable volatility.")

// =========================================================================
// 3. RISK MANAGEMENT
// =========================================================================
grp_risk = "3. Risk Management"
risk_pct = input.float(1.0, title="Risk Per Trade (%)", step=0.1, group=grp_risk)
rr_ratio = input.float(2.0, title="Risk/Reward Ratio", step=0.1, group=grp_risk)

// =========================================================================
// STATE VARIABLES & TYPES
// =========================================================================
var float init_open = na
var float init_close = na
var float init_high = na
var float init_low = na
var int trend_dir = 0 
var bool pullback_valid = false
var float pivot_extreme = na
var int bars_since_news = 0
var box session_box = na

// Trade state trackers for plotting
var float active_sl = na
var float active_tp = na

// =========================================================================
// SESSION LOGIC (The "Before" & "First Reaction" Phase)
// =========================================================================
in_news = not na(time(timeframe.period, news_session))
news_start = in_news and not in_news[1]
news_end = not in_news and in_news[1]

// Open the session
if news_start
    init_open := open
    init_high := high
    init_low := low
    trend_dir := 0
    pullback_valid := false
    pivot_extreme := na
    bars_since_news := 0
    // Create visual box
    session_box := box.new(bar_index, high, bar_index, low, border_color=color.new(color.gray, 50), border_style=line.style_dashed, bgcolor=color.new(color.gray, 90))

// Update high/low of the news session for the box
if in_news
    init_high := math.max(init_high, high)
    init_low := math.min(init_low, low)
    box.set_top(session_box, init_high)
    box.set_bottom(session_box, init_low)
    box.set_right(session_box, bar_index)

// Close the session and determine direction
if news_end
    init_close := close[1]
    trend_dir := init_close > init_open ? 1 : (init_close < init_open ? -1 : 0)
    pivot_extreme := trend_dir == 1 ? low : high 
    
    // Color the box based on the reaction direction
    box.set_bgcolor(session_box, trend_dir == 1 ? box_color_bull : (trend_dir == -1 ? box_color_bear : color.new(color.gray, 90)))

// =========================================================================
// PULLBACK & ENTRY LOGIC
// =========================================================================
ema = ta.ema(close, ema_len)
plot(ema, color=color.new(color.blue, 30), title="Pullback EMA", linewidth=2)

// Reset setup if trade is active or max bars exceeded
if strategy.position_size != 0
    trend_dir := 0
    active_sl := na
    active_tp := na

valid_window = not in_news and trend_dir != 0

if valid_window
    bars_since_news += 1
    
    // Expiration Filter: Cancel setup if it takes too long
    if bars_since_news > max_wait_bars
        trend_dir := 0
        pullback_valid := false

    // Track the extreme of the pullback for SL placement
    if trend_dir == 1
        pivot_extreme := math.min(pivot_extreme, low)
    else if trend_dir == -1
        pivot_extreme := math.max(pivot_extreme, high)

    // Calculate PIP conversion (works for Forex, Crypto, Indices)
    pip_size = syminfo.mintick * (syminfo.type == "forex" ? 10 : 1)

    // LONG SETUP
    if trend_dir == 1
        if close < ema // Validates the pullback
            pullback_valid := true
        
        if pullback_valid and close > ema // Trigger
            float sl = pivot_extreme
            float risk_pips = (close - sl) / pip_size
            
            // Volatility Filter
            if risk_pips <= max_sl_pips and risk_pips > 0
                float risk_usd = strategy.equity * (risk_pct / 100)
                float qty = risk_usd / (close - sl)
                float tp = close + ((close - sl) * rr_ratio)
                
                strategy.entry("News Long", strategy.long, qty=qty)
                strategy.exit("Exit Long", "News Long", stop=sl, limit=tp)
                
                active_sl := sl
                active_tp := tp
                trend_dir := 0 // Setup used

    // SHORT SETUP
    if trend_dir == -1
        if close > ema // Validates the pullback
            pullback_valid := true
        
        if pullback_valid and close < ema // Trigger
            float sl = pivot_extreme
            float risk_pips = (sl - close) / pip_size
            
            // Volatility Filter
            if risk_pips <= max_sl_pips and risk_pips > 0
                float risk_usd = strategy.equity * (risk_pct / 100)
                float qty = risk_usd / (sl - close)
                float tp = close - ((sl - close) * rr_ratio)
                
                strategy.entry("News Short", strategy.short, qty=qty)
                strategy.exit("Exit Short", "News Short", stop=sl, limit=tp)
                
                active_sl := sl
                active_tp := tp
                trend_dir := 0 // Setup used

// =========================================================================
// VISUAL TRADE MAPPING
// =========================================================================
// Draw SL and TP lines only when a trade is active
plot(strategy.position_size != 0 ? active_sl : na, color=color.new(color.red, 20), style=plot.style_linebr, linewidth=2, title="Stop Loss")
plot(strategy.position_size != 0 ? active_tp : na, color=color.new(color.green, 20), style=plot.style_linebr, linewidth=2, title="Take Profit")
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: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

How to adapt this to your central bank journaling:

Apply to 1m or 5m charts: This script is optimized for lower timeframes where the exact pullback extreme can be caught cleanly.

Review your template data: You mentioned you tracked that some speakers only move the market ~15 pips. You can now use the Max Allowed SL input to filter those speakers out. If the script sees that a speaker caused a 25-pip stop loss distance, and you know historically they don't produce enough continuation to hit a 1:2 RR on a 25-pip stop, you set the max SL to 15. The script will simply ignore the trade.

Analyze the Box: The shaded box on the chart represents the "First reaction" period you track in your template. If the box turns Green, the initial close was bullish. You can visually see how often the market respects that initial direction versus how often it completely reverses (invalidating the setup).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

To push this into institutional territory, we have to move beyond just finding the entry. Professional execution is about trade management and information display.

This ultra-pro version introduces four mechanics used by proprietary traders:

Multi-Stage Scaling (TP1 & Runner): Automatically takes partial profits at a safe 1:1 Risk/Reward to secure capital, while leaving a "runner" to catch the 2.5+ RR trend continuation.

Auto-Breakeven (Free Ride): Once TP1 is hit, the script automatically moves the Stop Loss for the remaining position to your exact entry price.

Stop-Loss Wick Buffer: Institutional algos often sweep exact pivot lows/highs by 1 or 2 pips to trigger retail stop-losses before reversing. This version adds a customizable fractional pip buffer to hide your stop just beyond the obvious extreme.

Live Trading HUD (Dashboard): A real-time data table on the chart showing current trend bias, setup status, and exact pip risk.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

The Ultra-Pro Pine Script (v5)

Code: Select all

//@version=5
strategy("Ultra-Pro News Scalper", overlay=true, initial_capital=10000, default_qty_type=strategy.cash, commission_type=strategy.commission.cash_per_order, commission_value=1, max_labels_count=50)

// =========================================================================
// 1. SESSION & REACTION
// =========================================================================
grp_time = "1. Session & Reaction"
news_session = input.session("14:00-14:15", title="News Chaos Window", group=grp_time)

// =========================================================================
// 2. TRIGGER & FILTERS
// =========================================================================
grp_trig = "2. Trigger & Filters"
ema_len = input.int(9, title="Pullback EMA", group=grp_trig)
max_wait_bars = input.int(30, title="Max Bars to Wait for Pullback", group=grp_trig)
max_sl_pips = input.float(30.0, title="Max Allowed SL (Pips)", group=grp_trig)
sl_buffer = input.float(1.5, title="SL Wick Buffer (Pips)", step=0.5, group=grp_trig, tooltip="Adds padding to the pivot to survive wick sweeps.")

// =========================================================================
// 3. PRO TRADE MANAGEMENT (SCALING & BE)
// =========================================================================
grp_risk = "3. Risk & Management"
risk_pct = input.float(1.0, title="Total Risk Per Trade (%)", step=0.1, group=grp_risk)
tp1_rr = input.float(1.0, title="Target 1 (R:R)", step=0.1, group=grp_risk)
tp1_qty = input.int(50, title="Target 1 Exit Size (%)", group=grp_risk)
tp2_rr = input.float(2.5, title="Target 2 / Runner (R:R)", step=0.1, group=grp_risk)
auto_be = input.bool(true, title="Auto-Breakeven after TP1?", group=grp_risk)

// =========================================================================
// STATE VARIABLES
// =========================================================================
var float init_open = na, var float init_close = na, var float init_high = na, var float init_low = na
var int trend_dir = 0 
var bool pullback_valid = false
var float pivot_extreme = na
var int bars_since_news = 0
var box session_box = na

// Trade Management State
var float active_entry = na
var float active_sl = na
var float active_tp1 = na
var float active_tp2 = na
var bool tp1_hit = false

// =========================================================================
// PIP CALCULATION
// =========================================================================
pip_size = syminfo.mintick * (syminfo.type == "forex" ? 10 : 1)
pip_buffer = sl_buffer * pip_size

// =========================================================================
// HUD DASHBOARD SETUP
// =========================================================================
var table hud = table.new(position.top_right, 2, 4, border_width=1, border_color=color.new(color.gray, 80), frame_color=color.new(color.gray, 80), frame_width=1)
if barstate.isfirst
    table.cell(hud, 0, 0, "SYSTEM STATUS", bgcolor=color.new(color.black, 20), text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 0, "DATA", bgcolor=color.new(color.black, 20), text_color=color.white, text_size=size.small)

// =========================================================================
// SESSION LOGIC
// =========================================================================
in_news = not na(time(timeframe.period, news_session))
news_start = in_news and not in_news[1]
news_end = not in_news and in_news[1]

if news_start
    init_open := open, init_high := high, init_low := low
    trend_dir := 0, pullback_valid := false, pivot_extreme := na, bars_since_news := 0
    session_box := box.new(bar_index, high, bar_index, low, border_color=color.new(color.gray, 50), border_style=line.style_dashed, bgcolor=color.new(color.gray, 90))

if in_news
    init_high := math.max(init_high, high), init_low := math.min(init_low, low)
    box.set_top(session_box, init_high), box.set_bottom(session_box, init_low), box.set_right(session_box, bar_index)

if news_end
    init_close := close[1]
    trend_dir := init_close > init_open ? 1 : (init_close < init_open ? -1 : 0)
    pivot_extreme := trend_dir == 1 ? low : high 
    box.set_bgcolor(session_box, trend_dir == 1 ? color.new(color.teal, 85) : (trend_dir == -1 ? color.new(color.maroon, 85) : color.new(color.gray, 90)))

// =========================================================================
// PULLBACK & ENTRY LOGIC
// =========================================================================
ema = ta.ema(close, ema_len)
plot(ema, color=color.new(color.blue, 30), title="Pullback EMA", linewidth=2)

// Reset trade state if flat
if strategy.position_size == 0
    tp1_hit := false
    active_entry := na

valid_window = not in_news and trend_dir != 0 and strategy.position_size == 0

if valid_window
    bars_since_news += 1
    if bars_since_news > max_wait_bars
        trend_dir := 0, pullback_valid := false // Expire setup

    // Track extreme for SL
    if trend_dir == 1
        pivot_extreme := math.min(pivot_extreme, low)
    else if trend_dir == -1
        pivot_extreme := math.max(pivot_extreme, high)

    // LONG SETUP
    if trend_dir == 1
        if close < ema
            pullback_valid := true
        
        if pullback_valid and close > ema
            float sl = pivot_extreme - pip_buffer
            float risk_pips = (close - sl) / pip_size
            
            if risk_pips <= max_sl_pips and risk_pips > 0
                float risk_usd = strategy.equity * (risk_pct / 100)
                float qty = risk_usd / (close - sl)
                
                active_entry := close
                active_sl := sl
                active_tp1 := close + ((close - sl) * tp1_rr)
                active_tp2 := close + ((close - sl) * tp2_rr)
                
                strategy.entry("News Long", strategy.long, qty=qty)
                trend_dir := 0 

    // SHORT SETUP
    if trend_dir == -1
        if close > ema
            pullback_valid := true
        
        if pullback_valid and close < ema
            float sl = pivot_extreme + pip_buffer
            float risk_pips = (sl - close) / pip_size
            
            if risk_pips <= max_sl_pips and risk_pips > 0
                float risk_usd = strategy.equity * (risk_pct / 100)
                float qty = risk_usd / (sl - close)
                
                active_entry := close
                active_sl := sl
                active_tp1 := close - ((sl - close) * tp1_rr)
                active_tp2 := close - ((sl - close) * tp2_rr)
                
                strategy.entry("News Short", strategy.short, qty=qty)
                trend_dir := 0

// =========================================================================
// TRADE MANAGEMENT (SCALING & BREAKEVEN)
// =========================================================================
if strategy.position_size > 0
    if high >= active_tp1
        tp1_hit := true
    float dynamic_sl = (tp1_hit and auto_be) ? active_entry : active_sl
    strategy.exit("TP1", "News Long", qty_percent=tp1_qty, limit=active_tp1, stop=dynamic_sl)
    strategy.exit("TP2", "News Long", limit=active_tp2, stop=dynamic_sl)

if strategy.position_size < 0
    if low <= active_tp1
        tp1_hit := true
    float dynamic_sl = (tp1_hit and auto_be) ? active_entry : active_sl
    strategy.exit("TP1", "News Short", qty_percent=tp1_qty, limit=active_tp1, stop=dynamic_sl)
    strategy.exit("TP2", "News Short", limit=active_tp2, stop=dynamic_sl)

// =========================================================================
// VISUALS & HUD UPDATES
// =========================================================================
// Draw dynamic SL/TP lines
plot(strategy.position_size != 0 ? (tp1_hit and auto_be ? active_entry : active_sl) : na, color=color.new(color.red, 20), style=plot.style_linebr, linewidth=2, title="Dynamic SL")
plot(strategy.position_size != 0 and not tp1_hit ? active_tp1 : na, color=color.new(color.orange, 20), style=plot.style_linebr, linewidth=1, title="TP1")
plot(strategy.position_size != 0 ? active_tp2 : na, color=color.new(color.green, 20), style=plot.style_linebr, linewidth=2, title="TP2 Runner")

// Update HUD Dashboard
string hud_bias = in_news ? "Awaiting News..." : (trend_dir == 1 ? "Bullish Pullback" : (trend_dir == -1 ? "Bearish Pullback" : "Flat / No Setup"))
color bias_col = trend_dir == 1 ? color.green : (trend_dir == -1 ? color.red : color.gray)

string hud_trade = strategy.position_size > 0 ? "LONG ACTIVE" : (strategy.position_size < 0 ? "SHORT ACTIVE" : "WAITING")
color trade_col = strategy.position_size > 0 ? color.green : (strategy.position_size < 0 ? color.red : color.gray)

if barstate.islast
    table.cell(hud, 0, 1, "Trend Bias", bgcolor=color.new(color.black, 40), text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 1, hud_bias, bgcolor=color.new(bias_col, 80), text_color=color.white, text_size=size.small)
    
    table.cell(hud, 0, 2, "Position", bgcolor=color.new(color.black, 40), text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 2, hud_trade, bgcolor=color.new(trade_col, 80), text_color=color.white, text_size=size.small)
    
    table.cell(hud, 0, 3, "BE Activated", bgcolor=color.new(color.black, 40), text_color=color.white, text_size=size.small)
    table.cell(hud, 1, 3, tp1_hit ? "YES (Risk Free)" : "NO", bgcolor=color.new(tp1_hit ? color.green : color.gray, 80), text_color=color.white, text_size=size.small)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

Porting this specific strategy from TradingView to MetaTrader requires a slight shift in how we handle the time series. Pine Script natively evaluates on bar close, meaning an EMA cross is only confirmed when the candle finishes. In MQL, OnTick() fires continuously.

To prevent tick-level whipsaws around the EMA during the pullback phase, both versions below evaluate the EMA cross on Shift 1 (completed bars) while continuously updating the pivotExtreme (Stop Loss level) on Shift 0 (live ticks). This mirrors the original Pine logic exactly while respecting MetaTrader's microstructure.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

Here are the translation templates for both environments.

MQL4 Expert Advisor

This MQL4 version uses a simple state machine to track whether the market is currently inside the "hands-off" news window.

Code: Select all

//+------------------------------------------------------------------+
//|                                        NewsPullbackScalperV1.mq4 |
//+------------------------------------------------------------------+
#property copyright "Forex Scalping Community"
#property link      ""
#property version   "1.00"
#property strict

input int    NewsStartHour   = 14;
input int    NewsStartMinute = 0;
input int    NewsEndHour     = 14;
input int    NewsEndMinute   = 15;
input int    EmaLength       = 9;
input double RrRatio         = 2.0;
input double LotSize         = 0.1;

// State Variables
double initOpen = 0;
int    trendDir = 0; 
bool   pullbackValid = false;
bool   tradeTaken = false;
double pivotExtreme = 0;
bool   wasInNews = false;

datetime lastBarTime = 0;

bool IsInNewsWindow(datetime time) {
    MqlDateTime dt;
    TimeToStruct(time, dt);
    int currentMins = dt.hour * 60 + dt.min;
    int startMins = NewsStartHour * 60 + NewsStartMinute;
    int endMins = NewsEndHour * 60 + NewsEndMinute;
    
    if(startMins < endMins) return (currentMins >= startMins && currentMins < endMins);
    return (currentMins >= startMins || currentMins < endMins); // Handles over-midnight
}

void OnTick() {
    bool isNewBar = false;
    if(Time[0] != lastBarTime) {
        isNewBar = true;
        lastBarTime = Time[0];
    }

    bool inNews = IsInNewsWindow(TimeCurrent());

    // 1. Session Start (Stay Flat)
    if(inNews && !wasInNews) {
        initOpen = Open[0];
        trendDir = 0;
        pullbackValid = false;
        tradeTaken = false;
        pivotExtreme = 0;
    }

    // 2. Session End (Determine Initial Reaction)
    if(!inNews && wasInNews) {
        double initClose = Close[1];
        if(initClose > initOpen) trendDir = 1;
        else if(initClose < initOpen) trendDir = -1;
        else trendDir = 0;
        
        pivotExtreme = (trendDir == 1) ? Low[1] : High[1];
    }
    
    wasInNews = inNews;

    // 3. Pullback & Entry Logic
    if(!inNews && trendDir != 0 && !tradeTaken) {
        
        // Dynamically track the absolute extreme for our stop loss
        if(trendDir == 1 && Low[0] < pivotExtreme) pivotExtreme = Low[0];
        if(trendDir == -1 && High[0] > pivotExtreme) pivotExtreme = High[0];

        // Evaluate triggers only on confirmed bar closes to match Pine Script
        if(isNewBar) {
            double ema1 = iMA(Symbol(), 0, EmaLength, 0, MODE_EMA, PRICE_CLOSE, 1);
            double close1 = Close[1];

            // LONG SETUP
            if(trendDir == 1) {
                if(close1 < ema1) pullbackValid = true;
                
                if(pullbackValid && close1 > ema1) {
                    double sl = NormalizeDouble(pivotExtreme, Digits);
                    double risk = Ask - sl;
                    double tp = NormalizeDouble(Ask + (risk * RrRatio), Digits);
                    
                    int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl, tp, "News Scalp Long", 0, 0, clrGreen);
                    if(ticket > 0) {
                        tradeTaken = true;
                        pullbackValid = false;
                    }
                }
            }
            
            // SHORT SETUP
            if(trendDir == -1) {
                if(close1 > ema1) pullbackValid = true;
                
                if(pullbackValid && close1 < ema1) {
                    double sl = NormalizeDouble(pivotExtreme, Digits);
                    double risk = sl - Bid;
                    double tp = NormalizeDouble(Bid - (risk * RrRatio), Digits);
                    
                    int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, sl, tp, "News Scalp Short", 0, 0, clrRed);
                    if(ticket > 0) {
                        tradeTaken = true;
                        pullbackValid = false;
                    }
                }
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

MQL5 Expert Advisor

In MQL5, we use CTrade for execution and must initialize the EMA handle in OnInit(). Array handling requires copying the buffers, making the logic slightly more verbose but execution significantly faster.

Code: Select all

//+------------------------------------------------------------------+
//|                                        NewsPullbackScalperV1.mq5 |
//+------------------------------------------------------------------+
#property copyright "Forex Scalping Community"
#property link      ""
#property version   "1.00"

#include <Trade\Trade.mqh>

input int    NewsStartHour   = 14;
input int    NewsStartMinute = 0;
input int    NewsEndHour     = 14;
input int    NewsEndMinute   = 15;
input int    EmaLength       = 9;
input double RrRatio         = 2.0;
input double LotSize         = 0.1;

CTrade trade;
int emaHandle;

double initOpen = 0;
int    trendDir = 0;
bool   pullbackValid = false;
bool   tradeTaken = false;
double pivotExtreme = 0;
bool   wasInNews = false;
datetime lastBarTime = 0;

int OnInit() {
    emaHandle = iMA(_Symbol, PERIOD_CURRENT, EmaLength, 0, MODE_EMA, PRICE_CLOSE);
    if(emaHandle == INVALID_HANDLE) return INIT_FAILED;
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
    IndicatorRelease(emaHandle);
}

bool IsInNewsWindow(datetime time) {
    MqlDateTime dt;
    TimeToStruct(time, dt);
    int currentMins = dt.hour * 60 + dt.min;
    int startMins = NewsStartHour * 60 + NewsStartMinute;
    int endMins = NewsEndHour * 60 + NewsEndMinute;
    
    if(startMins < endMins) return (currentMins >= startMins && currentMins < endMins);
    return (currentMins >= startMins || currentMins < endMins);
}

void OnTick() {
    datetime timeArray[];
    ArraySetAsSeries(timeArray, true);
    CopyTime(_Symbol, PERIOD_CURRENT, 0, 1, timeArray);
    
    bool isNewBar = false;
    if(timeArray[0] != lastBarTime) {
        isNewBar = true;
        lastBarTime = timeArray[0];
    }

    bool inNews = IsInNewsWindow(TimeCurrent());

    double openPrices[], closePrices[], lowPrices[], highPrices[];
    ArraySetAsSeries(openPrices, true);
    ArraySetAsSeries(closePrices, true);
    ArraySetAsSeries(lowPrices, true);
    ArraySetAsSeries(highPrices, true);
    CopyOpen(_Symbol, PERIOD_CURRENT, 0, 2, openPrices);
    CopyClose(_Symbol, PERIOD_CURRENT, 0, 2, closePrices);
    CopyLow(_Symbol, PERIOD_CURRENT, 0, 2, lowPrices);
    CopyHigh(_Symbol, PERIOD_CURRENT, 0, 2, highPrices);

    // 1. Session Start
    if(inNews && !wasInNews) {
        initOpen = openPrices[0];
        trendDir = 0;
        pullbackValid = false;
        tradeTaken = false;
        pivotExtreme = 0;
    }

    // 2. Session End
    if(!inNews && wasInNews) {
        double initClose = closePrices[1];
        if(initClose > initOpen) trendDir = 1;
        else if(initClose < initOpen) trendDir = -1;
        else trendDir = 0;
        
        pivotExtreme = (trendDir == 1) ? lowPrices[1] : highPrices[1];
    }
    
    wasInNews = inNews;

    // 3. Pullback & Entry
    if(!inNews && trendDir != 0 && !tradeTaken) {
        
        if(trendDir == 1 && lowPrices[0] < pivotExtreme) pivotExtreme = lowPrices[0];
        if(trendDir == -1 && highPrices[0] > pivotExtreme) pivotExtreme = highPrices[0];

        if(isNewBar) {
            double emaBuffer[];
            ArraySetAsSeries(emaBuffer, true);
            if(CopyBuffer(emaHandle, 0, 1, 1, emaBuffer) <= 0) return;
            
            double ema1 = emaBuffer[0];
            double close1 = closePrices[1];

            double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
            double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

            // LONG SETUP
            if(trendDir == 1) {
                if(close1 < ema1) pullbackValid = true;
                
                if(pullbackValid && close1 > ema1) {
                    double sl = NormalizeDouble(pivotExtreme, _Digits);
                    double risk = ask - sl;
                    double tp = NormalizeDouble(ask + (risk * RrRatio), _Digits);
                    
                    if(trade.Buy(LotSize, _Symbol, ask, sl, tp, "News Scalp Long")) {
                        tradeTaken = true;
                        pullbackValid = false;
                    }
                }
            }
            
            // SHORT SETUP
            if(trendDir == -1) {
                if(close1 > ema1) pullbackValid = true;
                
                if(pullbackValid && close1 < ema1) {
                    double sl = NormalizeDouble(pivotExtreme, _Digits);
                    double risk = sl - bid;
                    double tp = NormalizeDouble(bid - (risk * RrRatio), _Digits);
                    
                    if(trade.Sell(LotSize, _Symbol, bid, sl, tp, "News Scalp Short")) {
                        tradeTaken = true;
                        pullbackValid = false;
                    }
                }
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

The primary logic behaves identically across both platforms. To integrate the "Pro" features later, you'd swap the fixed LotSize input for an account balance percentage calculation (AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0), dividing the output by the point value of the stop-loss distance.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

Porting the "Pro" version introduces three major institutional mechanics to your MetaTrader environments: dynamic fractional lot sizing based on real-time tick value, a maximum bar expiration filter for stale setups, and the visual mapping of the news chaos box directly onto the chart.

Because you are scalping spot forex pairs, the lot sizing algorithm specifically queries MODE_TICKVALUE (MQL4) and SYMBOL_TRADE_TICK_VALUE (MQL5) to automatically handle cross-currency conversions against your account deposit currency.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3921
Joined: Mon Jul 20, 2026 1:28 pm

Re: Post-central bank speech review template for scalpers

Post by PTScalper »

Here are the translation templates.

MQL4: Pro News Pullback Scalper

Code: Select all

//+------------------------------------------------------------------+
//|                                     ProNewsPullbackScalper.mq4   |
//+------------------------------------------------------------------+
#property strict

input string   Grp1            = "--- Time & Reaction ---";
input int      NewsStartHour   = 14;
input int      NewsStartMinute = 0;
input int      NewsEndHour     = 14;
input int      NewsEndMinute   = 15;

input string   Grp2            = "--- Trigger & Filters ---";
input int      EmaLength       = 9;
input int      MaxWaitBars     = 30;   // Cancel setup if pullback takes too long
input double   MaxSlPips       = 30.0; // Volatility filter

input string   Grp3            = "--- Risk Management ---";
input double   RiskPercent     = 1.0;
input double   RrRatio         = 2.0;

// State Variables
double   initOpen = 0, initHigh = 0, initLow = 0;
int      trendDir = 0, barsSinceNews = 0; 
bool     pullbackValid = false, tradeTaken = false, wasInNews = false;
double   pivotExtreme = 0;
datetime lastBarTime = 0, sessionStartTime = 0;
string   currentBox = "";
double   pipSize = 0;

int OnInit() {
    pipSize = (Digits == 3 || Digits == 5) ? Point * 10 : Point;
    wasInNews = IsInNewsWindow(TimeCurrent());
    return INIT_SUCCEEDED;
}

bool IsInNewsWindow(datetime time) {
    MqlDateTime dt; TimeToStruct(time, dt);
    int currentMins = dt.hour * 60 + dt.min;
    int startMins = NewsStartHour * 60 + NewsStartMinute;
    int endMins = NewsEndHour * 60 + NewsEndMinute;
    if(startMins < endMins) return (currentMins >= startMins && currentMins < endMins);
    return (currentMins >= startMins || currentMins < endMins);
}

double CalculateLotSize(double slPrice, int type) {
    double entryPrice = (type == OP_BUY) ? Ask : Bid;
    double riskUsd = AccountBalance() * (RiskPercent / 100.0);
    double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
    double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
    
    double ticksAtRisk = MathAbs(entryPrice - slPrice) / tickSize;
    if(ticksAtRisk <= 0) return 0;
    
    double lots = riskUsd / (ticksAtRisk * tickValue);
    double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
    lots = MathFloor(lots / lotStep) * lotStep;
    
    if(lots < MarketInfo(Symbol(), MODE_MINLOT)) return 0;
    if(lots > MarketInfo(Symbol(), MODE_MAXLOT)) lots = MarketInfo(Symbol(), MODE_MAXLOT);
    return lots;
}

void OnTick() {
    bool isNewBar = false;
    if(Time[0] != lastBarTime) {
        isNewBar = true;
        lastBarTime = Time[0];
    }

    bool inNews = IsInNewsWindow(TimeCurrent());

    // 1. Session Start (Draw Box)
    if(inNews && !wasInNews) {
        initOpen = Open[0]; initHigh = High[0]; initLow = Low[0];
        trendDir = 0; pullbackValid = false; tradeTaken = false;
        pivotExtreme = 0; barsSinceNews = 0; sessionStartTime = Time[0];
        
        currentBox = "NewsBox_" + TimeToStr(TimeCurrent());
        ObjectCreate(0, currentBox, OBJ_RECTANGLE, 0, sessionStartTime, initHigh, Time[0], initLow);
        ObjectSetInteger(0, currentBox, OBJPROP_COLOR, clrDarkGray);
        ObjectSetInteger(0, currentBox, OBJPROP_BACK, true);
    }

    // 2. Update Box During Session
    if(inNews) {
        initHigh = MathMax(initHigh, High[0]);
        initLow = MathMin(initLow, Low[0]);
        ObjectMove(0, currentBox, 0, sessionStartTime, initHigh);
        ObjectMove(0, currentBox, 1, Time[0], initLow);
    }

    // 3. Session End (Set Trend & Color Box)
    if(!inNews && wasInNews) {
        double initClose = Close[1];
        if(initClose > initOpen) { trendDir = 1; ObjectSetInteger(0, currentBox, OBJPROP_COLOR, clrTeal); }
        else if(initClose < initOpen) { trendDir = -1; ObjectSetInteger(0, currentBox, OBJPROP_COLOR, clrMaroon); }
        else trendDir = 0;
        
        pivotExtreme = (trendDir == 1) ? Low[1] : High[1];
    }
    
    wasInNews = inNews;

    // 4. Pullback Logic & Filters
    if(!inNews && trendDir != 0 && !tradeTaken) {
        if(isNewBar) barsSinceNews++;
        
        // Expiration Filter
        if(barsSinceNews > MaxWaitBars) {
            trendDir = 0; pullbackValid = false; return;
        }
        
        // Dynamic Stop Loss Pivot
        if(trendDir == 1 && Low[0] < pivotExtreme) pivotExtreme = Low[0];
        if(trendDir == -1 && High[0] > pivotExtreme) pivotExtreme = High[0];

        if(isNewBar) {
            double ema1 = iMA(Symbol(), 0, EmaLength, 0, MODE_EMA, PRICE_CLOSE, 1);
            double close1 = Close[1];

            // LONG SETUP
            if(trendDir == 1) {
                if(close1 < ema1) pullbackValid = true;
                
                if(pullbackValid && close1 > ema1) {
                    double sl = NormalizeDouble(pivotExtreme, Digits);
                    double slDistance = MathAbs(Ask - sl) / pipSize;
                    
                    if(slDistance > 0 && slDistance <= MaxSlPips) {
                        double lots = CalculateLotSize(sl, OP_BUY);
                        if(lots > 0) {
                            double tp = NormalizeDouble(Ask + ((Ask - sl) * RrRatio), Digits);
                            if(OrderSend(Symbol(), OP_BUY, lots, Ask, 3, sl, tp, "Pro Scalp", 0, 0, clrGreen) > 0) {
                                tradeTaken = true; pullbackValid = false;
                            }
                        }
                    }
                }
            }
            
            // SHORT SETUP
            if(trendDir == -1) {
                if(close1 > ema1) pullbackValid = true;
                
                if(pullbackValid && close1 < ema1) {
                    double sl = NormalizeDouble(pivotExtreme, Digits);
                    double slDistance = MathAbs(sl - Bid) / pipSize;
                    
                    if(slDistance > 0 && slDistance <= MaxSlPips) {
                        double lots = CalculateLotSize(sl, OP_SELL);
                        if(lots > 0) {
                            double tp = NormalizeDouble(Bid - ((sl - Bid) * RrRatio), Digits);
                            if(OrderSend(Symbol(), OP_SELL, lots, Bid, 3, sl, tp, "Pro Scalp", 0, 0, clrRed) > 0) {
                                tradeTaken = true; pullbackValid = false;
                            }
                        }
                    }
                }
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply