
Candlestick Trend Constraintモデルの構築(第8回):エキスパートアドバイザーの開発 (I)
内容
はじめに
MetaEditorソフトウェアには、プロファイリング中に検出されたエラーを効果的に管理するコンパイラが含まれています。このツールのおかげで、旧バージョンでリスクとリターンの矩形が意図したとおりに表示されなかった原因を突き止めることができました。プログラムは正常にコンパイルされましたが、問題はコードそのものにあったわけではありません。その代わりに、主に特定のテクニカルのために、振り返りローソク足の範囲内に何も表示されなかったという事実に課題がありました。- 振り返りローソク足の値がデフォルトで5,000本と高く設定されすぎていた
- 1つのプログラムに複数のバッファがあったため、計算が複雑になり、指標チャートウィンドウの表示が遅くなった
遭遇した問題の解決方法について簡単に説明した後、この記事の主な目標である、改良されたTrend Constraint指標に基づいたエキスパートアドバイザーの開発に移ります。以下は、メイン指標で当初解決しようとしていた問題が別のスクリプトによってどのように解決されたかを示した画像です。
矩形を使用して自動的に描画されるリスクとリターンの矩形
リスクとリターンの矩形を描くことに関する先行課題の解決策
次のように指標プログラムの課題に対処します。- ルックバック期間を5000バーから1000バーに短縮して、計算するデータ量を大幅に減少しました。
- また、プログラムの負荷を軽減するために、ツールセットの一部としてスタンドアローンのスクリプトを作成しました。このスクリプトは、指標内のバッファ6とバッファ7が処理する条件を特に確認し、それらが満たされた場合、スクリプトは必要なリスクとリターンの矩形を描画します。さらに、エントリ価格、ストップロス、テイクプロフィットの各価格レベルを示すラインを追加します。このスクリプトは1回だけ実行されるものであり、継続的に動作するものではないため、ユーザーは手動でチャートにスクリプトを追加し、描画されたオブジェクトと価格マークによって表される取引レベルを確認する必要があります。
下の画像は、スクリプトを起動した際の画面例です。
Trend Constraint R-Rスクリプト:移動平均線がクロスオーバーしたときのリスクとリターンの矩形の描画
この機能を分離することで、コンピュータや取引端末がフリーズすることなく、指標がスムーズに動作するようにしました。リスクとリターンの矩形を組み込み、エグジットレベルをマークすることで、トレーダーは取引の方向性とターゲットを事前に視覚的に評価することができ、EAを使わなくても手動取引が可能になります。必要なロジックを備えたスクリプトプログラムは完璧に機能しました。以下がスクリプトの全プログラムです。//+------------------------------------------------------------------+ //| Trend Constraint R-R.mq5 | //| Script program | //+------------------------------------------------------------------+ #property strict #property script_show_inputs #property copyright "2024 Clemence Benjamin" #property version "1.00" #property link "https://www.mql5.com/ja/users/billionaire2024/seller" #property description "A script program for drawing risk and rewars rectangles based on Moving Averaage crossover." //--- input parameters input int FastMAPeriod = 14; input int SlowMAPeriod = 50; input double RiskHeightPoints = 5000.0; // Default height of the risk rectangle in points input double RewardHeightPoints = 15000.0; // Default height of the reward rectangle in points input color RiskColor = clrIndianRed; // Default risk color input color RewardColor = clrSpringGreen; // Default reward color input int MaxBars = 500; // Maximum bars to process input int RectangleWidth = 10; // Width of the rectangle in bars input bool FillRectangles = true; // Option to fill rectangles input int FillTransparency = 128; // Transparency level (0-255), 128 is 50% transparency //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- delete existing rectangles and lines DeleteExistingObjects(); //--- declare and initialize variables int i, limit; double FastMA[], SlowMA[]; double closePrice, riskLevel, rewardLevel; //--- calculate moving averages if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0) { Print("Error in calculating moving averages."); return; } ArraySetAsSeries(FastMA, true); ArraySetAsSeries(SlowMA, true); CopyBuffer(iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, FastMA); CopyBuffer(iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, SlowMA); limit = MathMin(ArraySize(FastMA), ArraySize(SlowMA)); for (i = 1; i < limit - 1; i++) { //--- check for crossover if (FastMA[i] > SlowMA[i] && FastMA[i - 1] <= SlowMA[i - 1]) { //--- long position entry point (bullish crossover) closePrice = iClose(NULL, 0, i); riskLevel = closePrice + RiskHeightPoints * Point(); rewardLevel = closePrice - RewardHeightPoints * Point(); //--- draw risk rectangle DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor); //--- draw reward rectangle DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor); //--- draw entry, stop loss, and take profit lines DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits)); DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits)); DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits)); } else if (FastMA[i] < SlowMA[i] && FastMA[i - 1] >= SlowMA[i - 1]) { //--- short position entry point (bearish crossover) closePrice = iClose(NULL, 0, i); riskLevel = closePrice - RiskHeightPoints * Point(); rewardLevel = closePrice + RewardHeightPoints * Point(); //--- draw risk rectangle DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor); //--- draw reward rectangle DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor); //--- draw entry, stop loss, and take profit lines DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits)); DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits)); DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits)); } } } //+------------------------------------------------------------------+ //| Function to delete existing rectangles and lines | //+------------------------------------------------------------------+ void DeleteExistingObjects() { int totalObjects = ObjectsTotal(0, 0, -1); for (int i = totalObjects - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, -1); if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 || StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 || StringFind(name, "TakeProfit_") >= 0) { ObjectDelete(0, name); } } } //+------------------------------------------------------------------+ //| Function to draw rectangles | //+------------------------------------------------------------------+ void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); datetime startTime = iTime(NULL, 0, startBar); datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar); if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice)) Print("Failed to create rectangle: ", name); // Set the color with transparency (alpha value) int alphaValue = FillTransparency; // Adjust transparency level (0-255) color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); // Combine alpha with RGB ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background if (FillRectangles) { ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); // Fill color with transparency } else { ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); // No fill color } } //+------------------------------------------------------------------+ //| Function to draw price lines | //+------------------------------------------------------------------+ void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText) { datetime time = iTime(NULL, 0, barIndex); datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); // Extend line to the right if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price)) Print("Failed to create price line: ", name); ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background // Create text label string labelName = name + "_Label"; if (ObjectFind(0, labelName) >= 0) ObjectDelete(0, labelName); if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price)) Print("Failed to create label: ", labelName); ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_LEFT); ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); ObjectSetInteger(0, labelName, OBJPROP_XOFFSET, 5); ObjectSetInteger(0, labelName, OBJPROP_YOFFSET, 0); }
EAの開発を進める前に、スクリプトの性能について徹底的に議論しましょう。
- 入力パラメータを定義することで、取引戦略の主要な側面をカスタマイズできるようにしました。具体的には、高速移動平均と低速移動平均の期間の調整、リスクとリターンの矩形の寸法や色の設定、処理するバーの最大数の指定、矩形を塗りつぶすかどうかの選択が可能です。これにより、ユーザーの好みに合わせたさまざまな取引戦略に対応できるようになり、プログラムの入力パラメータセクションが形成されました。
//---- Input Parameters input int FastMAPeriod = 14; input int SlowMAPeriod = 50; input double RiskHeightPoints = 5000.0; input double RewardHeightPoints = 15000.0; input color RiskColor = clrIndianRed; input color RewardColor = clrSpringGreen; input int MaxBars = 500; input int RectangleWidth = 10; input bool FillRectangles = true; input int FillTransparency = 128;
- OnStart関数では、まず既存のリスクとリターンの矩形および価格ラインを削除するようにスクリプトを設計し、チャートが常にクリーンな状態で保たれるようにしました。次に、iMA関数を使用して高速移動平均と低速移動平均を計算し、それらの値をさらなる処理のために配列に格納しました。スクリプトがチャート上のバーをループする際、動きの速い平均が動きの遅い平均を上回る「強気のクロスオーバー」を検出する条件を設定しました。これらの条件が満たされると、エントリ価格、リスクレベル(ストップロス)、リワードレベル(テイクプロフィット)を計算し、チャートに矩形と価格ラインをを描画して、これらの重要な取引レベルを視覚的に示すようにしました。次に、これに関連するサブコードスニペットについて説明します。
//----Onstart Function void OnStart() { //--- delete existing rectangles and lines DeleteExistingObjects(); //--- declare and initialize variables int i, limit; double FastMA[], SlowMA[]; double closePrice, riskLevel, rewardLevel; //--- calculate moving averages if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0) { Print("Error in calculating moving averages."); return; }
- チャートの明瞭さを保つために、DeleteExistingObjects関数を作成しました。この関数は、スクリプトが以前に描画した売買シグナルに関連するオブジェクトを削除する役割を果たします。スクリプトは、チャート上のすべてのオブジェクトの名前を確認し、不要なものを削除することで、最新の関連情報のみが表示されるようにします。これにより、チャートの見やすさが向上し、不要な情報による混乱が排除されました。
//---- DeleteAllExistingObjects Function void DeleteExistingObjects() { int totalObjects = ObjectsTotal(0, 0, -1); for (int i = totalObjects - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, -1); if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 || StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 || StringFind(name, "TakeProfit_") >= 0) { ObjectDelete(0, name); } } }
- DrawRectangle関数では、まず重複を避けるために、同じ名前の既存の矩形をすべて削除します。これにより、スクリプトがリスクとリワードのレベルを視覚的に表現できるようになります。次に、バーのインデックスに基づいて矩形の開始時間と終了時間を計算し、色と透明度を慎重に設定します。この工夫により、矩形は他の重要な詳細を隠すことなく、チャート上で際立つようになります。
///---Draw rectangle function void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); datetime startTime = iTime(NULL, 0, startBar); datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar); if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice)) Print("Failed to create rectangle: ", name); int alphaValue = FillTransparency; color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); if (FillRectangles) { ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); } else { ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); } }
- 最後に、スクリプトにエントリ、ストップロス、テイクプロフィットレベルに水平ラインを追加するよう指示するDrawPriceLine関数を実装しました。この関数により、スクリプトはこれらの線をチャート全体に伸ばし、対応する価格水準を表示するテキストラベルを追加します。これにより、ユーザーは移動平均線によって生成されたシグナルに基づき、取引のキーポイントを素早く特定し、管理することが可能になります。
///---- Draw Price Lines Function void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText) { datetime time = iTime(NULL, 0, barIndex); datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price)) Print("Failed to create price line: ", name); ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_BACK, true); string labelName = name + "_Label"; if (ObjectFind(0, labelName) >= 0) ObjectDelete(0, labelName); if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price)) Print("Failed to create label: ", labelName); ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor); ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10); ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); ObjectSetInteger(0, labelName, OBJPROP_BACK, true); }
取引キットの一部として、このスクリプトを定期的に起動し、過去と現在の取引レベルを視覚化できます。次に、独自のEAを作成します。この記事では、最終的に作動するEAまでの開発全体を説明することに重点を置きます。特に、前回作成した指標「Trend Constraint V1.09」に沿って動作させることに焦点を当てます。
指標に基づいて動作するEAを作成する
カスタム指標を使用してMQL5 EAを作成するには、カスタム指標のファイル(.ex5)がMetaTrader 5プラットフォーム内のIndicatorsフォルダに存在していることを確認する必要があります。この場合はTrend Constraint V1.09です。MetaEditorを使用して、指標のバッファ値にアクセスするMQL5関数を組み込んだEAを作成します。EA内でカスタム指標を呼び出すために、iCustom()関数を用いて銘柄や時間枠などの必要なパラメータを指定します。
指標バッファからデータを抽出するには、CopyBuffer()関数を使用します。この関数は、取引シグナルを分析するためのバッファ値を取得します。次に、これらのバッファ値に基づいて取引ロジックを実装し、戦略に従って注文をオープン、クローズ、または修正する条件を定義します。た、ストップロスやテイクプロフィットなどのリスク管理機能を統合し、慎重な取引管理を実現します。MetaTrader 5ストラテジーテスターを使用してEAをバックテストし、そのパフォーマンスを評価し、パラメータを微調整します。最後に、ライブ取引に移行する前に、デモ口座環境でEAの機能を検証し、実際の市場環境下で効果的に動作することを確認します。
まずはMetaEditorでEAのテンプレートを起動し、この画像の例に基づいて開発や修正をおこないます。
MetaEditorでEAテンプレートを起動する
EAの構築手順を6つの段階に分けて説明します。この作業を進めるにあたり、コードスニペットを直接MetaEditorに入力することをお勧めします。この実践的なアプローチは、特にEA開発初心者にとっては、手順をよりよく理解し、内面化するのに役立ちます。
1. ヘッダーとメタデータ
ヘッダーセクションでは、EAの名前と目的を定義します。著作権情報、プロフィールへのリンク、バージョンを明記することで、EAを簡単に識別し、追跡できるようにしています。このメタデータは、特にEAが共有されたり変更されたりした場合に、自分や他のユーザーがEAの出所や目的を理解するのに役立ちます。
//You can replace the author details with yours. //+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/ja/users/billionaire2024/seller" #property version "1.0" #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"
2. 入力パラメータ
ここでは、コードを変更することなくEAの動作をカスタマイズするための主要な入力パラメータを定義します。ロットサイズ、スリッページ、ストップロス、テイクプロフィット、マジックナンバーなどのパラメータを設定することで、EAの柔軟性が向上し、さまざまな取引戦略に適応できるようになります。このマジックナンバーは特に重要であり、このEAが実行した取引を一意に識別できるため、複数のEAや手動取引が関与する場合には非常に役立ちます。
///----------- EA inputs parameters for customizations input double Lots = 0.1; // Lot size input int Slippage = 3; // Slippage input double StopLoss = 50; // Stop Loss in points input double TakeProfit = 100; // Take Profit in points input int MagicNumber = 123456; // Magic number for orders
3. 初期化関数(OnInit)
OnInit関数では、EAの動作の基盤を整えるために必要なコンポーネントを初期化します。まず、カスタム指標であるTrend Constraint V1.09のハンドルを取得します。このハンドルにより、プログラムが指標と対話できるようになります。ハンドルの取得に成功した場合、バッファ配列(Buffer6とBuffer7)を直列に設定し、指標値の保存と操作が可能になります。一方、ハンドルの取得に失敗した場合は、EAが初期化に失敗し、問題の診断に役立つエラーメッセージを返します。
////-------Initialization Function int OnInit() { //--- Get the indicator handle indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09"); if (indicator_handle < 0) { Print("Failed to get the indicator handle. Error: ", GetLastError()); return(INIT_FAILED); } //--- Set the buffer arrays as series ArraySetAsSeries(Buffer6, true); ArraySetAsSeries(Buffer7, true); return(INIT_SUCCEEDED); }
4. 初期化解除関数(OnDeinit)
EAがチャートから削除されるか、プラットフォームが閉じられると、OnDeinit関数が実行されます。この関数では、EAの動作中に割り当てられたリソースを確実に解放するために、指標ハンドルを適切に解放します。このクリーンアップ手順は、不必要なリソースの消費を防ぎ、取引環境の効率性と安定性を維持するために非常に重要です。
///------Deinitialization Function(OnDeinit) void OnDeinit(const int reason) { //--- Release the indicator handle IndicatorRelease(indicator_handle); }
5. メイン実行関数(OnTick)
OnTick関数は、実際の取引アクションがおこなわれる場所です。この関数は、新しいマーケットティックを受信するたびに呼び出されます。まず、同じマジックナンバーのポジションがすでに存在するかどうかを確認し、重複した取引を避けます。次に、指標バッファ(Buffer6とBuffer7)から最新の値をコピーし、売買の意思決定をおこないます。売買シグナルの条件が満たされた場合、適切な取引リクエストを作成し、送信します。注文の種類、価格、ストップロス、テイクプロフィット、スリッページなど、必要なパラメータをすべて指定し、取引戦略を効果的に実行できるよう配慮しています。
///---Main Execution Function(OnTick) void OnTick() { //--- Check if there is already an open position with the same MagicNumber if (PositionSelect(Symbol())) { if (PositionGetInteger(POSITION_MAGIC) == MagicNumber) { return; // Exit OnTick if there's an open position with the same MagicNumber } } //--- Calculate the indicator if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0) { Print("Failed to copy buffer values. Error: ", GetLastError()); return; } //--- Check for a buy signal if (Buffer7[0] != EMPTY_VALUE) { double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits); double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits); //--- Prepare the buy order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Buy Order"; //--- Send the buy order if (!OrderSend(request, result)) { Print("Error opening buy order: ", result.retcode); } else { Print("Buy order opened successfully! Ticket: ", result.order); } } //--- Check for a sell signal if (Buffer6[0] != EMPTY_VALUE) { double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits); double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits); //--- Prepare the sell order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Sell Order"; //--- Send the sell order if (!OrderSend(request, result)) { Print("Error opening sell order: ", result.retcode); } else { Print("Sell order opened successfully! Ticket: ", result.order); } } }
6. その他の関数
EAが遭遇する可能性のあるさまざまなイベントを処理するために、いくつかの関数も用意されていますが、これらはEAテンプレートの一部として提供されたものであり、現在は簡略化のために使用していません。
- OnTrade:ここでは、取引イベントが発生した際に必要な特定のアクションを処理できます。現在は空ですが、必要に応じてロジックを追加するためのスペースとして確保されています。
- OnTester:この関数は、バックテスト中のカスタム計算に使用されます。値を返すことで、特定の指標に基づいて戦略を最適化できます。
- OnTesterInit、OnTesterPass、OnTesterDeinit:これらの関数は、ストラテジーテスター内の最適化プロセスに関与しています。これにより、設定の初期化、最適化パス後のアクションの実行、最適化後のリソースのクリーンアップが可能となります。
- OnChartEvent:この関数を使用すると、マウスのクリックやキーの押下など、さまざまなチャートイベントを処理できます。現在は空ですが、このスペースを利用してEAにインタラクティブな機能を追加することが可能です。
///----Other Template functions available void OnTrade() { //--- Handle trade events if necessary } double OnTester() { double ret = 0.0; //--- Custom calculations for strategy tester return (ret); } void OnTesterInit() { //--- Initialization for the strategy tester } void OnTesterPass() { //--- Code executed after each pass in optimization } void OnTesterDeinit() { //--- Cleanup after tester runs } void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handle chart events here }
最終的なプログラムはこのようになります。
//+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/ja/users/billionaire2024/seller" #property version "1.0" #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09" //--- Input parameters for the EA input double Lots = 0.1; // Lot size input int Slippage = 3; // Slippage input double StopLoss = 50; // Stop Loss in points input double TakeProfit = 100; // Take Profit in points input int MagicNumber = 123456; // Magic number for orders //--- Indicator handle int indicator_handle; double Buffer6[]; double Buffer7[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Get the indicator handle indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09"); if (indicator_handle < 0) { Print("Failed to get the indicator handle. Error: ", GetLastError()); return(INIT_FAILED); } //--- Set the buffer arrays as series ArraySetAsSeries(Buffer6, true); ArraySetAsSeries(Buffer7, true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release the indicator handle IndicatorRelease(indicator_handle); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Check if there is already an open position with the same MagicNumber if (PositionSelect(Symbol())) { if (PositionGetInteger(POSITION_MAGIC) == MagicNumber) { return; // Exit OnTick if there's an open position with the same MagicNumber } } //--- Calculate the indicator if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0) { Print("Failed to copy buffer values. Error: ", GetLastError()); return; } //--- Check for a buy signal if (Buffer7[0] != EMPTY_VALUE) { double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits); double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits); //--- Prepare the buy order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Buy Order"; //--- Send the buy order if (!OrderSend(request, result)) { Print("Error opening buy order: ", result.retcode); } else { Print("Buy order opened successfully! Ticket: ", result.order); } } //--- Check for a sell signal if (Buffer6[0] != EMPTY_VALUE) { double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits); double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits); //--- Prepare the sell order request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Sell Order"; //--- Send the sell order if (!OrderSend(request, result)) { Print("Error opening sell order: ", result.retcode); } else { Print("Sell order opened successfully! Ticket: ", result.order); } } } //+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade() { //--- Handle trade events if necessary } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { double ret = 0.0; //--- Custom calculations for strategy tester return (ret); } //+------------------------------------------------------------------+ //| TesterInit function | //+------------------------------------------------------------------+ void OnTesterInit() { //--- Initialization for the strategy tester } //+------------------------------------------------------------------+ //| TesterPass function | //+------------------------------------------------------------------+ void OnTesterPass() { //--- Code executed after each pass in optimization } //+------------------------------------------------------------------+ //| TesterDeinit function | //+------------------------------------------------------------------+ void OnTesterDeinit() { //--- Cleanup after tester runs } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handle chart events here } //+------------------------------------------------------------------+
コンパイルに成功した次のセグメントでは、プログラムをテストします。
テスト
MetaEditorでは、[Compile]または[Run]ボタンを使って、テスト用のプログラムを準備することができます。この場合、コンパイルは成功し、ストラテジーテスターを使ってBoom 500 Indexのテストを開始しました。
ナビゲーターからのテスターの起動
ストラテジーテスターパネルが開き、右下の[スタート]ボタンをクリックする前にいくつかの設定を調整できます。例えば、EAのデフォルトのロットサイズは0.1ロットに設定されていますが、Boom500指数の場合、今回は最低0.2ロットまで増やす必要がありました。
ストラテジーテスターパネル
驚くべきことに、私たちのシステムはストラテジーテスターで優れた成績を収めました。
ストラテジーテスターの可視化:Trend Constraint EA
結論
リスクとリターンの矩形を追加したことで、トレーダーは取引を明確かつグラフィカルに表示できるようになり、ポジションの監視と管理が容易になりました。この視覚的支援は、迅速な意思決定が求められる動きの速い市場において特に有用です。矩形は、取引の潜在的な結果を常に意識させる役割を果たし、トレーダーが当初の取引計画を維持するのに役立ちます。
Trend Constraint V1.09指標とEAのコラボレーションの成功は、取引戦略におけるツール間の相乗効果の重要性を浮き彫りにしています。指標は潜在的なトレンドや反転を識別し、EAはこの情報に基づいて取引を実行し、リスクを管理します。この統合されたアプローチは、より一貫性のある効果的な取引戦略につながります。
以下に、使用した指標、スクリプト、EAを添付します。まだ改善や修正の余地がありますが、この情報が貴重であることを願っています。コメント欄でご意見をお聞かせください。取引をお楽しみください。
添付ファイル | 詳細 |
---|---|
Trend_Constraint V1.09.mq5 | EAで動作する指標のソースコード |
Trend Constraint R-R.mq5 | リスクとリターンの矩形スクリプトのソースコード |
Trend Constraint Expert.mq5 | Trend Constraint V1.09に厳密に対応するEAのソースコード |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/15321





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索