Advertisement IC Markets

How to ask support for historical spread data and what they send

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
LondonScalper
Posts: 701
Joined: Sat Sep 05, 2026 7:54 am

How to ask support for historical spread data and what they send

Post by LondonScalper »

Practical note on asking support for spread history -- expectations vs reality.

I occasionally request historical spread or execution stats when I am reviewing a pair or considering a change. The ask matters; vague "send me spreads" gets a vague reply.

What I request
1. Symbol, account type, date range, and session window (e.g. London 07:00-10:00)
2. Prefer CSV or clear timestamps over a marketing PDF
3. Clarify whether figures are bid-ask width, markup included, or something else

What I usually get
Sometimes a useful export. Sometimes averages that hide the open. Sometimes a polite deflection to the economic calendar. I log whatever arrives and still trust my own click-to-fill samples more for decisions.

Support data is a supplement, not a verdict. If they cannot provide anything usable after two polite tries, that itself is information about how I will be treated in a dispute later.

What have you actually received when you asked -- usable ticks, or brochure numbers?

Hi LondonScalper,

As a C# developer, you are going to hit a hard architectural wall with TradingView: Pine Script runs in a sandboxed, cloud-hosted environment. There is no System.IO, no file streams, and no background tick processing while your browser is closed.

Furthermore, TradingView's backend databases do not store historical bid/ask tick data. Historical bars only store OHLC based on the Last Traded Price (or an aggregated midpoint, depending on the feed). syminfo.ask and syminfo.bid only return data for the live, real-time bar.

To replicate the cTrader logger in TradingView, you have to use a workaround:

Run the script on a 1-second chart (requires a Premium plan) and leave the window open during the session.

Plot the live spread values to the chart's Data Window.

Use TradingView's native "Export chart data..." UI feature to download the resulting CSV.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

Here is the Pine Script (v5) to track and format that data so your export is clean.

Code: Select all

//@version=5
indicator("Pro Spread Tracker [Exportable]", overlay=false, precision=2)

// =====================================================================
// Inputs
// =====================================================================
grp_session = "Session Filter"
session_window = input.session("0700-1000", title="Session Time", group=grp_session)
session_tz     = input.string("UTC", title="Timezone", group=grp_session)

// =====================================================================
// Logic
// =====================================================================
// 1. Check if the current bar is within the targeted session
in_session = not na(time(timeframe.period, session_window, session_tz))

// 2. Fetch live Bid/Ask. 
// NOTE: This will return 'na' on historical bars. It only populates live.
live_bid = syminfo.bid
live_ask = syminfo.ask

// 3. Calculate spread in standard Pips
// Standardizes calculation whether it's a 5-digit Forex pair or an Equity
is_forex = syminfo.type == "forex"
pip_multiplier = is_forex ? 10 : 1
pip_size = syminfo.mintick * pip_multiplier

spread_pips = (live_ask - live_bid) / pip_size

// =====================================================================
// Plotting for CSV Export
// =====================================================================
// We only plot the value if we are in the session; otherwise we plot 'na' 
// so out-of-session rows are blank in your CSV.
export_spread = in_session ? spread_pips : na
export_bid    = in_session ? live_bid : na
export_ask    = in_session ? live_ask : na

plot(export_spread, title="Spread (Pips)", color=color.new(color.blue, 0), style=plot.style_linebr)
plot(export_bid, title="Bid", display=display.data_window)
plot(export_ask, title="Ask", display=display.data_window)

// =====================================================================
// UI Table (Optional: Just to see it running on the chart)
// =====================================================================
var table stats = table.new(position.top_right, 2, 2, border_width=1)
if barstate.isrealtime and in_session
    table.cell(stats, 0, 0, "Live Spread:", text_color=color.gray)
    table.cell(stats, 1, 0, str.tostring(spread_pips, "#.##") + " pips", text_color=color.white)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

How to get your CSV out of TradingView:

1.) Apply this script to a 1-second or 1-minute chart.

2.) Let the chart run live through your targeted session (e.g., London Open).

3.) Once the session is over, click the App Menu (three lines) in the top left of TradingView.

4.) Go to Export chart data...

5.) Select the "Pro Spread Tracker" indicator and click Export.

6.) This generates a CSV with time, Spread (Pips), Bid, Ask.

A final note on data integrity: TradingView data feeds (like FXCM, OANDA, or ICE) often provide composite/indicative pricing rather than the raw, executable order book you see in cTrader. If you are logging this to dispute execution quality or markup spikes, the cTrader C# script connected to your exact broker feed will always carry more evidentiary weight than a TradingView export.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

