Page 1 of 2

DOM imbalance settings I use for EURUSD scalp bias only

Posted: Tue Sep 22, 2026 11:13 pm
by LondonScalper
DOM imbalance as EURUSD bias filter only

I treat DOM imbalance as a filter, not an entry trigger. On EURUSD it can confirm that a level is being defended; it can also lie during news, thin books, and spoofy theatre. The mistake is letting a colourful ladder override a plan you wrote when you were calm.

How I use it in practice:
  • Bias only: helps me choose whether a pullback is worth stalking, not where to market-click.
  • Ignored entirely inside blackout windows and when spread is outside my filter.
  • Never overrides a higher-timeframe invalidation I already wrote before the session.
DOM toys become dangerous when you are bored and hunting for permission to click. Keep them in the filter bucket and your entries stay boring — which is usually where the expectancy lives after costs.

If you use DOM or imbalance tools on FX, are they filters for you too — or do you let them fire entries?

If the ladder and the M5 structure disagree, structure wins and the ladder becomes entertainment only. Filters are allowed to say no; they are not allowed to invent a yes against the plan.

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:10 am
by PTScalper
LondonScalper wrote: Tue Sep 22, 2026 11:13 pm DOM imbalance as EURUSD bias filter only

I treat DOM imbalance as a filter, not an entry trigger. On EURUSD it can confirm that a level is being defended; it can also lie during news, thin books, and spoofy theatre. The mistake is letting a colourful ladder override a plan you wrote when you were calm.

How I use it in practice:
  • Bias only: helps me choose whether a pullback is worth stalking, not where to market-click.
  • Ignored entirely inside blackout windows and when spread is outside my filter.
  • Never overrides a higher-timeframe invalidation I already wrote before the session.
DOM toys become dangerous when you are bored and hunting for permission to click. Keep them in the filter bucket and your entries stay boring — which is usually where the expectancy lives after costs.

If you use DOM or imbalance tools on FX, are they filters for you too — or do you let them fire entries?

If the ladder and the M5 structure disagree, structure wins and the ladder becomes entertainment only. Filters are allowed to say no; they are not allowed to invent a yes against the plan.
Hi LondonScalper,

Your framing is spot-on: "Filters are allowed to say no; they are not allowed to invent a yes." That is arguably the single most important rule for surviving spot FX.

The order book on EURUSD is highly fragmented. Any Level 2 data you see is just a localized glimpse from a specific liquidity provider or aggregator, making it ripe for spoofy theatre. When the DOM shows a massive block of limit orders sitting just above a swing high, it’s often just bait to engineer liquidity for a sweep. If the M15 or M5 raw price action structure dictates a short bias, a flashy, colorful ladder showing heavy bidding shouldn't override that plan.

DOM and imbalance tools are strictly environmental reads. They can tell you if the engine is running hot or cold, but they shouldn't steer the car. If you use them to hunt for permission to click during a slow session, you are just paying spread for the privilege of being entertained.

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:10 am
by PTScalper
Pine Script: DOM Imbalance Bias Filter

Unlike cTrader or MQL5 environments where you can hook directly into real-time Level 2 depth, TradingView Pine Script does not have native access to historical DOM data for spot FX.

To solve this in Pine, the script below uses a Tick Volume Delta Proxy to estimate intrabar buying versus selling pressure. It strictly enforces your philosophy: it does not plot entry arrows. It only highlights the background to indicate a "stalking environment" when your time, spread, structure, and imbalance rules align. If any filter says no, the background stays empty.

Code: Select all

//@version=5
indicator("DOM Imbalance Bias Filter (Proxy)", overlay=true)

// =========================================================================
// INPUTS
// =========================================================================
grp1 = "Filter Rules"
imbThreshold = input.float(20.0, title="Imbalance Delta % Threshold", group=grp1, tooltip="Minimum volume delta required to confirm bias.")
maxSpread    = input.float(1.5, title="Max Spread (Pips)", group=grp1, tooltip="Disables filter if real-time spread widens.")
sessionZone  = input.session("0800-1700", title="Trading Session", group=grp1, tooltip="Blackout window filter. Set to your active stalking hours.")

grp2 = "Structure Alignment"
structPeriod = input.int(20, title="Structure Baseline (EMA)", group=grp2, tooltip="Ensures imbalance agrees with current M5 flow.")

