Pythonを用いたビットフライヤーの仮想通貨APIの使い方

ビットフライヤーのAPIを使って、仮想通貨botを作りましょう。

この記事では、Pythonを使ってビットフライヤーのAPIを呼び、実際に動作するコードを紹介します。

前準備

bitFlyerに口座を開設する

この記事を読み終わるころには、実際にAPIでビットコインを購入できるようにビットプライヤーで口座を開設しておきましょう。

Private API を使うときには、口座開設をしてAPIキーを取得する必要があります。

かそつーくん
かそつーくん

ビットフライヤーには「友達招待プログラム」があります。
招待URLからアクセスして、招待コードを入力して口座開設すると、1,500円分のビットコイン を受け取ることができます。読者様と私の両者にプレゼントされます。ぜひ受け取ってください。

招待URL: https://bitflyer.com/invitation?id=50xhjxbl&lang=ja-JP
招待コード:50xhjxbl
達成条件:招待された友達が bitFlyer の本人確認のお手続きを完了し、口座を開設

bitFlyerのAPI仕様書

ビットフライヤーのAPI仕様書はこちらになります。
サンプルコードでは入力パラメータの説明は省いていますので、必須パラメータなどはAPI仕様書でご確認ください。

requestsモジュールのAPI仕様書

botでつまづくところは、APIを呼んでも思った通りに動かないことです。
requestsの使い方が良くない場合があるので、そのときはrequestsの仕様書を見ましょう。

HTTP レスポンスのステータスコードは、こちらで確認しましょう。

Pythonモジュールのインストール

サンプルコードで必要なモジュールは以下です。未インストールの場合は pip install してください。

import requests
import json
import time
from datetime import datetime
import hmac
import hashlib

Public API を使う

APIの練習をしたい方は、Public APIを使用してお試しされるのもアリです。PCさえあれば、口座開設なしで今すぐAPIを使えます。

短時間にAPIを何度も呼ぶと負荷がかかるのでAPI制限があります。お気をつけください。

取引所の状態の取得 gethealth

bot運用を開始すると、取引所がメンテナンス状態のときは取引しないようなコードを書きます。そんなときに取引所の状態取得APIを使用します。

endPoint = "https://api.bitflyer.com"
path     = "/v1/gethealth"
url = endPoint + path

response = requests.get(url)
res = response.json()
print(json.dumps(res, indent=4))

取引所のステータスが返ってきます。

{
    "status": "NORMAL"
}

取り扱い通貨の取得 getmarkets

ビットフライヤーは、販売所と取引所で扱っている通貨の種類が異なります。このAPIを使えば、取引できる通貨を確認できます。ですが、取り扱い通貨取得APIはあまり使いどころがないかもしれませんね。

endPoint = "https://api.bitflyer.com"
path     = "/v1/getmarkets"
url = endPoint + path

response = requests.get(url)
res = response.json()
print(json.dumps(res, indent=4))

ビットフライヤー取引所では、”Spot”が現物で8種類、”FX”がCFDで1種類が取り扱われています。

[
    {
        "product_code": "BTC_JPY",
        "market_type": "Spot"
    },
    {
        "product_code": "XRP_JPY",
        "market_type": "Spot"
    },
    {
        "product_code": "ETH_JPY",
        "market_type": "Spot"
    },
    {
        "product_code": "XLM_JPY",
        "market_type": "Spot"
    },
    {
        "product_code": "MONA_JPY",
        "market_type": "Spot"
    },
    {
        "product_code": "ELF_JPY",
        "market_type": "Spot"
    },
    {
        "product_code": "ETH_BTC",
        "market_type": "Spot"
    },
    {
        "product_code": "BCH_BTC",
        "market_type": "Spot"
    },
    {
        "product_code": "FX_BTC_JPY",
        "market_type": "FX"
    }
]

Ticker情報の取得 getticker

Ticker、つまりシンボルですね。もしくは通貨ペアと言ってもいいと思います。

主に、LTP(Last Traded Price: 最終取引価格)を取得したいときにTicker情報取得APIを使います。下の例ではBTC_JPYの情報を取得しています。

endPoint = "https://api.bitflyer.com"
path     = "/v1/getticker"
url = endPoint + path

parameters = {
    "product_code": "BTC_JPY",
}

response = requests.get(url, params=parameters)
res = response.json()
print(json.dumps(res, indent=4))

レスポンスは長くなるので省略していますが、LTPやbid/askの情報が返ってきます。

{
    "product_code": "BTC_JPY",
    "state": "RUNNING",
    "best_bid": 8518626.0,
    "best_ask": 8521664.0,
    "best_bid_size": 0.003,
    "best_ask_size": 0.02,
    "ltp": 8521666.0,
    "volume": 2054.49467602,
}

板情報の取得 getboard

LTPではなく、最新のbid/ask価格で売買価格を決めたい場合は、板情報取得APIを使ってもいいと思います。

endPoint = "https://api.bitflyer.com"
path     = "/v1/getboard"
url = endPoint + path

parameters = {
    "product_code": "BTC_JPY",
}

