金融市場における季節効果の統計的検定

本記事では「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ファクターモデルなどマーケットモデルを用いるやり方など様々ですが、平均値検定は手軽にできるのでちょっと調べてみたいときには便利だと思われます。

コメント

タイトルとURLをコピーしました