Advertisement IC Markets

How strict is your flat-before-Tier1 timer really?

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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).
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: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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);
      }
   }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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$.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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; }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
LondonScalper
Posts: 701
Joined: Sat Sep 05, 2026 7:54 am

Re: How strict is your flat-before-Tier1 timer really?

Post 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.
PropScalpDesk
Posts: 273
Joined: Sat Sep 19, 2026 7:50 pm

Re: How strict is your flat-before-Tier1 timer really?

Post 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.
Post Reply