2011/05/16

MT4用EA開発時代 - ロット数の計算(サイジング)プログラミングについて


★★★★★ 本記事に誤りがあり、改訂版を別途書いてますのでそちらを参照ください ★★★★★

さて前回はMT4用EAプログラミングにあたって、価格計算とMODE_FREEZELEVELについて検討した様子を書いて見ました。今回は、ロット数の計算について書いて見たいと思います。


これが面倒で面倒で。。。



頭がぐちゃぐちゃに

なりました。。


やりたい事は、口座残高のN%が初期リスクになる様に、発注価格とストップロス価格からロット数を求めるというもの(今回の記事の内容は、発注価格とストップロス価格の差の代わりにATR等のボラティリティでも基本的には同じ方法)。

これを基本に、FX業者が決めた「増やせるロット数の単位」/「ロット数の桁数」/「最小ロット数」/「最大ロット数」と、「余剰証拠金」/「ストップ・アウトレベル」から計算される「最大ロット数」を元に計算しないといけない。

今回共通部品としては、「余剰証拠金残」と「ストップアウトレベルから求める最大ロット数をさらに自己制限しようとしている。

基本的なやりたい事の具体例としては、以下。

【やりたい事の具体例】
---------------------
口座残高:10万円(なので、口座通貨も円)
トレードする通貨ペア:USDJPY
初期リスク:口座残高の1%
発注価格:100.10円
ストップロス価格:99.90円
1ロットの通貨単位数:10万通貨

まず基本的にやりたい事を例を元に計算すると、

ロット数=(10万円×1%)÷(100.10円-99.90円)÷10万通貨単位
     = 0.05ロット
---------------------

こういう単純な事でも、前述のことを踏まえて、プログラムにするととっても面倒。

まず、ここまでをひとつずつ。

【基本的な計算方法】
---------------------
1.口座残高を求めるのはAccountBalance()関数を使えば取得できる
  なので、最初の「10万円×1%」の部分は、「AccountBalance()*0.01」でOK。
2.「(100.10円-99.90円)÷10万通貨単位」の部分について
  この計算式でよかったのは、口座通貨が円で、トレードしている通貨ペアがドル円だったから。

  でも、
  口座通貨がJPYで、「EURUSD」の場合は?
  口座通貨がUSDで、「GBPJPY」の場合は?

  と言う事で登場するのが、「MarketInfo(Symbol(),MODE_TICKVALUE)」。
  これで「口座通貨でのティック値」が求まる。
  つまり、1ロットだと1pipsが何口座通貨価格の変動に相当するか。
#この部分はヘルプや購入書籍からは理解できず、ここから情報を得ました。
  
  なので、「(100.10-99.90)÷10万通貨単位」の部分は、以下に。

  ロット数((100.10-99.90))/Point*MarketInfo(Symbol(),MODE_TICKVALUE)
---------------------

さて、こうやって求めた基本的なロット数もとに、FX業者が決めた「ロット数を増やせる単位」/「ロット数の桁数」/「最小ロット数」/「最大ロット数」への考慮を加味すると。

【FX業者が決めた制約の考慮】
------------------------
1.「増やせるロット数の単位」を考慮する
  ロット数を増やせる単位もFX業者によって決められている。
  これを取得できるのが、「MarketInfo(Symbol() , MODE_LOTSTEP)」。
  普通、0.10とか、0.01単位だったりして、最小ロット数と一致している事が多いかも
  しれないけど、最小ロット数が0.05で、後は0.01ロット刻みの発注になるFX業者
  もあるかもしれない。
 
  なので、まずはこの「増やせるロット数の単位」で補正する必要がある。
  これを計算するロジックが以下。※丸め方としては、ロット数が少なくなる方向

  ロット数MathFloor(前述のロット数/MarketInfo(Symbol(),MODE_LOTSTEP))
        *MarketInfo(Symbol(),MODE_LOTSTEP);

