pandasで時系列データのOHLC(四本値)を算出・ダウンサンプリング
pandasで株価や為替などの時系列データから特定の期間のOHLC(四本値: 始値、高値、安値、終値)やOHLCV(OHLC + 出来高)を算出したりダウンサンプリングしたりする方法について、以下の内容を説明する。
- OHLCとOHLCV
- 終値・始値や歩み値(ティック)からOHLC, OHLCVを算出
- OHLC, OHLCVデータをダウンサンプリング
OHLCデータからローソク足チャートを作成する方法は以下の記事を参照。
OHLCとOHLCV
OHLCはある期間(日、週、月など)における以下の4つの値の総称。
- Open: 始値
- High: 高値
- Low: 安値
- Close: 終値
四本値とも呼ばれる。
OHLCに出来高(Volume)を加えたものをOHLCVと呼ぶ。
終値・始値や歩み値(ティック)からOHLC, OHLCVを算出
株価などの終値・始値や歩み値(ティック)データからOHLC, OHLCVを算出するにはresample()
およびohlc()
, sum()
を使う。
ohlc()
, sum()
はpandas.DataFrame
からではなく、resample()
の返り値から更に呼び出す。
以下の簡単な日次データを例とする。
import pandas as pd
dates = pd.date_range('2018-08-01', '2018-08-31', freq='B')
df = pd.DataFrame({'price': dates.day, 'volume': dates.day * 10}, index=dates)
print(df)
# price volume
# 2018-08-01 1 10
# 2018-08-02 2 20
# 2018-08-03 3 30
# 2018-08-06 6 60
# 2018-08-07 7 70
# 2018-08-08 8 80
# 2018-08-09 9 90
# 2018-08-10 10 100
# 2018-08-13 13 130
# 2018-08-14 14 140
# 2018-08-15 15 150
# 2018-08-16 16 160
# 2018-08-17 17 170
# 2018-08-20 20 200
# 2018-08-21 21 210
# 2018-08-22 22 220
# 2018-08-23 23 230
# 2018-08-24 24 240
# 2018-08-27 27 270
# 2018-08-28 28 280
# 2018-08-29 29 290
# 2018-08-30 30 300
# 2018-08-31 31 310
print(df.index)
# DatetimeIndex(['2018-08-01', '2018-08-02', '2018-08-03', '2018-08-06',
# '2018-08-07', '2018-08-08', '2018-08-09', '2018-08-10',
# '2018-08-13', '2018-08-14', '2018-08-15', '2018-08-16',
# '2018-08-17', '2018-08-20', '2018-08-21', '2018-08-22',
# '2018-08-23', '2018-08-24', '2018-08-27', '2018-08-28',
# '2018-08-29', '2018-08-30', '2018-08-31'],
# dtype='datetime64[ns]', freq='B')
インデックス列がDatetimeIndex
の時系列データとなっている必要がある。例では新規にpandas.DataFrame
を作成しているが、既存のpandas.DataFrame
を時系列データとして扱う方法については以下の記事を参照。
freq='B'
は営業日(月 - 金)を表している。
この日次データから週次のOHLC, OHLCVデータを算出する。
時系列データをresample()
でリサンプリングしohlc()
を適用する。resample()
の引数freq
に渡す頻度コードによってOHLCを算出する期間を指定できる。
ここでは価格を表す列に対して処理を行う。
print(df['price'].resample('W').ohlc())
# open high low close
# 2018-08-05 1 3 1 3
# 2018-08-12 6 10 6 10
# 2018-08-19 13 17 13 17
# 2018-08-26 20 24 20 24
# 2018-09-02 27 31 27 31
週次の期間コードW
は結果のインデックス列(ラベル)がデフォルトで期間の最終日になる。引数label
, closed
を'left'
とすると期間の開始日に変更できる。月曜始まりにする例は以下の通り。
print(df['price'].resample('W-MON', label='left', closed='left').ohlc())
# open high low close
# 2018-07-30 1 3 1 3
# 2018-08-06 6 10 6 10
# 2018-08-13 13 17 13 17
# 2018-08-20 20 24 20 24
# 2018-08-27 27 31 27 31
OHLCVのV(出来高)は出来高を表す列の期間ごとの合計をsum()
で算出する。
print(df['volume'].resample('W').sum())
# 2018-08-05 60
# 2018-08-12 400
# 2018-08-19 750
# 2018-08-26 1100
# 2018-09-02 1450
# Freq: W-SUN, Name: volume, dtype: int64
print(df['volume'].resample('W-MON', label='left', closed='left').sum())
# 2018-07-30 60
# 2018-08-06 400
# 2018-08-13 750
# 2018-08-20 1100
# 2018-08-27 1450
# Freq: W-MON, Name: volume, dtype: int64
OHLCVをまとめて取得するにはpd.concat()
での連結やassign()
での新規列追加を行う。
print(pd.concat([df['price'].resample('W-MON', label='left', closed='left').ohlc(),
df['volume'].resample('W-MON', label='left', closed='left').sum()], axis=1))
# open high low close volume
# 2018-07-30 1 3 1 3 60
# 2018-08-06 6 10 6 10 400
# 2018-08-13 13 17 13 17 750
# 2018-08-20 20 24 20 24 1100
# 2018-08-27 27 31 27 31 1450
print(df['price'].resample('W-MON', label='left', closed='left').ohlc()
.assign(volume=df['volume'].resample('W-MON', label='left', closed='left').sum()))
# open high low close volume
# 2018-07-30 1 3 1 3 60
# 2018-08-06 6 10 6 10 400
# 2018-08-13 13 17 13 17 750
# 2018-08-20 20 24 20 24 1100
# 2018-08-27 27 31 27 31 1450
例ではprint()
の括弧の中なので.assign()
の前で改行しているが、括弧の外で実行する場合は.assign()
の前で改行するとエラーになるので注意(前の行の末尾にバックスラッシュ\
を入れれば改行可能)。
pd.concat()
で連結する場合はインデックスが揃っていないとダメだが、assign()
で新規の列を追加する場合は要素数が合っていればリストやNumPy配列ndarray
でもOK。インデックスが異なっていてもvalues
属性でnumpy.ndarray
を取得して追加できる。
print(pd.concat([df['price'].resample('W-MON', label='left', closed='left').ohlc(),
df['volume'].resample('W').sum()], axis=1))
# open high low close volume
# 2018-07-30 1.0 3.0 1.0 3.0 NaN
# 2018-08-05 NaN NaN NaN NaN 60.0
# 2018-08-06 6.0 10.0 6.0 10.0 NaN
# 2018-08-12 NaN NaN NaN NaN 400.0
# 2018-08-13 13.0 17.0 13.0 17.0 NaN
# 2018-08-19 NaN NaN NaN NaN 750.0
# 2018-08-20 20.0 24.0 20.0 24.0 NaN
# 2018-08-26 NaN NaN NaN NaN 1100.0
# 2018-08-27 27.0 31.0 27.0 31.0 NaN
# 2018-09-02 NaN NaN NaN NaN 1450.0
print(df['price'].resample('W-MON', label='left', closed='left').ohlc()
.assign(volume=df['volume'].resample('W').sum().values))
# open high low close volume
# 2018-07-30 1 3 1 3 60
# 2018-08-06 6 10 6 10 400
# 2018-08-13 13 17 13 17 750
# 2018-08-20 20 24 20 24 1100
# 2018-08-27 27 31 27 31 1450
歩み値(ティック)データの場合
上の例のような日次データではなく、取引時刻と取引数量からなる歩み値(ティック)データの場合もやり方は同じ。
時刻のデータをpd.to_datetime()
などで変換しDatetimeIndex
としてインデックスに指定し、resample()
の頻度コードをH
(時間)、T
(分)、S
(秒)などで指定する。例えば5分足のOHLCを算出したい場合は'5T'
とすればよい。
OHLC, OHLCVデータをダウンサンプリング
既存のOHLC, OHLCVデータをより長い期間にダウンサンプリングするには注意が必要。簡易的には終値や始値に対してOHLC, OHLCVを算出してもよいが、その場合はザラ場高値やザラ場安値の情報が失われてしまう。
ここではpandas-datareaderで取得して保存したApple(AAPL)の株価データを例とする。
pd.read_csv()
でparse_dates=True
として時系列データとして読み込み、さらに['2017']
で2017年のデータを抽出する。
import pandas as pd
df = pd.read_csv('data/src/aapl_2015_2019.csv', index_col=0, parse_dates=True)['2017']
print(df)
# open high low close volume
# 2017-01-03 115.80 116.3300 114.760 116.15 28781865
# 2017-01-04 115.85 116.5100 115.750 116.02 21118116
# 2017-01-05 115.92 116.8642 115.810 116.61 22193587
# 2017-01-06 116.78 118.1600 116.470 117.91 31751900
# 2017-01-09 117.95 119.4300 117.940 118.99 33561948
# ... ... ... ... ... ...
# 2017-12-22 174.68 175.4240 174.500 175.01 16052615
# 2017-12-26 170.80 171.4700 169.679 170.57 32968167
# 2017-12-27 170.10 170.7800 169.710 170.60 21672062
# 2017-12-28 171.00 171.8500 170.480 171.08 15997739
# 2017-12-29 170.52 170.5900 169.220 169.23 25643711
#
# [251 rows x 5 columns]
resample()
のあとohlc()
ではなくagg()
メソッドに{列名: 関数}
の辞書を指定することで各列にそれぞれ異なる処理を適用できる。
既存のOHLCをダウンサンプリングするにはそれぞれ以下のような関数を適用すればよい。
- open:
first
(期間中の最初の値を返す) - high:
max
(期間中の最大値を返す) - low:
min
(期間中の最小値を返す) - close:
last
(期間中の最後の値を返す)
組み込み関数やNumPyの関数などを呼び出し可能オブジェクトとして指定することもできるが、ここではそれぞれの関数名を文字列で指定する。
open
やhigh
などは元のpandas.DataFrame
の列名に合わせる。
頻度コードによって様々な期間にダウンサンプリング可能。月、四半期、2週間の例を示す。
d_ohlc = {'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last'}
print(df.resample('MS').agg(d_ohlc))
# open high low close
# 2017-01-01 115.80 122.4400 114.76 121.35
# 2017-02-01 127.03 137.4800 127.01 136.99
# 2017-03-01 137.89 144.5000 137.05 143.66
# 2017-04-01 143.71 145.4600 140.06 143.65
# 2017-05-01 145.10 156.6500 144.27 152.76
# 2017-06-01 153.17 155.9800 142.20 144.02
# 2017-07-01 144.88 153.9900 142.41 148.73
# 2017-08-01 149.10 164.5200 148.41 164.00
# 2017-09-01 164.80 164.9400 149.16 154.12
# 2017-10-01 154.26 169.6499 152.46 169.04
# 2017-11-01 169.87 176.2400 165.28 171.85
# 2017-12-01 169.95 177.2000 166.46 169.23
print(df.resample('QS').agg(d_ohlc))
# open high low close
# 2017-01-01 115.80 144.50 114.76 143.66
# 2017-04-01 143.71 156.65 140.06 144.02
# 2017-07-01 144.88 164.94 142.41 154.12
# 2017-10-01 154.26 177.20 152.46 169.23
print(df.resample('2W-MON', closed='left', label='left').agg(d_ohlc))
# open high low close
# 2017-01-02 115.800 119.9300 114.7600 119.04
# 2017-01-16 118.340 122.4400 118.2200 121.95
# 2017-01-30 120.930 132.9400 120.6200 132.12
# 2017-02-13 133.080 137.4800 132.7500 136.66
# 2017-02-27 137.140 140.2786 136.2800 139.14
# 2017-03-13 138.850 142.8000 138.8200 140.64
# 2017-03-27 139.390 145.4600 138.6200 143.34
# 2017-04-10 143.600 143.8792 140.0600 142.27
# 2017-04-24 143.500 148.9800 143.1800 148.96
# 2017-05-08 149.030 156.6500 149.0300 153.06
# 2017-05-22 154.000 155.4500 152.2200 155.45
# 2017-06-05 154.340 155.9800 142.2000 142.27
# 2017-06-19 143.660 148.2800 142.2800 144.02
# 2017-07-03 144.880 149.3300 142.4100 149.04
# 2017-07-17 148.820 153.9900 147.3000 149.50
# 2017-07-31 149.900 161.8300 148.1300 157.48
# 2017-08-14 159.320 162.5100 155.1101 159.86
# 2017-08-28 160.140 164.9400 158.5300 158.63
# 2017-09-11 160.500 163.9600 150.5600 151.89
# 2017-09-25 149.990 155.4900 149.1600 155.30
# 2017-10-09 155.810 160.8700 155.0200 156.25
# 2017-10-23 156.890 174.2600 155.2700 172.50
# 2017-11-06 172.365 176.2400 168.3800 170.15
# 2017-11-20 170.290 175.5000 167.1600 171.05
# 2017-12-04 172.480 174.1700 166.4600 173.97
# 2017-12-18 174.880 177.2000 169.2200 169.23
なお、agg()
とaggregate()
, apply()
は等価。どれでも同じ。
- pandas.core.resample.Resampler.aggregate — pandas 0.23.4 documentation
- pandas.core.resample.Resampler.apply — pandas 0.23.4 documentation
OHLCVの場合は、
- volume:
sum
(期間中の合計値を返す)
を追加すればOK。
d_ohlcv = {'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'}
print(df.resample('MS').agg(d_ohlcv))
# open high low close volume
# 2017-01-01 115.80 122.4400 114.76 121.35 563331160
# 2017-02-01 127.03 137.4800 127.01 136.99 574968547
# 2017-03-01 137.89 144.5000 137.05 143.66 562091214
# 2017-04-01 143.71 145.4600 140.06 143.65 371280180
# 2017-05-01 145.10 156.6500 144.27 152.76 635292989
# 2017-06-01 153.17 155.9800 142.20 144.02 664986406
# 2017-07-01 144.88 153.9900 142.41 148.73 411377229
# 2017-08-01 149.10 164.5200 148.41 164.00 638221161
# 2017-09-01 164.80 164.9400 149.16 154.12 669594016
# 2017-10-01 154.26 169.6499 152.46 169.04 496135305
# 2017-11-01 169.87 176.2400 165.28 171.85 581876496
# 2017-12-01 169.95 177.2000 166.46 169.23 518560008