To make this truly "Pro" in TradingView, we need to think like a systems architect bypassing a sandbox. Since TradingView won't let you write to a local file stream, the enterprise solution is to stream the data out of TradingView using JSON over Webhooks, while simultaneously maintaining an in-memory state to track the exact spread spikes (the "max spread") that brokers try to hide.

As a C# developer, you can easily spin up an ASP.NET Core Minimal API locally to catch these webhooks and write them to your SQL server or CSV, completely automating the process.

Here is the Enterprise-grade Pine Script.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

What makes this "Pro":

Webhook JSON Export: It formats real-time tick data as a JSON payload and fires it via the alert() function. You can pipe this to a webhook to log data entirely outside of TradingView.

In-Memory Session Aggregation: It persists state using var to track the Absolute Max, Absolute Min, and Session Average spread. If a broker spikes the spread for 300 milliseconds during a liquidity sweep, this script catches and holds it on screen.

Smart Pip Normalization: It dynamically adjusts the multiplier not just for Forex, but for Metals (Gold/Silver) and Equities, which is crucial if you are trading across different asset classes.

Institutional Dashboard: It renders a clean, real-time telemetry table on the chart.

Code: Select all

//@version=5
indicator("Enterprise Spread Telemetry", overlay=true, precision=2)

// =====================================================================
// Inputs
// =====================================================================
grp_sess = "Session Filter"
session_window = input.session("0700-1000", title="Session Time (UTC)", group=grp_sess)

grp_alerts = "JSON Webhook Export"
enable_alerts = input.bool(false, title="Fire Alerts (For Webhooks)", group=grp_alerts)
alert_freq = input.int(5, title="Alert Frequency (Seconds)", minval=1, group=grp_alerts, tooltip="Limits I/O to prevent TV rate-limiting")

grp_ui = "Dashboard"
warn_threshold = input.float(2.0, title="Spread Warning Threshold (Pips)", group=grp_ui)

// =====================================================================
// State Variables (Persistent across real-time ticks)
// =====================================================================
var float max_spread = 0.0
var float min_spread = 9999.0
var float sum_spread = 0.0
var int   tick_count = 0
var int   last_alert_time = 0
var bool  is_new_session = true

// =====================================================================
// Core Logic
// =====================================================================
in_session = not na(time(timeframe.period, session_window, "UTC"))

// Reset session stats when we cross into a new session
if in_session and not in_session[1]
    max_spread := 0.0
    min_spread := 9999.0
    sum_spread := 0.0
    tick_count := 0
    is_new_session := true

// Auto-detect asset class for accurate pip/point calculation (Forex, Metals, Equities)
get_pip_multiplier() =>
    sym = syminfo.ticker
    is_jpy = str.contains(sym, "JPY")
    is_gold_silver = str.contains(sym, "XAU") or str.contains(sym, "XAG")
    
    multiplier = 1.0
    if syminfo.type == "forex"
        multiplier := is_jpy ? 100 : 10000
    else if is_gold_silver
        multiplier := 10
    else
        multiplier := 1 // Equities / Crypto default
    multiplier

live_bid = syminfo.bid
live_ask = syminfo.ask

// Only calculate and update state on REAL-TIME bars within the session
spread_pips = 0.0
if barstate.isrealtime and in_session and not na(live_bid) and not na(live_ask)
    spread_pips := (live_ask - live_bid) * get_pip_multiplier()
    
    max_spread := math.max(max_spread, spread_pips)
    min_spread := math.min(min_spread, spread_pips)
    sum_spread += spread_pips
    tick_count += 1

avg_spread = tick_count > 0 ? (sum_spread / tick_count) : 0.0

// =====================================================================
// JSON Webhook Alerting
// =====================================================================
if barstate.isrealtime and in_session and enable_alerts
    // Throttle alerts to respect TradingView's rate limits
    if (timenow - last_alert_time) >= (alert_freq * 1000)
        
        // Construct JSON Payload
        json_payload = '{"timestamp": "' + str.tostring(timenow) + '", ' +
                       '"symbol": "' + syminfo.ticker + '", ' +
                       '"bid": ' + str.tostring(live_bid) + ', ' +
                       '"ask": ' + str.tostring(live_ask) + ', ' +
                       '"spread_pips": ' + str.tostring(spread_pips, "#.##") + '}'
        
        alert(json_payload, alert.freq_all)
        last_alert_time := timenow

