ホーム » FX自動売買基礎と応用 » 【MT5 API入門⑨】FX市場のカレンダーアノマリー分析

【MT5 API入門⑨】FX市場のカレンダーアノマリー分析


この記事では、カレンダーアノマリーを見つける方法を解説します。

カレンダーアノマリーは、分析方法が単純でわかりやすいことが多く、データに傾向が見つかった場合は、システムトレードにも組み入れやすい要素です。

多くのトレーダーが戦略に取り入れているだけでなく、論文でもこの手の研究は多く行われており、実用的な分野であると考えられます。

※この記事はAPIトレードに関する知識を提供することを目的としています。
技術的なお問い合わせは受け付けておりません。技術的な課題はお客様ご自身で解決をお願いいたします。

カレンダーアノマリーとは

セルインメイ(Sell in May and go away. But remember come back in September.)とは「5月に売って9月に買い戻す」と訳される株式相場の有名な格言です。

他にも「月替わり効果」と呼ばれる、月末や月初に株価が上昇しやすい現象もよく知られています。

このような日時情報に基づく価格変化の繰り返し観測されるパターンは、カレンダーアノマリーと呼ばれます。

単純にアノマリーというと「異常値」を示すわけですが、シーンによってはカレンダーアノマリーの意味で呼ばれることもあります。

また、カレンダーに載らない詳細な日時情報、例えば時間帯などにもアノマリーを見つけられる可能性があります。

価格が上昇しやすい月や下降しやすい日といった日時情報に基づくパターンをもし発見することができたら、そのパターンに合わせて買ったり売ったりするアノマリー戦略を運用したり、テクニカル分析などと組み合わせた戦略を構築したりすることも可能です。

また、機械学習を用いた戦略を視野に入れるなら、アノマリーに関する知識が特徴量エンジニアリングに活きることも考えられます。

FXにおけるアノマリー

FX市場でもアノマリーについて研究されており、ここではいくつかの例を挙げておきます。

・Intraday Patterns in FX Returns and Order Flow
https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2099321

この論文では、多くの通貨は現地の日中に下落する傾向があると報告されています。

例えばユーロ/米ドルでは、欧州の午前からニューヨーク市場のオープンまでの時間帯に価格が下降しやすいといった傾向を見つけています。

・Forex Trading Strategy That Might Be Executed Due to the Popularity of Gotobi Anomaly
https://arxiv.org/abs/2301.13204

こちらの論文では、五十日(5で割り切れる日)の東京仲値(日本時間午前9時55分)に向けて米ドル/円が上昇しやすい傾向があると報告されています。

また、テクニカル分析と組み合わせることで、収益性の向上につながる可能性についても考察されています。

なぜアノマリーが発生するのか

アノマリーは、経済合理性では説明できない規則性(経験則)である、というような説明がよくされます。

FX市場においては、投資家、投機家の注文だけではなく、各国の貿易決済や対外投資決済などさまざまな目的で注文が行われます。

東京仲値のアノマリーの発生は、五十日に清算する日本企業の商慣習が要因ではないかと考えられています。

このように、慣習や行動パターンなどが、アノマリーを発生させると考えることもできるのではないでしょうか。

日時でグループ化した平均リターン

今回のカレンダーアノマリーの分析では、以下のような4本値を含むpandasデータフレームを対象とし、主に終値を使っています。

MT5API_9_01

以下のようなコードで終値から対数リターンを作成します。


import numpy as np

rates_df["log_close"] = np.log(rates_df["close"])
rates_df["log_return"] = rates_df["log_close"].diff()

この対数リターンの傾向を日時情報に基づいて可視化していきます。


import matplotlib.pyplot as plt

# --- 月の列を作成 ---
# DatetimeIndex から month を抽出
rates_df["month"] = rates_df.index.month

# --- 月ごとの平均対数リターンを計算 ---
monthly_mean = rates_df.groupby("month")["log_return"].mean()

# --- グラフ描画 ---
plt.figure(figsize=(10, 5))
monthly_mean.plot(kind="bar")

plt.title("Average Monthly Log Returns")
plt.xlabel("Month")
plt.ylabel("Average Log Return")

plt.grid(True)
plt.show()

対象データの各月ごとの平均対数リターンを計算し、グラフ化しています。


rates_df["month"] = rates_df.index.month

コード内のこの部分でデータフレームの各行に「何月のデータか」を示す列「month」を作成しています。


monthly_mean = rates_df.groupby("month")["log_return"].mean()

続いてこの部分で月ごとにデータをグループ化し、対数リターンの平均を出しています。

