Hi,

I’ve figured out from the AB online knowledge base articles how to generate P&L quoted in terms of ATR using the CBT module. As you see from the code sample below, I manually accumulate the average statistics for the summary report. Now, I want to expand the statistics from just the average to include also standard deviation, quartile values, e.g. Min, 25%-tile, 50%-tile, 75%-tile, Max for winners and losers. Is there a way to call some array based statistics functions with the array/list of individual trade’s ATR P&L, like one can do in Matlab or Python/numpy, instead of creating a whole set of static variables and “roll my own” statistical calculations for each of the statistics?

Thanks.

```
/*
Example Code using CBT to generate ATR P&L stats.
*/
Buy = Sell = Short = Cover = 0;
SetOption("InitialEquity", 100 * 1000);
SetPositionSize(100, spsPercentOfEquity);
// A random (but reasonable?) set of trading rules.
Buy =
C == LLV(C, 20) AND C > LLV(C, 50);
Sell =
C == HHV(C, 50) OR
C == LLV(C, 50);
// Compute ATR for "P&L in ATR" statistics.
StaticVarSet("ATR_" + Name(), ATR(30) / C * 100);
SetCustomBacktestProc( "" );
function AddATRPnL( trade )
{
symbolATR = StaticVarGet( "ATR_" + trade.Symbol );
ATRperc = Lookup( symbolATR, trade.EntryDateTime );
trade.AddCustomMetric("ATR %", ATRperc);
ATRPnL = trade.GetPercentProfit() / ATRPerc;
trade.AddCustomMetric( "ATR Profit", ATRPnL );
// Accumulate values for summary statistics.
sumATRPnL = StaticVarGet("SumATRPnL", 0.0);
countATRPnL = StaticVarGet("CountATRPnL", 0);
sumATRPnLWin = StaticVarGet("SumATRPnLWin", 0.0);
countATRPnLWin = StaticVarGet("CountATRPnLWin", 0);
sumATRPnLLoss = StaticVarGet("SumATRPnLLoss", 0.0);
countATRPnLLoss = StaticVarGet("CountATRPnLLoss", 0);
sumATRPnL = sumATRPnL + ATRPnL;
countATRPnL++;
if (ATRPnL > 0.0) {
sumATRPnLWin = sumATRPnLWin + ATRPnL;
countATRPnLWin++;
} else {
sumATRPnLLoss = sumATRPnLLoss + ATRPnL;
countATRPnLLoss++;
}
StaticVarSet("SumATRPnL", sumATRPnL);
StaticVarSet("CountATRPnL", countATRPnL);
StaticVarSet("SumATRPnLWin", sumATRPnLWin);
StaticVarSet("CountATRPnLWin", countATRPnLWin);
StaticVarSet("SumATRPnLLoss", sumATRPnLLoss);
StaticVarSet("CountATRPnLLoss", countATRPnLLoss);
return ATRPnL;
}
if ( Status( "action" ) == actionPortfolio )
{
bo = GetBacktesterObject();
// run default backtest procedure without generating the trade list
bo.Backtest( True );
StaticVarSet("SumATRPnL", 0.0);
StaticVarSet("CountATRPnL", 0);
StaticVarSet("SumATRPnLWin", 0.0);
StaticVarSet("CountATRPnLWin", 0);
StaticVarSet("SumATRPnLLoss", 0.0);
StaticVarSet("CountATRPnLLoss", 0);
// iterate through closed trades
for ( trade = bo.GetFirstTrade( ); trade; trade = bo.GetNextTrade( ) )
{
ATRPnL = AddATRPnL( trade );
}
// iterate through open positions
for ( trade = bo.GetFirstOpenPos( ); trade; trade = bo.GetNextOpenPos( ) )
{
ATRPnL = AddATRPnL( trade );
}
// generate trade list
bo.ListTrades( );
// Custom Statistics on ATR PnL
sumATRPnL = StaticVarGet("SumATRPnL", 0.0);
countATRPnL = StaticVarGet("CountATRPnL", 0);
sumATRPnLWin = StaticVarGet("SumATRPnLWin", 0.0);
countATRPnLWin = StaticVarGet("CountATRPnLWin", 0);
sumATRPnLLoss = StaticVarGet("SumATRPnLLoss", 0.0);
countATRPnLLoss = StaticVarGet("CountATRPnLLoss", 0);
avgATRPnL = IIf(countATRPnL < 1, 0.0, SumATRPnL / countATRPnL);
avgATRPnLWin = IIf(countATRPnLWin < 1, 0.0, SumATRPnLWin / countATRPnLWin);
avgATRPnLLoss = IIf(countATRPnLLoss < 1, 0.0, SumATRPnLLoss / countATRPnLLoss);
bo.AddCustomMetric("Avg P&L (ATR)", avgATRPnL);
bo.AddCustomMetric("Avg Win (ATR)", avgATRPnLWin);
bo.AddCustomMetric("Avg Loss (ATR)", avgATRPnLLoss);
bo.AddCustomMetric("ATR Profit Factor", -avgATRPnLWin / avgATRPnLLoss);
}
```