对于币圈量化老司机来说,相信或多或少都有接触过ccxt这个接口,ccxt为我们提供了多交易所统一的标准格式API,方便我们做两件事:第一,跨市场的策略,可以轻松对接不同交易所;第二,同一个策略,可以在几乎不修改代码的前提下,移植到不同的交易所。

大多数情况下,我们使用ccxt接口连接行情、交易,默认接入的都是币币交易(spot)。其实,ccxt还有一个强大的隐藏功能,它也能用来交易数字货币期货,也就是我们通常说的合约,包括交割合约、永续合约、币本位合约、U本位合约,只要ccxt封装了交易所的相关API,我们就可以实现对应的交易。有了这个利器,我们甚至可以用ccxt做一些期现套利,以及跨交易所的期货策略。

ccxt的官方帮助文档中,并没有详细介绍如何用ccxt交易合约,我们通过这篇文章为大家总结。

CCXT的显式调用与隐式调用
介绍如何使用ccxt调用期货方法之前,我们先来看两个重要的概念:显式调用与隐式调用。

显式调用很好理解,就算用ccxt封装的统一接口,来完成行情获取、交易等相关的操作。对于ccxt所支持的每一个交易所,我们都可以用同样的方式,只需更改交易所的名称,即可用同样的代码,在不同交易所,完成同样的操作。我们来看以下代码示例:

import ccxtbinance = ccxt.binance()okex = ccxt.okex()spot_symbol = "BTC/USDT"# fetch_order_bookspot_depth = binance.fetch_order_book(spot_symbol)spot_depth = okex.fetch_order_book(spot_symbol)# limit_buy_order binance.create_limit_buy_order(spot_symbol, amount, price)okex.create_limit_buy_order(spot_symbol, amount, price)

关于显式调用,ccxt的帮助文档中已经介绍的比较详细了,大家有不清楚的可以参照github上ccxt的manual,我们在这里不在赘述。我们重点来看ccxt的隐式调用。隐式调用在ccxt的官方文档中,几乎是一笔带过,仅仅告诉我们,使用dir()命令,我们可以获取该交易所ccxt支持的隐式方法。
我们根据上述的说明,以币安交易所为例,通过dir(ccxt.binance()),查看币安交易所的隐式方法,由于隐式的方法实在太多,我们只能截取部分方法来显示。在这些方法中,我们看到了一些共同的抬头,比如dapi、fapi、sapi,其实,这些抬头的API,就分别是币安某个合约接口(比如u本位永续、币本位交割、币本位永续)的特有类别的API。如此多的方法,让我们看的眼花缭乱,那么我们如何知道,每个方法该如何使用,参数又该如何传入、传入哪些。

这就需要我们对照币安交易所的API,来为大家解释了。我们以u本位合约获取所有交易对的api为例,我们先看币安api说明对于这个方法的描述,仔细看这个api的地址GET /fapi/v1/exchangeInfo。我们再在dir(ccxt.binance())的显示结果中搜索“exchangeInfo”。我们看到fapi开头的方法中,有如下几个,这些方法,其实是一个方法(ccxt分为驼峰和下划线,两种命名方式),fapiPublicGetExchangeInfo,这个方法,是将GET /fapi/v1/exchangeInfo这个原始api的关键词,按照一定的顺序排列组合起来,并且在里面加上了Public。因为这是一个所有人都可以查询得到的公有方法。如果方法中包含Private字样,说明该方法是原始api中的私有方法。所以说,我们对于每个原始api,都可以用他的关键词,在ccxt的隐式方法列表中查询,如果能够查询到该关键词的方法,而且方法的其他字段,也与该方法的原始请求地址一致,那么说明该方法,极有可能就是原始api封装的隐式方法。我们通过实例来验证,由于fapiPublicGetExchangeInfo是不需要传入参数的,我们直接调用symbols = binance.fapiPublicGetExchangeInfo(),这个方法,并看一下这个方法返回的symbols 结果。通过比较ccxt隐式方法返回的结果,与api说明中的返回一致,取到了u本位所有的交易币种信息,的确和api中的描述一样。由此可以判断,我们调用隐式方法的思路是正确的。隐式调用:接收行情
我们刚才调用的binance.fapiPublicGetExchangeInfo(),已经给我们列出了所有在币安能够交易的U本位合约,并且每个symbols list里的symbol字段,就是币安交易所的u本位合约标准字段。我们以’BTCUSDT’字段为例,调用U本位合约的隐式api,来获取深度行情。

