English Русский 中文 Español Deutsch Português
ヘッジ  Expert Advisor コーディングの基礎

ヘッジ Expert Advisor コーディングの基礎

MetaTrader 4 | 15 3月 2016, 10:14
785 0
chayutra sriboonruang
chayutra sriboonruang

はじめに

シンプルなヘッジ expert advisor の考え方を提供していこうと思います。ヘッジ EA の基礎の大きなメモ

  • ヘッジ(金融)(ウィキペディア、フリー百科事典より)

    ヘッジングからリダイレクト)

    金融では、ヘッジとは別の投資でリスク を減らしたり相殺するために特別に引き出す投資を言います。ヘッジングとは、一方では、ビジネスが投資活動により利益を上げることを可能にしながら、不要なビジネスリスクへの露出を最小に抑えるために作成される戦略です。一般的にヘッジ取引を行う投資家は、『公正価格』(たとえば、それから負う住宅ローン)に相対的に低く価格設定されていると信じる証券に投資し、これを関連の証券や複数の有価証券の 空売り と組み合わせるのです。ヘッジ取引を行う投資家は、市場全体で値が上昇か下降かを気にかけません。気にかけるのは、安い値のついた証券がヘッジに対して上がるかどうかだけです。ヘッジ理論のパイオニアであるホルブルック・ワーキングはこの戦略を『基礎投機』[1]と呼びました。ここで基礎とはヘッジの理論的値と実際の値の間(またはワーキングの時代のスポットと先物の間)の差を言います。

    リスクテイキングのなかにはビジネス活動に固有のものがあります。特定のビジネスにとって、リスクの一部は『当然のこと』とみなされています。価格の増減は石油掘削、精製企業にとっては当然なのです。その他のリスクは望まれませんが、ヘッジなしでは避けることができません。たとえば、店舗を所有している人は競争、低品質のまたは不人気な製品、などといったリスクに気をつけます。店主の在庫が火事にあって失われるというリスクは望まれないものですが、火災保険契約によってヘッジすることができます。ヘッジは必ずしも金融商品というわけではありません。:たとえば海外に輸出をしている製造者は、費用を希望する通貨にリンクして販売することで通貨リスクをヘッジすることもあるでしょう。
    ここを参照

  • サーバーから必要とすることはすべて MarketInfo(文字列記号、int タイプ)関数で呼び出す必要があります。この関数により現行のチャートウィンドウに表示されているデータ以上にどのようなデータでも呼び出すことができるのです。また、この関数によって、現行のチャートウィンドウに表示されているシンボル以外にもあらゆるシンボルのあらゆる種類のオーダーを送信することが可能となるのです。そしてこれで2つのシンボルを簡単にヘッジすることができます。神様と MT4 のチームメンバーのおかげで、ひじょうに助かります。

  • ヘッジングで一つ必要なのは、以下に示す小さないくつかの関数で見つけ出す、監視している2つのシンボルの間の「相関関係」です。

    金融界での相関関係とは2つの有価証券の間の関係の統計的尺度です。相関係数の範囲は -1 と +1 の間です。+1 の相関は2つの通貨ペアが 100% 同方向に変化することを意味します。-1 の相関は2つの通貨ペアが 100% 逆方向に変化することを意味します。相関ゼロは通貨ペア間の関係が完全にランダムであることを示します。ここを参照


上記はすべて MT4 Expert Advisor を利用する Forex のヘッジャーが知る必要のあるシンプルな事柄です。ここからヘッジ EA の作成に入ります。



ヘッジ EA コード化へのステップバイステップ EA

ステップ1:入力パラメータ

ヘッジ EA を書き始める前にまず相関シンボルを2つ選ぶ必要があります。それが以下です。

  • つねに同様に変動するGBPUSD と EURUSD
  • つねに逆に変動するGBPUSD と EURUSD
  • * など。

本稿では、私が個人的に好むヘッジペアを選択します。それは EURJPY と GBPJPY です。それはつねに同じように変動し、ヘッジングオーダータイプの設定が簡単なものです。それでは始めましょう。ヘッジ EA の作成を始めるにあたり、以下の入力変数について学習します。


// this is to block the order sending function but 
// not to block the close function.
extern bool BlockOpening = false; 
 
extern string BaseSymbol = "EURJPY";//the 1st symbol 
 
extern string H_Symbol = "GBPJPY";//the 2nd symbol 
 
extern bool MoveSameWay = true;//they move the same way or not 
 
extern int CorPeriod = 5;//your favorite correlation period 
 
extern double BaseLotSize = 1.5;//1st symbol lotsize 
 
extern double H_LotsSize = 1.0;//2nd symbol lotsize 
 
extern double ExpectProfit$ = 137;//your expect profit in USD 
//your acceptable loss in USD in case any error occurred 
extern double AcceptableLoss$ = -77; 
 
