python:快速傅里叶分析用于股价波动

快速傅里叶变换 (FFT) 分析股价波动 Python 示例
核心思路:股价原始序列是时域信号,通过 FFT 转换到频域,分离长期趋势、中期波动、短期噪声,再逆变换还原平滑后的股价,实现降噪、周期提取。下面示例包含:数据模拟 / 真实股价获取、FFT 频域分析、滤波降噪、绘图可视化。

下面给出滚动窗口 FFT + 周期分析 + 自动生成买卖信号完整版,直接用 A 股真实行情,可直接跑。
特点:

  • 滑动窗口滚动傅里叶变换,捕捉实时变化的周期
  • 提取主周期,计算相位,给出买入/卖出信号
  • 可视化:股价、平滑线、买卖点、周期强度

1. 先安装依赖

pip install numpy pandas matplotlib tushare

2. 完整可运行代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tushare as ts

# ---------------------- 配置Tushare ----------------------
ts.set_token('你的Tushare Token')
pro = ts.pro_api()

# ---------------------- 参数设置 ----------------------
STOCK_CODE = "300502.SZ"    # 股票代码
START_DATE = "20240101"
END_DATE   = "20260531"
WINDOW     = 120            # 滚动窗口大小(交易日)
KEEP_NUM   = 20             # FFT保留低频分量
TRADE_SIGNAL_THRESHOLD = 0.2 # 相位阈值

# ---------------------- 下载数据 ----------------------
df = pro.daily(ts_code=STOCK_CODE, start_date=START_DATE, end_date=END_DATE)
df = df.sort_values("trade_date").reset_index(drop=True)
df["trade_date"] = pd.to_datetime(df["trade_date"])
price = df["close"].values
dates = df["trade_date"].values
n = len(price)
print(df.head())

# 用于保存结果
smooth_list = np.full(n, np.nan)
signal_buy  = np.full(n, np.nan)
signal_sell = np.full(n, np.nan)
period_list = np.full(n, np.nan)

# ---------------------- 滚动窗口FFT ----------------------
for i in range(WINDOW, n):
    win_data = price[i-WINDOW:i]
    fft_vals = np.fft.fft(win_data)
    fft_freq = np.fft.fftfreq(WINDOW, d=1)
    amp = np.abs(fft_vals)
    
    # 滤波
    fft_filtered = fft_vals.copy()
    fft_filtered[KEEP_NUM : WINDOW-KEEP_NUM] = 0
    smooth = np.fft.ifft(fft_filtered).real[-1]
    smooth_list[i] = smooth
    
    # 找主周期(去掉0频)
    amp[0] = 0
    main_idx = np.argmax(amp)
    main_freq = fft_freq[main_idx]
    if main_freq > 0:
        main_period = 1 / main_freq
        period_list[i] = main_period
    else:
        main_period = None
    
    # 相位判断买卖:正弦波 0附近买入,π附近卖出
    phase = np.angle(fft_vals[main_idx])
    if main_period and abs(phase) < TRADE_SIGNAL_THRESHOLD:
        signal_buy[i] = price[i]
    elif main_period and abs(abs(phase)-np.pi) < TRADE_SIGNAL_THRESHOLD:
        signal_sell[i] = price[i]

# ---------------------- 绘图 ----------------------
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

fig, axes = plt.subplots(3,1, figsize=(16,12))

# 图1:股价+FFT平滑+买卖点
axes[0].plot(dates, price, label="收盘价", alpha=0.7)
axes[0].plot(dates, smooth_list, label="滚动FFT平滑线", c="red", lw=2)
axes[0].scatter(dates, signal_buy, marker="^", c="green", s=80, label="买入")
axes[0].scatter(dates, signal_sell, marker="v", c="red", s=80, label="卖出")
axes[0].set_title(f"{STOCK_CODE} 滚动FFT周期交易信号")
axes[0].legend()
axes[0].grid(True)

# 图2:实时主周期变化
axes[1].plot(dates, period_list, c="blue")
axes[1].set_title("滚动窗口识别的主波动周期(交易日)")
axes[1].set_ylabel("周期(天)")
axes[1].grid(True)

# 图3:FFT振幅示例(最后一个窗口)
win_data = price[-WINDOW:]
amp = np.abs(np.fft.fft(win_data))
freq = np.fft.fftfreq(WINDOW,1)
mask = freq>=0
axes[2].plot(freq[mask], amp[mask])
axes[2].set_title("最新窗口频域振幅谱")
axes[2].set_xlabel("频率(1/天)")
axes[2].grid(True)

plt.tight_layout()
plt.show()

# ---------------------- 输出统计 ----------------------
valid_periods = period_list[~np.isnan(period_list)]
print("="*50)
print(f"股票 {STOCK_CODE} 分析结果")
print(f"平均主波动周期:{np.mean(valid_periods):.1f} 天")
print(f"最大周期:{np.max(valid_periods):.1f} 天")
print(f"最小周期:{np.min(valid_periods):.1f} 天")
print("="*50)

3. 关键参数解释(直接调参即可实战)

  • WINDOW=120:滚动窗口,越大看大周期,越小抓短线周期
  • KEEP_NUM=20:FFT保留低频分量,越大波动越多,越小越平滑
  • TRADE_SIGNAL_THRESHOLD=0.2:相位阈值,越小买卖信号越严格

4. 实战用法

  1. 替换 你的Tushare Token
  2. 更换 STOCK_CODE 即可分析任意A股
  3. 输出:
    • 实时平滑股价
    • 自动绿三角买入、红倒三角卖出
    • 实时变化的主周期(比如20/30/60天)

下面直接给出滚动FFT周期策略 + 完整回测完整版,包含:
买卖信号、持仓、净值曲线、胜率、盈亏比、最大回撤、年化收益,一键出结果。

安装依赖

pip install numpy pandas matplotlib tushare

完整可运行代码(FFT周期策略 + 回测)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tushare as ts

# ---------------------- 配置 ----------------------
ts.set_token('你的Tushare Token')
pro = ts.pro_api()

# 策略参数
STOCK_CODE     = "300308.SZ"   # 可替换
START_DATE     = "20240101"
END_DATE       = "20260531"
WINDOW         = 120           # FFT滚动窗口
KEEP_NUM       = 20            # 保留低频分量
PHASE_THRESH   = 0.2           # 相位阈值,越小信号越少越稳
INIT_CAPITAL   = 100000        # 初始资金
FEE_RATE       = 0.0003        # 交易手续费+滑点

# ---------------------- 下载数据 ----------------------
df = pro.daily(ts_code=STOCK_CODE, start_date=START_DATE, end_date=END_DATE)
df = df.sort_values("trade_date").reset_index(drop=True)
df["trade_date"] = pd.to_datetime(df["trade_date"])
price = df["close"].values
dates = df["trade_date"].values
n = len(price)
print(df.tail())

# 信号存储
smooth_list  = np.full(n, np.nan)
buy_signal   = np.full(n, False)
sell_signal  = np.full(n, False)
period_list  = np.full(n, np.nan)

# ---------------------- 滚动FFT生成买卖信号 ----------------------
for i in range(WINDOW, n):
    win_data = price[i-WINDOW:i]
    fft_vals = np.fft.fft(win_data)
    fft_freq = np.fft.fftfreq(WINDOW, d=1)
    amp = np.abs(fft_vals)
    amp[0] = 0  # 去除直流趋势
    
    # 滤波平滑
    fft_filtered = fft_vals.copy()
    fft_filtered[KEEP_NUM : WINDOW-KEEP_NUM] = 0
    smooth = np.fft.ifft(fft_filtered).real[-1]
    smooth_list[i] = smooth
    
    # 主周期
    main_idx = np.argmax(amp)
    main_freq = fft_freq[main_idx]
    if main_freq > 0:
        period_list[i] = 1 / main_freq
    else:
        continue
    
    # 相位判断:接近0买入,接近π卖出
    phase = np.angle(fft_vals[main_idx])
    if abs(phase) < PHASE_THRESH:
        buy_signal[i] = True
    elif abs(abs(phase) - np.pi) < PHASE_THRESH:
        sell_signal[i] = True

# ---------------------- 回测计算 ----------------------
cash = INIT_CAPITAL
shares = 0
net_value = []
position = []
trade_records = []

