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

了准备Tick数据

 
 
要获取Tick数据,并插入到vn.py数据库中,整体上有3种方法:

 

  1. 使用vn.py行情记录模块DataRecorder来自行录取:在保证网络稳定的情况下,启动一个独立的进程,负责在交易时段录制行情数据;并在收盘后,使用脚本工具完成数据的自动清洗工作,整体上实现起来比较耗费精力;
  2. 从RQData或者其他数据源下载:专业的数据服务商可以提供已经清洗好的Tick数据,但是价格往往比较贵;
  3. 通过CSV文件来载入:CSV格式的Tick数据相对来说比较容易获取(万能的淘宝~),因此对个人用户来说是个比较容易的选择。
     

那么本文我们就选择第3种方法,通过读取CSV文件,把数据载入到数据库中。

 

 

载入CSV文件

 

首先需要保证你已经在系统上安装配置好了数据库,这里演示用的是MongoDB数据库以及图形化客户端Robo 3T。
 

注意在MongoDB中需要创建新数据库“vnpy”,然后在全局配置对话框中,修改相关配置:

 

    "database.driver": "mongodb",
    "database.database": "vnpy",
    "database.host": "localhost",
    "database.port": 27017,
    "database.user": "",
    "database.password": "",
    "database.authentication_source": ""

 

注意输入上述内容到配置对话框中时,请忽略引号。修改完毕保存后,请重新启动VN Trader,检查相关配置是否已经修改成功。
 

然后我们把所有的CSV文件放在同一文件夹下,这样就可以使用一个脚本来读取该文件夹内的所有CSV格式文件,并批量载入到数据库中。

 

description

 

在开始处理数据之前,我们需要知道CSV文件中的表头信息和数据特征。用Excel打开其中任意一个CSV文件,查看其中的内容后,建立一个比较直观的印象,大概知道:

 

  • 哪些数据需要写入到数据库;
  • 哪些数据不需要写入;
  • 哪些数据需要强行赋值。

 

这里我们的CSV文件,表头以及第一行内容如下:

 

交易日,合约代码,交易所代码,合约在交易所的代码,最新价,上次结算价,昨收盘,昨持仓量,今开盘,最高价,最低价,数量,成交金额,持仓量,今收盘,本次结算价,涨停板价,跌停板价,昨虚实度,今虚实度,最后修改时间,最后修改毫秒,申买价一,申买量一,申卖价一,申卖量一,申买价二,申买量二,申卖价二,申卖量二,申买价三,申买量三,申卖价三,申卖量三,申买价四,申买量四,申卖价四,申卖量四,申买价五,申买量五,申卖价五,申卖量五,当日均价,业务日期
20190102,ru1905,,,11280.0000,11290.0000,11305.0000,322472,11280.0000,11280.0000,11280.0000,246,27748800.0000,322468,0.0000,0.0000,12080.0000,10495.0000,0,0,08:59:00,500,11280.0000,10,11290.0000,10,0.0000,0,0.0000,0,0.0000,0,0.0000,0,0.0000,0,0.0000,0,0.0000,0,0.0000,0,112800.0000,20190102

 

从以上内容中,我们发现下述特征:

 

  • 表头是中文;
  • 可以直接载入的数据:最新价,持仓量,买一价,买一量,卖一价,卖一量;
  • 需要合成的数据:datetime,它由3列数据合成,分别是交易日,最后修改时间,最后修改毫秒;
  • 强行赋值数据是主力连续合约(RU88)和交易所(SHFE);
  • 集合竞价阶段数据需要保留;
  • 非交易时段产生的垃圾数据需要剔除。

 
有了这样的需求后,我们在接下来开发脚本的过程中就有了方向:

 

