この度、ナンピン幅および利確幅をATRに連動させて、破綻リスクを確率的に制御するナンピンマーチン型のFX自動売買ツール(EAまたはBOT)の開発を行いました。
MQL5で開発しMT5で稼働させるEA、MQL4で開発しMT4で稼働させるEA、Pythonで開発しMT5のPython APIを利用して稼働させるBOT、とそれぞれ用意しているのですが、この記事ではそのEAまたはBOTの開発における思考過程を少しずつ具体的に記事にしていきたいと思います。
なお、作成したMT5用EAとMT4用EAについては、以下からダウンロード可能です。
マーチンゲール法とは?
まず、マーチンゲール法という基本に立ち返ります。
マーチンゲール法(Martingale System)
勝率5割ほどのカジノゲームにおいて、賭け金を失った際、次回に賭ける金額を2倍に増やすことで、前回までの負け分を取り返そうとする作戦。 https://casinoschool.co.jp/casino_glossary/martingale-system/
ここで注目したいポイントは、「勝率5割ほどのカジノゲーム」という点と、「前回までの負け分を取り返そうとする」という点です。
では、これをFXにおけるナンピンマーチンEAに当てはめようとしてみます。
前回までの負け分を取り返そうとする
ナンピンマーチンEAの基本戦略は、最初の1つ目のポジションを取った後に、思った通りの方向に価格が動いた場合に利益確定してポジションを閉じます。一方で、価格が反対方向に動いた場合は、ナンピンを行なってポジションを1つ増やします。
これを勝ち負けで捉えると、利益確定すれば勝ち、ナンピンをしたら負け、となります。ナンピンマーチンEAにおいて、ナンピンを1回行うということは、1回負けていることを意味します。
ナンピンした状態で、保有しているポジションの含み損益合計がプラスになった状態でポジションを全てクローズさせて利益を確定させます。これまでのナンピン(負け)を1回の利確(勝ち)で帳消しにするということです。
以上は、まさに「前回までの負け分を取り返そうとする」と言えると思います。
勝率5割ほどのカジノゲーム
上記で説明したのがナンピンマーチンゲール法の基本的な戦略ですが、ここでさらに”勝率”ということについて考えていきます。
まずは、「勝率5割ほどのカジノゲーム」とありましたが、それに近いわかりやすい例として「コイントス」について見てみます。
材質が均一なコインを、人間が指で回転運動を与えて投げ、コインがある程度多い回転を経てから地平面などに落ち静止したとする。このとき見えているコインの面が表になるか裏になるかは、ほぼ同様に確からしいことが期待できる。
この「ほぼ同様に確からしい」というのが、「勝率5割ほど」とほぼ同義になるでしょう。コインの面が表になれば勝ち、裏になれば負け、とした時に、勝つ確率(表が出る確率)が50%くらいということです。
マーチンゲール法は、このようなゲーム(勝率が約50%)において、連続して負け続ける確率は限りなく低くなっていく(いつかは勝てる)という特徴を上手く利用した戦略になります。
具体的に説明すると、1回のコイントス勝負で表が出るか裏が出るかは50%ですが、仮に1回目に裏が出て負けたとして、2回目も続けて裏が出る確率は、50%×50%=25%になります。さらに続けて3回目も裏が出る確率は、50%×50%×50%=12.5%です。同様にして、10回続けて裏が出る確率は、50%×50%…×50%=0.097656%と非常に小さくなります。
言い方を変えると、10回続けて負けない確率(10回のうちどこかで勝てる確率)は、99%以上ということになります。
これが、マーチンゲール法がほぼ勝てる(無限に試行回数を増やせれば確実に勝てる)戦略と言われる所以です。
ナンピンマーチンEAにおける勝率
さて、ではナンピンマーチンEAにおける勝率はどのように考えたら良いでしょうか。
先ほど見た通り、1つ目のポジションを持ってから2つ目のポジションを持つまでの間に、利益確定したら勝ち、ナンピンしたら負け、という設定にします。
すると、この勝敗は利確幅とナンピン幅の設定に左右されるということが見えてきます。
例えば、利確幅を価格が10上昇した場合、ナンピン幅を価格が10下落した場合、とします。相場が完全なランダムだと仮定すると、10上昇する確率と10下落する確率は、どちらもほぼ50%になるでしょう。この前提であれば、利確幅を10、ナンピン幅を10と設定するだけで、勝率5割ほどの環境を作り出すことが可能です。
具体的には、最初のbuyポジションを1600とした時に、利確ポイントは1610、ナンピンポイントは1590です。1610に到達する前に1590に達した場合は、そこでナンピンを行います。その時のロット数を最初のポジションの2倍にしておきます。1590における利確ポイントは1600、ナンピンポイントは1580とします。ここで1600に到達すれば勝ちです。最初の負けは帳消しになって利益はプラスです。その前に1580に到達してしまった場合は、再びナンピンです。その後また価格が1590に戻れば利確(勝ち)です。
上記の例において、利確するかナンピンするかの確率は毎回が約50%の設定ですので、負け続ける確率はどんどん小さくなっていきます。そして最終的に(10回ナンピンするまでには99%以上の確率で)勝ちが現れて、これまでの負け(ナンピン)を帳消しにしてくれるのです。
固定のナンピン幅と利確幅の場合
ここからさらに具体的な話に入ります。実際のヒストリカルデータで、勝率を見ていきます。
取り上げる対象は、XMTradingのKIWAMI極口座のGOLD#(XAU/USD)です。
KIWAMI極口座のゴールドは、最低スプレッドが0.12と海外FXの中では非常に小さくなっていて、ここで開発していくEAの取引条件に相応しいものとなっているのが選定理由の一つです。
なお、ここからの分析はGoogle Colabを用いて行います。
GOLD#のM1(1分足)の四本値データ
まず、以下のようなpandasのdf(データフレーム)を用意します。
これは MT5のチャートバーからGOLD#のM1(1分足)の四本値を2017年〜2022年の6年間分抽出したものです。211万レコードありますので、それなりのデータ量かと思います。
ちなみにGOLD#というのはKIWAMI極口座のゴールドのシンボル名ですが、このKIWAMI極口座は2022年10月26日からXMTraidingが始めた新しい口座です。したがって、それ以上前のヒストリカルデータは存在しないはずですが、おそらくStandard口座とかの値が入っているのでしょう。ただ、今回の用途においてはそこまで細かいことは気にしないことにします。
勝率試算用関数の定義
続いて、以下のような関数を定義します。
@numba.njit
def calc_result(op=None, lo=None, hi=None, lc_range=None, tp_range=None):
y = op.copy()
y[:] = np.nan
for i in range(op.size):
lc_price = op[i] - lc_range
tp_price = op[i] + tp_range
for j in range(i + 1, op.size):
if lo[j] < lc_price:
y[i] = -1
break
if hi[j] > tp_price:
y[i] = 1
break
return y
こちらも細かすぎる解説は省略しますが、
- 毎分op(始値)でエントリーしたとする。
- 利確価格(tp_price)をエントリープライス(op)からtp_rangeだけ離れた位置に設定
- ロスカット価格(lc_price)をエントリープライス(op)からlc_rangeだけ離れた位置に設定
- 次以降の分足を順番に見ていって、利確とロスカットどちらの価格に先に到達するか判定
というような処理を行う関数になっています。
勝率の試算結果
そして、用意したdfに以下の処理で簡易的な勝率を試算してみます。
df['y']= calc_result(
op=df['Open'].values,
lo=df['Low'].values,
hi=df['High'].values,
lc_range=1.00,
tp_range=1.00,
)
wins = len(df[df['y']==1])
trials = len(df)
win_rate = round( wins / trials * 100, 2)
print('勝ち:',wins,'試行回数:', trials, '勝率:',win_rate,'%')
まずは、lc_range=1.00, tp_range=1.00,
と、同じ値幅を設定してみました。
結果は以下のように表示されます。
勝ち: 1040930 試行回数: 2118651 勝率: 49.13 %
図らずも50%に近い勝率になりました。試行回数が結構多いので、それなりにランダムな動きになっているということでしょうか。
ただし、ここで注意しなくてはならないのは、上記試算ではスプレッドを考慮していないという点です。スプレッドがあると、始値でエントリーするということは出来ませんので、最初からスプレッド分だけ不利な状態でスタートすることになります。
GOLD#の場合、最低スプレッドは0.12、平均スプレッドは0.15程度ですので、これを仮想的に反映してみます。
まずは、lc_range=0.85, tp_range=1.15,
と、スプレッド分だけエントリー価格をずらしたような設定で試算してみると結果は以下の通りになりました。
50%近くあった勝率が結構落ちてしまいましたね。こういう確率でみると、スプレッドの重要性というのがまた違った形で見えてきたりします。
勝率を高める工夫を加えてみる
さて、これまではかなり単純な、相場が完全なランダムであるという前提でしか考えていませんでしたが、実際はそんなことはないと思います。そこで、エントリー条件をもう少し工夫してみて、勝率がどの程度変わるのかについて分析してみたいと思います。続きは別の記事にします。