VeighNa量化社区
你的开源社区量化交易平台
Member
avatar
加入于:
帖子: 1
声望: 0

参考资料及鸣谢

实验完成时间:2022年3月10日
实验结果:对接成功(见下图)

description

几个重点注意事项:
1.2022年3月10日的提示信息显示,jqdata数据的price字段只给提供14天;
2.jqdata的数据未和rqdata等其他的数据源进行校对,数据质量不能确保,需要交叉验证。

具体步骤

  • 安装jqdata——根据jqdata注册邮件的内容进行安装,pip install jqdatasdk,如果有问题请参考官方链接https://www.joinquant.com/view/community/detail/cdf86c624992fc86ed51d920ef8c637b
  • 测试jqdata,确保安装成果且账户名密码正确——
    安装成功后,打开代码编辑器,输入如下代码登录JQData
    from jqdatasdk import *
    auth('账号','密码')  #账号是申请时所填写的手机号;密码为聚宽官网登录密码
    运行后如果显示“auth success”代表安装成功且用户名密码没问题了
  • 通过程序配置jqdata和vnpy的datafeed模式对接
    1. 在site-packages目录下新建文件夹vnpy_jqdata
    2. 在vnpy_jqdata目录下,新建init.py。python新手注意要新建不要复制rqdata文件夹再修改。
    3. 在vnpy_jqdata目录下,新建jqdata_datafeed.py
      具体代码:init.py
import importlib_metadata
from .jqdata_datafeed import JqdataDatafeed as Datafeed
try:
    __version__ = importlib_metadata.version("vnpy_jqdata")
except importlib_metadata.PackageNotFoundError:
    __version__ = "dev"

具体代码jqdata_datafeed.py

import jqdatasdk as jq
from datetime import timedelta, datetime
import datetime
from typing import List

from vnpy.trader.constant import Exchange, Interval
# from vnpy.trader.mddata.dataapi import MdDataApi
from vnpy.trader.datafeed import BaseDatafeed
from vnpy.trader.object import BarData, HistoryRequest
from vnpy.trader.setting import SETTINGS

from pytz import timezone


CHINA_TZ = timezone("Asia/Shanghai")

INTERVAL_VT2JQ = {
    Interval.MINUTE: '1m',
    Interval.HOUR: '60m',
    Interval.DAILY: '1d',
}

INTERVAL_ADJUSTMENT_MAP_JQ = {
    Interval.MINUTE: timedelta(minutes=1),
    Interval.HOUR: timedelta(hours=1),
    Interval.DAILY: timedelta()  # no need to adjust for daily bar
}


class JqdataDatafeed(BaseDatafeed):
    """聚宽JQData客户端封装类"""

    def __init__(self):

        """"""
        self.username = SETTINGS["datafeed.username"]
        self.password = SETTINGS["datafeed.password"]

        self.inited = False
        print("here")

    def init(self, username="", password=""):
        """"""
        if self.inited:
            return True

        if username and password:
            self.username = username
            self.password = password

        if not self.username or not self.password:
            return False

        try:
            jq.auth(self.username, self.password)
        except Exception as ex:
            print("jq auth fail:" + repr(ex))
            return False

        self.inited = True
        return True

    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", "9999"]:
                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_bar_history(self, req: HistoryRequest):
        """
        Query history bar data from JQData.
        """
        symbol = req.symbol
        exchange = req.exchange
        interval = req.interval
        start = req.start
        end = req.end

        jq_symbol = self.to_jq_symbol(symbol, exchange)
        # if jq_symbol not in self.symbols:
        #     return None

        jq_interval = INTERVAL_VT2JQ.get(interval)
        if not jq_interval:
            return None

        # For adjust timestamp from bar close point (RQData) to open point (VN Trader)
        # adjustment = INTERVAL_ADJUSTMENT_MAP_JQ.get(interval)
        adjustment = INTERVAL_ADJUSTMENT_MAP_JQ[interval]
        # For querying night trading period data
        end += timedelta(1)

        df = jq.get_price(
            jq_symbol,
            frequency=jq_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,
                    datetime=row.name.to_pydatetime() - adjustment,
                    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)

        return data
  • 修改vnpy的datafeed配置为jqdata
    点击顶部菜单栏的“配置”,然后参考下图进行

description

保存后,按提示关闭trader,重新从vnpy的station打开trader,回看到成功的提示信息

description

  • 测试下载数据并回测
    点击左边栏的“CTA回测”,修改本地代码为“IF9999.CFFEX”或其他有效的本地代码(注意默认的本地代码是不行的,估计因为jqdata修改了数据规则)。然后点击“下载数据”,会看到类似下面的

description

最后会提示“历史数据加载完成,数据量:175200”诸如此类的信息
点击回测按钮,会看到类似下图的,代表完全成功了

description

Member
avatar
加入于:
帖子: 2
声望: 0

description
请问这个是什么原因,我按照你的步骤来,jddata安转成功了,就是vnpy上报错!

Member
avatar
加入于:
帖子: 3
声望: 0

Quenson_7se wrote:

description
请问这个是什么原因,我按照你的步骤来,jddata安转成功了,就是vnpy上报错!

----------新建的init.py 文件名 修改为 init.py (init 前后都有两个下划线,发帖出来竟然吃掉了)

Member
avatar
加入于:
帖子: 3
声望: 0

lslling wrote:

Quenson_7se wrote:

description
请问这个是什么原因,我按照你的步骤来,jddata安转成功了,就是vnpy上报错!

----------新建的init.py 文件名 修改为 init.py (init 前后都有两个下划线,发帖出来竟然吃掉了)

---------------回帖要加转义符才能显示出下划线。。。 __init__.py

Member
avatar
加入于:
帖子: 59
声望: 4

确实可行,感谢
新建py文件的时候,不能用txt文件后缀直接改成py文件,会出现uft-8编码错误

Member
avatar
加入于:
帖子: 59
声望: 4

3.1.0版本,如果想通过 数据管理 模块的“下载数据”按钮,进行下载,jadata_datafeed.py文件中的query_bar_history函数,需要增加登录初始化的语句
description

    def query_bar_history(self, req: HistoryRequest):
        """
        Query history bar data from JQData.
        """
        # 检查是否登录
        if not self.inited:
            self.init()     

        symbol = req.symbol
        exchange = req.exchange
        interval = req.interval
        start = req.start
        end = req.end
Member
avatar
加入于:
帖子: 59
声望: 4

to_jq_symbol函数其实可以不用自己定义,聚宽有个normalize_code函数,可以将symbol转化成聚宽的代码格式

description

Member
avatar
加入于:
帖子: 7
声望: 0

我这个是怎么了

15:28:20 初始化CTA回测引擎
15:28:20 策略文件加载完成
15:28:20 数据服务初始化成功
15:28:28 ----------------------------------------
15:28:28 IF88.CFFEX-1m开始下载历史数据
15:28:29 数据下载失败,触发异常:
Traceback (most recent call last):
File "D:\VeighNa\lib\site-packages\vnpy_ctabacktester\engine.py", line 404, in run_downloading
data: List[BarData] = self.datafeed.query_bar_history(req)
File "D:\VeighNa\lib\site-packages\vnpy_jqdata\jqdata_datafeed.py", line 126, in query_bar_history
df = jq.get_price(
File "C:\Users\linle\AppData\Roaming\Python\Python310\site-packages\jqdatasdk\utils.py", line 308, in _wrapper
return func(*args, kwargs)
File "C:\Users\linle\AppData\Roaming\Python\Python310\site-packages\jqdatasdk\api.py", line 39, in get_price
return JQDataClient.instance().get_price(
locals())
File "C:\Users\linle\AppData\Roaming\Python\Python310\site-packages\jqdatasdk\client.py", line 347, in <lambda>
return lambda kwargs: self(method, kwargs)
File "C:\Users\linle\AppData\Roaming\Python\Python310\site-packages\jqdatasdk\client.py", line 331, in call
result = self.query(method, kwargs)
File "C:\Users\linle\AppData\Roaming\Python\Python310\site-packages\jqdatasdk\client.py", line 271, in query
raise self.get_error(response)
Exception: 无效的证券代码 'IF88.CCFX'

15:29:05 ----------------------------------------
15:29:05 LH9999.XDCE-1m开始下载历史数据
15:29:05 LH9999.XDCE解析失败,请检查交易所后缀
15:29:16

Member
avatar
加入于:
帖子: 124
声望: 4

上面写着合约代码是错的,中金所好像是CFFEX

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】