// =====================================================================
// Institutional Dashboard
// =====================================================================
var table dash = table.new(position.bottom_right, 2, 5, border_width=1, border_color=color.new(color.gray, 80), frame_color=color.new(color.gray, 80), frame_width=1)

if barstate.isrealtime
    status_color = in_session ? color.new(color.green, 20) : color.new(color.gray, 50)
    spread_color = spread_pips >= warn_threshold ? color.new(color.red, 10) : color.new(color.white, 0)

    table.cell(dash, 0, 0, "Telemetry", text_color=color.gray, text_size=size.small, bgcolor=color.new(color.black, 0))
    table.cell(dash, 1, 0, in_session ? "ACTIVE" : "OUT OF SESSION", text_color=status_color, text_size=size.small, bgcolor=color.new(color.black, 0))
    
    table.cell(dash, 0, 1, "Live Spread", text_color=color.gray, text_size=size.normal, bgcolor=color.new(color.black, 0))
    table.cell(dash, 1, 1, str.tostring(spread_pips, "#.##") + " pips", text_color=spread_color, text_size=size.normal, bgcolor=color.new(color.black, 0))
    
    table.cell(dash, 0, 2, "Session Max", text_color=color.gray, text_size=size.small, bgcolor=color.new(color.black, 0))
    table.cell(dash, 1, 2, min_spread == 9999.0 ? "-" : str.tostring(max_spread, "#.##"), text_color=color.new(color.red, 30), text_size=size.small, bgcolor=color.new(color.black, 0))
    
    table.cell(dash, 0, 3, "Session Min", text_color=color.gray, text_size=size.small, bgcolor=color.new(color.black, 0))
    table.cell(dash, 1, 3, min_spread == 9999.0 ? "-" : str.tostring(min_spread, "#.##"), text_color=color.new(color.green, 30), text_size=size.small, bgcolor=color.new(color.black, 0))
    
    table.cell(dash, 0, 4, "Session Avg", text_color=color.gray, text_size=size.small, bgcolor=color.new(color.black, 0))
    table.cell(dash, 1, 4, min_spread == 9999.0 ? "-" : str.tostring(avg_spread, "#.##"), text_color=color.new(color.yellow, 30), text_size=size.small, bgcolor=color.new(color.black, 0))
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

How to complete the pipeline:

In TradingView: Add this script to a 1-second chart. In the settings, check "Fire Alerts". Create a TradingView Alert, select this script, choose "Any alert() function call", and paste your Webhook URL into the Notifications tab.

In C# / ASP.NET Core: Write a simple API endpoint [HttpPost("api/ticks")] that accepts the JSON payload, deserializes it, and maps it directly to a local MS SQL Server table or appends it to a CSV using standard System.IO streams.

This setup bridges the gap. It gives you immediate visual analytics directly on your TradingView chart (highlighting those momentary spread blowouts in red on the dashboard) while silently funneling the raw tick data to your own local infrastructure via the webhook.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

In MetaTrader, writing naive file I/O inside OnTick() will degrade terminal execution speed during volatile bursts. Furthermore, MetaTrader sandboxes file operations inside randomized terminal data paths (AppData\Roaming\MetaQuotes\Terminal\<GUID>\...).

The solution below is a universal, cross-compatible Expert Advisor that compiles directly in both MT4 and MT5 (.mq4 and .mq5).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

Enterprise Design Highlights:

