Rotational Trading Position Sizing Using Delayed Entry

I am working on a system that uses Rotational Trading and position size to size the trades. It is run on daily data and trades on tomorrows open.
I am using the following formula for to calculate the position size:
PctSize = PositionRisk * BuyPrice / RiskPerShare
SetPositionSize( PctSize, spsPercentOfEquity)
When I run a Backtest with Detail Log AmiBroker will show what trades I need to place the next day. To compute the position size AmiBroker is using the Close of today. I then place the orders overnight to execute on Open. Now when I run at the end of the next day I find that AmiBroker has used the Open price to compute the position size and sure nuff the size has changed and I my holdings are now not in sync with the system. Unless I sync up which is costly my holding over time differ from the system and I have to do a major reorg.
Is there a way to avoid this? I thought of using Ref(C,-1) for BuyPrice but I don’t think that will solve the problem.

This might be what you're after:

SetOption("UsePrevBarEquityForPosSizing", True);

Ref: https://www.amibroker.com/guide/afl/setoption.html

Yep! Thanks a lot! I will try it.

Whoops! I spoke to soon. Using “UsePervBarEquityForPosSizing” did not change the result.
I am still getting different shares purchased when I run to see tomorrows trades and on the following day.

Below is the code I used that I run with Daily setting.
Also I show the results of the run I made last night and the run I made today.
If I would have place trades this morning using the “ tomorrow’s trades (last night)” I would have been out of sync on all symbols.
Is there a way to hold the shares constant so you don’t get out of sync?

SetOption( "UsePrevBarEquityForPosSizing", True  );// Uses prior EOD day equity for position size
SetBacktestMode( backtestRotational );
SetOption( "AllowSameBarExit", False );// FALSE FOR ROTATIONAL TRADING
SetTradeDelays( 1, 1, 1, 1 );
SetOption( "CommissionMode",               2     );// Mode 2 = $ per trade
SetOption( "CommissionAmount",             7.95  );// Commissions set to $0
Init_equity = Param ( "Initial System Equity", 25000, 5000, 100000, 100);
SetOption("Initialequity", Init_equity );
BuyPrice = Open;
SellPrice = Open;
RoundLotSize             = 1;
SetOption( "MinShares",  5 );

MOP   = Param( "Max Open Positions", 10, 0, 50, 5 ); 
WRH   = Param( "Worst Rank Held", 10, 0, 50, 1 );

SetOption( "MaxOpenPositions", MOP );
SetOption( "WorstRankHeld", WRH);  

RiskPerShare =  3 * ATR(21); 
PositionRisk = 1.5;     
PctSize =  PositionRisk * BuyPrice / RiskPerShare;
SetPositionSize( PctSize, spsPercentOfEquity);

PositionScore = RSI(500) * 100;

Date Information
8/17/2017
Enter Long, NVDA, Price: 165.15, Shares: 91
Enter Long, ADBE, Price: 151.8, Shares: 221
Enter Long, CHTR, Price: 400.78, Shares: 60
Enter Long, EA, Price: 119.25, Shares: 226
Enter Long, ATVI, Price: 62.89, Shares: 6

Date Information
8/17/2017
Enter Long, NVDA, Price: 164.81, Shares: 88
Enter Long, ADBE, Price: 151.66, Shares: 205
Enter Long, CHTR, Price: 401, Shares: 57
Enter Long, EA, Price: 119.19, Shares: 213
Enter Long, ATVI, Price: 62.57, Shares: 97

5 Open Positions:NVDA(+88),ADBE(+205),CHTR(+57),EA(+213),ATVI(+97) 

8/18/2017
FB not entered because of insufficient funds
5 Open Positions:NVDA(+88),ADBE(+205),CHTR(+57),EA(+213),ATVI(+97)

How are you getting AmiBroker to show you tomorrow’s trades when you run the AFL today? Are you turning on Add Artificial Future Bar in the Analysis Settings, or doing something else?

Matt

Yes! I am turning on “Add Artificial Future Bar”. You also have to set the Range “To” date to a future date.

Here is an excerpt from the AB Help file:

Add artificial future bar

When checked AmiBroker adds tommorrow’s bar and this enables you to see tommorrow’s (or next bar) trade recommendations when your system uses one bar delay. Artificial future bar is has incremented date and volume set to zero and all price fields (OHLC) set to CLOSE price of last data bar.

So when you run your back test on Monday, Tuesday’s artificial bar will have OHLC fields all set to today’s close. But when you run the back test on Tuesday, Tuesday’s actual bar will have real OHLC data. Since you are using the Open price to determine the number of shares to purchase, your number of shares will change slightly.

  • Matt
3 Likes

That is true but I am also using “UsePrevBarEquityForPosSizing” which should use the previous bars close price to set the position size and therefore there should be NO change in the stock positions. Unless this can be corrected I see no way of using a backtest result to trade in real time unless you code the IB interface and have the system place your order on open. You would get out of step with the backtest and be making mistakes.

You need to review the Help file. UsePrevBarEquityForPosSizing uses the EQUITY from the previous close to determine your position size. It doesn’t use the PRICE from the previous close.

Trading in real time using AmiBroker brings some additional challenges. Although my experience in that area is quite limited, I haven’t seen anyone use standard back test results to enter and exit live trades. Perhaps someone more familiar with this topic can jump in with suggestions.

