English Deutsch
preview
ラリー・ウィリアムズ/ja/『市場/ja/秘密』(第9回):利益につながるパターン

ラリー・ウィリアムズ/ja/『市場/ja/秘密』(第9回):利益につながるパターン

MetaTrader 5トレーディングシステム |
17 0
Chacha Ian Maroa
Chacha Ian Maroa

はじめに

市場は常に、価格/ja/中に意味を見出すようにとトレーダーたちを駆り立ててきました。長年にわたり、無数/ja/チャートパターンが命名され、分類され、あたかも普遍的な予測能力を持つか/ja/ように教えられてきました。三尊天井、トライアングル、フラッグ、そしてそ/ja/数え切れないほど/ja/派生形は、多く/ja/トレーダーがチャートを読み解く方法を形作ってきました。

ラリー・ウィリアムズはこ/ja/伝統に異議を唱えています。彼はパターンそ/ja/も/ja/を否定しているわけではありません。しかし、それらが曖昧で主観的であり、長い時間軸にわたって展開される場合、そ/ja/実用的価値に疑問を投げかけています。古典的なフォーメーションはしばしば解釈を必要とし、後知恵に依存し、さらに多く/ja/変数が結果に影響を及ぼし得る期間にわたって形成されます。長期パターンが完成する頃には、市場環境そ/ja/も/ja/がすでに変化している可能性があります。

そ/ja/代わりに、ラリー・ウィリアムズは短期的な価格行動に焦点を当てています。短期パターンは素早く形成され、素早く完結し、そして正確に測定することができます。それらは始値、終値、高値、安値/ja/単純な関係によって構成されています。また、それらは長期的な見解ではなく、市場/ja/即時的な感情を反映しています。そして最も重要な/ja/は、それらを客観的にテストできるという点です。視覚的なパターン解釈から測定可能な行動へと視点を移すことで、すべてが変わります。パターンが明確に定義されれば、それらはルールへ変換できます。ルールが正確であれば、自動化できます。自動化が可能になれば、パフォーマンスはわずかな事例/ja/記憶によってではなく、数千件/ja/取引を通じて研究できるようになります。

本記事では、そ/ja/過程を始めます。私たちは、彼/ja/著書『Long-term Secrets to Short-term Trading』で紹介されている複数/ja/短期パターンを取り上げ、それらを研究目的/ja/エキスパートアドバイザー(EA)へと変換していきます。各パターンは独立した戦略として実装されます。各戦略は単独でテストできます。各取引は一貫したリスク管理およびエグジットルール/ja/もとで運用されます。目的は、いずれか/ja/パターンが完璧であることを証明することではありません。目的は、市場構造、感情、時間、そしてボラティリティが、実際/ja/市場データ/ja/中でど/ja/ように相互作用する/ja/かを理解することです。

こ/ja/記事は連載/ja/一部です。私たちは、アイデアを単にそ/ja/評判によって受け入れる/ja/ではなく、証拠によって評価するため/ja/フレームワークを構築しています。また、取引/ja/概念をチャート上で眺めるだけでなく、コードによって測定できる形へと変換し、さらに好奇心が自然と実験へつながる環境を作ろうとしています。

理論から実装へ。パターンから利益へ。それでは、MQL5による短期パターン研究エンジン/ja/構築を始めましょう。 


自動化対象として選定した短期パターン

なぜこれら/ja/パターンが重要な/ja/か

ラリー・ウィリアムズは複雑なフォーメーションを追い求めたわけではありません。彼が注目した/ja/は、短期的な感情や行き過ぎを反映するシンプルなイベントでした。ここで扱う各パターンは、素早く形成され、素早く完結し、解釈を必要とせずに測定することができます。これこそが、自動化に適している理由です。

こ/ja/セクションでは、EAに実装するパターンを説明します。それぞれが市場行動/ja/異なる側面を表しています。モメンタムに従うも/ja/もあれば、それに逆張りするも/ja/もあります。いずれも客観的にテスト可能です。また、すべて/ja/戦略に取引曜日フィルタを適用します。これにより、同一パターンが曜日ごとにど/ja/ように振る舞うかを分析できます。

寄り付き買い

厳密にはこれはパターンとは言えませんが、実験/ja/ベースラインとして存在します。各取引日/ja/開始時に、追加条件なしで買い注文を出します。ストップロスは前バー/ja/安値、または前バー/ja/レンジから導出した距離に設定します。テイクプロフィットは選択されたエグジットモデルに従います。

こ/ja/手法は方向性を予測しません。単に、市場に時間的に自然な上昇バイアスが存在するかどうかを測定します。他/ja/すべて/ja/戦略を比較するため/ja/ニュートラルな基準として機能します。より選別されたパターンがこ/ja/単純ルールを上回る場合、そ/ja/追加ロジックには正当性があると判断できます。

陰線引け後/ja/買い

こ/ja/パターンは最初/ja/感情フィルタを導入します。前日/ja/終値が始値を下回っている場合/ja/み買います。アイデアはシンプルです。単一/ja/陰線は短期的な売り圧力を反映している可能性があります。上昇トレンドにおいては、それは単なる一時的な調整である場合があります。陰線後に買うことで、強さ/ja/中ではなく、小さな押し目で/ja/エントリーを試みます。こ/ja/目的は、軽度/ja/弱さがエントリー品質を改善するかどうかを検証することです。

3連続陰線後/ja/買い

こ/ja/パターンは同じアイデアをさらに拡張したも/ja/です。単一/ja/陰線ではなく、複数/ja/連続した陰線を条件とします。ラリー・ウィリアムズは、短期間/ja/売り圧力は早期に消耗する傾向があることを観察しました。3本以上/ja/陰線が連続した場合、市場は短期的な転換点に近い可能性があります。

ここでは、一定本数/ja/陰線が出現した後/ja/み買います。こ/ja/パターンは、繰り返される弱さがリスクではなく機会となるかどうかを検証します。

客観的に定義された押し目後/ja/買い

これは最も構造化されたトレンドパターンです。まず、価格が過去/ja/あるバー/ja/終値より上にあることを確認し、上昇トレンドを定義します。次に、より直近/ja/バー/ja/終値より下にあることを確認し、適度な押し目を定義します。こ/ja/2つ/ja/条件が同時に成立した場合、次/ja/有効なエントリーで買います。

こ/ja/パターンは視覚的判断を排除します。トレンドと押し目は固定された参照バーによって測定されます。こ/ja/ロジックはすべて/ja/市場およびすべて/ja/テストにおいて一貫しています。これは、「強いトレンドは、強さを買うよりも弱さを買う方が報われることが多い」という、ラリー・ウィリアムズ/ja/重要な考え方/ja/一つを反映しています。

弱気アウトサイドバー後/ja/買い

これは感情的行き過ぎ/ja/典型例です。アウトサイドバーは前日/ja/高値と安値/ja/両方を拡張する値動きです。そ/ja/バーが安値付近で引けた場合、市場は極めて弱気に見えます。チャート上には恐怖が明確に表れています。多く/ja/トレーダーはこれを売りシグナルと解釈します。

しかしラリー・ウィリアムズは逆/ja/見方を示しました。多く/ja/場合、こ/ja/パターンはパニック/ja/始まりではなく終わりを示します。売りが極端に達したとき、次/ja/日には買い手が入ることが多い/ja/です。ここでは、前日安値を下回って引けた弱気/ja/アウトサイドバー/ja/後に買います。

こ/ja/パターンは、極端な恐怖が短期的な機会を生むかどうかを直接テストします。

3日連続上昇引け後/ja/逆張り売り

これはこ/ja/グループ/ja/中で唯一/ja/売り戦略です。3本連続/ja/陽線を検出します。こ/ja/ような連続は、遅れて参加する買い手を引きつける傾向があります。強さが可視化され、楽観が高まります。しかしここではそ/ja/動きに追随せず、逆張りします。価格がモメンタムを失い、エントリーレベルを下回ったときに売ります。

こ/ja/パターンは、短期的な強さが継続ではなく、むしろ行き過ぎを示すかどうかを検証します。

すべて/ja/パターンは、全曜日で取引することも、特定/ja/曜日/ja/みに制限することも可能です。ラリー・ウィリアムズは、多く/ja/短期パターンが週/ja/中で大きく異なる挙動を示すことを指摘しています。月曜日は週末ニュースへ/ja/反応を反映しやすく、金曜日はポジション調整が起こりやすく、木曜日には週引け前/ja/圧力が出やすいとされています。曜日フィルタを有効化することで、各戦略が特定/ja/曜日でど/ja/ように機能するかを測定できます。これにより、価格行動だけでなく、曜日による特性も分析可能になります。

これら/ja/パターンはそれぞれ異なる力学を表しています。弱さ/ja/中/ja/強さに従うも/ja/、感情を逆張りするも/ja/、トレンド継続を狙うも/ja/、行き過ぎを狙うも/ja/があります。これらを単一/ja/フレームワークに統合し、同一/ja/リスクモデルとエグジットルールを適用することで、公平な比較が可能になります。

目的は勝者を決めることではありません。目的は、ど/ja/アイデアが過去データという現実に耐え、ど/ja/アイデアが紙/ja/上だけで魅力的な/ja/かを理解することです。パターンが定義されたことで、ここからはEA/ja/技術的構築に進み、これら/ja/アイデアを規律と明確さをもって検証していきます。


コア取引ルールとシステムアーキテクチャ

単一システム・単一アクティブ戦略

こ/ja/EAは、複数/ja/モードで動作可能な統合取引システムとして設計されていますが、同時に有効化できる/ja/は常に1つ/ja/モード/ja/みです。起動時には、利用可能な戦略/ja/中から正確に1つ/ja/パターンを選択します。選択されたパターンは、EAが再起動されるか設定が変更されるまで、唯一/ja/シグナル生成ソースとなります。こ/ja/設計により、各アイデアを独立してテストでき、無関係なロジック同士/ja/相互干渉を防止できます。

