Page 2 of 2
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:12 pm
by PTScalper
MQL4 Implementation
Save this script as TrueExecutionCost.mq4 inside your MT4 MQL4/Indicators/ directory.
Code: Select all
#property copyright "Custom"
#property link ""
#property version "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 clrGray
#property indicator_color2 clrOrange
#property indicator_width1 1
#property indicator_width2 2
#property indicator_label1 "Raw Spread (Pips)"
#property indicator_label2 "All-In Cost (Pips)"
//--- Inputs
input string InpCostsGroup = "--- Cost Settings ---";
input double InpCommissionPerLot = 6.0; // Round-turn Commission per Lot ($)
input double InpPipValuePerLot = 10.0; // Pip Value per Lot ($)
input string InpTimeGroup = "--- Time Offset (Hours) ---";
input int InpBrokerGmtOffset = 2; // Broker Server GMT Offset (e.g., 2 or 3)
input int InpLondonGmtOffset = 0; // London GMT Offset (0 in Winter, 1 in Summer)
//--- Buffers
double SpreadBuffer[];
double AllInCostBuffer[];
double g_pipSize;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorDigits(2);
SetIndexBuffer(0, SpreadBuffer);
SetIndexStyle(0, DRAW_LINE);
SetIndexBuffer(1, AllInCostBuffer);
SetIndexStyle(1, DRAW_LINE);
// Determine 1 pip based on instrument digits
g_pipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment(""); // Clear chart HUD
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total <= 0 || g_pipSize <= 0) return(0);
// Only calculate on live incoming ticks
int lastBar = rates_total - 1;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double commPips = (InpPipValuePerLot > 0) ? (InpCommissionPerLot / InpPipValuePerLot) : 0.0;
double currentSpread = (ask - bid) / g_pipSize;
double allInCost = currentSpread + commPips;
SpreadBuffer[lastBar] = currentSpread;
AllInCostBuffer[lastBar] = allInCost;
// Update HUD Dashboard
UpdateDashboard(currentSpread, commPips, allInCost);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Session Evaluation & Dashboard HUD |
//+------------------------------------------------------------------+
void UpdateDashboard(double spread, double comm, double totalCost)
{
datetime serverTime = TimeCurrent();
datetime londonTime = serverTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
MqlDateTime dt;
TimeToStruct(londonTime, dt);
int totalMinutes = (dt.hour * 60) + dt.min;
string sessionText = "Off-Peak / Outside Window";
// Asian Session: 00:30 (30m) to 03:00 (180m)
if(totalMinutes >= 30 && totalMinutes <= 180)
sessionText = "Asian (00:30 - 03:00 London)";
// London Session: 07:00 (420m) to 10:00 (600m)
else if(totalMinutes >= 420 && totalMinutes <= 600)
sessionText = "London (07:00 - 10:00 London)";
string dashboard = StringFormat(
"=== TRUE EXECUTION COST ===\n" +
"Session Window : %s\n" +
"London Time : %02d:%02d\n" +
"Real-Time Spread : %.2f pips\n" +
"Commission (RT) : %.2f pips\n" +
"Total All-In Cost: %.2f pips",
sessionText, dt.hour, dt.min, spread, comm, totalCost
);
Comment(dashboard);
}
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:12 pm
by PTScalper
MQL5 Implementation
Save this script as TrueExecutionCost.mq5 inside your MT5 MQL5/Indicators/ directory.
Code: Select all
#property copyright "Custom"
#property link ""
#property version "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots 2
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrGray
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
#property indicator_label1 "Raw Spread (Pips)"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrange
#property indicator_style2 STYLE_SOLID
#property indicator_width2 2
#property indicator_label2 "All-In Cost (Pips)"
//--- Inputs
input group "Cost Settings"
input double InpCommissionPerLot = 6.0; // Round-turn Commission per Lot ($)
input double InpPipValuePerLot = 10.0; // Pip Value per Lot ($)
input group "Time Offset (Hours)"
input int InpBrokerGmtOffset = 2; // Broker Server GMT Offset (e.g., 2 or 3)
input int InpLondonGmtOffset = 0; // London GMT Offset (0 in Winter, 1 in Summer)
//--- Buffers
double SpreadBuffer[];
double AllInCostBuffer[];
double g_pipSize;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorSetInteger(INDICATOR_DIGITS, 2);
SetIndexBuffer(0, SpreadBuffer, INDICATOR_DATA);
SetIndexBuffer(1, AllInCostBuffer, INDICATOR_DATA);
// Determine 1 pip based on instrument digits
g_pipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;
// Empty value configuration so historical bars without live ticks stay unplotted
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment(""); // Clear chart HUD
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total <= 0 || g_pipSize <= 0) return(0);
// Initialize historical bars to empty value when the indicator loads
if(prev_calculated == 0)
{
ArrayInitialize(SpreadBuffer, 0.0);
ArrayInitialize(AllInCostBuffer, 0.0);
}
int lastBar = rates_total - 1;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double commPips = (InpPipValuePerLot > 0) ? (InpCommissionPerLot / InpPipValuePerLot) : 0.0;
double currentSpread = (ask - bid) / g_pipSize;
double allInCost = currentSpread + commPips;
SpreadBuffer[lastBar] = currentSpread;
AllInCostBuffer[lastBar] = allInCost;
// Update HUD Dashboard
UpdateDashboard(currentSpread, commPips, allInCost);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Session Evaluation & Dashboard HUD |
//+------------------------------------------------------------------+
void UpdateDashboard(double spread, double comm, double totalCost)
{
datetime serverTime = TimeCurrent();
datetime londonTime = serverTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
MqlDateTime dt;
TimeToStruct(londonTime, dt);
int totalMinutes = (dt.hour * 60) + dt.min;
string sessionText = "Off-Peak / Outside Window";
// Asian Session: 00:30 (30m) to 03:00 (180m)
if(totalMinutes >= 30 && totalMinutes <= 180)
sessionText = "Asian (00:30 - 03:00 London)";
// London Session: 07:00 (420m) to 10:00 (600m)
else if(totalMinutes >= 420 && totalMinutes <= 600)
sessionText = "London (07:00 - 10:00 London)";
string dashboard = StringFormat(
"=== TRUE EXECUTION COST ===\n" +
"Session Window : %s\n" +
"London Time : %02d:%02d\n" +
"Real-Time Spread : %.2f pips\n" +
"Commission (RT) : %.2f pips\n" +
"Total All-In Cost: %.2f pips",
sessionText, dt.hour, dt.min, spread, comm, totalCost
);
Comment(dashboard);
}
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:12 pm
by PTScalper
Implementation Details
Live Feed Handling: MetaTrader bar history stores integer points from historical candle closes rather than sub-pip tick floating spreads. Both scripts initialize historical bars to empty/zero and plot the subwindow line forward only on incoming live market ticks to ensure calculations remain uncorrupted.
Pip Normalization: Both scripts detect 3-digit and 5-digit quotes (_Digits == 3 || _Digits == 5) and scale _Point by 10.0 so fractional pip calculations are displayed in standard pips.
Time Alignment: Most retail MetaTrader brokers run server clocks set to Eastern European Time (GMT+2 in winter, GMT+3 in summer). The inputs InpBrokerGmtOffset and InpLondonGmtOffset allow exact conversion to London clock time to match the session boundaries you tracked.
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:13 pm
by PTScalper
Here is the updated MQL5 implementation.
To handle high-frequency tick data safely without freezing your terminal, this version introduces two critical structural changes:
File Handle Management: Instead of opening and closing the CSV file on every single tick (which causes severe I/O lag), the file is opened once when you attach the indicator and held in memory. FileFlush() is used after every write so you can open the CSV in Excel while MT5 is still running, without data corruption.
Alert Rate-Limiting: If the spread hovers above your threshold, generating a popup alert on every single tick will instantly crash your terminal. A cooldown timer (default 60 seconds) is applied to the visual popups, while the CSV continues to log every violating tick silently.
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:13 pm
by PTScalper
Updated MQL5 Script (TrueExecutionCost_Logger.mq5)
Code: Select all
#property copyright "Custom"
#property link ""
#property version "1.10"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots 2
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrGray
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
#property indicator_label1 "Raw Spread (Pips)"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrange
#property indicator_style2 STYLE_SOLID
#property indicator_width2 2
#property indicator_label2 "All-In Cost (Pips)"
//--- Inputs
input group "Cost Settings"
input double InpCommissionPerLot = 6.0; // Round-turn Commission per Lot ($)
input double InpPipValuePerLot = 10.0; // Pip Value per Lot ($)
input group "Time Offset (Hours)"
input int InpBrokerGmtOffset = 2; // Broker Server GMT Offset
input int InpLondonGmtOffset = 0; // London GMT Offset (0=Winter, 1=Summer)
input group "Spike Alerts & Tick Logging"
input double InpSpikeThreshold = 1.5; // Spread Spike Threshold (Pips)
input bool InpEnableAlerts = true; // Enable Visual Popup Alerts
input int InpAlertCooldown = 60; // Alert Cooldown (Seconds)
input bool InpEnableLogging = true; // Enable CSV Tick Logging
input bool InpLogAsianOnly = true; // Only Log/Alert during Asian Session
input string InpLogFileName = "SpreadSpikes.csv"; // CSV File Name
//--- Buffers
double SpreadBuffer[];
double AllInCostBuffer[];
double g_pipSize;
int g_fileHandle = INVALID_HANDLE;
datetime g_lastAlertTime = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorSetInteger(INDICATOR_DIGITS, 2);
SetIndexBuffer(0, SpreadBuffer, INDICATOR_DATA);
SetIndexBuffer(1, AllInCostBuffer, INDICATOR_DATA);
g_pipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
// Initialize CSV Logging
if(InpEnableLogging)
{
// Open with FILE_SHARE_READ so you can view the file in Excel while MT5 runs
g_fileHandle = FileOpen(InpLogFileName, FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI|FILE_SHARE_READ, ",");
if(g_fileHandle != INVALID_HANDLE)
{
FileSeek(g_fileHandle, 0, SEEK_END);
// Write header if the file is brand new
if(FileSize(g_fileHandle) == 0)
{
FileWrite(g_fileHandle, "Time(Broker)", "Time(London)", "Session", "Ask", "Bid", "Spread(Pips)");
FileFlush(g_fileHandle);
}
}
else
{
Print("Failed to open CSV file for logging. Error: ", GetLastError());
}
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment(""); // Clear chart HUD
if(g_fileHandle != INVALID_HANDLE)
{
FileClose(g_fileHandle);
}
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total <= 0 || g_pipSize <= 0) return(0);
if(prev_calculated == 0)
{
ArrayInitialize(SpreadBuffer, 0.0);
ArrayInitialize(AllInCostBuffer, 0.0);
}
int lastBar = rates_total - 1;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double commPips = (InpPipValuePerLot > 0) ? (InpCommissionPerLot / InpPipValuePerLot) : 0.0;
double currentSpread = (ask - bid) / g_pipSize;
double allInCost = currentSpread + commPips;
SpreadBuffer[lastBar] = currentSpread;
AllInCostBuffer[lastBar] = allInCost;
string activeSession = GetCurrentSession();
// Handle Alerts and Logging
ProcessSpikeTracking(currentSpread, ask, bid, activeSession);
// Update HUD
UpdateDashboard(currentSpread, commPips, allInCost, activeSession);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Evaluates tick data for alerts and CSV logging |
//+------------------------------------------------------------------+
void ProcessSpikeTracking(double spread, double ask, double bid, string sessionText)
{
if(spread < InpSpikeThreshold) return;
bool isAsianSession = (StringFind(sessionText, "Asian") >= 0);
// Enforce session restriction if enabled
if(InpLogAsianOnly && !isAsianSession) return;
datetime currentTime = TimeCurrent();
// 1. Visual Alert (Rate-Limited)
if(InpEnableAlerts && (currentTime - g_lastAlertTime >= InpAlertCooldown))
{
Alert(StringFormat("SPREAD SPIKE [%s]: %.2f pips (Ask: %.5f / Bid: %.5f)", _Symbol, spread, ask, bid));
g_lastAlertTime = currentTime;
}
// 2. CSV Tick Logging (Every Violating Tick)
if(InpEnableLogging && g_fileHandle != INVALID_HANDLE)
{
datetime londonTime = currentTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
string strBrokerTime = TimeToString(currentTime, TIME_DATE|TIME_SECONDS);
string strLondonTime = TimeToString(londonTime, TIME_DATE|TIME_SECONDS);
FileWrite(g_fileHandle, strBrokerTime, strLondonTime, sessionText,
DoubleToString(ask, _Digits), DoubleToString(bid, _Digits), DoubleToString(spread, 2));
// Force disk write immediately so data isn't lost in a crash
FileFlush(g_fileHandle);
}
}
//+------------------------------------------------------------------+
//| Retrieves the current session name based on offset |
//+------------------------------------------------------------------+
string GetCurrentSession()
{
datetime serverTime = TimeCurrent();
datetime londonTime = serverTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
MqlDateTime dt;
TimeToStruct(londonTime, dt);
int totalMinutes = (dt.hour * 60) + dt.min;
if(totalMinutes >= 30 && totalMinutes <= 180)
return "Asian Open (00:30-03:00)";
if(totalMinutes >= 420 && totalMinutes <= 600)
return "London Open (07:00-10:00)";
return "Off-Peak Window";
}
//+------------------------------------------------------------------+
//| Dashboard HUD Rendering |
//+------------------------------------------------------------------+
void UpdateDashboard(double spread, double comm, double totalCost, string sessionText)
{
datetime serverTime = TimeCurrent();
datetime londonTime = serverTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
MqlDateTime dt; TimeToStruct(londonTime, dt);
string dashboard = StringFormat(
"=== TRUE EXECUTION COST ===\n" +
"Session Window : %s\n" +
"London Time : %02d:%02d\n" +
"Real-Time Spread : %.2f pips\n" +
"Commission (RT) : %.2f pips\n" +
"Total All-In Cost: %.2f pips\n" +
"---------------------------\n" +
"Logging Active : %s (Threshold: %.1f)",
sessionText, dt.hour, dt.min, spread, comm, totalCost,
(InpEnableLogging ? "YES" : "NO"), InpSpikeThreshold
);
Comment(dashboard);
}
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:15 pm
by PTScalper
Where to Find Your Logged Data
MetaTrader runs a strictly sandboxed file system for security. Your CSV will not save to your desktop. To find the exported file:
1.) Open MT5 and go to File > Open Data Folder.
2.) Navigate into MQL5 > Files.
3.) You will find SpreadSpikes.csv here.
Because the code uses FILE_SHARE_READ, you can open this CSV in Excel while MT5 is running. As the Asian session unfolds, you can click "Refresh" in Excel to see the new ticks populate in real-time.
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:16 pm
by PTScalper
Here is the exact equivalent converted for MetaTrader 4 (MQL4).
Because MT4 handles indicator arrays (buffers) and file I/O slightly differently than MT5, this version has been adjusted so that historical bars remain properly empty, live ticks are plotted on the current [0] index (MT4's default timeseries behavior), and the CSV file is safely shared so you can watch it update in Excel.
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:16 pm
by PTScalper
MQL4 Implementation (TrueExecutionCost_Logger.mq4)
Save this inside your MT4 MQL4/Indicators/ directory.
Code: Select all
#property copyright "Custom"
#property link ""
#property version "1.10"
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 clrGray
#property indicator_color2 clrOrange
#property indicator_width1 1
#property indicator_width2 2
#property indicator_label1 "Raw Spread (Pips)"
#property indicator_label2 "All-In Cost (Pips)"
//--- Inputs
input string InpCostsGroup = "--- Cost Settings ---";
input double InpCommissionPerLot = 6.0; // Round-turn Commission per Lot ($)
input double InpPipValuePerLot = 10.0; // Pip Value per Lot ($)
input string InpTimeGroup = "--- Time Offset (Hours) ---";
input int InpBrokerGmtOffset = 2; // Broker Server GMT Offset
input int InpLondonGmtOffset = 0; // London GMT Offset (0=Winter, 1=Summer)
input string InpSpikeGroup = "--- Spike Alerts & Logging ---";
input double InpSpikeThreshold = 1.5; // Spread Spike Threshold (Pips)
input bool InpEnableAlerts = true; // Enable Visual Popup Alerts
input int InpAlertCooldown = 60; // Alert Cooldown (Seconds)
input bool InpEnableLogging = true; // Enable CSV Tick Logging
input bool InpLogAsianOnly = true; // Only Log/Alert during Asian Session
input string InpLogFileName = "SpreadSpikes_MT4.csv"; // CSV File Name
//--- Buffers
double SpreadBuffer[];
double AllInCostBuffer[];
double g_pipSize;
int g_fileHandle = INVALID_HANDLE;
datetime g_lastAlertTime = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorDigits(2);
// Set up Spread Buffer
SetIndexBuffer(0, SpreadBuffer);
SetIndexStyle(0, DRAW_LINE);
SetIndexEmptyValue(0, EMPTY_VALUE); // Prevents drawing flat zero lines on history
// Set up All-In Cost Buffer
SetIndexBuffer(1, AllInCostBuffer);
SetIndexStyle(1, DRAW_LINE);
SetIndexEmptyValue(1, EMPTY_VALUE);
// Calculate fractional pip size
g_pipSize = (_Digits == 3 || _Digits == 5) ? _Point * 10.0 : _Point;
// Initialize CSV Logging
if(InpEnableLogging)
{
// Open with FILE_SHARE_READ so you can view it in Excel while MT4 runs
g_fileHandle = FileOpen(InpLogFileName, FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI|FILE_SHARE_READ, ",");
if(g_fileHandle != INVALID_HANDLE)
{
FileSeek(g_fileHandle, 0, SEEK_END);
// Write header if the file is brand new
if(FileSize(g_fileHandle) == 0)
{
FileWrite(g_fileHandle, "Time(Broker)", "Time(London)", "Session", "Ask", "Bid", "Spread(Pips)");
FileFlush(g_fileHandle);
}
}
else
{
Print("Failed to open CSV file for logging. Error: ", GetLastError());
}
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment(""); // Clear chart HUD
if(g_fileHandle != INVALID_HANDLE)
{
FileClose(g_fileHandle);
}
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total <= 0 || g_pipSize <= 0) return(0);
// On first load, fill the entire history with EMPTY_VALUE so no line is drawn
if(prev_calculated == 0)
{
ArrayInitialize(SpreadBuffer, EMPTY_VALUE);
ArrayInitialize(AllInCostBuffer, EMPTY_VALUE);
}
// MQL4 indicator arrays are Timeseries by default. Index [0] is ALWAYS the live bar.
double commPips = (InpPipValuePerLot > 0) ? (InpCommissionPerLot / InpPipValuePerLot) : 0.0;
double currentSpread = (Ask - Bid) / g_pipSize;
double allInCost = currentSpread + commPips;
// Update the live bar only
SpreadBuffer[0] = currentSpread;
AllInCostBuffer[0] = allInCost;
string activeSession = GetCurrentSession();
// Handle Alerts and Logging
ProcessSpikeTracking(currentSpread, Ask, Bid, activeSession);
// Update HUD
UpdateDashboard(currentSpread, commPips, allInCost, activeSession);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Evaluates tick data for alerts and CSV logging |
//+------------------------------------------------------------------+
void ProcessSpikeTracking(double spread, double currentAsk, double currentBid, string sessionText)
{
if(spread < InpSpikeThreshold) return;
bool isAsianSession = (StringFind(sessionText, "Asian") >= 0);
// Enforce session restriction if enabled
if(InpLogAsianOnly && !isAsianSession) return;
datetime currentTime = TimeCurrent();
// 1. Visual Alert (Rate-Limited)
if(InpEnableAlerts && (currentTime - g_lastAlertTime >= InpAlertCooldown))
{
Alert(StringFormat("SPREAD SPIKE [%s]: %.2f pips (Ask: %.5f / Bid: %.5f)", _Symbol, spread, currentAsk, currentBid));
g_lastAlertTime = currentTime;
}
// 2. CSV Tick Logging (Every Violating Tick)
if(InpEnableLogging && g_fileHandle != INVALID_HANDLE)
{
datetime londonTime = currentTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
string strBrokerTime = TimeToString(currentTime, TIME_DATE|TIME_SECONDS);
string strLondonTime = TimeToString(londonTime, TIME_DATE|TIME_SECONDS);
FileWrite(g_fileHandle, strBrokerTime, strLondonTime, sessionText,
DoubleToString(currentAsk, _Digits), DoubleToString(currentBid, _Digits), DoubleToString(spread, 2));
// Force disk write immediately so data isn't lost if terminal crashes
FileFlush(g_fileHandle);
}
}
//+------------------------------------------------------------------+
//| Retrieves the current session name based on offset |
//+------------------------------------------------------------------+
string GetCurrentSession()
{
datetime serverTime = TimeCurrent();
datetime londonTime = serverTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
MqlDateTime dt;
TimeToStruct(londonTime, dt);
int totalMinutes = (dt.hour * 60) + dt.min;
if(totalMinutes >= 30 && totalMinutes <= 180)
return "Asian Open (00:30-03:00)";
if(totalMinutes >= 420 && totalMinutes <= 600)
return "London Open (07:00-10:00)";
return "Off-Peak Window";
}
//+------------------------------------------------------------------+
//| Dashboard HUD Rendering |
//+------------------------------------------------------------------+
void UpdateDashboard(double spread, double comm, double totalCost, string sessionText)
{
datetime serverTime = TimeCurrent();
datetime londonTime = serverTime - (InpBrokerGmtOffset * 3600) + (InpLondonGmtOffset * 3600);
MqlDateTime dt; TimeToStruct(londonTime, dt);
string dashboard = StringFormat(
"=== TRUE EXECUTION COST ===\n" +
"Session Window : %s\n" +
"London Time : %02d:%02d\n" +
"Real-Time Spread : %.2f pips\n" +
"Commission (RT) : %.2f pips\n" +
"Total All-In Cost: %.2f pips\n" +
"---------------------------\n" +
"Logging Active : %s (Threshold: %.1f)",
sessionText, dt.hour, dt.min, spread, comm, totalCost,
(InpEnableLogging ? "YES" : "NO"), InpSpikeThreshold
);
Comment(dashboard);
}
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Sun Oct 04, 2026 11:17 pm
by PTScalper
Where to find the MT4 Data Export
MetaTrader 4 sandboxes its file system differently than standard Windows applications.
1.) Open MT4 and go to File > Open Data Folder.
2.) Navigate to MQL4 > Files.
3.) You will find SpreadSpikes_MT4.csv here.
Because FileFlush() and FILE_SHARE_READ are utilized, the CSV file can remain open in Excel on your other monitor. As the rollover/Asian session executes, you can periodically hit "Refresh" in Excel to see the broker's spread spikes populate in real time without having to detach the indicator.
Re: Raw vs standard on AUDUSD: 30-day cost race
Posted: Mon Oct 05, 2026 7:50 am
by LondonScalper
The compounding frame is fair, and I understated it in the write-up. Thirty-five pips at half a lot looks small on a monthly statement; annualised at the same pace it is real money, and at full lots it stops being a rounding error.
Where I still land differently is which gap I care about most. The London window on those 118 tickets was a tidy 0.3-pip edge for raw. The 00:30–01:30 stretch was nearly double that, and that is the hour that decided the experiment for me. If someone only trades AUDUSD after 07:00 London, I would not fight them for keeping a standard account. My book is not that book.
I did not leave the standard login open as a control after the month. Once the fills looked identical and the cost line did not, keeping both terminals was just more noise on the desk.