Matt

1 Like

I have several real time systems running using IB but not with AmiBroker. It is a bit challenging because the coding is quite complex but it is doable.
I see what you are pointing out about the use of Equity. My thought was that AB would be smart enough to give you a way to freeze the number of shares so you could keep your live trades in line with the backtest.

QUESTION:::
Has anyone written a custom backtest procedure that will handle this? That should be doable. You may have to keep a cushion of equity so that if there were price changes you could still keep the same shares or at least the same in most of the symbols.

You could try having a conditional switch in the code which allows you to switch between two different modes.

In a “signal” mode have it calculate the position size and number of shares off the current close, using the close as the buy price.

In a “performance” mode have it calculate the position size and number of shares off the previous close, and use the open as the buy price.

Leave the trade delays at 0 for signal mode and 1 for performance mode, which should make the signals line up. Running a backtest in signal mode after you have the EoD data would give you the size to buy, and running it in performance mode would generate the actual performance the way you trade it. The sizes should match as they are using the same closing price for both modes. You shouldn’t then need the additional bar in the settings.

Switch between the two modes using, for example:

Mode = ParamToggle("Mode of Operation", "Signal Generation|Performance Reporting");

Unless I’m mistaken, when using % of Equity position sizing AB will always use the entry price to calculate the correct number of shares to buy. That means that you could only do this by writing your own CBT and manipulating the number of shares before entering. If you’re going to write a CBT anyway, then I don’t think there’s any reason to have the two modes, because you can always use the previous day’s close for calculating your “correct” number of shares. When scanning for signals, the “previous day” is actually “today”, i.e. the day before the artificial future bar.

3 Likes

I took a look at using the CBT today and it look quite complex in that you would have to do all the work the rotational trading is doing. I.E. deciding on adding or dropping symbols as well as deciding on shares to purchase.

Another idea my be to just use CBT to override the backtest with an imported file that would have the shares and the price you paid. That would get you in total agreement. And it may be easy to code.

Regarding the CBT: The price of having full control is having to handle all the details! I write almost all of my AFL using a CBT because that extra control can be very useful. I’ve also written several rotational strategies using the CBT, and you’re correct that it’s not trivial. However, if you’ve done significant programming in the past, it’s really not too bad.

stepped in the same issue today.

Did anyone figure out something new or have you settled with trading on close, no artificial future bar and tried to “offset” by estimating slippage?

Dio

There is no good answer here. The system I am running trades of Open Monday so I kind of know what is going to happen at close on Friday. I may or may not make the trades called for but most of the time just wait until Tuesday to place the correct trade. Real time data is the only true answer.

How about a simple exploration that you can run at the end of the trading session

RiskPerShare =  3 * ATR(21); 
PositionRisk = 1.5;     
PctSize =  PositionRisk * BuyPrice / RiskPerShare;

PositionScore = RSI(500) * 100;
Filter = Status ("lastbarinrange");
AddColumn (pctsize ,"size %" ); 
AddColumn (PositionScore ,"score" ); 
SetSortColumns(4 ); 
AddRankColumn(); 

Look at those sizes though :
image

the thing is this:

try running the exploration on “close” with delays set to zero.
have a close look at the ATR column. then do the same when trading on open.

ATR = ATR(20);//
Addcolumn (ATR, "ATR20");

as mentioned, the artificial futurebar setting causes your ATR calculations to do this:

Basically your results will be off slightly, as per my current understanding an exploration does exactly the same thing.

My “solution” is to run everything on close with delays set to zero and estimate a realistic value for slippage:

slippage= ATR(5)*0.05;//
buyprice = C + slippage;//

IB let’s you place market-on-open orders which is pretty convenient hence this request.
the error itself cascades when you’re in stocks with liquidity issues and your holding period is low (say 3 days).

Dio

SetOption( "UsePrevBarEquityForPosSizing", True  );// Uses prior EOD day equity for position size
SetBacktestMode( backtestRotational );
SetOption( "AllowSameBarExit", False );// FALSE FOR ROTATIONAL TRADING
SetTradeDelays( 1, 1, 1, 1 );
SetOption( "CommissionMode",               2     );// Mode 2 = $ per trade
SetOption( "CommissionAmount",             7.95  );// Commissions set to $0
Init_equity = Param ( "Initial System Equity", 25000, 5000, 100000, 100);
SetOption("Initialequity", Init_equity );
BuyPrice = Open;
SellPrice = Open;
RoundLotSize             = 1;
SetOption( "MinShares",  5 );

MOP   = Param( "Max Open Positions", 10, 0, 50, 5 ); 
WRH   = Param( "Worst Rank Held", 10, 0, 50, 1 );

SetOption( "MaxOpenPositions", MOP );
SetOption( "WorstRankHeld", WRH);  

RiskPerShare =  3 * ATR(21); 
PositionRisk = 1.5;     
PctSize =  PositionRisk * BuyPrice / RiskPerShare;
SetPositionSize( PctSize, spsPercentOfEquity);

PositionScore = RSI(500) * 100;

I played with this again.

Try, when trading on open with delay=1, referencing ATR from yesterday.
That way, the shares come out ok, or off by +/-1.

RiskPerShare =  3 * ref(ATR(21),-1);