量化趋势分析:基于Python的统计建模
外汇市场量化趋势分析的本质
量化分析将混沌的市场波动转化为有序的数据与模式。量化趋势分析将混沌的市场波动转化为有序的数字与模式体系。当大多数交易者依赖直觉和看图说话时,数学方法在解析趋势变化上具有压倒性优势。用精准数据取代主观臆断:无论是趋势的平均持续天数、典型波动点数,还是趋势起止的特征模式,皆由数据定义。
这种客观性正是专业交易的核心基石。正如传奇交易员威廉·埃克哈特(William Eckhardt)所说,交易不是心理学领域,而是统计学领域。一旦您掌握欧元兑美元(EURUSD)上涨趋势的平均持续时间比下跌趋势更长,或者英镑兑美元(GBPUSD)70%的趋势在触及200点前终结,这些信息便不再枯燥,而是直接指导交易策略的实战依据。
为何趋势统计至关重要
试想在雾中行车。如果无精密仪器,只能依赖模糊视野。交易中的量化分析,正是那套让隐性信息无所遁形的工具。掌握价格波动的平均特征,交易者便能更精准地设定止盈、优化风控,并把握最佳持仓周期。
但量化方法的核心优势,在于其假设检验能力。无需空谈”趋势是朋友”或”市场终将回归”这类口号,您完全可以通过数学方法,针对特定时段的特定货币对,验证这些命题的真伪。
分析工具架构
我们的量化趋势分析系统采用Python开发,通过MetaTrader 5库直接获取市场数据,这使您能够使用最新信息,并基于各种时间框架和货币对进行研究。该程序的核心组件概览:
初始化与数据获取。
import MetaTrader5 as mt5 import pandas as pd import numpy as np import matplotlib matplotlib.use('Agg') # Non-interactive mode for saving charts import matplotlib.pyplot as plt from datetime import datetime, timedelta import pytz import os def initialize_mt5(): """Initializing connection to MetaTrader5""" if not mt5.initialize(): print("Initialization error MT5") mt5.shutdown() return False return True def get_eurusd_data(timeframe=mt5.TIMEFRAME_D1, bars_count=1000): """Downloading EURUSD historical data""" # Set time zone to UTC timezone = pytz.timezone("UTC") utc_now = datetime.now(timezone) # Get data eurusd_data = mt5.copy_rates_from_pos("EURUSD", timeframe, 0, bars_count) if eurusd_data is None or len(eurusd_data) == 0: print("Error retrieving EURUSD data") return None # Convert into pandas DataFrame df = pd.DataFrame(eurusd_data) # Convert time from timestamp to datetime df['time'] = pd.to_datetime(df['time'], unit='s') # Set time as index df.set_index('time', inplace=True) return df
该代码块负责初始化与MetaTrader 5终端连接并下载历史数据。我们使用Pandas库处理时间序列数据,以简化数据操作并执行各种计算。
关键在于选择合适的时间框架。日线图(TIMEFRAME_D1)适合长期趋势策略; 小时图(TIMEFRAME_H4/H1)适合中期波段策略;分钟图(TIMEFRAME_M30/M15)适合短期日内策略。不同时间框架下的趋势特征迥异,组合多周期数据可构建多层级策略。
趋势识别
该分析工具的核心是趋势识别算法。其执行几项关键任务:识别局部高点和低点,确定这些极值的序列,并基于它们生成趋势。每个趋势的特征包括其类型(上升趋势或下降趋势)、起始和结束日期、起点和终点的价格、持续时间和幅度。
def identify_trends(df, window_size=5): """ Identification of local highs and lows to identify trends window_size: the size of a window for searching for local extremes """ # Copy DataFrame so as not to change the original df_trends = df.copy() # Search for local highs and lows df_trends['local_max'] = df_trends['high'].rolling(window=window_size, center=True).apply( lambda x: x[window_size//2] == max(x), raw=True ).fillna(0).astype(bool) df_trends['local_min'] = df_trends['low'].rolling(window=window_size, center=True).apply( lambda x: x[window_size//2] == min(x), raw=True ).fillna(0).astype(bool) # Make structures for storing information about trends trends = [] current_trend = {'start_idx': 0, 'start_price': 0, 'end_idx': 0, 'end_price': 0, 'type': None} local_max_idx = df_trends[df_trends['local_max']].index.tolist() local_min_idx = df_trends[df_trends['local_min']].index.tolist() # Combine and sort highs and lows by index all_extremes = [(idx, 'max', df_trends.loc[idx, 'high']) for idx in local_max_idx] + \ [(idx, 'min', df_trends.loc[idx, 'low']) for idx in local_min_idx] all_extremes.sort(key=lambda x: x[0]) # Filter recurring local extremes filtered_extremes = [] for i, extreme in enumerate(all_extremes): if i == 0: filtered_extremes.append(extreme) continue last_extreme = filtered_extremes[-1] if last_extreme[1] != extreme[1]: # Different types (highs and lows) filtered_extremes.append(extreme) # Create trends based on sequence of extremes for i in range(1, len(filtered_extremes)): prev_extreme = filtered_extremes[i-1] curr_extreme = filtered_extremes[i] trend = { 'start_date': prev_extreme[0], 'start_price': prev_extreme[2], 'end_date': curr_extreme[0], 'end_price': curr_extreme[2], 'duration': (curr_extreme[0] - prev_extreme[0]).days, 'points': abs(curr_extreme[2] - prev_extreme[2]) * 10000, # Convert to points 'type': 'Uptrend' if curr_extreme[1] == 'max' else 'Downtrend' } # Add percentage change trend['percentage'] = (abs(trend['end_price'] - trend['start_price']) / trend['start_price']) * 100 trends.append(trend) return pd.DataFrame(trends)
需要重点关注window_size参数。它决定了局部极值的搜素窗口大小,直接影响识别出的趋势数量与特征。该参数值较小时,程序会捕捉大量的短期趋势;参数值较大时,则捕捉较少的长期走势。这样能够灵活适配不同的交易策略和时间跨度。
滑动窗口方法的效率
相较于其他方法,滑动窗口算法具备三大核心优势。首先,逻辑直观:将局部高点定义为窗口内价格高于相邻点的位置。其次,计算高效,能够快速处理大量数据。第三,通过改变窗口大小,可以轻松调整算法灵敏度。
然而,此方法也具有局限性。例如,它可能会遗漏一些不符合局部极值定义的特殊价格形态。为弥补这一局限,未来版本计划引入小波分析或时间序列分割等辅助趋势识别方法。
统计分析与可视化
完成趋势识别后,量化分析才真正展现威力 —— 对检索的数据进行统计处理。程序计算趋势持续时间和波动幅度的平均值,分析其方向分布,并以多种图表形式输出可视化结果:
def analyze_trends(trend_df, output_dir): """Trend analysis and statistics printing with saving of results""" if trend_df.empty: print("No data available for trend analysis") return # Create file to save statistics stats_file = os.path.join(output_dir, 'trend_statistics.txt') with open(stats_file, 'w', encoding='utf-8') as f: # Basic statistics on all trends header = "=" * 50 + "\nOverall trend statistics:\n" + "=" * 50 print(header) f.write(header + "\n") stats_text = f”Total number of trends: {len(trend_df)}\n" stats_text += f"Mean trend duration: {trend_df['duration'].mean():.2f} days\n" stats_text += f"Mean trend value: {trend_df['points'].mean():.2f} points\n" stats_text += f"Mean trend value: {trend_df['percentage'].mean():.2f}%\n" print(stats_text) f.write(stats_text) # Statistics on trend types up_trends = trend_df[trend_df['type'] == 'Uptrend'] down_trends = trend_df[trend_df['type'] == 'Downtrend'] up_stats = "Statistics on upward trends:\n" up_stats += f"Quantity: {len(up_trends)}\n" if not up_trends.empty: up_stats += f"Mean duration: {up_trends['duration'].mean():.2f} days\n" up_stats += f"Mean value: {up_trends['points'].mean():.2f} points\n" up_stats += f"Mean value: {up_trends['percentage'].mean():.2f}%\n" up_stats += f"Maximum value: {up_trends['points'].max():.2f} points\n" up_stats += f"Minimum value: {up_trends['points'].min():.2f} points\n" print(up_stats) f.write(up_stats) # Statistics on downtrends and other categories of trends...
多种可视化方案的深度分析
可视化是数据分析的核心。图表将枯燥数据转化为直观图像,助您快速识别模式。本工具实现了多种可视化类型:
def save_distribution_plots(trend_df, output_dir): """Creating and saving additional distribution charts""" # 1. Chart of trend distribution by magnitude plt.figure(figsize=(12, 8)) up_trends = trend_df[trend_df['type'] == 'Uptrend'] down_trends = trend_df[trend_df['type'] == 'Downtrend'] plt.hist([up_trends['points'], down_trends['points']], bins=10, alpha=0.7, color=['green', 'red'], label=['Uptrends', 'Downtrends']) plt.title('Distribution of trend values (in points)') plt.xlabel('Trend value (points)') plt.ylabel('Number of trends') plt.grid(True, alpha=0.3) plt.legend() plt.tight_layout() plt.savefig(os.path.join(output_dir, 'trend_magnitude_distribution.png')) plt.close() # 2. Chart of trend distribution by duration plt.figure(figsize=(12, 8)) plt.hist([up_trends['duration'], down_trends['duration']], bins=10, alpha=0.7, color=['green', 'red'], label=['Uptrends', 'Downtrends']) plt.title('Distribution of trend duration (in days)') plt.xlabel('Trend duration (days)') plt.ylabel('Number of trends') plt.grid(True, alpha=0.3) plt.legend() plt.tight_layout() plt.savefig(os.path.join(output_dir, 'trend_duration_distribution.png')) plt.close() # 3. Chart of the ratio of duration and magnitude of trends plt.figure(figsize=(12, 8)) plt.scatter(up_trends['duration'], up_trends['points'], color='green', alpha=0.7, label='Uptrends') plt.scatter(down_trends['duration'], down_trends['points'], color='red', alpha=0.7, label='Downtrends') plt.title('Ratio of duration and magnitude of trends') plt.xlabel('Trend duration (days)') plt.ylabel('Trend magnitude (points)') plt.grid(True, alpha=0.3) plt.legend() plt.tight_layout() plt.savefig(os.path.join(output_dir, 'trend_correlation.png')) plt.close()
每种图表都提供了对趋势特征的独特分析。趋势幅度直方图展示了趋势按点数大小的分布情况。针对特定货币对,能够帮助您设定切实可行的盈利目标。
趋势持续时间直方图展示统计趋势通常持续的天数。这些信息对于确最优持仓周期至关重要。持续时间与幅度的相关性图表有助于您理解趋势持续时长与其最终强度之间是否存在关联。
在价格图表上显示趋势
除统计图表外,程序还支持在价格图表上直接标注识别结果。这使您能清晰地看到程序识别出的趋势,并评估算法的质量:
def plot_trends(df, trend_df, output_dir): """Visualization of trends on a chart with saving""" plt.figure(figsize=(15, 10)) # Main price chart plt.subplot(2, 1, 1) plt.plot(df.index, df['close'], label='EURUSD Close', color='blue', alpha=0.7) plt.title('EURUSD Trend Analysis') plt.grid(True, alpha=0.3) plt.legend() # Mark local highs and lows for _, trend in trend_df.iterrows(): start_color = 'green' if trend['type'] == 'Uptrend' else 'red' end_color = 'red' if trend['type'] == 'Downtrend' else 'green' # Starting point of trend plt.scatter(trend['start_date'], trend['start_price'], color=start_color, s=50) # Endpoint of trend plt.scatter(trend['end_date'], trend['end_price'], color=end_color, s=50) # Trend line plt.plot([trend['start_date'], trend['end_date']], [trend['start_price'], trend['end_price']], color='orange', alpha=0.5, linestyle='--') # The second distribution chart # ...
高级统计趋势分析
对平均值和分布的简要分析仅仅是开始。要真正深入理解趋势特征,有必要使用更复杂的统计方法。在未来的程序版本中,计划实现趋势季节性分析、用于趋势预测的马尔可夫模型,以及货币对之间的相关性分析。
在趋势走势方面,许多货币对表现出季节性模式。例如,一些研究表明,在夏季月份,EURUSD的波动率以及随之趋势的幅度会下降。季节性分析将有助于我们识别此类模式,并根据季节调整交易策略。
马尔可夫链是一种数学工具,用于对趋势序列进行建模。如果当前为下降趋势,那么下一趋势转为上涨的概率是多少?下一个趋势的幅度是否取决于前一个趋势的幅度?马尔可夫模型将帮助回答这些问题,并构建市场发展的概率预测。
不同货币对通常在趋势走势上表现出相关性。例如,如果EURUSD开始出现强劲上升趋势,那么通常GBPUSD也会观察到类似的走势。货币对之间趋势的交叉相关性分析可为多货币策略的开发提供有价值的信息。
分析结果在交易中的应用
这些统计数据绝非枯燥的数字游戏,而是交易者的实战指南。例如,如果分析显示EURUSD的上升趋势平均为280点,下降趋势为220点,这将显著影响盈利目标和止损的设置。
指标参数优化
许多流行的技术分析指标,如移动平均线或震荡指标,需要指定计算周期。人们往往随意选取指标周期(如14、20、50等),却忽视了特定货币对的个性特征。量化趋势分析允许您依据市场实况,量身定制指标周期。
例如,如果分析显示EURUSD的趋势平均持续时间为15天,那么使用15周期移动平均线比使用标准的20周期移动平均线更合理。对于快速移动平均线,最优周期为8-10天;对于慢速移动平均线,则为16-20 天。14天的ADX周期将使我们能够有效跟踪当前趋势,并且设置抛物线转向指标(Parabolic SAR)为step=0.02和maximum=0.2,用来匹配识别出的趋势特征。基于客观数据微调指标参数,可显著提升其性能表现。
基于统计的入场和出场策略开发
趋势分布统计可用于开发市场入场和出场系统。基于趋势的典型持续时间,您可以开发一种策略,即使在未达到目标盈利的情况下,也能在持有一定天数后自动平仓。此举可避免潜在盈利因趋势反转而转为亏损的情况。
最有效的策略或许是在不同的盈利水平上部分平仓。当获得相对较小但极有可能实现的利润时,平掉该仓位的第一部分;当达到给定货币对的平均趋势价位时,平掉该仓位的第二部分;剩余部分则设置跟踪止损,以捕捉可能出现的大趋势行情。
自适应交易系统
量化分析的一大核心价值,在于能构建自适应交易系统,这类系统可以根据市场当前状态调整自身参数。例如,如果分析显示,在高波动率时期,平均趋势值会增长40%,那么当系统检测到此类市场条件时,便可自动调整盈利目标。
自适应策略的另一个例子是根据趋势类型调整参数。如果统计数据显示,平均而言,上涨趋势持续时间更长且幅度大于下跌趋势,那么系统可针对不同类型的趋势应用不同的入场和出场规则。
EURUSD趋势分析结果
方法与数据
本次EURUSD量化趋势分析基于小时时间框架(H1),采用的历史数据涵盖2018年2月至2025年5月。研究分析45,000根价格K线。涵盖超过7年的交易历史。研究采用100根K线的时间窗口作为趋势识别参数,这使我们能够识别出中长期趋势走势,同时过滤掉市场噪音和短期价格波动。
总体趋势统计数据
在所考虑的时间段内,该算法识别出471次趋势走势,方向分布近乎均匀:236次上涨趋势和235次下跌趋势。这种均衡表明EURUSD无长期方向偏好,印证了其结构上的中性特征。

趋势平均持续时间为5.11天,以小时时间框架计算,相当于约120小时的持续单向走势。对于中期交易者而言,这是一项关键指标,直接决定了最佳持仓周期。
平均趋势值为167.37点(占价格的1.51%),这代表了一次显著的价格波动。如果资金管理得当,这样的波动足以带来可观的利润。值得注意的是,平均而言,下跌趋势比上涨趋势略强(170.09点对164.67点),这或许折射出市场心态的不对称性:恐慌性抛售往往比上涨行情更为猛烈。
按幅度划分的趋势分布
对按幅度划分的趋势分布进行分析后发现,大部分走势(51.59%)落在100 - 200点的范围内。这一信息对设定盈利目标至关重要:正是在这个范围内,目标实现概率与潜在盈利幅度之间达到了“黄金平衡”。
幅度在50 - 100点的趋势占总数的19.11%,而幅度超过200点的走势占25.47%。极端强劲的趋势(超过500点)尤为罕见,仅占所有识别出的走势的1.27%。

幅度小于50点的趋势数量较少(占3.82%),这归因于识别窗口规模较大(100根柱线),该窗口可过滤掉短期价格波动。
按持续时间划分的趋势分布
所有识别出的趋势中,超过一半(56.69%)的持续时间不超过5天。另有28.87%的趋势持续5至10天。因此,绝大多数趋势走势(85.56%)在10天内完成。持续时间超过10天的长期趋势则少得多(10 - 20天区间占8.92%,20 - 30天区间仅占0.64%)。

