さて、前回の記事で考えた自動売買BOTのロジックを実装していきましょう。まず必要なのは、1分足の価格データです。
Bitflyerでは、各時間軸のローソク足の情報(始値・高値・安値・終値)が、APIでは提供されていません。そのため、ローソク足のデータを使いたい場合は、Cryptowatchという外部APIを利用する必要があります。
なお、CryptowatchのAPIの基本的な使い方は以下の記事で説明しています。以下の内容は前提として進めさせてください。
・APIって何?CryptowatchのAPIを使って説明
・APIで取得したJSONデータから欲しい数字を取り出す方法
さて、やっていきましょう!
まずBitflyerの1分足の価格データの取得する
ローソク足の価格データのセット(始値・高値・安値・終値)のことを、よく英語やAPIの仕様書では OHLC と表記します。
これは Open-High-Low-Close の略ですが、よく見かけるので覚えておくといいです。ここに出来高(Volume)を加えて、OHLCVと表記することもあります。OHLCVという単語を知っていると、API仕様書を読むのが少し楽になります。
さて、BitflyerのOHLCを取得するためのCryptowatchのAPIは以下になります。
例)1分足のBitflyerFXのOHLC価格
https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?periods=60
1分足以外の情報を取得したい場合は「60」の部分を秒換算で変更します。5分足のデータが欲しい場合は「?periods=300」を指定します。1時間足なら「?periods=3600」、日足なら「?periods=86400」です。
これを10秒おきにAPIで取得して、それを黒い画面に表示していくpythonコードを書いてみましょう。
始値と終値を取得する
前回の記事の自動売買ロジックは、『始値と終値の両方が切り上がっている』ことがエントリーの判定条件になっていました。なので、まずは最新の1分足の価格データだけを取り出して、始値と終値だけを抽出してみましょう。
pythonで書くと以下のようなコードになります。
import requests from datetime import datetime # CryptowatchのAPIで1分足を取得 response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?periods=60") response = response.json() # 最後から2番目のローソク足を取り出す data = response["result"]["60"][-2] # ローソク足から日時・始値・終値を取り出す close_time = datetime.fromtimestamp(data[0]).strftime('%Y/%m/%d %H:%M') open_price = data[1] close_price = data[4] print( "時間: " + close_time + " 始値: " + str(open_price) + " 終値: " + str(close_price) )
時間を表示する部分以外は、今まで勉強した内容で書けるのではないでしょうか!
コードの解説
3行目では、request.get()でAPIのリクエストを送り、返ってきた内容を response という変数に入れています。4行目では、そのJSON形式のデータをパースしています。これは、データを扱うための準備のようなものです。
5行目では、1分足の価格データのうち1番新しいものを取得しています。Cryptowatchの価格データは古い順に並んでいるので、一番新しいものを取り出すには、[-1]と指定します。ただし1番新しいローソク足だとまだ終値が固まっていないため、その1つ前を指定するために、[-2]としています。これを data という変数に入れます。
なぜ response[“result”][“60”][-2] になるのかわからない方は、以下の記事を読んでください。
・CryptowatchのAPIで取得したbitcoin価格のJSONデータから欲しい数字を取り出す
さて、data に入れた最新の価格データは以下の順番で並んでいます。
[ 日時, 始値, 高値, 安値, 終値, 出来高 ]
ここから日時、始値、終値の3つを取り出します。ただし日時はUNIX時間なので、これを人間が見やすい時間に変換します。
6行目では、取得したデータの先頭 [0] にある日時(UNIXタイムスタンプ)を、datetime.fromtimestamp() という関数で、datetime型に変換しています。datetime型というのは、pythonで時間という概念を扱うための形式だと考えてください。
さらにこのdatetime型を、strftime()という関数で、以下のように文字列に変換しています。
strftime(‘%Y/%m/%d %H:%M’)
→ 「年/月/日 時間:分」の形式の文字列に変換
7行目では、始値が[1]番目、終値が[4]番目に入っているので、それぞれを取得して open_price、close_priceという2つの変数に入れています。最後の8行目は、それらをprint()で見やすく表示しています。
実行結果
さて、上記のコードを実行してみましょう。
以下のような結果になりました。
このコードを実行したのは7時6分です。
ちゃんと1つ前のローソク足の、始値と終値が取得できていました!
While文を使って自動ループにしてみる
では、今度は10秒おきに自動的にAPIから1分足の価格データを取得して、それをプロンプト(黒い画面)に表示していくpythonコードを書いてみましょう。今回、ループ処理をさせるために、はじめて「while文」を実践で使います!
pythonコードは以下のようになります。
import requests from datetime import datetime import time while True: response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?periods=60") response = response.json() data = response["result"]["60"][-2] close_time = datetime.fromtimestamp(data[0]).strftime('%Y/%m/%d %H:%M') open_price = data[1] close_price = data[4] print( "時間: " + close_time + " 始値: " + str(open_price) + " 終値: " + str(close_price) ) time.sleep(10)
コードの解説
変更したのは3箇所です。
まずsleep()という関数を使うために、最初に「import time」を新しく読み込んでいます。sleep()というのは、ループ中の処理と処理の間に休止を挟みたいときに使います。コードの一番最後に、time.sleep(10)と書くことで、次の処理までに10秒間の待機を挟んでいます。
さらにこの処理全体を、while True: と書いた行の下にインデント付きで入れています。while True: というのは、無限ループという意味で、エラーが出たり手動で止めたりしない限り、while True:の下にインデント(先頭の空白)付きで書かれた処理を、繰り返し永久に実行し続けます。
さて、このコードを実行してみましょう。
実行結果
このコードを実行すると以下のようになります。
眺めていると、次々と一番下に新しい価格情報が追加されていくのがわかります。プログラム初心者の方からすると、いよいよ「プログラミングをしてる!」という実感が湧いてきたのではないでしょうか(笑)
無限ループの実行を止める
なお、こちらは While True: の無限ループで書いているので、何もしなければずっと動き続けます。止めたい場合は、キーボードの「Ctrl + C」を押してください。
「Ctrl + C」を押すと以下のように、KeyboardInterrupt と出て、プログラムが停止します。
if文を使って条件判定してみよう!
ここまでの記事で、10秒おきにAPIから価格データを取得してそれを表示するpythonプログラムができました。しかし実際には、10秒おきに価格データを取得しても、それを全て表示する必要はありません。
10秒おきでの表示だと、同じ価格データが何行にも渡って並んでしまいます。1分足であれば1分間に1度だけ、5分足であれば5分間に1度だけ、ローソク足が更新された場合に価格データを表示されるようにしたいですよね。
このような条件判定をするには「if文」を使います。if文を聞いたことがない方は、まず先に以下の記事に目を通してください。
・最低限必要な3つの構文(While文/if文/for文)の使い方
では、While文のループの中にif文を入れて、価格データに更新があった場合のみ、それをprintするコードを書いてみましょう!
「価格データに更新があったかどうか?」を判定するためには、(前回のデータ)と(今回のデータ)の2つを比較する必要があります。つまり、変数が2つ必要だということを意識しながら読んでみてください。
以下のようなコードになります。
Pythonコード
import requests from datetime import datetime import time response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?periods=60") response = response.json() # 前回の時間を last_time に入れる last_data = response["result"]["60"][-2] last_time = datetime.fromtimestamp(last_data[0]).strftime('%Y/%m/%d %H:%M') time.sleep(10) while True: response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcfxjpy/ohlc?periods=60") response = response.json() data = response["result"]["60"][-2] # 今回の時間を close_time に入れる close_time = datetime.fromtimestamp(data[0]).strftime('%Y/%m/%d %H:%M') open_price = data[1] close_price = data[4] # 前回の時間(last_time)と今回の時間(close_time)が違う場合のみprintする if close_time != last_time: print( "時間: " + close_time + " 始値: " + str(open_price) + " 終値: " + str(close_price) ) # 前回の時間(last_time)を今回の時間(close_time)で上書きする last_time = close_time time.sleep(10)
取得した最新のローソク足データから、日時・終値・始値を取得して、close_time/open_price/close_price の3つの変数に入れるところまでは全く同じです。
違うのは if close_time != last_time の部分です。今回は、価格データに変更があった場合のみprint()を実行したいので、以下のように条件判定をします。
if文の仕組み
(1)前回取得したローソク足の日時を変数(last_time)に入れておく
(2)今回新しく取得したローソク足の日時(close_time)に入れる
(3)if文の条件判定で、変数(last_time)と(close_time)を比べる
(4)日時に変更があれば、if文の中身が実行されて価格データがprintされる。さらに今回新しく取得したローソク足の日時(close_time)が前回のローソク足の日時(last_time)に上書きされる
(5)変更がなければif文の中身は実行されず、10秒待機してまたWhile文の先頭に戻る
上記のコードを実行すると、1分足なら1分間ごと、5分足なら5分間ごとに価格データが表示されるようになります。試してみてください。
「関数」の使い方
さて、プログラムを書くために必要な構文は(for文/while文/if文)の3つだけだと説明しましたが、実はあと1つだけどうしても「お勉強」しならないことがあります。それが「関数」です。
関数というのは、プログラムの中で何度も登場するようなコード(処理)を1つの部品としてまとめておいて、後から1行で呼び出せるようにする記述方法のことです。
例えば、今回勉強した「APIで価格を取得する」というような処理もそうですし、「Bitflyerに注文を出す」というような処理もそうです。何度も使いそうな処理は、そのたびにプログラムを書くのが面倒ですし、何回も同じコードを書いていると修正するときにも不便です。
まずは練習として、今回の記事で作った以下の処理を自作の関数にまとめてしまいましょう!
(1)CryptowatchのAPIで最新の価格を取得する処理
(2)ローソク足データの日時・始値・終値の3つを表示する処理
以下の2つの処理を関数にして、外部から呼び出せるようにしています。以下の記事でその方法を説明するので、関数の作り方を何となく理解してみてください。何となくで大丈夫です!
次回以降の記事では、この2つの点で改良を施したコードを用います。
次回
次は、今回取得した始値と終値を使って、「3回連続で終値が切りあがっている」「3回連続で始値が切りあがっている」「上ヒゲ・下ヒゲが付いている(付いていない)」というのを、どうpythonのプログラムで判定するのかを解説します!
勉強させて頂いてます。
解りやすくてとてもありがたいです、ありがとうございます。
上記ソースですが、このままだとlast_timeが宣言されずにif文に入るため
エラーになります。
while文の前で宣言すると正常に動作しました。
今後とも参考にさせて頂きます。
本当ですね、
こういうご指摘すごく助かります!
ありがとうございますm(__)m
修正しておきました。
是非また見に来てください^^