vn.py官网
Veighna量化社区 | 你的开源社区量化交易平台

置顶主题

把Word、WPS人了吧,用VSCode一样可以写就完美Markdown文档!

(贴子标题不小心打错字了,无法修改 ,抱歉!!!)

准备工作

安装Markdown插件

  • Markdown All in One
  • Markdown Paste
  • Markdown PDF
  • Markdown Preview Enhanced
  • Markdown Preview Mermaid Support

description

工作区界面

description

markdown文档的编辑和预览联动示意

description

相关的快捷键

  • Shift+Alt+F :格式化文档
  • Ctrl+Alt+V :Markdown Paste
  • Ctrl+Alt+C :Markdown Paste Code
  • Ctrl+K V :MPE:打开侧边预览
  • Ctrl+Shift+P :命令面板

鼠标右键菜单命令

  • Markdown PDF: Export (settings.json)
  • Markdown PDF: Export (pdf)
  • Markdown PDF: Export (html)
  • Markdown PDF: Export (png)
  • Markdown PDF: Export (jipeg)
  • Markdown PDF: Export (all: pdf, html, png, jpeg)

下面是一个用VSCode编写Markdown的演示文档

创建markdown文档

首先用VSCode创建新文档,把它保存为扩展名为md的文档,这就表示它是一个markdown文档了。

说明:

  • 虽然vn.py官网支持markdown语法,但是支持的还不够全面(也许是咱不熟悉),我编写的演示文档有许多地方还是不可在vn.py官网,所以我把不可以直接被显示的地方贴在显示效果之前,您可以把这些代码复制到VSCode中,在markdown相关插件齐全的情况下,是可以成功显示的。
  • 例如后面Tek数学公式可mermaid绘图语法的演示,分成实现代码和显示效果,您可以复制到VSCode中,参考示例的写法,修改成符合自己要求的各种图。

基本markdown语法的使用

无序列表

  • 一月份
  • 二月份
  • 三月份
  • 四月份

第二级标题

标题的写法:Markdown标题由 ‘#’ 开始:

# —— 一级标题
## —— 二级标题
### —— 三级标题
#### —— 四级标题
##### —— 五级标题
###### —— 六级标题

其中侧边栏上半部分显示的是当前工作区文件夹下的文件,包括 Markdown 文件和素材。下半部分可以展开「Outline」视图,即大纲,可以根据当前正在编辑的 Markdown 文件显示其各级标题的大纲目录。

这里是引用演示:

> 1st reference
Markdown 文件和素材。下半部分可以展开「Outline」视图,即大纲,可以根据当前正在编辑的 Markdown 文件显示其各级标题的大纲目录。
>> 2nd reference
Markdown 文件和素材。下半部分可以展开「Outline」视图,即大纲,可以根据当前正在编辑的 Markdown 文件显示其各级标题的大纲目录。
>>> third level reference
Markdown 文件和素材。下半部分可以展开「Outline」视图,即大纲,可以根据当前正在编辑的 Markdown 文件显示其各级标题的大纲目录。

显示效果:

1st reference
Markdown 文件和素材。下半部分可以展开「Outline」视图,即大纲,可以根据当前正在编辑的 Markdown 文件显示其各级标题的大纲目录。

2nd reference
Markdown 文件和素材。下半部分可以展开「Outline」视图,即大纲,可以根据当前正在编辑的 Markdown 文件显示其各级标题的大纲目录。

third level reference
Markdown 文件和素材。下半部分可以展开「Outline」视图,即大纲,可以根据当前正在编辑的 Markdown 文件显示其各级标题的大纲目录。

下面是python代码示例:

python
import time

# Quick,count to ten!for i in range( 10):
# ( but not *too* quick)time.sleep(0.5)

for i in range(1,100):
    print(i)

这是行内的代码:x = this.count,它是嵌入到文字中的。

第三级标题

无序列表

  • 侧边栏
  • 编辑区域
  • 预览区域

有序列表

  1. 其中侧边栏上半部分显示的是当
  2. 前工作区文件夹下的文件,
  3. 包括 Markdown 文件和素材。
  4. 下半部分可以展开「Outline」
  5. 视图,即大纲,可以根

字体修饰(加重、斜体、删除)

加重显示 值得一提的是斜体字, VS Code 的 Markdown 预览默认渲染的是当前你正在编辑的文档,不过如果你希望将某个文档的预览渲染锁定不动,可以通过「Markdown: Toggle Preview Locking」调出一个锁定某个文档的预览界面。

LaTex 数学表达式

···
InLine math equations:$\omega = d\phi / dt$. Display math should get its own line like so:

$$I = \int \rho R^{2} dV$$
$$ 2+3 = 5 $$
$x+y=z^2$
$ax+by=cz$
$2x+3y=4z$
···
显示:

description

表格示例

  • 表格示例1
    ···
    | 源文件 | 和预览界面 | 是同步的 |
    | :----: | :--------: | :------:|
    | 即你无 | 论是在编辑 | 界面还是 |
    | 即你无 | 论是在编辑 | 界面还是 |
    | 即你无 | 论是在编辑 | 界面还是 |
    ···

description

  • 表格示例2————表格还可以如此方便
    ···
    title | 标题
    --:|:--
    dateFormat |日期格式
    section |模块
    Completed | 已经完成
    Active | 当前正在进行
    Future | 后续待处理
    crit | 关键阶段
    日期缺失 | 默认从上一项完成后
    ···
    显示:

description

图片示例

用代码插入图片示例 !

![test.jpg](images/test.jpg)

显示:

description

剪贴板图片插入示例 !

  1. 先找到自己需要的界面,将图形复制到剪贴板
  2. 在VSCode中编辑的md文档需要插入图片的位置,点击鼠标右键,选择“Markdown Paste”或者Ctrl+Alt+V快捷键,进入下面的图片命名环节,可以默认,也可以另外输入新名称。
  3. 完成上面的两步后,md文档就被自动添加了类似下面的语句:![图片插入.png](图片插入.png),当然它和手工输入是同样的显示效果。

description

超链接示例

百度搜索

  • apple
  • carrot
  • pear

下面是一些非常炫酷的Mermaid图

序列图示例1:

```mermaid
sequenceDiagram
    participant Alice
    participant Bob
    Alice->>John: Hello John,how are you?
    loop Healthcheck
        John->>John: Fight against hypochondria
    end
    Note right of John: Rational thoughts<br/>prevail...
    John-->>Alice: Great!
    John->>Bob: How about you?
    Bob-->>John: Jolly good!|
    John-->Alice:我爱你!
```

显示:

description


序列图示例2:

```mermaid
  sequenceDiagram
    Note right of A: 倒霉, 碰到B了
    A->B:   Hello B, how are you ?
    note left of B: 倒霉,碰到A了
    B-->A:  Fine, thx, and you?
    note over A,B: 快点溜,太麻烦了
    A->>B:  I'm fine too.
    note left of B: 快点打发了A
    B-->>A: Great!
    note right of A: 溜之大吉
    A-xB:   Wait a moment
    loop Look B every minute
      A->>B: look B, go?
      B->>A: let me go?
    end
    B--xA: I'm off, byte
    note right of A: 太好了, 他走了
```

description


序列图示例3:

```mermaid
  sequenceDiagram
    Alice->>Bob: Hello Bob, how are you?
  alt is sick
    Bob->>Alice:not so good :(
  else is well
    Bob->>Alice:good
  end
  opt Extra response
    Bob->>Alice:Thanks for asking
  end
```

description


序列图示例4:

