In [1]:
%matplotlib inline
import numpy as np
import pandas as pd
from pandas import DataFrame
import matplotlib.pyplot as plt
import requests

まずは実験に使うデータを取ってきます(´・ω・`)

In [2]:
url_executions = 'https://api.bitflyer.jp/v1/getexecutions'
before = None
data = []

for i in range(5):
    url = url_executions + '?count=500' + ('&before={}'.format(before) if before else '')
    data.extend(requests.get(url).json())
    before = data[-1]['id']

tick = DataFrame(data).set_index('exec_date').sort_values('id')['price']
tick.index = pd.to_datetime(tick.index).tz_localize('UTC').tz_convert('Asia/Tokyo')
print('shape', tick.shape)
tick.plot(figsize=(15,3))
shape (2500,)
Out[2]:
<matplotlib.axes._subplots.AxesSubplot at 0x9b8fef0>

tickのSeriesからm1, m5, m15のDataFrameを作ります(´・ω・`)

In [3]:
def resample(tick, freq='T', **kwargs):
    df = tick.resample(freq, **kwargs).ohlc()
    df.columns = df.columns.map(str.capitalize)
    df.index.name = 'Date'
    return df

m1 = resample(tick, freq='T').dropna()
m5 = resample(tick, freq='5T').dropna()
m15 = resample(tick, freq='15T').dropna()

print('m1 len', m1.shape[0])
print('m5 len', m5.shape[0])
print('m15 len', m15.shape[0])
m1 len 88
m5 len 19
m15 len 7

m5のインデックスがm15だと何番目か、の配列を取得

※ MT4でいうところのiBarShift()的な目的

In [4]:
idx = m15.index.searchsorted(m5.index, side='right')-1
print(idx)
[0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6]

これだとこんな感じ

In [5]:
df = pd.concat([m5, m15.iloc[idx].set_index(m5.index)], axis=1)
df.columns = 'o h l c m15_o m15_h m15_l m15_c'.split()
df.head(10)
Out[5]:
o h l c m15_o m15_h m15_l m15_c
Date
2017-10-09 15:05:00+09:00 510000.0 510000.0 509993.0 509993.0 510000.0 510000.0 507780.0 508282.0
2017-10-09 15:10:00+09:00 510000.0 510000.0 507780.0 508282.0 510000.0 510000.0 507780.0 508282.0
2017-10-09 15:15:00+09:00 508274.0 508969.0 507244.0 508138.0 508274.0 508969.0 507244.0 508422.0
2017-10-09 15:20:00+09:00 508021.0 508969.0 508021.0 508605.0 508274.0 508969.0 507244.0 508422.0
2017-10-09 15:25:00+09:00 508604.0 508899.0 508093.0 508422.0 508274.0 508969.0 507244.0 508422.0
2017-10-09 15:30:00+09:00 508422.0 508500.0 507330.0 507330.0 508422.0 508500.0 506695.0 507890.0
2017-10-09 15:35:00+09:00 507300.0 507548.0 506700.0 506859.0 508422.0 508500.0 506695.0 507890.0
2017-10-09 15:40:00+09:00 506859.0 507890.0 506695.0 507890.0 508422.0 508500.0 506695.0 507890.0
2017-10-09 15:45:00+09:00 507890.0 508150.0 507440.0 507479.0 507890.0 508150.0 506380.0 506780.0
2017-10-09 15:50:00+09:00 507600.0 507800.0 506380.0 506793.0 507890.0 508150.0 506380.0 506780.0

バックテストなどを行うときは、長いほうの足は確定済みの値を参照したい、という状況もあると思います。
(実際そうしないと上位足の H L C はその時点から見ると未来の情報を含んでしまったデータになっているので)
なので、こんな感じ?

In [6]:
fixbarmode = True
idx = m15.index.searchsorted(m5.index, side='right')-(1+int(fixbarmode))

m5idxm15 = m15.iloc[idx].set_index(m5.index)
m5idxm15.iloc[idx<0] = np.nan