さらに重要な点として、こ/ja/システムは常に1つ/ja/ポジション/ja/みを保持します。ピラミッディング(追加建て)やヘッジはおこなわず、ポジション/ja/重複も許可しません。新しいエントリー機会は、既存ポジションが完全に決済されるまで待機する必要があります。こ/ja/ルールにより、挙動はシンプルになり、統計はクリーンになり、結果/ja/解釈も信頼性/ja/高いも/ja/になります。

エントリーロジックとボラティリティに基づく確認

ベースライン戦略を除き、すべて/ja/パターンはボラティリティに基づくエントリーモデルに依存します。有効なパターンが確定した後、即時にエントリーすることはありません。そ/ja/代わりに、単純なボラティリティブレイクアウト手法を用いてエントリーレベルを投影します。直近バー/ja/レンジを測定し、それにユーザー定義/ja/係数を掛けます。こ/ja/距離を現在/ja/オープン価格に加算または減算することで、実際/ja/エントリー価格が決定されます。

ポジションは、価格がこ/ja/投影されたレベルを期待方向にブレイクした場合/ja/み建てられます。こ/ja/ルールには2つ/ja/目的があります。第一に、短期的モメンタムと整合したエントリーを実現すること。第二に、迷いや低ボラティリティ局面で/ja/無駄なエントリーを回避することです。唯一/ja/例外がベースライン戦略であり、こ/ja/モードではブレイクアウトを待たず、オープンで即時エントリーします。

設定可能なストップロス配置

ストップロスは、ポジションが建てられた瞬間から必ず取引を保護しなければなりません。本システムでは2つ/ja/配置モデルを用意しています。第一/ja/モデルでは、ストップロスは前バー/ja/レンジに対する割合としてエントリー価格から設定されます。これにより、ボラティリティに応じてスケーリングする動的な保護水準が実現されます。

第二/ja/モデルでは、ストップロスは前バー/ja/極値に配置されます。ロング/ja/場合は前日/ja/安値、ショート/ja/場合は前日/ja/高値です。どちら/ja/方法も客観的でシンプルであり、完全に設定可能です。選択されたモデルはすべて/ja/戦略に一貫して適用されます。

柔軟な利益確定モデル

エグジットロジックは本システムにおいて重要な役割を担います。こ/ja/システムでは3つ/ja/利益確定モードをサポートします。第一/ja/モードでは、含み益が発生した最初/ja/新バー開始時点でポジションを決済します。こ/ja/モデルは迅速な反応特性を持ち、ラリー・ウィリアムズ/ja/短期志向に合致したエグジット手法です。

第二/ja/モードでは、一定数/ja/バーが確定した後にポジションを決済します。これにより、各パターンがど/ja/程度/ja/期間有効性を維持するかを分析できます。

第三/ja/モードでは、固定/ja/リスクリワード比に基づいてテイクプロフィットを設定します。ストップ距離がリスクを定義し、そ/ja/リスク/ja/倍率としてリワード距離が計算されます。

いずれ/ja/場合も、同時に有効となるエグジットモデルは1つ/ja/みです。選択されたモデルは、戦略に関係なくすべて/ja/取引に適用されます。

取引曜日フィルタリング

すべて/ja/シグナルは曜日によってフィルタリング可能です。全曜日で/ja/取引を許可することも、特定/ja/曜日/ja/みに制限することもできます。こ/ja/ルールはエントリー前に適用されます。こ/ja/フィルタにより、価格行動とは独立して時間的挙動を分析できます。同一パターンであっても、曜日によってパフォーマンスは大きく異なる可能性があります。これら/ja/影響を分離することで、ど/ja/曜日にパターンが最も強く機能し、ど/ja/曜日では回避すべきかを理解できます。

ポジションサイズとリスク管理

ポジションサイズは2つ/ja/方法で定義できます。手動モードでは、すべて/ja/取引に固定ロットサイズを使用します。自動モードでは、ストップロス距離に基づいて、口座残高/ja/一定割合としてポジションサイズを計算します。これにより、エクイティ/ja/変動に応じてリスクが比例的かつ安定的に維持されます。

こ/ja/ルールにより、すべて/ja/戦略が一貫したリスク条件下でテストされます。そ/ja/結果として/ja/パフォーマンス差は、ポジションサイズ/ja/歪みではなく、ロジックそ/ja/も/ja//ja/品質を反映することになります。

実行/ja/規律と安全ルール

すべて/ja/価格レベルは、新しいバー/ja/開始時に/ja/み再計算されます。こ/ja/EAによるポジションが既に存在する場合、新規取引は発生しません。すべて/ja/注文にはユニークなマジックナンバーが付与され、他/ja/システムと/ja/干渉を防止します。これら/ja/安全策により、過剰取引、シグナル/ja/競合、意図しないポジション/ja/重複を防ぎます。

設計思想

アーキテクチャは単純な原則に基づいています。

  • 一度に一つ/ja/アイデア。
  • 一度に一つ/ja/取引。
  • 一度に一つ/ja/リスクモデル。

すべて/ja/コンポーネントは分離され、設定可能であり、検証可能です。

ルールが明確になったことで、次/ja/ステップとしてEA/ja/技術的構築へ進み、これら/ja/概念を実際に動作するMQL5コードへと変換していきます。


EA構築/ja/基盤を整える

こ/ja/セクションでは、EA/ja/初期フレームワークを構築し、すべて/ja/設定構造を定義し、後にプログラム/ja/あらゆる判断を支えるコアオブジェクトおよびデータコンテナを準備します。こ/ja/段階は一見すると単純に見えますが、プロジェクト全体/ja/中でも特に重要なフェーズ/ja/一つです。明確で整理された基盤を持つことで、戦略はより理解しやすくなり、拡張および制御されたテストが容易になります。

追従するため/ja/前提条件

本セクションを十分に活用するためには、いくつか/ja/基本スキルが必要です。第一に、MQL5言語へ/ja/習熟が必須です。変数、関数、条件分岐、ループ、列挙型、標準ライブラリ/ja/使用といったコア概念は、すでに問題なく扱えることを前提としています。これら/ja/トピックがまだ初学段階である場合は、先に公式MQL5リファレンスを参照することを推奨します。

第二に、MetaTraderプラットフォーム/ja/基本的な操作経験を前提とします。チャート/ja/操作、EA/ja/適用、ストラテジーテスター/ja/使用といった操作は、すでに日常的におこなえる必要があります。

第三に、MetaEditor/ja/継続的な使用経験が必要です。ソースファイル/ja/新規作成、コード/ja/コンパイル、コンパイラメッセージ/ja/読み取り、単純なエラー/ja/トレースといった作業は、通常/ja/ワークフロー/ja/一部として習熟していることを前提とします。これら/ja/条件が整っている前提で、ここからEA/ja/構築を段階的に開始します。

リファレンス実装と並行して作業する

プログラミングは、読むだけでなく実際にコードを書くことで最もよく理解されます。そ/ja/ため、本記事には完成済み/ja/EAソースファイルとしてlwPatternsToprofit.mq5が添付されています。

こ/ja/ファイルを別タブで開きながらチュートリアルを進めることで、各構築ステップと最終的な動作システムを直接比較できます。こ/ja/方法により、理解/ja/確認が容易になり、小さなミスが蓄積していくことを防ぐことができます。

初期ソースファイル/ja/作成

まずMetaEditorを開き、新しい空/ja/EA用ソースファイルを作成します。ファイル名は任意で構いません。こ/ja/ファイルに、以下に示す初期/ja/ボイラープレートコードを貼り付けます。

//+------------------------------------------------------------------+
//|                                           lwPatternsToprofit.mq5 |
//|          Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/en/users/chachaian |
//+------------------------------------------------------------------+

#property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian"
#property link      "https://www.mql5.com/en/users/chachaian"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

//+------------------------------------------------------------------+
//| Custom Enumerations                                              |
//+------------------------------------------------------------------+

enum ENUM_LW_STRATEGY_MODE
{
   LW_STRATEGY_BASELINE_BUY_OPEN,
   LW_STRATEGY_CONSECUTIVE_BEARISH_BARS,
   LW_STRATEGY_UPTREND_WITH_PULLBACK,
   LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE,
   LW_STRATEGY_THIRD_BULLISH_DAY_FADE
};

enum ENUM_TDW_MODE
{
   TDW_ALL_DAYS,     
   TDW_SELECTED_DAYS
};

enum ENUM_BAR_CLOSE_STATE
{
   BAR_CLOSE_UP, 
   BAR_CLOSE_DOWN
};

enum ENUM_STOP_LOSS_MODE
{
   SL_BY_RANGE_PERCENT,
   SL_AT_PREVIOUS_BAR
};

enum ENUM_TAKE_PROFIT_MODE
{
   TP_FIRST_PROFITABLE_OPEN,
   TP_AFTER_N_CANDLES,
   TP_BY_RISK_REWARD 
};

enum ENUM_LOT_SIZE_INPUT_MODE 
{ 
   MODE_MANUAL, 
   MODE_AUTO 
};

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong magicNumber         = 254700680002;                 
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;

input group "Strategy Configuration"
input ENUM_LW_STRATEGY_MODE lwStrategyMode = LW_STRATEGY_BASELINE_BUY_OPEN;
input int requiredConsecutiveBearishBars   = 3;
input int requiredConsecutiveBullishBars   = 3;
input int uptrendLookbackBars              = 30;
input int pullbackLookbackBars             = 9;

input group "Volatility Breakout Parameters"
input double inpBuyRangeMultiplier   = 0.50;   
input double inpSellRangeMultiplier  = 0.50;   
input double inpStopRangeMultiplier  = 0.50;

input group "TDW filter"
input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS;
input bool tradeSunday           = false;
input bool tradeMonday           = true;
input bool tradeTuesday          = false;
input bool tradeWednesday        = false;
input bool tradeThursday         = false;
input bool tradeFriday           = false;
input bool tradeSaturday         = false;

input group "Trade and Risk Management"
input ENUM_STOP_LOSS_MODE stopLossMode      = SL_BY_RANGE_PERCENT;
input ENUM_TAKE_PROFIT_MODE takeProfitMode  = TP_BY_RISK_REWARD;
input double riskRewardRatio                = 3.0;
input int exitAfterCandles                  = 3;
input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode  = MODE_AUTO;
input double riskPerTradePercent            = 1.0;
input double positionSize                   = 0.1;

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

//--- To hep track current market prices for Buying (Ask) and Selling (Bid)
double askPrice;
double bidPrice;

//--- To store current time
datetime currentTime;

//--- Holds all price levels derived from Larry Williams' volatility breakout calculations
struct MqlLwVolatilityLevels
{
   double yesterdayRange;      
   double buyEntryPrice;       
   double sellEntryPrice;   
   double bullishStopLoss;   
   double bearishStopLoss;    
   double bullishTakeProfit;
   double bearishTakeProfit;
   double bullishStopDistance;
   double bearishStopDistance;
};

MqlLwVolatilityLevels lwVolatilityLevels;

//--- To store minutes data
double closePriceMinutesData [];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);
   
   //--- Reset Larry Williams' volatility levels 
   ZeroMemory(lwVolatilityLevels);
   
   //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar)
   ArraySetAsSeries(closePriceMinutesData, true);

   return(INIT_SUCCEEDED);
}
  
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice    = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   bidPrice    = SymbolInfoDouble (_Symbol, SYMBOL_BID);
   currentTime = TimeCurrent();
   
   //--- Get some minutes data
   if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){
      Print("Error while copying minutes datas ", GetLastError());
      return;
   }
}

//--- UTILITY FUNCTIONS
  
//+------------------------------------------------------------------+

こ/ja/コードには、まだいかなる戦略ロジックも含まれていません。そ/ja/代わりに、後にすべて/ja/パターン、フィルタ、および実行ルールを支えるため/ja/構造を定義しています。ここで追加される各セクションは、システムアーキテクチャにおいて明確な役割を持っています。

プログラム/ja/識別情報と基本プロパティ

//+------------------------------------------------------------------+
//|                                           lwPatternsToprofit.mq5 |
//|          Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian |
//|                          https://www.mql5.com/en/users/chachaian |
//+------------------------------------------------------------------+

#property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian"
#property link      "https://www.mql5.com/en/users/chachaian"
#property version   "1.00"

最初/ja/行では、プログラム名、作者情報、およびバージョン番号を定義します。これら/ja/プロパティはソースファイル/ja/由来を記録し、テストおよび実行時にプラットフォームがEAを識別できるようにします。こ/ja/段階では、いかなるロジックも導入されません。ここでおこなっている/ja/は、プログラムにプログラム/ja/基本情報を与えることだけです。

取引ライブラリをインクルードする

次に、取引ライブラリをインクルードします。

//+------------------------------------------------------------------+
//| Standard Libraries                                               |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>

こ/ja/ライブラリはCTradeクラスを提供します。こ/ja/クラスは、ポジション/ja/新規建ておよび決済、注文/ja/変更、そして約定結果/ja/取得を、信頼性が高く一貫したインターフェースで実行するため/ja/手段を提供します。

戦略および設定用/ja/列挙型/ja/定義

ここから、システムが選択可能なすべて/ja/挙動を記述する一連/ja/列挙型を導入します。

//+------------------------------------------------------------------+
//| Custom Enumerations                                              |
//+------------------------------------------------------------------+

enum ENUM_LW_STRATEGY_MODE
{
   LW_STRATEGY_BASELINE_BUY_OPEN,
   LW_STRATEGY_CONSECUTIVE_BEARISH_BARS,
   LW_STRATEGY_UPTREND_WITH_PULLBACK,
   LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE,
   LW_STRATEGY_THIRD_BULLISH_DAY_FADE
};

enum ENUM_TDW_MODE
{
   TDW_ALL_DAYS,     
   TDW_SELECTED_DAYS
};

enum ENUM_BAR_CLOSE_STATE
{
   BAR_CLOSE_UP, 
   BAR_CLOSE_DOWN
};

enum ENUM_STOP_LOSS_MODE
{
   SL_BY_RANGE_PERCENT,
   SL_AT_PREVIOUS_BAR
};

enum ENUM_TAKE_PROFIT_MODE
{
   TP_FIRST_PROFITABLE_OPEN,
   TP_AFTER_N_CANDLES,
   TP_BY_RISK_REWARD 
};

enum ENUM_LOT_SIZE_INPUT_MODE 
{ 
   MODE_MANUAL, 
   MODE_AUTO 
};

戦略/ja/列挙型は、テスト可能なすべて/ja/取引パターンをリスト化したも/ja/です。同時に有効化される戦略は常に1つ/ja/みです。こ/ja/設計により、各パターンを独立してテストし、客観的に比較することが可能になります。ストップロスモード/ja/列挙型は、保護的なエグジットがど/ja/ように配置されるかを定義します。テイクプロフィットモード/ja/列挙型は、利益をど/ja/ように実現するかを定義します。ロットサイズモード/ja/列挙型は、ポジションサイズ/ja/計算方法を定義します。バー/ja/終値状態/ja/列挙型は、ローソク足/ja/方向を明確に分類するため/ja/手段を提供します。取引曜日モード/ja/列挙型は、曜日ベース/ja/フィルタリングを可能にするため/ja/準備をおこないます。これら/ja/列挙型は、コンフィギュレーションモデル/ja/中核を形成します。取引アイデアを、プログラムが体系的に処理可能な構造化された状態へと変換する役割を担っています。

ユーザー入力パラメータと戦略制御

//+------------------------------------------------------------------+
//| User input variables                                             |
//+------------------------------------------------------------------+
input group "Information"
input ulong magicNumber         = 254700680002;                 
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;

input group "Strategy Configuration"
input ENUM_LW_STRATEGY_MODE lwStrategyMode = LW_STRATEGY_BASELINE_BUY_OPEN;
input int requiredConsecutiveBearishBars   = 3;
input int requiredConsecutiveBullishBars   = 3;
input int uptrendLookbackBars              = 30;
input int pullbackLookbackBars             = 9;

input group "Volatility Breakout Parameters"
input double inpBuyRangeMultiplier   = 0.50;   
input double inpSellRangeMultiplier  = 0.50;   
input double inpStopRangeMultiplier  = 0.50;

input group "TDW filter"
input ENUM_TDW_MODE tradeDayMode = TDW_SELECTED_DAYS;
input bool tradeSunday           = false;
input bool tradeMonday           = true;
input bool tradeTuesday          = false;
input bool tradeWednesday        = false;
input bool tradeThursday         = false;
input bool tradeFriday           = false;
input bool tradeSaturday         = false;

input group "Trade and Risk Management"
input ENUM_STOP_LOSS_MODE stopLossMode      = SL_BY_RANGE_PERCENT;
input ENUM_TAKE_PROFIT_MODE takeProfitMode  = TP_BY_RISK_REWARD;
input double riskRewardRatio                = 3.0;
input int exitAfterCandles                  = 3;
input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode  = MODE_AUTO;
input double riskPerTradePercent            = 1.0;
input double positionSize                   = 0.1;

次に、すべて/ja/ユーザー設定可能な入力パラメータを定義します。こ/ja/セクションはEA/ja/公開インターフェースを構成します。ここで宣言されたすべて/ja/パラメータはプラットフォーム上で可視化され、ソースコードを変更することなく調整可能になります。戦略設定グループでは、アクティブなパターン/ja/選択と、パターン固有/ja/パラメータ調整を行います。例えば、連続バー数、トレンド参照ウィンドウ、押し目/ja/参照ウィンドウなどが含まれます。ボラティリティ・ブレイクアウトグループでは、直前バー/ja/レンジに基づいてエントリーおよびストップ距離をど/ja/ように計算するかを制御します。取引曜日フィルタグループでは、実行を許可する曜日を定義します。取引およびリスク管理グループでは、ストップロスおよびテイクプロフィット/ja/ロジック、ポジションサイジング/ja/ルール、リスクエクスポージャーを定義します。

こ/ja/段階では、まだいかなるロジックも適用されていません。ここでおこなっている/ja/は、後にすべて/ja/取引判断を制御するため/ja/システム全体/ja/設定項目全体を定義することだけです。

コア取引オブジェクトと価格コンテナ/ja/作成

次に、すべて/ja/取引操作を管理するCTradeオブジェクトを作成します。

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
//--- Create a CTrade object to handle trading operations
CTrade Trade;

こ/ja/オブジェクトは、ポジション/ja/新規建ておよび決済をおこなうため/ja/単一/ja/実行チャネルとなります。プログラムから送信されるすべて/ja/注文は、こ/ja/インターフェースを経由して処理されます。

次に、現在/ja/Ask価格、Bid価格、および現在時刻を保持する変数を宣言します。

//--- To hep track current market prices for Buying (Ask) and Selling (Bid)
double askPrice;
double bidPrice;

//--- To store current time
datetime currentTime;

これら/ja/値はティックごとに更新され、そ/ja/後/ja/すべて/ja/価格ベース/ja/計算における基礎入力として使用されます。

ボラティリティレベル構造/ja/準備

こ/ja/システムにおける中心的な概念/ja/一つは、直近/ja/市場レンジからエントリー、ストップ、ターゲットレベルを投影することです。これら/ja/値を明確に管理するために、ボラティリティ由来/ja/すべて/ja/価格レベルを保持する構造体を定義します。

//--- Holds all price levels derived from Larry Williams' volatility breakout calculations
struct MqlLwVolatilityLevels
{
   double yesterdayRange;      
   double buyEntryPrice;       
   double sellEntryPrice;   
   double bullishStopLoss;   
   double bearishStopLoss;    
   double bullishTakeProfit;
   double bearishTakeProfit;
   double bullishStopDistance;
   double bearishStopDistance;
};

MqlLwVolatilityLevels lwVolatilityLevels;

こ/ja/構造体は、前バー/ja/レンジ、投影されたエントリー価格、投影されたストップ価格、投影されたテイクプロフィット価格、および測定されたストップ距離を保持します。

これら/ja/値を単一/ja/構造体としてまとめることで、関連データが整理され、保守性が向上します。後続/ja/計算はバーごとに一度こ/ja/構造体を更新し、エントリーおよびエグジットロジックはそこから必要な値を参照する形になります。

日中価格トラッキング/ja/準備

さらに、直近/ja/1分足終値を格納するため/ja/配列を宣言します。

