Rotational trading is a strategy used by investors that involves purchasing top-performing assets and simultaneously selling the underperforming ones in their portfolio. It’s a great way to periodically manage a portfolio by holding winners and selling losers.
Backtesting a rotational trading strategy is easy using PyBroker, an open-source Python framework for developing trading strategies. To begin using PyBroker, you can install the library with pip:
pip install -U lib-pybroker
Or you can clone the Github repository:
git clone https://github.com/edtechre/pybroker
Once installed, let’s create a new notebook and add some required imports:
import pybroker as pyb
from pybroker import ExecContext, Strategy, StrategyConfig, YFinance
Rotational Strategy
Our strategy will involve ranking and buying stocks with the highest price rate-of-change (ROC). To start, we’ll define a 20-day ROC indicator using TA-Lib, a widely used technical analysis library that implements many financial indicators:
import talib as ta
roc_20 = pyb.indicator(
'roc_20', lambda data: ta.ROC(data.adj_close, timeperiod=20))
Next, let’s define the rules of our strategy:
- Buy the two stocks with the highest 20-day ROC.
- Allocate 50% of our capital to each stock.
- If either of the stocks is no longer ranked among the top five 20-day ROCs, then we will liquidate that stock.
- Trade these rules daily.
Let’s set up our config and some parameters for the above rules:
config = StrategyConfig(max_long_positions=2)
pyb.param('target_size', 1 / config.max_long_positions)
pyb.param('rank_threshold', 5)
To proceed with our strategy, we will implement a rank
function that ranks each stock by their 20-day ROC in descending order, from highest to lowest.
def rank(ctxs: dict[str, ExecContext]):
scores = {
symbol: ctx.indicator('roc_20')[-1]
for symbol, ctx in ctxs.items()
}
sorted_scores = sorted(
scores.items(),
key=lambda score: score[1],
reverse=True
)
threshold = pyb.param('rank_threshold')
top_scores = sorted_scores[:threshold]
top_symbols = [score[0] for score in top_scores]
pyb.param('top_symbols', top_symbols)
The top_symbols
global parameter contains the symbols of the stocks with the top five highest 20-day ROCs.
Now that we have a method for ranking stocks by their ROC, we can proceed with implementing a rotate
function to manage the rotational trading.
def rotate(ctx: ExecContext):
if ctx.long_pos():
if ctx.symbol not in pyb.param('top_symbols'):
ctx.sell_all_shares()
else:
target_size = pyb.param('target_size')
ctx.buy_shares = ctx.calc_target_shares(target_size)
ctx.score = ctx.indicator('roc_20')[-1]
We liquidate the currently held stock if it is no longer ranked among the top five 20-day ROCs. Otherwise, we rank all stocks by their 20-day ROC and buy up to the top two ranked. For more information on ranking when placing buy orders, see the Ranking and Position Sizing notebook in PyBroker’s documentation.
We will use the set_before_exec method to execute our ranking with rank
before running the rotate
function. For this backtest, we will use a universe of 10 stocks:
strategy = Strategy(
YFinance(),
start_date='1/1/2018',
end_date='1/1/2023',
config=config
)
strategy.set_before_exec(rank)
strategy.add_execution(rotate, [
'TSLA',
'NFLX',
'AAPL',
'NVDA',
'AMZN',
'MSFT',
'GOOG',
'AMD',
'INTC',
'META'
], indicators=roc_20)
Now, let’s run our backtest!
result = strategy.backtest(warmup=20)
The result
contains trades and performance metrics from the backtest. Here is a sample of a few:
result.metrics_df.round(2)
trade_count 129.00
initial_market_value 100000.00
end_market_value 484715.10
total_pnl 399906.80
max_drawdown -429073.10
(Ouch! Hopefully you can come up with a better strategy with less drawdown!)
We can also access the orders that were placed by the strategy:
result.orders.head()
type symbol date shares limit_price fill_price fees
buy NFLX 2018-02-01 184 NaN 267.67 0.0
buy AMD 2018-02-01 3639 NaN 11.75 0.0
sell AMD 2018-02-05 3639 NaN 11.56 0.0
buy AMZN 2018-02-05 627 NaN 69.49 0.0
sell AMD 2018-04-03 627 NaN 69.23 0.0
And that’s a wrap! You can now begin backtesting your own rotational trading strategies with PyBroker to improve your portfolio’s performance. You can also find more tutorials on using PyBroker on https://www.pybroker.com, with all code available in the Github repository.
Thanks for reading!