Gamestudio Links
Zorro Links
Newest Posts
Zorro 3.01 recoded MMI function issue
by TipmyPip. 06/04/26 05:44
SGT_FW
by Aku_Aku. 05/31/26 11:05
ZorroGPT
by TipmyPip. 05/28/26 01:43
Issues resuming trades on Demo account
by Martin_HH. 05/22/26 13:31
XTB
by pr0logic. 05/18/26 12:27
Purchase A8 full licence version
by NeoDumont. 05/13/26 20:17
Black Book, 4th edition
by TipmyPip. 05/11/26 08:40
AUM Magazine
Latest Screens
Dorifto samurai
Shadow 2
Rocker`s Revenge
Stug 3 Stormartillery
Who's Online Now
0 registered members (), 6,352 guests, and 4 spiders.
Key: Admin, Global Mod, Mod
Newest Members
Seraphinang, Koti, curry, DeepxKalsi, Samed
19219 Registered Users
Previous Thread
Next Thread
Print Thread
Rate Thread
Zorro 3.01 recoded MMI function issue #489443
06/01/26 16:02
06/01/26 16:02
Joined: Apr 2024
Posts: 9
11honza11 Offline OP
Newbie
11honza11  Offline OP
Newbie

Joined: Apr 2024
Posts: 9
Hi, after upgrading to 3.01 one of my strategy using MMI got clearly different performance metrics in a backtest. I found that recoded MMI is the culprit. It returns different data than the previous one. So I am wondering which version is "flawed" and which one is "correct".

Re: Zorro 3.01 recoded MMI function issue [Re: 11honza11] #489444
06/02/26 00:21
06/02/26 00:21
Joined: Sep 2017
Posts: 304
TipmyPip Offline
Senior Member
TipmyPip  Offline
Senior Member

Joined: Sep 2017
Posts: 304
I would not judge this only by the changed performance metrics. I would first compare both MMI implementations against the published/reference formula. If the new 3.01 MMI differs from that formula, then the recoded version is likely wrong. If the old version differed, then the old backtest results were based on a flawed indicator. Since MMI is often used as a trend filter, even small output differences can strongly affect entries/exits and performance.

The correct MMI should follow the reference definition: calculate the median of the data window, then count cases where price is above median and continues upward, or below median and continues downward. The classic Zorro/Financial Hacker version is:

Code
var MMI(var *Data,int Length)  
{  
  var m = Median(Data,Length);  
  int i, nh=0, nl=0;
  for(i=1; i<Length; i++) {
    if(Data[i] > m && Data[i] > Data[i-1])
      nl++;
    else if(Data[i] < m && Data[i] < Data[i-1])
      nh++;
  }
  return 100.*(nl+nh)/(Length-1);
}


Zorro’s manual says MMI measures mean-reversal tendency in a 0–100% range, with random numbers around 75%, and that source code is in indicators.c.

So the practical answer is: compare both versions against this reference formula. The version matching this formula is the correct one. If 3.01 changed the median handling, indexing direction, equal-value handling, or the denominator, it can absolutely produce different backtests.

Re: Zorro 3.01 recoded MMI function issue [Re: 11honza11] #489448
06/02/26 11:39
06/02/26 11:39
Joined: Apr 2024
Posts: 9
11honza11 Offline OP
Newbie
11honza11  Offline OP
Newbie

Joined: Apr 2024
Posts: 9
Thank you for a reply! That was the first thing I did - compared both versions and I can confirm the source code is different, and when I copy pasted the previous implementation to my custom indicators script and use it, the backtest showed the original performance metrics - confirming the recoded version indeed changed the strategy.

Original code (indicators.c):
Code
// Zorro's Market Meanness Index
var MMI(var* Data,int TimePeriod)
{
// clip time period to history length
	TimePeriod = Min(TimePeriod,1000);
	checkLookBack(TimePeriod);
	TimePeriod = Min((uint)TimePeriod,g->nBar-1);
	if(TimePeriod < 2) return 75;
// calculate MMI statistics
	var m = Median(Data,TimePeriod); 
	int i, nh=0, nl=0;
	for(i=1; i<TimePeriod; i++) {
		if(Data[i] > m && Data[i] > Data[i-1])
			nl++;
		else if(Data[i] < m && Data[i] < Data[i-1])
			nh++;
	}
	return 100.*(nl+nh)/(TimePeriod-1);
}



Recoded version (indicators.c):
Code
// Zorro's Market Meanness Index
var MMI(var* Data,int TimePeriod)
{
	checkLookBack(TimePeriod);
// clip time period to history length
	TimePeriod = Min((uint)TimePeriod,g->nBar-2);
	if(TimePeriod < 2) return 75;
// calculate MMI statistics
	var m = Median(Data,TimePeriod|1); 
	int i, nh=0, nl=0;
	for(i=1; i<TimePeriod; i++) {
		if(Data[i] > m && Data[i] > Data[i-1])
			nl++;
		else if(Data[i] < m && Data[i] < Data[i-1])
			nh++;
	}
	return 100.*(nl+nh)/(TimePeriod-1);
}


Here are the changes
[Linked Image]

It seems like the previous version is matching the "classic" version you mentioned slightly more, probably indicating the recoded version is somehow flawed.

Attached Files
mmi.png (35 downloads)
Last edited by 11honza11; 06/02/26 11:40.
Re: Zorro 3.01 recoded MMI function issue [Re: 11honza11] #489449
06/03/26 16:38
06/03/26 16:38
Joined: Jul 2000
Posts: 28,101
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 28,101
Frankfurt
The change was the '|1' that prevented even time periods, so that the median always was the data value in the middle. But this should normally only produce tiny differences with negligible effect on the backtest. How large was the difference that you got? Which time periods do you use for the MMI?

Re: Zorro 3.01 recoded MMI function issue [Re: 11honza11] #489450
06/04/26 05:38
06/04/26 05:38
Joined: Sep 2017
Posts: 304
TipmyPip Offline
Senior Member
TipmyPip  Offline
Senior Member

Joined: Sep 2017
Posts: 304
How to test whether your backtest is affected

You can copy both versions into your script and compare them bar by bar.

Code
// Old MMI version
var MMI_old(vars Data,int TimePeriod)
{
    TimePeriod = Min(TimePeriod,1000);
    checkLookBack(TimePeriod);
    TimePeriod = Min(TimePeriod,g->nBar-1);

    if(TimePeriod < 2)
        return 75;

    var m = Median(Data,TimePeriod);

    int i, nh = 0, nl = 0;

    for(i = 1; i < TimePeriod; i++)
    {
        if(Data[i] > m && Data[i] > Data[i-1])
            nl++;
        else if(Data[i] < m && Data[i] < Data[i-1])
            nh++;
    }

    return 100.*(nl+nh)/(TimePeriod-1);
}


// New MMI version
var MMI_new(vars Data,int TimePeriod)
{
    TimePeriod = Min(TimePeriod,1000);
    checkLookBack(TimePeriod);
    TimePeriod = Min(TimePeriod,g->nBar-2);

    if(TimePeriod < 2)
        return 75;

    var m = Median(Data,TimePeriod|1);

    int i, nh = 0, nl = 0;

    for(i = 1; i < TimePeriod; i++)
    {
        if(Data[i] > m && Data[i] > Data[i-1])
            nl++;
        else if(Data[i] < m && Data[i] < Data[i-1])
            nh++;
    }

    return 100.*(nl+nh)/(TimePeriod-1);
}


function run()
{
    BarPeriod = 60;
    LookBack = 300;

    asset("EUR/USD");

    vars Price = series(priceClose());

    int Period = 100;

    var OldMMI = MMI_old(Price,Period);
    var NewMMI = MMI_new(Price,Period);

    plot("Old MMI",OldMMI,NEW,BLUE);
    plot("New MMI",NewMMI,0,RED);
    plot("Difference",NewMMI-OldMMI,NEW,GREEN);

    static int ValueDiffs = 0;
    static int SignalDiffs = 0;

    if(!is(LOOKBACK))
    {
        if(abs(NewMMI-OldMMI) > 0.0001)
            ValueDiffs++;

        int OldSignal = OldMMI > 75;
        int NewSignal = NewMMI > 75;

        if(OldSignal != NewSignal)
        {
            SignalDiffs++;
            printf("\nBar %i: Old MMI %.2f, New MMI %.2f",Bar,OldMMI,NewMMI);
        }
    }

    if(is(EXITRUN))
    {
        printf("\nMMI value differences: %i",ValueDiffs);
        printf("\nMMI signal differences: %i",SignalDiffs);
    }
}



What to look for

If the green Difference plot is often zero, your backtest is probably not affected.

If you see many lines like:

Old MMI 74.95, New MMI 75.12

and your system uses a threshold near 75, then your backtest can change.

The highest-risk cases are:

Code
if(MMI(Price,Period) > 75)
if(MMI(Price,Period) < 50)
if(crossOver(MMI_Series,Threshold))


The lowest-risk case is when MMI is only plotted and not used for trading decisions.

Re: Zorro 3.01 recoded MMI function issue [Re: 11honza11] #489451
06/04/26 05:44
06/04/26 05:44
Joined: Sep 2017
Posts: 304
TipmyPip Offline
Senior Member
TipmyPip  Offline
Senior Member

Joined: Sep 2017
Posts: 304
It can affect backtesting when MMI is used in a trade condition, filter, optimizer, or ML feature, because the changed MMI value can change whether a signal is true or false.

The original MMI code calculates a median over TimePeriod and then counts rising/falling behavior around that median. Zorro’s indicator source shows the old version using TimePeriod = Min(TimePeriod,g->nBar-1); and Median(Data,TimePeriod). The Median function sorts the data and returns the middle value within the given period.

When the backtest can change
1. When TimePeriod is even

Old:

var m = Median(Data,TimePeriod);

New:

var m = Median(Data,TimePeriod|1);

If TimePeriod = 100, then:

TimePeriod|1 = 101

So the median is calculated from 101 values instead of 100.

That can shift the median slightly. Since MMI compares every value against the median:

if(Data[i] > m && Data[i] > Data[i-1])
nl++;
else if(Data[i] < m && Data[i] < Data[i-1])
nh++;

a small median shift can change nl or nh, which changes the returned MMI value.

This matters if your strategy does something like:

if(MMI(Price,100) > 75)
enterLong();

A value changing from 74.9 to 75.2 can create a trade in one version but not the other.

2. Near the beginning of the backtest or WFO cycle

Old:

TimePeriod = Min(TimePeriod,g->nBar-1);

New:

TimePeriod = Min(TimePeriod,g->nBar-2);

The new version uses one less available bar when history is short.

This can affect the first valid bars after LookBack, or the beginning of a walk-forward cycle, especially if TimePeriod is close to the available history length.

Zorro uses LookBack to execute bars before trading begins so indicators have enough history. The manual says the first bar where trades can be entered is greater than or equal to LookBack, and LookBack should cover the longest period of all used indicators, assets, and time frames.

So this change can affect backtesting mostly when:

LookBack ? MMI period

or when TimePeriod is optimized and sometimes becomes large.

3. When MMI is used as a filter

Example:

vars Price = series(priceClose());
var MeanState = MMI(Price,100);

if(MeanState > 75)
enterLong();

If the old version gives:

MMI_old = 74.8

and the new version gives:

MMI_new = 75.3

then the trade only happens with the new version.

That can change:

entry timing, number of trades, profit factor, drawdown, optimized parameters, WFO results

The Zorro manual notes that indicators often become buy/sell signals when they reach thresholds, cross each other, or cross the price curve.

4. When MMI period is optimized

This is a big one.

Example:

int MMIPeriod = optimize(100,50,300,10);
var M = MMI(Price,MMIPeriod);

If some optimized values are even, the new version internally turns the median length odd:

100 -> 101
120 -> 121
200 -> 201

So the optimizer may find a different best parameter than before.

Zorro’s documentation specifically warns that when optimizing an indicator time period, LookBack should be set to at least the maximum period, otherwise the backtest period can change with the optimized value and affect results unexpectedly.


Moderated by  Petra 

Gamestudio download | Zorro platform | shop | Data Protection Policy

oP group Germany GmbH | Birkenstr. 25-27 | 63549 Ronneburg / Germany | info (at) opgroup.de

Powered by UBB.threads™ PHP Forum Software 7.7.1