English
preview
MQL5での取引戦略の自動化(第41回):ローソク足レンジ理論(CRT)-蓄積・操作・分配(AMD)

MQL5での取引戦略の自動化(第41回):ローソク足レンジ理論(CRT)-蓄積・操作・分配(AMD)

MetaTrader 5トレーディング |
16 6
Allan Munene Mutiiria
Allan Munene Mutiiria

はじめに

前回の記事(第40回)では、MetaQuotes Language 5 (MQL5)を用いたフィボナッチリトレースメント取引システムを開発しました。このシステムでは、日足のレンジまたはルックバック配列からリトレースメントレベルを算出し、強気または弱気のセットアップを特定、価格が指定レベルを横切った際にエントリーをおこない、新しいフィボナッチ計算に応じた任意の決済も可能にしました。本記事では、ローソク足レンジ理論(CRT)取引システムを開発し、蓄積、操作、分配(AMD)の各フェーズを取り入れます。

このシステムでは、指定した時間足で蓄積レンジを特定し、任意のフィルタで操作の深さを確認しながらブレイクを検知します。分配フェーズでは、ローソク足の終値を確認して反転を確定させ、エントリーをおこないます。リスクリワード比に基づく動的または静的なストップロスおよびテイクプロフィットの設定に対応し、必要に応じてトレーリングストップや方向ごとのポジション上限を適用することでリスク管理もおこなうことができます。各フェーズはチャート上に矩形、レベル線、テキストラベルで可視化され、直感的に状態を把握できるようになっています。本記事では以下のトピックを扱います。

  1. ローソク足レンジ理論(CRT)フレームワークの理解
  2. MQL5での実装
  3. バックテスト
  4. 結論

これにより、AMDフェーズを組み込んだCRT取引戦略のMQL5版を作成し、カスタマイズ可能な状態で活用できるようになります。さっそく見ていきましょう。


ローソク足レンジ理論(CRT)フレームワークの理解

ローソク足レンジ理論(CRT)は、ローソク足のレンジ内に存在する重要なフェーズを特定することに焦点を当て、高確率の反転トレードを狙うプライスアクション戦略です。CRTでは、市場の動きを蓄積、操作、分配という3つの主要なフェーズに分類します。蓄積フェーズでは、価格が特定のレンジ内で落ち着き、機関投資家のポジション構築がおこなわれることを示唆します。操作フェーズでは、価格がレンジの上下限を一時的に超え、トレーダーを誘い込み弱いポジションを振るい落とす動きが発生します。分配フェーズでは、価格がレンジ内に反転して戻った後、本来の方向性に沿った大きな値動きが展開されます。このアプローチは、重要なブレイクの多くが流動性を作るための偽の動きであり、その後に反対方向への強い値動きが続くという考えに基づいています。

強気のレンジセットアップでは、終値が上方向に位置するローソク足レンジを特定し、下方向への一時的なブレイクを操作フェーズとして捉え、安値を上抜けて反転したタイミングで買いエントリーをおこない、上方向の分配を狙います。逆に、弱気のレンジセットアップでは、下方向に終値が位置するローソク足レンジを確認し、高値を上抜けるブレイクを操作として捉え、高値を下抜けて反転したタイミングで売りエントリーをおこない、下方向の分配を狙います。最小操作深度やローソク足の終値による反転確認などのフィルタを組み込むことで、低品質なセットアップを避け、より信頼度の高い取引に注力できます。以下にCRTセットアップのサンプルを示します。

ローソク足レンジ理論(CRT)セットアップ

本システムでは、ユーザー指定の時間足で蓄積レンジを定義します。ブレイクを検知した際には、オプションで設定された操作深度の閾値に基づき検証をおこないます。反転確認は、確認用の時間足における一定本数のローソク足終値で判断します。エントリーは方向ごとのポジション上限を適用して実行されます。ストップロスおよびテイクプロフィットは、リスクリワード比に応じて動的または静的に設定可能です。利益閾値到達後には任意でトレーリングストップも導入できます。すべてのフェーズは、チャート上に矩形、レベル線、ラベルとして可視化され、直感的に状態を把握できるようになっています。以下に想定されるビジュアル表示の例を示します。

CRTフレームワークサンプル


MQL5での実装

MQL5でプログラムを作成するには、まずMetaEditorを開き、ナビゲーターで[Experts]フォルダを探します。[新規]タブをクリックして指示に従い、ファイルを作成します。ファイルが作成されたら、コーディング環境で、まずプログラム全体で使用する入力パラメータグローバル変数をいくつか宣言する必要があります。

//+------------------------------------------------------------------+
//|                                   CRT Candle Range Theory 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"

#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| Enums                                                            |
//+------------------------------------------------------------------+
enum SLTP_Method {                                                // Define SL/TP method enum
   Dynamic_Method = 0,                                            // Dynamic based on breach extreme
   Static_Method  = 1                                             // Static based on fixed points
};

enum TrailingTypeEnum {                                           // Define trailing type enum
   Trailing_None   = 0,                                           // None
   Trailing_Points = 1                                            // By Points
};

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input ENUM_TIMEFRAMES RangeTF = PERIOD_H4;                        // Timeframe for Range Definition
input double TradeVolume = 0.01;                                  // Trade Volume Size
input double RR_Ratio = 1.3;                                      // Risk to Reward Ratio
input SLTP_Method SLTP_Approach = Static_Method;                  // SL/TP Calculation Method
input int SL_Points = 100;                                        // SL Points (for Static Method)
input TrailingTypeEnum TrailingType = Trailing_None;              // Trailing Stop Type
input double Trailing_Stop_Points = 30.0;                         // Trailing Stop in Points
input double Min_Profit_To_Trail_Points = 50.0;                   // Min Profit to Start Trailing in Points
input int UniqueID = 123456789;                                   // Unique Trade Identifier
input int MaxPositionsDir = 1;                                    // Max Positions per Direction
input ENUM_TIMEFRAMES ConfirmTF = PERIOD_CURRENT;                 // Confirmation Timeframe (for bar closures)
input int ConfirmBars = 1;                                        // Bars to Confirm Reversal on Close (0 to disable)
input bool UseManipFilter = true;                                 // Use Manipulation Depth Filter
input double MinManipPct = 5.0;                                   // Min Manipulation % of Range (if filter enabled)
input double DistribProjPct = 50.0;                               // Distribution Projection % of Range Duration

