若葉の技術メモ

コンピュータやプログラミングに関して調べたり、取り組んだりしたことをまとめる若葉のノート📓。コンピュータ・プログラミング初めてって方も一緒に勉強していきましょう!初心は大事!いつでも若葉☘のような意気込みで!

  • No.   

若葉の技術メモ

コンピュータやプログラミング・数理に関して調べたり、取り組んだりしたことをまとめる若葉のノート。

コンピュータ・プログラミング・数理が初めてって方も一緒に勉強していきましょう!

初心は大事!いつでも若葉☘のような意気込みで!

日経平均の分布はどんな形?

こんにちは!若葉のマフィンです!

今回の記事では、前回の記事

wakaba-mafin.hatenablog.com

の続きでダウンロードした日経平均のデータの性質を詳しく見ていきたいと思います!


はじめに

まず、株価などの経済のデータはランダムに変動しているとみなすことができるということが知られています。そして株価などの分布に対してOsborne, "Brownian Motion in the Stock Market," 1954などの研究により「対数正規分布」と呼ばれる確率分布を仮定して解析を進めていくことが1つの理論として確立しているようです。

ここで「対数正規分布」とはその名が示す通り、確率変数の対数をとれば正規分布(身長などが従う分布)に従う分布で、確率密度関数


p(x) = \frac{1}{\sqrt{2\pi}\sigma x} \exp\left( -\frac{(\log x - \mu)^{2}}{2\sigma^{2}}\right)

と表されます(なんか煩雑...)。

正規分布確率密度関数 n(x)=\frac{1}{\sqrt{2\pi}\sigma}\exp\left( -\frac{(x-\mu)^{2}}{2\sigma^{2}}\right)

パラメータを変えながら対数正規分布確率密度関数をplotすると、こんな感じのグラフになります。

f:id:wakaba-mafin:20181116014120j:plain

というわけで、今回は日経平均が本当に対数正規分布に従っているかどうか見てみましょう!

準備

環境としては

  • scipy(numpy)

  • matplotlib

  • pandas

  • pandas-datareader

を使いますが、基本的には

wakaba-mafin.hatenablog.com

と同じ環境にpandas-datareaderが加わっていれば問題ないです!

最初に日経平均データを準備しましょう!まずはライブラリ。

import scipy as sp
from scipy import stats
import pandas as pd
import matplotlib.pyplot as plt
import pandas_datareader.data as web
from datetime import datetime

そして前回の記事

wakaba-mafin.hatenablog.com

と同じようにして日経平均のデータを過去5年間くらいダウンロードします。

df = web.DataReader(
    'NIKKEI225',
    'fred',
    datetime(2014, 11, 14),
    datetime(2018, 11, 14)
)

続いて、前処理としてNaNなどを処置します。

nikkei225=df["NIKKEI225"].interpolate().values

この処理はある日にちの日経平均データがNaNであれば、その前後の日にちの日経平均データでNaNを補間するものです。

さて、これで準備は完了!早速分布を見ていきましょう!

日経平均の分布

日経平均自体を見てもいいのですが、日経平均の値自体は時間に大きく依存しています。そこで、その影響をなくすために前日と比較してどのくらい騰落したかを表す騰落率に相当する量


\mbox{日経平均の前日比} =  \frac{\mbox{当日の日経平均の終値}}{\mbox{前日の日経平均の終値}}

を使います!

ヒストグラム

データの分布を見るための最も基本的な作業はヒストグラムを作ることです。というわけで、ヒストグラムを見て実際に日経平均がどのように分布しているかを眺めてみます!

plt.hist(nikkei225[1:]/nikkei225[:-1], density=True, bins=15)
plt.xlabel("NIKKEI225(current)/NIKKEI225(previous)")
plt.ylabel("probability density")
plt.show()

f:id:wakaba-mafin:20181116014334j:plain

ぱっと見、正規分布に見えないこともないような...でも、とりあえず、今はOsborneの考え方に従って、日経平均は対数正規分布、つまり当日の日経平均を前日の日経平均で割ったものも対数正規分布に従っていると考えてみましょう💦

分布のフィッティング

とうわけで、最尤推定(持っているデータがモデルに対して最も起こりやすくなるようにパラメータを推定する推定方法)によって、対数正規分布のパラメータを推定します。

shape, loc, scale = sp.stats.lognorm.fit(nikkei225[1:]/nikkei225[:-1])

これでScipyのlognormに必要なパラメータが尤もらしく推定できます!というわけでplot

