2011/05/15

MT4用EA開発時代 - 発注時の価格計算と計算結果チェック+MODE_FREEZELEVELについて



さて前回は、MT4用EAプログラミングにあたって、オーダ関連関数のエラー発生時処理とリトライ制御について書きました。今回は、発注時やポジション変更時の価格計算「MODE_FREEZELEVEL」について書いて見たいと思います。

まずは価格計算。
これが、単純な様でいて結構面倒。

計算する価格としては以下の3つを前提

【検討対象の価格の種類】
------------------
1.発注逆指値価格
2.ストップロス価格
3.決済指値価格
------------------

話を単純にするために、簡単な発注ルールを例に。

【発注価格ルール例】
------------------
ある条件を満たしたときに「買い」で、以下の発注価格計算ルールがあったとする
1.前のバーの高値に、前バーのレンジの10%上の価格を逆指値で発注
2.ストップロスは前のバーの安値
3.決済指値は、前バーのレンジの半分を前バーの高値に足した価格
------------------


こんな価格計算プログラムぐらい、

できるわい!


と思うかもしれないんですが、いろんなパターンや制約を考えると結構面倒。




もし面倒じゃないんだったら、

ワシがアホだという事。


例として、前バーの価格が、以下だったとする。

●前バーの価格例
 高値:100.33
 安値:100.22
 始値:100.25
 終値:100.33

この場合、単純に前述のルールに沿って計算すると。

【単純に計算した場合】
-----------------
発注逆指値   :High[1]+(High[1]-Low[1])*0.1
           = 100.33+(100.33-100.22)*0.1 = 100.341
ストップロス価格:Low[1] = 100.22
決済指値    :High[1]+(High[1]-Low[1])/2.0
           = 100.33+(100.33-100.22)/2.0   = 100.385
-----------------

一見、何も問題なさそう。

で、何を考慮しないといけないかと言うと。

【価格計算時の考慮ポイント】
------------------
1.チャート上の価格はBidベース
  スプレッドの考慮が必要
2.価格の桁数を正しい桁数にしないといけない
  桁数があわなかったらエラーになるらしい
3.ストップ・レベルの考慮
  MarketInfoの「MODE_STOPLEVEL」
4.価格の上下関係
  当たり前だけど、「買い」の場合、
  決済指値>発注逆指値>ストップロス価格
  という価格の上下関係じゃないといけない。
------------------

これを元に、計算していくと。

【発注逆指値の計算】
--------------------------------
●「1.チャート上の価格はBidベース」であることへの考慮
 計算式に加えるためには、「買い」注文なので、スプレッド分を発注逆指値に足さないと
 いけない。ここで、スプレッドが2pipsだったとすると、以下の計算結果に。

 発注逆指値=100.341 + (Ask-Bid) = 100.361
 ※「Ask-Bid」の部分は、「MarketInfo(Symbol(),MODE_SPREAD)*Point」の方が正しいかもしれない。
  

●「2.価格の桁数を正しい桁数にしないといけない」への考慮
 これををクリアするには、以下の計算式に。
 (結果は、有効小数点以下桁数が2桁の場合)

 発注逆指値=NormalizeDouble(100.361,Digits)
        =100.36

●「3.ストップ・レベルの考慮」
 これが一体なんなのか、と言う事なんですが、例えば「買い」の場合、現在のAskから
 一定のpips分だけ離して逆指値を計算しないといけないということ。この「ストップ・レベル」
 は業者によって違ってて、1pipsというFX業者もあれば、10pipsや20pipsというFX業者もある。
 この値を取得するのが、MarketInfo(Symbol(),MODE_STOPLEVEL)。

 現在のBidが前バーの終値と同じというシンプルな例で考えたとすると、
 逆指値として使える価格は、終値(100.33)+スプレッド(0.02)+ストップ・レベル+1pips
 以上の価格じゃないといけない。

 以下の計算結果は、口座開設しているFX業者のストップレベルが10pipsの場合。

 逆指値最低価格=100.33+(Ask-Bid)+(MarketInfo(Symbol(),MODE_STOPLEVEL)+1)*Point;
            =100.33+0.02+0.11 = 100.46

 ここで思い出してほしいのが、単純計算した結果との上下関係。
 単純計算した逆指値が「100.36」に対して、逆指値最低価格が「100.46」。

 つまり、ストップレベルを考慮していないと、発注がエラーになってしまう。
 ちなみに私の場合、この様に逆転している場合は、「逆指値最低価格」を発注価格に使う事にしてい
 る。(なので開発中共通関数も同様)
------------------------------------

次に、ストップロスでも同じように計算。

