multi-timeframe “Market Mode Index” that blends a fast and a slow version of the same measure, then adapts it dynamically using machine learning.

Let’s break it down step-by-step:

1. Core Idea: Market Mode Index (MMI)
MMI here is a composite metric built from three sub-indicators:

sineMI() – Compares the price’s deviation from a pure sine wave.

Measures cyclicity — whether the market is behaving like a smooth oscillation.

predictabilityMI() – Uses a simple linear regression on lagged prices.

Measures linear predictability — can future price be explained by the past two bars?

spectralEnergy() – Compares “low-band” vs. “high-band” volatility.

Measures energy distribution — is the market dominated by slow or fast components?

These are averaged and normalized between 0 and 1, where:

Low MMI (~0.3 or less) ? Market is more trending or predictable.

High MMI (~0.7 or more) ? Market is more noisy or mean-reverting.

2. Two Timeframes You calculate MMI in:

Fast timeframe ? Base BarPeriod (e.g., 60 min bars).

Slow timeframe ? SLOW_FACTOR * BarPeriod (e.g., 4h bars if SLOW_FACTOR=4).

This produces:

mmiFast = Short-term market mode.

mmiSlow = Longer-term market mode (re-projected to the base TF so they align).

3. Machine Learning Forecast
The script builds a feature vector from past values of both MMI series:

First 5 values from mmiFast (lagged 1..5 bars)

First 5 values from mmiSlow (lagged 1..5 bars)

That 10-dimensional vector goes into:

adviseLong(TRAIN_BARS, 0, features, MAX_FEATURES)
This uses Zorro’s built-in machine learning (default perceptron) to predict ? (change) in MMI from this combined history.

4. Adaptive Blending
The predicted ? (direction & magnitude) controls:

How much weight to give mmiFast vs. mmiSlow in the final blend.

How fast the blended MMI is smoothed (adaptive EMA period).

This way:

If the ML thinks fast MMI is stable ? more weight on fast component.

If ML thinks change is coming ? more weight on slow component, slower smoothing.

5. Outputs It plots:

Adaptive MMI (red) ? The blended, ML-weighted index.

Fast MMI (blue)

Slow MMI (black, projected to base TF)

Pred ? (purple) ? Normalized ML forecast of MMI change.

Low (green line at 0.3) ? Often a trend-friendly zone.

High (orange line at 0.7) ? Often a range/noise zone.

How to Use It
Traders often interpret MMI like this:

Below low threshold (~0.3) ? Favors trend-following systems.

Above high threshold (~0.7) ? Favors mean-reversion systems.

Between thresholds ? No clear bias.

Here, you have an adaptive, multi-TF version that tries to smooth noise and anticipate regime changes rather than reacting only to raw MMI.

Code
// === Config ===
#define INPUT_SIZE     5       // past points per TF
#define TRAIN_BARS     500
#define SLOW_FACTOR    4       // slow TF = SLOW_FACTOR * BarPeriod
#define MAX_FEATURES   10      // 2 * INPUT_SIZE
#define LONGEST_EMA    89      // longest fixed EMA length in code

// === Safety Helpers ===
int safeWin(int requested)
{
    return min(requested, max(2, Bar - 1)); // clamp to available bars
}