実装では、まず「#include <Trade\Trade.mqh>」を使用してTradeライブラリをインクルードします。このライブラリは、取引操作に必要なクラスや関数を提供します。次に、ユーザーが設定可能なオプションを分類するための列挙型を定義します。まずSLTP_Method列挙型を作成し、ブレイクの極値に基づく動的なストップロスとテイクプロフィットの計算にはDynamic_Method、固定値による設定にはStatic_Methodを使用します。また、TrailingTypeEnum列挙型も定義します。Trailing_Noneはトレーリングストップを無効にし、Trailing_Pointsは指定ポイント数でトレーリングを有効にします。これにより柔軟なリスク管理が可能になります。

続いて、エキスパートアドバイザー(EA)のプロパティダイアログからユーザーが調整可能な入力パラメータを宣言します。これには、蓄積レンジを定義する時間足を指定するRangeTF、各エントリーのロットサイズを設定するTradeVolume、リスクリワード比率を決定するRR_Ratio、先ほど定義した列挙型を用いてストップロスとテイクプロフィット方式を選択するSLTP_Approachなどが含まれます。その他のパラメータも、名称から意味が直感的に分かるようになっています。これらの入力により、システムは異なる市場環境やユーザーの好みに応じて柔軟に調整可能となります。コンパイル後には、以下のような入力セットが得られます。

入力セット

ここまで設定したら、プログラム全体で使用するグローバル変数を定義していきます。

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
CTrade obj_Trade;                                                 //--- Trade object
datetime prevRangeTime = 0;                                       //--- Previous range time
double rangeMax = 0.0;                                            //--- Range maximum
double rangeMin = 0.0;                                            //--- Range minimum
bool positiveDirection = false;                                   //--- Positive direction flag
bool rangeBreached = false;                                       //--- Range breached flag
double breachPoint = 0.0;                                         //--- Breach point
string maxLevelObj = "RangeMaxLevel";                             //--- Max level object name
string minLevelObj = "RangeMinLevel";                             //--- Min level object name
string maxTextObj = "CRT_High_Text";                              //--- CRT high text object
string minTextObj = "CRT_Low_Text";                               //--- CRT low text object
bool tradedSetup = false;                                         //--- Traded setup flag
datetime breachTime = 0;                                          //--- Breach time
datetime lastConfirmTime = 0;                                     //--- Last confirm time

次に、プログラム全体で状態を保持し、CRTロジックを管理するためのグローバル変数を宣言します。CTradeクラスからobj_Tradeオブジェクトをインスタンス化し、エントリーやポジション変更など、すべての取引関連操作を担当します。蓄積レンジを追跡するための変数として、前回のレンジローソク足のタイムスタンプを保持するprevRangeTime、現在のレンジの高値と安値を格納するrangeMaxおよびrangeMin、レンジローソク足が陽線か陰線かを示すブールフラグpositiveDirectionを定義します。さらに、ブレイク発生を示すrangeBreached、操作中の極値を記録するbreachPoint、同一セットアップでの重複エントリーを防ぐtradedSetupなどのフラグや値も設定します。

チャートオブジェクト名を管理するための文字列変数も用意します。レンジの極値を示す水平線用のmaxLevelObjとminLevelObj、CRTの高値と安値を表示するテキストラベル用のmaxTextObjとminTextObjです。また、操作フェーズの開始時刻を記録するbreachTime、最後の確認ローソク足の時刻を追跡するlastConfirmTimeも設定し、タイミング依存のイベントを正確に監視できるようにします。これで準備が整いました。OnInitイベントハンドラ内でトレード用のマジックナンバーを設定するところから実装を始めます。

//+------------------------------------------------------------------+
//| EA Start Function                                                |
//+------------------------------------------------------------------+
int OnInit() {
   obj_Trade.SetExpertMagicNumber(UniqueID);                      //--- Set magic number
   return(INIT_SUCCEEDED);                                        //--- Return success
}

OnInitイベントハンドラは、EAが起動したときやチャートにアタッチされたときに実行されます。ここで、obj_Trade.SetExpertMagicNumberをUniqueID入力値で呼び出し、プログラムで開かれるすべての取引に一意の識別子を割り当てます。これにより、取引のフィルタリングや管理が容易になります。初期化が正常に完了したことを示すために、最後にINIT_SUCCEEDEDを返します。これでプログラムの実行準備が整いました。次に、チャート上にラベルやレベルを描画するためのヘルパー関数を定義していきます。以下がそのロジックです。

//+------------------------------------------------------------------+
//| Render Horizontal Level                                          |
//+------------------------------------------------------------------+
void RenderLevel(string objName, double levelVal, color levelClr, string levelDesc) {
   ObjectDelete(ChartID(), objName);                                //--- Delete object
   ObjectCreate(ChartID(), objName, OBJ_HLINE, 0, 0, levelVal);     //--- Create hline
   ObjectSetInteger(ChartID(), objName, OBJPROP_COLOR, levelClr);   //--- Set color
   ObjectSetInteger(ChartID(), objName, OBJPROP_STYLE, STYLE_DOT);  //--- Set style
   ObjectSetString(ChartID(), objName, OBJPROP_TOOLTIP, levelDesc); //--- Set tooltip
   ChartRedraw(ChartID());                                          //--- Redraw chart
}

//+------------------------------------------------------------------+
//| Render Text Label                                                |
//+------------------------------------------------------------------+
void RenderText(string objName, datetime timeVal, double priceVal, string textStr, color textClr, int anchorVal) {
   ObjectDelete(ChartID(), objName);                                 //--- Delete object
   ObjectCreate(ChartID(), objName, OBJ_TEXT, 0, timeVal, priceVal); //--- Create text
   ObjectSetString(ChartID(), objName, OBJPROP_TEXT, textStr);       //--- Set text
   ObjectSetInteger(ChartID(), objName, OBJPROP_COLOR, textClr);     //--- Set color
   ObjectSetInteger(ChartID(), objName, OBJPROP_ANCHOR, anchorVal);  //--- Set anchor
   ObjectSetInteger(ChartID(), objName, OBJPROP_FONTSIZE, 10);       //--- Set fontsize
   ChartRedraw(ChartID());                                           //--- Redraw chart
}