```mermaid
sequenceDiagram
  # 通过设定参与者(participant)的顺序控制展示顺序
  participant B
  participant A
  Note right of A: 倒霉, 碰到B了
  A->B:   Hello B, how are you ?
  note left of B: 倒霉,碰到A了
  B-->A:  Fine, thx, and you?
  note over A,B:快点溜,太麻烦了。。。
  A->>B:  I'm fine too.
  note left of B: 快点打发了A
  B-->>A: Great!
  note right of A: 溜之大吉
  A-xB:   Wait a moment
  loop Look B every minute
    A->>B: look B, go?
    B->>A: let me go?
  end
  B--xA: I'm off, byte
  note right of A: 太好了, 他走了
```

description


序列图示例5:

```mermaid
  sequenceDiagram 
    # 通过设定参与者(participants)的顺序控制展示模块顺序
    participant Alice
    participant Bob 
    participant John 
    Alice->John:Hello John,how are you?
    loop Healthcheck
      John->John:Fight against hypochondria
    end
    Note right of John:Rational thoughts <br/>prevail...  John-->Alice:Great! 
    John->Bob: How about you?
    Bob-->John: good!
```

description


甘特图示例

甘特图示例1:

```mermaid
  gantt
    dateFormat YYYY-MM-DD
    section S1
    T1: 2014-01-01, 9d
    section S2
    T2: 2014-01-11, 9d
    section S3
    T3: 2014-01-02, 9d
```

description

甘特图示例2:

```mermaid
  gantt
    dateFormat  YYYY-MM-DD
    title Adding GANTT diagram functionality to mermaid

    section A section
    Completed task            :done,    des1, 2014-01-06,2014-01-08
    Active task               :active,  des2, 2014-01-09, 3d
    Future task               :         des3, after des2, 5d
    Future task2               :         des4, after des3, 5d

    section Critical tasks
    Completed task in the critical line :crit, done, 2014-01-06,24h
    Implement parser and jison          :crit, done, after des1, 2d
    Create tests for parser             :crit, active, 3d
    Future task in critical line        :crit, 5d
    Create tests for renderer           :2d
    Add to mermaid                      :1d

    section Documentation
    Describe gantt syntax               :active, a1, after des1, 3d
    Add gantt diagram to demo page      :after a1  , 20h
    Add another diagram to demo page    :doc1, after a1  , 48h

    section Last section
    Describe gantt syntax               :after doc1, 3d
    Add gantt diagram to demo page      : 20h
    Add another diagram to demo page    : 48h
```

description


类图示例:

```mermaid
  classDiagram
    Class01 <|-- AveryLongClass : Cool    
    Class03 *-- Class04
    Class05 o-- Class06
    Class07 .. Class08
    Class09 --> C2 : Where am i?
    Class09 --* C3
    Class09 --|> Class07
    Class07 : equals()
    Class07 : Object[] elementData
    Class01 : size()
    Class01 : int chimp
    Class01 : int gorilla
    Class08 <--> C2: Cool label    
```

description

饼图示例:

```mermaid
  pie
    "Dogs" : 386
    "Cats" : 85
    "Rats" : 15
```

description


实体关系图示例

```mermaid
erDiagram
    CUSTOMER ||--o{ ORDER : places
    ORDER ||--|{ LINE-ITEM : contains
    CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
```

description


旅游图示例:

```mermaid
  journey
      title My working day
      section Go to work
        Make tea: 5: Me
        Go upstairs: 3: Me
        Do work: 1: Me, Cat
      section Go home
        Go downstairs: 5: Me
        Sit down: 5: Me
```

description

总结

本文给您介绍了这些文档元素:

  • 各级标题(1~6级)
  • 文本修饰(字体、颜色、加粗、斜体、删除线...)
  • 无序列表
  • 有序列表
  • 程序代码(行内和独立)
  • 引用
  • LaTex数学表达式(行内和独立)
  • 表格(包括对齐方式)
  • 图片(手工插入和自动插入)
  • 超链接
  • 各种Mermaid图
    • 序列图
    • 流程图
    • 甘特图
    • 类图
    • 饼图
    • 实体
    • 旅游图

掌握了上述的Markdown语法,您可以轻松编写自己的Markdown文档了!



2021年vn.py回顾总结

发布于vn.py社区公众号【vnpy-community】
 
原文作者:用Python的交易员 | 发布时间:2022-01-18
 
之前12月底发布的2.9.0,为vn.py自从2019年以来开发迭代了三年的2.0大版本画上了一个句号。2022年已经开始了半个多月,虽然迟了点,不过老规矩还是要对2021年来做个总结回顾。

 

回顾2021年

 

先来看一眼2021年的vn.py项目整体数据(虽然统计日期是2022年1月17日):

  • 用户数量

    • Github Star:17.3k(+3.8k)
    • Github Fork:7k(+1.2k)
    • Gitee Star:1k
    • Gitee Fork:419
  • 项目开发

    • 版本迭代:9次(2.1.9 -> 2.9.0)
    • 交易接口:21个(移除23个)
    • 策略应用:16个(不变)
    • 数据库适配器:8个
    • 数据服务:7个
  • 社区交流

    • 论坛用户数量:31033 (+12889)
    • 论坛帖子数量:28485 (+10911)
    • 社区活动:8次
    • 专题小班课:3次

 

用户数量

截止2022年1月17日,vn.py在Github上一共收获了17369个Star,Fork数量达到7k,继续保持线性增长。下图来源于统计工具Star History

description

除了Github外,我们在国内代码托管平台Gitee上维护的vn.py仓库也保持同步更新,累计已经收获1018个Star和419个Fork。

 

项目开发

2021年vn.py的主要开发工作就是【模块剥离计划】,截止12月底发布的2.9.0版本已经基本收尾。除了通用功能依旧保留在核心框架项目下,所有的交易接口(gateway)、应用模块(app)、数据库适配器(database)、数据服务接口(datafeed)都已经剥离到了独立的功能模块项目中。具体剥离后的模块信息,可以参考之前发布的《vn.py发布v2.9.0 - 模块剥离计划收尾》。

除了模块剥离方面的工作外,2021年也更加聚焦于国内机构用户的需求,精简移除了部分用户量较少的交易接口,新增了以下证券和期货方面的接口:

  • EsunnyGateway:易盛柜台兼容交易接口(内盘期货、黄金TD);
  • OstGateway:东方证券OST极速柜台交易接口(股票);
  • HtsGateway:顶点HTS柜台交易接口(ETF期权);
  • TtsGateway:TTS仿真系统交易接口(仿真模拟)。

数据库方面,增加了一系列读写性能优秀的NoSQL数据库支持,包括:

  • DolphinDB:国产高性能分布式时序数据库;
  • Arctic:基于MongoDB扩展开发的金融时序数据库;
  • LevelDB:Google推出的高性能Key/Value数据库。

对于历史数据服务则是开发了独立的抽象接口(vnpy.trader.datafeed),并扩展了支持的数据服务范围:

  • 云端类:

    • 恒有数UData
    • TuShare
    • 天勤TQSDK
  • 客户端类:

    • 万得Wind
    • 同花顺iFinD
    • 天软TinySoft

策略应用方面,根据社区反馈的需求重构开发了新的WebTrader模块,采用FastAPI作为后端Web服务器:

description

整体架构图

description

服务参数配置

description

前端接口文档

 

社区交流

虽然2021年疫情明显好转,但反复出现的零星案例还是多次打乱了我们的线下活动计划。全年一共举办了8场社区活动(包括线上和线下),大概是近几年来比较少的了,专题小班课也因为同样的原因一共只搞了3场。

