利用箱形图(Boxplot)探索金融时间序列的季节性形态(基础篇)
📘

利用箱形图(Boxplot)探索金融时间序列的季节性形态(基础篇)

第 1/3 篇

用箱形图扒开季节性的底

在 MT5 里看 EURUSD 的日线,很多人只盯均线纠缠,却忽略了每个月价格波动的离散程度差异。把 2015—2019 年的收盘价按日历月分组,画成箱形图,能直观看到 12 月箱体中位数偏高且须线拉长,说明年末波动扩张的概率不低。 箱形图的四分位距(IQR)比单纯算标准差更抗极端值干扰。对贵金属 XAUUSD 同样适用:统计同段样本,8 月箱体常压扁、中位数贴近下沿,反映夏季流动性薄、窄幅震荡倾向。外汇与贵金属杠杆高,季节性只是概率线索,不能直接当方向指令。 实操上,开 MT5 的自定义指标加载历史分组,先验证自己交易品种的月度箱体,再决定挂单间距。别把正态当圣经,非正态的厚尾才是真实账户 killers。

◍ 弱式有效也挡不住周期痕迹

  • 年尤金·法玛因有效市场假说拿下诺贝尔经济学奖,核心论点是资产价格已充分反映重要信息,没人能稳定占优。但该假说留了口子:效率分三档,弱式只消化历史价量,半强式加进当前公开信息,强式连内部信息也折算进报价。

对做价格行为的人而言,弱式有效反而最值得盯——它承认过去信息已体现在价格里,却没说这些信息排列毫无节奏。法玛自己框定的弱式前提,正好给季节性周期留了生存缝。 实际盘面上,年、月、季、时各级别活跃度常有差异,这种差异不是随机噪声。多个周期叠加会拼出可识别的合成形态,MT5 里调出 EURUSD 的月线成交量就能肉眼验证年年有别。 外汇与贵金属属高杠杆品种,周期倾向只是概率优势,不等于方向锁死,拿真金白银去试前先开模拟盘跑一遍。

「用 EURUSD 日线剥出月度季节壳」

原始收盘价序列带着长期趋势和通胀偏移,直接做统计会被均值漂移带偏;计量经济学里更稳的做法是取一阶百分比变化(price returns),把所有波动压到同一量纲再谈季节。 下面这段把 MT5 终端和 IPython 打通:先指定你本机的 terminal64.exe 路径(别照抄我的盘符),用 MT5CopyRatesRange 拉取 EURUSD 的 D1 数据,时间窗设成 2010-01-01 到 2020-01-01,正好 10 年样本。rates 拿到后只留 time 和 close,用 pct_change(1) 算日度增幅。 [CODE] from MetaTrader5 import * from datetime import datetime import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline import seaborn; seaborn.set() # Initializing MT5 connection MT5Initialize("C:\Program Files\MetaTrader 5\terminal64.exe") MT5WaitForTerminal() print(MT5TerminalInfo()) print(MT5Version()) rates = pd.DataFrame(MT5CopyRatesRange("EURUSD", MT5_TIMEFRAME_D1, datetime(2010, 1, 1), datetime(2020, 1, 1)), columns=['time', 'open', 'low', 'high', 'close', 'tick_volume', 'spread', 'real_volume']) # leave only 'time' and 'close' columns rates.drop(['open', 'low', 'high', 'tick_volume', 'spread', 'real_volume'], axis=1) # get percent change (price returns) returns = pd.DataFrame(rates['close'].pct_change(1)) returns = returns.set_index(rates['time']) returns = returns[1:] returns.head(5) Monthly_Returns = returns.groupby([returns.index.year.rename('year'), returns.index.month.rename('month')]).mean() Monthly_Returns.boxplot(column='close', by='month', figsize=(15, 8)) [/CODE] 代码逐行拆解:MT5Initialize 那行建立与终端的链接,路径需按你安装目录改;MT5CopyRatesRange 第二个参数是周期枚举,D1 即日线;rates.drop 按 axis=1 删掉除 time/close 外的六列,减少噪声;pct_change(1) 算相邻日收盘价百分比差,即单日回报;set_index 把时间设为索引后 returns[1:] 去掉首行 NaN;groupby 用年、月双层分组求均值,最后 boxplot 按 month 画箱形图,纵轴是 close 列的月度平均增幅。 跑完你会得到一张 1–12 月箱形图,横轴月份、纵轴是十年里该月日回报均值分布。外汇与贵金属属高风险品类,样本内形态只代表历史概率,实盘前请在 MT5 换品种、换周期自查。若某月箱体明显偏离零线,说明该月倾向存在弱季节性偏移,可据此做假设而非结论。

