Backtest result with daily P/L

backtest

I'm testing my intraday strategy with 1 min bar (above). Is there a way to report daily P/L ?

For example, above shows two trades on 12/16, with profits 350.8 and 860.8, total would be 1211.6. Is there a way to add a column that shows 1211.6 for 12/16? It'd be similar to Cum. Profit column, but resets everyday.

Of course it is possible via high level CBT.

function CBT_DailyProfit(trade, i) {
	// https://forum.amibroker.com/t/backtest-result-with-daily-p-l/23186/2
	// by fxshrat@gmail.com
	dn = DateTimeConvert(0, trade.EntryDateTime);
	if ( i > 0 && i < BarCount ) {
		dn_arr[ i ] = dn;
		if ( dn_arr[ i ] != dn_arr[ i-1 ] ) cs = 0; 
		cs += trade.GetProfit();
		cs_prof[ i ] = cs;
	} else {
		cs_prof[ 0 ] = cs = trade.GetProfit();
		dn_arr[ 0 ] = dn;
	}
	return cs_prof;
}

SetCustomBacktestProc("");
if ( Status( "action" ) == actionPortfolio ) {
	bo = GetBacktesterObject(); 
	bo.Backtest( 1 ); 
	// iterate closed trades
	for(trade = bo.GetFirstTrade(), i = 0; trade; trade = bo.GetNextTrade() ) {
		cs_prof = CBT_DailyProfit(trade, i);
        trade.AddCustomMetric("Daily Cum.Profit", cs_prof[i], DecPlaces = 2 );
        i++;
	}
	// iterate open positions
	for (trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() ) {
		cs_prof = CBT_DailyProfit(trade, i);
		trade.AddCustomMetric("Daily Cum.Profit", cs_prof[i], DecPlaces = 2);
		i++;
	}  
	bo.ListTrades();  
}

//######### DUMMY SYSTEM ############
SetPositionSize( 100, spsShares );
m = MA( Close, 20 ); 
Buy = Cross( Close, m );
Sell = Cross( m, Close );
Short = Cover = 0;

15

4 Likes

@fxshrat Thank you. It works really well. I expand on it slightly to add another column to indicate first profit of the day, so it's easier to spot total of the previous day (last entry of previous day).

function CBT_DailyProfit(trade, i) {
	// https://forum.amibroker.com/t/backtest-result-with-daily-p-l/23186/2
	// by fxshrat@gmail.com
	dn = DateTimeConvert(0, trade.EntryDateTime);
	if ( i > 0 && i < BarCount ) {
		dn_arr[ i ] = dn;
		if ( dn_arr[ i ] != dn_arr[ i-1 ] ) cs = 0; 
		cs += trade.GetProfit();
		cs_prof[ i ] = cs;
	} else {
		cs_prof[ 0 ] = cs = trade.GetProfit();
		dn_arr[ 0 ] = dn;
	}
	return cs_prof;
}

function CBT_DailyProfitIndicator(trade, i) {
	// orignal function by fxshrat@gmail.com
	// from https://forum.amibroker.com/t/backtest-result-with-daily-p-l/23186/2
	// updated by dhwang141 to indicate first trade of the day (0) or subsequent trades in the same day (1)
	// in the report, "1" before "0" is the total profit for the day
	dn = DateTimeConvert(0, trade.EntryDateTime);
	if ( i > 0 && i < BarCount ) {
		dn_arr[ i ] = dn;
		if ( dn_arr[ i ] != dn_arr[ i-1 ] ) 
			cs = 0;
		else
			cs = 1; // updated to use static "1" to indicate subsequent trades in the day
		cs_prof[ i ] = cs;
	} else {
		cs_prof[ 0 ] = cs = 0;
		dn_arr[ 0 ] = dn;
	}
	return cs_prof;
}