2021年回顾完,下一篇就是对2022年的计划了。今年我们将会启动全新3.0大版本的开发工作,如果你对vn.py过去一年有什么想分享的吐槽的,或者对新的一年有什么建议和期望,都在文章下面欢迎留言告诉我们!

 



在Mac上安装vnpy,保证一次成功!

一、提前下载需要的安装包:
1、Miniconda3
https://docs.conda.io/en/latest/miniconda.html#
选择MacOSX installers里的最新版本,这里是Python 3.9下载。

2、pycharm
pycharm-community-2020.3.3.dmg
从官网上下载社区版https://www.jetbrains.com/pycharm/

3、vnpy安装包(解压后,复制文件夹到自己喜欢的位置)
从vnpy在gitee的官方地址下载最新的安装包,采用zip格式下载。
https://gitee.com/vnpy/vnpy

二、安装
1、安装Miniconda,这里是Miniconda3-latest-MacOSX-x86_64.pkg
2、添加国内源:
添加国内源:在当前用户下,编辑.condarc,内容如下:
channels:

3、创建虚拟环境
conda create -n py37_vnpy python=3.7
conda activate py37_vnpy
(退出:conda deactivate)

4、安装python.app
conda install -c conda-forge python.app
可能会因为网络问题不成功,多试几次。

5、安装pycharm-community-2020.3.3.dmg
从官网上下载社区版https://www.jetbrains.com/pycharm/

7、打开vnpy所在的文件夹,进行配置
点击‘PyCharm’菜单->Preferences菜单->Project:vnpy一级菜单->Python Interpreter二级菜单->点击右上齿轮->Add菜单->Conda Environment->Existing enviroment->Interpreter:/opy/miniconda3/envs/py37_vnpy/bin/pythonw(选择前面新建的虚拟环境的pythonw)->点OK->点OK->点OK

8、(确认在PyCharm里已经打开了vnpy项目),在PyCharm的底部,找到Terminal的标签,点击,进入py37_vnpy环境的终端,并且当前路径位于vnpy项目的文件夹。
执行以下的安装语句(requirements.txt是vnpy项目文件夹下面的一个文件),这个安装时间比较长,需要较好的网络。

pip install -r requirements.txt -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

9、创建run.py文件,复制以下代码,来源 README.md

因为mac上不支持ctp接口,所以要注释掉ctp接口,否则运行会报错。

from vnpy.event import EventEngine
from vnpy.trader.engine import MainEngine
from vnpy.trader.ui import MainWindow, create_qapp

# from vnpy.gateway.ctp import CtpGateway

from vnpy.app.cta_strategy import CtaStrategyApp
from vnpy.app.cta_backtester import CtaBacktesterApp

def main():
"""Start VN Trader"""
qapp = create_qapp()

event_engine = EventEngine()
main_engine = MainEngine(event_engine)

# main_engine.add_gateway(CtpGateway)
main_engine.add_app(CtaStrategyApp)
main_engine.add_app(CtaBacktesterApp)

main_window = MainWindow(main_engine, event_engine)
main_window.showMaximized()

qapp.exec()


if name == "main":
main()

10、运行 python run.py,注意环境名称是 py37_vnpy



【公告】国泰君安期货vn.py专属版即将上线!

2021年7月26日,国泰君安期货和韦纳软件科技达成战略合作。国泰君安期货和韦纳软件科技两家公司在各自的领域都处于领跑的位置,此次的战略合作,国泰君安期货将为vn.py量化交易平台的客户提供更优质的服务和更丰富的资源,使得量化客户的体验更上一个新的台阶。

国泰君安期货即将在官网上线【国泰君安期货vn.py专属版】,在vn.py开源版的基础上提供专属定制服务

  1. 快速交易接入。拒绝繁杂外部接入申请,一键迈入期货交易;
  2. 内置仿真测试。内置仿真测试环境,与实盘环境无缝切换;
  3. 国泰君安期货客户专属的vn.py量化答疑交流群,遇到问题第一时间找到小助手快速解决;
  4. 定期举办的期货量化实战线下分享活动,覆盖从策略开发回测、智能参数优化到算法交易执行的量化业务全环节;

最后也是最重要的:

以上所有增值服务对于国泰君安期货客户完全免费!
针对新客户更有提供数据福利哦!
另外,我们与米筐数据也建立了深度合作,提供快速的数据对接服务。
若您对期货、股票,期权等数据感兴趣,也可以申请试用!

详情请扫描下方二维码,联系国泰君安期货客户经理咨询。

description

更多vn.py问题欢迎入群交流。

description



如何更有效地利用合约交易状态信息——拒绝CTP接口中的脏数据。

如何更有效地利用合约交易状态信息——拒绝CTP接口中的脏数据。

1. 任何接口中都会有脏数据,CTP接口也不例外

不知道您是否发现这些情况:

  1. 明明已经休市或者收盘了,可是行情列表中的合约数据还在疯狂地涌来,如果此时你的策略还在运行状态,那会发生什么?
  2. 市场中许多人使用数据记录器(DataRecorder)进行合约数据的录制,经常发现录制到的结果里有许多莫名其妙的数据!
  3. 于是我们会说,尽量要靠近开盘再启动程序或者策略,一旦收盘就关闭vnpy系统,这样就可以避免被接口中的脏数据干扰和影响。

这种情况不只是使用CTP接口的交易者会遇到,其他接口也是一样。

2. CTP接口本身已经有完善的机制

为了防止客户端接收到脏数据,CTP接口会在某个交易时间端的开始结束时,在公共流中播发所有品种的合约品种的交易状态通知,只是vnpy系统没有使用。
交易状态通知的格式见帖子 如何更有效地利用合约交易状态信息——交易状态信息管理器

3. 拒绝CTP接口中的脏数据的方法

系统连接接口后,订阅的行情数据就会从CTP网关的MdApi接口中推送给客户端。目前vnpy在收到tick数据时没有任何有效性判断,就直接推送给了各种订阅者了,而这正是脏数据的来由!
正确的做法是:客户端就在收到tick数据的时候,根据合约品种的交易状态判断该数据是是否为有效数据,如果合约的状态是开盘前、非交易或者是收盘状态,则将该tick丢弃。
这样就可以杜绝脏数据对交易者、各种应用策略或数据记录器的影响。

4. 代码实现

4.1 实现交易状态管理器

交易状态管理器的功能及实现代码详见 如何更有效地利用合约交易状态信息——交易状态信息管理器 ,这里就不再提供了。

4.2 添加EVENT_ORIGIN_TICK消息类型

在vnpy\trader\event.py文件中增加下面的消息定义:

EVENT_ORIGIN_TICK = "eOriginTick."              # hxxjava debug

4.3 修改网关Gateway的on_tick()函数

在vnpy\trader\gateway.py文件中,将所有网关的父类Gateway的on_tick()函数做如下修改:

    def on_tick(self, tick: TickData) -> None:
        """
        Tick event push.
        Tick event of a specific vt_symbol is also pushed.
        """
        # self.on_event(EVENT_TICK, tick)
        # self.on_event(EVENT_TICK + tick.vt_symbol, tick)
        self.on_event(EVENT_ORIGIN_TICK, tick)

4.4 修改OmsManager