2.「ロット数の桁数」を考慮する
  ロット数にも桁数の制約があるので、桁数を正しくしないといけない。
  じゃあ、ロット数の桁数を求める方法が必要なんだけど、価格データで言うDigits相当
  の予約変数だったり関数だったりが無い。。
  で、桁数の求め方が以下。
  #これは、「FXメタトレーダー実践プログラミング」に書いてあった。

  ロット数の小数点以下桁数MathLog(1.0/MarketInfo(Symbol(),MODE_MINLOT))/MathLog(10.0);

  ここまで求めれば、後は価格の桁数を合わせるのと同じ様にすれば良くて、計算式は以下。

  ロット数NormalizeDouble(上記「1.」で求めたロット数 , 上記で求めた桁数);

3.「最小ロット数」を考慮する
  今度は、上記「2.」で求めたロット数に、FX業者が決めた「最小ロット数」への考慮を加味する。
  この場合、トレード手法として2通りあって、上記「2.」で求めたロット数が、
  「最小ロット数」より小さければ、そもそも発注しないという手法と、最小ロット数で
  発注するというやり方。ちなみに、今作っている共通部品では、後者のやり方。
  #つまり最小ロット数で発注する。

  ロット数MathMax(上記「2.」で求めたロット数 , MarketInfo(Symbol(),MODE_MINLOT));

4.「最大ロット数」を考慮する
  今度は、上記「3.」で求めたロット数に、FX業者が決めた「最大ロット数」の考慮を加味する。
  基本は前述「3.」とほとんど同じ。

  ロット数MathMin(上記「3.」で求めたロット数 , MarketInfo(Symbol(),MODE_MAXLOT));
------------------------

これで、少なくとも証拠金さえ足りていれば、エラーにならないロット数が求まる事に。

次は、「余剰証拠金」/「ストップアウト・レベル」との兼ね合いから最大ロット数を求めて、それを超えない様にする必要がある。

【証拠金残から最大ロット数を求める】
---------------------------
1.利用可能な証拠金を求める
  単純に、余剰証拠金を求めるのは、AccountFreeMargin()でOK。
  ここに、「ストップアウト」というFX業者が指定する制約が発生する。
  つまり、余剰証拠金から一定割合を残した金額分しか使えない。
  
  さらにややこしい事に、この「ストップアウト」の指定方法がFX業者によって
  違うみたいで、「百分率」を指定する場合と「余剰証拠金額」を指定される場合
  の2種類がある。なので、まずはどっちで指定されるかを取得する必要がある。

  これを求めるのが、AccountStopoutMode()で、「0」であれば「百分率」(0~100)で、
  「1」であれば「余剰証拠金額」。

  ●「百分率」指定の場合
   利用可能な余剰証拠金=AccountFreeMargin() * (1.0-AccountStopoutLevel()/100.0);
  ●「余剰証拠金額」指定の場合
   利用可能な余剰証拠金=AccountFreeMargin() - AccountStopoutLevel();

   2011/5/16 15:10加筆
   -----------------------
   上記、取り消し線箇所の記述は誤りです。
   内容は後日訂正しますが、急ぎ詳細は、kartz様から頂いた本記事へのコメントを
   参照ください。
   →kartz様
    ご指摘ありがとうございます!!とても助かります!
    自分の理解を深めたい事もあり、別途コメントにて返信させていただくと思います。
    頂いたコメントを租借するお時間頂ければ幸いです。m(_ _)m
   -----------------------

  今検討しているFX業者のデモ環境は、「百分率」のパターンなので、
  「余剰証拠金額」指定の場合の動作確認できてません。。。

2.利用可能な証拠金から、最大ロット数を求める
  まずは、余剰証拠金と、1ロットの買いポジションを持つために必要な余剰証拠金
  を取得して、「最大ロット数1」を求める。

  最大ロット数1上記「1.」で求めた利用可能な証拠金
             /MarketInfo(Symbol() , MODE_MARGINREQUIRED);

  さらに、「ロット数を増やせる単位」/「ロット数の桁数」を考慮すると。

  最大ロット数2MathFloor(最大ロット数1/MarketInfo(Symbol(),MODE_LOTSTEP))
             *MarketInfo(Symbol(),MODE_LOTSTEP);
  最大ロット数3NormalizeDouble(最大ロット数2 , 前述の「ロット数の小数点以下桁数」);

