Page 1 of 5

The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 6:40 pm
by LondonScalper
The one indicator I still trust for bias only

I stripped most indicators years ago. One remains, and only as bias, never as an entry trigger: a simple higher-timeframe trend / structure read (think H1 or H4 swing direction, or a slow MA slope — nothing exotic). If M1 noise disagrees with that bias, I take fewer counter-trend scratches and accept that sitting is often the trade.

Rules that keep it honest:
  • Bias is set pre-session; I do not flip it mid-scalp because RSI coughed
  • Entries still come from price and levels — the indicator cannot green-light a bad location
  • If bias is unclear, size down or sit — the tool’s job is permission to stand aside
I am not selling a stack. I am asking: if you were allowed one non-price tool for bias only, what would it be — and how do you stop it becoming a crutch for forced trades?

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:14 pm
by PTScalper
Hi LondonScalper,

Great topic. Stripping the charts bare is usually the turning point for most consistently profitable traders.

My core methodology relies almost entirely on raw price action, candlestick structure, and liquidity sweeps on the Daily and 15-minute charts. Lagging indicators usually just muddy the waters when you are trying to read the actual order flow.

But if forced to choose exactly one non-price tool strictly for bias, it would be a Higher-Timeframe (HTF) Exponential Moving Average (e.g., a Daily 20 EMA). Not to trade from, but simply to dictate the macro directional environment.

How do you stop it from becoming a crutch? By physically removing it from the price action.

If you plot a moving average directly over your execution candles, your brain inevitably tries to use it as dynamic support or resistance. That leads to forced trades in bad locations just because price "touched the line."

To keep it honest, I code the HTF bias as a simple, unobtrusive colored ribbon at the bottom of the screen. Green means longs are permitted; red means shorts are permitted. The actual entry must still come exclusively from a structural shift or a liquidity sweep at a key level on the execution timeframe. The indicator doesn't give the green light to enter; it only gives the red light to stand aside.

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:14 pm
by PTScalper
The Pine Script: HTF Bias Ribbon

This script keeps your main chart completely clean for raw price action. It plots a simple histogram ribbon in a separate pane below your chart. It pulls the trend from your chosen higher timeframe (default is Daily) and colors the ribbon based on where the HTF price is relative to the HTF moving average.

Code: Select all

//@version=5
indicator("HTF Bias Ribbon", overlay=false)

// --- Inputs ---
grp1 = "Bias Settings"
htf = input.timeframe("D", title="Higher Timeframe", group=grp1)
ma_type = input.string("EMA", title="MA Type", options=["EMA", "SMA"], group=grp1)
ma_length = input.int(20, title="MA Length", minval=1, group=grp1)

// --- HTF Calculations ---
// Calculate the MA based on user selection
float ma_value = ma_type == "EMA" ? ta.ema(close, ma_length) : ta.sma(close, ma_length)

// Request the HTF close and HTF MA
// lookahead_off ensures no repainting/peeking into the future on historical bars
htf_close = request.security(syminfo.tickerid, htf, close, lookahead=barmerge.lookahead_off)
htf_ma = request.security(syminfo.tickerid, htf, ma_value, lookahead=barmerge.lookahead_off)

// --- Bias Logic ---
bool is_bullish = htf_close > htf_ma
bool is_bearish = htf_close < htf_ma

// --- Visuals ---
// Use muted colors so it isn't distracting
color ribbon_color = is_bullish ? color.new(color.teal, 30) : 
                     is_bearish ? color.new(color.maroon, 30) : 
                     color.new(color.gray, 50)

// Plot as a histogram in a separate pane to create a clean ribbon effect
plot(1, title="Directional Bias", color=ribbon_color, style=plot.style_histogram, linewidth=4)

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:16 pm
by PTScalper
If forced to deploy exactly one non-price tool strictly for bias, it cannot be a binary "above is long, below is short" moving average. That is how retail traders get chopped to pieces in ranging markets. The tool must be a Higher-Timeframe (HTF) Regime Filter that incorporates volume and explicitly defines when not to trade.

To stop it from becoming a crutch, you enforce two strict rules:

Spatial Isolation: It must be removed from the price pane. If an indicator is overlaid on your candles, your brain will subconsciously try to trade it as dynamic support or resistance.

The Trinary State: The bias must have a "Neutral" state. By wrapping the HTF mean in an ATR-based buffer zone, the tool forces you to sit on your hands when the higher timeframe is consolidating. It doesn't give you permission to enter; it simply revokes permission to counter-trend.

Here is the professional-grade implementation.

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:16 pm
by PTScalper
The Pine Script: Pro Regime Ribbon

