MQL5での取引戦略の自動化(第26回):複数ポジション取引のためのピンバーナンピンシステムの構築
はじめに
前回の記事(第25回)では、最小二乗法を用いてサポートラインとレジスタンスラインを検出し、価格がラインに接触した際に自動でエントリーを生成し、視覚的なフィードバックも提供するトレンドライン取引システムをMetaQuotes Language 5 (MQL5)で開発しました。第26回となる今回は、ピンバーを検出して取引を開始し、複数ポジションをナンピン戦略で管理する「ピンバーナンピン」プログラムを作成します。これには、トレーリングストップ、ブレークイーブン調整、リアルタイム監視用のダッシュボードが含まれます。本記事では以下のトピックを扱います。
この記事を読み終える頃には、ピンバーを基盤とした強力なMQL5取引戦略を構築できるようになります。さっそく始めましょう。
ピンバーナンピンフレームワークの理解
今回構築するのは、ピンバーと呼ばれるローソク足パターンを活用した自動売買システムです。ピンバーは長いヒゲと小さな実体を持つ単一のローソク足で構成されており、市場の重要なレベルで強い反転を示すことが多いため、価格の拒否(プライスリジェクション)が起こったタイミングを高い精度で捉えられる点で人気があります。特にサポートやレジスタンスのレベルと組み合わせた場合、エントリー精度が高まります。以下は一般的なピンバー形成の例です。

本システムでは、現在の時間足でこれらのピンバーを検出し、最初の取引が逆行した場合にナンピン戦略を用いて追加ポジションを段階的に開きます。これは、トレーリングストップやブレークイーブン調整を用いたリスク管理をおこないつつ、取引全体の結果を改善することを目的としています。これを実現するために、まず前回のH4足の終値から算出したサポートまたはレジスタンスレベルに対してピンバーを特定し、取引が重要な市場ゾーンに沿うようにします。
次に、あらかじめ設定した価格間隔でポジションを追加するナンピン機構を実装し、ボラティリティの高い相場でも柔軟に対応できるようにします。最後に、リアルタイムの取引指標を表示するダッシュボードを組み込み、ラインなどの視覚的指標を用いて重要なレベルをマークし、戦略を効果的に監視し、調整できるようにします。まず、目指す機能の概要を示したうえで、実装の説明に進みます。

