排名和仓位调整
在本文档中,我们将学习有关 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_shares 或 sell_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 |
使用这种方法可以实现很多可能性,例如使用 均值-方差优化 来确定投资组合分配。