Advertisement IC Markets

Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Optimize MetaTrader 4, MetaTrader 5, cTrader, and TradingView for speed. Discuss Level II Market Depth (DOM), custom hotkeys, and volume indicators.
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

Why this is a significant architectural upgrade:

Institutional Sizing (Line 96): Amateurs use fixed lots. This script calculates the exact pip distance between your Entry and your Swing Stop Loss, and sizes the position dynamically so that if your stop is hit, you lose exactly X% of your account equity.

Structural Stop Loss Topology (Line 99/105): Instead of an arbitrary ATR trailing stop right at entry, the initial stop is mapped anatomically below the Swing Low (for longs) or above the Swing High (for shorts), plus a small ATR fraction to protect against spread spikes/liquidity sweeps.

The Telemetry HUD (Line 124): Rather than outputting data to the Data Window, it dynamically renders a clean UI table at the bottom right, reporting on internal variables (Session Status, Current Structural Bias, Position State) on the last bar.

Memory Efficiency: Drawing objects (box and table) are properly initialized once and updated/deleted dynamically, preventing the TradingView engine from lagging by rendering thousands of historical overlapping boxes.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

Translating this architecture from Pine Script to the MetaTrader environment requires shifting from TradingView’s continuous series array model to a state-aware, event-driven model (OnTick).

Since MetaTrader handles tick data and order execution natively, the anchored VWAP needs to be calculated via a dynamic loop back to the identified swing index, and position sizing must be normalized using SYMBOL_TRADE_TICK_VALUE to handle different asset classes (Forex, Metals, Equities).

Here are the complete, single-file Expert Advisors for both MT5 (Object-Oriented) and MT4 (Procedural).
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

1. MetaTrader 5 (MQL5) Expert Advisor

This version utilizes the standard <Trade\Trade.mqh> library for robust order execution and handles arrays as time series for 1:1 mapping with traditional chart indexing.

Code: Select all

//+------------------------------------------------------------------+
//|                                             Pro_Volume_OTE_5.mq5 |
//+------------------------------------------------------------------+
#property copyright "Enterprise Algorithmic Architecture"
#property version   "1.00"

#include <Trade\Trade.mqh>
CTrade trade;

// --- Inputs ---
input double   InpRiskPercent = 1.0;       // Risk Per Trade (%)
input int      InpPivotLookback = 15;      // Structural Pivot Lookback
input double   InpFibUpper = 0.618;        // OTE Upper Boundary
input double   InpFibLower = 0.786;        // OTE Lower Boundary
input int      InpAtrPeriod = 14;          // ATR Period
input double   InpAtrBuffer = 0.5;         // ATR Stop Buffer Multiplier
input double   InpAtrTrail = 2.0;          // ATR Trailing Multiplier
input int      InpStartHour = 8;           // Session Start (Broker Time)
input int      InpEndHour = 17;            // Session End (Broker Time)

// --- State Variables ---
int atrHandle;
double swingHigh = 0, swingLow = 0;
int swingHighIdx = -1, swingLowIdx = -1;
int trendDir = 0; // 1 = Bullish, -1 = Bearish
bool visitedOTE = false;

int OnInit() {
    atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
    if(atrHandle == INVALID_HANDLE) return INIT_FAILED;
    return(INIT_SUCCEEDED);
}

