Rolling maximum drawdown calculation

I am trying to create a rolling annual maximum draw down plot in the back test report. How I want it to work is to take the back test equity curve, move along it bar by bar, look 252 days ahead and calculate the maximum draw down. I want to store the maximum draw downs for each bar in an array and plot it.

My methodology was to use a nested loop as follows:

  1. The primary for loop captures the maximum draw downs calculated after each iteration of the nested loop
  2. The nested for loop calculates the maximum draw downs every bar by doing the following calculations:
    a) Equity high water level looking forward 252 bars only
    b) Draw down
    c) Maximum draw down after 252 bars only

The issues I’m having are:

  1. I want the nested for loop to stop after 252 bars (or if BarCount < 252 then BarCount). Currently my code keeps iterating all the way to the end of the equity curve (BarCount).
  2. I can’t figure out how to store the maximum draw downs of each iteration in an array.
eq = C; // Equity array

// Variables
eqMax = Null;
dd = Null;
ddMax = 0;
ddMaxSys = 0;
window = 252;

for( i = 455; i < BarCount; i++ )
{
    for( j = i; j < BarCount; j++ ) // Maximum draw down calculation for each new equity curve beginning
    {
        if( j == 0 ) // First bar
        {
            eqMax[j] = eq[j]; // Equity high water level
            dd[j] = 0; // Daily draw down
        }
        
        if( j > 0 ) // Subsequent bars
        {
            eqMax[j] = Max( eq[j], eqMax[j-1] ); // Equity high water level
            dd[j] = 100 * ( eq[j] / eqMax[j] - 1 ); // Daily draw down
            ddMax[j] = Min( dd[j], ddMax[j-1] ); // Maximum draw down
        }
    }
    
    ddMaxSys[i] = ddMax[i]; // Capture maximum draw downs from nested loop
}

Title = StrFormat( "Annual Rolling Drawdowns" );
Plot( ddMaxSys, "", colorLightYellow, styleLine );
SetChartOptions( 2, 0, chartGridPercent );
if( Name() != "~~~EQUITY" AND Name() != "~~~OSEQUITY" ) Title = "Warning: wrong ticker! This chart should be used on ~~~EQUITY or ~~~OSEQUITY only";

I’ve spent considerable time on this and really am stuck. I know this is very noob but if anyone can help me out I would be very appreciative.

Before resorting to loops, you should always consider whether what you're after can be achieved with array processing. It's almost always simpler to code and much quicker to execute.

I'm not sure I fully understand exactly what you're trying to achieve, but as an example, if you were just looking for the rolling max drawdown in the last 252 bars, you could simply use the LLV() function.

So using the default Underwater Equity chart code as a starting point, you could have:

EQ = C;
MaxEQ = Highest( EQ );
DD = 100 * ( Eq - MaxEQ ) / MaxEq;
MaxDD = Lowest( DD );

Title = StrFormat("Drawdown = %.2g%%, Max. drawdown %.2g%%", DD, LastValue( MaxDD ) );

SetGradientFill( GetChartBkColor(), colorBlue, 0 );

Plot( DD, "Drawdown ", colorBlue, styleGradient | styleLine );
Plot( MaxDD, "Max DD", colorRed, styleNoLabel );

// Max DD in last 252 bars
RollingMax = LLV(DD, 252);  
Plot(RollingMax, "Rolling Max", colorViolet, styleThick);

SetChartOptions( 2, 0, chartGridPercent );
if( Name() != "~~~EQUITY" AND Name() != "~~~OSEQUITY"  ) Title = "Warning: wrong ticker! This chart should be used on ~~~EQUITY or ~~~OSEQUITY only";

That gives the violet coloured line below for the Rolling Max:
image

1 Like

Thanks for the reply @HelixTrader. This isn't quite what I am after. Perhaps the graphic below with a few words could explain it a bit better.

  1. Back test 2008 -> 2010 = upper equity and draw down profiles. MaxDD 36.5%, final equity $76,500
  2. Back test 2008 -> 2009 = bottom left equity and draw down profiles. MaxDD 34.9%, final equity $66,800
  3. Back test 2008 -> 2009 = bottom left equity and draw down profiles. MaxDD 14.9%, final equity $141,700
    As we can see the outcomes are different for each bar as opposed to using the single back test MaxDD.

profiles

So for example:

  1. At BarCount = 1 calculate MaxDD looking forward 252 bars. Store MaxDD in an array.
  2. Step forward one bar to BarCount = 2 calculate MaxDD looking forward 252 bars. Store MaxDD in an array.
  3. And so on until we have finished counting all bars and I have an array with MaxDD's for each step forward.

What I'm trying to extract here is what the outcomes would have been if I had started trading the model at different start dates and see what the outcome would be after 252 bars.

Further to the above explanation is a few more graphics.

If we start trading from bar 65 we get a MaxDD of about 14% which matches the entire MaxDD profile.
However, if we start trading the model from bar 360 we get a MaxDD of 19% which is less that the entire MaxDD profile of 22.5%.

I hope this makes sense now.

example

To produce the draw down curves shown above I made the following code.

eq = C; // Equity array

// Variables
eqMax = Null;
dd = Null;
ddMax = 0;
window = 252;

testOffest = 360;

for( i = testOffest; i <= testOffest + window; i++ )
{
	if( i == 0 )
    {
        eqMax[i] = eq[i];
        dd[i] = 0;
        ddMax[i] = 0;
    }
    
    if( i > 0 )
    {
        eqMax[i] = Max( eq[i], eqMax[i-1] );
        dd[i] = 100 * ( eq[i] / eqMax[i] - 1 );
        ddMax[i] = Min( dd[i], ddMax[i-1] );
    }
}

Title = StrFormat( "Annual Rolling Drawdowns" );
Plot( dd, "", colorRed, styleLine );
Plot( ddMax, "", colorLightYellow, styleLine );
SetChartOptions( 2, 0, chartGridPercent );
if( Name() != "~~~EQUITY" AND Name() != "~~~OSEQUITY" ) Title = "Warning: wrong ticker! This chart should be used on ~~~EQUITY or ~~~OSEQUITY only";

OK, so you want to know what the worst drawdown would be in the next 252 bars, if we started trading on any one of each of the bars.

Well if we can work out what the worst drawdown has been in the last 252 bars, we can use Ref to look ahead and see what that value would be in 252 bars time. We can then find the drawdown percentage of the current equity that represents.

We then cap the array values at zero using Min, to take care of periods where we started trading at the bottom of a drawdown period and the equity recovered in the next 252 bars without making any further drawdown, which can cause a positive result.

Finally we can eliminate the final 252 bars from the data, as there will not be enough data to perform the calculation correctly.

EQ = C;
MaxDDAhead = (Ref(LLV(EQ, 252), 252) / EQ - 1) * 100; 
MaxDDAhead = Min(MaxDDAhead, 0); // Prevent positive results
MaxDDAhead = IIf(BarIndex() > BarCount - 252, Null, MaxDDAhead);  // Prevent results less than 252 bars before the end
Plot(MaxDDAhead, "MaxDDAhead", colorViolet, styleThick);

image

5 Likes

Should read: “if we can work out what the lowest equity value has been in the last 252 bars”

Alan, thank you so much. Clearly I was thinking about it too much. The time and effort you’ve taken to provide me with a solution is greatly appreciated.

3 Likes