為VNPY 2版本加入聚寬資料來源

張國平發表於2020-05-02

之前寫過一篇文章,是為VNPY1.9.2 版本加入聚寬的資料來源,這個算是後續版本,為VNPY2版本加入聚寬資料來源。


VNPY論壇裡面有個篇文章也是講加入聚寬資料來源的,不過他是相當於整合到VNPY庫程式碼中去,和VNPY自帶的米筐資料來源一樣。我這篇其實也是借鑑他的,當然他說他借鑑之前我的1.92那篇。反正互相借鑑。


之前VNPY 1版本中,我的個人程式碼很多是直接在VNPY庫程式碼直接修改或者增加的。每次VNPY升級就是非常頭疼,要做程式碼對比,在一些可能被更新覆蓋的地方再次維護測試。而且因為更新的地方很亂,造成後面生產版本一致停留在VNPY1.92。


還是要慢慢遷移到VNPY2版本,畢竟很多吸引的特性。這次準備不在VNPY的庫檔案程式碼上修改,而是像引用NUMPY或者Pandas這樣,採用引用繼承的方式,把自己的程式碼和VNPY的庫程式碼隔離;這樣即使VNPY升級,個人程式碼不用太擔心,只要簡單測試,保證繼承引用VNPY的類或方法正常工作就可以了。


這裡只要安裝好了VNPY2.1以後版本,只要在任意地方複製下面程式碼,維護聚寬登入資訊執行就可以下載聚寬資料。

其中config.json是儲存聚寬登入賬戶,和需要下載的品種。品種資訊是按照VNPY的品種和交易所資訊組合字串,如Symbol.Exchang。

而JQDataload.py是執行下載的,類JQDataService初始化是連線聚寬資訊源,方法to_jq_symbol是把VNPY品種名稱轉換為聚寬名稱,方法query_history是按照時間段下載分鐘線要指定憑證,方法downloadAllMinuteBar是按照指定最近天數,下載config.json所以的品種資料。

最好去我的Github下載,比較方便

程式碼如下:

# encoding: UTF-8
import json
import time
from datetime import datetime, timedelta
from typing import List
import jqdatasdk as jq
from vnpy.trader.constant import Exchange, Interval
from vnpy.trader.database import database_manager
from vnpy.trader.object import (
   BarData
)
class JQDataService:
   """
   Service for download market data from Joinquant
   """
   def __init__(self):
      # 載入配置
      config = open('config.json')
      self.setting = json.load(config)
      USERNAME = self.setting['jqdata.Username']
      PASSWORD = self.setting['jqdata.Password']
      try:
         jq.auth(USERNAME, PASSWORD)
      except Exception as ex:
         print("jq auth fail:" + repr(ex))
   def to_jq_symbol(self, symbol: str, exchange: Exchange):
      """
      CZCE product of RQData has symbol like "TA1905" while
      vt symbol is "TA905.CZCE" so need to add "1" in symbol.
      """
      if exchange in [Exchange.SSE, Exchange.SZSE]:
         if exchange == Exchange.SSE:
            jq_symbol = f"{symbol}.XSHG"  # 上海證券交易所
         else:
            jq_symbol = f"{symbol}.XSHE"  # 深圳證券交易所
      elif exchange == Exchange.SHFE:
         jq_symbol = f"{symbol}.XSGE"  # 上期所
      elif exchange == Exchange.CFFEX:
         jq_symbol = f"{symbol}.CCFX"  # 中金所
      elif exchange == Exchange.DCE:
         jq_symbol = f"{symbol}.XDCE"  # 大商所
      elif exchange == Exchange.INE:
         jq_symbol = f"{symbol}.XINE"  # 上海國際能源期貨交易所
      elif exchange == Exchange.CZCE:
         # 鄭商所 的合約程式碼年份只有三位 需要特殊處理
         for count, word in enumerate(symbol):
            if word.isdigit():
               break
         # Check for index symbol
         time_str = symbol[count:]
         if time_str in ["88", "888", "99", "8888"]:
            return f"{symbol}.XZCE"
         # noinspection PyUnboundLocalVariable
         product = symbol[:count]
         year = symbol[count]
         month = symbol[count + 1:]
         if year == "9":
            year = "1" + year
         else:
            year = "2" + year
         jq_symbol = f"{product}{year}{month}.XZCE"
      return jq_symbol.upper()
   def query_history(self, symbol, exchange, start, end, interval='1m'):
      """
      Query history bar data from JQData and update Database.
      """
      jq_symbol = self.to_jq_symbol(symbol, exchange)
      # if jq_symbol not in self.symbols:
      #     return None
      # For querying night trading period data
      # end += timedelta(1)
      now = datetime.now()
      if end >= now:
         end = now
      elif end.year == now.year and end.month == now.month and end.day == now.day:
         end = now
      df = jq.get_price(
         jq_symbol,
         frequency=interval,
         fields=["open", "high", "low", "close", "volume"],
         start_date=start,
         end_date=end,
         skip_paused=True
      )
      data: List[BarData] = []
      if df is not None:
         for ix, row in df.iterrows():
            bar = BarData(
               symbol=symbol,
               exchange=exchange,
               interval=Interval.MINUTE,
               datetime=row.name.to_pydatetime() - timedelta(minutes=1),
               open_price=row["open"],
               high_price=row["high"],
               low_price=row["low"],
               close_price=row["close"],
               volume=row["volume"],
               gateway_name="JQ"
            )
            data.append(bar)
      database_manager.save_bar_data(data)
      return data
   def downloadAllMinuteBar(self, days=1):
      """下載所有配置中的合約的分鐘線資料"""
      if days != 0:
         startDt = datetime.today() - days * timedelta(1)
         enddt = datetime.today()
      else:
         startDt = datetime.today() - 10 * timedelta(1)
         enddt = datetime.today()
      print('-' * 50)
      print(u'開始下載合約分鐘線資料')
      print('-' * 50)
      if 'Bar.Min' in self.setting:
         l = self.setting["Bar.Min"]
         for VNSymbol in l:
            dt0 = time.process_time()
            symbol = VNSymbol.split(".")[0]
            exchange = Exchange(VNSymbol.split(".")[1])
            self.query_history(symbol, exchange, startDt, enddt, interval='1m')
            cost = (time.process_time() - dt0)
            print(u'合約%s的分鐘K線資料下載完成%s - %s,耗時%s秒' % (symbol, startDt, enddt, cost))
            print(jq.get_query_count())
         print('-' * 50)
         print
         u'合約分鐘線資料下載完成'
         print('-' * 50)
      return None
if __name__ == '__main__':
   JQdata = JQDataService()
   JQdata.downloadAllMinuteBar(days=30)

config.json

{
   "jqdata.Username": "",
   "jqdata.Password": "",
   "Bar.Min":
   [
      "MA8888.CZCE",
      "MA009.CZCE",
      "SR8888.CZCE",
      "RM8888.CZCE",
    ]
}


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2689657/,如需轉載,請註明出處,否則將追究法律責任。

相關文章