【Python】OANDAのFX時系列データを分析しやすいデータに変換

FX投資
https://fxwalker.com/fx-co/oanda/
この記事は約11分で読めます。

FX時系列データを分析するための準備

FXの時系列データを統計的に、あるいは機械学習、深層学習を用いてデータ分析したいと考えた場合、OANDAから取得したままのデータではできません。

今回は、OANDAから取得したFX市場の時系列データをデータ分析しやすい形式に変換する方法を書いていきたいと思います。具体的にやりたいことは以下になります。

  • 日付データから年、月、日、時間、曜日データを取り出す
  • 米欧州で適用されているサマータイムの情報を取り出す

FX市場の動きは、期間、日時、そして米欧州の時間帯に大きく影響を受ける可能性があるので、それらを日時情報から抽出して、分析しやすいようにデータ形式を変えていきます。OANDAのデータを取得する方法はこちらの投稿を御覧ください。
PythonとOANDA APIを使ってFXの過去データを取得

時系列データの変換(年、月、日、時間、曜日、夏時間、冬時間の取得)

モジュールのインポート

まずはモジュールのインポートです。
今回は東京時間、ニューヨークの時間などを扱いたいので、pytz, tzlocalというモジュールをインポートしています。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import time
from datetime import datetime,timedelta
from pytz import timezone, utc
from tzlocal import get_localzone
import pickle

過去データの読み込み

FXの過去データの取得に関してはこちらを参照してください。
参照:PythonとOANDA APIを使ってFXの過去データを取得

pickleで保存しておいたデータをloadして、pandasのデータフレームに格納します。
通貨ペアはUSD_JPY。5分足のデータを用います。

with open("USD_JPY_M5_20100101-20190501.pickle", mode='rb') as f:
    df = pickle.load(f)

データの前処理

データを読み込むと以下のような形式になっています。

列名を時間、始値、高値、安値、終値、出来高の順番になるように変換します。(この内容は特に必須ではありません。)
また、始値〜出来高の値が文字列なので、数値に変換しています。
あと日付と時間の間に「T」の文字が入っていて邪魔なので省きます。また、OANDAで取得したデータはUTC(協定世界時)なので、datetime_utcという列名にします。

def prep(df):
    df.iloc[:,1:] = df.iloc[:,1:].astype(float) #文字列からfloatへの変換
    df = df.loc[:,['time','open','high','low','close','volume']] #ohlcへの変換
    df['datetime_utc'] = [datetime.strptime(i,'%Y-%m-%dT%H:%M:%S') for i in df['time']] #時系列データの変換
    df = df.drop('time',axis=1)
    return df

df1 = prep(df)

時系列インデックスの補完

OANDAのデータはなぜかたまに時系列が一部飛んでいることがあります。なので、5分足であればちゃんと5分刻みになるようにインデックスを補完する処理をします。

取得したデータの期間、インデックスが正しく5分刻みになっているデータフレームを作成し、そこに先程のデータをマージします。

def fill_index(df, gran):
    #取得したデータの期間を確認
    time_index = []
    time = df['datetime_utc'][0]
    time_range = int((df.iloc[-1,5] - df.iloc[0,5]).days * 24 * 60/gran) \
                    + int((df.iloc[-1,5] - df.iloc[0,5]).seconds/60/gran)

    #インデックスが正しく5分刻みになったデータフレームの作成
    for i in range(time_range):
        time_index.append(time)
        time += timedelta(minutes=gran) 
    df_ = pd.DataFrame()
    df_['datetime_utc'] = time_index
    
    return pd.merge(df_,df, on='datetime_utc',how='left') #データフレームをマージ

gran = 5 #5分足を使用
df2 = fill_index(df1, gran)

おそらく以下のようにところどころ欠損(NAN)が入ったデータが得られるはずです。本当は欠損を埋めたいところですが、良い方法も思いつかないので、今回は欠損のままとします。

時系列データの変換

この時系列データの処理が一番重要かなと思います。
utcの時間からjp_time(日本時間)とny_time(ニューヨーク時間)を得ます。

次にjp_timeからyear(年)、month(月)、day(日)、time(時:分:秒)、hour(時間)、weekday(月曜〜日曜:0〜6)のデータを生成します。

また、ny_timeからその時間がEDT(夏時間)からEST(冬時間)かを生成します。

あと、開始時間が少し中途半端なので、2010年1月10日〜2019年5月1日までにデータを限定しておきます。

