Thenextquant

Thenextquant

  • Docs
  • Tutorial
  • Help
  • Blog

›教程

开始

  • 概述
  • 安装

教程

  • 快速入门
  • 服务配置
  • 日志
  • 市场数据
  • 交易

主要概念

  • 交易所
  • 交易对
  • 现货交易
  • 期货交易
  • 订单

高级指南

  • 定时任务
  • RABBITMQ

API参考

  • 参考指南

操作指南

  • 操作指南

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. 构建项目
  • 2. 服务配置
  • 3. 策略编写
  • 4. 入口文件
  • 5. 一切就位,启动
  • 6. 接下来

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

2. 服务配置config.json

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)

3.3 订单薄回调 on_event_orderbook_update

这里我们将实现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)

3.4 订单状态回调 on_event_order_update

当我们下单后,我们最关心订单的状态,订单是否成交对交易者至关重要,在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

以上代码是如此简洁,是的,我们只需要按以上方式即可获取订单信息,不止是订单状态。

3.5 资产状态回调 on_event_asset_update

量化交易中很重要的一块是对资产的管理,我们很关心我们的仓位变化,在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。

4. 入口文件main.py

上面我们只是定义了策略,怎么让它执行,没错,我们需要入口文件,代码如下:

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。

← 安装服务配置 →
  • 1. 构建项目
    • 1.1 项目结构
  • 2. 服务配置config.json
    • 2.1 交易平台及账户设置
    • 2.2 策略名称设置
    • 2.3 策略交易对设置
  • 3. 策略编写
    • 3.1 类初始化
    • 3.2 策略初始化
    • 3.3 订单薄回调 on_event_orderbook_update
    • 3.4 订单状态回调 on_event_order_update
    • 3.5 资产状态回调 on_event_asset_update
  • 4. 入口文件main.py
  • 5. 一切就位,启动
  • 6. 接下来
Thenextquant
文档
Getting StartedGuidesAPI Reference
介绍
框架介绍关于我们
More
BlogGitHubStar
Thenextquant Open Source
Copyright © 2019 Thenextquant