まず、RenderLevel関数を定義し、レンジの高値や安値などの重要な価格レベルを示す水平線をチャート上に描画または更新します。この関数は、オブジェクト名、価格レベル値、色、説明文をパラメータとして受け取ります。関数内では、まずObjectDeleteを使って同名の既存オブジェクトを削除し、重複を避けます。その後、ObjectCreateOBJ_HLINEタイプの水平線オブジェクトを作成し、指定された価格レベルに配置します。色はObjectSetIntegerOBJPROP_COLORで設定し、OBJPROP_STYLEにSTYLE_DOTを指定して点線スタイルを適用、OBJPROP_TOOLTIPでツールチップとして説明を追加します。最後にChartRedrawを呼び出して、変更を即座にチャート上に反映させます。

同様に、RenderText関数を作成し、フェーズの識別などの注釈用テキストラベルをチャート上に配置または更新します。この関数はオブジェクト名、時間・価格座標、表示テキスト、色、アンカーポイントをパラメータとして受け取ります。まずObjectDeleteで既存オブジェクトを削除し、続いてObjectCreateでOBJ_TEXTタイプのテキストオブジェクトを指定の時間と価格に作成します。テキスト内容はObjectSetStringOBJPROP_TEXTで設定し、色はObjectSetIntegerとOBJPROP_COLORで指定します。アンカー位置はOBJPROP_ANCHORで定義し、フォントサイズはOBJPROP_FONTSIZEを10に設定します。最後にChartRedrawを呼び出してラベルを正しく表示させます。これらの関数を用いることで、OnTickイベントハンドラ内でレンジを定義し、チャート上に可視化することが可能になります。

//+------------------------------------------------------------------+
//| Tick Processing Function                                         |
//+------------------------------------------------------------------+
void OnTick() {
   double currBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);        //--- Get current bid
   double currAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);        //--- Get current ask
   datetime currRangeTime = iTime(_Symbol, RangeTF, 0);           //--- Get current range time
   if (currRangeTime != prevRangeTime) {                          //--- Check new range
      prevRangeTime = currRangeTime;                              //--- Update prev time
      double prevMax = iHigh(_Symbol, RangeTF, 1);                //--- Get prev high
      double prevMin = iLow(_Symbol, RangeTF, 1);                 //--- Get prev low
      double prevStart = iOpen(_Symbol, RangeTF, 1);              //--- Get prev open
      double prevEnd = iClose(_Symbol, RangeTF, 1);               //--- Get prev close
      rangeMax = prevMax;                                         //--- Set range max
      rangeMin = prevMin;                                         //--- Set range min
      positiveDirection = (prevEnd > prevStart);                  //--- Set direction
      rangeBreached = false;                                      //--- Reset breached
      breachPoint = positiveDirection ? rangeMin : rangeMax;      //--- Set breach point
      tradedSetup = false;                                        //--- Reset traded
      breachTime = 0;                                             //--- Reset breach time
      lastConfirmTime = 0;                                        //--- Reset confirm time
      RenderLevel(maxLevelObj, rangeMax, clrOrange, "Range Max"); //--- Render max level
      RenderLevel(minLevelObj, rangeMin, clrPurple, "Range Min"); //--- Render min level
      // Add text labels for current CRT High and Low
      datetime labelTime = currRangeTime;                         //--- Set label time
      RenderText(maxTextObj, labelTime, rangeMax, "CRT High", clrOrange, ANCHOR_RIGHT_LOWER); //--- Render high text
      RenderText(minTextObj, labelTime, rangeMin, "CRT Low", clrPurple, ANCHOR_RIGHT_UPPER);  //--- Render low text
      // Draw background rectangle for the accumulation phase (range candle) with fill true
      string rangeRectObj = "RangeRectangle_" + IntegerToString(currRangeTime);               //--- Range rect name
      datetime rangeStartTime = iTime(_Symbol, RangeTF, 1);                                   //--- Get start time
      datetime rangeEndTime = currRangeTime;                                                  //--- Set end time
      ObjectCreate(ChartID(), rangeRectObj, OBJ_RECTANGLE, 0, rangeStartTime, rangeMax, rangeEndTime, rangeMin); //--- Create rect
      color rectClr = positiveDirection ? clrLightGreen : clrLightPink;                       //--- Set rect color
      ObjectSetInteger(ChartID(), rangeRectObj, OBJPROP_COLOR, rectClr);                      //--- Set color
      ObjectSetInteger(ChartID(), rangeRectObj, OBJPROP_FILL, true);                          //--- Set fill
      ObjectSetInteger(ChartID(), rangeRectObj, OBJPROP_BACK, true);                          //--- Set back
      ObjectSetInteger(ChartID(), rangeRectObj, OBJPROP_STYLE, STYLE_SOLID);                  //--- Set style
      ChartRedraw(ChartID());                                                                 //--- Redraw chart
   }
}

OnTickイベントハンドラでは、まずSymbolInfoDoubleを使用してSYMBOL_BIDの現在のBid価格を取得しcurrBidに代入します。同様にSYMBOL_ASKを用いてAsk価格を取得しcurrAskに代入します。次にiTimeでレンジ時間足の最新バーのタイムスタンプを取得しcurrRangeTimeに格納します。このタイムスタンプがprevRangeTimeと異なる場合、新しいレンジバーが形成されたことを示すため、prevRangeTimeを更新します。前バーの高値はiHighでshift 1を使ってprevMaxに、安値はiLowでprevMinに、始値はiOpenでprevStartに、終値はiCloseでprevEndに取得します。レンジの境界はrangeMaxに高値、rangeMinに安値を設定し、終値が始値を上回ればpositiveDirectionをtrueとして強気セットアップと判断します。rangeBreachedはfalseにリセットし、breachPointは陽線の場合は最小値、陰線の場合は最大値で初期化、tradedSetupをクリアし、breachTimeとlastConfirmTimeをゼロにします。

可視化のため、RenderLevelを呼び出してレンジ最大値をオレンジ色、説明文「Range Max」で描画し、最小値は紫色で「Range Min」とします。ラベルはlabelTimeに現在のレンジ時間を設定し、RenderTextを呼んでCRT Highをオレンジ色で右下アンカーに、CRT Lowを紫色で右上アンカーに描画します。蓄積フェーズをハイライトするため、RangeRectangle_に変換した現在のレンジ時間文字列を結合して一意の矩形名を作成します。前バーの開始時間はiTimeのshift 1でrangeStartTimeに取得し、rangeEndTimeを現在時間に設定します。ObjectCreateOBJ_RECTANGLEタイプの矩形を作成し、時間と価格の範囲に広げます。陽線の場合はライトグリーン、陰線の場合はライトピンクを選択し、色を適用、塗りつぶしを有効にして背景として設定、スタイルはソリッドにしてChartRedrawでチャートに反映します。マイルストーンごとにコンパイルして動作を確認するのは良いプログラミング習慣です。この時点で、以下のような結果になります。

