Page 1 of 1

Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Tue Sep 22, 2026 2:50 pm
by LondonScalper
Apex versus raw IC Markets on EURUSD is a spread reality check I run with my own timestamps.

Review threads argue. My sheet lists median London spreads and how often the quote is actually usable for a short-target scalp. If Apex is wider in the hours I trade, my funded playbook shrinks even when the chart pattern is identical to the personal account.

Comparison hygiene
1. Same session windows, same pair, enough samples.
2. All-in cost mindset — commission and spread together.
3. News minutes separated so they do not define the "normal" median.

I am not here to crown a winner for the internet. I am here to size the venue I am actually clicking.

What has your Apex vs raw IC EURUSD cost comparison looked like in London hours?

I re-run the Apex vs IC sample after any platform update or account type change. Venues drift. A conclusion from last quarter is a hypothesis until the new sample agrees.

Partial fills and weekend maintenance notes sit in the same sheet. Spread medians alone can flatter an unstable path.

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:14 am
by FTtrader
LondonScalper wrote: Tue Sep 22, 2026 2:50 pm Apex versus raw IC Markets on EURUSD is a spread reality check I run with my own timestamps.

Review threads argue. My sheet lists median London spreads and how often the quote is actually usable for a short-target scalp. If Apex is wider in the hours I trade, my funded playbook shrinks even when the chart pattern is identical to the personal account.

Comparison hygiene
1. Same session windows, same pair, enough samples.
2. All-in cost mindset — commission and spread together.
3. News minutes separated so they do not define the "normal" median.

I am not here to crown a winner for the internet. I am here to size the venue I am actually clicking.

What has your Apex vs raw IC EURUSD cost comparison looked like in London hours?

I re-run the Apex vs IC sample after any platform update or account type change. Venues drift. A conclusion from last quarter is a hypothesis until the new sample agrees.

Partial fills and weekend maintenance notes sit in the same sheet. Spread medians alone can flatter an unstable path.
Hi LondonScalper,

Here is the baseline breakdown for Apex (CME 6E Futures) versus IC Markets (Raw EURUSD) during London overlap, followed by a Pine Script designed to visualize how this friction impacts short-target scalping.

The All-In Cost Math (London Median)
When you normalize both venues to a 100,000 EUR position size to strip away the futures vs. CFD sizing difference, the paper costs are nearly identical. The divergence happens in the execution mechanics.

IC Markets (Raw Spread CFD)

London Spread Median: ~0.1 pips ($1.00 per standard lot).

Commission: $7.00 round turn.

Total All-In Cost: ~$8.00 per 100k (Equivalent to 0.8 pips).

Execution Reality: Price is continuous. If the bid hits your target, you are filled. However, during momentum spikes, you absorb the liquidity gap. A 0.1 pip spread can instantly widen to 0.5, creating micro-slippage that degrades the theoretical 0.8 pip cost.

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:15 am
by FTtrader
Apex (CME 6E Futures)

London Spread Median: 1 tick strictly (0.00005, which is 0.5 pips). The 6E contract size is 125,000 EUR, making 1 tick worth $6.25.

Commission: ~$4.50 round turn (varies slightly by platform/rhythmic setup).

Total All-In Cost: $10.75 per contract.

Normalized Cost (100k equivalent): ~$8.60 (Equivalent to 0.86 pips).

Execution Reality: The CME operates on a FIFO (First In, First Out) order book. Limit orders at your short-target must wait in the queue. The market can touch your price, fill 200 contracts ahead of you, and reverse. You get a partial fill or no fill. This is the primary reason an identical chart pattern yields different PnL between the two venues.

For short-target scalping (e.g., 3 to 5 pips), IC Markets generally provides higher fill reliability at the target price, while Apex provides absolute protection against spread-widening stop hunts because the 6E spread is rigidly centralized.

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:15 am
by FTtrader
Pine Script: Scalp Friction & Session Tracker

Since you track spread medians and partial fills externally, this Pine Script visualizes the Friction Ratio on your chart. It highlights the London session and calculates exactly how much of your actual price movement is being eaten by the all-in cost (spread + commission).

