vn.py量化社区
By Traders, For Traders.
Super Moderator
avatar
加入于:
帖子: 36
声望: 7

发布于vn.py社区公众号【vnpy-community】
 

原文作者:庸木 | 发布时间: 2020-02-17
 

1.0时代的vn.py只支持MongoDB数据库,在数据结构上使用MongoDB的数据库(DATABASE)来区分不同的历史数据时间周期,使用集合(COLLECTION)来区分不同的交易合约数据,许多用户(尤其以职业交易员背景居多)喜欢这种一目了然的数据结构。
 

2.0版本在数据库的设计上则是选择同时兼容SQL(SQLite、MySQL、PostgreSQL)和NoSQL(MongoDB)两种不同范式,使用软件工程中ORM(对象关系印射)/ODM(对象文件印射)构建了独立的抽象中间层(vnpy.trader.database),在数据存储的结构上发生了比较大的变化。
 

以2.0版本中MongoDB数据库为例,首先需要创建一个单独的数据库,通常命名为vnpy。在该数据库中只包含两张表,分别用于存放K线和tick数据:
 

  • db_bar_data集合

    • 存放所有K线数据,包括不同的交易合约以及时间周期

    • 通过vt_symbol+interval+datetime三个字段来保证唯一性

  • db_tick_data集合

    • 存放所有Tick数据

    • 通过vt_symbol+datetime两个字段来保证唯一性
       

通过Robo3T客户端查看的结构如下图:
 

description

 

尽管这种设计保证了和SQL类数据库的兼容性,但缺陷是随着使用时间的增长,集合中数据的长度和宽度都在增加。刚开始数据库可能只存5年内IF88的数据,到后来变成了所有国内期货品种从开始上市至今的所有1分钟历史数据,此时单一集合的数据总量会变得相当大,读取数据时查询检索很费时。
 

本文中试着给出一种解决方案:
 

  • 若指定特定的集合名,则保存在该集合中;

  • 若没有指定,则将数据仍保存在原有的集合中。
     

配置数据库为MongDB

 

如果数据库已经配置为MongoDB可以直接跳过这一段。找到用户目录下.vntrader文件夹,用VSCode打开vt_setting.json文件,对全局配置中的数据库相关字段进行以下修改。这里我们为了演示方便,创建一个新的名为【vnpytest】的数据库:
 

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

 

如果尚未安装MongDB数据库以及可视化工具,可以参考往期文章:vn.py社区精选6 - 做交易,你需要选好数据库
 

修改database.py文件

 

找到vn.py源代码所在的路径,进入目录vnpy/trader/database打开database.py 文件,需要对save_bar_datasave_tick_data这两个函数进行修改,主要增加了一个参数collection_name:
 

@abstractmethod
def save_bar_data(
    self,
    datas: Sequence["BarData"],
    collection_name: str = None,
):
    pass

@abstractmethod
def save_tick_data(
    self,
    datas: Sequence["TickData"],
    collection_name: str = None,
):
    pass

 

修改database_mongo.py文件

 

然后打开位于同一目录下的database_mongo.py 文件,同样我们需要对 save_bar_datasave_tick_data 进行修改。
 

改动后的数据写入逻辑如下:
 

  • 若没有指定collection_name,数据存入db_bar_data或db_tick_data;

  • 若指定了collection_name,则调用switch_collection函数,把数据切换到写入到指定的Collection中。
     

from mongoengine.context_managers import switch_collection


def save_bar_data(self, datas: Sequence[BarData], collection_name: str = None):
  for d in datas:
      updates = self.to_update_param(d)
      updates.pop("set__gateway_name")
      updates.pop("set__vt_symbol")            
      if collection_name is None:
          (
              DbBarData.objects(
                  symbol=d.symbol, interval=d.interval.value, datetime=d.datetime
              ).update_one(upsert=True, **updates)
          )
      else:
          with switch_collection(DbBarData, collection_name):
              (
                  DbBarData.objects(
                      symbol=d.symbol, interval=d.interval.value, datetime=d.datetime
                  ).update_one(upsert=True, **updates)
              )


