The Next Logical Step: The CSV Dump
Right now, this outputs directly to the Experts terminal tab. If you are serious about that "CPI / high-impact sheet" you mentioned, you should add the FileOpen() and FileWrite() functions directly into the Audit block.
By dumping [Symbol, Time, intendedEntry, actualFill, currentSpread, slippagePts, execTime] into a local .csv inside your MetaTrader Files directory, you fully automate the data collection for your execution sheet.
Do you execute these directly on the VPS, or are you piping signals from TradingView webhooks into a local Trade Copier / MQL bridge?
Slippage during CPI: second-by-second fill log analysis
Re: Slippage during CPI: second-by-second fill log analysis
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Slippage during CPI: second-by-second fill log analysis
cTrader is arguably the strongest retail platform for this specific architecture. Because cAlgo is pure C# running natively in the .NET runtime, you don't just get access to trading functions—you get access to system-level diagnostics and file I/O.
We can use the System.Diagnostics.Stopwatch class to measure the exact millisecond latency of the broker's execution server, and System.IO.File to automatically append your fills directly to your CSV execution sheet on the fly.
Here is the complete cBot framework for cTrader.
The cTrader (cAlgo) Implementation
To use this, ensure you set the bot's access rights to Full Access so it has permission to write the CSV file to your local drive.
We can use the System.Diagnostics.Stopwatch class to measure the exact millisecond latency of the broker's execution server, and System.IO.File to automatically append your fills directly to your CSV execution sheet on the fly.
Here is the complete cBot framework for cTrader.
The cTrader (cAlgo) Implementation
To use this, ensure you set the bot's access rights to Full Access so it has permission to write the CSV file to your local drive.
Code: Select all
using System;
using System.Diagnostics;
using System.IO;
using cAlgo.API;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public class CPI_Execution_Reality : Robot
{
// --- DESK PARAMETERS ---
[Parameter("News Hour (UTC)", Group = "Time Constraints", DefaultValue = 12)]
public int NewsHourUTC { get; set; }
[Parameter("News Minute Start", Group = "Time Constraints", DefaultValue = 30)]
public int NewsMinuteStart { get; set; }
[Parameter("News Minute End", Group = "Time Constraints", DefaultValue = 35)]
public int NewsMinuteEnd { get; set; }
[Parameter("Standard Risk (%)", Group = "Sizing", DefaultValue = 1.0)]
public double RiskPercent { get; set; }
[Parameter("News Risk Multiplier", Group = "Sizing", DefaultValue = 0.5)]
public double NewsRiskModifier { get; set; }
[Parameter("Min Stop Distance (Pips)", Group = "Sizing", DefaultValue = 10.0)]
public double MinStopDistancePips { get; set; }
[Parameter("Max Spread Tolerance (Pips)", Group = "Microstructure", DefaultValue = 2.0)]
public double MaxSpreadPips { get; set; }
[Parameter("Hard Time Stop (Minutes)", Group = "Microstructure", DefaultValue = 3)]
public int HardTimeStopMinutes { get; set; }
[Parameter("Local CSV Path", Group = "Audit", DefaultValue = "C:\\Trading\\CPI_Fill_Log.csv")]
public string LogFilePath { get; set; }
private const string LABEL = "Desk_CPI";
private bool _isProcessing = false;
protected override void OnStart()
{
// Initialize the CSV headers if the file doesn't exist
try
{
string dir = Path.GetDirectoryName(LogFilePath);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
if (!File.Exists(LogFilePath))
File.AppendAllText(LogFilePath, "Time,Symbol,Direction,Intent,Fill,Spread,Slippage,LatencyMs\n");
}
catch (Exception ex)
{
Print("CRITICAL: Cannot write to CSV. Ensure AccessRights = FullAccess. Error: ", ex.Message);
}
}
protected override void OnTick()
{
ManageHardTimeStop();
// Block new tickets if we are already in the market or processing an order
if (_isProcessing || Positions.FindAll(LABEL, SymbolName).Length > 0)
return;
// 1. Time Constraints
DateTime now = Server.Time;
bool inNewsWindow = now.Hour == NewsHourUTC && now.Minute >= NewsMinuteStart && now.Minute <= NewsMinuteEnd;
// 2. Spread Filter
double currentSpreadPips = Symbol.Spread / Symbol.PipSize;
bool spreadViolated = currentSpreadPips > MaxSpreadPips;
// 3. Signal Engine (Replace with your specific 1M price action triggers)
bool longTrigger = inNewsWindow && !spreadViolated; // Mock signal
if (longTrigger)
{
ExecuteAndAudit(TradeType.Buy, currentSpreadPips);
}
}
private void ExecuteAndAudit(TradeType direction, double currentSpreadPips)
{
_isProcessing = true;
// Snapshot the exact price you *wanted*
double intendedEntry = direction == TradeType.Buy ? Symbol.Ask : Symbol.Bid;
// Calculate Risk
double activeRisk = RiskPercent * NewsRiskModifier;
double riskAmount = Account.Balance * (activeRisk / 100.0);
// Dynamic Volume based on pip value (cTrader pip value is per unit)
double rawVolume = riskAmount / (MinStopDistancePips * Symbol.PipValue);
double volume = Symbol.NormalizeVolumeInUnits(rawVolume, RoundingMode.Down);
// Ping the stopwatch just before network transmission
Stopwatch sw = Stopwatch.StartNew();
// Synchronous execution block
TradeResult result = ExecuteMarketOrder(direction, SymbolName, volume, LABEL, MinStopDistancePips, null);
// Stop the watch the millisecond the broker returns the result
sw.Stop();
long latencyMs = sw.ElapsedMilliseconds;
if (result.IsSuccessful)
{
double actualFill = result.Position.EntryPrice;
double slippagePips = Math.Abs(actualFill - intendedEntry) / Symbol.PipSize;
// On-Screen Print
Print("\n================ CPI FILL LOG AUDIT ================");
Print($"Intent (Click): {intendedEntry} | Actual Fill: {actualFill}");
Print($"Spread at Click: {Math.Round(currentSpreadPips, 1)} pips | Slippage Incurred: {Math.Round(slippagePips, 1)} pips");
Print($"Broker Latency: {latencyMs} ms");
Print("====================================================");
// Silent CSV Dump
try
{
string logLine = $"{Server.Time:yyyy-MM-dd HH:mm:ss.fff},{SymbolName},{direction},{intendedEntry},{actualFill},{Math.Round(currentSpreadPips, 1)},{Math.Round(slippagePips, 1)},{latencyMs}\n";
File.AppendAllText(LogFilePath, logLine);
}
catch { }
}
else
{
Print($"Ticket Rejected. Spread: {currentSpreadPips}. Code: {result.Error}");
}
_isProcessing = false;
}
private void ManageHardTimeStop()
{
var positions = Positions.FindAll(LABEL, SymbolName);
foreach (var pos in positions)
{
TimeSpan heldDuration = Server.Time - pos.EntryTime;
if (heldDuration.TotalMinutes >= HardTimeStopMinutes)
{
ClosePosition(pos);
Print($"DESK RULE: Hard Time Stop Triggered at {Math.Round(heldDuration.TotalMinutes, 1)}m. Position Flattened.");
}
}
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Slippage during CPI: second-by-second fill log analysis
Why this architecture excels in cTrader
Stopwatch Precision: MQL relies on GetTickCount(), which measures system uptime and can occasionally drift. Because cAlgo is pure C#, we use the native .NET System.Diagnostics.Stopwatch, which taps directly into the high-resolution performance counter of the CPU. It is the most accurate way to measure your broker's true routing latency.
Synchronous Trapping: ExecuteMarketOrder freezes the thread until the broker responds. Wrapping the stopwatch precisely around this single method guarantees the millisecond reading is pure network/broker latency.
The CSV Automation: Instead of manually maintaining your high-impact execution sheet, this script writes [Time, Symbol, Direction, Intent, Fill, Spread, Slippage, LatencyMs] to your hard drive every time a ticket is filled.
Once you have ten or twenty prints logged in that CSV, you don't just have a sense of your slippage—you have hard data to calculate the exact mathematical expectancy of the setup after the broker takes their cut.
Stopwatch Precision: MQL relies on GetTickCount(), which measures system uptime and can occasionally drift. Because cAlgo is pure C#, we use the native .NET System.Diagnostics.Stopwatch, which taps directly into the high-resolution performance counter of the CPU. It is the most accurate way to measure your broker's true routing latency.
Synchronous Trapping: ExecuteMarketOrder freezes the thread until the broker responds. Wrapping the stopwatch precisely around this single method guarantees the millisecond reading is pure network/broker latency.
The CSV Automation: Instead of manually maintaining your high-impact execution sheet, this script writes [Time, Symbol, Direction, Intent, Fill, Spread, Slippage, LatencyMs] to your hard drive every time a ticket is filled.
Once you have ten or twenty prints logged in that CSV, you don't just have a sense of your slippage—you have hard data to calculate the exact mathematical expectancy of the setup after the broker takes their cut.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
-
LondonScalper
- Posts: 529
- Joined: Sat Sep 05, 2026 7:54 am
Re: Slippage during CPI: second-by-second fill log analysis
Agreed — the stress-sheet follow-ups are the right direction. Planned versus fill, spread, and seconds from the print beat any clean backtest candle.PTScalper wrote:You are absolutely right: the fill log is the only ground truth. If a setup cannot survive a four-point slip on an M1 breakout, it is not a viable high-impact strategy but a theoretical artifact.
My CPI sheet logs planned entry, requested, fill, and mid at release. Slip is tagged in points separately from spread so all-in cost is visible. Under four points of adverse slip on an M1 breakout, most tickets stop being trades — the stop no longer sits where you drew it. Half-size and a hard time stop help; they do not rescue an edge smaller than the cost.
Desk rule: if fill logs show median adverse slip of 4 pts or more in the first 60 seconds, that M1 breakout is retired for Tier-1 releases. Flat or pre-written half-size only when prior data supports it — never fade the first spike on hope.
Viability: edge after p50 slip and spread, or no ticket. In your Pine run, what share of M1 breakouts still clear minimum R after a forced 40-tick slip?