排名和仓位调整

在本文档中,我们将学习有关 PyBroker 的特性,这些特性使你能够对股票代码进行排名并为交易策略中的一组代码设置仓位大小。通过这些功能,你可以轻松优化策略并更有效地管理风险。

[1]:
import pybroker
from pybroker import Strategy, StrategyConfig, YFinance

pybroker.enable_data_source_cache('ranking_and_pos_sizing')
[1]:
<diskcache.core.Cache at 0x7fd17427dd60>

给股票排序

在本节中,我们将学习如何在下买单时对股票代码进行排名。让我们从一个示例开始,说明如何根据成交量对下买单时的股票代码进行排名。

[2]:
def buy_highest_volume(ctx):
    # If there are no long positions across all tickers being traded:
    if not tuple(ctx.long_positions()):
        ctx.buy_shares = ctx.calc_target_shares(1)
        ctx.hold_bars = 2
        ctx.score = ctx.volume[-1]

buy_highest_volume 函数根据股票代码的最近交易量进行排名,并为 2 个时间段分配 100% 的投资组合。ctx.score 被设置为 ctx.volume[-1],即最近的交易量。

[3]:
config = StrategyConfig(max_long_positions=1)
strategy = Strategy(YFinance(), '6/1/2021', '6/1/2022', config)
strategy.add_execution(buy_highest_volume, ['T', 'F', 'GM', 'PFE'])

要将同时持有的多头仓位数量限制为 1,我们在 StrategyConfig 中将 max_long_positions 设置为 1。在此示例中,我们将 buy_highest_volume 函数添加到 Strategy 对象中,并指定要交易的股票代码:['T', 'F', 'GM', 'PFE']

[4]:
result = strategy.backtest()
result.trades
Backtesting: 2021-06-01 00:00:00 to 2022-06-01 00:00:00

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

Test split: 2021-06-01 00:00:00 to 2022-05-31 00:00:00
100% (253 of 253) |######################| Elapsed Time: 0:00:00 Time:  0:00:00

Finished backtest: 0:00:02
[4]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop
id
1 long F 2021-06-02 2021-06-04 14.85 16.13 6734 8619.52 8.62 8619.52 2 4309.76 bar
2 long F 2021-06-07 2021-06-09 15.93 15.51 6801 -2856.42 -2.64 5763.10 2 -1428.21 bar
3 long F 2021-06-10 2021-06-14 15.43 15.06 6832 -2527.84 -2.40 3235.26 2 -1263.92 bar
4 long F 2021-06-15 2021-06-17 14.96 14.99 6900 207.00 0.20 3442.26 2 103.50 bar
5 long F 2021-06-18 2021-06-22 14.61 14.96 7003 2451.05 2.40 5893.31 2 1225.53 bar
... ... ... ... ... ... ... ... ... ... ... ... ... ...
80 long F 2022-05-10 2022-05-12 13.43 12.47 7263 -6972.48 -7.15 -9423.13 2 -3486.24 bar
81 long F 2022-05-13 2022-05-17 13.25 13.34 6835 615.15 0.68 -8807.98 2 307.58 bar
82 long F 2022-05-18 2022-05-20 13.03 12.59 6739 -2965.16 -3.38 -11773.14 2 -1482.58 bar
83 long F 2022-05-23 2022-05-25 12.72 12.57 6936 -1040.40 -1.18 -12813.54 2 -520.20 bar
84 long F 2022-05-26 2022-05-31 12.99 13.59 6711 4026.60 4.62 -8786.94 2 2013.30 bar

84 rows × 13 columns

设置仓位大小

PyBroker 中,你可以根据多个股票代码设置仓位大小。为了说明这一点,我们来看一个简单的买入并持有策略,该策略在 100 天后开始交易,持有仓位 30 天:

[5]:
def buy_and_hold(ctx):
    if not ctx.long_pos() and ctx.bars >= 100:
        ctx.buy_shares = 100
        ctx.hold_bars = 30

strategy = Strategy(YFinance(), '6/1/2021', '6/1/2022')
strategy.add_execution(buy_and_hold, ['T', 'F', 'GM', 'PFE'])

