こんにちは。システムトレーダーの卵ことKenKenです。今回も「Python3ではじめるシステムトレード ──環境構築と売買戦略」で勉強したことを簡単にまとめたいと思います。今回のテーマは、「季節効果」です!金融市場の季節性として、以下のようなものが有名。
- 月次効果
- ヘッジファンドの決算売り(5月)
- 米国ファンドの決算売り(6月)
- 夏枯れ相場(7, 8月)
- 米国の節税売り(11月)
- ヘッジファンドの決算売り(11月)
- 12月の節税売り(12月)
- 曜日効果
- 月曜効果
- 金曜効果
- 週末効果
- 月末効果
- 節分天井彼岸底:3月末の決算前に利益確定売りが出る
- 掉尾の一振:年末にかけて株価が上昇
- サンタクロースラリークリスマス:クリスマスから年末にかけて株価が上昇
- もちつき相場:年末のボラティリティが上がること
- 週次効果
- 第1金曜:米国雇用統計
- 第2金曜:先物SQ
- 祝日相場
- ゴールデンウィーク相場:昭和の日前日からゴールデンウイーク明けにかけて相場が上昇
- 盆休み相場:8月10日前後にポジションを調整する動き
- ラマダン相場:ラマダン期間中は相場は横ばいか下げやすい
平均値の検定
1月効果を確認する為に、例として、バブル崩壊前の1989年1月の終値の変化率の母平均について検定を行ってみる。母平均\(\mu\)について、帰無仮説\(H_0\)と対立仮説\(H_1\)を以下のように設定する。
$$H_0: \mu = 0$$
$$H_1 : \mu > 0$$
これらの仮説が実際の観測値と整合性があるかどうかの判定は、統計的仮設の検定(The Test of Significant Approach)を用いて行う。検定統計量\(T\)は以下で定義され、その実現値を\(t\)で表す。
$$T=\frac{\bar{\mu}-\mu}{\sqrt{\frac{s^2}{n}}}$$
ここで、\(\bar{\mu}\)は標本平均、\(s\)は不偏分散、\(n\)はサンプルサイズである。統計的検定では、実現値\(t\)が採択域にいれば帰無仮説を棄却することなく、また棄却域にいれば帰無仮説を棄却する。
import pandas_datareader as pdr import numpy as np import pandas as pd from scipy.stats import t import matplotlib.pyplot as plt %matplotlib inline N225 = pdr.DataReader('NIKKEI225', 'fred', '1949/5/16').dropna() ex = N225['1989/1/1':'1989/1/31'] # 例として1989年1月分 R_ex = ex.pct_change().dropna() # 変化率 # 統計量を計算 mu_ = R_ex.mean()[0] s = R_ex.std()[0] n = R_ex.size t = mu_ * np.sqrt(n) / s print('μ_:{0:.5f}, s:{1:.5f}, n:{2}, t:{3:.5f}'.format(mu_, s, n, t))
μ_:0.00242, s:0.00514, n:18, t:1.99739
\(t_{0.05, 17}=1.74\)であり、t=1.997である為、\(H_0\)は棄却され、1989年1月の変化率の平均値はプラスの可能性が否定できない。
季節効果の分析
上では、例として、1989年1月の終値の変化率の母平均について検定を行い、平均値はプラスの可能性があることがわかった。次は、データが取れるてかつ1月開始として、1950年1月1日から直近の2019年12月31日までの各月(1月~12月)における変化率の平均がプラスか有意水準10%で統計的仮設検定を用いて判断する。そして、帰無仮説が棄却された年の数で季節効果の判定を行う。ただし標本、標準偏差を用いるため、不均一性の問題が生じる。そこで、この問題を避けるために、1年間ごとに分けて実現値を分析することにする。
import pandas_datareader as pdr import numpy as np import pandas as pd from scipy.stats import t import matplotlib.pyplot as plt %matplotlib inline start, end = '1950/1/1', '2019/12/31' alpha = 0.1 N225 = pdr.DataReader('NIKKEI225', 'fred', start, end).dropna() # 年数を算出し、各年のリストを作成 year_num = pd.to_datetime(end).year - pd.to_datetime(start).year + 1 years = [pd.to_datetime(start).year + i for i in range(year_num)] # 各月で帰無仮説を棄却した回数を格納用リスト count = [0] * 12 count_after_bubble = [0] * 12 # バブル崩壊後 # 月集計用ラムダ m = lambda x:x.month for i in range(len(years)): year = N225[str(years[i])] R = year.pct_change().dropna().groupby(m) t_value = R.mean() * np.sqrt(R.count()) / R.std() # 実現値t t0 = t.ppf(1-alpha, len(R)-1) for j in range(12): if t_value.values[j] > t0: count[j] += 1 if years[i] >= 1990: count_after_bubble[j] += 1 print('全期間:', end='') print(count) print('1990年以降:', end='') print(count_after_bubble)
全期間:[25, 15, 14, 11, 14, 10, 9, 14, 18, 12, 14, 16] 1990年以降:[3, 3, 2, 4, 4, 2, 2, 1, 7, 4, 3, 5]
結果は、左から順に1月~12月における帰無仮説が棄却された年の数となっている。平均値がプラスと判定された月は、1950年から2019年までの1月では、25回もあった。そのうち、1990年以降では2回だけであり、1月効果はバブル崩壊前の現象であった可能性が高い。バブル崩壊以降は、どの月においても明確な方向性はないように思われる。
同様の実験をマイナスの変化率に対して行った結果は、以下の通り。
# 各月で帰無仮説を棄却した回数を格納用リスト count = [0] * 12 count_after_bubble = [0] * 12 # バブル崩壊後 # 月集計用ラムダ m = lambda x:x.month for i in range(len(years)): year = N225[str(years[i])] R = year.pct_change().dropna().groupby(m) t_value = R.mean() * np.sqrt(R.count()) / R.std() # 実現値t t0 = t.ppf(1-alpha, len(R)-1) for j in range(12): if t_value.values[j] < -t0: count[j] += 1 if years[i] >= 1990: count_after_bubble[j] += 1 print('全期間:', end='') print(count) print('1990年以降:', end='') print(count_after_bubble)
全期間:[2, 5, 5, 2, 10, 4, 6, 6, 5, 4, 7, 3] 1990年以降:[1, 2, 2, 0, 5, 2, 2, 2, 3, 0, 3, 1]
結果としては、全体的に特徴は見られなかった。しいて言えば、5月のヘッジファンド売りはある可能性もあるが、上記でっ見たように5月はプラスになる可能性も高い。
曜日効果の分析
次に曜日効果について分析してみる。
# 各曜日で帰無仮説を棄却した回数を格納用リスト count = [0] * 5 count_after_bubble = [0] * 5 # バブル崩壊後 # 週次集計用ラムダ w = lambda x:x.weekday for i in range(len(years)): year = N225[str(years[i])] R = year.pct_change().dropna().groupby([w]) t_value = R.mean() * np.sqrt(R.count()) / R.std() # 実現値t t0 = t.ppf(1-alpha, len(R)-1) for j in range(5): if t_value.values[j] > t0: count[j] += 1 if years[i] >= 1990: count_after_bubble[j] += 1 print('全期間:', end='') print(count) print('1990年以降:', end='') print(count_after_bubble)
全期間:[10, 7, 15, 15, 11] 1990年以降:[3, 2, 0, 6, 2]
左から月曜~金曜となっている。結果としては、月次効果の時と同じように、ほとんどのを有意な曜日を持つ年はバブル崩壊前に起きていて、バブル崩壊後は特徴は見られいように思われる。
同様の実験をマイナスの変化率に対して行った結果は、以下の通り。
# 各曜日で帰無仮説を棄却した回数を格納用リスト count = [0] * 5 count_after_bubble = [0] * 5 # バブル崩壊後 # 週次集計用ラムダ w = lambda x:x.weekday for i in range(len(years)): year = N225[str(years[i])] R = year.pct_change().dropna().groupby([w]) t_value = R.mean() * np.sqrt(R.count()) / R.std() # 実現値t t0 = t.ppf(1-alpha, len(R)-1) for j in range(5): if t_value.values[j] < -t0: count[j] += 1 if years[i] >= 1990: count_after_bubble[j] += 1 print('全期間:', end='') print(count) print('1990年以降:', end='') print(count_after_bubble)
全期間:[5, 3, 2, 1, 3] 1990年以降:[3, 1, 0, 1, 2]
特に注目するような特徴は見られない。
まとめ
今回は、日経平均株価についての季節効果・曜日効果を統計的検定を用いて調べてみました。結果としては、バブル崩壊前では季節効果・曜日効果は見られる傾向にあったものの、バブル崩壊後はあまり見られないというものになりました。何か新しいアノマリーに気が付いたら、上で述べたような平均値の検定を用いて調べてみてはいかがでしょうか。アノマリー検定には、CAPMやFF3ファクターモデルなどマーケットモデルを用いるやり方などいろいろあると思いますが、平均値検定は手軽にできるのでちょっと調べてみたいときには便利かもしれません。シストレになる確ゲー要素が強くなるので、統計的に有意なシグナルなどを発見することが大切かもしれませんね。。。
以上