スクリーンショット_2018-03-17_18

暗号通貨自動トレードシステム開発キャンプ#3:pythonで実際にbotを作ってみよう

#bot開発キャンプ第3夜
#そろそろbotを作り始めます
//本キャンプの方針
//AKAGAMI卍さんのnoteはおすすめです。
//新SFDの導入が開始されました。
//早速SFD botを開発しましょう。
//初心者だけどこれを回せば儲かりますか?

こんばんは、しゃしゃしゃしゃです。
bot開発キャンプ第3夜を進めて行こうと思います。今回のnoteでは実際にトレードするbotを開発して行きます。今回使用する取引所はbitflyerです。
皆さんすでに取引所の登録は完了していると思いますが、まだの方は登録しておいてください。
<bitflyer>
https://bitflyer.jp?bf=yaa5px3n
今後BitMEXでのbot開発もしていくのでこちらも登録よろしくお願いします。
<BitMEX:以下リンクから登録すると手数料が10%割引になります。>
https://www.bitmex.com/register/smlBwx

・本キャンプの方針
 bot開発に必要な基本的な考え方や、これは知っておいた方が為になるなと個人的に思った考え方について書いて行きます。ソースコードも簡単な解説つきで適宜公開します。ただ、環境の導入部分など調べればすぐわかる情報については、参考になるサイトのリンクを貼るのでそちらを参考にしてください。初心者の方でもわかるように書いていくつもりですが、環境の導入やプログラムの記法、開発の際のお作法などnoteで完結させるのはあまりにも無謀なので・・・
私が時間を書けて説明するよりも丁寧に書いてあるWebサイト、書籍などがたくさんあるのでそちらへのリンクをまとめていって全部参照することで初心者の方でもbotが作れるようになる手伝いができればと思います。
皆さんの最終的な目標はプログラミングが得意になることではなく、トレードをしてくれるbot(できれば自動で稼いでくれるbot)を作ることだと思います。その為にはただ漫然と有料noteを買ってコピペしたり、情報商材をそのまま使ったりするだけでは到底ダメなんです。自分で考えて、仮説を立てて、実験して検証して、を繰り返すのは当然必須の能力です。

・AKAGAMI卍さんのnoteはおすすめです。
 bot開発に必要な基本的な考え方や、これは知っておいた方が為になるなと個人的に思った考え方についてはnoteで取り扱う!って述べておいてしょっぱなから他の方のnoteを紹介する愚行を許して欲しいですが結構為になるなと思ったので紹介します。
皆さん、AKAGAMI卍さんのnoteはすでに購入されました?
https://note.mu/akagami/n/ncdeb281343f3
まだ購入されていない方は購入しておくといいと思います。と言うのも環境の導入部分から丁寧に書いてあるので最初の段階でつまづかなくて済むと思うからです。と言うのも、初心者の方がプログラミングするときに悩むのって以外に環境導入より前の部分なんですよね。
「そもそも何の言語勉強すればいいのか」、「どうやって書けばいいのか」、「どのエディタを使えばいいのか」とか色々あってからやっと開発のための環境を導入しましょう。ってなったと思ったらその環境導入でつまる。振り返ってみたら時間だけ浪費してほとんど開発せずにそっとPCを閉じる・・・。みたいなことになることがよくあると思うんです。その点、AKAGAMI卍さんのnote買っておけば、実際にbot運用している人がどんな環境でやっているのかがわかるので最初の部分で躓かなくて済むんじゃないかなぁ。と思いますね。
ただ、買って中を読まずにコードだけコピペとかは本当にやめた方がいいですよ!AKAGAMI卍さんのコードが完成系ではないですし、改良できる点は有ります。ですので、買って、実際に動かしてみて不便だと感じたら調べて改良する。というステップを省略せずに進んで行きましょう。
 ちなみにですが、本noteは基本的にpython 3系を使って進めていく予定ですので、anaconda pythonで検索してそれっぽいサイトからインストールして置いてください。以下の記事とかわかりやすいんじゃないでしょうか。
<Anaconda で Python 環境をインストールする>
 https://qiita.com/t2y/items/2a3eb58103e85d8064b6
 あと、今回bitflyerのAPIがpythonで利用できる[pybitflyer]というパッケージも使用するのでインストールしておいてください。ターミナル立ち上げて以下のコマンドを打てばインストールできます。

$ pip install pybitflyer

こちらのサイトも参考になります。APIキーの取得方法とかも書いてあるのでとりあえずしたがってみてください。
<bitFlyer LightningのAPIをPythonから使えるパッケージ「pybitflyer」を作りました>
http://wolfin.hatenablog.com/entry/2016/08/29/010112


・新SFDの導入が開始されました
 タイトルの通りですが、日本時間の2018 年 3 月 17 日 午前 4 時からSFDの変更が実施されましたね。皆さんすでにご存知かと思いますが、変更点は以下の文書で正式に発表されていますね。
https://bitflyer.com/pub/changes-to-sfd-have-been-implimented-ja.pdf
簡単に説明すると、今までのSFDは乖離を抑制する取引に対するインセンティブになっていなかったので、新SFDでは、乖離を縮める取引>乖離を広げる取引になるように設計し直しましたよっていう話です。
 具体的な変更点は文書に書いてある通りなのですが、旧SFDだと「境界手前でロング、境界超えたら即ショート」を繰り返せば相場の方向をあまり気にせずにSFD手数料が稼げてました。これが俗に言うSFD botです。しかし、新SFDだと乖離を広げる方向の取引は新規(エントリー)・決済関係なくSFD手数料を徴収して、縮める方向の取引は新規(エントリー)取引にのみSFD手数料を付与する形になりました。これによって乖離が正の方向(本noteではFXの値段>現物の値段とします)の時はショートエントリーのインセンティブが高まるようになりました(ロングエントリーのリスクが増えたとも)。
なので今回は境界値付近ではショートエントリーのみをしてリスクをちょっと減らしたSFD botを作成しようと思います。

※作成するbotは実際に注文を出します。トレードの際はロットを小さくするなど、ご自身の責任でおこなってください。

・早速SFD botを開発しましょう
 
それでは、境界値(乖離率5%,10%,15%,20%)を超えた地点にショートエントリーの指値注文を行い、ポジションができれば境界値より低い地点にポジションクローズの指値注文を行うbotを作成したいと思います。(※本当に必要最小限の機能しか持たせていません。細かいところは今後のキャンプで実装していく予定です。)

まずは必要なライブラリをimport、注文するロット数を定義
今回ロット数を定義しているのはプログラム中の「SIZE=0.002」の部分で、0.002を適当な数字に変えればロット数を増やせます。BTC換算なのでこの場合だと0.002BTCを基本発注量としていることになります。

# coding=utf-8
import pybitflyer
import json
import time
import ccxt

# 定数定義
SIZE = 0.002

次にクラス、メソッドの作成。上のコードの下に貼ってください。この時インデントが崩れないように気をつけてください。「自分のAPIキー」「自分の秘密鍵」はそれぞれ自分の情報をいれてください。

class API:
   # 初期化
   def __init__(self):
       self.public_api = pybitflyer.API()
       self.api = pybitflyer.API(api_key="自分のAPIキー", api_secret="自分の秘密鍵")

   # 注文用のメソッド
   # type = 0:成り行き(MARKET)
   # type = 1:指値(LIMIT)
   # product_code:扱う商品
   # minute_to_expire:期限執行期間
   def placeOrder(self, product_code, side, type, price, size):
       if type == 0:
           print(side + ' ' + str(self.api.sendchildorder(product_code=product_code, child_order_type="MARKET",
                                                          minute_to_expire=60, side=side, size=size)))
       elif type == 1:
           print(side + ' ' + str(self.api.sendchildorder(product_code=product_code, child_order_type="LIMIT",
                                                          minute_to_expire=60, side=side, size=size, price=price)))
   # 建玉情報の取得メソッド
   # side : ポジション方向
   # size : 全建玉のサイズ
   # sfd_valu : sfdが付与されているポジションの合計額
   def getMypos(self, product_code):
       side = ""
       size = 0
       sfd_valu = 0
       poss = self.api.getpositions(product_code=product_code)
       # もしポジションがあれば合計値を取得
       if len(poss) != 0:
           for pos in poss:
               side = pos["side"]
               size += pos["size"]
               if pos["sfd"] > 0:
                   sfd_valu += pos["size"] * pos["price"]
       return side, size, sfd_valu

   # 注文をキャンセルするメソッド
   def cancelAllOrder(self, product_code):
       self.api.cancelallchildorders(product_code=product_code)

クラスとは何か、メソッドとは何かと言うことについては以下のリンクなどをご参照ください。
https://www.sejuku.net/blog/28182
https://qiita.com/Usek/items/a206b8e49c02f756d636
めちゃくちゃざっくり言うと、
 クラス:APIを使ってbitflyerの情報を取得したり注文を出してくれる物体
 メソッド:クラスの中にある情報を取得したり注文を出したりする処理
ここで止まってしまうのは時間の無駄なのでとりあえず進めましょう。

最後にメインの処理の実装。これも先ほどの続きに貼ってください。

def main():
   public_api = pybitflyer.API()
   mypos_side = ""
   mypos_size = 0
   mypos_sfd_size = 0
   fx_size = SIZE
   order_OK = False
   pos_stone = 5
   # インスタンスの作成
   api = API()
   # メインループ
   while True:
       # 乖離率の計算
       spot = round(public_api.ticker(product_code="BTC_JPY")["ltp"])#現物の最終執行価格
       fx = round(public_api.ticker(product_code="FX_BTC_JPY")["ltp"])#FXの最終執行価格
       diff = round((fx - spot) * 100 / spot, 2)#乖離率
       # sfdが発生する値段
       sfd_5 = spot * 1.05
       sfd_10 = spot * 1.10
       sfd_15 = spot * 1.15
       sfd_20 = spot * 1.20
       # 売り指値
       ask_price_5 = round(sfd_5 + 60, 0)
       ask_price_10 = round(sfd_10 + 60, 0)
       ask_price_15 = round(sfd_15 + 60, 0)
       ask_price_20 = round(sfd_20 + 60, 0)
       # 買い指値
       bid_price_5 = round(sfd_5 - 60, 0)
       bid_price_10 = round(sfd_10 - 60, 0)
       bid_price_15 = round(sfd_15 - 60, 0)
       bid_price_20 = round(sfd_20 - 60, 0)
       print("SPOT: " + str(spot) + "/FX: " + str(fx) + "/ DIFF: " + str(diff) + "%")

       # 自分のポジションを取得
       # ポジションがなければ注文可能状態(エントリーできる)
       # ポジションがある場合は決済方向にのみ注文可能
       mypos_side, mypos_size, mypos_sfd_size = api.getMypos("FX_BTC_JPY")
       mypos_size = round(mypos_size, 4)
       mypos_sfd_size = round(mypos_sfd_size, 4)
       if mypos_size == 0:
           order_OK = True
       else:
           order_OK = False

       # FXポジションなし(注文OK状態)かつ
       # プラスの乖離が発生している場合に売り指値注文
       # FXポジションあり(注文NG状態)の時
       #  FXの決済注文をする
       if order_OK:
           if diff >= 4.78 and diff <= 5.5:
               api.placeOrder("FX_BTC_JPY", "SELL", 1, ask_price_5, SIZE)
               pos_stone = 5
           elif diff >= 9.78 and diff <= 10.5:
               api.placeOrder("FX_BTC_JPY", "SELL", 1, ask_price_10, SIZE)
               pos_stone = 10
           elif diff >= 14.78 and diff <= 15.5:
               api.placeOrder("FX_BTC_JPY", "SELL", 1, ask_price_15, SIZE)
               pos_stone = 15
           elif diff >= 19.78 and diff <= 20.5:
               api.placeOrder("FX_BTC_JPY", "SELL", 1, ask_price_20, SIZE)
               pos_stone = 20
       else:
           if mypos_side == "SELL":
               if pos_stone == 5:
                   api.placeOrder("FX_BTC_JPY", "BUY", 1, bid_price_5, mypos_size)
               elif pos_stone == 10:
                   api.placeOrder("FX_BTC_JPY", "BUY", 1, bid_price_10, mypos_size)
               elif pos_stone == 15:
                   api.placeOrder("FX_BTC_JPY", "BUY", 1, bid_price_15, mypos_size)
               elif pos_stone == 20:
                   api.placeOrder("FX_BTC_JPY", "BUY", 1, bid_price_20, mypos_size)
       # SFDが発生する値段がさっき注文した指値価格よりも高くなっていれば全注文をキャンセル
       spot_New = round(public_api.ticker(product_code="BTC_JPY")["ltp"])
       fx_New = round(public_api.ticker(product_code="FX_BTC_JPY")["ltp"])
       diff_New = round((fx_New - spot_New) * 100 / spot_New, 2)
       # sfdの値段
       sfd_5_New = spot_New * 1.05
       sfd_10_New = spot_New * 1.10
       sfd_15_New = spot_New * 1.15
       sfd_20_New = spot_New * 1.20

       if (sfd_5_New > ask_price_5
           or sfd_10_New > ask_price_10
           or sfd_15_New > ask_price_15
           or sfd_20_New > ask_price_20
           ):
           api.cancelAllOrder("FX_BTC_JPY")
           print("Canceled the all order")

       # 乖離が望んだ方向と違う方向へと大きく動けば損切りする(FX)
       if mypos_side == "SELL":
           if pos_stone == 5 and diff > 5.8:
               api.placeOrder("FX_BTC_JPY", "BUY", 0, 0, SIZE)
           elif pos_stone == 10 and diff > 10.8:
               api.placeOrder("FX_BTC_JPY", "BUY", 0, 0, SIZE)
           elif pos_stone == 15 and diff > 15.8:
               api.placeOrder("FX_BTC_JPY", "BUY", 0, 0, SIZE)
           elif pos_stone == 20 and diff > 20.8:
               api.placeOrder("FX_BTC_JPY", "BUY", 0, 0, SIZE)
       time.sleep(5)

if __name__ == "__main__":
   main()


今回は5秒ごとに現物とFXの乖離率を計算し、乖離率が指定した閾値の範囲ならばSFDが発生する価格の60円上に売り指値注文を出すようにしています。
プログラム中の以下の部分は乖離率が4.78%~5.5%なら注文を出す。と言う判定をしている部分です。これを変えれば自分のお好きな閾値を決められます。

if diff >= 4.78 and diff <= 5.5:

指値の価格も以下の60の部分を適当な数字に変えれば指値も変更できます。

 # 売り指値
      ask_price_5 = round(sfd_5 + 60, 0)
      ask_price_10 = round(sfd_10 + 60, 0)
      ask_price_15 = round(sfd_15 + 60, 0)
      ask_price_20 = round(sfd_20 + 60, 0)
      # 買い指値
      bid_price_5 = round(sfd_5 - 60, 0)
      bid_price_10 = round(sfd_10 - 60, 0)
      bid_price_15 = round(sfd_15 - 60, 0)
      bid_price_20 = round(sfd_20 - 60, 0)

それでは実際に動かしてみましょう。実行の仕方は「python 実行方法」とかでググればすぐ出てきます。以下サイトにも書いてます。
https://www.python-izm.com/introduction/execution/
エラーが出た場合はエラーメッセージをコピペしてググりましょう。
ググってもわからなかった場合はコメントしていただくか、Twitterにリプライください。対応できない場合もありますが、時間があればみてみます。

・初心者だけどこれを回せば儲かりますか?
 自分で公開しておいてこんなこと言うのもあまりよくないとは思いますが、微妙じゃないかなって感じですね。
サーバが遅くて注文が通らなかったらどうするんだとか、相場が急激に動いたらどうするんだとか、本当に考慮漏れがたくさんあるんですね。ならなぜ公開したんだって話ですけど、皆さんに実際に動く+うまくいけば稼ぐこともできるbotを開発してもらいたかったという気持ちがあったからです。次回以降のnoteでは、短くなると思いますがサーバ状態が重いときにどうするのかという問題の対処法などを書いて行こうかなと思います。

・最後に
 これを読んでくださっている方の中には私よりもっと技術力のある方が大勢いらっしゃると思います。なので私のコードをみて、「いけてないコードだな」と思った方はぜひ、コードの改善点や指摘事項をTwitterのDMやリプライ、noteのコメントなどでいただければと思います。協力していただけた方には修正後のbotなどお渡しするなど何らかのお返しができるように考えていきます。

最後までお読みいただきありがとうございました。

この記事が気に入ったらサポートをしてみませんか?