3.最大ロット数が、最小ロット数を下回っていないか確認する
  余剰証拠金から求めた最大ロット数も、FX業者が指定する「最小ロット数」への考慮
  が必要。もし、「最小ロット数」よりも小さい値であれば、
  
  証拠金不足という事で、発注できない。。

4.サイジングで求めたロット数と、今回求めた最大ロット数の小さい方
  上記「3.」までで、余剰証拠金とストップアウトレベルから求めた最大ロット数
  が求まったので、最終的に適用するロット数は、サイジングで求めたロット数と、
  今回求めたロット数で、小さい方の値を適用する。
---------------------------------------

ここまでが、必要最低限の話。
#サイジングが固定ロット数じゃなければの話ですが。

開発中共通部品としては、この「余剰証拠金」から求める「最大ロット数計算」にさらに以下の考慮をすることに。

共通部品での考慮ポイント】
-------------------------------
1.ポジション保有中にストップアウトレベルにならない様に余裕を持たせる
  実は「ストップアウトレベル」になるとどうなるのかよくわかっていない。
  少なくとも、とあるFX業者では、余剰証拠金がストップアウトレベルになると、
  含み損が出ているポジションから強制決済されるらしい。
  #つまり「追証」が発生するレベルの事かな??

  で、ポジション保有中は余剰証拠金は変化していくので、「ストップアウトレベル」
  のぎりぎりまで証拠金を使ってしまうと、ちょっとしたことで「ストップアウト」
  発生時のルールが執行されてしまうことになる。

  なので、余剰証拠金を求める時に、一定比率は余裕分として残すことに。
  共通部品ではその比率をプロパティ化していて、今のところ余剰証拠金の
  90%という初期設定値に。

2.1トレードで証拠金の大半を使い切ってしまわない様にする
  複数通貨ペアや、複数EAを同じ口座でトレードする場合の考慮ポイント。
  サイジングロジックによっては、1トレードで大量のロット数になってしまって、証拠金を大量に
  使ってしまう事も。
  例えば、今回の例で言うと、トレードルールにのっとった初期リスクのpips数が異常に
  小さくなってしまった場合、口座残高の一定比率から求めるとロット数が非常に多く
  なってしまうことに。この計算では余剰証拠金が考慮されていないし。

  なので、余剰証拠金から最大ロット数を求める時に、ストップアウトに上記「1.」
  の余裕をもった余剰証拠金のうち、一定割合だけに制限する機能を設けた。
-------------------------------





最近、MT4のサイジングロジックの作り方がわからないというブログ記事を見たが、
その難しさがようやくわかった。。








以上

オチなし







それが何か?


そして、プログラミングが済んでいる箇所の記事を書くにも混乱しながら、「FXシステムトレード初心者奮闘記」の「MT4用EA開発時代」は部品の説明等が続くのでした。
#MT4用EAのコーディングは一通り終った。テストはまだだけど。。