void OnTick() {
    // Only execute on new bar completion for structural setups
    static datetime lastTime = 0;
    datetime currentTime = iTime(_Symbol, _Period, 0);
    if(currentTime == lastTime) return;
    
    // --- 1. Session Filter ---
    MqlDateTime dt;
    TimeCurrent(dt);
    bool inSession = (dt.hour >= InpStartHour && dt.hour < InpEndHour);

    // --- 2. Structural Pivot Tracking ---
    double high[], low[];
    ArraySetAsSeries(high, true); ArraySetAsSeries(low, true);
    CopyHigh(_Symbol, _Period, 0, InpPivotLookback * 2 + 1, high);
    CopyLow(_Symbol, _Period, 0, InpPivotLookback * 2 + 1, low);

    bool isPH = true, isPL = true;
    for(int i = 1; i <= InpPivotLookback; i++) {
        if(high[InpPivotLookback] <= high[InpPivotLookback - i] || high[InpPivotLookback] <= high[InpPivotLookback + i]) isPH = false;
        if(low[InpPivotLookback] >= low[InpPivotLookback - i] || low[InpPivotLookback] >= low[InpPivotLookback + i]) isPL = false;
    }

    if(isPH) {
        swingHigh = high[InpPivotLookback];
        swingHighIdx = InpPivotLookback;
        if(swingLow != 0) { trendDir = 1; visitedOTE = false; }
    } else if(swingHighIdx >= 0) swingHighIdx++; 

    if(isPL) {
        swingLow = low[InpPivotLookback];
        swingLowIdx = InpPivotLookback;
        if(swingHigh != 0) { trendDir = -1; visitedOTE = false; }
    } else if(swingLowIdx >= 0) swingLowIdx++;

    // --- 3. OTE Math & VWAP ---
    if(swingHigh == 0 || swingLow == 0) return;
    
    double range = swingHigh - swingLow;
    double oteLongUp = swingHigh - (range * InpFibUpper);
    double oteLongDn = swingHigh - (range * InpFibLower);
    double oteShortDn = swingLow + (range * InpFibUpper);
    double oteShortUp = swingLow + (range * InpFibLower);

    double avwap = CalculateAnchoredVWAP(trendDir == 1 ? swingHighIdx : swingLowIdx);
    
    // --- 4. Entry Logic ---
    double currentClose = iClose(_Symbol, _Period, 1);
    double currentLow = iLow(_Symbol, _Period, 1);
    double currentHigh = iHigh(_Symbol, _Period, 1);
    double prevClose = iClose(_Symbol, _Period, 2);
    
    if(trendDir == 1 && currentLow <= oteLongUp && currentHigh >= oteLongDn) visitedOTE = true;
    if(trendDir == -1 && currentHigh >= oteShortDn && currentLow <= oteShortUp) visitedOTE = true;

    double atrVals[];
    CopyBuffer(atrHandle, 0, 1, 1, atrVals);
    double atr = atrVals[0];

    bool triggerLong = inSession && trendDir == 1 && visitedOTE && (prevClose < avwap && currentClose > avwap);
    bool triggerShort = inSession && trendDir == -1 && visitedOTE && (prevClose > avwap && currentClose < avwap);

    if(!PositionsTotal()) {
        if(triggerLong) {
            double sl = swingLow - (atr * InpAtrBuffer);
            double lots = CalculatePositionSize(sl);
            trade.Buy(lots, _Symbol, 0, sl, 0, "OTE Long");
            lastTime = currentTime;
        }
        if(triggerShort) {
            double sl = swingHigh + (atr * InpAtrBuffer);
            double lots = CalculatePositionSize(sl);
            trade.Sell(lots, _Symbol, 0, sl, 0, "OTE Short");
            lastTime = currentTime;
        }
    } else {
        ManageTrailingStop(atr * InpAtrTrail);
    }
    
    // --- 5. Telemetry ---
    string hud = "SYSTEM TELEMETRY\n";
    hud += "Market State: " + (trendDir == 1 ? "BULLISH\n" : "BEARISH\n");
    hud += "Session Active: " + (inSession ? "YES\n" : "NO\n");
    hud += "Current AVWAP: " + DoubleToString(avwap, _Digits) + "\n";
    Comment(hud);
}

// --- Helpers ---
double CalculateAnchoredVWAP(int startIndex) {
    if(startIndex <= 0) return 0;
    long vol[]; double h[], l[], c[];
    ArraySetAsSeries(vol, true); ArraySetAsSeries(h, true); ArraySetAsSeries(l, true); ArraySetAsSeries(c, true);
    
    CopyTickVolume(_Symbol, _Period, 1, startIndex, vol);
    CopyHigh(_Symbol, _Period, 1, startIndex, h);
    CopyLow(_Symbol, _Period, 1, startIndex, l);
    CopyClose(_Symbol, _Period, 1, startIndex, c);
    
    double cumVol = 0, cumSrcVol = 0;
    for(int i = 0; i < startIndex; i++) {
        double hlc3 = (h[i] + l[i] + c[i]) / 3.0;
        cumVol += (double)vol[i];
        cumSrcVol += hlc3 * (double)vol[i];
    }
    return (cumVol > 0) ? (cumSrcVol / cumVol) : 0;
}