1)使用for循环遍历同一文件夹内所有CSV格式的文件(即以“.csv"结尾的文件名),使用csv_load函数来载入数据:

 

import os 
import csv
from datetime import datetime, time

from vnpy.trader.constant import Exchange
from vnpy.trader.database import database_manager
from vnpy.trader.object import TickData


def run_load_csv():
    """
    遍历同一文件夹内所有csv文件,并且载入到数据库中
    """
    for file in os.listdir("."): 
        if not file.endswith(".csv"): 
            continue

        print("载入文件:", file)
        csv_load(file)

 

2)csv_load函数的具体设计

 

  1. 使用csv.DictReader类,来访问CSV文件中的数据;
  2. 通过字符串相加合成标准时间,在使用datetime.strptime函数转化成时间元组;
  3. 通过datetime.time函数的判断来剔除非交易时间段数据;
  4. 将符合标准数据,生成TickData数据对象;
  5. 在循环中把TickData插入到ticks列表中;
  6. 最终使用database_manager.save_tick_data函数把ticks列表中的数据写入到数据库中。

 

def csv_load(file):
    """
    读取csv文件内容,并写入到数据库中    
    """
    with open(file, "r") as f:
        reader = csv.DictReader(f)

        ticks = []
        start = None
        count = 0

        for item in reader:

            # generate datetime
            date = item["交易日"]
            second = item["最后修改时间"]
            millisecond = item["最后修改毫秒"]

            standard_time = date + " " + second + "." + millisecond
            dt = datetime.strptime(standard_time, "%Y%m%d %H:%M:%S.%f")

            # filter
            if dt.time() > time(15, 1) and dt.time() < time(20, 59):
                continue

            tick = TickData(
                symbol="RU88",
                datetime=dt,
                exchange=Exchange.SHFE,
                last_price=float(item["最新价"]),
                volume=float(item["持仓量"]),
                bid_price_1=float(item["申买价一"]),
                bid_volume_1=float(item["申买量一"]),
                ask_price_1=float(item["申卖价一"]),
                ask_volume_1=float(item["申卖量一"]), 
                gateway_name="DB",       
            )
            ticks.append(tick)

            # do some statistics
            count += 1
            if not start:
                start = tick.datetime

        end = tick.datetime
        database_manager.save_tick_data(ticks)

        print("插入数据", start, "-", end, "总数量:", count)  

 if __name__ == "__main__":
    run_load_csv()

 
创建好脚本后可以直接运行:进入cmd或者Powershell,运行命令python load_tickdata.py即可,效果如下图所示:

 

description
 

此时我们使用Robo 3T客户端来连接上MongoDB,在数据库【vnpy】->【db_tick_data】可以看到新载入的数据:

 
description

 

 

Tick模式回测

 

CTA策略模块(CtaStrategy)的回测引擎BacktestingEngine支持Tick数据的回测,以下代码推荐在Jupyter Notebook中运行。

 

第一步我们需要在策略文件中进行一些修改,这里以AtrRsiStrategy策略为例:找到on_init函数,把其中的load_bar(10)改为load_tick(10),即指定加载过去10天的Tick数据来执行策略初始化任务,而不是加载K线Bar数据进行初始化。

 
然后在加载回测相关的模块时,需要额外加载BacktestingMode枚举类型,其中包含有回测引擎所支持的Bar(K线)和Tick两种模式:
 

from vnpy.app.cta_strategy.backtesting import BacktestingEngine, OptimizationSetting
from vnpy.app.cta_strategy.base import BacktestingMode
from datetime import datetime
from atr_rsi_strategy import  AtrRsiStrategy

 
创建回测引擎对象的实例后,在调用set_parameters函数时,参数中需要新增“mode=BacktestingMode.TICK ”,来指定回测引擎使用Tick回测模式。
 

同时需要注意另外2点:

 

  • interval需要传入"1m",尽管在Tick回测中用不到K线,但不能设置为None或者不传,会导致报错;
  • 合约乘数、手续费、滑点三个参数,需要根据合约品种的具体情况做调整。

 

engine = BacktestingEngine()
engine.set_parameters(
    vt_symbol="RU88.SHFE",
    interval="1m",
    start=datetime(2019, 1, 1),
    end=datetime(2019, 4, 1),
    rate=0.5/10000,
    slippage=5,
    size=10,
    pricetick=5,
    capital=1_000_000,
    mode=BacktestingMode.TICK   
)
engine.add_strategy(AtrRsiStrategy, {})

 

后续的操作和K线模式回测就几乎完全相同了,加载历史数据并执行数据回放,然后基于逐笔成交计算每日盈亏数据,并生成最终的策略统计结果以及回测图表:

 

