BitFlyer API + Python でビットコインを売買するプログラムを作る

年末年始の休みに入ったこともあり、デジタル大掃除ということで各種アカウントを整理していると、最近ほとんど取引していなかった BitFlyer のアカウントに残高が残っていた。ちょうどいい機会と思い、暗号資産*1とプログラミングの勉強を兼ねて、ビットコインを自動売買するプログラムを書いて動かしてみた。

BitFlyer API

BitFlyer では、開発者向けに各種 API を提供している。今回は HTTP API を利用している*2が、一般的な API と同様に、形式に従ってリクエストボディを作成し、所定のエンドポイントに POST することで、BitFlyer の機能を利用することができる。

公式のドキュメントは以下で参照できる。

lightning.bitflyer.com

BitFlyer での各操作に対応した API エンドポイントにリクエストを投げることになるが、前準備として以下が必要になる。

  • BitFlyer に口座を作成し、API key と API secret を取得しておく。
  • リクエストボディに、各APIで必要となる情報を含める。
  • リクエストヘッダに、以下の3つの情報を含める。
    • ACCESS-KEY: 開発者ページで発行した API key
    • ACCESS-TIMESTAMP: リクエスト時の Unix Timestamp
    • ACCESS-SIGN: 以下の方法でリクエストごとに生成した署名
  • ACCESS-SIGN は以下の方法で生成する。
ACCESS-SIGN は、ACCESS-TIMESTAMP, HTTP メソッド, リクエストのパス, リクエストボディ を文字列として連結したものを、API secret で HMAC-SHA256 署名を行った結果です。

(公式のものをそのまま引用しているが、)Node.js でのサンプルは以下のようになる。

// Node.js のサンプル
var request = require('request');
var crypto = require('crypto');

var key = '{{ YOUR API KEY }}';
var secret = '{{ YOUR API SECRET }}';

var timestamp = Date.now().toString();
var method = 'POST';
var path = '/v1/me/sendchildorder';
var body = JSON.stringify({
    product_code: 'BTC_JPY',
    child_order_type: 'LIMIT',
    side: 'BUY',
    price: 30000,
    size: 0.1
});

var text = timestamp + method + path + body;
var sign = crypto.createHmac('sha256', secret).update(text).digest('hex');

var options = {
    url: 'https://api.bitflyer.com' + path,
    method: method,
    body: body,
    headers: {
        'ACCESS-KEY': key,
        'ACCESS-TIMESTAMP': timestamp,
        'ACCESS-SIGN': sign,
        'Content-Type': 'application/json'
    }
};
request(options, function (err, response, payload) {
    console.log(payload);
});

pybitflyer を使う

上記のように、リクエストボディと認証情報を作成するのが中々面倒なので、python ラッパーである pybitflyer を利用させていただく。

github.com

GitHub からダウンロードできるほか、pip でインストールもできる。

% pip install pybitflyer

使う際はそのまま import pybitflyer で利用可能。例えば、板情報を閲覧したい場合は、以下のように実行すると良い。

import pybitflyer
print(pybitflyer.API().board(product_code='FX_BTC_JPY'))

pybitflyer を使って売買する