CRTレンジ設定

レンジの設定が正常に完了したことが確認できました。次に、ブレイクを判定しセットアップの取引を実行していきます。

if (rangeMax == 0.0 || rangeMin == 0.0) return;                //--- Return if no range
bool justBreached = false;                                     //--- Init just breached
if (positiveDirection && currBid <= rangeMin) {                //--- Check positive breach
   if (!rangeBreached) {                                       //--- Check not breached
      rangeBreached = true;                                    //--- Set breached
      justBreached = true;                                     //--- Set just breached
      breachTime = TimeCurrent();                              //--- Set breach time
   }
   breachPoint = MathMin(breachPoint, currBid);                //--- Update breach point
} else if (!positiveDirection && currBid >= rangeMax) {        //--- Check negative breach
   if (!rangeBreached) {                                       //--- Check not breached
      rangeBreached = true;                                    //--- Set breached
      justBreached = true;                                     //--- Set just breached
      breachTime = TimeCurrent();                              //--- Set breach time
   }
   breachPoint = MathMax(breachPoint, currBid);                //--- Update breach point
}
if (rangeBreached && !tradedSetup) {                           //--- Check breached and not traded
   // Check for confirmed reversal on bar closures
   bool reversalConfirmed = false;                              //--- Init confirmed
   if (ConfirmBars == 0) {                                      //--- Check no confirm
      reversalConfirmed = true;                                 //--- Set confirmed
   } else {                                                     //--- Else
      datetime currConfirmTime = iTime(_Symbol, ConfirmTF, 0);  //--- Get confirm time
      if (currConfirmTime != lastConfirmTime) {                 //--- Check new confirm
         lastConfirmTime = currConfirmTime;                     //--- Update last confirm
         int confirmedCount = 0;                                //--- Init count
         for (int i = 1; i <= ConfirmBars; i++) {               //--- Iterate bars
            double confirmClose = iClose(_Symbol, ConfirmTF, i); //--- Get close
            if (positiveDirection && confirmClose > rangeMin) { //--- Check positive
               confirmedCount++;                                //--- Increment count
            } else if (!positiveDirection && confirmClose < rangeMax) { //--- Check negative
               confirmedCount++;                                //--- Increment count
            }
         }
         if (confirmedCount >= ConfirmBars) {                   //--- Check confirmed
            reversalConfirmed = true;                           //--- Set confirmed
         }
      }
   }
   // Calculate manipulation depth for filter
   bool manipSufficient = true;                                 //--- Init sufficient
   double rangeSize = rangeMax - rangeMin;                      //--- Calc range size
   double manipDepth = positiveDirection ? (rangeMin - breachPoint) : (breachPoint - rangeMax); //--- Calc depth
   double manipPct = (manipDepth / rangeSize) * 100.0;          //--- Calc percent
   if (UseManipFilter) {                                        //--- Check filter
      if (manipPct < MinManipPct) {                             //--- Check insufficient
         manipSufficient = false;                               //--- Set insufficient
      }
   }
   bool justEntered = false;                                    //--- Init entered
   datetime entryTime = 0;                                      //--- Init entry time
   double entryPrice = 0.0;                                     //--- Init entry price
   double gainTarget = 0.0;                                     //--- Init target
   if (reversalConfirmed && manipSufficient) {                  //--- Check confirmed and sufficient
      if (positiveDirection && currBid > rangeMin && ActivePositions(POSITION_TYPE_BUY) < MaxPositionsDir) { //--- Check buy entry
         double lossStop;                                       //--- Init SL
         if (SLTP_Approach == Dynamic_Method) {                 //--- Check dynamic
            lossStop = NormalizeDouble(breachPoint, _Digits);   //--- Set SL
            double riskDistance = currAsk - breachPoint;        //--- Calc risk
            gainTarget = NormalizeDouble(currAsk + riskDistance * RR_Ratio, _Digits); //--- Set TP
         } else {                                               //--- Static
            lossStop = NormalizeDouble(currAsk - SL_Points * _Point, _Digits); //--- Set SL
            gainTarget = NormalizeDouble(currAsk + SL_Points * RR_Ratio * _Point, _Digits); //--- Set TP
         }
         if (obj_Trade.Buy(TradeVolume, _Symbol, currAsk, lossStop, gainTarget, "CRT Positive Entry")) { //--- Open buy
            if (obj_Trade.ResultRetcode() == TRADE_RETCODE_DONE) { //--- Check success
               Print("Positive Signal: Range raided below min, reversed back in (confirmed). Entry at ", DoubleToString(currAsk, _Digits), 
                     " SL at ", DoubleToString(lossStop, _Digits), " TP at ", DoubleToString(gainTarget, _Digits)); //--- Log entry
               Print("Debug: Accumulation Range: ", DoubleToString(rangeSize / _Point, 0), " points. Manipulation Depth: ", DoubleToString(manipDepth / _Point, 0), " points (", DoubleToString(manipPct, 2), "% of range)"); //--- Log debug
               string markerName = "EntryMarker_" + IntegerToString(TimeCurrent()); //--- Marker name
               ObjectCreate(ChartID(), markerName, OBJ_ARROW, 0, TimeCurrent(), currBid); //--- Create marker
               ObjectSetInteger(ChartID(), markerName, OBJPROP_ARROWCODE, 233); //--- Set code
               ObjectSetInteger(ChartID(), markerName, OBJPROP_COLOR, clrBlue); //--- Set color
               ObjectSetInteger(ChartID(), markerName, OBJPROP_ANCHOR, ANCHOR_BOTTOM); //--- Set anchor
               tradedSetup = true;                                 //--- Set traded
               justEntered = true;                                 //--- Set entered
               entryTime = TimeCurrent();                          //--- Set entry time
               entryPrice = currAsk;                               //--- Set entry price
            }
         }
      } else if (!positiveDirection && currBid < rangeMax && ActivePositions(POSITION_TYPE_SELL) < MaxPositionsDir) { //--- Check sell entry
         double lossStop;                                       //--- Init SL
         if (SLTP_Approach == Dynamic_Method) {                 //--- Check dynamic
            lossStop = NormalizeDouble(breachPoint, _Digits);   //--- Set SL
            double riskDistance = breachPoint - currBid;        //--- Calc risk
            gainTarget = NormalizeDouble(currBid - riskDistance * RR_Ratio, _Digits); //--- Set TP
         } else {                                               //--- Static
            lossStop = NormalizeDouble(currBid + SL_Points * _Point, _Digits); //--- Set SL
            gainTarget = NormalizeDouble(currBid - SL_Points * RR_Ratio * _Point, _Digits); //--- Set TP
         }
         if (obj_Trade.Sell(TradeVolume, _Symbol, currBid, lossStop, gainTarget, "CRT Negative Entry")) { //--- Open sell
            if (obj_Trade.ResultRetcode() == TRADE_RETCODE_DONE) { //--- Check success
               Print("Negative Signal: Range raided above max, reversed back in (confirmed). Entry at ", DoubleToString(currBid, _Digits), 
                     " SL at ", DoubleToString(lossStop, _Digits), " TP at ", DoubleToString(gainTarget, _Digits)); //--- Log entry
               Print("Debug: Accumulation Range: ", DoubleToString(rangeSize / _Point, 0), " points. Manipulation Depth: ", DoubleToString(manipDepth / _Point, 0), " points (", DoubleToString(manipPct, 2), "% of range)"); //--- Log debug
               string markerName = "EntryMarker_" + IntegerToString(TimeCurrent()); //--- Marker name
               ObjectCreate(ChartID(), markerName, OBJ_ARROW, 0, TimeCurrent(), currAsk); //--- Create marker
               ObjectSetInteger(ChartID(), markerName, OBJPROP_ARROWCODE, 234); //--- Set code
               ObjectSetInteger(ChartID(), markerName, OBJPROP_COLOR, clrRed); //--- Set color
               ObjectSetInteger(ChartID(), markerName, OBJPROP_ANCHOR, ANCHOR_TOP); //--- Set anchor
               tradedSetup = true;                                 //--- Set traded
               justEntered = true;                                 //--- Set entered
               entryTime = TimeCurrent();                          //--- Set entry time
               entryPrice = currBid;                               //--- Set entry price
            }
         }
      }
   }
}

