找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3536

积分

0

好友

486

主题
发表于 昨天 06:31 | 查看: 3| 回复: 0

你有没有想过,能不能写一段 Python 代码,让它自动帮你盯盘、判断趋势、发出买卖信号,甚至回测 25 年的历史数据来验证效果?今天我们要深入探讨的,就是一个完整的量化交易策略实战项目——CRL-SuperTrend Pullback Reversal Strategy。这个策略专门针对美股 Charles River Laboratories(CRL),利用 SuperTrend 和 OSMA 动量指标,构建了一套纯规则驱动的交易系统。没有主观判断,没有情绪干扰,一切由代码说了算。

对于正在学习 Python 的你来说,这个项目是一个绝佳的练手案例:它串联了数据获取、技术指标计算、信号生成、回测模拟等多个实战环节。

免责声明: 本文仅供学习交流,不构成任何投资建议。回测结果基于历史数据,不代表未来收益。

一、策略核心思路

这个策略的设计哲学可以用一句话概括:在下跌中入场,在动量恢复时离场

听起来有点反直觉,对吧?大多数人习惯追涨杀跌,而这个策略偏偏要做“逆势交易者”——在市场处于下行趋势时买入,等到动量指标确认回暖时卖出。

它依赖两个技术指标:

SuperTrend(超级趋势指标):负责判断当前市场处于上升趋势还是下降趋势。当 SuperTrend 判定为下降趋势时,策略发出入场信号。

OSMA(MACD 振荡器):即 MACD 线与信号线的差值。当 OSMA 从负值穿越零线变为正值时,说明动量由弱转强,策略发出离场信号。

二、动手实现:从数据到信号

2.1 获取历史数据

第一步是用 yfinance 库下载 CRL 的日线数据,时间跨度从 2000 年到 2025 年底,覆盖了多个市场周期。

import pandas as pd
import numpy as np
import yfinance as yf
import vectorbt as vbt

# -------------------------
# 下载数据
# -------------------------
symbol = "CRL"           # 股票代码
start_date = "2000-01-01" # 起始日期
end_date = "2026-01-01"   # 截止日期
interval = "1d"           # 日线级别

# 通过 yfinance 下载历史行情数据
df = yf.download(symbol, start=start_date, end=end_date, interval=interval)

# 保存为 CSV 文件,方便后续复用
df.to_csv("CRL_clean.csv", index=False)

这段代码会生成一个包含开盘价、最高价、最低价、收盘价和成交量的 DataFrame。后续所有的分析都基于这份数据。

2.2 计算 SuperTrend 指标

SuperTrend 是策略的“趋势裁判”,它通过 ATR(真实波幅均值)动态计算上下轨,然后判定当前处于上升还是下降趋势。

# SuperTrend 参数
SUPERTREND_MULTIPLIER = 3    # ATR 乘数
SUPERTREND_PERIOD = 14       # ATR 计算周期