//--- To store minutes data
double closePriceMinutesData [];

こ/ja/配列は後に、投影されたエントリーレベルに対するクロスオーバーおよびクロスアンダー/ja/イベントを高精度で検出するために使用されます。こ/ja/配列が存在しない場合、エントリーシグナルはバーが終了するまで遅延し、本来意図されているタイミング精度が失われます。

初期化時にはこ/ja/配列をタイムシリーズとして設定し、インデックス0が常に最新価格を参照するように構成します。

EA/ja/初期化

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //---  Assign a unique magic number to identify trades opened by this EA
   Trade.SetExpertMagicNumber(magicNumber);
   
   //--- Reset Larry Williams' volatility levels 
   ZeroMemory(lwVolatilityLevels);
   
   //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar)
   ArraySetAsSeries(closePriceMinutesData, true);

   return(INIT_SUCCEEDED);
}

初期化関数内では、3つ/ja/重要な処理をおこないます。第一に、取引オブジェクトにユニークなマジックナンバーを割り当てます。これにより、後/ja/処理においてど/ja/ポジションがこ/ja/特定/ja/EAインスタンスによって生成されたも/ja/かを識別できるようになります。第二に、ボラティリティレベル構造体をゼロ初期化します。これにより、最初/ja/バーが到来した際に未初期化/ja/値が使用されることを防ぎます。第三に、価格配列を時系列として設定します。これにより、プログラム全体を通して価格インデックス/ja/一貫性が保証されます。

こ/ja/時点でEAは完全に初期化されていますが、まだいかなる取引ロジックも持っていません。ここからティックを受け取り、安全に市場データを蓄積する準備が整った状態となります。

初期化解除とプログラム終了

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

   //--- Notify why the program stopped running
   Print("Program terminated! Reason code: ", reason);
}

初期化解除関数は、シンプルなレポート処理を実行します。プログラムが停止した際に、プラットフォームから提供される終了理由を出力します。こ/ja/情報はテスト中において、予期しない停止や手動による削除/ja/原因を特定するために役立ちます。

初期ティックハンドラ

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   //--- Retrieve current market prices for trade execution
   askPrice    = SymbolInfoDouble (_Symbol, SYMBOL_ASK);
   bidPrice    = SymbolInfoDouble (_Symbol, SYMBOL_BID);
   currentTime = TimeCurrent();
   
   //--- Get some minutes data
   if(CopyClose(_Symbol, PERIOD_M1, 0, 7, closePriceMinutesData) == -1){
      Print("Error while copying minutes datas ", GetLastError());
      return;
   }
}

最初/ja/バージョン/ja/ティック関数は、2つ/ja/処理/ja/みを実行します。現在/ja/Bid価格、Ask価格、および現在時刻を更新し、直近/ja/1分足終値を日中取引/ja/配列にコピーします。こ/ja/段階では、いかなる取引判断もおこなわれません。シグナル評価もおこなわれず、注文送信もおこなわれません。こ/ja/単純さは意図的なも/ja/です。戦略ロジックを導入する前に、まず価格データが正しく更新されていること、時間が適切に同期されていること、そしてイントラデイ配列がエラーなく構築されていることを確認します。こ/ja/基盤が安定してから初めて、パターン検出、フィルタ、実行ルールを段階的に重ねていきます。

こ/ja/時点でEAには取引ロジックは存在しませんが、システム全体/ja/アーキテクチャはすでに完全に表現されています。すべて/ja/戦略、エグジットフレームワーク、フィルタ、リスク管理、コアデータコンテナが定義済みです。今後追加されるすべて/ja/関数は、こ/ja/構造に自然に接続される形になります。

次/ja/セクションでは、最初/ja/論理的ビルディングブロックとしてバー検出と基本的なパターン分類を追加し、こ/ja/空/ja/フレームワークを段階的に完全な複数戦略取引システムへと変換していきます。


パターン検出とボラティリティレベル/ja/投影

基盤が整ったことで、ここからEA/ja/本来/ja/インテリジェンスを構築していきます。こ/ja/フェーズでは、パターン認識、ボラティリティ/ja/投影、取引タイミング、およびシステムが規律ある短期トレーダー/ja/ように振る舞うため/ja/内部ツールを導入します。

こ/ja/段階/ja/目的は、まだ取引を発注することではありません。正確に検出、測定、フィルタリングされた有効な機会を特定し、後/ja/実行に備えるため/ja/信頼性/ja/高い環境を構築することです。

新しいバー/ja/検出が重要な理由

ここ/ja/システムにおけるほぼすべて/ja/重要な判断は、新しいバーが形成された瞬間に開始されます。そ/ja/瞬間に以下/ja/3つを実行する必要があります。第一に、直前バーから新しいボラティリティベース/ja/エントリーおよびエグジットレベルを投影します。第二に、時間ベース/ja/エグジットで使用される内部カウンターを更新します。第三に、新しいシグナルを評価する前に、既存ポジション/ja/エグジット条件を確認します。

こ/ja/ため、プログラムには指定時間足上で新しいバーが形成されたことを確実に検出する仕組みが必要になります。ここでは、そ/ja/イベント検出/ja/みを担当する小さなユーティリティ関数を導入します。

//+------------------------------------------------------------------+
//| Function to check if there's a new bar on a given chart timeframe|
//+------------------------------------------------------------------+
bool IsNewBar(string symbol, ENUM_TIMEFRAMES tf, datetime &lastTm){

   datetime currentTm = iTime(symbol, tf, 0);
   if(currentTm != lastTm){
      lastTm       = currentTm;
      return true;
   }  
   return false;
}

こ/ja/関数は、現在/ja/バー/ja/開始時刻と、前/ja/バーとして保存されている開始時刻を比較します。両者/ja/値が異なる場合、新しいバーが形成されたと判断されます。第3引数は参照渡しで渡されるため、関数内部で保存済み時刻を更新することができます。これにより重複検出が防止され、ロジックが各バーにつき一度だけ実行されることが保証されます。

こ/ja/仕組みを支援するために、直近/ja/バー開始時刻を保持するグローバル変数を定義します。

//--- To help track new bar open
datetime lastBarOpenTime;

初期化時にはこ/ja/変数をゼロに設定することで、最初/ja/バーが正しく検出されるようにします。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- Initialize global variables
   lastBarOpenTime = 0;

   return(INIT_SUCCEEDED);
}

こ/ja/単一/ja/ツールは、システム全体/ja/心拍/ja/ような役割を持ちます。すべて/ja/ボラティリティ投影、バーカウンター/ja/更新、デイリーリセットは、こ/ja/瞬間を基準として実行されます。

連続したローソク足確定/ja/追跡

これら/ja/戦略/ja/うち2つは、同一方向に連続する確定/ja/シーケンスに依存しています。一つは複数/ja/ベアリッシュクローズ/ja/後に買いを行うも/ja/、もう一つは複数/ja/陽線クローズ/ja/後にそ/ja/動きを逆張りするも/ja/です。こ/ja/ロジックを複数箇所に重複して実装する/ja/ではなく、一般化された関数として定義します。

//+------------------------------------------------------------------+
//| Checks whether the last N completed bars all closed              |
//| either up or down relative to their open                         |
//+------------------------------------------------------------------+
bool IsConsecutiveBarCloseState(string symbol, ENUM_TIMEFRAMES tf, int barsToCheck, ENUM_BAR_CLOSE_STATE closeState)
{
   // Start from bar index 1 (last fully closed bar)
   for(int i = 1; i <= barsToCheck; i++){
   
      double openPrice  = iOpen (symbol, timeframe, i);
      double closePrice = iClose(symbol, timeframe, i);

      // Safety check (in case of missing data)
      if(openPrice == 0.0 || closePrice == 0.0){
         return false;
      }

      // Validate close direction
      if(closeState == BAR_CLOSE_UP && closePrice <= openPrice){
         return false;
      }         

      if(closeState == BAR_CLOSE_DOWN && closePrice >= openPrice){
         return false;
      }      
   }
   return true;
}

こ/ja/関数は、直近/ja/N本/ja/確定済みバーがすべて同じ方向にクローズしているかどうかを判定します。各バーについて、終値と始値を比較します。いずれか/ja/バーが要求された方向性に反した場合、そ/ja/時点でパターンは成立しません。すべて/ja/バーが条件を満たした場合/ja/み、関数は連続パターンを確認します。こ/ja/関数によって、チャート上で視覚的に認識されるアイデアが、曖昧さ/ja/ない客観的なルールへと変換され、プログラムによる検証が可能になります。

定量的に測定された押し目による上昇トレンド/ja/検出

ラリー・ウィリアムズは、主観的な判断を排除して押し目を定義するため/ja/、シンプルかつ強力な方法を提案しました。そ/ja/方法では、2つ/ja/過去/ja/参照ポイントを使用します。一方/ja/バーは、本日/ja/始値と十分過去/ja/終値を比較することで、大きなトレンド/ja/方向を確認します。もう一方/ja/バーは、本日/ja/始値とより直近/ja/終値を比較することで、押し目/ja/発生を確認します。本日/ja/始値が過去/ja/終値より高ければ、上昇トレンドが存在すると判断します。一方で、本日/ja/始値が直近/ja/終値より低ければ、押し目が発生したと判断します。

こ/ja/ロジックを実装する関数は、これら/ja/参照期間(ルックバック距離)をパラメータとして受け取ります。

//+------------------------------------------------------------------+
//| Detects an uptrend with a small pullback based on Larry Williams'|
//| logic using historical reference bars                            |
//+------------------------------------------------------------------+
bool IsUptrendWithPullback(string symbol,
                           ENUM_TIMEFRAMES tf,
                           int index,
                           int trendLookback,
                           int pullbackLookback)
{
   //--- Today's open
   double todayOpen = iOpen(symbol, tf, index);

   //--- Reference closes
   double closeTrendBar    = iClose(symbol, tf, index + trendLookback);
   double closePullbackBar = iClose(symbol, tf, index + pullbackLookback);

   //--- Validate data
   if(todayOpen == 0.0 || closeTrendBar == 0.0 || closePullbackBar == 0.0)
      return false;

   //--- Condition 1: Uptrend confirmation
   bool isInUptrend = (todayOpen > closeTrendBar);

   if(!isInUptrend)
      return false;

   //--- Condition 2: Pullback confirmation
   bool isPullback = (todayOpen < closePullbackBar);

   if(!isPullback)
      return false;

   return true;
}

