R-Breaker是个经典的具有长生命周期的日内模型。
类型:日内趋势追踪+反转策略
周期:1分钟、5分钟
根据前一个交易日的收盘价、最高价和最低价数据通过一定方式计算出六个价位,
从大到小依次为:
突破买入价(buy_break)、观察卖出价(sell_setup)、
反转卖出价(sell_enter)、反转买入价(buy_enter)、
观察买入价(buy_setup)、突破卖出价(sell_break)
以此来形成当前交易日盘中交易的触发条件。
交易规则:
反转:
持多单,当日内最高价超过观察卖出价后,盘中价格出现回落,且进一步跌破反转卖出价构成的支撑线时,采取反转策略,即在该点位反手做空;
持空单,当日内最低价低于观察买入价后,盘中价格出现反弹,且进一步超过反转买入价构成的阻力线时,采取反转策略,即在该点位反手做多;
突破:
在空仓的情况下,如果盘中价格超过突破买入价,则采取趋势策略,即在该点位开仓做多;
在空仓的情况下,如果盘中价格跌破突破卖出价,则采取趋势策略,即在该点位开仓做空;
2. 代码解读:
2.1 R_Breaker.ini
- [strategy]
- ;td_addr=localhost:8001
- username=
- password=
- strategy_id=
- ;模拟状态
- mode=4
- ;订阅代码注意及时更新
- subscribe_symbols=CFFEX.IF1703.tick,CFFEX.IF1703.bar.60
- [backtest]
- start_time=2017-06-15 09:00:00
- end_time=2017-07-07 15:00:00
- initial_cash=10000000
- transaction_ratio=1
- commission_ratio=0
- slippage_ratio=0
- [para]
- trade_symbol=CFFEX.IF1703
- begin_time = 09:30:00
- end_time = 14:55:00
- observe_size =0.35
- reversal_size = 0.07
- break_size = 0.25
- # encoding: utf-8
- from gmsdk.api import StrategyBase
- from gmsdk import md
- from gmsdk.enums import *
- import arrow
- import time
- # 每次开仓量
- OPEN_VOL = 5
- class R_Breaker(StrategyBase):
- def __init__(self, *args, **kwargs):
- super(R_Breaker, self).__init__(*args, **kwargs)
- self.__get_param()
- self.__init_data()
- def __get_param(self):
- '''
- 获取配置参数
- '''
- self.trade_symbol = self.config.get('para', 'trade_symbol')
- pos = self.trade_symbol.find('.')
- # 策略的一些阀值
- self.exchange = self.trade_symbol[:pos]
- self.sec_id = self.trade_symbol[pos + 1:]
- self.observe_size = self.config.getfloat('para', 'observe_size')
- self.reversal_size = self.config.getfloat('para', 'reversal_size')
- self.break_size = self.config.getfloat('para', 'break_size')
- # 交易开始和结束时间
- FMT = '%sT%s'
- today = arrow.now().date()
- begin_time = self.config.get('para', 'begin_time')
- et = FMT % (today.isoformat(), begin_time)
- self.begin_trading = arrow.get(et).replace(tzinfo='local').timestamp
- end_time = self.config.get('para', 'end_time')
- et = FMT % (today.isoformat(), end_time)
- self.end_trading = arrow.get(et).replace(tzinfo='local').timestamp
- print((begin_time,end_time))
- print("start time %s, end time %s" % (self.begin_trading, self.end_trading))
- def __init_data(self):
- '''
- 提取数据,计算价位
- '''
- prev_dailybar = self.get_last_dailybars(self.trade_symbol)
- if len(prev_dailybar) < 1:
- return
- self.prev_high = prev_dailybar[0].high
- self.prev_low = prev_dailybar[0].low
- self.prev_close = prev_dailybar[0].close
- self.high = self.prev_close
- self.low = self.prev_close
- self.close = self.prev_close
- # 观察卖出价
- self.sell_setup = self.prev_high + self.observe_size * (self.prev_close - self.prev_low)
- print('sell_setup price: %s' % self.sell_setup)
- # 反转卖出价
- self.sell_enter = (1 + self.reversal_size) / 2 * (
- self.prev_high + self.prev_low) - self.reversal_size * self.prev_low
- print('sell_enter price:%s' % self.sell_enter)
- # 反转买入价
- self.buy_enter = (1 + self.reversal_size) / 2 * (
- self.prev_high + self.prev_low) - self.reversal_size * self.prev_high
- print('buy_enter price:%s' % self.buy_enter)
- # 观察买入价
- self.buy_setup = self.prev_low - self.observe_size * (self.prev_high - self.prev_close)
- print('buy_setup:%s' % self.buy_setup)
- # 突破买入价
- self.buy_break = self.sell_setup + self.break_size * (self.sell_setup - self.buy_setup)
- print('buy_break:%s' % self.buy_break)
- # 突破卖出价
- self.sell_break = self.buy_setup + self.break_size * (self.sell_setup - self.buy_setup)
- print('sell_break:%s' % self.sell_break)
- self.bid_holding = 0.0
- position = self.get_position(self.exchange, self.sec_id, OrderSide_Bid);
- if position is not None:
- self.bid_holding = position.volume
- print((self.bid_holding))
- self.ask_holding = 0.0
- position = self.get_position(self.exchange, self.sec_id, OrderSide_Ask)
- if position is not None:
- self.ask_holding = position.volume
- def on_tick(self, tick):
- '''
- tick行情事件
- '''
- # 即时获取当天高、低、现价
- self.high = tick.high
- self.low = tick.low
- self.close = tick.last_price
- def on_bar(self, bar):
- '''
- bar周期数据事件
- '''
- x = time.localtime(bar.utc_time)
- bartime = (time.strftime('%H:%M:%S',x))
- y = time.localtime(self.begin_trading)
- begin_trading = (time.strftime('%H:%M:%S',y))
- z = time.localtime(self.end_trading)
- end_trading = (time.strftime('%H:%M:%S',z))
- if bartime > begin_trading and bartime < end_trading:
- if self.close > self.buy_break and self.bid_holding < 1:
- # 空仓做多
- self.open_long(self.exchange, self.sec_id, self.close, OPEN_VOL)
- print('open long: price %s, vol %s' % (self.close, OPEN_VOL))
- elif self.close < self.sell_break and self.ask_holding < 1:
- # 空仓做空
- self.open_short(self.exchange, self.sec_id, self.close, OPEN_VOL)
- print('open short: price %s, vol %s' % (self.close, OPEN_VOL))
- elif self.bid_holding > 0 and self.high > self.sell_setup and self.close < self.sell_enter:
- # 多单反转
- self.close_long(self.exchange, self.sec_id, self.close, self.bid_holding)
- print('close long: price %s, vol %s' % (self.close, self.bid_holding))
- self.open_short(self.exchange, self.sec_id, self.close, OPEN_VOL)
- print('Reverse open short: price %s, vol %s' % (self.close, OPEN_VOL))
- elif self.ask_holding > 0 and self.low < self.buy_setup and self.close > self.buy_enter:
- # 空单反转
- self.close_short(self.exchange, self.sec_id, self.close, self.ask_holding)
- print('close short: price %s, vol %s' % (self.close, self.ask_holding))
- self.open_long(self.exchange, self.sec_id, self.close, OPEN_VOL)
- print('Reverse open long: price %s, vol %s' % (self.close, OPEN_VOL))
- if bartime > end_trading:
- # 日内平仓
- if self.bid_holding > 0:
- self.close_long(self.exchange, self.sec_id, 0, self.bid_holding)
- elif self.ask_holding > 0:
- self.close_short(self.exchange, self.sec_id, 0, self.ask_holding)
- def on_execrpt(self, rpt):
- '''
- 委托回报事件回调
- '''
- if rpt.exec_type != ExecType_Trade:
- return
- # 从成交回报累计持仓量
- if PositionEffect_Open == rpt.position_effect and OrderSide_Bid == rpt.side:
- self.bid_holding += rpt.volume
- elif PositionEffect_Open == rpt.position_effect and OrderSide_Ask == rpt.side:
- self.ask_holding += rpt.volume
- print ('self.ask_holding')
- elif PositionEffect_Close == rpt.position_effect and OrderSide_Bid == rpt.side:
- self.bid_holding -= rpt.volume
- elif PositionEffect_Close == rpt.position_effect and OrderSide_Ask == rpt.side:
- self.ask_holding -= rpt.volume
- if __name__ == '__main__':
- r_breaker = R_Breaker(config_file='R_Breaker.ini')
- ret = r_breaker.run()
- print(r_breaker.get_strerror(ret))
3.1 Python标准函数:
功能 | 函数原型 | 参数 | 返回值 | ||
参数名 | 含义 | ||||
find | 检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,如果包含子字符串返回开始的索引值,否则返回-1。 | str.find(str, beg=0, end=len(string)) | str | 指定检索的字符串。 | 如果包含子字符串返回开始的索引值,否则返回-1。 |
beg | 开始索引,默认为0。 | ||||
end | 结束索引,默认为字符串的长度。 | ||||
arrow | Arrow是一个专门处理时间和日期的轻量级Python库,它提供了一种合理、智能的方式来创建、操作、格式化、转换时间和日期。 | 转换为Arrow对象后,我们可以很方便的获取我们想要的各种时间数据,通过year、month、day、hour、minute、second、week等属性。 | |||
time.localtime() | 将一个时间戳转换为当前时区的struct_time。 | time.localtime([secs]) | secs | 若未提供,则以当前时间为准。 | |
time.gmtime() | 是将一个时间戳转换为UTC时区(0时区)的struct_time。 | time.gmtime([secs]) | secs | 若未提供,则以当前时间为准。 | |
time.time | 返回当前时间的时间戳。 | time.time() | |||
time.mktime | 将一个struct_time转化为时间戳。 | time.mktime() | |||
time.sleep | 线程推迟指定的时间运行。单位为秒。 | time.sleep() | |||
time.asctime | 把一个表示时间的元组或者struct_time表示为这种形式:'Sun Jun 20 23:21:05 1993'。 | time.asctime([t]) | t | 如果没有参数,将会将time.localtime()作为参数传入。 | |
time.ctime | 把一个时间戳(按秒计算的浮点数)转化time.asctime()的形式。 | time.ctime | 如果参数未给或者为None的时候,将会默认time.time() 为参数。它的作用相当于time.asctime(time.localtime(secs))。 | ||
time.clock | 这个需要注意,在不同的系统上含义不同。在UNIX系统上,它返回的是“进程时间”,它是用秒表示的浮点数(时间戳)。而在WINDOWS中,第一次调用,返回的是进程运行的实际时间。而第二次之后的调用是自第一次调用以后到现在的运行时间。(实际上是以WIN32上QueryPerformanceCounter()为基础,它比毫秒表示更为精确) | time.clock() | |||
len | 返回对象(字符、列表、元组等)长度或项目个数。 | len(s) | s | 对象 | 返回对象长度。 |
3.2 掘金接口函数:
功能 | 函数原型 | 参数 | 返回值 | |||
参数名 | 类型 | 说明 | ||||
on_bar | 响应Bar事件,收到Bar数据后本函数被调用。 | on_bar(bar) | bar | bar | bar数据 | 无 |
on_tick | 响应Tick事件,收到Tick数据后本函数被调用。 | on_tick(tick) | tick | tick | tick数据 | 无 |
on_execrpt | 响应委托执行回报事件,收到Execution数据后本函数被调用。 | on_execrpt(rpt) | rpt | ExecRpt | 无 | |
get_last_dailybars | 提取最新1条DailyBar数据,支持单个代码提取或多个代码组合提取。策略类和行情服务类都提供该接口。 | get_last_dailybars(symbol_list) | symbol | string | 证券代码, 带交易所代码以确保唯一,如SHSE.600000,同时支持多只代码 | DailyBar列表 |
get_positions | 查询当前策略指定symbol(由交易所代码和证券ID组成)和买卖方向的持仓信息。策略类和交易服务类都提供该接口。 | get_position(exchange, sec_id, side); | exchange | string | 交易所代码 | Position对象,持仓信息 |
sec_id | string | 证券代码 | ||||
side | int | 买卖方向 | ||||
open_long | 异步开多仓,以参数指定的symbol、价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口。 | open_long(exchange, sec_id, price, volume) | exchange | string | 交易所代码, 如上交所SHSE | 委托下单生成的Order对象 |
sec_id | string | 证券代码,如浦发银行600000 | ||||
price | float | 委托价,如果price=0,为市价单,否则为限价单 | ||||
volume | float | 委托量 | ||||
close_short | 异步平空仓接口,以参数指定exchange, 证券代码sec_id, 价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口。 | close_short(exchange, sec_id, price, volume) | exchange | string | 交易所代码, 如上交所SHSE | 返回委托下单生成的Order对象 |
sec_id | string | 证券代码,如浦发银行600000 | ||||
price | float | 委托价,如果price=0,为市价单,否则为限价单 | ||||
volume | float | 委托量 | ||||
close_long | 异步平多仓接口,以参数指定exchange, 证券代码sec_id, 价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口。 | close_long(exchange, sec_id, price, volume) | exchange | string | 交易所代码, 如上交所SHSE | 委托下单生成的Order对象 |
sec_id | string | 证券代码,如浦发银行600000 | ||||
price | float | 委托价,如果price=0,为市价单,否则为限价单 | ||||
volume | float | 平仓量 | ||||
open_short | 异步开空仓接口,以参数指定的exchange, 证券代码sec_id, 价和量下单。如果价格为0,为市价单,否则为限价单。策略类和交易服务类都提供该接口。 | open_short(exchange, sec_id, price, volume) | exchange | string | 交易所代码, 如上交所SHSE | 委托下单生成的Order对象 |
sec_id | string | 证券代码,如浦发银行600000 | ||||
price | float | 委托价,如果price=0,为市价单,否则为限价单 | ||||
volume | float | 平仓量 |
4. 金融术语
R-Breaker是个经典的具有长生命周期的日内模型。曾14年排名Future Trust杂志年度前10最赚钱的策略。R-Breaker交易规则示意图如下图: