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

2018-07-26 12:00:31 浏览数 (1)

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

0 人点赞