仮想通貨アービトラージシステムを作る[Binance vs Poloniex]
公開日:
:
APIの仮想通貨自動売買 仮想通貨, アービトラージ
目次/もくじ
この記事ではポロニエックスとバイナンスでAPIを使ったアービトラージシステムを作る具体的な方法を解説しています。
開発の背景
ずっと国内の大手3社でアービトラージしているのですが、サーバーの脆さや約定力のなさが目立つことが良くあります。
組み合わせを変えたり、4社目(QuoinExとか)を入れるという選択肢もあるのですが、プログラマーの経験上、この方向性は泥沼になるような気がしていました。
他にも日本の取引所vs海外の取引所でアービトラージする方法もあるにはありますが、ドル円の換算レートをどうするかという問題があり、
ドル円レートがちょっとでも変わるとそれぞれのレートが大きく変わってしまいます。
本当にレート差が発生しているのか、参照しているドル円レートの差によるものなのか分からないので、変数が増えて苦労して終わりそうな予感がします。
ということで、第三の選択肢として、今回は海外の取引所同士でアービトラージをしてみようと思います。
なぜバイナンスとポロニエックスか
海外同士であれば、ドルベースでレートを比較できるため、為替レートを考慮する必要がありません。
また、海外の取引所サーバーは日本の取引所よりも頑丈で、かつ、取引量も多いので、上記のような問題は多かれ少なかれ解決するのではないか、という魂胆です。
Poloniexに関しては、以前までは破たんの噂やチケットが処理されないなどの問題がありましたが、買収されて管理者が変わることで改善されていくのではないかと思います。
Binanceは歴史が浅すぎるのでかなり心配していたのですが、以前のハッキングの対応や、拠点をマルタに移すこともあり、FX業界の人からするとそこそこ長くやるつもりなのかな、という印象を持ちました。
他にも海外の取引所の中で取引量が大きくてメジャーなところはありますが、日本人がフツーに口座開設できて大きなところはこの2つだと思います。
(bybitも昨今にぎわっている取引所です。)
Poloniexに関してはこれまでにレート取得や残高照会をAPIでやっているので、そちらを見ていただければ特に問題なく実装できると思います。
Poloniex APIドキュメント
https://poloniex.com/support/api/
Binanceに関してはドキュメントがちょっと読みにくい印象があったのですが、こちらも特に問題なくこれまでのメソッドをちょっと書き換えるだけで流用できると思います。
Binance APIドキュメント
https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md
Binanceはレバレッジ取引(証拠金取引)ができないので、アービトラージは必然的に現物のみとなります。(せっかくPoloniexでレバレッジ取引のメソッドを作ったのに・・・)
この点からも実装に関しては国内3社のときよりもかなり楽になるかと思います。
レートを比較してみる
下記は実際にレートを比較してみた際のレートです。
時刻 2018/4/3 14:6:43
Poloniex ask:7377.355 -bid:7364.275 = 13.08
Binance ask:7360 -bid:7355.02 = 4.98
時刻 2018/4/3 14:8:6
Poloniex ask:7368.188 -bid:7353 = 15.188
Binance ask:7355.05 -bid:7355 = 0.05
時刻 2018/4/3 14:8:39
Poloniex ask:7365.589 -bid:7353 = 12.589
Binance ask:7349.03 -bid:7349 = 0.03
Binanceは他と比較してかなりスプレッドが狭くなる傾向があるようです。
しかし、取引量が多いということはそれだけアービトラージをする人たちも多いということで、レート差は国内に比べると小さいみたいですね。
ちなみにBinanceのレートは
1 |
https://api.binance.com/api/v1/depth?symbol=BTCUSDT |
Poloniexは、
1 |
https://poloniex.com/public?command=returnTicker¤cyPair=USDT_BTC |
です。
どちらもGETで、jsonで返ってくるので、適当に配列に入れて取り出してください。
国内の取引所だとビットコインのアービトラージしかできませんが、この組み合わせならライトコインやイーサリアムをはじめとするアルトコインのアービトラージができるんじゃないかと思います。
バイナンスのプライベートAPIのクセ
BinanceのプライベートAPIにちょっと他の取引所とは違うクセがあったので、今回はそれを紹介します。
APIドキュメント
https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md
一般的にプライベートメソッドを使うときは、特定のパラメータを連結させてHash化するのですが、Binanceの場合、Hash化した署名をさらに再帰的にパラメータの中に入れる必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Mixed query string and request body queryString: symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC requestBody: quantity=1&price=0.1&recvWindow=5000×tamp=1499827319559 HMAC SHA256 signature: [linux]$ echo -n "symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTCquantity=1&price=0.1&recvWindow=5000×tamp=1499827319559" | openssl dgst -sha256 -hmac "NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j" (stdin)= 0fd168b8ddb4876a0358a8d14d0c9f3da0e9b20c5d52b2a00fcf7d1c602f9a77 curl command: (HMAC SHA256) [linux]$ curl -H "X-MBX-APIKEY: vmPUZE6mv9SD5VNHk4HlWFsOr6aKE2zvsw0MuIgwCIPy6utIco14y7Ju91duEh8A" -X POST 'https://api.binance.com/api/v3/order?symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC' -d 'quantity=1&price=0.1&recvWindow=6000000×tamp=1499827319559&signature=0fd168b8ddb4876a0358a8d14d0c9f3da0e9b20c5d52b2a00fcf7d1c602f9a77' Note that the signature is different in example 3. There is no & between "GTC" and "quantity=1". |
今までにない方法なので、ちょっと違和感があります。(というか署名はヘッダーに入れればそれでよくない?)
オーダーするときはこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$URL_b = "https://api.binance.com/api/v3/order"; $TimeStamp_b = round(microtime(true) * 1000); $arrQuery_b = array("symbol" => "BTCUSDT", "side" => $action, "type" => "MARKET", "timestamp" => $TimeStamp_b, "quantity" => $orderlot, ); $Body_b = http_build_query($arrQuery_b); $Signature_b = hash_hmac("sha256", $Body_b, $AccessSecret_b); $arrQuery_b = array("symbol" => "BTCUSDT", "side" => $action, "type" => "MARKET", "timestamp" => $TimeStamp_b, "quantity" => $orderlot, "signature" => $Signature_b ); $Body_b = http_build_query($arrQuery_b); |
ただ、Binanceはエラーコードが親切丁寧なので、エラーさえ出れば何が悪いのかがすぐに分かるようになっています。
Binance APIエラーコード
https://github.com/binance-exchange/binance-official-api-docs/blob/master/errors.md
MQL並に細かいエラーコードがあるので、この点はさすがと言った感じです。
あとは現物しかできないので、証拠金取引特有の変なクセやバグも気にせず使えます。
ポロニエックスのAPIのおさらい
Binanceのオーダーができるようになったので、一応PoloniexのAPIによるオーダーもおさらいしておきます。
今回のシステムは現物のみなので、(Binanceが現物しかできないので)、
・ポジションの決済
・片方の取引所だけポジションをとった時の処理
などめんどくさいことは考えずに済みます。
また、2社固定なので、組み合わせによる場合分けもしなくてOKです。
ドキュメントには以下のように記載されています。
Trading API Methods
To use the trading API, you will need to create an API key.
Please note that there is a default limit of 6 calls per second. If you require more than this, please consider optimizing your application using the push API, the “moveOrder” command, or the “all” parameter where appropriate. If this is still insufficient, please contact support to discuss a limit raise.
All calls to the trading API are sent via HTTP POST to https://poloniex.com/tradingApi and must contain the following headers:
Key – Your API key.
Sign – The query’s POST data signed by your key’s “secret” according to the HMAC-SHA512 method.
Additionally, all queries must include a “nonce” POST parameter. The nonce parameter is an integer which must always be greater than the previous nonce used.
いろいろメソッドがありますが、使うのはbuyくらいです。
buy
Places a limit buy order in a given market. Required POST parameters are “currencyPair”, “rate”, and “amount”. If successful, the method will return the order number. Sample output:
{“orderNumber”:31226040,”resultingTrades”:[{“amount”:”338.8732″,”date”:”2014-10-18 23:03:21″,”rate”:”0.00000173″,”total”:”0.00058625″,”tradeID”:”16164″,”type”:”buy”}]}
You may optionally set “fillOrKill”, “immediateOrCancel”, “postOnly” to 1. A fill-or-kill order will either fill in its entirety or be completely aborted. An immediate-or-cancel order can be partially or completely filled, but any portion of the order that cannot be filled immediately will be canceled rather than left on the order book. A post-only order will only be placed if no portion of it fills immediately; this guarantees you will never pay the taker fee on any part of the order that fills.
sell
Places a sell order in a given market. Parameters and output are the same as for the buy method.
Poloniexでは成行注文のメソッドがないので、自動的に指値になります。指値でアービトラージするとオーダーが約定しなかったり、一部残ることがあるので、現在レートから不利なレートを指定することで実質的な成行注文にします。
もちろん指値でアービトラージできなくもないと思いますが、約定しなかったり、タイミングが大きくずれてきたりすることがあるので、ちょっと意味合いが変わってくると思います。
ポロニエックスのオーダー関数
以下は例によってオーダー関数です。一応0からドキュメントを読んで自前で作った方が良いと思いますが、
ドキュメントが良く分からなくてもこの関数にてきとうに変数に値を代入すれば動きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
FUNCTION TradePL($ordertype,$orderrate,$orderlot) { global $AccessKey_p; global $AccessSecret_p; $orderpair = "USDT_BTC"; $intNonce_p = round(microtime(true) * 1000); if( $ordertype == "buy" ) { $ordertype = "buy"; $arrQuery_p = array("nonce" => $intNonce_p, "command" => $ordertype, "amount" => $orderlot, "currencyPair"=> $orderpair, "rate" => $orderrate); } else if( $ordertype == "sell" ) { $ordertype = "sell"; $arrQuery_p = array("nonce" => $intNonce_p, "command" => $ordertype, "amount" => $orderlot, "currencyPair" => $orderpair, "rate" => $orderrate); } $URL_p = "https://poloniex.com/tradingApi"; $Body_p = http_build_query($arrQuery_p); $res_p = CURL_PL("POST",$URL_p,$Body_p,$arrQuery_p); $res_p = "Poloniex:".$res_p; return $res_p; } FUNCTION CURL_PL($postorget,$URL_p,$Body_p,$arrQuery_p) { global $AccessKey_p; global $AccessSecret_p; $Signature_p = hash_hmac("sha512", $Body_p, $AccessSecret_p); $curl_p = curl_init(); curl_setopt($curl_p,CURLOPT_RETURNTRANSFER,true); if( $postorget == "POST") { curl_setopt($curl_p, CURLOPT_POST, true); curl_setopt($curl_p, CURLOPT_POSTFIELDS, $Body_p); } else if( $postorget == "GET" ) { curl_setopt($curl_p, CURLOPT_CUSTOMREQUEST, 'GET'); curl_setopt($curl_p, CURLOPT_GETFIELDS, $arrQuery_p); } $headers_p = array( "Sign: {$Signature_p}", "Key: {$AccessKey_p}", ); curl_setopt($curl_p, CURLOPT_HTTPHEADER,$headers_p) ; curl_setopt($curl_p, CURLOPT_URL, $URL_p); $res_p = curl_exec($curl_p); curl_close($curl_p); return($res_p); } |
今回は現物の取引しかしないので、わざわざCURLを関数にまとめる必要はないと思いますが、使い回しなのでそのままにしています。
APIで残高を参照する
順番が前後しましたが次は残高照会をやります。アービトラージをする上で必ずなければならないものではないので、残高照会をせずに構築する場合はスルーして結構です。
まず、Binanceです。
GetでSHA256でパラメータにタイムスタンプが必要です。
Account information (USER_DATA)
GET /api/v3/account (HMAC SHA256)
Get current account information.
Weight: 5
Parameters:
Name Type Mandatory Description
recvWindow LONG NO
timestamp LONG YES
Response:
{
“makerCommission”: 15,
“takerCommission”: 15,
“buyerCommission”: 0,
“sellerCommission”: 0,
“canTrade”: true,
“canWithdraw”: true,
“canDeposit”: true,
“updateTime”: 123456789,
“balances”: [
{
“asset”: “BTC”,
“free”: “4723846.89208129”,
“locked”: “0.00000000”
},
{
“asset”: “LTC”,
“free”: “4763368.68006011”,
“locked”: “0.00000000”
}
]
}
例のように返ってくるjsonはbalancesの中に通貨ごとに入っているので、取り出す際はちょっと面倒です。BTCは常に先頭ですが、USDTの残高を紹介したい場合は、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
FUNCTION GetTickBN() { global $BN; $Url = "https://api.binance.com/api/v3/ticker/bookTicker?symbol=BTCUSDT"; $file = file_get_contents($Url); $file = mb_convert_encoding($file, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN'); $BN = json_decode($file,true); $BN["ask"] = round($BN["askPrice"],3); $BN["bid"] = round($BN["bidPrice"],3); } $Equity_bn = json_decode(EquityBN(),true); for($i=0;$i<100;$i++) { if( $Equity_bn["balances"][$i]["asset"] == "USDT" ) { $Company2Equity = $Equity_bn["balances"][$i]["free"]; break; } } |
こんな感じでfor文で回せばいいと思います。(もっと良い方法がありますが、こちらの方が汎用性が高いので)
Poloniexはスマホアプリを作った際にやったと思いますが、
1 2 3 4 5 6 7 8 9 10 |
FUNCTION EquityPL() { $intNonce_p = round(microtime(true) * 1000); $URL_p = "https://poloniex.com/tradingApi"; $arrQuery_p = array("nonce" => $intNonce_p, "command" => "returnBalances"); $Body_p = http_build_query($arrQuery_p); $res_p = CURL_PL("POST",$URL_p,$Body_p,$arrQuery_p); return $res_p; } |
こんな感じでとりだせます。
returnBalances
Returns all of your available balances. Sample output:
{“BTC”:”0.59098578″,”LTC”:”3.31117268″, … }
例のような戻り方をするので、binanceよりも取り出しが楽です。
開発の枠組み
今までAPIを使ったトレードシステムを作るときには、オーダーとレート取得ができれば後の枠組みは解説していなかったのですが、今回はどのような枠組みで構築するかを少し説明したいと思います。
MQLで自動売買プログラムを作ったことがある人には説明不要ですが、基本的には上から順番に
・トレードに使う変数の数値の確認
・レート取得(パブリックAPI)
・最後のトレード時刻と現在時刻を比較
・取得したレートを比較
・条件を満たした場合オーダー(プライベートAPI)
・通知メール送信
という感じです。
必要に応じてアカウント残高を参照して、あと何回トレードできるかを調べたり、トレードの前にサーバー状態を確認するオプションを付けても良いと思います。
あとはこれを繰り返し呼び出してあげます。自動売買専用プログラムであるMQLの場合、繰り返しの呼び出しはStart(),OnTick()などの関数で行われるのですが、自前のプログラムの場合は何らかの方法で繰り返し実行させる必要があります。ここらへんについてはすでに過去の記事で説明済みなので、そちらを参照してください。
通知メールについてはPHPの場合、少し検索すればいくらでもテンプレがでてくるので、そちらをコピペして微修正してみてください。
一回の演算で複数のAPIリクエストを送るやり方
基本的にjson形式でAPIをたたく場合、nounceにUNIXタイムスタンプを使用します。
(取引所も推奨、もしくは指定しています。)
しかし、一回の演算で、レートを取得して、資産残高を取得して、オーダーを出すということをする場合、
UNIXタイムスタンプ(秒)では時刻が同じになってしまい、エラーになります。
つまり、一回の演算の中で、取引所へのアクセス毎にnounceを増大させる必要があります。
やり方はいろいろあると思います。(UNIXタイムスタンプの最後に一桁足して、送信毎に+1するとか)
しかし、今回は単純なミリ秒を使います。(ちなみに、ZAIFはデフォルトでミリ秒に対応しています)
ただし、小数点が入っていると整数でないため、エラーになります。そこで、1000倍して、四捨五入します。
$intNonce_c = time(true);
↓
$intNonce_c = round(microtime(true) * 1000);
コインチェックの場合は”前回よりも大きな数字”であればOKな記述とドキュメントに書いてありますが、
ビットフライヤーの場合は”UNIXタイムスタンプ”と指定しているので、
これで通るか心配でしたが、特に問題ありませんでした。(ドキュメントは嘘ってことですね)
ひとまずこれで一回の演算で複数回APIをたたくことができるようになったので、本格的なアービトラージソフトの開発が捗ります。
[API]アルトコインのアービトラージの作り方[Poloniex Binance]
国内の取引所のAPIではアルトコインのアービトラージができない
たまに「アルトコインの自動アービトラージシステムを作れませんか」、という質問が来るのですが、国内の取引所は現在点ではAPIがそもそもアルトコインに対応していないので”困難”です。(APIを使わなければ不可能ではありません。)
ビットフライヤーはイーサリアムができますが、対通貨がビットコインです。
ZAIFは特に注意書きはありませんが、レートが取得できるペアすべてがトレードできるとは考えにくいので、一つ一つチェックした方が良いと思います。
https://api.zaif.jp/api/1/currency_pairs/all
アービトラージするにはそれぞれの取引所で同じ通貨ペアを扱うことができないといけないので、国内の取引所は現時点では取引所の対応待ちです。もしアルトコインも多数自動売買できる謳い文句の商材があれば”ダウト”なので、注意してください。
チャットAPIのように、実際には実装されていても、ドキュメント上にないパターンも考えられますが、お金が動くトレードでそれをするのはちょっとリスクが高いと思います。
[コインチェック]APIが存在しないはずのチャットの自動読み上げシステムを作るやり方
単一の取引所で可能なスキャルピングであればアルトコインで実装できます。
また、海外の取引所でも対応しているところであれば実装可能です。
あるいは、そもそも「APIを使わない」という選択肢であれば可能です。ブラウザをCOMでオブジェクトとして操作してトレードすれば自動でできます。この周辺については過去記事にありますので、そちらを参照してください。
PoloniexとBinanceでアルトコインアービトラージ
国内の取引所ではAPI取引でビットコイン以外をトレードできないため、アルトコインの自動アービトラージをするには海外の取引所を使う必要があります。
以前PoloniexとBinanceのビットコインアービトラージをやったので、それをそのまま流用します。(取引ペアの箇所を変えるだけです。)
ポロニエックスとバイナンスの共通の通貨ペア
PoloniexはBTC,ETH,STR,XRP,LTC,ETC,BCH,DASH,XMR,ZEC,REP,NXTがUSDT建てで取引できます。
BinanceはBTC,BCC,LTC,NEO,QTUM,BNB,ETHがUSDT建てで取引できます。
ビットコイン建てにすれば共通する仮想通貨ペアは増えるのですが、ビットコインそのもののレート差もあるので、「ロジック的にどうなの?」と思い、今回は保留にしました。
共通しているBTC,BCC(BCH),LTC,ETHの4つのアルトコインをアービトラージに組み込みます。
ビットコインキャッシュだけそれぞれで表記が違うので、そこだけ追加のコーディングをしてください。
1 2 3 4 5 6 7 8 9 10 |
if( $symbol == "BCH" ) { $orderpair_pl = "USDT_".$symbol; $orderpair_bn = $symbol."USDT"; } else { $orderpair_pl = "USDT_".$symbol; $orderpair_bn = $symbol."USDT"; } |
通貨ペアのパラメータの名称もポロニエックスはUSDT_BTCなのに対し、バイナンスではBTCUSDTとなるので、分けて変数に入れました。
ところで通貨の順序が変わると意味合いが変わるはずなのですが、どちらも同じものを指示しているのは謎です。FXの場合、USDJPYとJPYUSDだとチャートが真逆になり、レートも全然違うものになるので、今回はPoloniexが悪いという判決になりそうです。
しかも、
https://poloniex.com/public?command=returnTicker¤cyPair=USDT_BTC
にアクセスしたときに、最後のパラメータの通貨ペアを間違うとエラーメッセージになるくせに、通貨ペアを入れても入れなくても全通貨ペアのティッカーを吐きだす仕様はどうにかならないものでしょうか。一つの通貨ペアだけ取れればそれだけスピードが上がるはずです。
そう考えるとBinanceのAPIは良くできている(当たり前)ような気がします。
あとは、
jsonの吐きだしもBTCと同じなので、本当に楽ちんです。
後ろの背景はお遊びで入れた動くjavascriptアニメーションです。
また、取引結果がたまったら紹介します。(ETHに関しては意外とBTCよりもレート差が少ない印象です。)
関連記事
-
ZAIFのAPIの使い方[PHP]
APIを使ってbitFlyerからレートを取得したり、残高を確認したり、オーダーを出す方法まとめです
-
コインチェックのAPIの使い方[PHP]
公開:2017/6/21 PHPプログラマと言っても、いろいろな分野の人がいるわけで、WEB系だか
-
bitFlyerのAPIの使い方と注意事項
APIを使ってbitFlyerからレートを取得したり、残高を確認したり、オーダーを出す方法まとめです
-
APIビットコインアービトラージシステム
ここまで業者間アービトラージシステムのプログラミングの考察?をしてきましたが、 中には「中身がどう
-
[API]ビットコインスキャルピングの自動売買システムの作り方(Poloniex)
「アービトラージあるからいいじゃん」ってずっと思ってたのですが、(今でも思ってる)スキャルピングに関