double CalculatePositionSize(double slPrice) {
    double riskAmt = AccountInfoDouble(ACCOUNT_EQUITY) * (InpRiskPercent / 100.0);
    double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
    double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
    
    double slPoints = MathAbs(SymbolInfoDouble(_Symbol, SYMBOL_BID) - slPrice) / tickSize;
    double rawLots = riskAmt / (slPoints * tickValue);
    
    double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    double stepLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
    return MathMax(minLot, MathRound(rawLots / stepLot) * stepLot);
}

void ManageTrailingStop(double atrTrailDist) {
    for(int i = PositionsTotal() - 1; i >= 0; i--) {
        if(PositionGetSymbol(i) != _Symbol) continue;
        
        ulong ticket = PositionGetTicket(i);
        double currentSL = PositionGetDouble(POSITION_SL);
        long type = PositionGetInteger(POSITION_TYPE);
        
        if(type == POSITION_TYPE_BUY) {
            double newSL = SymbolInfoDouble(_Symbol, SYMBOL_BID) - atrTrailDist;
            if(newSL > currentSL) trade.PositionModify(ticket, newSL, 0);
        } else if(type == POSITION_TYPE_SELL) {
            double newSL = SymbolInfoDouble(_Symbol, SYMBOL_ASK) + atrTrailDist;
            if(newSL < currentSL || currentSL == 0) trade.PositionModify(ticket, newSL, 0);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

2. MetaTrader 4 (MQL4) Expert Advisor

The MQL4 version relies on OrderSend and loops through historical indexing directly using iHigh, iLow, and iVolume.

Code: Select all

//+------------------------------------------------------------------+
//|                                             Pro_Volume_OTE_4.mq4 |
//+------------------------------------------------------------------+
#property copyright "Enterprise Algorithmic Architecture"
#property version   "1.00"
#property strict

// --- Inputs ---
extern double  RiskPercent = 1.0;
extern int     PivotLookback = 15;
extern double  FibUpper = 0.618;
extern double  FibLower = 0.786;
extern int     AtrPeriod = 14;
extern double  AtrBuffer = 0.5;
extern double  AtrTrail = 2.0;
extern int     StartHour = 8;
extern int     EndHour = 17;

// --- State Variables ---
double swingHigh = 0, swingLow = 0;
int swingHighIdx = -1, swingLowIdx = -1;
int trendDir = 0; 
bool visitedOTE = false;

int OnInit() { return(INIT_SUCCEEDED); }

void OnTick() {
    static datetime lastTime = 0;
    if(Time[0] == lastTime) return;
    
    bool inSession = (Hour() >= StartHour && Hour() < EndHour);

    // --- 1. Structural Pivot Tracking ---
    bool isPH = true, isPL = true;
    for(int i = 1; i <= PivotLookback; i++) {
        if(High[PivotLookback] <= High[PivotLookback - i] || High[PivotLookback] <= High[PivotLookback + i]) isPH = false;
        if(Low[PivotLookback] >= Low[PivotLookback - i] || Low[PivotLookback] >= Low[PivotLookback + i]) isPL = false;
    }

    if(isPH) {
        swingHigh = High[PivotLookback];
        swingHighIdx = PivotLookback;
        if(swingLow != 0) { trendDir = 1; visitedOTE = false; }
    } else if(swingHighIdx >= 0) swingHighIdx++; 

    if(isPL) {
        swingLow = Low[PivotLookback];
        swingLowIdx = PivotLookback;
        if(swingHigh != 0) { trendDir = -1; visitedOTE = false; }
    } else if(swingLowIdx >= 0) swingLowIdx++;

    if(swingHigh == 0 || swingLow == 0) return;
    
    // --- 2. OTE & VWAP ---
    double range = swingHigh - swingLow;
    double oteLongUp = swingHigh - (range * FibUpper);
    double oteLongDn = swingHigh - (range * FibLower);
    double oteShortDn = swingLow + (range * FibUpper);
    double oteShortUp = swingLow + (range * FibLower);

    double avwap = CalculateAnchoredVWAP(trendDir == 1 ? swingHighIdx : swingLowIdx);
    double atr = iATR(_Symbol, 0, AtrPeriod, 1);
    
    // --- 3. Entry Logic ---
    if(trendDir == 1 && Low[1] <= oteLongUp && High[1] >= oteLongDn) visitedOTE = true;
    if(trendDir == -1 && High[1] >= oteShortDn && Low[1] <= oteShortUp) visitedOTE = true;

    bool triggerLong = inSession && trendDir == 1 && visitedOTE && (Close[2] < avwap && Close[1] > avwap);
    bool triggerShort = inSession && trendDir == -1 && visitedOTE && (Close[2] > avwap && Close[1] < avwap);

    if(OrdersTotal() == 0) {
        if(triggerLong) {
            double sl = swingLow - (atr * AtrBuffer);
            double lots = CalculatePositionSize(sl);
            OrderSend(_Symbol, OP_BUY, lots, Ask, 3, sl, 0, "OTE Long", 0, 0, clrGreen);
            lastTime = Time[0];
        }
        if(triggerShort) {
            double sl = swingHigh + (atr * AtrBuffer);
            double lots = CalculatePositionSize(sl);
            OrderSend(_Symbol, OP_SELL, lots, Bid, 3, sl, 0, "OTE Short", 0, 0, clrRed);
            lastTime = Time[0];
        }
    } else {
        ManageTrailingStop(atr * AtrTrail);
    }
    
    // --- 4. Telemetry ---
    string hud = "SYSTEM TELEMETRY\n";
    hud += "Market State: " + (trendDir == 1 ? "BULLISH\n" : "BEARISH\n");
    hud += "Session Active: " + (inSession ? "YES\n" : "NO\n");
    hud += "Current AVWAP: " + DoubleToString(avwap, Digits) + "\n";
    Comment(hud);
}

double CalculateAnchoredVWAP(int startIndex) {
    if(startIndex <= 0) return 0;
    double cumVol = 0, cumSrcVol = 0;
    for(int i = 1; i <= startIndex; i++) {
        double hlc3 = (High[i] + Low[i] + Close[i]) / 3.0;
        cumVol += Volume[i];
        cumSrcVol += hlc3 * Volume[i];
    }
    return (cumVol > 0) ? (cumSrcVol / cumVol) : 0;
}

double CalculatePositionSize(double slPrice) {
    double riskAmt = AccountEquity() * (RiskPercent / 100.0);
    double tickValue = MarketInfo(_Symbol, MODE_TICKVALUE);
    double tickSize = MarketInfo(_Symbol, MODE_TICKSIZE);
    
    double slPoints = MathAbs(Bid - slPrice) / tickSize;
    double rawLots = riskAmt / (slPoints * tickValue);
    
    double minLot = MarketInfo(_Symbol, MODE_MINLOT);
    double stepLot = MarketInfo(_Symbol, MODE_LOTSTEP);
    return MathMax(minLot, MathRound(rawLots / stepLot) * stepLot);
}

void ManageTrailingStop(double atrTrailDist) {
    for(int i = OrdersTotal() - 1; i >= 0; i--) {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == _Symbol) {
            if(OrderType() == OP_BUY) {
                double newSL = NormalizeDouble(Bid - atrTrailDist, Digits);
                if(newSL > OrderStopLoss()) OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrGreen);
            } 
            else if(OrderType() == OP_SELL) {
                double newSL = NormalizeDouble(Ask + atrTrailDist, Digits);
                if(newSL < OrderStopLoss() || OrderStopLoss() == 0) OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrRed);
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

Execution Notes:

Position Sizing Math: Both scripts use total AccountEquity() and query SYMBOL_TRADE_TICK_VALUE to accurately price the pip distance. This ensures 1.0% risk applies correctly whether you deploy it on Gold, GBPUSD, or NAS100.

Tick Volume vs Real Volume: The MT5 version uses CopyTickVolume. If you are trading Equities or Futures through an MT5 exchange broker that provides real depth, swap it to CopyRealVolume inside the CalculateAnchoredVWAP function for exact volume weighting.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

Here is the complete, professional-grade C# implementation for cTrader (cAlgo).

cTrader's modern API allows us to utilize true asynchronous tick-level trailing, precise volume normalization using Symbol.PipValue, and rich chart drawing objects to recreate the OTE Box, the Anchored VWAP line, and the Telemetry HUD exactly as they appeared in TradingView.

The cTrader (C#) cBot Architecture

Open cTrader Automate, create a new cBot, and paste this entire source code:

Code: Select all

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ProVolumeOTE : Robot
    {
        // =========================================================================
        // 1. CONFIGURATION & INPUTS
        // =========================================================================
        [Parameter("Risk Per Trade (%)", DefaultValue = 1.0, MinValue = 0.1, Step = 0.25, Group = "Risk & Execution")]
        public double RiskPercent { get; set; }

        [Parameter("ATR Buffer Length", DefaultValue = 14, Group = "Risk & Execution")]
        public int AtrLength { get; set; }

        [Parameter("ATR Stop Buffer", DefaultValue = 0.5, Step = 0.1, Group = "Risk & Execution")]
        public double AtrBuffer { get; set; }

        [Parameter("ATR Trailing Multiplier", DefaultValue = 2.0, Step = 0.1, Group = "Risk & Execution")]
        public double AtrTrail { get; set; }

        [Parameter("Structural Pivot Lookback", DefaultValue = 15, MinValue = 5, Group = "Market Structure")]
        public int PivotLookback { get; set; }

        [Parameter("OTE Upper Boundary", DefaultValue = 0.618, Group = "Market Structure")]
        public double FibUpper { get; set; }

        [Parameter("OTE Lower Boundary", DefaultValue = 0.786, Group = "Market Structure")]
        public double FibLower { get; set; }

        [Parameter("Session Start (Hour)", DefaultValue = 8, Group = "Time & Session")]
        public int SessionStart { get; set; }

        [Parameter("Session End (Hour)", DefaultValue = 17, Group = "Time & Session")]
        public int SessionEnd { get; set; }

        [Parameter("Show Telemetry HUD", DefaultValue = true, Group = "UI & Visuals")]
        public bool ShowHud { get; set; }

        // =========================================================================
        // 2. STATE VARIABLES & INDICATORS
        // =========================================================================
        private AverageTrueRange _atr;
        private string _botLabel = "ProVolumeOTE";

        private double _swingHigh = 0;
        private double _swingLow = 0;
        private int _anchorIndex = -1;
        private int _trendDir = 0; // 1 = Bullish, -1 = Bearish
        private bool _visitedOte = false;

        protected override void OnStart()
        {
            _atr = Indicators.AverageTrueRange(AtrLength, MovingAverageType.Simple);
            UpdateTelemetry(false, 0);
        }

        // =========================================================================
        // 3. STRUCTURAL PIVOT TRACKING (ON BAR CLOSE)
        // =========================================================================
        protected override void OnBar()
        {
            int index = Bars.Count - 2; // Last fully closed bar
            int pIndex = index - PivotLookback;

            if (pIndex < PivotLookback) return;

            bool isPH = true;
            bool isPL = true;

            // Pivot Scan
            for (int i = 1; i <= PivotLookback; i++)
            {
                if (Bars.HighPrices[pIndex] <= Bars.HighPrices[pIndex - i] || Bars.HighPrices[pIndex] <= Bars.HighPrices[pIndex + i]) isPH = false;
                if (Bars.LowPrices[pIndex] >= Bars.LowPrices[pIndex - i] || Bars.LowPrices[pIndex] >= Bars.LowPrices[pIndex + i]) isPL = false;
            }

            if (isPH)
            {
                _swingHigh = Bars.HighPrices[pIndex];
                if (_swingLow != 0) 
                {
                    _trendDir = 1;
                    _anchorIndex = pIndex;
                    _visitedOte = false;
                }
            }

            if (isPL)
            {
                _swingLow = Bars.LowPrices[pIndex];
                if (_swingHigh != 0) 
                {
                    _trendDir = -1;
                    _anchorIndex = pIndex;
                    _visitedOte = false;
                }
            }

            if (_swingHigh == 0 || _swingLow == 0 || _anchorIndex == -1) return;

            // =========================================================================
            // 4. OTE MATH & VOLUME MICROSTRUCTURE (AVWAP)
            // =========================================================================
            double range = _swingHigh - _swingLow;
            double oteLongUp = _swingHigh - (range * FibUpper);
            double oteLongDn = _swingHigh - (range * FibLower);
            double oteShortDn = _swingLow + (range * FibUpper);
            double oteShortUp = _swingLow + (range * FibLower);

            double avwapCurr = GetAnchoredVwap(_anchorIndex, index);
            double avwapPrev = GetAnchoredVwap(_anchorIndex, index - 1);

            // Draw AVWAP Segment
            Chart.DrawTrendLine("avwap_" + index, index - 1, avwapPrev, index, avwapCurr, Color.Fuchsia, 2);

            // Draw OTE Box
            if (ShowHud)
            {
                if (_trendDir == 1)
                    Chart.DrawRectangle("ote_box", _anchorIndex, oteLongUp, index, oteLongDn, Color.FromArgb(40, Color.Teal)).IsFilled = true;
                else
                    Chart.DrawRectangle("ote_box", _anchorIndex, oteShortUp, index, oteShortDn, Color.FromArgb(40, Color.Orange)).IsFilled = true;
            }

            // =========================================================================
            // 5. EXECUTION LOGIC
            // =========================================================================
            bool inSession = (Server.Time.Hour >= SessionStart && Server.Time.Hour < SessionEnd);
            double curHigh = Bars.HighPrices[index];
            double curLow = Bars.LowPrices[index];
            double curClose = Bars.ClosePrices[index];
            double prevClose = Bars.ClosePrices[index - 1];

            if (_trendDir == 1 && curLow <= oteLongUp && curHigh >= oteLongDn) _visitedOte = true;
            if (_trendDir == -1 && curHigh >= oteShortDn && curLow <= oteShortUp) _visitedOte = true;

            bool triggerLong = inSession && _trendDir == 1 && _visitedOte && prevClose < avwapPrev && curClose > avwapCurr;
            bool triggerShort = inSession && _trendDir == -1 && _visitedOte && prevClose > avwapPrev && curClose < avwapCurr;

            double atrVal = _atr.Result.LastValue;

            if (Positions.FindAll(_botLabel).Length == 0)
            {
                if (triggerLong)
                {
                    double slPrice = _swingLow - (atrVal * AtrBuffer);
                    double slPips = Math.Abs(curClose - slPrice) / Symbol.PipSize;
                    double volume = CalculateRiskVolume(slPips);
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, volume, _botLabel, slPips, null);
                }
                else if (triggerShort)
                {
                    double slPrice = _swingHigh + (atrVal * AtrBuffer);
                    double slPips = Math.Abs(slPrice - curClose) / Symbol.PipSize;
                    double volume = CalculateRiskVolume(slPips);
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, volume, _botLabel, slPips, null);
                }
            }

            UpdateTelemetry(inSession, avwapCurr);
        }

        // =========================================================================
        // 6. TICK-LEVEL ATR TRAILING STOP
        // =========================================================================
        protected override void OnTick()
        {
            var positions = Positions.FindAll(_botLabel);
            if (positions.Length == 0) return;

            double atrVal = _atr.Result.LastValue;

            foreach (var pos in positions)
            {
                if (pos.TradeType == TradeType.Buy)
                {
                    double newSl = Symbol.Bid - (atrVal * AtrTrail);
                    if (pos.StopLoss == null || newSl > pos.StopLoss)
                    {
                        ModifyPosition(pos, newSl, pos.TakeProfit);
                    }
                }
                else if (pos.TradeType == TradeType.Sell)
                {
                    double newSl = Symbol.Ask + (atrVal * AtrTrail);
                    if (pos.StopLoss == null || newSl < pos.StopLoss)
                    {
                        ModifyPosition(pos, newSl, pos.TakeProfit);
                    }
                }
            }
        }

        // =========================================================================
        // 7. HELPER FUNCTIONS
        // =========================================================================
        private double GetAnchoredVwap(int startIndex, int endIndex)
        {
            if (startIndex < 0 || endIndex < startIndex) return 0;

            double cumVol = 0;
            double cumSrcVol = 0;

            for (int i = startIndex; i <= endIndex; i++)
            {
                double hlc3 = (Bars.HighPrices[i] + Bars.LowPrices[i] + Bars.ClosePrices[i]) / 3.0;
                double vol = Bars.TickVolumes[i]; 
                
                cumVol += vol;
                cumSrcVol += hlc3 * vol;
            }

            return cumVol > 0 ? cumSrcVol / cumVol : 0;
        }

        private double CalculateRiskVolume(double slPips)
        {
            double riskAmt = Account.Equity * (RiskPercent / 100.0);
            double exactVolume = riskAmt / (slPips * Symbol.PipValue);
            return Symbol.NormalizeVolumeInUnits(exactVolume, RoundingMode.Down);
        }

        private void UpdateTelemetry(bool inSession, double avwap)
        {
            if (!ShowHud) return;

            string dirText = _trendDir == 1 ? "BULLISH" : (_trendDir == -1 ? "BEARISH" : "FLAT");
            string posText = Positions.FindAll(_botLabel).Length > 0 ? "ACTIVE" : "FLAT";
            string sessionText = inSession ? "YES" : "NO";

            string hudText = $"==== SYSTEM TELEMETRY ====\n" +
                             $"Market Bias : {dirText}\n" +
                             $"Session     : {sessionText}\n" +
                             $"Position    : {posText}\n" +
                             $"Risk %      : {RiskPercent}%\n" +
                             $"Cur AVWAP   : {Math.Round(avwap, Symbol.Digits)}";

            Chart.DrawStaticText("hud", hudText, VerticalAlignment.Bottom, HorizontalAlignment.Right, Color.White);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

cTrader Architectural Advantages Used Here:

Symbol.NormalizeVolumeInUnits: cTrader handles asset classes (Indices vs Forex vs Crypto) perfectly by dynamically checking Symbol.PipValue. We compute exactly how much cash to risk, convert it to PIP risk, and let the API determine the exact contract size down to the nearest legal micro-lot rounding limit.

True Tick-Level Trailing: The OnBar loop handles structural calculations (Pivots, AVWAP, OTE box sizing), meaning you don't burn CPU cycles. However, the Trailing Stop Loss is cleanly delegated to OnTick(), locking in profits the instant Symbol.Bid or Symbol.Ask moves in your favor, rather than waiting for the 15-minute bar to close.

Semi-Transparent Rendering: Unlike MT4/MT5, cTrader supports Alpha channels natively. We use Color.FromArgb(40, Color.Teal) to draw the OTE box on the chart in a way that highlights the zone without obscuring your candlesticks. The Anchored VWAP dynamically renders as segments via Chart.DrawTrendLine.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
LondonScalper
Posts: 693
Joined: Sat Sep 05, 2026 7:54 am

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by LondonScalper »

PTScalper wrote:thank you for your feedback. You are welcome, you can test it as you want. Plus i prepared another versions in PineScript, MQL5 and for Ctrader as well.
Appreciated — multi-platform ports make forward-testing less of an excuse. I will keep the same London caution I wrote earlier: Fib OTE plus volume weighting plus ATR trail is decision support, not autopilot. FX "volume" is broker-dependent, swing anchors move the cluster, and a tight ATR multiple will stop you out on ordinary London noise even when the idea was sound.

Desk practice before any live size: demo with my real spread for two weeks, log whether the trail or the Fib logic actually drove the result, and keep a discretionary news veto. Code quality is not edge. A one-page rules card stating when I ignore the signal is more valuable than another port.

On the Pine / MQL5 / cTrader versions, are the Fib anchors taken from the same swing definition across platforms, or does each port let the trader pick swing length independently?
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

LondonScalper wrote: Tue Sep 22, 2026 10:11 pm
PTScalper wrote:thank you for your feedback. You are welcome, you can test it as you want. Plus i prepared another versions in PineScript, MQL5 and for Ctrader as well.
Appreciated — multi-platform ports make forward-testing less of an excuse. I will keep the same London caution I wrote earlier: Fib OTE plus volume weighting plus ATR trail is decision support, not autopilot. FX "volume" is broker-dependent, swing anchors move the cluster, and a tight ATR multiple will stop you out on ordinary London noise even when the idea was sound.

Desk practice before any live size: demo with my real spread for two weeks, log whether the trail or the Fib logic actually drove the result, and keep a discretionary news veto. Code quality is not edge. A one-page rules card stating when I ignore the signal is more valuable than another port.

On the Pine / MQL5 / cTrader versions, are the Fib anchors taken from the same swing definition across platforms, or does each port let the trader pick swing length independently?
Hi LondonScalper,

In robust multi-platform ports, the swing length is exposed as an independent, user-adjustable input parameter on each platform rather than being hardcoded.

Because TradingView (Pine Script), MetaTrader (MQL5), and cTrader (C#) use fundamentally different native functions for identifying peaks and valleys—such as ta.pivothigh/ta.pivotlow in Pine versus fractal arrays or custom ZigZag buffers in MQL and cAlgo—locking in a single algorithmic definition inevitably causes cross-platform discrepancies.

By exposing the lookback periods or deviation depth as adjustable inputs, the ports allow you to normalize the swing anchors manually. This ensures the Fib tool maps correctly to the raw price action structure on your daily and 15-minute charts, rather than defaulting to an arbitrary mathematical peak that the platform's native engine happens to flag.

This independent adjustability is exactly why your "one-page rules card" is critical. Because the indicator allows you to change the swing length, your rules card must strictly define which structural swing constitutes a valid anchor for that specific session. Otherwise, in the heat of a fast-moving London open, the temptation is to quietly tweak the swing length input until the Fib OTE perfectly justifies the trade you already wanted to take.

Your instinct is correct: code quality and cross-platform symmetry only deliver the data. The edge is entirely in the discretionary rules that dictate when you ignore it.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 2648
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

To prevent curve-fitting, your rules card must define anchors by market mechanics, not visual convenience. If a swing definition requires you to squint or toggle timeframes to make the math work, it is invalid.

Write these objective constraints on the card to lock down your anchors before the London open:

The Structural Requirement: A valid swing point must have accomplished something tangible on the 15-minute chart. The anchor must be the origin of a move that either cleanly swept a major liquidity pool or caused a clear Break of Structure (BOS). If a peak or valley did neither, it is internal session noise and cannot be used for the Fib.

Mechanical Session Extremes: Default to levels that cannot be debated. Use the Previous Day High/Low (PDH/PDL), the Asian session boundaries, or the Frankfurt open extreme. You cannot subconsciously curve-fit a session high—it is a static, objective fact on the chart.

The Multi-Candle Rule: Define the anchor geometrically. For example, a valid high must have at least three consecutive lower highs to its left and three to its right. Set your script's lookback parameters to enforce this mathematically. If the algorithm flags a swing but it doesn't meet this visual candle rule, you ignore the signal.

Daily Alignment: The 15-minute anchor is only valid if drawing the Fib aligns with the daily chart's narrative. If drawing the OTE has you buying into a daily supply zone, the anchor is vetoed.

The "No Redraw" Veto: This is the most critical desk rule. Once the 15-minute anchor is set and the London session begins, the coordinates are locked. If price aggressively breaks your anchor, the trade idea is invalidated. You do not drag the tool to the next highest peak just to manufacture a new 61.8% retracement level.

If the rule is strictly tied to liquidity sweeps and session extremes, the spread can move as fast as it wants—your parameters are already carved in stone.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply