勝率とドローダウン期間の関係 (2018/05/23)

勝率とドローダウン期間の関係を調べてみる。トレーダーはドローダウン期間が続くと、自分の戦略に疑いを抱き、やたらと戦略に手を加えたくなるものである。だが、ドローダウンはトレードには付き物であって、ある程度は初めから覚悟しなければならない。

その「ある程度」とはどのくらいなのか、ということをシミュレーションによって調べてみる。単純化のためにいくつかの仮定を置く。

  • トレードのリターンは1%、または-1%のいずれかとする。つまりペイオフレシオ=1.0である。
  • トレードは1日に1回とする。
  • 1年の営業日を260日とする。

シミュレーション

以上の仮定に基づいて1年間トレードするという試行を25回行い、平均のドローダウン期間を勝率ごとに求める。勝率は5%刻みで55%-100%まで調べる。乱数を用いているので、結果は毎回必ずしも同じではない。

○以下のコマンドをIPythonに入力して「Enter」キーを押す。

import forex_system as fs
import numpy as np
import pandas as pd

max_iter = 25
durations = np.zeros(20)
print('win_per' + '\t' + 'durations')
for i in range(10):
    win_per = (i + 11) / 20
    for j in range(max_iter):
        ret = np.random.choice([0.01, -0.01], 260, p=[win_per, 1 - win_per])
        ret = pd.Series(ret)
        temp = fs.get_durations(ret, 1440)
        durations[i] += temp
    durations[i] = durations[i] / max_iter
    print(str(win_per) + '\t' + str(durations[i]))

win_per durations
0.55    83.28
0.6     53.52
0.65    39.04
0.7     24.2
0.75    15.44
0.8     10.96
0.85    7.84
0.9     6.08
0.95    3.6
1.0     0.0

ペイオフレシオ1.0で勝率60%はなかなかのパフォーマンスである。それでも53.52営業日(2ヶ月強)のドローダウン期間がある。勝率70%となると、むしろありえないくらいの高パフォーマンスである。それでも24.2営業日(1ヶ月強)のドローダウン期間がある。

一定の勝率でドローダウン期間を短縮するにはトレード回数を増やすしかない。だが、それにはトレード間に相関性がないことが前提で、似たようなトレードを数多くやってもドローダウン期間の短縮にはあまり効果がない。

トレーダーとしてはドローダウンの回復に1ヶ月も2ヶ月もかかったのでは大変つらいことである。だが、勝率60%、70%でもこの程度のドローダウン期間は避けがたいのである。だとすれば数ヶ月のドローダウン期間は覚悟すべきだろう。

2%ルール (2017/10/30)

2%ルールとは

2%ルールとは投資のリスク管理において、損失が資金の2%に達したら損切りする、というルールである。だが、なぜ2%なのかという根拠を私は寡聞にして知らない。恐らく経験則なのだろう。

シミュレーション

そこで、仮に2%にするとどうなるかということを簡単に検証してみた。

単純化のため、先ずペイオフレシオ = 1.0、つまり平均利益=平均損失と仮定する。次に、勝率がそれぞれ30%、40%、50%、60%、70%であるトレーダーが260回トレードしたとする。1年を260営業日とし、毎営業日1回トレードするとすれば、1年後にどうなったかということを見るわけである。そして、ルールを1%、2%、3%で場合分けした。

○IPythonに以下のコマンドを入力して実行する。

import matplotlib.pyplot as plt
import numpy as np

n_trades = 260
n_tests = 10000
balance = np.empty(4)
risk = np.array(range(4)) / 100
for i in range(5):
    win_per = (i + 3) / 10
    for j in range(4):
        temp = np.empty(n_trades)
        mean = 0
        for k in range(n_tests):
            change = np.random.choice([risk[j], -risk[j]], n_trades,
                                      p=[win_per, 1.0-win_per])
            for l in range(n_trades):
                if l == 0:
                    temp[0] = 1.0 + change[l]
                else:
                    temp[l] = temp[l-1] * (1.0 + change[l])
            mean += temp[n_trades-1]
        mean = mean / n_tests
        balance[j] = mean
    plt.plot(risk, balance, label=str(win_per))
plt.title('Balance and Risk')
plt.xlabel('Risk')
plt.ylabel('Balance')
plt.xlim(0.0, 0.03)
plt.ylim(0.0, 2.0)
plt.xticks([0.00, 0.01, 0.02, 0.03])
plt.legend(title='win_per', fontsize = 'small')
plt.axhline(y=1.0, color='black', linestyle=':')
plt.tight_layout()
plt.savefig('balance_and_risk.png', dpi=150)
plt.show()

シミュレーションは1万回繰り返され、その平均を取った。乱数を使っているので、必ずしも同じ結果にはならないが、大体このようである。

勝てない人は何%にしても勝てない

グラフによると、2%ルールの場合、勝率30%のトレーダーはほとんどの資金を、勝率40%のトレーダーでも大半の資金をたったの1年で失っている。

そして1%ルールであれば損失が減り、3%ルールであれば損失が増えている。とすると、許容する損失が小さいほど損失は少ないと言える。だが全体として負けていることに変わりはない。

一方、勝率50%のトレーダーは許容する損失の大小の影響は受けず、勝ちもしなければ負けもしない。

勝率60%、70%のトレーダーは2%ルールの場合、たったの1年で資金を倍以上にしている。

そして1%ルールであれば利益が減り、3%ルールであれば利益が増えている(グラフでは資金の増え方がすごいので上の方をカットした)。とすると、許容する損失が小さいほど利益が少ないと言える。だが全体として勝っていることに変わりはない。

要するに重要なのは「勝てるかどうか」であって、「何%にするか」ではないのである。勝てない人は許容する損失を小さくしたところで資金の減り方が遅くなるだけのことであって、資金が減ることに変わりはない。勝てない人は勝てるようになるまでデモ口座で訓練するのが身のためだ。

勝てる人は何%にしても勝てる?

では、勝てる人は何%にしても勝てるのだろうか。今回のシミュレーションの結果を見ると、そのように思えなくもない。だが、実際には違うのである。

今回、許容する損失は最大でも3%にすぎなかった。これをどんどん大きくすると、勝てる人ですら最終的には負けてしまうということが起きる。これについてはケリー基準、最適レバレッジを理解する必要があるが、それは別の機会に触れることとする。

破産レバレッジ (2017/04/15)

破産させるレバレッジを考えてみる。ここではそれを破産レバレッジと呼ぶことにする。

破産レバレッジの計算式

計算式は

破産レバレッジ >= 1 / 最小リターン * (-1)

とする。

もし最小リターンが-1%だったとする。すると破産レバレッジは1 / (-0.01) * (-1) = 100.0となる。最悪で1%の負けでもレバレッジ100倍なら100%の負け、つまり破産となるわけである。

最悪の負けはすべてのトレードの中で少なくとも1つはある。それがトレードの最初であろうと、最後であろうと、それ以外であろうと関係ない。破産レバレッジでそのトレードが行われた瞬間、たちまち破産する。

もちろん、最小リターンがマイナスでない場合、つまりすべてのトレードで1度も負けがない場合、破産レバレッジは意味をなさない。あくまでも勝つこともあれば負けることもあるトレードというのが前提である。

シミュレーション

さて、破産レバレッジをシミュレーションで確認してみる。ここに1トレード当たりの期待利益が0.1%、リスクが0.5%の戦略があるとする。トレードを100回行うとして実行してみる。

以下のコマンドを実行してシミュレーションを行う。

import matplotlib.pyplot as plt
import numpy as np

trades = 100
ret = np.random.normal(0.001, 0.005, trades)
x = np.array(range(1, 101))
y = np.zeros(trades)
for i in range(trades):
    if i == 0:
        y[i] = 1.0 * (1.0 + ret[i])
    else:
        y[i] = y[i-1] * (1.0 + ret[i])
    if y[i] < 0.0:
        y[i] = 0.0
mean = np.mean(ret)
std = np.std(ret)
worst_kelly = 1.0 / np.min(ret) * (-1)
ax=plt.subplot()
plt.plot(x, y)
plt.xlabel('Trades')
plt.ylabel('Equity curve')
plt.text(0.05, 0.9, 'Worst Kelly = ' + str(worst_kelly),
         transform=ax.transAxes)
plt.savefig('bankruptcy_leverage1.png', dpi=150)
plt.show()
plt.close()

n = 10000
equity_curve = np.empty(trades)
x = np.array(range(n)) / 100
y = np.empty(n)
for j in range(n):
    leverage = j / 100
    for i in range(trades):
        if i == 0:
            equity_curve[i] = 1.0 * (1.0 + ret[i] * leverage)
        else:
            equity_curve[i] = equity_curve[i-1] * (1.0 + ret[i] * leverage)
        if equity_curve[i] < 0.0:
            equity_curve[i] = 0.0
    y[j] = equity_curve[trades-1]
argmin = np.argmin(y)
ax=plt.subplot()
plt.plot(x, y)
plt.axvline(x=worst_kelly, color='green')
plt.axvline(x=x[argmin], color='green')
plt.axhline(y=1.0, color='red')
plt.xlabel('Leverage')
plt.ylabel('Balance')
plt.text(0.05, 0.9, 'Worst Kelly(actual value) = ' + str(x[argmin]),
         transform=ax.transAxes)
plt.text(0.05, 0.85, 'Worst Kelly = ' + str(worst_kelly),
         transform=ax.transAxes)
plt.savefig('bankruptcy_leverage2.png', dpi=150)
plt.show()
plt.close()

シミュレーション結果によると、破産レバレッジは63.7157243783で、実測値は63.72となっている。実測値は0.01のインターバルで測っているので、もっと細かくやれば理論値と一致するだろう。最適レバレッジの計算に比べれば非常に単純明快であり、近似式も必要ない。

永遠にトレードすれば破産?

ところで、最適レバレッジではトレード数が増えれば、リターンの平均、標準偏差は理論値に収束していく。だが、最小リターンというのは曲者で、わずか1回の発生でも最小リターンとなりうる。つまり、トレード数を1万回、100万回と増やしていけば最小リターンはより小さくなりうるのである。

最小リターンがより小さくなるなら、破産レバレッジもより小さくなるだろう。言い換えると、トレードを永遠に続ければ、いずれは破産するだろう。

ただ、上のコマンドの「trades」の数を変更してトレードを50万回やっても破産レバレッジは45倍前後である。日本国内の規制である25倍よりはまだ大きい。

トレーダーは果たして一生のうちに50万回もトレードできるのだろうか。人にもよるだろうが、それほど多くはないだろう。だとすれば、トレードしていればいずれ破産するというのは杞憂だ。

もちろん、1トレード当たりの期待利益が0.1%、リスクが0.5%の戦略であればの話である。期待利益がマイナスの戦略ならレバレッジなどは関係ない。遅かれ早かれ破産するので、トレードはやめたほうがいい。

最適レバレッジの近似式の誤差 (2017/04/14)

ケリー基準によって最適レバレッジを計算する近似式にはわずかな誤差がある。その誤差は無視できるのかどうかを考えてみる。

最適レバレッジの近似式

近似式は

最適レバレッジ = リターンの平均 / (リターンの標準偏差 * リターンの標準偏差)

となっている。

本来の式はレバレッジについて級数展開して求める。レバレッジが小さい場合、第3項以降は無視できるほど小さくなる。そこで、近似式では初めの2項のみを用いて求めているのである。

逆に、レバレッジが小さくない場合、近似式はかなり大きな誤差を生じることになる。細かい点については以下のページがとても参考になる。

http://www.geocities.jp/y_infty/management/criterion_3.html

シミュレーション

では、どのくらいの誤差が生じうるかということを調べてみることとする。

ここに1トレード当たりの期待利益が0.1%、リスクが0.5%の戦略があるとする。近似式を用いると、

最適レバレッジ = 0.001 / (0.005 * 0.005) = 40.0

となる。この戦略に基づき、乱数を用いて100回トレードを行うシミュレーションを行ってみる。

以下のコマンドを実行してシミュレーションを行う。

import matplotlib.pyplot as plt
import numpy as np

trades = 100
ret = np.random.normal(0.001, 0.005, trades)
x = np.array(range(1, 101))
y = np.zeros(trades)
for i in range(trades):
    if i == 0:
        y[i] = 1.0 * (1.0 + ret[i])
    else:
        y[i] = y[i-1] * (1.0 + ret[i])
    if y[i] < 0.0:
        y[i] = 0.0
mean = np.mean(ret)
std = np.std(ret)
kelly = mean / (std * std)
ax=plt.subplot()
plt.plot(x, y)
plt.xlabel('Trades')
plt.ylabel('Equity curve')
plt.text(0.05, 0.9, 'Kelly Approximation = ' + str(kelly),
         transform=ax.transAxes)
plt.savefig('optimal_leverage1.png', dpi=150)
plt.show()
plt.close()

n = 10000
equity_curve = np.empty(trades)
x = np.array(range(n)) / 100
y = np.empty(n)
for j in range(n):
    leverage = j / 100
    for i in range(trades):
        if i == 0:
            equity_curve[i] = 1.0 * (1.0 + ret[i] * leverage)
        else:
            equity_curve[i] = equity_curve[i-1] * (1.0 + ret[i] * leverage)
        if equity_curve[i] < 0.0:
            equity_curve[i] = 0.0
    y[j] = equity_curve[trades-1]
argmax = np.argmax(y)
ax=plt.subplot()
plt.plot(x, y)
plt.axvline(x=kelly, color='green')
plt.axvline(x=x[argmax], color='green')
plt.axhline(y=1.0, color='red')
plt.xlabel('Leverage')
plt.ylabel('Balance')
plt.text(0.05, 0.9, 'Kelly = ' + str(x[argmax]), transform=ax.transAxes)
plt.text(0.05, 0.85, 'Kelly Approximation = ' + str(kelly),
         transform=ax.transAxes)
plt.savefig('optimal_leverage2.png', dpi=150)
plt.show()
plt.close()

乱数を使っているので結果は毎回違う。このシミュレーションを繰り返して誤差の小さいケースと大きいケースとを選んでみた。

誤差の小さいケース

先ずは誤差が小さいケースの例を見てみよう。

誤差は0.01以下である。この程度の誤差であれば無視してもよさそうだ。

誤差の大きいケース

次に誤差が大きいケースを見てみる。

誤差は15以上で小さくない。より問題なのは近似式での最適レバレッジが本来の最適レバレッジでは破産レベルになっていることだ。これはさすがに無視できない。

近似式は使わないべきか

誤差の大きいケースを見ると、近似式は使うべきではないようにも思える。だが、このような誤差は何十回もシミュレーションをして、ようやく1回見かけるくらいのレアなものである。

また、誤差の大きいケースでは本来の最適レバレッジは38.11倍となっている。これが近似式では過大評価されて53.47倍となっている。だが、現在の日本の規制ではレバレッジは25倍までである。

日本の規制にしたがってトレードしている限り、近似式を用いても問題はないように思える。もちろん、最適レバレッジそのものはあくまでも目安である。取りうるリスクの上限と考えるべきだ。

最適レバレッジ

レバレッジをどのくらいにすれば利益を最大化できるかということを考えてみる。

レバレッジは高いほどいいのか

FXはレバレッジを使えば少ない資金で大きな利益を挙げられる、という宣伝文句をよく見かける。だが正確には、大きな損失を蒙ることもある、と付け加えるべきだ。宣伝する側は都合のいい面しか強調しないから困ったものである。

「トータルで勝てる人なら大きな損失を大きな利益でカバーして余りあるから、やはりレバレッジは高いほどいいのではないか」と考える人もいるだろう。果たしてそうなのか、これから検討してみる。

レバレッジ1倍で運用した場合の資産曲線

ここに1年当たり100トレード、1トレード当たりの期待利益が0.1%、リスクが0.5%の戦略があるとする。単純化して考えると、1年当たりの期待利益は0.1% * 100 = 10.0%、リスクは√Tルールに基づけば0.5% * sqrt(100) = 5.0%となる。したがって、シャープレシオは10.0 / 5.0 = 2.0となり、戦略としてはまずまずである。

この戦略に基づいて100回トレードを行うシミュレーションをやってみる。乱数を使っているので、結果は毎回違う。シミュレーション結果の利益、リスクも必ずしも指定した数値にはならないことを予め断っておく。

それはさて、先ずはレバレッジ1倍で運用した場合の資産曲線を見てみる。

In [1]:
import matplotlib.pyplot as plt
import numpy as np

trades = 100
ret = np.random.normal(0.001, 0.005, trades)
equity_curve = np.zeros(trades)
for i in range(trades):
    if i == 0:
        equity_curve[i] = 1.0 * (1.0 + ret[i])
    else:
        equity_curve[i] = equity_curve[i-1] * (1.0 + ret[i])
    if equity_curve[i] < 0.0:
        equity_curve[i] = 0.0
mean = np.mean(ret)
std = np.std(ret)
kelly = mean / (std * std)
ax=plt.subplot()
plt.plot(equity_curve)
plt.xlabel('Trades')
plt.ylabel('Equity curve')
plt.text(0.05, 0.9, 'Kelly = ' + str(kelly), transform=ax.transAxes)
plt.savefig('optimal_leverage1.png', dpi=150)
plt.show()


初期資産は1.0から開始し、そこから何%増減したかを見る。グラフによると最終資産はおよそ1.1となっているので、10%の利益であったことが分かる。

最適レバレッジの計算

グラフに「Kelly = 41.2146523106」とあるが、これはケリー基準による最適レバレッジである。

ケリー基準の計算には本来の計算式と簡易式とがあるが、ここでは

最適レバレッジ = 期待利益 / (リスク * リスク)

という簡易式を用いる。

期待利益が0.1%、リスクが0.5%である場合は

0.001 / (0.005 * 0.005) = 40.0

でレバレッジ40倍が最適レバレッジとなる。

レバレッジ別の最終資産

最後にレバレッジ別の最終資産を見てみる。

In [2]:
n = 100
result = np.zeros(n)
for j in range(n):
    leverage = j
    for i in range(trades):
        if i == 0:
            equity_curve[i] = 1.0 * (1.0 + ret[i] * leverage)
        else:
            equity_curve[i] = equity_curve[i-1] * (1.0 + ret[i] * leverage)
        if equity_curve[i] < 0.0:
            equity_curve[i] = 0.0
        result[j] = equity_curve[trades-1]
plt.plot(result)
plt.axhline(y=1.0, color='red')
plt.axvline(x=kelly, color='black', linestyle=':')
plt.xlabel('Leverage')
plt.ylabel('Balance')
plt.savefig('optimal_leverage2.png', dpi=150)
plt.show()


縦の点線はケリー基準による最適レバレッジの位置である。その付近で利益が最大化していることが分かる。必ずしも一致しないのはこれが簡易式だからである。

利益を最大化した場合、最終資産は約9.0である。800%もの利益があったことになる。レバレッジ1倍のときは10%程度であったのだから、レバレッジの力は偉大である。

だが、最適レバレッジを超えると利益が減っていくことが分かる。赤の水平線は初期資産である1.0の水準だが、レバレッジ60倍を超えた当たりで最終資産が水平線を下回っている。期待利益がプラスであるにもかかわらず、損失を招いているのである。

そしてレバレッジ80倍手前で最終資産は0になっている。つまり破産である。

最適レバレッジならよいのか

検証結果によると、例え利益の期待値がプラスであってもレバレッジは高いほどよいとは言えないことになる。トレードはリスク管理だけで利益を挙げられるわけではない。だが、それをしっかりやっていないと、トータルで勝てるはずのトレードでも損失を蒙り、さらには破産するという結果になる。

では、最適レバレッジを計算してそれを適用すればよいのだろうか。シミュレーションでは期待利益はプラスに設定してあり、勝ちは約束されている。それでも乱数を使っていることから生じるぶれがある。まして、実際のトレードで勝ちが約束されているなどということはありえない。

したがって、実際のトレードはシミュレーションよりはるかに不確実性が高い。算出した最適レバレッジもはるかに大きなぶれがあることを想定しなければならない。

適用するレバレッジが最適レバレッジより小さい場合、利益の最大化はできないが、期待利益がプラスである限り、資産を増やすことはできる。だが、適用するレバレッジが最適レバレッジより大きい場合、利益の最大化ができないばかりか、期待利益がプラスであったとしても資産を減らしたり、最悪、破産したりするのである。

だとすれば、実際に使用するレバレッジは最適レバレッジより小さくしたほうが安全である。「最適レバレッジ」とは言っても、むしろこれは上限と考えるべきなのだ。慣習的には「ハーフケリー」などと言って、算出された最適レバレッジの2分の1を適用するということも行われている。

複利運用でない場合

ケリー基準というのは実は複利運用が前提となっている。複利運用でない場合、レバレッジが高いからといって必ずしも損失や破産をもたらさない。しかし、複利にしなければ資産は算術的にしか増えないのである。

資産を低レバレッジで幾何学的に増やすのと高レバレッジで算術的に増やすのとではどちらがよいか。トレード期間が長くなれば、いずれ「低レベレッジ + 幾何学的」が「高レバレッジ + 算術的」を上回るだろう。だが、この問題については私の知識が不足しているので、これ以上は述べない。

(2017/03/10更新)