Py学习  »  Python

用 Python 实现自适应均线 + 波动率交易策略,新手也能看懂

数据科学实战 • 1 周前 • 50 次点击  

欢迎加入专注于财经数据与量化投研的【数据科学实战】知识星球!在这里,您将获取持续更新的《财经数据宝典》和《量化投研宝典》,这两部宝典相辅相成,为您在量化投研道路上提供明确指引。 我们提供了精选的国内外量化投研的 180+ 篇高质量文章,并每日更新最新研究成果,涵盖策略开发、因子分析、风险管理等核心领域。 无论您是量化投资新手还是经验丰富的研究者,星球社区都能帮您少走弯路,事半功倍,共同探索数据驱动的投资世界!

引言

在量化交易中,如何在追踪趋势的同时控制风险,一直是交易者们关注的核心问题。今天给大家介绍一个结合了 Kaufman 自适应移动平均线(KAMA)和平均真实波幅(ATR)的交易策略。这个策略的核心思想是:用 KAMA 捕捉趋势,用 ATR 控制风险,再通过 Optuna 自动优化参数。

这篇文章将带你从零开始理解这个策略的原理和实现方法,即使你是 Python 新手,也能跟着代码一步步学习。

策略的核心思想

这个策略主要由三个部分组成:

KAMA 自适应趋势跟踪:不同于简单移动平均线(SMA)或指数移动平均线(EMA),KAMA 会根据市场波动自动调整平滑程度。在趋势明显时反应迅速,在震荡行情中则更加平滑,避免频繁交易。

ATR 波动率过滤:ATR 用来衡量价格波动的剧烈程度。当波动率过高时,策略会避免入场;如果持仓期间波动率突然飙升,则立即止损出场。

Optuna 参数优化:不用手动猜测最佳参数,让 Optuna 自动搜索最优的 KAMA 周期和 ATR 阈值组合。

代码实现

1. 获取历史数据

首先,我们需要获取股票的历史价格数据。这里使用 Financial Modeling Prep API:

import pandas as pd
import requests

def fetch_data_fmp(symbol: str, start_date: str, end_date: str, api_key: str):
    """
    从 Financial Modeling Prep API 获取历史价格数据
    
    参数:
        symbol: 股票代码,如 'TSLA'
        start_date: 开始日期,格式 'YYYY-MM-DD'
        end_date: 结束日期,格式 'YYYY-MM-DD'
        api_key: API 密钥
    
    返回:
        包含日期、开盘价、最高价、最低价、收盘价、成交量的 DataFrame
    """

    url = f"https://financialmodelingprep.com/api/v3/historical-price-full/{symbol}?from={start_date}&to={end_date}&apikey={api_key}"
    resp = requests.get(url)
    resp.raise_for_status()
    data = resp.json().get('historical', [])
    df = pd.DataFrame(data)
    df['date'] = pd.to_datetime(df['date'])
    df = df.sort_values('date').reset_index(drop=True)
    return df[['date''open''high''low''close''volume']]

2. 计算 KAMA 指标

KAMA 的核心是效率比率(ER)。当价格频繁变向时,ER 较低,KAMA 平滑程度更高;当价格趋势明显时,ER 较高,KAMA 更紧密跟随价格。

import numpy as np

def kaufman_adaptive_moving_average(df, er_period, fast_period, slow_period):
    """
    计算 Kaufman 自适应移动平均线
    
    参数:
        df: 包含收盘价的 DataFrame
        er_period: 效率比率计算周期
        fast_period: 快速平滑常数周期
        slow_period: 慢速平滑常数周期
    
    返回:
        添加了 KAMA 列的 DataFrame
    """

    close = df['close'].values
    
    # 计算价格变化幅度
    change = abs(close[er_period:] - close[:-er_period])
    
    # 计算波动性(价格变化的累加)
    volatility = np.sum(abs(close[1:] - close[:-1]).reshape(-1, er_period), axis=1)
    
    # 效率比率 = 价格变化 / 波动性
    er = change / volatility
    
    # 计算平滑常数
    sc_fast = 2 / (fast_period + 1)
    sc_slow = 2 / (slow_period + 1)
    sc = (er * (sc_fast - sc_slow) + sc_slow) ** 2
    
    # 计算 KAMA
    kama = np.zeros(len(close))
    kama[:er_period] = close[:er_period]
    for i in range(er_period, len(close)):
        kama[i] = kama[i-1] + sc[i-er_period] * (close[i] - kama[i-1])
    
    df['KAMA'] = kama
    return df

