专栏首页维恩的派VNPIEvn.py入门-低买高卖示例

vn.py入门-低买高卖示例

1

前言

本文用一个例子来介绍vnpy的用法。从项目创建开始,到一个简单策略的设计。 这个例子连接到CTP接口,每秒检查一下目标合约的价格,若低于指定价格则买入,若高于指定价格则卖出。

2

创建项目

首先,请看前篇《手动搭建vnpy环境-编程环境》,依步骤创建一个项目。然后找个目录添加以下文件:

  • buy_low_sell_high.py : 主脚本
  • CTP_connect.json : CTP连接参数

3

初始化vn.py

首先,我们创建一个EventEngine和MainEngine。这次我们没有UI,用EventEngine2就好了。

from vnpy.trader.vtEngine import *

from vnpy.trader.uiQt import createQApp

class Main: def __init__(self): self.event_engine = EventEngine() self.main_engine = MainEngine(eventEngine=self.event_engine) def start(self): pass

def main(): app = createQApp() m = Main() m.start() exit(app.exec_())

其中有用的代码就4行,其他都是为了方便下文设计的结构。

4

连接CTP

添加CTP接口

使用MainEngine.addGateway可以添加接口,在该例子中,我们使用CTP接口。

from vnpy.trader.gateway import ctpGateway

class Main: def __init__(self):

self.main_engine.addGateway(gatewayModule=ctpGateway)

连接到服务器

使用MainEngine.connect可以连接到服务器。不过在此之前,我们需要在CTP_connection.json中设置好连接参数。 在CTP_connect.json中填写如下内容:

{ "brokerID": "9999", "mdAddress": "tcp://218.202.237.33:10012", "tdAddress": "tcp://218.202.237.33:10002", "userID": "??????", "password": "??????"

}

其中:

  • brokerID : 填写你的brokerID
  • mdAddress : 填写你要使用的行情服务器地址,格式是协议://ip:port
  • tdAddress : 填写你要使用的交易服务器地址
  • userID : 填写你的用户ID
  • password : 填写你的登录密码

填好CTP_connect.json之后,添加如下代码:

class Main: def start(self): self.main_engine.connect(ctpGateway.gatewayName)

此时,如果一切正确,运行py脚本,你应该能看到如下文本:

2018-06-12 04:42:50,486 INFO:MAIN_ENGINEMongoDB连接成功

2018-06-12 04:42:50,503 INFO: CTP行情服务器连接成功

2018-06-12 04:42:50,503 INFO: CTP交易服务器连接成功

2018-06-12 04:42:50,505 INFO: CTP行情服务器连接断开

2018-06-12 04:42:50,506 INFO: CTP交易服务器连接断开

订阅行情

连接到了服务器,我们需要知道目标合约的价格是多少,才能做出买还是卖的决策。 所以我们需要订阅行情。使用MainEngine.subscribe就可以订阅行情了。

class Main: def subscribe_market_data(self): """订阅我们感兴趣的合约的行情""" subscribe_req = VtSubscribeReq() subscribe_req.symbol = target_symbol # 合约代码 # subscribe_req.exchange = constant.EMPTY_STRING # 交易所代码 self.main_engine.subscribe(subscribe_req, ctpGateway.gatewayName) def __init__(self): self.subscribe_market_data()

当你订阅行情之后,服务器会向你推送行情。vnpy在这里展现出了它友好的一面:vnpy会自动缓存行情,而不需要我们自己手动去分析服务器推送的行情。我们需要查看行情的时候,只需要调用MainEngine.getTick即可。

接下来,我们就监听一个vnpy自带的1HZ的计时器,然后查询当前的行情。如果低于指定价格就买入,如果高于制定价格就卖掉。

target_symbol = 'cu1807' # 目标合约predicted_buy_price = 54100 # 低于此价格则买入

predicted_sell_price = 54200 # 高于此价格则卖出

class Main: def last_price(self, symbol): """获取某个合约的当前价格""" tick = self.main_engine.getTick(target_symbol) # type: VtTickData return tick.lastPrice if tick else None # 判断一下,因为tick有可能是None def on_timer(self, event): """1hz 计时器响应函数""" price = self.last_price(target_symbol) if price < predicted_buy_price: self.buy_one() else: self.sell_all()

至此,我们需要的行情已经ok了。我们应该在#1处买入,在#2处卖出。但是在买入和卖出之前,我们必须先了解自己的仓位信息:如果你已经买入很多了,就别再买了。同理,如果你什么都没有了,就没法卖了(虽然有做空,但是现在先这样简化一下策略)。

获取当前仓位

使用MainEngine.getAllPositions()可以获取当前仓位,我们把相应的代码加入on_timer中:

class Main: def current_long_position(self, symbol): """返回买的仓位""" for data in self.main_engine.getAllPositions(): # type: VtPositionData if data.symbol == symbol and data.direction == constant.DIRECTION_LONG: return data.position return 0 # 替换掉原来的on_timer def on_timer(self, event): current_position = self.current_long_position(target_symbol) price = self.last_price(target_symbol) if price: if price < predicted_buy_price: if current_position == 0: self.buy_one() else: # price >= predicted_sell_price: if current_position > 0: self.sell_all()

这样,我们的策略就没问题了。接下来只要完成买入和卖出的操作就可以了。

5

买入卖出

MainEngine.sendOrder可以下单,无论是买入还是卖出都可以通过这个函数完成。 在卖出之前,我们可以用上文提到的MainEngine.getAllPositions()获取当前的仓位,然后根据仓位下单。

下面是买入和卖出的代码:

class Main: def buy_one(self): """买开我们感兴趣的合约一手""" order = VtOrderReq() order.symbol = target_symbol # 合约代码 # order.exchange = constant.EMPTY_STRING

# 交易所, ctp中不需要 # order.vtSymbol = symbol

# VT合约代码, ctp中不需要 order.price = 0 # 价格 order.volume = 1 # 数量 order.direction = constant.DIRECTION_LONG # 买/卖(多头、空头) order.priceType = constant.PRICETYPE_MARKETPRICE # 价格类型 order.offset = constant.OFFSET_OPEN # 开平 # order.currency = currency # 货币类型, ctp中不需要 # order.productClass = productClass # 合约类型, ctp中不需要

self.main_engine.sendOrder(order,ctpGateway.gatewayName)

def sell(self, symbol, direction, volume): """平仓""" order = VtOrderReq() order.symbol = symbol order.price = 0 order.volume = volume order.direction = direction order.priceType = constant.PRICETYPE_MARKETPRICE order.offset = constant.OFFSET_CLOSE # 平仓 self.main_engine.sendOrder(order, ctpGateway.gatewayName) def sell_all_volume(self): """平掉所有持仓""" for position in self.main_engine.getAllPositions():

# type: VtPositionData self.sell(position.symbol, position.direction, position.position) def sell_all(self): """平掉所有持仓""" self.sell_all_volume()

至此,我们的策略就算完成了。运行程序,程序就会自动低买高卖啦!

6

总结

vnpy将许多工作都简化了,所以我们要做的事情其实非常简单,虽然看起来代码挺多,但是实际上用到的东西非常少。我们来回顾一下吧:

  • createQApp : 初始化Qt环境。
  • EventEngine, MainENgine : 必要步骤
  • MainEngine.subscribe : 订阅行情
  • MainEngine.connect : 连接到服务器
  • MainEngine.getTick : 获取实时行情(价格)
  • MainEngine.getAllPositions() : 获取仓位信息
  • MainEngine.sendOrder() : 下单 这样一看是不是觉得非常简单了呢?

成品代码

下面附上整个程序的代码(和本文的实例稍稍不同,但是大体上还是一致的):

# encoding: UTF-8

from vnpy.event import EventEngine2 as EventEngine

from vnpy.trader.gateway import ctpGateway

from vnpy.trader.vtEngine import *

from vnpy.trader.uiQt import createQApp

target_symbol = 'cu1807'

predicted_buy_price = 54100

predicted_sell_price = 54200

class Main: def __init__(self): self.fully_initialized = False self.price_initialized = False self.subscribed = False self.timer_count = 0 self.event_engine = EventEngine() self.event_engine.register(EVENT_TIMER, self.on_timer) self.event_engine.register(EVENT_TICK, self.on_tick) self.main_engine = MainEngine(eventEngine=self.event_engine)

self.main_engine.addGateway(gatewayModule=ctpGateway) # 为了在非交易日也能获取行情,我修改了一下调用的先后顺序 # 这句话在调到on_timer里面去了。 # self.subscribe_market_data() pass def subscribe_market_data(self): """订阅我们感兴趣的合约的行情""" subscribe_req = VtSubscribeReq() subscribe_req.symbol = target_symbol # 合约代码 # subscribe_req.exchange = constant.EMPTY_STRING # 交易所代码 self.main_engine.subscribe(subscribe_req, ctpGateway.gatewayName)

