基于AKShare的MFI量化交易策略实战解析

ARIMA+傅里叶+技术指标+深度学习的多源信号融合框架 时间序列预测不是单一模型的任务,而是对趋势惯性、周期节律与市场情绪的协同建模。ARIMA擅长捕捉线性趋势与记忆衰减,傅里叶变换可解析多尺度周期及其相位演化,技术指标(如BBW、MFI、IMR)则量化实时多空强度;三者需通过可解释的深度学习架构进行动态权重融合,而非端到端黑箱拟合。这种多源异构特征协同机制,显著提升信号鲁棒性与归因能力,广泛应用于量化交易决策、风控预警与策略回测等工程场景,尤其适合需兼顾准确性与可调试性的实盘系统。 阅读详情

1. 从零开始:为什么选择AKShare和MFI指标?

如果你对量化交易感兴趣,但又觉得那些复杂的金融数据接口和晦涩的技术指标让人望而却步,那你来对地方了。今天我想跟你分享一个我用了很久的“懒人”组合:AKShare 加 MFI指标。这个组合特别适合刚入门的朋友,因为它既解决了数据从哪里来的问题,又提供了一个逻辑清晰、容易上手的交易策略构建思路。

我刚开始接触量化的时候,最头疼的就是数据。很多数据源要么收费昂贵,要么接口复杂,要么数据质量参差不齐。后来发现了AKShare,它就像一个免费的“金融数据百宝箱”,覆盖了股票、基金、期货、期权等各个市场,数据直接通过Python就能获取,简直是国内量化爱好者的福音。你不用再去各个网站手动下载CSV,也不用担心API调用次数限制,几行代码就能把历史行情数据拉到本地,省时省力。

那为什么是MFI指标呢?在众多技术指标里,MFI(Money Flow Index,资金流量指标)算是一个比较“实在”的指标。它不像MACD、KDJ那样纯粹基于价格,而是把价格和成交量结合起来考虑。你可以把它理解成RSI指标的“升级版”,RSI只关心价格涨跌的强度,而MFI还关心推动这个涨跌用了多少“钱”(即成交量)。这背后的逻辑很直观:如果股价上涨,并且伴随着巨大的成交量(真金白银的流入),那么这个上涨的可靠性就更高;反之,如果只是无量空涨,可能就值得警惕了。

MFI的值在0到100之间波动,通常我们用20和80作为超卖和超买的阈值。当MFI从下方突破20,被认为是资金开始流入的超卖买入信号;当MFI从上方跌破80,则被认为是资金流出的超卖卖出信号。这个逻辑非常直接,不像一些复杂的策略需要理解一大堆假设。对于初学者来说,从一个逻辑简单、易于理解的指标入手,能够快速建立起策略构建、回测和评估的完整闭环,获得正反馈,这对保持学习热情至关重要。接下来,我们就手把手,从搭建环境开始,一步步构建并分析一个属于你自己的MFI交易策略。

2. 实战第一步:搭建你的量化分析环境

工欲善其事,必先利其器。在开始写策略代码之前,我们需要先把“厨房”收拾好,把必要的“工具”备齐。别担心,整个过程就像安装几个手机APP一样简单,我会把每一步都拆解清楚。

首先,你需要一个Python环境。我强烈推荐使用 Anaconda 来管理你的Python环境。它是一个集成了Python和大量科学计算库(如pandas, numpy)的发行版,还能方便地创建独立的环境,避免不同项目之间的库版本冲突。去Anaconda官网下载安装包,一路点击“下一步”安装即可。安装好后,打开“Anaconda Prompt”(Windows)或终端(Mac/Linux),我们就可以开始安装必要的库了。

我们的核心工具是以下几个库:

  • AKShare: 免费、开源的金融数据接口库,是我们的数据来源。
  • TA-Lib: 技术指标计算库,非常高效和权威,我们用它来计算MFI。
  • Pandas & NumPy: 数据处理和分析的基石,就像Excel的超级增强版。
  • Matplotlib: 画图库,用来可视化我们的股价走势、买卖信号和收益曲线。

在Anaconda Prompt里,依次输入以下命令进行安装:

# 安装AKShare,使用清华镜像源会更快
pip install akshare -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装TA-Lib,这个稍微麻烦一点,因为需要编译
# 对于Windows用户,最简单的方法是去 https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib 下载对应你Python版本和系统位数的whl文件
# 例如,对于64位Python 3.8,就下载 TA_Lib‑0.4.24‑cp38‑cp38‑win_amd64.whl
# 然后 pip install 你下载的文件路径
# 对于Mac用户,可以用 Homebrew: brew install ta-lib,然后再 pip install TA-Lib
# 对于Linux用户,通常可以用包管理器,如 sudo apt-get install ta-lib (Ubuntu)

# 安装其他依赖库,Anaconda通常已自带,但为了保险可以再安装一次
pip install pandas numpy matplotlib

安装过程中如果遇到任何报错,别慌。最常见的就是TA-Lib安装失败,仔细对照上面的注释,找到适合你系统的方法。我当初在Windows上装TA-Lib也折腾了小半天,这都是必经之路。安装成功后,我们可以创建一个新的Python脚本文件(比如叫 mfi_strategy.py),然后开始导入这些库。

#!/usr/bin/env python
# coding: utf-8

# 引入数据分析核心库
import pandas as pd
import numpy as np

# 引入绘图库,并设置中文字体显示(避免图表出现乱码)
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
mpl.rcParams['axes.unicode_minus'] = False    # 正常显示负号

# 引入技术指标计算库
import talib as ta

# 引入时间处理模块
import time
from datetime import datetime, timedelta

# 引入我们的数据获取神器
import akshare as ak

print("所有库已成功导入!环境准备就绪。")

