Go言語を1から勉強してアプリを作り、仮想通貨の自動取引で1ヶ月1万円の利益が出た話

# 概要
元々Javaエンジニアの私がGo言語を１から学習して、仮想通貨の自動売買を行うWebアプリケーションを開発しました。
開発したアプリはクラウド上にデプロイし、１ヶ月弱運用を行った結果、１万円以上の利益が出ました。
その過程を綴った記録になります。

実際に作成したアプリのソースコードは以下
↓↓↓
https://github.com/Kohei-Sato-1221/crypto-trading-golang


# Go言語でのアプリ開発をしようと思った経緯
私はJavaがメインスキルだったのですが、「最近Go言語が流行っている！」という話を聞いて、
１から勉強しようと思いました。単に文法を学習するだけだとモチベーションがわかないので、
**「GoでWebアプリを作って、Githubに公開する」**
を目標に活動をはじめたのです。

# なんで仮想通貨の自動取引を選んだのか？
理由は以下です。

1. 元々仮想通貨やブロックチェーンに興味持っていて、いくつかの取引所にアカウントを保有していた。
2. 各仮想通貨取引所は売買のためのAPIを豊富に用意していることが多い。
3. 株などの金融商品に比べてわりと少額から取引できる。bitflyer使えば0.001BTC(執筆時点で約１０００円)から注文可能。
4. ボラティリティ（一日の価格変動）が高い。
5. 24時間365日取引ができる。

