from vnpy.trader.vtObject import VtBarData
from datetime import timedelta
import pymongo
from queue import Queue, Empty
from threading import Thread
class Bargenerate02:
'''自己创建的一个K线引擎'''
def __init__(self, x=5):
# 记录K线和上一笔行情
self.bar = None
self.lastTick = None
# 记录XminK线
self.minxBar = None
self.minX = x
# 存放不同合约的collection
self.collections = {}
# 消息处理相关
self.active = False # 开关
self.queue = Queue() # 队列
self.thread = Thread(target=self.run) # 数据存储线程
# 标记集合竞价
self.callOpen = 0
self.callAction = 0
# 给定一个初始成交量
self.initVolume = 0
def setDBname(self, dbname):
'''设置存储数据库名'''
self.dbname = dbname
def connetDB(self):
'''连接数据库'''
self.client = pymongo.MongoClient('localhost', 27017)
def createCollection(self, symbols):
'''
对每一个订阅行情建立一个collection
格式:分钟K线:rb1905 X分钟K线:rb1905.Xmin
:param symbols: list 订阅合约列表
:return:
'''
for symbol in symbols:
self.collections[symbol] = []
self.symbol = symbol
self.symbolXmin = symbol + '.' + str(self.minX) + 'min'
collection = self.client[self.dbname][self.symbol]
collection.ensure_index([('datetime', pymongo.DESCENDING)])
collectionXmin = self.client[self.dbname][self.symbolXmin]
collectionXmin.ensure_index([('datetime', pymongo.DESCENDING)])
self.collections[symbol].append(collection)
self.collections[symbol].append(collectionXmin)
def formatMin(self, dateTime):
'''格式化时间'''
# 格式化时间,10:55:50 格式化为10:56:00
dateTime += timedelta(minutes=1)
dateTime = dateTime.replace(second=0, microsecond=0)
return dateTime
def updateTick(self, tick):
'''收到推送过来的tick并处理'''
# 提取tick的时间戳
self.barTime = tick.datetime
if self.bar:
if self.barTime.day != self.lastTick.datetime.day:
# 跨天,建立一个新bar
self.createNewBar(tick, first=1)
elif self.barTime >= self.formatMin(self.lastTick.datetime):
# 当前时间大于格式化后的上个时间,说明这个tick不属于上个bar
# 这是要更新上个bar的time,同时新增一个bar
self.updateBar(self.lastTick, timeFormat=1)
if self.checkClose(self.barTime):
# 如果卡在了收盘点,直接更新tick,不用格式化更新
# 也不用建立新bar
self.updateBar(tick)
else:
self.createNewBar(tick)
else:
# 当前时间段,更新
self.updateBar(tick)
else:
# 没有bar实例的情况
# 建立一个新bar
self.createNewBar(tick, first=1)
def checkCallAction(self, t):
'''检查集合竞价'''
if (t.hour == 12 and t.minute == 59) or (
t.hour == 0 and t.minute == 59):
return True
def checkOpenLose(self, now, tick):
'''补全第一笔交易之前的空余K线'''
hour = now.hour
# 夜场第一笔
if 1 <= hour < 13:
nightOpenTime = now.replace(hour=1, minute=0, second=0, microsecond=0)
self.addBlankBar(now, nightOpenTime, tick, first=1)
elif 13 <= hour < 19:
dayOpenTime = now.replace(hour=13, minute=0, second=0, microsecond=0)
self.addBlankBar(now, dayOpenTime, tick, first=1)
def addBlankBar(self, now, last, tick, first=0):
'''
插入空白K线
如果是插入开盘第一根之前的,有集合竞价,空白K线的OHLC通过集合竞价确定,
如果没有集合竞价,则通过昨收确定。
如果是插入两个之间的,空白OHLC通过上一个tick的交易价确定
:param now: 当前tick时间
:param last: 同当前作比较的时间
:param tick:
:param first: 是否为开盘第一根
:return:
'''
delta = (now - last).seconds // 60
for i in range(1, delta + 1):
bar_datetime = last + timedelta(minutes=i)
if (bar_datetime.hour >= 3 and bar_datetime.hour < 13) \
or (bar_datetime.hour == 15 and bar_datetime.minute >= 30) \
or (bar_datetime.hour == 16) \
or (bar_datetime.hour == 17 and bar_datetime.minute <= 30) \
or (bar_datetime.hour >= 19) \
or (bar_datetime.hour == 14 and 15 <= bar_datetime.minute <= 30):
continue
bar = VtBarData()
if self.callAction:
# 如果上一根是集合竞价,那么开盘价等于call_open
bar.open = self.callOpen
bar.high = self.callOpen
bar.low = self.callOpen
bar.close = self.callOpen
# 两个标记复位
# 这里是补位,两个标记不复位
# self.callOpen = 0
# self.callAction = 0
# 碰到集合竞价时 上一个交易量归零
self.lastVolume = 0
else:
if first:
bar.open = tick.preClosePrice
bar.high = tick.preClosePrice
bar.low = tick.preClosePrice
bar.close = tick.preClosePrice
else:
bar.open = tick.lastPrice
bar.high = tick.lastPrice
bar.low = tick.lastPrice
bar.close = tick.lastPrice
bar.openInterest = tick.openInterest
bar.volume = '0'
bar.date = bar_datetime.strftime('%Y%m%d')
bar.time = bar_datetime.strftime('%H:%M:%S')
bar.datetime = bar_datetime
bar.gatewayName = tick.gatewayName
bar.symbol = tick.symbol
bar.vtSymbol = tick.vtSymbol
bar.exchange = tick.exchange
self.insertData(bar, update=0)
def checkClose(self, now):
'''检查收盘时间点'''
huor = now.hour
minute = now.minute
second = now.second
microsecond = now.microsecond
if huor == 3 and minute == 0 and second == 0 and microsecond == 0:
return True
elif huor == 14 and minute == 15 and second == 0 and microsecond == 0:
return True
elif huor == 15 and minute == 30 and second == 0 and microsecond == 0:
return True
elif huor == 19 and minute == 0 and second == 0 and microsecond == 0:
return True
def insertData(self, data, index=0, update=1, timeFormat=0):
'''
将tick或者bar放入队列
:param data: tick,bar
:param index: 0表示1min 1 表示xmin
:param update: 0表示insert 1 表示 update
:return:
'''
dataPut = data
self.queue.put((dataPut, index, update, timeFormat))
def createNewBar(self, tick, first=0):
'''建立新的K线'''
# 判断集合竞价,如果是则暂缓建立
if self.checkCallAction(self.barTime):
# 集合竞价TICK出现,记录lastPrice
# 做个标记,表示集合进价TICK出现,告诉下一个tick,上一笔是集合竞价
self.callOpen = tick.lastPrice
self.callAction = 1
self.lastTick = tick
else:
# 补全之前的空白
if first:
# 第一根,从开盘价补齐
self.checkOpenLose(self.barTime, tick)
else:
self.addBlankBar(self.barTime, self.formatMin(self.lastTick.datetime), self.lastTick)
# 建立一个新bar
self.bar = VtBarData()
if self.callAction:
# 如果上一根是集合竞价,那么开盘价等于call_open
self.bar.open = self.callOpen
# 两个标记复位
self.callOpen = 0
self.callAction = 0
# 碰到集合进价时,上一个交易量归零
self.initVolume = 0
else:
self.bar.open = tick.lastPrice
if self.lastTick:
if first:
# 新的一天交易量直接归零
self.initVolume = 0
else:
# 如果有前一笔tick,初始volume 为上一笔volume
self.initVolume = self.lastTick.volume
self.bar.high = tick.lastPrice
self.bar.low = tick.lastPrice
self.bar.close = tick.lastPrice
self.bar.openInterest = tick.openInterest
self.bar.volume = int(tick.volume) - self.initVolume
self.bar.date = tick.date
self.bar.time = tick.time
self.bar.datetime = tick.datetime
self.bar.gatewayName = tick.gatewayName
self.bar.symbol = tick.symbol
self.bar.vtSymbol = tick.vtSymbol
self.bar.exchange = tick.exchange
self.insertData(self.bar, update=0)
# 记录上一笔tick
self.lastTick = tick
def updateBar(self, tick, timeFormat=0):
'''更新K线'''
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.openInterest = tick.openInterest
self.bar.volume = str(int(tick.volume) - self.initVolume)
self.bar.time = tick.time
self.bar.datetime = tick.datetime
self.insertData(self.bar, timeFormat=timeFormat)
self.lastTick = tick
def run(self):
'''数据库储存线程'''
while self.active:
try:
data, index, update, timeFormat = self.queue.get(block=True, timeout=1)
if update:
flt = {'_id': data._id}
if timeFormat:
data.datetime = self.formatMin(data.datetime)
self.collections[data.symbol][index].update(flt, {'$set': data.__dict__})
else:
self.collections[data.symbol][index].insert(data.__dict__)
except Empty:
pass
def start(self):
'''开始'''
self.active = True
self.thread.start()
def stop(self):
'''结束'''
if self.active:
self.active = False
self.thread.join()
最近一个期货项目,需要实时的对推送过来的tick进行储存,并且还要实时生成1分钟K线,5分钟K线,和日K线
目前把基本的1分钟K线给整出来了,就写在博客里好了。