运行这段代码,如果没有报错,恭喜你,你的量化分析环境已经搭建成功了!这一步可能有点枯燥,但就像盖房子打地基,地基稳了,后面砌墙盖楼才快。有了这个环境,你不仅能做MFI策略,以后想尝试MACD、布林带等各种指标,或者分析不同的股票、基金,都在这同一个平台上进行,非常方便。

3. 核心操作:用AKShare获取数据并计算MFI

环境搞定,我们就要动真格的了。量化策略的核心是数据,没有数据,再好的想法也是空中楼阁。这一节,我们就用AKShare把数据“抓”下来,并计算出关键的MFI指标。

首先,我们得确定要分析什么。这里我们以上证指数(代码:sh000001)为例,因为它代表大盘走势,流动性好,数据完整。我们获取从2010年1月1日到最近一个交易日的数据。AKShare的 stock_zh_a_daily 接口可以完美胜任这个任务。

# 设置时间范围
start_date = '2010-01-01'
end_date = time.strftime("%Y-%m-%d", time.localtime()) # 获取当前日期作为结束日期

# 使用AKShare获取上证指数日线数据
print(f"正在获取上证指数数据,时间范围:{start_date} 至 {end_date}")
df = ak.stock_zh_a_daily(symbol='sh000001', start_date=start_date, end_date=end_date)

# 查看一下数据的前几行
print("数据预览:")
print(df.head())

运行后,你会看到一个包含 date(日期)、open(开盘)、close(收盘)、high(最高)、low(最低)、volume(成交量)等字段的表格。AKShare返回的数据已经非常规整了,我们只需要做一点简单的处理:把日期列设置为索引,并按照时间排序。这样后续按时间序列计算会非常方便。

# 数据预处理
df['date'] = pd.to_datetime(df['date']) # 将日期字符串转为日期时间格式
df.set_index('date', inplace=True)      # 将日期设为索引
df.sort_index(inplace=True)             # 按日期升序排列

# 计算每日收益率,这在后续计算策略收益时会用到
df['ret'] = df['close'] / df['close'].shift(1) - 1

print("数据预处理完成!")
print(f"数据总行数:{len(df)}")
print(df[['open', 'high', 'low', 'close', 'volume', 'ret']].head())

数据准备好了,接下来就是计算MFI指标。这里我们直接使用TA-Lib库中的 MFI 函数。这个函数需要四个价格序列(最高价、最低价、收盘价、成交量)和一个时间周期参数。MFI的经典周期是14天,所以我们传入 timeperiod=14。TA-Lib会帮我们完成所有复杂的中间计算,直接返回MFI值序列。

# 提取计算MFI所需的序列,并转换为numpy数组(TA-Lib要求的格式)
high = df['high'].values
low = df['low'].values
close = df['close'].values
volume = df['volume'].values

# 计算MFI指标
df['mfi'] = ta.MFI(high, low, close, volume, timeperiod=14)

# 由于MFI需要前14天的数据才能计算,所以前14行的MFI值是NaN,我们看看第15行之后的数据
print("MFI指标计算完成!")
print(df[['close', 'volume', 'mfi']].iloc[10:20]) # 查看第10到20行,观察MFI从NaN到有值的变化

为了更直观地感受MFI指标和股价的关系,我们可以把它们画在同一张图上。通常用上下两个子图,上面画股价走势,下面画MFI指标。

# 绘制股价与MFI指标
plt.figure(figsize=(16, 10))

# 子图1:股价走势
plt.subplot(2, 1, 1)
plt.plot(df.index, df['close'], color='royalblue', linewidth=1.5)
plt.title('上证指数走势图 (2010至今)', fontsize=15, fontweight='bold')
plt.ylabel('收盘价', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.5)

# 子图2:MFI指标
plt.subplot(2, 1, 2)
plt.plot(df.index, df['mfi'], color='darkorange', linewidth=1.5)
# 在MFI图上添加20和80的阈值线
plt.axhline(y=20, color='green', linestyle='--', alpha=0.7, label='超卖线 (20)')
plt.axhline(y=80, color='red', linestyle='--', alpha=0.7, label='超买线 (80)')
plt.fill_between(df.index, 20, df['mfi'], where=(df['mfi'] <= 20), color='green', alpha=0.3)
plt.fill_between(df.index, 80, df['mfi'], where=(df['mfi'] >= 80), color='red', alpha=0.3)
plt.title('MFI资金流量指标', fontsize=15, fontweight='bold')
plt.ylabel('MFI值', fontsize=12)
plt.xlabel('日期', fontsize=12)
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)

plt.tight_layout() # 自动调整子图间距
plt.show()

运行这段代码,你会得到一张清晰的图表。你可以观察在股价大幅波动时,MFI指标是如何反应的。当MFI进入红色区域(>80),往往对应着一段上涨后的高位;当MFI进入绿色区域(<20),则可能对应着下跌后的低位。这种视觉上的关联,能帮你更好地理解这个指标的含义,也为下一步制定交易规则打下基础。

4. 策略逻辑与信号生成:把指标变成买卖指令

看到MFI的曲线在图上上下穿梭,你可能已经在想:到底什么时候该买,什么时候该卖呢?这就是策略逻辑要解决的问题。我们不能凭感觉在图表上指指点点,必须有一套明确、可量化的规则,让计算机能够自动执行。对于MFI指标,最经典也最直接的应用规则就是“阈值突破法”。

它的逻辑非常简单:

  1. 买入信号:当MFI从低于20的区域(超卖区)向上突破20线时,我们认为市场可能已经过度卖出,资金开始回流,因此产生一个买入信号。
  2. 卖出信号:当MFI从高于80的区域(超买区)向下跌破80线时,我们认为市场可能已经过度买入,资金开始流出,因此产生一个卖出信号(或平仓信号)。

