勝率とボラティリティ (2017/11/12)

利益を上げるという前提で、勝率とボラティリティの関係を考えてみる。

シミュレーション

単純化のために、

  1. 現在の足の始値でエントリー
  2. 次の足の始値でエグジット
  3. ボラティリティは一定
  4. コストは1.0で固定

と仮定して、シミュレーションを実行する。

コストを考慮しない場合、ボラティリティの大小に関わらず、勝率が50%を超えていれば利益が出る。だが、コストを考慮した場合はどうなるか。

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

import matplotlib.pyplot as plt
import numpy as np

cost = 1.0
volatility = np.empty(10)
winning_percentage = np.empty(10)
for i in range(10):
    upper_limit = 50.0
    lower_limit = 0.0
    volatility[i] = (upper_limit + lower_limit) / 2
    profit = volatility[i] - cost
    loss = volatility[i] + cost
    winning_percentage[i] = (i + 11) * 0.05
    for j in range(100):
        if (profit * winning_percentage[i] >= loss * (1 - winning_percentage[i])):
            upper_limit = volatility[i]
            volatility[i] = (upper_limit + lower_limit) / 2
            profit = volatility[i] - cost
            loss = volatility[i] + cost
        else:
            lower_limit = volatility[i]
            volatility[i] = (upper_limit + lower_limit) / 2
            profit = volatility[i] - cost
            loss = volatility[i] + cost
plt.plot(volatility, winning_percentage, label='Cost=1.0')
plt.title('Winning Percentage and Volatility')
plt.xlabel('Volatility')
plt.ylabel('Winning Percentage')
plt.xlim(1.0, 10.0)
plt.ylim(0.5, 1.0)
plt.legend()
plt.savefig('winning_percentage_and_volatility.png', format='png', dpi=150)
plt.show()

曲線より上では利益が出ており、下では損失が出ている。

ボラティリティが小さいほど勝率は高くなければならない

ボラティリティが1.0の場合、勝率が100%でようやくとんとんである。逆にボラティリティが10.0の場合、勝率が60%弱でも利益が出ている。

スキャルピングは損失が限定できるので安全に利益を上げられる、というFX業者(及びその回し者)の宣伝文句をよく見かける。確かに個々のトレードの損失は小さい。だが、トータルで勝たなければ意味がない。まして、レバレッジをかけすぎれば安全ですらない。

例えばドル円でコストが0.4pipsであるとする。もし1pipsで利食い、あるいは損切りするとする。となると、値幅はコストの2.5倍である。グラフを見ると、ボラティリティがコストの2.5倍である場合、利益を上げるためには勝率70%程度を求められているのが分かる。

これは損大利小での勝率ではない。ペイオフレシオ1.0での勝率である。これが決して容易でないことはペーパートレーダーでもない限り理解されるはずだ。

どのようなトレードスタイルを採用しようとトレーダーの自由ではある。だが、宣伝文句に踊らされるようでは仕方がない。これは少しでも考えれば、あるいは検証してみれば分かることだ。

週単位、月単位で勝つためのトレード数 (2017/10/05)

週単位、月単位で勝つには勝率がどのくらいのトレードを1日に何回行う必要があるかということを考えてみる。

ここでは「週単位、月単位で勝つ」を週単位、月単位で損益がプラスとなる確率が80%以上と見なすこととする。また、1トレード当たりの利益/損失は常に一定で「1.0」としておく。

それでは、1トレード当たりの勝率を55%とした場合、トレードを1日に何回行う必要があるかをシミュレーションしてみる。

○以下のプログラムを実行する。

threshold = 0.8
wp = 0.55

def func(n, r):
    if n == 0 or r == 0:
        return 1
    else:
        return func(n, r - 1) * (n - r + 1) / r

prob = 0.0
n_trade = -1
while (prob < threshold):
    n_trade = n_trade + 2 # 必ず奇数にする
    prob = 0.0
    for i in range(int((n_trade + 1) / 2), n_trade + 1):
        r = i
        temp = func(n_trade, r) * wp**r * (1 - wp)**(n_trade - r)
        prob = prob + temp
print("週単位で勝つための1日当たりのトレード数 = ", n_trade / 5.0)
print("月単位で勝つための1日当たりのトレード数 = ", n_trade / 20.0)

週単位で勝つための1日当たりのトレード数 =  14.2
月単位で勝つための1日当たりのトレード数 =  3.55

シミュレーションによると、週単位で勝つなら14.2回、月単位で勝つなら3.55回は必要ということになる。なかなか大変だ。

