Page 7 of 13

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:45 pm
by PTScalper
Architectural Mechanics

Chart Focus Requirement: The Chart.KeyDown event only fires if the specific chart window running the cBot is the active, focused window in the OS. If you click on a different chart or application, the 'Escape' key will not register.

Stateful Key Routing (_pendingConfirmationLtid): By storing the active LTID in a class-level variable when the dialog opens, you enable the 'Enter' key to execute OnDialogConfirm without needing a mouse click. This transforms the dialog into a fully keyboard-navigable HUD.

Memory Sanitization: Setting _pendingConfirmationLtid = null inside CloseActiveDialog is a critical safety lock. If you fail to clear it, pressing 'Enter' while the dialog is closed could accidentally bypass the UI and instantly liquidate the last trade you interacted with.

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:46 pm
by PTScalper
To build a global panic button, you must step outside the bounds of your specific Logical Trade IDs (LTIDs) and iterate over the broker's master Positions and PendingOrders collections.

Because a panic scenario requires immediate execution, you must fire every closure and cancellation request asynchronously in a single loop to flood the broker's API with concurrent requests, rather than waiting for them to close one by one.

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:46 pm
by PTScalper
Here is the code to inject a master "Close All" button into your existing HUD architecture:

Code: Select all

using System;
using cAlgo.API;

namespace cAlgo.Robots
{
    public partial class InteractiveFiatHUD : Robot
    {
        // State lock to prevent the user from spamming the button while the API is processing
        private bool _isPanicProcessing = false;

        /// <summary>
        /// Update your existing InitializeHUD method to include this button.
        /// </summary>
        private void InitializeHUD()
        {
            _mainHudContainer = new StackPanel
            {
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Top,
                BackgroundColor = Color.FromArgb(200, 20, 20, 20),
                Margin = new Thickness(10)
            };

            // 1. Create the Panic Button with a distinct, high-contrast visual style
            Button panicBtn = new Button
            {
                Text = "⚠ PANIC CLOSE ALL",
                BackgroundColor = Color.DarkRed,
                ForegroundColor = Color.Yellow,
                FontWeight = FontWeight.ExtraBold,
                Margin = new Thickness(5, 5, 5, 15),
                Height = 35
            };

            // 2. Wire the click event to the master liquidation method
            panicBtn.Click += args => ExecuteGlobalPanicLiquidation();

            _mainHudContainer.AddChild(panicBtn);

            // ... (Rest of your existing HUD initialization code follows here)
            
            Chart.AddControl(_mainHudContainer);
        }

        /// <summary>
        /// Instantly liquidates all open market exposure and cancels all resting orders across the entire account.
        /// </summary>
        private void ExecuteGlobalPanicLiquidation()
        {
            // Prevent double-firing if the user panic-clicks the button rapidly
            if (_isPanicProcessing) return;
            _isPanicProcessing = true;

            Print("[PANIC] Global Account Liquidation Initiated.");

            // 1. Cancel all resting Limit and Stop orders globally
            foreach (var pendingOrder in PendingOrders)
            {
                CancelPendingOrderAsync(pendingOrder, OnPanicOrderCancelled);
            }

            // 2. Close all active market positions globally
            foreach (var position in Positions)
            {
                ClosePositionAsync(position, OnPanicPositionClosed);
            }
        }

        private void OnPanicOrderCancelled(TradeResult result)
        {
            if (!result.IsSuccessful && result.PendingOrder != null)
            {
                Print($"[Panic Error] Failed to cancel order {result.PendingOrder.Id}: {result.Error}");
            }
        }

        private void OnPanicPositionClosed(TradeResult result)
        {
            if (!result.IsSuccessful && result.Position != null)
            {
                Print($"[Panic Error] Failed to close position {result.Position.Id}: {result.Error}");
            }
        }