class OmsEngine(BaseEngine):
    """
    Provides order management system function for VN Trader.
    """

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super(OmsEngine, self).__init__(main_engine, event_engine, "oms")

        # self.trade_status = TradeStatus(event_engine,6)   # hxxjava add

        self.ticks: Dict[str, TickData] = {}
        self.orders: Dict[str, OrderData] = {}
        self.trades: Dict[str, TradeData] = {}
        self.positions: Dict[str, PositionData] = {}
        self.accounts: Dict[str, AccountData] = {}
        self.contracts: Dict[str, ContractData] = {}
        self.active_orders: Dict[str, OrderData] = {}
        self.trade_status_manager = TradeStatusManager(event_engine,30)       # 创建交易状态管理器

        self.add_function()
        self.register_event()

    def add_function(self) -> None:
        """Add query function to main engine."""
        self.main_engine.get_tick = self.get_tick
        self.main_engine.get_order = self.get_order
        self.main_engine.get_trade = self.get_trade
        self.main_engine.get_position = self.get_position
        self.main_engine.get_account = self.get_account
        self.main_engine.get_contract = self.get_contract
        self.main_engine.get_all_ticks = self.get_all_ticks
        self.main_engine.get_all_orders = self.get_all_orders
        self.main_engine.get_all_trades = self.get_all_trades
        self.main_engine.get_all_positions = self.get_all_positions
        self.main_engine.get_all_accounts = self.get_all_accounts
        self.main_engine.get_all_contracts = self.get_all_contracts
        self.main_engine.get_all_active_orders = self.get_all_active_orders
        self.main_engine.get_status = self.get_status                   # hxxjava debug

    def register_event(self) -> None:
        """"""
        self.event_engine.register(EVENT_TICK, self.process_tick_event)
        self.event_engine.register(EVENT_ORDER, self.process_order_event)
        self.event_engine.register(EVENT_TRADE, self.process_trade_event)
        self.event_engine.register(EVENT_POSITION, self.process_position_event)
        self.event_engine.register(EVENT_ACCOUNT, self.process_account_event)
        self.event_engine.register(EVENT_CONTRACT, self.process_contract_event)  
        self.event_engine.register(EVENT_STATUS, self.process_status_event)  # 订阅合约交易状态数据
        self.event_engine.register(EVENT_ORIGIN_TICK, self.process_origin_tick_event)   # 订阅原始行情数据

    def process_origin_tick_event(self,event: Event):#-> None:  # 处理原始行情数据     
        """ 对原始tick数据进行有效性判断和处理 """
        tick = event.data
        status:StatusData = self.trade_status_manager.get_tick_status(tick)

        if not status:
            print(f"{datetime.now()} {tick.vt_symbol} 还没有收到交易状态")
            return

        # 有效交易状态
        valid_statuses = [
            InstrumentStatus.CONTINOUS,
            InstrumentStatus.AUCTION_ORDERING,
            InstrumentStatus.AUCTION_BALANCE,
            InstrumentStatus.AUCTION_MATCH
        ]
        if status.instrument_status in valid_statuses:
            # 这里是所有有效数据的发源地
            self.event_engine.put(Event(EVENT_TICK, tick))
            self.event_engine.put(Event(EVENT_TICK + tick.vt_symbol, tick))
        else:
            print(f"{datetime.now()} 特别交易状态={status} {tick}")


    def process_tick_event(self, event: Event) -> None:
        """"""
        tick = event.data
        self.ticks[tick.vt_symbol] = tick

    def process_order_event(self, event: Event) -> None:
        """"""
        order = event.data
        self.orders[order.vt_orderid] = order

        # If order is active, then update data in dict.
        if order.is_active():
            self.active_orders[order.vt_orderid] = order
        # Otherwise, pop inactive order from in dict
        elif order.vt_orderid in self.active_orders:
            self.active_orders.pop(order.vt_orderid)

    def process_trade_event(self, event: Event) -> None:
        """"""
        trade = event.data
        self.trades[trade.vt_tradeid] = trade

    def process_position_event(self, event: Event) -> None:
        """"""
        position = event.data
        self.positions[position.vt_positionid] = position

    def process_account_event(self, event: Event) -> None:
        """"""
        account = event.data
        self.accounts[account.vt_accountid] = account

    def process_contract_event(self, event: Event) -> None:
        """"""
        contract = event.data
        self.contracts[contract.vt_symbol] = contract

    def process_status_event(self, event: Event) -> None:   # 处理交易状态信息
        """"""
        status = event.data
        # print(f"【{datetime.now()} {status}】")
        self.trade_status_manager.save_status(status)

    def get_tick(self, vt_symbol: str) -> Optional[TickData]:
        """
        Get latest market tick data by vt_symbol.
        """
        return self.ticks.get(vt_symbol, None)

    def get_order(self, vt_orderid: str) -> Optional[OrderData]:
        """
        Get latest order data by vt_orderid.
        """
        return self.orders.get(vt_orderid, None)

    def get_trade(self, vt_tradeid: str) -> Optional[TradeData]:
        """
        Get trade data by vt_tradeid.
        """
        return self.trades.get(vt_tradeid, None)

    def get_position(self, vt_positionid: str) -> Optional[PositionData]:
        """
        Get latest position data by vt_positionid.
        """
        return self.positions.get(vt_positionid, None)

    def get_account(self, vt_accountid: str) -> Optional[AccountData]:
        """
        Get latest account data by vt_accountid.
        """
        return self.accounts.get(vt_accountid, None)

    def get_contract(self, vt_symbol: str) -> Optional[ContractData]:
        """
        Get contract data by vt_symbol.
        """
        return self.contracts.get(vt_symbol, None)

    def get_all_ticks(self) -> List[TickData]:
        """
        Get all tick data.
        """
        return list(self.ticks.values())

    def get_all_orders(self) -> List[OrderData]:
        """
        Get all order data.
        """
        return list(self.orders.values())

    def get_all_trades(self) -> List[TradeData]:
        """
        Get all trade data.
        """
        return list(self.trades.values())

    def get_all_positions(self) -> List[PositionData]:
        """
        Get all position data.
        """
        return list(self.positions.values())

    def get_all_accounts(self) -> List[AccountData]:
        """
        Get all account data.
        """
        return list(self.accounts.values())

    def get_all_contracts(self) -> List[ContractData]:
        """
        Get all contract data.
        """
        return list(self.contracts.values())

    def get_status(self,vt_symbol:str) -> List[StatusData]:     # hxxjava debug
        """
        Get the vt_symbol's status data.
        """
        return self.trade_status_manager.get_status(vt_symbol)

    def get_all_active_orders(self, vt_symbol: str = "") -> List[OrderData]:
        """
        Get all active orders by vt_symbol.

        If vt_symbol is empty, return all active orders.
        """
        if not vt_symbol:
            return list(self.active_orders.values())
        else:
            active_orders = [
                order
                for order in self.active_orders.values()
                if order.vt_symbol == vt_symbol
            ]
            return active_orders

5. 采用这样过滤脏数据的方法的好处

采用这样过滤脏数据的方法,可以从源头一次性地过滤掉脏数据,避免后面的各种应用中分别地对推送的行情数据进行过滤,应用策略无需再考虑脏数据的影响,逻辑简单,效率高。现在再回过头来看帖子 如何更有效地利用合约交易状态信息——交易状态信息管理器 里的方法,就有点效率低下了!

6. 其他接口怎么办?

本文只提供了CTP接口修改方法,没有涉及到其他类型的接口。可是作为一个完善的交易接口,通常都应该提供类似CTP接口中的类似合约交易状态通知的信息。采用的类似的方法也是可以办到的。



防骗公告:vn.py项目的官网,请认准www.vnpy.com

说实话,写这么个公告我的感觉挺奇怪的。

看到过很多其他知名的公司,被别有用心的人冒名顶替,去做一些诈骗忽悠的事情,没想到有一天会发生在vn.py身上。

遇到对vn.py搞碰瓷、蹭流量,没想到现在手段更升级了, 某些现在居然(不提名字)打着vn.py即将倒闭、被他收购的说法,去忽悠金融机构(券商)来和他合作, 还扬言要拿下vnpy.cn域名。