勝率55%という設定は低いように思われるかもしれない。だが、利益/損失=1.0という条件下では勝率55%でもまずまずである。

勝率とペイオフレシオ (2017/07/08)

勝率とペイオフレシオの関係がどのようであるかを調べてみる。

勝率の高い戦略が優れているとは限らない。ペイオフレシオが低ければ勝率が高くても負ける戦略となる場合がある。

逆に、ペイオフレシオの高い戦略が優れているとは限らない。勝率が低ければペイオフレシオが高くても負ける戦略となる場合がある。

要するにバランスが重要だ。それを考慮せずに「損小利大」はよいとか、「損大利小」はだめとか言っても無意味である。

さて、私は勝率が高ければペイオフレシオは低く、勝率が低ければペイオフレシオは高い、つまり、トレードオフの関係にあると考えている。勝率もペイオフレシオも高いというのが理想ではある。だが、そう簡単に実現できることではない。

もし、勝率とペイオフレシオがトレードオフの関係にあるとすれば、

平均利益 / 平均損失 = 負け数 / 勝ち数

という関係を想定することができるだろう。これを整理すると、

ペイオフレシオ = 負け数 / 勝ち数
ペイオフレシオ = (負け数 / (勝ち数+負け数)) / (勝ち数 / (勝ち数+負け数))
ペイオフレシオ = (1 - 勝率) / 勝率
ペイオフレシオ * 勝率 = 1 - 勝率
ペイオフレシオ * 勝率 + 勝率 = 1
(ペイオフレシオ + 1) * 勝率 = 1
勝率 = 1 / (ペイオフレシオ + 1)

となる。

すると、

勝率 = 傾き * (1 / (ペイオフレシオ + 1)) + 切片

というモデルを想定することができる。特に理由がなければ、傾きは1、切片は0になると考えられる。

では上のモデルをランダムウォークのデータを使って検証してみる。

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

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

②ペイオフレシオごとの勝率を求める。

n = 1000
max_iter = 10000
wp_long = np.empty(9)
wp_short = np.empty(9)
payoff_ratio_long = np.empty(9)
payoff_ratio_short = np.empty(9)

for i in range(9):
    profit = i + 1
    loss = 9 - i
    win_long = 0
    lose_long = 0
    win_short = 0
    lose_short = 0
    for j in range(max_iter):
        rnd = np.random.randn(n) * 0.01
        close = np.cumsum(rnd) + np.log(100)
        close = np.exp(close)
        count = 0
        for k in range(n):
            if count < k:
                count = k
            if close[k] >= 100 + profit:
                win_long += 1
                lose_short += 1
                break
            if close[k] <= 100 - loss:
                lose_long += 1
                win_short += 1
                break
    wp_long[i] = win_long / (win_long + lose_long)
    wp_short[i] = win_short / (win_short + lose_short)
    payoff_ratio_long[i] = profit / loss
    payoff_ratio_short[i] = loss / profit

先ず、ペイオフレシオを1:9、2:8、3:7、4:6、5:5、6:4、7:3、8:2、9:1で場合分けする。次にランダムウォークのデータを作り、それぞれのペイオフレシオでの勝敗をカウントするシミュレーションを1万回行って勝率を求める。

検証は買いの場合と売りの場合の2つのパターンで行った。同じ値幅での上昇確率は下落確率よりわずかに高く、買いが売りよりわずかに有利だと考えられるからである。これについては以下を参照。

https://storage.googleapis.com/imoz-jp/slides/160321_trading.pdf(p19-22)

③グラフを表示する。

plt.plot(payoff_ratio_long, wp_long, label='wp_long')
plt.plot(payoff_ratio_short, wp_short, label='wp_short')
plt.xlabel('Payoff Ratio')
plt.ylabel('Winning Percentage')
plt.xlim(0, 9)
plt.ylim(0, 1)
plt.legend(loc="upper right")
plt.savefig('wp_por.png', format='png', dpi=150)
plt.show()

わずかに買いの勝率が売りの勝率より高いことが分かる。

④モデルを作成する。

def model(payoff_ratio, a, b):
    return a * (1 / (1 + payoff_ratio)) + b

⑤モデルの傾きと切片を求める。

popt, pcov = curve_fit(model, payoff_ratio_long, wp_long)
slope_long = popt[0]
intercept_long = popt[1]
popt, pcov = curve_fit(model, payoff_ratio_short, wp_short)
slope_short = popt[0]
intercept_short = popt[1]

⑥モデルを表示する。

print('Long:')
print(
      '\t Winning Percentage = ', slope_long, ' * (1 / (Payoff Ratio + 1)) + ',
      intercept_long)
