上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ランダムウォークとZスコア (2017/12/24)

ランダムウォークのデータにおいて、Zスコアの標準偏差を計算期間ごとに調べてみる。

シミュレーション

  1. 平均0.0、分散1.0の乱数を1000000個生成する。
  2. 乱数の累積和でデータを作成する。
  3. 計算期間を3から24まで3ずつ場合分けして、それぞれのZスコアの標準偏差を求める。

○以下のコマンドをIPythonコンソールで実行する。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

size = 1000000
rnd = np.random.normal(0.0, 1.0, size)
rnd = pd.Series(rnd)
data = rnd.cumsum()

n = 8
zscore_std = np.empty(n)
period = np.array(range(3, 3*n+3, 3))
for i in range(n):
    mean = data.rolling(window=period[i]).mean()
    std = data.rolling(window=period[i]).std()
    zscore = (data-mean) / std
    zscore = zscore.dropna()
    zscore_std[i] = zscore.std()

print(zscore_std)

plt.plot(period, zscore_std)
plt.xlabel('Period')
plt.ylabel('ZScoreSTD')
plt.savefig('zscore_std.png', dpi=150)
plt.show()

[ 0.86935286  1.08296061  1.17453778  1.22621332  1.25927873  1.28268614
  1.2998276   1.31289542]

結果

  • 計算期間が増加するにしたがって、Zスコアの標準偏差も増加している。
  • だが、Zスコアの標準偏差の増加は次第に小さくなっている。

ここから想像するに、計算期間とZスコアの標準偏差の関係は、1階微分でプラス、2階微分でマイナスとなるような関数、例えばxの指数が0から1の間にあるような関数ではないだろうか。

だが、そのような想定でモデルを作成してみたものの、うまくいかない。今回はモデルの作成はあきらめた。

スポンサーサイト

ランダムウォークとトレンド継続期間 (2018/01/03)

ランダムウォークのデータにおいてトレンドが継続する期間の標準偏差を計算期間ごとに調べてみる。

ここではトレンドを安値が移動平均線の上、または高値が移動平均線の下にある状態とする。安値が移動平均線の上にあれば上昇トレンド、高値が移動平均線の下にあれば下降トレンドとなる。また、トレンド継続期間は上昇トレンドの場合はプラス、下降トレンドの場合はマイナスとする。

シミュレーション

  1. 平均0.0、分散1.0の乱数を1000000個生成する。
  2. 乱数の累積和でデータを作成する。
  3. 計算期間を5から250まで5ずつ場合分けして、それぞれのトレンド継続期間の標準偏差を求める。

○以下のコマンドをIPythonコンソールで実行する。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

size = 100000000
index = pd.date_range('1/1/2000', periods=size, freq='S')
rnd = np.random.normal(0.0, 1.0, size)
rnd = pd.Series(rnd, index=index)
data = rnd.cumsum()
data = data.resample('5min').ohlc()

n = 50
period = np.array(range(5, 255, 5)).astype(int)
trend_duration_std = np.empty(n)
for i in range(n):
    high = data['high']
    low = data['low']
    ma = data['close'].rolling(window=period[i]).mean()
    above = low.iloc[period[i]-1:] > ma.iloc[period[i]-1:]
    above = above * (
            above.groupby((above!=above.shift()).cumsum()).cumcount()+1)
    below = high.iloc[period[i]-1:] < ma.iloc[period[i]-1:]
    below = below * (
            below.groupby((below!=below.shift()).cumsum()).cumcount()+1)
    trend_duration = above - below
    trend_duration_std[i] = trend_duration.std()

plt.plot(period, trend_duration_std)
plt.xlabel('Period')
plt.ylabel('TrendDurationSTD')
plt.savefig('trend_duration_std.png', dpi=150)
plt.show()