def current_long_position(self, symbol): """返回买的仓位""" for data in self.main_engine.getAllPositions(): # type: VtPositionData if data.symbol == symbol and data.direction == constant.DIRECTION_LONG: return data.position return 0 def last_price(self, symbol): """获取某个合约的当前价格""" tick = self.main_engine.getTick(symbol) # type: VtTickData return tick.lastPrice if tick else None def do_strategy(self): """很简单的策略:高于特定价格就卖,低于特定价格就买""" current_position = self.current_long_position(target_symbol) price = self.last_price(target_symbol) if price: if price < predicted_buy_price: if current_position == 0: self.buy_one() else: # price >= predicted_sell_price: if current_position > 0: self.sell_all() def buy_one(self): """买开我们感兴趣的合约一手""" order = VtOrderReq() order.symbol = target_symbol # 合约代码 # order.exchange = constant.EMPTY_STRING # 交易所, ctp中不需要 # order.vtSymbol = symbol # VT合约代码, ctp中不需要 order.price = 0 # 价格 order.volume = 1 # 数量 order.direction = constant.DIRECTION_LONG # 买/卖(多头、空头) order.priceType = constant.PRICETYPE_MARKETPRICE # 价格类型 order.offset = constant.OFFSET_OPEN # 开平 # order.currency = currency # 货币类型, ctp中不需要 # order.productClass = productClass # 合约类型, ctp中不需要 self.main_engine.sendOrder(order, ctpGateway.gatewayName) def sell(self, symbol, direction, volume): """平仓""" order = VtOrderReq() order.symbol = symbol order.price = 0 order.volume = volume order.direction = direction order.priceType = constant.PRICETYPE_MARKETPRICE order.offset = constant.OFFSET_CLOSE # 平仓 self.main_engine.sendOrder(order, ctpGateway.gatewayName) def cancel_all_working_orders(self): """撤掉所有挂单""" for order in self.main_engine.getAllWorkingOrders(): # type: VtOrderData self.main_engine.cancelOrder(order, order.gatewayName) pass def sell_all_volume(self): """平掉所有持仓""" for position in self.main_engine.getAllPositions(): # type: VtPositionData self.sell(position.symbol, position.direction, position.position) def sell_all(self): """撤掉所有挂单、平掉所有持仓""" self.cancel_all_working_orders() self.sell_all_volume() def start(self): self.main_engine.connect(ctpGateway.gatewayName) def on_timer(self, event): # 如果还没订阅行情,则订阅行情。 # 在这里订阅行情,即使在非交易日也能获取一个行情消息 if not self.subscribed: self.subscribe_market_data() self.subscribed = True # 等待10s,让DataEngine(MainEngine的一部分)缓存一些数据,例如仓位、资金之类的 if not self.fully_initialized: self.timer_count += 1 if self.timer_count > 10: self.fully_initialized = True return # 等完10s,再开始我们的策略 self.do_strategy() pass

def on_tick(self, event): pass

def main(): app = createQApp() # 初始化Qt环境 m = Main() # 初始化我们的策略程序 m.start() # 连接到服务器 exit(app.exec_()) # 让程序自己跑

if __name__ == '__main__': main()

至于EventEngine和MainEngine中各种函数的用法,可以看《vnpy中的EventEngine和MainEngine用法介绍》文中的代码都是片段。所有的代码除非有提示,否则都是添加进原来的代码中的,不要覆盖原来的代码。

基于python的开源交易平台开发框架。截止目前,vn.py项目在Github上的Star已经达到5787,量化交易类开源项目第1,量化类项目第3(1、2依旧分别是Zipline和TuShare)。

项目官网:http://www.vnpy.org

论坛地址:www.vnpie.com

知乎专栏:https://zhuanlan.zhihu.com/vn-py

Developed by Traders,

for Traders

本文分享自微信公众号 - 维恩的派VNPIE(vn-pie)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-06-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何用vn.py做隔夜交易?

    本文提供了一个每个交易日开盘前不用重连CTP的方法。如果不是特殊需求,强烈建议每天盘前重启程序。感谢viponedream在维恩的派论坛里的分享!

    用Python的交易员
  • R-Breaker策略

    本文提供了一个用vn.py来编写R-breaker交易策略的示例。只提供一个参考模板,并不能直接进入市场进行交易。感谢‘爱谁谁’在维恩的派论坛里的分享!

    用Python的交易员
  • CTP接口入门

    本文主要面向有C++基础,并且想用C++来做程序化交易的用户。 主要介绍了CTP的简单使用方式以及在使用过程中易遇到的‘坑’,并附上一些代码帮助学习。

    用Python的交易员
  • Python学习——数据模型/特殊方法

    数据模型其实是对Python框架的描述,它规范了这门语言自身构架模块的接口,这些模块包括但不限于序列、迭代器、函数、类和上下文管理器。简单来说,数据模型就是Py...

    陆勤_数据人网
  • 教你制作可移动的导航栏

    Dwyane
  • 我的tkinter学习笔记4

    用户6367961
  • python操作mysql数据库

    py3study
  • 数据结构(二):栈

    py3study
  • python3爬虫-通过requests

    py3study
  • PyQt5--QProgressBar

    py3study

扫码关注云+社区

领取腾讯云代金券