OnTick関数内の処理を続けると、まずレンジが正しく定義されているかを確認します。rangeMaxまたはrangeMinがゼロの場合は不正な状態と判断し、処理を早期に終了します。新たなブレイクを追跡するために、ブール型変数justBreachedをfalseで初期化します。陽線方向のセットアップの場合、現在のBid価格がレンジの最小値以下で、まだブレイクが発生していなければ、rangeBreachedをtrueに設定し、justBreachedをtrueにマーク、さらにTimeCurrent関数でブレイク発生時刻をbreachTimeに記録します。その後、breachPointはMathMin関数を用いて現在値とBidのうち低い方に更新します。陰線方向の場合も同様の処理をおこないます。

ブレイクが発生しており、まだこのセットアップで取引が実行されていない場合は、反転の確認に進みます。まずreversalConfirmedをfalseで初期化します。ConfirmBarsが0の場合は、即座にtrueに設定します。それ以外の場合、確認時間足の最新バーの時刻をiTimeで取得しcurrConfirmTimeに格納します。これがlastConfirmTimeと異なる場合はlastConfirmTimeを更新し、確認ローソク足をカウントします。Shift 1からConfirmBarsまでループし、各終値をiCloseで取得、陽線セットアップでは最小値以上、陰線セットアップでは最大値以下の終値があればカウンターを増やします。カウントがConfirmBars以上であれば、反転を確認済みとします。次に、操作の十分性を評価します。manipSufficientをtrueで初期化し、レンジサイズをrangeMaxとrangeMinの差として計算、操作深さをレンジ端からbreachPointまでの距離として求めます(陽線は引き算、陰線は加算)、パーセンテージは深さをサイズで割り100倍します。UseManipFilterが有効で、パーセンテージがMinManipPct未満の場合はmanipSufficientをfalseに設定します。

続いてエントリー用変数を準備し初期化します。反転が確認され、操作の十分性が満たされている場合、陽線方向の買いエントリー条件をチェックします。Bidがレンジ最小値を上回り、ActivePositions関数で取得した買いポジション(POSITION_TYPE_BUY)がMaxPositionsDir未満であれば、エントリーレベルを計算します。ActivePositions関数はコードをモジュール化するために定義したヘルパー関数で、現在の有効なポジション数を返します。その実装は以下の通りです。

//+------------------------------------------------------------------+
//| Count Active Positions by Type                                   |
//+------------------------------------------------------------------+
int ActivePositions(ENUM_POSITION_TYPE posType) {
   int total = 0;                                                 //--- Init total
   for (int pos = PositionsTotal() - 1; pos >= 0; pos--) {        //--- Iterate positions
      if (PositionGetSymbol(pos) == _Symbol && PositionGetInteger(POSITION_MAGIC) == UniqueID && PositionGetInteger(POSITION_TYPE) == posType) { //--- Check position
         total++;                                                 //--- Increment total
      }
   }
   return total;                                                  //--- Return total
}

関数自体はシンプルです。理解を助けるためにコメントも追加しています。ロジックに戻ると、obj_Trade.Buyを使用して買い注文を試みます。引数には数量、銘柄、Ask、ストップロス、テイクプロフィット、コメントを指定します。ResultRetcodeがTRADE_RETCODE_DONEの場合、注文は成功と判断され、シグナルのログやレンジ・操作深さに関するデバッグ情報をPrintで出力します。その後、ObjectCreateでエントリーマーカーの矢印を作成します。OBJ_ARROWタイプで現在時刻とBid位置に配置、コード233、青色、下側アンカーを設定します。tradedSetupとjustEnteredをtrueにフラグし、エントリー時刻と価格を記録します。MQL5ではWingdingsフォント由来の矢印コードが使用可能で、任意のコードを選択できます。

MQL5 WINGDINGS

陰線方向の場合も同様の処理をおこないます。Bidがレンジ最大値を下回り、売りポジション数が制限以下であれば、ストップロスをbreachPointを基準にリスク幅から動的に計算、テイクプロフィットはリスク×比率で算出します。静的設定の場合はBidにSL_Points×ポイントを加減算してストップロスとテイクプロフィットを設定します。obj_Trade.Sellで売り、成功時には同様にログ出力、コード234、赤色、上側アンカーでマーカーを描画し、フラグとエントリー情報を更新します。コンパイル後、以下の結果が得られます。コンパイルすると、次の結果が得られます。

エントリー確認

画像から、確認が取れた場合に取引が成立することが確認できます。取引の明確化のため、レベルをチャート上に可視化し、実際の状況を視覚的に追跡できるようにします。

// If just entered trade, draw manipulation rectangle, distribution, and labels (including accumulation)
if (justEntered) {                                             //--- Check entered
   string setupSuffix = IntegerToString(prevRangeTime);        //--- Setup suffix
   // Label the range as Accumulation phase (now only for complete setups)
   string accumTextUnique = "AccumText_" + setupSuffix;        //--- Accum text name
   double accumPrice = (rangeMax + rangeMin) / 2;              //--- Accum price
   datetime labelTime = prevRangeTime;                         //--- Label time
   RenderText(accumTextUnique, labelTime, accumPrice, "Accumulation", clrBlue, ANCHOR_RIGHT); //--- Render accum text
   // Calculate the manipulation extreme using candle highs/lows between currRangeTime and entryTime
   int startBar = iBarShift(_Symbol, PERIOD_CURRENT, prevRangeTime); //--- Start bar
   int endBar = iBarShift(_Symbol, PERIOD_CURRENT, entryTime); //--- End bar
   if (startBar < 0 || endBar < 0) return;                     //--- Return invalid
   if (startBar < endBar) { int temp = startBar; startBar = endBar; endBar = temp; } //--- Swap if needed
   int barCount = startBar - endBar + 1;                       //--- Calc bar count
   double manipExtreme;                                        //--- Init manip extreme
   double manipStartPrice = positiveDirection ? rangeMin : rangeMax; //--- Manip start
   if (positiveDirection) {                                    //--- Check positive
      int lowestBar = iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, barCount, endBar); //--- Get lowest
      manipExtreme = iLow(_Symbol, PERIOD_CURRENT, lowestBar); //--- Set extreme
   } else {                                                    //--- Negative
      int highestBar = iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, barCount, endBar); //--- Get highest
      manipExtreme = iHigh(_Symbol, PERIOD_CURRENT, highestBar); //--- Set extreme
   }
   // Draw manipulation rectangle (border only) from CRT end to signal time
   string manipRectObj = "ManipRectangle_" + setupSuffix;      //--- Manip rect name
   double topPrice = MathMax(manipStartPrice, manipExtreme);   //--- Top price
   double bottomPrice = MathMin(manipStartPrice, manipExtreme); //--- Bottom price
   ObjectCreate(ChartID(), manipRectObj, OBJ_RECTANGLE, 0, prevRangeTime, topPrice, entryTime, bottomPrice); //--- Create rect
   ObjectSetInteger(ChartID(), manipRectObj, OBJPROP_COLOR, clrBlue); //--- Set color
   ObjectSetInteger(ChartID(), manipRectObj, OBJPROP_FILL, false); //--- Set no fill
   ObjectSetInteger(ChartID(), manipRectObj, OBJPROP_BACK, true); //--- Set back
   ObjectSetInteger(ChartID(), manipRectObj, OBJPROP_STYLE, STYLE_DOT); //--- Set style
   ObjectSetInteger(ChartID(), manipRectObj, OBJPROP_WIDTH, 2); //--- Set width
   ChartRedraw(ChartID());                                     //--- Redraw chart
   // Add manipulation text label at breach time
   string manipTextUnique = "ManipText_" + setupSuffix;        //--- Manip text name
   int anchorManip = positiveDirection ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER; //--- Manip anchor
   RenderText(manipTextUnique, breachTime, manipExtreme, "Manipulation", clrBlue, anchorManip); //--- Render manip text
   // Label and draw distribution
   string distribTextUnique = "DistribText_" + setupSuffix;    //--- Distrib text name
   color distribClr = positiveDirection ? clrGreen : clrRed;   //--- Distrib color
   int anchor = positiveDirection ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER; //--- Distrib anchor
   RenderText(distribTextUnique, entryTime, entryPrice, "Distribution", distribClr, anchor); //--- Render distrib text
   // Draw border rectangle (fill false) for distribution phase (% of range duration)
   string distribRectObj = "DistribRectangle_" + setupSuffix;  //--- Distrib rect name
   datetime rangeStartTime = iTime(_Symbol, RangeTF, 1);       //--- Range start
   datetime rangeEndTime = prevRangeTime;                      //--- Range end
   long duration = rangeEndTime - rangeStartTime;              //--- Calc duration
   double projFactor = MathMax(DistribProjPct / 100.0, 0.01);  //--- Proj factor
   datetime projEndTime = entryTime + (datetime)(duration * projFactor); //--- Proj end
   double topDistrib = MathMax(entryPrice, gainTarget);        //--- Top distrib
   double bottomDistrib = MathMin(entryPrice, gainTarget);     //--- Bottom distrib
   ObjectCreate(ChartID(), distribRectObj, OBJ_RECTANGLE, 0, entryTime, topDistrib, projEndTime, bottomDistrib); //--- Create rect
   ObjectSetInteger(ChartID(), distribRectObj, OBJPROP_COLOR, distribClr); //--- Set color
   ObjectSetInteger(ChartID(), distribRectObj, OBJPROP_FILL, false); //--- Set no fill
   ObjectSetInteger(ChartID(), distribRectObj, OBJPROP_BACK, true); //--- Set back
   ObjectSetInteger(ChartID(), distribRectObj, OBJPROP_STYLE, STYLE_SOLID); //--- Set style
   ObjectSetInteger(ChartID(), distribRectObj, OBJPROP_WIDTH, 2); //--- Set width
   ChartRedraw(ChartID());                                     //--- Redraw chart
}

ここで、justEnteredがtrueで取引が直前に実行された場合、残りのフェーズを可視化します。オブジェクト名用の一意の接尾辞をprevRangeTimeに対してIntegerToStringで作成します。蓄積ラベル用には「AccumText_」に接尾辞を付与して一意のテキスト名を生成し、レンジの最大値と最小値の平均をmidpointPriceとして計算、ラベル時間をprevRangeTimeに設定してRenderTextを呼び出し、右アンカーに青色で「Accumulation」と表示します。操作フェーズの極値を決定するため、開始時刻prevRangeTimeとentryTimeをiBarShiftでバーインデックスに変換し、無効なら早期リターンします。開始インデックスが終了インデックスより大きい場合は入れ替え、バー数を算出します。manipStartPriceは陽線の場合はレンジ最小値、陰線の場合は最大値に設定します。陽線ではiLowestを使って終了バーからバー数分の範囲で最安値のバーを取得し、iLowで価格を取得します。陰線ではiHighest (MODE_HIGH)とiHighで最高値を取得します。