# Go言語の文法を学習
文法や環境構築の方法に関しては
 ドットインストールの動画
 [スターティングGo言語](https://www.shoeisha.co.jp/book/detail/9784798142418)の書籍
を使って学習しました。

ドットインストールの[Go言語入門](https://dotinstall.com/lessons/basic_golang)の動画に合わせて、実際に手を動かして環境構築や文法を一通り勉強しました。  
スターティングGo言語の方がどちらかと言えばリファレンスみたいな使い方をしました。
ちょっと動画ではわかりずらかったり、復習したりしたい場合などは書籍が便利でした。

エディタは無料のVSCodeを使用しました。
デバッグもやりたかったので、GoのプラグインをVScodeに入れました。

# Go言語を学習した感想 ~良いところ~
- シンプルな記述でやりたいことが簡単にできる。数行のコードでWebサーバー作れたりして、すごいなーって思いました。
- 記述のお作法がGoogle側で指定されていて書き方にあまり困らない。
- 並列処理が書きやすい（goroutineがめっちゃ便利）


# Go言語を学習した感想 ~苦戦したところ~
- 変数を宣言しても使わないとコンパイルエラーになる。動作確認時とかに不便だった。
- channel・ポインタの使い方を理解するのに苦労した。
- 結局GOPATHをどのように設定したら良いかわからなかった(上手くビルドができなくて ```export GOPATH=xxxxxx```　を何回も実行した....:joy:)
- ネットで検索するとき関係ない情報がヒットしがち(ポケモンGOとか笑)
- Javaと違ってオブジェクト指向じゃないので、ファイルの構成とかどーしたら良いのか悩むことがしばしば...


# 自動取引アプリの開発に着手
Goの基本をおさえたところで自動売買アプリの開発に着手しました。
この時点で作ろうと思ってたアプリはざっくり以下のような感じ。

- スケジューラーで定期的に買い注文を入れる。
- 買い注文が約定したかを定期的にチェックし、約定したら約定価格よりも少し高い価格で売り注文を入れる。
- 上記のデータをDBで管理し、どれくらいの利益が出たかを日次で把握できるようにする。
- AWS(EC2) or ラズベリーパイにアプリをデプロイして、運用をしてみる。

基本的にはこのとき思い描いていた通りのアプリが出来上がりました。

# 取引所の選定
私は複数の仮想通貨取引所のアカウントを持っていたのですが、
このアプリ開発で私はbitflyerを選択しました。

私はZaif、bitbankのAPIを使ってJavaアプリの開発をしたことがあり（※）、APIを使ったことがない取引所を使ってみよう！、という浅い理由でbitflyerを選択しました。


※ Zaif、bitbankのAPIを使って、日次で仮想通貨の積立売買を行うSpringBootのアプリケーションを既に開発済み(https://github.com/Kohei-Sato-1221/CryptoTrading_SpringBoot)

# Go言語での開発をスタート
ここからはネットの情報や書籍を利用して、ひたすら開発を進めました。
そこまで苦労した部分はなかったのですが、基本の文法学習では対応できず、ネットで調べて試行錯誤した点は以下です。

(1) configファイルの読み込み
APIキーやDBの接続情報は外部ファイルに記載するようにしたいと思いました。
```gopkg.in/ini.v1```というライブラリが有効ということで、これを使いました。


(2) Mysqlとの接続
```github.com/go-sql-driver/mysql```というドライバーが有名らしいので採用しました。
JavaではもちろんDBの連携をやったことがあるのですが、ちょっと勝手が違う部分もあり、試行錯誤に苦労しました。 

(3) 取引所APIのリクエスト
取引APIなどのAPIキーが必要なPrivateAPIを使う場合は、HTTPリクエストヘッダに認証情報を入れないといけません。
bitflyerの場合だと

```
"ACCESS-KEY":APIキー,
"ACCESS-TIMESTAMP": タイムスタンプ,
"ACCESS-SIGN": ※bodyやタイムスタンプを含めた値をHash化して、キーでSignした値
"Content-Type":"application/json"
```
が必要になります。
ACCESS-SIGNの値が少しでも間違うと認証されないので、試行錯誤して正しいリクエストを出せるようにしました。

HTTPリクエスト作成、HASH化、SIGNなどは基本的にGoのライブラリでカバーできるので、Go特有の部分での苦労はありませんでした。

# 自動取引アプリの構成図
最終的には以下のようなアーキテクチャのアプリが完成しました。
![スクリーンショット 2020-05-17 11.12.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454171/0187678f-325d-a622-aea0-4afb788e37b1.png)


**【TABLE】**
**(1)買い注文テーブル**
買い注文の取引ID、金額、数量、ステータスを管理するテーブルです。

ステータスに関しては、「未約定」、「約定済み」、「売り注文発注済み」の３つのステータスがあります。
注文情報同期JOBにて買い注文の情報をInsertします。この段階ではステータスは「未約定」、
約定確認JOBで買い注文の約定が確認されれば、ステータスは「約定済み」に変更、
売り注文JOBで特定の買い注文に対応する売り注文が発注された場合、ステータスを「売り注文発注済み」に変更します。


**(2)売り注文テーブル**
売り注文の取引ID、金額、数量、ステータス、親取引IDを管理するテーブルです。

ステータスに関しては「未約定」「約定済み」の２つで、約定確認JOBでステータス変更します。

親取引IDは該当の売り注文に対応する買い注文の取引ID、になります。
買い注文の金額に＋αした金額の売り注文を出す、という仕組みなので、このようなカラムを追加しています。

**【JOB】**
**(1)買い注文JOB**
一日数回、特定の時間に買い注文を発注するJOBです。成行ではなく指値での注文です。

発注する価格に関しては、
 1. 現在価格のxx%低い価格
 2. 現在価格と、２４時間内の最低価格の平均値
などシンプルなロジックで実装していました。

発注する数量、金額、頻度は運用しながら調整しました。
最終的には以下の通りになりました。

- 大体2時間おきに買い注文を入れる
- 注文する通貨は、BTCとETHの現物（レバレッジ取引はやらない）
- BTCの１回あたり発注数量は0.006BTC or 0.009BTC
- ETHの１回あたり発注数量は0.2ETH or 0.3ETH


**(2)売り注文JOB** 
買い注文テーブルから「約定済み」のステータスの買い注文の情報一覧を取得します。
その後、bitflyerのAPIを叩いて売り注文を入れます。

売り注文を入れた後、買い注文テーブルのステータスupdate及び、売り注文テーブルに注文情報のinsertを行います。

売り注文の数量は、元々の買い注文の数量と同じで、価格に関しては買い注文価格の+1.5%としました。
この1.5%という数字に明確な根拠はないのですが、日々BTCやETHのチャートを見ていると、数％の上昇・下落が頻繁に見られるので、1.5%くらいならすぐ約定するかな、という想定で決めました。実際に運用してみると想定通りには売り注文が約定しているように思います。

実行頻度は４5秒に１回としました。

**(3)注文情報同期JOB**
買い注文の情報をbitflyerから取得し、買い注文テーブルへデータを同期するJOBです。
この時、買い注文JOBで入れた注文かどうかは区別しません。つまり、手動で入れた買い注文も同期の対象としました。（この仕様にした理由は後述します）

実行頻度は４0秒に１回としました。

**(4)約定確認JOB**
買い注文・売り注文が約定しているかどうかをbitflyerから取得し、買い注文テーブル・売り注文テーブルのステータスを変更します。

実行頻度は４5秒に１回としました。

# 運用実績(2020年4月20日~5月17日)
2020年4月20日から、1ヶ月弱、運用を行ってみました（実際はもっと前から運用していたのですが、利益管理機能を実装していなかったので、2020年4月20日からのデータしか存在しないのです:sweat_smile:）

運用実績は以下です（１日あたりの利益は、売り注文が約定した日付でカウントしています）

**合計利益：1万6,765円　一日平均：605円**

![スクリーンショット 2020-05-17 13.51.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454171/d0001f9b-540b-a6ee-88af-ef20f1b76bb9.png)

![スクリーンショット 2020-05-17 13.51.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454171/15a3c0e1-f1c7-bfc1-ed41-44c3be843219.png)

|日付	|1日あたり利益(円)	|累計(円)|
|---|---|---|
|04/20	|185.92	|185.92|
|04/21	|140.72	|326.64|
|04/22	|410.94	|737.58|
|04/23	|855.04	|1,592.62|
|04/24	|67.15	|1,659.77|
|04/25	|130.97	|1,790.74|
|04/26	|288.46	|2,079.2|
|04/27	|98.28	|2,177.48|
|04/28	|161.21	|2,338.69|
|04/29	|703.34	|3,042.03|
|04/30	|2,223.58|5,265.61|
|05/01	|330.82	|5,596.43|
|05/02	|225.37	|5,821.8|
|05/03	|244.29	|6,066.09|
|05/04	|757.07	|6,823.16|
|05/05	|348.14	|7,171.3|
|05/06	|277.91	|7,449.21|
|05/07	|1,037.3|8,486.51|
|05/08	|685.99	|9,172.5|
|05/09	|63.02	|9,235.52|
|05/10	|1,953.49|11,189.01|
|05/11	|1,381.77|12,570.78|
|05/12	|731.35	|13,302.13|
|05/13	|379.61	|13,681.74|
|05/14	|1,374.74|15,056.48|
|05/15	|618.21	|15,674.69|
|05/16	|1,090.55|16,765.24|
|1日平均	|605.18	|ー|

結果的に２８日間で１万５０００円以上の利益が出ました。１日あたりの利益も６００円超とワンコインを超えました。
毎日軽いランチ一食分は浮いてたなー、という気分です。

しかし、本当はもっと利益が出る可能性がありました。というのもの、途中で買い注文の発注数量を増やしたためです。最終的には「自動取引アプリの構成図」で説明した通りの数量になっているのですが、4月２０日の時点での購入数量はもっと少なかったのです。

一ヶ月弱運用してみて、結構安定的に利益が出ていることがわかったので、今後はさらに発注数量や買い注文を入れる頻度を高めたいと思います。（単純に購入する数量を２倍にしていれば、利益も２倍になったはずですので、、、、）


# 工夫した点
**(1) Goのプロセスが落ちる**
原因が不明なのですが、Goのプロセスが落ちていることがたまーにありました。
Goに対する経験不足で、原因がわからないという状況でした。
苦肉の策として、
「プロセスが動いているか監視して、落ちていたらアプリを再起動する」
というシェルスクリプトを作り、cronで１時間に１回実行するようにしました。

しかし、これは根本的な原因解決ではないので、もう少しGOに関する知見を深め、停止しないようなアプリに改修できればと思っています。

**(2) APIの呼び出し制限**
[bitflyerのAPI](https://lightning.bitflyer.com/docs?lang=ja)では、Private APIの呼び出し回数が５分間で500回までと制限があるようです。
実装しはじめの頃はこの点を完全に無視して、APIリクエストを投げまくっていたため、注文情報の確認JOBなどで急にエラーが発生する事態が起きました。

注文情報等を取得する際はなるべく一括で取得（個別の注文IDに対してリクエストを一回一回投げない）
や
JOBの頻度を減らす

といった手段で解決を図りました。

**(3) マニュアルで買い注文いれた際の挙動**
基本は

JOBによる買い注文　→　買い注文の約定チェック　→　約定したら売り注文

をひたすら繰り返すアプリなのですが、**買い注文を手動で入れた場合も、約定チェックの対象としたい**
と考えました。

つまり、bitflyerの注文画面から買い注文を入れた場合でも、その情報をDBに格納し、その注文を約定確認JOBの対象として、約定後にJOBで売り注文を発注するようにしたい、ということです。

アプリの初回の実装時は、買い注文JOBにてDBに買い注文のデータをinsertするようにしていたのですが、その方法だとJOBで入れた買い注文のみがDBに登録されます。この仕組みを変更し、定期的に注文情報をDBに同期する「注文情報同期JOB」を新規実装することで対応しました。

# 今後の課題
- テクニカル分析を用いて売買のタイミング等を判別したい。
- レバレッジ取引に挑戦（現状は現物取引のみ）
- バックテストの機能を実装したい。→ 新しい売買ロジック作ったときにシュミレーションできるように。
- 他の取引所（海外取引所を含む）を使って幅広くAPI取引。→　最近では[OKEX](https://www.okex.com/)という海外取引所のAPIを呼び出す機能も実装してみました。
- 通貨価格が高騰・暴落した際に、買い注文・売り注文が約定せずにずっと残ってしまう。なんらかの対応を考えたい、、、、

# UdemyでAWS×Golang×ビットコインの講義を作りました（2021年4月追記）
Golangを実務でも使うようになり、Golangでの開発経験も長くなってきました。そこで、UdemyでAWS上にビットコインの取引アプリを開発する講義で公開しました！

https://www.udemy.com/course/bitcoin-lambda-golang/?referralCode=674B788BFA80A564B9EA

こちらの講義ではEC2ではなく、AWS Lambdaを使ったサーバーレスのアプリケーションです。
プロモーションコード欲しい方は、気軽にご連絡ください！

関連ページ：
[AWS LambdaとGolangを用いてサーバーレスなビットコイン積立アプリケーションをクラウド上に構築した話](https://qiita.com/Kohei-Sato-1221/items/272fc610510db92a9e6a)