def save_tick_data(self, datas: Sequence[TickData], collection_name: str = None):
  for d in datas:
      updates = self.to_update_param(d)
      updates.pop("set__gateway_name")
      updates.pop("set__vt_symbol")
      if collection_name is None:
          (
              DbTickData.objects(
                  symbol=d.symbol, exchange=d.exchange.value, datetime=d.datetime
              ).update_one(upsert=True, **updates)
          )
      else:
          with switch_collection(DbTickData, collection_name):
              (
                  DbTickData.objects(
                      symbol=d.symbol, exchange=d.exchange.value, datetime=d.datetime
                  ).update_one(upsert=True, **updates)
              )

 

Jupyter Notebook使用示例

 

最后可以通过Jupyter Notebook来测试下使用效果,这里我们将把CSV格式的1小时周期的XBTUSD数据,载入到指定的集合【XBTUSD】中。
 

具体步骤如下:
 

  1. 调用csv库读取"XBTUSD.csv"文件到内存中,其中DictReader类似于常规reader,但是将每行中的信息映射到一个字典,该字典的key由表头可选参数给出;

  2. 遍历每行的rows对象,生成BarData对象,并且缓存至bars列表中;

  3. 调用save_bar_data函数,把bars列表中的数据写入到我们指定的集合【XBTUSD】中。

 

注意:若该函数第二个参数collection_name为空,则bars列表中的数据会被写入默认的数据集合【db_bar_data】中。
 

from vnpy.trader.database import database_manager
from vnpy.trader.object import BarData
from vnpy.trader.constant import Interval, Exchange
import csv

file_name='XBTUSD.csv'
with open(file_name,newline='',encoding='UTF-8') as csvfile:
    rows=csv.DictReader(csvfile)
    bars = []
    for i in rows:
        bar = BarData(
            symbol=i["symbol"],
            exchange=Exchange.BITMEX,
            datetime=i["datetime"],
            interval=Interval.HOUR,
            volume=i["volume"],
            open_price=i["open"],
            high_price=i["high"],
            low_price=i["low"],
            close_price=i["close"],
            gateway_name='DB',
        )
        bars.append(bar)
    print('Saving data to database ...')
    database_manager.save_bar_data(bars, "XBTUSD")

 

载入完成后,打开MongDB客户端Robo3T,我们可以看到在vnpytest数据库下新增了一个集合【XBTUSD】,且所有1小时K线数据均已成功写入其中:
 
description
 
 

《vn.py全实战进阶 - 期权零基础入门》课程已经更新到第9集,内容专门面向从未接触过期权交易的新手,共计30节课程带你一步步掌握期权的基础知识、了解合约特征和品种细节、学习方向交易和套利组合等各种常用期权交易策略,详细内容请戳新课上线:《期权零基础入门》
 
 

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

楼主你好!可以把XBTUSD.csv的文件发出来参照一下吗?我的csv文件对不上

Super Moderator
avatar
加入于:
帖子: 36
声望: 7

可以查看这篇帖子,对数据入库有详细介绍vn.py数据入库

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

李怡然 wrote:

可以查看这篇帖子,对数据入库有详细介绍vn.py数据入库
我查看了一下这个帖子,尝试这样进行修改,以达到自动分表入库的诉求,不知道能否可以达到效果
vnstudio\Lib\site-packages\vnpy\app\data_recorder\engine.py文件

    def run(self):
        """"""
        while self.active:
            try:
                task = self.queue.get(timeout=1)
                task_type, data = task
                collection_name = data.vt_symbol
                if task_type == "tick":
                    collection_name = collection_name + '_tick'
                    database_manager.save_tick_data([data], collection_name)
                elif task_type == "bar":
                    collection_name = collection_name + '_bar'
                    database_manager.save_bar_data([data], collection_name)

            except Empty:
                continue

            except Exception:
                self.active = False

                info = sys.exc_info()
                event = Event(EVENT_RECORDER_EXCEPTION, info)
                self.event_engine.put(event)
Member
avatar
加入于:
帖子: 42
声望: 0

请问如何将数据分到SQLite不同的表中入库?

Member
avatar
加入于:
帖子: 526
声望: 24

可以参考你楼上的方法试试看

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