破産レバレッジ (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更新)

勝率とドローダウン期間の関係

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

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

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

以上の仮定に基づいて1年間トレードするという試行を10回行い、平均のドローダウン期間を勝率ごとに求める。勝率は5%刻みで5%-100%まで調べる。結果は以下の通りである。

win_per durations
0.05    259.0
0.1     259.0
0.15    258.9
0.2     258.6
0.25    257.5
0.3     256.4
0.35    256.9
0.4     238.9
0.45    240.0
0.5     160.0
0.55    109.7
0.6     50.6
0.65    35.2
0.7     19.2
0.75    16.6
0.8     10.8
0.85    7.6
0.9     5.8
0.95    3.6
1.0     0.0

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

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

サンプルプログラム

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

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

durations = np.zeros(20)
print('win_per' + '\t' + 'durations')
for i in range(20):
    win_per = (i + 1) / 20
    for j in range(10):
        ret = np.random.choice([0.01, -0.01], 260, p=[win_per, 1 - win_per])
        ret = pd.Series(ret)
        temp = fs.calc_durations(ret, 1440)
        durations[i] += temp
    durations[i] = durations[i] / 10
    print(str(win_per) + '\t' + str(durations[i]))
(2017/02/08更新)

許容損失は資金の何%に抑えるべきか

ネットでは2%が多数派のようだが…

許容損失は資金の何%に抑えるべきかという議論がある。FXサイトなどを見ると、どうやら2%とするのが通説のようである。ところが、2%とする根拠は何かというと、ちゃんとした説明は私の知る限り、見当たらない。どうも有名なトレード本に2%がいいと書いてあるから、ということらしい。

私はいつも思うのだが、有名な本に書かれているから、有名な人が言ったから、という理由で無批判に受け入れてしまうというのはいかがなものだろうか。しかも、そういう意見がコピペを繰り返して、いつの間にかに通説になっていくように思われる。この議論に限ったことではないが、他人の言葉などどうでもよく、自分で検証してみるということが大事だと思う。

2%ではすぐに資金を溶かしてしまうことも

というわけで、簡単な検証をしてみた。

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

このようなシミュレーションを1万回行い、その平均を取った。その結果が以下のグラフである。

乱数を使っているので、必ずしも同じ結果にはならないが、大体このようである。グラフによると、許容損失を2%とした場合、勝率30%のトレーダーはほとんどの資金を、勝率40%のトレーダーでも大半の資金をたったの1年で失っている。

そして許容損失が1%であれば損失が減り、3%であれば損失が増えている。とすると、許容損失は少ないほどいいと言える。

勝てなければ何%にしても勝てない

一方、勝率50%のトレーダーは許容損失の大きさの影響は受けず、勝ちもしなければ負けもしない。まあ、当たり前だが。

勝率60%、70%のトレーダーは許容損失を2%とした場合、たったの1年で資金を倍以上にしている。そして許容損失が1%であれば利益が減り、3%であれば利益が増えている(グラフでは資金の増え方がすごいので上の方をカットした)。とすると、許容損失は多いほどいいと言える。

以上、検証結果を総合すると、「資金の何%」という議論は無意味で、勝てるトレーダーはどんどんリスクを取っていいし(注)、勝てないトレーダーはリスクを減らす、というよりトレードしないのが最善ということになりそうだ。

(注)ところがそうはいかない場合もある。「リスク管理」カテゴリの「最適レバレッジ」の記事を参照。

サンプルプログラム

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

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()
(2017/02/09更新)