回测一个策略

我们已经准备好使用 PyBroker 测试一个基本的交易策略了!首先,我们将导入以下所列的必要类:

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

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

对于我们的回测,我们将使用 Yahoo Finance 作为我们的 数据源。我们还将使用数据源缓存来确保在运行回测时只下载一次所需的数据。

下一步是设置一个新的 Strategy 类实例,它将用于对我们的交易策略进行回测。以下是操作方法:

首先,你可以创建一个 StrategyConfig 对象来配置策略。在这个例子中,我们将初始资金设为 500,000:

[2]:
config = StrategyConfig(initial_cash=500_000)

接下来,你可以通过传入以下参数来创建一个 Strategy 类的新实例:

  • 数据源:在这种情况下,我们使用 Yahoo Finance 作为数据源。

  • 开始日期:这是回测的开始日期。

  • 结束日期:这是回测的结束日期。

  • 之前创建的配置对象。

[3]:
strategy = Strategy(YFinance(), '3/1/2017', '3/1/2022', config)

现在,Strategy 实例已准备好在使用指定的配置选项运行回测之前,从 Yahoo Finance 下载 2017 年 3 月 1 日至 2022 年 3 月 1 日之间的数据。如果你需要修改其他配置选项,可以参考 StrategyConfig 参考文档。

定义策略规则

在本节中,你将学习如何在 PyBroker 中实现一个基本的交易策略,包括以下规则:

  1. 如果最后收盘价低于前一根K线的最低价,并且该股票没有未平仓的多头头寸,则购买该股票的股份。

  2. 将买单的限价设置为比最后收盘价低 0.01 的价格。

  3. 持有头寸 3 天后以市价平仓。

  4. 在 AAPL 和 MSFT 上执行这些规则,为每只股票分配最多 25% 的投资组合。

为实现这一目标,你将定义一个 buy_low 函数,PyBroker 将在每条数据的每个K线上分别为 AAPL 和 MSFT 调用该函数。每个 K 线对应一天的数据:

[4]:
def buy_low(ctx):
    # If shares were already purchased and are currently being held, then return.
    if ctx.long_pos():
        return
    # If the latest close price is less than the previous day's low price,
    # then place a buy order.
    if ctx.bars >= 2 and ctx.close[-1] < ctx.low[-2]:
        # Buy a number of shares that is equal to 25% the portfolio.
        ctx.buy_shares = ctx.calc_target_shares(0.25)
        # Set the limit price of the order.
        ctx.buy_limit_price = ctx.close[-1] - 0.01
        # Hold the position for 3 bars before liquidating (in this case, 3 days).
        ctx.hold_bars = 3

这里有很多内容需要解释!buy_low 函数将接收一个包含当前股票代码(AAPL 或 MSFT)数据的 ExecContextctx)。ExecContext 将包含当前股票代码最近 K 线之前的所有收盘价。通过 ctx.close[-1] 获取最新的收盘价。

buy_low 函数将使用 ExecContext 来下达买单。购买的股份数量通过 ctx.buy_shares,设置,它是通过 ctx.calc_target_shares 计算的。在这种情况下,要购买的股份数量将等于投资组合的 25%。

订单的限价通过 buy_limit_price 设置。如果满足条件,买单将在下一根 K 线成交。订单成交的时间可以通过 StrategyConfig.buy_delay 进行配置,成交价可以通过 ExecContext.buy_fill_price 设置。默认情况下,买单在下一根 K 线(buy_delay=1)成交,成交价等于该 K 线的最低价和最高价之间的中点。

最后,ctx.hold_bars 指定在平仓之前持有头寸的 K 线数。平仓时,股票以市价出售,市价等于 ExecContext.sell_fill_price,这是可配置的,默认值为K线最低价和最高价之间的中点。

要将 buy_low 规则添加到 AAPL 和 MSFT 的 策略 中,你需要使用 add_execution

[5]:
strategy.add_execution(buy_low, ['AAPL', 'MSFT'])

添加第二个执行逻辑

在同一个 Strategy 实例中,你可以为不同的股票代码使用不同的交易规则。换句话说,你并不局限于对一组股票代码只使用一套交易规则。

为了说明这一点,提供了一个名为 short_high 的函数,其中包含一个做空策略的新规则集,这与之前的规则集类似:

[6]:
def short_high(ctx):
    # If shares were already shorted then return.
    if ctx.short_pos():
        return
    # If the latest close price is more than the previous day's high price,
    # then place a sell order.
    if ctx.bars >= 2 and ctx.close[-1] > ctx.high[-2]:
        # Short 100 shares.
        ctx.sell_shares = 100
        # Cover the shares after 2 bars (in this case, 2 days).
        ctx.hold_bars = 2

short_high 中的规则将应用于 TSLA

[7]:
strategy.add_execution(short_high, ['TSLA'])

(注意,你还可以通过调用 ExecContext#foreign 获取另一个股票代码的 K 线数据)

运行回测

要运行回测,请在 Strategy 实例上调用 backtest 方法。以下是一个例子:

[8]:
result = strategy.backtest()
Backtesting: 2017-03-01 00:00:00 to 2022-03-01 00:00:00

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

Test split: 2017-03-01 00:00:00 to 2022-02-28 00:00:00

100% (1259 of 1259) |####################| Elapsed Time: 0:00:00 Time:  0:00:000:00

Finished backtest: 0:00:03

这很快!backtest 方法将返回一个 TestResult 实例。你可以通过此实例访问有关回测的各种信息和指标。例如,要查看投资组合的每日余额,你可以使用 Matplotlib 绘制市值:

[9]:
import matplotlib.pyplot as plt

chart = plt.subplot2grid((3, 2), (0, 0), rowspan=3, colspan=2)
chart.plot(result.portfolio.index, result.portfolio['market_value'])
[9]:
[<matplotlib.lines.Line2D at 0x7f44544ecfd0>]
../_images/notebooks_2._Backtesting_a_Strategy_18_1.png

你还可以访问每个持有头寸的每日余额、每次进出场的交易以及下达的所有订单:

[10]:
result.positions
[10]:
long_shares short_shares close equity market_value margin unrealized_pnl
symbol date
MSFT 2017-03-03 1952 0 64.25 125416.00 125416.00 0.00 585.60
2017-03-06 1952 0 64.27 125455.03 125455.03 0.00 624.63
2017-03-07 1952 0 64.40 125708.80 125708.80 0.00 878.40
2017-03-14 1937 0 64.41 124762.18 124762.18 0.00 116.23
TSLA 2017-03-15 0 100 17.05 0.00 1718.00 1704.87 13.13
... ... ... ... ... ... ... ... ...
MSFT 2022-02-22 583 0 287.72 167740.76 167740.76 0.00 -1375.88
AAPL 2022-02-22 1005 0 164.32 165141.61 165141.61 0.00 -4060.19
MSFT 2022-02-23 583 0 280.27 163397.40 163397.40 0.00 -5719.24
AAPL 2022-02-23 1005 0 160.07 160870.36 160870.36 0.00 -8331.44
TSLA 2022-02-28 0 100 290.14 0.00 28193.00 29014.33 -821.33

938 rows × 7 columns

[11]:
result.trades
[11]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop mae mfe
id
1 long MSFT 2017-03-03 2017-03-08 63.95 64.67 1952 1405.44 1.13 1405.44 3 468.48 bar -0.33 0.83
2 long MSFT 2017-03-14 2017-03-17 64.35 64.96 1937 1181.57 0.95 2587.01 3 393.86 bar -0.20 0.61
3 short TSLA 2017-03-15 2017-03-17 17.18 17.55 100 -37.00 -2.11 2550.01 2 -18.50 bar -0.54 0.23
4 short TSLA 2017-03-27 2017-03-29 17.68 18.50 100 -82.00 -4.43 2468.01 2 -41.00 bar -1.03 0.36
5 short TSLA 2017-04-04 2017-04-06 19.98 19.87 100 11.00 0.55 2479.01 2 5.50 bar -0.35 0.37
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
384 long AAPL 2022-02-11 2022-02-16 170.56 171.69 984 1111.92 0.66 180139.06 3 370.64 bar -4.00 2.52
385 long MSFT 2022-02-11 2022-02-16 299.26 297.27 560 -1114.40 -0.66 179024.66 3 -371.47 bar -7.91 5.03
386 short TSLA 2022-02-16 2022-02-18 304.61 287.41 100 1720.00 5.98 180744.66 2 860.00 bar -4.20 17.20
387 long AAPL 2022-02-18 2022-02-24 168.36 157.43 1005 -10984.65 -6.49 169760.01 3 -3661.55 bar -10.93 2.18
388 long MSFT 2022-02-18 2022-02-24 290.08 283.34 583 -3929.42 -2.32 165830.59 3 -1309.81 bar -9.98 3.78

388 rows × 15 columns

[12]:
result.orders
[12]:
type symbol date shares limit_price fill_price fees
id
1 buy MSFT 2017-03-03 1952 64.00 63.95 0.0
2 sell MSFT 2017-03-08 1952 NaN 64.67 0.0
3 buy MSFT 2017-03-14 1937 64.70 64.35 0.0
4 sell TSLA 2017-03-15 100 NaN 17.18 0.0
5 sell MSFT 2017-03-17 1937 NaN 64.96 0.0
... ... ... ... ... ... ... ...
773 buy AAPL 2022-02-18 1005 168.87 168.36 0.0
774 buy MSFT 2022-02-18 583 290.72 290.08 0.0
775 sell AAPL 2022-02-24 1005 NaN 157.43 0.0
776 sell MSFT 2022-02-24 583 NaN 283.34 0.0
777 sell TSLA 2022-02-28 100 NaN 281.93 0.0

777 rows × 7 columns

此外,result.metrics_df 包含一个使用回测收益率计算得出的指标 DataFrame。你可以在参考文档中了解这些指标的含义

[13]:
result.metrics_df
[13]:
name value
0 trade_count 388.000000
1 initial_market_value 500000.000000
2 end_market_value 665009.260000
3 total_pnl 165830.590000
4 unrealized_pnl -821.330000
5 total_return_pct 33.166118
6 total_profit 402053.210000
7 total_loss -236222.620000
8 total_fees 0.000000
9 max_drawdown -31619.460000
10 max_drawdown_pct -4.722785
11 win_rate 52.577320
12 loss_rate 47.422680
13 winning_trades 204.000000
14 losing_trades 184.000000
15 avg_pnl 427.398428
16 avg_return_pct 0.279639
17 avg_trade_bars 2.414948
18 avg_profit 1970.849069
19 avg_profit_pct 3.168775
20 avg_winning_trade_bars 2.465686
21 avg_loss -1283.818587
22 avg_loss_pct -2.923533
23 avg_losing_trade_bars 2.358696
24 largest_win 20973.750000
25 largest_win_pct 14.490000
26 largest_win_bars 3.000000
27 largest_loss -10984.650000
28 largest_loss_pct -6.490000
29 largest_loss_bars 3.000000
30 max_wins 7.000000
31 max_losses 7.000000
32 sharpe 0.055208
33 sortino 0.061908
34 profit_factor 1.317514
35 ulcer_index 0.659022
36 upi 0.035627
37 equity_r2 0.902314
38 std_error 65830.655991

筛选回测数据

你可以筛选用于回测的数据,仅包括特定的 K 线。例如,你可以通过筛选仅包含周一的数据来限制策略仅在周一交易:

[14]:
result = strategy.backtest(days='mon')
result.orders
Backtesting: 2017-03-01 00:00:00 to 2022-03-01 00:00:00

Loaded cached bar data.

Test split: 2017-03-06 00:00:00 to 2022-02-28 00:00:00
100% (238 of 238) |######################| Elapsed Time: 0:00:00 Time:  0:00:000:00

Finished backtest: 0:00:00
[14]:
type symbol date shares limit_price fill_price fees
id
1 sell TSLA 2017-03-27 100 NaN 17.68 0.0
2 buy TSLA 2017-04-10 100 NaN 20.75 0.0
3 sell TSLA 2017-04-17 100 NaN 20.09 0.0
4 buy TSLA 2017-05-01 100 NaN 21.40 0.0
5 sell TSLA 2017-05-08 100 NaN 20.65 0.0
... ... ... ... ... ... ... ...
178 sell TSLA 2022-02-07 100 NaN 308.41 0.0
179 sell AAPL 2022-02-14 777 NaN 168.07 0.0
180 buy MSFT 2022-02-14 457 300.94 294.06 0.0
181 buy TSLA 2022-02-28 100 NaN 281.93 0.0
182 buy AAPL 2022-02-28 811 168.87 163.92 0.0

182 rows × 7 columns

由于启用了缓存,因此无需再次从 Yahoo Finance 下载数据,只需对缓存的数据进行筛选即可。

你还可以使用 between_time 参数按时间范围筛选数据,例如 9:30-10:30 AM。

尽管之前的指标表明我们拥有一个盈利的策略,但我们可能被随机性误导。在下一篇文章中,我们将讨论如何使用自助法进一步评估我们的交易策略