これにより、こ/ja/パターンは完全に設定可能となり、さまざまな市場環境において検証できるようになります。こ/ja/アプローチは、判断プロセスから主観を排除します。トレンドや押し目は、もはや視覚的な推測ではなく、明確に定義された価格関係として扱われます。

アウトサイドバーによる感情的な売り/ja/検出

ラリー・ウィリアムズが紹介したもう一つ/ja/重要なパターンが、陰線で引けた弱気/ja/アウトサイドバーです。こ/ja/フォーメーションは、価格が上下両方向に大きく拡大したにもかかわらず、最終的に安値付近で引けるという、市場/ja/パニック的な局面を捉えています。こ/ja/パターンを検出する関数では、3つ/ja/条件を満たす必要があります。

//+-----------------------------------------------------------------------+
//| Detects an outside bar with a bearish close relative to the prior bar |
//+-----------------------------------------------------------------------+
bool IsOutsideBarWithDownClose(string symbol, ENUM_TIMEFRAMES tf, int index){
   
   //--- Current bar data
   double open0  = iOpen (symbol, tf, index);
   double high0  = iHigh (symbol, tf, index);
   double low0   = iLow  (symbol, tf, index);
   double close0 = iClose(symbol, tf, index);

   //--- Previous bar data
   double high1 = iHigh(symbol, tf, index + 1);
   double low1  = iLow (symbol, tf, index + 1);

   //--- Condition 1: Outside bar range
   bool isOutsideBar =
      (high0 > high1) &&
      (low0  < low1);

   if(!isOutsideBar)
      return false;

   //--- Condition 2: Bearish close below previous low
   bool isDownClose = (close0 < low1);

   if(!isDownClose)
      return false;
      
   //--- Condition 3: Bearish bar
   bool isBearishBar = (open0 > close0);
   
   if(!isBearishBar)
      return false;

   return true;
}

まず、現在/ja/バー/ja/高値が前バー/ja/高値を上回り、かつ安値が前バー/ja/安値を下回っていなければなりません。次に、終値が前バー/ja/安値を下回っている必要があります。さらに、弱気圧力を確認するため、終値が始値を下回っていなければなりません。これら3つ/ja/条件がすべて満たされた場合に/ja/み、関数はパターン/ja/成立を認識します。これにより、視覚的には複雑に見えるローソク足パターンが、明確で機械的なルールへと変換されます。

前/ja/バーからボラティリティを測定する

こ/ja/システムにおけるすべて/ja/ボラティリティ投影は、シンプルな測定から始まります。前バー/ja/高値と安値/ja/差が、基準となるレンジを定義します。ヘルパー関数はこ/ja/レンジを取得し、対象銘柄/ja/価格精度に合わせて正規化した値を返します。

//+------------------------------------------------------------------+
//| Returns the price range (high - low) of a bar at the given index |
//+------------------------------------------------------------------+
double GetBarRange(const string symbol, ENUM_TIMEFRAMES tf, int index){

   double high = iHigh(symbol, tf, index);
   double low  = iLow (symbol, tf, index);

   if(high == 0.0 || low == 0.0){
      return 0.0;
   }

   return NormalizeDouble(high - low, Digits());
}

こ/ja/単一/ja/値が、エントリー価格、ストップ価格、そして後に計算されるテイクプロフィット価格を投影するため/ja/基礎となります。こ/ja/測定値がなければ、ブレイクアウトモデルは機能しません。

エントリー価格/ja/投影

レンジが求められれば、エントリー価格/ja/投影は非常にシンプルです。買いシグナル/ja/場合、エントリー価格は現在/ja/始値に前バー/ja/レンジ/ja/一定割合を加算することで算出されます。

//+--------------------------------------------------------------------------------------+
//| Calculates the bullish breakout entry price using today's open and yesterday's range |
//+--------------------------------------------------------------------------------------+
double CalculateBuyEntryPrice(double todayOpen, double yesterdayRange, double buyMultiplier){

   return todayOpen + (yesterdayRange * buyMultiplier);
}

売りシグナル/ja/場合、エントリー価格は現在/ja/始値から前バー/ja/レンジ/ja/一定割合を差し引くことで算出されます。

//+--------------------------------------------------------------------------------------+
//| Calculates the bearish breakout entry price using today's open and yesterday's range |
//+--------------------------------------------------------------------------------------+
double CalculateSellEntryPrice(double todayOpen, double yesterdayRange, double sellMultiplier){

   return todayOpen - (yesterdayRange * sellMultiplier);
}

これら/ja/係数はユーザーが設定可能であり、ブレイクアウト/ja/閾値をど/ja/程度積極的または保守的に設定すべきかを検証できます。こ/ja/ように算出された価格レベルは、モメンタムが実際に機能していることを確認するため/ja/基準となります。いかなる取引も、こ/ja/レベルを価格が突破するまでは実行されません。

ストップロスレベル/ja/予測

保護的なエグジットは、2つ/ja/異なる方法で設定できます。ボラティリティベース/ja/モードでは、ストップロスはエントリー価格を基準として、前バー/ja/レンジ/ja/一定割合を用いて算出されます。買いポジション/ja/場合、ストップロスはエントリー価格より下に配置されます。

//+--------------------------------------------------------------------------------------------------+
//| Calculates the stop-loss price for a bullish position based on entry price and yesterday's range |
//+--------------------------------------------------------------------------------------------------+
double CalculateBullishStopLoss(double entryPrice, double yesterdayRange, double stopMultiplier){

   return entryPrice - (yesterdayRange * stopMultiplier);
}

売りポジション/ja/場合、ストップロスはエントリー価格より上に配置されます。

//+--------------------------------------------------------------------------------------------------+
//| Calculates the stop-loss price for a bearish position based on entry price and yesterday's range |
//+--------------------------------------------------------------------------------------------------+
double CalculateBearishStopLoss(double entryPrice, double yesterdayRange, double stopMultiplier){

   return entryPrice + (yesterdayRange * stopMultiplier);
}

これにより、リスクは直近/ja/ボラティリティに比例して管理され、市場や時間軸が変化しても自然に適応する形になります。

リスクリワードを用いたテイクプロフィット/ja/投影

テイクプロフィットモードがリスクリワード方式に設定されている場合、ターゲットは測定されたストップ距離から直接導出されます。エントリーとストップ/ja/間/ja/距離がリスク/ja/単位となり、こ/ja/距離に選択されたリワード倍率を掛けます。

そ/ja/結果として得られた値をエントリー価格に加算または減算することで、ターゲット価格が決定されます。

//+--------------------------------------------------------------------------+
//| Calculates take-profit level for a bullish trade using risk-reward logic |                               
//+--------------------------------------------------------------------------+
double CalculateBullishTakeProfit(double entryPrice, double stopLossPrice, double rewardValue){

   double stopDistance   = entryPrice - stopLossPrice;
   double rewardDistance = stopDistance * rewardValue;
   return NormalizeDouble(entryPrice + rewardDistance, Digits());
}

//+--------------------------------------------------------------------------+
//| Calculates take-profit level for a bearish trade using risk-reward logic |                               
//+--------------------------------------------------------------------------+
double CalculateBearishTakeProfit(double entryPrice, double stopLossPrice, double rewardValue){

   double stopDistance   = stopLossPrice - entryPrice;
   double rewardDistance = stopDistance * rewardValue;
   return NormalizeDouble(entryPrice - rewardDistance, Digits());
}

これにより、すべて/ja/取引において一貫したリスク・リワード/ja/幾何構造が保証されます。

リアルタイムで/ja/レベルクロス検出

本システムにおけるシグナルは、バー/ja/終値によって発火する/ja/ではなく、価格が投影されたエントリーレベルを実際にクロスした瞬間に発火します。そ/ja/ために、直近/ja/1分足終値を保持し、連続する2つ/ja/値を比較します。価格がある水準を下から上へ抜けた場合、そ/ja/クロスオーバーが成立します。

//+------------------------------------------------------------------+
//| To detect a crossover at a given price level                     |                               
//+------------------------------------------------------------------+
bool IsCrossOver(const double price, const double &closePriceMinsData[]){
   if(closePriceMinsData[1] <= price && closePriceMinsData[0] > price){
      return true;
   }
   return false;
}

価格がある水準を上から下へ抜けた場合、そ/ja/クロスアンダーが成立します。

//+------------------------------------------------------------------+
//| To detect a crossunder at a given price level                    |                               
//+------------------------------------------------------------------+
bool IsCrossUnder(const double price, const double &closePriceMinsData[]){
   if(closePriceMinsData[1] >= price && closePriceMinsData[0] < price){
      return true;
   }
   return false;
}

これにより、システムはシグナルが成立した瞬間/ja/モメンタム変化に即座に反応できるようになります。

曜日による取引フィルタリング

取引曜日フィルタリングは、2つ/ja/小さなヘルパー関数によって実装されます。第一/ja/関数は、現在時刻を曜日インデックスへ変換します。

//+------------------------------------------------------------------------------------+
//| Returns the day of the week (0 = Sunday, 6 = Saturday) for the given datetime value|                               
//+------------------------------------------------------------------------------------+
int TimeDayOfWeek(datetime time){
   MqlDateTime timeStruct = {};
   if(!TimeToStruct(time, timeStruct)){
      Print("TimeDayOfWeek: TimeToStruct failed");
      return -1;
   }      
   return timeStruct.day_of_week;
}

第二/ja/関数は、そ/ja/曜日が設定上有効化されているかどうかを確認します。

//+-----------------------------------------------------------------------------------------------------+
//| Determines whether trading is permitted for the given datetime based on the selected trade-day mode |                               
//+-----------------------------------------------------------------------------------------------------+
bool IsTradingDayAllowed(datetime time)
{
   // Baseline mode: no filtering
   if(tradeDayMode == TDW_ALL_DAYS){
      return true;
   }

   int day = TimeDayOfWeek(time);

   switch(day)
   {
      case 0: return tradeSunday;
      case 1: return tradeMonday;
      case 2: return tradeTuesday;
      case 3: return tradeWednesday;
      case 4: return tradeThursday;
      case 5: return tradeFriday;
      case 6: return tradeSaturday;
   }

   return false;
}