所以在这里专门发布一个公告,vn.py项目的官方社区的网址是

www.vnpy.com

老的论坛“维恩的派”已经停止更新(将会在2019年中关闭),请认准了,谨防李鬼!

尤其需要提防的一些:vnpy.cn、vnpy.com.cn等等非常类似的域名。



获得属于自己的保证金率和手续费(率)

1. 合约信息中包含保证金率

1.1 合约信息查询命令:

ReqQryInstrument : 请求查询合约,填空可以查询到所有合约。
响应:OnRspQryInstrument
◇ 1.函数原型
virtual int ReqQryInstrument(CThostFtdcQryInstrumentField *pQryInstrument, int nRequestID) = 0;
◇ 2.参数
pQryInstrument:查询合约
struct CThostFtdcQryInstrumentField
{
    TThostFtdcInstrumentIDType InstrumentID; ///合约代码
    TThostFtdcExchangeIDType ExchangeID; ///交易所代码
    TThostFtdcExchangeInstIDType ExchangeInstID; ///合约在交易所的代码
    TThostFtdcInstrumentIDType ProductID;///产品代码
};
nRequestID:请求ID,对应响应里的nRequestID,无递增规则,由用户自行维护。
◇ 3.返回
0,代表成功。
-1,表示网络连接失败;
-2,表示未处理请求超过许可数;
-3,表示每秒发送请求数超过许可数。

1.2 合约信息查询结果:

请求查询合约响应,当执行ReqQryInstrument后,该方法被调用。
◇ 1.函数原型
virtual void OnRspQryInstrument(CThostFtdcInstrumentField *pInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};
◇ 2.参数pInstrument:
合约
struct CThostFtdcInstrumentField
{
    TThostFtdcInstrumentIDType InstrumentID;///合约代码
    TThostFtdcExchangeIDType ExchangeID; ///交易所代码
    TThostFtdcInstrumentNameType InstrumentName; ///合约名称
    TThostFtdcExchangeInstIDType ExchangeInstID;///合约在交易所的代码
    TThostFtdcInstrumentIDType ProductID; ///产品代码
    TThostFtdcProductClassType ProductClass; ///产品类型
    TThostFtdcYearType DeliveryYear; ///交割年份
    TThostFtdcMonthType DeliveryMonth;///交割月
    TThostFtdcVolumeType MaxMarketOrderVolume; ///市价单最大下单量
    TThostFtdcVolumeType MinMarketOrderVolume;///市价单最小下单量
    TThostFtdcVolumeType MaxLimitOrderVolume; ///限价单最大下单量
    TThostFtdcVolumeType MinLimitOrderVolume; ///限价单最小下单量
    TThostFtdcVolumeMultipleType VolumeMultiple; ///合约数量乘数
    TThostFtdcPriceType PriceTick; ///最小变动价位
    TThostFtdcDateType CreateDate; ///创建日
    TThostFtdcDateType OpenDate; ///上市日
    TThostFtdcDateType ExpireDate;///到期日
    TThostFtdcDateType StartDelivDate; ///开始交割日
    TThostFtdcDateType EndDelivDate; ///结束交割日
    TThostFtdcInstLifePhaseType InstLifePhase; ///合约生命周期状态
    TThostFtdcBoolType IsTrading;///当前是否交易
    TThostFtdcPositionTypeType PositionType; ///持仓类型
    TThostFtdcPositionDateTypeType PositionDateType;///持仓日期类型
    TThostFtdcRatioType LongMarginRatio;///多头保证金率
    TThostFtdcRatioType ShortMarginRatio; ///空头保证金率
    TThostFtdcMaxMarginSideAlgorithmType MaxMarginSideAlgorithm;///是否使用大额单边保证金算法
    TThostFtdcInstrumentIDType UnderlyingInstrID;///基础商品代码
    TThostFtdcPriceType StrikePrice;///执行价
    TThostFtdcOptionsTypeType OptionsType;///期权类型
    TThostFtdcUnderlyingMultipleType UnderlyingMultiple; ///合约基础商品乘数
    TThostFtdcCombinationTypeType CombinationType;///组合类型
};
VolumeMultiple:合约乘数(同交易所)
PriceTick:最小变动价位(同交易所)
IsTrading:是否活跃(同交易所)
DeliveryYear:交割年份(同交易所)
DeliveryMonth:交割月(同交易所)
OpenDate:上市日(同交易所)
CreateDate:创建日(同交易所)
ExpireDate:到期日(同交易所)
StartDeliveDate:开始交割日(同交易所)
EndDelivDate:结束交割日(同交易所)

同交易所表示这些字段每天更新自交易所,其余字段为柜台设置值。如果发现有些字段值有误,则以此来判断是交易所问题还是CTP柜台设置问题。
pRspInfo:响应信息
struct CThostFtdcRspInfoField
{
    TThostFtdcErrorIDType ErrorID; ///错误代码
    TThostFtdcErrorMsgType ErrorMsg;///错误信息
};
nRequestID:返回用户操作请求的ID,该ID 由用户在操作请求时指定。
bIsLast:指示该次返回是否为针对nRequestID的最后一次返回。

2. 保证金率查询结果中包含保证金

2.1 保证金率查询命令

ReqQryInstrumentMarginRate
请求查询合约保证金率,对应响应OnRspQryInstrumentMarginRate。如果InstrumentID填空,则返回持仓对应的合约保证金率,否则返回相应InstrumentID的保证金率。
目前无法通过一次查询得到所有合约保证金率,如果要查询所有,则需要通过多次查询得到。

◇ 1.函数原型
virtual int ReqQryInstrumentMarginRate(CThostFtdcQryInstrumentMarginRateField *pQryInstrumentMarginRate, int nRequestID) = 0;
◇ 2.参数pQryInstrumentMarginRate:
查询合约保证金率
struct CThostFtdcQryInstrumentMarginRateField
{
    ///经纪公司代码
    TThostFtdcBrokerIDType BrokerID;
    ///投资者代码
    TThostFtdcInvestorIDType InvestorID;
    ///合约代码
    TThostFtdcInstrumentIDType InstrumentID;
    ///投机套保标志
    TThostFtdcHedgeFlagType HedgeFlag;
    ///交易所代码
    TThostFtdcExchangeIDType ExchangeID;
    ///投资单元代码
    TThostFtdcInvestUnitIDType InvestUnitID;
};
nRequestID:请求ID,对应响应里的nRequestID,无递增规则,由用户自行维护。
◇ 3.返回
0,代表成功。
-1,表示网络连接失败;
-2,表示未处理请求超过许可数;
-3,表示每秒发送请求数超过许可数。

2.2 保证金率查询结果

OnRspQryInstrumentMarginRate
请求查询合约保证金率响应,当执行ReqQryInstrumentMarginRate后,该方法被调用。

◇ 1.函数原型
virtual void OnRspQryInstrumentMarginRate(CThostFtdcInstrumentMarginRateField *pInstrumentMarginRate, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};

◇ 2.参数    ///:
合约保证金率
struct CThostFtdcInstrumentMarginRateField
{
    TThostFtdcInstrumentIDType InstrumentID;///合约代码
    TThostFtdcInvestorRangeType InvestorRange;///投资者范围
    TThostFtdcBrokerIDType BrokerID; ///经纪公司代码
    TThostFtdcInvestorIDType InvestorID;///投资者代码
    TThostFtdcHedgeFlagType HedgeFlag; ///投机套保标志
    TThostFtdcRatioType LongMarginRatioByMoney;///多头保证金率
    TThostFtdcMoneyType LongMarginRatioByVolume;///多头保证金费
    TThostFtdcRatioType ShortMarginRatioByMoney; ///空头保证金率
    TThostFtdcMoneyType ShortMarginRatioByVolume; ///空头保证金费
    TThostFtdcBoolType IsRelative;///是否相对交易所收取
    TThostFtdcExchangeIDType ExchangeID;///交易所代码
    TThostFtdcInvestUnitIDType InvestUnitID; ///投资单元代码
};
pRspInfo:响应信息

