0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

正規分布からのズレを特定する流れ

Posted at

1:概要

・トレードは必ず正規分布からのズレから生じる
・実データにおける正規分布からのズレを特定する方法を(GPTに)検討してもらった
・ズレの大きさの定量化から始まり、最終的にはズレているポイントまで特定できた

2:背景

正規分布からのズレがトレードにおけるαとなる。
しかし、そのαが実際にはどの程度存在しているか、まず知らなければ戦略も立てられない
そこで正規分布からのズレ具合を(GPTに)確認してもらった

3:正規分布からのズレを出す(数値情報)

まずは数値として、どれぐらい正規分布からズレいているか把握する

①:統計的検定(正規性検定)

「この分布は正規分布とどれだけ違うか」を調べるには、まず 正規性の検定(正規分布化どうかチェックする) を行うのが一般的です。

✅ 代表的な検定
Shapiro-Wilk検定
Kolmogorov-Smirnov検定
Anderson-Darling検定
Jarque-Bera検定

これらは「正規分布からのズレ」を数値(統計量)として出してくれます。

②:KLダイバージェンス(情報量的なズレ)

ある分布 𝑃 と正規分布 Q の間の距離を測る指標として Kullback-Leibler (KL) ダイバージェンス を使うことができます。これは「PがQからどれだけズレているか」を%ではなく 「情報量の差」 で測る方法です。
image.png

③:ヒストグラムの差異から%ズレを評価

分布 P をヒストグラムにして、正規分布のヒストグラムと比べることができます。 各ビンの差を合計 し、以下のように%を出す方法です
※ これは正規化された確率ヒストグラムで行うとよいです。

image.png

4:日経平均で試した場合

①:正規性検定

image.png

このデータは統計的にはほぼ正規分布と言ってよい結果です。

②:KLダイバージェンス

値:25.83
これは「真の分布と正規分布の情報的なズレ」を意味します。値が大きいほどズレが大きいですが、絶対的な解釈は難しいため、他の分布と比較して相対的に使います。

③:ヒストグラム差異による%ズレ

正規分布との絶対差(正規化ヒストグラムで): 約25.83%
つまり、ヒストグラムの各ビンの合計的な差異が全体の約26%存在する、という意味になります。

※:25.83の解釈

あるビンでは、正規分布よりも実データの方が多く
別のビンでは、逆に正規分布の方が多く
…という 偏りや歪みの総合的なズレを全部足し合わせて、それが 理想的な正規分布の確率全体(100%)のうち25.83%に相当する ということです。

実務的な解釈

image.png

意外にズレが大きい

5:正規分布からのズレを可視化する

数値だけだと分からないので、可視化する

①:ヒストグラムの可視化

コード
# ズレを塗るために実データと正規分布の密度差をプロット
plt.figure(figsize=(10, 6))

# 実データのヒストグラム(密度)
hist_p, bins = np.histogram(returns, bins=50, density=True)
bin_centers = (bins[1:] + bins[:-1]) / 2

# 正規分布の密度(同じビン中心で計算)
hist_q = stats.norm.pdf(bin_centers, mu, std)

# プロット
plt.plot(bin_centers, hist_p, drawstyle='steps-mid', label='Actual Returns', color='blue')
plt.plot(bin_centers, hist_q, linestyle='--', label='Fitted Normal', color='red')

# 差の部分を塗る
plt.fill_between(bin_centers, hist_p, hist_q, where=(hist_p > hist_q), color='orange', alpha=0.4, label='Actual > Normal')
plt.fill_between(bin_centers, hist_p, hist_q, where=(hist_p < hist_q), color='green', alpha=0.4, label='Normal > Actual')

plt.title('Distribution Comparison with Deviation Highlighted')
plt.xlabel('Daily Return')
plt.ylabel('Probability Density')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

image.png

中央が多くて、周辺で少なくなっているのが何となく分かる

②:累積分布(CDF)での比較、尖度・歪度の可視化やQ-Qプロット

コード
# CDF(累積分布)の可視化
plt.figure(figsize=(15, 4))

# サブプロット1: CDF
plt.subplot(1, 3, 1)
sorted_returns = np.sort(returns)
cdf_empirical = np.arange(1, len(returns)+1) / len(returns)
cdf_normal = stats.norm.cdf(sorted_returns, mu, std)
plt.plot(sorted_returns, cdf_empirical, label='Empirical CDF', color='blue')
plt.plot(sorted_returns, cdf_normal, linestyle='--', label='Normal CDF', color='red')
plt.title('CDF Comparison')
plt.xlabel('Daily Return')
plt.ylabel('Cumulative Probability')
plt.legend()
plt.grid(True)