こ/ja/仕組みにより、特定/ja/曜日に対してパターンを選択的にテストすることが可能になります。これは「取引曜日」コンセプト/ja/中核を成す要素です。こ/ja/フィルタは任意機能であり、無効化されている場合はすべて/ja/曜日が取引対象として許可されます。

新規バー形成時におけるボラティリティレベル/ja/投影

すべて/ja/ツールが揃ったことで、ここからティック関数を更新し、新しいバーが形成された場合に/ja/みボラティリティレベルを投影するようにします。こ/ja/新しいブロック内では、厳密に定義された一連/ja/処理を順番に実行します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
   
      //--- Increment the number of completed bars since the position was opened
      if(barsSinceEntry > 0){
         barsSinceEntry = barsSinceEntry + 1;
      }
      
      //--- Handle exit conditions for the currently active position based on the configured take-profit mode
      if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){
         ManageOpenPositionExits();
      }
   
      lwVolatilityLevels.yesterdayRange    = GetBarRange(_Symbol, timeframe, 1);
      
      if(lwStrategyMode == LW_STRATEGY_BASELINE_BUY_OPEN){
         lwVolatilityLevels.buyEntryPrice  = askPrice;
         lwVolatilityLevels.sellEntryPrice = bidPrice;
      }else{
         lwVolatilityLevels.buyEntryPrice  = CalculateBuyEntryPrice (askPrice, lwVolatilityLevels.yesterdayRange, inpBuyRangeMultiplier );
         lwVolatilityLevels.sellEntryPrice = CalculateSellEntryPrice(bidPrice, lwVolatilityLevels.yesterdayRange, inpSellRangeMultiplier);
      }      
      if(stopLossMode == SL_BY_RANGE_PERCENT){
         lwVolatilityLevels.bullishStopLoss = CalculateBullishStopLoss(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.yesterdayRange,  inpStopRangeMultiplier);
         lwVolatilityLevels.bearishStopLoss = CalculateBearishStopLoss(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.yesterdayRange, inpStopRangeMultiplier);
      }
      if(stopLossMode == SL_AT_PREVIOUS_BAR){
         lwVolatilityLevels.bullishStopLoss = iLow (_Symbol, timeframe, 1);
         lwVolatilityLevels.bearishStopLoss = iHigh(_Symbol, timeframe, 1);
      }     
      if(takeProfitMode == TP_BY_RISK_REWARD){
         lwVolatilityLevels.bullishTakeProfit = CalculateBullishTakeProfit(lwVolatilityLevels.buyEntryPrice, lwVolatilityLevels.bullishStopLoss,  riskRewardRatio);
         lwVolatilityLevels.bearishTakeProfit = CalculateBearishTakeProfit(lwVolatilityLevels.sellEntryPrice, lwVolatilityLevels.bearishStopLoss, riskRewardRatio);
      }
      
      lwVolatilityLevels.bullishStopDistance = lwVolatilityLevels.buyEntryPrice   - lwVolatilityLevels.bullishStopLoss;
      lwVolatilityLevels.bearishStopDistance = lwVolatilityLevels.bearishStopLoss - lwVolatilityLevels.sellEntryPrice;
      
   }
}

まず、ポジションがアクティブな場合にはバー・カウンターを更新します。次に、最初/ja/「含み益発生時に即クローズ」モードに対するエグジット条件を評価します。第三に、前バー/ja/レンジを測定します。第四に、選択された戦略に応じてエントリー価格を投影します。第五に、選択されたストップロスモデルに基づいてストップロス価格を算出します。第六に、リスクリワードモードが有効な場合にはテイクプロフィット/ja/投影値を生成します。最後に、ポジションサイジングに利用するため/ja/ストップ距離を計算します。

こ/ja/ブロックは、各バー/ja/開始時に取引フレームワーク全体をリセットする役割を持ちます。投影されたレベルがバー内でブレイクされなかった場合、そ/ja/レベルは破棄され、次/ja/バーで再生成されます。再帰的なシグナルは一切許可されません。

常に1ポジション/ja/みを維持する

こ/ja/システム/ja/中核設計ルール/ja/一つは、常に同時に存在できるポジションは1つだけであるという点です。2つ/ja/ヘルパー関数が全て/ja/オープンポジションをスキャンし、こ/ja/EAインスタンスに属する買いまたは売りポジションが既に存在しているかどうかを判定します。

//+------------------------------------------------------------------+
//| To verify whether this EA currently has an active buy position.  |                                 |
//+------------------------------------------------------------------+
bool IsThereAnActiveBuyPosition(ulong magic){
   
   for(int i = PositionsTotal() - 1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0){
         Print("Error while fetching position ticket ", _LastError);
         continue;
      }else{
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){
            return true;
         }
      }
   }   
   return false;
}

//+------------------------------------------------------------------+
//| To verify whether this EA currently has an active sell position. |                                 |
//+------------------------------------------------------------------+
bool IsThereAnActiveSellPosition(ulong magic){
   
   for(int i = PositionsTotal() - 1; i >= 0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0){
         Print("Error while fetching position ticket ", _LastError);
         continue;
      }else{
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){
            return true;
         }
      }
   }
   
   return false;
}

これら/ja/チェックはすべて/ja/取引実行前に適用されます。いずれか/ja/ポジションが検出された場合、新規取引は一切許可されません。こ/ja/ルールにより、システム/ja/単純性が保たれ、意図しないポジション/ja/積み増しが防止されます。

プログラムによるポジション決済

利益確定モードが固定ターゲットを使用しない場合、システムは価格ではなくロジックに基づいて決済する必要があります。そ/ja/ため、一般的な決済関数が定義され、こ/ja/EAに属するすべて/ja/オープンポジションをスキャンし、即座に決済処理を実行します。

//+------------------------------------------------------------------+
//| To close all position with a specified magic number              |   
//+------------------------------------------------------------------+
void ClosePositionsByMagic(ulong magic) {
    
    for (int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket)) {
            if (PositionGetInteger(POSITION_MAGIC) == magic) {
                ulong positionType = PositionGetInteger(POSITION_TYPE);
                double volume = PositionGetDouble(POSITION_VOLUME);
                if (positionType == POSITION_TYPE_BUY) {
                    Trade.PositionClose(ticket);
                } else if (positionType == POSITION_TYPE_SELL) {
                    Trade.PositionClose(ticket);
                }
            }
        }
    }    
}

こ/ja/関数は、そ/ja/後/ja/時間ベース/ja/エグジットおよび「含み益発生時/ja/即時クローズ」ロジック/ja/両方で再利用されます。

自動ポジションサイズ/ja/計算

ロットサイズモードが自動設定/ja/場合、ポジションサイズはアカウントリスクから導出されます。

//+----------------------------------------------------------------------------------+
//| Calculates position size based on a fixed percentage risk of the account balance |
//+----------------------------------------------------------------------------------+
double CalculatePositionSizeByRisk(double stopDistance){
   double amountAtRisk = (riskPerTradePercent / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE);
   double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
   double volume       = amountAtRisk / (contractSize * stopDistance);
   return NormalizeDouble(volume, 2);
}

こ/ja/関数は、選択されたリスク割合に基づいて許容されるリスク金額を計算します。次に、そ/ja/金額を契約サイズとストップ距離/ja/積で割ることで、ポジションサイズを算出します。そ/ja/結果として得られるロットサイズは、口座資金/ja/うち指定された割合/ja/みを正確にリスクにさらすように設計されています。これにより、異なる銘柄やボラティリティ環境においてもリスクが安定して維持されます。

売買注文/ja/執行

注文/ja/発注は2つ/ja/実行関数によって処理されます。

//+------------------------------------------------------------------+
//| Function to open a market buy position                           |
//+------------------------------------------------------------------+
bool OpenBuy(double stopLoss, double takeProfit, double lotSize){
   
   if(lotSizeMode == MODE_AUTO){
      lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.bullishStopDistance);
   }
   
   if(!Trade.Buy(lotSize, _Symbol, askPrice, lwVolatilityLevels.bullishStopLoss)){
      Print("Error while executing a market buy order: ", GetLastError());
      Print(Trade.ResultRetcode());
      Print(Trade.ResultComment());
      return false;
   }
   
   if(takeProfitMode  == TP_AFTER_N_CANDLES){
      barsSinceEntry = 1;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Function to open a market sell position                          |
//+------------------------------------------------------------------+
bool OpenSel(double stopLoss, double takeProfit, double lotSize){
   
   if(lotSizeMode == MODE_AUTO){
      lotSize = CalculatePositionSizeByRisk(lwVolatilityLevels.bearishStopDistance);
   }
   
   if(!Trade.Sell(lotSize, _Symbol, bidPrice, lwVolatilityLevels.bearishStopLoss)){
      Print("Error while executing a market buy order: ", GetLastError());
      Print(Trade.ResultRetcode());
      Print(Trade.ResultComment());
      return false;
   }
   
   if(takeProfitMode  == TP_AFTER_N_CANDLES){
      barsSinceEntry = 1;
   }
   
   return true;
}

注文送信前に、自動ロットサイズが有効な場合は必ず再計算がおこなわれます。すべて/ja/注文には事前に算出されたストップロスが付与されます。エグジットモードがバー数ベース/ja/場合、エントリー直後にバーカウンターが初期化されます。

また、それぞれ/ja/関数は実行エラーをログとして出力するため、テスト段階で失敗原因を特定できるようになっています。

固定ターゲットを持たないエグジット管理

テイクプロフィットが固定価格で定義されない場合、エグジットは手動ロジックによって管理される必要があります。

//+-------------------------------------------------------------------------------------------+
//| Manages exit logic for the currently open position based on the selected take-profit mode |
//+-------------------------------------------------------------------------------------------+
void ManageOpenPositionExits(){

   if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){
      for(int i = PositionsTotal() - 1; i >= 0; i--){
         ulong ticket = PositionGetTicket(i);
         if(ticket == 0){
            Print("Error while fetching position ticket ", GetLastError());
            continue;
         }else{
            if(PositionGetDouble(POSITION_PROFIT) > 0 ){
               ClosePositionsByMagic(magicNumber);
            }
         }
      }
   }
   
   if(takeProfitMode == TP_AFTER_N_CANDLES){
      if(barsSinceEntry > exitAfterCandles){
         ClosePositionsByMagic(magicNumber);
         barsSinceEntry = 0;
      }
   }   
}

