売買シグナルをpythonで実装し、実際にどのようなチャートでシグナルが点灯するのかを確認したい場合、リアルタイムの価格データで検証するのは時間の無駄です。
特にちょっとしたパラメーター(%など)の調整のために、どのようなローソク足がヒットするかを確認したいだけであれば、リアルタイムのデータを使う必要はありません。
今回の記事では、Cryptowatchで取得できるBitflyerの価格の過去データからシグナルを検証する方法を紹介します。
今回、検証する売買シグナル
前回までの記事で紹介した「(酒田五法の)赤三兵というチャートパターンを買いシグナルにするpythonコード」を使って説明していきます。そのため、こちらで紹介しているコードを改良するかたちで説明します。
検証用のコード
import requests from datetime import datetime import time response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc",params = { "periods" : 60 }) def get_price(min,i): data = response.json() last_data = data["result"][str(min)][i] return { "close_time" : last_data[0], "open_price" : last_data[1], "high_price" : last_data[2], "low_price" : last_data[3], "close_price":last_data[4] } def print_price( data ): print( "時間: " + datetime.fromtimestamp(data["close_time"]).strftime('%Y/%m/%d %H:%M') + " 始値: " + str(data["open_price"]) + " 終値: " + str(data["close_price"]) ) def check_candle( data ): realbody_rate = abs(data["close_price"] - data["open_price"]) / (data["high_price"]-data["low_price"]) increase_rate = data["close_price"] / data["open_price"] - 1 if data["close_price"] < data["open_price"] : return False elif increase_rate < 0.0005 : return False elif realbody_rate < 0.5 : return False else : return True def check_ascend( data,last_data ): if data["open_price"] > last_data["open_price"] and data["close_price"] > last_data["close_price"]: return True else: return False last_data = get_price(60,0) print_price( last_data ) time.sleep(10) flag = 0 i = 1 while i < 500: data = get_price(60,i) if data["close_time"] != last_data["close_time"]: print_price( data ) if flag == 0 and check_candle( data ): flag = 1 elif flag == 1 and check_candle( data ) and check_ascend( data,last_data ): print("2本連続で陽線") flag = 2 elif flag == 2 and check_candle( data ) and check_ascend( data,last_data ): print("3本連続で陽線 なので 買い!") flag = 3 else: flag = 0 last_data["close_time"] = data["close_time"] last_data["open_price"] = data["open_price"] last_data["close_price"] = data["close_price"] i += 1 time.sleep(0)
前回の実践用のコードからの主な変更点は3か所だけです。
上記のコードには無駄な部分もあり、もっとコード量を減らすことはできます。ですが、あまり大幅に変更してしまうと、リアルタイム用と過去データ用のソースコードが全然違うものになってしまい、2種類のコードを管理しなければならなくなります。
そのため、今回は実践用のコードの内容をできるだけ変更せずに、最小限の修正だけで過去データの検証をすることで、いつでも簡単に実践用(リアルタイム用)のコードに戻せるようにしておきます。
コードの変更点
APIを呼ぶのは最初の1回だけにする
response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc",params = { "periods" : 60 })
先頭で、requests.get()を使って1度だけAPIを使用しています。
実践用のコードでは10秒間に1回APIを使用するため、get_price()の中にrequests.get()を入れていましたが、過去データの検証では1回だけ取得したデータを使うので、requests.get()をget_price()から外して先頭に記述しています。
get_price()の中身を修正する
def get_price(min,i): data = response.json() last_data = data["result"][str(min)][i] return { "close_time" : last_data[0], "open_price" : last_data[1], "high_price" : last_data[2], "low_price" : last_data[3], "close_price":last_data[4] }
実践用のコードでは、get_price()は最新のローソク足の価格データを1つだけ返す関数として作成しました。
しかし過去データの検証では、1度だけ取得した価格データを古い順番(時系列順)に試していきます。そのため、i という引数を追加し、指定された i番目のデータを返す関数に修正します。変更点は「i」のところを追加しただけです。
While文の中でiという変数を1ずつ増やす
i = 1 while i < 500: data = get_price(60,i) #if文の処理 i += 1 time.sleep(0)
実践用のコードでは、While文の中で、10秒に1回APIを使用して最新の価格を取得しにいきます。
しかし過去データの検証では、最初に取得した価格データを先頭から順番(時系列順)に試していくだけです。そのため、While文の中の get_price()に i番目 という意味の変数 i を渡し、ループ処理のたびに i を1ずつ増やす処理に変更します。また10秒待機する必要はないので、sleep()の中を0秒にします。
変更点は以下の部分だけです。
・i = 1 の宣言
・data = get_price(60,i)の部分
・i += 1 を追加
・time.sleep()を0秒に修正
While文のループを500回に限定する
実践用のコードは、リアルタイムの価格を取得し続けるプログラムなので、While True:という無限ループで記述しています。
しかし過去データの検証では、最初に取得した価格データを先頭から順番(時系列順)に試していくだけです。Cryptowatchの価格データの取得件数は、デフォルトでは500件なので、500回ループしたら止まるように、While True:の部分を While i<500: に変更します。
実行結果
これを実行すると以下のようになります。
実際にシグナルが点灯した日のチャートを目で確認しながら、実体の割合、上ヒゲや下ヒゲの割合、上昇率、などのパラメーターを調整していけば、自分の理想のローソク足のパターンでシグナルが点灯するように、コードをカスタマイズすることができます。
今回は1分足で解説していますが、もちろん1時間足や日足でもやり方は同じです。その場合は、最初のAPIの{ periods : } を3600(1時間足)や86400(日足)に変えて使ってください。
なお、勝率の検証などのいわゆる「バックテスト」については、もっと高度な内容になりますが、そちらも今後、解説していく予定です。
僕もバリバリの文系ですがここまで本当にわかりやすい説明でどんどん読み進めることができました!本当にありがとうございます!質問なのですが、cryptowatchでのアカウント登録を想定しているのか、サイト内のコードでcryptowatch apiの制限に引っかかることがあるかどうか知りたいです!
このコードをこのままvscodeで実行するとKeyerror: resultとでてしまいます。原因を探したところcryptowatch apiの制限に引っかかっている可能性が高い?(apiのurlにアクセスしたらerror表示が出てアカウントの登録をするように促される)と考えており質問いたしました。