If the cost eats more than 20% of your target, the venue is actively fighting your playbook.

Code: Select all

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Assistant

//@version=5
indicator("Session Cost & Scalp Friction [Reality Check]", overlay=true)

// --- Inputs ---
grp_trade = "Trade Parameters"
targetPips = input.float(5.0, title="Scalp Target (Pips)", group=grp_trade)
commRoundTurn = input.float(7.0, title="Commission (RT $ per lot)", group=grp_trade)
avgSpreadPips = input.float(0.1, title="Tracked Median Spread (Pips)", tooltip="Input your tracked median from your sheet", group=grp_trade)

grp_sess = "Session Settings"
sessTime = input.session("0800-1600", title="London Session", group=grp_sess)
sessColor = input.color(color.new(color.blue, 90), title="Session Highlight", group=grp_sess)

// --- Session Logic ---
inSession = time(timeframe.period, sessTime)
bgcolor(inSession ? sessColor : na, title="Session Background")

// --- Cost Calculations ---
// Assuming standard lot ($10 per pip) to normalize commission into pips
commPips = commRoundTurn / 10.0
allInCostPips = avgSpreadPips + commPips

// Friction Ratio: What % of your target is eaten by the venue?
frictionRatio = (allInCostPips / targetPips) * 100

// Required Gross Move to hit Net Target
grossTargetRequired = targetPips + allInCostPips

// --- ATR / Volatility Check ---
// Are we moving enough in this session to comfortably cover the target?
atr = ta.atr(14)
atrPips = atr * 10000 // Convert standard forex price to pips. Adjust multiplier for JPY pairs.

// --- Dashboard / Table ---
var table realityTable = table.new(position.bottom_right, 2, 4, border_width = 1, border_color = color.gray, frame_color = color.gray, frame_width = 1)

if barstate.islast
    // Headers
    table.cell(realityTable, 0, 0, "Metric", text_color=color.white, bgcolor=color.gray)
    table.cell(realityTable, 1, 0, "Value", text_color=color.white, bgcolor=color.gray)
    
    // All-In Cost
    table.cell(realityTable, 0, 1, "All-In Cost (Pips)", text_color=color.white, bgcolor=color.black)
    table.cell(realityTable, 1, 1, str.tostring(allInCostPips, "#.##"), text_color=color.white, bgcolor=color.black)
    
    // Friction Ratio (Turns Red if Cost is > 20% of Target)
    frictionColor = frictionRatio > 20 ? color.new(color.red, 70) : color.new(color.green, 70)
    table.cell(realityTable, 0, 2, "Friction Ratio", text_color=color.white, bgcolor=frictionColor)
    table.cell(realityTable, 1, 2, str.tostring(frictionRatio, "#.#") + "%", text_color=color.white, bgcolor=frictionColor)
    
    // Gross Move Required
    table.cell(realityTable, 0, 3, "Gross Move Required", text_color=color.white, bgcolor=color.black)
    table.cell(realityTable, 1, 3, str.tostring(grossTargetRequired, "#.##") + " Pips", text_color=color.white, bgcolor=color.black)

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:16 am
by FTtrader
How to Apply This to Your Testing

1.) Apply the script to a 1M or 5M EURUSD chart.

2.) In the settings, input your latest spreadsheet median for IC Markets (e.g., 0.1 spread, $7 comm) and note the Friction Ratio.

3.) Change the settings to reflect your Apex 6E normalization (e.g., 0.5 spread, $8.60 normalized comm) and compare the Friction Ratio.

If your target is 4 pips, the math will show you immediately that Apex requires a gross price movement of ~5.36 pips to clear a net 4, while IC requires ~4.8 pips. That half-pip difference, combined with the CME FIFO queue, is exactly where short-target playbooks bleed out on funded futures accounts compared to raw CFDs.

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:18 am
by FTtrader
Here are the standalone, production-ready indicators for both MetaTrader 4 (MQL4) and MetaTrader 5 (MQL5).

Both versions feature:

