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: 2204
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: 2204
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: 2204
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: 2204
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: 2204
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: 2204
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: 2204
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.
Post Reply