# サブプロット2: Q-Qプロット
plt.subplot(1, 3, 2)
stats.probplot(returns, dist="norm", plot=plt)
plt.title('Q-Q Plot')

# サブプロット3: 歪度・尖度の可視化(棒グラフ)
plt.subplot(1, 3, 3)
skewness = stats.skew(returns)
kurtosis = stats.kurtosis(returns, fisher=True)  # Fisher=True で 0 が正規分布の基準
bars = plt.bar(['Skewness', 'Kurtosis'], [skewness, kurtosis], color=['skyblue', 'lightcoral'])
plt.axhline(0, color='gray', linestyle='--')
plt.title('Skewness and Kurtosis')
plt.grid(True)

plt.tight_layout()
plt.show()


image.png

今までの結果とは違って、ほとんど正規分布的に見える(逆に言えば、これでも25%はズレている)

※:累積分布(CDF)での比較、尖度・歪度やQ-Qプロットの解釈

📊【1. CDF(累積分布関数)比較グラフ】
● 何を見ているか?
実際のデータが「ある値以下になる確率」を順に並べた線(青)
正規分布に従った場合の累積確率(赤い破線)

解釈
2本の線が重なっていれば → 正規分布に近い
ズレていれば → 分布の位置や形状が違う

今回の観察
真ん中付近はよく重なっている → 平均付近は正規的
両端(特に左端)でややズレ → 外れ値や裾野に**非正規性(fat tailsや偏り)**がある

📊【2. Q-Qプロット(Quantile-Quantile Plot)】
● 何を見ているか?
実際のデータの分位点(何%目の値)と、正規分布の同じ分位点を比較したプロット
解釈
点が一直線上(45度線)に並べば → 正規分布と同じ形
端が曲がっていれば → 歪みや厚い尾(heavy tails)

今回の観察
中央付近は直線的 → 中央は正規的
両端がやや外れる → 外れ値の頻度が正規分布より多い(厚い尾)

📉【3. 歪度(Skewness)と尖度(Kurtosis)の棒グラフ】
● 何を見ているか?
データの「非対称性(歪み)」と「鋭さ(尖り具合)」を数値で表示

image.png

解釈
Skewness(歪度):正なら右に長い尾 → リターンが大きく上に跳ねることが多い
Kurtosis(尖度):正なら尖ってる → 外れ値が多い(リスク高め)

今回の観察
歪度も尖度も0よりやや上 → 軽く右に偏っていて、外れ値もやや多め

📉全体の解釈
image.png

GPT歪度の解釈間違ってない?

どこが一番正規分布からズレているか特定する

①:正規分布からのズレ領域の特定

コード

# 各ビンごとのズレ率を計算(正規分布との絶対差 / 正規分布値)
bin_differences = hist_p_sp - hist_q_sp
bin_relative_diff = (bin_differences / hist_q_sp) * 100  # パーセント化
bin_relative_diff = np.round(bin_relative_diff, 2)

# ズレの大きい順にデータフレーム化
deviation_df = pd.DataFrame({
    "Bin Center": bin_centers_sp,
    "Actual Density": hist_p_sp,
    "Normal Density": hist_q_sp,
    "Difference (%)": bin_relative_diff
}).sort_values(by="Difference (%)", key=abs, ascending=False).reset_index(drop=True)

# 上位のズレ(絶対値が大きい)10件を表示
tools.display_dataframe_to_user(name="ズレの大きいビン上位10件", dataframe=deviation_df.head(10))

image.png

解釈
image.png

とりあえず大きそう(小並)

②ズレの大きさを可視化:ズレが大きいビンの棒グラフ(上位10件)

コード

top_deviation = deviation_df.head(10)

plt.figure(figsize=(12, 6))
bars = plt.bar(top_deviation["Bin Center"], top_deviation["Difference (%)"], width=0.0015, color='orange')
plt.axhline(0, color='gray', linestyle='--')
plt.xlabel("Return Bin Center")
plt.ylabel("Difference (%) vs Normal Distribution")
plt.title("Top 10 Deviations Between Actual and Normal Distribution (S&P500)")
plt.grid(True)

# 差が正か負かで色を変える
for bar, diff in zip(bars, top_deviation["Difference (%)"]):
    bar.set_color('orange' if diff > 0 else 'green')

plt.tight_layout()
plt.show()

image.png

解釈
特に-2〜-3%付近の下落が正規分布より「はるかに頻繁」に発生(最大で4500%以上の差!)
一方で、+2%付近の急騰も正規分布より多め
→ fat tails(厚い尾)を持つ分布であり、正規性を仮定するモデルはリスクを過小評価する可能性がある

まとめ

以上のような流れで正規分布からのズレを特定してきた。
実際にはもっと色々調査する必要はあるだろうが、差し当たっての正規分布からのズレ調査としては十分だろう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?