应用止损

止损可在证券达到指定价格水平时自动买入或卖出。它们既能通过退出糟糕的交易来限制潜在损失,也能在证券达到目标价格时卖出以锁定利润。

本笔记本将介绍如何在 PyBroker 中模拟止损:

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

pybroker.enable_data_source_cache("stops")

strategy = Strategy(YFinance(), "1/1/2018", "1/1/2023")

止损

止损单用于在证券价格达到或跌破指定水平时自动退出交易。例如,以下代码展示了一个在入场价格下跌 20% 处设置的止损单示例:

[2]:
def buy_with_stop_loss(ctx):
    if not ctx.long_pos():
        ctx.buy_shares = ctx.calc_target_shares(1)
        ctx.stop_loss_pct = 20


strategy.add_execution(buy_with_stop_loss, ["TSLA"])
result = strategy.backtest()
result.trades
Backtesting: 2018-01-01 00:00:00 to 2023-01-01 00:00:00

Loading bar data...
[*********************100%***********************]  1 of 1 completed
Loaded bar data: 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:01
[2]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop mae mfe
id
1 long TSLA 2018-01-03 2018-03-28 21.36 17.09 4679 -19988.69 -20.00 -19988.69 58 -344.63 loss -4.27 2.67
2 long TSLA 2018-03-29 2019-05-20 17.31 13.73 4622 -16546.76 -20.68 -36535.45 286 -57.86 loss -3.58 8.52

锁定利润

可以使用获利单来锁定交易的利润。以下代码在入场价格上涨 10% 处添加了一个获利单:

[3]:
def buy_with_stop_loss_and_profit(ctx):
    if not ctx.long_pos():
        ctx.buy_shares = ctx.calc_target_shares(1)
        ctx.stop_loss_pct = 20
        ctx.stop_profit_pct = 10


strategy.clear_executions()
strategy.add_execution(buy_with_stop_loss_and_profit, ["TSLA"])
result = strategy.backtest()
result.trades
Backtesting: 2018-01-01 00:00:00 to 2023-01-01 00:00:00

Loaded cached bar data.

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:00
[3]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop mae mfe
id
1 long TSLA 2018-01-03 2018-01-22 21.36 23.50 4679 9994.34 10.0 9994.34 12 832.86 profit -0.98 2.14
2 long TSLA 2018-01-23 2018-03-27 23.72 18.98 4637 -21997.93 -20.0 -12003.58 44 -499.95 loss -4.74 0.31
3 long TSLA 2018-03-28 2018-04-04 17.36 19.10 4727 8206.07 10.0 -3797.51 4 2051.52 profit -1.05 1.74
4 long TSLA 2018-04-05 2018-06-07 19.82 21.80 4853 9618.65 10.0 5821.13 44 218.61 profit -1.59 1.98
5 long TSLA 2018-06-08 2018-06-12 21.39 23.53 4947 10581.63 10.0 16402.77 2 5290.82 profit -0.25 2.14
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
85 long TSLA 2022-07-29 2022-10-07 288.71 230.97 1010 -58319.42 -20.0 133548.98 49 -1190.19 loss -57.74 25.96
86 long TSLA 2022-10-10 2022-11-09 222.68 178.14 1046 -46584.66 -20.0 86964.32 22 -2117.48 loss -44.54 14.72
87 long TSLA 2022-11-10 2022-12-19 185.51 148.41 1007 -37361.71 -20.0 49602.61 26 -1436.99 loss -37.10 15.31
88 long TSLA 2022-12-20 2022-12-27 143.07 114.46 998 -28556.77 -20.0 21045.84 4 -7139.19 loss -28.61 5.40
89 long TSLA 2022-12-28 2022-12-29 112.25 123.48 1078 12100.55 10.0 33146.39 1 12100.55 profit -4.01 11.23

89 rows × 15 columns

移动止损

移动止损单会在证券价格跌破最高市场价格达到指定百分比或现金金额时自动退出交易。以下示例展示了在最高市场价格下跌 20% 处设置移动止损:

[4]:
def buy_with_trailing_stop_loss_and_profit(ctx):
    if not ctx.long_pos():
        ctx.buy_shares = ctx.calc_target_shares(1)
        ctx.stop_trailing_pct = 20
        ctx.stop_profit_pct = 10


strategy.clear_executions()
strategy.add_execution(buy_with_trailing_stop_loss_and_profit, ["TSLA"])
result = strategy.backtest()
result.trades
Backtesting: 2018-01-01 00:00:00 to 2023-01-01 00:00:00

Loaded cached bar data.

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:00
[4]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop mae mfe
id
1 long TSLA 2018-01-03 2018-01-22 21.36 23.50 4679 9994.34 10.00 9994.34 12 832.86 profit -0.98 2.14
2 long TSLA 2018-01-23 2018-03-27 23.72 19.20 4637 -20959.24 -19.06 -10964.90 44 -476.35 trailing -4.52 0.31
3 long TSLA 2018-03-28 2018-04-04 17.36 19.10 4783 8303.29 10.00 -2661.61 4 2075.82 profit -1.05 1.74
4 long TSLA 2018-04-05 2018-06-07 19.82 21.80 4911 9733.60 10.00 7071.99 44 221.22 profit -1.59 1.98
5 long TSLA 2018-06-08 2018-06-12 21.39 23.53 5005 10705.70 10.00 17777.69 2 5352.85 profit -0.25 2.14
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
102 long TSLA 2022-08-02 2022-10-03 300.25 251.74 1095 -53122.83 -16.16 175831.91 43 -1235.41 trailing -48.51 14.42
103 long TSLA 2022-10-04 2022-10-24 249.75 199.80 1104 -55144.80 -20.00 120687.11 14 -3938.91 trailing -49.95 7.75
104 long TSLA 2022-10-25 2022-11-08 217.18 189.92 1016 -27696.16 -12.55 92990.95 10 -2769.62 trailing -27.26 20.22
105 long TSLA 2022-11-09 2022-12-13 186.50 160.66 1008 -26050.75 -13.86 66940.19 23 -1132.64 trailing -25.84 14.32
106 long TSLA 2022-12-14 2022-12-22 158.46 128.79 1037 -30765.72 -18.72 36174.48 6 -5127.62 trailing -29.67 3.16