次に操作矩形を描画します。「ManipRectangle_」に接尾辞を付けて一意の名前を作成し、開始価格と極値価格の上限/下限をMathMax/MathMinで決定し、ObjectCreateOBJ_RECTANGLEとして前回レンジ時間の上からエントリー時間の下まで矩形を作成します。色は青、塗りつぶし無効、背景として配置、スタイルは点線、幅2に設定し、ChartRedrawで描画します。次に操作ラベルは「ManipText_」に接尾辞を付与し、陽線では右上、陰線では右下アンカーを選択してbreachTimeと極値価格に青色で「Manipulation」と表示します。分配フェーズでは「DistribText_」に接尾辞を付与、陽線は緑、陰線は赤色を選び、アンカーは陽線で左下、陰線で左上としてentryTimeとエントリー価格に「Distribution」をRenderTextで表示します。分配矩形も同様の手順で作成し、ChartRedrawで描画します。以下に結果を示します。

蓄積、操作、配布フェーズ

画像から、操作フェーズと分配フェーズが明確に可視化されていることが確認できます。次におこなうのは、ポジションが有利に動いた場合にトレーリングストップで管理する処理です。この処理も関数として実装します。

//+------------------------------------------------------------------+
//| Apply Points Trailing Stop                                       |
//+------------------------------------------------------------------+
void ApplyPointsTrailing() {
   double point = _Point;                                         //--- Get point
   for (int i = PositionsTotal() - 1; i >= 0; i--) {              //--- Iterate positions
      if (PositionGetTicket(i) > 0) {                             //--- Check ticket
         if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == UniqueID) { //--- Check symbol magic
            double sl = PositionGetDouble(POSITION_SL);              //--- Get SL
            double tp = PositionGetDouble(POSITION_TP);              //--- Get TP
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get open
            ulong ticket = PositionGetInteger(POSITION_TICKET);      //--- Get ticket
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //--- Check buy
               double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID) - Trailing_Stop_Points * point, _Digits); //--- Calc new SL
               if (newSL > sl && SymbolInfoDouble(_Symbol, SYMBOL_BID) - openPrice > Min_Profit_To_Trail_Points * point) { //--- Check conditions
                  obj_Trade.PositionModify(ticket, newSL, tp);       //--- Modify position
               }
            } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //--- Check sell
               double newSL = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK) + Trailing_Stop_Points * point, _Digits); //--- Calc new SL
               if (newSL < sl && openPrice - SymbolInfoDouble(_Symbol, SYMBOL_ASK) > Min_Profit_To_Trail_Points * point) { //--- Check conditions
                  obj_Trade.PositionModify(ticket, newSL, tp);       //--- Modify position
               }
            }
         }
      }
   }
}

//--- Call the function per tick in the "OnTick" event handler

if (TrailingType == Trailing_Points && PositionsTotal() > 0) { //--- Check trailing
   ApplyPointsTrailing();                                      //--- Apply trailing
}

ApplyPointsTrailing関数は、ポイントベースのトレーリングストップを有効化した際にストップロスを調整するために定義します。銘柄の1ポイント値を_Pointでpointに代入します。その後、PositionsTotalですべてのポジションを逆順ループし、修正中のインデックス問題を回避します。各ポジションのチケット有効性はPositionGetTicketで確認します。銘柄が一致するポジション(PositionGetStringPOSITION_SYMBOLを確認)かつマジックナンバーが一致する場合(PositionGetIntegerでPOSITION_MAGICを確認)、現在のストップロスはPositionGetDoubleでPOSITION_SLから取得、テイクプロフィットはPOSITION_TP、オープン価格はPOSITION_PRICE_OPEN、チケット番号はPOSITION_TICKETで取得します。買いポジション(POSITION_TYPE_BUY)の場合、現在のBid(SYMBOL_BID)をSymbolInfoDoubleで取得し、Trailing_Stop_Points × pointを引いて新しいストップロスを計算し、銘柄の小数桁に正規化します。この新しいストップロスが既存ストップロスを上回り、かつ利益(Bid − Open)がMin_Profit_To_Trail_Points × pointを超える場合、obj_Trade.PositionModifyを用いて新しいストップロスを適用し、テイクプロフィットは変更せずに更新します。

売りポジション(POSITION_TYPE_SELL)についても同様に、新しいストップロスを計算し、現在のストップロスを下回り、利益(Open − Ask)が最小閾値を満たす場合にポジションを更新します。最後に、OnTick関数内でTrailingTypeがTrailing_Pointsに設定され、PositionsTotalでポジションが存在する場合、ApplyPointsTrailingを呼び出して各ティックでトレーリングを適用します。ここで、作成したオブジェクトを初期化解除時に削除して管理します。

//+------------------------------------------------------------------+
//| EA Stop Function                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int code) {
   ObjectDelete(ChartID(), maxLevelObj);                          //--- Delete max level
   ObjectDelete(ChartID(), minLevelObj);                          //--- Delete min level
   ObjectDelete(ChartID(), maxTextObj);                           //--- Delete max text
   ObjectDelete(ChartID(), minTextObj);                           //--- Delete min text
   // Clean dynamic rects and texts
   ObjectsDeleteAll(ChartID(), "RangeRectangle_", OBJ_RECTANGLE); //--- Delete range rects
   ObjectsDeleteAll(ChartID(), "ManipRectangle_", OBJ_RECTANGLE); //--- Delete manip rects
   ObjectsDeleteAll(ChartID(), "DistribRectangle_", OBJ_RECTANGLE); //--- Delete distrib rects
   ObjectsDeleteAll(ChartID(), "AccumText_", OBJ_TEXT);           //--- Delete accum texts
   ObjectsDeleteAll(ChartID(), "ManipText_", OBJ_TEXT);           //--- Delete manip texts
   ObjectsDeleteAll(ChartID(), "DistribText_", OBJ_TEXT);         //--- Delete distrib texts
}