【ストップロス価格の計算】
----------------------------------
発注逆指値で考慮した事が、ストップロスにも同じ事が言える。

●「1.チャート上の価格はBidベース」
 「2.価格の桁数を正しい桁数にしないといけない」

 ストップロス単純計算値=Low[1] = 100.22

 この場合は、「買い」ポジションのストップロスなので、「売り」になる。
 なので、スプレッドの考慮は不要で、Bidベースの価格のままでOK。

 次に、「ストップレベル」を考慮したストップロス最高値を計算すると、以下。

 ストップロス最高値=Bid(100.33) - (MarketInfo(Symbol(),MODE_STOPLEVEL)+1)*Point;
             =100.33 - 0.11 = 100.22

 なので、今回はどっちでも同じ値に
----------------------------------

次に決済指値を計算してみると。

【決済指値価格の計算】
---------------------------
同じように決済指値価格の計算をしてみると。

●「1.チャート上の価格はBidベース」
 「2.価格の桁数を正しい桁数にしないといけない」

 今回も「買い」に対する決済なので、「売り」になる。
 なので、スプレッドの考慮不要で、Bidベースの計算でOK。なので桁数補正すると。

 決済指値価格=NormalizeDouble(100.385,Digits)=100.38

●その他
 今回、逆指値じゃないので、「ストップ・レベル」への考慮は不要。
 でも、上記で掲載した「発注逆指値」と「ストップロス価格」を比較してみると。

 発注逆指値  :100.46
 ストップロス  :110.22
 決済指値価格 :100.38

 このオーダは「買い」注文なので、「決済指値価格」が「発注逆指値」より低い価格になって
 しまっていて、矛盾している。
 今のところ、決済指値を使う予定は無いので、ちゃんと考えてないけど、共通部品としては、
 こういう価格の逆転現象を検出したら、クリティカルなエラー扱いにすることに。
 #共通部品から見ると、どちらの価格が正しいのかわからないので。
---------------------------

ちなみに、「売り」注文の場合は、スプレッドの考慮が逆になるので、注意。

つまり、以下の通り。
・発注逆指値   :スプレッド分の補正は不要
・ストップロス価格:計算値から、スプレッド分を足す必要あり。
・決済指値価格  :計算値から、スプレッド分を足す必要あり。

今回ちょっと極端な例にしたけど、チャートのパターンなんて無数にあるんだから、この様な矛盾が起きてしまう可能性はゼロではない。こんな時にOrderSendからエラー返却されてリトライをしても、最新の価格情報を元に正しく計算し直さない限り、リトライが成功するはずもない。

さて、価格計算の話題は終って、次の話題である「MODE_FREEZELEVEL」ついて。

【「MODE_FREEZELEVEL」ついて】
---------------------------
MODE_FREEZELEVELは、MarketInfoで取得できる値のひとつ。
発注済ポジションの価格変更の計算をするときは、発注時価格計算時の考慮にさらに、
「MODE_FREEZELEVEL」への考慮も必要かもしれななぁ、なんて思って、調べたり考えたりしながら、ブログ記事を書いている。



これがまた面倒。



面倒というか、正確な事がよくわからない事が多かったというのが正解かもしれない。

・純粋にヘルプを見ると
 「Order freeze level in points. If the execution price lies within the range defined 
  by the freeze level, the order cannot be modified, cancelled or closed.」
・Webの翻訳サービスにかけると
 「ポイントで凍結レベルを命令してください。 実行価格が凍結レベルによって定義された範囲
  に属すなら、オーダーを変更できないか、取り消すことができないか、終えることができま
  せん。」
愛用している日本語ヘルプサイト(有志で作成されています)を見ると。
 「注文凍結レベルのポイント数。もし、強制執行価格が凍結レベルの定義範囲内にある場合は、
  注文を修正することはできません。キャンセルか閉じるかです。」

日本語訳された両者の違いは、オーダが「注文凍結レベル」にあると、オーダの修正はできない点は同じだけど、「キャンセル/閉じる」という操作を、「できない」⇔「するしかない」という風に全く違う解釈に。

でも原文を見ると、「できない」と解釈する方が正しそう。

で、両方の翻訳を併せて、プログラムチックに翻訳すると。

「注文凍結レベルのpips数。もし、発注済オーダの、『OrderOpenPrice()/OrderStopLoss()
 /
OrderTakeProfit()』が、『Ask/Bid±MarketInfo(Symbol(),MODE_FREEZELEVEL)
 *Point』の範囲内
にある場合は、『OrderModify()/OrderDelete()/OrderClose()』
 ができない。」




OrderClose()ができない????

手仕舞いぐらい自由にさせてくれ!!


