tutorial
What's In This Document
update: 20190530
这里假定你可以直连币安(Binance)交易所API;
这里假定你会安装rabbitmq并会进行简单配置和启动;
本示例简单实现了币安(Binance)
ETH/BTC
交易对买单简单maker策略:策略首先订阅了
ETH/BTC
的实时订单薄;根据当前买3(bid3)和买4(bid4)的价格,计算买3和买4的平均价格(price = (bid3+bid4)/2.0);
判断当前策略是否已经有挂单,如果没有挂单, 那么直接使用price挂单;如果已经挂单,判断挂单价格是否超出当前bid3和bid4的价格区间,超出则撤单之后重新挂单;
同时对已下单的订单状态进行实时跟踪,当订单状态更新时您可以进行下一步策略(比如记录、通知、对冲...)
1. 构建项目
A. 确保您已经安装了thenextquant
,并且是最新的版本:
# install
$ pip install thenextquant
# upgrade
$ pip install --upgrade thenextquant
B. 打开您熟悉的IDE,构建如下项目:
# create project
$ cd /path
$ mkdir quantdemo
$ cd quantdemo
1.1 项目结构
建议按如下工程结构创建您的项目文件。
quantdemo
├── README.md
├── config.json
├── main.py
└── strategy
└── strategy.py
config.json
2. 服务配置2.1 交易平台及账户设置
编辑您上面已经创建的config.json
文件:
quantdemo/config.json
{
"RABBITMQ": {
"host": "127.0.0.1",
"port": 5672,
"username": "test",
"password": "123456"
},
"PLATFORMS": {
"binance": {
"account": "quantdemo@thenextquant.com",
"access_key": "Iymq8hQAP5akBn4hLmfANPi7zYej3US6rkPfj636jeQ5G1bMSNRkGxlxp3FyT1Zx",
"secret_key": "TnZpevlsicv7nB5NVkVA5XWOsMBE1FNujazJ14oHs2DifSV9BhBhh2PX3SOYNIGq"
}
}
}
我们在这里设置了rabbitmq
的连接信息为什么选择rabbitmq
作为消息中间件,请参考why rabbitmq,接着指定交易平台为binance
,并设置了该平台账户的api
信息。
2.2 策略名称设置
继续在config.json
文件中增加如下:
quantdemo/config.json
"strategy": "stgA",
此时config.json
文件内容如下:
quantdemo/config.json
{
"RABBITMQ": {
"host": "127.0.0.1",
"port": 5672,
"username": "test",
"password": "123456"
},
"PLATFORMS": {
"binance": {
"account": "quantdemo@thenextquant.com",
"access_key": "Iymq8hQAP5akBn4hLmfANPi7zYej3US6rkPfj636jeQ5G1bMSNRkGxlxp3FyT1Zx",
"secret_key": "TnZpevlsicv7nB5NVkVA5XWOsMBE1FNujazJ14oHs2DifSV9BhBhh2PX3SOYNIGq"
}
},
"strategy": "stgA"
}
这里我们新增设置了策略名称为stgA
。
2.3 策略交易对设置
继续在config.json
文件中增加如下:
quantdemo/config.json
"symbol": "ETH/BTC"
此时config.json
文件内容如下:
quantdemo/config.json
{
"RABBITMQ": {
"host": "127.0.0.1",
"port": 5672,
"username": "test",
"password": "123456"
},
"PLATFORMS": {
"binance": {
"account": "quantdemo@thenextquant.com",
"access_key": "Iymq8hQAP5akBn4hLmfANPi7zYej3US6rkPfj636jeQ5G1bMSNRkGxlxp3FyT1Zx",
"secret_key": "TnZpevlsicv7nB5NVkVA5XWOsMBE1FNujazJ14oHs2DifSV9BhBhh2PX3SOYNIGq"
}
},
"strategy": "stgA",
"symbol": "ETH/BTC"
}
这里我们设置策略的交易对为ETH/BTC
,到这里我们的config.json
就配置完成了。
3. 策略编写
3.1 类初始化
打开strategy/strategy.py
,新建类MyStrategy
如下:
quantdemo/strategy/strategy.py
from quant import const
from quant.config import config
class MyStrategy:
def __init__(self):
""" 初始化
"""
self.platform = const.BINANCE
self.account = config.platforms.get(self.platform, {}).get("account")
self.access_key = config.platforms.get(self.platform, {}).get("access_key")
self.secret_key = config.platforms.get(self.platform, {}).get("secret_key")
self.symbol = config.symbol
self.stg_name = config.strategy
self.order_no = None # 创建订单的id
self.create_order_price = "0.0" # 创建订单的价格
这里我们首先导入了框架常量库const
:
from quant import const
接着我们导入了框架底层为了读取config.json
文件的config
模块:
from quant.config import config
我们新建了一个名为MyStrategy
的类,并进行了简单初始化:
# 设置交易平台BINANCE;
# 交易账户及其api key;
# 策略的交易对和名称;
# 并且自定义了订单号和订单价格。
def __init__(self):
""" 类初始化
"""
self.platform = const.BINANCE
self.account = config.platforms.get(self.platform, {}).get("account")
self.access_key = config.platforms.get(self.platform, {}).get("access_key")
self.secret_key = config.platforms.get(self.platform, {}).get("secret_key")
self.symbol = config.symbol
self.name = config.strategy
self.order_no = None # 创建订单的id
self.create_order_price = "0.0" # 创建订单的价格
3.2 策略初始化
继续编辑strategy/strategy.py
,在文件头部导入如下模块和常量:
quantdemo/strategy/strategy.py
from quant.market import Market
from quant.trade import Trade
我们导入了框架的两个核心模块Market
和Trade
,这里不做过多解释。
在类MyStrategy
中的初始化方法中加入以下代码:
quantdemo/strategy/strategy.py
class MyStrategy:
def __init__(self):
""" 类初始化
"""
# ...code... #
# for market
Market(const.MARKET_TYPE_ORDERBOOK,
self.platform,
self.symbol,
self.on_event_orderbook_update)
# for trade
self.trade = Trade(strategy=self.stg_name,
platform=self.platform,
symbol=self.symbol,
account=self.account,
access_key = self.access_key,
secret_key = self.secret_key,
asset_update_callback=self.on_event_asset_update,
order_update_callback=self.on_event_order_update)
这里我们做了几件事情:
A. 在MyStrategy
中调用了Market
模块,并定义了self.trade
;
B. 我们向框架订阅了平台(self.platform
)交易对(self.symbol
)的订单薄(MARKET_TYPE_ORDERBOOK
),并设置其回调方法为self.on_event_orderbook_update
(我们还未定义该方法);
C. 我们通过框架初始化了trade模块,并在里面注册了两个回调方法self.on_event_asset_update
和self.on_event_order_update
(我们还未定义该方法);
D. asset_update_callback
表示资产更新时,框架会推送消息给我们;
E. order_update_callback
表示订单更新时,框架会推送消息给我们。
此时quantdemo/strategy/strategy.py
的内容应该如下(希望您能跟上我的脚步):
from quant import const
from quant.config import config
from quant.market import Market
from quant.trade import Trade
class MyStrategy:
def __init__(self):
""" 类初始化
"""
self.platform = const.BINANCE
self.account = config.platforms.get(self.platform, {}).get("account")
self.access_key = config.platforms.get(self.platform, {}).get("access_key")
self.secret_key = config.platforms.get(self.platform, {}).get("secret_key")
self.symbol = config.symbol
self.stg_name = config.strategy
self.order_no = None # 创建订单的id
self.create_order_price = "0.0" # 创建订单的价格
# for market
Market(const.MARKET_TYPE_ORDERBOOK,
self.platform,
self.symbol,
self.on_event_orderbook_update)
# for trade
self.trade = Trade(strategy=self.stg_name,
platform=self.platform,
symbol=self.symbol,
account=self.account,
access_key=self.access_key,
secret_key=self.secret_key,
asset_update_callback=self.on_event_asset_update,
order_update_callback=self.on_event_order_update)
on_event_orderbook_update
3.3 订单薄回调 这里我们将实现on_event_orderbook_update
方法,注意这里的方法是异步的,而大部分框架提供的方法都是同步的,非常低效。
async def on_event_orderbook_update(self, orderbook):
""" 订单薄更新
"""
logger.debug("orderbook:", orderbook, caller=self)
bid3_price = orderbook["bids"][2][0] # 买三价格
bid4_price = orderbook["bids"][3][0] # 买四价格
# 判断是否需要撤单
if self.order_no:
if float(bid4_price) < float(self.create_order_price) < float(bid3_price):
return
await self.trade.revoke_order(self.order_no)
self.order_no = None
logger.info("revoke order:", self.order_no, caller=self)
# 创建新订单
new_price = (float(bid3_price) + float(bid4_price)) / 2
quantity = 0.1 # 假设委托数量为0.1
action = ORDER_ACTION_BUY
price = tools.float_to_str(new_price)
quantity = tools.float_to_str(quantity)
# limit price order
order_no = await self.trade.create_order(action, price, quantity)
self.order_no = order_no
self.create_order_price = price
logger.info("create new order:", order_no, caller=self)
这里的代码比较好理解,我们还是过一遍:
A. 首先我们通过框架拿到orderbook
,获取买3
和买4
价格;
B. 我们判断之前是否已经下单,如果已经下单,进一步判断它的价格是否符合我们预设的条件(在买3
和买4
之间)。符合则什么都不做;否则就撤单;
C. 撤单后我们买3
和买4
价格重新下单(create_order
),并将该订单的价格和订单号保存到上下文中。
现在quantdemo/strategy/strategy.py
的内容应该如下(我们快成功了):
from quant import const
from quant.config import config
from quant.market import Market
from quant.trade import Trade
from quant.utils import logger, tools
from quant.order import ORDER_ACTION_BUY
class MyStrategy:
def __init__(self):
""" 类初始化
"""
self.platform = const.BINANCE
self.account = config.platforms.get(self.platform, {}).get("account")
self.access_key = config.platforms.get(self.platform, {}).get("access_key")
self.secret_key = config.platforms.get(self.platform, {}).get("secret_key")
self.symbol = config.symbol
self.stg_name = config.strategy
self.order_no = None # 创建订单的id
self.create_order_price = "0.0" # 创建订单的价格
# for market
Market(const.MARKET_TYPE_ORDERBOOK,
self.platform,
self.symbol,
self.on_event_orderbook_update)
# for trade
self.trade = Trade(strategy=self.stg_name,
platform=self.platform,
symbol=self.symbol,
account=self.account,
access_key=self.access_key,
secret_key=self.secret_key,
asset_update_callback=self.on_event_asset_update,
order_update_callback=self.on_event_order_update)
async def on_event_orderbook_update(self, orderbook):
""" 订单薄更新
"""
logger.debug("orderbook:", orderbook, caller=self)
bid3_price = orderbook["bids"][2][0] # 买三价格
bid4_price = orderbook["bids"][3][0] # 买四价格
# 判断是否需要撤单
if self.order_no:
if float(bid4_price) < float(self.create_order_price) < float(bid3_price):
return
await self.trader.revoke_order(self.order_no)
self.order_no = None
logger.info("revoke order:", self.order_no, caller=self)
# 创建新订单
new_price = (float(bid3_price) + float(bid4_price)) / 2
quantity = 0.1 # 假设委托数量为0.1
action = ORDER_ACTION_BUY
price = tools.float_to_str(new_price)
quantity = tools.float_to_str(quantity)
# limit price order
order_no = await self.trade.create_order(action, price, quantity)
self.order_no = order_no
self.create_order_price = price
logger.info("create new order:", order_no, caller=self)
on_event_order_update
3.4 订单状态回调 当我们下单后,我们最关心订单的状态,订单是否成交对交易者至关重要,在thenextquant
中,我们可以很方便的获取订单状态更新的情况(注意,这里同样是异步(async
)获取):
async def on_event_order_update(self, order):
""" 订单状态更新
"""
logger.info("order update:", order, caller=self)
# 如果订单失败、订单取消、订单完成交易
if order.status in [ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED]:
self.order_no = None
以上代码是如此简洁,是的,我们只需要按以上方式即可获取订单信息,不止是订单状态。
on_event_asset_update
3.5 资产状态回调 量化交易中很重要的一块是对资产的管理,我们很关心我们的仓位变化,在Thenextquant
框架中,您可以很容易驾驭您的资产,我们同样不需要同步去获取资产信息,而是通过底层框架异步(async
)获取资产信息。
async def on_event_asset_update(self, asset):
""" 资产更新
"""
logger.info("asset:", asset, caller=self)
一行代码即可在资产变更时获取当前资产详情。
截止目前quantdemo/strategy/strategy.py
的代码如下:
from quant import const
from quant.config import config
from quant.market import Market
from quant.trade import Trade
from quant.utils import logger, tools
from quant.order import ORDER_ACTION_BUY, ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED
class MyStrategy:
def __init__(self):
""" 类初始化
"""
self.platform = const.BINANCE
self.account = config.platforms.get(self.platform, {}).get("account")
self.access_key = config.platforms.get(self.platform, {}).get("access_key")
self.secret_key = config.platforms.get(self.platform, {}).get("secret_key")
self.symbol = config.symbol
self.stg_name = config.strategy
self.order_no = None # 创建订单的id
self.create_order_price = "0.0" # 创建订单的价格
# for market
Market(const.MARKET_TYPE_ORDERBOOK,
self.platform,
self.symbol,
self.on_event_orderbook_update)
# for trade
self.trade = Trade(strategy=self.stg_name,
platform=self.platform,
symbol=self.symbol,
account=self.account,
access_key=self.access_key,
secret_key=self.secret_key,
asset_update_callback=self.on_event_asset_update,
order_update_callback=self.on_event_order_update)
async def on_event_orderbook_update(self, orderbook):
""" 订单薄更新
"""
logger.debug("orderbook:", orderbook, caller=self)
bid3_price = orderbook["bids"][2][0] # 买三价格
bid4_price = orderbook["bids"][3][0] # 买四价格
# 判断是否需要撤单
if self.order_no:
if float(bid4_price) < float(self.create_order_price) < float(bid3_price):
return
await self.trader.revoke_order(self.order_no)
self.order_no = None
logger.info("revoke order:", self.order_no, caller=self)
# 创建新订单
new_price = (float(bid3_price) + float(bid4_price)) / 2
quantity = 0.1 # 假设委托数量为0.1
action = ORDER_ACTION_BUY
price = tools.float_to_str(new_price)
quantity = tools.float_to_str(quantity)
# limit price order
order_no = await self.trade.create_order(action, price, quantity)
self.order_no = order_no
self.create_order_price = price
logger.info("create new order:", order_no, caller=self)
async def on_event_asset_update(self, asset):
""" 资产更新
"""
logger.info("asset:", asset, caller=self)
async def on_event_order_update(self, order):
""" 订单状态更新
"""
logger.info("order update:", order, caller=self)
# 如果订单失败、订单取消、订单完成交易
if order.status in [ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED]:
self.order_no = None
到这里,恭喜您,你是英雄,已经完成了98%的工作,接下来我们要构建入口文件main.py
。
main.py
4. 入口文件上面我们只是定义了策略,怎么让它执行,没错,我们需要入口文件,代码如下:
quantdemo/main.py
import sys
def initialize():
from strategy.strategy import MyStrategy
MyStrategy()
def main():
if len(sys.argv) > 1:
config_file = sys.argv[1]
else:
config_file = None
from quant.quant import quant
quant.initialize(config_file)
initialize()
quant.start()
if __name__ == '__main__':
main()
A. 我们导入并初始化我们的策略MyStrategy
;
B. 我们定义了main
方法,引入quant
框架,并通过config
文件(config.json
)来对其进行初始化;
C. 在quant
框架准备好后,我们初始化策略,最后启动整个框架。
5. 一切就位,启动
# 进入当前工程目录
$ cd quantdemo
# 执行代码
$ python main.py config.json
6. 接下来
本框架使用的是python原生异步库(asyncio
)实现异步事件驱动,所以在使用之前,需要先了解 python asyncio。