MQL5での実装
MQL5でプログラムを作成するには、まずMetaEditorを開き、ナビゲータに移動して、Indicatorsフォルダを見つけ、[新規作成]タブをクリックして、表示される手順に従ってファイルを作成します。ファイルが作成されたら、コーディング環境でプログラムをより柔軟にするための入力パラメータやグローバル変数を宣言していきます。
//+------------------------------------------------------------------+ //| a. Pin Bar Averaging EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #property strict #include <Trade\Trade.mqh> //--- Include Trade library for trading operations CTrade obj_Trade; //--- Instantiate trade object //+------------------------------------------------------------------+ //| Trading signal enumeration | //+------------------------------------------------------------------+ enum EnableTradingBySignal { //--- Define trading signal enum ENABLED = 1, // Enable trading signals DISABLED = 0 // Disable trading signals }; //+------------------------------------------------------------------+ //| Input parameters | //+------------------------------------------------------------------+ input bool useSignalMode = DISABLED; // Set signal mode (ENABLED/DISABLED) input int orderDistancePips = 50; // Set order distance (pips) input double lotMultiplier = 1; // Set lot size multiplier input bool useRSIFilter = false; // Enable RSI filter input int magicNumber = 123456789; // Set magic number input double initialLotSize = 0.01; // Set initial lot size input int compoundPercent = 2; // Set compounding percent (0 for fixed lots) input int maxOrders = 5; // Set maximum orders input double stopLossPips = 400; // Set stop loss (pips) input double takeProfitPips = 200; // Set take profit (pips) input bool useAutoTakeProfit = true; // Enable auto take profit input bool useTrailingStop = true; // Enable trailing stop input double trailingStartPips = 15; // Set trailing start (pips) input double breakevenPips = 10; // Set breakeven (pips) input string orderComment = "Forex_Algo_Trader"; // Set order comment input color lineColor = clrBlue; // Set line color input int lineWidth = 2; // Set line width //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ bool isTradingAllowed(); //--- Declare trading allowed check double slBreakevenMinus = 0; //--- Initialize breakeven minus double normalizedPoint; //--- Declare normalized point ulong currentTicket = 0; //--- Initialize current ticket double buyCount, currentBuyLot, totalBuyLots; //--- Declare buy metrics double sellCount, currentSellLot, totalSellLots; //--- Declare sell metrics double totalSum, totalSwap; //--- Declare total sum and swap double buyProfit, sellProfit, totalOperations; //--- Declare profit and operations double buyWeightedSum, sellWeightedSum; //--- Declare weighted sums double buyBreakEvenPrice, sellBreakEvenPrice; //--- Declare breakeven prices double minBuyLot, minSellLot; //--- Declare minimum lot sizes double maxSellPrice, minBuyPrice; //--- Declare price extremes
ピンバーナンピンシステムをMQL5で構築し、ピンバーパターンに基づく自動取引と堅牢なポジション管理システムを実現するための基盤として、まず<Trade\Trade.mqh>ライブラリをインクルードし、obj_TradeをCTradeオブジェクトとしてインスタンス化して、ポジション建てや決済などの取引操作を扱います。次に、EnableTradingBySignal列挙型を定義し、ENABLED(1)とDISABLED(0)でポジション管理に取引シグナルを使用するかどうかを制御します。続いて、EAをカスタマイズするための入力パラメータを設定します。シグナルモードの切替用ブール値、注文間隔(pips)、ロットサイズ倍率、RSIフィルタの切替、取引識別用マジックナンバー、初期ロットサイズ、複利率(0は固定ロット)、最大注文数、SL(ストップロス)およびTP(テイクプロフィット)(pips)、自動TPとトレーリングストップの切替、トレーリング開始およびブレークイーブン(pips)、注文コメント、視覚的指標用のラインカラーと幅です。
最後に、グローバル変数を宣言します。取引条件を確認する関数isTradingAllowed、SL調整用に初期化されたslBreakevenMinus=0、価格スケーリング用のnormalizedPoint、取引追跡用のcurrentTicket、buyCount、currentBuyLot、totalBuyLots、sellCount、currentSellLot、totalSellLots、totalSum、totalSwap、buyProfit、sellProfit、totalOperations、buyWeightedSum、sellWeightedSum、buyBreakEvenPrice、sellBreakEvenPrice、minBuyLot、minSellLot、maxSellPrice、minBuyPriceなどのポジションメトリクス用カウンタと合計値を宣言し、ピンバー検出とナンピン戦略のEAコアフレームワークを構築します。これで、ほとんどの処理がティックベースで実行されるため、プログラムの初期化に進むことができます。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { normalizedPoint = _Point; //--- Initialize point value if (_Digits == 5 || _Digits == 3) { //--- Check for 5 or 3 digit symbols normalizedPoint *= 10; //--- Adjust point value } ChartSetInteger(0, CHART_SHOW_GRID, false); //--- Disable chart grid obj_Trade.SetExpertMagicNumber(magicNumber); //--- Set magic number for trade object obj_Trade.SetTypeFilling(ORDER_FILLING_IOC); //--- Set order filling type return(INIT_SUCCEEDED); //--- Return success } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0); //--- Delete all chart objects ChartRedraw(0); //--- Redraw chart }
まず、OnInitイベントハンドラでは、normalizedPointを_Pointで初期化し、_Digitsを使用して5桁または3桁の銘柄に対して10倍して価格計算の精度を確保します。次に、ChartSetIntegerでCHART_SHOW_GRIDをfalseに設定してチャートグリッドを無効化し、表示をすっきりさせます。obj_TradeにはSetExpertMagicNumberでmagicNumberを設定して取引識別をおこない、SetTypeFillingで注文埋め方式をORDER_FILLING_IOCに設定します。最後にINIT_SUCCEEDEDを返して初期化が正常に完了したことを確認します。次に、OnDeinitイベントハンドラでは、ObjectsDeleteAllですべてのチャートオブジェクトを削除して、後で定義するダッシュボードやラインなどの視覚要素をクリアします。これは、チャートを確実に初期状態に戻すためだけにおこないます。さらに、ChartRedrawを呼び出してチャートを再描画し、クリーンな終了処理を保証します。複雑な取引ロジックに進む前に、プログラムを動的で保守しやすくするために必要なヘルパー関数をいくつか定義しておきましょう。
//+------------------------------------------------------------------+ //| Count total trades | //+------------------------------------------------------------------+ int CountTrades() { int positionCount = 0; //--- Initialize position count for (int trade = PositionsTotal() - 1; trade >= 0; trade--) { //--- Iterate through positions ulong ticket = PositionGetTicket(trade); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL || PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check trade type positionCount++; //--- Increment position count } } return(positionCount); //--- Return total count } //+------------------------------------------------------------------+ //| Count buy trades | //+------------------------------------------------------------------+ int CountTradesBuy() { int buyPositionCount = 0; //--- Initialize buy position count for (int trade = PositionsTotal() - 1; trade >= 0; trade--) { //--- Iterate through positions ulong ticket = PositionGetTicket(trade); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position buyPositionCount++; //--- Increment buy count } } return(buyPositionCount); //--- Return buy count } //+------------------------------------------------------------------+ //| Count sell trades | //+------------------------------------------------------------------+ int CountTradesSell() { int sellPositionCount = 0; //--- Initialize sell position count for (int trade = PositionsTotal() - 1; trade >= 0; trade--) { //--- Iterate through positions ulong ticket = PositionGetTicket(trade); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position sellPositionCount++; //--- Increment sell count } } return(sellPositionCount); //--- Return sell count } //+------------------------------------------------------------------+ //| Normalize price | //+------------------------------------------------------------------+ double NormalizePrice(double price) { return(NormalizeDouble(price, _Digits)); //--- Normalize price to symbol digits } //+------------------------------------------------------------------+ //| Get lot digit for normalization | //+------------------------------------------------------------------+ int fnGetLotDigit() { double lotStepValue = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP); //--- Get lot step value if (lotStepValue == 1) return(0); //--- Return 0 for step 1 if (lotStepValue == 0.1) return(1); //--- Return 1 for step 0.1 if (lotStepValue == 0.01) return(2); //--- Return 2 for step 0.01 if (lotStepValue == 0.001) return(3); //--- Return 3 for step 0.001 if (lotStepValue == 0.0001) return(4); //--- Return 4 for step 0.0001 return(1); //--- Default to 1 } //+------------------------------------------------------------------+ //| Check buy orders for specific magic number | //+------------------------------------------------------------------+ int CheckBuyOrders(int magic) { int buyOrderCount = 0; //--- Initialize buy order count for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position buyOrderCount++; //--- Increment buy count break; //--- Exit loop } } } return(buyOrderCount); //--- Return buy order count } //+------------------------------------------------------------------+ //| Check sell orders for specific magic number | //+------------------------------------------------------------------+ int CheckSellOrders(int magic) { int sellOrderCount = 0; //--- Initialize sell order count for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position sellOrderCount++; //--- Increment sell count break; //--- Exit loop } } } return(sellOrderCount); //--- Return sell order count } //+------------------------------------------------------------------+ //| Check total buy orders | //+------------------------------------------------------------------+ int CheckTotalBuyOrders(int magic) { int totalBuyOrderCount = 0; //--- Initialize total buy order count for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position totalBuyOrderCount++; //--- Increment buy count } } } return(totalBuyOrderCount); //--- Return total buy count } //+------------------------------------------------------------------+ //| Check total sell orders | //+------------------------------------------------------------------+ int CheckTotalSellOrders(int magic) { int totalSellOrderCount = 0; //--- Initialize total sell order count for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_MAGIC) != magic) continue; //--- Skip non-matching magic if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position totalSellOrderCount++; //--- Increment sell count } } } return(totalSellOrderCount); //--- Return total sell count } //+------------------------------------------------------------------+ //| Check market buy orders | //+------------------------------------------------------------------+ int CheckMarketBuyOrders() { int marketBuyCount = 0; //--- Initialize market buy count for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching magic if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position marketBuyCount++; //--- Increment buy count } } } return(marketBuyCount); //--- Return market buy count } //+------------------------------------------------------------------+ //| Check market sell orders | //+------------------------------------------------------------------+ int CheckMarketSellOrders() { int marketSellCount = 0; //--- Initialize market sell count for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching magic if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position marketSellCount++; //--- Increment sell count } } } return(marketSellCount); //--- Return market sell count } //+------------------------------------------------------------------+ //| Close all buy positions | //+------------------------------------------------------------------+ void CloseBuy() { while (CheckMarketBuyOrders() > 0) { //--- Check buy orders exist for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check symbol and magic if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position obj_Trade.PositionClose(ticket); //--- Close position } } } } } //+------------------------------------------------------------------+ //| Close all sell positions | //+------------------------------------------------------------------+ void CloseSell() { while (CheckMarketSellOrders() > 0) { //--- Check sell orders exist for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check symbol and magic if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position obj_Trade.PositionClose(ticket); //--- Close position } } } } } //+------------------------------------------------------------------+ //| Calculate lot size | //+------------------------------------------------------------------+ double GetLots() { double calculatedLot; //--- Initialize calculated lot double minLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); //--- Get minimum lot double maxLot = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX); //--- Get maximum lot if (compoundPercent != 0) { //--- Check compounding calculatedLot = NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE) * compoundPercent / 100 / 10000, fnGetLotDigit()); //--- Calculate compounded lot if (calculatedLot < minLot) calculatedLot = minLot; //--- Enforce minimum lot if (calculatedLot > maxLot) calculatedLot = maxLot; //--- Enforce maximum lot } else { calculatedLot = initialLotSize; //--- Use fixed lot size } return(calculatedLot); //--- Return calculated lot } //+------------------------------------------------------------------+ //| Check account free margin | //+------------------------------------------------------------------+ double AccountFreeMarginCheck(string symbol, int orderType, double volume) { double marginRequired = 0.0; //--- Initialize margin required double price = orderType == ORDER_TYPE_BUY ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID); //--- Get price double calculatedMargin; //--- Declare calculated margin bool success = OrderCalcMargin(orderType == ORDER_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, symbol, volume, price, calculatedMargin); //--- Calculate margin if (success) marginRequired = calculatedMargin; //--- Set margin if successful return AccountInfoDouble(ACCOUNT_MARGIN_FREE) - marginRequired; //--- Return free margin } //+------------------------------------------------------------------+ //| Check if trading is allowed | //+------------------------------------------------------------------+ bool isTradingAllowed() { bool isAllowed = false; //--- Initialize allowed flag return(true); //--- Return true }
ここでは、取引のカウント、ポジションのクローズ、ロットサイズ計算、証拠金チェック、取引許可管理などをおこなうユーティリティ関数を実装し、堅牢な取引処理を実現します。まず、取引をカウントする関数を作成します。CountTradesはPositionsTotalをループし、PositionGetTicketで有効なチケットを確認し、SymbolとmagicNumberが一致するかどうかを確認したうえで、買いまたは売りポジションごとにpositionCountをインクリメントします。CountTradesBuyとCountTradesSellはそれぞれ買いと売りポジションをカウントし、POSITION_TYPE_BUYまたはPOSITION_TYPE_SELLでフィルタリングします。CheckBuyOrdersとCheckSellOrdersは、特定のmagicNumberを持つ買いまたは売りポジションが1つ以上存在するかどうかを検出し、最初の一致後にループを抜けます。CheckTotalBuyOrdersとCheckTotalSellOrdersは、magicNumberを持つすべての買いまたは売りポジションをカウントします。CheckMarketBuyOrdersとCheckMarketSellOrdersは、magicNumberを持つ買いまたは売りポジションをカウントします。
次に、NormalizeDoubleを実装して価格を_Digitsに正規化(NormalizeDouble使用)、fnGetLotDigitを実装してSYMBOL_VOLUME_STEPに基づくロットサイズの小数桁を返します(例:1なら0、0.1なら1)。さらに、CloseBuyとCloseSellを作成し、SymbolとmagicNumberを確認しながらポジションをループし、obj_Trade.PositionCloseを使用して、CheckMarketBuyOrdersまたはCheckMarketSellOrdersが0になるまですべての買いまたは売りポジションをクローズします。最後に、GetLotsを実装し、compoundPercentの場合は「AccountInfoDouble (ACCOUNT_BALANCE)*compoundPercent/100/10000」をfnGetLotDigitで正規化し、SYMBOL_VOLUME_MINおよびSYMBOL_VOLUME_MAXで制約、またはinitialLotSizeを使用してロットサイズを算出します。AccountFreeMarginCheckは、OrderCalcMarginで指定の注文タイプとボリュームに必要な証拠金を計算して利用可能証拠金を求めます。isTradingAllowedはプレースホルダとしてtrueを返します。可視化のために、チャート上にラインやラベルを描画する関数も必要となります。
//+------------------------------------------------------------------+ //| Draw support/resistance line | //+------------------------------------------------------------------+ void MakeLine(double price) { string name = "level"; //--- Set line name if (ObjectFind(0, name) != -1) { //--- Check if line exists ObjectMove(0, name, 0, iTime(Symbol(), PERIOD_CURRENT, 0), price); //--- Move line return; //--- Exit function } ObjectCreate(0, name, OBJ_HLINE, 0, 0, price); //--- Create horizontal line ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); //--- Set color ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); //--- Set style ObjectSetInteger(0, name, OBJPROP_WIDTH, lineWidth); //--- Set width ObjectSetInteger(0, name, OBJPROP_BACK, true); //--- Set to background } //+------------------------------------------------------------------+ //| Create dashboard label | //+------------------------------------------------------------------+ void LABEL(string labelName, string fontName, int fontSize, int xPosition, int yPosition, color textColor, int corner, string labelText) { if (ObjectFind(0, labelName) < 0) { //--- Check if label exists ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0); //--- Create label } ObjectSetString(0, labelName, OBJPROP_TEXT, labelText); //--- Set label text ObjectSetString(0, labelName, OBJPROP_FONT, fontName); //--- Set font ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, fontSize); //--- Set font size ObjectSetInteger(0, labelName, OBJPROP_COLOR, textColor); //--- Set text color ObjectSetInteger(0, labelName, OBJPROP_CORNER, corner); //--- Set corner ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, xPosition); //--- Set x position ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, yPosition); //--- Set y position }
プログラムの視覚要素を作成するために、MakeLine関数を開発します。この関数は指定されたpriceで水平線を描画し、サポートやレジスタンスレベルを示すlevelとして名前を設定します。ObjectFindで存在を確認し、見つかった場合はObjectMoveでiTimeから現在のバー時間に移動させ、存在しない場合はObjectCreateでOBJ_HLINEとして作成します。その後、ObjectSetIntegerでOBJPROP_COLORをlineColor、OBJPROP_STYLEをSTYLE_SOLID、OBJPROP_WIDTHをlineWidth、OBJPROP_BACKをtrueに設定して背景に配置します。
次にLABEL関数を実装します。この関数はダッシュボードのラベルを作成または更新し、labelNameが存在するか確認し、存在しなければObjectCreateでOBJ_LABELとして作成します。プロパティはObjectSetStringでOBJPROP_TEXTをlabelText、OBJPROP_FONTをfontNameに設定し、ObjectSetIntegerでOBJPROP_FONTSIZEをfontSize、OBJPROP_COLORをtextColor、OBJPROP_CORNERをcorner、OBJPROP_XDISTANCEをxPosition、OBJPROP_YDISTANCEをyPositionに設定します。これにより、使用するインジケーターのユーティリティ関数を定義できます。
//+------------------------------------------------------------------+ //| Calculate ATR indicator | //+------------------------------------------------------------------+ double MyiATR(string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift) { int handle = iATR(symbol, timeframe, period); //--- Create ATR handle if (handle == INVALID_HANDLE) return 0; //--- Check invalid handle double buffer[1]; //--- Declare buffer if (CopyBuffer(handle, 0, shift, 1, buffer) != 1) buffer[0] = 0; //--- Copy ATR value IndicatorRelease(handle); //--- Release handle return buffer[0]; //--- Return ATR value } //+------------------------------------------------------------------+ //| Check bullish engulfing pattern | //+------------------------------------------------------------------+ bool BullishEngulfingExists() { if (iOpen(Symbol(), PERIOD_CURRENT, 1) <= iClose(Symbol(), PERIOD_CURRENT, 2) && iClose(Symbol(), PERIOD_CURRENT, 1) >= iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) >= 10 * _Point && iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1) >= 10 * _Point) { //--- Check bullish engulfing conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check bullish harami pattern | //+------------------------------------------------------------------+ bool BullishHaramiExists() { if (iClose(Symbol(), PERIOD_CURRENT, 2) < iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 1) < iClose(Symbol(), PERIOD_CURRENT, 1) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) > MyiATR(Symbol(), PERIOD_CURRENT, 14, 2) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) > 4 * (iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1))) { //--- Check bullish harami conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check doji at bottom pattern | //+------------------------------------------------------------------+ bool DojiAtBottomExists() { if (iOpen(Symbol(), PERIOD_CURRENT, 3) - iClose(Symbol(), PERIOD_CURRENT, 3) >= 8 * _Point && MathAbs(iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2)) <= 1 * _Point && iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1) >= 8 * _Point) { //--- Check doji at bottom conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check doji at top pattern | //+------------------------------------------------------------------+ bool DojiAtTopExists() { if (iClose(Symbol(), PERIOD_CURRENT, 3) - iOpen(Symbol(), PERIOD_CURRENT, 3) >= 8 * _Point && MathAbs(iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2)) <= 1 * _Point && iOpen(Symbol(), PERIOD_CURRENT, 1) - iClose(Symbol(), PERIOD_CURRENT, 1) >= 8 * _Point) { //--- Check doji at top conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check bearish harami pattern | //+------------------------------------------------------------------+ bool BearishHaramiExists() { if (iClose(Symbol(), PERIOD_CURRENT, 2) > iClose(Symbol(), PERIOD_CURRENT, 1) && iOpen(Symbol(), PERIOD_CURRENT, 2) < iOpen(Symbol(), PERIOD_CURRENT, 1) && iClose(Symbol(), PERIOD_CURRENT, 2) > iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 1) > iClose(Symbol(), PERIOD_CURRENT, 1) && iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2) > MyiATR(Symbol(), PERIOD_CURRENT, 14, 2) && iClose(Symbol(), PERIOD_CURRENT, 2) - iOpen(Symbol(), PERIOD_CURRENT, 2) > 4 * (iOpen(Symbol(), PERIOD_CURRENT, 1) - iClose(Symbol(), PERIOD_CURRENT, 1))) { //--- Check bearish harami conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check long up candle pattern | //+------------------------------------------------------------------+ bool LongUpCandleExists() { if (iOpen(Symbol(), PERIOD_CURRENT, 2) < iClose(Symbol(), PERIOD_CURRENT, 2) && iHigh(Symbol(), PERIOD_CURRENT, 2) - iLow(Symbol(), PERIOD_CURRENT, 2) >= 40 * _Point && iHigh(Symbol(), PERIOD_CURRENT, 2) - iLow(Symbol(), PERIOD_CURRENT, 2) > 2.5 * MyiATR(Symbol(), PERIOD_CURRENT, 14, 2) && iClose(Symbol(), PERIOD_CURRENT, 1) < iOpen(Symbol(), PERIOD_CURRENT, 1) && iOpen(Symbol(), PERIOD_CURRENT, 1) - iClose(Symbol(), PERIOD_CURRENT, 1) > 10 * _Point) { //--- Check long up candle conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check long down candle pattern | //+------------------------------------------------------------------+ bool LongDownCandleExists() { if (iOpen(Symbol(), PERIOD_CURRENT, 1) > iClose(Symbol(), PERIOD_CURRENT, 1) && iHigh(Symbol(), PERIOD_CURRENT, 1) - iLow(Symbol(), PERIOD_CURRENT, 1) >= 40 * _Point && iHigh(Symbol(), PERIOD_CURRENT, 1) - iLow(Symbol(), PERIOD_CURRENT, 1) > 2.5 * MyiATR(Symbol(), PERIOD_CURRENT, 14, 1)) { //--- Check long down candle conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check bearish engulfing pattern | //+------------------------------------------------------------------+ bool BearishEngulfingExists() { if (iOpen(Symbol(), PERIOD_CURRENT, 1) >= iClose(Symbol(), PERIOD_CURRENT, 2) && iClose(Symbol(), PERIOD_CURRENT, 1) <= iOpen(Symbol(), PERIOD_CURRENT, 2) && iOpen(Symbol(), PERIOD_CURRENT, 2) - iClose(Symbol(), PERIOD_CURRENT, 2) >= 10 * _Point && iClose(Symbol(), PERIOD_CURRENT, 1) - iOpen(Symbol(), PERIOD_CURRENT, 1) >= 10 * _Point) { //--- Check bearish engulfing conditions return(true); //--- Return true } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Calculate average range over 4 days | //+------------------------------------------------------------------+ double AveRange4() { double rangeSum = 0; //--- Initialize range sum int count = 0; //--- Initialize count int index = 1; //--- Initialize index while (count < 4) { //--- Loop until 4 days MqlDateTime dateTime; //--- Declare datetime structure TimeToStruct(iTime(Symbol(), PERIOD_CURRENT, index), dateTime); //--- Convert time if (dateTime.day_of_week != 0) { //--- Check non-Sunday rangeSum += iHigh(Symbol(), PERIOD_CURRENT, index) - iLow(Symbol(), PERIOD_CURRENT, index); //--- Add range count++; //--- Increment count } index++; //--- Increment index } return(rangeSum / 4.0); //--- Return average range } //+------------------------------------------------------------------+ //| Check buy pinbar | //+------------------------------------------------------------------+ bool IsBuyPinbar() { double currentOpen, currentClose, currentHigh, currentLow; //--- Declare current candle variables double previousHigh, previousLow, previousClose, previousOpen; //--- Declare previous candle variables double currentRange, previousRange, currentHigherPart, currentHigherPart1; //--- Declare range variables currentOpen = iOpen(Symbol(), PERIOD_CURRENT, 1); //--- Get current open currentClose = iClose(Symbol(), PERIOD_CURRENT, 1); //--- Get current close currentHigh = iHigh(Symbol(), PERIOD_CURRENT, 0); //--- Get current high currentLow = iLow(Symbol(), PERIOD_CURRENT, 1); //--- Get current low previousOpen = iOpen(Symbol(), PERIOD_CURRENT, 2); //--- Get previous open previousClose = iClose(Symbol(), PERIOD_CURRENT, 2); //--- Get previous close previousHigh = iHigh(Symbol(), PERIOD_CURRENT, 2); //--- Get previous high previousLow = iLow(Symbol(), PERIOD_CURRENT, 2); //--- Get previous low currentRange = currentHigh - currentLow; //--- Calculate current range previousRange = previousHigh - previousLow; //--- Calculate previous range currentHigherPart = currentHigh - currentRange * 0.4; //--- Calculate higher part currentHigherPart1 = currentHigh - currentRange * 0.4; //--- Calculate higher part double averageDailyRange = AveRange4(); //--- Get average daily range if ((currentClose > currentHigherPart1 && currentOpen > currentHigherPart) && //--- Check close/open in higher third (currentRange > averageDailyRange * 0.5) && //--- Check pinbar size (currentLow + currentRange * 0.25 < previousLow)) { //--- Check nose length double lowArray[3]; //--- Declare low array CopyLow(Symbol(), PERIOD_CURRENT, 3, 3, lowArray); //--- Copy low prices int minIndex = ArrayMinimum(lowArray); //--- Find minimum low index if (lowArray[minIndex] > currentLow) return(true); //--- Confirm buy pinbar } return(false); //--- Return false } //+------------------------------------------------------------------+ //| Check sell pinbar | //+------------------------------------------------------------------+ bool IsSellPinbar() { double currentOpen, currentClose, currentHigh, currentLow; //--- Declare current candle variables double previousHigh, previousLow, previousClose, previousOpen; //--- Declare previous candle variables double currentRange, previousRange, currentLowerPart, currentLowerPart1; //--- Declare range variables currentOpen = iOpen(Symbol(), PERIOD_CURRENT, 1); //--- Get current open currentClose = iClose(Symbol(), PERIOD_CURRENT, 1); //--- Get current close currentHigh = iHigh(Symbol(), PERIOD_CURRENT, 1); //--- Get current high currentLow = iLow(Symbol(), PERIOD_CURRENT, 1); //--- Get current low previousOpen = iOpen(Symbol(), PERIOD_CURRENT, 2); //--- Get previous open previousClose = iClose(Symbol(), PERIOD_CURRENT, 2); //--- Get previous close previousHigh = iHigh(Symbol(), PERIOD_CURRENT, 2); //--- Get previous high previousLow = iLow(Symbol(), PERIOD_CURRENT, 2); //--- Get previous low currentRange = currentHigh - currentLow; //--- Calculate current range previousRange = previousHigh - previousLow; //--- Calculate previous range currentLowerPart = currentLow + currentRange * 0.4; //--- Calculate lower part currentLowerPart1 = currentLow + currentRange * 0.4; //--- Calculate lower part double averageDailyRange = AveRange4(); //--- Get average daily range if ((currentClose < currentLowerPart1 && currentOpen < currentLowerPart) && //--- Check close/open in lower third (currentRange > averageDailyRange * 0.5) && //--- Check pinbar size (currentHigh - currentRange * 0.25 > previousHigh)) { //--- Check nose length double highArray[3]; //--- Declare high array CopyHigh(Symbol(), PERIOD_CURRENT, 3, 3, highArray); //--- Copy high prices int maxIndex = ArrayMaximum(highArray); //--- Find maximum high index if (highArray[maxIndex] < currentHigh) return(true); //--- Confirm sell pinbar } return(false); //--- Return false }
ここでは、ローソク足パターンを検出し、システム用にATRを計算する関数を実装します。まず、MyiATR関数を作成します。この関数は、指定された銘柄、時間足、期間に対してiATR関数でハンドルを作成し、ハンドルが無効な場合は0を返します。CopyBufferでATR値をバッファにコピーし、IndicatorReleaseでハンドルを解放してATR値を返します。
次に、ローソク足パターン検出関数を実装します。BullishEngulfingExistsは、現在のローソク足が前の陰線を実体サイズで包み込むかどうかを確認します。BullishHaramiExistsは、MyiATRを使用して大きな陰線内に小さな陽線があるかを判定します。DojiAtBottomExistsは、陰線と陽線の間にある十字線をモーニングスターとして検出します。DojiAtTopExistsは、陽線と陰線の間の十字線を宵の明星として判定します。BearishHaramiExistsは、大きな陽線内に小さな陰線があるかどうかを確認します。LongUpCandleExistsは、ATRを使って強い陽線の後に陰線が続くかどうかを確認します。LongDownCandleExistsは強い陰線を検出します。BearishEngulfingExistsは、陰線が陽線を包み込むかどうかを確認します。
最後に、IsBuyPinbarとIsSellPinbarを実装します。これらは、現在のローソク足の終値と始値がレンジの上部または下部三分の一に位置するか、レンジがAveRange4(日曜日を除く直近4日間の高値-安値の平均)で計算した平均日幅の半分を超えているか、ピンバーのヒゲが前のローソク足の高値または安値を超えているかどうかを確認することでピンバーを判定します。最近の安値や高値はCopyLowやCopyHigh、ArrayMinimumやArrayMaximumを使用して比較します。これにより、表示用のシグナルタイプ取得関数やポジション管理用の加重価格取得関数も定義できるようになります。
//+------------------------------------------------------------------+ //| Analyze candlestick patterns | //+------------------------------------------------------------------+ string CandleStick_Analyzer() { string candlePattern, comment1 = "", comment2 = "", comment3 = ""; //--- Initialize pattern strings string comment4 = "", comment5 = "", comment6 = "", comment7 = ""; //--- Initialize pattern strings string comment8 = "", comment9 = ""; //--- Initialize pattern strings if (BullishEngulfingExists()) comment1 = " Bullish Engulfing "; //--- Check bullish engulfing if (BullishHaramiExists()) comment2 = " Bullish Harami "; //--- Check bullish harami if (LongUpCandleExists()) comment3 = " Bullish LongUp "; //--- Check long up candle if (DojiAtBottomExists()) comment4 = " MorningStar Doji "; //--- Check morning star doji if (DojiAtTopExists()) comment5 = " EveningStar Doji "; //--- Check evening star doji if (BearishHaramiExists()) comment6 = " Bearish Harami "; //--- Check bearish harami if (BearishEngulfingExists()) comment7 = " Bearish Engulfing "; //--- Check bearish engulfing if (LongDownCandleExists()) comment8 = " Bearish LongDown "; //--- Check long down candle candlePattern = comment1 + comment2 + comment3 + comment4 + comment5 + comment6 + comment7 + comment8 + comment9; //--- Combine patterns return(candlePattern); //--- Return combined pattern } //+------------------------------------------------------------------+ //| Calculate average price for order type | //+------------------------------------------------------------------+ double rata_price(int orderType) { double totalVolume = 0; //--- Initialize total volume double weightedOpenSum = 0; //--- Initialize weighted open sum double averagePrice = 0; //--- Initialize average price for (int positionIndex = 0; positionIndex < PositionsTotal(); positionIndex++) { //--- Iterate through positions ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber && (PositionGetInteger(POSITION_TYPE) == orderType)) { //--- Check position match totalVolume += PositionGetDouble(POSITION_VOLUME); //--- Add volume weightedOpenSum += (PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN)); //--- Add weighted open } } if (totalVolume != 0) { //--- Check non-zero volume averagePrice = weightedOpenSum / totalVolume; //--- Calculate average price } return(averagePrice); //--- Return average price }
ポジション管理を強化するため、CandleStick_Analyzer関数を作成します。本関数では、comment1からcomment9までの文字列変数を空で初期化し、既に定義したBullishEngulfingExistsなどのローソク足パターン検出関数を用いてパターンの有無を確認します。検出された場合は、該当する変数に「Bullish Engulfing」などの説明文字列を代入し、最終的にこれらを結合してcandlePatternとして返します。これにより、ダッシュボードに表示する検出パターンの文字列をまとめることができます。
次に、rata_price関数を実装します。本関数は指定されたorderType(買いまたは売り)に対して加重平均価格を計算します。まずtotalVolumeとweightedOpenSumを0で初期化し、PositionsTotalですべてのポジションを順番に処理します。対象の銘柄、magicNumber、orderTypeに一致するポジションについて、PositionGetTicketでチケット番号を取得し、PositionGetStringで銘柄を確認、PositionGetIntegerでPOSITION_VOLUMEを取得し、PositionGetDoubleでPOSITION_PRICE_OPENを取得します。weightedOpenSumにはPOSITION_VOLUMEとPOSITION_PRICE_OPENの積を加算し、totalVolumeにはPOSITION_VOLUMEを加算します。totalVolumeが0でなければ、averagePriceを「weightedOpenSum / totalVolume」で算出して返します。この関数により、取引シグナルの重要なパターン分析や、ナンピンや利確の調整のための正確な平均取得価格を計算することができます。ポジションの情報は、まず各ポジションの指標を取得してから処理する必要があります。そのためのロジックを定義しましょう。
//+------------------------------------------------------------------+ //| Calculate position metrics | //+------------------------------------------------------------------+ void calculatePositionMetrics() { buyCount = 0; //--- Reset buy count currentBuyLot = 0; //--- Reset current buy lot totalBuyLots = 0; //--- Reset total buy lots sellCount = 0; //--- Reset sell count currentSellLot = 0; //--- Reset current sell lot totalSellLots = 0; //--- Reset total sell lots totalSum = 0; //--- Reset total sum totalSwap = 0; //--- Reset total swap buyProfit = 0; //--- Reset buy profit sellProfit = 0; //--- Reset sell profit buyWeightedSum = 0; //--- Reset buy weighted sum sellWeightedSum = 0; //--- Reset sell weighted sum buyBreakEvenPrice = 0; //--- Reset buy breakeven price sellBreakEvenPrice = 0; //--- Reset sell breakeven price minBuyLot = 9999; //--- Initialize min buy lot minSellLot = 9999; //--- Initialize min sell lot maxSellPrice = 0; //--- Initialize max sell price minBuyPrice = 999999999; //--- Initialize min buy price for (int i = 0; i < PositionsTotal(); i++) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; //--- Skip non-matching symbols if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position buyCount++; //--- Increment buy count totalOperations++; //--- Increment total operations currentBuyLot = PositionGetDouble(POSITION_VOLUME); //--- Set current buy lot buyProfit += PositionGetDouble(POSITION_PROFIT); //--- Add buy profit totalBuyLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total buy lots minBuyLot = MathMin(minBuyLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min buy lot buyWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price minBuyPrice = MathMin(minBuyPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update min buy price } if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position sellCount++; //--- Increment sell count totalOperations++; //--- Increment total operations currentSellLot = PositionGetDouble(POSITION_VOLUME); //--- Set current sell lot sellProfit += PositionGetDouble(POSITION_PROFIT); //--- Add sell profit totalSellLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total sell lots minSellLot = MathMin(minSellLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min sell lot sellWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price maxSellPrice = MathMax(maxSellPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update max sell price } } if (totalBuyLots > 0) { //--- Check buy lots buyBreakEvenPrice = buyWeightedSum / totalBuyLots; //--- Calculate buy breakeven } if (totalSellLots > 0) { //--- Check sell lots sellBreakEvenPrice = sellWeightedSum / totalSellLots; //--- Calculate sell breakeven } }
複数ポジションを効果的に管理するための重要な指標を計算するために、calculatePositionMetrics関数を実装します。まず、正確な追跡のために主要な変数を0またはそれぞれの初期値にリセットします。次に、PositionsTotalですべてのポジションを順番に処理し、PositionGetTicketで各ポジションのチケット番号を取得します。無効なチケットや対象銘柄と一致しない場合はPositionGetStringを使用してスキップします。買いポジション(POSITION_TYPE_BUY)の場合は、buyCountおよびtotalOperationsをインクリメントし、currentBuyLotを設定し、POSITION_PROFITをbuyProfitに加算、POSITION_VOLUMEをtotalBuyLotsに加算します。minBuyLotはMathMinで更新し、加重平均の計算用にbuyWeightedSumにPOSITION_VOLUMEとPOSITION_PRICE_OPENの積を加算し、minBuyPriceを更新します。売りポジション(POSITION_TYPE_SELL)の場合も同様に、売り指標を更新します。最後に、totalBuyLotsが正の値であればbuyBreakEvenPriceを「buyWeightedSum / totalBuyLots」で算出し、totalSellLotsが正の値であればsellBreakEvenPriceを「sellWeightedSum / totalSellLots」で算出します。これにより、ブレークイーブン管理のための加重平均価格を算出し、ナンピンやリスク管理のためのポジション指標を正確に追跡することができます。これらの関数が揃ったことで、ポジション建てのロジックを開始する準備が整いました。実装はOnTickイベントハンドラ内でおこないます。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { static datetime previousBarTime = 0; //--- Store previous bar time if (previousBarTime != iTime(Symbol(), PERIOD_CURRENT, 0)) { //--- Check new bar previousBarTime = iTime(Symbol(), PERIOD_CURRENT, 0); //--- Update previous bar time ChartRedraw(0); //--- Redraw chart } else { return; //--- Exit if not new bar } if (iVolume(Symbol(), PERIOD_H4, 0) > iVolume(Symbol(), PERIOD_H4, 1)) return; //--- Exit if volume increased double supportResistanceLevel = NormalizeDouble(iClose(Symbol(), PERIOD_H4, 1), _Digits); //--- Get support/resistance level ObjectDelete(0, "level"); //--- Delete existing level line MakeLine(supportResistanceLevel); //--- Draw support/resistance line if (SymbolInfoInteger(Symbol(), SYMBOL_SPREAD) > 150) return; //--- Exit if spread too high int totalBuyPositions = 0; //--- Initialize buy positions count int totalSellPositions = 0; //--- Initialize sell positions count for (int i = 0; i < PositionsTotal(); i++) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) != Symbol() || PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; //--- Skip non-matching positions if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position totalBuyPositions++; //--- Increment buy count } if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position totalSellPositions++; //--- Increment sell count } } }
OnTickイベントハンドラ内では、ピンバーを利用したナンピンシステムの初期ロジックを実装し、各新しいバーごとに取引判断と視覚的な更新を管理します。まず、previousBarTime(staticで初期値0)と、iTimeで取得した現在の銘柄、時間足、shift 0のバー時間を比較し、新しいバーが形成されているかどうかを確認します。新しいバーであればpreviousBarTimeを更新し、ChartRedrawを呼び出します。新しいバーでなければ処理を終了します。
次に、現在のH4バーの出来高をiVolumeで取得し、前のバーの出来高を上回る場合は高ボラティリティ期間として処理を終了します。続いて、サポート/レジスタンスレベルを計算します。前のH4バーの終値をiCloseで取得し、NormalizeDoubleで正規化します。既存のlevelラインがあればObjectDeleteで削除し、MakeLine関数で新たに水平線を描画します。最後に、SymbolInfoIntegerでスプレッドを確認し、150ポイントを超える場合は処理を終了します。開いているポジションの数はPositionsTotalで反復処理し、PositionGetTicketでチケットを取得、無効または対象銘柄・magicNumberと一致しないポジションはスキップします。PositionGetIntegerで判定した買いポジションはtotalBuyPositionsを、売りポジションはtotalSellPositionsをインクリメントします。この初期設定により、EAは有利な条件下でのみ新しいバーに対して取引を処理し、視覚的な参照情報を常に更新できます。コンパイルすると、次の結果が得られます。

画像からわかるように、サポートおよびレジスタンスレベルをチャート上に動的に表示しています。次のステップとして、ポジションも動的に反映させる処理を追加する必要があります。
if (CheckMarketBuyOrders() < 70 && CheckMarketSellOrders() < 70) { //--- Check order limits if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == DISABLED) { //--- Check buy condition if (IsBuyPinbar() && totalBuyPositions < maxOrders && (isTradingAllowed() || totalBuyPositions > 0)) { //--- Check buy pinbar and limits double buyStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossPips * normalizedPoint, _Digits); //--- Calculate buy stop loss double buyTakeProfit = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + takeProfitPips * normalizedPoint, _Digits); //--- Calculate buy take profit if (AccountFreeMarginCheck(Symbol(), ORDER_TYPE_BUY, GetLots()) > 0) { //--- Check margin obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_ASK), buyStopLoss, buyTakeProfit, orderComment); //--- Open buy position if (useAutoTakeProfit) { //--- Check auto take profit ModifyTP(ORDER_TYPE_BUY, rata_price(ORDER_TYPE_BUY) + takeProfitPips * normalizedPoint); //--- Modify take profit } CloseSell(); //--- Close sell positions } } } if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == DISABLED) { //--- Check sell condition if (IsSellPinbar() && totalSellPositions < maxOrders && (isTradingAllowed() || totalSellPositions > 0)) { //--- Check sell pinbar and limits double sellStopLoss = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) + stopLossPips * normalizedPoint, _Digits); //--- Calculate sell stop loss double sellTakeProfit = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - takeProfitPips * normalizedPoint, _Digits); //--- Calculate sell take profit if (AccountFreeMarginCheck(Symbol(), ORDER_TYPE_SELL, GetLots()) > 0) { //--- Check margin obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_BID), sellStopLoss, sellTakeProfit, orderComment); //--- Open sell position if (useAutoTakeProfit) { //--- Check auto take profit ModifyTP(ORDER_TYPE_SELL, rata_price(ORDER_TYPE_SELL) - takeProfitPips * normalizedPoint); //--- Modify take profit } CloseBuy(); //--- Close buy positions } } } } if (CountTrades() == 0) { //--- Check no trades if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == ENABLED) { //--- Check buy signal mode if (IsBuyPinbar() && CountTrades() < maxOrders) { //--- Check buy pinbar and limit obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_ASK), SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_ASK) + (takeProfitPips * normalizedPoint), orderComment); //--- Open buy position } } } if (CountTrades() == 0) { //--- Check no trades if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0) && useSignalMode == ENABLED) { //--- Check sell signal mode if (IsSellPinbar() && CountTrades() < maxOrders) { //--- Check sell pinbar and limit obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, GetLots(), SymbolInfoDouble(_Symbol, SYMBOL_BID), SymbolInfoDouble(_Symbol, SYMBOL_BID) + stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_BID) - (takeProfitPips * normalizedPoint), orderComment); //--- Open sell position } } }
OnTick関数の実装を続け、ピンバーシグナルと市場状況に基づいて新規ポジションを建てるロジックを追加します。まず、CheckMarketBuyOrdersおよびCheckMarketSellOrdersを使用して、買いおよび売りの保有ポジション数が70未満であることを確認し、EAが実務上の制限を超えないようにします。次に、useSignalModeがDISABLEDの場合、買い条件を評価します。supportResistanceLevelがiOpenで取得した現在の始値を上回り、IsBuyPinbarで買いピンバーが検出され、totalBuyPositionsがmaxOrders未満で、isTradingAllowedがtrueまたは既存の買いポジションが存在する場合、SymbolInfoDoubleを使用してstopLossPipsおよびtakeProfitPipsをnormalizedPointで調整し、buyStopLossおよびbuyTakeProfitを計算します。次に、AccountFreeMarginCheckで証拠金を確認し、obj_Trade.PositionOpenを用いて買いポジションを建てます。
useAutoTakeProfitがtrueの場合はModifyTPで利確を修正し、CloseSellで売りポジションをクローズします。売り条件の場合も同様で、supportResistanceLevelが始値を下回り、IsSellPinbarで売りピンバーが検出され、totalSellPositionsがmaxOrders未満で、isTradingAllowedがtrueまたは既存の売りポジションが存在する場合、stopLossおよびtakeProfitを計算し、証拠金を確認した上でobj_Trade.PositionOpenで売りポジションを建てます。useAutoTakeProfitがtrueの場合はModifyTPで利確を修正し、CloseBuyで買いポジションをクローズします。さらに、既存取引が存在せず(CountTradesが0)かつuseSignalModeがENABLEDの場合は、買いピンバーが検出されCountTradesがmaxOrders未満であれば、obj_Trade.PositionOpenで買いポジションを建て、売りピンバーが検出されCountTradesがmaxOrders未満であれば売りポジションを建てます。これにより、EAはピンバーシグナルに基づき、主要レベルで適切なリスク管理をおこないながらポジションを建てることができます。コンパイルすると、次の結果が得られます。

これでシグナルを確認してポジションを建てられるようになったので、次はシグナルの管理をおこなう必要があります。そのためにいくつかの関数を定義します。
//+------------------------------------------------------------------+ //| Update stop loss and take profit | //+------------------------------------------------------------------+ void updateStopLossTakeProfit() { for (int i = PositionsTotal() - 1; i >= 0; i--) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; //--- Skip non-matching symbols if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position double buyTakeProfitLevel = (buyBreakEvenPrice + takeProfitPips * _Point) * (takeProfitPips > 0); //--- Calculate buy take profit double buyStopLossLevel = PositionGetDouble(POSITION_SL); //--- Get current stop loss if (slBreakevenMinus > 0) { //--- Check breakeven adjustment buyStopLossLevel = (buyBreakEvenPrice - slBreakevenMinus * _Point); //--- Set breakeven stop loss } if (buyCount == 1) { //--- Check single buy position buyTakeProfitLevel = NormalizePrice(PositionGetDouble(POSITION_PRICE_OPEN) + takeProfitPips * _Point) * (takeProfitPips > 0); //--- Set take profit if (laterUseSL > 0) { //--- Check unused stop loss buyStopLossLevel = (PositionGetDouble(POSITION_PRICE_OPEN) - laterUseSL * _Point); //--- Set stop loss } } buyTakeProfitLevel = NormalizePrice(buyTakeProfitLevel); //--- Normalize take profit buyStopLossLevel = NormalizePrice(buyStopLossLevel); //--- Normalize stop loss if (SymbolInfoDouble(_Symbol, SYMBOL_BID) >= buyTakeProfitLevel && buyTakeProfitLevel > 0) { //--- Check take profit hit obj_Trade.PositionClose(ticket); //--- Close position } if (SymbolInfoDouble(_Symbol, SYMBOL_BID) <= buyStopLossLevel) { //--- Check stop loss hit obj_Trade.PositionClose(ticket); //--- Close position } if (NormalizePrice(PositionGetDouble(POSITION_TP)) != buyTakeProfitLevel || NormalizePrice(PositionGetDouble(POSITION_SL)) != buyStopLossLevel) { //--- Check modification needed obj_Trade.PositionModify(ticket, buyStopLossLevel, buyTakeProfitLevel); //--- Modify position } } if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position double sellTakeProfitLevel = (sellBreakEvenPrice - takeProfitPips * _Point) * (takeProfitPips > 0); //--- Calculate sell take profit double sellStopLossLevel = PositionGetDouble(POSITION_SL); //--- Get current stop loss if (slBreakevenMinus > 0) { //--- Check breakeven adjustment sellStopLossLevel = (sellBreakEvenPrice + slBreakevenMinus * _Point); //--- Set breakeven stop loss } if (sellCount == 1) { //--- Check single sell position sellTakeProfitLevel = (PositionGetDouble(POSITION_PRICE_OPEN) - takeProfitPips * _Point) * (takeProfitPips > 0); //--- Set take profit if (laterUseSL > 0) { //--- Check unused stop loss sellStopLossLevel = (PositionGetDouble(POSITION_PRICE_OPEN) + laterUseSL * _Point); //--- Set stop loss } } sellTakeProfitLevel = NormalizePrice(sellTakeProfitLevel); //--- Normalize take profit sellStopLossLevel = NormalizePrice(sellStopLossLevel); //--- Normalize stop loss if (SymbolInfoDouble(_Symbol, SYMBOL_ASK) <= sellTakeProfitLevel) { //--- Check take profit hit obj_Trade.PositionClose(ticket); //--- Close position } if (SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= sellStopLossLevel && sellStopLossLevel > 0) { //--- Check stop loss hit obj_Trade.PositionClose(ticket); //--- Close position } if (NormalizePrice(PositionGetDouble(POSITION_TP)) != sellTakeProfitLevel || NormalizePrice(PositionGetDouble(POSITION_SL)) != sellStopLossLevel) { //--- Check modification needed obj_Trade.PositionModify(ticket, sellStopLossLevel, sellTakeProfitLevel); //--- Modify position } } } } //+------------------------------------------------------------------+ //| Add averaging order | //+------------------------------------------------------------------+ void addAveragingOrder() { int positionIndex = 0; //--- Initialize position index double lastOpenPrice = 0; //--- Initialize last open price double lastLotSize = 0; //--- Initialize last lot size bool isLastBuy = false; //--- Initialize buy flag int totalBuyPositions = 0; //--- Initialize buy positions count int totalSellPositions = 0; //--- Initialize sell positions count long currentSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); //--- Get current spread double supportResistanceLevel = iClose(Symbol(), PERIOD_H4, 1); //--- Get support/resistance level for (positionIndex = 0; positionIndex < PositionsTotal(); positionIndex++) { //--- Iterate through positions ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check buy position if (lastOpenPrice == 0) { //--- Check initial price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price } if (lastOpenPrice > PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check lower price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price } if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size } isLastBuy = true; //--- Set buy flag totalBuyPositions++; //--- Increment buy count } if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check sell position if (lastOpenPrice == 0) { //--- Check initial price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price } if (lastOpenPrice < PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check higher price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price } if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size } isLastBuy = false; //--- Clear buy flag totalSellPositions++; //--- Increment sell count } } if (isLastBuy) { //--- Check buy position if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check buy condition if (IsBuyPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_BID) <= lastOpenPrice - (orderDistancePips * _Point)) { //--- Check buy pinbar and distance obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_ASK), SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_ASK) + (takeProfitPips * normalizedPoint), orderComment); //--- Open buy position isLastBuy = false; //--- Clear buy flag return; //--- Exit function } } } else if (!isLastBuy) { //--- Check sell position if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check sell condition if (IsSellPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= lastOpenPrice + (orderDistancePips * _Point)) { //--- Check sell pinbar and distance obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_BID), SymbolInfoDouble(_Symbol, SYMBOL_BID) + stopLossPips * normalizedPoint, SymbolInfoDouble(_Symbol, SYMBOL_BID) - (takeProfitPips * normalizedPoint), orderComment); //--- Open sell position return; //--- Exit function } } } } //+------------------------------------------------------------------+ //| Add averaging order with auto take profit | //+------------------------------------------------------------------+ void addAveragingOrderWithAutoTP() { int positionIndex = 0; //--- Initialize position index double lastOpenPrice = 0; //--- Initialize last open price double lastLotSize = 0; //--- Initialize last lot size bool isLastBuy = false; //--- Initialize buy flag int totalBuyPositions = 0; //--- Initialize buy positions count int totalSellPositions = 0; //--- Initialize sell positions count long currentSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); //--- Get current spread double supportResistanceLevel = iClose(Symbol(), PERIOD_H4, 1); //--- Get support/resistance level for (positionIndex = 0; positionIndex < PositionsTotal(); positionIndex++) { //--- Iterate through positions ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check buy position if (lastOpenPrice == 0) { //--- Check initial price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price } if (lastOpenPrice > PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check lower price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price } if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size } isLastBuy = true; //--- Set buy flag totalBuyPositions++; //--- Increment buy count } if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check sell position if (lastOpenPrice == 0) { //--- Check initial price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set initial price } if (lastOpenPrice < PositionGetDouble(POSITION_PRICE_OPEN)) { //--- Check higher price lastOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Update last price } if (lastLotSize < PositionGetDouble(POSITION_VOLUME)) { //--- Check larger lot lastLotSize = PositionGetDouble(POSITION_VOLUME); //--- Update lot size } isLastBuy = false; //--- Clear buy flag totalSellPositions++; //--- Increment sell count } } if (isLastBuy) { //--- Check buy position if (supportResistanceLevel > iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check buy condition if (IsBuyPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_BID) <= lastOpenPrice - (orderDistancePips * _Point)) { //--- Check buy pinbar and distance obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_ASK), 0, 0, orderComment); //--- Open buy position calculatePositionMetrics(); //--- Calculate position metrics updateStopLossTakeProfit(); //--- Update stop loss and take profit isLastBuy = false; //--- Clear buy flag return; //--- Exit function } } } else if (!isLastBuy) { //--- Check sell position if (supportResistanceLevel < iOpen(Symbol(), PERIOD_CURRENT, 0)) { //--- Check sell condition if (IsSellPinbar() && SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= lastOpenPrice + (orderDistancePips * _Point)) { //--- Check sell pinbar and distance obj_Trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, NormalizeDouble((lastLotSize * lotMultiplier), fnGetLotDigit()), SymbolInfoDouble(_Symbol, SYMBOL_BID), 0, 0, orderComment); //--- Open sell position calculatePositionMetrics(); //--- Calculate position metrics updateStopLossTakeProfit(); //--- Update stop loss and take profit return; //--- Exit function } } } }
ここでは、SL、TP、およびナンピン注文を管理するために、updateStopLossTakeProfit関数、addAveragingOrder関数、およびaddAveragingOrderWithAutoTP関数を実装します。これにより、ポジションの動的な調整が可能となります。まず、updateStopLossTakeProfit関数を作成します。本関数ではすべてのポジションを順番に処理します。買いポジション(POSITION_TYPE_BUY)の場合、buyBreakEvenPriceに「takeProfitPips * _Point」を加算した値をbuyTakeProfitLevelとして計算します(takeProfitPipsが正の場合)。現在のSLはPositionGetDoubleで取得し、slBreakevenMinusが正の場合は「buyBreakEvenPrice - slBreakevenMinus * _Point」に調整します。また、単一ポジション(buyCount == 1)の場合は、POSITION_PRICE_OPENに対してTPおよびSLを設定します。両レベルはNormalizePriceで正規化し、Bid価格がTPまたはSLに到達した場合はobj_Trade.PositionCloseでポジションをクローズし、レベルが異なる場合はobj_Trade.PositionModifyで修正します。売りポジションについても、sellBreakEvenPriceおよびAsk価格を用いて同様のロジックを適用します。
次に、addAveragingOrder関数を実装します。本関数ではPositionsTotalを反復処理して最新ポジションを追跡し、lastOpenPriceを買いでは最安値、売りでは最高値に更新し、lastLotSizeを最大ロットに更新します。isLastBuyも設定します。買いの場合、supportResistanceLevelが現在の始値を上回り、IsBuyPinbarで買いピンバーが検出され、Bid価格がlastOpenPriceより「orderDistancePips * _Point」だけ下回っている場合、obj_Trade.PositionOpenでロットサイズを「lastLotSize * lotMultiplier」でfnGetLotDigitに基づき正規化し、計算済みのSLとTPで買いポジションを建て、isLastBuyをクリアします。売りの場合も同様に、Ask価格がlastOpenPriceより「orderDistancePips * _Point」だけ上回っている場合に売りポジションを建てます。
最後に、addAveragingOrderWithAutoTP関数を実装します。本関数はaddAveragingOrderと同じロジックを使用しますが、初期のSLやTPを0に設定してポジションを建てます。その後、calculatePositionMetricsを呼び出してbuyBreakEvenPriceなどの指標を更新し、updateStopLossTakeProfitを呼び出してブレークイーブンベースのレベルを設定します。これにより、ナンピンポジションの動的調整が可能となります。これらの関数は、ティックロジック内で呼び出すことで、実際の取引に反映されます。
if (useSignalMode == ENABLED && CountTradesBuy() >= 1 && CountTradesBuy() < maxOrders && useAutoTakeProfit == false) { //--- Check buy averaging addAveragingOrder(); //--- Add buy averaging order } if (useSignalMode == ENABLED && CountTradesSell() >= 1 && CountTradesSell() < maxOrders && useAutoTakeProfit == false) { //--- Check sell averaging addAveragingOrder(); //--- Add sell averaging order } if (useSignalMode == ENABLED && CountTradesBuy() >= 1 && CountTradesBuy() < maxOrders && useAutoTakeProfit == true) { //--- Check buy averaging with auto TP addAveragingOrderWithAutoTP(); //--- Add buy averaging order with auto TP } if (useSignalMode == ENABLED && CountTradesSell() >= 1 && CountTradesSell() < maxOrders && useAutoTakeProfit == true) { //--- Check sell averaging with auto TP addAveragingOrderWithAutoTP(); //--- Add sell averaging order with auto TP }
ティックロジックの実装を完了するため、特定条件下でのナンピンポジションの処理ロジックを追加し、EAがポジションを動的にスケールさせる能力を強化します。まず、useSignalModeがENABLEDの場合、CountTradesBuyで少なくとも1つの買いポジションが存在し、買いポジション数がmaxOrders未満であるかどうかどうかを確認します。useAutoTakeProfitがfalseの場合、addAveragingOrderを呼び出し、ピンバー検出および価格距離の条件に基づいて追加の買いポジションを、乗算されたロットサイズで建てます。
次に、同様のロジックを売りポジションに適用します。CountTradesSellを確認し、useAutoTakeProfitがfalseであれば、addAveragingOrderを呼び出して同様の条件下で売りポジションを追加します。続いて、useAutoTakeProfitがtrueの場合、買いポジションに対してaddAveragingOrderWithAutoTPを呼び出し、初期のSLおよびTPなしで買いポジションを建て、その後calculatePositionMetricsで指標を更新し、ブレークイーブンベースのレベルを調整します。最後に、売りポジションでも同様に、useAutoTakeProfitがtrueの場合はaddAveragingOrderWithAutoTPを呼び出し、動的なSLおよびTPの調整をおこないながら売りポジションを追加します。このロジックにより、EAはシグナルモードでナンピンポジションを効果的に管理し、市場の動きに応じて柔軟に対応することが可能となります。コンパイルすると、次の結果が得られます。

ナンピン機能を追加したので、次に残るのはリスク管理のためのトレーリングストップロジックの追加です。このロジックは正確なリスク管理のために毎ティック実行する必要があるため、バー制限ロジックの外で実装します。
double setPointValue = normalizedPoint; //--- Set point value for calculations if (useTrailingStop && trailingStartPips > 0 && breakevenPips < trailingStartPips) { //--- Check trailing stop conditions double averageBuyPrice = rata_price(ORDER_TYPE_BUY); //--- Calculate average buy price double trailingReference = 0; //--- Initialize trailing reference for (int iTrade = 0; iTrade < PositionsTotal(); iTrade++) { //--- Iterate through positions ulong ticket = PositionGetTicket(iTrade); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check buy position if (useAutoTakeProfit) { //--- Check auto take profit trailingReference = averageBuyPrice; //--- Use average buy price } else { //--- Use open price trailingReference = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set open price } if (SymbolInfoDouble(_Symbol, SYMBOL_BID) - trailingReference > trailingStartPips * setPointValue) { //--- Check trailing condition if (SymbolInfoDouble(_Symbol, SYMBOL_BID) - ((trailingStartPips - breakevenPips) * setPointValue) > PositionGetDouble(POSITION_SL)) { //--- Check stop loss adjustment obj_Trade.PositionModify(ticket, SymbolInfoDouble(_Symbol, SYMBOL_BID) - ((trailingStartPips - breakevenPips) * setPointValue), PositionGetDouble(POSITION_TP)); //--- Modify position } } } } double averageSellPrice = rata_price(ORDER_TYPE_SELL); //--- Calculate average sell price for (int iTrade2 = 0; iTrade2 < PositionsTotal(); iTrade2++) { //--- Iterate through positions ulong ticket2 = PositionGetTicket(iTrade2); //--- Get position ticket if (ticket2 == 0) continue; //--- Skip invalid tickets if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == magicNumber) { //--- Check sell position if (useAutoTakeProfit) { //--- Check auto take profit trailingReference = averageSellPrice; //--- Use average sell price } else { //--- Use open price trailingReference = PositionGetDouble(POSITION_PRICE_OPEN); //--- Set open price } if (trailingReference - SymbolInfoDouble(_Symbol, SYMBOL_ASK) > trailingStartPips * setPointValue) { //--- Check trailing condition if (SymbolInfoDouble(_Symbol, SYMBOL_ASK) + ((trailingStartPips - breakevenPips) * setPointValue) < PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL) == 0) { //--- Check stop loss adjustment obj_Trade.PositionModify(ticket2, SymbolInfoDouble(_Symbol, SYMBOL_ASK) + ((trailingStartPips - breakevenPips) * setPointValue), PositionGetDouble(POSITION_TP)); //--- Modify position } } } } }
トレーリングストップのロジックは、まずsetPointValueをnormalizedPointに設定して価格計算の一貫性を確保し、useTrailingStopがtrueであり、trailingStartPipsが正の値で、かつbreakevenPipsがtrailingStartPips未満であることを確認して、トレーリング条件が有効であることをチェックするところから実装します。次に、買いポジションを処理します。rata_priceを使用してORDER_TYPE_BUYのaverageBuyPriceを計算し、すべてのポジションを反復処理してSymbolおよびmagicNumberに一致する有効な買いポジションのチケットを取得します。trailingReferenceはuseAutoTakeProfitがtrueの場合はaverageBuyPrice、そうでなければPOSITION_PRICE_OPENに設定します。Bid価格がtrailingReferenceより「trailingStartPips * setPointValue」だけ上回り、新しいSLが現在のSLより高い場合は、obj_Trade.PositionModifyでSLを「SYMBOL_BID - (trailingStartPips - breakevenPips) * setPointValue」に修正します。
売りポジションについても同様のロジックを適用します。ORDER_TYPE_SELLのaverageSellPriceをrata_priceで計算し、すべてのポジションを反復処理してtrailingReferenceをaverageSellPriceまたはPPOSITION_PRICE_OPENに設定します。Ask価格がtrailingReferenceより「trailingStartPips * setPointValue」だけ下回り、新しいSLが現在のSLより低い、または未設定の場合は、obj_Trade.PositionModifyでSLを「SYMBOL_ASK + (trailingStartPips - breakevenPips) * setPointValue」に修正します。最後に、「PositionGetDouble(POSITION_TP)」を介して変更によって既存のTPが維持されることを確認し、親関数でChartRedrawを呼び出してチャートを更新します。コンパイルすると、次の結果が得られます。
トレーリングストップ適用前

トレーリングストップ適用後

ポジション管理ロジックが完成したので、次は口座の各種指標を可視化するダッシュボードを作成します。管理を容易にするため、これも専用の関数として実装します。
//+------------------------------------------------------------------+ //| Display dashboard information | //+------------------------------------------------------------------+ void Display_Info() { buyCount = 0; //--- Reset buy count currentBuyLot = 0; //--- Reset current buy lot totalBuyLots = 0; //--- Reset total buy lots sellCount = 0; //--- Reset sell count currentSellLot = 0; //--- Reset current sell lot totalSellLots = 0; //--- Reset total sell lots totalSum = 0; //--- Reset total sum totalSwap = 0; //--- Reset total swap buyProfit = 0; //--- Reset buy profit sellProfit = 0; //--- Reset sell profit buyWeightedSum = 0; //--- Reset buy weighted sum sellWeightedSum = 0; //--- Reset sell weighted sum buyBreakEvenPrice = 0; //--- Reset buy breakeven price sellBreakEvenPrice = 0; //--- Reset sell breakeven price minBuyLot = 9999; //--- Initialize min buy lot minSellLot = 9999; //--- Initialize min sell lot maxSellPrice = 0; //--- Initialize max sell price minBuyPrice = 999999999; //--- Initialize min buy price for (int i = 0; i < PositionsTotal(); i++) { //--- Iterate through positions ulong ticket = PositionGetTicket(i); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) != Symbol()) continue; //--- Skip non-matching symbols if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy position buyCount++; //--- Increment buy count totalOperations++; //--- Increment total operations currentBuyLot = PositionGetDouble(POSITION_VOLUME); //--- Set current buy lot buyProfit += PositionGetDouble(POSITION_PROFIT); //--- Add buy profit totalBuyLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total buy lots minBuyLot = MathMin(minBuyLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min buy lot buyWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price minBuyPrice = MathMin(minBuyPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update min buy price } if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell position sellCount++; //--- Increment sell count totalOperations++; //--- Increment total operations currentSellLot = PositionGetDouble(POSITION_VOLUME); //--- Set current sell lot sellProfit += PositionGetDouble(POSITION_PROFIT); //--- Add sell profit totalSellLots += PositionGetDouble(POSITION_VOLUME); //--- Add to total sell lots minSellLot = MathMin(minSellLot, PositionGetDouble(POSITION_VOLUME)); //--- Update min sell lot sellWeightedSum += PositionGetDouble(POSITION_VOLUME) * PositionGetDouble(POSITION_PRICE_OPEN); //--- Add weighted open price maxSellPrice = MathMax(maxSellPrice, PositionGetDouble(POSITION_PRICE_OPEN)); //--- Update max sell price } } if (totalBuyLots > 0) { //--- Check buy lots buyBreakEvenPrice = buyWeightedSum / totalBuyLots; //--- Calculate buy breakeven } if (totalSellLots > 0) { //--- Check sell lots sellBreakEvenPrice = sellWeightedSum / totalSellLots; //--- Calculate sell breakeven } int minutesRemaining, secondsRemaining; //--- Declare time variables minutesRemaining = (int)(PeriodSeconds() - (TimeCurrent() - iTime(Symbol(), PERIOD_CURRENT, 0))); //--- Calculate remaining time secondsRemaining = minutesRemaining % 60; //--- Calculate seconds minutesRemaining = minutesRemaining / 60; //--- Calculate minutes long currentSpread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD); //--- Get current spread string spreadPrefix = "", minutesPrefix = "", secondsPrefix = ""; //--- Initialize prefixes if (currentSpread < 10) spreadPrefix = ".."; //--- Set spread prefix for single digit else if (currentSpread < 100) spreadPrefix = "."; //--- Set spread prefix for double digit if (minutesRemaining < 10) minutesPrefix = "0"; //--- Set minutes prefix if (secondsRemaining < 10) secondsPrefix = "0"; //--- Set seconds prefix int blinkingColorIndex; //--- Declare blinking color index color equityColor = clrGreen; //--- Initialize equity color if (AccountInfoDouble(ACCOUNT_EQUITY) - AccountInfoDouble(ACCOUNT_BALANCE) < 0.0) { //--- Check negative equity equityColor = clrRed; //--- Set equity color to red } color profitColor = (buyProfit + sellProfit >= 0) ? clrGreen : clrRed; //--- Set profit color MqlDateTime currentDateTime; //--- Declare datetime structure TimeToStruct(TimeCurrent(), currentDateTime); //--- Convert current time if (currentDateTime.sec >= 0 && currentDateTime.sec < 10) { //--- Check first 10 seconds blinkingColorIndex = clrRed; //--- Set red color } if (currentDateTime.sec >= 10 && currentDateTime.sec < 20) { //--- Check next 10 seconds blinkingColorIndex = clrOrange; //--- Set orange color } if (currentDateTime.sec >= 20 && currentDateTime.sec < 30) { //--- Check next 10 seconds blinkingColorIndex = clrBlue; //--- Set blue color } if (currentDateTime.sec >= 30 && currentDateTime.sec < 40) { //--- Check next 10 seconds blinkingColorIndex = clrDodgerBlue; //--- Set dodger blue color } if (currentDateTime.sec >= 40 && currentDateTime.sec < 50) { //--- Check next 10 seconds blinkingColorIndex = clrYellow; //--- Set yellow color } if (currentDateTime.sec >= 50 && currentDateTime.sec <= 59) { //--- Check last 10 seconds blinkingColorIndex = clrYellow; //--- Set yellow color } if (ObjectFind(0, "DashboardBG") < 0) { //--- Check dashboard background ObjectCreate(0, "DashboardBG", OBJ_RECTANGLE_LABEL, 0, 0, 0); //--- Create dashboard background ObjectSetInteger(0, "DashboardBG", OBJPROP_CORNER, 0); //--- Set corner ObjectSetInteger(0, "DashboardBG", OBJPROP_XDISTANCE, 100); //--- Set x distance ObjectSetInteger(0, "DashboardBG", OBJPROP_YDISTANCE, 20); //--- Set y distance ObjectSetInteger(0, "DashboardBG", OBJPROP_XSIZE, 260); //--- Set width ObjectSetInteger(0, "DashboardBG", OBJPROP_YSIZE, 300); //--- Set height ObjectSetInteger(0, "DashboardBG", OBJPROP_BGCOLOR, clrLightGray); //--- Set background color ObjectSetInteger(0, "DashboardBG", OBJPROP_BORDER_TYPE, BORDER_FLAT); //--- Set border type ObjectSetInteger(0, "DashboardBG", OBJPROP_COLOR, clrBlack); //--- Set border color ObjectSetInteger(0, "DashboardBG", OBJPROP_BACK, false); //--- Set to foreground } if (ObjectFind(0, "CLOSE ALL") < 0) { //--- Check close all button ObjectCreate(0, "CLOSE ALL", OBJ_BUTTON, 0, 0, 0); //--- Create close all button ObjectSetInteger(0, "CLOSE ALL", OBJPROP_CORNER, 0); //--- Set corner ObjectSetInteger(0, "CLOSE ALL", OBJPROP_XDISTANCE, 110); //--- Set x distance ObjectSetInteger(0, "CLOSE ALL", OBJPROP_YDISTANCE, 280); //--- Set y distance ObjectSetInteger(0, "CLOSE ALL", OBJPROP_XSIZE, 240); //--- Set width ObjectSetInteger(0, "CLOSE ALL", OBJPROP_YSIZE, 25); //--- Set height ObjectSetString(0, "CLOSE ALL", OBJPROP_TEXT, "Close All Positions"); //--- Set button text ObjectSetInteger(0, "CLOSE ALL", OBJPROP_COLOR, clrWhite); //--- Set text color ObjectSetInteger(0, "CLOSE ALL", OBJPROP_BGCOLOR, clrRed); //--- Set background color ObjectSetInteger(0, "CLOSE ALL", OBJPROP_BORDER_COLOR, clrBlack); //--- Set border color } string headerText = "Pin Bar Averaging EA"; //--- Set header text LABEL("Header", "Impact", 20, 110, 20, clrNavy, 0, headerText); //--- Create header label string copyrightText = "Copyright 2025, Allan Munene Mutiiria"; //--- Set copyright text LABEL("Copyright", "Arial", 9, 110, 55, clrBlack, 0, copyrightText); //--- Create copyright label string linkText = "https://t.me/Forex_Algo_Trader"; //--- Set link text LABEL("Link", "Arial", 9, 110, 70, clrBlue, 0, linkText); //--- Create link label string accountHeader = "Account Information"; //--- Set account header LABEL("AccountHeader", "Arial Bold", 10, 110, 90, clrBlack, 0, accountHeader); //--- Create account header label string balanceText = "Balance: " + DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2); //--- Set balance text LABEL("Balance", "Arial", 9, 120, 105, clrBlack, 0, balanceText); //--- Create balance label string equityText = "Equity: " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2); //--- Set equity text LABEL("Equity", "Arial", 9, 120, 120, equityColor, 0, equityText); //--- Create equity label string marginText = "Free Margin: " + DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_FREE), 2); //--- Set margin text LABEL("Margin", "Arial", 9, 120, 135, clrBlack, 0, marginText); //--- Create margin label string profitText = "Open Profit: " + DoubleToString(buyProfit + sellProfit, 2); //--- Set profit text LABEL("Profit", "Arial", 9, 120, 150, profitColor, 0, profitText); //--- Create profit label string positionsText = "Buy Positions: " + IntegerToString((int)buyCount) + " Sell Positions: " + IntegerToString((int)sellCount); //--- Set positions text LABEL("Positions", "Arial", 9, 120, 165, clrBlack, 0, positionsText); //--- Create positions label string buyBEText = "Buy Break Even: " + (buyCount > 0 ? DoubleToString(buyBreakEvenPrice, _Digits) : "-"); //--- Set buy breakeven text LABEL("BuyBE", "Arial", 9, 120, 180, clrBlack, 0, buyBEText); //--- Create buy breakeven label string sellBEText = "Sell Break Even: " + (sellCount > 0 ? DoubleToString(sellBreakEvenPrice, _Digits) : "-"); //--- Set sell breakeven text LABEL("SellBE", "Arial", 9, 120, 195, clrBlack, 0, sellBEText); //--- Create sell breakeven label string spreadText = "Spread: " + spreadPrefix + IntegerToString((int)currentSpread) + " points"; //--- Set spread text LABEL("Spread", "Arial", 9, 120, 210, clrBlack, 0, spreadText); //--- Create spread label string timeText = "Time to next bar: " + minutesPrefix + IntegerToString(minutesRemaining) + ":" + secondsPrefix + IntegerToString(secondsRemaining); //--- Set time text LABEL("Time", "Arial", 9, 120, 225, clrBlack, 0, timeText); //--- Create time label string pinbarText; //--- Declare pinbar text if (IsBuyPinbar()) pinbarText = "Buy Pinbar"; //--- Check buy pinbar else if (IsSellPinbar()) pinbarText = "Sell Pinbar"; //--- Check sell pinbar else pinbarText = "None"; //--- Set no pinbar LABEL("Pinbar", "Arial", 9, 120, 240, clrBlack, 0, "Pinbar Signal: " + pinbarText); //--- Create pinbar label string patternText = "Candle Pattern: " + CandleStick_Analyzer(); //--- Set candlestick pattern text LABEL("Pattern", "Arial", 9, 120, 255, clrBlack, 0, patternText); //--- Create pattern label }
Display_Info関数を実装し、リアルタイムでの取引モニタリング用の包括的なダッシュボードを作成します。まず、buyCountなどの主要な指標をそれぞれ初期化値にリセットし、すべてのポジションを反復処理して、Symbolに一致する買い・売りポジションの指標を更新します。カウントのインクリメント、利益や取引量、加重平均取得価格の合計、最小/最大価格の追跡をおこない、必要に応じてブレークイーブン価格を計算します。
次に、次のバーまでの残り時間を計算します。PeriodSecondsから現在時刻とiTimeの差を引き、minutesRemainingおよびsecondsRemainingに変換し、スプレッド表示や時間表示のフォーマット用プレフィックスを設定します。さらに、equityColorを(資産が残高を上回る場合は緑、下回る場合は赤)、profitColorを(総利益が正なら緑、負なら赤)に設定し、視覚効果として現在秒数に基づく点滅色インデックスを設定します。最後に、ダッシュボードの背景を作成します。DashboardBGが存在しない場合はObjectCreateでOBJ_RECTANGLE_LABELとして作成し、[CLOSE ALL]ボタンをOBJ_BUTTONとして作成します。また、LABEL関数を使用して複数のラベルを作成し、EAのタイトル、著作権情報、リンク、口座情報(残高、資産、余剰証拠金、利益)、ポジション数、ブレークイーブン価格、スプレッド、次のバーまでの時間、ピンバーシグナル、およびCandleStick_Analyzerから取得したローソク足パターンを表示します。これにより、取引情報を明確かつ動的に可視化できます。ボタンに関しては、OnChartEventイベントハンドラ内でロジックを実装します。
//+------------------------------------------------------------------+ //| Handle chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) { //--- Check object click event if (sparam == "CLOSE ALL") { //--- Check close all button ObjectSetInteger(0, "CLOSE ALL", OBJPROP_STATE, false); //--- Reset button state for (int positionIndex = PositionsTotal() - 1; positionIndex >= 0; positionIndex--) { //--- Iterate through positions ulong ticket = PositionGetTicket(positionIndex); //--- Get position ticket if (ticket == 0) continue; //--- Skip invalid tickets if (PositionGetString(POSITION_SYMBOL) == Symbol()) { //--- Check symbol obj_Trade.PositionClose(ticket); //--- Close position } } } } }
OnChartEvent関数内では、イベントidがCHARTEVENT_OBJECT_CLICKであるかどうかを確認し、チャート上のオブジェクトクリックを検出します。次に、クリックされたオブジェクトsparamが[CLOSE ALL]ボタンであるかどうかを確認し、該当する場合はObjectSetIntegerでOBJPROP_STATEをfalseにリセットしてボタンの状態を初期化します。その後、すべてのポジションを反復処理し、PositionGetTicketで各ポジションのチケットを取得します。無効なチケットはスキップし、PositionGetStringでポジションの銘柄が現在のSymbolと一致するかどうかを確認します。最後に、条件に一致したポジションについて、obj_Trade.PositionCloseを使用してクローズ処理を実行します。これにより、ダッシュボード上の[Close All Positions]ボタンから、開いているすべてのポジションを手動で即座に管理できるようになります。OnTick内で本関数を呼び出し、コンパイル後には上記の処理が反映されます。

画像からわかるように、本プログラムはサポートおよびレジスタンスレベルの検出と可視化、ポジションの新規エントリーおよびナンピンによる追加エントリー、トレーリングによるポジション追随、そして口座メタデータのパネル表示をおこなうことができます。これにより、本記事で設定した目的を達成できていることが確認できます。残っている作業は、このプログラムのバックテストをおこなうことです。バックテストについては次のセクションで扱います。
バックテスト
徹底的なバックテストの結果、次の結果が得られました。
バックテストグラフ

バックテストレポート

結論
まとめとして、本記事ではMQL5でのピンバーナンピンシステムを開発しました。本システムはピンバーのローソク足パターンを用いて取引を開始し、ナンピン戦略で複数ポジションを管理します。さらに、トレーリングストップやブレークイーブン調整、リアルタイム監視用の動的ダッシュボードを組み合わせることで、より高度なポジション管理を実現しています。CandleStick_AnalyzerやaddAveragingOrderといったモジュール化された関数を活用することで、反転取引を体系的におこない、リスクコントロールをカスタマイズ可能な形で提供します。
免責条項:本記事は教育目的のみを意図したものです。取引には重大な財務リスクが伴い、市場の変動によって損失が生じる可能性があります。本プログラムを実際の市場で運用する前に、十分なバックテストと慎重なリスク管理が不可欠です。
提示された概念と実装を活用することで、このピンバーシステムを自分の取引スタイルに適応させ、アルゴリズム戦略を強化できます。取引をお楽しみください。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19087
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
プライスアクション分析ツールキットの開発(第36回):MetaTrader 5マーケットストリームへ直接アクセスするPython活用法
MQL5での取引戦略の自動化(第25回):最小二乗法と動的シグナル生成を備えたTrendline Trader
MQL5での取引戦略の自動化(第27回):視覚的なフィードバックによるプライスアクションクラブハーモニックパターンの作成
MQL5とデータ処理パッケージの統合(第5回):適応学習と柔軟性
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索