for i in range(n):
    p = price[i]
    # 买入
    if buy_signal[i] and shares == 0:
        shares = cash / p * (1 - FEE_RATE)
        cash = 0
        trade_records.append({"date": dates[i], "type":"buy", "price":p})
    # 卖出
    elif sell_signal[i] and shares > 0:
        cash = shares * p * (1 - FEE_RATE)
        shares = 0
        trade_records.append({"date": dates[i], "type":"sell", "price":p})
    
    nv = cash + shares * p
    net_value.append(nv)
    position.append(1 if shares>0 else 0)

net_value = np.array(net_value)
position = np.array(position)

# 回测指标
final_cap = net_value[-1]
total_return = (final_cap / INIT_CAPITAL) - 1
days = (dates[-1] - dates[0]).astype(int)
annual_return = (1 + total_return) ** (365/days) - 1

# 最大回撤
peak = np.maximum.accumulate(net_value)
drawdown = (net_value - peak) / peak
max_dd = drawdown.min()

# 胜率
trades = pd.DataFrame(trade_records)
win_count = 0
profit_sum = 0
loss_sum = 0
for i in range(0, len(trades)-1, 2):
    if i+1 >= len(trades): break
    buy_p = trades.loc[i, "price"]
    sell_p = trades.loc[i+1, "price"]
    ret = (sell_p / buy_p) - 1
    if ret > 0:
        win_count += 1
        profit_sum += ret
    else:
        loss_sum += ret

total_trades = win_count + (len(trades)//2 - win_count)
win_rate = win_count / total_trades if total_trades>0 else 0
profit_loss_ratio = abs(profit_sum / loss_sum) if loss_sum !=0 else np.inf

# ---------------------- 输出回测结果 ----------------------
print("="*60)
print(f"【FFT周期策略回测结果】{STOCK_CODE}")
print(f"初始资金: {INIT_CAPITAL:.0f}")
print(f"期末资金: {final_cap:.2f}")
print(f"总收益率: {total_return:.2%}")
print(f"年化收益率: {annual_return:.2%}")
print(f"最大回撤: {max_dd:.2%}")
print(f"交易次数: {total_trades}")
print(f"胜率: {win_rate:.2%}")
print(f"盈亏比: {profit_loss_ratio:.2f}")
print("="*60)

# ---------------------- 绘图 ----------------------
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

fig, axes = plt.subplots(3,1, figsize=(16,12))

# 图1:股价+平滑线+买卖点
axes[0].plot(dates, price, label="收盘价", alpha=0.7)
axes[0].plot(dates, smooth_list, label="FFT平滑线", c="red", lw=2)
axes[0].scatter(dates[buy_signal], price[buy_signal], marker="^", c="g", s=80, label="买入")
axes[0].scatter(dates[sell_signal], price[sell_signal], marker="v", c="r", s=80, label="卖出")
axes[0].set_title(f"{STOCK_CODE} FFT周期交易信号")
axes[0].legend()
axes[0].grid(True)

# 图2:净值曲线
axes[1].plot(dates, net_value, c="purple", label="策略净值")
axes[1].axhline(y=INIT_CAPITAL, c="gray", ls="--", label="初始资金")
axes[1].set_title("策略净值曲线")
axes[1].legend()
axes[1].grid(True)

# 图3:主周期变化
axes[2].plot(dates, period_list, c="blue")
axes[2].set_title("滚动窗口识别的主波动周期(交易日)")
axes[2].set_ylabel("周期(天)")
axes[2].grid(True)

plt.tight_layout()
plt.show()

输出指标说明

  • 总收益率 / 年化收益率:策略整体收益
  • 最大回撤:账户最大亏损幅度,越小越稳
  • 胜率:盈利交易占比
  • 盈亏比:平均盈利 / 平均亏损,>1.5 算优秀

调参实战建议(直接改这4个)

  1. WINDOW=120:窗口越大,抓大周期;窗口60做短线
  2. KEEP_NUM=20:越大保留更多波动,信号变多;越小越平滑
  3. PHASE_THRESH=0.2:阈值越小,买卖点越少,过滤假信号
  4. STOCK_CODE:换股票测试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值