なんだか使いづらそうなpoloniexのAPIを呼び出してみた

こんにちは。くろしばです。

多くの仮想通貨取引所が取引のレートや、実際に注文をすることができる機能を持たせたAPIを公開しています。

その中でもpoloniexの取引所は単にURLを公開していると言うよりは、プログラムを公開して、これを使ってね。

みたいなスタンス。

そのpoloniexのAPIを呼び出すプログラムを実装してみました!

スポンサーリンク

まずはAPIキーを発行する

f:id:corbo93:20180106131018j:plain

他の取引所では必要なかったのですが、poloniexではpublic APIを呼び出すのにもAPIキーが必要みたいです。

ちなみにここでは

public API : ログインの必要がない情報を取得するAPI(各通貨の価格など)

認証API : 本来ログイン後にのみ使用することができる機能(通貨の売買など)

てな感じで表現していきます(-∀-)!

APIキーの発行はそんなに難しくありません。

poloniexにアクセスし、右上のスパナマークをクリックすると、

ペロンとメニューがでてきます。

f:id:corbo93:20180106131418p:plain

ここの「API KEYS」を選択します。

送信されたメールのURLにアクセスすればAPIキーの発行が完了します!

f:id:corbo93:20180106132557p:plain

ここで注意なのはとりあえずAPI叩いてみたいよってレベルの人は必ず

「Enable Withdraws」にチェックをしないでください!

シークレットキーを発行

f:id:corbo93:20180106133142j:plain

先ほどのキー発行画面のAPIキーの下にシークレットキーを表示するボタンがあるのでそこから確認することができます。

このキーの扱いには十分注意してくださいね!

普段お金を送金させたりするときは、

poloniexにログインして、

多くの人が2段階認証の認証を行い、

画面を操作し、確認を行いながら

送金を行うかと思いますが、APIというのはURLリクエスト1つでサクッと送金ができてしまう道具です。

え?ログインもなしに?

と思った方もいるかと思いますが、そのログイン作業の代わりがシークレットキーというわけです。

逆に言えばシークレットキーが人にバレてしまうと、ログインして好きな操作していいよ!

と言っているようなものなのです。

前述ではありますが、それを防止するためにも「Enable Withdraws」にチェックをつけなければAPIから引き出しは行えないはずです。

ラッパークラスを引っ張ってくる

f:id:corbo93:20180106134224j:plain

下記のページからそれぞれ

PHP wrapper

Python wrapper

をコピーしてきます。

Poloniex API Reference

今回はサクッと作りたかったのでいつも使っているPHPを使いました。

ディレクトリ構成はこんな感じ。

├── coin
│   ├── poloniex
│   │   ├── getPoloniexData.php
│   │   └── poloniex.php
│   ├── shell
│   │   └── getPoloniexData.sh
│   └── util
│       └── util.php
└── mail
└── mail.php

環境は

OS : macOS High Sierra

PHP : 7.1.7

とりあえず呼び出してみた

f:id:corbo93:20180106135107j:plain

各ファイルの中身はこんな感じです。

ラッパークラスは多少クラス名を変えたりしたので一応載っけときます。

・poloniex.php(ラッパークラス)

