時間帯別・曜日別・月の日付別の季節性インジケータ
価格は、特定の日や時間帯に繰り返されるリズムを示します。これらの繰り返されるリズム、つまり季節的パターンは、市場がいつ上昇しやすく、いつ下降しやすいのかを判断するための手掛かりをトレーダーに与えることがあります。金融市場における季節性は、単なる興味深い現象ではありません。それは、価格変動という混沌の中から予測可能なタイミングを見つけ出すためのツールです。たとえば、特定の通貨ペアが月曜日によく上昇したり、月末に下落したりすることに気付いたことはありませんか。これが季節性です。そして、それを研究することはトレーダーに優位性を与える可能性があります。
このガイドでは、MetaTrader 5プラットフォーム向けにMQL5で季節性インジケータを作成します。このインジケータは、過去の価格データを分析し、月の日付(1日~31日)、曜日(月曜日~日曜日)、または時間帯(0時~23時)ごとの平均リターンを特定します。結果は、別ウィンドウ内にヒストグラムとして表示されます。さらに、季節性の値を結ぶ予測ラインと、次の2本のバーで予想される値を強調表示するドットを表示します。加えて、インジケータは予測、最良期間、最悪期間を含むテキスト統計を表示します。開発プロセスを段階的に説明しながら、コードの各部分について詳しく解説していきます。そのため、このインジケータを単に使用するだけでなく、自分自身のアイデアに合わせて変更することも可能になります。このガイドでは、プログラミングと金融分析の両方を扱います。コードがデータとトレード判断を結ぶ橋として機能する様子を見ていきます。
なぜ季節性を研究するのか
季節性は、市場に隠された鼓動のようなものです。それは、トレーダーの行動、経済イベント、さらには人間心理など、さまざまな要因によって発生します。たとえば、月末には大規模なファンドがポジションを決済することがあり、それによって予測可能な価格変動が発生する場合があります。また、ロンドンやニューヨークの取引セッション開始に伴って、市場の活動が活発になることがあります。このインジケータは、混沌として見えるデータを明確なチャートや統計情報へ変換することで、そのようなパターンを発見する手助けをします。これは魔法ではなく、データ分析がトレーダーに有利に働いているにすぎません。
今回作成するインジケータは汎用的なものになります。ユーザーは季節性の種類を選択し、分析するバー数を設定し、リターンをパーセントまたは絶対値で表示するかを選択できます。また、チャート上の予測表示を有効または無効にすることもできます。柔軟性こそが、このインジケータをさまざまな市場や時間足で利用できる有用なツールにする鍵です。たとえば、日足のEURUSDチャートでも、ゴールドの1時間足チャートでも使用できます。
インジケータ構造の定義
インジケータ作成の第一歩は、それがどのように表示され、どのように動作するかを理解することです。ここでは、価格チャートのメイン画面を乱雑にしないように、価格チャートの下にある別ウィンドウへ表示することを想定します。可視化には3つの要素を使用します。1つ目は、各期間の平均リターンを表示するヒストグラムです。2つ目は、季節性の値と予測データを結ぶ予測ラインです。3つ目は、次の2本のバーで予測される値を強調する予測ポイントです。それぞれの要素は、データバッファと関連付けられます。データバッファとは、描画する値を保存する配列です。また、季節性データや期間ラベルを保存するためのグローバル変数も必要になります。さらに、ユーザーがインジケータをカスタマイズできるように、入力パラメータも用意します。
//+------------------------------------------------------------------+ //| SeasonalityIndicator.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 3 //--- plot Seasonality #property indicator_label1 "Seasonality" #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- plot Forecast Line #property indicator_label2 "Forecast" #property indicator_type2 DRAW_LINE #property indicator_color2 clrRed #property indicator_style2 STYLE_DASH #property indicator_width2 2 //--- plot Forecast Points #property indicator_label3 "Forecast Points" #property indicator_type3 DRAW_ARROW #property indicator_color3 clrOrange #property indicator_width3 3
このコードでは、インジケータに関する基本情報を設定します。著作権情報、MetaQuotes Webサイトへのリンク、そしてバージョンを指定します。#property indicator_separate_windowディレクティブは、インジケータが別ウィンドウに表示されることを指定します。3つのバッファと3つのプロットを定義します。DRAW_HISTOGRAMによるヒストグラムは青色で表示され、DRAW_LINEによる予測ラインは赤色で表示されます。また、DRAW_ARROWによる予測ポイントはオレンジ色で表示され、幅は3ピクセルに設定されています。これらの設定によって、視認性が高く、分かりやすい表示スタイルが決まります。
入力項目の設定
インジケータを柔軟に使用できるようにするため、ユーザーがMetaTrader 5の設定ウィンドウから変更できる入力項目を追加します。季節性タイプを選択するパラメータによって、月の日付、曜日、または1日の時間帯を分析できます。分析するバー数は、どれだけ過去まで遡ってデータを見るかを決定します。リターンは、パーセント表示または絶対値表示のどちらかを選択できます。また、正の季節性値だけを表示することも可能です。さらに、チャート上で予測を有効にするオプションや、ラインおよび予測ポイントの色をカスタマイズする機能を追加することで、インジケータをより柔軟に設定できるようになります。
//--- Input parameters enum ENUM_SEASONALITY_TYPE { SEASONALITY_DAYS_OF_MONTH = 0, // Days of the month (1-31) SEASONALITY_DAYS_OF_WEEK = 1, // Days of the week (Mon-Sun) SEASONALITY_HOURS = 2 // Hours (0-23) }; input ENUM_SEASONALITY_TYPE SeasonalityType = SEASONALITY_DAYS_OF_MONTH; // Seasonality type input int BarsToAnalyze = 1000; // Number of bars to analyze input bool ShowPercentage = true; // Show as a percentage input bool ShowPositiveOnly = false; // Show only positive values input bool ShowForecastOnChart = true; // Show forecast on chart input color ForecastColor = clrRed; // Forecast line color input color ForecastPointsColor = clrOrange; // Color of forecast points
ENUM_SEASONALITY_TYPE列挙型では、3種類の季節性オプションを定義します。デフォルトでは、月の日付による分析が選択されています。BarsToAnalyzeパラメータは1000本のバーに設定されています。これは、日足チャートではおよそ4年分のデータに相当します。ShowPercentageオプションはデフォルトで有効になっており、リターンをパーセント表示します。この表示方法は、より直感的に理解できます。ShowPositiveOnlyおよびShowForecastOnChartパラメータによって、さらに柔軟な設定が可能になります。また、色の設定はMetaTraderのインターフェースを通じて変更できます。
バッファと変数の定義
データを保存するために、このインジケータでは3つのバッファを使用します。1つ目はヒストグラム用、2つ目は予測ライン用、3つ目は予測ポイント用です。さらに、グローバル変数も必要になります。これには、期間ごとの平均リターンを保存する配列、期間ラベルを保存する配列(たとえば「Mon」や「1」)、そして期間数を保持する変数が含まれます。期間数は、選択された季節性タイプによって変化します。月の日付の場合は31、曜日の場合は7、時間帯の場合は24になります。
//--- Indicator buffers double SeasonalityBuffer[]; double ForecastLineBuffer[]; double ForecastPointsBuffer[]; //--- Global variables double seasonality_data[]; string period_labels[]; int periods_count;
バッファには描画されるデータが格納されます。また、seasonality_dataとperiod_labels変数は、分析結果を保存および表示するために使用されます。periods_count変数は、分析対象となる期間の数を決定します。この値は、選択された季節性タイプに応じて設定されます。
インジケータの初期化
OnInit関数は、インジケータが開始されたときに実行され、初期設定を担当します。この関数では、バッファをインジケータのプロットへ関連付け、予測ポイント用の矢印スタイルを設定し、ユーザーが指定したパラメータから色を設定します。季節性タイプに応じて、期間ラベルが作成されます。また、季節性データを保存するための配列は、すべてゼロで初期化されます。さらに、パーセント表示または絶対値表示のどちらが選択されているかに応じて、表示精度が調整されます。
int OnInit() { SetIndexBuffer(0, SeasonalityBuffer, INDICATOR_DATA); SetIndexBuffer(1, ForecastLineBuffer, INDICATOR_DATA); SetIndexBuffer(2, ForecastPointsBuffer, INDICATOR_DATA); PlotIndexSetInteger(2, PLOT_ARROW, 159); PlotIndexSetInteger(1, PLOT_LINE_COLOR, ForecastColor); PlotIndexSetInteger(2, PLOT_LINE_COLOR, ForecastPointsColor); switch(SeasonalityType) { case SEASONALITY_DAYS_OF_MONTH: periods_count = 31; ArrayResize(period_labels, periods_count); for(int i = 0; i < periods_count; i++) period_labels[i] = IntegerToString(i + 1); IndicatorSetString(INDICATOR_SHORTNAME, "Seasonality by day of the month"); break; case SEASONALITY_DAYS_OF_WEEK: periods_count = 7; ArrayResize(period_labels, periods_count); period_labels[0] = "Mon"; period_labels[1] = "Tue"; period_labels[2] = "Wed"; period_labels[3] = "Thu"; period_labels[4] = "Fri"; period_labels[5] = "Sat"; period_labels[6] = "Sun"; IndicatorSetString(INDICATOR_SHORTNAME, "Seasonality by day of the week"); break; case SEASONALITY_HOURS: periods_count = 24; ArrayResize(period_labels, periods_count); for(int i = 0; i < periods_count; i++) period_labels[i] = IntegerToString(i) + ":00"; IndicatorSetString(INDICATOR_SHORTNAME, "Seasonality by hours"); break; } ArrayResize(seasonality_data, periods_count); ArrayInitialize(seasonality_data, 0.0); IndicatorSetInteger(INDICATOR_DIGITS, ShowPercentage ? 2 : _Digits); return(INIT_SUCCEEDED); }
この関数は、インジケータの動作基盤を設定します。たとえば、曜日による分析の場合、「Mon」から「Sun」までのラベル配列が作成され、時間帯による分析の場合は「0:00」から「23:00」までのラベルが作成されます。また、インジケータ名も変更されるため、ユーザーは現在どの種類の季節性が選択されているのかをすぐに理解できます。これにより、インジケータは直感的に使えるようになります。
季節性の計算
主な計算はOnCalculate関数内でおこなわれます。この関数では、過去の価格データを分析し、各期間の平均リターンを計算し、描画用のバッファへデータを書き込みます。まず、チャート上に十分な数のバーが存在するかを確認します。その後、各期間のリターン合計とバー数を保存するための補助配列を作成します。
各バーについて、リターンは、隣接する終値の差を基準となる現在バーの終値で割って計算されます。GetPeriodIndex関数によって、そのバーがどの期間に属するのかが判定され、そのデータが配列へ保存されます。その後、平均リターンが計算されます。必要に応じて、その値はパーセントへ変換されます。また、負の値だけを表示しない設定が有効な場合は、負の値はゼロにリセットされます。最後に、バッファへデータが設定され、予測処理をおこなう専用関数が呼び出されます。
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total < BarsToAnalyze + 1) return(0); ArrayInitialize(seasonality_data, 0.0); double period_returns[]; int period_counts[]; ArrayResize(period_returns, periods_count); ArrayResize(period_counts, periods_count); ArrayInitialize(period_returns, 0.0); ArrayInitialize(period_counts, 0); int start_pos = MathMax(0, rates_total - BarsToAnalyze - 1); for(int i = start_pos; i < rates_total - 1; i++) { double return_value = 0.0; if(close[i] != 0) return_value = (close[i+1] - close[i]) / close[i]; int period_index = GetPeriodIndex(time[i]); if(period_index >= 0 && period_index < periods_count) { period_returns[period_index] += return_value; period_counts[period_index]++; } } for(int i = 0; i < periods_count; i++) { if(period_counts[i] > 0) { seasonality_data[i] = period_returns[i] / period_counts[i]; if(ShowPercentage) seasonality_data[i] *= 100.0; if(ShowPositiveOnly && seasonality_data[i] < 0) seasonality_data[i] = 0.0; } } for(int i = 0; i < rates_total; i++) { int period_index = GetPeriodIndex(time[i]); if(period_index >= 0 && period_index < periods_count) { SeasonalityBuffer[i] = seasonality_data[period_index]; ForecastLineBuffer[i] = seasonality_data[period_index]; } else { SeasonalityBuffer[i] = 0.0; ForecastLineBuffer[i] = 0.0; } ForecastPointsBuffer[i] = EMPTY_VALUE; } if(ShowForecastOnChart) DrawForecastOnChart(rates_total, time); return(rates_total); }
このコードは、インジケータの中心となる部分です。生の価格データを、意味のあるパターンへ変換します。たとえば、ある通貨ペアの日足チャートを分析していると想像してください。インジケータは、曜日ごとに価格がどれだけ変化したのかを計算し、その平均値を求めます。もし、金曜日には市場が平均で0.05%下落していることが分かった場合、それはトレード戦略を構築するためのシグナルになる可能性があります。
期間の定義
各バーがどの期間に属しているのかを理解するために、GetPeriodIndex 関数を作成します。この関数は、バーの時間情報を季節性タイプに応じた期間インデックスへ変換します。月の日付による分析の場合、日付番号を取得し、1を引くことで0から30までのインデックスを取得します。曜日による分析の場合、曜日の値(ここでは日曜日が0、月曜日が1)を、月曜日を0、日曜日を6とする曜日順へ変換します。時間帯による分析の場合は、0から23までの時間値をそのまま返します。
int GetPeriodIndex(datetime bar_time) { MqlDateTime dt; TimeToStruct(bar_time, dt); switch(SeasonalityType) { case SEASONALITY_DAYS_OF_MONTH: return(dt.day - 1); case SEASONALITY_DAYS_OF_WEEK: return(dt.day_of_week == 0 ? 6 : dt.day_of_week - 1); case SEASONALITY_HOURS: return(dt.hour); } return(-1); }
この関数は、インジケータがバーのデータをどの「箱」に分類すべきかを理解するためのコンパスのような役割を果たします。これがなければ、価格データを特定の日付や時間帯と関連付けることはできません。
予測の描画
予測機能こそが、このインジケータを単なる分析ツールではなく、実際に利用可能なツールにしています。DrawForecastOnChart関数は、チャートの最後の2本のバー上に予想される季節性の値を描画します。この関数では、現在の期間と次の2つの期間を、GetNextPeriod関数を使用して判定します。これらの期間に対応する季節性の値は、予測ライン用バッファと予測ポイント用バッファへ書き込まれます。予測ラインを滑らかに表示するために、現在の期間の値は最後から2番目のバーのバッファにも書き込まれます。
void DrawForecastOnChart(int rates_total, const datetime &time[]) { if(rates_total < 3) return; datetime current_time = time[rates_total - 1]; int current_period = GetPeriodIndex(current_time); int next_period1 = GetNextPeriod(current_period); int next_period2 = GetNextPeriod(next_period1); int forecast_pos1 = rates_total - 2; int forecast_pos2 = rates_total - 1; if(next_period1 >= 0 && next_period1 < periods_count) { ForecastLineBuffer[forecast_pos1] = seasonality_data[next_period1]; ForecastPointsBuffer[forecast_pos1] = seasonality_data[next_period1]; } if(next_period2 >= 0 && next_period2 < periods_count) { ForecastLineBuffer[forecast_pos2] = seasonality_data[next_period2]; ForecastPointsBuffer[forecast_pos2] = seasonality_data[next_period2]; } if(rates_total >= 3) { int current_pos = rates_total - 3; int current_period_idx = GetPeriodIndex(time[current_pos]); if(current_period_idx >= 0 && current_period_idx < periods_count) { double current_value = seasonality_data[current_period_idx]; double next_value = (next_period1 >= 0) ? seasonality_data[next_period1] : current_value; ForecastLineBuffer[current_pos] = current_value; if(forecast_pos1 < rates_total) ForecastLineBuffer[forecast_pos1] = next_value; } } }
予測とは、未来を覗き見るようなものです。このインジケータは、過去のデータに基づいて、今後何が起こる可能性があるのかを示します。たとえば、火曜日には通常+0.07%のリターンが発生している場合、インジケータはその値をチャート上に表示し、トレーダーが事前に準備できるようにします。
次の期間の計算
GetNextPeriod関数は、現在の期間の次に続く期間を決定します。この関数では、循環的な移行を実現するために剰余演算を使用しています。つまり、期間が最後まで到達した場合、最初へ戻ります。たとえば、月の日付の場合は31日の次に1日が続きます。曜日の場合は、日曜日の次に月曜日が続きます。時間帯の場合は、23:00の次に0:00へ戻ります。
int GetNextPeriod(int current_period) { if(current_period < 0) return(-1); switch(SeasonalityType) { case SEASONALITY_DAYS_OF_MONTH: return((current_period + 1) % 31); case SEASONALITY_DAYS_OF_WEEK: return((current_period + 1) % 7); case SEASONALITY_HOURS: return((current_period + 1)rate_total % 24); } return(-1); }
この関数は、予測が常に正しい未来の期間を参照することを保証し、循環的な時間構造を維持します。これは正確な市場カレンダーのような役割を果たし、月の日付、曜日、または時間帯の変化に関係なく、インジケータがある期間から次の期間へ正しく移動できるようにします。市場を巨大な時計のようなものとして考えてみてください。そこでは、すべてのティックが単なる時間の経過だけでなく、潜在的な価格変動も意味しています。月の31日が終わると、この関数は滑らかに1日へ戻ります。日曜日の後には月曜日へ移動し、23:00の後には0:00へ戻ります。この循環的なアプローチは、金融市場における実際の時間構造を反映しています。そのため、インジケータはトレーダーにとって重要になる可能性のある期間を予測できます。
統計情報の表示