FILE_COMMON Integration: Writes to the global MetaQuotes Common folder (Terminal\Common\Files). This bypasses instance GUID folders so external programs (Python, C#, Excel) can read data from multiple terminals in one predictable directory.

Batched I/O & FILE_SHARE_READ: Keeps a single persistent handle open with shared read access and flushes buffers every N ticks via FileFlush().

Daily Partitioning: Automatically detects UTC rollover at 00:00, safely flushes, and rotates the CSV name (EURUSD_SpreadLog_20260924.csv).

Sub-Second Precision: Captures milliseconds via MqlTick.time_msc.

Real-Time Spread Telemetry: Tracks session Min, Max, and Moving Average spread, updating a zero-latency HUD via Comment().
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

Universal Source Code (SpreadLoggerEA.mq4 / SpreadLoggerEA.mq5)

Code: Select all

//+------------------------------------------------------------------+
//|                                             SpreadLoggerEA.mq4/5 |
//|                                  Universal MT4 & MT5 Tick Logger |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link      ""
#property version   "2.00"
#property strict

//--- Input Parameters
input string   InpGroupGeneral      = "=== General Settings ==="; // ---
input bool     InpEnableLogging     = true;                       // Enable Logging
input bool     InpUseCommonFolder   = true;                       // Save to Terminal/Common/Files

input string   InpGroupSession      = "=== Session Window (UTC) ==="; // ---
input bool     InpUseSession        = true;                       // Filter by Session
input string   InpSessionStart      = "07:00:00";                 // Start Time (HH:mm:ss)
input string   InpSessionEnd        = "10:00:00";                 // End Time (HH:mm:ss)

input string   InpGroupIO           = "=== I/O Optimization ==="; // ---
input int      InpFlushInterval     = 100;                        // Disk Flush Interval (Ticks)
input double   InpWarnThresholdPips = 2.0;                        // Spread Alert Threshold (Pips)

//--- Internal State
int      g_fileHandle       = INVALID_HANDLE;
string   g_currentFileDate  = "";
int      g_ticksSinceFlush  = 0;
double   g_lastBid          = 0.0;
double   g_lastAsk          = 0.0;

//--- Session Statistics
double   g_sessionMinSpread = 9999.0;
double   g_sessionMaxSpread = 0.0;
double   g_sessionSumSpread = 0.0;
ulong    g_sessionTickCount = 0;
bool     g_wasInSession     = false;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   EventSetTimer(1); // Timer for UI telemetry refresh during low-liquidity
   Print(StringFormat("[SpreadLogger] Initialized on %s. Common Folder: %s", 
                      _Symbol, InpUseCommonFolder ? "TRUE" : "FALSE"));
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   EventKillTimer();
   CloseLogFile();
   Comment("");
   Print("[SpreadLogger] Cleaned up and file stream flushed.");
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   MqlTick tick;
   if(!SymbolInfoTick(_Symbol, tick))
      return;

   // 1. Prevent writing duplicate redundant quotes
   if(tick.bid == g_lastBid && tick.ask == g_lastAsk)
      return;

   datetime gmtNow = TimeGMT();
   bool inSession = IsWithinSession(gmtNow, InpSessionStart, InpSessionEnd);

   // 2. Reset session statistics on session entry boundary
   if(inSession && !g_wasInSession)
   {
      g_sessionMinSpread = 9999.0;
      g_sessionMaxSpread = 0.0;
      g_sessionSumSpread = 0.0;
      g_sessionTickCount = 0;
   }
   g_wasInSession = inSession;

   // 3. Normalized spread calculation
   double pipSize = GetPipUnit();
   double spreadPips = (pipSize > 0) ? (tick.ask - tick.bid) / pipSize : 0.0;

   // 4. Update session telemetry
   if(inSession)
   {
      g_sessionMaxSpread = MathMax(g_sessionMaxSpread, spreadPips);
      g_sessionMinSpread = MathMin(g_sessionMinSpread, spreadPips);
      g_sessionSumSpread += spreadPips;
      g_sessionTickCount++;
   }

   // 5. Append to I/O Stream
   if(InpEnableLogging && (!InpUseSession || inSession))
   {
      EnsureFileReady(gmtNow);

      if(g_fileHandle != INVALID_HANDLE)
      {
         int ms = (int)(tick.time_msc % 1000);
         string timeStr = StringFormat("%s.%03d", TimeToString(gmtNow, TIME_DATE|TIME_SECONDS), ms);

         FileWrite(g_fileHandle, timeStr, _Symbol, 
                   DoubleToString(tick.bid, _Digits), 
                   DoubleToString(tick.ask, _Digits), 
                   DoubleToString(spreadPips, 2));

         g_ticksSinceFlush++;
         if(g_ticksSinceFlush >= InpFlushInterval)
         {
            FileFlush(g_fileHandle);
            g_ticksSinceFlush = 0;
         }
      }
   }

   g_lastBid = tick.bid;
   g_lastAsk = tick.ask;

   UpdateDashboard(spreadPips, inSession);
}

//+------------------------------------------------------------------+
//| Timer event for dashboard refresh                                |
//+------------------------------------------------------------------+
void OnTimer()
{
   MqlTick tick;
   if(SymbolInfoTick(_Symbol, tick))
   {
      double pipSize = GetPipUnit();
      double spreadPips = (pipSize > 0) ? (tick.ask - tick.bid) / pipSize : 0.0;
      UpdateDashboard(spreadPips, IsWithinSession(TimeGMT(), InpSessionStart, InpSessionEnd));
   }
}

