exchange.IO()函数用于调用交易所对象的其它相关接口。
string / number / bool / object / array / any (平台支持的任意类型)
exchange.IO(k, ...args)
`k`参数用于设置调用类型,可选值包括`"api"`、`"currency"`、`"base"`、`"trade_margin"`、`"trade_normal"`、`"public_base"`、`"mbase"`、`selfTradePreventionMode`、`simulate`、`cross`、`dual`、`unified`等。
k
true
string
扩展参数,根据具体调用场景传入。`arg`参数支持传入多个值。由于`exchange.IO()`函数采用多态机制,不同的参数设置对应不同的功能。`exchange.IO()`函数的参数个数和类型均不固定。
arg
true
string / number / bool / object / array / any (平台支持的任意类型)
```javascript
function main() {
var arrOrders = [
{"instId":"BTC-USDT-SWAP","tdMode":"cross","side":"buy","ordType":"limit","px":"16000","sz":"1","posSide":"long"},
{"instId":"BTC-USDT-SWAP","tdMode":"cross","side":"buy","ordType":"limit","px":"16000","sz":"2","posSide":"long"}
]
// 调用 exchange.IO 直接访问交易所批量下单接口
var ret = exchange.IO("api", "POST", "/api/v5/trade/batch-orders", "", JSON.stringify(arrOrders))
Log(ret)
}```
```python
import json
def main():
arrOrders = [
{"instId":"BTC-USDT-SWAP","tdMode":"cross","side":"buy","ordType":"limit","px":"16000","sz":"1","posSide":"long"},
{"instId":"BTC-USDT-SWAP","tdMode":"cross","side":"buy","ordType":"limit","px":"16000","sz":"2","posSide":"long"}
]
ret = exchange.IO("api", "POST", "/api/v5/trade/batch-orders", "", json.dumps(arrOrders))
Log(ret)```
```cpp
void main() {
json arrOrders = R"([
{"instId":"BTC-USDT-SWAP","tdMode":"cross","side":"buy","ordType":"limit","px":"16000","sz":"1","posSide":"long"},
{"instId":"BTC-USDT-SWAP","tdMode":"cross","side":"buy","ordType":"limit","px":"16000","sz":"2","posSide":"long"}
])"_json;
auto ret = exchange.IO("api", "POST", "/api/v5/trade/batch-orders", "", arrOrders.dump());
Log(ret);
}```
使用```exchange.IO()```函数的```exchange.IO("api", httpMethod, resource, params, raw)```调用形式,需要首先了解该交易所的API接口并查阅相关文档。通过此方式可以扩展FMZ平台尚未添加的功能。提交```POST```请求时无需担心参数加密、签名、验证等过程,FMZ已在底层完成处理,只需填入相应参数即可。
可以参考**OKX交易所**期货合约的批量下单示例,使用参数```raw```传递订单参数:
```javascript
var amount = 1
var price = 10
var basecurrency = "ltc"
function main () {
// 注意 amount.toString() 和 price.toString() 左边右边都有一个 ' 字符
var message = "symbol=" + basecurrency + "&amount='" + amount.toString() + "'&price='" + price.toString() + "'&side=buy" + "&type=limit"
var id = exchange.IO("api", "POST", "/v1/order/new", message)
}```
```python
amount = 1
price = 10
basecurrency = "ltc"
def main():
message = "symbol=" + basecurrency + "&amount='" + str(amount) + "'&price='" + str(price) + "'&side=buy" + "&type=limit"
id = exchange.IO("api", "POST", "/v1/order/new", message)```
```cpp
void main() {
auto amount = 1.0;
auto price = 10.0;
auto basecurrency = "ltc";
string message = format("symbol=%s&amount=\"%.1f\"&price=\"%.1f\"&side=buy&type=limit", basecurrency, amount, price);
auto id = exchange.IO("api", "POST", "/v1/order/new", message);
}```
如果```params```参数(即HTTP请求参数)中的键值为字符串类型,需要在参数值两侧使用单引号(即符号')将参数值包裹起来。
```javascript
function main() {
var ret = exchange.IO("api", "GET", "https://www.okx.com/api/v5/account/max-withdrawal", "ccy=BTC")
Log(ret)
}```
```python
def main():
ret = exchange.IO("api", "GET", "https://www.okx.com/api/v5/account/max-withdrawal", "ccy=BTC")
Log(ret)```
```cpp
void main() {
auto ret = exchange.IO("api", "GET", "https://www.okx.com/api/v5/account/max-withdrawal", "ccy=BTC");
Log(ret);
}```
支持传入完整的 URL 参数,这样可以省略切换基地址的操作(调用 ```exchange.SetBase()``` 函数)。
```javascript
function main(){
var ret = exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT")
Log(ret)
}```
```python
def main():
ret = exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT")
Log(ret)```
```cpp
void main() {
auto ret = exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT");
Log(ret);
}```
不使用参数 ```raw``` 的调用示例:
```javascript
function main() {
// 例如开始实盘上设置交易所对象目前交易对为BTC_USDT,打印目前交易对行情
Log(exchange.GetTicker())
// 切换交易对为LTC_BTC
exchange.IO("currency", "LTC_BTC")
Log(exchange.GetTicker())
}```
```python
def main():
Log(exchange.GetTicker())
exchange.IO("currency", "LTC_BTC")
Log(exchange.GetTicker())```
```cpp
void main() {
Log(exchange.GetTicker());
exchange.IO("currency", "LTC_BTC");
Log(exchange.GetTicker());
}```
切换当前交易所的交易对,通过代码切换**实盘创建时**或**回测时**配置的交易对。
```javascript
function main () {
// exchanges[0]就是实盘创建时,第一个添加的交易所对象
exchanges[0].IO("base", "https://api.huobi.pro")
}```
```python
def main():
exchanges[0].IO("base", "https://api.huobi.pro")```
```cpp
void main() {
exchanges[0].IO("base", "https://api.huobi.pro");
}```
例如交易所对象封装时默认的基地址为 ```https://api.huobipro.com```,需要切换至 ```https://api.huobi.pro``` 时,使用以下代码进行切换:
```javascript
function main() {
exchange.SetBase("https://api.bitfinex.com")
exchange.IO("mbase", "https://api-pub.bitfinex.com")
}```
```python
def main():
exchange.SetBase("https://api.bitfinex.com")
exchange.IO("mbase", "https://api-pub.bitfinex.com")```
```cpp
void main() {
exchange.SetBase("https://api.bitfinex.com");
exchange.IO("mbase", "https://api-pub.bitfinex.com");
}```
对于行情接口和交易接口基地址不同的交易所,例如 Bitfinex 期货有两个地址,一个是行情接口地址,一个是交易接口地址。
- Bitfinex 期货切换私有接口基地址使用 ```exchange.SetBase("xxx")```。
- Bitfinex 期货切换公共接口基地址使用 ```exchange.IO("mbase", "xxx")```。
```javascript
function main() {
// 场景1: 限制GetTicker每秒最多调用10次,超限返回null
exchange.IO("rate", "GetTicker", 10, "1s")
for (var i = 0; i < 20; i++) {
var ticker = exchange.GetTicker("BTC_USDT")
if (ticker) {
Log("Ticker:", ticker.Last)
} else {
Log("Rate limit exceeded")
}
}
}```
```python
def main():
# 场景1: 限制GetTicker每秒最多调用10次,超限返回null
exchange.IO("rate", "GetTicker", 10, "1s")
for i in range(20):
ticker = exchange.GetTicker("BTC_USDT")
if ticker:
Log("Ticker:", ticker["Last"])
else:
Log("Rate limit exceeded")```
```cpp
// C++暂不支持```
限制 GetTicker 每秒最多调用 10 次,超限时报错并返回空值:
```javascript
function main() {
// 场景2: 使用delay参数,超限时自动等待
exchange.IO("rate", "GetTicker", 10, "1s", "delay")
for (var i = 0; i < 20; i++) {
var ticker = exchange.GetTicker("BTC_USDT")
Log("Call", i+1, "Ticker:", ticker.Last)
}
}```
```python
def main():
# 场景2: 使用delay参数,超限时自动等待
exchange.IO("rate", "GetTicker", 10, "1s", "delay")
for i in range(20):
ticker = exchange.GetTicker("BTC_USDT")
Log("Call", i+1, "Ticker:", ticker["Last"])```
```cpp
// C++暂不支持```
限制 GetTicker 每秒最多调用 10 次,超限时自动等待:
```javascript
function main() {
// 场景3: GetTicker和GetDepth共享限制,合计每秒最多10次
exchange.IO("rate", "GetTicker,GetDepth", 10, "1s")
for (var i = 0; i < 20; i++) {
if (i % 2 == 0) {
Log("Ticker:", exchange.GetTicker("BTC_USDT"))
} else {
Log("Depth:", exchange.GetDepth("BTC_USDT"))
}
}
}```
```python
def main():
# 场景3: GetTicker和GetDepth共享限制,合计每秒最多10次
exchange.IO("rate", "GetTicker,GetDepth", 10, "1s")
for i in range(20):
if i % 2 == 0:
Log("Ticker:", exchange.GetTicker("BTC_USDT"))
else:
Log("Depth:", exchange.GetDepth("BTC_USDT"))```
```cpp
// C++暂不支持```
限制多个函数的联合调用频率:
```javascript
function main() {
// 场景4: 限制所有API调用每分钟最多100次
exchange.IO("rate", "*", 100, "1m")
for (var i = 0; i < 10; i++) {
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")
exchange.GetAccount()
Log("Round", i+1, "completed")
Sleep(1000)
}
}```
```python
def main():
# 场景4: 限制所有API调用每分钟最多100次
exchange.IO("rate", "*", 100, "1m")
for i in range(10):
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")
exchange.GetAccount()
Log("Round", i+1, "completed")
Sleep(1000)```
```cpp
// C++暂不支持```
使用通配符限制所有API调用:
```javascript
function main() {
// 场景5: 严格限制,时间窗口对齐到整秒
exchange.IO("quota", "GetTicker", 3, "1s")
for (var i = 0; i < 10; i++) {
var ticker = exchange.GetTicker("BTC_USDT")
if (ticker) {
Log(_D(), "Ticker:", ticker.Last)
} else {
Log(_D(), "Quota exceeded, waiting for next window")
}
Sleep(100)
}
}```
```python
def main():
# 场景5: 严格限制,时间窗口对齐到整秒
exchange.IO("quota", "GetTicker", 3, "1s")
for i in range(10):
ticker = exchange.GetTicker("BTC_USDT")
if ticker:
Log(_D(), "Ticker:", ticker["Last"])
else:
Log(_D(), "Quota exceeded, waiting for next window")
Sleep(100)```
```cpp
// C++暂不支持```
使用quota模式进行严格时间窗口对齐限流:
```javascript
function main() {
// 场景6: 每天最多调用1000次GetTicker,每天08:15重置配额
exchange.IO("quota", "GetTicker", 1000, "@0815")
var count = 0
while (true) {
var ticker = exchange.GetTicker("BTC_USDT")
if (ticker) {
count++
Log("Call count:", count, "Ticker:", ticker.Last)
} else {
Log("Daily quota exceeded, waiting for reset at 08:15")
Sleep(60000) // Wait 1 minute
}
Sleep(1000)
}
}```
```python
def main():
# 场景6: 每天最多调用1000次GetTicker,每天08:15重置配额
exchange.IO("quota", "GetTicker", 1000, "@0815")
count = 0
while True:
ticker = exchange.GetTicker("BTC_USDT")
if ticker:
count += 1
Log("Call count:", count, "Ticker:", ticker["Last"])
else:
Log("Daily quota exceeded, waiting for reset at 08:15")
Sleep(60000) # Wait 1 minute
Sleep(1000)```
```cpp
// C++暂不支持```
使用日内配额,每天在指定时间重置:
```javascript
function main() {
// 场景7: 组合多个限流规则
exchange.IO("rate", "GetTicker", 10, "1s") // GetTicker每秒10次
exchange.IO("rate", "GetDepth", 5, "1s") // GetDepth每秒5次
exchange.IO("rate", "CreateOrder", 2, "1s") // CreateOrder每秒2次
exchange.IO("quota", "*", 1000, "@0000") // 所有API每天00:00重置,限1000次
Log("Rate limits configured successfully")
// 测试限流
for (var i = 0; i < 5; i++) {
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")
Sleep(200)
}
}```
```python
def main():
# 场景7: 组合多个限流规则
exchange.IO("rate", "GetTicker", 10, "1s") # GetTicker每秒10次
exchange.IO("rate", "GetDepth", 5, "1s") # GetDepth每秒5次
exchange.IO("rate", "CreateOrder", 2, "1s") # CreateOrder每秒2次
exchange.IO("quota", "*", 1000, "@0000") # 所有API每天00:00重置,限1000次
Log("Rate limits configured successfully")
# 测试限流
for i in range(5):
exchange.GetTicker("BTC_USDT")
exchange.GetDepth("BTC_USDT")
Sleep(200)```
```cpp
// C++暂不支持```
组合使用多个限流规则:
**一、用于加密货币中心化交易所其它未统一封装的API接口调用,参数```k```设置为```"api"```:**
```javascript
exchange.IO("api", httpMethod, resource, params, raw)
POST、GET等。URL编码。**二、用于切换交易对,参数```k```设置为```"currency"```:**
```javascript
exchange.IO("currency", currency)
currency:该参数为字符串类型,格式统一为大写,使用下划线分隔baseCurrency与quoteCurrency,例如:BTC_USDT。
1、回测系统现已支持切换交易对(仅限数字货币现货交易所对象),回测时需要注意只能切换为相同计价币的交易对,例如当前交易对为ETH_BTC只能切换为LTC_BTC,不能切换为LTC_USDT。
2、对于加密货币期货合约交易所对象,切换交易对后需要再次设置合约代码以确定要交易的具体合约。
3、使用{@fun/Account/exchange.SetCurrency exchange.SetCurrency}函数切换交易对的功能与使用exchange.IO("currency", currency)切换交易对的功能完全相同。
三、用于切换加密货币现货交易所对象杠杆账户模式:
参数k设置为"trade_margin",切换为现货杠杆账户模式。下单、获取账户资产将访问交易所现货杠杆接口。
如果交易所现货杠杆区分全仓、逐仓,则使用:exchange.IO("trade_super_margin")切换为杠杆账户全仓,exchange.IO("trade_margin")切换为杠杆账户逐仓。
参数k设置为"trade_normal",切换回普通现货账户模式。
支持切换杠杆账户模式的现货交易所:
| 交易所 | 特殊备注 |
|---|---|
| OKX | 杠杆账户模式的交易对与普通模式有所不同,部分交易对可能不支持。使用exchange.IO("trade_super_margin")切换为杠杆账户全仓,exchange.IO("trade_margin")切换为逐仓。使用trade_normal切换为普通币币模式。使用exchange.IO("tdMode", "cross")直接指定杠杆模式。如果OKX账户设置为「组合保证金」模式,必须切换为全仓模式,OKX的组合保证金模式仅支持全仓杠杆。 |
| 火币 | 杠杆账户模式的交易对与普通模式有所不同,部分交易对可能不支持。火币杠杆账户分为全仓和逐仓,使用trade_margin切换为杠杆账户逐仓,使用trade_super_margin切换为杠杆账户全仓。使用trade_normal切换为普通币币模式。 |
| 币安(Binance) | 杠杆账户模式分为逐仓、全仓,使用trade_margin切换为逐仓,使用trade_super_margin切换为全仓,使用trade_normal切换为普通币币模式。 |
| GateIO | 杠杆账户模式分为逐仓、全仓,使用trade_margin切换为逐仓,使用trade_super_margin切换为全仓,使用trade_normal切换为普通币币模式。 |
| AscendEx | 使用exchange.IO("trade_margin")切换为杠杆账户模式,使用exchange.IO("trade_normal")切换回普通账户模式。 |
| WOO | 使用exchange.IO("trade_margin")切换为杠杆账户模式,使用exchange.IO("trade_normal")切换回普通账户模式。 |
| CoinEx | 使用exchange.IO("trade_margin")切换为杠杆账户模式,使用exchange.IO("trade_normal")切换回普通账户模式。 |
四、其它切换功能:
在用户指南中可以查看exchange.IO()函数的其它切换功能。
五、API限流控制功能:
FMZ平台支持对交易所API调用进行频率限制,防止触发交易所的频率限制。支持两种限流模式:
函数签名:
exchange.IO("rate", functionNames, maxCalls, period, [behavior])
exchange.IO("quota", functionNames, maxCalls, period, [behavior])
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
| mode | string | 限流模式:”rate”(平滑限流)或 “quota”(额度限流) |
| functionNames | string | 要限制的函数名,支持单个函数、多个函数(逗号分隔)、通配符(*表示所有函数) |
| maxCalls | number | 时间周期内允许的最大调用次数 |
| period | string | 时间周期或重置时间点。支持时间单位:ns, us, µs, ms, s, m, h, d(例如:”1s”, “1m”, “1h”)。支持重置时间点:@HHMM 或 @HHMMSS(例如:”@0815”表示每天08:15重置) |
| behavior | string | 可选参数,超限行为。默认为空(超限返回null并报错);设置为”delay”时超限会等待 |
支持的函数列表:
交易类:CreateOrder, CancelOrder, Buy, Sell, CreateConditionOrder, CancelConditionOrder
账户类:GetAccount, GetAssets, GetPositions
订单类:GetOrder, GetOrders, GetHistoryOrders, GetConditionOrder, GetConditionOrders, GetHistoryConditionOrders
行情类:GetTicker, GetTickers, GetDepth, GetRecords, GetTrades
其他:GetMarkets, GetFundings, SetMarginLevel, Go, IO/api
注意事项:
Buy/Sell函数的限流遵循CreateOrder的限流设置。Go函数的限流遵循实际并发调用函数的限流设置。IO/api的限流仅对exchange.IO("api", ...)调用生效。"1s"时,时间窗口按整秒对齐。"delay"参数时,调用时间与日志记录时间可能存在差异,此为正常现象。{@fun/NetSettings/exchange.SetBase exchange.SetBase}, {@fun/Account/exchange.SetCurrency exchange.SetCurrency}, {@var EXCHANGE_OP_IO_CONTROL}