Python笔记11-基于VN.py的实时K线生成器

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线给整出来了,就写在博客里好了。

猜你喜欢

转载自blog.csdn.net/q275343119/article/details/85165752