2011/05/18

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



★2011/5/22 12:26修正 : 「2.「証拠金維持率」と「AccountFreeMarginCheck」による発注可否判定」の計算式
さて前回は、ロット数の計算について書いたところ、大きな勘違いが発覚しました。なので今回は、勘違いしていたところを見直して、改めてロット数の計算についての「改訂版」を書いて見たいと思います。#内容的には勘違い箇所を訂正した内容です。なので、内容は前回記事と重複箇所が多いです。



勘違い箇所を理解するのに、これまた、




頭がぐちゃぐちゃに

なりました。。


大きな差分としては、「ストップアウト」に関する記述と、「証拠金」との兼ね合いの箇所です。
#何をどう勘違いしていて、考慮がもれていたかは、前回記事のコメントを見て頂ければと思います。

まずは、重複箇所も含めて、一から整理した結果の最初の内容からです。
#内容一新したのは、「【余剰保証金チェック】」以降です。

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

これを基本に、FX業者が決めた「増やせるロット数の単位」/「ロット数の桁数」/「最小ロット数」/「最大ロット数」を元にロット数を計算し、「余剰証拠金」が足りているかチェックする。

今回共通部品としては、「証拠金維持率」が足りるかどうかチェックするときに、一定の安全マージンを確保する機能と、1トレードで証拠金を使いすぎない様にロット数を制限して局所化をしようとしている。

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

【やりたい事の具体例】
---------------------
口座残高: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));
------------------------

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


さて、ここからが前回記事と大きく異なる点です。


まず次に必要なのは、計算したロット数で発注してエラーにならないかどうか。

【余剰保証金チェック】
----------------------------
前回は「ストップアウトレベル」からチェックしていたけど、一新することに。
つまり、前述で求めたロット数と、AccountFreeMarginCheck()を使って、発注可否判定をするということ。

●チェック方法
 AccountFreeMarginCheck(Symbol(), cmd, 前述で求めたロット数) > 0 

これを満たしていれば、少なくとも「ストップアウト」にはならない。
#チェックしてから発注までの間に、保有中ポジションの含み益が急激に減っていれば別ですが。。

で、上記の式の">0"の箇所の「0」が本当に「0」でいいのか?というところが懸念点。
つまり、左辺の計算結果が、限りなく「0」に近ければ、ちょっとした保有中ポジションの含み損益の変動でストップアウトになってしまう。これについては後述の「共通部品での考慮ポイント」にて。
---------------------------------


これをクリアすれば無事発注!!


そして、前回記事で登場してた「ストップアウト」はどこいったんだ?

ということで、次は「ストップアウト」について。

【ストップアウトについて】
---------------------------------
まず、「ストップアウトレベル」=「ロスカットレベル」でした。

で、「ロスカットレベル」を簡単に言うと、証拠金維持率(※)が危険域で新規発注不可の状態という事。
※証拠金維持率=証拠金残高÷必要証拠金
          =AccountEquity()/AccountMargin()
 つまり、証拠金から評価損益を加減して、必要証拠金に対する証拠金残高の割合。

そして、ロスカットレベルに達すると、保有中ポジションがロスカット(FX業者による自動決済)される。
実際はFX業者毎に扱いが違うみたいなので、約款を確認した方がよさそう。

この「ロスカット」に関しては、以下の記事がわかりやすかったです。
●ロスカットレベル関連記事

じゃあ、今回なぜ「AccountStopLevel()」を使わないのか?という事なんですが、前回記事のコメントにも書いてある通り、FX業者によってAccountStopLevel()が返す値で判断することがかえって誤判断を生むケースがあるという理由。

もうひとつの理由として、「ロスカットレベル」より手前の警告として、「マージンコール」があるのに、それを取得する手段がMT4にはなさそうという理由。実運用では「ロスカットレベル」になる以前に「マージンコール」からさらに余裕を持たせた「証拠金維持率」をキープしたいところ。

結論としては、MT4で用意されているAPIを使わずに、「証拠金維持率」を計算して、プロパティ化した変数と比較して、一定の「証拠金維持率」を下回った場合には新規発注しない様にする。
-----------------------------------------

「後述」としていた「AccountFreeMarginCheck」によるチェックと「ストップアウト」を考慮に加味して、共通部品として一体どうするのか、という事を検討してみた。