Live vs. Sheet Toggle: Choose between reading the broker’s real-time tick spread or locking in your spreadsheet median.

Automatic Pip Normalization: Automatically adjusts for 3/5-digit fractional brokers.

Friction Ratio Engine: Colors the HUD green (acceptable drag) or red if venue costs consume $>20\%$ of your target move.

Clean Cleanup: Properly cleans up all chart objects on removal.

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:18 am
by FTtrader
MetaTrader 4 (MQL4)

Save this file as ScalpFrictionCheck.mq4 in your MT4 MQL4/Indicators directory.

Code: Select all

//+------------------------------------------------------------------+
//|                                         ScalpFrictionCheck.mq4   |
//|                                  All-In Cost & Scalp Friction    |
//+------------------------------------------------------------------+
#property copyright "Assistant"
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window

// --- Input Parameters ---
input string  InpSection1        = "=== Scalp Parameters ===";
input double  InpTargetPips      = 5.0;       // Scalp Target (Pips)
input double  InpCommRoundTurn   = 7.0;       // Commission ($ RT per 100k Lot)
input double  InpPipValueUSD     = 10.0;      // Pip Value ($ per 100k Lot)

input string  InpSection2        = "=== Spread Mode ===";
input bool    InpUseLiveSpread   = false;     // Use Real-Time Broker Spread? (false = Sheet Median)
input double  InpManualSpread    = 0.1;       // Tracked Median Spread (Pips)

input string  InpSection3        = "=== HUD Display ===";
input ENUM_BASE_CORNER InpCorner = CORNER_RIGHT_UPPER; // Screen Corner
input int     InpXOffset         = 20;        // X Offset (px)
input int     InpYOffset         = 30;        // Y Offset (px)
input int     InpFontSize        = 10;        // Font Size
input color   InpNormalColor     = clrWhite;
input color   InpWarningColor    = clrCrimson;
input color   InpPassColor       = clrMediumSeaGreen;

#define PREFIX "SFC_"

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Pip Size Determination                                           |
//+------------------------------------------------------------------+
double GetPipPoint()
{
   if(Digits == 3 || Digits == 5) return(Point * 10.0);
   return(Point);
}

//+------------------------------------------------------------------+
//| 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[])
{
   double pipPoint = GetPipPoint();
   double currentSpreadPips = 0.0;

   if(InpUseLiveSpread)
   {
      double rawSpread = Ask - Bid;
      currentSpreadPips = (pipPoint > 0) ? (rawSpread / pipPoint) : 0.0;
   }
   else
   {
      currentSpreadPips = InpManualSpread;
   }

   // Normalized Commission in Pips
   double commPips = (InpPipValueUSD > 0) ? (InpCommRoundTurn / InpPipValueUSD) : 0.0;
   double allInCostPips = currentSpreadPips + commPips;
   
   // Friction Ratio (% of Target consumed by friction)
   double frictionRatio = (InpTargetPips > 0) ? (allInCostPips / InpTargetPips) * 100.0 : 0.0;
   double grossTargetRequired = InpTargetPips + allInCostPips;

   // Update Dashboard HUD
   RenderHUD(currentSpreadPips, commPips, allInCostPips, frictionRatio, grossTargetRequired);

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Draw or Update HUD Label                                         |
//+------------------------------------------------------------------+
void UpdateLabel(string name, string text, int x, int y, color clr, int fontSize, bool isBold = false)
{
   string objName = PREFIX + name;
   if(ObjectFind(0, objName) < 0)
   {
      ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, objName, OBJPROP_CORNER, InpCorner);
      ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
   }
   ObjectSetString(0, objName, OBJPROP_TEXT, text);
   ObjectSetString(0, objName, OBJPROP_FONT, isBold ? "Arial Bold" : "Arial");
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
}