extern string ExpectCor = "______";//your expect correlation to hedge 
//this is the upper expect cor value and has to be greater than "And" 
extern double Between = 1.05; 
 
extern double And = 0.9;//this is the lower expect cor level 
 
extern string MISC = "______";//some thing more 
 
extern int MagicNo = 318;//your favorite magic number 
 
extern bool ShowStatus = true;//to show the present hedging status 
//to play sound when SendH and CloseH function done 
extern bool PlayAudio = false;

ステップ 2:変数宣言

以下はこの EA で使用される変数です。ここでは EA がどのように動作するか理解するのに必要な変数のみ説明していきます。

int BSP       // the spread of base symbol 
 
    , HSP      // the spread of hedge symbol 
 
    , gsp
 
    , BOP = -1 // base symbol order type 
 
    , HOP = -1 // hedge symbol order type 
 
    , up = 0
 
    , Hcnt = 0
 
    , u = 0
 
    , d = 0
 
    , day = 0
 
    , sent=0
 
    , firstopen
 
    , expire;
 
double Lot
 
       , BaseOpen // base symbol order open price 
 
       , HOpen    // hedge symbol order open price 
 
       , BPt      // base symbol Point value 
 
       , HPt      // hedge symbol Point value 
 
       , BSwapL   // base symbol swap long value 
 
       , BSwapS   // base symbol swap short value 
 
       , HSwapL   // hedge symbol swap long value 
 
       , HSwapS;  // hedge symbol swap short value 
 

bool SResult = false, BResult = false, H1.profitswap,
     H2.profitswap, H3.profitswap;
 
bool SwapMode = true, allmeetcor = false, BlockOpen = false,
     buy,sell,cleared = false;
 
string H1.string = "", H2.string = "", H3.string = "",
       OrdComment = "", candletxt,tdstxt = "";

ステップ 3:必要な全静的パラメータ を取得する。

ここで init() 部分で宣言される静的な値をいくつか指定します。

//+------------------------------------------------------------------+ 
//| expert initialization function                                   | 
//+------------------------------------------------------------------+ 
 
int init() 
  { 
    //---- 
    BSP = MarketInfo(BaseSymbol,MODE_SPREAD); 
    HSP = MarketInfo(H_Symbol ,MODE_SPREAD); 
    BPt = MarketInfo(BaseSymbol,MODE_POINT); 
    HPt = MarketInfo(H_Symbol ,MODE_POINT); 
    BSwapL = MarketInfo(BaseSymbol,MODE_SWAPLONG); 
    BSwapS = MarketInfo(BaseSymbol,MODE_SWAPSHORT);
    HSwapL = MarketInfo(H_Symbol,MODE_SWAPLONG); 
    HSwapS = MarketInfo(H_Symbol,MODE_SWAPSHORT); 
//---- 
    return(0); 
  }

ステップ 4:有用な関数

待ち望む楽しい部分: "start()" 関数、に入る前に、本 EA で使用される関数から始めます。ただし、すべての関数は start() 関数の外にあることにご注意ください。

1. 相関関数

まず相関計算関数から始める必要があります。以下の関数は無料の相関インディケータ (igorad2004@list.ru) を提供している方からのもので、本 EA でより簡単に使用できるよう私が変更を加えました。これでもはや外部インディケータから相関値を呼ぶ必要がありません。よいアイデアでしょう?


