In [1]:
%matplotlib inline
import numpy as np
import pandas as pd
from pandas import DataFrame, Series
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')
In [2]:
hst_path = 'USDJPY5.hst'
with open(hst_path, 'rb') as f:
    dtype = [('Time','u8'), ('Open','f8'), ('High','f8'), ('Low','f8'),
             ('Close','f8'), ('Volume','i8'), ('s','i4'), ('r','i8')]
    df = pd.DataFrame(np.frombuffer(f.read()[148:], dtype=dtype).astype(dtype[:-3]))
    df = df.set_index(pd.to_datetime(df['Time'], unit='s')).drop('Time', axis=1)

print(len(df))
df['Close'].resample('D').last().dropna().plot(figsize=(15,5))
752980
Out[2]:
<matplotlib.axes._subplots.AxesSubplot at 0x98fcd68>

09:30~09:59間の値動きを見てみる

In [3]:
ret = df['Close'].pct_change().fillna(0).apply(np.log1p)
ret0930_0955 = ret[(ret.index.hour==9) & (ret.index.minute>=30)]
ret0930_0955.cumsum().plot(figsize=(15,5))
Out[3]:
<matplotlib.axes._subplots.AxesSubplot at 0xad6ea90>

ふむふむ(´・ω・`)

もう少し細かく見てみる
09:30からの5分間隔の足別に見てみる

In [4]:
for m in np.unique(ret0930_0955.index.minute):
    ret0930_0955[ret0930_0955.index.minute==m].cumsum().plot(figsize=(15,5))
plt.legend(np.unique(ret0930_0955.index.minute), loc='best', fontsize=12)
Out[4]:
<matplotlib.legend.Legend at 0xccc1ef0>

ふむふむ09:55からの5分間以外は上昇傾向にあるみたい

たぶん仲値の影響ですね(´・ω・`)
ついでにゴトー日の09:30~09:50部分とそうでないときのも見てみましょう

※青ラインがゴトー日、赤がそうでない日で、標本数で割った値を累積してます

In [5]:
ret_5_10 = ret[(ret.index.day%5==0) & (ret.index.hour==9) &
               (ret.index.minute>=30) & (ret.index.minute<55)]
ret_no_5_10 = ret[~(ret.index.day%5==0) & (ret.index.hour==9) &
                  (ret.index.minute>=30) & (ret.index.minute<55)]
(ret_5_10/len(ret_5_10)).cumsum().plot(figsize=(15,3), c='b')
(ret_no_5_10/len(ret_no_5_10)).cumsum().plot(figsize=(15,3), c='r')
plt.legend(['5,10', 'no 5,10'], loc='best', fontsize=14)
plt.show()

ret0930_0950 = ret[(ret.index.hour==9) & (ret.index.minute>=30) & (ret.index.minute<55)]
ret0930_0950.groupby(ret0930_0950.index.day).mean().plot.bar(figsize=(15,3))
Out[5]:
<matplotlib.axes._subplots.AxesSubplot at 0xd2d8550>

2016くらいからはっきりしてる感じはあるけどそれ以前は大差無い感じ?

09:30~09:50の足と09:55の足を分けて見てみる

In [6]:
ret0930_0955[ret0930_0955.index.minute<55].cumsum().plot(figsize=(15,5))
ret0930_0955[ret0930_0955.index.minute>=55].cumsum().plot(figsize=(15,5))
Out[6]:
<matplotlib.axes._subplots.AxesSubplot at 0xcd946a0>

ふむふむ(´・ω・`)

前回の記事で作ったおもちゃテスターでバックテストしてみます
09:30にLして09:55にドテンして10:00でSを決済します

In [7]:
class Tester(object):
    def __init__(self, df):
        self.I,_=df.index.values,[setattr(self,c[0],df[c].values)for c in df.columns]
    def run(S):
        P,T,V,r,(ob,os,cb,cs)=0,0,0,[],[(getattr(S,a))for a in['ob','os','cb','cs']]
        for i in range(len(S.I)-1):
            if(P==1and cb[i])or(P==-1and cs[i]):P=r.append([P,T,V,S.I[i+1],S.O[i+1]])
            if not P:P,T,V=(1if ob[i]else-1if os[i]else None),S.I[i+1],S.O[i+1]
        return DataFrame(r+([[P,T,V,S.I[-1],S.O[-1]]]if P else[]),None,'P ot op ct cp'.split())

class Test(Tester):
    def __init__(self, df):
        super(Test, self).__init__(df)
        hour = df.index.hour
        minute = df.index.minute
        
        # サインの次の足の始値でエントリするので25分、50分でサインを出して
        # 30分, 55分でエントリするようにします
        self.ob =  (hour==9) & (minute==25)
        self.os =  (hour==9) & (minute==50)
        self.cb =  self.os
        self.cs = (hour==9) & (minute==55)

spread = 0.00
res = Test(df).run()
res = Series((((res['cp']-res['op'])*res['P']-spread).values),
             index=pd.to_datetime(res['ct'],unit='ns'))
print(len(res))
print(res.sum())
res.cumsum().plot(figsize=(15,5))
5270
48.17500000000078
Out[7]:
<matplotlib.axes._subplots.AxesSubplot at 0xae06780>

ふむふむプラスです(´・ω・`)
とはいえこれはスプレッドを加味していない結果なので
上の例だと5270回分のスプレッドコストを引いてやらないといけないので
これだけじゃ微妙ですね

