R-Breaker 策略

ZaynPei Lv6

R-Breaker是一种日内回转交易策略,属于短线交易。日内回转交易是指当天买入或卖出标的后于当日再卖出或买入标的。日内回转交易通过标的短期波动盈利,低买高卖,时间短、投机性强,适合短线投资者。

它的核心是一种双模式切换的交易思想。它不像单纯的趋势策略那样一条路走到黑,也不像单纯的反转策略那样总是在逆势抄底摸顶。它会根据当前是否持仓,来决定自己扮演“追随者”还是“逆行者”的角色。

  • 空仓时 → 趋势跟踪模式:当账户没有任何仓位时,策略表现得像一个突破策略 (Breakout Strategy)。它认为,如果价格能够强力突破根据昨日波动计算出的关键阻力/支撑位,那么日内很可能会形成一轮值得追随的趋势。

  • 持仓时 → 反转交易模式:一旦持有了仓位,策略的“性格”就会立刻改变。它变得警惕起来,不再追涨杀跌,而是开始寻找趋势衰竭的迹象。如果价格在创出日内新高/新低后显现出疲态并回落到某个关键点位,策略会果断地认为趋势即将反转,从而进行反手操作。

六大关键价位

策略的灵魂在于根据前一交易日的数据,计算出六个固定的价格水平。这六个价位构成了当天所有交易决策的“地图”和“标尺”。

不过在进行进一步的计算关键价位之前, 我们还要先进行基础计算确定枢轴点:

枢轴点 (Pivot Point): P = (前一日最高价 H + 前一日最低价 L + 前一日收盘价 C) / 3

它代表了前一交易日市场多空双方力量的均衡点价值中枢, 它是所有其他价位计算的基础。

下面介绍六大价位 (从高到低):

  1. 突破买入价 (Breakout Buy): bBreak = H + 2 * (P - L)

    • 这个价格显著高于前一日的最高价 H。价差 2 * (P - L) 衡量了从最低点到中心点的两倍距离,可以理解为前一日多头力量的一种体现。只有当今日价格极度强势,远超昨日高点时,才触发趋势性买入。
  2. 观察卖出价 (Setup Sell): sSetup = P + (H - L)

    • 逻辑解析: H - L 是前一日的波幅 (Range)。这个价位等于 中心价位 + 一个完整的波幅,它同样高于前一日的最高价 H。当价格达到这里时,意味着上涨趋势可能已接近潜在的衰竭点,需要开始“观察”是否有反转迹象。
  3. 反转卖出价 (Enter Sell): sEnter = 2 * P - L

    • 逻辑解析: 这个价位是多头持仓的反转止损线。它的位置相对灵活,但通常介于观察卖出价和枢轴点之间。
  4. 反转买入价 (Enter Buy): bEnter = 2 * P - H

    • 逻辑解析: 与反转卖出价对称,是空头持仓的反转止损线。
  5. 观察买入价 (Setup Buy): bSetup = P - (H - L)

    • 逻辑解析: 与观察卖出价对称,等于 中心价位 - 一个完整的波幅。当价格跌至此处,意味着下跌趋势可能接近潜在的衰竭点,需要“观察”反弹。
  6. 突破卖出价 (Breakout Sell): sBreak = L - 2 * (H - P)

    • 逻辑解析: 与突破买入价对称。价差 2 * (H - P) 衡量了从中心点到最高点的两倍距离,代表前一日空头力量。只有当价格极度弱势时,才触发趋势性卖出。 alt text

背后逻辑解析

首先看一下这6个价格与前一日价格之间的关系。

反转卖出价和反转买入价 根据公式推导,发现这两个价格和前一日最高最低价没有确定的大小关系。

观察卖出价和观察买入价。 用观察卖出价 - 前一交易日最高价发现,(H+P-L)-H = P - L >0,说明观察卖出价>前一交易日最高价;同理可证,观察买入价<前一交易日最低价。

突破买入价和突破卖出价 突破买入价>观察卖出价>前一交易日最高价,可以说明突破买入价>>前一交易日最高价。做差后可以发现,突破买入价 - 前一交易日最高价 = 2[(C-L)+(H-L)]/3。

用K线形态表示:

前一交易日K线越长,下影线越长,突破买入价越高。 前一交易日K线越长,上影线越长,突破卖入价越高。

这样一来就可以解释R Breaker背后的逻辑了。 当今日的价格突破前一交易日的最高点,形态上来看会是上涨趋势,具备一定的开多仓条件,但还不够。若前一交易日的下影线越长,说明多空方博弈激烈,多方力量强大。因此可以设置更高的突破买入价,一旦突破说明多方力量稳稳地占据了上风,那么就有理由相信未来会继续上涨。同理可解释突破卖出价背后的逻辑。