<?php
// FINAL TESTED CODE - Created by Compcentral
// NOTE: currency pairs are reverse of what most exchanges use...
//       For instance, instead of XPM_BTC, use BTC_XPM
class Poloniex {
protected $api_key;
protected $api_secret;
protected $trading_url = "https://poloniex.com/tradingApi";
protected $public_url = "https://poloniex.com/public";
public function __construct($api_key, $api_secret) {
$this->api_key = $api_key;
$this->api_secret = $api_secret;
}
private function query(array $req = array()) {
// API settings
$key = $this->api_key;
$secret = $this->api_secret;
// generate a nonce to avoid problems with 32bit systems
$mt = explode(' ', microtime());
$req['nonce'] = $mt[1].substr($mt[0], 2, 6);
// generate the POST data string
$post_data = http_build_query($req, '', '&');
$sign = hash_hmac('sha512', $post_data, $secret);
// generate the extra headers
$headers = array(
'Key: '.$key,
'Sign: '.$sign,
);
// curl handle (initialize if required)
static $ch = null;
if (is_null($ch)) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT,
'Mozilla/4.0 (compatible; Poloniex PHP bot; '.php_uname('a').'; PHP/'.phpversion().')'
);
}
curl_setopt($ch, CURLOPT_URL, $this->trading_url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
// run the query
$res = curl_exec($ch);
if ($res === false) throw new Exception('Curl error: '.curl_error($ch));
//echo $res;
$dec = json_decode($res, true);
if (!$dec){
//throw new Exception('Invalid data: '.$res);
return false;
}else{
return $dec;
}
}
protected function retrieveJSON($URL) {
$opts = array('http' =>
array(
'method'  => 'GET',
'timeout' => 10
)
);
$context = stream_context_create($opts);
$feed = file_get_contents($URL, false, $context);
$json = json_decode($feed, true);
return $json;
}
public function get_balances() {
return $this->query(
array(
'command' => 'returnBalances'
)
);
}
public function get_open_orders($pair) {
return $this->query(
array(
'command' => 'returnOpenOrders',
'currencyPair' => strtoupper($pair)
)
);
}
public function get_my_trade_history($pair) {
return $this->query(
array(
'command' => 'returnTradeHistory',
'currencyPair' => strtoupper($pair)
)
);
}
public function buy($pair, $rate, $amount) {
return $this->query(
array(
'command' => 'buy',
'currencyPair' => strtoupper($pair),
'rate' => $rate,
'amount' => $amount
)
);
}
public function sell($pair, $rate, $amount) {
return $this->query(
array(
'command' => 'sell',
'currencyPair' => strtoupper($pair),
'rate' => $rate,
'amount' => $amount
)
);
}
public function cancel_order($pair, $order_number) {
return $this->query(
array(
'command' => 'cancelOrder',
'currencyPair' => strtoupper($pair),
'orderNumber' => $order_number
)
);
}
public function withdraw($currency, $amount, $address) {
return $this->query(
array(
'command' => 'withdraw',
'currency' => strtoupper($currency),
'amount' => $amount,
'address' => $address
)
);
}
public function get_trade_history($pair) {
$trades = $this->retrieveJSON($this->public_url.'?command=returnTradeHistory&currencyPair='.strtoupper($pair));
return $trades;
}
public function get_order_book($pair) {
$orders = $this->retrieveJSON($this->public_url.'?command=returnOrderBook&currencyPair='.strtoupper($pair));
return $orders;
}
public function get_volume() {
$volume = $this->retrieveJSON($this->public_url.'?command=return24hVolume');
return $volume;
}
public function get_ticker($pair = "ALL") {
$pair = strtoupper($pair);
$prices = $this->retrieveJSON($this->public_url.'?command=returnTicker');
if($pair == "ALL"){
return $prices;
}else{
$pair = strtoupper($pair);
if(isset($prices[$pair])){
return $prices[$pair];
}else{
return array();
}
}
}
public function get_trading_pairs() {
$tickers = $this->retrieveJSON($this->public_url.'?command=returnTicker');
return array_keys($tickers);
}
public function get_total_btc_balance() {
$balances = $this->get_balances();
$prices = $this->get_ticker();
$tot_btc = 0;
foreach($balances as $coin => $amount){
$pair = "BTC_".strtoupper($coin);
// convert coin balances to btc value
if($amount > 0){
if($coin != "BTC"){
$tot_btc += $amount * $prices[$pair];
}else{
$tot_btc += $amount;
}
}
// process open orders as well
if($coin != "BTC"){
$open_orders = $this->get_open_orders($pair);
foreach($open_orders as $order){
if($order['type'] == 'buy'){
$tot_btc += $order['total'];
}elseif($order['type'] == 'sell'){
$tot_btc += $order['amount'] * $prices[$pair];
}
}
}
}
return $tot_btc;
}
}
?>

・getPoloniexData.php