3. 计算 ATR 指标

ATR 用于衡量价格波动的幅度,是风险控制的关键指标:

def average_true_range(df, window=14):
    """
    计算平均真实波幅(ATR)
    
    参数:
        df: 包含最高价、最低价、收盘价的 DataFrame
        window: ATR 计算窗口,默认 14 天
    
    返回:
        添加了 ATR 列的 DataFrame
    """

    # 计算真实波幅的三个组成部分
    high_low = df['high'] - df['low']  # 当日最高价减最低价
    high_close = abs(df['high'] - df['close'].shift())  # 当日最高价减昨日收盘价
    low_close = abs(df['low'] - df['close'].shift())  # 当日最低价减昨日收盘价
    
    # 取三者中的最大值作为真实波幅
    tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
    
    # 计算移动平均得到 ATR
    atr = tr.rolling(window).mean()
    df['ATR'] = atr
    return df

4. 生成交易信号

根据 KAMA 和 ATR 生成买卖信号:

def generate_signals(df, atr_min_pct, atr_max_pct, atr_exit_pct):
    """
    生成交易信号
    
    买入条件:价格在 KAMA 之上,且 ATR 百分比在合理范围内
    卖出条件:价格跌破 KAMA,或 ATR 百分比超过退出阈值
    
    参数:
        df: 包含 KAMA 和 ATR 的 DataFrame
        atr_min_pct: ATR 最小百分比阈值
        atr_max_pct: ATR 最大百分比阈值
        atr_exit_pct: ATR 退出百分比阈值
    
    返回:
        添加了持仓信号的 DataFrame
    """

    # 计算 ATR 占收盘价的百分比
    df['atr_pct'] = df['ATR'] / df['close'] * 100
    df['position'] = 0
    
    for i in range(1, len(df)):
        # 买入信号:价格高于 KAMA 且波动率在可接受范围内
         if df['close'][i] > df['KAMA'][i] and atr_min_pct <= df['atr_pct'][i] < atr_max_pct:
            df.at[i, 'position'] = 1
        # 卖出信号:价格低于 KAMA 或波动率过高
        elif df['close'][i] < df['KAMA'][i] or df['atr_pct'][i] > atr_exit_pct:
            df.at[i, 'position'] = 0
        else:
            # 保持前一天的持仓状态
            df.at[i, 'position'] = df.at[i-1'position']
    
    return df

5. 回测逻辑

模拟交易过程,计算最终收益:

def backtest(df, initial_balance=1000):
    """
    回测策略表现
    
    参数:
        df: 包含持仓信号的 DataFrame
        initial_balance: 初始资金,默认 1000
    
    返回:
        最终账户余额
    """

    balance = initial_balance  # 账户余额
    position = 0  # 持仓数量
    entry_price = 0  # 入场价格
    
    for i in range(len(df)):
        # 开仓:从无仓位变为有仓位
        if position == 0 and df['position'][i] == 1:
            position = balance / df['close'][i]  # 全仓买入
            entry_price = df['close'][i]
            balance = 0
        # 平仓:从有仓位变为无仓位
        elif position > 0 and df['position'][i] == 0:
            balance = position * df['close'][i]  # 卖出所有持仓
            position = 0
    
    # 如果最后还有持仓,按最后收盘价结算
    if position > 0:
        balance = position * df['close'].iloc[-1]
    
    return balance

