さて、実際に稼働用BOTのプログラムのコーディングをしてみましょう。
検討(ちょっと設計)の部分とは分けました。なので、この記事では心おきなくコーディングについてバリバリと解説していきますね^^笑
稼働用BOTのプログラム作成準備
プログラムを作る前の検討や簡単な設計については、前回までの記事で完了しています。実際にコーディングに入る上での準備・検討すべき点を改めて挙げてみます。
元にするプログラムは?
売買シグナルや注文処理をイメージするために作成したプログラムがありますので、そちらをまず参考にすると分かりやすいと思います。
今回のプログラムの追加点
・CryptowatchAPIへのリクエスト処理は、ループの中(一定期間毎)で行う
・OHLCの情報は対象日時の終値、もしくは、最新の終値(OHLCの配列[-2])を基準にしてシグナル判定する
※対象日時の終値について:日足を元にしてシグナル判定を行う場合、本日が「2018/3/20」であれば、OHLCの日付が「2018–03-20 00:00:00」となっているデータを最新データと定義する
※なぜOHLCの配列[-2]が最新なのか?:配列[-1]は現在作成中のOHLCデータになります。終値が出ないので、1つ前のローソク足=[-2]を最新とします。
・売買時の情報を変数に格納する(購入価格、購入日時等)
・Jupyterやspyderではなく、プログラムファイル「○○.py」というプログラムを作成して実行する
今回のプログラムの条件(制約等含む)
・注文をした時のエラー(注文が通らない、約定しない)などは考慮しない
・今回は実際の注文はせず、注文をシュミレートした処理とする
・処理開始日は2018/3/20、処理終了日は2018/3/31と仮定してプログラム実行します。
・実際の日付を元に稼働させるのではなく、2018/3/20を開始日と仮定して、ループするたびに日付を+1日します。(実際に稼働させる場合は最新の終値(OHLC配列[-2])を設定すべきです)
・ローソク足は「日足」を対象にする
稼働用BOTの処理の流れ
検討段階でも処理の流れは記載しましたが、一部制約を追加しているので、今回のサンプルコードに合わせて、改めて処理の流れを書きますね。
処理の流れ(概要)
まずは大まかな処理の流れを考えてみます。
1)ライブラリ読み込み、初期化
2)ループ開始
3)ループ内の初期化
4)CryptowatchAPIにリクエスト(OHLCデータ取得)
5)売買シグナルの判定(新規注文、手仕舞い注文)
6)シグナルが出ている場合は新規注文
7)シグナルが出ている場合は手仕舞い注文
8)次のループへ
処理の流れ(詳細)
処理の流れの概要で挙げたものについて、もう少し細かく見ていきましょう。
1)ライブラリ読み込み、初期化
ライブラリもこれまでのBOT作成と同様に読み込んできたものを読み込みます。
また、(既に検討済みの)BOTの処理をコントロールする変数定義を忘れずに。
2)ループ開始
ループを開始する時は条件を指定しなければなりません。
稼働用BOTの場合は、検討の時と同じ様に「While True:」で無限ループできます。※手動で処理終了にする事はできます。
■While文のサンプル
1 2 3 4 5 6 7 8 9 10 11 |
#Whileの条件式で回数を指定してループ i = 0 while i < 5: i += 1 #While True:でループ i = 0 while True: if i == 5: break i += 1 |
ちなみに、While Trueの中でbreakをしなければ無限ループになります。AnacondaPromptなどであれば手動で停止できますので、無限ループでも全然OKです。
3)ループ内の初期化
ループ内で使用する変数はここで初期化しましょう。
4)CryptowatchAPIにリクエスト(OHLCデータ取得)
これまでに何度も行っている処理ですが、CryptowatchAPIからOHLCデータを取得します。
今回はループ内でOHLCデータの取得を行いますので、CryptowatchAPIへのリクエストもループ内で行う事になります。
5)売買シグナルの判定(新規注文、手仕舞い注文)
今回は(これまでと同じ)チャネルブレイクアウトの判定を行います。
尚、ドテン買い、売りをしてみたいと思いますので、新規注文の場合も手仕舞い注文の場合も判定箇所は同じになります。
※新規注文と手仕舞いのタイミングは普通は別になります。また、買いと売りでもタイミングがずれる事はよくあります。
例えば、過去20期間の高値を上に抜けたら、新規買い注文を入れ、売りポジションを持っている場合は手仕舞いします。既に買い注文を持っている場合は、買い増しする事になります。
6)シグナルが出ている場合は新規注文
売買シグナルの判定により、新規注文シグナルが出た場合は新規注文を入れます。
過去20期間の高値を上抜け⇒買い
過去20期間の安値を下抜け⇒売り
のポジションになります。
7)シグナルが出ている&ポジションを持っている場合は手仕舞い注文
売買シグナルの判定により、手仕舞いシグナルが出た場合は決済注文を入れます。
※上述している通り、新規注文と同じタイミングでシグナルが発生します。
過去20期間の高値を上抜け⇒売りポジションを持っている場合は手仕舞い
過去20期間の安値を下抜け⇒買いポジションを持っている場合は手仕舞い
となります。
8)次のループへ
次のループに行く前に少し休憩時間を入れましょう。
これを「スリープを入れる」などといいます。
以下がサンプルです。sleepモジュールの読み込みさえ完了すれば、使い方は簡単です。
■Pythonコード
1 2 3 4 5 6 7 8 9 10 |
from time import sleep #1ミリ秒待つ sleep(0.001) #1秒待つ sleep(1) #1分待つ sleep(60) |
プログラムを作ろう
では検討を踏まえて、実際にプログラムを作成してみましょう。
ただ、今回は、実際の日付で実行する訳ではなく処理対象日を2018/3/20と仮定して、ループを回します。
2018/3/21、2018/3/22、・・・と日付を1日ずつ足しています。
※日付を今日日付にしたり、常に最新のデータをCryptowatchAPIから取得するなどすれば実際に稼働させられるコードになります。
■Pythonコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
#-------- ライブラリ・モジュール -----------# import requests from datetime import datetime from datetime import timedelta from time import sleep import pandas as pd #-------- サブ関数 -------------------------# #OHLCデータ取得関数 def get_data(min, before=0, after=0): #パラメータ設定する params = {"periods" : min } if before != 0: params["before"] = before if after != 0: params["after"] = after #リクエスト送信 response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc",params) data = response.json() return data #過去20日分の高値と安値を算出する関数 def judge_high_and_low(df,ref_date,period_day): for j in range(len(df)): if str(df['Time'][j]) == str(ref_date): #DataFrameの中に「基準日」を見つけた! s_idx = j - (period_day-1) #20日前の日のインデックス ※基準日を含むため-1 if s_idx >= 0 : #DataFrameの範囲内(最低限の判定) return df[s_idx:j]['high'].max(), df[s_idx:j]['low'].min()#過去20日分の高値と安値 break #終値を算出する関数 def get_close_price(df,ref_date): for j in range(len(df)): if str(df['Time'][j]) == str(ref_date): #DataFrameの中に「基準日」を見つけた! return df['close'][j] #終値を返す #【新規注文関数】 #買い注文を入れる関数 def test_func_buy(trade_info): print("買い注文を入れます。") #買い注文のコードを記載する trade_info["position_buy_flag"] = True return trade_info #売り注文を入れる関数 def test_func_sell(trade_info): print("売り注文を入れます。") #売り注文のコードを記載する trade_info["position_sell_flag"] = True return trade_info #【手仕舞い注文関数】 #買い注文を入れる関数 def test_func_buy_2(trade_info): print("買い注文(売り決済)を入れます。") #買い注文のコードを記載する #売り注文のポジションをクローズ trade_info["position_sell_flag"] = False return trade_info #売り注文を入れる関数 def test_func_sell_2(trade_info): print("売り注文(買い決済)を入れます。") #売り注文のコードを記載する #買い注文のポジションをクローズ trade_info["position_buy_flag"] = False return trade_info #------------ main処理 ------------# #変数の定義 min = 86400 #1日(86400秒) start_date = datetime(2018, 3, 1) #OHLC開始日 today = datetime(2018, 3, 20) #今日 #テスト変数 ref_date = today #判定開始日 #テスト変数 period_day = 20 #チャネルブレイクのレンジ close_price_work = 0 #終値の処理用変数 trade_info = { "order_buy_flag": False, "order_sell_flag": False, "position_buy_flag": False, "position_sell_flag": False, "close_buy_flag": False, "close_sell_flag": False, "no_order_flag": False } #ループ開始 while ref_date <= datetime(2018, 3, 31): #ループ内初期化 after = start_date.strftime('%s') #OHLCの取得開始日 before = today.strftime('%s') #OHLCの取得終了日 #OHLCデータの取得 out_data = get_data(min,before,after) Time_Data, Open_price, High_price, Low_price, Close_price = [],[],[],[],[] for ohlc in out_data["result"][str(min)]: Time_Data.append(datetime.fromtimestamp(ohlc[0])) Open_price.append(ohlc[1]) High_price.append(ohlc[2]) Low_price.append(ohlc[3]) Close_price.append(ohlc[4]) df = pd.DataFrame({'Time':Time_Data, 'open':Open_price, 'high':High_price, 'low':Low_price, 'close':Close_price}) #シグナル判定に必要な情報の収集 high_val,low_val = judge_high_and_low(df,ref_date,period_day) #過去20日間の高値と安値を取得 close_price_work = get_close_price(df,ref_date) #基準日の終値を取得 print(str(ref_date) + " : 過去" + str(period_day) +"日間の高値:" + str(high_val) + " 安値:" + str(low_val)) #シグナルの判定 #買い注文のシグナル判定 if high_val < close_price_work: print("終値が過去20日間の高値を上抜け。終値は:" + str(close_price_work)) trade_info["order_buy_flag"] = True #この後の処理で買い注文を出す if trade_info["position_sell_flag"] == True: #売りポジションを持っている trade_info["close_sell_flag"] = True #この後の処理で売りポジションを決済する #売り注文のシグナル判定 elif low_val > close_price_work: print("終値が過去20日間の安値を下抜け。終値は:" + str(close_price_work)) trade_info["order_sell_flag"] = True #この後の処理で売り注文を出す if trade_info["position_buy_flag"] == True: #買いポジションを持っている trade_info["close_buy_flag"] = True #この後の処理で買いポジションを決済する #売買注文を行う #買い注文のシグナルが出ている場合 if trade_info["order_buy_flag"] == True: test_func_buy(trade_info) #買い注文を入れる trade_info["order_buy_flag"] = False #注文要求は終了 #売り注文のシグナルが出ている場合 elif trade_info["order_sell_flag"] == True: test_func_sell(trade_info) #売り注文を入れる trade_info["order_sell_flag"] = False #注文要求は終了 #手仕舞いの売買注文を行う #買い注文の手仕舞いシグナルが出ている場合 if trade_info["close_buy_flag"] == True: test_func_sell_2(trade_info) #売り注文を入れる trade_info["close_buy_flag"] = False #注文要求は終了 #売り注文の手仕舞いシグナルが出ている場合 elif trade_info["close_sell_flag"] == True: test_func_buy_2(trade_info) #買い注文を入れる trade_info["close_sell_flag"] = False #注文要求は終了 #次の日のデータ設定 today = today + timedelta(days=1) #明日 start_date = start_date + timedelta(days=1) #OHLCの取得開始日を+1 ref_date = today #判定に使用する日(今日を設定) #スリープ(次の実行時間まで待つ) sleep(5) |
■実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
2018-03-20 00:00:00 : 過去20日間の高値:1246120.0 安値:777870.0 2018-03-21 00:00:00 : 過去20日間の高値:1246120.0 安値:777870.0 2018-03-22 00:00:00 : 過去20日間の高値:1246120.0 安値:777870.0 2018-03-23 00:00:00 : 過去20日間の高値:1246120.0 安値:777870.0 2018-03-24 00:00:00 : 過去20日間の高値:1246120.0 安値:777870.0 2018-03-25 00:00:00 : 過去20日間の高値:1246120.0 安値:777870.0 2018-03-26 00:00:00 : 過去20日間の高値:1229800.0 安値:777870.0 2018-03-27 00:00:00 : 過去20日間の高値:1158090.0 安値:777870.0 2018-03-28 00:00:00 : 過去20日間の高値:1076451.0 安値:777870 2018-03-29 00:00:00 : 過去20日間の高値:1062040.0 安値:777870 2018-03-30 00:00:00 : 過去20日間の高値:1062040.0 安値:777870 終値が過去20日間の安値を下抜け。終値は:750130.0 売り注文を入れます。 2018-03-31 00:00:00 : 過去20日間の高値:1062040.0 安値:748213 終値が過去20日間の安値を下抜け。終値は:731360.0 売り注文を入れます。 |
■コードと結果解説
今回実行した結果は、実際その日(2018/3/20~3/31)にBOTを稼働させた場合と同じ結果となっているはずです。
各処理の詳細については、全体のコードの前で記載しているのでそちらを参考にして下さい。
■実際に動かす場合の注意点
実際に動かす場合には以下の点を変更する必要があります。
※最新データを常に確認する方法です。
・注文を入れるコードを追加
・無限ループにする(「While True:」を使う)
・参照するOHLCは最新のもの(OHLCの配列[-2])を見れば良い
※OHLCの配列[-1]のデータは現在作成中のOHLCとなり、終値が出ないため対象外
・Cryptowatch APIから取得するデータは直近データにする(beforeの指定はいらない)
・Cryptowatch APIのリクエスト制限に注意(実行し過ぎるとリクエストブロックになる可能性があります)
・注文エラー時の処理を追加する(今回は注文エラーは発生しない前提で実施しています)
まとめ
今回は実際に稼働させるBOTのサンプルを作成しました。
本当に稼働させるためには注文の処理を組み込むなど、もう少し手を入れる必要がありますが、そこはこれまでにお伝えしている内容を把握されていれば簡単なはずです。
※もし、分からない箇所がある。という場合には、これまでの内容を改めて復習してみて下さい。
シグナルの変更などは頻繁に発生すると思いますが、稼働するBOTのフローやコードというのは基本的には大きくは変わりません。
一度、しっかりと軸となる様なコードを用意しておけば、その後の変更も非常に楽です。