Page 2 of 2

Re: Broker scorecard quarterly update: spreads, rejects, and uptime

Posted: Mon Sep 14, 2026 10:05 pm
by FTtrader
The Analytical Edge with cAlgo

cTrader generally routes through superior liquidity hubs compared to vanilla MT4 servers, meaning your limit orders and take-profits should logically experience a higher frequency of positive slippage.

When you parse this generated CSV for your Q4 scorecard update, run a quick pivot table comparing Latency_ms against Slippage_Pips. If you detect a structural correlation—for example, latency spiking from 30ms to 400ms exclusively when adverse slippage exceeds 0.5 pips—it provides empirical proof that the liquidity provider is employing a "last look" latency plugin to front-run your M1 sweeps.

Re: Broker scorecard quarterly update: spreads, rejects, and uptime

Posted: Mon Sep 14, 2026 10:06 pm
by FTtrader
To elevate this to an institutional standard, we must address a critical architectural flaw present in most retail execution trackers: I/O thread blocking.

Writing synchronously to a disk (File.AppendAllText) immediately after an order execution pollutes the hardware timer and locks the primary trading thread. In an M1 scalping environment where microstructure edges are measured in milliseconds, executing I/O operations on the hot path artificially inflates latency metrics and delays subsequent algorithmic logic.

A production-grade solution decouples the execution event from the logging mechanism. We achieve this by pushing execution telemetry into a thread-safe ConcurrentQueue on the main thread, and asynchronously flushing it to disk via an independent timer loop.

Re: Broker scorecard quarterly update: spreads, rejects, and uptime

Posted: Mon Sep 14, 2026 10:06 pm
by FTtrader
Enterprise Microstructure Auditor (C# cAlgo)

This architecture ensures zero latency drag on the execution thread, utilizes high-resolution hardware timers, and formats output for immediate parsing into a quantitative scorecard.

Code: Select all

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections.Concurrent;
using cAlgo.API;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
    public class InstitutionalExecutionAuditor : Robot
    {
        [Parameter("Audit Log Filename", DefaultValue = "cTrader_Microstructure_Audit.csv")]
        public string LogFileName { get; set; }

        private string _filePath;
        private readonly ConcurrentQueue<string> _telemetryQueue = new ConcurrentQueue<string>();
        
        // Timer for high-resolution profiling
        private readonly Stopwatch _executionTimer = new Stopwatch();

        protected override void OnStart()
        {
            string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            _filePath = Path.Combine(docPath, LogFileName);

            InitializeScorecardEngine();

            // Offload disk I/O to a background timer (every 2 seconds)
            Timer.Start(2);
            
            Print($"[ENGINE] Microstructure Auditor active. I/O decoupled.");
        }

        private void InitializeScorecardEngine()
        {
            if (!File.Exists(_filePath))
            {
                string header = "Timestamp_UTC,Symbol,Type,Latency_ms,Intended_Px,Filled_Px,Slip_Pips,Spread_Pips,Order_State\n";
                File.WriteAllText(_filePath, header);
            }
        }

        // =========================================================================
        // HOT PATH: SYNCHRONOUS EXECUTION WRAPPER
        // =========================================================================
        public void ExecuteAuditedOrder(TradeType direction, double volume)
        {
            // 1. Capture snapshot of liquidity strictly prior to FIX dispatch
            double intendedPx = direction == TradeType.Buy ? Symbol.Ask : Symbol.Bid;
            double spreadPips = (Symbol.Ask - Symbol.Bid) / Symbol.PipSize;

            // 2. Hardware-level latency profiling
            _executionTimer.Restart();
            
            // 3. Dispatch to Liquidity Provider
            TradeResult result = ExecuteMarketOrder(direction, SymbolName, volume, "Inst_Audit");
            
            _executionTimer.Stop();
            double latencyMs = _executionTimer.Elapsed.TotalMilliseconds;

            // 4. Calculate Execution Drag
            if (result.IsSuccessful)
            {
                double filledPx = result.Position.EntryPrice;
                double slipPips = direction == TradeType.Buy 
                    ? (filledPx - intendedPx) / Symbol.PipSize 
                    : (intendedPx - filledPx) / Symbol.PipSize;

                QueueTelemetry(direction, latencyMs, intendedPx, filledPx, slipPips, spreadPips, "FILLED");
            }
            else
            {
                QueueTelemetry(direction, latencyMs, intendedPx, 0, 0, spreadPips, $"REJECT_{result.Error}");
            }
        }

        // =========================================================================
        // TELEMETRY QUEUEING (NON-BLOCKING)
        // =========================================================================
        private void QueueTelemetry(TradeType dir, double lat, double req, double fill, double slip, double spread, string state)
        {
            string record = $"{Server.Time:yyyy-MM-dd HH:mm:ss.fff},{SymbolName},{dir},{lat:F2},{req},{fill},{slip:F2},{spread:F2},{state}";
            _telemetryQueue.Enqueue(record);
        }

        // =========================================================================
        // BACKGROUND I/O FLUSH
        // =========================================================================
        protected override void OnTimer()
        {
            if (_telemetryQueue.IsEmpty) return;

            var sb = new StringBuilder();
            while (_telemetryQueue.TryDequeue(out string record))
            {
                sb.AppendLine(record);
            }

            try
            {
                File.AppendAllText(_filePath, sb.ToString());
            }
            catch (Exception ex)
            {
                Print($"[I/O FAULT] Failed to flush telemetry: {ex.Message}");
            }
        }

        // =========================================================================
        // LIGHTWEIGHT TERMINAL UI
        // =========================================================================
        protected override void OnTick()
        {
            double spread = (Symbol.Ask - Symbol.Bid) / Symbol.PipSize;
            Chart.DrawStaticText("micro_dash", 
                $"INSTITUTIONAL AUDITOR\nSpread: {spread:F1} pips\nQueue Depth: {_telemetryQueue.Count}", 
                VerticalAlignment.Top, HorizontalAlignment.Right, Color.DimGray);
        }
    }
}

Re: Broker scorecard quarterly update: spreads, rejects, and uptime

Posted: Mon Sep 14, 2026 10:07 pm
by FTtrader
Analyzing the Microstructure Data

Once this runs through a quarter of high-volume overlaps, you bypass traditional platform analytics and construct a rigid scatter plot: Execution Latency (X-axis) vs. Adverse Slippage (Y-axis).

The "Last Look" Signature: If you observe a cluster of rejected orders or heavy negative slippage exclusively when Latency_ms exceeds ~150-200ms, the broker's liquidity provider is utilizing a "last look" holding window. They are pausing the execution to verify if the M1 price action moves against them before confirming your fill.

The True DMA Profile: A genuine STP/ECN environment will exhibit sub-50ms execution times regardless of market volatility, and the slippage distribution will resemble a standard bell curve (equal occurrences of positive and negative slippage).