新高値・新安値とボラティリティ (2018/01/17)

直近高値、または直近安値が更新されたとき、ボラティリティがどうなるかを調べてみる。

検証

  • 通貨ペア:EURJPY、EURUSD、USDJPY
  • 期間:2013年1月1日〜2017年12月31日
  • 足の種類:5分
  • 直近の計算期間:1時間から24時間まで1時間刻み(5分足なので12本から288本まで12本刻み)
  • ボラティリティの指標:直近高値、または直近安値が更新されたときのTrue Rangeの平均を全体のTrue Rangeの平均で除したもの

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

import forex_system as fs
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime

fs.remove_folder('temp')

start = datetime.strptime('2013.01.01 00:00', '%Y.%m.%d %H:%M')
end = datetime.strptime('2017.12.31 23:59', '%Y.%m.%d %H:%M')
timeframe = 5
n = 24
x = np.empty(n)
y = np.empty(n)
plt.figure(figsize=(6, 12))
cnt = 0
for symbol in ['EURJPY', 'EURUSD', 'USDJPY']:
    cnt += 1
    plt.subplot(3, 1, cnt)
    ret = fs.i_atr(symbol, timeframe, 1, 0)[start:end]
    for i in range(n):
        minute = (i + 1) * 60
        period = fs.to_period(minute, timeframe)
        highest = fs.i_highest(symbol, timeframe, period, 0)[start:end]
        lowest = fs.i_lowest(symbol, timeframe, period, 0)[start:end]
        x[i] = i + 1
        y[i] = np.mean(ret[(highest==0) | (lowest==0)]) / np.mean(ret)
    plt.plot(x, y)
    plt.title('New High/Low and Volatility (' + symbol + ')')
    plt.xlabel('Hour')
    plt.ylabel('True Range (mean=1.0)')
    plt.axhline(y=1.0, color='black', linestyle=':')
    plt.tight_layout()
plt.savefig('new_high_low_and_volatility.png', dpi=150)
plt.show()

結果

  • 直近高値、または直近安値が更新されたとき、ボラティリティが大きくなる傾向がある。
  • 直近の計算期間が長くなるほど、ボラティリティが大きくなる傾向がある。

ラウンドナンバーとボラティリティ (2017/12/09)

ラウンドナンバーとボラティリティとの関係を調べる。

  • 通貨ペア:EURJPY、EURUSD、USDJPY
  • 期間:2012年1月1日〜2016年12月31日
  • 足の種類:5分足
  • ATRの計算期間:1本
  • 価格帯の区分:始値を0pipsから100pipsまで10pips刻み

なお、ボラティリティは各価格帯のATRの平均を全体のATRの平均で除したものとする。

検証

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

import forex_system as fs
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime

fs.remove_folder('temp')

start = datetime.strptime('2012.01.01 00:00', '%Y.%m.%d %H:%M')
end = datetime.strptime('2016.12.31 23:59', '%Y.%m.%d %H:%M')
timeframe = 5
n = 10
y = np.empty(n)
plt.figure(figsize=(6, 12))
cnt = 0
for symbol in ['EURJPY', 'EURUSD', 'USDJPY']:
    cnt += 1
    plt.subplot(3, 1, cnt)
    atr0 = fs.i_atr(symbol, timeframe, 1, 0)[start:end]
    mean = atr0.mean()
    open0 = fs.i_open(symbol, timeframe, 0)[start:end]
    if symbol=='EURUSD':
        tenth = (open0*100.0) - np.floor(open0*100.0)
    else:
        tenth = open0 - np.floor(open0)
    for i in range(n):
        y[i] = atr0[(tenth>=i/n) & (tenth<(i+1.0)/n)].mean() / mean
    plt.plot(y)
    plt.title('Round Number and Volatility (' + symbol + ')')
    plt.xlabel('Number (10pips=1)')
    plt.ylabel('Volatility')
    plt.legend()
    plt.axhline(y=1.0, color='black', linestyle=':')
    plt.tight_layout()
plt.savefig('round_number_and_volatility.png', dpi=150)
plt.show()

fs.remove_folder('temp')

結果

  • EURUSDとUSDJPYでは0-10pips、90-100pipsの価格帯でボラティリティがやや高めで、ラウンドナンバーが意識されているように思えなくもない
  • EURJPYでは上記の傾向は見られない

時間帯とボラティリティ (2017/12/04)

時間帯とボラティリティとの関係を調べてみる。

  • 通貨ペア:EURJPY、EURUSD、USDJPY
  • 期間:2012年1月1日〜2016年12月31日
  • 足の種類:60分
  • 時間帯の区分:0時から23時まで1時間刻み

なお、ボラティリティは各時間帯のATRの平均を全体のATRの平均で除したものとした。

検証

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

import forex_system as fs
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime

fs.remove_folder('temp')

start = datetime.strptime('2012.01.01 00:00', '%Y.%m.%d %H:%M')
end = datetime.strptime('2016.12.31 23:59', '%Y.%m.%d %H:%M')
timeframe = 60

close = fs.i_close('USDJPY', timeframe, 0)[start:end]
index = close.index
hour = fs.time_hour(index)

n = 24
x = np.arange(n)
y = np.empty([n, 3])
plt.figure(figsize=(6, 12))
cnt = 0
for symbol in ['EURJPY', 'EURUSD', 'USDJPY']:
    plt.subplot(3, 1, cnt+1)
    atr = fs.i_atr(symbol, timeframe, 1, 0)[start:end]
    mean = atr.mean()
    for i in range(n):
        temp = atr[hour==i].mean()
        y[i][cnt] = temp / mean
    plt.plot(x, y[:, cnt])
    plt.title('Hour and Volatility (' + symbol + ')')
    plt.xlabel('Hour')
    plt.ylabel('Volatility')
    plt.axhline(y=1.0, color='black', linestyle=':')
    plt.tight_layout()
    cnt += 1
plt.savefig('hour_and_volatility.png', dpi=150)
plt.show()

df = pd.DataFrame()
df['EURJPY'] = y[:, 0]
df['EURUSD'] = y[:, 1]
df['USDJPY'] = y[:, 2]
print(df)

      EURJPY    EURUSD    USDJPY
0   0.826781  0.640181  0.846515
1   0.793721  0.599766  0.855468
2   1.030448  0.782120  1.193423
3   0.980855  0.766167  1.131828
4   0.832272  0.656800  0.942155
5   0.810685  0.581162  0.913225
6   0.780100  0.528205  0.886427
7   0.821699  0.638011  0.879218
8   1.037476  1.022195  0.991075
9   1.330279  1.378958  1.149425
10  1.340930  1.391855  1.120735
11  1.200517  1.246501  0.996245
12  1.080462  1.132956  0.893253
13  1.009897  1.103229  0.860897
14  1.313742  1.636213  1.326202
15  1.382428  1.697924  1.374079
16  1.394414  1.693869  1.405629
17  1.232568  1.451308  1.243473
18  0.986616  1.136340  0.987048
19  0.898855  1.006588  0.911971
20  0.843778  0.963688  0.925485
21  0.765300  0.820246  0.819648
22  0.700646  0.649198  0.728238
23  0.605533  0.476520  0.618337

結果

  • いずれの通貨ペアでも14〜16時のボラティリティが高い

14〜16時のボラティリティが高いのは米雇用統計の発表がある14時30分とその直後だからだろう。だが、米雇用統計は月に1回であり、それ以外の日では必ずしも高くない。それだけ米雇用統計のインパクトが強いのである。

平均回帰戦略とボラティリティ

ボラティリティが平均回帰戦略にどのような影響を与えるかを調べてみる。

検証に当たっては「トレード戦略」カテゴリの「基本的な平均回帰戦略」を用いた。シグナルはそのままとして、指定したボラティリティを超えた部分をカットしてパフォーマンスがどうなるかを見てみる。

ボラティリティは平均回帰戦略に悪影響か

先ずリターンの標準偏差を求め、標準偏差の0.5、1.0、1.5、2.0の4つのパターンで調べる。

In [1]:
import forex_system as fs
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime

fs.remove_temp_folder()

symbol = 'USDJPY'
timeframe = 5
period = 10
spread = 0.0
position = 2
start = datetime.strptime('2016.01.01 00:00', '%Y.%m.%d %H:%M')
end = datetime.strptime('2016.12.31 23:59', '%Y.%m.%d %H:%M')

entry_threshold = 1.5
filter_threshold = 10

zscore1 = fs.i_zscore(symbol, timeframe, period, 'MODE_SMA', 1)
bandwalk1 = fs.i_bandwalk(symbol, timeframe, period, 'MODE_SMA', 1)
buy_entry = (zscore1 <= -entry_threshold) & (bandwalk1 <= -filter_threshold)
buy_exit = zscore1 >= 0.0
sell_entry = (zscore1 >= entry_threshold) & (bandwalk1 >= filter_threshold)
sell_exit = zscore1 <= 0.0
data = symbol, timeframe, buy_entry, buy_exit, sell_entry, sell_exit, 0.1, 0, 0
signal, lots = fs.calc_signal(data)
ret = fs.calc_ret(symbol, timeframe, signal, spread, position, start, end)
cumret = (ret + 1.0).cumprod() - 1.0
sharpe = fs.calc_sharpe(ret, start, end)
print('std\t', 'sharpe ratio')
print('org\t', sharpe)
plt.plot(cumret, label='org')

op = fs.i_open(symbol, timeframe, 0)
std = np.std(op.shift(-1)/op-1.0)
for i in range (4):
    rnd = (i + 1) * 0.5
    ret2 = ret.copy()
    ret2[ret2/std>=rnd] = std * rnd
    ret2[ret2/std<=-rnd] = std * (-rnd)
    cumret = (ret2 + 1.0).cumprod() - 1.0
    sharpe = fs.calc_sharpe(ret2, start, end)
    print(str(rnd), '\t', sharpe)
    plt.plot(cumret, label=str(rnd))

plt.title('Mean Reversion and Volatility')
plt.xlabel('Date')
plt.ylabel('Cumulative Return')
plt.legend(loc='upper left')
ax=plt.subplot()
ax.set_xticklabels(cumret.index, rotation=45)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.tight_layout()
plt.savefig('mean_reversion_and_volatility.png', dpi=150)

fs.remove_temp_folder()


std      sharpe ratio
org      2.22657550743
0.5      5.68911369135
1.0      4.93028818462
1.5      4.02796478075
2.0      3.48310576555


指定したボラティリティで超えた部分をカットした場合、いずれも元の戦略のシャープレシオを超えている。また、カットする部分が多いほどシャープレシオは高くなっている。

グラフを見ると、標準偏差1.0以上の場合では累積リターンでも元の戦略を超えている。標準偏差0.5の場合ではさすがにカットされる部分が大きすぎるようだが、それでも累積リターンは元の戦略と大差ない。

指定したボラティリティを超えた部分をカットした場合、カットされるのは損失だけではなく、利益もである。にもかかわらず、カットしたことによって累積リターンが増えたのはなぜか。これは損失のときのほうが利益のときよりボラティリティが大きく、カットされる部分がより大きいからだろう。

この結果はボラティリティが平均回帰戦略に悪影響を与えていることを示唆しているようにも思える。だが、そうだと断定するのは早計だろう。別の角度からも検証するべきかと思う

ボラティリティは避けるべきか

私は数時間程度の時間軸では為替レートは平均回帰的であるが、ファンダメンタルによる変化は回帰しないというイメージを持っている。ファンダメンタルの変化は往々にして大きなボラティリティをもたらす。こうして起きた変動は多少は戻すこともあるだろうが、基本的に戻らないと考えるのである。

すると、ボラティリティが拡大する局面でトレードを避けたならば、平均回帰戦略のパフォーマンスを向上させることができるのではないかということは容易に思いつく。この想定に基づいて、いろいろ検証してはいるのだが、今のところ、結果は出ていない。

現時点ではボラティリティはやはり避けたほうが無難だと考えている。だが、単純に避けるだけではパフォーマンスを向上させることはできず、さらに工夫を加える必要がある。

(2017/03/09更新)

直前のTR拡縮と現在のTR拡縮の関係

直前のTR拡縮と現在のTR拡縮は負の比例関係

直前のTR(トゥルー・レンジ)の拡縮と現在のTRの拡縮がどのような関係にあるか調べてみた。足の種類は5分足である。TRの拡縮は標準化している。

グラフを見ると、直前のTRの拡縮と現在のTRの拡縮とは負の比例関係にあることが分かる。

サンプルプログラム

TRは正の値しか取らないためか、拡縮を標準化すると分布に偏りがある。そこで、先ずTRを対数変換し、それから拡縮を標準化している。

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

import forex_system as fs
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime

fs.remove_temp_folder()

timeframe = 5
n = 6
x = np.empty(n)
y = np.empty(n)
plt.figure(figsize=(6, 20))
cnt = 0
for symbol in ['AUDUSD', 'EURUSD', 'GBPUSD', 'USDJPY', 'RANDOM']:
    cnt += 1
    plt.subplot(5, 1, cnt)
    for year in [2012, 2013, 2014, 2015, 2016]:
        start = datetime.strptime(str(year) + '.01.01 00:00',
                                  '%Y.%m.%d %H:%M')
        end = datetime.strptime(str(year) + '.12.31 23:59',
                                '%Y.%m.%d %H:%M')
        atr = fs.i_atr(symbol, timeframe, 1, 0)
        atr[atr==0] = np.nan
        atr = atr.dropna()
        atr = np.log(atr)
        change = atr - atr.shift(1)
        mean = np.mean(change[start:end])
        std = np.std(change[start:end])
        change = (change - mean) / std
        change0 = change[start:end]
        change1 = change.shift(1)[start:end]
        for i in range(n):
            x[i] = i - 2.5
            y[i] = np.mean(change0[(change1.shift(1) >= x[i] - 0.5) &
             (change1.shift(1) < x[i] + 0.5)])
        plt.plot(x, y, label=str(year))
    plt.title('Previous TR Change and Current TR Change (' + symbol + ')')
    plt.xlabel('Previous TR Change')
    plt.ylabel('Current TR Change')
    plt.xlim(-2.5, 2.5)
    plt.ylim(-0.2, 0.2)
    plt.legend(loc='upper right')
    plt.axvline(x=0, color='black', linestyle=':')
    plt.axhline(y=0, color='black', linestyle=':')
    plt.tight_layout()
plt.savefig('previous_tr_change_and_current_tr_change.png', dpi=150)
plt.show()

fs.remove_temp_folder()
(2017/02/12更新)