        protected override void OnTick()
        {
            // Release the panic lock only when the account is genuinely flat
            if (_isPanicProcessing && Positions.Count == 0 && PendingOrders.Count == 0)
            {
                _isPanicProcessing = false;
                Print("[PANIC] Account is now completely flat.");
            }

            // ... (Your existing trailing logic)
        }
    }
}

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:46 pm
by PTScalper
Architectural Mechanics

Global Account Iteration: Notice that the foreach loops use PendingOrders and Positions directly, without using a .FindAll(Label) or .FindAll(SymbolName) filter. This ensures that even trades opened manually by you, or trades opened by entirely different cBots running on other charts, are instantly liquidated.

Order of Operations (Cancel Before Close): The code cancels PendingOrders before closing active Positions. If you reverse this order during high volatility, a price spike could trigger a resting Stop/Limit order in the exact millisecond after your bot finishes closing the active positions, leaving you unexpectedly exposed to the market.

The Flat-State Release: Setting _isPanicProcessing = true deadens the button instantly. However, the bot must eventually re-arm itself in case you resume trading. The check inside OnTick verifies that both the Positions.Count and PendingOrders.Count have mathematically reached zero before releasing the lock, ensuring complete API synchronization.

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:47 pm
by PTScalper
cTrader includes a native Notifications interface specifically designed to handle asynchronous audio playback without blocking the bot's execution thread.

To implement this, you expose a parameter for the local file path so you can easily swap the sound file directly from the cBot settings menu, and then trigger the native method at the exact moment the panic sequence initiates.

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:47 pm
by PTScalper
Here is the updated architecture incorporating the audio alarm:

Code: Select all

using System;
using cAlgo.API;

namespace cAlgo.Robots
{
    public partial class InteractiveFiatHUD : Robot
    {
        // 1. Add a parameter to define the absolute path to your custom .wav file
        [Parameter("Panic Alarm File Path", Group = "Alerts", DefaultValue = @"C:\Windows\Media\Alarm01.wav")]
        public string AlarmFilePath { get; set; }

        [Parameter("Play Sound on Panic", Group = "Alerts", DefaultValue = true)]
        public bool EnablePanicAlarm { get; set; }

        private bool _isPanicProcessing = false;

        private void ExecuteGlobalPanicLiquidation()
        {
            if (_isPanicProcessing) return;
            _isPanicProcessing = true;

            Print("[PANIC] Global Account Liquidation Initiated.");

            // 2. Trigger the asynchronous sound playback
            if (EnablePanicAlarm && !string.IsNullOrWhiteSpace(AlarmFilePath))
            {
                TriggerAudioAlarm();
            }

            // Cancel all resting Limit and Stop orders globally
            foreach (var pendingOrder in PendingOrders)
            {
                CancelPendingOrderAsync(pendingOrder, OnPanicOrderCancelled);
            }

            // Close all active market positions globally
            foreach (var position in Positions)
            {
                ClosePositionAsync(position, OnPanicPositionClosed);
            }
        }

        /// <summary>
        /// Fires the native cTrader audio notification.
        /// </summary>
        private void TriggerAudioAlarm()
        {
            try
            {
                // Notifications.PlaySound runs asynchronously and will not delay your trade closures
                Notifications.PlaySound(AlarmFilePath);
            }
            catch (Exception ex)
            {
                // Catch unauthorized access or missing file errors so they don't crash the panic sequence
                Print($"[Audio Error] Could not play alarm file: {ex.Message}");
            }
        }

        // ... (Remaining HUD and Tick logic stays exactly the same)
    }
}

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:48 pm
by PTScalper
Architectural Mechanics

Thread Safety (Notifications.PlaySound): Using the native cTrader Notifications API is critical here. If you were to use standard .NET System.Media.SoundPlayer.PlaySync(), the bot would completely freeze and wait for the 5-second audio clip to finish playing before sending the first close request to the broker. Notifications.PlaySound fires asynchronously in the background.

The @ String Literal: By prefixing the default path string with @ (e.g., @"C:\Windows\Media\Alarm01.wav"), C# treats backslashes as literal characters rather than escape characters. This prevents pathing errors when you paste a directory link directly from Windows Explorer.

Exception Isolation (The try/catch Block): During a panic liquidation, the absolute worst-case scenario is an unhandled exception crashing the cBot before it iterates through your open positions. If the .wav file was accidentally deleted or locked by another program, the try/catch block swallows the error, prints a warning to the log, and allows the code to immediately proceed to closing your trades.

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:48 pm
by PTScalper
To completely disable the bot, you must introduce a permanent state flag (a Soft Halt) or trigger cTrader's native Stop() method (a Hard Kill).

The most critical engineering rule for this feature is timing. If you call Stop() inside the initial ExecuteGlobalPanicLiquidation method, the bot will instantly terminate itself before the broker server has time to process your asynchronous close requests. Your bot will die, and your trades will remain open.

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:49 pm
by PTScalper
You must apply the kill switch only after the bot verifies the account is mathematically flat.

Here is the architecture to safely permanently disable the bot after a panic liquidation:

Code: Select all

using System;
using cAlgo.API;

namespace cAlgo.Robots
{
    public partial class InteractiveFiatHUD : Robot
    {
        [Parameter("Hard Stop Bot After Panic", Group = "Safety", DefaultValue = true)]
        public bool HardStopAfterPanic { get; set; }

        // 1. The permanent software lock
        private bool _isTradingHalted = false;
        
        private bool _isPanicProcessing = false;

        protected override void OnTick()
        {
            // 2. The Verification Gate: Wait for the broker to confirm the account is flat
            if (_isPanicProcessing && Positions.Count == 0 && PendingOrders.Count == 0)
            {
                _isPanicProcessing = false;
                _isTradingHalted = true; // Lock the software

                Print("[PANIC COMPLETE] Account is mathematically flat. Trading is permanently disabled.");

                // 3. The Hard Kill
                if (HardStopAfterPanic)
                {
                    Print("[SYSTEM] Executing native bot termination...");
                    Stop(); // Instantly unloads the cBot from the chart
                }
            }

            // 4. The Global Software Lock Check
            // Place this at the very top of your normal OnTick logic
            if (_isTradingHalted)
            {
                return; // Immediately exit OnTick, preventing any further logic from running
            }

            // ... (Your normal trailing stop and trade management logic goes here)
        }

        /// <summary>
        /// Example of how to protect your entry methods.
        /// </summary>
        private void ExecuteNewTrade()
        {
            // 5. Protect all entry triggers
            if (_isTradingHalted)
            {
                Print("[BLOCKED] New trade rejected. Bot is in a halted state.");
                return;
            }

            // ... (Normal entry execution logic)
        }
        
        // ... (Existing ExecuteGlobalPanicLiquidation logic remains the same)
    }
}

Re: Spread Filters for Scalping: When Not to Enter

Posted: Thu Oct 08, 2026 1:49 pm
by PTScalper
Architectural Mechanics

The Native Stop() Method (Hard Kill): Calling Stop() is the nuclear option. It is a native API command that tells the cTrader terminal to completely unhook the bot from the chart. The bot's memory is dumped, all HUD elements (including your buttons) vanish from the screen, and the bot cannot restart unless you manually click "Start" in the cTrader interface.

The _isTradingHalted Flag (Soft Halt): If HardStopAfterPanic is set to false, the bot continues running but is fundamentally paralyzed. The return; statement at the top of OnTick prevents it from evaluating market data or executing new signals. The advantage of a Soft Halt is that your custom HUD remains visible on the chart, allowing you to review the final data while safely preventing new financial exposure.

Entry Method Protection: Relying solely on OnTick for the halt is not enough. If your bot executes trades based on asynchronous events (like OnTimer or OnBar), you must manually inject the if (_isTradingHalted) return; check at the very top of those specific methods to guarantee total lockdown.