def convert_datetime(buf):
    #日本時間とニューヨーク時間の取得
    utc = timezone('UTC')
    tokyo = get_localzone()
    ny = timezone('America/New_York')
    jp_time = list(map(lambda x: utc.localize(x).astimezone(tokyo), buf['datetime_utc']))
    ny_time = list(map(lambda x: utc.localize(x).astimezone(ny), buf['datetime_utc']))

    df = pd.DataFrame()
    for col in buf:
        df[col] = buf[col]
    df['datetime_jp'] = [v.strftime('%Y-%m-%d %H:%M:%S') for v in jp_time]
    df['year'] = [t.year for t in jp_time]
    df['month'] = [t.month for t in jp_time]
    df['day'] = [t.day for t in jp_time]
    df['time'] = [v.strftime('%H:%M:%S') for v in jp_time]
    df['hour'] = [t.hour for t in jp_time]
    df['weekday'] = [t.weekday() for t in jp_time]    
    df['EST_EDT'] = [v.strftime('%Y-%m-%d %H:%M:%S%Z')[-3:] for v in ny_time] #EDT = summer, EST = std
    return df

#時系列データの変換
df3 = convert_datetime(df2)

#2010年1月10日〜2019年5月1日までにデータを限定
df4 = df3[(df3['datetime_jp'] > '2010-01-05')&(df3['datetime_jp'] < '2019-05-01')].reset_index(drop=True)

変換すると以下のようなデータが得られると思います。
ここまでデータが変換できれば、あとは煮るなり焼くなり好きにしてくれという状態まできたかなと思います。

まとめコード

最後にまとめコード書いておきます。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import time
from datetime import datetime,timedelta
from pytz import timezone, utc
from tzlocal import get_localzone
import pickle

with open("USD_JPY_M5_20100101-20190501.pickle", mode='rb') as f:
    df = pickle.load(f)

def prep(df):
    df.iloc[:,1:] = df.iloc[:,1:].astype(float) #文字列からfloatへの変換
    df = df.loc[:,['time','open','high','low','close','volume']] #ohlcへの変換
    df['datetime_utc'] = [datetime.strptime(i,'%Y-%m-%dT%H:%M:%S') for i in df['time']] #時系列データの変換
    df = df.drop('time',axis=1)
    return df

def fill_index(df, gran):
    #取得したデータの期間を確認
    time_index = []
    time = df['datetime_utc'][0]
    time_range = int((df.iloc[-1,5] - df.iloc[0,5]).days * 24 * 60/gran) \
                    + int((df.iloc[-1,5] - df.iloc[0,5]).seconds/60/gran)

    #インデックスが正しく5分刻みになったデータフレームの作成
    for i in range(time_range):
        time_index.append(time)
        time += timedelta(minutes=gran) 
    df_ = pd.DataFrame()
    df_['datetime_utc'] = time_index
    
    return pd.merge(df_,df, on='datetime_utc',how='left') #データフレームをマージ

def convert_datetime(buf):
    #日本時間とニューヨーク時間の取得
    utc = timezone('UTC')
    tokyo = get_localzone()
    ny = timezone('America/New_York')
    jp_time = list(map(lambda x: utc.localize(x).astimezone(tokyo), buf['datetime_utc']))
    ny_time = list(map(lambda x: utc.localize(x).astimezone(ny), buf['datetime_utc']))

    df = pd.DataFrame()
    for col in buf:
        df[col] = buf[col]
    df['datetime_jp'] = [v.strftime('%Y-%m-%d %H:%M:%S') for v in jp_time]
    df['year'] = [t.year for t in jp_time]
    df['month'] = [t.month for t in jp_time]
    df['day'] = [t.day for t in jp_time]
    df['time'] = [v.strftime('%H:%M:%S') for v in jp_time]
    df['hour'] = [t.hour for t in jp_time]
    df['weekday'] = [t.weekday() for t in jp_time]    
    df['EST_EDT'] = [v.strftime('%Y-%m-%d %H:%M:%S%Z')[-3:] for v in ny_time] #EDT = summer, EST = std
    return df


df1 = prep(df)
gran = 5 #5分足を使用
df2 = fill_index(df1, gran)
#時系列データの変換
df3 = convert_datetime(df2)

#2010年1月10日〜2019年5月1日までにデータを限定
df4 = df3[(df3['datetime_jp'] > '2010-01-05')&(df3['datetime_jp'] < '2019-05-01')].reset_index(drop=True)
タイトルとURLをコピーしました