ビットコイン自動裁定取引システムを開発・トレードした結果

# ビットコインをAPIで自動売買する
ビットコインの自動裁定取引システムのプロトタイプを開発しました。
以下の取引所に対し、3秒ごとに板情報を解析し、裁定機会があれば注文を送信します。

- bitFlyer
- Quoine
- Coincheck
- bitbank.cc
- BTCBox

ソースコードをGitHubに公開しています。TypeScriptで実装しており、Node.js環境でコンソールアプリとして動作します。

[R2 Bitcoin Arbitrager](https://github.com/bitrinjani/r2) [![GitHub stars](https://img.shields.io/github/stars/bitrinjani/r2.svg?style=social)](https://github.com/bitrinjani/r2/stargazers) [![Build Status](https://travis-ci.org/bitrinjani/r2.svg?branch=master)](https://travis-ci.org/bitrinjani/r2) [![Coverage Status](https://coveralls.io/repos/github/bitrinjani/r2/badge.svg?branch=master&i=4)](https://coveralls.io/github/bitrinjani/r2?branch=master) 


R2はMac OS, Windows, Linux環境で実行可能です。各取引所に口座開設をし、APIキーを取得すればだれでも実行可能です。

ライセンスは[MIT](https://ja.osdn.net/projects/opensource/wiki/licenses%2FMIT_license)です。

- 無償で無制限に利用可能です
- 複製、販売の制限もありません
- 作者は本ソフトウェアによって生じる一切の損害について責任を負いません

**⚠️オークションサイトで本ソフトウェアを高額で出品している方がいますが、作者とは無関係です。**

質問・機能追加依頼は[GitHubイシュー](https://github.com/bitrinjani/r2/issues)[![GitHub issues](https://img.shields.io/github/issues/bitrinjani/r2.svg?style=social)](https://github.com/bitrinjani/r2/issues)からお願いします。

## 裁定(アービトラージ)取引で収益をあげられるのか?
ビットコインには、取引所間での価格差を利用した裁定機会が残っています。実際に各取引所で配信されている価格をみると、タイミングによってはかなりの価格差があります。

この価格差を利用して、ある取引所で安く買い、同時に他の取引所で高く売れば、Bitcoinの価格変動にかかわらず、リスクなしに収益を得られることになります。

大半のビットコイン取引所はAPIを公開しており、理論的には一瞬の価格差を利用して、自動取引で継続的に収益を上げることが可能です。仮に価格差が非常に小さかったとしても、10秒単位で繰り返し取引を行うことで累積収益は十分に大きくなる可能性があります。

そのようなシステムを実際に作ることができるのか、という疑問を解消するために自分で開発してみました。

## 実際の実行結果

以下は実際にこの自動裁定取引システムを実行したときのスクリーン動画です。

![rinjani.gif](https://media.giphy.com/media/l378pfLDLVfkqykus/source.gif)

約30秒で二回の裁定機会を捉え、66円の収益を上げています。取引所の手数料等を差し引いても、秒速1円以上の収益を上げています。

このことから、ビットコイン市場には十分な裁定機会がある、と結論付けることができます。

しかし、実際にしばらく運用した後、長期運用にはいくつかの現実的な手間とコストが発生することが判明しました。それについては後述します。

# 自動裁定取引システムの動作
## 裁定ポジションオープンフロー
1. 3秒ごとに各取引所から板情報(価格・数量ペアのリスト)を取得する。
2. 現在のBTCポジションのネットエクスポージャーが設定された最大値を超えていないかチェックする。超えている場合、ステップ１に戻る。
3. 各取引所から取得した板情報のうち、裁定取引の対象になりえないものを取り除く。例えば、Bitflyerで現在のポジションが０で、かつ空売りをしない設定の場合、Bitflyerからのビッド(売値)は取り除く。
4. 最良ビッド(売値)、最良アスク(買値)を計算する。もしそのスプレッドが逆転してない場合(買値が売値より高い場合)、裁定機会は存在しないため、ステップ１に戻る。
5. スプレッドが逆転しているとき、期待収益割合(仮に最良ビッド/最良アスクで売り買いできたときに取引金額に対してどれだけの収益が得られるか)を計算する。その値が設定された目標収益割合(`minTargetProfitPercent`)を超えている場合、２つの取引所に指値注文を同時送信する。
6. 注文送信後、一定時間約定状況をチェックする。
7. もし売り買い注文両方が約定した場合、収益を表示しステップ１に戻る。一定時間後も片方のみ約定した状態の場合は、`onSingleLeg`設定に応じて反対売買等で偏りを解消する。

## 裁定ポジションクローズフロー
ポジションがオープンされた後は、以下の処理が1と2の間に追加される。

i. オープン中のポジション(オープンペア)に対し、反対売買によって`exitNetProfitRatio`の割合以上の利益を確定できるかチェックする。
ii. もし上記利益が確保できる可能性がある場合、２つの取引所に指値注文を同時送信しクローズを試みる。

## 設定値の動的な変更
`minTargetProfitPercent`, `exitNetProfitRatio`などの設定値は、アプリケーション実行中にも`config.json`を上書きすれば自動的に変更が反映されます。また、スプレッド統計データに基づいて動的に変更するAnalyticsプラグインをユーザーが作成することも可能です。Analyticsプラグインの詳細は[GitHubドキュメント](https://github.com/bitrinjani/r2/blob/master/docs/ANALYTICS_PLUGIN_JP.md)を参照ください。

##アーキテクチャ概要
各コンポーネントの説明は[GitHubのドキュメント](
https://github.com/bitrinjani/r2/blob/master/docs/ARCHITECTURE_JP.md)を参照ください。

![diagram_ja.png](https://qiita-image-store.s3.amazonaws.com/0/202597/c8bb5401-f8f1-6837-a122-c27215b8b6aa.png)

# インストール方法
1) [Node.js](https://nodejs.org) 8.5以降をインストール
2) ターミナルからgitリポジトリをクローン

```bash
git clone https://github.com/bitrinjani/r2.git
```

3) フォルダr2に移動し、`npm install`をコンソールで実行

```bash
cd r2
npm install
```

4) インストールフォルダ内の`config_default.json`を`config.json`にリネーム
5) `key`、`secret`フィールドを、各取引所から取得したAPIキー、シークレットに置き換える 
6) 日本語UIにする場合、`language`フィールドを"en"から"ja"に変更する
7) コンソールから`npm start`で起動

https://github.com/bitrinjani/r2/blob/master/docs/INSTALL_JP.md

## 設定概要
設定は`config.json`ファイルで行います。全体に影響する設定と、各取引所に対する設定があります。
全設定項目のリファレンスは[CONFIG_JP.md](https://github.com/bitrinjani/r2/blob/master/docs/CONFIG_JP.md)を参照ください。

設定項目は、裁定取引システムの先駆者である[Blackbird](https://github.com/butor/blackbird)を参考にしています。

### 全体設定(概要)
|Name|Values|Description|
|----|------|-----------|
|demoMode|true or false|デモモード。trueのとき、裁定機会の解析は行うが実際の注文は送らない。|
|maxSize|number|取引所に送る注文数量の最大値。仮にこの値よりも大きい数量で裁定可能であっても、この設定値の注文を送信する。|
|minSize|number|取引所に送るオーダー数量の最小値。裁定機会がこの値より小さい数量の場合、取引を行わない。|
|minTargetProfitPercent|number|最小目標収益割合。期待収益の裁定取引の円換算(取引価格*数量)に対する割合(%)がこれより小さい場合、取引を行わない。|
|exitNetProfitRatio|number|オープン時の収益に対し、クローズによって何%の利益を確定するか指定する。このパーセンテージ分だけオープン時からスプレッドが縮小した時、そのオープンペアをクローズする。例えば、オープン時の収益が200かつexitNetProfitRatio=20とすると、クローズのコストが160を下回ったときにR2はクローズオーダーを送信し、40の利益を確定しようとする。(オープンによる収益200, クローズによる費用160 -> 利益40)|
|maxNetExposure|number|最大ネットエクスポージャー。取引所の合計ネットエクスポージャーの絶対値がこの値を超える場合、取引を行わない。| 
|onSingleLeg|-|裁定ペアの片側だけ約定したときの動作を指定。詳細は[CONFIG_JP.md](https://github.com/bitrinjani/r2/blob/master/docs/CONFIG_JP.md)を参照|

#### minTargetProfitPercentの例
minTargetProfitPercent: 0.1%
ベストアスク: 800,000円, 0.3 BTC
ベストビッド: 801,000円, 0.2 BTC
-> MID: 800,500円, 目標数量0.2BTC,期待収益200円となり、収益の割合は 200 / (800500 * 0.2) = 0.0012、つまり0.12%。
この値はminTargetProfitPercentである0.1%を上回っているので、取引を送信します。

#### maxNetExposureの例
ここでのネットエクスポージャーとは、各取引所のポジションを合計した"BTC数量"です。一般にはエクスポージャーには数量ではなく割合を指しますが、簡略化のため数量としています。例えば、Bitflyerで0.1 BTC, Quoineで0.1 BTC, Coincheckでマイナス0.1 BTC(空売り)のとき、ネットエクスポージャーは 0.1 + 0.1 - 0.1 = 0.1 BTCとなります。仮にMaxNetExposure=0.05と設定されていた場合、0.1 > 0.05のため取引は送信しません。

### 取引所設定
|Name|Values|Description|
|----|------|-----------|
|broker|Bitflyer, Quoine or Coincheck|取引所名|
|enabled|true or false|裁定取引の対象とするかどうかの設定|
|key|string|取引所APIのキーもしくはトークン|
|secret|string|取引所APIのシークレット|
|maxLongPosition|number|最大ロングポジション|
|maxShortPosition|number|最大ショートポジション|
|cashMarginType|Cash, MarginOpen or NetOut|オーダータイプ。現金取引、証拠金取引オープン、ネットアウトのどれか。取引所ごとにサポートしているタイプは異なる。下記テーブルを参照。|
|commissionPercent|number|取引手数料割合。裁定プロセスが予想利益を計算する際、取引手数料(目標価格 * 目標数量 * (commissionPercent / 100))を期待収益から差し引いた上で、取引を送信するか判断する。|
|noTradePeriods|["開始時間", "終了時間"]のリスト|[CONFIG_JP.md](https://github.com/bitrinjani/r2/blob/master/docs/CONFIG_JP.md)を参照|

*取引所は最低2つ有効になっている必要があります。
### cashMarginType詳細
|取引所|サポートされるcashMarginType|
|--------|----------------|
|Bitflyer|Cash|
|Quoine|Cash, NetOut|
|Coincheck|Cash, MarginOpen, NetOut*|
|bitbank.cc|Cash|
|BTCBox|Cash|

*Coincheckのネットアウトは、取引所APIに存在しない取引タイプのため、アプリケーション内部でどのポジションをクローズするか判断しています。(Quoine APIはネットアウトをネイティブでサポートしています)
CoincheckのcashMarginTypeをNetOutに設定すると、裁定プロセスはオーダーを送信する前に現在のオープンポジションをチェックします。もしほとんど同じサイズのポジションが見つかれば、そのうち最も古いものに対しクローズオーダーを送信します。(FIFO)
ここで「ほとんど同じ」とは、1%以内の差異としています。コインチェックは、0.01 BTCの売注文を出すと、発生するポジションの数量が0.010005 BTCなど微妙に違う値になります。この違いを吸収するために1%の差異を許容しています。

### ログ通知設定 (Slack, LINE通知)

出力されるログの内容に応じて、Slack, LINEに通知を送ることができます。`keywords`で指定された複数キーワードのうち一つが含まれると通知します。
詳細は[CONFIG_JP.md](https://github.com/bitrinjani/r2/blob/master/docs/CONFIG_JP.md)を参照ください。

## 補助スクリプト
R2の裁定プロセスとは別で、単体で動作する補助スクリプトをいくつか用意しています。
config.jsonのキー、シークレットを読み取り取引所APIを実行します。
ソースは[toolsディレクトリ](https://github.com/bitrinjani/r2/tree/master/tools)下です。

### getBalance - 各取引所のJPY, BTC残高をCSV形式で出力

```bash
npm run -s getBalance
```

出力例:

```
Exchange, Currency, Type, Amount
bitFlyer, JPY, Cash, 300000
bitFlyer, BTC, Cash, 1.234
Coincheck, JPY, Cash, 300000
Coincheck, BTC, Cash, 0.123
Coincheck, JPY, Margin, 200000
Coincheck, JPY, Free Margin, 123456
Coincheck, BTC, Leverage Position, 3.456
Quoine, JPY, Margin, 300000
Quoine, JPY, Free Margin, 123456
Quoine, BTC, Leverage Position, 0.01
```

### closeCcPosition - Coincheckの全レバレッジポジションを成行注文でクローズ

```bash
npm run closeCcPosition
```

### closeBfPosition - bitFlyerの全現物BTCを成行注文で売却

```bash
npm run closeBfPosition
```

### closeQuPosition - Quoineの全レバレッジポジションを成行注文でクローズ

```bash
npm run closeQuPosition
```

### clearPairs - R2が保持しているオープンペア情報をクリアする(取引は送信されません)

```bash
npm run clearPairs
```

### closeAll - 上記3つのcloseを順に実行し、clearPairsでペア情報をクリア後、getBalanceを実行する。

```bash
npm run closeAll
```

# 問題点
## 取引所の証拠金取引(レバレッジ取引)の可否
取引所によっては、証拠金取引が行えないケースがあります。
例えば、bitFlyerは証拠金取引を「ビットコインFX」として提供していますが、ビットコイン現物とはまったく異なる価格帯で取引が行われています。ビットコインFXに対しては、シンプルな価格比較では裁定取引が行なえません。
そのため、bitFlyerに対しては、この自動裁定取引システムは~~ビットコイン現物のみを裁定対象~~としています。

1/21追記: bitFlyerのBTC-FX/JPYを現物と別ブローカーとして追加するプラグインを作成しました。https://github.com/bitrinjani/bitflyer-fx

レバレッジがかけられないと、ショートポジションが取れない、多額の現金が必要になるなど、継続的な裁定取引の障害となります。


## 取引所のAPIのスピード、品質
取引所によっては、APIから指値注文を送信後、10秒以上実際の注文が作成されない場合があります。この現象は特にBitflyerで一時的に発生していました。(現在は解消されている模様です。)
これがAPIインフラの遅延なのか、意図的なものなのかは不明ですが、10秒遅れると約定する見込みはほとんどなくなります。

また、取引所によってAPIの品質に非常に大きなばらつきがあります。今回使用した３社だけでなく、７社の口座を開設しAPIの使用感を確認しました。
使いやすさ、統一性という点では、Bitflyer, Quoineが非常に素晴らしいAPIを提供しています。(上記のようにBitflyerにはスピードの問題がありましたが、それはAPI設計ではなくインフラの問題だと思われます)
それに対し、価格取得すら安定的にできないレベルのAPIを提供している取引所も多数あります。
より多くの取引所を対象にすれば、より多くの裁定機会を見つけられるはずですが、実際に裁定取引に耐える品質のAPIを提供している取引所は多くありません。

## 取引所ごとの価格の偏りから派生するポジションの偏り
取引所ごとに、価格の高低に偏りがあります。例えば、Bitflyerは安値になる傾向があり、Quoineは高値になる傾向がありました。このとき、裁定取引はBitflyerに対し買いを出し、Quoineに対し売りをだすことになります。そして、この傾向が長期間続くとBitflyerではロングポジションが増大していき、Quoineではショートポジションが増大していきます。スプレッド縮小時に`exitNetProfitRatio`設定に応じてポジションがクローズされることを想定していますが、長期間スプレッドが縮小しないこともあります。

各取引所でポジションが偏ると、その分だけ現金・証拠金・手数料が多く必要になります。また、偏りをならすために取引所間でBTCを移動する必要が出てきます。また、移動後の取引所内で売り買いの決済取引のコストが発生します。決済取引のコストは、その取引所のその時点のスプレッドとなり、１BTCあたり1000円以上になることがあります。

# 結論
自動裁定取引システムである程度の収益を上げることは十分可能です。今後さらに取引所API、取引所システムが洗練されていけば、より効率的に裁定が行えるようになっていくはずです。それに伴い、裁定取引への参入者が増え、各人の裁定収益は減っていくことも予想されます。
品質の悪いAPIを提供している取引所を逆に自分以外への参入障壁とみなし、あえてそこで裁定取引を行うことで独占的利益を得られるかもしれません。

# FAQ

## Q. Zaifに対応してほしい
A. Zaifに対応させる予定はありません。理由は[こちら](https://github.com/bitrinjani/r2/issues/113
)をご覧ください。

## Q. Unauthorizedエラーが発生する
以下のようなUnauthorizedエラーが表示される。
>HTTP request failed. Response from https://api.bitflyer.jp/v1/me/getbalance. Status Code: 401 (**Unauthorized**) Content: {"status":-500,"error_message":"Invalid signature","data":null}

A. 取引所のAPIキー/シークレットがconfig.jsonに正しく設定されているか確認してください。APIキー/シークレットの取得方法は、各取引所のWebページを参照してください。

## Q. スプレッド解析結果の取得に失敗する
以下のような警告が表示される。
>スプレッド解析結果の取得に失敗しました。 ベストビッドが見つかりませんでした。

A. config.jsonの各取引所設定部分のmaxLongPosition, maxShortPositionが十分に大きいか確認してください。
売り試行許可、買い試行許可は、取引所設定のmaxLongPosition、maxShortPositionで決まります。
例えば、Quoineの売りが続くといずれQuoineのmaxShortPosition設定にぶつかり、売り試行許可がFalseに切り替わり、ベストビッドの候補対象から外れます。

## Q. 取引所間でBTC保有量のバランスが崩れた際、バランスを判定し、自動で取引所間で送金するというような機能を実装する予定はありますか?

送金は実装予定はありません。理由は、BTC送金に時間がかかることと、日本円送金に大きな手数料がかかるため、自動化してもメリットがないためです。
BTC送金自体は簡単なのですが、BTCを送金した取引所口座は、そのBTCを購入するのにかかった費用の分だけ日本円が減っている状態になります。そのバランスを修正するには、BTCを受け取った取引所口座から日本円を送金するか、銀行から日本円を送金する必要があります。それに伴い、日本円送金の手数料が発生します。
この部分を自動化して細かい単位で送金をしつづけると、手数料がつもり重なって大きなコストになります。

## Q. このようなアプリケーションを公開することで、裁定機会がなくなってしまうのではないか?

いいえ。仮に数百人が使ったとしても、このアプリケーションが原因で裁定機会はなくなることはありません。ビットコインの取引高は月間何兆円という単位です。個人にマーケットを動かす力はありません。