Hello,
I'm trying to calculate Van Tharp's SQN as a custom backtest metric.
I don't believe it's the best objective function but it's more a way to learn AFL and AB.

This code doesn't work because I guess I have a problem calculating the standard deviation.
You can see that I calculate it on an array "trades" that I manually created to be sure it's properly populated.

Problem is that "stdrmultiples" variable is always 0.

Do you see any problem in my syntax there that would prevent the proper calculation of the standard deviation on array "trades"?

/* Now custom-backtest procedure follows */
if( Status("action") == actionPortfolio )
{
_TRACE("!CLEAR!");
bo = GetBacktesterObject();
bo.Backtest(1); // run default backtest procedure
st = bo.GetPerformanceStats(0); // get stats for all trades
averageRisk = st.GetValue("LosersAvgLoss"); // Average loss = 1R
SumProfitPerRisk = 0;
NumTrades = 0;
// iterate through closed trades first
for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
{
// Avg loss = LosersAvgLoss
RMultiple = trade.GetProfit()/ abs(averageRisk); // Rmultipe per trade
SumProfitPerRisk = SumProfitPerRisk + RMultiple; // Summ of all R multiple
trade.AddCustomMetric("R-Multiple", RMultiple );
NumTrades++;
}
trades = 0;
trades[ 0 ] = 1;
trades[ 1 ] = 11;
trades[ 2 ] = 41;
trades[ 3 ] = 21;
_TRACE("trades" + trades[ 3 ]); // 21
expectancy = SumProfitPerRisk / NumTrades; // Average of sum of R multiples = Expectancy.
bo.AddCustomMetric( "Expectancy (per risk)", expectancy );
stdrmultiples = StDev( trades , 4 , False );
_TRACE("stdrmultiples" + stdrmultiples); // 0
MSQN = ( expectancy / stdrmultiples ) * ( sqrt(4) );
bo.AddCustomMetric( "stddev", MSQN );
bo.ListTrades();
}

It's not that the Standard Deviation function "doesn't work", it's that it's not a good match for what you're trying to do. The StDev() function takes an array and a lookback period as input, and it produces a new array as output. In the CBT, all arrays are the same size, with one element per bar in the backtest date range. In your example, you only used 4 of the N bars of the trades array, so the standard deviation value that you want is in the fourth element of the returned array (i.e. index 3). But what if your backtest had produced more trades than there were bars? You wouldn't have room to store all your values.

The method described by @fxshrat in the post you linked to does not require that you put values (R multiples in this case) into an array first, because it accumulates the values needed to calculate Standard Deviation as they are encountered. This is a far superior way to approach the problem.

At least for me StdDev function is not working at all in CBT. I tried to put the values at the end of the array instead and backwards:

Rdist[BarCount-NumTrades]=RMultiple;

But still returns nothing:

bo.AddCustomMetric( "StdDev" , StDev(Rdist,5));

Hopefully @Brakkar reference did the trick and I think it is a much better way of calculate standard deviation, but I wonder why native function does not work on CBT

StDev requires input ARRAY and outputs the ARRAY (not single value as you wrongly assume) and it is "running" / walking / moving stdev like "moving average"

If you only want to use 5 last bars you have to FILL last bars and use LastValue!

Something along these lines:

Rdist = Null; // initialize ALL bars with Null
Rdist[ BarCount - 1 ] = non-null-values here
Rdist[ BarCount - 2 ] = non-null-values here
Rdist[ BarCount - 3 ] = non-null-values here
Rdist[ BarCount - 4 ] = non-null-values here
Rdist[ BarCount - 5 ] = non-null-values here
Rdist[ BarCount - 6 ] = non-null-values here
bo.AddCustomMetric( "StDev 5 last bars", LastValue( StDev( RDist, 5 ) ) );