response = requests.get(url, params=parameters)
res = response.json()
print(json.dumps(res, indent=4))

大量のbidとaskの情報が返るので、レスポンスの一部だけ載せます。

{
    "mid_price": 8517738.0,
    "bids": [
        {
            "price": 8515627.0,
            "size": 0.02
        },
        {
            "price": 8515612.0,
            "size": 0.01
        }
    ],
    "asks": [
        {
            "price": 8519849.0,
            "size": 0.20557575
        },
        {
            "price": 8519850.0,
            "size": 0.011
        }
    ]
}

Private API を使う

Private API にはAPIキーが必要なので、口座開設が必要になります。

APIキーを使ってもAPIコールでエラーが返る状態に陥っている方は、ビットフライヤーのサイトでAPIのパーミッション設定を見てみるといいかもしれません。

APIのパーミッションは、bitFlyer lightning => 左MenuのAPI => APIキーのActionsの編集 で変更できます。

下のサンプルコード内に apiKeysecretKey という変数が出てきますが、ご自身のものに置き換えてください。
・apiKey : ご自身の API Key
・secretKey: ご自身の API Secret

資産残高の取得 getbalance

資産残高取得APIは、自分の口座の各通貨や日本円の残高がわかります。

botで日本円の残高が少ないときはビットコインのみ取引し、アルトコインは売買しないということも実現できます。

method   = "GET"
endPoint = "https://api.bitflyer.com"
path     = "/v1/me/getbalance"
url = endPoint + path

timestamp = "{0}000".format(int(time.mktime(datetime.now().timetuple())))
text = timestamp + method + path
sign = hmac.new(bytes(secretKey.encode("ascii")), bytes(text.encode("ascii")), hashlib.sha256).hexdigest()

headers = {
    "ACCESS-KEY": apiKey,
    "ACCESS-TIMESTAMP": timestamp,
    "ACCESS-SIGN": sign,
    "Content-Type": "application/json",
    }

response = requests.get(url, headers=headers)
res = response.json()
print(json.dumps(res, indent=4))

ハイライトしている行の secretKey と apiKey はご自身のものに置き換えてください。

以下のように、通貨名と残高が返ってきます。
※”残高の数字”の箇所は、実際はfloat型の数値です。

[
    {
        "currency_code": "JPY",
        "amount": 残高の数字,
        "available": 残高の数字
    },
    {
        "currency_code": "BTC",
        "amount": 残高の数字,
        "available": 残高の数字
    }
]

新規注文 sendchildorder

ビットフライヤーの新規注文APIには、親注文(parent order)と子注文(child order)と2種類ありますが、ここでは child order で説明します。指値のときは child order を使うので。

サンプルコードは、BTC_JPYを指値で800万円で0.001サイズ注文しています。
練習なので、絶対に約定しないような価格設定でAPIを呼びます。

method   = "POST"
endPoint = "https://api.bitflyer.com"
path     = "/v1/me/sendchildorder"
url = endPoint + path

parameters = {
    "product_code": "BTC_JPY",
    "child_order_type": "LIMIT", # 指値
    "side": "BUY",
    "size": 0.001,
    "price": 8000000,
}

timestamp = "{0}000".format(int(time.mktime(datetime.now().timetuple())))
text = timestamp + method + path + json.dumps(parameters)
sign = hmac.new(bytes(secretKey.encode("ascii")), bytes(text.encode("ascii")), hashlib.sha256).hexdigest()

headers = {
    "ACCESS-KEY": apiKey,
    "ACCESS-TIMESTAMP": timestamp,
    "ACCESS-SIGN": sign,
    "Content-Type": "application/json",
    }

response = requests.post(url, headers=headers, data=json.dumps(parameters))
res = response.json()
print(json.dumps(res, indent=4))

戻り値は”受付ID”が返ってきます。※”nn”の部分は数字が入ります。

{
    "child_order_acceptance_id": "JRFnnnnnnnn-nnnnnn-nnnnnn"
}

注文一覧の取得 getchildorders

新規注文が正しく通っているかを確認するために、注文一覧取得APIを使います。

4行目のハイライトしている行でハマりました。
url内にproduct_codeを入れ込むのですが、”?product_code=”形式じゃないとエラーになりました。
そのときのエラーは”error_message”: “Invalid signature” でした。

method   = "GET"
endPoint = "https://api.bitflyer.com"
path     = "/v1/me/getchildorders"
path += "?product_code=BTC_JPY"
url = endPoint + path

timestamp = "{0}000".format(int(time.mktime(datetime.now().timetuple())))
text = timestamp + method + path
sign = hmac.new(bytes(secretKey.encode("ascii")), bytes(text.encode("ascii")), hashlib.sha256).hexdigest()

headers = {
    "ACCESS-KEY": apiKey,
    "ACCESS-TIMESTAMP": timestamp,
    "ACCESS-SIGN": sign,
    "Content-Type": "application/json",
    }

response = requests.get(url, headers=headers)
res = response.json()
print(json.dumps(res, indent=4))