7 件のコメント:

  1. kartz 【旧ブログから転記】2013/05/07 18:04

    はじめまして。kartz と申します。頑張ってらっしゃいますね o(^^)。

    初回コメントが誤りの指摘になるのはつらいのですが、ストップアウトに関して、

    > つまり、余剰証拠金から一定割合を残した金額分しか使えない。
    > ●「百分率」指定の場合
    > 利用可能な余剰証拠金=AccountFreeMargin() * (1.0-AccountStopoutLevel()/100.0);
    > ●「余剰証拠金額」指定の場合
    > 利用可能な余剰証拠金=AccountFreeMargin() - AccountStopoutLevel();

    この部分がおかしいです。

    新規発注が可能な条件は、
    ・AccountStopoutMode() == 0 の場合
    AccountFreeMarginCheck(Symbol(), cmd, vol) > 0.01 * AccountStopoutLevel() * AccountEquity()
    ・AccountStopoutMode() == 1 の場合
    AccountFreeMarginCheck(Symbol(), cmd, vol) > AccountStopoutLevel()
    となります。

    普通のブローカですと、AccountStopoutLevel() は 100%を返します。
    Forex.com JP では、ストップアウト基準が2つあり (NYクローズで100%、それ以外の時刻では30%)、
    AccountStopoutLevel() は 30%を返すので (つまり、50×3.33 = レバ 167倍)、うまく判定できません。
    日本のレバ規制では 1日1回100%基準でチェックすればいいので SOL 30% でも合法です。

    【旧ブログから転記】
    ※ このコメントは、旧ブログで頂いたコメントを、ブログ筆者が転記したものです。

    返信削除
  2. kartz 【旧ブログから転記】2013/05/07 18:05

    すみません。間違っていましたので、

    ・AccountStopoutMode() == 0 の場合
    AccountFreeMarginCheck(Symbol(), cmd, vol) > 0
    かつ
    AccountEquity() > 0.01 * AccountStopoutLevel() * (AccountEquity() - AccountFreeMarginCheck(Symbol(), cmd, vol))

    に訂正します。実際、SOL が 100%を超えることはないとは思いますが。。。

    混乱させてごめんなさい。

    【旧ブログから転記】
    ※ このコメントは、旧ブログで頂いたコメントを、ブログ筆者が転記したものです。

    返信削除
  3. kartz様

    度々申し訳ないのですが、私の理解が正しいかコメント返信いただけないでしょうか?

    そもそも、「ストップアウトレベル」というものをとんでもない勘違いをしていました。。
    整理すると、ストップアウトレベルは証拠金維持率が何%を切ったらロスカットするかなので、「AccountStopLevel()/100>=AccountEquity()/AccountMargin()」になると、ストップアウトによる強制決済になるという事ですよね?#「百分率」の場合

    そしてもともと、最終的にストップアウトレベルにもとづいた許容される最大ロット数を求めたかったのですが、その最大ロット数を求めるには、以下の計算式で、私の理解はあってますでしょうか? 
    【ストップアウトレベルに基づいた最大ロット数】
    ●「百分率」の場合
     最大ロット数=(AccountEquity()/(AccountStopLevel()/100)-AccountMargin() ) /MarketInfo(Symbol() , MODE_MARGINREQUIRED)
    ●「余剰証拠金レベル」指定の場合
     最大ロット数=(AccountFreeMargin()-AccountStopoutLevel())/MarketInfo(Symbol() , MODE_MARGINREQUIRED)

    ここで解らなくなってきたのが、「余剰証拠金レベル」指定の場合。
    #ブログ記事と同じになってしまった。。。

    「AccountFreeMarginCheck(Symbol(), cmd, vol)」の箇所を、「AccountFreeMargin()-(vol*MarketInfo(Symbol() , MODE_MARGINREQUIRED)」に置き換えて導き出した計算式なんですが、そもそもそこがおかしいのでしょうか?

    【旧ブログから転記】
    ※ このコメントは、旧ブログのコメントを、ブログ筆者が転記したものです。

    返信削除
  4. kartz様

    何度もすみません。
    先ほど私が返信したコメント内の文言に間違いがあり、修正いたしました。
    修正内容:「ストップロスレベル」→「ストップアウトレベル」

    あわせて、コメント入力字数制限上伝えられなかったのですが、
    ご指摘本当に感謝しております。
    また、ブログ記事本文中で無茶ぶりした結果、お手数をおかけする結果になってしまい、申し訳ありません。。

    【旧ブログから転記】
    ※ このコメントは、旧ブログのコメントを、ブログ筆者が転記したものです。

    返信削除
  5. kartz 【旧ブログから転記】2013/05/07 18:08

    おはようございます。

    > 「AccountStopLevel()/100>=AccountEquity()/AccountMargin()」になると、
    > ストップロスによる強制決済になるという事ですよね?#「百分率」の場合

    そうです。レベル以下になった時に即時に強制決済するかどうかはブローカの裁量ですが、覚悟はしておくようにということです。

    >【ストップアウトレベルに基づいた最大ロット数】
    > ●「百分率」の場合
    >  最大ロット数=(AccountEquity()/(AccountStopLevel()/100)-AccountMargin())
    > /MarketInfo(Symbol(),MODE_MARGINREQUIRED)

    この式が使えそうなのは、AccountStopoutLevel() >= 100 の場合だけですね。AccountLeverage() を別途考慮に入れているなら別ですが、記事本文にもそういう記述はありませんでしたし、AccountStopoutLevel() = 30 だと、正しく計算できなくなるでしょう?

    『ストップアウトレベルに基づいた最大ロット数』という表現がどうも気になります (違和感があります)。最大ロット数を決めるのは、新規建て時の許容最大レバレッジであって、ストップアウトレベルではありません。

    定義というか、意味的に、AccountStopoutLevel() <= 100 でないと変ですので、前回のコメントとも関係しますが、実質、チェックは AccountFreeMarginCheck(Symbol(), cmd, vol) > 0 だけで十分なのです。

    あと、MODE_MARGINREQUIRED を使うのは、個人的には問題ありだと思っていますが、次項と共通なのでそちらで書きます。

    # 最大800字だそうですので分割しました。

    【旧ブログから転記】
    ※ このコメントは、旧ブログで頂いたコメントを、ブログ筆者が転記したものです。

    返信削除
  6. kartz 【旧ブログから転記】2013/05/07 18:09

    (つづき)

    > ●「余剰証拠金レベル」指定の場合
    >  最大ロット数=(AccountFreeMargin()-AccountStopoutLevel())
    > /MarketInfo(Symbol(),MODE_MARGINREQUIRED)
    >
    > ここで解らなくなってきたのが、「余剰証拠金レベル」指定の場合。
    > 「AccountFreeMarginCheck(Symbol(), cmd, vol)」の箇所を、
    > 「AccountFreeMargin()-(vol*MarketInfo(Symbol(),MODE_MARGINREQUIRED)」に置き換えて

    雰囲気はそうなんですが、ホントに MODE_MARGINREQUIRED で置き換えることができるのでしょうか…。証拠金関係の MarketInfo() は、MODE_MARGINREQUIRED 以外に、両建て絡みを除けば MODE_MARGINCALCMODE、MODE_MARGININIT、MODE_MARGINMAINTENANCE の3つがありますが、詳細仕様が不明ですし、仮に FX では(普通は)無視してよいとしても、金・銀・原油・CFD ではそれでいいんですかね。あれこれ悩まなくて済む AccountFreeMarginCheck() の方を使うべきじゃないかと思います。

    【旧ブログから転記】
    ※ このコメントは、旧ブログで頂いたコメントを、ブログ筆者が転記したものです。

    返信削除
  7. kartz様

    コメント返信ありがとうございます!
    #遅くなってすみません!

    > 『ストップアウトレベルに基づいた最大ロット数』という表現がどうも気になります (違和感が
    > あります)。最大ロット数を決めるのは、新規建て時の許容最大レバレッジであって、ストップ
    > アウトレベルではありません。

    なるほど。。。
    そこが根本的に間違えているという事ですね。。

    > 雰囲気はそうなんですが、ホントに MODE_MARGINREQUIRED で置き換えることができるのでしょうか
    > …。 ・・・・

    なんと!

    でも、確かにヘルプ見てると、MODE_MARGININIT/MODE_MARGINMAINTENANCE/MODE_MARGINREQUIREDをどうやって使い分ければいいのかさっぱりわからないですね。。
    #なので最大ロット数を仮に、コメント頂いた新規建て時の許容最大レバレッジから求めようと
     しても曖昧さが残ってしまうんですね。。

    最初、AccountFreeMarginCheckでひっかかるロット数なら、ひっかからない様なロット数に減らして発注しようかと思っていたのですが、そんなギリギリのレベルで発注するのは危険ですよね~

    なので、コメントで頂いた方法で発注可否判定して、NGならロット数を減らして発注なんてことはせずに、素直にエラーで新規発注しない事にします!

    とにもかくにも、いろいろ指導していただきありがとうございます!
    #ところで、kartz様はブログとかTwitterとかされていないのでしょうか?

    【旧ブログから転記】
    ※ このコメントは、旧ブログのコメントを、ブログ筆者が転記したものです。

    返信削除