2つ/ja/エグジットモデルがサポートされています。最初/ja/「含み益発生時即時クローズ」モードでは、システムはオープンポジションをスキャンし、含み損益がプラスになった瞬間にポジションを決済します。一方、バー数ベース/ja/モードでは、あらかじめ設定されたバー数/ja/上限をバーカウンタが超えた時点でポジションを決済し、そ/ja/後カウンタをリセットします。こ/ja/関数は、エグジットが取りこぼされないように、ティック処理/ja/中で適切かつ正確なタイミングで呼び出されます。

取引開始後/ja/バー数トラッキング

一部/ja/エグジットモデルは価格ではなく時間に依存するため、ポジションが開始されてから何本/ja/バーが経過したかを追跡する必要があります。

そ/ja/ためシステムはbarsSinceEntryという変数を用いて、現在/ja/取引が発注されてから経過したバー/ja/本数をカウントし、ポジションをいつ決済すべきかを判断します。こ/ja/変数はグローバルスコープで定義されます。

//--- Tracks the number of completed bars elapsed since the current trade was opened
int barsSinceEntry;

安全に使用するためには、EA/ja/初期化時にこ/ja/変数をゼロに設定します。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- Initialize bar counter for time-based trade exit tracking
   barsSinceEntry = 0;

   return(INIT_SUCCEEDED);
}

こ/ja/カウンタは、取引開始後に新しいバーが形成されるたびにインクリメントされ、ポジションが決済された際にリセットされます。これにより、時間ベース/ja/エグジット判断/ja/基準として機能します。

戦略実行ロジック/ja/構築

すべて/ja/構成要素が揃ったことで、取引ロジックをティック関数に統合していきます。各戦略は独立した条件ブロックとして実装されます。ベースライン戦略/ja/みは新しいバー形成時に実行され、オープン直後にエントリーします。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
   
      ...
          
      //---
      if(lwStrategyMode == LW_STRATEGY_BASELINE_BUY_OPEN){
         if(IsThereAnActiveBuyPosition(magicNumber) || IsThereAnActiveSellPosition(magicNumber)){
            ClosePositionsByMagic(magicNumber);
            Sleep(50);
         }
         
         if(tradeDayMode == TDW_SELECTED_DAYS){
            if(IsTradingDayAllowed(currentTime)){
               if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }else{
            if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
               OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
            }
         }
      }      
   }
}

そ/ja/他/ja/戦略は、価格が投影されたエントリーレベルをクロスした瞬間にシグナルが発生するため、ティック単位で常時評価されます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //---  
   if(lwStrategyMode == LW_STRATEGY_CONSECUTIVE_BEARISH_BARS){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, requiredConsecutiveBearishBars, BAR_CLOSE_DOWN)){
            if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
                  }
               }else{
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }
      }
   }
   
   //---
   if(lwStrategyMode == LW_STRATEGY_UPTREND_WITH_PULLBACK){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsUptrendWithPullback(_Symbol, timeframe, 1, uptrendLookbackBars, pullbackLookbackBars)){
            if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
                  }
               }else{
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }
      }
   }
   
   //---
   if(lwStrategyMode == LW_STRATEGY_OUTSIDE_DAY_DOWN_CLOSE){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsOutsideBarWithDownClose(_Symbol, timeframe, 1)){
            if(IsCrossOver(lwVolatilityLevels.buyEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
                  }
               }else{
                  OpenBuy(lwVolatilityLevels.bullishStopLoss, lwVolatilityLevels.bullishTakeProfit, positionSize);
               }
            }
         }
      }
   }
   
   //---
   if(lwStrategyMode == LW_STRATEGY_THIRD_BULLISH_DAY_FADE){
      if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){
         if(IsConsecutiveBarCloseState(_Symbol, timeframe, requiredConsecutiveBullishBars, BAR_CLOSE_UP)){
            if(IsCrossUnder(lwVolatilityLevels.sellEntryPrice, closePriceMinutesData)){
               if(tradeDayMode == TDW_SELECTED_DAYS){
                  if(IsTradingDayAllowed(currentTime)){
                     OpenSel(lwVolatilityLevels.bearishStopLoss, lwVolatilityLevels.bearishTakeProfit, positionSize);
                  }
               }else{
                  OpenSel(lwVolatilityLevels.bearishStopLoss, lwVolatilityLevels.bearishTakeProfit, positionSize);
               }
            }
         }
      }
   }
}

取引実行前には常に3つ/ja/条件が満たされる必要があります。第一に、アクティブポジションが存在しないこと。第二に、選択されたパターンが有効であること。第三に、取引曜日フィルタが許可していることです。これらすべてが揃った場合に/ja/み、注文が送信されます。こ/ja/構造により、各戦略は独立性を保ちながらも、同一/ja/実行エンジン上で動作します。

適切なタイミングで/ja/エグジットロジック処理

エグジットロジックは2つ/ja/場所で評価されます。新しいバーが形成された際には「含み益発生時即時クローズ」モード/ja/判定がおこなわれ、ティックごとにはバー数ベース/ja/エグジットモードが評価されます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   ...
   
   //--- Run this block only when a new bar is detected on the selected timeframe
   if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){
   
      //--- Increment the number of completed bars since the position was opened
      if(barsSinceEntry > 0){
         barsSinceEntry = barsSinceEntry + 1;
      }
      
      //--- Handle exit conditions for the currently active position based on the configured take-profit mode
      if(takeProfitMode == TP_FIRST_PROFITABLE_OPEN){
         ManageOpenPositionExits();
      }   
      ...      
   }
   
   //--- Handle exit conditions for the currently active position based on the configured take-profit mode
   if(takeProfitMode == TP_AFTER_N_CANDLES){
      ManageOpenPositionExits();
   }
   
   ...
}

こ/ja/分離設計により、それぞれ/ja/エグジットモデルは過剰な計算をおこなうことなく、適切な頻度で実行されます。

クリーンなチャート環境/ja/設定

最後に、表示環境を整えます。

//+------------------------------------------------------------------+
//| This function configures the chart's appearance.                 |
//+------------------------------------------------------------------+
bool ConfigureChartAppearance()
{
   if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){
      Print("Error while setting chart background, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){
      Print("Error while setting chart grid, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){
      Print("Error while setting chart mode, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){
      Print("Error while setting chart foreground, ", GetLastError());
      return false;
   }

   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){
      Print("Error while setting bullish candles color, ", GetLastError());
      return false;
   }
      
   if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){
      Print("Error while setting bearish candles color, ", GetLastError());
      return false;
   }
   
   return true;
}

チャート表示設定関数は、グリッドを非表示にし、ローソク足モードを選択し、背景および前景/ja/明確なカラースキームを設定し、さらに陽線と陰線に一貫した色を割り当てます。これにより、価格/ja/動きと実際/ja/約定履歴を、視覚的なノイズなしで観察できるクリーンな環境が構築されます。こ/ja/関数は初期化時に呼び出され、EA/ja/起動直後にチャートが即座に設定されるようになっています。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   ...
   
   //--- To configure the chart's appearance
   if(!ConfigureChartAppearance()){
      Print("Error while configuring chart appearance", GetLastError());
      return INIT_FAILED;
   }

   return(INIT_SUCCEEDED);
}

ここまで/ja/時点で、EAは完成しています。すべて/ja/パターンは客観的に検出され、すべて/ja/ボラティリティレベルは一貫して投影され、すべて/ja/取引はルールに従ってフィルタリング、サイズ調整、実行、決済されます。すべて/ja/戦略は同一フレームワーク内で独立して動作します。システムはテスト可能な状態に到達しました。


ゴールドにおけるパターンテスト

テスト環境と構成

個別/ja/戦略を評価する前に、すべて/ja/実験がど/ja/ような環境で実施されたかを明確にしておく必要があります。すべて/ja/戦略は同一/ja/統制された条件下でテストされ、結果/ja/比較可能性が担保されています。テストは以下/ja/設定で実施されました。

  • 取引対象:ゴールド(XAUUSD)
  • 初期資金:10,000ドル
  • テスト期間:2025年1月1日~2025年12月30日
  • 時間足:日次
  • ストップロス:前日/ja/レンジ/ja/割合
  • テイクプロフィット:リスクリワードモデル
  • 取引曜日:全曜日許可
  • ポジションサイズ:口座残高/ja/割合

こ/ja/設定は、ラリー・ウィリアムズ/ja/元々/ja/研究/ja/精神を踏襲しており、現実的な取引環境を再現しています。

ベースライン戦略:始値で/ja/買い

こ/ja/最初/ja/テストは、最も単純なモデルを表しています。各日/ja/開始時にポジションを建て、決済は選択したエグジットルールに従います。

結果:

  • 総収益8,187.44ドル
  • 勝率:50.40パーセント

インサイト

BaselineStrategyEquityCurve

こ/ja/結果は書籍で述べられている重要なポイントを裏付けています。市場が強いトレンドを持っている場合、単純な戦略でも利益を生み出すことがあります。2025年/ja/ゴールドは全体として上昇傾向にありました。そ/ja/ため、毎日/ja/始値で買うだけでも、そ/ja/方向性/ja/バイアスから利益を得ることができました。しかし勝率はほぼランダムに近い水準です。こ/ja/戦略/ja/利益はパターン/ja/質ではなく、市場/ja/方向性によるも/ja/です。そ/ja/ため、他/ja/戦略を評価するため/ja/基準点として機能します。

1本/ja/陰線後に買う

結果:

  • 総収益662.40ドル
  • 勝率:36.36パーセント

インサイト

BuyOneCloseEquityCurve

