XEMの自動積立システムを自分でつくろう！(Zaif自動積立サービス停止で困っているNEMberの方々へ！？)

(この記事はNEM #2 Advent Calendar 2018 10日目に登録しています。)
#自己紹介
はじめまして。サラリーマン投資家と申します。普段はブログを書いているくらいなのですが、javascript、Node.js、monacaを利用したハイブリッドアプリの作成等について最近勉強しています。
これがQiita初投稿となります。よろしくお願い致します。
#記事投稿のきっかけ
自身の投資活動の中で、仮想通貨にも、限定的な割合ながら投資しています。
NEMについては、活発なコミュニティで、クオリティの高い様々なプロダクトが開発されていることや、APIの使いやすさ等、常々大きな可能性を感じており、Zaif取引所で自動積立サービスを利用して毎日淡々と自動的に枚数を増やしていました。

しかし、Zaifのハッキング事件を機に、自動積立サービスが停止してしまい、今後も淡々と買い続けるには手動で注文する必要が出てきました。「毎日注文し続けるのは正直めんどくさすぎる！」→「でも、値動きが激しすぎて、買付頻度を減らすとものすごい高値掴みになってしまいそうでコワイ…」→**「それならば自分で自動積立の仕組みを作ってしまおう！」**ということで作ったものが一応動かせるようになったので公開させて頂きたいと思います。

不具合、バグ、改善提案などあれば、ぜひ色々ご指摘頂けますと幸いです。よろしくお願いします。

なお、本記事のプログラムは取引所の本番環境に実際に注文を出すため、見方によってはリスクの高いプログラムです。本記事の内容によっていかなる損害が発生したとしても、当方としては責任を取ることはできません。トライする際は発注箇所を最初はコメントアウトするなどして慎重なトライをお勧めします。
#概要
作りたい仕組みの概要は以下の通りです。

1. 取引所への入金は手動
2. 毎日のXEM買付けは、「Node.js」とnpmパッケージ「ccxt」とZaif取引所APIを利用して、自動的に個人保有のPCから現物の買い注文を出す

本来はAPIで出した注文が約定したかどうか確認し、長期間約定しない場合は注文を出しなおすなどの対応が必要ですが、今回は簡略化のため、その機能は実装しないことにします。
#必要なもの
1. PC(WindowsでもMacでもLinuxでもNode.jsが動くならOK？)
2. Zaif取引所アカウント
    3. API key
    4. API secret

Zaif取引所のアカウントとAPI key、API secretは事前に準備しておきましょう。info, trade, idの権限が必要だと思います。いうまでもないかもしれませんが、これらの情報は第三者に決して教えてはいけません。
#手順
##Node.jsのインストール
[Node.js公式](https://nodejs.org/ja/)からダウンロードして普通にインストールします。今回作成する仕組みではバージョン依存性はそんなに無いと思われるので、LTS推奨版くらいで良いと思います。(8以上くらいの縛りがccxtであったかもしれません。)
##作業フォルダの作成
どこでも良いのですが、abc2pというフォルダを作成します。Automatic Buying Crypto Currency Programの略を意識しています。以下では「C:\abc2p」とフォルダを作成したものとして説明しますが、適宜、必要に応じて読み替えてください。
##作業フォルダでnpm init
コマンドプロンプトなどで作業フォルダ「C:\abc2p」に移動し、

npm init

を実行します。
色々聞かれますが、全部そのままエンターで良いです。
##ccxtのインストール
引き続き、作業フォルダ「C:\abc2p」でコマンドプロンプトで

npm install ccxt --save

コマンドを実行します。少し時間がかかった後に何か出てくると思います。警告らしきものが出てきますが、ここではあまり気にする必要はないでしょう。コマンドプロンプトの作業はいったんここまでですが、また後でコマンドプロンプトをこの状態で使用するので、閉じずにそのままにしておきましょう。
##jsファイルの作成
作業フォルダ「C:\abc2p」に「abc2p.js」ファイルを作成し、以下のソースコードをコピペし、apiKey、secretのところのシングルクオーテーション''の間に自身のアカウントのAPI情報を書き込み、積立金額や積立対象の通貨についても、必要に応じて書き換えて、保存してください。デフォルトでは、月1万円、全額XEMの積立という設定となっています。

```js:abc2p.js
'use strict';

//定義
//npmパッケージのccxtを読み込み
const ccxt = require ('ccxt');

//ここの''内のapikey、secretは自分で設定したものに変更する必要あり。この情報は決して第三者に渡してはならない。
const zaif = new ccxt.zaif ({
        apiKey: '',
        secret: '',
    })

//ここの金額を変えれば積立金額を変えられる。
const money_per_year = 120000 //年間積立額をここに入力→例として1月1万円の積立なら120000
const money_per_day = money_per_year / 365 //年間365日毎日積立

//ここの割合を変えれば、各通貨の積立割合を変えられる。ここではXEMのみの積立とする。
const btc_ratio = 0 / 4 * 100 //%
const xem_ratio = 100 //%
const eth_ratio = 0 / 4 * 100 //%
const mona_ratio = 0 / 4 * 100 //%

//各通貨の1日当りの購入金額の計算と端数の処理
const money_per_day_btc = money_per_day * btc_ratio / 100
const money_per_day_eth = money_per_day * eth_ratio / 100
const money_per_day_xem = money_per_day * xem_ratio / 100
const money_per_day_mona = money_per_day * mona_ratio / 100

//積立間隔をミリ秒で設定する　例えば1日なら 24 * 60 * 60 * 1000
const interval = 24 * 60 * 60 * 1000

//次回積立タイミングまで待機する関数の定義
const sleep = (timer) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
        }, timer)
    })
}

//ここからプログラム本体(同期、非同期を考慮した記述)
(async function () {
    //常に繰り返し→明示的にキルしない限り実行され続ける
    while (true) {
        //板情報取得
        const ticker_btc = await zaif.fetchTicker ('BTC/JPY')
        const ticker_eth = await zaif.fetchTicker ('ETH/JPY')
        const ticker_xem = await zaif.fetchTicker ('XEM/JPY')
        const ticker_mona = await zaif.fetchTicker ('MONA/JPY')
        
        //makerのほうが手数料有利のため、ベストaskより最小刻み幅分安い価格を指定
        //価格刻み最小幅はZaifではBTCは5JPY、XEMは0.0001JPY、ETHは5JPY、MONAは0.1JPY
        //計算の誤差が発生し、発注価格が最小刻み幅以下の桁を含む価格となるバグを避けるため、四捨五入の処理を追加(2018/12/15追記)
        const ask_btc_maker = Math.round(ticker_btc.ask - 5)
        const ask_eth_maker = Math.round(ticker_eth.ask - 5)
        const ask_xem_maker = Math.round((ticker_xem.ask - 1 / 10000) * 10000) / 10000
        const ask_mona_maker = Math.round((ticker_mona.ask - 1 / 10) * 10) / 10

        //積立金額にあわせて各通貨購入数量の計算及び端数の処理
        const amount_btc = Math.round(money_per_day_btc / ask_btc_maker * 10000) / 10000
        const amount_eth = Math.round(money_per_day_eth / ask_eth_maker * 10000) / 10000
        const amount_xem = Math.round(money_per_day_xem / ask_xem_maker * 10) / 10
        const amount_mona = Math.round(money_per_day_mona / ask_mona_maker * 10) / 10
        
        //年月日＆日時の取得
        const today = new Date()
        
        //確認のための出力
        console.log(today)
        console.log(ticker_btc.ask, ticker_eth.ask, ticker_xem.ask, ticker_mona.ask)
        console.log(ask_btc_maker, ask_eth_maker, ask_xem_maker, ask_mona_maker)
        console.log(money_per_day_btc, money_per_day_eth, money_per_day_xem, money_per_day_mona)
        console.log(amount_btc, amount_eth, amount_xem, amount_mona)
        
        //指値注文実行と確認のための出力(XEM以外をコメントアウトし、XEMのみ買付けされる設定)
        //console.log (zaif.id, await zaif.createLimitBuyOrder ('BTC/JPY', amount_btc, ask_btc_maker))
        //console.log (zaif.id, await zaif.createLimitBuyOrder ('ETH/JPY', amount_eth, ask_eth_maker))
        console.log (zaif.id, await zaif.createLimitBuyOrder ('XEM/JPY', amount_xem, ask_xem_maker))
        //console.log (zaif.id, await zaif.createLimitBuyOrder ('MONA/JPY', amount_mona, ask_mona_maker))
        
        //次回積立タイミングまで待機
        await sleep(interval)
    }
}) ();
```
##Node.jsでabc2p.jsを実行
作業フォルダ「C:\abc2p」にて、コマンドプロンプトで

node abc2p.js

コマンドを実行して以下のような結果が返ってくれば成功です。「Ctrl」+「C」等で強制的に終了するまで、24時間毎に現在価格を調べて積立分の金額だけ自動的に買付注文を出し続けてくれるでしょう。

```
2018-12-09T06:28:01.940Z
382245 10115 8.0689 null
382240 10110 8.0688 -0.1
0 0 328.7671232876712 0
0 0 40.7 -0
zaif { info:
   { success: 1,
     return:
      { received: 40.6593, remains: 0, order_id: 0, funds: [Object] } },
  id: '0' }
```
#結果
##メリット
###とにかく楽
これはいうまでもありませんね。ただし、まだ、プログラム稼動させ始めたばかりで、エラー頻発なら、逆にめんどくさいことになってしまうかもしれません。しばらく様子を見てみようと思います。
###Zaif自動積立より手数料を安くできる
これはいざやってみると意外な効能でした。入金を手動にすることで入金時の手数料がかからなくなったことが大きいです。取引毎の手数料についても、ベストASKそのもので買い注文を入れるとtaker手数料が発生してしまうのですが、「ベストASK - 価格最小刻み幅」の価格で発注を入れることで、maker手数料扱いとなり、現在maker手数料は無料化されていることから、完全な無料手数料での自動積立が可能となったのです。これは非常にうれしいことです。(もっと早くからやっておけばよかった(笑))
##課題
###月間、年間等の積立金額に端数が出てしまう
取引所での注文では、仮想通貨の最小発注数量や刻み幅が制限されており、Zaifの自動積立と比べて積立金額の合計でそれなりの端数が出てしまうと思います。少し大目に入金しておく等の対応が必要になりそうです。
###エラー処理がない
エラー処理がないため、エラー時にはプログラムがクラッシュして強制終了してしまうと思います。同期、非同期のプログラムの書き方に対してもう少し理解が深まったらチャレンジしてみたいですね。
###APIで出したオーダーの約定状況の監視が必要
本来であれば、APIで出したオーダーの約定状況を監視し、長期間約定しない場合は、オーダーを出しなおす手順が必要でしょう。こちらも同期、非同期のプログラムの書き方に対してもう少し理解が深まったらチャレンジしてみたいですね。
###
設定情報がプログラム本体に書かれており、今後、gitで管理したり、npmパッケージにしたりする際に問題がありそうです。config.jsonのような設定ファイルにAPIの情報や積立金額や各通貨の積立比率等を分けて記述するように改善しようと思います。
##その他所感
###ccxtは偉大なり
今回ccxtを利用してみて、取引所APIを活用したプログラミングの容易さに驚きました。ccxtを利用せずに書くと、もっともっとものすごく大変なはずです。他の取引所にも幅広く対応しているようなので、色々なところで活用できると思います。皆様も色々触ってみて、良いサービスができたらぜひ紹介しあえるとよいですね。私も、取引所間の価格差を利用した自動裁定取引ツールなど作ってみたいと思います。
ccxtのgithubは[こちら](https://github.com/ccxt/ccxt)をどうぞ。node.jsだけでなくpython、phpでも使えるようです。すばらしいですね。
###金融サービスの未来を少し垣間見た気分
大げさかもしれませんが、フィンテックが作り出す未来をこの手に感じたような、そんな気持ちになりました。仮想通貨の技術は適切に発展していくと、証券取引所APIの開放や、証券のトークン化や、DAppsを介した証券取引の実現といったそんな未来につながっていくと思います。そういったわくわくする未来に期待しつつ、これからも色々な活動を進めていきたいと思います。

XEM Address: NDU7JGAEOIEFOYZYOIL4ZZDMRAL222XR6C5Q55QU