Page 2 of 3
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:52 pm
by PTScalper
Moving from a Pine Script alert front-end to native MetaTrader execution shifts this from an advisory warning to an authoritative execution kill-switch.
In MetaTrader (MT4/MT5), a production-grade pre-news liquidation EA must solve three critical infrastructure problems that amateur scripts ignore:
1.) The Pre-News Quote Freeze (OnTick Trap): Right before high-impact events like CPI or NFP, liquidity providers routinely withdraw depth-of-market quotes. Tick frequency drops off a cliff. If your EA relies solely on OnTick(), it might not fire at $T-10$ if no new quote arrives. Execution must be driven by a hardware-clock timer loop (OnTimer()) running at 1 Hz.
2.) Reverse Index Iteration: When an order is closed or canceled, MetaTrader’s internal order pool shifts instantly. Iterating forward (0 to Total-1) causes tickets to be skipped. You must iterate backwards (Total-1 down to 0).
3.) Architecture Divergence (MT4 vs. MT5): In MT4, pending orders and open positions sit in the same OrdersTotal() stack. In MT5, active positions (PositionsTotal()) and pending orders (OrdersTotal()) reside in separate queues and require distinct API calls (PositionClose vs. OrderDelete).
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:53 pm
by PTScalper
Below are production-ready Expert Advisors for both environments, architected around a 1-second system clock, state tracking, and atomic liquidation.
MetaTrader 5 (MQL5)
Save this as Tier1NewsLockdown.mq5 in MQL5/Experts/. It utilizes the native CTrade class for asynchronous or immediate fills and handles position/order separation cleanly.
Code: Select all
//+------------------------------------------------------------------+
//| Tier1NewsLockdown.mq5 |
//| Automated Risk Execution Engine |
//+------------------------------------------------------------------+
#property copyright "Algo Risk Core"
#property link ""
#property version "2.00"
#property strict
#include <Trade\Trade.mqh>
//--- Input Parameters
input group "=== Event Schedule (Server Time) ==="
input int InpEventHour = 15; // Event Hour (0-23)
input int InpEventMinute = 30; // Event Minute (0-59)
input group "=== Risk Controls ==="
input int InpPreKillMin = 10; // Pre-News Kill Window (Minutes)
input int InpPostCoolMin = 5; // Post-News Cooldown (Minutes)
input bool InpAllSymbols = true; // Flatten Account-Wide (false = chart symbol only)
input ulong InpMagicFilter = 0; // Magic Number Filter (0 = Purge ALL)
input ulong InpDeviation = 30; // Max Slippage Points
//--- State Machine Enumeration
enum ENUM_ENGINE_STATE
{
STATE_IDLE,
STATE_WARNING,
STATE_LOCKED_PRE,
STATE_LOCKED_POST
};
CTrade trade;
ENUM_ENGINE_STATE currentState = STATE_IDLE;
bool liquidationExecuted = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetDeviationInPoints(InpDeviation);
trade.SetAsyncMode(false); // Synchronous execution to guarantee status
// Run 1-second timer independent of incoming market ticks
EventSetTimer(1);
Print("[INIT] Tier-1 Risk Lock Engine active. Timer running at 1Hz.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
Comment("");
}
//+------------------------------------------------------------------+
//| Timer event: Hardware clock execution engine |
//+------------------------------------------------------------------+
void OnTimer()
{
datetime currentServerTime = TimeCurrent();
MqlDateTime dt;
TimeToStruct(currentServerTime, dt);
// Construct event timestamp for current trading day
dt.hour = InpEventHour;
dt.min = InpEventMinute;
dt.sec = 0;
datetime eventTargetTime = StructToTime(dt);
int deltaSeconds = (int)(eventTargetTime - currentServerTime);
int preKillSec = InpPreKillMin * 60;
int postCoolSec = InpPostCoolMin * 60;
// Update State Machine
if(deltaSeconds > preKillSec)
{
currentState = (deltaSeconds <= preKillSec + 900) ? STATE_WARNING : STATE_IDLE;
liquidationExecuted = false; // Reset trigger for next session
}
else if(deltaSeconds <= preKillSec && deltaSeconds > 0)
{
currentState = STATE_LOCKED_PRE;
if(!liquidationExecuted)
{
ExecuteAccountFlush();
liquidationExecuted = true;
}
}
else if(deltaSeconds <= 0 && deltaSeconds >= -postCoolSec)
{
currentState = STATE_LOCKED_POST;
}
else
{
currentState = STATE_IDLE;
}
RenderDashboard(deltaSeconds);
}
//+------------------------------------------------------------------+
//| Atomic Liquidation: Purges Positions & Pending Limits |
//+------------------------------------------------------------------+
void ExecuteAccountFlush()
{
Print("[RISK TRIGGER] Mandate active. Liquidating all active risk...");
// 1. Close Active Positions (Reverse iteration)
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(!InpAllSymbols && PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
if(InpMagicFilter != 0 && PositionGetInteger(POSITION_MAGIC) != InpMagicFilter)
continue;
trade.PositionClose(ticket);
PrintFormat("[CLOSED] Position #%I64u | Symbol: %s", ticket, PositionGetString(POSITION_SYMBOL));
}
}
// 2. Purge Pending Orders (Reverse iteration)
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket > 0 && OrderSelect(ticket))
{
if(!InpAllSymbols && OrderGetString(ORDER_SYMBOL) != _Symbol)
continue;
if(InpMagicFilter != 0 && OrderGetInteger(ORDER_MAGIC) != InpMagicFilter)
continue;
trade.OrderDelete(ticket);
PrintFormat("[CANCELLED] Limit/Stop Order #%I64u", ticket);
}
}
}
//+------------------------------------------------------------------+
//| Heads-Up Display Telemetry |
//+------------------------------------------------------------------+
void RenderDashboard(int deltaSec)
{
string statusStr = "";
switch(currentState)
{
case STATE_IDLE: statusStr = "ARMED / NORMAL MONITORING"; break;
case STATE_WARNING: statusStr = StringFormat("WARNING: T-%d SECONDS", deltaSec); break;
case STATE_LOCKED_PRE: statusStr = "EXECUTION LOCK: PRE-PRINT FLATTENING"; break;
case STATE_LOCKED_POST: statusStr = "EXECUTION LOCK: SPREAD COOLDOWN"; break;
}
string hud = "====================================\n" +
" TIER-1 RISK CONTROL NODE (MT5) \n" +
"====================================\n" +
" Server Time : " + TimeToString(TimeCurrent(), TIME_SECONDS) + "\n" +
" Target Print: " + StringFormat("%02d:%02d:00", InpEventHour, InpEventMinute) + "\n" +
" Engine State: " + statusStr + "\n" +
" Open Delta : " + (currentState >= STATE_LOCKED_PRE ? "ZERO TOLERANCE" : "PERMITTED") + "\n" +
"====================================";
Comment(hud);
}
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:53 pm
by PTScalper
MetaTrader 4 (MQL4)
Save this as Tier1NewsLockdown.mq4 in MQL4/Experts/. In MT4, order closure requires fetching bid/ask quotes individually and processing tickets in descending index order.
Code: Select all
//+------------------------------------------------------------------+
//| Tier1NewsLockdown.mq4 |
//| Automated Risk Execution Engine |
//+------------------------------------------------------------------+
#property copyright "Algo Risk Core"
#property link ""
#property version "2.00"
#property strict
//--- Input Parameters
input string Group1 = "=== Event Schedule (Server Time) ===";
input int InpEventHour = 15; // Event Hour (0-23)
input int InpEventMinute = 30; // Event Minute (0-59)
input string Group2 = "=== Risk Controls ===";
input int InpPreKillMin = 10; // Pre-News Kill Window (Minutes)
input int InpPostCoolMin = 5; // Post-News Cooldown (Minutes)
input bool InpAllSymbols = true; // Flatten Entire Account
input int InpMagicFilter = 0; // Magic Number (0 = Purge All)
input int InpSlippage = 30; // Max Slippage (Points)
bool liquidationExecuted = false;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
EventSetTimer(1); // 1Hz Hardware Timer
Print("[INIT] MT4 Risk Core Initialized. Hardware timer running.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
Comment("");
}
//+------------------------------------------------------------------+
//| Timer event function |
//+------------------------------------------------------------------+
void OnTimer()
{
datetime currentServerTime = TimeCurrent();
// Compute today's news timestamp
string dateStr = TimeToStr(currentServerTime, TIME_DATE);
datetime eventTargetTime = StrToTime(dateStr + " " + IntegerToString(InpEventHour) + ":" + IntegerToString(InpEventMinute) + ":00");
int deltaSeconds = (int)(eventTargetTime - currentServerTime);
int preKillSec = InpPreKillMin * 60;
int postCoolSec = InpPostCoolMin * 60;
string stateText = "CLEAR / NORMAL";
if(deltaSeconds > preKillSec)
{
liquidationExecuted = false;
if(deltaSeconds <= preKillSec + 900)
stateText = "WARNING: APPROACHING KILL WINDOW";
}
else if(deltaSeconds <= preKillSec && deltaSeconds > 0)
{
stateText = "MANDATORY LIQUIDATION ACTIVE";
if(!liquidationExecuted)
{
ExecuteAccountFlush();
liquidationExecuted = true;
}
}
else if(deltaSeconds <= 0 && deltaSeconds >= -postCoolSec)
{
stateText = "POST-EVENT LIQUIDITY RECOVERY";
}
// Visual Telemetry
string hud = "====================================\n" +
" TIER-1 RISK CONTROL NODE (MT4) \n" +
"====================================\n" +
" Server Time : " + TimeToStr(currentServerTime, TIME_SECONDS) + "\n" +
" Target Print: " + StringFormat("%02d:%02d:00", InpEventHour, InpEventMinute) + "\n" +
" Delta Time : " + IntegerToString(deltaSeconds) + "s\n" +
" Status : " + stateText + "\n" +
"====================================";
Comment(hud);
}
//+------------------------------------------------------------------+
//| Atomic Liquidation: MT4 Reverse Pool Flush |
//+------------------------------------------------------------------+
void ExecuteAccountFlush()
{
Print("[RISK] Mandatory liquidation sequence initiated.");
// Loop backward through the combined active/pending pool
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
continue;
if(!InpAllSymbols && OrderSymbol() != _Symbol)
continue;
if(InpMagicFilter != 0 && OrderMagicNumber() != InpMagicFilter)
continue;
int type = OrderType();
// Close Market Orders
if(type == OP_BUY)
{
RefreshRates();
double closePrice = MarketInfo(OrderSymbol(), MODE_BID);
OrderClose(OrderTicket(), OrderLots(), closePrice, InpSlippage, clrRed);
}
else if(type == OP_SELL)
{
RefreshRates();
double closePrice = MarketInfo(OrderSymbol(), MODE_ASK);
OrderClose(OrderTicket(), OrderLots(), closePrice, InpSlippage, clrRed);
}
// Delete Pending Orders (Limits / Stops)
else if(type >= OP_BUYLIMIT && type <= OP_SELLSTOP)
{
OrderDelete(OrderTicket(), clrOrange);
}
}
}
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:54 pm
by PTScalper
Setup Best PracticesServer Time Alignment:
Set InpEventHour and InpEventMinute directly to your broker's terminal clock, not your local timezone. This eliminates Daylight Saving Time offsets.
VPS Deployment: Run this on your VPS instance. If your desktop internet connection experiences jitter right before the print, the terminal on the VPS still drops the axe at exactly $T-10$.
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:55 pm
by PTScalper
Moving to cTrader (cAlgo) allows us to abandon the archaic reverse-index loops of MQL and leverage a modern C# .NET architecture.
Because cTrader exposes the book as enumerable collections, we can use LINQ for precise filtering. More importantly, we can utilize asynchronous execution methods (ClosePositionAsync, CancelPendingOrderAsync) to fire all liquidation commands to the broker concurrently. This guarantees that a slow server response on ticket #1 doesn't delay the cancellation of ticket #2 during the critical $T-10$ pre-news quote freeze.
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:55 pm
by PTScalper
Here is the production-ready C# cBot. It runs on a 1-second hardware timer and uses UTC time mapping to completely eliminate broker server-time offsets.
Code: Select all
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Tier1RiskNode : Robot
{
// --- Event Schedule ---
[Parameter("Event Hour (UTC)", Group = "Schedule", DefaultValue = 13)]
public int EventHour { get; set; }
[Parameter("Event Minute (UTC)", Group = "Schedule", DefaultValue = 30)]
public int EventMinute { get; set; }
// --- Risk Controls ---
[Parameter("Pre-Kill Window (Min)", Group = "Risk Controls", DefaultValue = 10)]
public int PreKillMin { get; set; }
[Parameter("Post-Cooldown (Min)", Group = "Risk Controls", DefaultValue = 5)]
public int PostCoolMin { get; set; }
[Parameter("Account Wide Flush", Group = "Risk Controls", DefaultValue = true)]
public bool AccountWide { get; set; }
private enum EngineState
{
Idle,
Warning,
LockedPrePrint,
LockedCooldown
}
private EngineState _currentState = EngineState.Idle;
private bool _liquidationExecuted = false;
protected override void OnStart()
{
// Initialize 1Hz hardware clock independent of tick flow
Timer.Start(TimeSpan.FromSeconds(1));
Print("[INIT] C# Risk Node Active. Monitoring for Tier-1 UTC Event.");
}
protected override void OnTimer()
{
DateTime currentUtc = Server.TimeInUtc;
DateTime targetUtc = new DateTime(currentUtc.Year, currentUtc.Month, currentUtc.Day, EventHour, EventMinute, 0, DateTimeKind.Utc);
double deltaSeconds = (targetUtc - currentUtc).TotalSeconds;
int preKillSec = PreKillMin * 60;
int postCoolSec = PostCoolMin * 60;
// State Machine Evaluation
if (deltaSeconds > preKillSec)
{
_currentState = (deltaSeconds <= preKillSec + 900) ? EngineState.Warning : EngineState.Idle;
_liquidationExecuted = false;
}
else if (deltaSeconds <= preKillSec && deltaSeconds > 0)
{
_currentState = EngineState.LockedPrePrint;
if (!_liquidationExecuted)
{
ExecuteAtomicFlush();
_liquidationExecuted = true;
}
}
else if (deltaSeconds <= 0 && deltaSeconds >= -postCoolSec)
{
_currentState = EngineState.LockedCooldown;
}
else
{
_currentState = EngineState.Idle;
}
RenderTelemetry(deltaSeconds);
}
private void ExecuteAtomicFlush()
{
Print("[RISK TRIGGER] Commencing asynchronous book purge.");
// 1. Filter and purge active positions
var positionsToClose = AccountWide ? Positions : Positions.Where(p => p.SymbolName == SymbolName);
foreach (var position in positionsToClose)
{
// Fire and forget asynchronous closure for concurrent server processing
ClosePositionAsync(position, (result) =>
{
if (result.IsSuccessful)
Print($"[CLOSED] Position {result.Position.Id} | {result.Position.SymbolName}");
else
Print($"[ERROR] Failed to close {position.Id}: {result.Error}");
});
}
// 2. Filter and purge pending limits/stops
var ordersToCancel = AccountWide ? PendingOrders : PendingOrders.Where(o => o.SymbolName == SymbolName);
foreach (var order in ordersToCancel)
{
CancelPendingOrderAsync(order, (result) =>
{
if (result.IsSuccessful)
Print($"[CANCELLED] Pending Order {result.PendingOrder.Id}");
else
Print($"[ERROR] Failed to cancel order {order.Id}: {result.Error}");
});
}
}
private void RenderTelemetry(double deltaSec)
{
string statusStr = _currentState switch
{
EngineState.Idle => "ARMED / MONITORING",
EngineState.Warning => $"WARNING: T-{(int)deltaSec} SECONDS",
EngineState.LockedPrePrint => "EXECUTION LOCK: PRE-PRINT PURGE",
EngineState.LockedCooldown => "EXECUTION LOCK: SPREAD COOLDOWN",
_ => "UNKNOWN"
};
Color statusColor = _currentState >= EngineState.LockedPrePrint ? Color.Red : (_currentState == EngineState.Warning ? Color.Orange : Color.LimeGreen);
string hud = $"================================\n" +
$" TIER-1 C# RISK NODE (UTC)\n" +
$"================================\n" +
$" Current UTC : {Server.TimeInUtc:HH:mm:ss}\n" +
$" Target Print: {EventHour:D2}:{EventMinute:D2}:00\n" +
$" Status : {statusStr}\n" +
$"================================";
Chart.DrawStaticText("RiskHUD", hud, VerticalAlignment.Top, HorizontalAlignment.Right, statusColor);
}
}
}
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:56 pm
by PTScalper
Here is the complete architectural implementation. This example uses a standard HttpClient to fetch data from a hypothetical economic calendar API, filters the payload for "High" impact events, and dynamically manages the execution lock.
Code: Select all
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using cAlgo.API;
namespace cAlgo.Robots
{
// FullAccess is required for System.Net.Http to open external sockets
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public class DynamicTier1RiskNode : Robot
{
[Parameter("API Endpoint", Group = "Data Feed", DefaultValue = "https://api.example.com/calendar?timezone=UTC")]
public string ApiEndpoint { get; set; }
[Parameter("Pre-Kill Window (Min)", Group = "Risk Controls", DefaultValue = 10)]
public int PreKillMin { get; set; }
[Parameter("Post-Cooldown (Min)", Group = "Risk Controls", DefaultValue = 5)]
public int PostCoolMin { get; set; }
private enum EngineState { Idle, Warning, LockedPrePrint, LockedCooldown }
private EngineState _currentState = EngineState.Idle;
private List<DateTime> _tier1EventTimes = new List<DateTime>();
private DateTime _lastFetchDate = DateTime.MinValue;
private static readonly HttpClient _httpClient = new HttpClient();
protected override void OnStart()
{
// Configure HTTP Client
_httpClient.Timeout = TimeSpan.FromSeconds(10);
_httpClient.DefaultRequestHeaders.Add("User-Agent", "cTrader-RiskNode/1.0");
// Perform initial synchronous fetch to ensure the bot is armed before trading
FetchCalendarDataSynchronously();
Timer.Start(TimeSpan.FromSeconds(1));
Print("[INIT] Dynamic Risk Node Active. Monitoring API for Tier-1 UTC Events.");
}
protected override void OnTimer()
{
DateTime currentUtc = Server.TimeInUtc;
// Check if the UTC day has rolled over to fetch fresh calendar data
if (currentUtc.Date > _lastFetchDate.Date)
{
// Fire and forget to avoid blocking the 1Hz execution timer
Task.Run(() => FetchCalendarDataAsync(currentUtc.Date));
}
EvaluateExecutionState(currentUtc);
}
private void EvaluateExecutionState(DateTime currentUtc)
{
// Find the closest event in the future or within the cooldown window
var relevantEvent = _tier1EventTimes
.Where(ev => (ev - currentUtc).TotalSeconds > -(PostCoolMin * 60))
.OrderBy(ev => ev)
.FirstOrDefault();
if (relevantEvent == default)
{
_currentState = EngineState.Idle;
RenderTelemetry(currentUtc, null, 0);
return;
}
double deltaSeconds = (relevantEvent - currentUtc).TotalSeconds;
int preKillSec = PreKillMin * 60;
int postCoolSec = PostCoolMin * 60;
if (deltaSeconds > preKillSec)
{
_currentState = (deltaSeconds <= preKillSec + 900) ? EngineState.Warning : EngineState.Idle;
}
else if (deltaSeconds <= preKillSec && deltaSeconds > 0)
{
if (_currentState != EngineState.LockedPrePrint)
{
_currentState = EngineState.LockedPrePrint;
ExecuteAtomicFlush();
}
}
else if (deltaSeconds <= 0 && deltaSeconds >= -postCoolSec)
{
_currentState = EngineState.LockedCooldown;
}
RenderTelemetry(currentUtc, relevantEvent, deltaSeconds);
}
private void FetchCalendarDataSynchronously()
{
try
{
// GetAwaiter().GetResult() safely blocks only during OnStart
var json = _httpClient.GetStringAsync(ApiEndpoint).GetAwaiter().GetResult();
ProcessApiPayload(json, Server.TimeInUtc.Date);
}
catch (Exception ex)
{
Print($"[API ERROR] Initial fetch failed: {ex.Message}");
}
}
private async Task FetchCalendarDataAsync(DateTime targetDate)
{
try
{
var json = await _httpClient.GetStringAsync(ApiEndpoint);
// Route UI-thread updates back to the main cBot thread
BeginInvokeOnMainThread(() => ProcessApiPayload(json, targetDate));
}
catch (Exception ex)
{
Print($"[API ERROR] Background daily fetch failed: {ex.Message}");
}
}
private void ProcessApiPayload(string json, DateTime fetchDate)
{
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var events = JsonSerializer.Deserialize<List<EconomicEvent>>(json, options);
// Filter for High Impact ("Tier-1") and exact currency matches if desired
_tier1EventTimes = events
.Where(e => e.Impact.Equals("High", StringComparison.OrdinalIgnoreCase))
.Select(e => e.UtcTime)
.ToList();
_lastFetchDate = fetchDate.Date;
Print($"[API SYNC] Successfully loaded {_tier1EventTimes.Count} Tier-1 events for {fetchDate:yyyy-MM-dd}");
}
private void ExecuteAtomicFlush()
{
Print("[RISK TRIGGER] Dynamic Event Detected. Commencing asynchronous book purge.");
foreach (var position in Positions.Where(p => p.SymbolName == SymbolName))
{
ClosePositionAsync(position);
}
foreach (var order in PendingOrders.Where(o => o.SymbolName == SymbolName))
{
CancelPendingOrderAsync(order);
}
}
private void RenderTelemetry(DateTime currentUtc, DateTime? nextEvent, double deltaSec)
{
string statusStr = _currentState switch
{
EngineState.Idle => "ARMED / MONITORING API",
EngineState.Warning => $"WARNING: T-{(int)deltaSec} SECONDS",
EngineState.LockedPrePrint => "EXECUTION LOCK: PRE-PRINT PURGE",
EngineState.LockedCooldown => "EXECUTION LOCK: SPREAD COOLDOWN",
_ => "UNKNOWN"
};
Color statusColor = _currentState >= EngineState.LockedPrePrint ? Color.Red :
(_currentState == EngineState.Warning ? Color.Orange : Color.LimeGreen);
string eventStr = nextEvent.HasValue ? nextEvent.Value.ToString("HH:mm:ss") : "NO TIER-1 REMAINING";
string hud = $"================================\n" +
$" TIER-1 DYNAMIC API NODE (UTC)\n" +
$"================================\n" +
$" Current UTC : {currentUtc:HH:mm:ss}\n" +
$" Next Event : {eventStr}\n" +
$" Status : {statusStr}\n" +
$"================================";
Chart.DrawStaticText("RiskHUD", hud, VerticalAlignment.Top, HorizontalAlignment.Right, statusColor);
}
// --- Data Contract for JSON Deserialization ---
public class EconomicEvent
{
public string Title { get; set; }
public string Impact { get; set; }
public string Currency { get; set; }
public DateTime UtcTime { get; set; }
}
}
}
Re: How strict is your flat-before-Tier1 timer really?
Posted: Fri Sep 18, 2026 7:56 pm
by PTScalper
By decoupling the HTTP request from the main timer thread using Task.Run() and pushing the result back into the execution flow via BeginInvokeOnMainThread(), the bot ensures that latency from the calendar API provider never causes the hardware loop to stutter or miss an exact $T-10$ liquidation timestamp.
Re: How strict is your flat-before-Tier1 timer really?
Posted: Sat Sep 19, 2026 5:45 pm
by LondonScalper
PTScalper wrote:For me, flat means absolutely zero open positions, zero hedges, and crucially, zero pending limit orders.
That is the definition I settled on after the audit, including the forgotten buy-limit. A green result with a leftover order still went in the book as a process fail. Hedges included. If it can be filled while the spread is wide, it is not flat, whatever the net delta looks like a minute before the number.
I am less keen on outsourcing that honesty to a chart colour. A red background at T-10 is a useful nag if you are already staring at that chart. It does not prove the ticket window is empty, and it cannot see a pending order on a second platform. The checks that changed my behaviour were dull: an alarm labelled FLAT NOW, a screenshot of positions and orders filed at T-10, and a weekly count of exceptions. More than one, and the timer moves earlier. A second script that restates the first does not make the rule stricter.
I would rather sit the print bored, with nothing working, than invent a reason the setup looked clean enough to hold.
Re: How strict is your flat-before-Tier1 timer really?
Posted: Thu Sep 24, 2026 1:51 am
by PropScalpDesk
PTScalper wrote:Here is the enterprise-grade implementation: Code: Select all //@version=5 indicator("Execution State Machine: Tier-1 Liquidity Node", overlay=true) // --- System Configuration --- grp_event = "Event Vector" eventHour = input.int(14, "Event Hour (0-23)", group=grp_event) eventMinute = input.int(30, "Event Minute (0-59)", group=grp_event) eventTz = input.
Flat-before-Tier1 is a hard timer on this desk, not a mood. Soft “maybe I stay” is how floating DD spends the day before the candle closes.
Prop news rules make the timer easier to defend.
What is your exact T-minus for XAU into NFP/CPI?
I also log refused tickets so flat time counts as work — otherwise the desk invents activity.
I would rather log a refused ticket than invent activity for the journal.
I write the walk-away before London so it is not negotiated mid-tape.
Topic note from my sheet for t=12524: keep risk unchanged until the sample says otherwise.