// =========================================================================
// FILTER 1: BLACKOUT WINDOWS
// =========================================================================
inSession = not na(time(timeframe.period, sessionZone))

// =========================================================================
// FILTER 2: SPREAD / THIN BOOK PROTECTION
// =========================================================================
// syminfo.ask/bid only updates in real-time. Historically, it defaults to na.
realtimeSpread = (syminfo.ask - syminfo.bid) * 10000 // Convert to standard pips for EURUSD
validSpread    = na(realtimeSpread) or (realtimeSpread <= maxSpread)

// =========================================================================
// FILTER 3: IMBALANCE PROXY (TICK VOLUME DELTA)
// =========================================================================
// Estimates aggressive buying vs selling pressure within the candle
range_hl     = high - low == 0 ? 1 : high - low
bullPressure = volume * ((close - low) / range_hl)
bearPressure = volume * ((high - close) / range_hl)
deltaPct     = ((bullPressure - bearPressure) / volume) * 100

// =========================================================================
// FILTER 4: M5 STRUCTURE ALIGNMENT
// =========================================================================
// The ladder must not invent a "yes" against the structural plan.
m5Structure = ta.ema(close, structPeriod)
structBull  = close > m5Structure
structBear  = close < m5Structure

// =========================================================================
// BIAS LOGIC (STALKING PERMISSION)
// =========================================================================
biasBull = inSession and validSpread and structBull and (deltaPct > imbThreshold)
biasBear = inSession and validSpread and structBear and (deltaPct < -imbThreshold)

// =========================================================================
// VISUALS
// =========================================================================
// Highlights the background ONLY when all conditions permit stalking.
bgcolor(biasBull ? color.new(color.green, 90) : biasBear ? color.new(color.red, 90) : na, title="Bias Filter Zone")

// Optional: Plot the structural baseline for visual context
plot(m5Structure, color=color.new(color.gray, 50), title="Structure Baseline")

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:11 am
by PTScalper
How this enforces the plan:

No Entry Signals: There are no strategy.entry commands or shiny arrows. It only provides a background tint, keeping the chart visually quiet.

Structure Wins: The structBull and structBear checks ensure that even if a massive volume delta spike occurs, it is ignored if it attempts to invent a trade against the current structural flow.

Blackout & Spread: If you are outside the sessionZone, or if the real-time spread widens beyond maxSpread (e.g., during news events), the filter immediately cuts off the bias highlight, enforcing discipline when the book thins out.

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:12 am
by PTScalper
If we are discarding the retail training wheels, we need to strip out lagging indicators entirely. Moving averages have no place in a microstructure or liquidity-based framework. Real market structure is dictated by pivot breaks, liquidity sweeps, and effort-versus-result volume anomalies.

Furthermore, since spot FX is decentralized, retail DOM data is just a fragmented B-book or single-aggregator view. True imbalance on a platform like TradingView (which lacks full FIX API Level 2 depth) must be inferred from the footprint of the price action itself: identifying when volume spikes but price fails to progress (absorption/sweeps), or when volume spikes and price closes on its absolute extreme (initiation).

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:12 am
by PTScalper
Here is an enterprise-grade Pine Script v5 architecture. It uses Pine's object-oriented type and method structures to encapsulate state management—keeping the global scope clean—and relies strictly on raw price action structure (BoS) and volume footprint proxies.

Pine Script v5: Structure & Liquidity State Engine

Code: Select all

//@version=5
indicator("Microstructure & Imbalance Filter [Pro]", overlay=true, max_lines_count=50)

// =========================================================================
// INPUTS & CONFIGURATION
// =========================================================================
grp_struct = "Market Structure (Raw PA)"
mtfRes     = input.timeframe("15", title="HTF Structure Resolution", group=grp_struct)
pivotLeft  = input.int(5, title="Pivot Left Legs", group=grp_struct)
pivotRight = input.int(2, title="Pivot Right Legs", group=grp_struct)

grp_flow   = "Order Flow / Imbalance Proxy"
volSpike   = input.float(1.5, title="Volume Spike Multiplier", tooltip="Requires volume to be X times the SMA to register as institutional footprint", group=grp_flow)
wickAbsorb = input.float(0.4, title="Sweep/Absorption Wick %", tooltip="If price closes within this % of the opposite end on high volume, it signals absorption.", group=grp_flow)