这将在每个 ['T', 'F', 'GM', 'PFE'] 中购买 100 股。但是,如果你不想使用相等的仓位大小怎么办?例如,你可能希望为波动性较低的股票代码分配更多的份额,以降低投资组合的整体波动性。

要为每个股票代码自定义仓位大小,我们可以定义一个 pos_size_handler 函数来计算每个股票代码的仓位大小:

[6]:
import numpy as np

def pos_size_handler(ctx):
    # Fetch all buy signals.
    signals = tuple(ctx.signals("buy"))
    # Return if there are no buy signals (i.e. there are only sell signals).
    if not signals:
        return
    # Calculates the inverse volatility, where volatility is defined as the
    # standard deviation of close prices for the last 100 days.
    get_inverse_volatility = lambda signal: 1 / np.std(signal.bar_data.close[-100:])
    # Sums the inverse volatilities for all of the buy signals.
    total_inverse_volatility = sum(map(get_inverse_volatility, signals))
    for signal in signals:
        size = get_inverse_volatility(signal) / total_inverse_volatility
        # Calculate the number of shares given the latest close price.
        shares = ctx.calc_target_shares(size, signal.bar_data.close[-1], cash=95_000)
        ctx.set_shares(signal, shares)

strategy.set_pos_size_handler(pos_size_handler)

每当在 ExecContext 上设置 buy_sharessell_shares 时,处理程序都会在生成买入或卖出信号的每个数据点上运行:

[7]:
result = strategy.backtest()
Backtesting: 2021-06-01 00:00:00 to 2022-06-01 00:00:00

Loaded cached bar data.

Test split: 2021-06-01 00:00:00 to 2022-05-31 00:00:00
100% (253 of 253) |######################| Elapsed Time: 0:00:00 Time:  0:00:00

Finished backtest: 0:00:00
[8]:
result.trades
[8]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop
id
1 long T 2021-10-21 2021-12-03 19.60 17.54 2248 -4630.88 -10.51 -4630.88 30 -154.36 bar
2 long F 2021-10-21 2021-12-03 16.41 19.66 2028 6591.00 19.80 1960.12 30 219.70 bar
3 long GM 2021-10-21 2021-12-03 58.20 60.28 129 268.32 3.57 2228.44 30 8.94 bar
4 long PFE 2021-10-21 2021-12-03 42.76 53.75 235 2582.65 25.70 4811.09 30 86.09 bar
5 long T 2021-12-06 2022-01-19 17.81 20.49 2665 7142.20 15.05 11953.29 30 238.07 bar
6 long F 2021-12-06 2022-01-19 19.05 23.66 1075 4955.75 24.20 16909.04 30 165.19 bar
7 long GM 2021-12-06 2022-01-19 59.72 57.99 209 -361.57 -2.90 16547.47 30 -12.05 bar
8 long PFE 2021-12-06 2022-01-19 52.57 53.97 290 406.00 2.66 16953.47 30 13.53 bar
9 long T 2022-01-20 2022-03-04 20.53 17.87 2625 -6982.50 -12.96 9970.97 30 -232.75 bar
10 long F 2022-01-20 2022-03-04 22.22 17.01 789 -4110.69 -23.45 5860.28 30 -137.02 bar
11 long GM 2022-01-20 2022-03-04 55.88 43.08 255 -3264.00 -22.91 2596.28 30 -108.80 bar
12 long PFE 2022-01-20 2022-03-04 53.80 48.09 192 -1096.32 -10.61 1499.96 30 -36.54 bar
13 long T 2022-03-07 2022-04-19 17.88 19.51 3132 5105.16 9.12 6605.12 30 170.17 bar
14 long F 2022-03-07 2022-04-19 16.43 15.98 1303 -586.35 -2.74 6018.77 30 -19.55 bar
15 long GM 2022-03-07 2022-04-19 41.09 41.50 220 90.20 1.00 6108.97 30 3.01 bar
16 long PFE 2022-03-07 2022-04-19 48.18 50.62 200 488.00 5.06 6596.97 30 16.27 bar

使用这种方法可以实现很多可能性,例如使用 均值-方差优化 来确定投资组合分配。

在下一篇文档中,我们将讨论如何在 PyBroker 中实现自定义指标