MQL5 / C++
from MetaTrader5 class="kw">import *
from class="type">class="kw">datetime class="kw">import class="type">class="kw">datetime
class="kw">import numpy as np
class="kw">import pandas as pd 
class="kw">import matplotlib.pyplot as plt 
%matplotlib class="kw">inline
class="kw">import seaborn; seaborn.set()
# Initializing MT5 connection 
MT5Initialize("C:\Program Files\MetaTrader class="num">5\terminal64.exe")
MT5WaitForTerminal()
print(MT5TerminalInfo())
print(MT5Version())
rates = pd.DataFrame(MT5CopyRatesRange("EURUSD", MT5_TIMEFRAME_D1, class="type">class="kw">datetime(class="num">2010, class="num">1, class="num">1), class="type">class="kw">datetime(class="num">2020, class="num">1, class="num">1)), 
                    columns=[&class="macro">#x27;time&class="macro">#x27;, &class="macro">#x27;open&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;close&class="macro">#x27;, &class="macro">#x27;tick_volume&class="macro">#x27;, &class="macro">#x27;spread&class="macro">#x27;, &class="macro">#x27;real_volume&class="macro">#x27;])
# leave only &class="macro">#x27;time&class="macro">#x27; and &class="macro">#x27;close&class="macro">#x27; columns
rates.drop([&class="macro">#x27;open&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;tick_volume&class="macro">#x27;, &class="macro">#x27;spread&class="macro">#x27;, &class="macro">#x27;real_volume&class="macro">#x27;], axis=class="num">1)
# get percent change(price returns)
returns = pd.DataFrame(rates[&class="macro">#x27;close&class="macro">#x27;].pct_change(class="num">1))
returns = returns.set_index(rates[&class="macro">#x27;time&class="macro">#x27;])
returns = returns[class="num">1:]
returns.head(class="num">5)
Monthly_Returns = returns.groupby([returns.index.year.rename(&class="macro">#x27;year&class="macro">#x27;), returns.index.month.rename(&class="macro">#x27;month&class="macro">#x27;)]).mean()
Monthly_Returns.boxplot(column=&class="macro">#x27;close&class="macro">#x27;, by=&class="macro">#x27;month&class="macro">#x27;, figsize=(class="num">15, class="num">8))

箱形图怎么读:五条线背后的分布

做价格行为分析时,想看一段区间内波动到底散成什么样,箱形图比烛条图更直接。它不画开盘收盘,而是用五个统计量把数据分布标准化摊开,外观像烛条但逻辑完全不同。 箱子里的绿色横线是中位数(Q2,第50百分位),代表这段样本的中间水平。箱体下边缘是Q1(第25百分位),上边缘是Q3(第75百分位),两者夹住的实体叫四分位间距(IQR),覆盖中间50%的数据。 须线向外延伸到样本99%置信区间,须线之外的点才是极端值。一套图下来,波动范围和内部离散度一眼可判,开MT5把这段数据拉出来画一次就懂。 外汇和贵金属波动受事件驱动,箱形图只描述历史分布,对跳空风险仅作概率提示,实盘仍需结合止损。

◍ 把月份和星期叠起来看才有用

图例 1 里五月的中位数增幅偏在零轴下方,十年里只有一年五月是涨的,异常值却高悬零轴上方,这和“五月卖出离场”的老话对得上,但只是概率倾向不是铁律。 六月紧接着几乎每年都回升(仅一年例外),波动带很窄且没有异常值,说明这段季节性比五月稳。十一月下跌概率高,十二月多数回弹,一月波动放大又相对十二月回落。 把月度周期拆到日线,零代表周一、四代表周五,按日波动率大体持平,但均值上周一和周五更偏下行。单看全月汇总会漏掉细节:一月份周五多数下跌,只有 EURUSD 那根异常值翻正;三月份是周二普遍收低、周一也偏空;十月份只有周三的波动和潜力最突出,其余日子多空几乎对称。 外汇和贵金属受季节与周内结构影响明显,但杠杆品种回撤风险极高,这些分布只能当仓位倾向的参考。改代码里的月份数字就能拉出任意月的日增幅分布,建议开 MT5 导十年日线自己跑一遍验证。

MQL5 / C++
Daily_Returns = returns.groupby([returns.index.week.rename(&class="macro">#x27;week&class="macro">#x27;), returns.index.dayofweek.rename(&class="macro">#x27;day&class="macro">#x27;)]).mean()
# leave only one month "returns.index[~returns.index.month.isin([class="num">1])"
returns = returns.drop(returns.index[~returns.index.month.isin([class="num">1])])

常见问题

重点看中位数线和上下四分位盒体,能直接对比不同月份价格分布的集中区间,须配合具体品种验证。
建议至少取 8~10 年日线,样本太少月份盒体会被极端值拉变形,季节性结论概率偏低。
小布盯盘已内置按月份剥壳的 AIGC 诊断,打开对应品种页即可直接看季节分布,不用自己导数据画图。
单看月份会混进星期效应,叠加后能分离出独立周期痕迹,弱式有效市场下仍可能残留可识别结构。
散点是极端离群价,贵金属和外汇高风险下不能忽视,可能对应流动性枯竭日的异常波动,需回看当时基本面。