engine.load_data()
engine.run_backtesting()
df = engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()

 

由于这3个月行情多为区间震荡,所以以去趋势跟踪为核心逻辑的AtrRsiStrategy的回测效果不太理想:
 
description

 

下面我们如果想再看看详细的逐笔成交记录,可以遍历回测引擎中保存所有成交数据的trades字典,并打印每笔成交相关的字段信息:

 

trades = engine.trades
for value in trades.values():
    print("时间:",value.datetime,value.direction.value,value.offset.value, "价格:",value.price, "数量:",value.volume)
    if value.offset.value == "平":
        print("---------------------------------------------------------")

 

这样就能看到每笔成交具体发生的时间点,并和Tick数据当时的盘口情况进行相应的比对检查了:
 

description

 

《vn.py全实战进阶》课程已经更新过半!一共50节内容覆盖从策略设计开发、参数回测优化,到最终实盘自动交易的完整CTA量化业务流程,详细内容请戳课程上线:《vn.py全实战进阶》!

 
了解更多知识,请关注vn.py社区公众号。

description

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

1秒钟有两条数据只能载入一条吗

Administrator
avatar
加入于:
帖子: 4500
声望: 320

两条的时间戳是不同的,带有毫秒部分,所以都是正常载入不用担心

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

回测时不想载入过去一天的数据,只想用当天的tick历史数据进行回测怎么操作呀

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

之前vnpy有initDays,现在vnpy2.0之后在哪里设置,多谢

Member
avatar
加入于:
帖子: 141
声望: 57

@zzb

def on_init(self):
        self.load_bar(5) #载入5天分钟数据初始化
Member
加入于:
帖子: 14
声望: 0

description

请问from atr_rsi_strategy import AtrRsiStrategy,怎么会变成Unable to import 'atr_rsi_strategy',可以不把策略复制进去吗

Super Moderator
avatar
加入于:
帖子: 31
声望: 13

请问from atr_rsi_strategy import AtrRsiStrategy,怎么会变成Unable to import 'atr_rsi_strategy' ?

 

这是因为python解释器找不到 atr_rsi_strategy.py 这个文件 。这个文件并不储存在 python 解释器的默认读取路径中。
 

你可以尝试加入下面代码:

import sys
sys.path.append('atr_rsi_strategy.py 文件所在的路径')
from atr_rsi_stratgey import AtrRsiStrategy
Member
加入于:
帖子: 14
声望: 0

description

将这个策略放在桌面、C:/Users/.../strategies、D:\vnstudio\Lib\site-packages\vnpy\app\cta_strategy\strategies,都没成功,一般是怎么回事的

Super Moderator
avatar
加入于:
帖子: 31
声望: 13

一般来说有两种可能:

  1. atr_rsi_strategy.py 文件的路径还是没有被添加到python解释器中。你的路径后面少填写了一个/。修改为C:/Users/.../strategies/试一试
  2. 路径已经被添加,但是文件名字出错(比如atr_rsi_strategy2.py 错写成atr_rsi_strategy.py)或者策略名字写错了(比如atr_rsi_strategy 错写成AtrRsiStrategy)。

针对第一个路径问题你还可以尝试下面步骤:

  1. atr_rsi_strategy.py 文件所在的目录
  2. 在该目录下按住左 shift键和鼠标右键,在弹出的对话框中选择 在此处打开powershell
  3. 在powershell 中 键入 jupyter notebook
  4. 将你的所有代码复制到 jupyter notebook 中运行看一看。

如果,上面操作还是不可以。

请发一下你的 atr_rsi_strategy.py 代码的截图和错误输出信息。

Member
加入于:
帖子: 14
声望: 0

description
做到第4.时,将回测的.py放到atr_rsi_strategy.py的所在目录,直接用from atr_rsi_strategy import AtrRsiStrategy就可以了,之后又将回测的.py和atr_rsi_strategy.py放在桌面,同样可以。奇怪。

Super Moderator
avatar
加入于:
帖子: 31
声望: 13