This script calculates a Volume-Weighted Moving Average (VWMA) or EMA from a higher timeframe. Crucially, it calculates an ATR band around that moving average. If the HTF price is trapped inside that band, the ribbon turns grey—indicating chop, meaning you size down or sit out.

Code: Select all

//@version=5
indicator("HTF Regime Ribbon [Pro]", overlay=false)

// --- Inputs ---
grp_ma = "Regime Parameters"
htf_res = input.timeframe("D", title="Higher Timeframe", group=grp_ma)
ma_type = input.string("VWMA", title="MA Type", options=["VWMA", "EMA", "SMA"], group=grp_ma)
length = input.int(20, title="Lookback Length", minval=1, group=grp_ma)

grp_chop = "Neutral Zone (Chop Filter)"
atr_length = input.int(14, title="ATR Length", group=grp_chop)
atr_mult = input.float(0.5, title="ATR Band Multiplier", step=0.1, group=grp_chop, tooltip="Distance from the MA considered as consolidation.")

// --- Local Calculations ---
// Calculate MA and ATR on the current timeframe before security call to ensure proper data handling
float local_ma = switch ma_type
    "VWMA" => ta.vwma(close, length)
    "EMA"  => ta.ema(close, length)
    => ta.sma(close, length)

float local_atr = ta.atr(atr_length)

// --- HTF Data Request ---
// lookahead_off is critical to prevent repainting and forward-looking bias
[htf_c, htf_ma, htf_atr] = request.security(syminfo.tickerid, htf_res, [close, local_ma, local_atr], lookahead=barmerge.lookahead_off)

// --- Logic & State ---
// Define the upper and lower boundaries of the neutral zone
float upper_band = htf_ma + (htf_atr * atr_mult)
float lower_band = htf_ma - (htf_atr * atr_mult)

// Determine the regime state
bool is_bullish = htf_c > upper_band
bool is_bearish = htf_c < lower_band
// If it's neither, it's trapped in the neutral zone (chop)

