Exporting Daily Exposure

I am looking to show the daily exposure of a trading system. In the backtest, Amibroker calculates the Exposure % and produces the Avg Exposure over the period of the test. The following I found in the user manual.

Exposure % - 'Market exposure of the trading system calculated on bar by bar basis. Sum of bar exposures divided by number of bars. Single bar exposure is the value of open positions divided by portfolio equity.

I am wondering what the best way would be to see the single bar exposure on a bar by bar basis? I can't seem to find anywhere where that has been discussed. Thanks!

See example here at KB:
http://www.amibroker.com/kb/2008/05/19/historical-portfolio-backtest-metrics/
as well as manual
https://www.amibroker.com/guide/a_custombacktest.html

So

/// derived from:
/// @link http://www.amibroker.com/kb/2008/05/19/historical-portfolio-backtest-metrics/
/// modified and discussed at:
/// @link https://forum.amibroker.com/t/exporting-daily-exposure/14109
SetCustomBacktestProc("");
composite_name = "~~~ExposurePercent";
if ( Status( "action" ) == actionPortfolio ) {
    bo = GetBacktesterObject();

    bo.PreProcess(); // Initialize backtester

    // initialize with null
    exposure = Null;
    for ( i = 0; i < BarCount; i++ ) {
        bo.ProcessTradeSignals( i );

        // recalculate built-in stats on EACH BAR
        stats = bo.GetPerformanceStats( 0 );

        // the line below reads the metric and stores it as array element
        // you can add many lines for each metric of your choice
        exposure[ i ] = stats.GetValue( "ExposurePercent" ); // get exposure value calculated this bar
   }

    bo.PostProcess(); // Finalize backtester

    // now STORE the historical data series representing the metric of your choice
    // duplicate the line below for as many metrics as you want
    AddToComposite( exposure, composite_name, "X", atcFlagEnableInPortfolio | atcFlagDefaults );
}

// Plotting exposure AFTER backtest
PlotForeign(composite_name, "%Exposure Historical", colorRed, styleLine );


// dummy system
m = MA( Close, 20 ); // simple moving average
Buy = Cross( Close, m ); // buy when close crosses ABOVE moving average
Sell = Cross( m, Close ); // sell when closes crosses BELOW moving average
Short = Cover = 0;

5

1 Like

@fxshrat: If you call bo.GetPerformanceStats() on each bar, I believe the metrics returned will be from the start of the backtest through the current bar. Therefore, you're not getting bar-by-bar exposure, but rather average exposure for all bars up to and including the current bar. It also seems odd that in your example exposure is increasing throughout the entire test.

@QEdges I usually put something like this at the end of my bar-by-bar loop to track exposure:

for (openPos=bo.GetFirstOpenPos(); openPos; openPos=bo.GetNextOpenPos())
{  
	currPosSize = openPos.Shares * openPos.GetPrice(i,"C");
	pctExposure[i] += 100 * currPosSize / bo.Equity;
}

Of course you would also need to initialize pctExposure to 0 before the bar-by-bar loop.

Note that this method of calculating exposure reports the percentage of actual equity (not leveraged equity or buying power), which is different than what AB reports. For example, if you set Account Margin to 50 and on a particular bar you have $100K of equity and $150K of open positions, then this method will calculate % Exposure as 150%(150K/100K) whereas AB would calculate it as 75% (150K/200K). Obviously you could change the calculation shown here to match AB's method.

1 Like

@mradtke, the CBT code basically is the same one as the one of the KB as posted by link in 2nd post.

It just uses % exposure in GetValue as documented here.

https://www.amibroker.com/guide/a_custombacktest.html

Is it average? It rather looks like sum to me.

Yes, I'm not sure how to interpret the % Exposure value returned by stats.GetValue() in the middle of a backtest. I know that after the backtest completes, that call returns average exposure. @Tomasz can you offer any guidance here?

OK, so here is code for average exposure which outputs same result as in AmiBroker report.
Each value on curve is the same one as if you would use different end date in analysis from-to range.
Values are in line with backtest report.

You can also plot non average exposure (see comment in code);