这还是python解释器读取路径的问题:

  1. 执行回测的backtesting.py文件是python解释器执行的文件。 python解释器在执行这个文件的时候,会把该文件所在的路径暂时添加到默认读取路径里。
  2. atr_rsi_strategy.py文件是需要被import的策略所在的文件。 你前面不能import成功,就是因为该文件并不在python解释器默认读取路径里。
  3. 综上,当你把上面两个文件放在同一目录下,这就等于把 atr_rsi_strategy.py文件所在的路径添加到了python解释器默认读取路径里。
Member
avatar
加入于:
帖子: 4
声望: 1

@KeKe @用Python的交易员
二位晚上好,反馈并求助:
问题1:

tick = TickData(
...
                volume=float(item["持仓量"]),
...
            )

应为:

open_interest=float(item["持仓量"]),
volume=float(item["数量"]),

问题2:VNPY2.1.4 TICK MODE的回测无法完成,报错:

2020-07-25 15:31:47.633707  开始加载历史数据
2020-07-25 15:32:28.954328  加载进度: [5%]
...
2020-07-25 15:44:10.700282  加载进度:######### [93%]
2020-07-25 15:44:55.731819  加载进度:######### [99%]
2020-07-25 15:44:59.925135  加载进度:########## [100%]
2020-07-25 15:44:59.925135  历史数据加载完成,数据量:9831737
2020-07-25 15:44:59.925135  触发异常,回测终止
2020-07-25 15:44:59.927129  Traceback (most recent call last):
  File "c:\vnstudio\lib\site-packages\vnpy\app\cta_strategy\backtesting.py", line 286, in run_backtesting
    self.callback(data)
  File "c:\vnstudio\lib\site-packages\vnpy\app\cta_strategy\strategies\atr_rsi_strategy.py", line 93, in on_bar
    am.update_bar(bar)
  File "c:\vnstudio\lib\site-packages\vnpy\trader\utility.py", line 360, in update_bar
    self.close_array[-1] = bar.close_price
AttributeError: 'TickData' object has no attribute 'close_price'

2020-07-25 15:44:59.927129  开始计算逐日盯市盈亏
2020-07-25 15:44:59.927129  成交记录为空,无法计算

问题3:VNPY2.1.4 BAR MODE回测无法完成,按照教程导入TickData后,似乎没有生成BarData:

2020-07-25 18:38:58.427694  开始加载历史数据
2020-07-25 18:38:58.427694  加载进度: [5%]
...
2020-07-25 18:38:58.428693  加载进度:######### [99%]
2020-07-25 18:38:58.428693  加载进度:########## [100%]
2020-07-25 18:38:58.428693  历史数据加载完成,数据量:0
2020-07-25 18:38:58.428693  策略初始化完成
2020-07-25 18:38:58.428693  开始回放历史数据
2020-07-25 18:38:58.428693  历史数据回放结束
2020-07-25 18:38:58.428693  开始计算逐日盯市盈亏
2020-07-25 18:38:58.428693  成交记录为空,无法计算
None
2020-07-25 18:38:58.428693  开始计算策略统计指标
2020-07-25 18:38:58.428693  ------------------------------
2020-07-25 18:38:58.428693  首个交易日:  
2020-07-25 18:38:58.428693  最后交易日:  
2020-07-25 18:38:58.428693  总交易日:   0
2020-07-25 18:38:58.428693  盈利交易日:  0
2020-07-25 18:38:58.429133  亏损交易日:  0
2020-07-25 18:38:58.429133  起始资金:   1,000,000.00
2020-07-25 18:38:58.429133  结束资金:   0.00
2020-07-25 18:38:58.429133  总收益率:   0.00%
2020-07-25 18:38:58.429133  年化收益:   0.00%
...
Member
avatar
加入于:
帖子: 4622
声望: 284

可以检查一下你的数据,报错上说没有close_price

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

为啥你们的数据载入这么快?

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

把tick的数据导入到数据库里,还要在”行情记录“里再添加一下tick的记录才能使用数据吧?

Member
avatar
加入于:
帖子: 4622
声望: 284

只要数据库里有数据就可使用,行情记录里添加是对该合约数据进行实时录制

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

沪公网安备 31011502017034号

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