↑ わざわざテスターを使いましたがこの例だとこれ ↓ でいいですね(´・ω・`)

In [8]:
buy = Series((df['Open'][(df.index.hour==9)&(df.index.minute==55)].values) -
             (df['Open'][(df.index.hour==9)&(df.index.minute==30)].values),
             index = df.index[(df.index.hour==9)&(df.index.minute==55)])
sell = Series((df['Open'][(df.index.hour==9)&(df.index.minute==55)].values) -
              (df['Open'][(df.index.hour==10)&(df.index.minute==0)].values),
              index = df.index[(df.index.hour==10)&(df.index.minute==0)])

pd.concat([buy, sell]).sort_index().cumsum().plot(figsize=(15,5))
Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0xd843438>

東京時間全体の値動きで見ると ↓ こんな感じ(´・ω・`)

In [9]:
ret[(ret.index.hour>=9) & (ret.index.hour<15)].cumsum().plot(figsize=(15,5))
Out[9]:
<matplotlib.axes._subplots.AxesSubplot at 0xe25c4e0>

東京時間を常にSというのポジだとこんな感じ

クリスマスあたりにいくつか15:00の欠損値があったので
こんな感じで集計

In [10]:
tk_sell = pd.concat([df['Open'][(df.index.hour==9)&(df.index.minute==0)],
                     df['Open'][(df.index.hour==15)&(df.index.minute==0)]],
                    axis=1, keys=['entry', 'exit'])
tk_sell.index = pd.to_datetime(tk_sell.index.date)
tk_sell['entry'] = tk_sell['entry'].shift()
tk_sell.dropna(inplace=True)
tk_sell['profit'] = tk_sell['entry']-tk_sell['exit']

print(len(tk_sell))
print(tk_sell['profit'].sum())
tk_sell['profit'].cumsum().plot(figsize=(15,5))
2632
71.87999999999957
Out[10]:
<matplotlib.axes._subplots.AxesSubplot at 0x1298acf8>

09:30からL、09:55から15:00までSという戦略をみてみましょう(´・ω・`)

In [11]:
class Test2(Tester):
    def __init__(self, df):
        super(Test2, self).__init__(df)
        hour = df.index.hour
        minute = df.index.minute
        self.ob =  (hour==9) & (minute==25)
        self.os =  (hour==9) & (minute==50)
        self.cb =  self.os
        self.cs = ((hour>=14)&(minute>=55)) | ((hour<9)|(hour>15))

spread = 0.00
%time res = Test2(df).run()
res = Series((((res['cp']-res['op'])*res['P']-spread).values),
             index=pd.to_datetime(res['ct'],unit='ns'))
print(len(res))
print(res.sum())
print(res.sum()/len(res))
res.cumsum().plot(figsize=(15,5))
Wall time: 491 ms
5270
127.71700000000081
0.02423472485768516
Out[11]:
<matplotlib.axes._subplots.AxesSubplot at 0x1205c860>

ふむふむ(´・ω・`)

10年くらいで12700pipsになりましたが
1トレードあたりの平均は2.4pipsでスプレッドを考えると
スプ狭めの業者でも1トレードあたり2pipsくらいでしょうか
まぁもうちょっと手を加えればもう少し良くなりそうですね

今回のものは仲値と東京時間の傾向を利用した戦略ですが、
こういうアノマリーを軸にするとインジケータを組み合わせてどうにかしたものよりは
賞味期限が長いかもしれませんね

東京時間に限らず各市場の時間の特徴というのはあるので
そういうのを組み合わせてやってみるのも面白いです
たとえばkuchartの市場別の時間帯の累積値ではっきり特徴があるもので組み合わせて
通貨ペアを選択してその市場の時間に合った方向のポジションをとるようにするとかね(´・ω・`)