最適レバレッジ

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

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

日本国内では規制があって、レバレッジは25倍までとなっている。そこで、規制を受けない国外の業者はレバレッジの高さを売りにして日本から顧客を集めようとしたりする。だが、日本の規制を超えるレバレッジなど本当に必要なのだろうか。

要は勝てばいいのだ、負けたときのことなど考える必要はない、という人も中にはいるかもしれない。確かに勝率が100%なら負けたときのことを考える必要はないし、レバレッジも「多々益々弁ず」だが、果たしてそのようなことが可能なのだろうか。勝つこともあれば負けることもあるが、長期的には利益を積み上げている、というのが現実的だ。

では、負けることがあるトレードにおいても本当にレバレッジは高いほどよいのだろうか。負けることがあっても長期的に利益を挙げられる戦略ならやはりレバレッジは高いほどよいのではないか。答えは「否」だが、レバレッジは高いほどよいわけではないという認識がどうも常識とまではなっていないように思われる。

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

レバレッジ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回トレードを行うシミュレーションをやってみる。乱数を使っているので、結果は毎回違う。シミュレーション結果の利益、リスクも必ずしも指定した数値にはならない。試行回数を増やせば指定した数値に近づくが、100回程度の試行ではかなりの乖離がある。

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

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

最適レバレッジの計算

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

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

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

という簡易式を用いる。

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

0.001 / (0.005 * 0.005) = 40.0

でレバレッジ40倍が最適レバレッジとなる。だが、シミュレーションでは乱数を使っており、期待利益、リスクが必ずしも指定した数値にはならないので、レバレッジも上の計算通りにはならない。

レバレッジ別の最終資産

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

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

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

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

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

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

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

では、最適レバレッジを計算してそれを適用すればよいのか。実はそうではない。シミュレーションでは期待利益はプラスに設定してあり、これは間違いない。それでも乱数を使っていることから生じるぶれがある。まして、実際のトレードでは期待利益が確実にプラスであるなどという根拠はどこにもない。実際のトレードはシミュレーションよりはるかに不確実性が高いものであり、算出した最適レバレッジも大きなぶれがあることを想定しなければならない。

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

「最適レバレッジ」とは言っても、むしろこれは上限と考えるべきなのだ。

サンプルプログラム

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

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()

②以下のコマンドを「IPython console」にコピー&ペーストして「Enter」キーを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()
(2017/02/09更新)

トレード戦略の分類

トレード戦略はトレーダーの数だけ存在する、というより、1人で複数の戦略を持つであろうことを考えると、トレーダーの数以上に存在するのだろう。それらを特徴に基づいて分類するにしても、なかなか大変なことである。下のSlideShareの4ページでは5つの基本的なクオンツ戦略に分類しているのが参考になる。

http://www.slideshare.net/JessStauth/d-30965323

これによると、戦略は

  1. 平均回帰戦略:上がるものは下がる。

  2. モメンタム戦略:トレンドは友である。

  3. バリュー戦略:安く買って高く売る。

  4. センチメント戦略:噂で買って事実で売る。

  5. 季節性戦略:5月に売る。

に分類されている。

続くページでは具体例を紹介しながら各戦略について説明されているが、ここでは私の独断と偏見による解釈を用いて、各戦略について簡単に説明する。

平均回帰戦略(上がるものは下がる)

上がるものも永遠に上がり続けることはない。いつかは必ず下がるものである。下がるものも永遠に下がり続けることはない。いつかは必ず上がるものである。

上がったものは下がり、下がったものは上がって、いずれ平均に戻ってくる。そこで、平均より下がれば買い、上がれば売る。これが平均回帰戦略である。ぐだぐだ書いたが、要は逆張りである。

ところで、平均回帰について説明すると、ならば平均の下で買い、上で売れば必ず勝てるのではないか、と勘違いする人が時々いるが、実際にはそうはいかない。平均自体も動くので、平均の下で買っても価格が平均に戻ったときには平均は買った価格より下になっていることもあれば、平均の上で売っても価格が平均に戻ったときには平均は売った価格より上になっていることもあり、そのような場合は負けとなる。

平均回帰戦略で勝つためには価格がある程度、平均から乖離しており、かつ平均の動きが水平に近くなるタイミングを測る必要がある。

モメンタム戦略(トレンドは友である)

トレンドは友、というのはtrendとfriendをかけた洒落だが、米国の投資家ジョージ・ソロスの言葉らしい。別に誰の言葉でも構わないが、トレンドとは仲良くしろ、逆らうな、従え、ということのようだ。つまりはトレンドフォロー、順張りである。

平均回帰戦略の逆で、上がれば、その勢い、つまりモメンタムに付いて行って買い、下がれば、そのモメンタムに付いて行って売るのである。では、順張りと逆張り、どちらの戦略が正しいのだろうか。

時々、順張りが正しく、逆張りは間違っている、というような意見を見かけるが、それは違うと私は思う。値動きというのは時には平均回帰的であり、時にはモメンタム的であるものだ。したがって状況に合った戦略を採用しなければならない。言うは易く行うは難し、だが。

バリュー戦略(安く買って高く売る)

「安く買って高く売る」を「下がったら買って上がったら売る」と解してはいけないのだろう。それだと平均回帰戦略と変わらない。「価格が本来の価値より安ければ買い、高ければ売る」と解すべきなのだろう。

本来の価値を知るためには価格以外の指標が必要である。この辺が過去の価格さえ分かればよい平均回帰戦略との違いである。FXの場合、各国の経済指標などを見ることになるのだろうが、少なくとも短期トレードではこれらの指標は使いにくい。

私は取引する通貨ペア以外の通貨ペアの価格を用い、取引する通貨ペアのあるべき価格を算出して、それより高いか安いかで売買するという手法をバリュー戦略としてもいいのではないかなと考えている。

センチメント戦略(噂で買って事実で売る)

「噂で買って事実で売る」とは、例えば米国の利上げ観測が持ち上がったらドルが高くなるだろうと予測して買い、実際に利上げが決まったらもはやこれ以上は高くならないだろうと予測して売る、といったような戦略である。

もし、これをシステムトレードに導入するのであれば、価格などを用いた定量分析だけではなく、テキストなどを用いた定性分析も必要になるだろう。

季節性戦略(5月に売る)

「5月に売る」というのは株価は5月以降に下がる傾向があるので、5月になったら売るということである。実際に5月以降に下がる傾向があるのかは怪しいが、年、四半期、月、週、日などを通した周期性があると考えて、それに基づいた売買を行うのが季節性戦略である。

(2016/09/15更新)

時刻効果を用いた季節性戦略

時刻効果とは時刻が価格変動に対して与える効果のことである。例えば、ある時刻では上昇(下落)する傾向がある、ボラティリティが大きくなる(小さくなる)傾向がある、などである。

日中効果と呼ぶほうが一般的なようにも思うが、それはどちらかというと昼(取引時間)と夜(取引時間外)に着目した呼び方であるように思われる。ここでは昼、夜というくくりではなく、時刻に着目することとする。

それはさて、戦略はtime_of_day_effect、通貨ペアはUSDJPY、足の種類は60分足、期間は2012年1月1日から2015年12月31日まで、スプレッドは0.4pipsという設定でウォークフォワードテストを行った。

トレードルール

time_of_day_effectのトレードルールは以下の通りである。

買いエントリー

○下落傾向のある時間帯から上昇傾向のある時間帯に移行したとき。

買いエグジット

○上昇傾向のある時間帯から下落傾向のある時間帯に移行したとき。

売りエントリー

○上昇傾向のある時間帯から下落傾向のある時間帯に移行したとき。

売りエグジット

○下落傾向のある時間帯から上昇傾向のある時間帯に移行したとき。

備考

①24時間のうち、下落傾向のある時間帯から上昇傾向のある時間帯に移行する時刻を1つ、上昇傾向のある時間帯から下落傾向のある時間帯に移行する時刻を1つ選び、1日に2回ドテンする。

②3月から10月までを夏時間とみなして調整した。

ウォークフォワードテストの結果は年率15.73%、シャープレシオ1.42であった。

ところで、2015年1月1日から2015年12月31日まで適用して最適化したところ、日本時間の10時から売り、15時から買いという結果になり、日本の株式市場の取引時間とほぼ一致している。買いから売りに転じる時間が10時で日本の株式市場が始まる9時より1時間遅れているが、仲値が10時で、10時までの仲値買いがあることを考えれば納得のいく結果である。

検証ではウォークフォワードテストを行ったが、最適化された時刻には若干のずれがあり、パフォーマンスもやや劣化している。

ウォークフォワードテストではインサンプル期間のトレンドに影響を受けやすい。例えば期間中に上昇(下落)トレンドがあれば買い(売り)の時間が長くなり、売り(買い)の時間が短くなる傾向が出てしまうだろう。正確を期するのであれば、トレンドを除去した上で最適化するべきかもしれない。

サンプルプログラム

①以下のプログラムをSpyderの「エディタ」にコピー&ペーストし、ファイル名を「time_of_day_effect.py」として「~/py」フォルダーに保存する。

# coding: utf-8
import forex_system as fs
import numpy as np
import pandas as pd

# パラメータの設定
UP = 8
DOWN = 3
PARAMETER = [UP, DOWN]
# 最適化の設定
START_UP = 0
END_UP = 23
STEP_UP = 1
START_DOWN = 0
END_DOWN = 23
STEP_DOWN = 1
RRANGES = (
    slice(START_UP, END_UP, STEP_UP),
    slice(START_DOWN, END_DOWN, STEP_DOWN),
)

def strategy(parameter, symbol, timeframe):
    '''戦略を記述する。
    Args:
        parameter: パラメーター。
        symbol: 通貨ペア。
        timeframe: 期間。
    Returns:
        シグナル。
    '''
    # パラメータを格納する。
    up = int(parameter[0])
    down = int(parameter[1])
    # 戦略を記述する。
    close = fs.i_close(symbol, timeframe, 0)
    month = fs.time_month(close.index)
    hour = fs.time_hour(close.index)
    buy = pd.Series(index=close.index)
    sell = pd.Series(index=close.index)
    if up < down:
        buy[(month >= 3) & (month < 11)] = ((hour >= (up + 1) % 24) &
            (hour < (down + 1) % 24)) * 1
        buy[(month < 3) | (month >= 11)] = ((hour >= up) & (hour < down)) * 1
        buy = buy.fillna(0)
        sell[buy != 1] = -1
        sell = sell.fillna(0)
    elif up > down:
        sell[(month >= 3) & (month < 11)] = ((hour >= (down + 1) % 24) &
            (hour < (up + 1) % 24)) * (-1)
        sell[(month < 3) | (month >= 11)] = (((hour >= down) & (hour < up)) *
            (-1))
        sell = sell.fillna(0)
        buy[sell != -1] = 1
        buy = buy.fillna(0)
    else:
        buy = pd.Series(np.zeros(len(close)), index=close.index)
        sell = pd.Series(np.zeros(len(close)), index=close.index)
    signal = buy + sell
    signal = signal.astype(int)

    return signal

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

%run -t ~/py/backtest.py --ea1 time_of_day_effect --symbol1 USDJPY --spread1 4 --timeframe 60 --start 2012.01.01 --end 2015.12.31 --optimization 2
(2016/11/21更新)

FXシステムの使い方(トレード)

FXシステムを用いたトレードのやり方について簡単に説明する。初めに断っておくが、ライブ口座では絶対に使用しないこと。トレード機能はまだまだ試験的なものなので、デモ口座でのみ使用すること。

トレンドを行う場合のコマンドの書式は以下の通りである。

python ~/py/trade.py --ea1 戦略 --symbol1 通貨ペア --timeframe1 足の種類 --lots1 ロット数 --mail メール --mt4 MT4

バックテストのときとは違って端末を使う。Spyderからでも実行できるが、途中で止まってしまうことがある。

トレードでは「~/py/trade.py」ファイルを使う。

「--ea1」で1個目の戦略を設定することになる。最大5個の戦略を同時にトレードできる。例えば、2つの戦略を同時にトレードする場合は

python ~/py/trade.py --ea1 戦略 --symbol1 通貨ペア --timeframe1 足の種類 --lots1 ロット数 --ea2 戦略 --symbol2 通貨ペア --timeframe2 足の種類 --lots2 ロット数 --mail メール --mt4 MT4

のようにする。

「戦略」〜「足の種類」はバックテストと同じである。

「ロット数」は「1ロット=1万通貨単位」で設定する。

「メール」はシグナルの発生をメールで通知するかどうかの設定で、「0」で通知しない、「1」で通知するとなる。通知する設定にした場合、買いエントリー、買いエグジット、売りエントリー、売りエグジットが発生したタイミングで指定したメールアドレスにメールが送信される。その場合、件名は戦略名となっており、本文は「[通貨ペア]を[レート]で買い(売り)エントリー(エグジット)です。」となっている。

「MT4」はシグナルをMT4に送信するかどうかの設定で、「0」で送信しない、「1」で送信するとなる。送信する設定にした場合、MT4の「MQL4/Files」フォルダーに戦略名と同じ名前のcsvファイルを作成し、シグナルを書き込む。シグナルは売りの場合は「1」、ノーポジションの場合は「2」、買いの場合は「3」が書き込まれる。

初めは-1、0、1のようにしたかったのだが、シグナルが書き込まれるタイミングでMT4のEAがそれを読み込もうとすると、EAは「0」と判断してしまうようだ。「0」をノーポジションに設定すると、EAは間違えて保有しているポジションを決済してしまう。そういうこともあって、それぞれに2を加えて0を使わないようにした。

「メール」と「MT4」の設定は複数の戦略で共通である。

出力については

年.月.日 時:分:秒 戦略 通貨ペア 足の種類 シグナル

となっている。

「シグナル」は売りなら「-1」、ノーポジションなら「0」、買いなら「1」と表示される。単にシグナルがそうなっている、というだけであって、実際のポジション状況を反映しているわけではない。

トレードを終了する場合は単に端末を閉じればいい。

(2017/02/08更新)