struct CThostFtdcRspInfoField
{
    TThostFtdcErrorIDType ErrorID;///错误代码
    TThostFtdcErrorMsgType ErrorMsg;///错误信息
};
nRequestID:返回用户操作请求的ID,该ID 由用户在操作请求时指定。

bIsLast:指示该次返回是否为针对nRequestID的最后一次返回。

3. 手续费(率)查询结果中包含手续费

3.1 手续费(率)查询命令

ReqQryInstrumentCommissionRate
请求查询合约手续费率,对应响应OnRspQryInstrumentCommissionRate。如果InstrumentID填空,则返回持仓对应的合约手续费率。
目前无法通过一次查询得到所有合约手续费率,如果要查询所有,则需要通过多次查询得到。
◇ 1.函数原型
virtual int ReqQryInstrumentCommissionRate(CThostFtdcQryInstrumentCommissionRateField *pQryInstrumentCommissionRate, int nRequestID) = 0;
◇ 2.参数pQryInstrumentCommissionRate:
查询手续费率
struct CThostFtdcQryInstrumentCommissionRateField
{
    TThostFtdcBrokerIDType BrokerID; ///经纪公司代码
    TThostFtdcInvestorIDType InvestorID;///投资者代码
    TThostFtdcInstrumentIDType InstrumentID;///合约代码
    TThostFtdcExchangeIDType ExchangeID;///交易所代码
    TThostFtdcInvestUnitIDType InvestUnitID;///投资单元代码
};

InstrumentID:返回手续费率对应的合约。
但是如果在柜台没有设置具体合约的手续费率,则默认会返回产品的手续费率,InstrumentID就为对应产品ID。
nRequestID:请求ID,对应响应里的nRequestID,无递增规则,由用户自行维护。
◇ 3.返回
0,代表成功。
-1,表示网络连接失败;
-2,表示未处理请求超过许可数;
-3,表示每秒发送请求数超过许可数。

3.3 手续费(率)查询结果

OnRspQryInstrumentCommissionRate
请求查询合约手续费率响应,当执行ReqQryInstrumentCommissionRate后,该方法被调用。

◇ 1.函数原型
virtual void OnRspQryInstrumentCommissionRate(CThostFtdcInstrumentCommissionRateField *pInstrumentCommissionRate, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {};
◇ 2.参数pInstrumentCommissionRate:合约手续费率
struct CThostFtdcInstrumentCommissionRateField
{
    TThostFtdcInstrumentIDType InstrumentID; ///合约代码
    TThostFtdcInvestorRangeType InvestorRange; ///投资者范围
    TThostFtdcBrokerIDType BrokerID;///经纪公司代码
    TThostFtdcInvestorIDType InvestorID; ///投资者代码
    TThostFtdcRatioType OpenRatioByMoney; ///开仓手续费率
    TThostFtdcRatioType OpenRatioByVolume; ///开仓手续费
    TThostFtdcRatioType CloseRatioByMoney;///平仓手续费率
    TThostFtdcRatioType CloseRatioByVolume;///平仓手续费
    TThostFtdcRatioType CloseTodayRatioByMoney;///平今手续费率
    TThostFtdcRatioType CloseTodayRatioByVolume;///平今手续费
    TThostFtdcExchangeIDType ExchangeID; ///交易所代码
    TThostFtdcBizTypeType BizType;///业务类型    
    TThostFtdcInvestUnitIDType InvestUnitID;///投资单元代码
};

pRspInfo:
响应信息
struct CThostFtdcRspInfoField
{
    TThostFtdcErrorIDType ErrorID; ///错误代码
    TThostFtdcErrorMsgType ErrorMsg; ///错误信息
};
nRequestID:返回用户操作请求的ID,该ID 由用户在操作请求时指定。

bIsLast:指示该次返回是否为针对nRequestID的最后一次返回。

4. 合约+保证金率+手续费(率)= 完整的合约参数

令:
合约查询结果 = C
保证金率查询结果 = M
手续费查询结果 = S
则:

合约乘数:

C["VolumeMultiple"]

保证金率:

if M["Is_Relative"] == 1:
    多头保证金率 = C["LongMarginRatio"] + M["LongMarginRatioByMoney"] 
    空头保证金率 = C["ShortMarginRatio"] + M["ShortMarginRatioByMoney"] 

else:
多头保证金率 = M["LongMarginRatioByMoney"]
空头保证金率 = M["ShortMarginRatioByMoney"]

手续费(率):

        if S.open_ratio_bymoney == 0.0:
            开仓手续费= [FeeType.LOT,S["OpenRatioByVolume"] ]
            平仓手续费= [FeeType.LOT,S["CloseRatioByVolume"] ]
            平今手续费= [FeeType.LOT,S["CloseTodayRatioByVolume"] ]
        else:
            开仓手续费 = [FeeType.RATE,S["OpenRatioByMoney"] ]
            平仓手续费 = [FeeType.RATE,S["CloseRatioByMoney"] ]
            平今手续费 = [FeeType.RATE,S["CloseTodayRatioByMoney"] ]      


全市场期货数据的批量$和更新

不管是研究套利策略,还是多因子策略,都需要多品种的历史数据,所以下面介绍一下,如何调用vnpy的数据下载模块,来下载全市场的期货数据。

 

批量下载

 

1)设置合约品种

 

首先,我们要先生成一个字典,来指定需要下载的数据,关键字段有3个:

 

  • 交易所代号:上期所-> SHFE
  • 合约代号: 螺纹钢-> RB
  • 合约品种类型: 指数合约 -> 99

 

这样,在RQData中,我们要下载螺纹钢指数合约的历史数据,需要转成的代号为RB99.SHFE。
 

然后,由于是全市场行情的数据,所以字典的数据结构如下:key是交易所,value是列表,里面包含各种期货品种,这样,只要在遍历一下这个字典,就可以得到所有,如RB99.SHFE这样结构的字符串。

 

symbols = {
    "SHFE": ["CU", "AL", "ZN", "PB", "NI", "SN", "AU", "AG", "RB", "WR", "HC", "SS", "BU", "RU", "NR", "SP", "SC", "LU", "FU"],
    "DCE": ["C", "CS", "A", "B", "M", "Y", "P", "FB","BB", "JD", "RR", "L", "V", "PP", "J", "JM", "I", "EG", "EB", "PG"],
    "CZCE": ["SR", "CF", "CY", "PM","WH", "RI", "LR", "AP","JR","OI", "RS", "RM", "TA", "MA", "FG", "SF", "ZC", "SM", "UR", "SA", "CL"],
    "CFFEX": ["IH","IC","IF", "TF","T", "TS"]
}
​
symbol_type = "99"

 
2) 设置下载时间
 

我们只需要设置下载的开始和结束时间即可,需要注意的是,vnpy数据下载模块的入参是datetime.datetime格式,所以,要做到格式的一致,代码如下:
 