と言いたいところなんですが、「もう直ぐオーダのステータスが変わるからちょっと待て」という事に見える。なので、発注約定済みオーダに関しては、もうすぐ決済されるという風に考えれば、神経質になる必要は無いかもしれない。
#トレードスタイルにもよるかもしれないけど。

嫌なのは、ペンディング中オーダをキャンセルしたくなったのに、注文約定されるまで待たされてしまうところ。約定後すぐに手仕舞いしても、どんなに上手くいってもスプレッド分は損するという事。手数料がかかるんであれば、あわせて手数料も取られてしまう。

幸い、今予定しているFX業者は、この「MODE_FREEZELEVEL」が設定されていないっぽい。
(デモ口座で調べた時の話ですが)

でも設定されていればどう対処していけばいいか、という事を考えて見た。

●対策案:OrderModify()の場合
 案1.ともかくOrderModify()してみる。エラー(ERR_TRADE_MODIFY_DENIEDらしい)が発生したら、
    RefreshRates()の後、リトライしてフリーズ・レベルの範囲外になることを期待する。
    リトライアウトしたら、次のティックデータ受信まで待つ。
 案2.事前にフリーズ・レベルにひっかかるかどうかを計算して、ひっかかるのであれば、
    あっさり次のティックデータ受信まで待つ。

 → 前回記事執筆時までは、「案1.」のつもりだった。
   でも、よく考えると、リトライすると、貴重な通信用スレッドを無駄に使う事になるので、
   同時実行している他の通貨ペアやEAがあれば、ERR_TRADE_CONTEXT_BUSYを誘発して
   しまう。
   しかも、今回の共通部品の設計だと、リトライアウトしたら、[[WARN]]レベルで、メール通知
   が無駄に来てしまう。

   なので、「案2.」にする。
   つまり、「Ask/Bid±MarketInfo(Symbol(),MODE_FREEZELEVEL)*Point」の範囲内だと、
   OrderModify()しないという風に修正する(MODE_FREEZELEVELが設定されていれば)。

   プログラム修正しなきゃ。。。

   でも、前回記事で書いたエラーコード毎の処理は変更しない。
   理由は、「ERR_TRADE_MODIFY_DENIED」の発生原因が、フリーズ・レベルにひっかかった
   事だけじゃないかもしれないから。
   もし、すれ違いでフリーズ・レベルに引っかかって、「ERR_TRADE_MODIFY_DENIED」
   になった場合は、それこそ価格差が近いという事なので、リトライすれば成功する
   可能性が高いと思うから。ここから先は実際の運用を始めてみないとわからない。。

●対応案:OrderClose()の場合
 前述の「対策案:OrderModify()の場合」とほぼ同じ結論。

 ただ、リトライに入ってしまった時に、リトライ中に該当オーダが決済されてしまって、
 決済済みオーダをクローズしようとしてしまう事がある点。
 なので、OrderClose()の時は、RefreshRates()だけじゃなくて、該当ポジションが
 クローズされているか確認してからリトライすればいいと思うけど、それでも隙間が
 あるので、エラーリターンされる可能性が残ってしまう。

 しかも、この時に返却されるエラーコードがわからない。。。
 #エラーコードがわかったところで、他の原因で同じエラーコードが返却される
  のであれば、意味無いけど。。

 なので、エラー発生した時に、該当ポジションが決済されていればリトライしないという事に

 プログラム修正しなきゃ。。。

 ん?これは、OrderModify()ついても言える事かも。。。

●結論
 ・OrderModify()/OrderClose()共、実行前にOrderStopLoss()とOrderTakeProfit()が、
  「Ask/Bid±MarketInfo(Symbol(),MODE_FREEZELEVEL)*Point」の範囲であれば、
  実行せずに失敗と見なして、次のティックデータを待つ。メール通知はしない。
 ・別の要因で、リトライする際には、ポジションが決済されているかどうかを確認して、決済されて
  いれば、正常終了と見なす。エラー検出時は、該当ポジションが決済されているか確認し、
  決済されていれば、正常終了と見なす。
  #ここは、「MODE_FREEZELEVEL」とは関係ないけど。
---------------------------








結局、共通部品の設計見直し。。。

しかも、プログラム修正箇所が多いかも。。







間違えているところがあれば

誰か指摘してください。










お願いします m(_ _)m



そして、ブログ記事を書いた結果、プログラム修正が必要な事が判明し、ショックを受けながら、「FXシステムトレード初心者奮闘記」の「MT4用EA開発時代」は部品の説明等が続くのでした。
#ブログかいてる時にもう一つバグ発見。早くMT4用EA修正しなきゃ。。。

0 件のコメント:

コメントを投稿