[Add]实现K线合成器功能,自动管理合成K线相关的逻辑

This commit is contained in:
vn.py 2017-09-25 13:51:24 +08:00
parent eba153f3da
commit 553cf002d8
8 changed files with 325 additions and 276 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@ from vnpy.trader.app.ctaStrategy.ctaBacktesting import BacktestingEngine, MINUTE
if __name__ == '__main__':
from vnpy.trader.app.ctaStrategy.strategy.strategyAtrRsi import AtrRsiStrategy
from vnpy.trader.app.ctaStrategy.strategy.strategyKingKeltner import KkStrategy
# 创建回测引擎
engine = BacktestingEngine()
@ -32,8 +32,8 @@ if __name__ == '__main__':
engine.setDatabase(MINUTE_DB_NAME, 'IF0000')
# 在引擎中创建策略对象
d = {'atrLength': 11}
engine.initStrategy(AtrRsiStrategy, d)
d = {}
engine.initStrategy(KkStrategy, d)
# 开始跑回测
engine.runBacktesting()

View File

@ -5,8 +5,9 @@
'''
from vnpy.trader.vtConstant import *
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.app.ctaStrategy.ctaBase import *
from .ctaBase import *
########################################################################
@ -303,3 +304,116 @@ class TargetPosTemplate(CtaTemplate):
vtOrderID = self.short(shortPrice, abs(posChange))
self.orderList.append(vtOrderID)
########################################################################
class BarManager(object):
"""
K线合成器支持
1. 基于Tick合成1分钟K线
2. 基于1分钟K线合成X分钟K线X可以是23510153060
"""
#----------------------------------------------------------------------
def __init__(self, onBar, xmin=0, onXminBar=None):
"""Constructor"""
self.bar = None # 1分钟K线对象
self.onBar = onBar # 1分钟K线回调函数
self.xminBar = None # X分钟K线对象
self.xmin = xmin # X的值
self.onXminBar = onXminBar # X分钟K线的回调函数
self.lastTick = None # 上一TICK缓存对象
#----------------------------------------------------------------------
def updateTick(self, tick):
"""TICK更新"""
newMinute = False # 默认不是新的一分钟
# 尚未创建对象
if not self.bar:
self.bar = VtBarData()
newMinute = True
# 新的一分钟
elif self.bar.datetime.minute != tick.datetime.minute:
# 生成上一分钟K线的时间戳
self.bar.datetime = self.bar.datetime.replace(second=0, microsecond=0) # 将秒和微秒设为0
self.bar.date = self.bar.datetime.strftime('%Y%m%d')
self.bar.time = self.bar.datetime.strftime('%H:%M:%S.%f')
# 推送已经结束的上一分钟K线
self.onBar(self.bar)
# 创建新的K线对象
self.bar = VtBarData()
newMinute = True
# 初始化新一分钟的K线数据
if newMinute:
self.bar.vtSymbol = tick.vtSymbol
self.bar.symbol = tick.symbol
self.bar.exchange = tick.exchange
self.bar.open = tick.lastPrice
self.bar.high = tick.lastPrice
self.bar.low = tick.lastPrice
# 累加更新老一分钟的K线数据
else:
self.bar.high = max(self.bar.high, tick.lastPrice)
self.bar.low = min(self.bar.low, tick.lastPrice)
# 通用更新部分
self.bar.close = tick.lastPrice
self.bar.datetime = tick.datetime
self.bar.openInterest = tick.openInterest
if self.lastTick:
self.bar.volume += (tick.volume - self.lastTick.volume) # 当前K线内的成交量
# 缓存Tick
self.lastTick = tick
#----------------------------------------------------------------------
def updateBar(self, bar):
"""1分钟K线更新"""
newX = False # 默认不是新的X分钟
# 尚未创建对象
if not self.xminBar:
self.xminBar = VtBarData()
newX = True
# X分钟已经走完
elif not bar.datetime.minute % self.xmin: # 可以用X整除
# 生成上一X分钟K线的时间戳
self.xminBar.datetime = self.xminBar.datetime.replace(second=0, microsecond=0) # 将秒和微秒设为0
self.xminBar.date = self.xminBar.datetime.strftime('%Y%m%d')
self.xminBar.time = self.xminBar.datetime.strftime('%H:%M:%S.%f')
# 推送
self.onXminBar(self.xminBar)
# 创建新的K线
self.xminBar = VtBarData()
newX = True
# 初始化K线数据
if newX:
self.xminBar.vtSymbol = bar.vtSymbol
self.xminBar.symbol = bar.symbol
self.xminBar.exchange = bar.exchange
self.xminBar.open = bar.open
self.xminBar.high = bar.high
self.xminBar.low = bar.low
# 累加老K线
else:
self.xminBar.high = max(self.xminBar.high, bar.high)
self.xminBar.low = min(self.xminBar.low, bar.low)
# 通用部分
self.xminBar.close = bar.close
self.xminBar.datetime = bar.datetime
self.xminBar.openInterest = bar.openInterest
self.xminBar.volume += int(bar.volume)

View File

@ -15,7 +15,7 @@ import numpy as np
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.vtConstant import EMPTY_STRING
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate, BarManager
########################################################################
@ -34,9 +34,6 @@ class AtrRsiStrategy(CtaTemplate):
fixedSize = 1 # 每次交易的数量
# 策略变量
bar = None # K线对象
barMinute = EMPTY_STRING # K线当前的分钟
bufferSize = 100 # 需要缓存的数据的大小
bufferCount = 0 # 目前已经缓存了的数据的计数
highArray = np.zeros(bufferSize) # K线最高价的数组
@ -82,6 +79,9 @@ class AtrRsiStrategy(CtaTemplate):
"""Constructor"""
super(AtrRsiStrategy, self).__init__(ctaEngine, setting)
# 创建K线合成器对象
self.bm = BarManager(self.onBar)
# 注意策略类中的可变对象属性通常是list和dict等在策略初始化时需要重新创建
# 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险,
# 策略类中的这些可变对象属性可以选择不写全都放在__init__下面写主要是为了阅读
@ -118,35 +118,7 @@ class AtrRsiStrategy(CtaTemplate):
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送必须由用户继承实现"""
# 计算K线
tickMinute = tick.datetime.minute
if tickMinute != self.barMinute:
if self.bar:
self.onBar(self.bar)
bar = VtBarData()
bar.vtSymbol = tick.vtSymbol
bar.symbol = tick.symbol
bar.exchange = tick.exchange
bar.open = tick.lastPrice
bar.high = tick.lastPrice
bar.low = tick.lastPrice
bar.close = tick.lastPrice
bar.date = tick.date
bar.time = tick.time
bar.datetime = tick.datetime # K线的时间设为第一个Tick的时间
self.bar = bar # 这种写法为了减少一层访问,加快速度
self.barMinute = tickMinute # 更新当前的分钟
else: # 否则继续累加新的K线
bar = self.bar # 写法同样为了加快速度
bar.high = max(bar.high, tick.lastPrice)
bar.low = min(bar.low, tick.lastPrice)
bar.close = tick.lastPrice
self.bm.updateTick(tick)
#----------------------------------------------------------------------
def onBar(self, bar):

View File

@ -8,7 +8,7 @@ from datetime import time
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.vtConstant import EMPTY_STRING
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate, BarManager
########################################################################
@ -25,8 +25,6 @@ class DualThrustStrategy(CtaTemplate):
initDays = 10
# 策略变量
bar = None # K线对象
barMinute = EMPTY_STRING # K线当前的分钟
barList = [] # K线对象的列表
dayOpen = 0
@ -65,6 +63,7 @@ class DualThrustStrategy(CtaTemplate):
"""Constructor"""
super(DualThrustStrategy, self).__init__(ctaEngine, setting)
self.bm = BarManager(self.onBar)
self.barList = []
#----------------------------------------------------------------------
@ -94,35 +93,7 @@ class DualThrustStrategy(CtaTemplate):
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送必须由用户继承实现"""
# 计算K线
tickMinute = tick.datetime.minute
if tickMinute != self.barMinute:
if self.bar:
self.onBar(self.bar)
bar = VtBarData()
bar.vtSymbol = tick.vtSymbol
bar.symbol = tick.symbol
bar.exchange = tick.exchange
bar.open = tick.lastPrice
bar.high = tick.lastPrice
bar.low = tick.lastPrice
bar.close = tick.lastPrice
bar.date = tick.date
bar.time = tick.time
bar.datetime = tick.datetime # K线的时间设为第一个Tick的时间
self.bar = bar # 这种写法为了减少一层访问,加快速度
self.barMinute = tickMinute # 更新当前的分钟
else: # 否则继续累加新的K线
bar = self.bar # 写法同样为了加快速度
bar.high = max(bar.high, tick.lastPrice)
bar.low = min(bar.low, tick.lastPrice)
bar.close = tick.lastPrice
self.bm.updateTick(tick)
#----------------------------------------------------------------------
def onBar(self, bar):

View File

@ -15,7 +15,7 @@ from __future__ import division
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.vtConstant import EMPTY_STRING, EMPTY_FLOAT
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate, BarManager
########################################################################
@ -30,9 +30,6 @@ class EmaDemoStrategy(CtaTemplate):
initDays = 10 # 初始化数据所用的天数
# 策略变量
bar = None
barMinute = EMPTY_STRING
fastMa = [] # 快速EMA均线数组
fastMa0 = EMPTY_FLOAT # 当前最新的快速EMA
fastMa1 = EMPTY_FLOAT # 上一根的快速EMA
@ -63,6 +60,8 @@ class EmaDemoStrategy(CtaTemplate):
"""Constructor"""
super(EmaDemoStrategy, self).__init__(ctaEngine, setting)
self.bm = BarManager(self.onBar)
# 注意策略类中的可变对象属性通常是list和dict等在策略初始化时需要重新创建
# 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险,
# 策略类中的这些可变对象属性可以选择不写全都放在__init__下面写主要是为了阅读
@ -96,40 +95,7 @@ class EmaDemoStrategy(CtaTemplate):
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送必须由用户继承实现"""
# 计算K线
tickMinute = tick.datetime.minute
if tickMinute != self.barMinute:
if self.bar:
self.onBar(self.bar)
bar = VtBarData()
bar.vtSymbol = tick.vtSymbol
bar.symbol = tick.symbol
bar.exchange = tick.exchange
bar.open = tick.lastPrice
bar.high = tick.lastPrice
bar.low = tick.lastPrice
bar.close = tick.lastPrice
bar.date = tick.date
bar.time = tick.time
bar.datetime = tick.datetime # K线的时间设为第一个Tick的时间
# 实盘中用不到的数据可以选择不算,从而加快速度
#bar.volume = tick.volume
#bar.openInterest = tick.openInterest
self.bar = bar # 这种写法为了减少一层访问,加快速度
self.barMinute = tickMinute # 更新当前的分钟
else: # 否则继续累加新的K线
bar = self.bar # 写法同样为了加快速度
bar.high = max(bar.high, tick.lastPrice)
bar.low = min(bar.low, tick.lastPrice)
bar.close = tick.lastPrice
self.bm.updateTick(tick)
#----------------------------------------------------------------------
def onBar(self, bar):

View File

@ -17,7 +17,7 @@ import numpy as np
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.vtConstant import EMPTY_STRING
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate, BarManager
########################################################################
@ -34,10 +34,6 @@ class KkStrategy(CtaTemplate):
fixedSize = 1 # 每次交易的数量
# 策略变量
bar = None # 1分钟K线对象
barMinute = EMPTY_STRING # K线当前的分钟
fiveBar = None # 1分钟K线对象
bufferSize = 100 # 需要缓存的数据的大小
bufferCount = 0 # 目前已经缓存了的数据的计数
highArray = np.zeros(bufferSize) # K线最高价的数组
@ -77,6 +73,8 @@ class KkStrategy(CtaTemplate):
"""Constructor"""
super(KkStrategy, self).__init__(ctaEngine, setting)
self.bm = BarManager(self.onBar, 5, self.onFiveBar) # 创建K线合成器对象
#----------------------------------------------------------------------
def onInit(self):
"""初始化策略(必须由用户继承实现)"""
@ -104,82 +102,17 @@ class KkStrategy(CtaTemplate):
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送必须由用户继承实现"""
# 聚合为1分钟K线
tickMinute = tick.datetime.minute
if tickMinute != self.barMinute:
if self.bar:
self.onBar(self.bar)
bar = VtBarData()
bar.vtSymbol = tick.vtSymbol
bar.symbol = tick.symbol
bar.exchange = tick.exchange
bar.open = tick.lastPrice
bar.high = tick.lastPrice
bar.low = tick.lastPrice
bar.close = tick.lastPrice
bar.date = tick.date
bar.time = tick.time
bar.datetime = tick.datetime # K线的时间设为第一个Tick的时间
self.bar = bar # 这种写法为了减少一层访问,加快速度
self.barMinute = tickMinute # 更新当前的分钟
else: # 否则继续累加新的K线
bar = self.bar # 写法同样为了加快速度
bar.high = max(bar.high, tick.lastPrice)
bar.low = min(bar.low, tick.lastPrice)
bar.close = tick.lastPrice
self.bm.updateTick(tick)
#----------------------------------------------------------------------
def onBar(self, bar):
"""收到Bar推送必须由用户继承实现"""
# 如果当前是一个5分钟走完分钟线的时间戳是当前分钟的开始时间戳因此要+1
if (bar.datetime.minute + 1) % 5 == 0:
# 如果已经有聚合5分钟K线
if self.fiveBar:
# 将最新分钟的数据更新到目前5分钟线中
fiveBar = self.fiveBar
fiveBar.high = max(fiveBar.high, bar.high)
fiveBar.low = min(fiveBar.low, bar.low)
fiveBar.close = bar.close
# 推送5分钟线数据
self.onFiveBar(fiveBar)
# 清空5分钟线数据缓存
self.fiveBar = None
else:
# 如果没有缓存则新建
if not self.fiveBar:
fiveBar = VtBarData()
fiveBar.vtSymbol = bar.vtSymbol
fiveBar.symbol = bar.symbol
fiveBar.exchange = bar.exchange
fiveBar.open = bar.open
fiveBar.high = bar.high
fiveBar.low = bar.low
fiveBar.close = bar.close
fiveBar.date = bar.date
fiveBar.time = bar.time
fiveBar.datetime = bar.datetime
self.fiveBar = fiveBar
else:
fiveBar = self.fiveBar
fiveBar.high = max(fiveBar.high, bar.high)
fiveBar.low = min(fiveBar.low, bar.low)
fiveBar.close = bar.close
self.bm.updateBar(bar)
#----------------------------------------------------------------------
def onFiveBar(self, bar):
"""收到5分钟K线"""
print bar.datetime, bar.open, bar.high, bar.low, bar.close
# 撤销之前发出的尚未成交的委托(包括限价单和停止单)
for orderID in self.orderList:
self.cancelOrder(orderID)