Day 20 - 实测盘中订阅 tick 与 bidask 资料是否有先後顺序 (下)

本篇重点

  • Quote-Binding Mode介绍
  • 透过Quote-Binding Mode,将订阅资料写入Redis
  • 结论

Quote-Binding Mode介绍

官方说明文件:https://sinotrade.github.io/tutor/market_data/streaming/quote_binding/
因为在这篇,我们要订阅tick与bidask的资料,并且将这些资料储存起来,这时候就要用到Shioaji API中的Quote-Binding Mode。
在官方的说明文件中,Quote-Binding Mode的范例有两种,第一种是在callback中,将quote资料存入deque中,程序说明如下:

from collections import defaultdict, deque
from shioaji import TickFOPv1, Exchange

msg_queue = defaultdict(deque) #设定要用来储存quote资讯的msg_queue
api.set_context(msg_queue) #将msg_queue设定为quote_callback中的context

@api.on_tick_fop_v1(bind=True) #在decorator中,将bind设为True来启用Quote-Binding Mode
def quote_callback(self, exchange:Exchange, tick:TickFOPv1): #quote_callback,参数多加一个self
    self[tick.code].append(tick) #这里的self,其实就是刚才宣告的msg_queue,将quote的tick存入

api.quote.subscribe(
    api.Contracts.Futures.TXF['TXF202107'],
    quote_type = sj.constant.QuoteType.Tick, 
    version = sj.constant.QuoteVersion.v1
)

关於defaultdict和deque的说明,可参考Python官方说明文件
https://docs.python.org/zh-tw/3/library/collections.html
而第二个方式,就是将quote资料,存入Redis中。这次测试的资料会以这个方式先储存在Redis中,程序范例如下:

import redis #汇入redis模组
import json #汇入json模组
from dotenv import load_dotenv
import os, threading
import shioaji as sj
from shioaji import Exchange, TickSTKv1, BidAskSTKv1

load_dotenv('D:\\python\\shioaji\\.env') #读取.env中的环境变数
r = redis.Redis(host='localhost', port=6379, decode_responses=True) #建立一个Redis的连线

api = sj.Shioaji()
api.login(
    person_id=os.getenv('YOUR_PERSON_ID'), 
    passwd=os.getenv('YOUR_PASSWORD')
)

api.set_context(r) #将Redis的连线传入

@api.on_tick_stk_v1(bind=True) #将bind设为True来启用Quote-Binding Mode
def quote_callback(self, exchange: Exchange, tick:TickSTKv1):
    channel = 'Q:' + tick.code #设定channel名称,即存入资料所使用的key值
    #将tick quote资料转换成json并存入Redis中
    self.xadd(channel, {'tick':json.dumps(tick.to_dict(raw=True))})

@api.on_bidask_stk_v1(bind=True) #将bind设为True来启用Quote-Binding Mode
def quote_callback(self, exchange: Exchange, bidask:BidAskSTKv1):
    channel = 'Q:' + bidask.code #设定channel名称,即存入资料所使用的key值
    #将bidask quote资料转换成json并存入Redis中
    self.xadd(channel, {'bidask':json.dumps(bidask.to_dict(raw=True))})

# 订阅盘中tick资料
api.quote.subscribe(
    api.Contracts.Stocks["3008"], 
    quote_type = sj.constant.QuoteType.Tick,
    version = sj.constant.QuoteVersion.v1
)
# 订阅盘中bidask资料
api.quote.subscribe(
    api.Contracts.Stocks["3008"], 
    quote_type = sj.constant.QuoteType.BidAsk,
    version = sj.constant.QuoteVersion.v1
)

print('Event().wait()...')
threading.Event().wait() #执行Event().wait(),防止程序未等待quote资料而直接结束

透过以上的程序,就可以订阅盘中的tick及bidask资料,并将资料储存至Redis中

读取Redis中的quote,并使用Pandas将资料转换

储存完资料後,最花时间的大概就是取出资料这一部份。因为前面的部份,我把tick和bidask的资料都放在同一个db中,如果一开始就放在不同的db,可能就不用花太多时间。

Redis预设会建立16个db,若在建立连线时没有指定,预设就是用第0个db做储存
若要指定,可以在建立连线时,加上「db=n」(n为0~15)即可

import redis, json #汇入redis及json模组
import pandas as pd

r = redis.Redis(host='localhost', port=6379, decode_responses=True) #建立一个Redis的连线

# 将所有的tick资料取出来并转换成DataFrame
tick_datas = [json.loads(x[-1]['tick']) for x in r.xread({'Q:3008':'0-0'})[0][-1] if 'tick' in x[-1].keys()]
df_tick = pd.DataFrame(tick_datas)
df_tick.to_csv('test_tick_data.csv', encoding='utf_8_sig')
# 将所有的bidask资料取出来并转换成DataFrame
bidask_datas = [json.loads(x[-1]['bidask']) for x in r.xread({'Q:3008':'0-0'})[0][-1] if 'bidask' in x[-1].keys()]
df_bidask = pd.DataFrame(bidask_datas)
df_bidask.to_csv('test_bidask_data.csv', encoding='utf_8_sig')

df_tick['type'] = 'tick' #tick的DataFrame中,增加type栏位,并设为'tick'
df_bidask['type'] = 'bidask' #bidask的DataFrame中,增加type栏位,并设为'bidask'
df = df_tick[['code', 'datetime', 'type']] #抓df_tick中的code、datetime及type,产生新的DataFrame
df = df.append(df_bidask[['code', 'datetime', 'type']]) #将df_bidask中的code、datetime及type,append到新的DataFrame中
df = df.sort_values(by='datetime') #依照datatime栏位内容排序
df = df.reset_index(drop=True) #依照现有的资料,重新产生index编号

df.to_csv('test_result.csv', encoding='utf_8_sig')

取出资料後,我用了DataFrame.append将tick和bidask的资料取出code、datetime及type这三个栏位,并重新组成新的DataFrame,并使用sort_values依照datetime做排序。相关的文件说明请参考以下连结
https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_values.html
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html

结论

在这个测试中,我是特别选3008这种成交量不高但又不算冷门的股票。可以看到,bidask资料比较多,而tick资料比较少。结论就是,tick是只有成交时才会有资料;而bidask则是只有买卖的最佳五档有变化时,就会有资料产生,跟tick是没有前後关系的。
https://ithelp.ithome.com.tw/upload/images/20211004/20140827sktzs3FxMC.png


<<:  成为工具人应有的工具包-20 UninstallView

>>:  应用系统开发的防护基准

【Day28】一些实用好工具 - 自制 Youtube-downloader

Youtube-downloader 不管是在做声音研究或是音乐研究的时候,虽然已经有很多资料集可以...

python3-日历

在python3中,想要制作日历有两种方式,先介绍第一种: -直接使用python中calendar...

30天学习笔记 -day 26-Motion Editor(上篇)

Motion Editor是自 Android Studio 4.0 版本开始为MotionLayo...

#8 Web Crawler 1

今天终於要开始写点有用的东西了:网路爬虫。 这次我们就来爬铁人赛的文章吧。 设定希望的资料结构 在做...

LeetCode解题 Day11

224. Basic Calculator https://leetcode.com/problem...