注文一覧を取得できます。

[
    {
        "id": 0,
        "child_order_id": "JORnnnnnnnn-nnnnnn-nnnnnn",
        "product_code": "BTC_JPY",
        "side": "BUY",
        "child_order_type": "LIMIT",
        "price": 8000000.0,
        "average_price": 0.0,
        "size": 0.001,
        "child_order_state": "ACTIVE",
        "expire_date": "yyyy-mm-ddT00:00:00",
        "child_order_date": "yyyy-mm-ddT00:00:00",
        "child_order_acceptance_id": "JRFnnnnnnnn-nnnnnn-nnnnnn",
        "outstanding_size": 0.001,
        "cancel_size": 0.0,
        "executed_size": 0.0,
        "total_commission": 0.0,
        "time_in_force": "GTC"
    }
]

注文のキャンセル cancelchildorder

注文キャンセルAPIにも、親注文用と子注文用がありますが、子注文用の例を示します。

キャンセルする注文の指定は新規注文時の”受付ID”で”JRF”で始まる文字列となります。
※”order_id”を”受付ID”に置き換えてください。

method   = "POST"
endPoint = "https://api.bitflyer.com"
path     = "/v1/me/cancelchildorder"
url = endPoint + path

parameters = {
    "product_code": "BTC_JPY",
    "child_order_acceptance_id": order_id,
}

timestamp = "{0}000".format(int(time.mktime(datetime.now().timetuple())))
text = timestamp + method + path + json.dumps(parameters)
sign = hmac.new(bytes(secretKey.encode("ascii")), bytes(text.encode("ascii")), hashlib.sha256).hexdigest()

headers = {
    "ACCESS-KEY": apiKey,
    "ACCESS-TIMESTAMP": timestamp,
    "ACCESS-SIGN": sign,
    "Content-Type": "application/json",
    }

response = requests.post(url, headers=headers, data=json.dumps(parameters))
print(response)
print(response.status_code)

戻り値はHTTPレスポンスのステータスコードだけで、中身はありませんでした。
ステータスコード=200ならOKです。
中身が無いので response.json() するとエラーになります。

<Response [200]>
200

約定一覧の取得 getexecutions

botで注文の約定を確認するために、約定一覧取得APIを使います。注文サイズが最小注文数量よりも大きい場合は、一度に約定せず、複数の取引に分かれることがあるので、bot作成では注意が必要です。

method   = "GET"
endPoint = "https://api.bitflyer.com"
path     = "/v1/me/getexecutions"
path += "?product_code=BTC_JPY"
url = endPoint + path

timestamp = "{0}000".format(int(time.mktime(datetime.now().timetuple())))
text = timestamp + method + path
sign = hmac.new(bytes(secretKey.encode("ascii")), bytes(text.encode("ascii")), hashlib.sha256).hexdigest()

headers = {
    "ACCESS-KEY": apiKey,
    "ACCESS-TIMESTAMP": timestamp,
    "ACCESS-SIGN": sign,
    "Content-Type": "application/json",
    }

response = requests.get(url, headers=headers)
res = response.json()
print(json.dumps(res, indent=4))

約定一覧が返ってきます。

[
    {
        "id": 0,
        "side": "BUY",
        "price": 8000000.0,
        "size": 0.001,
        "exec_date": "yyyy-mm-ddT00:00:00.000",
        "child_order_id": "JORnnnnnnnn-nnnnnn-nnnnnn",
        "commission": 7e-07,
        "child_order_acceptance_id": "JRFnnnnnnnn-nnnnnn-nnnnnn"
    }
]

APIのレスポンス

APIコールには requests を使います。
requests の戻り値の型は、requests.Response です。

response = requests.get(url)
print(response)
print(response.status_code)

上記のように request.get() を呼ぶと、以下のようになります。
botでは、HTTPレスポンスのstatus_codeを使ってIF文を書くのもアリだと思います。

<Response [200]>
200

requestsの戻り値ではなく、ビットフライヤーのAPIのレスポンスの中身は以下のようにして取得します。

res = response.json()

エラーコード

API使用時にエラーで引っかかることがあると思います。仕様書を読んでもわからないエラーもあるので、なかなか解決できずムキムキしてしまいます。私が遭遇したエラーの例を書き出してみました。

{
    "status": -500,
    "error_message": "Invalid signature",
    "data": null
}

“Invalid signature” は、timestampの作成方法がおかしかったり、signatureの構造がおかしいと、このエラーになります。
NGになっているAPIに関して、私のサンプルコードと比べていただけると原因がわかるかもしれません。

{
    "status": -109,
    "error_message": "Price should be 0",
    "data": null
}

priceパラメータの設定が不要なのに設定してしまった。

{
    "status": -209,
    "error_message": "You have reached the maximum open special orders.",
    "data": null
}

ビットフライヤーはある一定の時間内に注文できる数に上限がありそうです。確かではないのですが、各通貨で10回/分くらいです。

{
    "status": -200,
    "error_message": "Insufficient funds",
    "data": null
}

資金不足です。