返回列表 发布新帖

经典期货日内策略R_Breaker在QMT的实现示例

1385 1
发表于 2023-12-18 16:30:42 | 显示全部楼层 阅读模式
R-Breaker策略由Richard Saidenberg在1994年开发,这种策略在《Futures Truth》的评选中连续十五年位列最优策略前十名。不同于其他策略,R-Breaker结合了趋势跟随和反转两种交易方式,可以在趋势行情中实现盈利,并且在市场反转时及时止盈并改变交易方向。因此,它提供了相对较多的交易机会,至今依然在全球范围内广泛使用和研究。

R-Breaker是一种日内回转交易策略,属于短线交易类型。日内回转交易即在同一天内买入或卖出标的,然后再将其卖出或买入。这种策略利用标的的短期波动来获取利润,是快进快出、投机性强的交易方式,适合短线投资者。重要的是,在交易日结束时所有仓位需要平清,不持有过夜仓位,所以并不适用于股票市场。

R-Breaker策略主要包括反转和趋势跟随两部分。当没有持仓时,它执行趋势跟随策略; 当有持仓时,等待反转信号以改变交易方向。

由于中国A股市场实行的是“T+1”交易制度,这里我们以期货市场为例来演示R-Breaker策略。

反转和趋势突破的价格点是根据前一交易日的收盘价、最高价和最低价计算得出的,它们从小到大依次是:突破买入价、观察卖出价、反转卖出价、反转买入价、观察买入价和突破卖出价。具体的计算公式如下:

[td]
指标计算方法
观察卖出价 = High + 0.35 * (Close – Low)
观察买入价 = Low – 0.35* (High – Close)
反转卖出价 = 1.07 / 2 (High + Low) – 0.07 Low
反转买入价 = 1.07 / 2 (High + Low) – 0.07 High
突破买入价 = 观察卖出价 + 0.25 * (观察卖出价 – 观察买入价)
突破卖出价 = 观察买入价 – 0.25 * (观察卖出价 – 观察买入价)

在R-Breaker策略中,High、Close、Low分别代表昨日最高价、昨日收盘价和昨日最低价。根据它们,我们可以计算出六个价格点: 突破买入价、观察卖出价、反转卖出价、反转买入价、观察买入价和突破卖出价。这些价格点从大到小排列。

由此可见,R-Breaker策略使用前一天的价格数据来绘制类似于网格的价格线,并且每天更新这些价格线。这些价格线在技术分析中相当于支撑位和阻力位,并且这两者的角色可能会互换。比如,当价格成功突破上方阻力位时,该阻力位可能会变为新的支撑位;同样,当价格成功突破下方支撑位时,该支撑位可能会变为新的阻力位。

在实际交易中,这些支撑位和阻力位对交易者指明了开平仓的方向以及买卖的具**置。交易者可以根据盘中价格、中心价、阻力位、支撑位进行灵活设置开平仓条件。同时,他们也可以依据这些网格价格线进行头寸管理,如增减仓位。


image.png


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

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

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

这种策略可以适应大趋势与震荡两种市场情况,并且具有明确的停损规则和退出机制。