df = pd.concat([m5, m5idxm15], axis=1)
df.columns = 'o h l c m15_o m15_h m15_l m15_c'.split()

df.head(10)
Out[6]:
o h l c m15_o m15_h m15_l m15_c
Date
2017-10-09 15:05:00+09:00 510000.0 510000.0 509993.0 509993.0 NaN NaN NaN NaN
2017-10-09 15:10:00+09:00 510000.0 510000.0 507780.0 508282.0 NaN NaN NaN NaN
2017-10-09 15:15:00+09:00 508274.0 508969.0 507244.0 508138.0 510000.0 510000.0 507780.0 508282.0
2017-10-09 15:20:00+09:00 508021.0 508969.0 508021.0 508605.0 510000.0 510000.0 507780.0 508282.0
2017-10-09 15:25:00+09:00 508604.0 508899.0 508093.0 508422.0 510000.0 510000.0 507780.0 508282.0
2017-10-09 15:30:00+09:00 508422.0 508500.0 507330.0 507330.0 508274.0 508969.0 507244.0 508422.0
2017-10-09 15:35:00+09:00 507300.0 507548.0 506700.0 506859.0 508274.0 508969.0 507244.0 508422.0
2017-10-09 15:40:00+09:00 506859.0 507890.0 506695.0 507890.0 508274.0 508969.0 507244.0 508422.0
2017-10-09 15:45:00+09:00 507890.0 508150.0 507440.0 507479.0 508422.0 508500.0 506695.0 507890.0
2017-10-09 15:50:00+09:00 507600.0 507800.0 506380.0 506793.0 508422.0 508500.0 506695.0 507890.0

初めの部分は、一つ前の確定済みのM15を取得することができないのでそのぶぶんはnanで埋めました

これは、
m15.index.searchsorted(m5.index, side='right')-(1+int(fixbarmode))
で負数になる部分があるので
このままiloc[idx]とすると、負数の部分は末尾からの価格データとなってしまうので
見やすいように idx<0 の箇所をnanにしました。

上記の例だと確認のためにpd.concatしたデータフレームを表示してみましたが、実際に使うときなんかだと、
算出したインデックスでその位置の値を参照する感じで使うことになりますかね(´・ω・`)

上位足の4本値の参照に限らず、上位足のインジケータの値や、経済指標のような
価格データと間隔の違うデータの対応する値を取得するときになんかも使えそうな関数ですな

In [ ]:
 

M1の位置の確定済みのM5、M15の終値に対応できてるかチェック(´・ω・`)

In [7]:
def ibarshift(upper_tf_data, idx, fixbarmode=True):
    idxmask = upper_tf_data.index.searchsorted(idx, side='right')-(1+int(fixbarmode))
    data = upper_tf_data.iloc[idxmask].copy()
    data.iloc[idxmask<0] = np.nan
    return data

mtfdf = m1[['Close']].copy()
mtfdf['m5Close_Shift1'] = ibarshift(m5, m1.index, True)['Close'].values
mtfdf['m15Close_Shift1'] = ibarshift(m15, m1.index, True)['Close'].values

mtfdf.plot(figsize=(15,5))
for i in m15.index: plt.axvline(i, lw=0.5, c='k')
for i in m5.index: plt.axvline(i, lw=0.5, c='k', ls=':')

(´・ω・`)OK

shiftさせなかったら以下のように上位足がM1の時刻より先の価格データを持つことになるのでやばい(´・ω・`)
※ インデックスが足の開始時刻というタイプのデータの場合です

In [8]:
mtfdf = m1[['Close']].copy()
mtfdf['m5Close_Shift0'] = ibarshift(m5, m1.index, False)['Close'].values
mtfdf['m15Close_Shift0'] = ibarshift(m15, m1.index, False)['Close'].values

mtfdf.plot(figsize=(15,5))
for i in m15.index: plt.axvline(i, lw=0.5, c='k')
for i in m5.index: plt.axvline(i, lw=0.5, c='k', ls=':')