コミュニティ

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

概要

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

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

Go言語でのアプリ開発をしようと思った経緯

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

なんで仮想通貨の自動取引を選んだのか?

理由は以下です。

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

Go言語の文法を学習

文法や環境構築の方法に関しては
ドットインストールの動画
スターティングGo言語の書籍
を使って学習しました。

ドットインストールのGo言語入門の動画に合わせて、実際に手を動かして環境構築や文法を一通り勉強しました。

スターティング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

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

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

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

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

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

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

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

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

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

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

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

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

実行頻度は45秒に1回としました。

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

実行頻度は40秒に1回としました。

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

実行頻度は45秒に1回としました。

運用実績(2020年4月20日~5月17日)

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

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

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

スクリーンショット 2020-05-17 13.51.36.png

スクリーンショット 2020-05-17 13.51.44.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

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

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

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

工夫した点

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

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

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

注文情報等を取得する際はなるべく一括で取得(個別の注文IDに対してリクエストを一回一回投げない)

JOBの頻度を減らす

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

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

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

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

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

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

今後の課題

  • テクニカル分析を用いて売買のタイミング等を判別したい。
  • レバレッジ取引に挑戦(現状は現物取引のみ)
  • バックテストの機能を実装したい。→ 新しい売買ロジック作ったときにシュミレーションできるように。
  • 他の取引所(海外取引所を含む)を使って幅広くAPI取引。→ 最近ではOKEXという海外取引所のAPIを呼び出す機能も実装してみました。
  • 通貨価格が高騰・暴落した際に、買い注文・売り注文が約定せずにずっと残ってしまう。なんらかの対応を考えたい、、、、
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
ユーザーは見つかりませんでした