plt.hist(nikkei225[1:]/nikkei225[:-1], density=True, bins=15, label="data")
plt.plot(sp.sort(nikkei225[1:]/nikkei225[:-1]), sp.stats.lognorm.pdf(sp.sort(nikkei225[1:]/nikkei225[:-1]), shape, loc, scale), label="log-normal")
plt.xlabel("NIKKEI225(current)/NIKKEI225(previous)")
plt.ylabel("probability density")
plt.show()

f:id:wakaba-mafin:20181116014348j:plain

うーん...まぁ、フィッティングできたか?

...

なんか信用ならないなぁというか、対数正規分布とは違うっぽいなぁということで... Kolmogorov–Smirnov検定と呼ばれる、持っているデータが帰無仮説において示された分布から生成されたかどうかを調べる検定を以て確認をしてみましょう。

これはPythonでは

sp.stats.kstest(nikkei225[1:]/nikkei225[:-1], "lognorm", (shape, loc, scale))

で検定することができます。さて、返ってきた結果は...

KstestResult(statistic=0.09370901235392531, pvalue=2.0162494118736163e-08)

なるほど。一応、有意に対数正規分布ではなさそうですね...

裾の厚い分布?

というわけで、日経平均の前日比がどのような分布か生存関数を使って考えてみます!

まず、確率変数 Xの生存関数とは


SF(x) := Pr\left\{ X \geq x\right\}

で定義される関数です。今回の日経平均の前日比の場合では SF(x)日経平均の前日比が x以上になる確率を表すことになります!

で、両対数plot

plt.plot(sp.sort(nikkei225[1:]/nikkei225[:-1]), 1-sp.linspace(0,1,len(nikkei225)+1)[1:-1], "ko-", label="data")
plt.plot(sp.sort(nikkei225[1:]/nikkei225[:-1]), sp.stats.lognorm.sf(sp.sort(nikkei225[1:]/nikkei225[:-1]), shape, loc, scale), "r-", label="log-normal")
plt.xscale("log")
plt.yscale("log")
plt.ylim(ymin=1e-4)
plt.xlabel("NIKKEI225(current)/NIKKEI225(previous)")
plt.ylabel("survival function")
plt.show()

f:id:wakaba-mafin:20181116020221j:plain

ふむ。日経平均の前日比が比較的小さい範囲では対数正規分布と実際のデータの生存関数はかなり近いようにも見えますが、日経平均の前日比が比較的大きいところでは大きく外れてますね... もちろん、これは日経平均の前日比が大きいところではサンプル数が減ってしまうことも関連はしてくるのでしょうが...対数正規分布と比べると日経平均の前日比が極めて大きくなることも"十分に"あるということを表しているようですね...もしかすると日経平均の前日比は対数正規分布と比べると裾が厚い可能性がありそうですね

ということは恐らく累積分布関数


CDF(x) := Pr\left\{ X \leq x\right\}

つまり日経平均の前日比が与えられたx以下になる確率を見てみると...

plt.plot(sp.sort(nikkei225[1:]/nikkei225[:-1]), sp.linspace(0,1,len(nikkei225)+1)[1:-1], "ko-", label="data")
plt.plot(sp.sort(nikkei225[1:]/nikkei225[:-1]), sp.stats.lognorm.cdf(sp.sort(nikkei225[1:]/nikkei225[:-1]), shape, loc, scale), "r-", label="log-normal")
plt.xscale("log")
plt.yscale("log")
plt.ylim(ymin=1e-4)
plt.xlabel("NIKKEI225(current)/NIKKEI225(previous)")
plt.ylabel("cumulative distribution function")
plt.show()

f:id:wakaba-mafin:20181116020250j:plain

予想通り!確かに対数正規分布と比べると日経平均の前日比が極めて小さくなることも"十分に"あるということがわかりますね!

つまり、日経平均の前日比は必ずしも対数正規分布に従っておらず、対数正規分布よりも裾の厚い、つまり極めて大きい/小さい値が起こる確率が比較的大きい分布に従っているということですね!確かにこれであれば、ブラックマンデーリーマンショックのような現象があっても全然不思議じゃなさそうです💦

まとめ

というわけで今回はPythonを使って過去の日経平均(の前日比)の分布を見てみました!その結果、従来(かなり昔)の理論では株価などは対数正規分布に従うとされていましたが、実際には対数正規分布よりも裾の厚い分布に従っている可能性があるということを確認することができました!なかなか奥が深いですね...

裾の厚い分布では分散が有限とは限らないので、大数の法則中心極限定理が破綻するような気もしないでもないですが...ちょっと勉強したいですね!

間違いのご指摘やご意見等ございましたら、ぜひコメントのほどよろしくお願いします!

今回はここまで!ここまで読んでくださりありがとうございました!

ではでは、次回以降もよろしくお願いします!