持有多仓时,若标的价格持续走高,则在当天收盘之前平仓获利离场。若价格不升反降,跌破观察卖出价时,此时价格仍处于前一交易日最高价之上,继续观望。若继续下跌,直到跌破反转卖出价时,平仓止损。

持有空仓时,若标的价格持续走低,则在当天收盘之前平仓获利离场。若价格不降反升,升至观察买入价时,此时价格仍处于前一交易日最低价之下,继续观望。若继续上涨,直到升至反转买入价时,平仓止损。

策略步骤

结合这六个价位,我们可以梳理出 R-Breaker 在一个交易日内的完整决策流程。

每日开盘前:

  • 获取前一交易日的 H, L, C 数据, 计算出枢轴点 P 以及上述六个关键价位。这些价位在当天盘中是固定不变的。

盘中 - 空仓状态:

  • 做多信号: 如果实时价格向上突破 突破买入价 (bBreak),则立即开仓做多。

  • 做空信号: 如果实时价格向下突破 突破卖出价 (sBreak),则立即开仓做空。

盘中 - 持有多仓状态:

  • 反转条件 (两步):

    • “预备”阶段: 盘中的日内最高价必须曾经触及或超过 观察卖出价 (sSetup)。这个条件达成后,策略就进入了“反转预警”状态。

    • “触发”阶段: 在“预备”条件满足后,如果价格从高位回落,并且向下跌破了 反转卖出价 (sEnter)。

  • 执行动作: 立即平掉所有多仓,并反手开立空仓

盘中 - 持有空仓状态:

  • 反转条件 (两步):

    • “预备”阶段: 盘中的日内最低价必须曾经触及或低于 观察买入价 (bSetup)。

    • “触发”阶段: 在“预备”条件满足后,如果价格从低位反弹,并且向上突破了 反转买入价 (bEnter)。

  • 执行动作: 立即平掉所有空仓,并反手开立多仓

每日收盘前:

  • 无论当前持有什么仓位,也无论盈亏,在收盘前几分钟强制平掉所有仓位,确保不持仓隔夜。这是日内交易的铁律。

策略代码详解