OnDeinitイベントハンドラでは、プログラムがチャートから削除されたり終了した際に実行されます。まず、静的に作成したチャートオブジェクトを個別に削除します。ChartIDで現在のチャート識別子を指定し、最大レンジ水平線はmaxLevelObj、最小レンジ水平線はminLevelObj、CRT高値ラベルはmaxTextObj、CRT安値ラベルはminTextObjをObjectDeleteで削除します。

次に、動的に生成されたオブジェクトについてはObjectsDeleteAllを用いてチャート上の対象オブジェクトをまとめて削除します。蓄積フェーズ用の「RangeRectangle_」で始まるOBJ_RECTANGLE、操作フェーズ用のManipRectangle_、分配フェーズ用のDistribRectangle_をすべて削除し、さらにテキストオブジェクトも「AccumText_」(蓄積ラベル)、「ManipText_」(操作ラベル)、「DistribText_」(分配ラベル)で始まるOBJ_TEXT をすべて削除します。これにより、残留する可視要素を残さず完全にクリーンアップされます。コンパイルすると、トレーリングストップが有効な状態で次の結果が得られます。

トレーリングストップを有効にした場合の最終結果

画像から、必要に応じてトレーリングストップを適用してポジションを管理できていることが確認できます。残っている作業は、このプログラムのバックテストをおこなうことです。バックテストについては次のセクションで扱います。



バックテスト

徹底的なバックテストによって、次の結果が得られました。

バックテストグラフ

グラフ

バックテストレポート

レポート


結論

本記事ではMQL5ローソク足レンジ理論(CRT)取引システムを構築しました。このシステムは、指定された時間枠で蓄積レンジを特定し、操作深度フィルタを用いたブレイク検出、バーのクローズによる反転確認、そして分配フェーズでの取引実行をおこないます。ストップロスおよびテイクプロフィットはリスクリワード比に基づき、動的または固定で設定可能です。

免責条項:本記事は教育目的のみを意図したものです。取引には重大な財務リスクが伴い、市場の変動によって損失が生じる可能性があります。本プログラムを実際の市場で運用する前に、十分なバックテストと慎重なリスク管理が不可欠です。

蓄積、操作、分配(AMD)の各フェーズを組み込んだCRT戦略により、反転のチャンスを捉えたトレードが可能となり、さらなる最適化の余地を残した状態で取引に臨むことができます。取引をお楽しみください。

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

添付されたファイル |
最後のコメント | ディスカッションに移動 (6)
Stanislav Korotky
Stanislav Korotky | 21 11月 2025 において 14:11
新しい蓄積フェーズの開始をどのように検出するのか説明していない。
Allan Munene Mutiiria
Allan Munene Mutiiria | 22 11月 2025 において 06:18
Christian Gomez #:
ありがとう。
ありがとう
Allan Munene Mutiiria
Allan Munene Mutiiria | 22 11月 2025 において 06:24
Stanislav Korotky #:
あなたは、それぞれの新しい蓄積段階の開始がどのように検出されるかを説明していませんでした。

それは可視化されたようにろうそくの範囲です。

      double prevMax = iHigh(_Symbol, RangeTF, 1);                //--- 前の高値を取得する
      double prevMin = iLow(_Symbol, RangeTF, 1);                 //--- 前安値を取得する
      double prevStart = iOpen(_Symbol, RangeTF, 1);              //--- 前のオープンを取得する
      double prevEnd = iClose(_Symbol, RangeTF, 1);               //--- 前のクローズを取得する
      rangeMax = prevMax;                                         //--- 範囲の最大値を設定する
Stanislav Korotky
Stanislav Korotky | 22 11月 2025 において 18:21
Allan Munene Mutiiria #:

これが可視化されたローソク足の範囲だ。

これは質問の答えになりません - どのように蓄積フェーズ(チャートの異なるセクションでフェーズが何度も発生するため、それぞれ、すべて)の開始を見つけるのですか?これは時間の問題であり、価格帯の問題ではない。視覚化についても同様である。

Juvenille Emperor Limited
Eleni Anna Branou | 24 11月 2025 において 08:12
このトピックに関係のないコメントは、「オフトピック・ポスト」に移動しました。
MetaTrader 5機械学習の設計図(第6回):実務で使えるキャッシュシステムの設計 MetaTrader 5機械学習の設計図(第6回):実務で使えるキャッシュシステムの設計
進捗バーを眺めるだけで、取引戦略のテストに時間を浪費していませんか。従来のキャッシュ手法では金融機械学習には対応できず、計算の無駄や再実行によるフラストレーションに悩まされます。私たちは、金融データ特有の課題、時間的依存関係、複雑なデータ構造、そして先読みバイアスのリスクを理解した洗練されたキャッシュアーキテクチャを設計しました。この三層構造のシステムにより、計算速度は劇的に向上し、古い結果の自動無効化やコストの高いデータリークの防止も可能です。もう計算待ちに時間を費やす必要はありません。市場が要求するペースで、迅速に反復作業をおこなえます。
ケンドールのタウ係数と距離相関を用いたVGTの市場ポジショニング分析コード ケンドールのタウ係数と距離相関を用いたVGTの市場ポジショニング分析コード
この記事では、相補的なインジケーターのペアをどのように活用して、過去5年間のVanguard Information Technology Index Fund ETF (VGT)を分析できるかを検討します。具体的には、ケンドールのタウ係数と距離相関という2つの指標を用い、VGTの取引に適したインジケーターの組み合わせだけでなく、それら2つのインジケーターに対するシグナルパターンの最適なペアリングも選定することを目指します。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
プライスアクション分析ツールキットの開発(第51回):ローソク足パターン発見のための革新的なチャート検索技術 プライスアクション分析ツールキットの開発(第51回):ローソク足パターン発見のための革新的なチャート検索技術
本記事は、アルゴリズムトレーダー、クオンツ分析担当者、そしてMQL5開発者を対象に、ローソク足パターン認識の理解を深めるための実践的な実装方法を紹介することを目的としています。本記事では、MetaTrader 5向けのCandlePatternSearch.mq5エキスパートアドバイザー(EA)を通じて、古典的なローソク足パターンの検出、可視化、モニタリングをおこなうための完全なフレームワークを詳しく解説します。コードの逐次解説に加え、アーキテクチャ設計、パターン検出ロジック、GUI統合、アラート機能についても説明し、従来のプライスアクション分析を効率的に自動化する方法を示します。