这些数据具有直接的实践应用价值:持仓超过10天会显著降低捕捉完整趋势走势的概率,因为此时大多数趋势已接近尾声。持仓的最佳周期为3 - 7天。
最强趋势分析
排名前五的最强趋势展现了显著超越平均特征的市场走势:
- 2022年11月的上涨趋势,持续12天,涨幅达751.20点(7.72%)
- 2022年6 - 7月的下跌趋势,持续21天,跌幅达653.60点(6.16%)
- 2018年4 - 5月的下跌趋势,持续22天,跌幅达591.10点(4.76%)
- 2025年2 - 3月的上涨趋势,持续10天,涨幅达587.10点(5.67%)
- 2020年5 - 6月的上涨趋势,持续10天,涨幅达513.30点(4.72%)
值得注意的是,这五个最强劲的趋势持续时间均超过平均水平(10 - 22天),且均发生在重大经济事件期间:新冠疫情(2020年)、通胀危机(2022年)以及2025年的特朗普关税战。
五个最强劲的趋势中,三个为上涨趋势,两个为下跌趋势,这表明两个方向均可能出现极端走势。
交易实践结论
基于上述分析,我们可以为EURUSD交易制定了一系列实践建议:
- 制定交易策略时,可以将150 - 180点作为目标盈利的平均趋势值。在约25%的情况下能够实现更远大的目标(超过200点)。
- 持仓的最佳时间为3 - 7天。不过,使用跟踪止损有助于捕捉更长、更强劲的走势。
- 为EURUSD小时图设置技术分析指标时,建议考虑平均趋势持续时间。对于移动平均线而言,最佳周期为100 - 120小时(与平均趋势持续时间对应)。
- 鉴于上涨趋势和下跌趋势的特征几乎相同,可采用对称策略进行双向交易。
- 应当特别关注波动率上升和重大经济事件期间,因为这些时期会出现最强劲的趋势走势。
趋势分布与网格策略问题
分析揭示了EURUSD趋势分布的一个显著特征 —— 分布右侧存在明显的“肥尾”。尽管大多数趋势(约70 - 75%)的幅度不超过200点,但由罕见但极其强劲的走势(达500 - 750点)构成了重要的统计部分。

