前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vn.py入门-低买高卖示例

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

作者头像
用Python的交易员
发布2018-07-26 12:00:31
2.6K0
发布2018-07-26 12:00:31
举报
文章被收录于专栏:维恩的派VNPIE维恩的派VNPIE

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-06-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 维恩的派VNPIE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档