注意,这里强调的是“穿越”,而不是“处于”。MFI小于20只代表超卖,但未必是买入时机,要等它重新站上20,才意味着下跌动能减弱,可能迎来转折。卖出信号同理。我们用代码来实现这个逻辑,在数据框df中新增两列:“收盘信号”记录买卖点,“当天仓位”记录每天是否持有股票。

# 初始化信号列
df['收盘信号'] = 0  # 0表示无信号,1表示买入,-1可以表示卖出(这里我们用0代表卖出信号,因为我们要控制仓位)
df['当天仓位'] = 0.0  # 0代表空仓,1代表满仓持有

# 循环遍历数据,生成买卖信号
# 从第15个数据点开始(因为MFI前14天是NaN)
for i in range(14, len(df)):
    # 买入条件:当天MFI > 20 且 前一天MFI < 20
    if df['mfi'].iloc[i] > 20 and df['mfi'].iloc[i-1] < 20:
        df.loc[df.index[i], '收盘信号'] = 1  # 在当天收盘位置标记买入信号
        print(f"买入信号:日期 {df.index[i].date()}, MFI值 {df['mfi'].iloc[i]:.2f}")

    # 卖出条件:当天MFI < 80 且 前一天MFI > 80
    if df['mfi'].iloc[i] < 80 and df['mfi'].iloc[i-1] > 80:
        df.loc[df.index[i], '收盘信号'] = 0  # 在当天收盘位置标记卖出信号(我们用0表示)
        print(f"卖出信号:日期 {df.index[i].date()}, MFI值 {df['mfi'].iloc[i]:.2f}")

print("\n信号生成完毕!")

生成了“收盘信号”,但这还不是仓位。“收盘信号”发生在当天收盘时,我们无法以收盘价成交。一个更实际的假设是:在出现买入信号的下一个交易日开盘时,我们以开盘价买入;在出现卖出信号的下一个交易日开盘时,我们以开盘价卖出。所以,我们需要根据“收盘信号”来推算每天的持仓状态。

# 计算仓位:将前一天的信号,作为今天的仓位(因为信号基于收盘价,第二天才能行动)
df['当天仓位'] = df['收盘信号'].shift(1)

# 初始仓位为0(空仓),并且向前填充仓位值。
# 这意味着一旦买入(仓位变为1),将一直保持持仓,直到遇到卖出信号(仓位变为0)
df['当天仓位'].fillna(method='ffill', inplace=True)
# 将最初的NaN值(信号产生前)填充为0
df['当天仓位'].fillna(0, inplace=True)

# 为了方便后续计算,我们从第一个买入信号的前一天开始截取数据
# 因为在这之前仓位始终为0,没有交易发生
if (df['当天仓位'] == 1).any():  # 检查是否有过买入
    first_buy_date = df[df['当天仓位'] == 1].index[0]
    start_date_for_calc = first_buy_date - timedelta(days=1)
    df_strategy = df.loc[start_date_for_calc:].copy()
else:
    df_strategy = df.copy()
    print("警告:在整个回测期内未产生任何买入信号!")

# 将回测起始日的收益率和仓位强制设为0,代表从这一天开始计算资金曲线
df_strategy.iloc[0, df_strategy.columns.get_loc('ret')] = 0
df_strategy.iloc[0, df_strategy.columns.get_loc('当天仓位')] = 0

print("仓位计算完成,回测数据准备就绪。")
print(df_strategy[['close', 'mfi', '收盘信号', '当天仓位']].head(20))

现在,df_strategy 这个数据框里,我们已经有了每一天的收盘价、MFI值、产生的信号以及我们实际持有的仓位(0或1)。接下来,我们就可以基于这个仓位,来模拟我们的投资组合净值如何变化了。但在看枯燥的数字之前,我们先让图表来说话,把买卖信号直观地标记在股价走势图上。

# 绘制带买卖信号的股价图
plt.figure(figsize=(16, 8))
plt.plot(df_strategy.index, df_strategy['close'], color='steelblue', linewidth=1.2, label='上证指数收盘价')

# 标记买入点(绿色向上箭头)
buy_signals = df_strategy[df_strategy['收盘信号'] == 1]
plt.scatter(buy_signals.index, buy_signals['close'], color='limegreen', marker='^', s=100, zorder=5, label='买入信号')

# 标记卖出点(红色向下箭头)
sell_signals = df_strategy[df_strategy['收盘信号'] == 0]
# 注意,我们只标记那些因为MFI下穿80而产生的卖出信号,而不是所有仓位为0的点
# 简化处理:标记仓位从1变为0的那一天(即前一天仓位为1,当天信号为0)
sell_dates = df_strategy[(df_strategy['收盘信号'] == 0) & (df_strategy['当天仓位'].shift(1) == 1)].index
sell_prices = df_strategy.loc[sell_dates, 'close']
plt.scatter(sell_dates, sell_prices, color='crimson', marker='v', s=100, zorder=5, label='卖出信号')