def calculate_supertrend(df, period=SUPERTREND_PERIOD, multiplier=SUPERTREND_MULTIPLIER):
    """计算 SuperTrend 指标,包括上轨、下轨和趋势方向"""
    hl2 = (df['High'] + df['Low']) / 2  # 最高价与最低价的中间值

    # 计算真实波幅(True Range)
    tr1 = df['High'] - df['Low']
    tr2 = abs(df['High'] - df['Close'].shift())
    tr3 = abs(df['Low'] - df['Close'].shift())
    tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)

    # 计算平均真实波幅(ATR)
    atr = tr.rolling(period).mean()

    # 计算基础上下轨
    upper_basic = hl2 + multiplier * atr
    lower_basic = hl2 - multiplier * atr

    # 初始化最终上下轨
    upper_band = upper_basic.copy()
    lower_band = lower_basic.copy()

    # 趋势列表:True 表示上升趋势,False 表示下降趋势
    supertrend = [True]

    for i in range(1, len(df)):
        # 上轨调整:如果前一天收盘价低于上轨,则取较小值
        if df['Close'].iloc[i-1] <= upper_band.iloc[i-1]:
            upper_band.iloc[i] = min(upper_basic.iloc[i], upper_band.iloc[i-1])

        # 下轨调整:如果前一天收盘价高于下轨,则取较大值
        if df['Close'].iloc[i-1] >= lower_band.iloc[i-1]:
            lower_band.iloc[i] = max(lower_basic.iloc[i], lower_band.iloc[i-1])

        # 判断趋势方向
        if df['Close'].iloc[i] > upper_band.iloc[i-1]:
            supertrend.append(True)   # 突破上轨,转为上升趋势
        elif df['Close'].iloc[i] < lower_band.iloc[i-1]:
            supertrend.append(False)  # 跌破下轨,转为下降趋势
        else:
            supertrend.append(supertrend[-1])  # 维持前一天的趋势

    df['SuperTrend'] = supertrend
    df['Upper_Band'] = upper_band
    df['Lower_Band'] = lower_band
    return df

def st_downtrend(df):
    """返回 SuperTrend 下降趋势的布尔序列(True = 下降趋势)"""
    df = calculate_supertrend(df)
    return ~df['SuperTrend']

关键点: 这个策略的入场条件是 SuperTrend 判定为下降趋势,这是一种逆向思维——在别人恐惧时贪婪。

2.3 计算 OSMA 动量指标

OSMA 是 MACD 与其信号线之间的差值,用来衡量动量的加速或减速。

# OSMA 参数
OSMA_FAST = 12    # 快速 EMA 周期
OSMA_SIGNAL = 9   # 信号线 EMA 周期
OSMA_SLOW = 26    # 慢速 EMA 周期

def calculate_macd_osma(df, fast=OSMA_FAST, slow=OSMA_SLOW, signal_period=OSMA_SIGNAL):
    """
    计算 MACD、信号线和 OSMA(MACD - 信号线)
    """
    df = df.copy()
    # 计算快速和慢速指数移动平均线
    df['EMA_fast'] = df['Close'].ewm(span=fast, adjust=False).mean()
    df['EMA_slow'] = df['Close'].ewm(span=slow, adjust=False).mean()

    # MACD = 快速 EMA - 慢速 EMA
    df['MACD'] = df['EMA_fast'] - df['EMA_slow']

    # 信号线 = MACD 的 EMA
    df['Signal'] = df['MACD'].ewm(span=signal_period, adjust=False).mean()

    # OSMA = MACD - 信号线
    df['OSMA'] = df['MACD'] - df['Signal']
    return df

def osma_cross_above_0(df):
    """判断 OSMA 是否从负值上穿零线(动量由弱转强)"""
    df = calculate_macd_osma(df)
    return (df['OSMA'].shift(1) <= 0) & (df['OSMA'] > 0)

当 OSMA 从负值穿越到正值,意味着动量恢复,策略选择在此时获利了结。

三、生成交易信号并回测

3.1 入场与离场条件

信号逻辑非常简洁:

# -------------------------
# 入场条件:SuperTrend 处于下降趋势
# -------------------------
df["Supertrend_DownTrend"] = st_downtrend(df)

entry_conditions = [
    'Supertrend_DownTrend',
]
df['entry_signal'] = df[entry_conditions].all(axis=1)

# -------------------------
# 离场条件:OSMA 上穿零线
# -------------------------
df["OSMA_Cross_Above_0"] = osma_cross_above_0(df)

exit_conditions = [
    'OSMA_Cross_Above_0',
]
df['exit_signal'] = df[exit_conditions].all(axis=1)

3.2 回测执行

为了避免“未来函数”,所有信号都向后移动一根 K 线再执行。同时加入了手续费和滑点,模拟真实交易环境。