result = pd.DataFrame(index=[['']*n])
result['Period'] = period
result['TrendDurationSTD'] = trend_duration_std
result['Ratio'] = trend_duration_std / period
print(result)

  Period  TrendDurationSTD     Ratio
       5          1.454045  0.290809
      10          4.531907  0.453191
      15          7.549229  0.503282
      20         10.572646  0.528632
      25         13.525033  0.541001
      30         16.577855  0.552595
      35         19.396050  0.554173
      40         22.626974  0.565674
      45         25.618781  0.569306
      50         28.267030  0.565341
      55         31.499318  0.572715
      60         34.443049  0.574051
      65         37.233329  0.572820
      70         41.057547  0.586536
      75         44.156904  0.588759
      80         48.551253  0.606891
      85         51.539219  0.606344
      90         54.659678  0.607330
      95         57.028719  0.600302
     100         59.007933  0.590079
     105         61.969907  0.590190
     110         64.496991  0.586336
     115         66.852159  0.581323
     120         70.811487  0.590096
     125         74.292662  0.594341
     130         77.569155  0.596686
     135         80.573640  0.596842
     140         83.404135  0.595744
     145         85.782141  0.591601
     150         88.834280  0.592229
     155         92.131061  0.594394
     160         96.577908  0.603612
     165        100.523398  0.609233
     170        103.030595  0.606062
     175        104.531151  0.597321
     180        106.637312  0.592430
     185        109.294397  0.590781
     190        112.634401  0.592813
     195        114.722630  0.588321
     200        116.697349  0.583487
     205        120.477368  0.587694
     210        122.815857  0.584837
     215        127.056278  0.590959
     220        129.200717  0.587276
     225        133.987214  0.595499
     230        135.617438  0.589641
     235        138.450543  0.589151
     240        139.665763  0.581941
     245        145.938998  0.595669
     250        148.859796  0.595439

結果

計算期間が増加するにしたがって、トレンド継続期間の標準偏差も増加している。計算期間が70以上だと大体0.58-0.60の間に収まっているようである。

ランダムウォークデータ同士のペアとボラティリティ

各データとペアのボラティリティの関係

2つのランダムウォークデータのボラティリティとペアのボラティリティの関係を調べてみる。第1のランダムウォークデータをベースとし、平均は0、標準偏差は変動させる。第2のランダムウォークデータをクウォートとし、平均は0、標準偏差は1.0で固定とする。ベースの標準偏差(ボラティリティ)を変動させることによってペアのボラティリティがどうなるかを見る。

データは累積和で作成しているので対数として扱う。したがって、ペアは「ベース / クウォート」ではなくて、「ベース - クウォート」となる。

試しにシミュレーションしてみたところ、どうやら以下のような関係にあるように思われた。

ペアのボラティリティ = sqrt(ベースのボラティリティ^2 + クウォートのボラティリティ^2)

さて、この想定に基づき、実測値と予測値のグラフを作成した。

実測値の上にほぼ完全に予測値が重なってしまい、よく分からなくなってしまった。それくらい一致しているわけで、上の想定で合っていると考えていいだろう。つまり、ランダムウォークデータ同士のペアのボラティリティは(ベースのボラティリティの2乗 + クウォートのボラティリティの2乗)の平方根である。

サンプルプログラム

○以下のコマンドをSpyderの「IPython console」にコピー&ペーストして「Enter」キーを2回押す。

import matplotlib.pyplot as plt
import numpy as np

def create_randomwalk_data(mean, std, size):
    temp = np.random.normal(loc=mean, scale=std, size=size)
    data = np.cumsum(temp)
    return data

n = 1000000

base_volatility = np.empty(10)
base_quote_volatility = np.empty(10)
base_quote_volatility_pred = np.empty(10)
quote = create_randomwalk_data(0.0, 1.0, n)
for i in range(10):
    base_volatility[i] = 1 + i
    base = create_randomwalk_data(0.0, base_volatility[i], n)
    base_quote = base - quote
    diff = base_quote[1:] - base_quote[:-1]
    base_quote_volatility[i] = np.std(diff)
    base_quote_volatility_pred[i] = np.sqrt(base_volatility[i]**2 + 1**2)

plt.plot(base_volatility, base_quote_volatility, label='base_quote_volatility')
plt.plot(base_volatility, base_quote_volatility_pred,
         label='base_quote_volatility_pred')
plt.title('Base/Quote Volatility')
plt.xlabel('Base Volatility (Quote Volatility = 1.0)')
plt.ylabel('Base/Quote Volatility')
plt.xlim(1, 10)
plt.legend()
plt.tight_layout()
plt.savefig('base_quote_volatility.png', dpi=150)
plt.show()
(2017/02/07更新)
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。