print('Short:')
print(
      '\t Winning Percentage = ', slope_short,
      ' * (1 / (Payoff Ratio + 1)) + ', intercept_short)

Long:
         Winning Percentage =  0.887583333429  * (1 / (Payoff Ratio + 1)) +  0.0613638886528
Short:
         Winning Percentage =  0.887583333879  * (1 / (Payoff Ratio + 1)) +  0.0510527775029

当初、期待していた「傾きが1、切片が0」となる結果とは少し違う。誤差とするにはやや大きすぎるように思う。だが、なぜこうなるかは分からない。

とりあえず目安としては買いと売りを分けずに

勝率 = 0.9 * (1 / (ペイオフレシオ + 1)) + 0.05

くらいに考えておけばいいだろうか。

これはランダムウォークで予測される勝率である。したがって、これより大きな勝率でなければランダムウォークに勝っていないことになる。

的中率とシャープレシオ (2017/07/06)

的中率とシャープレシオとの間にどのような関係があるかをシミュレーションによって調べてみる。

単純化のため、ここでいくつかの仮定を置く。

先ず、ボラティリティは1.0で固定とする。固定でさえあれば、実際にはどの数値でも結果は変わらない。

次に1年当たりのトレード数を260回とする。これは数値を変えると結果も変わるので動かせない。この数値は1年当たりの営業日が260日であることに基づく。イメージとしては毎日、上がるか下がるかを予測して売買し、1日の損益の絶対値は常に一定、と考えればいい。

最後にコストは考慮していない。

それではシミュレーションを始める。

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

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

②的中率とそれに対応したシャープレシオを計算する。

start = 50
end = 105
step = 5
n = int((end - start) / step)
accuracy = np.array(range(start, end, step)) / 100
sharpe_ratio = np.empty(n)
volatility = 1.0
trades = 260
for i in range(n):
    ret = ((volatility*accuracy[i]) - (volatility*(1.0-accuracy[i]))) * trades
    risk = volatility * np.sqrt(trades)
    sharpe_ratio[i] = ret / risk

的中率は50-100%の範囲で5%刻みとする。

③グラフを表示する。

plt.plot(accuracy, sharpe_ratio)
plt.xlabel('Accuracy')
plt.ylabel('Sharpe ratio')
plt.xlim(0.5, 1.0)
plt.savefig('sharpe_ratio_accuracy.png', dpi=150)
plt.show()

グラフを見ると、的中率とシャープレシオは比例していることが分かる。

④的中率とシャープレシオの関係を示すモデルを作成する。

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

上のグラフから以下のような関係を想定している。

シャープレシオ = 傾き * 的中率 + 切片

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

popt, pcov = curve_fit(model, accuracy, sharpe_ratio)
slope = popt[0]
intercept = popt[1]
print('Sharp Ratio = ', slope, ' * Accuracy + ', intercept)

Sharp Ratio =  32.2490309932  * Accuracy +  -16.1245154966

求められた傾き、切片によると、的中率が5%上昇するだけでシャープレシオは約1.61上昇するということが分かる。的中率で見ると、5%くらい大した違いではないように思うかもしれない。だが、これは大変な改善なのである。

もし的中率が70%であったら、

シャープレシオ = 32.2490309932 * 0.7 + (-16.1245154966) = 6.4498061986399975

となる。

シャープレシオが3.0以上なら、それは素晴らしい戦略だろう。6.0を超えるとなると、もはや尋常ではない。言い換えれば、的中率が70%に達するというのは尋常ではないのである。

機械学習、またはディープラーニングで株価予測的中率が○○%になった、というような話をよく聞く。だが、実際に尋常ではない素晴らしい成果が得られたのか、それとも何らかの間違いを犯していたり、単なる誇大広告であったりしないか、しっかりと吟味する必要がある。

週単位、月単位で勝つためのシャープレシオ (2017/05/14)

あるトレード戦略が週単位、月単位で勝つためにはどの程度のシャープレシオが必要かということを考えてみる。

勝てる戦略とは

例えば、年単位の利益が10%、年単位のリスクが10%だとする。すると、シャープレシオは

\[ sharpe = 0.1 / 0.1 = 1.0 \]

となる。したがって、1年後の想定利益は10±10%、つまり0~20%となる。

ところで、リスクは1σで計算している。したがって、想定利益は68.27%の確率でその範囲内に収まる。つまり1年後の利益がプラスである確率は68.27%以上となる。なぜ68.27%「以上」になるかというと、年単位20%を越える利益となる可能性もあるからである。

もう少し正確には