MT5API_9_02

こちらは出力結果のグラフです。

横軸は月を表し、各月の平均対数リターンを示しています。

同じコードで「rates_df.index.month」を任意の期間に変更することで、「年、月、日、曜日、時、分」などさまざまな形式の日時情報を扱うことができます。

※「分」など細かいデータを扱う際は、元の終値データが短期の時間軸である必要があります。


rates_df["year"] = rates_df.index.year
rates_df["month"] = rates_df.index.month
rates_df["day"] = rates_df.index.day
rates_df["weekday"] = rates_df.index.weekday   # 0=月曜日
rates_df["hour"] = rates_df.index.hour
rates_df["min"] = rates_df.index.min

MT5API_9_03

こちらは、先ほどの「月」ベースの平均対数リターンの出力例を「日」ベースに変更したものです。

市場の特性を意識したアノマリー

ここまでは「年、月、日、曜日、時、分」といった一般的な日時情報に基づいた説明でしたが、目的が投資や投機であれば、それに適した形式を考慮できます。

具体的な例をいくつか挙げて解説します。

大統領選挙の周期

米国大統領選は4年の周期で行われます。

選挙結果や選挙に向けた政策などは、金融市場にも大きな影響をもたらす可能性があります。

ジェフリー・A・ハーシュ著『アノマリー投資 市場のサイクルは永遠なり』(パンローリング)の中でも米国大統領選挙の影響の大きさについての記述があります。

4年の周期が相場に影響しているかどうかを調査するための方法の1つとして、「年」を4で割ったあとの余りに注目します。

大統領選挙があった年「2024, 2020, 2016…」は4で割り切れる数値なので余りは0となり、大統領選挙の翌年(任期1年目)「2025, 2021, 2017…」は4で割ると余り1となります。

このような形で4年の周期をデータに反映することができます。


rates_df['presidential_cycle'] = rates_df.index.year % 4

data = rates_df.groupby("presidential_cycle")["log_return"].mean()

plt.figure(figsize=(10, 5))
data.plot(kind="bar")

plt.grid(True)
plt.show()

こちらのコードは「年」を4で割ったときの余りを「presidential_cycle」という列に入れ、4年の周期ごとの平均対数リターンを可視化しています。

MT5API_9_04

「index.year % 4」を使い、各年を4で割った余りを求めています。

これによって、

  • ●任期1年目 = 0
  • ●任期2年目 = 1
  • ●任期3年目 = 2
  • ●任期4年目 = 3(選挙年)

というようなデータを作成することができます。

年始からの週数

一年のサイクルで発生するアノマリーを分析する際は、データを「月」でまとめる方法以外に「年始からの週数」といった表現方法もあります。

システムトレードなどでは、同じような意味を持つデータであっても、「月」と「年始からの週数」のように表現方法を変えると結果が変わる可能性もあります。

いろいろな視点からデータを作成することを意識すると、データ分析の目的に応じた相性のいい形式を見つけられる可能性があります。

ということで、ここでは同じような考え方で「年始からの週数」も作成してみましょう。


rates_df["week_of_year"] = rates_df.index.isocalendar().week

monthly_mean = rates_df.groupby("week_of_year")["log_return"].mean()

plt.figure(figsize=(10, 5))
monthly_mean.plot(kind="bar")

plt.grid(True)
plt.show()

MT5API_9_05

「index.isocalendar().week」は「その日付が、その年の何週目かを返す」という機能です。

「week_of_year」という列に入れることで、これまでと同様にグルーブ化して可視化することが可能になります。

月初からの営業日数

日時情報をグループ化してアノマリーを可視化する方法として、「日」でまとめる方法はこの記事内で解説しましたが、単純に「日」でまとめると市場が閉まっている土日もカウントされてしまいます。

例えば、月初のアノマリーを分析したいとき、1日(ついたち)のデータを抽出すると、1日が土日になる月が条件から外れてしまいます。

月初アノマリーは、日付が1日ではなく「毎月最初の営業日」とする方が適切であると考えられます。


rates_df["year"] = rates_df.index.year
rates_df["month"] = rates_df.index.month
rates_df["weekday"] = rates_df.index.weekday

# 月初から営業日数を計算(土日を除く)
rates_df["bizday_in_month"] = (
    rates_df[rates_df["weekday"] < 5]           # 平日だけ
    .index.normalize()                           # 日付化
    .to_series()
    .groupby([rates_df["year"], rates_df["month"]])
    .cumcount() + 1
)

