应用止损

止损是用于在证券达到指定价格水平时自动买入或卖出的工具。它们对于通过让交易者自动退出糟糕的交易来限制潜在损失非常有用,同时也可以在证券达到某个价格水平时自动卖出以获利。

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:02
[2]:
type symbol entry_date exit_date entry exit shares pnl return_pct agg_pnl bars pnl_per_bar stop
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
2 long TSLA 2018-03-29 2019-05-20 17.31 13.73 4622 -16531.36 -20.66 -36520.04 286 -57.80 loss

锁定利润

类似地,可以使用获利单来锁定交易的利润。以下代码在入场价格上涨 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
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
2 long TSLA 2018-01-23 2018-03-27 23.72 18.98 4637 -21997.93 -20.0 -12003.58 44 -499.95 loss
3 long TSLA 2018-03-28 2018-04-04 17.36 19.10 4727 8206.07 10.0 -3797.51 4 2051.52 profit
4 long TSLA 2018-04-05 2018-06-07 19.82 21.80 4853 9618.65 10.0 5821.13 44 218.61 profit
5 long TSLA 2018-06-08 2018-06-12 21.39 23.53 4947 10581.63 10.0 16402.77 2 5290.82 profit
... ... ... ... ... ... ... ... ... ... ... ... ... ...
85 long TSLA 2022-07-29 2022-10-07 288.71 230.97 1010 -58319.42 -20.0 133480.29 49 -1190.19 loss
86 long TSLA 2022-10-10 2022-11-09 222.68 178.14 1046 -46584.66 -20.0 86895.63 22 -2117.48 loss
87 long TSLA 2022-11-10 2022-12-19 185.51 148.41 1007 -37361.71 -20.0 49533.92 26 -1436.99 loss
88 long TSLA 2022-12-20 2022-12-27 143.07 114.46 997 -28528.16 -20.0 21005.76 4 -7132.04 loss
89 long TSLA 2022-12-28 2022-12-29 112.25 123.48 1078 12100.55 10.0 33106.31 1 12100.55 profit

89 rows × 13 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
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
2 long TSLA 2018-01-23 2018-03-27 23.72 19.20 4637 -20961.72 -19.06 -10967.37 44 -476.40 trailing
3 long TSLA 2018-03-28 2018-04-04 17.36 19.10 4783 8303.29 10.00 -2664.08 4 2075.82 profit
4 long TSLA 2018-04-05 2018-06-07 19.82 21.80 4910 9731.62 10.00 7067.54 44 221.17 profit
5 long TSLA 2018-06-08 2018-06-12 21.39 23.53 5005 10705.70 10.00 17773.23 2 5352.85 profit
... ... ... ... ... ... ... ... ... ... ... ... ... ...
102 long TSLA 2022-08-02 2022-10-03 300.25 251.73 1095 -53125.76 -16.16 175768.16 43 -1235.48 trailing
103 long TSLA 2022-10-04 2022-10-24 249.75 199.80 1104 -55144.80 -20.00 120623.36 14 -3938.91 trailing
104 long TSLA 2022-10-25 2022-11-08 217.18 189.92 1015 -27668.90 -12.55 92954.46 10 -2766.89 trailing
105 long TSLA 2022-11-09 2022-12-13 186.50 160.66 1008 -26050.75 -13.86 66903.71 23 -1132.64 trailing
106 long TSLA 2022-12-14 2022-12-22 158.46 128.79 1036 -30736.04 -18.72 36167.67 6 -5122.67 trailing

106 rows × 13 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
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
2 long TSLA 2018-01-23 2019-12-18 23.72 26.09 4637 10998.96 10.0 20993.31 480 22.91 profit
3 long TSLA 2019-12-19 2020-01-03 26.78 29.46 4518 12099.20 10.0 33092.51 9 1344.36 profit
4 long TSLA 2020-01-06 2020-01-08 29.72 32.69 4478 13308.62 10.0 46401.13 2 6654.31 profit
5 long TSLA 2020-01-09 2020-01-14 32.39 35.63 4462 14452.42 10.0 60853.55 3 4817.47 profit

取消止损

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

[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
id
1 long TSLA 2018-01-03 2018-03-27 21.36 19.23 4679 -9981.87 -9.99 -9981.87 57 -175.12 trailing

设定限价

By default, stops are checked against the bar’s low and high prices, and they are exited at the stop’s threshold (e.g., -2%) on the same bar when the stop is triggered.

To set a custom exit price, the “exit_price” fields for each stop type can be used. When these fields are set, the stop will be checked against the exit_price, and it will exit at the exit_price when triggered. For example, the code below sets the stop_trailing_exit_price to the open price on the bar that triggers the stop:

[7]:
from pybroker import PriceType

def buy_with_stop_trailing_and_exit_price(ctx):
    pos = ctx.long_pos()
    if not 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
id
1 long TSLA 2018-01-03 2018-03-28 21.36 17.64 4679 -17412.12 -17.42 -17412.12 58 -300.21 trailing
2 long TSLA 2018-03-29 2018-07-25 17.31 19.78 4771 11797.10 14.28 -5615.03 81 145.64 trailing
3 long TSLA 2018-07-26 2018-08-20 20.48 19.45 4585 -4737.83 -5.05 -10352.86 17 -278.70 trailing
4 long TSLA 2018-08-21 2018-09-07 21.13 17.34 4242 -16077.18 -17.94 -26430.04 12 -1339.76 trailing
5 long TSLA 2018-09-10 2018-12-26 18.57 20.00 3961 5664.23 7.70 -20765.81 74 76.54 trailing

要了解有关在 PyBroker 中设置止损的各种属性的更多信息,你可以查阅 ExecContext 参考文档。