勝率とペイオフレシオ (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

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

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