/// @link https://forum.amibroker.com/t/exporting-daily-exposure/14109/6
SetCustomBacktestProc("");
composite_name = "~~~ExposurePercent";
if ( Status( "action" ) == actionPortfolio ) {
	bo = GetBacktesterObject();
	bo.PreProcess(); // Initialize backtester

	pv = 0;
	for ( i = 0; i < BarCount; i++ ) {
		bo.ProcessTradeSignals( i );
		
		for (op=bo.GetFirstOpenPos(); op; op=bo.GetNextOpenPos())
			pv[i] = op.Shares * op.GetPrice(i, "C");// position value
	}

	exposure = pv / bo.EquityArray;

	bo.PostProcess(); // Finalize backtester

	AddToComposite(exposure, composite_name, "X", atcFlagEnableInPortfolio | atcFlagDefaults );
}

SetBarsRequired(sbrAll);

SetForeign(composite_name);
// average
exposure = Cum(C)/(BarIndex()+1)*100;
// or non average
//exposure = IIf( C > 0, C, Null);
RestorePriceArrays();

Plot( ValueWhen(Status( "barinrange"), exposure), "%Exposure", colorRed );

_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} - {{DATE}} %%Exposure %g", exposure) );

Using all quotes
5

6


Some different backtest end date

7

8

Same value in overall backtest range
9

2 Likes

Thank you @fxshrat and @mradtke for your suggestions.

I am looking for the daily exposure number - so is the portfolio 50% invested today? 75%? 100%? So I think I should be able to use a calc like @mradtke suggested to find that and then create a composite it with it as @fxshrat shows.

My initial hope upon reading in the manual that the Exposure % is derived from the sum of single bar exposures divided by the number of bars was that I could simply access the "single bar exposure" in a manner similar to how "portfolio equity" is accessed:

http://www.amibroker.com/kb/2006/03/11/how-to-create-copy-of-portfolio-equity/

If this were the case, then I could easily be sure that I was using the same exposure # that Amibroker was generating.

That Amibroker-standard single bar exposure number does not appear to be available, but by running a calc like @mradtke shared and creating a composite like @fxshrat suggests there appears to be a workable solution. Thanks for your help.

@QEdges, the last code of this thread has both average and single-bar exposure.

Please read the comments in the code.

1 Like

One small adjustment that I would make. The code generously provided by @fxshrat works when run against a single symbol, but not with multiple symbols. Therefore I would change this:

pv[i] = op.Shares * op.GetPrice(i, "C");

to this:

pv[i] += op.Shares * op.GetPrice(i, "C");

Also, as noted in my previous reply, if you want to exactly match AB's output you have to include Account Margin in your calculations as well. For example, set the Account Margin to 50 and run the test and you will see that AB's Exposure values are approximately (but not exactly) 50% of the ones @fxshrat is reporting. Not sure why there is a discrepancy.

2 Likes

Thanks. I missed that.

Hi, I have a question regarding the exposure value displayed in the backtest report. The user guide which says "single bar exposure is the value of open positions divided by portfolio equity" seems to be a bit ambiguously worded. When is the value of open positions computed?

The code graciously posted by @fxshrat and @mradtke above seems to compute overnight exposure which is not the exposure displayed in the backtest report.

The following when run on any symbol with Range = 5 recent bars shows the bar by bar exposure to be 0 (as it should since there is no overnight exposure). However, the backtest report shows the exposure to be 100%.

SetTradeDelays(0, 0, 0, 0);
SetOption("AllowSameBarExit", True);
SetOption("HoldMinBars", 0);
Buy = 1;
Sell = 1;
BuyPrice = Open;
SellPrice = Close;

/// @link https://forum.amibroker.com/t/exporting-daily-exposure/14109/6
SetCustomBacktestProc("");
if ( Status( "action" ) == actionPortfolio ) {
	dt = DateTime();
	bo = GetBacktesterObject();
	bo.PreProcess(); 
	pv = 0; 
	for ( i = 0; i < BarCount; i++ ) {
		bo.ProcessTradeSignals( i );
		for (op=bo.GetFirstOpenPos(); op; op=bo.GetNextOpenPos())
			pv[i] += op.Shares * op.GetPrice(i, "C");
	}
	exposure = 100 * pv / bo.EquityArray;
	for ( i = 0; i < BarCount; i++) 
		_TRACE("AT: " + 
				NumToStr(dt[i], formatDateTimeISO) + " " + 
				NumToStr(exposure[i], 1.4));
	bo.PostProcess();
}