//+------------------------------------------------------------------+ 
//|  CORRELATION                                                     |
//+------------------------------------------------------------------+ 
double symboldif(string symbol, int shift) 
  { 
    return(iClose(symbol, 1440, shift) - 
           iMA(symbol, 1440, CorPeriod, 0, MODE_SMA, 
               PRICE_CLOSE, shift)); 
  } 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double powdif(double val) 
  { 
    return(MathPow(val, 2)); 
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+ 
double u(double val1,double val2) 
  { 
    return((val1*val2)); 
  }
//+------------------------------------------------------------------+
//|  The real correlation function to call is here.                  |
//+------------------------------------------------------------------+
double Cor(string base, string hedge) 
  { 
    double u1=0,l1=0,s1=0; 
    for(int i = CorPeriod - 1; i >= 0; i--) 
      { 
        u1 += u(symboldif(base, i), symboldif(hedge, i)); 
        l1 += powdif(symboldif(base, i)); 
        s1 += powdif(symboldif(hedge, i)); 
      } 
    if(l1*s1 > 0) 
        return(u1 / MathSqrt(l1*s1)); 
  } 
//+------------------------------------------------------------------+

CorPeriod 変数はそれを調整できるようになるインプット変数として extern とします。2つのシンボル間の相関を計算したければ、この Cor(EURJPY,GBPJPY) のように Cor(string base,string hedge) 関数を呼ぶだけです。簡単ですよね?

2. ヘッジ送信関数

以下の SendH 関数を作成することでヘッジ注文を送信する方法が管理しやすくなると思います。

//+------------------------------------------------------------------+
//| SEND HEDGE                                                       |
//+------------------------------------------------------------------+
bool SendH(string symbol, int op, double lots,
           double price, int sp, string comment, int magic) 
  { 
    if(OrderSend(symbol 
                 , op 
                 , lots 
                 , price 
                 , sp 
                 , 0 
                 , 0 
                 , comment 
                 , magic 
                 , 0 
                 , CLR_NONE) 
                 > 0) 
      {
        return(true); 
        if(PlayAudio)
            PlaySound("expert.wav"); 
      } 
    else 
      {
        Print(symbol, ": ", magic, " : " 
              , ErrorDescription(GetLastError())); 
        return(false); 
      } 
  } 
//+------------------------------------------------------------------+

OrderSend 関数に関してはここからさらに情報入手ができます。

上記の ErrorDescription(GetLastError()) 関数によりトレード関数が動作していたときどんなエラーが発生したか EA が通知するようにしてくれます。それを使用するには、以下のようなコードを入れて"stdlib.mqh" ファイルをインクルードする必要があります。

//+------------------------------------------------------------------+ 
//|                                                     MyHedge.mq4  | 
//|                                                         myHedge  | 
//|                                     http://dailyh.blogspot.com/         | 
//+------------------------------------------------------------------+ 
#property copyright "myHedge" 
#property link "http://dailyh.blogspot.com/" 
#include <stdlib.mqh>
//+------------------------------------------------------------------+

そしてそれを使用するには、上記のように "ErrorDescription() " 関数を呼び出すだけです。

3. ヘッジ終了関数

オーダー送信以外に、期待した利益を得たらヘッジ注文を終了する関数も必要です。以下がそれです。

//+------------------------------------------------------------------+
//|  CLOSE HEDGE                                                     |
//+------------------------------------------------------------------+
bool CloseHedge(int magic) 
  { 
   for(int i = OrdersTotal() - 1; i >= 0; i--) 
     { 
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && 
                      OrderMagicNumber() == magic) 
         { 
           if(OrderClose(OrderTicket() 
              , OrderLots() 
              , OrderClosePrice() 
              , MarketInfo(OrderSymbol(), MODE_SPREAD) 
              , CLR_NONE))
                SResult = true; 
         } 
     } 
   if(SResult)
     {
       return(true);
       if(PlayAudio)
         {
           PlaySound("ok.wav");
         }
     } 
   else 
       Print("CloseHedge Error: ", ErrorDescription(GetLastError())); 
   RefreshRates(); 
   // return(0); 
  } 
//+------------------------------------------------------------------+

この関数は同一マジックナンバーの注文を終了するだけです。これは、別のマジックナンバーのヘッジ注文に影響はない、ということです。心配することではありません。この終了関数を使用する前に、以下の関数によって「今どれだけ利益を得ているか」を明確にする必要があります。

4. 総利益を求める関数

//+------------------------------------------------------------------+
//|  TOTAL PROFIT                                                    |
//+------------------------------------------------------------------+ 
double TotalCurProfit(int magic) 
  { 
   double MyCurrentProfit = 0; 
   for(int cnt = 0 ; cnt < OrdersTotal() ; cnt++) 
     { 
       OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES); 
       if(OrderMagicNumber() == magic) 
         { 
           MyCurrentProfit += (OrderProfit() + OrderSwap()); 
         } 
     } 
   return(MyCurrentProfit); 
  } 
//+------------------------------------------------------------------+


終了関数同様、ヘッジ利益を知るためには同一マジックナンバーの注文だけモニターし、それらを正しく終了するようにします。関数使用には以下のように呼び出します。

if(TotalCurProfit(318) > 100) 
    CloseHedge(318);

利益の全値はUSD で計算されます。上記の行では、マジックナンバー 318 の注文の総利益が $100 より大きい場合、その注文は終了となります。以上です。ヘッジ注文を開始するには、ヘッジを送信するとき、同一シンボルおよび同一マジックナンバーの注文が宙に浮いていないことを確認する必要があります。これは以下の関数で定義することができます。



5. 既存ポジション金額を取得する
//+------------------------------------------------------------------+
//| EXISTING POSITIONS                                               |
//+------------------------------------------------------------------+
int ExistPositions(string symbol, int magic) 
  { 
    int NumPos = 0; 
    for(int i = 0; i < OrdersTotal(); i++) 
      { 
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
                       && OrderSymbol() == symbol 
                       && OrderMagicNumber() == magic) 
          {  
            NumPos++; 
          } 
      }
    return(NumPos); 
 
  } 
//+------------------------------------------------------------------+ 

以下のように使用します。


ExistPositions("GBPJPY",318)

この関数は、そのとき現時点で「マジックナンバー 318 の GBPJPY オーダーがいくつオープンしているか」を返します。浮動オーダータイプを定義するもう一つの関数があります。


6. 特定の既存ポジションのオーダータイプを見つける

//+------------------------------------------------------------------+  
//| EXISTING OP POSITION                                             |
//+------------------------------------------------------------------+
int ExistOP(string symbol, int magic) 
  { 
    int NumPos = -1; 
    for(int i = 0; i < OrdersTotal(); i++) 
      { 
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
                       && OrderSymbol() == symbol 
                       && OrderMagicNumber() == magic) 
          {  
            NumPos = OrderType();
          } 
      } 
    return(NumPos); 
  } 
//+------------------------------------------------------------------+


この関数は、指定されたシンボルとマジックナンバーに対して現時点で浮動のオーダータイプの整数値を返します。GBPJPY 浮動オーダーが OP_BUY であれば、返される値は "0" です。この関数はトレード関数とは連携しません。それは現在のヘッジ状態を表示する関数とも連携します。この関数は "OP2Str" と呼ばれます。



7. トレード状態を表示する

//+------------------------------------------------------------------+
//| Transform OP Value To string                                     |
//+------------------------------------------------------------------+ 
string OP2Str(int op) 
  { 
    switch(op) 
      { 
        case OP_BUY : return("BUY"); 
        case OP_SELL: return("SELL"); 
        default : return("~~"); 
      } 
  }
//+------------------------------------------------------------------+


あまり説明することはありません。すでにどのように動作するか示していると思います。


8. 特定タイプのオーダーをすべてクローズする

ヘッジ注文を出したり終了する際エラーが発生した場合、単独のオーダーをすべて直接クローズする関数がもう一つあります。

//+------------------------------------------------------------------+
//| CLOSE SCRAP                                                      |
//+------------------------------------------------------------------+ 
bool CloseScrap(string sym, int op, int magic) 
  { 
    for(int i = OrdersTotal() - 1; i >= 0; i--) 
      { 
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
           && amp; OrderMagicNumber() == magic 
           && OrderSymbol() == sym 
           && OrderType() == op) 
          { 
            if(OrderClose(OrderTicket() 
               , OrderLots() 
               , OrderClosePrice() 
               , MarketInfo(OrderSymbol(), MODE_SPREAD) 
               , CLR_NONE))
                BResult = true; 
          } 
      } 
    if(BResult)
      {
        return(true);
        if(PlayAudio)
          {
            PlaySound("ok.wav");
          }
      } 
    else 
        Print("CloseScrap Error: ", ErrorDescription(GetLastError())); 
    RefreshRates(); 
    // return(0); 
  }


すなわち、CloseScrap("GBPJPY",OP_BUY,318) です。:これはマジックナンバー 318 の浮動の "GBPJPY" ロングだけをクローズします。簡単です。知っておくべき最後の関数です。



9. 希望のブール状態をすべて表示する
//+------------------------------------------------------------------+
//| Translate bool to string                                         |
//+------------------------------------------------------------------+
string bool2str( bool boolval) 
  { 
    if(boolval == true) 
        return("Yes"); 
    if(boolval == false)
        return("No"); 
  }
//+------------------------------------------------------------------+


特別なことはなにもありません。この関数は BlockOpening 値のようなパラメータのブール状態を表示します。それを真に設定する場合、この関数は画面上に「はい」を返します。偽に設定すると、「いいえ」を返します。必要な関数については以上です。ここからヘッジ処理のコーディングを楽しみましょう。


ステップ5: Expert Advisor の核

まず以下からスタートします。

//+------------------------------------------------------------------+ 
//| expert start function                                            | 
//+------------------------------------------------------------------+ 
int start() 
  {

そして相関範囲を指定します。

if(Cor(BaseSymbol, H_Symbol) > Between || 
   Cor(BaseSymbol, H_Symbol) < And) 
// Block opening when the correlation is out of 
// expected range. 
    BlockOpen = true; 
else 
    BlockOpen = false;


次に、ヘッジ方法を定義します(これは単に一例です)。本稿では、スワップ値でトレードスタイルを選択し、それから毎日スワップから利益を得る方法でのみ取引することとします。

// if they move the same way we will open long & short 
if(MoveSameWay) 
  { 
    if(((BSwapL*BaseLotSize) + (HSwapS*H_LotSize)) > 0) 
      {  
        BOP = OP_BUY; 
        HOP = OP_SELL; 
      } 
    else 
        if(((BSwapS*BaseLotSize) + (HSwapL*H_LotSize)) > 0) 
          { 
            BOP = OP_SELL; 
            HOP = OP_BUY; 
          } 
  } // end MoveSameWay
// if the move the opposite way we will open short & short or long & long
else 
  { 
    if(((BSwapL*BaseLotSize) + (HSwapL*H_LotSize)) > 0) 
      { 
        BOP = OP_BUY; 
        HOP = OP_BUY; 
      } 
    else 
        if(((BSwapS*BaseLotSize) + (HSwapS*H_LotSize)) > 0) 
          { 
            BOP = OP_SELL; 
            HOP = OP_SELL; 
          } 
  }

へッジを送信するときがやってきました。


// if they meet the correlation range and 
// you're not blocking them
if(!BlockOpen && !BlockOpening)
  {
    if(BOP == OP_BUY)
    // define the opening price    
        BaseOpen = MarketInfo(BaseSymbol, MODE_ASK);
    else
        BaseOpen = MarketInfo(BaseSymbol, MODE_BID);
    if(HOP == OP_BUY)
        HOpen = MarketInfo(H_Symbol, MODE_ASK);
    else
        HOpen = MarketInfo(H_Symbol, MODE_BID);
    // In case there is no any swap condition to gain 
    // from BOP & HOP will be -1.
    if(BOP >= 0 && HOP >= 0)
      {
        if(ExistPositions(BaseSymbol, MagicNo) == 0 &&
           ExistPositions(H_Symbol, MagicNo) == 0)
          {
            SendH(BaseSymbol, BOP, BaseLotSize, BaseOpen,
                  BSP, "COR : " +
                  DoubleToStr(Cor(BaseSymbol, H_Symbol), 2),
                  MagicNo);
            SendH(H_Symbol, HOP, H_LotsSize, HOpen, HSP,
                  "COR : " +
                  DoubleToStr(Cor(BaseSymbol, H_Symbol), 2),
                  MagicNo);
          }
        else // in case ping failed or requote 
          {
            if(ExistPositions(BaseSymbol, MagicNo) == 1&&
               TotalCurProfit(MagicNo)>AcceptableLoss$)
              {
                CloseScrap(BaseSymbol, ExistOP(BaseSymbol,
                           MagicNo), MagicNo);
              }
            else
                if(ExistPositions(H_Symbol, MagicNo) == 1&&
                   TotalCurProfit(MagicNo) > AcceptableLoss$)
                  {
                    CloseScrap(H_Symbol, ExistOP(H_Symbol,
                               MagicNo), MagicNo);
                  }
          }
 
      }
    else // if one of BOP and HOP is less than 0
      {
        string swaptxt = "No Swap Condition To Gain From :" +
                   "pls modify one or more input parameter(s).";
      }
  }


期待の利益が上がったら、クローズします。

if((TotalCurProfit(MagicNo) > ExpectProfit$)
  {
    CloseHedge(MagicNo);
  }

もっと面白い事があります。 ShowStatus 部分です。

    if(ShowStatus)
      {
        Comment("\nCorrel: " + DoubleToStr(Cor(BaseSymbol
                , H_Symbol), 2)
                , "\nBlockOpen : " + bool2str(BlockOpen 
                || BlockOpening)
                , "\n" + swaptxt
                , "\n~~~~~~~"
                , "\nB/H [sp] : " + BaseSymbol + " [" 
                + BSP + "]" + " / " 
                + H_Symbol+" ["+HSP+"]"
                , "\nCurOp [Lots]: " 
                + OP2Str(ExistOP(BaseSymbol, MagicNo)) 
                + " [" + DoubleToStr(BaseLotSize, 2) + "]"
                + " ~ " + OP2Str(ExistOP(H_Symbol, MagicNo)) 
                + " [" 
                + DoubleToStr(H_LotsRatio*BaseLotSize, 2) + "]"
                , "\nCurPF [Expect]: $" 
                + DoubleToStr(TotalCurProfit(MagicNo), 2) 
                + " [$"+DoubleToStr(ExpectProfit$, 2) + "]");
      }
    else 
        Comment("");

各 EA の終わりをもって終了します。

 return(0);
}


ステップ 6:すべてを集める

以下で myHedge.mq4 がどのように書かれるか確認できます。

//+------------------------------------------------------------------+
//|                                                      MyHedge.mq4 |
//|                                                          myHedge |
//|                                      http://dailyh.blogspot.com/ |
//+------------------------------------------------------------------+
#property copyright "myHedge"
#property link "http://dailyh.blogspot.com/"
//----
#include <stdlib.mqh>
// this is to block the order sending function but not to block the 
// close function.
extern bool BlockOpening = false;
extern string BaseSymbol = "EURJPY"; // the 1st symbol
extern string H_Symbol = "GBPJPY";   // the 2nd symbol
extern bool MoveSameWay = true; // they move the same way or not
extern int CorPeriod = 5; // your favorite correlation period
extern double BaseLotSize = 1.5; // 1st symbol lotsize
extern double H_LotSize = 1.0; // 2nd symbol lotsize
extern double ExpectProfit$ = 137; // your expect profit in USD
// your acceptable loss in USD in case any error occurred
extern double AcceptableLoss$ = -77; 
extern string ExpectCor = "______"; // your expect correlation to 
// hedge this is the upper expect cor value and has to be greater 
// than "And"
extern double Between = 1.05;
extern double And = 0.9; // this is the lower expect cor level
extern string MISC = "______"; // some thing more
extern int MagicNo = 318; // your favorite magic number
extern bool ShowStatus = true; // to show the present hedging status
// to play sound when SendH and CloseH function done
extern bool PlayAudio = false; 
//----
int BSP  // the spread of base symbol
    ,HSP // the spread of hedge symbol
    ,gsp
    ,BOP = -1 // base symbol order type
    ,HOP = -1 // hedge symbol order type
    ,up = 0
    ,Hcnt = 0
    ,u = 0
    ,d = 0
    ,day = 0
    ,sent = 0
    ,firstopen
    ,expire;
double Lot
       ,BaseOpen // base symbol order open price
       ,HOpen // hedge symbol order open price
       ,BPt // base symbol Point value
       ,HPt // hedge symbol Point value
       ,BSwapL // base symbol swap long value
       ,BSwapS // base symbol swap short value
       ,HSwapL // hedge symbol swap long value
       ,HSwapS; // hedge symbol swap short value
bool SResult = false, BResult = false, H1.profitswap, H2.profitswap, 
     H3.profitswap;
bool SwapMode = true, allmeetcor = false, BlockOpen = false, buy, 
     sell, cleared = false;
string H1.string = "", H2.string = "", H3.string = "", 
       OrdComment = "", candletxt,tdstxt = "";
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   BSP = MarketInfo(BaseSymbol, MODE_SPREAD);
   HSP = MarketInfo(H_Symbol, MODE_SPREAD);
//----
   BPt = MarketInfo(BaseSymbol, MODE_POINT);
   HPt = MarketInfo(H_Symbol, MODE_POINT);
//----
   BSwapL = MarketInfo(BaseSymbol, MODE_SWAPLONG);
   BSwapS = MarketInfo(BaseSymbol, MODE_SWAPSHORT);
//----
   HSwapL = MarketInfo(H_Symbol, MODE_SWAPLONG);
   HSwapS = MarketInfo(H_Symbol, MODE_SWAPSHORT);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   if(Cor(BaseSymbol, H_Symbol) > Between || 
      Cor(BaseSymbol, H_Symbol) < And)
   // Block opening when the correlation is out of expected range.
       BlockOpen = true;
   else 
       BlockOpen = false;
//----
   if(MoveSameWay)
     {
       if((BSwapL*BaseLotSize) + (HSwapS*H_LotSize) > 0)
         {
           BOP = OP_BUY;
           HOP = OP_SELL;
         }
       else 
           if((BSwapS*BaseLotSize) + (HSwapL*H_LotSize) > 0)
             {
               BOP = OP_SELL;
               HOP = OP_BUY;
             }
     }
   else
     {
       if((BSwapL*BaseLotSize) + (HSwapL*H_LotSize) > 0)
         {
           BOP = OP_BUY;
           HOP = OP_BUY;
         }
       else 
           if((BSwapS*BaseLotSize) + (HSwapS*H_LotSize) > 0)
             {
               BOP = OP_SELL;
               HOP = OP_SELL;
             }
     }
   if(!BlockOpen && !BlockOpening)
     {
       if(BOP == OP_BUY) 
           BaseOpen = MarketInfo(BaseSymbol, MODE_ASK);
       else            
           BaseOpen = MarketInfo(BaseSymbol, MODE_BID);
       if(HOP == OP_BUY)
           HOpen = MarketInfo(H_Symbol, MODE_ASK);
       else
           HOpen = MarketInfo(H_Symbol, MODE_BID);
       // In case there is no any swap condition that we can gain from.
       if(BOP >= 0 && HOP >= 0) 
         {
           if(ExistPositions(BaseSymbol, MagicNo) == 0 && 
              ExistPositions(H_Symbol,MagicNo) == 0)
             {
               SendH(BaseSymbol, BOP, BaseLotSize, BaseOpen, BSP, 
                     "COR : " + DoubleToStr(Cor(BaseSymbol, H_Symbol), 
                     2), MagicNo);
               SendH(H_Symbol, HOP, H_LotSize, HOpen, HSP, "COR : " + 
                     DoubleToStr(Cor(BaseSymbol, H_Symbol), 2), MagicNo);
             }     
           else // in case ping failed or requote
             {
               if(ExistPositions(BaseSymbol, MagicNo) == 1 && 
                  TotalCurProfit(MagicNo) > AcceptableLoss$)
                 {
                   CloseScrap(BaseSymbol, ExistOP(BaseSymbol, 
                              MagicNo), MagicNo);
                 }
               else 
                   if(ExistPositions(H_Symbol, MagicNo) == 1 && 
                      TotalCurProfit(MagicNo) > AcceptableLoss$)
                     {
                       CloseScrap(H_Symbol, ExistOP(H_Symbol, 
                                  MagicNo), MagicNo);
                     }
             }
         }
       else
         {
           string swaptxt = "No Swap Condition To Gain From : pls " + 
                            "modify one or more input parameter(s).";
         }
     }
   if(TotalCurProfit(MagicNo) > ExpectProfit$)
     {
       CloseHedge(MagicNo);
     }
   if(ShowStatus)
     {
       Comment("\nCorrel: "+DoubleToStr(Cor(BaseSymbol, H_Symbol), 2)
               , "\nBlockOpen : " + bool2str(BlockOpen || BlockOpening)
               , "\n" + swaptxt
               , "\n~~~~~~~"
               , "\nB/H [sp] : " + BaseSymbol + " [" + BSP + "]" + 
                 " / " + H_Symbol + " [" + HSP + "]"
               , "\nCurOp [Lots]: " + OP2Str(ExistOP(BaseSymbol, 
                 MagicNo)) + " [" + DoubleToStr(BaseLotSize, 2) + "]"
                 + " ~ " + OP2Str(ExistOP(H_Symbol, MagicNo)) + " [" + 
                 DoubleToStr(H_LotSize, 2) + "]"
               , "\nCurPF [Expect]: $" + 
                 DoubleToStr(TotalCurProfit(MagicNo), 2) + " [$" + 
                 DoubleToStr(ExpectProfit$, 2) + "]");
     }
   else 
       Comment("");
   return(0);
  }
//+------------------------------------------------------------------+
//| CORRELATION                                                      |
//+------------------------------------------------------------------+
double symboldif(string symbol, int shift)
  {
   return(iClose(symbol, 1440, shift) - 
          iMA(symbol, 1440, CorPeriod, 0, MODE_SMA, PRICE_CLOSE, shift));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double powdif(double val)
  {
   return(MathPow(val, 2));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double u(double val1, double val2)
  {
   return((val1*val2));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double Cor(string base, string hedge)
  {  
   double u1 = 0, l1 = 0, s1 = 0;
   for(int i = CorPeriod - 1; i >= 0; i--)
     {
       u1 += u(symboldif(base, i), symboldif(hedge, i));
       l1 += powdif(symboldif(base, i));
       s1 += powdif(symboldif(hedge, i));
     }
   if(l1*s1 > 0) 
       return(u1 / MathSqrt(l1*s1));
  }
//+------------------------------------------------------------------+
//| SEND HEDGE                                                       |
//+------------------------------------------------------------------+
bool SendH(string symbol, int op, double lots, double price, int sp, 
           string comment, int magic)
  {
   if(OrderSend(symbol
                ,op
                ,lots
                ,price
                ,sp
                ,0
                ,0
                ,comment
                ,magic
                ,0
                ,CLR_NONE)
                >0)
     {
       return(true);
       if(PlayAudio)
           PlaySound("expert.wav");
     }
   else 
     {
       Print(symbol, ": ", magic, " : "
             ,ErrorDescription(GetLastError()));
       return(false);      
     }      
  }
//+------------------------------------------------------------------+
//| CLOSE HEDGE                                                      |
//+------------------------------------------------------------------+
bool CloseHedge(int magic)
  {  
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {         
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && 
                      OrderMagicNumber() == magic)
         {
           if(OrderClose(OrderTicket()
                         , OrderLots()
                         , OrderClosePrice()
                         , MarketInfo(OrderSymbol(), MODE_SPREAD)
                         , CLR_NONE))
               SResult = true;
         }
     }
   if(SResult)
     {
       return(true);
       if(PlayAudio)
         {
           PlaySound("ok.wav");
         }
     }
   else 
       Print("CloseHedge Error: ", ErrorDescription(GetLastError()));
   RefreshRates();
//  return(0);
  }  
//+------------------------------------------------------------------+
//| TOTAL PROFIT                                                     |
//+------------------------------------------------------------------+
double TotalCurProfit(int magic)
  {   
   double MyCurrentProfit = 0;
   for(int cnt = 0; cnt < OrdersTotal(); cnt++)
     {
       OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
       if(OrderMagicNumber() == magic)
         {
           MyCurrentProfit += (OrderProfit() + OrderSwap());
         }   
     }
   return(MyCurrentProfit);
  }
//+------------------------------------------------------------------+
//| EXISTING POSITION                                                |
//+------------------------------------------------------------------+
int ExistPositions(string symbol,int magic) 
  {
   int NumPos = 0;
   for(int i = 0; i < OrdersTotal(); i++) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
          && OrderSymbol() == symbol
          && OrderMagicNumber() == magic)
         { 
           NumPos++;
         }
     }
   return(NumPos);
  }
