binance api メモ

# API の概要
## APIの種別
* REST API
* WebSocket

# REST API
## Endpoint の種類
* Spot Market
* USDT Futures
* COIN Futures

## Weightの概念
APIすべてにWeightが指定されている。
グローバルIPごとにレート制限があり、一定時間における累積Weightがレート制限を超えた場合、APIが利用できなくなる。

自分のレート制限は、以下APIにて確認できる。
`https://api.binance.com/api/v3/exchangeInfo`
`rateLimits`が該当箇所。

```
{
    "timezone": "UTC",
    "serverTime": 1615020505034,
    "rateLimits": [
        {
            "rateLimitType": "REQUEST_WEIGHT",
            "interval": "MINUTE",
            "intervalNum": 1,
            "limit": 1200
        },
        {
            "rateLimitType": "ORDERS",
            "interval": "SECOND",
            "intervalNum": 10,
            "limit": 100
        },
        {
            "rateLimitType": "ORDERS",
            "interval": "DAY",
            "intervalNum": 1,
            "limit": 200000
        }
    ],
    "exchangeFilters": [],
    "symbols": [
        {
            "symbol": "ETHBTC",
            "status": "TRADING",
            "baseAsset": "ETH",
            "baseAssetPrecision": 8,
            "quoteAsset": "BTC",
            "quotePrecision": 8,
            "quoteAssetPrecision": 8,
            "baseCommissionPrecision": 8,
            "quoteCommissionPrecision": 8,
            "orderTypes": [
                "LIMIT",
                "LIMIT_MAKER",
                "MARKET",
                "STOP_LOSS_LIMIT",
                "TAKE_PROFIT_LIMIT"
            ],
            "icebergAllowed": true,
            "ocoAllowed": true,
            "quoteOrderQtyMarketAllowed": true,
            "isSpotTradingAllowed": true,
            "isMarginTradingAllowed": true,
            "filters": [
                {
                    "filterType": "PRICE_FILTER",
                    "minPrice": "0.00000100",
                    "maxPrice": "100000.00000000",
                    "tickSize": "0.00000100"
                },
                {
                    "filterType": "PERCENT_PRICE",
                    "multiplierUp": "5",
                    "multiplierDown": "0.2",
                    "avgPriceMins": 5
                },
                {
                    "filterType": "LOT_SIZE",
                    "minQty": "0.00100000",
                    "maxQty": "100000.00000000",
                    "stepSize": "0.00100000"
                },
                {
                    "filterType": "MIN_NOTIONAL",
                    "minNotional": "0.00010000",
                    "applyToMarket": true,
                    "avgPriceMins": 5
                },
                {
                    "filterType": "ICEBERG_PARTS",
                    "limit": 10
                },
                {
                    "filterType": "MARKET_LOT_SIZE",
                    "minQty": "0.00000000",
                    "maxQty": "3102.16030493",
                    "stepSize": "0.00000000"
                },
                {
                    "filterType": "MAX_NUM_ORDERS",
                    "maxNumOrders": 200
                },
                {
                    "filterType": "MAX_NUM_ALGO_ORDERS",
                    "maxNumAlgoOrders": 5
                }
            ],
            "permissions": [
                "SPOT",
                "MARGIN"
            ]
        },{
// 実際には、大量に続く。９万行くらい。
        }
    ]
}
```

これを見ると、以下の３種ある。

* `"rateLimitType": "REQUEST_WEIGHT", "interval": "MINUTE","intervalNum": 1, "limit": 1200`
* `"rateLimitType": "ORDERS", "interval": "SECOND", "intervalNum": 10, "limit": 100`
* `"rateLimitType": "ORDERS", "interval": "DAY", "intervalNum": 1, "limit": 200000`

### rateLimitType
APIのジャンル？

### interval
恐らくレート制限のリセット間隔（単位）。

### intervalNum
恐らくレート制限のリセット周期。

`interval`が`MINUTE`で`intervalNum`が1ならば、１分間でリセットという計算。

#### 例
* `/api/v3/depth?limit=5000`は、weightが50である。
* `REQUEST_WEIGTH`の`limit`は1200である。

ということは、1200 = 50x ・・・x = 24
24回まで一分間に呼べるということ。

実際に試してみる。確かに25回目にエラーレスポンスがあった。
エラーメッセージもわかりやすく、そういう旨のことを示している。

```json
{
    "code": -1003,
    "msg": "Too much request weight used; current limit is 1200 request weight per 1 MINUTE. Please use the websocket for live updates to avoid polling the API."
}
```

### Response Headerにもレート使用状態が付加されている
`x-mbx-used-weight`や`x-mbx-used-weight-1m`ヘッダに付加されている。

先程の`/api/v2/depth?limit=5000`を呼ぶと、
`x-mbx-used-weight-1m`は、50。
もう一度呼ぶと、`x-mbx-used-weight-1m`は、100に増える。
1分経てばサーバ側では0になるため、再度呼び出すと、50が設定されて帰ってくる。
機能開発時には、こちらのヘッダのused値を常に確認してからリクエストを送るべきだろう。

## レート制限の対応方針
* レート制限下の有無の保持
* ping apiによる事前チェック

### レート制限下の有無の保持
エラーが起きた際は、稼働しているアプリケーションコンテキストにエラー状態とエラー発生時刻を保持し、制限解除時刻を計算して保持して不要なAPI呼び出しを重ねないようにすべき。
binanceのAPIはレート制限下で呼べば呼ぶほど、制限解除時刻が伸びる。最長3日制限される。。

### ping apiによる事前チェック
`/api/v3/ping`を呼べば、ヘッダーで現在のレート制限状況が把握できる。
これを例えば、10sごとに呼び出してWeightを把握すればよい。毎回1Weight消費してしまうが。


## POST系APIについて
### signatureについて
signature以外の必須・任意パラメータを文字列化したもの（queryString or urlencodedString)に`HMAC SHA256`変換を掛けたもの。
APIの説明欄に「`HMAC SHA256`」が登場したらsignature付きリクエストは必須。

### `application/x-www-form-urlencoded`しか受け付けない？
`application/json`は受け付けてくれなそうだった（自信なし）

### エラー
#### 罠メモ
##### -2014 BAD_API_KEY_FMT: `API-key format invalid.`
POST時に、`API-key format invalid.`が返ることがあるが、bodyの送り方が間違っていても返却されるケースがある。要注意。

# WebSocket
## SPOT/MARGIN/etc...
### 基本構造
POSTでSubscribeするためのパラメータ`listenKey`を払い出す。
WebSocketで`listenKey`をパスパラメータに利用して、Subscribeする。
何もしなければ、60分で切断。
keepAliveするなら、PINGエンドポイントへPUT。
UnSubscribeは、DELETEで`listenKey`を指定して削除。