基本信息
接口基本信息
- 本接口可能需要用户的 AGENT。
- 如需创建 AGENT,请点击此处,并在顶部切换至
Pro API
- 接口 baseurl:https://fapi.asterdex-testnet.com
- 所有接口的响应都是 JSON 格式。
- 响应中如有数组,数组元素以时间升序排列,越早的数据越提前。
- 所有时间、时间戳均为 UNIX 时间,单位为毫秒。
- 所有数据类型采用 JAVA 的数据类型定义。
HTTP 返回代码
- HTTP
4XX 错误码用于指示错误的请求内容、行为、格式。
- HTTP
403 错误码表示违反 WAF 限制(Web 应用程序防火墙)。
- HTTP
429 错误码表示警告访问频次超限,即将被封 IP。
- HTTP
418 表示收到 429 后继续访问,于是被封了。
- HTTP
5XX 错误码用于指示 Aster Finance 服务侧的问题。
- HTTP
503 表示 API 服务端已经向业务核心提交了请求但未能获取响应,特别需要注意的是其不代表请求失败,而是未知。很可能已经得到了执行,也有可能执行失败,需要做进一步确认。
接口错误代码
异常响应格式如下:
{
"code": -1121,
"msg": "Invalid symbol."
}
接口的基本信息
GET 方法的接口,参数必须在 query string 中发送。
POST、PUT 和 DELETE 方法的接口,在 request body 中发送(content type application/x-www-form-urlencoded)。
- 对参数的顺序不做要求。
访问限制
/fapi/v3/exchangeInfo 接口中 rateLimits 数组里包含有 REST 接口的访问限制,包括带权重的访问频次限制、下单速率限制。本篇枚举定义章节有限制类型的进一步说明。
- 违反上述任何一个访问限制都会收到 HTTP 429,这是一个警告。
IP 访问限制
- 每个请求将包含一个
X-MBX-USED-WEIGHT-(intervalNum)(intervalLetter) 的响应头,其中包含当前 IP 所有请求的已使用权重。
- 每个路由都有一个"权重",该权重确定每个接口计数的请求数。较重的接口和对多个交易对进行操作的接口将具有较重的"权重"。
- 收到 429 时,您有责任停止发送请求,不得持续请求 API。
- 重复违反访问限制或收到 429 后未停止请求,将导致 IP 被自动封禁(HTTP 418)。
- IP 封禁时间会随违规次数累计增长,最短 2 分钟,最长 3 天。
- 访问限制基于 IP,而非 API Key。
下单频率限制
- 每次下单响应将包含
X-MBX-ORDER-COUNT-(intervalNum)(intervalLetter) 响应头,其中包含该账户当前已使用的下单频率限制数量。
- 被拒绝或未成功的订单不保证包含上述响应头。
- 下单频率限制基于账户计算。
严肃的交易关乎时效性。 网络可能不稳定,导致请求到达服务器所需的时间有所波动。通过使用 recvWindow,您可以指定请求必须在一定毫秒数内被处理,否则将被服务器拒绝。
接口鉴权类型
- 每个接口都有自己的鉴权类型,决定了访问时应当进行何种鉴权。
- 如需鉴权,请求体中必须包含 signer 参数。
| 鉴权类型 |
描述 |
| NONE |
不需要鉴权的接口 |
| TRADE |
需要有效的 signer 和 signature |
| USER_DATA |
需要有效的 signer 和 signature |
| USER_STREAM |
需要有效的 signer 和 signature |
| MARKET_DATA |
不需要鉴权的接口 |
鉴权签名载荷
| 参数 |
描述 |
| user |
主账户钱包地址 |
| signer |
API 钱包地址 |
| nonce |
当前时间戳,单位为微秒 |
| signature |
签名 |
需要签名的接口
- 鉴权类型:TRADE、USER_DATA、USER_STREAM
POST /fapi/v3/order 的示例
所有参数均通过 request body 发送(Python 3.9.6)
示例:以下参数为 API 注册信息,user、signer、privateKey 仅供示范(privateKey 为 signer 的私钥)
| Key |
Value |
描述 |
| user |
0x63DD5aCC6b1aa0f563956C0e534DD30B6dcF7C4e |
登录钱包地址 |
| signer |
0x21cF8Ae13Bb72632562c6Fff438652Ba1a151bb0 |
点击此处 |
| privateKey |
0x4fd0a42218f3eae43a6ce26d22544e986139a01e5b34a62db53757ffca81bae1 |
点击此处 |
示例:nonce 参数为当前系统微秒值,超过系统时间或落后系统时间超过 5 秒为非法请求。
#python
nonce = math.trunc(time.time()*1000000)
print(nonce)
#1748310859508867
//java
Instant now = Instant.now();
long microsecond = now.getEpochSecond() * 1000000 + now.getNano() / 1000;
示例:以下为业务请求参数
import json
import time
import urllib
import threading
import requests
from eth_account.messages import encode_structured_data
from eth_account import Account
typed_data = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Message": [
{ "name": "msg", "type": "string" }
]
},
"primaryType": "Message",
"domain": {
"name": "AsterSignTransaction",
"version": "1",
"chainId": 714,
"verifyingContract": "0x0000000000000000000000000000000000000000"
},
"message": {
"msg": "$msg"
}
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'PythonApp/1.0'
}
host = 'https://fapi.asterdex-testnet.com'
# 在此配置您的用户和代理信息
user = '*'
signer = '*'
private_key = '*'
place_order = {"url":"/fapi/v3/order","method":"POST","params":{"symbol": "ASTERUSDT", "type": "LIMIT", "side": "BUY",
"timeInForce": "GTC", "quantity": "20", "price": "0.5"}}
batch_orders = {"url":"/fapi/v3/batchOrders","method":"POST","params":{
"batchOrders":"[{'symbol':'ASTERUSDT','type':'LIMIT','side':'BUY','timeInForce':'GTC','quantity':'20','price':'0.5'},{'symbol':'ASTERUSDT','type':'LIMIT','side':'BUY','timeInForce':'GTC','quantity':'20','price':'0.5'}]" }}
listen_key = {"url":"/fapi/v3/listenKey","method":"POST","params":{}}
_last_ms = 0
_i = 0
_nonce_lock = threading.Lock()
def get_nonce():
global _last_ms, _i
with _nonce_lock:
now_ms = int(time.time())
if now_ms == _last_ms:
_i += 1
else:
_last_ms = now_ms
_i = 0
return now_ms * 1_000_000 + _i
def send_by_url(api) :
my_dict = api['params']
url = host + api['url']
my_dict['nonce'] = str(get_nonce())
my_dict['user'] = user
my_dict['signer'] = signer
param = urllib.parse.urlencode(my_dict)
print(param)
typed_data['message']['msg'] = param
message = encode_structured_data(typed_data)
signed = Account.sign_message(message, private_key=private_key)
url = url + '?' + param + '&signature=' + signed.signature.hex()
print(url)
res = requests.post(url, headers=headers)
print(res.text)
def send_by_body(api) :
my_dict = api['params']
url = host +api['url']
my_dict['nonce'] = str(get_nonce())
my_dict['user'] = user
my_dict['signer'] = signer
param = urllib.parse.urlencode(my_dict)
typed_data['message']['msg'] = param
message = encode_structured_data(typed_data)
signed = Account.sign_message(message, private_key=private_key)
print(signed.signature.hex())
my_dict['signature'] = signed.signature.hex()
print(my_dict)
res = requests.post(url, data=my_dict, headers=headers)
# print(res.headers)
print(res.text)
if __name__ == '__main__':
send_by_url(place_order)
# send_by_url(listen_key)
# send_by_url(batch_orders)
# send_by_body(place_order)
# send_by_body(batch_orders)