\[ prob = 68.27 + (100 - 68.27) / 2 = 84.135 \]

で、84.135%となる(手計算なので誤差あり)。もちろん、これは机上の空論だ。だが、一応の目安にはなる。

ここではプラスとなる確率が84.135%以上であれば「勝てる戦略」と見なすことにする。すると、シャープレシオが1.0以上なら年単位では「勝てる戦略」と言える。同様に週単位、月単位のシャープレシオが1.0以上であれば、週単位、月単位で「勝てる戦略」と言える。

シャープレシオの計算式

ここで、週単位、月単位、年単位のシャープレシオの計算式を定義しておく。

ここでは1週間を5営業日、1ヶ月を20営業日、1年を260営業日としておく。また、1日単位の利益をr、1日単位のリスクをsとする。また、リスクフリーレートは考慮しないことにする。

すると、

\[ sharpe_{weekly} = \frac {5r} {\sqrt{5s}} \] \[ sharpe_{manthly} = \frac {20r} {\sqrt{20s}} \] \[ sharpe_{yearly} = \frac {260r} {\sqrt{260s}} \]

となる。

週単位で勝つためのシャープレシオ

では、先ず、週単位のシャープレシオが1.0以上であるには、年単位のシャープレシオがどのくらい以上でなければならないかを考える。

\[ \frac {sharpe_{yearly}} {sharpe_{weekly}} = \frac {\frac {260r} {\sqrt{260s}}} {\frac {5r} {\sqrt{5s}}} \] \[ {sharpe_{weekly}} = 1, \ \frac {sharpe_{yearly}} {1} = \frac {\frac {260r} {\sqrt{260s}}} {\frac {5r} {\sqrt{5s}}} \] \[ sharpe_{yearly} = \frac {\frac {260r} {\sqrt{260s}}} {\frac {5r} {\sqrt{5s}}} \] \[ = \frac {260r} {\sqrt{260s}} \times \frac {\sqrt{5s}} {5r} \] \[ = \frac {52} {\sqrt{52}} \times \frac {1} {1} \] \[ = \frac {52} {\sqrt{52}} \] \[ = \frac {52 \times \sqrt{52}} {\sqrt{52} \times \sqrt{52}} \] \[ = \frac {52 \times \sqrt{52}} {52} \] \[ = \sqrt{52} \] \[ \approx 7.21 \]

となる。つまり、週単位のシャープレシオが1.0以上であるには、年単位のシャープレシオが約7.21以上である必要がある。

月単位で勝つためのシャープレシオ

次に、月単位のシャープレシオが1.0以上であるには、年単位のシャープレシオがどのくらい以上でなければならないかを考える。

\[ \frac {sharpe_{yearly}} {sharpe_{monthly}} = \frac {\frac {260r} {\sqrt{260s}}} {\frac {20r} {\sqrt{20s}}} \] \[ {sharpe_{monthly}} = 1, \ \frac {sharpe_{yearly}} {1} = \frac {\frac {260r} {\sqrt{260s}}} {\frac {20r} {\sqrt{20s}}} \] \[ sharpe_{yearly} = \frac {\frac {260r} {\sqrt{260s}}} {\frac {20r} {\sqrt{20s}}} \] \[ = \frac {260r} {\sqrt{260s}} \times \frac {\sqrt{20s}} {20r} \] \[ = \frac {13} {\sqrt{13}} \times \frac {1} {1} \] \[ = \frac {13} {\sqrt{13}} \] \[ = \frac {13 \times \sqrt{13}} {\sqrt{13} \times \sqrt{13}} \] \[ = \frac {13 \times \sqrt{13}} {13} \] \[ = \sqrt{13} \] \[ \approx 3.61 \]

となる。つまり、月単位のシャープレシオが1.0以上であるには、年単位のシャープレシオが約3.61以上である必要がある。

まとめ

年単位のシャープレシオが3.61以上というのはなかなか難しい。まして、7.21以上というのは更に難しい。したがって、少なくとも単体の戦略で週単位、月単位で勝つということはなかなか容易ではないと言える。

逆に言えば、週単位、月単位で勝つためには複数の戦略が必要ということになるだろう。しかし、複数の戦略を用いるにしても、お互いの戦略のパフォーマンスが正の相関であれば、あまり意味がない。しかし、無相関、負の相関の戦略同士を組み合わせるというのは「言うは易く行うは難し」なのである。

だとすれば、週単位、月単位で勝つというのは理想として求めるべきものではある。だが、実際には難しいこととして、週単位、月単位で負けることはよくあること、とゆったりした気持ちで取り組むべきだろう。