grp_env    = "Execution Environment"
maxSpread  = input.float(1.2, title="Max Allowed Spread (Pips)", group=grp_env)
sessWindow = input.session("0800-1700", title="Active Liquidity Window", group=grp_env)

// =========================================================================
// TYPES & STATE MANAGEMENT (OOP Architecture)
// =========================================================================

// Encapsulates the execution environment state
type ExecutionEnvironment
    bool  isInSession
    float currentSpread
    bool  isTradable

method update(ExecutionEnvironment this) =>
    this.isInSession := not na(time(timeframe.period, sessWindow))
    // Spread calculation (defaults to 0 historically, tracks realtime accurately)
    rtSpread = (syminfo.ask - syminfo.bid) * 10000
    this.currentSpread := na(rtSpread) ? 0.0 : rtSpread
    this.isTradable := this.isInSession and (this.currentSpread <= maxSpread or this.currentSpread == 0.0)

// Encapsulates HTF market structure (BoS / Trend)
type MarketStructure
    int   bias        // 1 = Bullish, -1 = Bearish
    float lastSwingH
    float lastSwingL

method updateStructure(MarketStructure this, float ph, float pl, float c) =>
    if not na(ph)
        this.lastSwingH := ph
    if not na(pl)
        this.lastSwingL := pl
    
    // Break of Structure Logic (Close outside established swings)
    if c > this.lastSwingH
        this.bias := 1
    else if c < this.lastSwingL
        this.bias := -1

// Encapsulates Bar-by-Bar Microstructure (Initiation vs Absorption)
type OrderFlow
    bool isBullImbalance
    bool isBearImbalance

method mapFootprint(OrderFlow this, float h, float l, float c, float v, float avgV) =>
    range_hl = h - l == 0 ? syminfo.mintick : h - l
    isHighVol = v > (avgV * volSpike)
    
    // Close proximity to extremes (0.0 = Low, 1.0 = High)
    closePos = (c - l) / range_hl
    
    // Bull Imbalance: High vol pushing price to close near highs (Initiation) OR 
    // High vol sweeping lows but closing aggressively higher (Absorption)
    this.isBullImbalance := isHighVol and (closePos >= (1.0 - wickAbsorb))
    
    // Bear Imbalance: High vol pushing price near lows OR sweeping highs and rejecting
    this.isBearImbalance := isHighVol and (closePos <= wickAbsorb)

// =========================================================================
// INSTANTIATION & EXECUTION LOOP
// =========================================================================

// 1. Initialize State Objects
var env = ExecutionEnvironment.new(false, 0.0, false)
var ms  = MarketStructure.new(0, high, low)
var of  = OrderFlow.new(false, false)

// 2. Fetch HTF Data (Raw PA only, no smoothing)
[htf_ph, htf_pl, htf_c] = request.security(syminfo.tickerid, mtfRes, [ta.pivothigh(high, pivotLeft, pivotRight), ta.pivotlow(low, pivotLeft, pivotRight), close], lookahead=barmerge.lookahead_on)

// 3. Update States
env.update()
ms.updateStructure(htf_ph, htf_pl, htf_c)

avgVolume = ta.sma(volume, 20)
of.mapFootprint(high, low, close, volume, avgVolume)

// =========================================================================
// FILTER LOGIC (THE GATEKEEPER)
// =========================================================================

// Bias is ONLY granted when HTF structure and LTF order flow align in a valid environment.
// The filter says NO to everything else.
bool permitLong  = env.isTradable and (ms.bias == 1) and of.isBullImbalance
bool permitShort = env.isTradable and (ms.bias == -1) and of.isBearImbalance

// =========================================================================
// VISUALIZATION (Clean & Unobtrusive)
// =========================================================================

// Background color highlights specific bars where all conditions are met for stalking
color longFilterColor  = permitLong  ? color.new(color.teal, 80) : na
color shortFilterColor = permitShort ? color.new(color.maroon, 80) : na

bgcolor(longFilterColor, title="Stalk Long Bias")
bgcolor(shortFilterColor, title="Stalk Short Bias")

// Optional: Plot HTF Structure lines for visual confirmation (uncomment if desired)
// plot(ms.lastSwingH, color=color.new(color.red, 50), style=plot.style_stepline, title="HTF Resistance")
// plot(ms.lastSwingL, color=color.new(color.green, 50), style=plot.style_stepline, title="HTF Support")

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:12 am
by PTScalper
Architectural Upgrades