<?php
require_once realpath(dirname(__FILE__)) . '/poloniex.php';
require_once realpath(dirname(__FILE__)) . '/../util/util.php';
require_once realpath(dirname(__FILE__)) . '/../../mail/mail.php';
class GetPoloniexData extends Poloniex {
private $mailFlag;
/**
 * コンストラクタ
*/
public function __construct($argv) {
$keys = Util::pickUpKeys($argv);
$this->mailFlag = Util::isSendMail($argv);
parent::__construct($keys['api'], $keys['sec']);
}
/**
 * メインロジック
*/
public function execute(){
try{
// ユーザ情報を含むAPIの利用
if(is_null($this->api_secret) === false){
// 使いたくなったら使う
}
// publicAPIの利用
if(is_null($this->api_key) === false){
$ticker = $this->get_ticker();
}
if(is_null($ticker)){
throw new Exception('APIからのデータが取得なーい!!');
}
if($this->mailFlag){
$this->_sendMail($ticker);
}
}catch(Exception $e){
echo "<エラーが発生しました!!!>nn";
echo $e->getMessage() . "nn";
exit(0);
}
}
/**
 * メインロジック
*/
private function _sendMail($retJson){
$sendInfo = array();
$sendInfo['to']      = ''; // 実行前にメールアドレスを入力する。
$sendInfo['subject'] = 'poloniex info';
$sendInfo['message'] = $this->_getMailText($retJson);
$mail = new Mail();
$mail->sendMail($sendInfo);
}
private function _getMailtext($retJson){
$t = '';
foreach($retJson as $key => $coinData){
$t .= "・" . $key . "n";
$t .= "  quotVolume : " . $coinData['quoteVolume']. "n";
$t .= "  vaseVolume : " . $coinData['baseVolume']. "n";
$t .= "n";
}
return $t;
}
}
$poloniexApi = new getPoloniexData($argv);
$ret = $poloniexApi->execute();
exit(1);

・util.php

<?php
class Util{
/**
 * APIkeyとsecret keyをオプションから取り出す処理
 * 今の所、poloniexのリクエスト用に使用
 * @return $key
*/
public static function pickUpKeys($argv){
$key = array();
foreach($argv as $option){
$tmp = explode('=', $option);
$key[$tmp[0]] = $tmp[1];
}
return $key;
}
public static function isSendMail($argv){
return in_array('--sendMail', $argv) ? true : false;
}
}

・mail.php

<?php
class Mail{
public function sendMail($sendInfo){
$to         = isset($sendInfo['to'])        ? $sendInfo['to']         : null;
$subject    = isset($sendInfo['subject'])   ? $sendInfo['subject']    : null;
$message    = isset($sendInfo['message'])   ? $sendInfo['message']    : 'テスト送信です。';
$addHeaders = isset($sendInfo['addHeader']) ? $sendInfo['addHeader']  : null;
$addParam   = isset($sendInfo['addParam'])  ? $sendInfo['addParam']   : null;
echo $message;
// メール送信が可能かチェックする
$this->_canSendMail($to, $subject);
if(mb_send_mail($to, $subject, $message, $addHeaders, $addParam) == false){
throw new Exception('メール送信失敗したー!!');
}
}
private function _canSendMail($to, $subject){
if($to == null){
throw new Exception('宛先がないよ');
}
if($subject == null){
throw new Exception('題名がないよ');
}
if(strpos(gethostname(), 'local')){
throw new Exception('ローカルは送らないでおくね。');
}
}
}

・getPoloniexData.sh

#!/bin/sh
echo "<--call poloniex API-->n"
/usr/bin/php ../poloniex/getPoloniexData.php  "$@"
echo "<--end poloniex API-->n"

.shファイルを実行することでphpでレートを取得する流れになっているので、

やることはgetPoloniexData.shを実行するだけです。

sh getPoloniexData.sh api=ここにAPIキーを入力

オプションで取得結果をメール送信させる処理も入れましたが、

ローカル環境では多分動かないので今回は使わないでいきます。

細かいところがまだ未完成ではありますが、とりあえずmacのどこかにおけば動くとは思います。

まとめ

正直Javaをしっかりとやったことがないのでラッパークラスといってもいまいちピンときませんでした。(´・д・`)

アクセス修飾子を見る限りpublicがあったりprotectedがあったり、

ライブラリ的に使えばいいのか、はたまた継承すればいいのか。。。

とりあえず後者で実装してみました。

このバッチ自体は雑に作っちゃいましたが、他の取引所のデータも取得したりして少しまともなwebページを公開できたらなと思っていますのでお待ちくださいませー!

ではでは( *`ω´)ノ

スポンサーリンク