Looking for a simple method to benchmark a rotational momentum strategy

Hi All,

Many apologies if this has already been addressed in another post by someone. I have done a search on the forum and on google and can find similar threads, but nothing directly addressing my issue.

I have a monthly momentum rotational system that I have written, and I would like to benchmark it to the SPY. The issue is that because the system will exit any stocks falling out of the top ranked list monthly and then enter any stocks that have newly entered it (entering a constant position size each time), comparing it to a buy and hold strategy of the SPY would be like comparing apples and oranges, since one is basically compounding and the other is not.

To try and get a more green apples to red apples comparison, I had just thought to write a code that would enter at the open of the 1st day of the month (say $100K position in SPY), exit that position at the open of the first day of the next month, then immediately re-enter (with $100K position size again). My issue is that when I wrote a simple code to do this:

/********* ROTATIONAL DAY CHECK *********/
RotationDay = 1;

BarDay = Day();
BarMonth = Month();
IsRotationDay = 0;

for ( i = 0; i < BarCount; i++ )//begin iterating through the bars
{	
	if ( i-1 >= 0 AND i < BarCount) 
	{   //to avoid access array elements outside of 0 to Barcount-1     
		if ( BarDay[i] >= RotationDay AND ( BarDay[i-1] < RotationDay OR BarMonth[i-1] != BarMonth[i] )  OR ( BarMonth[i-1] != BarMonth[i] AND BarDay[i-1] < RotationDay ) )
		{
			IsRotationDay[i] = 1;
		}
	}
}

Buy = Sell = IsRotationDay;
Cover = Short = 0;

I find that the backtester enters then immediately exits the trade for 0 profit/loss for 0 time. In effect, at the start of each month, I want to sell, then buy, then wait a month, then repeat. But this code buys, then sells, then waits one month. I would like to get it to sell first then buy, not buy first then sell. I cannot set a buy delay since I am using end of day data and delaying the buy for an entire delay won't give the same results. Is there a simple way to do this I am missing without going into the CBT.

Or if I have to go into the CBT, does anyone know of a link to a code I could use as a template, that will do something similar?

I'm also open to ideas about how to make a better apples to apples comparison than this. Because the rotational strategy will only rebalance part of the portfolio each month (the stocks that newly enter), held stocks will compound, it isn't a straight apples to apples comparison either. The only thing I can think of here, would be to exit every single position at the start of the month and re-enter all at the say $10K (for 10 positions, makes .the same $100K float to compare with SPY example).

Anyway, thank you all for viewing and for any advice.

Cheers,

W.

All you need I think, is the bar-by-bar value of all open positions of your rotational strategy - you can get this using CBT.
With that information, you can calculate cumulative PL on SPY using bar-by-bar percent change from open to close of the same bar of SPY.
Finally, add your initial equity, and you get your benchmark.

2 Likes

You should be able to avoid writing a CBT by selling on the last day of the month at the close and then buying on the first day of the month at the open. Right now, you're buying and selling the same security on the same day. Something like,
buy = rotationday;
sell = ref(buy, -1);
sellprice = C;
buyprice = O;

1 Like

Thanks aron. That makes sense. I could use the CBT to record the bar by bar total portfolio value (I think open position value could be misleading when there is a rotation) and calculate the day by day percentage change in portfolio value. Could then look at the daily return of SPY and perform some simple calcs in Excel (could use AB but so long since I programmed a lot in it, it would take me awhile) to generate a comparison using compounding (or not) for both once I have the daily returns of both. This could give an idea of average annual profit and maximum drawdown.

Thanks for the method!

Thanks ghosenb, but I'm worried that if I sell at the close of the month and buy at the open of the next month, it doesn't fully represent what is happening in reality, as in reality I am holding the position over this time. If the market doesn't open next month at exactly what it closes at the previous month (which it doesn't exactly), there will be some errors introduced into my backtesting.

No need to reinvent the wheel no need for guesses. All that is required is reading the manual. It is all described here
http://www.amibroker.com/guide/h_portfolio.html

Open the Users Guide on that page, scroll down to "resolving same bar conflicts". The manual clearly says that when you "want to sell, then buy, then wait a month, then repeat" you should follow the Scenario 3 described in the guide.

Scenario 3. Both signals are used and entry signal comes after exit signal.
This scenario is used when AllowSameBarExit option is set to True (turned on) and HoldMinBars is set to 1 (or more).

SetOption("AllowSameBarExit", True );
SetOption("HoldMinBars", 1 ); 

But wait... why even bother with writing any code? B&H benchmark is built-in feature:

Also as @aron wrote, you don't even need to do even that, as simple built-in Buy and Hold stats option will give you the benchmark you need - ALL it is required is to turn ON B&H stats in the GUI:

2 Likes

Dear Tomasz,

Thank you very much for your reply. One of the things I love about Amibroker is that the developer and creator takes the time to answer questions on this forum. Thank you for proposing these 2 solutions.

  • Buy and Hold Stats option: Thank you, I was actually aware of this feature and have used it to benchmark my systems in the past. However, my rotational system (which was built several years ago using the low level CBT, not the rotational trading feature of Amibroker) will enter all trades with a constant position size, so does not compound with growing equity. Since the buy and hold SPY benchmark will compound (by nature of a buy and hold with one entry and one exit), I think it might be a bit misleading for me to compare them directly. However, Aron’s advice about using the CBT to get the portfolio value at the end of each day, then using that to calculate a daily return for the rotational system, then comparing that to SPY daily % change, should work out.
  • Resolving Same Bar Conflicts: Using the method you have pointed me to in the User’s Guide to ensure the exit comes before the entry resolves my problem perfectly. It is very clearly explained in the User’s Guide and I apologize for not finding the solution myself. Thank you again for taking the time to point me in the right direction.

Thanks again for the great software package, and for taking the time to answer questions like this.

W.

1 Like

Just adding a code snippet

SetOption( "MaxOpenPositions", MaxPositions = 4 );
SetOption( "WorstRankHeld", MaxPositions );
SetBacktestMode( backtestRotational );
PositionSize = 1000;
PositionScore = mtRandomA();

SetCustomBacktestProc( "" );

if( Status( "action" ) == actionPortfolio )
{
    bo = GetBacktesterObject();	//  Get backtester object
    bo.PreProcess();	//  Do pre-processing (always required)

    value = 0;

    for( i = 0; i < BarCount; i++ )	//  Loop through all bars
    {
    
		// portfolio value at open of this bar
        value[i] = 0;

        for( pos = bo.GetFirstOpenPos( ); pos; pos = bo.GetNextOpenPos( ) )
        {
            value[i] += pos.GetPositionValue(); 
        }

        bo.ProcessTradeSignals( i );	//  Process trades at bar (always required)
    }

    bo.PostProcess();	//  Do post-processing (always required)

    StaticVarSet( "value", value ) ;
}

SetForeign( "SPY" ) ;
pct = Close / Open - 1 ;
RestorePriceArrays();

pl = pct * StaticVarGet( "value" )  ;
cumpl = Cum( pl ) ;
benchmark = GetOption( "initialequity" ) + cumpl;


Plot( benchmark , "SPY", colorred );
Plot( Foreign( "~~~Equity", "C" ) , "Equity", colorgreen ) ;

3 Likes

Thanks aron. That code snippet will come in very handy next week when I will make modifications to my rotational code in the CBT to get an SPY comparison. Thanks again!