这种分布结构对算法交易,尤其是网格策略造成根本性影响。数学分析表明,正是这种”肥尾”分布,使得逆势加仓的网格策略显得极度脆弱。
网格策略的原理是在价格与初始持仓反向移动时增开仓位。然而,从数学角度出发,该策略的问题在于:在资金固定与加仓次数有限的前提下,任何网格策略的”网格深度”都是有限的。我们识别出的最强趋势(500点以上)极有可能超出这一深度,从而导致灾难性损失。
关键因素在于风险的不对称性。网格策略短期的持续盈利,往往营造出一种可靠的假象。然而,一次极端的走势就可能摧毁所有累积的利润,导致资本彻底损失。从数学上讲,这一点可用预期收益公式来描述,其中预期结果E(R)等于事件概率与其结果乘积之和。
对于 EURUSD的网格策略,在75%的情况下(趋势幅度不超过200点),可观察到资本5 - 10%的正收益;在24%的情况下(趋势幅度在200 - 500点之间),收益接近0或为负,损失在10 - 30%;在1%的情况下(趋势幅度超过500点),会出现资本50 - 100%的灾难性损失。由于这种收益分布情况,即使局部盈利概率很高,但受罕见却极具破坏性的事件影响,长期运作数学期望就会变为负值。
顺势金字塔加仓:有效性的数学论证
鉴于网格策略的缺陷,量化分析证实了反向策略 —— 顺势金字塔加仓的数学合理性。该策略的重点在于顺应既有趋势,并逐步加仓。
金字塔加仓的数学优势在于利用了恰恰对网格策略不利的分布“肥尾”。关键因素在于趋势持续时间和幅度之间的正相关性。分析表明,长期趋势很可能幅度更大。这就意味着趋势发展时间越长,其进一步发展至显著水平的数学概率就越高。
在金字塔加仓过程中,相对于初始入场点,每个后续仓位都在盈利状态下开立。这样就产生了复利(复息)效应,即盈利不是线性增长,而是呈指数增长。在金字塔加仓过程中,罕见但极其强劲的趋势可产生不成比例的高额利润。例如,一个700点的趋势,如果进行三次金字塔加仓,其收益可相当于数十次常规趋势交易的成功操作收益。
金字塔加仓的预期收益数学公式结构也发生了变化:趋势越强劲,金字塔加仓点数越多,最终收益越高;随着趋势的发展,将止损点移至盈亏平衡点,每个后续仓位的风险可降低;严格遵守资金管理规则,最大损失将限制在存款的预定百分比以内。
要在算法交易中实际应用金字塔加仓,首次加仓的最优点位是走势80 - 100点,第二次加仓为150 - 170点,第三次加仓为200 - 250点。将所有仓位的整体风险限制在资本的2 - 3%以内,即使在连续多次交易失败的情况下,也能有效控制回撤,同时保留捕捉罕见但利润极高的价格极端值的机会。
因此,EURUSD趋势分布的量化分析表明,从数学上讲,顺势金字塔加仓是一种合理的策略,具有正向长期预期,而网格策略则因趋势分布的“肥尾”而存在致命的脆弱性。
结论
针对EURUSD H1量化趋势分析,揭示了该货币对一致性的运行模式。大多数趋势具有中期性质,持续时间约为5天,幅度为150 - 180点,这就为交易提供了重大的机遇。
研究结果为制定契合EURUSD实情的交易策略提供了客观依据。利用这些数据,交易者可以设定现实的盈利目标,优化持仓时间,并根据识别出的模式调整技术指标参数。
对于此项研究的进一步延伸可能包括分析趋势的季节性、经济事件对趋势走势特征的影响,以及开发能够根据当前市场环境调整参数的自适应交易系统。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/18035
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
从基础到中级:指标(三)
新手在交易中的10个基本错误
计算机视觉在MQL5中的集成(第一部分):构建基础函数
非常有趣的工作!不过,在我看来,交易既是统计学,也是心理学--如果仅仅是因为金融市场是经济行为系统的话。如今,这一点更加突出--例如,市场会对特朗普或马斯克的言论做出即时反应。