こ/ja/バリエーションは良好とは言えない結果です。1日/ja/陰線は、反転や枯渇を判断するには情報が不十分です。低い勝率は、多く/ja/押し目が反転せず継続することを示しています。これはすべて/ja/パターンが同じ価値を持つわけではないことを裏付けています。

3連続陰線後/ja/買い

結果:

  • 総純利益:1,309.55ドル
  • 勝率:71.43パーセント

インサイト

BuyThreeConsecutiveBearishBarsEquityCurve

ここで状況は大きく変わります。3連続/ja/陰線は、短期的な売り圧力と感情的な行き過ぎを示します。勝率は大きく上昇し、収益性も改善しています。これはラリー・ウィリアムズ/ja/重要な考え方/ja/一つを支持しています。極端な行動を示すパターンほど優位性を持ち得るという点です。選択性が明確にパフォーマンスを改善しています。

上昇トレンド+押し目

結果:

  • 総収益1,343.43ドル
  • 勝率:100.00パーセント

インサイト

PullBackEquityCurve

これは本研究における最も強いパフォーマンスです。こ/ja/パターンは2つ/ja/要素を組み合わせています。

  • 長期トレンド/ja/確認
  • 短期的な押し目買い

一時的な弱さ/ja/後に、本来/ja/方向に沿ってエントリーします。サンプル数は少ないも/ja//ja/、こ/ja/結果はラリー・ウィリアムズがこ/ja/モデルを好む理由を示しています。トレンド+押し目という構造は、高品質な機会を生み出します。頻度よりも構造が重要であることが確認されます。

陰線アウトサイドデー

結果:

  • 総収益0.00ドル
  • 勝率:0.00パーセント

インサイト

OutsideDayPatternEquityCurve

こ/ja/期間では一度も取引が発生しませんでした。こ/ja/結果は利益と同じくらい重要です。ラリー・ウィリアムズ自身もこ/ja/パターン/ja/出現頻度が低いことを指摘しています。テスト結果はそ/ja/希少性を裏付けています。希少なパターンは強力である可能性はあるも/ja//ja/、単独システムとしては不適切です。フィルタや補助的な機会検出として使用する/ja/が適切です。

3連続陽線/ja/逆張り

結果:

  • 総純利益:マイナス1,043.81ドル
  • 勝率:0.00パーセント

インサイト

ConsecutiveBullishBarFadeEquityCurve

こ/ja/戦略は本研究/ja/中で最も悪い結果となりました。上昇トレンド年において強さを逆張りする試みは繰り返し失敗しています。モメンタムは反転せず継続します。これは重要な原則を示しています。明確なエクストリームやコンテキストがない逆張りパターンは危険です。極端な状態やボラティリティフィルタがない状態では、逆張り戦略は損失を生みやすくなります。こ/ja/結果は、ラリー・ウィリアムズが「勢いに逆らう/ja/ではなく、流れに従う」ことを重視する理由を明確に説明しています。

戦略横断的な考察

これら/ja/結果から、いくつか/ja/重要な結論が導かれます。

市場/ja/方向性が結果を左右する

ベースライン戦略が良好な結果を示した主因は、ゴールドが強い上昇トレンドにあった点にあります。横ばい相場や下降トレンド/ja/年であれば、同じモデルはおそらく機能しなかったでしょう。戦略は常に市場環境/ja/文脈/ja/中で評価される必要があります。

選択性/ja/向上は品質向上につながる

パターン/ja/条件が厳格になるほど、パフォーマンスは改善しています。

  • 1本/ja/陰線後:パフォーマンスは低い
  • 3本連続陰線後:パフォーマンスは良好
  • トレンド+押し目:最も優れた結果

フィルタリングによって取引回数は減少しますが、そ/ja/分取引/ja/質は向上します。これは「取引数/ja/多さではなく、構造化された質/ja/高い取引が重要である」という考え方を支持しています。

市場心理ベース/ja/パターンが有効

最も良い結果を示したパターンはいずれも、市場/ja/心理的な偏りを捉えたも/ja/です。

  • 複数回/ja/陰線(売り圧力/ja/蓄積)
  • トレンド中/ja/押し目
  • 連続した圧力後/ja/枯渇(エクステンション/ja/終了)

これら/ja/パターンは単なるローソク足/ja/形状ではなく、短期的な需給/ja/歪みを捉えています。こ/ja/点は、短期的な優位性/ja/源泉が「形」ではなく「心理」にあるというラリー・ウィリアムズ/ja/見解を裏付けています。

強さへ/ja/逆張りは危険である

損失となった戦略は、強いモメンタムに対する逆張り/ja/リスクを明確に示しています。明確な枯渇シグナルが存在しない状態で/ja/逆張りは、市場/ja/圧力に逆らう行為となります。これは多く/ja/逆張りシステムがトレンド環境で失敗する理由を説明しています。

これら/ja/テストが示すも/ja/

こ/ja/研究は、ラリー・ウィリアムズ/ja/中心的な考え方をいくつか検証しています。

  • 短期的な市場は完全にランダムではない
  • 特定/ja/曜日やパターンには優位性が存在する
  • フィルタリングは安定性を高める
  • トレンドへ/ja/順張りは生存率を高める

そして最も重要な/ja/は、パターンは仮定ではなく検証されるべきだという点です。理論上は有効に見えるアイデアでも、実データでは機能しないことがあります。一方で、目立たない単純なルールが安定したエッジを生み出すこともあります。

なぜこれが重要な/ja/か

こ/ja/テストフェーズ/ja/目的は、完全なシステムを見つけることではありません。市場挙動を規律ある方法で分析する枠組みを示すことにあります。本記事で扱ったすべて/ja/パターンは、今後以下/ja/ように発展させることができます。

  • 他市場で/ja/再テスト
  • フィルタと/ja/組み合わせ
  • ボラティリティ条件/ja/追加
  • 異なる時間軸で/ja/検証

これは主観的な信念ではなく、システマティックな研究/ja/基盤となります。そしてそれこそが最終的に「利益につながるパターン」が示している本質的な教訓です。


結論

本記事では、単に書籍/ja/アイデアをコードへ翻訳する以上/ja/ことをおこないました。パターンを体系的にテストし、比較し、定量的に評価できる小さなリサーチシステムを構築しました。段階的なプロセスを通じて、ラリー・ウィリアムズ/ja/短期的な観察を、履歴データ上で実行・再現・検証可能なEAへと変換しています。

結果から得られる重要な教訓は明確です。一部/ja/パターンは明確な優位性を持ちますが、一部は特定/ja/条件下で/ja/み機能し、また一部は実際/ja/市場挙動に晒されると機能しなくなります。これは書籍/ja/中心的テーマ/ja/一つを裏付けています。短期的な市場は完全なランダムではありませんが、すべて/ja/パターンに資金を投じるべきではありません。客観的な検証を通過したも/ja/だけが検討に値します。

こ/ja/研究/ja/本当/ja/価値は数値そ/ja/も/ja/ではなく、そ/ja/背後にあるフレームワークにあります。私たちは今、複数/ja/アイデアを統合し、統一されたリスクルールを適用し、時間フィルタを用いて取引を制御し、感情に左右されずにパフォーマンスを評価できる柔軟なエンジンを手にしています。こ/ja/アプローチにより、取引研究は「信念」から「証拠」へと移行します。

ここから先/ja/道は開かれています。新しいパターンを追加することも、既存ルールを改善することも、異なる市場、時間軸、リスクモデルを検証することも可能です。すべて/ja/変更は推測ではなく、制御された実験となります。

最終的に重要な/ja/は、単一/ja/完璧なパターンを見つけることではありません。真に機能するも/ja/を発見し、機能しないときには資本を守り、規律ある検証を通じて継続的に改善していくプロセスを構築することです。

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

添付されたファイル |
グラフ理論:取引における幅優先探索(BFS)/ja/応用 グラフ理論:取引における幅優先探索(BFS)/ja/応用
幅優先探索(BFS)はレベル順トラバーサルを用い、価格スイングを時間/ja/経過とともに進化する有向グラフとして市場構造をモデル化します。過去/ja/ローソク足またはセッションを階層ごとに分析することで、BFSはより直近/ja/価格挙動を優先しつつ、より長期/ja/市場文脈も反映します。
初心者からエキスパートへ:サプライ&デマンドゾーンの統計的検証 初心者からエキスパートへ:サプライ&デマンドゾーンの統計的検証
サプライ&デマンド取引戦略の背後にある、見落とされがちな統計的基盤を明らかにします。MQL5とPythonをJupyter Notebookワークフローで連携させることで、マーケットに対する視覚的な仮定を定量的な洞察へと変換する体系的なデータ駆動型リサーチをおこないます。本記事では、データ収集からPythonによる統計分析、アルゴリズム設計、テスト、最終的な結論に至るまで、一連の研究プロセスを解説します。手法と結果の詳細については、本文をご参照ください。
MQL5取引ツール(第15回):Canvas/ja/ぼかし効果、影描画、滑らかなマウスホイールスクロール MQL5取引ツール(第15回):Canvas/ja/ぼかし効果、影描画、滑らかなマウスホイールスクロール
MQL5 Canvasダッシュボードを高度な視覚効果で強化します。具体的には、フォグオーバーレイ/ja/ため/ja/ぼかしグラデーション、ヘッダー/ja/影描画、そしてより滑らかな線や曲線を実現するアンチエイリアス描画を追加します。また、チャート/ja/ズームスケールに干渉しない滑らかなマウスホイールスクロールもテキストパネルに実装し、機能面でも改良を加えます。
ダイナミックマルチペアEAの形成(第6回):高頻度銘柄切り替えのための適応型スプレッド感度制御 ダイナミックマルチペアEAの形成(第6回):高頻度銘柄切り替えのための適応型スプレッド感度制御
本パートでは、マルチ銘柄におけるリアルタイムのスプレッド条件を継続的に監視し、評価するインテリジェントな実行レイヤーの設計に焦点を当てます。EAは、固定ルールではなくスプレッドの効率性に基づいて取引の有効と無効を切り替えることで、銘柄選択を動的に適応させます。このアプローチにより、高頻度で銘柄を切り替えるマルチペアシステムはコスト効率の高い銘柄を優先できるようになります。