6. 使用 Optuna 优化参数

让 Optuna 自动搜索最优参数组合:

import optuna

def objective(trial):
    """
    Optuna 优化目标函数
    
    自动搜索最优的策略参数组合
    """

    # 定义参数搜索范围
    er_period = trial.suggest_int('er_period'230)  # 效率比率周期
    fast_period = trial.suggest_int('fast_period'220)  # 快速周期
    slow_period = trial.suggest_int('slow_period'20100)  # 慢速周期
    atr_window = trial.suggest_int('atr_window'530)  # ATR 窗口
    atr_min_pct = trial.suggest_uniform('atr_min_pct'0.53)  # ATR 最小百分比
    atr_max_pct = trial.suggest_uniform('atr_max_pct'15)  # ATR 最大百分比
    atr_exit_pct = trial.suggest_uniform('atr_exit_pct'520)  # ATR 退出百分比
    
    # 复制数据并应用策略
    df_copy = df.copy()
    df_copy = kaufman_adaptive_moving_average(df_copy, er_period, fast_period, slow_period)
    df_copy = average_true_range(df_copy, atr_window)
    df_copy = generate_signals(df_copy, atr_min_pct, atr_max_pct, atr_exit_pct)
    
    # 返回最终收益作为优化目标
    final_balance = backtest(df_copy)
    return final_balance

# 创建优化研究并运行
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)

实战案例:TSLA 回测结果

以 TSLA(特斯拉)股票为例,使用 Sharpe 比率作为优化指标,策略表现如下:

指标
数值
策略总收益
5394.21%
买入持有收益
1290.93%
总交易次数
90 次
盈利交易比例
45.56%
Sharpe 比率
1.76
年化收益率(CAGR)
77.58%
最大回撤
-39.42%

从结果可以看出,策略收益远超买入持有策略,同时通过 ATR 过滤有效控制了波动率较高时期的风险。

总结

这个 KAMA + ATR 策略的优势在于:

KAMA 的自适应特性使其在趋势行情和震荡行情中都能保持较好的表现,避免了传统均线在震荡期频繁产生假信号的问题。ATR 过滤机制则在高波动时期暂停交易,有效降低了策略的回撤风险。Optuna 自动优化省去了手动调参的繁琐工作,提高了策略开发效率。

当然,需要注意的是,任何基于历史数据优化的策略都存在过拟合风险,实盘交易前务必进行充分的样本外测试。

参考文章

加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取本文完整研究解析、代码实现细节。

财经数据与量化投研知识社区

核心权益如下:

  1. 赠送《财经数据宝典》完整文档,汇集多年财经数据维护经验
  2. 赠送《量化投研宝典》完整文档,汇集多年量化投研领域经验
  3. 赠送《PyBroker-入门及实战》视频课程,手把手学习量化策略开发
  4. 每日分享高质量量化投研文章(已更新 180+篇)、代码和相关资料
  5. 定期更新高频财经数据
  6. 参与年度不少于 10 次专属直播与录播课程
  7. 与核心开发者直接交流,解决实际问题
  8. 获取专业微信群交流机会和课程折扣

星球已有丰富内容积累,包括量化投研论文、财经高频数据、 PyBroker 视频教程、定期直播、数据分享和答疑解难。适合对量化投研和财经数据分析有兴趣的学习者及从业者。欢迎加入我们!

好文推荐

1. 用 Python 打造股票预测系统:Transformer 模型教程(一)

2. 用 Python 打造股票预测系统:Transformer 模型教程(二)

3. 用 Python 打造股票预测系统:Transformer 模型教程(三)

4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)

5. 揭秘隐马尔可夫模型:因子投资的制胜武器

6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用

7. 金融 AI 助手:FinGPT 让你轻松掌握市场分析

8. 量化交易秘籍:为什么专业交易员都在用对数收益率?

9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解

10. 掌握金融波动率模型:完整 Python 实现指南

好书推荐



Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/189875