# 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;
}

}
``````

@issqueiroz that won't work for a couple of reasons. First, `HHV()` and `LLV()` work on a specified number of periods, as described here: https://www.amibroker.com/guide/afl/hhv.html. Instead you should use the `Highest()` and `Lowest()` functions (https://www.amibroker.com/guide/afl/highest.html) 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));
``````
3 Likes

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.

2 Likes

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 http://www.amibroker.com/guide/a_custombacktest.html)

``````// '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 Equity.afl included in standard installation. It works on ~~~EQUITY ticker that is portfolio level equity. ``````// Underwater Equity chart
// (C)2009 AmiBroker.com
// 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 ) );

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:

``````SetCustomBacktestProc("");

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
}