首先,还是来看一下币安u本位合约行情的api说明。与exchangeInfo不同的是,深度行情的获取,需要传入参数,参数列表有两个,分别是symbol和limit,大家需要特别注意后面这个是否必需的字段,Yes代表该参数必须传,No代表该参数可以传,也可以按系统指定的默认值来。
我们再按照刚才的规则,找到ccxt对于币安u本位合约获取深度行情的隐式方法:fapiPublicGetDepth。该函数,同样符合我们之前所推断的命名规则。我们调用该函数,按照格式要求传入symbol函数,执行深度数据的获取。

ba_futures_symbol = "BTCUSDT"future_depth = binance.fapiPublicGetDepth({"symbol":ba_futures_symbol})![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/4ebce4a1522adc13a542af311a997816.webp?x-oss-process=image/format,png#pic_center)

通过返回结果可以看到,我们成功的使用隐式方法,调取到了币安u本位BTCUSDT合约的深度数据。

而对于OKEX交易所来说,我们也可以遵循同样的步骤,来获取OK我们需要币种(合约)的深度数据。我们还是通过print(dir(ccxt.okex())),打印ccxt支持的okex交易所的所有隐式方法。
我们再来看OKEX交易所的API,以交割合约为例,我们同样找到API关于获取深度行情的方法,以及需要传入的参数,这个方法的关键词包括”futures”、”instruments“、”book”等。
我们用上述关键词,在ccxt okex的所有隐式方法中寻找,可以找到对应的方法:futuresGetInstrumentsInstrumentIdBook,他的命名结构、命名逻辑,与ok的api传入地址很相似。我们调用这个方法来进行测试,这里需要注意的是,对于okex交易所,我们传入的合约ID(instrument_id)需要时具体的交割合约,比如我们以BTC-USD-0625举例,与此同时,再传入一个辅助的size参数。由于我们的size选择的是50档的盘口,返回的也是50档的深度数据。

ok_futures_symbol = "BTC-USD-0625"future_depth = okex.futuresGetInstrumentsInstrumentIdBook({"instrument_id":ok_futures_symbol, "size":50})

通过这个例子,可以看到ok交易所,我们也可以用同样的逻辑+方法,获取到ccxt隐式方法封装的合约深度。对于其他交易所的隐式行情调用,我们都可以以此类推,在这里就不一一详述了,大家对哪个交易所有兴趣,都可以print(交易所.dir()),并且与该交易所的官方api文档进行比对,调用相应命名规则的ccxt隐式方法。

隐式调用:交易
我们知道,交易api一般来说都比行情api更加复杂一些,但我们调用api的基本流程,应该来说不会发生变化,依然是先找到交易所api文档中,对应方法的地址,再根据地址中的关键词、传入参数,调用ccxt隐式交易api。

先看币安交易所,还是以u本位api举例,来看用的最多的下单函数的说明。下单函数请求地址:POST /fapi/v1/order。所以我们必须要找到包含这些关键词的ccxt隐式api对应方法,并且,按照binance的参数传入规则,将对应的下单参数传入。
ccxt中对应的交易方法的名称叫做fapiPrivatePostOrder,我们将该方法名称拆分:fapi、Private、Post、Order,除了Private,其余正好对应了原始api的关键词字段。Private是ccxt为每个隐式的私有方法,都会添加的前缀。找到了隐式方法,再根据api文档的说明,我们就可以传入合法参数进行下单了。以下,就是一个利用隐式api下市价单的例子,在下完单的同时,我们在返回值中提取出orderId,为后续的查询订单、撤单方法使用。

order_res = binance.fapiPrivatePostOrder({"symbol": _symbol,"side": _direction,"positionSide": _positionside,"type": "MARKET","quantity": _amount,"timestamp": severtime})orderid = order_res['orderId']

对于OK交易所来说,也是同理,我们先查看OK交易所的原始api,同样以下单函数为例。OK交易所的下单api,有两个重要的特征,大家需要特别注意:第一,传入的参数类型都是string,哪怕背后对应的是整数类型,传入时也要转换为string,不然就会报错。第二,下单的数量,必须转换为合约的张数,其他交易所,例如币安,在u本位合约下单时,允许传入例如下单0.01btc这样的合约单位,但OK交易所不行,必须转换为整数,不然订单就无法提交成功。此外,instrument_id、type等参数,也需要严格的按照交易所api的要求来传递,我们来看下方的例子。

order_res = okex.futures_post_order({"client_oid": "test",'instrument_id': _futsymbol,'type': str(1),'size': str(1),'order_type': '0','match_price': '1'})orderid = order_res["order_id"]