106 rows × 15 columns

设定限价

止损单可以与限价结合使用,以确保订单仅在特定价格水平执行。下面是在止损单上设置限价的示例:

[5]:
def buy_with_trailing_stop_loss_and_profit(ctx):
    if not ctx.long_pos():
        ctx.buy_shares = ctx.calc_target_shares(1)
        ctx.stop_trailing_pct = 20
        ctx.stop_trailing_limit = ctx.close[-1] + 1
        ctx.stop_profit_pct = 10
        ctx.stop_profit_limit = ctx.close[-1] - 1


strategy.clear_executions()
strategy.add_execution(buy_with_trailing_stop_loss_and_profit, ["TSLA"])
result = strategy.backtest()
result.trades.head()
Backtesting: 2018-01-01 00:00:00 to 2023-01-01 00:00:00

Loaded cached bar data.

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:00
[5]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop mae mfe
id
1 long TSLA 2018-01-03 2018-01-22 21.36 23.50 4679 9994.34 10.0 9994.34 12 832.86 profit -0.98 2.14
2 long TSLA 2018-01-23 2019-12-18 23.72 26.09 4637 10998.96 10.0 20993.31 480 22.91 profit -11.92 2.37
3 long TSLA 2019-12-19 2020-01-03 26.78 29.46 4518 12099.20 10.0 33092.51 9 1344.36 profit -0.35 2.68
4 long TSLA 2020-01-06 2020-01-08 29.72 32.69 4478 13308.62 10.0 46401.13 2 6654.31 profit -0.39 2.97
5 long TSLA 2020-01-09 2020-01-14 32.39 35.63 4462 14452.42 10.0 60853.55 3 4817.47 profit -0.87 3.24

取消止损

以下代码展示了取消止损单的示例:

[6]:
def buy_with_stop_trailing_and_cancel(ctx):
    pos = ctx.long_pos()
    if not pos:
        ctx.buy_shares = ctx.calc_target_shares(1)
        ctx.stop_trailing_pct = 20
    elif pos.bars > 60:
        ctx.cancel_stops(ctx.symbol)


strategy.clear_executions()
strategy.add_execution(buy_with_stop_trailing_and_cancel, ["TSLA"])
result = strategy.backtest()
result.trades
Backtesting: 2018-01-01 00:00:00 to 2023-01-01 00:00:00

Loaded cached bar data.

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:00
[6]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop mae mfe
id
1 long TSLA 2018-01-03 2018-03-27 21.36 19.22 4679 -9994.34 -10.0 -9994.34 57 -175.34 trailing -2.14 2.67

设置止损退出价格

默认情况下,PyBroker 会根据 K 线的最低价和最高价检查止损,并在止损触发的同一根 K 线上按止损的阈值(例如 -2%)退出交易。

要设置自定义退出价格,可以使用每种止损类型提供的 exit_price 字段。配置后,PyBroker 会根据 exit_price 检查止损,并在触发时使用该价格退出。以下代码将 stop_trailing_exit_price 设置为触发止损的 K 线的开盘价:

[7]:
from pybroker import PriceType


def buy_with_stop_trailing_and_exit_price(ctx):
    if not ctx.long_pos():
        ctx.buy_shares = ctx.calc_target_shares(1)
        ctx.stop_trailing_pct = 20
        ctx.stop_trailing_exit_price = PriceType.OPEN


strategy.clear_executions()
strategy.add_execution(buy_with_stop_trailing_and_exit_price, ["TSLA"])
result = strategy.backtest()
result.trades.head()
Backtesting: 2018-01-01 00:00:00 to 2023-01-01 00:00:00

Loaded cached bar data.

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:00
[7]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop mae mfe
id
1 long TSLA 2018-01-03 2018-03-28 21.36 17.64 4679 -17405.88 -17.42 -17405.88 58 -300.10 trailing -3.72 2.67
2 long TSLA 2018-03-29 2018-07-25 17.31 19.78 4771 11784.37 14.27 -5621.51 81 145.49 trailing -1.00 7.61
3 long TSLA 2018-07-26 2018-08-20 20.48 19.45 4585 -4722.55 -5.03 -10344.06 17 -277.80 trailing -1.40 5.35
4 long TSLA 2018-08-21 2018-09-07 21.13 17.34 4243 -16080.97 -17.94 -26425.03 12 -1340.08 trailing -3.79 0.69
5 long TSLA 2018-09-10 2018-12-26 18.57 20.00 3962 5665.66 7.70 -20759.37 74 76.56 trailing -2.05 6.73

有关配置止损可用属性的更多详情,请参阅 ExecContext 参考文档