ランダムウォークのトレンド継続期間 (2017/07/06)

ランダムウォークのデータにおいてトレンドが継続する期間がどのようであるかを調べてみる。

本来ならランダムウォークにトレンドは存在しない。だが、ランダムに動いてもトレンドに見えるようなことはよくある。ランダムウォークでもトレンドがどのくらい継続するものなのかということを知っておけば、実際のトレードにおいてトレンドがどのくらい継続するか想定する参考になるだろう。

ここではトレンドを安値が移動平均線の上、または高値が移動平均線の下にある状態とする。安値が移動平均線の上にあれば上昇トレンド、高値が移動平均線の下にあれば下降トレンドとなる。

トレンドには広く共有されている定義はないようである。そこでとりあえず上述のように定義する。当たらずといえども遠からずではないかと思う。

それでは実際にランダムウォークのデータを作成し、トレンドが継続する期間を調べてみる。

①この記事で使用するライブラリをインポートする。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit

②ランダムウォークのデータを作成する。

size = 10000000
index = pd.date_range('1/1/2000', periods=size, freq='S')
rnd = np.random.normal(0.0, 1.0/np.sqrt(1440*60), size)
rnd = pd.Series(rnd, index=index)
data = rnd.cumsum()
data = np.exp(data)
data = data.resample('T').ohlc()

ランダムウォークのデータは対数と考えられる。だが、為替などのデータは対数ではない。そこで比較しやすいよう、ランダムウォークのデータを変換しておく。

日付は適当である。こうしておくと四本値に変換するときに楽だというだけである。

データは1日の変動が1.0程度になるような乱数で作成した。ドル円で1日の値幅が1円くらいというイメージである。

③計算期間ごとにトレンド継続期間の標準偏差を求める。

trend_duration = np.empty(50)
period = np.array(range(5, 255, 5))
for i in range(50):
    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[i] = (above - below).std()

④グラフを表示する。

plt.plot(period, trend_duration)
plt.xlabel('Period')
plt.ylabel('Trend Duration')
plt.xlim(5, 250)
plt.savefig('trend_duration.png', dpi=150)
plt.show()

グラフからトレンド継続期間は計算期間に比例しているということが予想される。

⑤トレンド継続期間と計算期間の関係を示すモデルを作成する。

def model(period, slope, intercept):
    return slope * period + intercept

モデルは以下のようにしてみた。

トレンド継続期間 = 傾き * 計算期間 + 切片

⑥モデルの傾き、切片を求める。

popt, pcov = curve_fit(model, period, trend_duration)
slope = popt[0]
intercept = popt[1]
print('Trend Duration = ', slope, ' * Period + ', intercept)

Trend Duration =  0.594061280471  * Period +  -2.23805773853

トレンドの継続期間は計算期間の55-60%程度の長さと考えられる。乱数を使っているので、結果は毎回いくらか違う。