定義済みリスクおよびRRレシオに基づく半自動化ドラッグドロップExpert Advisor連携構築
はじめに
すべての取引を自動で行うトレーダーもいれば、複数インディケータのアウトプットを基にして自動と手動のミックスで取引を実行するトレーダーもいます。後者のグループの一員として、私はリスクと利益をチャートから直接、動的に評価するための連携ツールが必要でした。
資本の最大リスクを宣言し、チャートのストップ・ロスレベルに基づくリアルタイム パラメータを計算したいと思いました。また計算済みの 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 スクリーン ショット
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_BUY と ORDER_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
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索