English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
定義済みリスクおよびRRレシオに基づく半自動化ドラッグドロップExpert Advisor連携構築

定義済みリスクおよびRRレシオに基づく半自動化ドラッグドロップExpert Advisor連携構築

MetaTrader 5 | 7 10月 2015, 16:19
538 0
investeo
investeo

はじめに

すべての取引を自動で行うトレーダーもいれば、複数インディケータのアウトプットを基にして自動と手動のミックスで取引を実行するトレーダーもいます。後者のグループの一員として、私はリスクと利益をチャートから直接、動的に評価するための連携ツールが必要でした。

資本の最大リスクを宣言し、チャートのストップ・ロスレベルに基づくリアルタイム パラメータを計算したいと思いました。また計算済みの SL および TP に基づき、EA から直接トレードを実行する必要がありました。

本稿は、定義済みの資本リスクおよびR/Rレシオを連携する半自動化Expert Advisorを実装する方法を提供します。EA パネル実行中には、 Expert Advisor リスク、R/R、ロットサイズ パラメータが変更可能です。


1. 要件

EA に対する要件は以下です。

  • 起動時リスクを事前定義し、ポジションサイズにそれがどうかかわるか確認するために実行時変更する機能
  • 利益率に対するリスクを事前定義し、実行時変更する機能
  • 与えられたリスクとストップ・ロス レベルに対するリアルタイムの最大ロットサイズを計算する機能
  • 資本リスクおよび利益にどう影響するか確認するために実行時ロットサイズを変更する機能
  • EA から直接買い/売りマーケット注文を実行する機能
  • ストップ・ロスを設定し、利益レベルに対し事前定義されたリスクの価格レベルを確認するためのドロップ&ドラッグインターフェース


2. デザイン

実行時パラメータを表示し変更するという EA に対する要件に従い、チャートウィンドウに GUI を表示し、ユーザー連携用に着信するチャートイベントを処理するために CChartObject クラスとその派生クラスを使用することにしました。よって EA にはラベル、ボタン、編集フィールドを伴うユーザーインターフェースが必要となりました。

まず、パネル上の他のオブジェクトをグループ化するために CChartObjectPanel オブジェクトを使用したいと思いました。しかし、ここで別の方法をやってみることにしました。ラベル、編集フィールド、ボタンのあるクラスを設計し、背景画像にそれを表示するのです。インターフェースの背景画像は GIMP ソフトウェアを使って作成しました。MQL5 が作成したオブジェクトは、編集フィールド、リアルタイムに更新される赤いラベル、ボタンです。

チャート上にただラベル オブジェクトを置き、その位置を記録しました。そして CChartObjectEdit フィールドのパラメータを受け取り、ボタン状況を記録しつつ、計算されたアウトプット表示関数をすべて処理する CRRDialog クラスを構築しました。着色されたリスクと利益の長方形は CChartObjectRectangle クラスのオブジェクトで、ドラッグできるストップ・ロス ポインタは CChartObjectBitmap クラスのビットマップ オブジェクトです。

図1 ビジュアル EA スクリーン ショット

図1 ビジュアル EA スクリーン ショット


3. EA ダイアログ クラスの実装

CRRDialog クラスは EA のすべてのユーザーインターフェースを処理します。そこには表示される数々の変数、変数を表示するのに使用されるオブジェクト、get/set 変数値に対するメソッドまたダイアログをリフレッシュするメソッドが含まれています。

私は背景には CChartObjectBmpLabel オブジェクトを、編集フィールドには CChartObjectEdit オブジェクトを、ラベル表示には CChartObjectLabel オブジェクトを、ボタンには CChartObjectButton オブジェクトを使用しています。