var clamp(var value, var min, var max)
{
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

// === Adaptive Window ===
var adaptiveWinLength()
{
    static var* atrSeries;
    atrSeries = series(ATR(14));
    var atr_val = atrSeries[0];
    return max(10, round(50 * (atr_val / priceClose()) * 1.0));
}

// === Indicators ===
var sineWave()
{
    return sin(2 * PI * Bar / 20); // SINE_LEN = 20
}

var spectralEnergy(int baseWin)
{
    static var* pClose;
    static var* lowBandSeries;
    static var* highBandSeries;

    pClose = series(priceClose());

    var ema34 = EMA(pClose, safeWin(34));
    var ema89 = EMA(pClose, safeWin(LONGEST_EMA));
    var lowBand  = ema34 - ema89;

    var ema8  = EMA(pClose, safeWin(8));
    var highBand = pClose[0] - ema8;

    lowBandSeries  = series(lowBand);
    highBandSeries = series(highBand);

    var energyLow  = Variance(lowBandSeries, safeWin(baseWin));
    var energyHigh = Variance(highBandSeries, safeWin(baseWin));

    var spectralRatio = energyHigh / (energyHigh + energyLow + 0.000001);
    return clamp(spectralRatio, 0, 1);
}

var predictabilityMI(int window)
{
    static var* p;
    static var* p1;
    static var* p2;
    static var* p1_sq;
    static var* p2_sq;
    static var* p1p2;
    static var* p1p;
    static var* p2p;
    static var* res_sq;

    p     = series(priceClose());
    p1    = series(priceClose(1));
    p2    = series(priceClose(2));
    p1_sq = series(p1[0]*p1[0]);
    p2_sq = series(p2[0]*p2[0]);
    p1p2  = series(p1[0]*p2[0]);
    p1p   = series(p1[0]*p[0]);
    p2p   = series(p2[0]*p[0]);

    var sum_x1 = EMA(p1, safeWin(window));
    var sum_x2 = EMA(p2, safeWin(window));
    var sum_y  = EMA(p,  safeWin(window));

    var sum_x1x1 = EMA(p1_sq, safeWin(window));
    var sum_x2x2 = EMA(p2_sq, safeWin(window));
    var sum_x1x2 = EMA(p1p2,  safeWin(window));
    var sum_x1y  = EMA(p1p,   safeWin(window));
    var sum_x2y  = EMA(p2p,   safeWin(window));

    var denom = sum_x1x1 * sum_x2x2 - sum_x1x2 * sum_x1x2;
    var a = ifelse(denom != 0, (sum_x2x2 * sum_x1y - sum_x1x2 * sum_x2y) / denom, 0);
    var b = ifelse(denom != 0, (sum_x1x1 * sum_x2y - sum_x1x2 * sum_x1y) / denom, 0);

    var y_hat    = a * p1[0] + b * p2[0];
    var residual = p[0] - y_hat;

    res_sq = series(pow(residual, 2));

    var mse_pred  = EMA(res_sq, safeWin(window));
    var var_price = Variance(p, safeWin(50));

    return clamp(mse_pred / var_price, 0, 1);
}

var sineMI(int window)
{
    static var* p;
    static var* s;
    static var* sine_dev_sq;

    p = series(priceClose());
    s = series(sineWave());

    var sine_dev  = s[0] - EMA(s, safeWin(window));
    var price_dev = p[0] - EMA(p, safeWin(window));

    sine_dev_sq = series(pow(price_dev - sine_dev, 2));

    var mse_sine  = EMA(sine_dev_sq, safeWin(window));
    var var_price = Variance(p, safeWin(50));

    return clamp(mse_sine / var_price, 0, 1);
}

var computeMMI(int win)
{
    var mi_sine = sineMI(win);
    var mi_pred = predictabilityMI(win);
    var mi_spec = spectralEnergy(50);
    return clamp((mi_sine + mi_pred + mi_spec) / 3, 0, 1);
}

// === Feature readiness ===
int featuresReady(var* fast, var* slow, int size)
{
    int i;
    for(i = 0; i < size; i++)
        if(fast[i+1] == 0 || slow[i+1] == 0)
            return 0; 
    return 1;
}

// === Main ===
function run()
{
    set(PLOTNOW);
    BarPeriod = 60; 
    LookBack  = max(LONGEST_EMA * 2, 2*INPUT_SIZE + 12);

    // Global warm-up guard
    int slowBars = Bar / SLOW_FACTOR;
    int minSlowBars = max(LONGEST_EMA * 2, 2*INPUT_SIZE + 12);
    if (Bar < TRAIN_BARS || slowBars < minSlowBars)
        return;

    // ===== FAST TF =====
    int fastWin = adaptiveWinLength();
    static var* mmiFast;
    mmiFast = series(EMA(series(computeMMI(fastWin)), safeWin(5)));

    // ===== SLOW TF =====
    int tfKeep = TimeFrame;
    TimeFrame  = SLOW_FACTOR;

    int slowWin = max(10, round(fastWin / SLOW_FACTOR));
    static var* mmiSlowTF;
    mmiSlowTF = series(EMA(series(computeMMI(slowWin)), safeWin(5)));

    var mmiSlowNow = mmiSlowTF[0];
    TimeFrame = tfKeep;

    static var* mmiSlowOnBase;
    mmiSlowOnBase = series(mmiSlowNow);

    // ===== Feature guard =====
    if (!featuresReady(mmiFast, mmiSlowOnBase, INPUT_SIZE))
        return;

    // ===== ML Features =====
    var features[MAX_FEATURES];
    int i;
    for(i=0; i<INPUT_SIZE; i++)
        features[i] = mmiFast[i+1];
    for(i=0; i<INPUT_SIZE; i++)
        features[INPUT_SIZE + i] = mmiSlowOnBase[i+1];

    var predicted_delta = adviseLong(TRAIN_BARS, 0, features, MAX_FEATURES);
    var norm_delta = clamp(predicted_delta, -1, 1);

    var adaptFactor  = clamp(1 - fabs(norm_delta), 0.4, 0.9);
    int adaptPeriod  = max(2, min(50, (int)round(5./adaptFactor)));

    var w_fast = clamp(0.5 + 0.5*fabs(norm_delta), 0.4, 0.9);
    var w_slow = 1 - w_fast;
    var cmi_blend = w_fast*mmiFast[0] + w_slow*mmiSlowOnBase[0];

    var adaptiveMMI = clamp(EMA(series(cmi_blend), safeWin(adaptPeriod)), 0, 1);

    // ===== Plots =====
    plot("Adaptive MMI", adaptiveMMI, LINE, RED);
    plot("Fast MMI",     mmiFast[0],  LINE, BLUE);
    plot("Slow MMI",     mmiSlowOnBase[0], LINE, BLACK);
    plot("Pred ?",       norm_delta,  LINE, PURPLE);
    plot("Low",  0.3, LINE, GREEN);
    plot("High", 0.7, LINE, ORANGE);
}