//+------------------------------------------------------------------+
//| EXISTING OP POSITION                                             |
//+------------------------------------------------------------------+
int ExistOP(string symbol,int magic) 
  {
   int NumPos = -1;
   for(int i = 0; i < OrdersTotal(); i++) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
          && OrderSymbol() == symbol
          && OrderMagicNumber() == magic)
         { 
           NumPos = OrderType();
         }
     }
   return(NumPos);
  }
//+------------------------------------------------------------------+
//| Transform OP Value To string                                     |
//+------------------------------------------------------------------+
string OP2Str(int op)
  {
   switch(op)
     {
       case OP_BUY : return("BUY");
       case OP_SELL: return("SELL");
       default     : return("~~");
     }
  }
//+------------------------------------------------------------------+
//| CLOSE SCRAP                                                      |
//+------------------------------------------------------------------+
bool CloseScrap(string sym,int op,int magic)
  {  
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {         
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
          && OrderMagicNumber() == magic
          && OrderSymbol() == sym
          && OrderType() == op)
         {
           if(OrderClose(OrderTicket()
                         , OrderLots()
                         , OrderClosePrice()
                         , MarketInfo(OrderSymbol(), MODE_SPREAD)
                         , CLR_NONE))
               BResult = true;
         }
     }
   if(SResult || BResult)
     {
       return(true);
       if(PlayAudio)
         {
           PlaySound("ok.wav");
         }
     }
   else 
       Print("CloseScrap Error: ", ErrorDescription(GetLastError()));
   RefreshRates();
//  return(0);
  }  
//+------------------------------------------------------------------+
//| Translate bool to string                                         |
//+------------------------------------------------------------------+
string bool2str(bool boolval)
  {
   if(boolval == true) 
       return("Yes");
   if(boolval == false)
       return("No");
  }
//+------------------------------------------------------------------+

おわりに

これはシンプルなヘッジ Expert Advisor の一例にすぎません。みなさんのヘッジングスタイルに合わせて変更することが必要です。みなさんには数多くのヘッジングスタイルがあるはずです。また、この種の EA はそれ自体の制限によりストラテジーテスタでは検証できないことに留意ください。本番で検証するのみです。以下はヘッジ EA の結果サンプルです。


そして以下が ShowStatus 関数です。

本稿を楽しく読んでいただけたらよいのですが。また、本稿がみなさんご自身のヘッジ EA を作成されるのに役立つことを心から願っています。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/1479

添付されたファイル |
myHedge.mq4 (13.75 KB)
初心者向け MQL4 言語シンプルなフレーズにおける難しい質問 初心者向け MQL4 言語シンプルなフレーズにおける難しい質問
本稿は『初心者向け MQL4 言語』シリーズの第2弾です。より複雑で高度な言語構成を考察し、新しいオプションを学習し、日々の実践にそれらが応用されるか見ていきます。新しいサイクルタイプである 'while' と、新しい条件タイプ 'switch'、演算子 'break' と 'continue' を取得します。また、自分の関数を書き、多次元配列を処理する方法を学習します。そして仕上げには、プリプロセッサの説明です。
初心者向け MQL4 言語はじめに 初心者向け MQL4 言語はじめに
この記事シリーズは、プログラミングのことは何も知らないが、最短の時間、最小の労力でできるだけ速く MQL4 言語を学びたいという願いを持つトレーダーを対象としています。みなさんが『オブジェクト指向』、『三次元配列』などのフレーズに恐れをいだいているなら、本稿はそんな人が必要とするものです。レッスンは最大に迅速な結果を出すために作成されています。そのうえ、情報は理解しやすいように提供されています。理論を深く掘り下げすぎることはしませんが、初回レッスンですでに実用的な効用を得ることでしょう。
テクニカル分析:不可能を可能にする! テクニカル分析:不可能を可能にする!
本稿は疑問に答えます。多くが違うことを示す場面でなぜ不可能が可能になるのか?テクニカル分析推論
МetaТrader 4のイベント МetaТrader 4のイベント
この記事は、注文のオープン、クローズ、変更などのイベントを観測するМetaТrader 4ターミナルのプログラムに着目しており、MQL4でのプログラミングやターミナルを扱う基本的なスキルを持つユーザーを想定して書かれています。