//+------------------------------------------------------------------+
//| Stream Management & File Rolling                                 |
//+------------------------------------------------------------------+
void EnsureFileReady(datetime gmtNow)
{
   string dateToday = TimeToString(gmtNow, TIME_DATE);
   StringReplace(dateToday, ".", "");

   // Already open and matching current date
   if(dateToday == g_currentFileDate && g_fileHandle != INVALID_HANDLE)
      return;

   // Date rollover: close current stream cleanly
   CloseLogFile();

   g_currentFileDate = dateToday;
   string fileName = StringFormat("%s_SpreadLog_%s.csv", _Symbol, g_currentFileDate);

   int flags = FILE_CSV | FILE_READ | FILE_WRITE | FILE_SHARE_READ;
   if(InpUseCommonFolder)
      flags |= FILE_COMMON;

   bool fileExists = FileIsExist(fileName, InpUseCommonFolder ? FILE_COMMON : 0);

   g_fileHandle = FileOpen(fileName, flags, ",");
   if(g_fileHandle != INVALID_HANDLE)
   {
      if(!fileExists)
      {
         FileWrite(g_fileHandle, "Timestamp(UTC)", "Symbol", "Bid", "Ask", "Spread(Pips)");
         FileFlush(g_fileHandle);
      }
      else
      {
         FileSeek(g_fileHandle, 0, SEEK_END);
      }
   }
   else
   {
      Print(StringFormat("[SpreadLogger] Error opening file: %d", GetLastError()));
   }
}

void CloseLogFile()
{
   if(g_fileHandle != INVALID_HANDLE)
   {
      FileFlush(g_fileHandle);
      FileClose(g_fileHandle);
      g_fileHandle = INVALID_HANDLE;
   }
}

//+------------------------------------------------------------------+
//| Time & Math Utilities                                            |
//+------------------------------------------------------------------+
bool IsWithinSession(datetime time, string startStr, string endStr)
{
   datetime sod = StringToTime(TimeToString(time, TIME_DATE));
   int currentSec = (int)(time - sod);

   int startSec = ParseTimeToSeconds(startStr);
   int endSec   = ParseTimeToSeconds(endStr);

   if(startSec <= endSec)
      return (currentSec >= startSec && currentSec <= endSec);

   // Supports rollover crossing midnight (e.g., 22:00 to 02:00)
   return (currentSec >= startSec || currentSec <= endSec);
}

int ParseTimeToSeconds(string timeStr)
{
   string parts[];
   int count = StringSplit(timeStr, ':', parts);
   int h = (count > 0) ? (int)StringToInteger(parts[0]) : 0;
   int m = (count > 1) ? (int)StringToInteger(parts[1]) : 0;
   int s = (count > 2) ? (int)StringToInteger(parts[2]) : 0;
   return (h * 3600) + (m * 60) + s;
}

double GetPipUnit()
{
   if(_Digits == 3 || _Digits == 5)
      return _Point * 10.0;
   return _Point;
}

//+------------------------------------------------------------------+
//| Lightweight Head-Up Display                                      |
//+------------------------------------------------------------------+
void UpdateDashboard(double liveSpread, bool inSession)
{
   double avg = (g_sessionTickCount > 0) ? (g_sessionSumSpread / (double)g_sessionTickCount) : 0.0;
   string warn = (liveSpread >= InpWarnThresholdPips) ? " [! HIGH SPREAD !]" : "";

   string hud = "\n" +
      "-------------------------------------------\n" +
      StringFormat(" SPREAD TELEMETRY: %s\n", _Symbol) +
      "-------------------------------------------\n" +
      StringFormat(" Status       : %s\n", inSession ? "LOGGING (ACTIVE)" : "OUT OF SESSION") +
      StringFormat(" Live Spread  : %.2f pips%s\n", liveSpread, warn) +
      StringFormat(" Session Min  : %.2f pips\n", g_sessionMinSpread == 9999.0 ? 0.0 : g_sessionMinSpread) +
      StringFormat(" Session Max  : %.2f pips\n", g_sessionMaxSpread) +
      StringFormat(" Session Avg  : %.2f pips\n", avg) +
      StringFormat(" Ticks Logged : %I64u\n", g_sessionTickCount) +
      "-------------------------------------------";

   Comment(hud);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3027
Joined: Mon Jul 20, 2026 1:28 pm

Re: How to ask support for historical spread data and what they send

Post by PTScalper »

Where MetaTrader Saves Your Files

When InpUseCommonFolder = true is enabled, the EA logs to:

Code: Select all

C:\Users\<YourUsername>\AppData\Roaming\MetaQuotes\Terminal\Common\Files\
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply