前回の記事(「JSON形式のデータから自由に数字を取り出そう」)の、練習問題の答え合わせです。
まずCryptowatchのAPIで、過去のbitFlyerのビットコイン価格(日足データ)をまとめて取得します。この手順については、すでに説明済み(こちらの記事)なので省略します。以下のAPIをそのまま使ってで説明を進めていきます。
https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc?periods=86400&after=1514764800
このAPIでは2018年1月以降の日足データを取得しています。ここから、2018年1月16日、ビットコイン価格の大暴落があった日の終値と最安値を取得してみましょう。
取得したJSONデータの構造を把握する
取得したデータの最初の方の部分を切り取ってみると、以下のような構造になっていることがわかります。
{"result":{"86400":[[1514764800,1519999,1713306,1502242,1675100,20188.11,32083065000],[1514851200,1673600,1684270,1568150,1635320,10534.478,17226760000],[1514937600,1637228,1760110,1600000,1725726,10670.864,17824102000],....
最初の部分だけを見ると、
{“result”:{“86400”:[[
で始まっていますね。
この先のデータがものすごく長いのですが、少なくともこの先頭の文字列を見るだけでも、以下のようなデータ構造になっていることはわかります。
{ “result”: {“86400”: [[xx,xx,xx…],[xx,xx,xx…],[xx,xx,xx…]] }}
一番内側に
[xx,xx,xx…]
という数字が7個含まれた配列値のデータ群があり、これが1日分のデータです。
このデータ群が日付分だけ存在します。
つまり以下のように集まって、
[ [],[],[],[]…. ]
という大きな1つの配列値のデータ構造になっています。さらにこの大きな1つの配列値のデータが、
{ “86400” : [ [],[],[],[]…. ] }
と、{} のカッコで括られているため、”86400″(日足)をキーとして、[]内のデータ全てを1つの値とするデータ構造になっていることがわかります。さらに上記の{}内のすべてのデータを1つの値として、
{ “result” : {1つのデータ} }
と、result をキーとする構造になっていることがわかります。
ここで前回の記事で勉強したことを思い出せば、Pythonのプログラムで一番内側の価格データを指定するためには、
data[“result”][“86400”][0][0]
というように指定すればいいことがわかります。
※ 0 のところには、「左から何番目のデータか?」という数字が入ります。
目的の日付データを探す
さて、次に2018年1月16日(ビットコイン価格の大暴落があった日)のデータを探します。一番内側の数字データが、以下の順番で記載されていることはすでに説明しました。
[ 取引時間(クローズ) , 初値 , 高値 , 安値 , 終値 , 出来高 ]
実際に、JSONデータの先頭のUNIXタイムスタンプを、いくつか日時に直してみればわかりますが、Cryptowatchの価格データは毎日午前9時で区切られて日足データとしてまとめられていることがわかります。
ということは、2018年1月16日の暴落時の最安値が含まれた価格データを探すには、「2018年1月17日午前9時」までを区切りとするデータのまとまりを探せばいいことがわかります。これはUNIXタイムスタンプに変換すると、「1516147200」です。
JSONデータの配列値は、左から順番に0番目、1番目、2番目….、と数えることを思い出してください。
つまり今回の目的の価格データを探すためには、一番内側の[]のデータのまとまりのうち、先頭(0番目)が「1516147200」で始まっているデータの3番目の数字を取り出せばいいことがわかります。
このデータをどうやって探すのか?
さて、「自分で試してみてください」といっておいて申し訳ないのですが、実はここまで勉強した内容だけだと、ここで行き詰ってしまいます。なので、この部分までたどり着けた方はいったん合格です(笑)
ここから先に進むには、少し新しいプログラミングの知識が必要になります。
なぜなら先頭に「1516147200」を含むデータ群、[xx,xx,xx,xx….]が、その一つ外側のデータ構造[ [],[],[],[]….. ]の何番目にあるのかが、わからないからです。
前回の記事で紹介したbitFlyerのAPIのように、すべてのデータが、{ キー:値 } の形式になっていれば、キーを指定するだけで値を取り出すことができます。しかし、[[],[],[],[]…..]のようなデータ構造の場合は、目的の数字が何番目の[]に入っているのか、自分で調べなければわかりません。
for文を使ってループで探す
このような場合は、pythonのプログラミングで(先頭から順番に)片っ端から調べる、という方法を取ります。つまり、先頭の[]から順番に見ていき、一番内側の[]の先頭の数字が「1516147200」に一致するものを取り出す、というプログラムを組みます。
JSONのデータ形式によっては、このような処理が必要になることも多いので、ソースコードを理解しておきましょう。エディタを起動して以下のようなプログラムを書いてみてください。
import requests response = requests.get("https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc?periods=86400&after=1514764800") data = response.json() for item in data["result"]["86400"]: if item[0] == 1516147200: print(item[3])
先頭から4行目までは前回の記事と全く同じです。
なので、新しく出てきたforのところから意味を説明します。
for文とif文の意味
文系の初学者の方が、できるだけ勉強をすっ飛ばして、実践からプログラミングを始める場合でも、どうしても絶対に「if文」と「for文」だけは理解していないと先に進めなくなります。
for文/if文の概念自体が全くわからない方は、できれば以下の記事を先に読んで少しだけ勉強してみてください。15~30分くらいで大体、要領だけはつかめると思います。
今回のコードの意味
今回のJSONデータは、{ “result”: {“86400”: [[0番目],[1番目],[2番目]….]} というデータ構造になっており、知りたいのは何番目の[]に、2018年1月16日の価格データが含まれているか?です。
[]の中の先頭(0番目)に日付の数字が含まれていて、3番目に最安値の価格が含まれていることはすでに知っています。
data[“result”][“86400”][???][0]
が 1516147200 に一致する場合に、
data[“result”][“86400”][???][3]
の数字を取り出せばいいことになります。
つまり、for文のループ処理としては、
data[“result”][“86400”][0]
data[“result”][“86400”][1]
data[“result”][“86400”][2]
data[“result”][“86400”][3]
data[“result”][“86400”][4]
data[“result”][“86400”][5]
data[“result”][“86400”][6]
・
・
・
というのを順番に自動的に試していけばいいわけです。
for item in data[“result”][“86400”]: の部分では、86400をキーとするデータの値( [[0番目],[1番目],[2番目]….] のこと)の中の要素について、[ 〇番目 ] のデータを1個ずつ item という変数名に置き換えて、for文のループ処理を実行しています。
また、一番内側の[]の先頭の数字が「1516147200」に一致するかどうかは、if文で判定します。こちらは、現在、for文でループしている
data[“result”][“86400”][?番目]
(→ これをfor文の中では item に置き換えている)
のデータのうち、
data[“result”][“86400”][?番目][0]
(→ これはfor文の中では、item[0] に置き換えられる)
が、「1516147200」に一致すればいいわけです。そのため、if item[0] == 1516147200:という条件分岐を記述し、一致した場合にはその3番目の要素(item[3])を、print()で黒い画面に表示するように指示しています。
実行してみる
これをAnacondaプロンプトで実行してみます。
最安値は101万5000円でした。
この日の最高値は168万1000円、前日の終値は171万7000円ですから、この1日で70万円近くも暴落したことになりますね。(ぜひ練習として、この日の最高値や前日の終値も取得してみてください)
すごく良い取り組みだと思います。
pgm書かなくなって久しいですが、
ここを見つけて思わず書いてしまいました(笑)
関数引数のリテラル記述など気になるところはありますが、
とにかく素晴らしいと思いました。
(いつの日か)利益がまとまったらお礼をしたいので
BTCアドレスかXRPアドレスなどを記載してください。
更新を楽しみに拝読させて頂きます。
ありがとうございます!
関数引数のリテラル記述、全然わかってないので勉強します(笑)
チップアドレスなどは、もっと記事内容が充実してきたら考えてみます!
ぜひまた見に来てください^^
>関数引数のリテラル記述
ごめんなさい 方言が伝わらなかったみたいですね。
下記の呼出個所です。
>data = get_price(60,-2)
関数パラメータの
60 と -2 がリテラル表記です(昔話かもw)
メインプロシージャ内に二か所あると思います。
記事内で「リョータ」さんも記載済みでしたが
60 が ポーリング間隔(秒指定)だと思います。
リテラル表記だと仕様変更(或いは拡張)する際に
呼出個所すべてを置換しないとおかしな挙動になると思います。
そこで下記の様に呼出パラメータに変数を使用します。
(昔はですw 今は違うかもしれませんw)
~
intvsec = 60
offset = -2
data = get_price(intvsec,offset)
~
こうすると1分足参照の現行仕様を5分足参照に変える際に
intvsec = 300
とするだけで済みます。
年寄の戯言でした。
自由にノビノビと「リョータ」さんらしくやってください。
>リテラル表記だと仕様変更(或いは拡張)する際に
>呼出個所すべてを置換しないとおかしな挙動になると思います。
なるほど、そういうことですね!
たしかに60は最初に変数にしておくべきですね。。
(変数の対義語をリテラルって言うんですね)
勉強になりましたm(__)m
はじめまして、こんにちは。
ボット開発を始めた初心者で、このようなサイトがあることに非常に感謝しています。とにかく体に叩き込むようにして1から見ながら試行錯誤していますが、こちらのページにあるソース
=============
import requests
response = requests.get(“https://api.cryptowat.ch/markets/bitflyer/btcjpy/ohlc?periods=86400&after=1514764800”)
data = response.json()
for item in data[“result”][“86400”]:
if item[0] == 1516147200:
print(item[3])
=============
でAnacondaプロンプト画面で実行したら
1049900
と出ました。ページにある最安値の画像では1015000と異なったため、私のやり方がおかしいのかもしれないので気になって投稿しました。
リョータさん 野口 剛司さん こんにちは。
趣味でNumeraiなどやっているものですが、ふらっと見つけて読んでいます。とても丁寧で勉強になります。
さて野口さんのおっしゃるように結果は104990で間違いないようです。もしかしたらAdjustが入って執筆当時と価格が変わっているのかもしれません。ブラウザでAPI部分を呼び出した価格で確認できると思います。
またプログラムもitem[2]やitem[4]“として両脇の価格(高値、終値)を確認してりしても良いかもしれません。
それでは失礼します。
はじめまして。こんばんは。
野口さんの疑問点。
私も1049900になりました。
やり方は、間違っていないと思います。