売買する場合には、口座に加えてAPIキーとAPIシークレットが必要になる。BitFlyer にログイン後、Developer Page( https://lightning.bitflyer.com/developer )にアクセスし、必要な権限を指定して作成する。デフォルトでは、入出金も含めて全ての権限が付与されるため注意が必要。万が一APIキーが漏れてしまった場合や、誤って注文や入出金などをしてしまわないためにも、必要な権限のみに絞っておく方が良さそう。

f:id:gumfum:20211231103924p:plain

f:id:gumfum:20211231104013p:plain

APIキーが作成できたら、pybitflyer.API の引数として指定することで売買用の関数 sendchildorder が機能が利用できるようになる。引数として以下の項目を指定する。

  • product_code: 注文するプロダクトを指定する
  • child_order_type: 指値注文の場合は "LIMIT"、成行注文の場合は MARKET を指定する
  • side: 買い注文の場合は BUY、売り注文の場合は SELL を指定する
  • size: 必須。注文数量を指定する
  • price: 指値注文の場合は必須、価格を指定する
api = pybitflyer.API(api_key=api_key, api_secret=api_secret)

# 成行で買い注文
response = api.sendchildorder(product_code='BTC_JPY',
                              child_order_type='MARKET',
                              side='BUY',
                              size=0.01)

# 指値で売り注文
response = api.sendchildorder(product_code='BTC_JPY',
                              child_order_type='MARKET',
                              side='SELL',
                              size=0.01,
                              price=5700000)

自動売買してみる

ここまでできれば基本的な売買は可能になるので、単純な「一定値より安くなったら買う、一定値より高くなったら売る」を実行するスクリプトを書いて実行してみる。 このスクリプトでやっているのは以下の通り。

  • buy_price に買いたい金額、sell_price に売りたい金額を設定しておく。
  • 10秒ごとに板情報を取得して、mid_price : 売り注文/買い注文の真ん中の金額を参照する。
  • まだ購入しておらず( buy_flag == false )、mid_price が買いたい金額を下回っていれば( buy_price > mid_price )、size 分の買い注文を出す。
  • すでに購入しており( buy_flag == true )、mid_price が売りたい金額を上回っていれば( sell_price < mid_price )、size 分の売り注文を出す。
api_key = xxxxxxxxxxxxxxxxxxxxxxxx
api_secret = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

api = pybitflyer.API(api_key=api_key, api_secret=api_secret)

buy_price = 5700000
sell_price = 5720000
size = 0.01
buy_flag = True

while True:
  board = api.board(product_code='BTC_JPY')
  mid_price = board['mid_price']

  if buy_flag and buy_price > mid_price:
    print('#### BUY ####')
    print('buy_price:', mid_price)
    response = api.sendchildorder(product_code='BTC_JPY', child_order_type='MARKET', side='BUY', size=size)
    print(response)
    buy_flag = False
    print('#### ### ####')
  elif not buy_flag and sell_price < mid_price:
    print('#### SELL ####')
    print('sell_price:', mid_price)
    response = api.sendchildorder(product_code='BTC_JPY', child_order_type='MARKET', side='SELL', size=size)
    print(response)
    buy_flag = True
    print('#### #### ####')
  else:
    print('mid_price:', mid_price)

  sleep(10)

このスクリプトを走らせておくだけで、条件を満たした場合に自動で売買してくれる。タイミング良く設定した値の間を上下してくれると、放っておくだけで利益が出せる*3

ハマったポイント

基本的な操作は以上であるが、いくつかハマったポイントがあったのでメモしておく。

BTC_JPYFX_BTC_JPY

ビットコイン関連の produce_code には BTC_JPYFX_BTC_JPY が存在しているが、両者は同じものではなく、BTC_JPYは取引所、FX_BTC_JPYはFXとしての扱いとなる。

BTC_JPY は、BitFlyer の口座に入金したお金で取引がそのまま可能であるが、FX_BTC_JPY での取引には「証拠金」が必要となり、BitFlyer の口座に入金 → FX 用に資金を移動 をさせる必要があるので注意が必要。

手数料

BTC_JPY での手数料は、取引完了時に取引した仮想通貨立てで徴収される。 例えば、手数料が0.15%の場合、0.01₿ を購入すると口座に入るのは 0.009985₿ となり、同じ単位ではそのまま売り注文ができないため注意が必要。 また、直近30日間の取引金額が大きくなるほど手数料は安くなる。

FX_BTC_JPY での手数料は無料であるが、日付を跨いで*4建玉(買い/売りの注文は成立したが、その逆の取引が完了していないもの)を保持していると、スワップポイントが発生する。この金額は証拠金から引かれることになるので、注意しておく必要がある。

手数料の詳細は以下のページに記載されているので、取引開始前に確認しておくと良さそう。

bitflyer.com

取引失敗時のエラーコード

APIドキュメントには成功時のレスポンスしか記載がなく、失敗時の判定をしたい場合は注意が必要。 例えば、残高が少なく売り注文が失敗した場合には、以下のようなレスポンスが返されてきた。

最小単位以下の注文をしようとした場合

{'status': -104, 'error_message': 'Enter the size in units of 0.00000001 BTC.', 'data': None}

残高が足りないのに売り注文をしようとした場合

{'status': -200, 'error_message': 'Insufficient funds', 'data': None}

成功の場合は以下のようなレスポンスになるため、statusHTTPステータスコードとは無関係と考えた方が良さそう。error_message にエラーの内容は記載されているため、内容に合わせて対応すれば良い。

{'child_order_acceptance_id': 'JRF20211226-122614-006702'}

感想

何度かプログラムを改良して動かしてみたが、いまいち取引の流れや手数料の考え方を掴むことができなかったので、一度 BitFlyer のサイト上で手動で取引してみたところ結構面白くなり、ハマってしまった感がある。

思った通りに注文ができると利益が発生するのと、大きな数字が動くのが面白い*5のだとは思う一方、上手くいかなかった際に取り返そうとして失敗する、ギャンブルと同じような沼にハマる恐怖もあると感じたので、自己責任・余剰資産でやるものと心得ておくことが肝心だと思わされた。

基本的な使い方はほとんどこれだけなので、あとは年末年始の時間を使って(大火傷しない程度に)遊んでみようと思う。

*1:調べたところ、2019年に資金決済法と金融商品取引法が改正され、『仮想通貨』から『暗号資産』に呼称が変更されることが決定したとのことで一応この書き方にしているが、それ以前から触っていると『仮想通貨』の方がやはり馴染みがある。

*2:他にも Realtime API など様々なAPIが適用されている: https://bitflyer.com/ja-jp/api

*3:実際にこの記事を書きながら24時間実行してみたが、売買は2回しか発生していなかった。安定して利益を出すには、さらなる工夫と時の運が必要そう。

*4:このあたりの詳細な発生条件は手数料のページに記載がないが、一般的な取引所のルールや個人ブログの記載によると、0時0分0秒のタイミングで発生していると考えられる。私は一度も発生させたことがなく未確認。

*5:これは Cookie Clicker と同じ種類の面白さだと感じる。