How calculate the maximum drawdown in custom backtester

I need calculate the maximum drawdown to use this data to determine o PS. Help.

for( bar = 0; bar < BarCount; bar++ ) 
   	//To get the value of total cumulative Profit you can use : Current Portfolio Equity - Initial Equity
   	InitialEquity = 100000;
   	EquityAtual = bo.Equity;
   	Conta = bo.EquityArray;
    CumProfit = 	EquityAtual - InitialEquity;
     for( sig = bo.GetFirstSignal( bar ); sig; sig = bo.GetNextSignal( bar ) ) 
     Maximo = HHV (Conta);
     Minimo = LLV (Conta);
     MDD = Maximo - Minimo; //Is not working
	 Contrato = sig.MarginDeposit + (1.5*MDD);
	 Total = EquityAtual - Contrato;
	 Ncontratos = int(Total/(1.5*MDD));
	 TC = 1 + Ncontratos;
	 Dolar = TC * sig.MarginDeposit;
	 sig.PosSize = Dolar; 
     bo.ProcessTradeSignals( bar ); 

@issqueiroz that won't work for a couple of reasons. First, HHV() and LLV() work on a specified number of periods, as described here: Instead you should use the Highest() and Lowest() functions ( if you want the max and min values that have occurred at any bar up to and including the current bar.

However, there is a second problem, which is that you're not checking whether the equity low occurred after the equity high. Here is one way to calculate max DD as a percentage of the equity high:

eq = bo.EquityArray;
pctDD = 100 * (Eq/Highest(Eq) - 1);
maxDD = LastValue(Lowest(pctDD));

I think you can rather use Equity(1) function.

myEquity = Equity(1,0);
HighestPeak = HHV(myEquity,BarCount);
DD = (HighestPeak - myEquity)/HighestPeak * 100;
maxDD = HHV(DD,BarCount);

This way you can get maxDD at each bar (which gives the maxDD so far or in other words, till that bar)

FYI, Equity() function is single security backtester but not working on portfolio level.


Maximum drawdown is a standard metric that is simply available directly without need to calculate it by hand. It is included in the standard backtest report and it is included in built-in statistics object in custom backtester (see

// 'bo' variable holds Backtester object retrieved earlier
// should be called after backtest inside CBT formula
stats = bo.GetPerformanceStats( 0 );
mdd = stats.GetValue("MaxSystemDrawdown"); // or MaxSystemDrawdownPercent

If you need it as array for charting equity/drawdown chart, then maximum drawdown formula is known and included in Underwater included in standard installation. It works on ~~~EQUITY ticker that is portfolio level equity.


// Underwater Equity chart
// (C)2009
// Should be used only on ~~~EQUITY or ~~~OSEQUITY symbol

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 );

SetChartOptions( 2, 0, chartGridPercent );

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

In custom backtester, drawdown can be calculated out of bo.Equity or bo.EquityArray pretty much the same way, but not using HHV/LLV array functions but using looping.

Just update your maximo/minimo variables inside the loop

// INSIDE YOUR custom backtester code 
maximo = minimo = bo.Equity;
for( bar = 0; bar < BarCount; bar++ ) 
      maximo = Max( bo.Equity, maximo );
      minimo = Min( bo.Equity, minimo );
      incorrect_maxdd = maximo - minimo; // your INCORRECT mdd formula

As @mradtke noted the way you calculate MDD is incorrect because you are not taking into account sequence of events. For proper drawdown you should be tracking maximum of equity, then calculate current drawdown (maximo - bo.Equity) and then track maximum of current drawdown.

So your code should actually be looking like this:


if (Status("action") == actionPortfolio) 
    bo = GetBacktesterObject();	//  Get backtester object
    bo.PreProcess();	//  Do pre-processing (always required)

	// snippet INSIDE YOUR custom backtester code 
	// maxeq and maxdd are scalars, not arrays!
	maxeq = bo.Equity; 
	maxdd = 0;
    for (i = 0; i < BarCount; i++)	//  Loop through all bars
        maxeq = Max( bo.Equity, maxeq );
		curdd = maxeq - bo.Equity; 
		maxdd = Max( curdd, maxdd );
        for (sig = bo.GetFirstSignal( i ); sig; sig = bo.GetNextSignal( i ) )
              // do your custom signal processing
        bo.ProcessTradeSignals( i );	//  Process trades at bar (always required)
    bo.PostProcess();	//  Do post-processing (always required)