エンジニアの頭の中

知識と技術で「お金稼ぎの自動化」を実現するため、日々奮闘するエンジニアのブログです。

PythonによるLiquid by Quoine のプライベートAPIの認証方法

f:id:mitsu3204:20181025190055j:plain

先日、Liquid by Quoine(旧Quoinex)のプライベートAPIを呼び出す際の認証処理をGoで書きましたが、同様の処理をPythonでも書いたので、コードを公開しておきます。

www.hacky.xyz

Pythonで書いた理由

ccxtを使用していたので、APIの認証処理を意識する必要はなかったのですが、ボットを改良する過程で細かいエラーハンドリングを行う必要が出てきたからです。

ccxtだと、API実行時に何らかの例外が発生すると、Pythonの標準の例外クラスをccxt独自の例外クラスでラップして投げてきます。 それでもやりたきことの実現は可能なのですが、実装の効率・保守性等を考えると、あまりよろしくないため、Quoineのボットではccxtの使用をやめて、自前のコードでAPIを呼び出すことにしました。

PyJWTのインストール

以前、Goで書いた際の記事にも書きましたが、QuoineのAPI認証はJWTを使用します。

事前準備として、PythonでJWTを扱うためのライブラリがあるので、これをインストールします。

$ pip install PyJWT

PythonによるLiquid by QuoineのプライベートAPI認証を行う実装

例として、自分の取引履歴を取得する以下のプライベートAPIを呼び出してみます。

GET /executions/me?product_id=:product_id

署名生成関数の作成

プライベートAPIを呼び出す際は、HTTPリクエストのヘッダーに「X-Quoine-Auth」の名前でHTTPリクエストの署名を付与します。

署名を生成する関数を作成します。

import time
import jwt

def sign(path: str, api_key: str, api_secret: str) -> bytes:
    payload = {
        'path': path,
        'nonce': str(int(time.time())),
        'token_id': api_key
    }
    return jwt.encode(payload, api_secret, algorithm='HS256')
  • path

APIのURLに含まれるパスの部分の文字列です。例えばhttps://api.liquid.com/fiat_accountsというAPIを呼び出すのであれば、/fiat_accountsという文字列を指定します。

  • api_key

自分のAPIのキーを指定します。

  • api_secret

自分のAPIシークレットを指定します。

認証用のHTTPヘッダの作成

HTTPヘッダにX-Quoine-Authの名前でsign関数で生成した署名をセットします。

Content-TypeX-Quoine-API-Versionは、呼び出し対象のAPIがプライベートかどうかに関わらずセットするものです。

HTTPヘッダのUser-Agentを設定しているのは、urllibのデフォルトのHTTPヘッダだと、APIサーバから403(Forbidden)のステータスコードが返されたからです。回避するためUser-AgentFirefoxのものに偽装しています。

# 取引履歴を取得するAPIのパス。product_id=5というのはBTC/JPYのこと
path = '/executions/me?product_id=5'

# 署名付きヘッダーを作成
heaers = {
    'X-Quoine-Auth': sign(path, '<APIキー>', '<APIシークレット>'),
    'X-Quoine-API-Version': '2',
    'Content-Type': 'application/json',
    'User-Agent': "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0"
}

プライベートAPIの実行

作成したpathheadersを使ってHTTPリクエストオブジェクトを作成し、Liquid by QuoineのプライベートAPIを呼び出します。

HTTPリクエストの送信にはccxtを使用しないため、Pythonの標準ライブラリであるurllibを使います。

import urllib.request

# HTTPリクエストを作成
req = urllib.request.Request(url='https://api.liquid.com' + path, headers=headers, method='GET')

# HTTPリクエストを送信
res = urllib.request.urlopen(req)

# HTTPレスポンスを読み取る
result = res.read().decode('utf-8')

最終的にAPIの呼び出し結果を受け取ったresultという変数には、以下のようなJSON形式の文字列が格納されます。

{
 "current_page": 1,
 "total_pages": 10000,
 "models": [
  {
   "id": xxxxxxx,
   "quantity": "0.4",
   "price": "718848.0",
   "taker_side": "sell",
   "created_at": 1540458553,
   "my_side": "buy"
  },