from datetime import datetime
start_date = datetime(2005,1,1)
end_date = datetime(2020,9,10)

 
3)批量下载全市场数据

 
批量下载数据,并不难,其运作步骤如下:

 

  1. 遍历symbols字典,
  2. 生成不同的HistoryRequest,
  3. 调用数据下载模块rqdata_client.query_history,得到数据data
  4. 调用数据保存模块database_manager.save_bar_data,把下载好的数据data写入数据库
     
    from vnpy.trader.rqdata import rqdata_client
    from vnpy.trader.database import database_manager
    from vnpy.trader.constant import Exchange, Interval
    from vnpy.trader.object import HistoryRequest
    ​
    def load_data(req):
     data = rqdata_client.query_history(req)
     database_manager.save_bar_data(data)
     print(f"{req.symbol}历史数据下载完成")
    ​
    for exchange, symbols_list in symbols.items():
     for s in symbols_list:
         req = HistoryRequest(
         symbol=s+symbol_type,
         exchange=Exchange(exchange),
         start=start_date,
         interval=Interval.DAILY,
         end=end_date,
         )
         load_data(req=req)
     

写好脚本后,我们运行一下代码,可以看到很快就下完全市场期货的日线数据啦。

description

 

若要下载小时或者分钟级别数据,只要把日线周期(Interval.DAILY)改成对应的小时,或者分钟即可。

 

定时批量更新数据

 

有了历史数据后,自然产生每天定时更新数据的需求

 

1)设置定时器

 
我们希望在收盘后,某个时间点如下午5点启动脚本,来自动下载数据。这本质上是包含了一个父进程和一个子进程。
 

父进程可以是一个永远在运行的python程序,如while循环,然后设置触发条件,如当时间刚好到下午5点就启动子进程下载更新数据,其他时间则睡觉等待。

 
代码如下:

from datetime import datetime, time
from time import sleep
​
current_time = datetime.now().time()
start_time = time(17,0)
​
while True:
  sleep(10)

  if current_time == start_time:
    download_data()

 
2)获取数据库数据

 
更新数据时候,我们要以数据库里面最新的数据的时间点,作为开始时间,而结束时间就是当天。比如,昨天刚好下载完所有市场的数据,那么今天我们只需要下载从昨天到今天的所有数据即可。
 

这样实现起来也不难,步骤如下:
 

1)调用database_manager.get_bar_data_statistics来得到字典格式的数据数据库所有信息

 

data = database_manager.get_bar_data_statistics()

 

2)获取各品种最新数据的时间信息,并且插入到data字典中

 

for d in data:
    newest_bar = database_manager.get_newest_bar_data(
        d["symbol"], Exchange(d["exchange"]), Interval(d["interval"])
    )
    d["end"] = newest_bar.datetime

 

然后我们看看data字典,发现真的包含所有行情的数据,但我们是基于RQData来定期更新信息的,所以要进行筛选,得到国内期货品种(通过交易所来判断)并且是日线级别的数据。
 
description
 

3)基于交易所和K线周期筛选品种,得到新的字典symbols,其中key包含合约代码,交易所,value就是数据库的结束时间,如下图:

 

symbols = {}
for i in data:
    if i["interval"] == "d" and  i["exchange"] in Exchanges:
        vt_symbol = f"{i['symbol']}.{i['exchange']}"
        end = i["end"].date()
        symbols[vt_symbol] = end

description
 
4)设置下载结束时间为当天,基于symbols字典的信息,遍历组合得到HistoryRequest,然后再调用上面定义好的load_data函数下载数据并写入数据库中。

 

end_date = datetime.now().date()
​
for vt_symbol, start_date in symbols.items():
    symbol = vt_symbol.split(".")[0]
    exchange = vt_symbol.split(".")[1]
    req = HistoryRequest(
    symbol=symbol,
    exchange=Exchange(exchange),
    start=start_date,
    interval=Interval.DAILY,
    end=end_date,
    )
    load_data(req=req)

 

下载好之后,我们再获取数据库里面最新的K线时间,发现成功更新到今天了。

 
description



把你编写的指标用图表显示出来

用python编写指标和以往熟悉的文华,博弈大师等有很大的区别,你写的代码最终在图形上是什么样子,看一看才能心安。

于是有了下面这段代码:

from datetime import datetime
from typing import  Dict
from vnpy.trader.constant import Exchange, Interval
from vnpy.trader.database import database_manager
from vnpy.app.cta_strategy import ArrayManager
from vnpy.chart import ChartWidget, VolumeItem, CandleItem
import pyqtgraph as pg
from vnpy.trader.ui import create_qapp, QtCore, QtGui
from vnpy.trader.object import BarData
from vnpy.chart.manager import BarManager


class ZB(CandleItem):
    """自定义指标显示"""

    def __init__(self, manager: BarManager):
        """"""
        super().__init__(manager)

        self.blue_pen: QtGui.QPen = pg.mkPen(color=(100, 100, 255), width=2)
        self.sma_data: Dict[int, float] = {}

    def get_sma_value(self, ix: int) -> float:
        """"""
        if ix < 0:
            return 0

        if not self.sma_data:
            bars = self._manager.get_all_bars()
            sma_array = [bar.down_line for bar in bars]

            for n, value in enumerate(sma_array):
                self.sma_data[n] = value

        if ix in self.sma_data:
            return self.sma_data[ix]

        sma_value = sma_array[-1]

        return sma_value

    def _draw_bar_picture(self, ix: int, bar: BarData) -> QtGui.QPicture:
        """"""
        sma_value = self.get_sma_value(ix)
        last_sma_value = self.get_sma_value(ix - 1)

        # Create objects
        picture = QtGui.QPicture()
        painter = QtGui.QPainter(picture)

        # Set painter color
        painter.setPen(self.blue_pen)

        # Draw Line
        start_point = QtCore.QPointF(ix-1, last_sma_value)
        end_point = QtCore.QPointF(ix, sma_value)
        painter.drawLine(start_point, end_point)

        # Finish
        painter.end()
        return picture

    def get_info_text(self, ix: int) -> str:
        """"""
        if ix in self.sma_data:
            sma_value = self.sma_data[ix]
            text = f"ZB {sma_value:.2f}"
        else:
            text = "ZB -"

        return text

class ZB2(CandleItem):
    """自定义指标显示"""

    def __init__(self, manager: BarManager):
        """"""
        super().__init__(manager)

        self.blue_pen: QtGui.QPen = pg.mkPen(color=(100, 100, 255), width=2)
        self.sma_data: Dict[int, float] = {}

    def get_sma_value(self, ix: int) -> float:
        """"""
        if ix < 0:
            return 0

        if not self.sma_data:
            bars = self._manager.get_all_bars()
            sma_array = [bar.up_line for bar in bars]

            for n, value in enumerate(sma_array):
                self.sma_data[n] = value

        if ix in self.sma_data:
            return self.sma_data[ix]

        sma_value = sma_array[-1]

        return sma_value

    def _draw_bar_picture(self, ix: int, bar: BarData) -> QtGui.QPicture:
        """"""
        sma_value = self.get_sma_value(ix)
        last_sma_value = self.get_sma_value(ix - 1)

        # Create objects
        picture = QtGui.QPicture()
        painter = QtGui.QPainter(picture)

        # Set painter color
        painter.setPen(self.blue_pen)

        # Draw Line
        start_point = QtCore.QPointF(ix-1, last_sma_value)
        end_point = QtCore.QPointF(ix, sma_value)
        painter.drawLine(start_point, end_point)

        # Finish
        painter.end()
        return picture

    def get_info_text(self, ix: int) -> str:
        """"""
        if ix in self.sma_data:
            sma_value = self.sma_data[ix]
            text = f"ZB {sma_value:.2f}"
        else:
            text = "ZB -"

        return text


