
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含300篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
在量化交易领域,移动平均线是最常用的技术指标之一。然而,传统移动平均线面临一个两难困境:短周期响应快但容易产生虚假信号,长周期稳定但滞后严重。有没有一种方法能够两全其美呢?
分形自适应移动平均线(Fractal Adaptive Moving Average,简称 FRAMA)正是为解决这一问题而诞生的。它能够根据市场结构动态调整自身的响应速度——在趋势行情中快速跟随,在震荡行情中保持稳定。
本文将基于一篇最新的量化策略研究,带你了解 FRAMA 的核心原理,并提供完整的 Python 实现代码。
FRAMA 的理论基础是分形维度(Fractal Dimension),它用于衡量价格走势的复杂程度。算法将价格序列分为两半,然后比较各部分的波动范围与整体范围的关系。
分形维度的取值范围在 1 到 2 之间:
当市场处于趋势行情时,价格沿单一方向运动,复杂度低,维度接近 1。
当市场处于震荡行情时,价格上下波动,复杂度高,维度接近 2。
分形维度直接控制平滑系数 alpha,从而创建一个在趋势中加速、在噪音中减速的移动平均线。
FRAMA 有两个核心参数:
N(周期)表示计算分形维度的回溯窗口,常用值包括 10、20、40、60、80、100 天。
FC(快速常数)控制响应速度,数值越大适应越快,常用值包括 1、4、8、16、32。
下面是 FRAMA 指标的核心计算代码:
import numpy as npimport pandas as pdclass FRAMAIndicator: """分形自适应移动平均线指标类""" def __init__(self, n: int = 40, fc: int = 4): """ 初始化 FRAMA 指标 参数: n: 计算分形维度的回溯周期 fc: 快速常数,控制响应速度 """ self.n = n self.fc = fc def calculate(self, prices: pd.Series) -> pd.Series: """ 计算 FRAMA 值 参数: prices: 价格序列 返回: FRAMA 值序列 """ df = pd.DataFrame({'price': prices}) # 计算整个周期的最高价和最低价 high = df['price'].rolling(window=self.n).max() low = df['price'].rolling(window=self.n).min() # 计算半周期长度 n1 = self.n // 2 # 计算前半周期的最高价和最低价 high1 = df['price'].rolling(window=n1).max() low1 = df['price'].rolling(window=n1).min() # 计算后半周期的最高价和最低价 high2 = df['price'].shift(n1).rolling(window=n1).max() low2 = df['price'].shift(n1).rolling(window=n1).min() # 计算各部分的价格范围 n_range = (high - low) / self.n n1_range = (high1 - low1) / n1 n2_range = (high2 - low2) / n1 # 计算分形维度 dimension = np.log(n1_range + n2_range) - np.log(n_range) dimension = dimension / np.log(2) dimension = dimension.clip(1.0, 2.0) # 限制在 1 到 2 之间 # 根据分形维度计算平滑系数 alpha alpha = np.exp(-self.fc * (dimension - 1)) alpha = alpha.clip(0.01, 1.0) # 限制在合理范围内 # 计算 FRAMA 值 frama = pd.Series(index=prices.index, dtype=float) frama.iloc[:self.n] = np.nan for i in range(self.n, len(prices)): if pd.isna(alpha.iloc[i]) or pd.isna(frama.iloc[i-1]): frama.iloc[i] = prices.iloc[i] else: # FRAMA 的递推公式 frama.iloc[i] = alpha.iloc[i] * prices.iloc[i] + \ (1 - alpha.iloc[i]) * frama.iloc[i-1] return frama.dropna()基于 FRAMA 指标,我们可以构建一个简单的趋势跟踪策略:
import yfinance as yfdef simple_frama_strategy(ticker: str = 'SPY', start_date: str = '2020-01-01', end_date: str = '2024-12-31'): """ 简单的 FRAMA 交易策略示例 参数: ticker: 股票代码 start_date: 开始日期 end_date: 结束日期 返回: 策略收益率序列 """ # 获取股票数据 data = yf.download(ticker, start=start_date, end=end_date, progress=False) data.columns = [col[0] if isinstance(col, tuple) else col for col in data.columns] # 计算 FRAMA 指标 frama = FRAMAIndicator(n=40, fc=4) frama_values = frama.calculate(data['Close']) # 生成交易信号 signals = pd.Series(index=frama_values.index, dtype=float) for i in range(1, len(frama_values)): # 使用 T-1 时刻的数据生成 T 时刻的信号,避免未来函数 price_t1 = data['Close'].loc[frama_values.index[i-1]] frama_t1 = frama_values.iloc[i-1] if price_t1 > frama_t1: # 价格在 FRAMA 上方,做多 signals.iloc[i] = 1.0 elif price_t1 < frama_t1: # 价格在 FRAMA 下方,空仓 signals.iloc[i] = 0.0 else: # 价格等于 FRAMA,保持前一状态 signals.iloc[i] = signals.iloc[i-1] if i > 0 else 0.0 signals = signals.dropna() # 计算收益率 returns = data['Close'].pct_change().dropna() aligned_returns = returns.reindex(signals.index).dropna() signals = signals.reindex(aligned_returns.index) # 信号需要延迟一天,避免使用当天信号买入当天 strategy_returns = signals.shift(1).dropna() * aligned_returns strategy_returns = strategy_returns.dropna() return strategy_returns# 运行策略示例if __name__ == "__main__": returns = simple_frama_strategy() # 计算累计收益 cum_returns = (1 + returns).cumprod() total_return = cum_returns.iloc[-1] - 1 print(f"策略总收益率:{total_return*100:.2f}%")为了避免过度拟合,文章采用了前向分析(Walk-Forward Analysis)方法进行参数优化。这种方法的核心思想是:
首先使用历史数据进行参数优化(样本内期间)。
然后用优化后的参数在未来数据上进行测试(样本外期间)。
不断向前滚动这个过程,使用扩展窗口持续重新优化。
文章采用的具体设置是:初始训练期 3 年,样本外窗口 6 个月,每 6 个月重新优化一次。
在参数优化时,文章选择 Calmar 比率(年化收益率除以最大回撤)作为目标函数,而非常见的 Sharpe 比率。主要原因包括:
Calmar 比率直接惩罚深度回撤,对资金保护更为重视。
它不会惩罚上行波动,而 Sharpe 比率会将上涨也视为风险。
更适合收益分布非正态的趋势跟踪策略。
根据文章的回测结果,FRAMA 策略在 2013 年至 2024 年期间表现如下:
总收益率为 224.31%,年化收益率为 10.80%。
波动率为 10.45%,Sharpe 比率为 0.84。
最大回撤仅为 18.57%,Calmar 比率为 0.58。
虽然年化收益略低于基准的 12.27%,但最大回撤控制得更好,风险调整后的表现较为稳健。
扩展窗口提供越来越稳健的参数估计。
Calmar 比率优化在收益与尾部风险之间取得平衡。
二元仓位管理使策略简单透明。
严格的 T-1 规则完全消除未来函数。
趋势跟踪策略在长期横盘市场表现不佳。
不做空限制了熊市中的盈利能力。
参数稳定性依赖于市场环境的一致性。
过去的表现不能保证未来的结果。
FRAMA 是一种优雅的自适应技术指标,通过分形数学原理解决了传统移动平均线的固有矛盾。它在趋势明显时快速响应,在市场噪音中保持稳定,为趋势跟踪交易提供了一个原则性的框架。
结合前向分析优化和 Calmar 比率最大化,我们可以构建一个既适应市场变化,又注重风险控制的量化策略。虽然没有任何策略能在所有市场环境中奏效,但 FRAMA 的自适应特性为应对趋势跟踪的永恒挑战提供了有力工具。
希望这篇文章能够帮助你理解 FRAMA 的核心概念,并为你的 Python 量化交易学习之路提供参考。
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
好文推荐
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用
9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解
好书推荐