トレーダーが分析結果をすぐに理解できるようにするため、インジケータはチャート上に詳細な統計情報を直接表示します。ShowStatistics関数はテキストメッセージを作成します。このメッセージは、語り手のように単なる数字の集合を、市場パターンに関する分かりやすいストーリーへ変換します。
まず、選択された季節性タイプに応じてタイトルが表示されます。それは、月の日付による分析、曜日による分析、または時間帯による分析のいずれであるかによって変化します。次に、分析されたバー数が表示されます。これにより、トレーダーは計算の基礎となっている過去データの深さを把握できます。その後、GetForecast関数から取得した予測値が追加されます。この予測は、過去の傾向に基づいて、次の2つの期間で市場がどのように動く可能性があるかを示します。続いて、この関数はseasonality_data配列を詳細に調べ、最も高いリターンを示した期間と、最も低いリターンを示した期間を探します。これらの期間はそれぞれ「最良期間」と「最悪期間」として表示され、その値は小数点以下3桁まで表示されるため、トレーダーはその重要性を判断できます。
最後に、詳細な統計情報が表示されます。各期間について、そのラベル(たとえば「Mon」や「15」)と平均リターンが表示されます。このテキスト情報はチャート右上にコンパクトな分析サマリーとして表示され、市場のリズムを理解しやすくし、トレード判断に役立つ情報へ変換します。
結論
金融市場における季節性は、神秘的な現象ではありません。それは数学的に確認可能なパターンであり、トレーダーの強力なツールとなる可能性があります。今回作成したインジケータは、混沌として見える価格変動を理解可能なパターンへ変換し、月の日付、曜日、または取引時間帯ごとの隠れた市場リズムを明らかにします。
柔軟なパラメータ設定、予測付きヒストグラムによる視覚的な結果表示、そして詳細な統計情報によって、このツールはトレーダーが過去のデータを分析するだけでなく、将来の価格変動について根拠のある推測をおこなうことを可能にします。予測要素を統合することで、このインジケータはトレード戦略の開発にも実際に利用できるものになります。
このインジケータのコードは、さまざまな資産や時間足へ簡単に適応できます。また、各関数について段階的に説明しているため、自分自身のトレードアイデアに合わせてアルゴリズムを変更することも可能です。最終的に、このインジケータは複雑な統計計算と具体的なトレード判断を結ぶ橋渡しとなります。データを知識へ変え、知識を潜在的な利益へつなげるものです。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/18672
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5におけるタイムギャップ分析(第2回):時間経過に伴う流動性分布のヒートマップ作成
初級から中級まで:オブジェクト(IV)
市場シミュレーション:MQL5におけるSQL入門(IV)
初級から中級まで:オブジェクト(III)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索