Page 1 of 2
Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Tue Sep 22, 2026 11:16 am
by LondonScalper
Seconds after an ECB decision on EURUSD are not a scalp window for me — they are a timer.
I flat before the release when I am in anything that can gap through my stop theatre. After the print I do not hunt the first spike. I start a stand-aside clock: long enough for the first absurd spread prints to die, short enough that I am not inventing a new career as a news trader.
Timer rules
- No new orders until the clock ends and live spread is back inside my London filter
- Working orders cancelled before the window — no "leave it, maybe"
- First post-timer trade is half size if I take anything at all
Curiosity is expensive around central banks. The timer is there so adrenaline does not get a vote.
How long is your ECB stand-aside on EURUSD, and is it fixed minutes or "until the tape looks normal"?
I keep the ECB timer in local London minutes on a sticky note for the day of the meeting. Platform clocks and calendar plugins have surprised me after DST changes. The timer only works if everyone in the room — including me — agrees what now means.
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:49 pm
by PTScalper
LondonScalper wrote: Tue Sep 22, 2026 11:16 am
Seconds after an ECB decision on EURUSD are not a scalp window for me — they are a timer.
I flat before the release when I am in anything that can gap through my stop theatre. After the print I do not hunt the first spike. I start a stand-aside clock: long enough for the first absurd spread prints to die, short enough that I am not inventing a new career as a news trader.
Timer rules
- No new orders until the clock ends and live spread is back inside my London filter
- Working orders cancelled before the window — no "leave it, maybe"
- First post-timer trade is half size if I take anything at all
Curiosity is expensive around central banks. The timer is there so adrenaline does not get a vote.
How long is your ECB stand-aside on EURUSD, and is it fixed minutes or "until the tape looks normal"?
I keep the ECB timer in local London minutes on a sticky note for the day of the meeting. Platform clocks and calendar plugins have surprised me after DST changes. The timer only works if everyone in the room — including me — agrees what now means.
Hi LondonScalper,
For the ECB, the stand-aside is necessarily a hybrid: a fixed minimum followed by a dynamic tape check, but with a twist because of the event's structure.
The ECB is a two-headed beast: the rate release at 13:15 London, followed by the press conference at 13:45. A fixed 15-minute timer after the 13:15 print is usually enough for the HFT algorithms to finish sweeping liquidity and for the spread to normalize. However, engaging right as the spread normalizes at 13:35 leaves you exposed to Lagarde walking to the podium 10 minutes later and dropping a hawkish/dovish bombshell that rips the tape apart again.
My rule: 15 minutes fixed after the initial release, extending into a strict "tape looks normal" spread check. If I miss a setup between 13:30 and 13:45, so be it. Once the presser starts at 13:45, the timer resets until the Q&A ends and the spread compresses back to baseline.
Here is the cTrader (cAlgo) risk manager cBot to enforce your specific rules. Because OS clocks and calendar plugins are notoriously unreliable around DST, this script relies strictly on Broker Server Time. You simply translate your London sticky-note time to your broker's server time once for the day and input it.
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:49 pm
by PTScalper
ECB Stand-Aside Risk Manager
This cBot doesn't take trades. It acts as an enforcer on your chart:
Pre-Event: Cancels all pending limit/stop orders for the pair X minutes before the event.
The Timer: Blocks the screen with a red warning during your fixed stand-aside window.
The Filter: Once the time expires, it continuously monitors the live spread, clearing you for your half-size entry only when the spread drops back below your threshold.
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:49 pm
by PTScalper
Ctrader script version 1.0
Code: Select all
using System;
using System.Linq;
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class ECBRiskManager : Robot
{
[Parameter("Event Time (Broker Server HH:mm)", DefaultValue = "15:15")]
public string EventTimeString { get; set; }
[Parameter("Cancel Working Orders Before (Mins)", DefaultValue = 5, MinValue = 1)]
public int PreEventMinutes { get; set; }
[Parameter("Stand-Aside Timer (Mins)", DefaultValue = 15, MinValue = 1)]
public int StandAsideMinutes { get; set; }
[Parameter("Max Allowed Spread (Pips)", DefaultValue = 0.8)]
public double MaxSpreadPips { get; set; }
private DateTime _eventTime;
private bool _ordersCancelled = false;
private bool _timerCleared = false;
protected override void OnStart()
{
// Parse event time to the broker's current day
if (TimeSpan.TryParse(EventTimeString, out TimeSpan eventTimeOfDay))
{
_eventTime = Server.Time.Date.Add(eventTimeOfDay);
// If the time has already passed today, roll it to tomorrow
if (_eventTime < Server.Time)
_eventTime = _eventTime.AddDays(1);
Print($"ECB Risk Manager armed for Broker Time: {_eventTime}");
}
else
{
Print("Invalid time format. Please use HH:mm");
Stop();
}
}
protected override void OnTick()
{
var now = Server.Time;
// Phase 1: Pre-Event Order Cancellation
if (!_ordersCancelled && now >= _eventTime.AddMinutes(-PreEventMinutes) && now < _eventTime)
{
CancelAllWorkingOrders();
_ordersCancelled = true;
Print("ECB Pre-Window Reached: All pending orders cancelled. No 'leave it, maybe'.");
}
// Phase 2: The Stand-Aside Clock (Active Event)
if (now >= _eventTime && now < _eventTime.AddMinutes(StandAsideMinutes))
{
Chart.DrawText("Status", "STAND ASIDE: ECB TIMER ACTIVE", StaticPosition.TopCenter, Colors.Red);
_timerCleared = false;
}
// Phase 3: Post-Timer & Spread Normalization Check
if (now >= _eventTime.AddMinutes(StandAsideMinutes))
{
double currentSpread = Symbol.Spread / Symbol.PipSize;
if (currentSpread <= MaxSpreadPips)
{
if (!_timerCleared)
{
Print($"ECB Timer finished. Spread normalized to {currentSpread} pips.");
_timerCleared = true;
}
Chart.DrawText("Status", "CLEAR TO TRADE (HALF SIZE)", StaticPosition.TopCenter, Colors.LimeGreen);
}
else
{
_timerCleared = false; // Reset if spread blows out again during Lagarde's speech
Chart.DrawText("Status", $"WAIT: TAPE ABNORMAL (Spread: {currentSpread:F1} pips)", StaticPosition.TopCenter, Colors.Orange);
}
}
}
private void CancelAllWorkingOrders()
{
// Target only pending orders for the chart's specific symbol
var pendingOrders = PendingOrders.Where(o => o.SymbolName == SymbolName);
foreach (var order in pendingOrders)
{
CancelPendingOrderAsync(order);
}
}
}
}
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:50 pm
by PTScalper
How it fits your workflow:
1.) CancelAllWorkingOrders() executes asynchronously the second the PreEventMinutes threshold is breached. It targets only the chart's symbol, leaving other uncorrelated trades alone.
2.) The "Tape Looks Normal" check in Phase 3 is dynamic. Even if the fixed 15-minute clock expires, if the spread is still bouncing wildly above your MaxSpreadPips threshold (your London filter), the chart overlay remains an orange WAIT: TAPE ABNORMAL. It only switches to the green CLEAR TO TRADE (HALF SIZE) when both conditions are met simultaneously.
3.) Do you have a specific visual spread-monitoring indicator you use on the 1-minute chart alongside this, or do you rely entirely on the raw order book for your tape reads?
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:50 pm
by PTScalper
To make this professional grade, we need to move away from a basic script that just prints text and instead build an event-driven state machine with active enforcement.
If adrenaline doesn't get a vote, the bot shouldn't just warn you — it should actively intercept and kill unauthorized manual orders during the lockdown, manage state transitions cleanly, and use a proper Canvas UI dashboard rather than legacy text drawing.
Here is the
enterprise-grade C# implementation for cTrader. It separates time evaluation from tick evaluation, implements a strict state machine, and actively defends the account.
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 ECBRiskManagerPro : Robot
{
#region Parameters
[Parameter("Event Time (Broker HH:mm)", Group = "Schedule", DefaultValue = "13:15")]
public string EventTimeString { get; set; }
[Parameter("Pre-Event Lockdown (Mins)", Group = "Schedule", DefaultValue = 5, MinValue = 1)]
public int PreEventMinutes { get; set; }
[Parameter("Stand-Aside Timer (Mins)", Group = "Schedule", DefaultValue = 15, MinValue = 1)]
public int StandAsideMinutes { get; set; }
[Parameter("Max Allowed Spread (Pips)", Group = "Microstructure", DefaultValue = 0.8)]
public double MaxSpreadPips { get; set; }
[Parameter("Kill Manual Trades During Timer", Group = "Enforcement", DefaultValue = true)]
public bool StrictEnforcement { get; set; }
#endregion
#region State Management
private enum RiskState
{
Armed,
Lockdown, // Pre-event order cancellation phase
TimerActive, // Absolute stand-aside window
SpreadCheck, // Timer expired, waiting for liquidity to normalize
Cleared // Ready for half-size entry
}
private RiskState _currentState = RiskState.Armed;
private DateTime _eventTime;
// UI Elements
private TextBlock _statusText;
private Border _uiContainer;
#endregion
protected override void OnStart()
{
InitializeEventTime();
InitializeDashboard();
// Hook into manual order events for strict enforcement
if (StrictEnforcement)
{
Positions.Opened += OnPositionOpened;
PendingOrders.Created += OnPendingOrderCreated;
}
// Run the state evaluator every second, decoupling it from tick volume
Timer.Start(TimeSpan.FromSeconds(1));
}
protected override void OnTimer()
{
EvaluateStateTransitions();
}
protected override void OnTick()
{
// Tick events only matter for spread evaluation during the SpreadCheck phase
if (_currentState == RiskState.SpreadCheck)
{
EvaluateSpread();
}
}
#region Core Logic
private void EvaluateStateTransitions()
{
var now = Server.Time;
switch (_currentState)
{
case RiskState.Armed:
if (now >= _eventTime.AddMinutes(-PreEventMinutes) && now < _eventTime)
{
TransitionTo(RiskState.Lockdown);
CancelAllWorkingOrders();
}
break;
case RiskState.Lockdown:
if (now >= _eventTime)
{
TransitionTo(RiskState.TimerActive);
}
break;
case RiskState.TimerActive:
if (now >= _eventTime.AddMinutes(StandAsideMinutes))
{
TransitionTo(RiskState.SpreadCheck);
}
break;
}
}
private void EvaluateSpread()
{
double currentSpread = Symbol.Spread / Symbol.PipSize;
if (currentSpread <= MaxSpreadPips)
{
TransitionTo(RiskState.Cleared);
}
}
private void TransitionTo(RiskState newState)
{
if (_currentState == newState) return;
_currentState = newState;
UpdateDashboard();
Print($"[ECB Manager] State transitioned to: {newState}");
}
private void CancelAllWorkingOrders()
{
var pendingOrders = PendingOrders.Where(o => o.SymbolName == SymbolName).ToList();
foreach (var order in pendingOrders)
{
CancelPendingOrderAsync(order, result =>
{
if (result.IsSuccessful)
Print($"[ECB Manager] Cancelled pending order {order.Id}");
});
}
}
#endregion
#region Strict Enforcement (The Adrenaline Blocker)
private void OnPositionOpened(PositionOpenedEventArgs args)
{
if (args.Position.SymbolName != SymbolName) return;
if (_currentState == RiskState.Lockdown || _currentState == RiskState.TimerActive || _currentState == RiskState.SpreadCheck)
{
Print("[ECB Manager] UNAUTHORIZED ENTRY BLOCKED. Closing position immediately.");
ClosePositionAsync(args.Position);
}
}
private void OnPendingOrderCreated(PendingOrderCreatedEventArgs args)
{
if (args.PendingOrder.SymbolName != SymbolName) return;
if (_currentState == RiskState.Lockdown || _currentState == RiskState.TimerActive || _currentState == RiskState.SpreadCheck)
{
Print("[ECB Manager] UNAUTHORIZED PENDING ORDER BLOCKED. Cancelling immediately.");
CancelPendingOrderAsync(args.PendingOrder);
}
}
#endregion
#region Setup & UI
private void InitializeEventTime()
{
if (!TimeSpan.TryParse(EventTimeString, out TimeSpan timeOfDay))
{
Print("CRITICAL: Invalid time format. Use HH:mm.");
Stop();
return;
}
_eventTime = Server.Time.Date.Add(timeOfDay);
if (_eventTime < Server.Time)
{
_eventTime = _eventTime.AddDays(1);
}
Print($"[ECB Manager] Armed for {_eventTime} (Broker Time)");
}
private void InitializeDashboard()
{
_statusText = new TextBlock
{
Text = "ARMED: WAITING FOR ECB",
ForegroundColor = Color.White,
FontWeight = FontWeight.ExtraBold,
Margin = new Thickness(10)
};
_uiContainer = new Border
{
BackgroundColor = Color.FromArgb(200, 30, 30, 30),
BorderColor = Color.Gray,
BorderThickness = new Thickness(2),
CornerRadius = 3,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, 20, 0, 0),
Child = _statusText
};
Chart.AddControl(_uiContainer);
UpdateDashboard();
}
private void UpdateDashboard()
{
switch (_currentState)
{
case RiskState.Armed:
_statusText.Text = $"ARMED: PRE-LOCKDOWN SET FOR {_eventTime.AddMinutes(-PreEventMinutes):HH:mm}";
_uiContainer.BorderColor = Color.DarkGray;
break;
case RiskState.Lockdown:
_statusText.Text = "LOCKDOWN: FLATTENING WORKING ORDERS";
_uiContainer.BorderColor = Color.Orange;
_statusText.ForegroundColor = Color.Orange;
break;
case RiskState.TimerActive:
_statusText.Text = "STAND ASIDE: TAPE IS TOXIC";
_uiContainer.BorderColor = Color.Red;
_statusText.ForegroundColor = Color.Red;
break;
case RiskState.SpreadCheck:
_statusText.Text = "TIMER DONE: WAITING FOR SPREAD NORMALIZATION";
_uiContainer.BorderColor = Color.Yellow;
_statusText.ForegroundColor = Color.Yellow;
break;
case RiskState.Cleared:
_statusText.Text = "CLEAR TO TRADE: APPLY HALF-SIZE RULE";
_uiContainer.BorderColor = Color.LimeGreen;
_statusText.ForegroundColor = Color.LimeGreen;
break;
}
}
#endregion
}
}
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:51 pm
by PTScalper
Architectural Upgrades
The Adrenaline Blocker (StrictEnforcement): The bot now actively listens to Positions.Opened and PendingOrders.Created. If you attempt to override the timer and manually execute a market or limit order while the tape is toxic, the bot intercepts it and instantly sends an async close/cancel command to the server.
Deterministic State Machine: Replaced simple boolean flags with a strict RiskState enum. This ensures the lifecycle only progresses linearly (Armed → Lockdown → TimerActive → SpreadCheck → Cleared) without race conditions.
Decoupled Evaluators: Using OnTimer() running exactly once per second handles time transitions flawlessly, even if the price stalls and ticks stop flowing. OnTick() is now strictly reserved for phase 4 (SpreadCheck), evaluating micro-fluctuations in the order book only when required.
Canvas UI Dashboard: Removed the legacy Chart.DrawText overlay and built a proper WPF-style Canvas Border and TextBlock container. It sits cleanly on the chart with visual color coding that reflects the current state machine phase.
Non-Blocking Execution: Uses CancelPendingOrderAsync and ClosePositionAsync for rapid execution against the broker server without hanging the main chart thread.
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:51 pm
by PTScalper
To handle the ECB's two-step release structure cleanly, model the session as a sequential two-phase pipeline: Statement followed by Press Conference.
Rather than duplicating the entire state engine, the cleanest architectural approach is to introduce an EventPhase enum and have the state evaluator re-arm and pivot to the press conference schedule once the Statement phase reaches its Cleared state (or when the clock reaches the press conference pre-lockdown window).
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:52 pm
by PTScalper
Pro Multi-Phase Architecture
Here is the updated implementation. It introduces:
Phase Tracking (Statement vs. PressConference): Distinct stand-aside durations (e.g., 15 minutes for the initial print vs. 45 minutes to cover Lagarde's opening statement and the initial Q&A barrage).
Auto-Re-arm & Pre-Conference Purge: As soon as the clock hits Press Conference Time - PreEventMinutes, any state (even Cleared) is revoked, working orders are purged again, and the adrenaline blocker is re-locked.
Phase-Aware Dashboard: The Canvas UI explicitly displays whether you are in the Statement window or the Press Conference window.
Re: Seconds after ECB rate decision on EURUSD: my stand-aside timer
Posted: Wed Sep 23, 2026 7:52 pm
by PTScalper
Ctrader script version 3.0
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 ECBDualPhaseRiskManager : Robot
{
#region Parameters
[Parameter("Statement Time (Broker HH:mm)", Group = "Schedule", DefaultValue = "13:15")]
public string StatementTimeString { get; set; }
[Parameter("Press Conf Time (Broker HH:mm)", Group = "Schedule", DefaultValue = "13:45")]
public string PressConfTimeString { get; set; }
[Parameter("Pre-Event Purge Window (Mins)", Group = "Schedule", DefaultValue = 5, MinValue = 1)]
public int PreEventMinutes { get; set; }
[Parameter("Statement Stand-Aside (Mins)", Group = "Timers", DefaultValue = 15, MinValue = 1)]
public int StatementStandAsideMinutes { get; set; }
[Parameter("Press Conf Stand-Aside (Mins)", Group = "Timers", DefaultValue = 45, MinValue = 1)]
public int PressConfStandAsideMinutes { get; set; }
[Parameter("Max Allowed Spread (Pips)", Group = "Microstructure", DefaultValue = 0.8)]
public double MaxSpreadPips { get; set; }
[Parameter("Kill Manual Trades During Lockdown", Group = "Enforcement", DefaultValue = true)]
public bool StrictEnforcement { get; set; }
#endregion
#region State Machine
private enum EventPhase
{
Statement,
PressConference,
Complete
}
private enum RiskState
{
Armed,
Lockdown, // Working orders cancelled, trading blocked
TimerActive, // Absolute stand-aside window
SpreadCheck, // Timer expired, waiting for liquidity to normalize
Cleared // Half-size window open
}
private EventPhase _currentPhase = EventPhase.Statement;
private RiskState _currentState = RiskState.Armed;
private DateTime _statementTime;
private DateTime _pressConfTime;
private DateTime _activeTargetTime;
private int _activeStandAsideMinutes;
// UI Elements
private TextBlock _statusText;
private Border _uiContainer;
#endregion
protected override void OnStart()
{
InitializeSchedules();
InitializeDashboard();
if (StrictEnforcement)
{
Positions.Opened += OnPositionOpened;
PendingOrders.Created += OnPendingOrderCreated;
}
Timer.Start(TimeSpan.FromSeconds(1));
}
protected override void OnTimer()
{
EvaluateStateTransitions();
}
protected override void OnTick()
{
if (_currentState == RiskState.SpreadCheck)
{
EvaluateSpread();
}
}
#region Transition Engine
private void EvaluateStateTransitions()
{
var now = Server.Time;
// Phase 1 -> Phase 2 Transition Check
// If we are still in Statement phase but hit the Press Conference pre-lockdown window:
if (_currentPhase == EventPhase.Statement && now >= _pressConfTime.AddMinutes(-PreEventMinutes))
{
PivotoPressConference();
return;
}
// Standard State Lifecycle for Active Target
switch (_currentState)
{
case RiskState.Armed:
if (now >= _activeTargetTime.AddMinutes(-PreEventMinutes) && now < _activeTargetTime)
{
TransitionTo(RiskState.Lockdown);
CancelAllWorkingOrders();
}
break;
case RiskState.Lockdown:
if (now >= _activeTargetTime)
{
TransitionTo(RiskState.TimerActive);
}
break;
case RiskState.TimerActive:
if (now >= _activeTargetTime.AddMinutes(_activeStandAsideMinutes))
{
TransitionTo(RiskState.SpreadCheck);
}
break;
case RiskState.Cleared:
// If Statement phase cleared, wait for Press Conf lockdown trigger
if (_currentPhase == EventPhase.PressConference && now >= _pressConfTime.AddMinutes(_activeStandAsideMinutes + 60))
{
_currentPhase = EventPhase.Complete;
UpdateDashboard();
}
break;
}
}
private void PivotoPressConference()
{
Print("[ECB Engine] PIVOT: Entering Press Conference Phase. Revoking permissions.");
_currentPhase = EventPhase.PressConference;
_activeTargetTime = _pressConfTime;
_activeStandAsideMinutes = PressConfStandAsideMinutes;
TransitionTo(RiskState.Lockdown);
CancelAllWorkingOrders();
}
private void EvaluateSpread()
{
double currentSpread = Symbol.Spread / Symbol.PipSize;
if (currentSpread <= MaxSpreadPips)
{
TransitionTo(RiskState.Cleared);
}
}
private void TransitionTo(RiskState newState)
{
if (_currentState == newState) return;
_currentState = newState;
UpdateDashboard();
Print($"[ECB Engine] [{_currentPhase}] State -> {newState}");
}
private void CancelAllWorkingOrders()
{
var pendingOrders = PendingOrders.Where(o => o.SymbolName == SymbolName).ToList();
foreach (var order in pendingOrders)
{
CancelPendingOrderAsync(order, res =>
{
if (res.IsSuccessful)
Print($"[ECB Engine] Working order cancelled: {order.Id}");
});
}
}
#endregion
#region Strict Enforcement
private void OnPositionOpened(PositionOpenedEventArgs args)
{
if (args.Position.SymbolName != SymbolName) return;
if (_currentState != RiskState.Cleared && _currentState != RiskState.Armed)
{
Print("[ECB Engine] UNAUTHORIZED POSITION DURING LOCKDOWN. Liquidating.");
ClosePositionAsync(args.Position);
}
}
private void OnPendingOrderCreated(PendingOrderCreatedEventArgs args)
{
if (args.PendingOrder.SymbolName != SymbolName) return;
if (_currentState != RiskState.Cleared && _currentState != RiskState.Armed)
{
Print("[ECB Engine] UNAUTHORIZED PENDING ORDER CREATED. Purging.");
CancelPendingOrderAsync(args.PendingOrder);
}
}
#endregion
#region Initialization & UI
private void InitializeSchedules()
{
if (!TimeSpan.TryParse(StatementTimeString, out TimeSpan stmtTime) ||
!TimeSpan.TryParse(PressConfTimeString, out TimeSpan pressTime))
{
Print("CRITICAL: Invalid HH:mm format.");
Stop();
return;
}
_statementTime = Server.Time.Date.Add(stmtTime);
_pressConfTime = Server.Time.Date.Add(pressTime);
if (_statementTime < Server.Time && Server.Time < _pressConfTime)
{
// Statement passed, arm directly for Presser
_currentPhase = EventPhase.PressConference;
_activeTargetTime = _pressConfTime;
_activeStandAsideMinutes = PressConfStandAsideMinutes;
}
else if (_statementTime < Server.Time && _pressConfTime < Server.Time)
{
_statementTime = _statementTime.AddDays(1);
_pressConfTime = _pressConfTime.AddDays(1);
_activeTargetTime = _statementTime;
_activeStandAsideMinutes = StatementStandAsideMinutes;
}
else
{
_activeTargetTime = _statementTime;
_activeStandAsideMinutes = StatementStandAsideMinutes;
}
Print($"[ECB Engine] Armed: Statement at {_statementTime:HH:mm}, Presser at {_pressConfTime:HH:mm}");
}
private void InitializeDashboard()
{
_statusText = new TextBlock
{
Text = "ARMED",
ForegroundColor = Color.White,
FontWeight = FontWeight.ExtraBold,
Margin = new Thickness(10)
};
_uiContainer = new Border
{
BackgroundColor = Color.FromArgb(220, 20, 20, 20),
BorderThickness = new Thickness(2),
CornerRadius = 4,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, 25, 0, 0),
Child = _statusText
};
Chart.AddControl(_uiContainer);
UpdateDashboard();
}
private void UpdateDashboard()
{
string phaseTag = _currentPhase == EventPhase.Statement ? "[STATEMENT]" : "[PRESS CONF]";
switch (_currentState)
{
case RiskState.Armed:
_statusText.Text = $"{phaseTag} ARMED: PURGE AT {_activeTargetTime.AddMinutes(-PreEventMinutes):HH:mm}";
_uiContainer.BorderColor = Color.DarkGray;
_statusText.ForegroundColor = Color.White;
break;
case RiskState.Lockdown:
_statusText.Text = $"{phaseTag} LOCKDOWN: PURGING ORDERS & BLOCKING ENTRIES";
_uiContainer.BorderColor = Color.Orange;
_statusText.ForegroundColor = Color.Orange;
break;
case RiskState.TimerActive:
_statusText.Text = $"{phaseTag} STAND ASIDE: CLOCK RUNNING";
_uiContainer.BorderColor = Color.Red;
_statusText.ForegroundColor = Color.Red;
break;
case RiskState.SpreadCheck:
_statusText.Text = $"{phaseTag} TIMER ELAPSED: WAITING FOR SPREAD NORMALIZATION";
_uiContainer.BorderColor = Color.Yellow;
_statusText.ForegroundColor = Color.Yellow;
break;
case RiskState.Cleared:
string warning = _currentPhase == EventPhase.Statement ? $" (PRESSER AT {_pressConfTime:HH:mm})" : "";
_statusText.Text = $"{phaseTag} CLEAR TO TRADE: HALF SIZE ONLY{warning}";
_uiContainer.BorderColor = Color.LimeGreen;
_statusText.ForegroundColor = Color.LimeGreen;
break;
}
if (_currentPhase == EventPhase.Complete)
{
_statusText.Text = "ECB COMPLETE: STANDARD PROTOCOLS RESTORED";
_uiContainer.BorderColor = Color.Gray;
_statusText.ForegroundColor = Color.Gray;
}
}
#endregion
}
}