if __name__ == "__main__":
    app = create_qapp()

    symbol = "CL-20210322-USD-FUT"
    exchange = Exchange.NYMEX
    interval = Interval.MINUTE_30
    start = datetime(2021, 1, 1)
    end = datetime(2022, 1, 1)

    bars = database_manager.load_bar_data(
        symbol=symbol,
        exchange=exchange,
        interval=interval,
        start=start,
        end=end
    )

    am = ArrayManager(50)
    new_data = bars[:]
    line_up = []
    line_down = []

    while new_data :
        bar = new_data.pop(0)
        am.update_bar(bar)
        up, down = am.boll(20,2)
        line_up.append(up)
        line_down.append(down)   #这里调用合适的公式就好了

    print("K线数量是", len(bars), "指标数据是", len(line_up))

    i = 0
    while line_down :
        bars[i].down_line = line_down.pop(0)
        bars[i].up_line = line_up.pop(0)
        i = i + 1
    print("共处理了", i, "数据")

    widget = ChartWidget()
    widget.add_plot("candle", hide_x_axis=True)
    widget.add_plot("volume", maximum_height=250)
    widget.add_item(CandleItem, "candle", "candle")
    widget.add_item(VolumeItem, "volume", "volume")

    widget.add_item(ZB, "ZB", "candle")
    widget.add_item(ZB2, "ZB2", "candle")
    widget.add_cursor()

    history = bars
    widget.update_history(history)

    def update_bar():
        bar = new_data.pop(0)
        widget.update_bar(bar)

    timer = QtCore.QTimer()
    timer.timeout.connect(update_bar)

    widget.show()
    app.exec_()

因为我使用的盈透可以随时下载各种周期的历史数据,所以我只要下载然后存在数据库中。直接用代码读取就可以看见最新的k线。
如果你需要下载盈透的数据,可以参考我的上一个帖子。
https://www.vnpy.com/forum/topic/6123-ying-tou-jie-kou-xia-zai-5fen-zhong-xian-bing-xian-shi-tu-biao?page=1#pid21676

其他的接口我不熟。



Jupyter Notebook实现从IB接口历史数据获取,写入数据库,策略回测和实盘交易

刚好有个同学问怎么实现IB历史数据获取,和策略回测和实盘交易。想着熟悉vnpy2.0操作,就用Jupyter Notebook都是跑了一边。VNPY2.0的整体架构设计很有扩展性,而且调用也比起v1.0先进清晰很多,引擎加载调用非常方便。

讲讲注意点:

  1. IB接口历史数据大多是要收费的订阅的,如果收费会有报错信息提示,这里找个免费的作为使用。另外vnpy是按照最大6个月历史数据设计的。
  2. 数据库定义有个小坑,我是用mongodb的,在第一次填写 trader/setting.py中密码写错了,后面在trader/setting.py改发现怎么也改不好;原来当第一次维护后,配置会写入.vntrader/vt_setting,之后系统只会去.vntrader/vt_setting读取。去改vt_setting,而不是trader/setting.py。
  3. 使用CtaStrategyApp支持加入新策略,系统会自动输出json保持策略信息;所以第二次运行代码时候,会提示已经有了,不是问题。
  4. 我在代码里面把回测和实盘放在一次,如果直接跑下来可能会报错,建议跑实盘时候先注释的回测。
  5. 使用script_engine订阅历史数据是是默认从rqdata获取,vnpy v2.07 IB接口已经提供历史数据获取,这里创建HistoryRequest用main_engine来获取,

为了方便贴出来,改成.py代码格式,直接跑也没有问题。

from vnpy.app.script_trader import init_cli_trading
from vnpy.app.script_trader.cli import process_log_event
from vnpy.gateway.ib import IbGateway
from time import sleep
from datetime import datetime
import pandas as pd
# 连接到服务器
setting = {
    "TWS地址": "127.0.0.1",
    "TWS端口": 7497,
    "客户号":8 #每个链接用一个独立的链接号,一个IBAPI支持32个来同时链接
}
engine = init_cli_trading([IbGateway]) #返回Script_engine 示例,并且给main_engine注册了gateway
engine.connect_gateway(setting, "IB") #链接

# 查询资金 - 自动
sleep(10)
print("***查询资金和持仓***")
print(engine.get_all_accounts(use_df = True))
# 查询持仓
print(engine.get_all_positions(use_df = True))

# 订阅行情
from vnpy.trader.constant import Exchange
from vnpy.trader.object import SubscribeRequest
# 从我测试直接用Script_engine有问题,IB的品种太多,get_all_contracts命令不行,需要指定具体后才可以,这里使用main_engine订阅
req1 = SubscribeRequest("12087792",Exchange.IDEALPRO) #创建行情订阅
engine.main_engine.subscribe(req1,"IB")


# 使用script_engine订阅历史数据是从rqdata获取,vnpy v2.07已经提供历史数据获取,这里创建HistoryRequest来获取,
# 查询如果没有endtime,默认当前。返回历史数据输出到数据库和csv文件
# 关于api更多信息可以参见 https://interactivebrokers.github.io/tws-api/historical_bars.html
print("***从IB读取历史数据, 返回历史数据输出到数据库和csv文件***")
from vnpy.trader.object import HistoryRequest
from vnpy.trader.object import Interval
start = datetime.strptime('20190901', "%Y%m%d")

historyreq = HistoryRequest(
    symbol="12087792",
    exchange=Exchange.IDEALPRO,
    start=start,
    interval=Interval.MINUTE
)
# # 读取历史数据,并把历史数据BarData放入数据库
bardatalist = engine.main_engine.query_history(historyreq,"IB")
from vnpy.trader.database import database_manager
database_manager.save_bar_data(bardatalist)

# 把历史数据BarData输出到csv
pd.DataFrame(bardatalist).to_csv("C:\Project\\"+ str(historyreq.symbol) + ".csv" , index=True, header=True)
print("History data export to CSV")

# # 参考backtesting.ipynb, 使用自带的双均线策略回测,10日上穿60日做多,否则反之
print("***从数据库读取历史数据, 进行回测***")
from vnpy.app.cta_strategy.backtesting import BacktestingEngine
from vnpy.app.cta_strategy.strategies.double_ma_strategy import (
    DoubleMaStrategy,
)
btengine = BacktestingEngine() #新建回测引擎
btengine.set_parameters(
    vt_symbol="12087792.IDEALPRO",
    interval="1m",
    start=datetime(2019, 9, 1),
    end=datetime(2019, 10, 5),
    rate = 0,
    slippage=0.00005,
    size=1000,
    pricetick=0.00005,
    capital=1_000_000,
)
btengine.add_strategy(DoubleMaStrategy, {"fast_window":10, "slow_window": 60})

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

# 给script_engine载入双均线策略,实盘运行
print("***从数据库读取准备数据, 实盘运行***")
# 使用cta交易引擎
from vnpy.app.cta_strategy import CtaStrategyApp
from vnpy.app.cta_strategy.base import EVENT_CTA_LOG
engine.event_engine.register(EVENT_CTA_LOG, process_log_event)
cta_engine = engine.main_engine.add_app(CtaStrategyApp) #加入app
cta_engine.init_engine()
cta_engine.add_strategy("DoubleMaStrategy","DoubleMaStrategy_IB_12087792_v1", "12087792.IDEALPRO",{"fast_window":10, "slow_window": 50})
sleep(10)
cta_engine.init_strategy("DoubleMaStrategy_IB_12087792_v1")
sleep(10)
cta_engine.start_strategy("DoubleMaStrategy_IB_12087792_v1")

新消息

统计

主题
6445
帖子
28503
已注册用户
31272
最新用户
wyz
在线用户
528
在线来宾用户
3799
© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号-3

沪公网安备 31011502017034号

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