共通部品の考慮ポイント】
------------------------------
1.1トレードあたりの証拠金利用率の局所化
  やりたい事は前回記事と同じで、1トレードで証拠金を使いすぎて、他にポジションが
  取れなくなるという事態を避けたい。
  つまり具体例で言うと、1トレードで使える証拠金を全体の30%程度に抑え込みたい。

  これが意外と難敵だった。。。
  MODE_MARGINREQUIREDを使おうかと思っていたのですが、ヘルプを見ると他にも、
  MODE_MARGININITとかMODE_MARGINMAINTENANCEとか似たようなのがあるにも
  関わらず、使い分けの仕方がわからない。

  目的は局所化なので、安全サイドに倒れれば、ある程度誤差が出てもいいけど、
  極端にヘンな値だと、ちょっと嫌かも。なのでMT4のテスタ開始直後の値を見てみた。
  
  ●条件
   口座通貨:USD
   通貨ペア:AUDUSD
   保有ポジション:なし
   初期資金:5000USD
  ●各項目の値
   MODE_MARGINREQUIRED  :1857.60
   MODE_MARGININIT    :   0.00
   MODE_MARGINMAINTENANCE:   0.00

  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  ★ 2011/6/5 2:40修正
  ★ 以下の記事はその後のブログ記事【過去ブログ】で、「MODE_MARGINREQUIRED」を使う事に
  ★ なりました。但し、MODE_MARGINCALCMODEがゼロ(FX)の場合のみです。
                   ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  どうも、利用すべき項目には値が設定されてて、それ以外はゼロという事みたい。
  そして、テスタでEAを流して見たところ、最初「0」だった項目はポジション保有中も
  ずっと「0」。

  なので、上記3つのうち、「0」以外で、一番高い値を使うという事にしてみた。
  この「局所化機能」は、FX業者のクセとかの影響が考えられるので、
  この機能自体を無効化できるオプションも用意することに。
  ★★★★★★★★★★★★★★★ ここまで。        ★★★★★★★★★★★★★★★★★
  
  ということで、「1ロット当りの必要証拠金」(MODE_MARGINREQUIRED/MODE_MARGININIT
  /MODE_MARGINMAINTENANCEのいづれか)を使って、極端なロット数を制限する事に。
  例えば、AccountEquity()の30%を上限にしたロット数制限をしたとすると、以下の計算式に。
  
  最大ロット数AccountEquity()*0.30/MarketInfo(Symbol() , 上記1ロット当りの必要証拠金);

  ここで求めた最大ロット数を上限にすることで局所化をすることに。
  #実際ライブ口座でやってみると変わるかも。。

2.「証拠金維持率」と「AccountFreeMarginCheck」による発注可否判定
  キープしたい証拠金維持率をプロパティで設定できる様にする。
  実際には取引するFX業者のマージンコールのレベルの1.2倍とかの値にすると
  いいかかもしれない。なので、まず以下のチェックをする。

  ※2011/5/20 12:26 修正
  (AccountMargin()+AccountFreeMarginCheck(ロット数))/AccountMargin()
   > プロパティ値(1以上の値)

  ※2011/5/22 23:10 修正
  AccountEquity() / (AccountEquity() - AccountFreeMarginCheck(ロット数))
   > プロパティ値(1以上の値)


  これがOKで且つ、AccountFreeMarginCheckの結果が「0」より大きければ発注する。

3.上記「2.」で発注不可だった場合
  これは危険な水準なので、もう新規発注しない。
  で、ログ出力は初回メール通知は、当然するとした場合に、「新規発注停止状態」
  にするかどうか。つまり含み損が時とともに改善し、発注可能になった場合に
  トレードを再開させるのか否か。
  結論は、「新規発注停止状態」にして、以降は新規発注しない。
  やっぱり想定外の事が発生している状態なので、なぜその状況に陥ったのか原因が
  わかるまではトレードしないというのが安全かな、と。
------------------------------


共通部品には、生命線として「戦略ストップ」機能を設けているけど、証拠金観点はない。
なので、今回の機能もEAの重要なもう一つの生命線。




とっても神経使う。。







疲れちゃった


そして、難関だったテーマの記事を書き終えてホッとしながら、「FXシステムトレード初心者奮闘記」の「MT4用EA開発時代」は部品の説明等が続くのでした。
#前回からの差分だけ書いてもよかったけど、1つのテーマを1つの記事に纏めた方が後から読むときに解りやすいので、改訂版にしました。決して、字数稼ぎではありません。。

5 件のコメント:

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

    こんばんは。

    > ※ 2011/5/20 12:26 修正
    > (AccountMargin()+AccountFreeMarginCheck(ロット数))/AccountMargin() > プロパティ値(1以上の値)

    equity = margin + free_margin の関係がありますので、
    建玉直後の必要証拠金 (margin) は、
    AccountEquity() - AccountFreeMarginCheck(sym, cmd, vol)
    となります (スプレッド損は未考慮)。その時の証拠金維持率は、
    AccountEquity() / (AccountEquity() - AccountFreeMarginCheck(sym, cmd, vol))
    です。これとプロパティ値を比べるのが正しいのでは?

    あと、省略されたのでしょうが、AccountStopoutMode() == 1 の場合は、
    AccountFreeMarginCheck(sym, cmd, vol) > AccountStopoutLevel()
    でないといけません。「AccountFreeMarginCheck() > 0 でいい」とコメントしたのは、Mode == 0 の場合ですので。

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

    返信削除
  2. karts様

    度々指摘ありがとうございます!

    > > (AccountMargin()+AccountFreeMarginCheck(ロット数))/AccountMargin() > プロパティ値(1以上の値)
    >
    > となります (スプレッド損は未考慮)。その時の証拠金維持率は、
    > AccountEquity() / (AccountEquity() - AccountFreeMarginCheck(sym, cmd, vol))
    > です。これとプロパティ値を比べるのが正しいのでは?

    AccountFreeMarginCheckの返却値は「指定されたポジションを持った後に”残る”余剰証拠金」で、
    AccountEquity = AccountMargin + AccountFreeMargin
    であれば、上記式でもOKだと思っていたのですが、まずいでしょうか?
    #つまり、どちらの計算式でもOKだと思ったのですが。。

    800文字制限のため、cont..

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

    返信削除
  3. kartz様

    続きです。

    > あと、省略されたのでしょうが、AccountStopoutMode() == 1 の場合は、
    > AccountFreeMarginCheck(sym, cmd, vol) > AccountStopoutLevel()

    ここは理解した上で、手を抜いちゃいました。。
    結局ストップアウトレベルまで我慢するより、その手前のマージンコールのレベルより余裕を持た
    せた方がいいので、利用者がプロパティ値を手計算する必要がでてくることになります。

    そして、利用者による手計算が必要で、且つ、余裕を持たせるのであれば、ドンピシャである必要も無いので、
    「1」の場合でも、証拠金維持率による制限機能で代用できるのかなぁなんて思って、割愛しちゃいました。
    #そもそも代用できない???

    もし、ここを分岐させるのであれば、AccountStopoutLevel()の部分をプロパティ化した値と比較する様に
    すると思います。
    #ちなみに、モードが「1」というFX業者ってそれなりにあるのでしょうか?
     結構あるんであれば、共通部品に組み込もうと思います。

    #800文字制限が面倒。。

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

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

    こんばんは。

    > であれば、上記式でもOKだと思っていたのですが、まずいでしょうか?
    > #つまり、どちらの計算式でもOKだと思ったのですが。。

    数字を入れてみればすぐわかります。

    現在、equity 10万円、margin 3万円、free_margin 7万円 とします。
    ここから、2.5万円の証拠金を要する新規建てを行うべく試算すると、
    AccountFreeMarginCheck() は、7万円 - 2.5万円 = 4.5万円 を返します。

    ahaha さんの式だと、(3万円 + 4.5万円) / 3万円 = 2.5 となり、
    私の式だと、10万円 / (10万円 - 4.5万円) = 1.82 となります。

    建玉直後の状態は、equity 10万円 / margin 5.5万円 ですので、後者のみ正解です。

    > AccountStopoutMode()
    > #ちなみに、モードが「1」というFX業者ってそれなりにあるのでしょうか?

    私が使っている業者にはありませんが、、、まぁ、コードは書かずに Alert() だけ入れて止めておくとか。

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

    返信削除
  5. さっそくありがとうございます!

    > > #つまり、どちらの計算式でもOKだと思ったのですが。。
    > 数字を入れてみればすぐわかります。
    >
    > ahaha さんの式だと、(3万円 + 4.5万円) / 3万円 = 2.5 となり、
    > 私の式だと、10万円 / (10万円 - 4.5万円) = 1.82 となります。
    > 建玉直後の状態は、equity 10万円 / margin 5.5万円 ですので、後者のみ正解です。

    う。。。
    恥ずかしい。。。
    納得!!!

    私のアホな返信に、丁寧な説明ありがとうございます!

    > > AccountStopoutMode()
    > > #ちなみに、モードが「1」というFX業者ってそれなりにあるのでしょうか?
    >
    > 私が使っている業者にはありませんが、、、まぁ、コードは書かずに Alert() だけ入れて止めておくとか。

    確かに、何かしらの警告は入れておいたほうがよさそうですね~

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

    返信削除