// --- Rendering ---
// Institutional color palette: desaturated for minimal psychological interference
color regime_color = is_bullish ? color.new(#089981, 20) : // Muted Teal
                     is_bearish ? color.new(#F23645, 20) : // Muted Red
                     color.new(#787B86, 50)                // Grey (Neutral/Chop)

plot(1, title="Regime Bias", color=regime_color, style=plot.style_histogram, linewidth=4)

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:18 pm
by PTScalper
Translating this logic into MQL requires handling a specific architectural trap that Pine Script manages natively: multi-timeframe repainting.

When you pull a Daily moving average down to a 15-minute chart, the Daily candle is still forming. If price breaches the MA intraday, the indicator flips—only to flip back if price retracts before the daily close. This destroys the psychological benefit of a static bias.

To prevent this, both scripts below enforce a strict htf_shift + 1 rule. This locks your intraday ribbon to the data of the last fully closed higher-timeframe candle. The bias is set before the session and cannot morph while you are scalping.

(Note: MQL does not have a native Volume-Weighted Moving Average in its standard enumerations, so both scripts default to the EMA, which is standard for forex and index microstructure).

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:19 pm
by PTScalper
MT5 (MQL5) Implementation

MQL5 uses handles to fetch indicator data. We map a DRAW_COLOR_HISTOGRAM to dynamically shift colors based on the regime state.

Code: Select all

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

#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrTeal, clrMaroon, clrGray
#property indicator_style1  STYLE_SOLID
#property indicator_width1  4

input ENUM_TIMEFRAMES InpHTF       = PERIOD_D1;    // Higher Timeframe
input ENUM_MA_METHOD  InpMAType    = MODE_EMA;     // MA Type
input int             InpMAPeriod  = 20;           // MA Period
input int             InpATRPeriod = 14;           // ATR Period
input double          InpATRMult   = 0.5;          // ATR Band Multiplier

double BufferHist[];
double BufferColors[];

int handle_ma;
int handle_atr;

int OnInit() {
    SetIndexBuffer(0, BufferHist, INDICATOR_DATA);
    SetIndexBuffer(1, BufferColors, INDICATOR_COLOR_INDEX);
    IndicatorSetString(INDICATOR_SHORTNAME, "Regime Bias");

    handle_ma = iMA(_Symbol, InpHTF, InpMAPeriod, 0, InpMAType, PRICE_CLOSE);
    handle_atr = iATR(_Symbol, InpHTF, InpATRPeriod);

    if(handle_ma == INVALID_HANDLE || handle_atr == INVALID_HANDLE) {
        Print("Error initializing indicator handles.");
        return INIT_FAILED;
    }
    return INIT_SUCCEEDED;
}

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 limit = prev_calculated == 0 ? 0 : prev_calculated - 1;
    
    double ma_arr[1], atr_arr[1], htf_close[1];

    for(int i = limit; i < rates_total && !IsStopped(); i++) {
        // Find HTF bar corresponding to current timeframe's bar time
        int htf_shift = iBarShift(_Symbol, InpHTF, time[i], false);
        
        // +1 locks to the last CLOSED HTF bar to prevent intraday repainting
        int target_shift = htf_shift + 1;

        if(CopyBuffer(handle_ma, 0, target_shift, 1, ma_arr) <= 0) continue;
        if(CopyBuffer(handle_atr, 0, target_shift, 1, atr_arr) <= 0) continue;
        if(CopyClose(_Symbol, InpHTF, target_shift, 1, htf_close) <= 0) continue;
        
        double upper_band = ma_arr[0] + (atr_arr[0] * InpATRMult);
        double lower_band = ma_arr[0] - (atr_arr[0] * InpATRMult);

        BufferHist[i] = 1.0; // Static height for the ribbon effect

        if(htf_close[0] > upper_band) {
            BufferColors[i] = 0; // clrTeal (Bullish)
        } else if(htf_close[0] < lower_band) {
            BufferColors[i] = 1; // clrMaroon (Bearish)
        } else {
            BufferColors[i] = 2; // clrGray (Neutral / Chop)
        }
    }
    return rates_total;
}

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:19 pm
by PTScalper
MT4 (MQL4) Implementation

MQL4 handles color histograms differently and allows for direct polling of the iMA and iATR functions without needing to build handles in OnInit. We use three separate histogram buffers stacked on each other.

Code: Select all

//+------------------------------------------------------------------+
//|                                              HTF_Regime_Bias.mq4 |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 1
#property indicator_buffers 3

#property indicator_color1 clrTeal
#property indicator_color2 clrMaroon
#property indicator_color3 clrGray

#property indicator_width1 4
#property indicator_width2 4
#property indicator_width3 4

extern int    InpHTF       = PERIOD_D1; // Higher Timeframe (e.g., 1440 for D1)
extern int    InpMAType    = MODE_EMA;  // MA Type
extern int    InpMAPeriod  = 20;        // MA Period
extern int    InpATRPeriod = 14;        // ATR Period
extern double InpATRMult   = 0.5;       // ATR Band Multiplier

double BullBuffer[];
double BearBuffer[];
double ChopBuffer[];

int OnInit() {
    SetIndexStyle(0, DRAW_HISTOGRAM);
    SetIndexBuffer(0, BullBuffer);
    
    SetIndexStyle(1, DRAW_HISTOGRAM);
    SetIndexBuffer(1, BearBuffer);
    
    SetIndexStyle(2, DRAW_HISTOGRAM);
    SetIndexBuffer(2, ChopBuffer);
    
    IndicatorShortName("Regime Bias");
    return(INIT_SUCCEEDED);
}

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 limit = rates_total - prev_calculated;
    if (prev_calculated == 0) limit = rates_total - 1;

    for (int i = limit; i >= 0; i--) {
        // Find the shift of the HTF bar that aligns with the current timeframe's bar time
        int htf_shift = iBarShift(Symbol(), InpHTF, time[i], false);
        
        // +1 locks to the last CLOSED HTF bar to prevent intraday repainting
        int target_shift = htf_shift + 1; 

        double htf_c   = iClose(Symbol(), InpHTF, target_shift);
        double htf_ma  = iMA(Symbol(), InpHTF, InpMAPeriod, 0, InpMAType, PRICE_CLOSE, target_shift);
        double htf_atr = iATR(Symbol(), InpHTF, InpATRPeriod, target_shift);

        double upper_band = htf_ma + (htf_atr * InpATRMult);
        double lower_band = htf_ma - (htf_atr * InpATRMult);

        // Reset buffers
        BullBuffer[i] = 0;
        BearBuffer[i] = 0;
        ChopBuffer[i] = 0;

        if (htf_c > upper_band) {
            BullBuffer[i] = 1.0;
        } else if (htf_c < lower_band) {
            BearBuffer[i] = 1.0;
        } else {
            ChopBuffer[i] = 1.0;
        }
    }
    return(rates_total);
}

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:21 pm
by PTScalper
In the MetaTrader environment, moving to a production-grade indicator requires solving two architectural problems that basic retail scripts ignore:

Asynchronous Data Handling: MetaTrader 5 loads higher-timeframe data asynchronously. A standard script will throw array out-of-range errors or paint false signals (returning -1 or EMPTY_VALUE) if the HTF history hasn't fully synchronized in the background.

Trend Exhaustion (The Liquidity Trap): A trend isn't infinite. If the higher timeframe is stretched too far from its mean (e.g., > 2.5 ATR), the risk of a deep pullback or a liquidity sweep is extremely high. Buying into a bullish bias that is mathematically exhausted is how retail provides liquidity to institutions.

Here is the enterprise-grade rebuild. We are adding robust background synchronization checks and an Exhaustion State that warns you when the HTF price is overextended and vulnerable to a sweep.

Re: The one indicator I still trust for bias only

Posted: Fri Sep 18, 2026 7:21 pm
by PTScalper
MT5 (MQL5) Pro Implementation

This version enforces strict memory management, checks if the HTF series is synchronized before calculating, and introduces a 4-state output (Bullish, Bearish, Chop, Exhausted).

Code: Select all

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

#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrTeal, clrMaroon, clrGray, clrGoldenrod
#property indicator_style1  STYLE_SOLID
#property indicator_width1  4

// --- Input Parameters ---
input group "Regime Parameters"
input ENUM_TIMEFRAMES InpHTF           = PERIOD_D1;    // Higher Timeframe
input ENUM_MA_METHOD  InpMAType        = MODE_EMA;     // MA Type
input int             InpMAPeriod      = 20;           // MA Period

input group "Volatility Bands"
input int             InpATRPeriod     = 14;           // ATR Period
input double          InpATRNeutral    = 0.5;          // Chop Zone Multiplier (e.g. 0.5)
input double          InpATRExhaustion = 2.5;          // Exhaustion Multiplier (e.g. 2.5)

// --- Global Variables ---
double BufferHist[];
double BufferColors[];
int    handle_ma;
int    handle_atr;

int OnInit() {
    SetIndexBuffer(0, BufferHist, INDICATOR_DATA);
    SetIndexBuffer(1, BufferColors, INDICATOR_COLOR_INDEX);
    
    IndicatorSetString(INDICATOR_SHORTNAME, "Pro Regime Bias");
    IndicatorSetInteger(INDICATOR_DIGITS, 0);

    handle_ma = iMA(_Symbol, InpHTF, InpMAPeriod, 0, InpMAType, PRICE_CLOSE);
    handle_atr = iATR(_Symbol, InpHTF, InpATRPeriod);

    if(handle_ma == INVALID_HANDLE || handle_atr == INVALID_HANDLE) {
        Print("CRITICAL ERROR: Failed to acquire indicator handles.");
        return INIT_FAILED;
    }
    
    return INIT_SUCCEEDED;
}

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[]) {

    // 1. Check if HTF data is physically synchronized with the server
    if (!SeriesInfoInteger(_Symbol, InpHTF, SERIES_SYNCHRONIZED)) {
        return 0; // Return 0 forces MT5 to recalculate on the next tick once data arrives
    }

    int limit = prev_calculated == 0 ? 0 : prev_calculated - 1;
    
    // Arrays for copying HTF data
    double ma_arr[1], atr_arr[1], htf_close[1];

    for(int i = limit; i < rates_total && !IsStopped(); i++) {
        int htf_shift = iBarShift(_Symbol, InpHTF, time[i], false);
        int target_shift = htf_shift + 1; // Lock to last closed bar to prevent repainting

        // 2. Safely copy data; if unavailable, skip and leave blank to avoid false signals
        if(CopyBuffer(handle_ma, 0, target_shift, 1, ma_arr) <= 0) continue;
        if(CopyBuffer(handle_atr, 0, target_shift, 1, atr_arr) <= 0) continue;
        if(CopyClose(_Symbol, InpHTF, target_shift, 1, htf_close) <= 0) continue;
        
        double ma_val = ma_arr[0];
        double atr_val = atr_arr[0];
        double htf_c = htf_close[0];

        // 3. Define Volatility Bands
        double upper_chop = ma_val + (atr_val * InpATRNeutral);
        double lower_chop = ma_val - (atr_val * InpATRNeutral);
        
        double upper_exhaust = ma_val + (atr_val * InpATRExhaustion);
        double lower_exhaust = ma_val - (atr_val * InpATRExhaustion);

        BufferHist[i] = 1.0; 

        // 4. Regime Logic Mapping
        if(htf_c > upper_exhaust || htf_c < lower_exhaust) {
            BufferColors[i] = 3; // clrGoldenrod: Exhausted (High risk of liquidity sweep)
        } 
        else if(htf_c > upper_chop) {
            BufferColors[i] = 0; // clrTeal: Safe Bullish Trend
        } 
        else if(htf_c < lower_chop) {
            BufferColors[i] = 1; // clrMaroon: Safe Bearish Trend
        } 
        else {
            BufferColors[i] = 2; // clrGray: Neutral / Chop Zone
        }
    }
    
    return rates_total;
}