class CRRDialog
  {
private:

   int               m_baseX;
   int               m_baseY;
   int               m_fontSize;
   
   string            m_font;
   string            m_dialogName;
   string            m_bgFileName;

   double            m_RRRatio;
   double            m_riskPercent;
   double            m_orderLots;
   double            m_SL;
   double            m_TP;
   double            m_maxAllowedLots;
   double            m_maxTicksLoss;
   double            m_orderEquityRisk;
   double            m_orderEquityReward;
   ENUM_ORDER_TYPE   m_orderType;

   CChartObjectBmpLabel m_bgDialog;

   CChartObjectEdit  m_riskRatioEdit;
   CChartObjectEdit  m_riskValueEdit;
   CChartObjectEdit  m_orderLotsEdit;

   CChartObjectLabel m_symbolNameLabel;
   CChartObjectLabel m_tickSizeLabel;
   CChartObjectLabel m_maxEquityLossLabel;
   CChartObjectLabel m_equityLabel;
   CChartObjectLabel m_profitValueLabel;
   CChartObjectLabel m_askLabel;
   CChartObjectLabel m_bidLabel;
   CChartObjectLabel m_tpLabel;
   CChartObjectLabel m_slLabel;
   CChartObjectLabel m_maxAllowedLotsLabel;
   CChartObjectLabel m_maxTicksLossLabel;
   CChartObjectLabel m_orderEquityRiskLabel;
   CChartObjectLabel m_orderEquityRewardLabel;
   CChartObjectLabel m_orderTypeLabel;

   CChartObjectButton m_switchOrderTypeButton;
   CChartObjectButton m_placeOrderButton;
   CChartObjectButton m_quitEAButton;

public:

   void              CRRDialog(); // CRRDialog constructor
   void             ~CRRDialog(); // CRRDialog destructor

   bool              CreateCRRDialog(int topX,int leftY);
   int               DeleteCRRDialog();
   void              Refresh();
   void              SetRRRatio(double RRRatio);
   void              SetRiskPercent(double riskPercent);
   double            GetRiskPercent();
   double            GetRRRRatio();
   void              SetSL(double sl);
   void              SetTP(double tp);
   double            GetSL();
   double            GetTP();
   void              SetMaxAllowedLots(double lots);
   void              SetMaxTicksLoss(double ticks);
   void              SetOrderType(ENUM_ORDER_TYPE);
   void              SwitchOrderType();
   void              ResetButtons();
   ENUM_ORDER_TYPE   GetOrderType();
   void              SetOrderLots(double orderLots);
   double            GetOrderLots();
   void              SetOrderEquityRisk(double equityRisk);
   void              SetOrderEquityReward(double equityReward);
  };

get/set 変数メソッドは簡単ですから、 CreateCRRDialog() メソッドおよび Refresh() メソッドに注目していきたいと思います。CreateCRRDialog() メソッドは背景画像、ラベル、ボタン、編集フィールドを初期化します。

ラベルの初期化およびフィールド編集には、チャート上にオブジェクトを配置するコーディネートパラメータを持つ Create() メソッドを、フォント設定には Font() メソッドと FontSize() メソッドを、ラベルにテキストを入れるのには Description() メソッドを使用しています。

ボタンには、ボタンサイズを指定する追加パラメータに Create() メソッドを、また BackColor() メソッドでボタンの背景色を指定します。

bool CRRDialog::CreateCRRDialog(int topX,int leftY)
  {
   bool isCreated=false;

   MqlTick current_tick;
   SymbolInfoTick(Symbol(),current_tick);

   m_baseX = topX;
   m_baseY = leftY;

   m_bgDialog.Create(0, m_dialogName, 0, topX, leftY);
   m_bgDialog.BmpFileOn(m_bgFileName);

   m_symbolNameLabel.Create(0, "symbolNameLabel", 0, m_baseX + 120, m_baseY + 40);
   m_symbolNameLabel.Font("Verdana");
   m_symbolNameLabel.FontSize(8);
   m_symbolNameLabel.Description(Symbol());

   m_tickSizeLabel.Create(0, "tickSizeLabel", 0, m_baseX + 120, m_baseY + 57);
   m_tickSizeLabel.Font("Verdana");
   m_tickSizeLabel.FontSize(8);
   m_tickSizeLabel.Description(DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE), Digits()));

   m_riskRatioEdit.Create(0, "riskRatioEdit", 0, m_baseX + 120, m_baseY + 72, 35, 15);
   m_riskRatioEdit.Font("Verdana");
   m_riskRatioEdit.FontSize(8);
   m_riskRatioEdit.Description(DoubleToString(m_RRRatio, 2));

   m_riskValueEdit.Create(0, "riskValueEdit", 0, m_baseX + 120, m_baseY + 90, 35, 15);
   m_riskValueEdit.Font("Verdana");
   m_riskValueEdit.FontSize(8);
   m_riskValueEdit.Description(DoubleToString(m_riskPercent, 2));

   m_equityLabel.Create(0, "equityLabel", 0, m_baseX + 120, m_baseY + 107);
   m_equityLabel.Font("Verdana");
   m_equityLabel.FontSize(8);
   m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2));

   m_maxEquityLossLabel.Create(0, "maxEquityLossLabel", 0, m_baseX + 120, m_baseY + 122);
   m_maxEquityLossLabel.Font("Verdana");
   m_maxEquityLossLabel.FontSize(8);
   m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)*m_riskPercent/100.0,2));

   m_askLabel.Create(0, "askLabel", 0, m_baseX + 120, m_baseY + 145);
   m_askLabel.Font("Verdana");
   m_askLabel.FontSize(8);
   m_askLabel.Description("");

   m_bidLabel.Create(0, "bidLabel", 0, m_baseX + 120, m_baseY + 160);
   m_bidLabel.Font("Verdana");
   m_bidLabel.FontSize(8);
   m_bidLabel.Description("");

   m_slLabel.Create(0, "slLabel", 0, m_baseX + 120, m_baseY + 176);
   m_slLabel.Font("Verdana");
   m_slLabel.FontSize(8);
   m_slLabel.Description("");

   m_tpLabel.Create(0, "tpLabel", 0, m_baseX + 120, m_baseY + 191);
   m_tpLabel.Font("Verdana");
   m_tpLabel.FontSize(8);
   m_tpLabel.Description("");

   m_maxAllowedLotsLabel.Create(0, "maxAllowedLotsLabel", 0, m_baseX + 120, m_baseY + 208);
   m_maxAllowedLotsLabel.Font("Verdana");
   m_maxAllowedLotsLabel.FontSize(8);
   m_maxAllowedLotsLabel.Description("");

   m_maxTicksLossLabel.Create(0, "maxTicksLossLabel", 0, m_baseX + 120, m_baseY + 223);
   m_maxTicksLossLabel.Font("Verdana");
   m_maxTicksLossLabel.FontSize(8);
   m_maxTicksLossLabel.Description("");

   m_orderLotsEdit.Create(0, "orderLotsEdit", 0, m_baseX + 120, m_baseY + 238, 35, 15);
   m_orderLotsEdit.Font("Verdana");
   m_orderLotsEdit.FontSize(8);
   m_orderLotsEdit.Description("");

   m_orderEquityRiskLabel.Create(0, "orderEquityRiskLabel", 0, m_baseX + 120, m_baseY + 255);
   m_orderEquityRiskLabel.Font("Verdana");
   m_orderEquityRiskLabel.FontSize(8);
   m_orderEquityRiskLabel.Description("");

   m_orderEquityRewardLabel.Create(0, "orderEquityRewardLabel", 0, m_baseX + 120, m_baseY + 270);
   m_orderEquityRewardLabel.Font("Verdana");
   m_orderEquityRewardLabel.FontSize(8);
   m_orderEquityRewardLabel.Description("");

   m_switchOrderTypeButton.Create(0, "switchOrderTypeButton", 0, m_baseX + 20, m_baseY + 314, 160, 20);
   m_switchOrderTypeButton.Font("Verdana");
   m_switchOrderTypeButton.FontSize(8);
   m_switchOrderTypeButton.BackColor(LightBlue);

   m_placeOrderButton.Create(0, "placeOrderButton", 0, m_baseX + 20, m_baseY + 334, 160, 20);
   m_placeOrderButton.Font("Verdana");
   m_placeOrderButton.FontSize(8);
   m_placeOrderButton.BackColor(LightBlue);
   m_placeOrderButton.Description("Place Market Order");

   m_quitEAButton.Create(0, "quitEAButton", 0, m_baseX + 20, m_baseY + 354, 160, 20);
   m_quitEAButton.Font("Verdana");
   m_quitEAButton.FontSize(8);
   m_quitEAButton.BackColor(LightBlue);
   m_quitEAButton.Description("Quit");

   return isCreated;
  }

Refresh() メソッドは、CRRDialog 変数によりすべてのラベル、ボタン記述を、そしてまた現在の bid/ask レベル、アカウント資本、資本リスク値をリフレッシュします。

void CRRDialog::Refresh()
  {
   MqlTick current_tick;
   SymbolInfoTick(Symbol(),current_tick);

   m_equityLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),2));
   m_maxEquityLossLabel.Description(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY)*
                                         StringToDouble(m_riskValueEdit.Description())/100.0,2));
   m_askLabel.Description(DoubleToString(current_tick.ask, Digits()));
   m_bidLabel.Description(DoubleToString(current_tick.bid, Digits()));
   m_slLabel.Description(DoubleToString(m_SL, Digits()));
   m_tpLabel.Description(DoubleToString(m_TP, Digits()));
   m_maxAllowedLotsLabel.Description(DoubleToString(m_maxAllowedLots,2));
   m_maxTicksLossLabel.Description(DoubleToString(m_maxTicksLoss,0));
   m_orderEquityRiskLabel.Description(DoubleToString(m_orderEquityRisk,2));
   m_orderEquityRewardLabel.Description(DoubleToString(m_orderEquityReward,2));

   if(m_orderType==ORDER_TYPE_BUY) m_switchOrderTypeButton.Description("Order Type: BUY");
   else if(m_orderType==ORDER_TYPE_SELL) m_switchOrderTypeButton.Description("Order Type: SELL");
  }


4. チャートイベント

EA は相互作用的に設計されるので、チャートイベントを処理する必要があります。

処理されるイベントは以下です。

  • チャート上の S/L ポインター (CChartObjectBitmap クラスのSL_arrow オブジェクト) ドラッグ - これによりR/Rレシオに基づき S/L レベルの収集と T/P レベルの計算が可能となります。
  • オーダータイプの切り替え (買い/売り)ボタン
  • 「マーケット発注」ボタン押下
  • リスク、R/R、注文ロットフィールド編集
  • 『終了』ボタン押下後 EA 終了

トレーダーにより編集フィールドが更新された後、ポインター選択について処理されるイベント CHARTEVENT_OBJECT_CLICK 、ボタンは CHARTEVENT_OBJECT_DRAG 、S/Lポインターをドラッグするのは CHARTEVENT_OBJECT_ENDEDIT です。

最初 OnChartEvent() 関数を実装するのにはコードを数行費やしますが、私はそれを複数のイベントハンドラに分割しました。これにより OnChartEvent() 関数を人が読解できる形式に変換しました。

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Check the event by pressing a mouse button
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      string clickedChartObject=sparam;

      if(clickedChartObject==slButtonID)
         SL_arrow.Selected(!SL_arrow.Selected());

      if(clickedChartObject==switchOrderTypeButtonID)
        {
         EA_switchOrderType();
        };

      if(clickedChartObject==placeOrderButtonID)
        {
         EA_placeOrder();
        }

      if(clickedChartObject==quitEAButtonID) ExpertRemove();

      ChartRedraw();
     }

   if(id==CHARTEVENT_OBJECT_DRAG)
     {
      // BUY 
      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
        {
         EA_dragBuyHandle();
        };

      // SELL
      if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
        {
         EA_dragSellHandle();
        };
      ChartRedraw();
     }

   if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      if((sparam==riskRatioEditID || sparam==riskValueEditID || sparam==orderLotsEditID) && orderPlaced==false)
        {
         EA_editParamsUpdate();
        }
     }
  }

イベントハンドラの実装については、次項にて詳しく述べます。SL_arrowオブジェクト選択に使った方法は知って損はないと思います。通常、チャート上でオブジェクトを選択するには、そのオブジェクト上で二度クリックする必要があります。しかし、一度だけクリックし、CChartObject オブジェクトのSelected() メソッドまたは、CHARTEVENT_OBJECT_CLICK イベントハンドラ内のOnChartEvent() 関数で継承者をを呼び出して選択することが可能です。

if(clickedChartObject==slButtonID)
         SL_arrow.Selected(!SL_arrow.Selected());

ワンクリック後、オブジェクトはその以前の状態に応じて選択されるか非選択状態になります。


5. CMoneyFixedRisk に基づき拡張された資金管理クラス

ChartEvent ハンドラについて述べる前に資金管理クラスについて検討します。

資金管理のために、私は MetaQuotes により提供されたCMoneyFixedRisk クラスを再利用し、CMoneyFixedRiskExt クラスを実装しました。

元の CMoneyFixedRisk クラスメソッドは与えられた価格に対して許可されたオーダーロット金額、ストップ・ロスレベル、ブローカーによって許可された最大最小ロットサイズ間の資金リスクを返します。ここで私は、リスク要件が満たされない場合にロットサイズ 0.0 を返すようCheckOpenLong() メソッドおよび CheckOpenShort() メソッドを変更し、それらを4つのメソッドによって拡張しました。すなわち、 GetMaxSLPossible()、CalcMaxTicksLoss() 、CalcOrderEquityRisk() 、CalcOrderEquityReward() です。

class CMoneyFixedRiskExt : public CExpertMoney
  {
public:
   //---
   virtual double    CheckOpenLong(double price,double sl);
   virtual double    CheckOpenShort(double price,double sl);
   
   double GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType);
   double CalcMaxTicksLoss();
   double CalcOrderEquityRisk(double price, double sl, double lots);
   double CalcOrderEquityReward(double price, double sl, double lots, double rrratio);
  };

GetMaxSLPossible() メソッドは与えられた資金リスクに対し最大ストップ・ロス価格値と許可された最小トレードサイズを計算します。

たとえば、アカウント残高がアカウント基本通貨10,000 で、リスク率が 2% だったとしたら、最大アカウント通貨リスクは 200 と入れます。最小のトレードロットサイズが0.1ロットなら、このメソッドは価格レベル 0.1ロットのポジションに対する資金リスク値を満たす ORDER_TYPE_BUY 注文または ORDER_TYPE_SELL 注文への価格レベルを返します。これは最小ロットサイズのトレードに対して許容できる最大ストップ・ロスレベルがどこか推定するのに役立ちます。これは、既定の資金リスクレベルに対して超えることのできない価格レベルです。

double CMoneyFixedRiskExt::GetMaxSLPossible(double price, ENUM_ORDER_TYPE orderType)
{
   double maxEquityLoss, tickValLoss, maxTicksLoss;
   double minvol=m_symbol.LotsMin();
   double orderTypeMultiplier;
   
   if(m_symbol==NULL) return(0.0);
   
   switch (orderType)
   {
   case ORDER_TYPE_SELL: orderTypeMultiplier = -1.0; break;
   case ORDER_TYPE_BUY: orderTypeMultiplier = 1.0; break;
   default: orderTypeMultiplier = 0.0;
   }
   
   maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss 
   tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss
   maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss);
 
   return (price - maxTicksLoss*m_symbol.TickSize()*orderTypeMultiplier);
}

CalcMaxTickLoss() メソッドは既定のリスクと許可された最小ロットサイズに対し損失を許容できるティックの最大数を返します。

まず、最大資金損失が現在残高のパーセンテージとして計算され、それから選択されたシンボルについて許可された最小ロットサイズに対し1ティックの変化に伴うティック値損失を計算します。それから、最大資本損失がティック値損失で除算され結果がMathFloor() 関数を使って整数値に四捨五入されます。

double CMoneyFixedRiskExt::CalcMaxTicksLoss()
{
   double maxEquityLoss, tickValLoss, maxTicksLoss;
   double minvol=m_symbol.LotsMin();
   
   if(m_symbol==NULL) return(0.0);
   
   maxEquityLoss = m_account.Balance()*m_percent/100.0; // max loss 
   tickValLoss = minvol*m_symbol.TickValueLoss(); // tick val loss
   maxTicksLoss = MathFloor(maxEquityLoss/tickValLoss);
   
   return (maxTicksLoss);
}

CalcOrderEquityRisk() メソッドは与えられた価格、ストップ・ロスレベル、ロット金額を返します。それはティック損失値にロット数および価格を乗じ、その後さらに現在価格とストップ・ロスレベルの差を乗じることにより計算されます。

double CMoneyFixedRiskExt::CalcOrderEquityRisk(double price,double sl, double lots)
{
   double equityRisk;
   
   equityRisk = lots*m_symbol.TickValueLoss()*(MathAbs(price-sl)/m_symbol.TickSize()); 
   
   if (dbg) Print("calcEquityRisk: lots = " + DoubleToString(lots) +
                 " TickValueLoss = " + DoubleToString(m_symbol.TickValueLoss()) +
                 " risk = " + DoubleToString(equityRisk));
   
   return equityRisk;
}

CalcOrderEquityReward() メソッドは CalcOrderEquityRisk() メソッドに類似していますが、それは TickValueLoss() メソッドの代わりにTickValueProfit() メソッドを使い、結果は利益率に与えられたリスク率で除算されます。

double CMoneyFixedRiskExt::CalcOrderEquityReward(double price,double sl, double lots, double rrratio)
{
   double equityReward; 
   equityReward = lots*m_symbol.TickValueProfit()*(MathAbs(price-sl)/m_symbol.TickSize())*rrratio; 
   
   if (dbg) Print("calcEquityReward: lots = " + DoubleToString(lots) + 
                   " TickValueProfit = " + DoubleToString(m_symbol.TickValueProfit()) +
                 " reward = " + DoubleToString(equityReward));
   return equityReward;
}

最大ストップ・ロスレベルを計算し、リアルタイムのし資金リスクと利益を返すにはこれらメソッドで十分です。CalcMaxTickLoss() メソッドはリスク長方形の描画を修正するのに使われます。トレーダーが損失を許容できるティック数の境界をクロスするトレードを入れたい場合、損失可能性のあるティックの最大数のみ長方形で囲まれます。

チャートで直接見た方が理解しやすいでしょう。チャートは本稿の末尾に用意しているデモでご覧いただけます。


6. チャートイベント ハンドラの実装

EA_switchOrderType()ハンドラはm_switchOrderTypeButton オブジェクト上で CHARTEVENT_OBJECT_CLICKが受け取られた後、起動します。それは ORDER_TYPE_BUYORDER_TYPE_SELL の間でオーダータイプ、ボタン状態、ダイアログの変数をリセットし、チャート上のリスクおよび利益長方形を削除します。

void EA_switchOrderType()
  {
   symbolInfo.RefreshRates();

   visualRRDialog.SwitchOrderType();
   visualRRDialog.ResetButtons();
   visualRRDialog.SetSL(0.0);
   visualRRDialog.SetTP(0.0);
   visualRRDialog.SetMaxAllowedLots(0.0);
   visualRRDialog.SetOrderLots(0.0);
   visualRRDialog.SetMaxTicksLoss(0);
   visualRRDialog.SetOrderEquityRisk(0.0);
   visualRRDialog.SetOrderEquityReward(0.0);

   if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask());
   else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid());
   SL_arrow.SetInteger(OBJPROP_TIME,0,TimeCurrent());

   rectReward.Delete();
   rectRisk.Delete();

   visualRRDialog.Refresh();

  }

EA_dragBuyHandle() ハンドラは SL_arrow オブジェクトがドラッグされ、チャートにドロップされた後起動します。まず SL_arrow オブジェクトドロップポイント時刻、および価格パラメータをチャートから読み、トレードに対する仮定のストップ・ロスとして価格レベルを設定します。

それから、資本リスクに対してオープン可能なロット数を計算します。ストップ・ロス値がそのシンボルについて可能な最低トレードロットのリスク目標が保証できなければ、可能な最大SLレベルに自動的に移動します。これは、与えられたリスクに対しストップ・ロスにどのくらいスペースを充てることができるか決定するのに役立ちます。

損失と利益を計算したら、長方形はチャート上で更新されます。

void EA_dragBuyHandle()
  {
   SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

// BUY
   double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);
   Print("Allowed lots = "+DoubleToString(allowedLots,2));
   double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY);

   if(SL_price<lowestSLAllowed)
     {
      SL_price=lowestSLAllowed;
      ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());

   if(visualRRDialog.GetTP()<SL_price)
     {
      visualRRDialog.SetSL(0.0);
      visualRRDialog.SetTP(0.0);
      SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Ask());
      rectReward.Delete();
      rectRisk.Delete();
      return;
     }

   double lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);

   visualRRDialog.SetMaxAllowedLots(lotSize);
   visualRRDialog.SetOrderLots(lotSize);
   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, lotSize));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), 
                                       SL_price, lotSize, visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();

   rectUpdate(visualRRDialog.GetOrderType());

  }

EA_dragSellHandle() は売り注文構成のために起動します。

計算は symbolInfo.Bid() 価格に基づき、適切に長方形が描かれます。利益をマークしているグリーン部分は現在価格レベルより下にきます。

void EA_dragSellHandle()
  {
   SL_arrow.GetDouble(OBJPROP_PRICE,0,SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

   double allowedLots=MM.CheckOpenShort(symbolInfo.Bid(),SL_price);
   Print("Allowed lots = "+DoubleToString(allowedLots,2));
   double maxSLAllowed=MM.GetMaxSLPossible(symbolInfo.Bid(),ORDER_TYPE_SELL);

   if(SL_price>maxSLAllowed)
     {
      SL_price=maxSLAllowed;
      SL_arrow.SetDouble(OBJPROP_PRICE,0,maxSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());

   if(visualRRDialog.GetTP()>SL_price)
     {
      visualRRDialog.SetSL(0.0);
      visualRRDialog.SetTP(0.0);
      SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid());
      rectReward.Delete();
      rectRisk.Delete();
      return;
     }

   double lotSize=MM.CheckOpenShort(symbolInfo.Bid(),SL_price);

   visualRRDialog.SetMaxAllowedLots(lotSize);
   visualRRDialog.SetOrderLots(lotSize);
   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Bid(), SL_price, lotSize));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(),
                                       SL_price, lotSize, visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();

   rectUpdate(visualRRDialog.GetOrderType());

  }

EA_placeOrder() は m_placeOrderButton オブジェクトが押されてから起動します。計算されたSL や TP レベル、既定のロットサイズに対して買いまたは売りのマーケット発注をします。

CExpertTrade クラスを使うとマーケット発注がいかにも簡単なことに留意ください。

bool EA_placeOrder()
  {
   symbolInfo.RefreshRates();
   visualRRDialog.ResetButtons();

   if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
      orderPlaced=trade.Buy(visualRRDialog.GetOrderLots(),symbolInfo.Ask(),
                            visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent()));
   else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
      orderPlaced=trade.Sell(visualRRDialog.GetOrderLots(),symbolInfo.Bid(),
                            visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent()));

   return orderPlaced;
  }

EA_editParamsUpdate() ハンドラは、riskRatioEdit、riskValueEdit、orderLotsEdit の編集フィールドの一つを編集して「エンター」キーを押すと起動します。

その際、許容されるロットサイズ、TP レベル、最大ティック損失、資本リスク、利益が再計算される必要があります。

void EA_editParamsUpdate()
  {
   MM.Percent(visualRRDialog.GetRiskPercent());

   SL_arrow.GetDouble(OBJPROP_PRICE, 0, SL_price);
   SL_arrow.GetInteger(OBJPROP_TIME, 0, startTime);

   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();

   double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);

   double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY);
   if(SL_price<lowestSLAllowed)
     {
      SL_price=lowestSLAllowed;
      ObjectSetDouble(0,slButtonID,OBJPROP_PRICE,lowestSLAllowed);
     }

   visualRRDialog.SetSL(SL_price);
   visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());

   visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());
   visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), 
                                     SL_price, visualRRDialog.GetOrderLots()));
   visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, 
                                       visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
   visualRRDialog.Refresh();
   rectUpdate(visualRRDialog.GetOrderType());

   ChartRedraw();
  }

EA_onTick() は新規ティックが到着するたびに起動します。計算が行われるのは、発注がまだ行われていず、ストップ・ロスレベルが SL_arrow ポインターのドラッグによってすでに選択されたあとです。

発注後は、リスクと利益の描き直し同様、リスク、利益、TP レベルの描き直しも不要です。

void EA_onTick()
  {
   if(SL_price!=0.0 && orderPlaced==false)
     {
      double lotSize=0.0;
      SL_price=visualRRDialog.GetSL();
      symbolInfo.RefreshRates();

      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
         lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price);
      else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
         lotSize=MM.CheckOpenShort(symbolInfo.Ask(),SL_price);

      visualRRDialog.SetMaxAllowedLots(lotSize);
      if(visualRRDialog.GetOrderLots()>lotSize) visualRRDialog.SetOrderLots(lotSize);

      visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss());

      if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY)
        {
         visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());
         visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), 
                                           SL_price, visualRRDialog.GetOrderLots()));
         visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, 
                                             visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
        }
      else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL)
        {
         visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());
         visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(
                                           symbolInfo.Bid(), SL_price, visualRRDialog.GetOrderLots()));
         visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(), SL_price, 
                                             visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio()));
        }
      visualRRDialog.Refresh();
      rectUpdate(visualRRDialog.GetOrderType());
     }

   ChartRedraw(0);
  }

関数 rectUpdate() は色付リスクおよび利益を囲む長方形描きなおします。コントロールポイントは SL_arrow オブジェクト開始時刻で、現在の買いまたは売り価格値は注文タイプと SL および TP レベルに依存します。薄いピンクの長方形が示すのは、現在価格と SL レベルの間の価格範囲で、グリーンの長方形が示すのは、現在価格と TP レベル間の価格範囲です。

どちらの長方形も SL および TP 価格レベルにおける利益率に対するリスクの影響を観察するのに優れたツールで、トレードを始める前のリスク調整に役立ちます。

void rectUpdate(ENUM_ORDER_TYPE orderType)
  {
   symbolInfo.RefreshRates();
   currentTime=TimeCurrent();
   SL_arrow.GetInteger(OBJPROP_TIME,0,startTime);

   if(orderType==ORDER_TYPE_BUY)
     {
      rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Ask(),currentTime,symbolInfo.Ask()+
                       (symbolInfo.Ask()-visualRRDialog.GetSL())*visualRRDialog.GetRRRRatio());
      rectReward.Color(LightGreen);
      rectReward.Background(true);

      rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Ask());
      rectRisk.Color(LightPink);
      rectRisk.Background(true);
     }
   else if(orderType==ORDER_TYPE_SELL)
     {
      rectReward.Create(0,rewardRectID,0,startTime,symbolInfo.Bid(),currentTime,symbolInfo.Bid()-
                        (visualRRDialog.GetSL()-symbolInfo.Bid())*visualRRDialog.GetRRRRatio());
      rectReward.Color(LightGreen);
      rectReward.Background(true);

      rectRisk.Create(0,riskRectID,0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Bid());
      rectRisk.Color(LightPink);
      rectRisk.Background(true);
     }
  }


7. デモ

Expert Advisor が実際に動作しているデモを下記でご覧ください。2010年1月11日月曜日、マーケケットオープン直後大きなリバウンスの後に売り注文をしています。

見やすいようにビデオの画面を最大に、画質を480Pに設定してください。ビデオにはコメントも含まれています。


おわりに

本稿では、定義済みリスクおよびR/Rレシオに基づくマニュアルトレーディングのためのExpert Advisor連携構築方法を示しました。

チャート上のコンテンツ表示に標準クラスを使う方法、新規データ入力のためにチャートイベントを処理する方法、またドラッグ&ドロップオブジェクトを処理する方法をお見せしました。ここで提供した考え方が、 MQL5におけるその他の構成可能なビジュアルツールの構築の基礎として役に立つことを願っております。

ソースファイルおよびビットマップはすべて本稿に添付があります。

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

添付されたファイル |
visualrrea.mq5 (13.84 KB)
crrdialog.mqh (13.95 KB)
visualrrids.mqh (0.8 KB)
images.zip (159.05 KB)
CChartObject クラスに基づく新規GUIウィジェット設計と実装 CChartObject クラスに基づく新規GUIウィジェット設計と実装
前稿『GUI を使用した半自動Expert Advisor』を書いてのち、より複雑なインディケータやExpert Advisorsに新しい関数を伴うインターフェースを強化したいと思うようになりました。MQL5 標準ライブラリクラスを知ってから、新しいウィジェットを実装しました。本稿は、インディケータやExpert Advisorsで使用可能な新しい MQL5 GUI ウィジェットの設計と実装について述べます。本稿で提供しているウィジェットは CChartObjectSpinner、 CChartObjectProgressBar、CChartObjectEditTable です。
初心者のためのMQL5のカスタムインディケーター 初心者のためのMQL5のカスタムインディケーター
初めての人にはどんな新しいテーマも複雑で学ぶのが難しいように見えます。知っているテーマはシンプルでわかりやすく感じます。しかし、だれもが母国語さえも最初から勉強しなければならないことを単に忘れがちです。自分のトレーディングストラテジーを策定する上で幅広い可能性を提供するMQL5プログラミング言語でもそれは同じです。- 基本的な考えを最もシンプルな例から学びましょう。本記事ではテクニカルインディケーター とMetaTrader 5 クライアントターミナルの相互作用をシンプルなカスタムインディケーター SMAの例を用いて考えます。
MetaTrader 5での並列計算 MetaTrader 5での並列計算
人類の歴史において時間は高価値であり、われわれはそれを無駄に費やさないよう努力しています。本稿では、マルチ コア プロセッサのコンピュータをご使用の場合、お手元のExpert Advisor の動作スピードを上げる方法について述べていきます。また、提案のメソッド実装には MQL5以外の言語知識は必要とされません。
MQL5でのオブジェクトポインターの使用 MQL5でのオブジェクトポインターの使用
デフォルトで、MQL5ではすべてのオブジェクトがレファレンスによって渡されますが、オブジェクトポインターを使用する可能性があります。しかし、オブジェクトは初期化されないかもしれないので、ポインター 確認が必要です。 この場合、 MQL5はクリティカルエラーでアップロードされずにプログラムが終了します。自動作成されたオブジェクトはこのようなエラーが発生しないのでその意味で安全です。本記事ではオブジェクトレファレンスとオブジェクトポインターの違いを理解し、ポインターを使うセキュアコードの書き方を考察します。