//+------------------------------------------------------------------+
//| Render HUD Lines                                                 |
//+------------------------------------------------------------------+
void RenderHUD(double spreadPips, double commPips, double allIn, double friction, double gross)
{
   int y = InpYOffset;
   int step = InpFontSize + 8;
   
   color frictionColor = (friction > 20.0) ? InpWarningColor : InpPassColor;

   UpdateLabel("H1", "=== VENUE FRICTION CHECK ===", InpXOffset, y, clrSilver, InpFontSize, true);
   y += step;
   
   string spreadSource = InpUseLiveSpread ? "[Live]" : "[Sheet]";
   UpdateLabel("L1", "Spread " + spreadSource + ": " + DoubleToString(spreadPips, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
   y += step;
   
   UpdateLabel("L2", "Commission: " + DoubleToString(commPips, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
   y += step;
   
   UpdateLabel("L3", "All-In Drag: " + DoubleToString(allIn, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
   y += step;

   UpdateLabel("L4", "Friction: " + DoubleToString(friction, 1) + "% of " + DoubleToString(InpTargetPips, 1) + "p Target", InpXOffset, y, frictionColor, InpFontSize, true);
   y += step;

   UpdateLabel("L5", "Required Move: " + DoubleToString(gross, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
}

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:18 am
by FTtrader
MetaTrader 5 (MQL5)

Save this file as ScalpFrictionCheck.mq5 in your MT5 MQL5/Indicators directory.

Code: Select all

//+------------------------------------------------------------------+
//|                                         ScalpFrictionCheck.mq5   |
//|                                  All-In Cost & Scalp Friction    |
//+------------------------------------------------------------------+
#property copyright "Assistant"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0

// --- Input Parameters ---
input group "=== Scalp Parameters ==="
input double  InpTargetPips      = 5.0;       // Scalp Target (Pips)
input double  InpCommRoundTurn   = 7.0;       // Commission ($ RT per 100k Lot)
input double  InpPipValueUSD     = 10.0;      // Pip Value ($ per 100k Lot)

input group "=== Spread Mode ==="
input bool    InpUseLiveSpread   = false;     // Use Real-Time Broker Spread? (false = Sheet Median)
input double  InpManualSpread    = 0.1;       // Tracked Median Spread (Pips)

input group "=== HUD Display ==="
input ENUM_BASE_CORNER InpCorner = CORNER_RIGHT_UPPER; // Screen Corner
input int     InpXOffset         = 20;        // X Offset (px)
input int     InpYOffset         = 30;        // Y Offset (px)
input int     InpFontSize        = 10;        // Font Size
input color   InpNormalColor     = clrWhite;
input color   InpWarningColor    = clrCrimson;
input color   InpPassColor       = clrMediumSeaGreen;

#define PREFIX "SFC5_"

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, PREFIX);
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Pip Size Determination                                           |
//+------------------------------------------------------------------+
double GetPipPoint()
{
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   if(digits == 3 || digits == 5) return(pt * 10.0);
   return(pt);
}

//+------------------------------------------------------------------+
//| 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[])
{
   double pipPoint = GetPipPoint();
   double currentSpreadPips = 0.0;

   if(InpUseLiveSpread)
   {
      MqlTick lastTick;
      if(SymbolInfoTick(_Symbol, lastTick))
      {
         double rawSpread = lastTick.ask - lastTick.bid;
         currentSpreadPips = (pipPoint > 0) ? (rawSpread / pipPoint) : 0.0;
      }
   }
   else
   {
      currentSpreadPips = InpManualSpread;
   }

   // Normalized Commission in Pips
   double commPips = (InpPipValueUSD > 0) ? (InpCommRoundTurn / InpPipValueUSD) : 0.0;
   double allInCostPips = currentSpreadPips + commPips;
   
   // Friction Ratio (% of Target consumed by friction)
   double frictionRatio = (InpTargetPips > 0) ? (allInCostPips / InpTargetPips) * 100.0 : 0.0;
   double grossTargetRequired = InpTargetPips + allInCostPips;

   // Update Dashboard HUD
   RenderHUD(currentSpreadPips, commPips, allInCostPips, frictionRatio, grossTargetRequired);

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Draw or Update HUD Label                                         |
//+------------------------------------------------------------------+
void UpdateLabel(string name, string text, int x, int y, color clr, int fontSize, bool isBold = false)
{
   string objName = PREFIX + name;
   if(ObjectFind(0, objName) < 0)
   {
      ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, objName, OBJPROP_CORNER, InpCorner);
      ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
   }
   ObjectSetString(0, objName, OBJPROP_TEXT, text);
   ObjectSetString(0, objName, OBJPROP_FONT, isBold ? "Arial Bold" : "Arial");
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
}

//+------------------------------------------------------------------+
//| Render HUD Lines                                                 |
//+------------------------------------------------------------------+
void RenderHUD(double spreadPips, double commPips, double allIn, double friction, double gross)
{
   int y = InpYOffset;
   int step = InpFontSize + 8;
   
   color frictionColor = (friction > 20.0) ? InpWarningColor : InpPassColor;

   UpdateLabel("H1", "=== VENUE FRICTION CHECK ===", InpXOffset, y, clrSilver, InpFontSize, true);
   y += step;
   
   string spreadSource = InpUseLiveSpread ? "[Live]" : "[Sheet]";
   UpdateLabel("L1", "Spread " + spreadSource + ": " + DoubleToString(spreadPips, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
   y += step;
   
   UpdateLabel("L2", "Commission: " + DoubleToString(commPips, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
   y += step;
   
   UpdateLabel("L3", "All-In Drag: " + DoubleToString(allIn, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
   y += step;

   UpdateLabel("L4", "Friction: " + DoubleToString(friction, 1) + "% of " + DoubleToString(InpTargetPips, 1) + "p Target", InpXOffset, y, frictionColor, InpFontSize, true);
   y += step;

   UpdateLabel("L5", "Required Move: " + DoubleToString(gross, 2) + " pips", InpXOffset, y, InpNormalColor, InpFontSize);
}

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:19 am
by FTtrader
How to Run Your Comparison

Benchmarking IC Markets Raw:

Leave InpUseLiveSpread = true to check real-time tick spread during high-liquidity London overlap.

Switch InpUseLiveSpread = false and set InpManualSpread = 0.1 to see your true baseline median.

Benchmarking Apex (6E Normalized):

Keep the script on your MT4/MT5 EURUSD chart, but set:

InpUseLiveSpread = false

InpManualSpread = 0.5 (CME 6E 1-tick minimum spread)

InpCommRoundTurn = 8.60 (Normalized $4.50 contract commission scaled to 100k standard lot equivalent)

The HUD will instantly show the delta in Friction % and Required Move between both setups on the exact same price action.

Re: Spread reality on Apex vs raw IC Markets for EURUSD

Posted: Thu Sep 24, 2026 10:20 am
by FTtrader
Here is the production-ready C# indicator for cTrader (cAlgo).

Since cTrader supports advanced UI rendering, this version uses a native StackPanel to create a clean, semi-transparent HUD rather than relying on legacy static text coordinates. It updates tick-by-tick on the live bar and correctly isolates the C# API logic for pip conversions.

Save this in cTrader under Automate -> Indicators -> New and paste the code.

Code: Select all

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ScalpFrictionCheck : Indicator
    {
        // --- Scalp Parameters ---
        [Parameter("Scalp Target (Pips)", Group = "Scalp Parameters", DefaultValue = 5.0)]
        public double TargetPips { get; set; }

        [Parameter("Commission ($ RT per 100k Lot)", Group = "Scalp Parameters", DefaultValue = 7.0)]
        public double CommRoundTurn { get; set; }

        [Parameter("Pip Value ($ per 100k Lot)", Group = "Scalp Parameters", DefaultValue = 10.0)]
        public double PipValueUSD { get; set; }

        // --- Spread Mode ---
        [Parameter("Use Live Broker Spread?", Group = "Spread Mode", DefaultValue = false)]
        public bool UseLiveSpread { get; set; }

        [Parameter("Tracked Median Spread (Pips)", Group = "Spread Mode", DefaultValue = 0.1)]
        public double ManualSpread { get; set; }

        // --- HUD Display ---
        [Parameter("Horizontal Alignment", Group = "HUD Display", DefaultValue = HorizontalAlignment.Right)]
        public HorizontalAlignment HudAlignX { get; set; }

        [Parameter("Vertical Alignment", Group = "HUD Display", DefaultValue = VerticalAlignment.Top)]
        public VerticalAlignment HudAlignY { get; set; }

        // UI Elements
        private StackPanel _hudPanel;
        private TextBlock _headerText;
        private TextBlock _spreadText;
        private TextBlock _commText;
        private TextBlock _allInText;
        private TextBlock _frictionText;
        private TextBlock _requiredMoveText;

        protected override void Initialize()
        {
            // Build Native UI Panel
            _hudPanel = new StackPanel
            {
                HorizontalAlignment = HudAlignX,
                VerticalAlignment = HudAlignY,
                Margin = new Thickness(20, 30, 20, 20),
                Orientation = Orientation.Vertical,
                BackgroundColor = Color.FromArgb(200, 20, 20, 20),
                Width = 250
            };

            var border = new Border
            {
                BorderColor = Color.Gray,
                BorderThickness = 1,
                Child = _hudPanel
            };

            // Initialize TextBlocks
            _headerText = CreateHUDText("=== VENUE FRICTION CHECK ===", true, Color.Silver);
            _headerText.Margin = new Thickness(10, 10, 10, 5);
            
            _spreadText = CreateHUDText("Spread: ", false, Color.White);
            _commText = CreateHUDText("Commission: ", false, Color.White);
            _allInText = CreateHUDText("All-In Drag: ", false, Color.White);
            _frictionText = CreateHUDText("Friction: ", true, Color.White);
            _requiredMoveText = CreateHUDText("Required Move: ", false, Color.White);
            _requiredMoveText.Margin = new Thickness(10, 5, 10, 10);

            // Add to Panel
            _hudPanel.AddChild(_headerText);
            _hudPanel.AddChild(_spreadText);
            _hudPanel.AddChild(_commText);
            _hudPanel.AddChild(_allInText);
            _hudPanel.AddChild(_frictionText);
            _hudPanel.AddChild(_requiredMoveText);

            // Mount to Chart
            Chart.AddControl(border);
        }

        public override void Calculate(int index)
        {
            // Only update on the live/last bar
            if (!IsLastBar) return;
            UpdateFrictionEngine();
        }

        private void UpdateFrictionEngine()
        {
            // 1. Resolve Spread (Live via API vs Manual Sheet Median)
            double currentSpreadPips = UseLiveSpread ? (Symbol.Spread / Symbol.PipSize) : ManualSpread;
            string sourceFlag = UseLiveSpread ? "[Live]" : "[Sheet]";

            // 2. Resolve Commission in Pips
            double commPips = PipValueUSD > 0 ? (CommRoundTurn / PipValueUSD) : 0;
            
            // 3. Engine Math
            double allInCostPips = currentSpreadPips + commPips;
            double frictionRatio = TargetPips > 0 ? (allInCostPips / TargetPips) * 100.0 : 0;
            double grossTargetRequired = TargetPips + allInCostPips;

            // 4. Update UI Text
            _spreadText.Text = $"Spread {sourceFlag}: {Math.Round(currentSpreadPips, 2)} pips";
            _commText.Text = $"Commission: {Math.Round(commPips, 2)} pips";
            _allInText.Text = $"All-In Drag: {Math.Round(allInCostPips, 2)} pips";
            
            _frictionText.Text = $"Friction: {Math.Round(frictionRatio, 1)}% of {TargetPips}p Target";
            _frictionText.ForegroundColor = frictionRatio > 20.0 ? Color.Crimson : Color.MediumSeaGreen;

            _requiredMoveText.Text = $"Required Move: {Math.Round(grossTargetRequired, 2)} pips";
        }

        private TextBlock CreateHUDText(string text, bool isBold, Color clr)
        {
            return new TextBlock
            {
                Text = text,
                ForegroundColor = clr,
                FontWeight = isBold ? FontWeight.Bold : FontWeight.Normal,
                Margin = new Thickness(10, 3, 10, 3),
                FontSize = 12
            };
        }
    }
}