以下是策略源码,仅做演示分享使用
  1. # coding:gbk

  2. """

  3. R_break策略示例

  4. """


  5. import pandas as pd
  6. import numpy as np
  7. import datetime

  8. class _a():
  9.         pass
  10.         
  11. A = _a()


  12. def init(C):
  13.         
  14.         A.symbol = "rb00.SF" # 品种
  15.         
  16.         C.accID = "test"
  17.         # C.accID = account # 实盘用这个
  18.         A.period = "5m" # 数据周期
  19.         A.lots = 1 # 买卖手数
  20.         
  21.         

  22. def after_init(C):
  23.         # 不确定有没有本地数据的话,下一次本地数据
  24.         if 1:
  25.                 print("正在下载数据")
  26.                 download_history_data(A.symbol, A.period,"","")
  27.                 print("数据下载完成,程序继续")
  28.         
  29.         
  30.         # 开始获取数据
  31.         data = C.get_market_data_ex([], [A.symbol], period = "1d")
  32.         close_df = get_df_ex(data, "close")
  33.         open_df = get_df_ex(data, "open")
  34.         low_df = get_df_ex(data, "low")
  35.         high_df = get_df_ex(data,"high")
  36.         
  37.         pivot_df = (high_df + close_df + low_df) / 3 # 计算枢纽点
  38.         
  39.         A.bBreak = high_df + 2 * (pivot_df - low_df) # 突破买入价
  40.         A.sSetup = pivot_df + (high_df - low_df) # 观察卖出价
  41.         
  42.         A.sEnter = 2 * pivot_df  - low_df # 反转卖出价
  43.         A.bEnter = 2 * pivot_df - high_df  # 反转买入价
  44.         A.bSetup = pivot_df - (high_df - low_df)  # 观察买入价
  45.         A.sBreak = low_df - 2 * (high_df - pivot_df)  # 突破卖出价
  46.         
  47.         A.bBreak = A.bBreak.shift(1)
  48.         A.sSetup = A.sSetup.shift(1)
  49.         A.sEnter = A.sEnter.shift(1)
  50.         A.bEnter = A.bEnter.shift(1)
  51.         A.bSetup = A.bSetup.shift(1)
  52.         A.sBreak = A.sBreak.shift(1)
  53.         

  54.         
  55. def handlebar(C):
  56.         backTestTime = timetag_to_datetime(C.get_bar_timetag(C.barpos),"%Y%m%d%H%M%S")
  57.         
  58.         print(backTestTime) # 打印当前bar的时间,觉得眼花可以注释掉
  59.         
  60.         bBreak = A.bBreak.loc[backTestTime[:8],A.symbol]
  61.         sBreak = A.sBreak.loc[backTestTime[:8],A.symbol]
  62.         sSetup = A.sSetup.loc[backTestTime[:8],A.symbol]
  63.         sEnter = A.sEnter.loc[backTestTime[:8],A.symbol]
  64.         bSetup = A.bSetup.loc[backTestTime[:8],A.symbol]
  65.         bEnter = A.bEnter.loc[backTestTime[:8],A.symbol]
  66.         
  67.         holdings = get_Future_holdings(C.accID,symbol = A.symbol) #获取账户持仓信息
  68.         
  69.         position_long = holdings.get("多头数量",0)
  70.         position_short = holdings.get("空头数量",0)
  71.         
  72.         # 获取行情
  73.         data = C.get_market_data_ex([],[A.symbol],period = A.period, end_time = backTestTime, count = 2)
  74.         last_price = data[A.symbol].iloc[-1]["close"]
  75.         high = data[A.symbol].iloc[-1]["high"]
  76.         low = data[A.symbol].iloc[-1]["low"]
  77.         
  78.         # print(last_price,bBreak)
  79.         
  80.         if position_long == 0 and position_short == 0:
  81.                 if last_price > bBreak:
  82.                         # 在空仓的情况下,如果盘中价格超过突破买入价,则采取趋势策略,即在该点位开仓做多
  83.                         my_passorder(C,A.symbol,"buy_open",A.lots)# 做多
  84.                         print("空仓,盘中价格超过突破买入价: 开仓做多")
  85.                 elif last_price < sBreak:
  86.                         my_passorder(C,A.symbol,"sell_open",A.lots) # 做空
  87.                         print("空仓,盘中价格跌破突破卖出价: 开仓做空")
  88.         else:
  89.                
  90.                 # 反转的情况
  91.                 if position_long:
  92.                         
  93.                         if high > sSetup and last_price < sEnter:
  94.                                 # 多头持仓,当日内最高价超过观察卖出价后,
  95.                                 # 盘中价格出现回落,且进一步跌破反转卖出价构成的支撑线时,
  96.                                 # 采取反转策略,即在该点位反手做空
  97.                                 my_passorder(C,A.symbol, "sell_close", position_long) # 平多
  98.                                 my_passorder(C,A.symbol,"sell_open",A.lots) # 做空
  99.                                 print("盘中价格出现回落:反转做空")
  100.                 elif position_short:
  101.                         if low < bSetup and last_price > bEnter:
  102.                                 # 空头持仓,当日内最低价低于观察买入价后,
  103.                                 # 盘中价格出现反弹,且进一步超过反转买入价构成的阻力线时,
  104.                                 # 采取反转策略,即在该点位反手做多
  105.                                 
  106.                                 my_passorder(C,A.symbol, "buy_close", position_short) # 平空
  107.                                 my_passorder(C,A.symbol,"buy_open",A.lots)# 做多
  108.                                 print("盘中价格出现反弹:反转做多")
  109.                                 
  110.         if backTestTime[-6:] == "144500":
  111.                 # 收盘前平掉所有仓位
  112.                 if position_long:
  113.                         my_passorder(C,A.symbol, "sell_close", position_long) # 平多
  114.                 if position_short:
  115.                         my_passorder(C,A.symbol, "buy_close", position_short) # 平空
  116.                 print("收盘平仓")
  117.         return


  118. def get_df_ex(data:dict,field:str) -> pd.DataFrame:
  119.     '''
  120.     ToDo:用于在使用get_market_data_ex的情况下,取到标准df
  121.    
  122.     Args:
  123.         data: get_market_data_ex返回的dict
  124.         field: ['time', 'open', 'high', 'low', 'close', 'volume','amount', 'settelementPrice', 'openInterest', 'preClose', 'suspendFlag']
  125.         
  126.     Return:
  127.         一个以时间为index,标的为columns的df
  128.    
  129.     '''
  130.     _index = data[list(data.keys())[0]].index.tolist()
  131.     _columns = list(data.keys())
  132.     df = pd.DataFrame.from_dict({col: data[col][field] for col in _columns})
  133.     return df


  134. def get_Future_holdings(accid,symbol = None):
  135.     '''
  136.     针对期货返回持仓的奇葩结构做处理
  137.     Arg:
  138.         accondid:账户id
  139.         symbol: 品种,不填默认返会全部持仓
  140.             
  141.     return:
  142.         {股票名:{'手数':int,"持仓成本":float,'浮动盈亏':float,"可用余额":int}}
  143.     '''
  144.     datatype = "FUTURE"
  145.    
  146.     PositionInfo_dict = {}
  147.    
  148.     Long_dict={}
  149.    
  150.     Short_dict={}
  151.    
  152.     resultlist = get_trade_detail_data(accid,datatype,'POSITION')
  153.    
  154.     for obj in resultlist:
  155.         #防除零
  156.         if obj.m_nVolume == 0:
  157.             continue
  158.         if obj.m_nDirection == 48:
  159.             if not Long_dict.get(obj.m_strInstrumentID+"."+obj.m_strExchangeID):
  160.                 Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID] = {
  161.                 "多头数量":obj.m_nVolume,
  162.                 "多头成本":obj.m_dOpenPrice,
  163.                
  164.                 "浮动盈亏":obj.m_dFloatProfit,
  165.                 "保证金占用":obj.m_dMargin
  166.                 }
  167.             else:
  168.                     
  169.                     Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["多头数量"] += obj.m_nVolume
  170.                     # 算浮动盈亏
  171.                     Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["浮动盈亏"] += obj.m_dFloatProfit
  172.                     # 算保证金占用
  173.                     Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["保证金占用"] += obj.m_dMargin
  174.                     # 算多头成本
  175.                     Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["多头成本"] = (
  176.                         Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["多头成本"] * \
  177.                         (Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["多头数量"] - obj.m_nVolume) + \
  178.                         (obj.m_dOpenPrice * obj.m_nVolume)
  179.                     )/Long_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["多头数量"]
  180.                     
  181.         elif obj.m_nDirection == 49:
  182.             if not Short_dict.get(obj.m_strInstrumentID+"."+obj.m_strExchangeID):
  183.                 Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID] = {
  184.                 "空头数量":obj.m_nVolume  ,
  185.                 "空头成本":obj.m_dOpenPrice ,
  186.                 "浮动盈亏":obj.m_dFloatProfit,
  187.                 "保证金占用":obj.m_dMargin
  188.                 }
  189.             else:
  190.                 Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["空头数量"] += obj.m_nVolume
  191.                 # 算浮动盈亏
  192.                 Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["浮动盈亏"] += obj.m_dFloatProfit
  193.                 # 算保证金占用
  194.                 Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["保证金占用"] += obj.m_dMargin
  195.                 # 计算空头成本
  196.                 Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["空头成本"] = (
  197.                     Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["空头成本"] * \
  198.                     (Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["空头数量"] - obj.m_nVolume) + \
  199.                     (obj.m_dOpenPrice * obj.m_nVolume)
  200.                 )/Short_dict[obj.m_strInstrumentID+"."+obj.m_strExchangeID]["空头数量"]
  201.         
  202.    
  203.     for _symbol in set(list(Long_dict.keys()) + list(Short_dict.keys())):
  204.         
  205.         PositionInfo_dict[_symbol] = {
  206.         "多头数量":Long_dict[_symbol]["多头数量"] if Long_dict.get(_symbol) else 0 ,
  207.         
  208.         "空头数量":Short_dict[_symbol]["空头数量"] if Short_dict.get(_symbol) else 0 ,
  209.         
  210.         "多头成本":Long_dict[_symbol]["多头成本"] if Long_dict.get(_symbol) else None ,
  211.         
  212.         "空头成本":Short_dict[_symbol]["空头成本"] if Short_dict.get(_symbol) else None,
  213.         
  214.         "净持仓" : Long_dict.get(_symbol,{}).get("多头数量",0) -  Short_dict.get(_symbol,{}).get("空头数量",0),
  215.         
  216.         "浮动盈亏": Long_dict.get(_symbol,{}).get("浮动盈亏",0) +  Short_dict.get(_symbol,{}).get("浮动盈亏",0),
  217.         
  218.         "保证金占用": Long_dict.get(_symbol,{}).get("保证金占用",0) +  Short_dict.get(_symbol,{}).get("保证金占用",0)
  219.         }
  220.         
  221.     if symbol:
  222.         return PositionInfo_dict.get(symbol,{})
  223.     else :
  224.         return PositionInfo_dict

  225. def my_passorder(C,Futuer:str,opentype:str,lots:int,price = None,m_strRemark = '系统备注'):
  226.     '''
  227.    
  228.     Args:
  229.         C: ContextInfo \n
  230.         Futuer: 期货代码 \n
  231.         
  232.         opentype:
  233.                 'buy_open' 开多\n
  234.                 'sell_open' 开空\n
  235.                 'sell_close' 平多\n
  236.                 'buy_close' 平空\n
  237.         lots: 手
  238.         price: 下单价格,不指定时默认按市价下单
  239.         m_strRemark = '系统备注' 用于自定义寻找orderID
  240.     '''
  241.     Futuer_ExchangeID = Futuer.split(".")[1]
  242.     opentype = opentype #买卖方向
  243.     op =1101  #手数
  244.     # 期货区分开平
  245.     if opentype == "buy_open":
  246.         opType = 0
  247.     elif opentype == "sell_open":
  248.         opType = 3
  249.     elif opentype == "sell_close":
  250.         opType = 7
  251.     elif opentype == "buy_close":
  252.         opType = 9

  253.     volumex = lots
  254.     price = 0 if not price else price # price参数必须存在
  255.     if Futuer_ExchangeID == "SF":
  256.         prType = 14 if not price else 11 # 对于上期所,若不指定价格,则默认按对手价下单
  257.     elif Futuer_ExchangeID == "DF" or Futuer_ExchangeID == "ZF":
  258.         prType = 12 if not price else 11 # 对于大商所和郑商所,若不指定价格,则默认按涨跌停价下单
  259.     else:
  260.         prType = 14 if not price else 11 # 对于其他所,若不指定价格,则默认按对手价下单

  261.     print(f'{Futuer} 新委托信息 方向{opentype} 价格{price} 量{volumex}')
  262.     #print(f"opType:{opType} , op:{op} , C.accID{C.accID} , stock{stock} , prType{prType} , price{price} , volumex{volumex}")
  263.     passorder(opType, op, C.accID,Futuer, prType, price, volumex,'交易注释',1,'{}'.format(m_strRemark), C)
  264.     print(f'委托发送完成')
复制代码


评论1

任志鹏
发表于 2023-12-18 17:00:31 | 显示全部楼层
:qiang::qiang::qiang:

回复

您需要登录后才可以回帖 登录 | 立即注册

客服专线

400-080-8112

用思考的速度交易,用真诚的态度合作,我们是认真的!
  • 关注公众号
  • 添加微信客服
Copyright © 2001-2025 迅投QMT社区 版权所有 All Rights Reserved. 蜀ICP备19002686号-2
关灯 快速发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表