Understanding array population in Custom Back Tester

Hello,
from this thread Closed trade equity curve here is a portion of code that will calculate a closed equity curve.
There is something in this code that is working as intended but I don't understand why and would like some clarification please:

SetCustomBacktestProc(""); 
if( Status("action") == actionPortfolio ) 
{ 
    bo = GetBacktesterObject(); 
	bo.Backtest(); // run default backtest procedure 

	dt = DateTime();
	ClosedEquity = 0; // initialise array
	ClosedEquity[0] = bo.InitialEquity; // set first array element to initial equity
	
	for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade())
    {
		ClosedEquity += IIf(dt == trade.ExitDateTime, trade.GetProfit(), 0); // store cumulative trade profit values at exit date points
    }
    
    for( i = 1; i < BarCount; i++ ) // loop through bars to fill in blank values and accumulate closed equity values
	{
		if (ClosedEquity[i] == 0)
			ClosedEquity[i] = ClosedEquity[i - 1];
		else
			ClosedEquity[i] += ClosedEquity[i - 1];
	}
	
	StaticVarSet("ClosedEquity", ClosedEquity);

}

It is about this line:

ClosedEquity += IIf(dt == trade.ExitDateTime, trade.GetProfit(), 0); // store cumulative trade profit values at exit date points

I understand this line will cumulate for exit trades the trade.GetProfit() at appropriate bar.
The result is time equity curve with a value at each date corresponding to a exit trades dates (cumulated/bar if several trades are exited on same bar).

What I don't understand from this code is how can the code "knows" at which bar/time to add the value?
I would have expected and index value in square brackets on ClosedEquity at the left of the sign.

I read this https://www.amibroker.com/guide/h_understandafl.html and in the loop the documentation shows an index value at the variable: vaexp[ i ] =..... why not in the above code?

Hi @Brakkar,

I'm a bit rusty on how the AB custom back tester works, so verify the following with other sources.

I think that the answer to your question is already embedded in the post you referred to, but it could probably be a little clearer, particularly to address your question.

This diagram might help to illustrate how AB and the code work:

image

Given that the code you refer to is an array-based calculation, it seems to be using a "profit" variable stored within each "trade" object, which means that each trade has it's own dedicated array-variable (of length BarCount) for holding that trade's profit, which is the +/- amount derived on each and every bar. If the trade was "open", ie between its entry/exit points, there would be a corresponding value for the bar, but otherwise the profit would be zero or null.

But this code only takes the profit figure on the exit date/bar, and ignores everything else.

@HelixTrader does say (a little bit further down) that you might be able to use a for() loop with square bracket indexing, but why do that when you can use the power of array processing!

The code given is incorrect. Not technically but logically.

First, it is pointless as closed equity array is available directly without need to calculate anything:

ClosedEquity = bo.EquityArray; // directly available

So all the code above is totally unneeded and waste of CPU resources.

Secondly, contrary to views presented by @sinecospi, GetProfit() returns scalar, not array. It represents current profit of trade or open position. If you call it on CLOSED trade object, it represents the closed profit (scalar, not array).

Thirdly if you deeply look at the statement like this:

		if (ClosedEquity[i] == 0)
			ClosedEquity[i] = ClosedEquity[i - 1];
		else
			ClosedEquity[i] += ClosedEquity[i - 1];

You will quickly see the if-else condition makes no sense at all and is another example of wasted CPU resources. Ask yourself why do you need the 'if' at all? If ClosedEquity[ i ] is ZERO then

ClosedEquity[i] = ClosedEquity[i - 1];

is actually equivalent to

ClosedEquity[i] += ClosedEquity[i - 1]; // ClosedEquity[ i ] is zero and adding to it is the same as assignment

So this if-else is not needed at all and the same result you will get using just this:

for( i = 1; i < BarCount; i++ ) // loop through bars to fill in blank values and accumulate closed equity values
{
		ClosedEquity[i] += ClosedEquity[i - 1];
}

Lastly, the last loop is unneeded at all because instead of " loop through bars to fill in blank values and accumulate closed equity values", you could just call

ClosedEquity = Cum( ClosedEquity ); // this would do the same what entire loop is doing

But as I wrote, the whole code is pointless as EquityArray is directly available.

2 Likes

Thanks for the clarification @Tomasz, :smile:

First, it is pointless as closed equity array is available directly without need to calculate anything:

ClosedEquity = bo.EquityArray; // directly available

@Tomasz: I think there may be some confusion between "total equity as determined at the close of the bar" and "equity based only on closed trades". I thought that that bo.EquityArray() returned the former, not the latter, i.e. for each bar the equity value is equal to the initial equity, plus the net P/L for all trades closed prior to or on that bar, plus the P/L of all currently open trades (i.e. trades that were still open at the close of that bar), where the P/L for those open trades is determined by using the bar's closing price.

Both can be obtained using bo.EquityArray. You can get the second one using ValueWhen( any_trade_exit, bo.EquityArray ); also all other comments regarding array coding still apply.

1 Like