State Encapsulation (type and method): Moving away from flat script execution to an object-oriented approach. The ExecutionEnvironment, MarketStructure, and OrderFlow structs cleanly isolate logic, making it vastly easier to debug, extend, or port to C# (cAlgo) later.

Raw PA over Lagging Math: The MarketStructure engine drops EMAs entirely. It uses request.security to pull exact pivot highs/lows from a higher timeframe (defaulted to 15M) to establish structural bias strictly via Break of Structure (BoS).

Effort vs. Result Proxy: The OrderFlow engine doesn't just look at tick delta. It looks for volume anomalies (effort) combined with the close position relative to the bar's range (result). This captures both momentum initiation and liquidity absorption (sweeps) directly from the price footprint.

Strict Gatekeeping: The permitLong and permitShort booleans serve as the final gatekeepers. If the HTF structure is bearish, but the volume footprint shows massive bullish absorption, the filter outputs false. The ladder/footprint is not allowed to overrule the HTF map.

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:14 am
by PTScalper
Transitioning this logic from Pine Script to MetaQuotes requires architectural shifts. Since MetaTrader is a native client, painting background rectangles (OBJ_RECTANGLE) per bar will severely bottleneck the terminal thread on M1/M5 charts.

The enterprise-standard approach for MQL is to render this as a sub-window histogram (State: 1 for Long bias, -1 for Short bias, 0 for Flat).

Because you work in C# (cAlgo) and modern MQL, both scripts below are written using strict Object-Oriented paradigms to encapsulate state, mirroring the Pine type architecture.

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:14 am
by PTScalper
MT5 (MQL5) - The Superior Environment

MT5 is ideal for this because the MqlRates struct stores historical spread. This means the spread filter can actually be backtested. MT4 only knows the current real-time spread.

Code: Select all

//+------------------------------------------------------------------+
//|                                     MicroBiasFilter_MT5.mq5      |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   1

#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrNONE, clrTeal, clrMaroon
#property indicator_style1  STYLE_SOLID
#property indicator_width1  3

//--- Inputs
input ENUM_TIMEFRAMES InpHTF           = PERIOD_M15;  // HTF Structure Resolution
input int             InpPivotLookback = 10;          // HTF Pivot Lookback (Legs)
input double          InpVolSpike      = 1.5;         // Volume Spike Multiplier
input double          InpWickAbsorb    = 0.4;         // Sweep/Absorption Wick % (0.0 to 1.0)
input int             InpMaxSpreadPts  = 15;          // Max Spread (Points)
input int             InpSessStartHour = 8;           // Session Start (Hour)
input int             InpSessEndHour   = 17;          // Session End (Hour)

//--- Buffers
double BufferData[];
double BufferColor[];

//+------------------------------------------------------------------+
//| CLASSES (State Encapsulation)                                    |
//+------------------------------------------------------------------+
class CExecutionEnv {
private:
    int m_start, m_end, m_maxSpread;
public:
    void Init(int startH, int endH, int maxSpread) {
        m_start = startH; m_end = endH; m_maxSpread = maxSpread;
    }
    bool IsTradable(datetime time, int spread) {
        MqlDateTime dt;
        TimeToStruct(time, dt);
        bool inSession = (dt.hour >= m_start && dt.hour < m_end);
        return inSession && (spread <= m_maxSpread);
    }
};

class CMarketStructure {
private:
    ENUM_TIMEFRAMES m_htf;
    int m_lookback;
public:
    void Init(ENUM_TIMEFRAMES htf, int lookback) {
        m_htf = htf; m_lookback = lookback;
    }
    int GetBias(datetime ltfTime, double currentClose) {
        int htfShift = iBarShift(_Symbol, m_htf, ltfTime);
        if(htfShift < 0) return 0;
        
        // Find recent HTF extremes to define structure
        double htfHigh[], htfLow[];
        ArraySetAsSeries(htfHigh, true);
        ArraySetAsSeries(htfLow, true);
        
        if(CopyHigh(_Symbol, m_htf, htfShift+1, m_lookback, htfHigh) <= 0) return 0;
        if(CopyLow(_Symbol, m_htf, htfShift+1, m_lookback, htfLow) <= 0) return 0;
        
        int highestIdx = ArrayMaximum(htfHigh, 0, m_lookback);
        int lowestIdx  = ArrayMinimum(htfLow, 0, m_lookback);
        
        double swingH = htfHigh[highestIdx];
        double swingL = htfLow[lowestIdx];
        
        // BoS Bias Definition
        if(currentClose > swingH) return 1;
        if(currentClose < swingL) return -1;
        return 0; // Inside range / Neutral
    }
};

class COrderFlow {
private:
    double m_volMult, m_wickPct;
public:
    void Init(double volMult, double wickPct) {
        m_volMult = volMult; m_wickPct = wickPct;
    }
    int MapFootprint(double h, double l, double c, long v, double avgV) {
        double range = (h - l == 0) ? _Point : (h - l);
        bool isHighVol = v > (avgV * m_volMult);
        double closePos = (c - l) / range;
        
        if(isHighVol && (closePos >= (1.0 - m_wickPct))) return 1;  // Bull Imbalance
        if(isHighVol && (closePos <= m_wickPct)) return -1;         // Bear Imbalance
        return 0;
    }
};

//--- Instances
CExecutionEnv    Env;
CMarketStructure Struct;
COrderFlow       Flow;
int              VolHandle;

//+------------------------------------------------------------------+
//| INIT                                                             |
//+------------------------------------------------------------------+
int OnInit() {
    SetIndexBuffer(0, BufferData, INDICATOR_DATA);
    SetIndexBuffer(1, BufferColor, INDICATOR_COLOR_INDEX);
    IndicatorSetString(INDICATOR_SHORTNAME, "MicroBias MT5");
    
    Env.Init(InpSessStartHour, InpSessEndHour, InpMaxSpreadPts);
    Struct.Init(InpHTF, InpPivotLookback);
    Flow.Init(InpVolSpike, InpWickAbsorb);
    
    VolHandle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE); // Dummy MA for tick volume, requires custom loop in OnCalculate
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| CALCULATION                                                      |
//+------------------------------------------------------------------+
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[]) 
{
    int start = (prev_calculated > 0) ? prev_calculated - 1 : 20;
    
    for(int i = start; i < rates_total; i++) {
        BufferData[i]  = 0.0;
        BufferColor[i] = 0;
        
        // 1. Env Gatekeeper (MT5 stores historical spread!)
        if(!Env.IsTradable(time[i], spread[i])) continue;
        
        // 2. Volume Baseline (SMA of Tick Volume)
        double sumVol = 0;
        for(int v = 0; v < 20; v++) sumVol += (double)tick_volume[i - v];
        double avgVol = sumVol / 20.0;
        
        // 3. Imbalance Footprint
        int flowBias = Flow.MapFootprint(high[i], low[i], close[i], tick_volume[i], avgVol);
        if(flowBias == 0) continue;
        
        // 4. Structure Gatekeeper
        int msBias = Struct.GetBias(time[i], close[i]);
        
        // 5. Alignment Check
        if(msBias == 1 && flowBias == 1) {
            BufferData[i] = 1.0;
            BufferColor[i] = 1; // Teal
        } 
        else if(msBias == -1 && flowBias == -1) {
            BufferData[i] = -1.0;
            BufferColor[i] = 2; // Maroon
        }
    }
    return(rates_total);
}

Re: DOM imbalance settings I use for EURUSD scalp bias only

Posted: Wed Sep 23, 2026 8:14 am
by PTScalper
MT4 (MQL4) - The Legacy Port

MQL4 lacks DRAW_COLOR_HISTOGRAM, so we use two separate standard DRAW_HISTOGRAM buffers (one for Bull, one for Bear). It also lacks historical spread per candle, so the spread logic relies on MarketInfo() and will only enforce itself on the current live bar.

Code: Select all

//+------------------------------------------------------------------+
//|                                     MicroBiasFilter_MT4.mq4      |
//+------------------------------------------------------------------+
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1  clrTeal
#property indicator_color2  clrMaroon
#property indicator_width1  3
#property indicator_width2  3

//--- Inputs
input int    InpHTF           = 15;           // HTF Structure (Minutes)
input int    InpPivotLookback = 10;           // HTF Pivot Lookback (Legs)
input double InpVolSpike      = 1.5;          // Volume Spike Multiplier
input double InpWickAbsorb    = 0.4;          // Sweep/Absorption Wick %
input int    InpMaxSpreadPts  = 15;           // Max Spread (Points - Live Only)
input int    InpSessStartHour = 8;            // Session Start (Hour)
input int    InpSessEndHour   = 17;           // Session End (Hour)