plt.title('上证指数MFI策略买卖信号示意图', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('收盘价', fontsize=12)
plt.legend(loc='upper left')
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()

这张图会让你对策略的触发频率和时机有一个整体的感觉。你会发现,信号并不是频繁出现的,这符合MFI作为摆动指标的特性——它试图捕捉阶段性的极端情绪点。信号点都出现在股价波动相对剧烈的区域。现在,我们已经把冰冷的指标数值转化为了明确的交易指令,下一步就是看看,如果严格执行这些指令,我们的钱包会发生什么变化。

5. 策略回测与绩效评估:你的策略真的赚钱吗?

信号有了,仓位也确定了,是骡子是马,得拉出来溜溜。策略回测就是根据历史数据,模拟真实交易,计算出策略的收益、风险等各项指标,看看这个策略在过去表现如何。这是量化交易中最关键也最令人兴奋的环节之一。

首先,我们根据每天的仓位和指数的涨跌幅,来计算策略的资金净值曲线。这里我们做一个简化假设:策略要么满仓持有上证指数(仓位=1),要么空仓(仓位=0)。交易成本(手续费、印花税等)我们先忽略,但在实际应用中,这部分必须考虑。

# 计算策略净值
# 策略每日收益率 = 指数每日收益率 * 当日仓位
# 策略净值 = (1 + 策略每日收益率)的累计乘积
df_strategy['策略每日收益率'] = df_strategy['ret'] * df_strategy['当天仓位']
df_strategy['策略净值'] = (1 + df_strategy['策略每日收益率']).cumprod()

# 同时,我们也计算一个简单的买入持有(Buy and Hold)策略的净值作为基准
df_strategy['指数净值'] = (1 + df_strategy['ret']).cumprod()

# 选取最近几年的数据来展示,图形会更清晰(例如从2018年开始)
df_recent = df_strategy.loc['2018-01-01':]

# 绘制策略净值与基准净值对比图
plt.figure(figsize=(16, 8))
plt.plot(df_recent.index, df_recent['策略净值'], color='darkorange', linewidth=2.5, label='MFI策略净值')
plt.plot(df_recent.index, df_recent['指数净值'], color='steelblue', linewidth=1.5, alpha=0.8, label='上证指数净值(买入持有)')
plt.title('MFI策略 vs 买入持有策略 净值对比 (2018至今)', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('净值(起始为1)', fontsize=12)
plt.legend(loc='upper left')
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()

光看净值曲线还不够,我们需要一些量化的指标来客观评价策略的好坏。一个合格的策略评估至少要包含收益和风险两个维度。下面我们来计算几个最常用的绩效指标。

# 计算关键绩效指标
# 1. 总收益率
total_return_strategy = df_strategy['策略净值'].iloc[-1] - 1
total_return_index = df_strategy['指数净值'].iloc[-1] - 1

# 2. 年化收益率(假设一年250个交易日)
num_days = len(df_strategy)
annual_return_strategy = (1 + total_return_strategy) ** (250 / num_days) - 1
annual_return_index = (1 + total_return_index) ** (250 / num_days) - 1

# 3. 最大回撤(Max Drawdown) - 衡量历史最大亏损风险
# 计算历史净值曲线的前缀最大值
rolling_max = df_strategy['策略净值'].cummax()
# 计算当前净值距离前缀最大值的回撤
drawdown = (rolling_max - df_strategy['策略净值']) / rolling_max
max_drawdown_strategy = drawdown.max()

# 同样计算基准的最大回撤
rolling_max_index = df_strategy['指数净值'].cummax()
drawdown_index = (rolling_max_index - df_strategy['指数净值']) / rolling_max_index
max_drawdown_index = drawdown_index.max()

# 4. 夏普比率(Sharpe Ratio) - 衡量风险调整后的收益
# 假设无风险年化收益率为3%
risk_free_rate_annual = 0.03
risk_free_rate_daily = risk_free_rate_annual / 250
# 策略超额收益率(日度)
excess_return_daily = df_strategy['策略每日收益率'] - risk_free_rate_daily
# 计算夏普比率(年化)
sharpe_ratio = np.sqrt(250) * excess_return_daily.mean() / excess_return_daily.std()

# 5. Alpha & Beta (需要基准的日收益率序列)
df_strategy['策略收益率'] = df_strategy['策略净值'].pct_change()
df_strategy['指数收益率'] = df_strategy['ret'] # 这里直接用ret,因为指数净值是(1+ret)的累积,其日收益率就是ret
# 计算协方差和方差
covariance = df_strategy[['策略收益率', '指数收益率']].cov().iloc[0,1]
variance_index = df_strategy['指数收益率'].var()
beta = covariance / variance_index
# Alpha = 策略年化收益 - Beta * 基准年化收益
alpha = annual_return_strategy - beta * annual_return_index

# 格式化输出结果
print("="*60)
print("MFI策略绩效评估报告")
print("="*60)
print(f"回测期:{df_strategy.index[0].date()} 至 {df_strategy.index[-1].date()}")
print(f"总交易日数:{num_days}")
print("-"*60)
print(f"{'指标':<15} {'MFI策略':<20} {'买入持有基准':<20}")
print("-"*60)
print(f"{'累计收益率':<15} {total_return_strategy*100:>6.2f}%{'':<14}{total_return_index*100:>6.2f}%")
print(f"{'年化收益率':<15} {annual_return_strategy*100:>6.2f}%{'':<14}{annual_return_index*100:>6.2f}%")
print(f"{'最大回撤':<15} {max_drawdown_strategy*100:>6.2f}%{'':<14}{max_drawdown_index*100:>6.2f}%")
print("-"*60)
print(f"策略Alpha(超额收益): {alpha:.4f}")
print(f"策略Beta(市场风险暴露): {beta:.4f}")
print(f"夏普比率(年化): {sharpe_ratio:.4f}")
print("="*60)

运行这段代码,你会得到一份详细的策略“体检报告”。你需要重点关注这几个指标:

  • 年化收益率:策略每年平均能赚多少钱?是否跑赢了简单买入持有(基准)?
  • 最大回撤:历史上你最多会亏掉本金的百分之多少?这个数字直接关系到你的心理承受能力。一个年化收益20%但最大回撤50%的策略,很可能在下跌中途就被你放弃了。
  • 夏普比率:每承担一单位风险,能获得多少超额回报?通常大于1算不错,大于2就算优秀了。它综合衡量了收益和波动。
  • Alpha和Beta:Beta表示策略收益对市场波动的敏感程度(Beta=1表示跟市场同步)。Alpha是扣除市场波动影响后,策略自身的“真本事”,正的Alpha是每个主动管理者追求的目标。

通过这份报告,你就能客观地判断,这个基于MFI的简单策略,在历史数据上是否有效,它的收益风险特征是否符合你的预期。我回测过很多次,在不同的时间段和参数下,结果差异可能很大,这引出了我们下一个要讨论的重要话题:策略的参数和稳健性。

6. 参数优化与策略思考:让策略更上一层楼

看到回测结果,你可能会有疑问:为什么MFI的阈值偏偏是20和80?用15和85行不行?用30和70呢?回测期从2010年开始,如果从2015年牛市开始结果会怎样?这些问题都指向量化策略的两个核心概念:参数优化和过拟合。

首先,我们来尝试优化一下MFI的买卖阈值。我们把买入阈值(L)和卖出阈值(H)作为变量,测试多组参数,看看哪一组在历史上表现最好。这里我们用一个简单的网格搜索来实现。

# 定义一个函数,方便我们测试不同参数
def backtest_mfi_strategy(data, buy_threshold=20, sell_threshold=80):
    """
    回测MFI策略
    data: 包含'high','low','close','volume'列的DataFrame
    buy_threshold: 买入信号阈值(MFI上穿此值)
    sell_threshold: 卖出信号阈值(MFI下穿此值)
    返回:策略净值序列,以及最终的年化收益率和最大回撤
    """
    df = data.copy()
    # 计算MFI
    high = df['high'].values
    low = df['low'].values
    close = df['close'].values
    volume = df['volume'].values
    df['mfi'] = ta.MFI(high, low, close, volume, timeperiod=14)

    # 生成信号
    df['signal'] = 0
    for i in range(14, len(df)):
        if df['mfi'].iloc[i] > buy_threshold and df['mfi'].iloc[i-1] < buy_threshold:
            df.loc[df.index[i], 'signal'] = 1
        if df['mfi'].iloc[i] < sell_threshold and df['mfi'].iloc[i-1] > sell_threshold:
            df.loc[df.index[i], 'signal'] = 0

    # 计算仓位
    df['position'] = df['signal'].shift(1).fillna(method='ffill').fillna(0)

    # 计算策略净值(简化版,从第一个有效信号开始)
    if (df['position'] == 1).any():
        start_idx = df[df['position'] == 1].index[0]
        df = df.loc[start_idx:].copy()
        df.iloc[0, df.columns.get_loc('ret')] = 0
        df.iloc[0, df.columns.get_loc('position')] = 0

    df['strategy_ret'] = df['ret'] * df['position']
    df['strategy_net'] = (1 + df['strategy_ret']).cumprod()

    # 计算绩效
    total_ret = df['strategy_net'].iloc[-1] - 1
    annual_ret = (1 + total_ret) ** (250 / len(df)) - 1

    rolling_max = df['strategy_net'].cummax()
    drawdown = (rolling_max - df['strategy_net']) / rolling_max
    max_dd = drawdown.max()

    return df['strategy_net'], annual_ret, max_dd

# 准备数据(用之前获取的df)
data_for_test = df[['high', 'low', 'close', 'volume']].copy()
data_for_test['ret'] = df['ret']

# 测试多组参数
results = []
buy_thresholds = [15, 20, 25, 30]
sell_thresholds = [70, 75, 80, 85, 90]

print("开始参数扫描...")
for L in buy_thresholds:
    for H in sell_thresholds:
        if L >= H: # 买入阈值必须小于卖出阈值
            continue
        _, ann_ret, max_dd = backtest_mfi_strategy(data_for_test, L, H)
        results.append({
            '买入阈值(L)': L,
            '卖出阈值(H)': H,
            '年化收益率(%)': ann_ret * 100,
            '最大回撤(%)': max_dd * 100,
            '收益回撤比': (ann_ret / max_dd) if max_dd != 0 else 0 # 一个简单的综合指标
        })

# 将结果转为DataFrame并排序
results_df = pd.DataFrame(results)
results_df.sort_values(by='收益回撤比', ascending=False, inplace=True)

print("\n参数优化结果(按收益回撤比排序):")
print(results_df.head(10).to_string(index=False))

运行这个优化脚本,你会看到一个表格,列出了不同参数组合下的策略表现。你可能会发现,(20, 80) 这组经典参数未必是最优的,在某些时间段,(15, 90) 或者 (25, 75) 可能表现更好。这就是参数优化的魅力,也是陷阱所在。

警惕过拟合! 如果我们仅仅选择历史回测中表现最好的那组参数,很可能只是它“恰好”完美拟合了历史数据的噪音,而不是抓住了市场的本质规律。这样的策略在未来实盘时很容易失效。为了避免过拟合,我们可以采取以下方法:

  1. 样本外测试:把数据分成两段,前70%用来优化参数(训练集),后30%用来测试用最优参数的表现(测试集)。如果策略在测试集上表现大幅下滑,可能就是过拟合了。
  2. 多市场/多品种验证:用同样的参数去测试不同的股票指数(如沪深300、创业板指)或者个股,看策略是否依然有效。一个稳健的策略应该具有一定的普适性。
  3. 简化逻辑:与其追求复杂的参数组合,不如坚持简单、逻辑清晰的规则。MFI的(20,80)阈值虽然不一定是最优的,但它是经过长期市场检验的经典值,其稳健性可能比回测出来的“最优”参数更好。

除了参数,这个简单的MFI策略本身也有很多可以思考和改进的地方:

  • 它适合什么样的市场? 在趋势明显的牛市或熊市,MFI可能会在超买/超卖区钝化,发出错误信号。它更适合震荡市。
  • 可以结合其他指标吗? 比如,只有当MFI发出买入信号,并且价格站在某条均线之上时,才真正买入,这样可以过滤掉下跌趋势中的反弹假信号。
  • 仓位管理:我们一直是满仓或空仓。是否可以引入仓位控制?比如MFI越低,买入仓位越重?
  • 止损止盈:策略本身没有止损。如果MFI发出买入信号后市场继续下跌,会造成较大亏损。可以加入一个基于ATR(平均真实波幅)的移动止损。

这些思考远比找到一个“圣杯参数”更重要。量化交易是一个不断迭代、思考和优化的过程。这个基于AKShare和MFI的策略,为你提供了一个完整的、可运行的起点。你可以把它当成一个“模板”,尝试修改其中的规则,加入新的想法,用历史数据去验证,感受策略开发的完整流程。记住,回测的目的不是证明一个策略永远有效,而是理解其背后的逻辑、优势和局限性,从而在实盘中更好地管理风险和预期。

louie8.1-decision-os.zip 立即下载

相关推荐

移动应用畅销榜预测数据集10000款应用19特征

本数据集模拟移动应用商店的运营特征,包含10000款应用、19个字段、单CSV约937KB。字段覆盖应用类别(12类)、商业模式(免费、广告、内购、订阅、付费)、发布年份、内容分级、安装包大小、单次会话广告加载量、年更新次数、距上次更新天数、商店编辑推荐、买量支出、平均评分、次日与30日留存率,以及评分总数、累计下载量与营收等结果字段。提供top_grossing分类目标(营收前15%)与revenue_musd回归目标,结果字段独立分离,便于练习避免目标泄漏。数据完整无缺失、无需表关联,非线性效应与特征交互明显,调优后模型可超越朴素基线,兼具入门友好与研究深度,适合移动应用行业分析、商业化策略研究与机器学习教学。说明:本数据为Python生成的合成数据,仅反映生成规则,不含真实应用与个人信息。许可:CC0公有领域,可自由使用与二次分发。

AKShare 金融数据接口实战指南:从数据获取到量化分析

AKShare 是一个基于 Python 的开源金融数据接口库,旨在为量化交易者、金融分析师和研究人员提供便捷、高效的金融数据获取解决方案。该项目通过整合多种数据源,为用户提供包括股票、基金、期货、债券、外汇等在内的全面金融数据服务。 ## 核心功能特点 1. **多品种覆盖**:支持股票、基金、期货、债券、外汇、指数等多种金融产品数据 2. **多频率数据**:提供从分钟级到日线、周线、月线...

gitblog_00902的博客 3409

预算约束下的备件库存:MATLAB中的边际分配与动态规划,最小化预期欠单.rar

预算约束下的备件库存:MATLAB中的边际分配与动态规划,最小化预期欠单.rar

Python量化监控框架:从指标计算到批量监控的本地化实践

量化交易的核心在于通过数学模型和计算机技术对市场数据进行系统性分析,其原理是将投资逻辑转化为可执行的算法。在技术实现上,Python因其丰富的数据科学生态(如Pandas、NumPy)成为主流工具,能够高效处理金融时间序列数据。**资金流**和**波动率**是两类关键的市场分析指标,前者反映资金进出动向,后者衡量价格波动程度,对判断市场情绪和风险有重要价值。通过构建本地化的监控框架,开发者可以灵活计算、跟踪这些复杂指标,并将结果用于策略研究或预警系统。本文介绍的开源项目正是这样一个专注于指标计算与批量监控的

weixin_33875564的博客 309

Delphi 13.2控件之sbridge-12.1.0-d7pro.exe

Delphi 13.2控件之sbridge_12.1.0_d7pro.exe

chrome-headless-shell-linux64-159.0.8145.0(Canary).zip

内容概要:Chrome for Testing 官方 chrome-headless-shell 159.0.8145.0 linux64 平台(金丝雀版通道),原版未修改,附来源说明。 适合人群:Linux 64 位 上的自动化测试工程师、爬虫开发者、CI 环境搭建者。 使用场景及目标:①无头浏览器(Playwright/Puppeteer/Selenium 无界面自动化与爬虫的运行内核);②服务器/容器离线内置;③版本精准匹配(chromedriver 与 Chrome 需同大版本)。 其他说明:Chrome for Testing 为 Google 官方发布、允许自由分发的文件;本资源为第三方整理的下载便利服务。

无人机路径规划中多种优化算法的仿真研究(Matlab代码实现)

内容概要:本文系统研究了无人机路径规划中多种智能优化算法的仿真方法,重点实现了改进灰狼优化算法(I-GWO)、标准灰狼优化算法(GWO)、金鹰优化算法(GJO)和正弦余弦算法(SCA)在三维复杂环境下的路径规划应用。通过Matlab编程对各类算法进行建模与仿真,对比分析其在路径长度、飞行安全性、轨迹平滑度及算法收敛速度等方面的性能表现。研究涵盖三维空间建模、障碍物规避机制、多目标代价函数设计以及算法参数敏感性分析等内容,旨在为无人机在复杂场景下的自主导航提供高效、可靠的路径规划方案。; 适合人群:具备一定Matlab编程基础和优化算法理论知识,从事无人机系统开发、智能算法研究、自动化控制及相关领域的科研人员与工程技术人员,尤其适合研究生及以上学历的研究者。; 使用场景及目标:①应用于城市密集区、灾难救援、边境巡逻、军事侦察等复杂三维环境下的无人机航迹规划任务;②系统比较不同元启发式优化算法在路径规划中的搜索能力、稳定性和计算效率,为实际工程中算法选型与改进提供依据;③作为高校或科研机构在智能优化、机器人导航、飞行器控制等课程中的教学案例与实验素材。; 阅读建议:建议读者结合文中提供的Matlab代码进行仿真实验,深入理解目标函数构建逻辑与算法实现细节,可通过调整环境障碍分布、优化权重因子及算法参数等方式开展消融实验,以全面掌握各类算法的性能边界与适用条件。

汽车电子基于OTA的Bootloader更新机制:量产车软件升级断点版本限制与跨代更新规避策略

内容概要:本文深入剖析了量产汽车ECU中Bootloader在OTA升级过程中为何无法跨“断点版本”升级的核心原理。文章从Bootloader作为可信根的不可变性出发,解释了其在SOP后禁止常规OTA更新的原因,并揭示了Flash分区机制下OTA仅能擦写Application区的限制。通过介绍“两段式搬运”自更新机制,阐明了新版Bootloader需依赖旧版Bootloader执行迁移的“鸡生蛋”困境,进而引出断点版本的概念——即首次引入新Bootloader特性的关键过渡版本。文中详细列举了导致断点的典型变更(如签名算法、分区表等),并指出跨版本升级失败的根本原因在于旧Bootloader无法解析新镜像格式或执行更新流程。最后提出系统性解决方案,包括构建有向升级路径图、终端前置校验、A/B冗余、Golden恢复区等工程防护措施,强调需从架构、发布、OTA后台到售后体系全流程协同管理断点风险。; 适合人群:汽车电子工程师、车载软件开发人员、OTA系统设计师及从事ECU固件升级相关的技术管理人员,尤其适合具备嵌入式系统和UDS诊断基础的研发人员。; 使用场景及目标:①理解Bootloader在OTA中的核心作用及其不可随意更新的设计逻辑;②掌握断点版本的成因与应对策略,避免实际升级中出现“变砖”事故;③指导OTA系统设计与升级路径规划,提升整车软件迭代的安全性与可靠性。; 阅读建议:此资源结合真实工程场景深入浅出地讲解复杂机制,建议结合Bootloader自更新流程图与实际UDS刷写案例对照学习,重点关注版本兼容性判断、安全校验设计及故障恢复机制,在开发和测试中充分模拟跨断点升级场景以验证系统鲁棒性。

全桥LLC谐振(变频+移相混合控制)变换器闭环仿真模型(Simulink仿真实现)

内容概要:本文详细阐述了全桥LLC谐振变换器在变频与移相混合控制策略下的闭环仿真模型构建过程,重点探讨了其在Simulink环境中的实现方法。该模型融合变频控制与移相控制的优势,有效提升了变换器的转换效率与动态响应性能,适用于高效率、高功率密度的DC-DC变换应用场景。文中系统介绍了变换器的工作原理与数学建模,完整展示了闭环控制系统的设计流程,涵盖控制器参数整定、仿真模型搭建及关键波形分析,充分验证了所提出的混合控制策略在稳定性、响应速度与抗干扰能力方面的优越性。; 适合人群:具备电力电子与自动控制理论基础,熟悉Simulink仿真工具,从事开关电源、新能源变换系统、电力电子装置研发的工程师及电气工程、自动化等相关专业的研究生。; 使用场景及目标:①用于高效率LLC谐振变换器的研发设计与教学演示,深化对软开关技术的理解;②为科研人员提供混合控制策略的仿真参考,优化变换器的动态响应、效率及轻载性能;③支撑相关科研课题的实验验证、学术论文复现与毕业设计开发。; 阅读建议:在学习过程中应重点关注混合控制策略的实现逻辑、控制环路的耦合机制与仿真参数配置,结合理论推导与仿真结果进行对比分析,通过反复调试与优化,深入理解变频与移相控制协同作用对系统整体性能的影响机理。

chrome-headless-shell-mac-arm64-159.0.8153.0(Canary).zip

内容概要:Chrome for Testing 官方 chrome-headless-shell 159.0.8153.0 arm64 平台(金丝雀版通道),原版未修改,附来源说明。 适合人群:arm64 上的自动化测试工程师、爬虫开发者、CI 环境搭建者。 使用场景及目标:①无头浏览器(Playwright/Puppeteer/Selenium 无界面自动化与爬虫的运行内核);②服务器/容器离线内置;③版本精准匹配(chromedriver 与 Chrome 需同大版本)。 其他说明:Chrome for Testing 为 Google 官方发布、允许自由分发的文件;本资源为第三方整理的下载便利服务。

对外一种类型,对内几副身形:Redis键空间管理、五大类型内部编码与场景选型解析代码包

想解决的就是这道“会用”和“用好”之间的缝隙。它回答四个具体问题: 一个 Redis 实例应该怎样启动、连接和关闭,哪些做法会在重启后丢数据; 管理键的那一组命令(EXISTS、DEL、EXPIRE、RENAME、KEYS、SCAN……)各自的代价是什么,哪些会把主线程卡住; 对外只有五种类型,对内却有十来种编码,它们何时切换、为何切换、切换后内存差多少; 每种类型最适合什么访问模式,常见场景(缓存、计数、限流、队列、标签、排行榜、延迟队列、分页)应该怎样写才不踩坑。

半桥LLC谐振变换器开环+闭环仿真设计资料(Simulink仿真实现)

内容概要:本文提供了关于半桥LLC谐振变换器的开环与闭环仿真设计资料,基于Simulink平台实现,系统阐述了变频控制下的半桥LLC谐振变换器建模方法,涵盖谐振腔参数设计、系统动态响应分析、PI控制器设计及优化、软开关特性实现与高效率能量传输机制等内容。资料深入探讨了开环与闭环控制策略的差异,重点分析了闭环控制系统在负载突变与输入电压扰动下的稳定性与鲁棒性表现,并通过完整的仿真模型验证不同工况下的系统性能,帮助用户掌握LLC变换器的核心工作原理与工程设计要点。; 适合人群:具备电力电子技术基础,从事开关电源、新能源变换器、电力系统电能变换等相关领域研发工作的工程师,以及高等院校电气工程、自动化等相关专业的研究生和科研人员。; 使用场景及目标:①掌握基于Simulink的LLC谐振变换器建模与仿真全流程;②深入理解开环与闭环控制对系统效率、动态响应及稳定性的影响;③学习谐振参数(如Lr、Cr、Lm)的优化设计方法与PI控制器的整定策略,支撑实际产品开发与学术课题研究。; 阅读建议:建议结合提供的Simulink仿真模型进行同步操作,重点观察控制环路搭建细节与关键参数调节过程,通过设置不同负载条件与输入电压变化,对比分析系统在开环与闭环模式下的输出响应、开关管电压电流波形及软开关实现情况,从而深化对LLC变换器控制机理与设计规律的理解。

AI政务大模型数字人平台规划方案(技术方案版).pptx

AI政务大模型数字人平台规划方案(技术方案版).pptx

两路半桥LLC谐振变换器软开关Simulink仿真(模拟谐振参数不同、分裂电容)

内容概要:本文围绕两路半桥LLC谐振变换器的软开关特性展开Simulink仿真研究,重点分析了不同谐振参数(如谐振电感、谐振电容)以及分裂电容结构对系统性能的影响。通过构建详细的开环与闭环控制仿真模型,深入探究变换器在变频控制下的动态响应、能量传输效率及软开关实现条件,验证了在宽负载范围内实现零电压开通(ZVS)和零电流关断(ZCS)的可行性。研究特别关注分裂电容对谐振腔参数调节的作用机制,揭示其在优化高频高效电源设计中的关键价值,为高性能DC-DC变换器的设计提供了理论依据与实践指导。; 适合人群:电力电子、电气工程及其自动化等相关专业的高校师生,以及从事高频电源、新能源变换器、电动汽车充电模块等领域的研发工程师和技术人员。; 使用场景及目标:①深入理解LLC谐振变换器的工作原理及其软开关的实现条件;②掌握基于Simulink的电力电子系统建模与仿真分析方法;③探究分裂电容等关键结构对谐振参数与系统效率的影响,进而优化高效率、高功率密度电源的拓扑设计与参数匹配。; 阅读建议:读者应具备电路理论、电力电子变流技术的基础知识,建议在学习过程中结合所提供的Simulink模型进行实际操作,重点观察谐振参数变化对电流电压波形及软开关过程的影响,并尝试调整控制器参数以分析系统的动态响应与稳定性表现。

【机械故障诊断】项目介绍 MATLAB实现基于CWT-CNN-Transformer连续小波变换(CWT)时频图结合卷积神经网络(CNN)和Transformer编码器进行滚动轴承故障诊断(含模型描述

内容概要:本文介绍了一种基于连续小波变换(CWT)、卷积神经网络(CNN)和Transformer编码器相结合的滚动轴承故障诊断模型(CWT-CNN-Transformer),利用MATLAB实现。原始振动信号经分段和归一化后,通过CWT转化为二维时频图,以展现故障引起的非平稳特征;CNN用于提取时频图中的局部纹理、能量团和冲击条纹等空间特征;随后将CNN输出的特征图转换为令牌序列,并引入位置嵌入,交由Transformer编码器利用自注意力机制建模全局依赖关系,捕捉跨频带、长距离的故障模式关联;最终通过全局池化与分类层实现多类别故障识别。文中还提供了完整的代码示例,涵盖信号处理、时频图生成、数据划分、网络搭建、训练流程及结果评估全过程。; 适合人群:具备一定信号处理与机器学习基础,从事工业设备故障诊断、智能运维、深度学习应用研究的科研人员或工程技术人员,尤其适合熟悉MATLAB环境的研发者;; 使用场景及目标:①应用于旋转机械中滚动轴承的早期故障检测与多类别故障分类;②构建从振动信号采集到智能诊断的端到端自动化分析系统;③支持预测性维护、状态监测与工业智能化升级;目标是提升在变工况、强噪声、多故障耦合下的诊断准确率与模型鲁棒性; 阅读建议:建议结合提供的代码实例在MATLAB R2025b环境下实践操作,重点关注CWT参数设置、CNN-Transformer联合建模结构设计及混淆矩阵评估方法,同时注意数据预处理的一致性与标签管理规范,以实现可复现、可部署的诊断系统。

化学和超级电容混合储能双层容量配置平抑风电波动,采用RLMD分解风电功率,粒子群算法求解(Matlab代码实现)

内容概要:本资源聚焦于化学储能与超级电容器混合储能系统的双层容量配置方法,旨在平抑风电功率波动。通过采用鲁棒局部均值分解(RLMD)对风电功率进行多尺度分解,提取不同频率分量,并据此划分高频与低频功率响应任务,充分发挥超级电容器响应速度快和化学电池能量密度高的互补优势。在此基础上,构建以系统成本、寿命损耗与波动平抑效果为目标的多目标优化模型,并利用粒子群优化算法(PSO)求解最优容量配置方案。整个过程通过Matlab代码实现,提供了完整的仿真流程和技术细节,体现了信号分解、储能特性匹配与智能优化算法相结合的技术路线。; 适合人群:具备一定电力系统、新能源或自动化背景,熟悉Matlab编程,从事储能系统规划、风电并网或优化算法应用研究的研发人员与研究生。; 使用场景及目标:① 掌握RLMD等先进信号分解技术在风电功率处理中的应用;② 学习混合储能系统双层协调控制与容量配置的建模思路;③ 实践基于粒子群算法的多目标优化问题求解方法,并应用于实际工程场景。; 阅读建议:此资源以Matlab代码实现为核心,不仅展示算法流程,更注重从工程需求出发的系统设计与优化建模。学习者应结合代码运行、参数调整与结果分析,深入理解RLMD分解效果、储能分工机制及优化目标之间的权衡关系,从而掌握解决此类综合问题的方法论。

上一篇: 【实战拆解】影刀RPA高级考试:Python编码版数据抓取与数据库入库全流程
下一篇: 插值多项式为什么唯一?3分钟看懂Vandermonde行列式的几何意义
php55
博客等级 码龄15年 51粉丝 1553原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值