data = rates_df.groupby("bizday_in_month")["log_return"].mean()

plt.figure(figsize=(10, 5))
data.plot(kind="bar")

plt.grid(True)
plt.show()

MT5API_9_06

単純な日付でグラフを作成するとデータ数は1~31までになりますが、土日を除くことで1~23となっています。

※このコードは単純に土日を除いているだけの処理になっており、分析の目的によっては処理を変更する必要が生じる可能性があります。

ニューヨーク時間、ロンドン時間、東京時間

FX市場は世界中に参加者がいるため、各国の市場の影響を受けます。

ニューヨーク市場、ロンドン市場、東京市場は三大市場とも呼ばれ、取引量が多く、それぞれの市場オープン直後は特に流動性が集中する重要な時間帯となります。

世界標準の基準時刻(UTC)でも各国の時差を考慮して分析は可能ですが、ニューヨーク市場、ロンドン市場、東京市場では、それぞれ異なるタイムゾーンが採用されています。

つまり、重要な時間帯である三大市場の市場オープンを正確に検証するには、それぞれのタイムゾーンを反映したデータが必要となります。


rates_df = rates_df.tz_localize('UTC')
rates_df["NY_time"] = rates_df.index.tz_convert("America/New_York")
rates_df["LON_time"] = rates_df.index.tz_convert("Europe/London")
rates_df["TOKYO_time"] = rates_df.index.tz_convert("Asia/Tokyo")

rates_df["NY_hour"] = rates_df["NY_time"].dt.hour
rates_df["LON_hour"] = rates_df["LON_time"].dt.hour
rates_df["TOKYO_hour"] = rates_df["TOKYO_time"].dt.hour

「tz_convert」は「すでにタイムゾーン情報を持っている日時を、別のタイムゾーンの現地時刻に変換する」といったpandasの機能です。

tz_convertはタイムゾーン付きのデータフレームにしか使えません。

そのため、最初に「tz_localize('UTC')」を実行して、「この時刻はUTCである」という情報を与え、その後にニューヨーク、ロンドン、東京のタイムゾーンが反映された時刻に変換しています。

現地時刻が正しく反映されているかを確認するためのコードを実行してみましょう。


sample = rates_df.sample(10)[["NY_hour", "LON_hour", "TOKYO_hour"]]
print(sample)

MT5API_9_07

全行からランダムに10行を抽出して表示し、夏時間と冬時間で正しく時刻がずれていることを簡易的に確認できます。

アノマリーの累積グラフ

最後に別の可視化の方法を試してみましょう。

この記事の序盤に「通貨の価格は、現地の日中に下落する傾向がある」という論文を紹介しましたが、今回はその傾向を確認してみます。

「rates_df」にはユーロ/米ドルの対数リターンが入っている前提で以下のようなコードを実行します。


mask = (rates_df["LON_hour"] >= 9) & (rates_df["LON_hour"] <= 14)
df_london = rates_df.loc[mask].copy()

plt.figure(figsize=(10, 5))
plt.plot(df_london.index, , df_london["log_return"].cumsum() label="London 9–14h Session")

plt.title("Cumulative Log Return (London 9–14h Session)")
plt.grid(True)
plt.legend()
plt.show()

MT5API_9_08

これまでは主に平均対数リターンを可視化していましたが、ここではロンドンの日中にあたる時間帯の対数リターンを切り出して、その累積グラフを表示しています。


mask = (rates_df["LON_hour"] >= 9) & (rates_df["LON_hour"] <= 14)
df_london = rates_df.loc[mask].copy()

コード内のこの部分で、ロンドン現地時間で9時~14時(ロンドンの朝からニューヨークオープン前後まで)を切り取っています。

最後に単純な対数リターン(log_return)を表示するのではなく、「df_london["log_return"].cumsum()」とすることで、対数リターンの累積値を可視化しています。

対数リターンの累積グラフを表示することで、アノマリーが長期的に安定して発生しているか、戦略として利用できるものかどうかの判断につながるかもしれません。

【MT5 API入門】

本記事の執筆者:藍崎@システムトレーダー

               
本記事の執筆者:藍崎@システムトレーダー 経歴
藍崎@システムトレーダー個人投資家としてEA開発&システムトレード。
トレードに活かすためのデータサイエンス / 統計学 / 数理ファイナンス / 客観的なデータに基づくテクニカル分析 / 機械学習 / MQL5 / Python

EA(自動売買)を学びたい方へオススメコンテンツ

EA運用の注意点

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。


本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。

この記事をシェアする