#!/usr/bin/env python # coding: utf-8 # # モンテカルロで見える世界 # # インターネット上から金融関連の価格データを手に入れ、そのデータにトレンドが在るか無いかを見てきました。 # # トレンドがあれば、確定的トレンドと確率的トレンドに分類され、また、時間との関係では定常時系列と非定常時系列に分類されました。 # # 確定的トレンド、確率的トレンド、定常時系列、または時間トレンド、ランダムウォーク、AR(1)が時系列分析の3種の神器でした。 # # ## モンテカルロ・シミュレーションの利用   # # 日経平均株価の動きの3つのモデルは   # # $1.P_{t} = P_{t-1} + σw_{t}$ # # $2.P_{t} =a + βP_{t-1} + σw_{t}$  # # $3.P_{t} = P_{t-1} + σB_{t}$ # # です。 # # $P_{t}$は t 時の価格である。 # # $P_{t-1}$はtよりも1単位時間前の価格です。 # # $w_{t}$は平均ゼロ、分散1の正規分布に従う確率変数であり、σは定数です。 # # $B_{t}$は+1,-1からなるベルヌーイ過程である。シミュレーションではσ=0.00632とする。   # # ## ランダムウォーク   # # ランダムウォーク・モデルは   # # $P_{t} = P_{t-1} + σw_{t}$ # # で与えられる。 # # ここではα= 0としてドリフト無しランダムウォークを検討する。 # # σは一定である。   # # まず1年間の$P_{t}$の動きを人工的に生成してみよう。 # # np.randomというモジュールの中にある乱数生成器normalを用いて確率変数$w_{t}$を生成する。 # # 株価の初期値 P を1とする。 # # 株式市場の1年の営業日数を250日とし、250個の株価の動きを生成してみよう # In[1]: get_ipython().run_line_magic('matplotlib', 'inline') import pandas as pd import numpy as np sigma=0.1/np.sqrt(250) print(sigma) P=[1] for i in range(1,250): w=np.random.normal(0,1) P.append(P[i-1]+sigma*w) # これでPの時系列が生成できた。価格の差分を取り、その平均、標準偏差、歪度、尖度を確かめてみます。 # In[2]: price=pd.Series(P) dp=price.diff().dropna() print("mean %2.5f std %2.5f skew %2.5f kurt %2.5f"\ %(dp.mean(),dp.std(),dp.skew(),dp.kurt())) # 平均はほぼゼロ、標準偏差はほぼ予定通り、歪度と尖度もほぼゼロに近い。次に結果をグラフで確かめてみます。 # In[3]: import matplotlib.pyplot as plt plt.figure(figsize=(10,5.2)) plt.subplot(121) price.plot(color='darkgray') plt.xlabel('t') plt.ylabel('$P_t$') plt.subplot(122) dp.hist(color='lightgreen') mx=round(dp.max(),2) mn=round(dp.min(),2) plt.xticks([mn,0,mx]) plt.xlabel('$P_t-P_{t-1}$') plt.ylabel('frequency') # 左側のチャートは1日目の価格、2日目の価格といった具合に250日間の価格の推移を描いていて、右側の頻度図は日々の価格の変化の大きさに対する頻度を表示している。 # # 価格の差分は1回の試行の結果としては、かなりきれいな正規分布である。 # # #### プログラムコードの解説 # # P=[1] # # for i in range(250): # w=np.random.normal(0,1) # # P.append(P[i-1]*(1+sigma*w)) # # 生成された活くを格納する容器の準備を整えている # # [1]は初期値を1に設定している。 # # for文以下は250回繰り返す # # 平均ゼロ、分散1の確率変数を生成する。 # # 価格の1期間更新している。 # # P=[] 価格の配列の初期化 # # high=[0]*250 価格の上限の配列の初期化 # # low=[1]*250 価格の下限の配列の初期化 # # for j in range(10000): for文以下を10000回繰り返す # # P0=100 価格の初期値を100回に設定 # # for i in range(250): for文以下を250回繰り返す # # w=np.random.normal(0,1) 標準正規文に従う確率変数の生成 # # P0=P0*(1+sigma*w) 価格の更新 # # if P0>high[i]: 上限の更新 # # high[i]=P0 if P0high[i]: high[i]=P0 if P0high[i]: high[i]=P0 if P0 1であってもその程度が小さければ、価格過程は破たんすることが無いことが分かる。 # # またそれは観測期間にも左右されることも分かる。 # # 期間が短ければ、βが1よりも大きくても破たんせずにすむ確率は上がる。また、β< 1で1近辺の場合はどうであろうか?  # # ランダムウォークとの区別はうまく行くだろうか? これもまた難しいという結果になった。   # # 自己回帰モデルの最大の特徴は価格の中心回帰の動きである。 # # ランダムウォークの価格の動きは時間の経過とともに分散が大きくなるのに対して、自己回帰モデルではある点に収束する。 # # そのために将来の予測を行うためには便利です。 # ## ベルヌーイ試行   # # 金融資産の価格の動きの大きさがx に定められていると、上昇は+ x 、下落は- x となる。 # # このように結果が2つしかない実験をベルヌーイ試行とよび、とびとびな2つの値から成る独立な離散時間の確率変数列をベルヌーイ過程とよぶ。 # # このような2つのとびとびな確率変数からなる確率過程の性質を乱数生成器を用いて調べてみます。 # # ここではx を5とした時の1万個の確率変数を発生させその和を求め、それを1日分の取引としました。 # In[14]: def bernoulli(p,x,N,p0): s = (np.random.binomial(1, p, N)-0.5)*x P0=p0 P=[] for i in range(N): P0=P0+s[i] P.append(P0) return P P=bernoulli(0.5,5,10000,0) plt.figure(figsize=(10,6)) plt.plot(P,color='darkgray') plt.xlabel('steps $t$') plt.ylabel('$\sum_1^t B_t \cdot 5$') # 実際の価格の動きに似た動きが再現できていると思う。これはランダムウォークであり、一定の方向に価格が上昇したり下落しているのは確率的トレンドである。   # # # つぎにこの実験を10000回繰り返し、1回1回の実験で生成した確率変数の和をヒストグラムとして描いてみます。 # In[15]: N=10000 M=10000 Q=[] for j in range(N): P=bernoulli(0.5,5,M,0) Q.append(P[M-1]) plt.figure(figsize=(8,5)) plt.figure.left=-0.1 plt.hist(Q,normed=True,histtype='stepfilled',color='pink',bins=25) plt.xlabel('$\sum_1^t B_t \cdot 5$') plt.ylabel('probability density function') price=pd.Series(Q) dprice=price.diff() print("mean %2.5f std %2.5f skew %2.5f kurt %2.5f"\ %(dprice.mean(),dprice.std(),dprice.skew(),dprice.kurt())) # 結果はかなり正規分布に近いベル型の形をしている。次に1日の取引の回数を3000回に減らして、その実験の回数は10000回とします。 # In[16]: N=10000 M=3000 Q=[] for j in range(N): P=bernoulli(0.5,5,M,0) Q.append(P[M-1]) plt.figure(figsize=(8,5)) plt.figure.left=-0.1 plt.hist(Q,normed=True,histtype='stepfilled',color='pink',bins=25) plt.xlabel('$\sum_1^t B_t \cdot 5$') plt.ylabel('probability density function') price=pd.Series(Q) dprice=price.diff() print("mean %2.5f std %2.5f skew %2.5f kurt %2.5f"\ %(dprice.mean(),dprice.std(),dprice.skew(),dprice.kurt())) # 分布の形状は分布の幅が狭くなっている。これは1日の価格の動きが取引の回数に依存することを示しています。 # # 2番目の特徴は、ベル型の形は維持しているが、分布の滑らかさは維持されていない。 # # これが最も重要な点で、これこそが本題です。   # # ボラティリティは取引数と関係があるということです。 # In[ ]: