🎃

UKIさんのシストレのすすめを正座して読んで手を動かしてみる #1

2022/10/11に公開

きっかけ

Twitterを読んでたらこんなツイートがタイムラインに表示されました。

紹介されたUKIさんの記事を読んでみると、シストレを始める前に目標をちゃんと定める事が大事だということです。全くその通りだと思いました。

この記事はシリーズもので、どれもとても参考になるので、一つ一つ読んで手を動かしてみようと思いました。

第一回 運用目標

https://note.com/uki_profit/n/n09401db53ad2

資産運用する上で「運用目標」は絶対に必要です。なぜなら運用目標が明確化できていなければ、それを実現するためのプロセス(運用戦略)を考えることができないからです。世の中に存在する殆どの投資書籍は、この最も根本にある課題提起について触れていません。

例えば年利5%を目指すのと年利20%を目指すのであれば、当然売買の対象銘柄や取引スパンも変わります。場合によっては株式以外の投資の選択肢も含まれるでしょう。「とりあえずお金を儲けたい」で始めると上手くいきません。

運用目標は次の3つを明確化しておけばよいです。

①運用資金、②目標利回り、③許容リスク(=許容ドローダウン)

第二回 ストラテジーへの要件

https://note.com/uki_profit/n/nf1ba76ee47d6?magazine_key=md88fa4cd2fc1

この回では難しそうな数式が出てきました。Noteでは数式に対応していないのか、かなり読みづらい表記になっているので、Zennで数式表示の形にして引用していきます。

あるストラテジーについて、①リターンの期待値、②リターンの標準偏差、③トレード数を用いて収益性を予測します。リターンの母集団の期待値をe、標準偏差を\sigma、単位期間(例えば1年当たり)のトレード数をNとし、このストラテジーで単位期間運用したときの総利益について、その期待値Eと標準偏差\Sigmaを考えます。

(中略)

(a)運用期間が経過した時点での総利益の期待値

この計算は簡単です。1回当たりのトレードの期待値がeN回トレードをするため、運用期間通しての総利益の期待値は下記の通りです。
E = Ne

(中略)

上記で計算したのは2回のトレードにおける合成標準偏差ですが、これを N 回のトレードに一般化すると下記となります。
\Sigma = \sqrt{N} \sigma
(中略)
つまり、運用期間を経た時点でのシャープレシオ SR' は以下となります。
SR' = \frac E \Sigma = \frac {Ne} {\sqrt{N} \sigma} = \sqrt N \cdot SR

この式で理解できると思いますが、シャープレシオが同じ場合でもトレード数が多いストラテジーを単位期間運用した場合、見掛け上シャープレシオが改善する(収益性が向上する) ことに気付きます。これが「トレード数が資産運用に与える定量的な効果」となります。

(2)ストラテジーへの要件と運用レシオ

さて、前回までに導出した変数を用いて、ストラテジーを単位期間運用した場合の資産曲線を表すと次のような形となります。

資産の曲線の上下に引かれているラインは、それぞれ+3\sigma−3\sigmaのラインです。運用中の資産曲線がこのラインに触れる確率は0.3%です。

この資産曲線を用いて、運用目標で定めた目標利回りと許容DDからストラテジーへの必要要件を明確化します。図において、初期資金に対する最大ドローダウン(以下)は、−3\sigma曲線が極値を取るポイントとなります。トレード数nに対する−3\sigma曲線の式f(n)は以下となります。

f(n) = E(n) − 3 \Sigma(n) = n e - 3 \sqrt{n} \sigma
この値を微分し、f'(n) = 0としてnについて解くと、
f'(n) = e − \frac 3 2 n^{-1/2} \sigma = 0
n_\text {maxdd} = {(\frac 3 2 \cdot \frac \sigma e)}^2 = {(\frac 3 2 \cdot \frac 1 {SR})}^2

このストラテジーにおいて、上記のトレード数n_maxddのときに下記の最大ドローダウンDが0.3%の確率で発生する可能性があります。初期資金からだけでなく、どの時点の資金から見てもこの値となります。

D = \frac 9 4 \cdot \frac {\sigma^2} {e}

(中略)

ストラテジーは当初取り決めた目標利回りと許容DDに対し、以下の2つの式を満足する必要があります。
E = Ne \geq \text{目標利回り}
D = \frac 9 4 \cdot \frac {\sigma^2} {e} \leq \text{許容DD}

(中略)

ここで右辺の目標利回り / 許容DDを、運用レシオ A と仮に呼ぶことにします。この式をストラテジーの元の指標であるe, \sigma%, $Nで表すと、以下のようになります。この式が運用目標に対するストラテジーの必要要件を表す式となります。

\frac 4 9 N {(\frac e \sigma)}^2 = \frac 4 9 N \cdot {SR}^2 \geq A

シミュレーションしてみよう

だいたい感じは分かってきたので、手を動かしてシミュレーションしてみます。
計算を簡単にするためにトレードは8時間に1回やるとすると。

N = \frac {365 \cdot 24} 8
e = \frac E N
\sigma = \frac 2 3 \sqrt {D \cdot e}

パラメータを求めてみる

まずはUKIさんの記事の式をもとに各種パラメータを求めてみます。

import numpy as np

# 所望の年率リターン
_ret = 0.12

# 所望の最大ドローダウン率
_drawdown = 0.12

# トレード間隔
_trade_interval = 8

# 1年間のトレード回数N
_N = 365 * 24 / _trade_interval

# 一回のトレードの利益の期待値e
_e = _ret / _N

# 一回のトレードの標準偏差
_sigma = 2 / 3 * np.sqrt(_drawdown * _e)

# シャープレシオ
_SR = _e / _sigma

print(f'N = {_N}')
print(f'e = {_e}')
print(f'sigma = {_sigma}')
print(f'SR = {_SR}')

結果は…

N = 1095.0
e = 0.0001095890410958904
sigma = 0.0024175915408619973
SR = 0.04532984139116246

資産曲線を書いてみる

求めたパラメータを代入して、資産曲線を書いてみます。

df = pd.DataFrame()
df['n'] = np.arange(0, int(_N), 1)
df['E'] = _e * df['n']
df['sqrt_n_sigma'] = _sigma * np.sqrt(df['n'])

plt.figure(figsize = (10, 6))
plt.plot(df['E'], color = 'black', lw = 0.5)
plt.plot(df['E'] + df['sqrt_n_sigma'], color = 'blue', linestyle = 'dotted', label = '1 sigma')
plt.plot(df['E'] - df['sqrt_n_sigma'], color = 'blue', linestyle = 'dotted')
plt.plot(df['E'] + 2 * df['sqrt_n_sigma'], color = 'green', linestyle = 'dotted', label = '2 sigma')
plt.plot(df['E'] - 2 * df['sqrt_n_sigma'], color = 'green', linestyle = 'dotted')
plt.plot(df['E'] + 3 * df['sqrt_n_sigma'], color = 'red', linestyle = 'dotted', label = '3 sigma')
plt.plot(df['E'] - 3 * df['sqrt_n_sigma'], color = 'red', linestyle = 'dotted')
plt.hlines(0, xmin = -10, xmax = _N)
plt.ylabel('資産曲線 [%]')
plt.xlabel('トレード数 [回]')
plt.legend()
plt.title('リターンの期待値e = {_e * 100:.2f}%, 標準偏差 {_sigma:.4f}%, トレード数{_N:i}回')

結果は…だいぶそれっぽいのが出てきました。2\sigmaの下側のラインが0以下ということは、68-95-99.7則から考えて、5%以上の確率で損失が出ていそうです。

何度もランダムにシミュレーションして、結果を見てみる

一回のトレードでの期待値と標準偏差がわかっているので、1年分の損益シミュレーションを10000回くらいやって、どんな結果になったか見てみましょう。

df_simulation = pd.DataFrame()
array_last = []
array_mins = []

plt.figure()
for i in range(0, 10000):
  _series = pd.Series(np.random.normal(_e, _sigma, int(_N))).cumsum()
  _series.plot(lw = 0.5)
  array_last.append(_series.iloc[-1])
  array_mins.append(_series.min())
plt.show()

series_last =  pd.Series(array_last)
series_last.hist(bins = 50, density = True, cumulative = True)
plt.title('最終的な利益率の累積分布')
plt.show()
print(series_last.describe())
print(f'損失が出る確率 : {len(series_last[series_last <= 0]) / len(series_last) * 100: .4f}%')

series_mins = pd.Series(array_mins)
series_mins.hist(bins = 50, density = True, cumulative = True)
plt.title('最大ドローダウンの累積分布')
plt.show()
print(series_mins.describe())
print(f'12%以上のドローダウンが出る確率 : {len(series_mins[series_mins <= -12.0]) / len(series_mins) * 100: .4f}%')

結果は…こんな感じです。これだけだとわけがわかりません。

次は最終的な利益の分布です。利益の平均は想定通り約12%になっています。また、予想通り損失が出る確率は5%より少し上の6.49%になっています。

少し損失が出る確率が高いのが気になる場合は、許容するドローダウン率を下げると損失が出る確率が減ります。

count    10000.000000
mean        12.000408
std          8.044657
min        -16.981876
25%          6.557517
50%         11.952720
75%         17.443925
max         41.849533
dtype: float64
損失が出る確率 :  6.4900%

最大ドローダウンの分布も表示したところ、12%以下になるケースは0.83%でかなり小さくなっています。

count    10000.000000
mean        -2.415346
std          2.533211
min        -21.002064
25%         -3.468663
50%         -1.633711
75%         -0.602363
max          0.746871
dtype: float64
12%以上のドローダウンが出る確率 :  0.8300%

どうやら計算して得られたパラメータは正しかったようです。よかったよかった。

おわりに

C級Botterを目指すなら、最低限これができればよいらしい、ということが分かりました。

  • 原資は100万円。レバ1倍で常にフルベット。
  • トレード1回あたりの利益の期待値が0.0110%、利益の標準偏差が0.00242、1年で1095回トレードができるストラテジーを作る
  • シャープレシオは0.04になる

なんか妙に数字が低いですが…次のUKIさんの記事を読んでさらに勉強します。

おまけ

所望の利益率と許容ドローダウン率が常に同じと仮定し、さらに原資と取引回数の条件を変えないのなら、ストラテジーの利益率と標準偏差を10倍すればB級、100倍すればA級、1000倍すればS級に到達できるようです。

ストラテジーの期待値と標準偏差をそのままに、原資を1000万円にすればB級、1億円にすればA級、10億円にすればS級に到達できるようです。

夢物語ではありますが、夢はありますね!

Discussion