Rotational system in regular BT issue

Hi, in a previous post I've been suggested to code my rotational system using the regular backtester rules. This sounds great and in the following there is my attempt, but I'm experiencing a strange behavior on some trades. I'm surely doing something wrong but I can't figure out what and why this happens.

To debug my formula I've coded a simple version using backtestmode rotational and mimed the same system using the regular backtest mode.

System rules

  • weekly rotations that occurs on each Monday
  • trade the next day at open
  • one asset (100% of equity)
  • position score based on ROC

rotational backtest formula:

RotationDay = Param("day", 1, 0, 6, 1); 
Positions = Param("Positions",1, 1,10,1);
ROCLB = Param("ROC LookBack", 1, 1, 100, 1);

SetBacktestMode(backtestRotational); 
SetOption("MaxOpenPositions",Positions); 
SetOption("WorstRankHeld",Positions); 
SetOption("InitialEquity", 100000);
SetPositionSize(100/Positions, spsPercentOfEquity);

rocVals = ROC(Close, ROCLB);
IndRoc = rocVals + 1000;
IndRoc = IIf(DayOfWeek() == RotationDay, IIf(BarCount < ROCLB, 0, IndRoc), scoreNoRotate);

PositionScore = IndRoc;

regular backtest formula:

Positions = Param("Positions",1, 1,10,1);
ROCLB = Param("ROC LookBack", 1, 1, 20, 1);
RotationDay = Param("Day", 1, 0, 6, 1);

SetBacktestMode(backtestRegularRawMulti); 
SetOption("MaxOpenPositions",Positions);  
SetOption("InitialEquity", 100000);
SetPositionSize(100 / positions, spsPercentOfEquity);
SetTradeDelays(1,1,0,0);
BuyPrice = O;
SellPrice = Ref(O,1);

rocVals = ROC(Close, ROCLB) + 1000;

//	Rotational Logic
DOW = DayOfWeek();
tomorrowDOW = Ref(DOW, 1);
Buy = DOW == RotationDay;
Sell = tomorrowDOW == RotationDay;
Buy = ExRem(Buy, Sell);
Sell = ExRem(Sell, Buy);

PositionScore = rocVals;

THE ISSUE - example
In the following image you could see that every trade of the regular formula is identical of their corresponding rotational one, except the one on CRM:
different close prices trim

Looking at the chart, it seems that for any reason, instead of considering the "sell day + 1 open price", it considers the "sell day + 0 low price". Look at these images:

ROTATIONAL : ok
CRM rotational

REGULAR : wrong
CRM regular

This slight error produces big result differences:
Different Performances

What I'm doing wrong here?
Thank you a lot for any help :slight_smile:

If you look at your trade lists, you will see that in the regular backtest you are always exiting one day earlier than in the rotational backtest. You probably want your Sell condition to be the same as your Buy condition:

Sell = DOW == RotationDay;

Also, your Sell price is currently looking into the future (tomorrow's open), so you should change that to:

SellPrice = O;

Note that when you use trade delays, the SIGNAL (Buy or Sell) gets delayed, but the PRICE (BuyPrice, SellPrice) is not shifted. Therefore, if you set a 1-day trade delay and get a Sell signal on Monday, then the Sell execution will occur on Tuesday using Tuesday's value in the SellPrice array. If that price is not within the High-Low range of the day, AmiBroker will change the execution price to be within the actual trading range. You can override this behavior with SetOption, but you don't need to do that. You just need to specify the correct day's price, as noted above.

2 Likes

Thank you mradtke. Now I've understand why the price is on the (Day + 0) Low instead of on the (Day + 1) Open.

I've chosed to get the sell signal on the day before the rotation day to work around the behavior that result in a null trade when sell and buy occurs on the same day and on the same price.

for example if I write the following:

// E0_regular_v2.afl

Positions = Param("Positions",1, 1,10,1);
ROCLB = Param("ROC LookBack", 1, 1, 20, 1);
RotationDay = Param("Day", 1, 0, 6, 1);

SetBacktestMode(backtestRegularRawMulti); 
SetOption("MaxOpenPositions",Positions);  
SetOption("InitialEquity", 100000);
SetPositionSize(100 / positions, spsPercentOfEquity);
SetTradeDelays(1,1,0,0);
BuyPrice = O;
SellPrice = O;

rocVals = ROC(Close, ROCLB) + 1000;

//	Rotational Logic
DOW = DayOfWeek();
tomorrowDOW = Ref(DOW, 1);
Buy = DOW == RotationDay;
Sell = DOW == RotationDay;
Buy = ExRem(Buy, Sell);
Sell = ExRem(Sell, Buy);

PositionScore = rocVals;

I'll obtain:

  • a tradelist of all null trades if the flag "allow same bar exit - entry signal " is set to True
  • a wrong trade list where the trade happens every other week if the same flag is set on False

Neither one of the two solution works for me.

I'm searching a way to work around this... Maybe the idea could be something like this:
" If the ticker has been selected hence the trade is open AND there is another buy signal, ignore the sell and the buy signals"

but this seems quite tricky (involves controlling the ranking of the position score etc...)

Could you or anyone else see/know any smart idea that I don't see?

Try setting the minimum hold time:

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

Right mradtke! Clean and brilliant thank you.

I've Just tried and the equity line of the regular backtester is pretty close to the rotational backtester's one. Excellent!

Now the only one difference is: when the same asset is kept for consecutive weeks, in the rotational version it appears as an unique trade, but in the regular one it appears as several consecutive trades. This is a minor bug that sligthly affects the position size and some metrics.

The first idea that came in mind for fixing this is little bug, is to retrieve the positionscore's rank and check if the ticker is on the top ranks for consecutive rotational days. If this happens then I force buy and sell to false.
But as far as I know there isn't any built-in Amibroker's function that return the rank's value of the ticker related to positionscore, right?

If this is the case, I should use the StaticVarGenerateRanks function and top ranking mode as described into the ranking chapter of the manual (I never used that functions so I could be wrong... but it seems to be a viable way at first glance).
and then create the ranking static variable at first.

Could be a way in your opinion or it's a waste of time because there's another easier way?

Sure, you could do it that way if it's important to you. On the other hand, your current approach is automatically rebalancing your positions every week (rotation period), which some people find desirable.

1 Like

Yes I agree :+1:
So thank you mradtke your help has been precious for me, TOP

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