下面的代码示例展示了如何实现 R-Breaker 交易策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# coding=utf-8
from __future__ import print_function, absolute_import
import pandas as pd
from gm.api import *
from datetime import datetime, timedelta
"""
R-Breaker是一种短线日内交易策略
根据前一个交易日的收盘价、最高价和最低价数据通过一定方式计算出六个价位,从大到小依次为:
突破买入价、观察卖出价、反转卖出价、反转买入、观察买入价、突破卖出价。以此来形成当前交易
日盘中交易的触发条件。
追踪盘中价格走势,实时判断触发条件。具体条件如下:
突破
在空仓条件下,如果盘中价格超过突破买入价,则采取趋势策略,即在该点位开仓做多。
在空仓条件下,如果盘中价格跌破突破卖出价,则采取趋势策略,即在该点位开仓做空。
反转
持多单,当日内最高价超过观察卖出价后,盘中价格出现回落,且进一步跌破反转卖出价构成的支撑线时,采取反转策略,即在该点位反手做空。
持空单,当日内最低价低于观察买入价后,盘中价格出现反弹,且进一步超过反转买入价构成的阻力线时,采取反转策略,即在该点位反手做多。
设定止损条件。当亏损达到设定值后,平仓。
注意:
1:为回测方便,本策略使用了on_bar的一分钟来计算,实盘中可能需要使用on_tick。
2:实盘中,如果在收盘的那一根bar或tick触发交易信号,需要自行处理,实盘可能不会成交。
3:本策略使用在15点收盘时全平的方式来处理不持有隔夜单的情况,实际使用中15点是无法平仓的。
"""
def init(context):
# 设置交易品种
context.symbol = 'SHFE.ag'
# 设置止损点数
context.stopLossPrice = 50
# 获取前一交易日的主力合约
startDate = get_previous_trading_date(exchange='SHFE', date=context.now.date())
continuous_contract = get_continuous_contracts(context.symbol, startDate, startDate)
context.mainContract = continuous_contract[0]['symbol']
# 获取当前时间
time = context.now.strftime('%H:%M:%S')
# 如果当前时间是非交易时间段,则直接执行algo,以防直接进入on_bar()导致context.bBreak等未定义
if '09:00:00' < time < '15:00:00' or '21:00:00' < time < '23:00:00':
algo(context)
# 如果是交易时间段,等到开盘时间确保进入algo()
schedule(schedule_func = algo, date_rule = '1d', time_rule = '09:00:00')
schedule(schedule_func = algo, date_rule = '1d', time_rule = '21:00:00')
# 订阅行情
subscribe(continuous_contract[0]['symbol'], frequency='60s', count=1)
def algo(context):
# 检查主力和约,发生变化则更换订阅
# 由于主力合约在盘后才公布,为了防止未来函数,选择上一交易日的主力合约。
startDate = get_previous_trading_date(exchange='SHFE', date=context.now.date())
contractInfo = get_continuous_contracts(context.symbol, startDate, startDate)
if context.mainContract != contractInfo[0]['symbol']:
context.mainContract = contractInfo[0]['symbol']
subscribe(context.mainContract, frequency='60s', count=1, unsubscribe_previous=True)
# 获取历史数据
data = history_n(symbol=context.mainContract, frequency='1d',
end_time=context.now, fields='high,low,open,symbol,close', count=2, df=True)
high = data['high'].iloc[0] # 前一日的最高价
low = data['low'].iloc[0] # 前一日的最低价
close = data['close'].iloc[0] # 前一日的收盘价
pivot = (high + low + close) / 3 # 枢轴点
context.bBreak = high + 2 * (pivot - low) # 突破买入价
context.sSetup = pivot + (high - low) # 观察卖出价
context.sEnter = 2 * pivot - low # 反转卖出价
context.bEnter = 2 * pivot - high # 反转买入价
context.bSetup = pivot - (high - low) # 观察买入价
context.sBreak = low - 2 * (high - pivot) # 突破卖出价
context.data = data
def on_bar(context, bars):
# 获取止损价
STOP_LOSS_PRICE = context.stopLossPrice
# 设置参数
bBreak = context.bBreak
sSetup = context.sSetup
sEnter = context.sEnter
bEnter = context.bEnter
bSetup = context.bSetup
sBreak = context.sBreak
data = context.data
# 获取现有持仓
position_long = context.account().position(symbol=context.mainContract, side=PositionSide_Long)
position_short = context.account().position(symbol=context.mainContract, side=PositionSide_Short)
# 突破策略:
if not position_long and not position_short: # 空仓条件下
if bars[0].close > bBreak:
# 在空仓的情况下,如果盘中价格超过突破买入价,则采取趋势策略,即在该点位开仓做多
order_volume(symbol=context.mainContract, volume=10, side=OrderSide_Buy,
order_type=OrderType_Market, position_effect=PositionEffect_Open) # 做多
print("空仓,盘中价格超过突破买入价: 开仓做多")
context.open_position_price = bars[0].close
elif bars[0].close < sBreak:
# 在空仓的情况下,如果盘中价格跌破突破卖出价,则采取趋势策略,即在该点位开仓做空
order_volume(symbol=context.mainContract, volume=10, side=OrderSide_Sell,
order_type=OrderType_Market, position_effect=PositionEffect_Open) # 做空
print("空仓,盘中价格跌破突破卖出价: 开仓做空")
context.open_position_price = bars[0].close
# 设置止损条件
else: # 有持仓时
# 开仓价与当前行情价之差大于止损点则止损
if (position_long and context.open_position_price - bars[0].close >= STOP_LOSS_PRICE) or \
(position_short and bars[0].close - context.open_position_price >= STOP_LOSS_PRICE):
print('达到止损点,全部平仓')
order_close_all() # 平仓
# 反转策略:
if position_long: # 多仓条件下
if data.high.iloc[1] > sSetup and bars[0].close < sEnter:
# 多头持仓,当日内最高价超过观察卖出价后,
# 盘中价格出现回落,且进一步跌破反转卖出价构成的支撑线时,
# 采取反转策略,即在该点位反手做空
order_close_all() # 平仓
order_volume(symbol=context.mainContract, volume=10, side=OrderSide_Sell,
order_type=OrderType_Market, position_effect=PositionEffect_Open) # 做空
print("多头持仓,当日内最高价超过观察卖出价后跌破反转卖出价: 反手做空")
context.open_position_price = bars[0].close
elif position_short: # 空头持仓
if data.low.iloc[1] < bSetup and bars[0].close > bEnter:
# 空头持仓,当日内最低价低于观察买入价后,
# 盘中价格出现反弹,且进一步超过反转买入价构成的阻力线时,
# 采取反转策略,即在该点位反手做多
order_close_all() # 平仓
order_volume(symbol=context.mainContract, volume=10, side=OrderSide_Buy,
order_type=OrderType_Market, position_effect=PositionEffect_Open) # 做多
print("空头持仓,当日最低价低于观察买入价后超过反转买入价: 反手做多")
context.open_position_price = bars[0].close
if context.now.hour == 14 and context.now.minute == 59:
order_close_all()
print('全部平仓')
if __name__ == '__main__':
run(strategy_id='strategy_id',
filename='main.py',
mode=MODE_BACKTEST,
token='token_id',
backtest_start_time='2019-10-1 15:00:00',
backtest_end_time='2020-04-16 15:00:00',
backtest_initial_cash=1000000,
backtest_commission_ratio=0.0001,
backtest_slippage_ratio=0.0001)