//--- Buffers
double BufferBull[];
double BufferBear[];

//+------------------------------------------------------------------+
//| CLASSES (MQL4 Build 600+ supports OOP)                           |
//+------------------------------------------------------------------+
class CExecutionEnv {
private:
    int m_start, m_end, m_maxSpread;
public:
    void Init(int startH, int endH, int maxSpread) {
        m_start = startH; m_end = endH; m_maxSpread = maxSpread;
    }
    bool IsTradable(datetime time) {
        int h = TimeHour(time);
        bool inSession = (h >= m_start && h < m_end);
        
        // MT4 Cannot backtest spread. Use live spread if it's the current bar.
        int currentSpread = (int)MarketInfo(_Symbol, MODE_SPREAD);
        if(time == Time[0] && currentSpread > m_maxSpread) return false; 
        
        return inSession;
    }
};

class CMarketStructure {
private:
    int m_htf, m_lookback;
public:
    void Init(int htf, int lookback) {
        m_htf = htf; m_lookback = lookback;
    }
    int GetBias(datetime ltfTime, double currentClose) {
        int htfShift = iBarShift(_Symbol, m_htf, ltfTime, false);
        if(htfShift < 0) return 0;
        
        int highestIdx = iHighest(_Symbol, m_htf, MODE_HIGH, m_lookback, htfShift + 1);
        int lowestIdx  = iLowest(_Symbol, m_htf, MODE_LOW, m_lookback, htfShift + 1);
        
        double swingH = iHigh(_Symbol, m_htf, highestIdx);
        double swingL = iLow(_Symbol, m_htf, lowestIdx);
        
        if(currentClose > swingH) return 1;
        if(currentClose < swingL) return -1;
        return 0; 
    }
};

class COrderFlow {
private:
    double m_volMult, m_wickPct;
public:
    void Init(double volMult, double wickPct) {
        m_volMult = volMult; m_wickPct = wickPct;
    }
    int MapFootprint(double h, double l, double c, double v, double avgV) {
        double range = (h - l == 0) ? Point : (h - l);
        bool isHighVol = v > (avgV * m_volMult);
        double closePos = (c - l) / range;
        
        if(isHighVol && (closePos >= (1.0 - m_wickPct))) return 1;
        if(isHighVol && (closePos <= m_wickPct)) return -1;
        return 0;
    }
};

CExecutionEnv    Env;
CMarketStructure Struct;
COrderFlow       Flow;

//+------------------------------------------------------------------+
//| INIT                                                             |
//+------------------------------------------------------------------+
int OnInit() {
    SetIndexBuffer(0, BufferBull);
    SetIndexBuffer(1, BufferBear);
    SetIndexStyle(0, DRAW_HISTOGRAM);
    SetIndexStyle(1, DRAW_HISTOGRAM);
    IndicatorShortName("MicroBias MT4");
    
    Env.Init(InpSessStartHour, InpSessEndHour, InpMaxSpreadPts);
    Struct.Init(InpHTF, InpPivotLookback);
    Flow.Init(InpVolSpike, InpWickAbsorb);
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| CALCULATION                                                      |
//+------------------------------------------------------------------+
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[]) 
{
    int start = (prev_calculated > 0) ? rates_total - prev_calculated : rates_total - 21;
    if(start < 0) start = 0;

    for(int i = start; i >= 0; i--) { // MT4 loops backwards (0 is newest)
        BufferBull[i] = 0;
        BufferBear[i] = 0;
        
        if(!Env.IsTradable(Time[i])) continue;
        
        double sumVol = 0;
        for(int v = 0; v < 20; v++) sumVol += Volume[i + v];
        double avgVol = sumVol / 20.0;
        
        int flowBias = Flow.MapFootprint(High[i], Low[i], Close[i], Volume[i], avgVol);
        if(flowBias == 0) continue;
        
        int msBias = Struct.GetBias(Time[i], Close[i]);
        
        if(msBias == 1 && flowBias == 1) {
            BufferBull[i] = 1.0;
        } 
        else if(msBias == -1 && flowBias == -1) {
            BufferBear[i] = -1.0;
        }
    }
    return(rates_total);
}