シストレどうですか

  Algorithmic Trading for Dummies

OANDA API 解説編 第4回 ろうそく足を取得してみた (2)

今回はOANDA APIで取得したろうそく足をpandasのDataFrameに変換する方法を試してみました。

1. リストを使ってDataFrameに変換する


まずは単純に配列を使ってDataFrameに変換する方法を試してみました。 一番簡単なのはレートの部分だけを取り出すことなのでまずはこれから。
ろうそく足のフォーマットは、

{
  "instrument": "USD_JPY",
  "granularity": "M5",
  "candles": [
    {
      "complete": true,
      "volume": 127,
      "time": "2020-07-28T16:35:00.000000000Z",
      "mid": {
        "o": "105.086",
        "h": "105.086",
        "l": "105.063",
        "c": "105.076"
      }
    },

なので、レートの部分だけを考えれば"mid"以下の部分を取り出せばよいことになりますので、"candles"の部分をループさせて"mid"のところをリストに追加します。

# 必要なモジュールの読み込み
import requests
import json
import pandas as pd    👈追加

# 口座情報の設定 (口座の開設の仕方はググってね)
API_Token = '********************************-********************************'
API_AccountID = '999-999-99999999-999'

# URLの設定 (デモ口座用非ストリーミングURL)
API_URL =  "https://api-fxpractice.oanda.com"

# 通貨ペア
INSTRUMENT = "USD_JPY"

# <ろうそく足取得用URLの変数の設定>
# /v3/instruments/{Account ID}/candles 
url = API_URL + "/v3/instruments/%s/candles?count=5&price=M&granularity=M5" % INSTRUMENT 

# ヘッダー情報の変数の設定
headers = {
                "Authorization" : "Bearer " + API_Token
         }

# サーバーへの要求
response = requests.get(url, headers=headers)

# 処理結果の編集
Response_Body = response.json()
#
# ------------------------------------  👈ここから
#DataFrameへ変換   
list=[]
for row in Response_Body['candles']:   
      list.append(row['mid'])
      df = pd.DataFrame(list)
# ------------------------------------  👈ここまで
#
print(df)

出力結果

         o        h        l        c
0  104.968  104.971  104.948  104.948
1  104.950  104.952  104.910  104.912
2  104.911  104.947  104.911  104.946
3  104.948  104.952  104.916  104.920
4  104.926  104.926  104.919  104.919
5  104.924  104.960  104.924  104.945
6  104.950  104.956  104.950  104.956
7  104.960  104.960  104.946  104.946
8  104.950  104.950  104.926  104.928
9  104.942  104.959  104.935  104.951

せっかくですからforループを使わずにリスト内包表記を使って一行で表現してみます。

df = pd.DataFrame(row['mid'] for row in Response_Body['candles']) 


次に、やはりレートの部分だけをとりだしてもわかりずらいので、その他のあったら便利な部分も全部変換したいと思います。

list=[]
for row in Response_Body['candles']:
    list.append([Response_Body['instrument'], Response_Body['granularity'], row['complete'], row['volume'], row['time'], row['mid']['o'], row['mid']['h'], row['mid']['l'], row['mid']['c']])
print(df)                                                                                                                                                         

同様にリスト内包表記で表現すると

df = pd.DataFrame([Response_Body['instrument'], Response_Body['granularity'], row['complete'], row['volume'], row['time'], row['mid']['o'], row['mid']['h'],row['mid']['l'], row['mid']['c']] for row in Response_Body['candles']) 

一行にはなりますが、どちらの方法にせよ値を抽出する部分が長くなってしまいますね。

         0   1      2   3                               4        5        6        7        8
0  USD_JPY  M5   True  53  2020-07-29T22:00:00.000000000Z  104.934  104.939  104.910  104.928
1  USD_JPY  M5   True  40  2020-07-29T22:05:00.000000000Z  104.926  104.948  104.921  104.944
2  USD_JPY  M5   True  46  2020-07-29T22:10:00.000000000Z  104.946  104.958  104.946  104.957
3  USD_JPY  M5   True  61  2020-07-29T22:15:00.000000000Z  104.958  104.960  104.956  104.959
4  USD_JPY  M5   True  58  2020-07-29T22:20:00.000000000Z  104.958  104.961  104.956  104.957
5  USD_JPY  M5   True  44  2020-07-29T22:25:00.000000000Z  104.956  104.962  104.950  104.957
6  USD_JPY  M5   True  37  2020-07-29T22:30:00.000000000Z  104.959  104.977  104.958  104.977
7  USD_JPY  M5   True  47  2020-07-29T22:35:00.000000000Z  104.975  104.976  104.962  104.963
8  USD_JPY  M5   True  40  2020-07-29T22:40:00.000000000Z  104.962  104.963  104.950  104.954
9  USD_JPY  M5  False   6  2020-07-29T22:45:00.000000000Z  104.952  104.953  104.946  104.946

必要な値がすべて表示されましたが、ヘッダー部分が番号に置き換わってしまいました。
というわけなので、必要であればcolumnsでヘッダーに名前をつけてあげましょう。

df.columns = ['pair', 'ashi', 'complete', 'volume', 'time_UTC', 'open', 'high', 'low', 'close']
      pair ashi  complete  volume                        time_UTC     open     high      low    close
0  USD_JPY   M5      True      53  2020-07-29T22:00:00.000000000Z  104.934  104.939  104.910  104.928
1  USD_JPY   M5      True      40  2020-07-29T22:05:00.000000000Z  104.926  104.948  104.921  104.944
2  USD_JPY   M5      True      46  2020-07-29T22:10:00.000000000Z  104.946  104.958  104.946  104.957
3  USD_JPY   M5      True      61  2020-07-29T22:15:00.000000000Z  104.958  104.960  104.956  104.959
4  USD_JPY   M5      True      58  2020-07-29T22:20:00.000000000Z  104.958  104.961  104.956  104.957
5  USD_JPY   M5      True      44  2020-07-29T22:25:00.000000000Z  104.956  104.962  104.950  104.957
6  USD_JPY   M5      True      37  2020-07-29T22:30:00.000000000Z  104.959  104.977  104.958  104.977
7  USD_JPY   M5      True      47  2020-07-29T22:35:00.000000000Z  104.975  104.976  104.962  104.963
8  USD_JPY   M5      True      40  2020-07-29T22:40:00.000000000Z  104.962  104.963  104.950  104.954
9  USD_JPY   M5     False       7  2020-07-29T22:45:00.000000000Z  104.952  104.953  104.946  104.947

これで見やすくなりました。

2. json_normalizeを使ってDataFrameに変換する


変換したい項目が増えてくるとだらだらと長くなるのでどうにかならないかと思って調べたところ、pandasにはjson_normailizeという便利な関数があることがわかりましたので、これを使った方法で変換してみました。参考にしたサイトはこちら

note.nkmk.me

まずは同様に簡単そうなレートの部分だけを変換してみたいと思います。 1.のpythonコードのDataFrameへの変換部分を以下の命令に置き換えると

df = pd.json_normalize(Response_Body['candles'])

簡単に変換できた上にVolumeなども同時に変換できました!(^^)!

   complete  volume                            time    mid.o    mid.h    mid.l    mid.c
0      True     123  2020-07-30T23:50:00.000000000Z  104.696  104.698  104.662  104.668
1      True     224  2020-07-30T23:55:00.000000000Z  104.668  104.689  104.666  104.688
2      True     798  2020-07-31T00:00:00.000000000Z  104.689  104.700  104.554  104.596
3      True     404  2020-07-31T00:05:00.000000000Z  104.598  104.618  104.566  104.608
4      True     442  2020-07-31T00:10:00.000000000Z  104.606  104.606  104.528  104.542
5      True     316  2020-07-31T00:15:00.000000000Z  104.544  104.562  104.510  104.532
6      True     329  2020-07-31T00:20:00.000000000Z  104.530  104.575  104.522  104.572
7      True     426  2020-07-31T00:25:00.000000000Z  104.574  104.600  104.530  104.558
8      True     459  2020-07-31T00:30:00.000000000Z  104.560  104.598  104.522  104.528
9     False      83  2020-07-31T00:35:00.000000000Z  104.526  104.547  104.522  104.547

ここまでくると通貨ペアや足についても一度に変換したくなります。 (固定値なので列を追加しても同じ事なのですが・・・)

df = pd.json_normalize(Response_Body, record_path='candles', meta=['instrument', 'granularity'], sep='_')

上のような命令に変更すれば、

   complete  volume                            time    mid_o    mid_h    mid_l    mid_c instrument granularity
0      True     378  2020-07-31T00:40:00.000000000Z  104.592  104.594  104.516  104.529    USD_JPY          M5
1      True     459  2020-07-31T00:45:00.000000000Z  104.530  104.571  104.478  104.550    USD_JPY          M5
2      True     788  2020-07-31T00:50:00.000000000Z  104.549  104.606  104.506  104.578    USD_JPY          M5
3      True     514  2020-07-31T00:55:00.000000000Z  104.580  104.584  104.522  104.552    USD_JPY          M5
4      True     334  2020-07-31T01:00:00.000000000Z  104.554  104.580  104.550  104.558    USD_JPY          M5
5      True     249  2020-07-31T01:05:00.000000000Z  104.559  104.592  104.540  104.540    USD_JPY          M5
6      True     180  2020-07-31T01:10:00.000000000Z  104.539  104.545  104.499  104.500    USD_JPY          M5
7      True     293  2020-07-31T01:15:00.000000000Z  104.501  104.509  104.466  104.468    USD_JPY          M5
8      True     295  2020-07-31T01:20:00.000000000Z  104.470  104.496  104.458  104.462    USD_JPY          M5
9     False      29  2020-07-31T01:25:00.000000000Z  104.460  104.463  104.456  104.460    USD_JPY          M5

たった一行ですべてのろうそく足の情報を変換することができます。json_normalize万歳\(^o^)/

3. DataFrame内の時刻を変換してみる


最後にOANDA APIで取得したろうそく足の時間のフォーマットそのままでは見にくいので、これをいろいろな形に変更してみたいと思います。

時刻をUTC時間帯から任意の時間帯に変換したい場合は変換したい列をdatetime形式に変換したあとtz_convert関数で時間帯を変更してあげるとよいみたいです。 下の例では"time_JST"という新しい列を追加して、そこに日本時間がセットされるようにします。

df['time_JST'] = pd.to_datetime(df['time']).dt.tz_convert('Asia/Tokyo')
   complete  volume                            time    mid_o    mid_h    mid_l    mid_c instrument granularity                  time_JST
0      True     559  2020-07-31T16:50:00.000000000Z  105.886  105.916  105.874  105.874    USD_JPY          M5 2020-08-01 01:50:00+09:00
1      True     577  2020-07-31T16:55:00.000000000Z  105.876  105.896  105.833  105.837    USD_JPY          M5 2020-08-01 01:55:00+09:00
2      True     325  2020-07-31T17:00:00.000000000Z  105.835  105.857  105.825  105.830    USD_JPY          M5 2020-08-01 02:00:00+09:00

列の最後に日本時間に変換された新しい”time_JST"列が追加されました。 ちなみに指定できる時間帯の引数一覧はここにありますので、必要に応じて指定してあげてください。
さらにもっとフォーマットしたい人は.dt.strftime()を加える事によってより自分の見やすい形に編集できます。

df['time_JST'] = pd.to_datetime(df['time']).dt.tz_convert('Japan').dt.strftime('%Y-%m-%d %H:%M %Z')

であれば、こんな感じになります。

   complete  volume                            time    mid_o    mid_h    mid_l    mid_c instrument granularity              time_JST
0      True      68  2020-07-31T20:10:00.000000000Z  105.856  105.891  105.856  105.886    USD_JPY          M5  2020-08-01 05:10 JST
1      True      71  2020-07-31T20:15:00.000000000Z  105.885  105.906  105.864  105.906    USD_JPY          M5  2020-08-01 05:15 JST
2      True      50  2020-07-31T20:20:00.000000000Z  105.905  105.905  105.882  105.899    USD_JPY          M5  2020-08-01 05:20 JST

列の順番も並べ変えて不要な列も落としたい場合は、loc, iloc, []を使えば自由に編集できます。
ilocを使った例として、

df = df.iloc[:,[7,8,9,0,1,3,4,5,6]]

のように好きな順序で不要な列のindexを除けば完成です。

  instrument granularity              time_JST  complete  volume    mid_o    mid_h    mid_l    mid_c
0    USD_JPY          M5  2020-08-01 05:10 JST      True      68  105.856  105.891  105.856  105.886
1    USD_JPY          M5  2020-08-01 05:15 JST      True      71  105.885  105.906  105.864  105.906
2    USD_JPY          M5  2020-08-01 05:20 JST      True      50  105.905  105.905  105.882  105.899



まとめ

pandasを使うとデータの加工がとても簡単になりますね。
というわけですので、次回はろうそく足のデータをどのようにシンプルな形でグラフ化できるか考えてみたいと思います。