# -------------------------
# 回测设置
# -------------------------
# 信号延迟 1 天执行,避免使用当天信号当天交易
shift_entries = df['entry_signal'].shift(1).astype(bool).fillna(False).to_numpy()
shift_exits = df['exit_signal'].shift(1).astype(bool).fillna(False).to_numpy()

# 使用 vectorbt 进行向量化回测
pf = vbt.Portfolio.from_signals(
    close=df['Open'],          # 以开盘价成交
    entries=shift_entries,      # 入场信号
    exits=shift_exits,          # 离场信号
    init_cash=100_000,          # 初始资金 10 万美元
    fees=0.001,                 # 手续费 0.1%
    slippage=0.002,             # 滑点 0.2%
    freq='1d'                   # 日线频率
)

# 打印策略统计信息
print(pf.stats())

# 绘制权益曲线
pf.plot().show()

3.3 对比买入持有策略

为了评估策略是否真的有效,还需要和最简单的“买入持有”进行对比:

# -------------------------
# 买入持有基准回测
# -------------------------
df_holding = df['Open']
pf_holding = vbt.Portfolio.from_holding(df_holding, init_cash=100_000, freq='1d')
print(pf_holding.stats())

四、回测结果一览

根据原文给出的回测数据(2000 年 6 月 23 日至 2025 年 12 月 31 日):

指标 数值
总收益率 1,255%
最大回撤 74%
总交易次数 57 次
胜率 82%
盈亏比(Profit Factor) 1.50

这个策略在总收益上跑赢了买入持有,胜率高达 82%,但最大回撤也达到了 74%,说明在某些极端行情下策略仍然会经历较大的浮亏。

五、从这个项目中你能学到什么

对 Python 学习者而言,这个项目涵盖了以下实用技能:

数据处理:使用 Pandas 进行时间序列数据的清洗、计算和转换。移动平均、指数平滑、布尔索引等操作贯穿始终。

技术指标实现:从零手写 SuperTrend 和 MACD/OSMA 的计算逻辑,比直接调库更能帮你理解指标的本质,这涉及到核心的算法思想。

回测框架使用vectorbt 是一个高性能的向量化回测库,适合快速验证策略想法。

工程化思维:信号延迟执行、手续费和滑点模拟、与基准策略对比——这些细节体现了从“玩具代码”到“可信回测”的进阶。

六、策略的局限性

原文作者也坦诚指出,仅仅在历史数据上跑通 26 年的回测,并不足以证明策略在真实市场中能存活。还需要进行更深入的稳健性测试,比如:

Walk-Forward 分析:将数据分成多个训练/测试窗口,验证策略在“未见数据”上的表现。

多标的测试:目前策略只针对 CRL 一只股票,是否能迁移到其他标的需要验证。

风控增强:74% 的最大回撤在实盘中很难承受,加入止损、仓位管理等风控层是必要的改进方向。

总结

这个 CRL-SuperTrend 回调反转策略用极简的规则(1 个入场条件 + 1 个离场条件)实现了一个完整的量化交易系统。它的核心理念是“逆势入场、顺势离场”——在 SuperTrend 判定下跌时买入,在 OSMA 动量恢复时卖出。

对于 Python 学习者来说,这个项目的价值不仅在于量化交易本身,更在于它展示了如何用 Python 构建一个完整的“数据驱动决策系统”:获取数据、计算特征、生成信号、模拟执行、评估结果。这套思路可以迁移到很多智能与数据科学领域。

当然,请务必记住:回测不等于实盘,历史不代表未来。如果你对量化交易感兴趣,一定要在充分学习和测试之后再做决策。如果你希望找到更多类似的实战项目与开发者交流,不妨来云栈社区看看,那里有丰富的技术资源和活跃的讨论氛围。




上一篇:AI Agent能力拓展指南:Skill与MCP的选择困境与混合架构设计
下一篇:AI推理革命:专用芯片成本骤降10倍,GPU会步矿机后尘吗?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-25 09:11 , Processed in 1.434934 second(s), 45 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表