轮动交易

轮动交易涉及购买表现最好的资产,同时出售表现不佳的资产。你可能已经猜到了,PyBroker 是回测此类策略的极佳工具。那么,让我们深入了解,并开始测试我们的轮动交易策略!

[1]:
import pybroker as pyb
from pybroker import ExecContext, Strategy, StrategyConfig, YFinance

我们的策略将涉及对 价格涨幅(ROC) 最高的股票进行排名和购买。首先,我们将使用 TA-Lib 定义一个 20 天的 ROC 指标:

[2]:
import talib as ta

roc_20 = pyb.indicator(
    'roc_20', lambda data: ta.ROC(data.adj_close, timeperiod=20))

接下来,让我们定义策略的规则:

  • 购买 20 天涨幅(ROC)最高的两只股票。

  • 将我们的资本的 50% 分配给每只股票。

  • 如果其中一只股票不再位于前五名的 20 天涨幅(ROC)中,则我们将清盘该股票。

  • 每天交易这些规则。

为了实现上述规则,让我们设置配置和一些参数:

[3]:
config = StrategyConfig(max_long_positions=2)
pyb.param('target_size', 1 / config.max_long_positions)
pyb.param('rank_threshold', 5)
[3]:
5

为了继续我们的策略,我们将实现一个排名函数,根据每只股票的 20 天涨幅(ROC)降序 排列,从最高到最低。

[4]:
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)

top_symbols 全局参数包含了 20 天涨幅(ROC)最高的前五名股票的符号。

现在我们已经有了一个根据 ROC 对股票进行排名的方法,我们可以继续实现一个 轮动 函数来管理轮动交易。

[5]:
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]

如果目前持有的股票不再位于前五名的 20 天涨幅(ROC)中,我们将清盘该股票。否则,我们将根据它们的 20 天涨幅(ROC)对所有股票进行排名,并购买排名最高的前两名。有关在下达买入订单时的排名信息,请参阅 排名和仓位文档

我们将使用 set_before_exec 方法在运行 轮动 函数之前使用 rank 执行我们的排名。对于这次回测,我们将使用 10 只股票组成的股票池:

[6]:
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)
result = strategy.backtest(warmup=20)
Backtesting: 2018-01-01 00:00:00 to 2023-01-01 00:00:00

Loading bar data...
[*********************100%***********************]  10 of 10 completed
Loaded bar data: 0:00:03

Computing indicators...
100% (10 of 10) |########################| Elapsed Time: 0:00:00 Time:  0:00:00

Test split: 2018-01-02 00:00:00 to 2022-12-30 00:00:00
100% (1259 of 1259) |####################| Elapsed Time: 0:00:00 Time:  0:00:00

Finished backtest: 0:00:06
[7]:
result.orders
[7]:
type symbol date shares limit_price fill_price fees
id
1 buy NFLX 2018-02-01 184 NaN 267.67 0.0
2 buy AMD 2018-02-01 3639 NaN 13.53 0.0
3 sell AMD 2018-02-05 3639 NaN 11.56 0.0
4 buy AMZN 2018-02-05 627 NaN 69.49 0.0
5 sell AMZN 2018-04-03 627 NaN 69.23 0.0
... ... ... ... ... ... ... ...
256 buy AMD 2022-11-21 3589 NaN 72.28 0.0
257 sell AMD 2022-12-14 3589 NaN 70.16 0.0
258 buy NFLX 2022-12-14 881 NaN 319.57 0.0
259 sell NVDA 2022-12-28 1491 NaN 140.73 0.0
260 buy META 2022-12-28 1869 NaN 116.83 0.0

260 rows × 7 columns