SetCustomBacktestProc("");
if ( Status( "action" ) == actionPortfolio ) {
	bo = GetBacktesterObject(); 
	bo.Backtest( 1 ); 
	// iterate closed trades
	for(trade = bo.GetFirstTrade(), i = 0; trade; trade = bo.GetNextTrade() ) {
		cs_prof = CBT_DailyProfit(trade, i);
		cs_profind = CBT_DailyProfitIndicator(trade, i);
        trade.AddCustomMetric("Daily Cum.Profit", cs_prof[i], DecPlaces = 2 );
        trade.AddCustomMetric("Daily Cum.Profit Ind", cs_profind[i], DecPlaces = 2 ); // add indicator
        i++;
	}
	// iterate open positions
	for (trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() ) {
		cs_prof = CBT_DailyProfit(trade, i);
		cs_profind = CBT_DailyProfitIndicator(trade, i);
		trade.AddCustomMetric("Daily Cum.Profit", cs_prof[i], DecPlaces = 2);
        trade.AddCustomMetric("Daily Cum.Profit Ind", cs_profind[i], DecPlaces = 2 ); // add indicator
		i++;
	}  
	bo.ListTrades();  
}

//######### DUMMY SYSTEM ############
SetPositionSize( 1, spsShares );
m = MA( Close, 20 ); 
Buy = Cross( Close, m );
Sell = Cross( m, Close );
Short = Cover = 0;

Row above "0" (last column) is total profit for the day (-1014.4)
daily_profit

Creating two functions for that is too much overkill.

It can be done with single function using multi-dim array.

function CBT_DailyProfit(cs_prof, trade, i) {
	// https://forum.amibroker.com/t/backtest-result-with-daily-p-l/23186/2
	// by fxshrat@gmail.com
	dn = DateTimeConvert(0, trade.EntryDateTime);	
	rownum = MxGetSize(cs_prof,0);
	if ( i > 0 && i < rownum ) {
		dn_arr[ i ] = dn;
		if ( dn_arr[ i ] != dn_arr[ i-1 ] )
			cs_prof[ i ][ 1 ] = cs = 0;
		cs += trade.GetProfit();
		cs_prof[ i ][ 0 ] = cs;
	} else {
		cs_prof[ 0 ][ 0 ] = cs = trade.GetProfit();
		dn_arr[ 0 ] = dn;		
	}
	return cs_prof;
}

SetCustomBacktestProc("");
if ( Status( "action" ) == actionPortfolio ) {
	bo = GetBacktesterObject(); 
	bo.Backtest( 1 );
	///
	stats = bo.GetPerformanceStats( 0 );
	num_trades = stats.GetValue("AllQty"); 
	m = Matrix(Max(1,num_trades),2,1);
	///
	/// Create one zero block and one "1" block
	/// Original method by T. Janeczko
	/// @link https://forum.amibroker.com/t/osaka-sort-replace-with-matrix/6231/9
	m *= MxSetBlock(m * 0, 0, MxGetSize(m, 0)-1, 1, 1, 1 );
	///
	DecPlaces = 2;
	/// iterate closed trades	
	for(trade = bo.GetFirstTrade(), i = 0; trade; trade = bo.GetNextTrade() ) {
		cs_prof = CBT_DailyProfit(m, trade, i);
        trade.AddCustomMetric("Daily Cum.Profit", cs_prof[i][0], DecPlaces);
        trade.AddCustomMetric("New day marker", cs_prof[i][1], DecPlaces);
        i++;
	}
	/// iterate open positions
	for (trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos() ) {
		cs_prof = CBT_DailyProfit(m, trade, i);
		trade.AddCustomMetric("Daily Cum.Profit", cs_prof[i][0], DecPlaces);
		trade.AddCustomMetric("New day marker", cs_prof[i][1], DecPlaces);
		i++;
	}  
	bo.ListTrades();  
}

//######### DUMMY SYSTEM ############
SetPositionSize( 100, spsShares );
m = MA( Close, 20 ); 
Buy = Cross( Close, m );
Sell = Cross( m, Close );
Short = Cover = 0;
1 Like

@fxshrat Amazing. Thank you!

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.