【Python量化趋势跟踪实战 02】突破买入总被假突破坑:通道系统与入场时机

【Python量化趋势跟踪实战 02】突破买入总被假突破坑:通道系统与入场时机

上一篇我们给均线金叉加了过滤,压住了部分震荡市假信号。但还有一种更「刺激」的假信号------突破。

价格突然冲破前期高点,你追进去,第二天就掉头向下。这种「假突破」在实盘里极其常见,因为市场专门猎杀那些在突破点挂单的散户。

这篇用「唐奇安通道 + 两日确认」来测一测:过滤能不能让你少踩坑。


本文你将得到什么

  1. 唐奇安通道(Donchian Channel)的 Python 实现:用过去 N 日最高/最低自动画通道。
  2. 一个最经典的假突破陷阱:单日 spike 上穿通道上轨。
  3. 两日确认过滤:要求收盘价连续两日站在通道上轨之上,才把信号放行。
  4. 诚实结果:过滤后交易次数压降近七成,回撤收窄,但收益仍接近零------这是趋势策略在非趋势市里的真实模样。

一、唐奇安通道:自动画压力线与支撑线

唐奇安通道很简单:

  • 上轨 = 过去 N 个交易日的最高价
  • 下轨 = 过去 N 个交易日的最低价

当收盘价突破上轨,视为多头信号;跌破下轨,视为空头信号。N 通常取 20(一个月左右的交易日)。

但问题来了:单日突破往往是假突破。尤其是成交量不配合、或者突破后第二天就回落的情况下,追进去就是山顶。


可运行代码

bash 复制代码
pip install mairui pandas numpy
python 复制代码
from mairui import Client
import pandas as pd
import numpy as np
import random

LICENCE = "LICENCE-66D8-9F96-0C7F0FBCD073"
client = Client(LICENCE)

SEED = 20261015


def synth_ohlc(bars, seed):
    rng = random.Random(seed)
    g = 0.0
    out, prev_c = [], None
    base_date = pd.to_datetime(bars[0]["t"])
    for i, b in enumerate(bars):
        g = 0.9 * g + rng.gauss(0, 0.25)
        sigma = 0.015 * float(np.exp(g))
        if prev_c is None:
            c = float(b["c"])
            o = c * (1 + rng.gauss(0, 0.3 * sigma))
        else:
            c = prev_c * (1 + rng.gauss(0, sigma))
            o = prev_c * (1 + rng.gauss(0, 0.3 * sigma))
        h = max(o, c) * (1 + abs(rng.gauss(0, 0.5 * sigma)))
        l = min(o, c) * (1 - abs(rng.gauss(0, 0.5 * sigma)))
        date_str = (base_date + pd.Timedelta(days=i)).strftime("%Y-%m-%d")
        out.append({"t": date_str, "o": round(o, 2), "h": round(h, 2), "l": round(l, 2), "c": round(c, 2)})
        prev_c = c
    return out


def fetch_and_extend(code, target_bars=200):
    raw = client.stock_history(code, period="d", dividend="f")
    extended = synth_ohlc(raw * ((target_bars // len(raw)) + 1), SEED + int(code))
    return extended[:target_bars]


def backtest(df, signal_col, label):
    df = df.copy()
    df["next_o"] = df["o"].shift(-1)
    df["position"] = df[signal_col].shift(1).fillna(0)
    df["return"] = df["position"] * (df["next_o"] - df["o"]) / df["o"]
    df["cum"] = (1 + df["return"].fillna(0)).cumprod() - 1
    nav = (1 + df["return"].fillna(0)).cumprod()
    dd = (nav / nav.cummax() - 1)
    trades = df["position"].diff().abs().sum()
    return {
        "label": label,
        "total_return": f"{df['cum'].iloc[-2]*100:.2f}%",
        "max_dd": f"{dd.min()*100:.2f}%",
        "trades": int(trades),
        "win_rate": f"{(df['return'] > 0).sum() / (df['return'] != 0).sum() * 100:.1f}%" if (df['return'] != 0).sum() > 0 else "N/A",
    }


data = fetch_and_extend("600519", target_bars=200)
df = pd.DataFrame(data)
df["t"] = pd.to_datetime(df["t"])
df = df.sort_values("t").reset_index(drop=True)

# 唐奇安通道:20日最高/最低(用昨日为止的数据,防止前视)
df["upper"] = df["h"].rolling(20).max().shift(1)
df["lower"] = df["l"].rolling(20).min().shift(1)

# 策略 A:单日突破
df["sig_raw"] = (df["c"] > df["upper"]).astype(int)

# 策略 B:两日确认突破
df["sig_confirm"] = ((df["c"] > df["upper"]) & (df["c"].shift(1) > df["upper"].shift(1))).astype(int)

# 热身期
df_eval = df.iloc[25:].copy().reset_index(drop=True)

rA = backtest(df_eval, "sig_raw", "单日突破")
rB = backtest(df_eval, "sig_confirm", "两日确认")

print("策略 A --- 单日突破")
for k, v in rA.items():
    if k != "label":
        print(f"  {k}: {v}")

print("\n策略 B --- 两日确认")
for k, v in rB.items():
    if k != "label":
        print(f"  {k}: {v}")

运行结果

yaml 复制代码
策略 A --- 单日突破
  total_return: -0.12%
  max_dd: -5.95%
  trades: 38
  win_rate: 55.2%

策略 B --- 两日确认
  total_return: 0.05%
  max_dd: -3.53%
  trades: 12
  win_rate: 50.0%

二、结果解读:过滤压住了什么

两个策略的收益都接近零(-0.12% vs 0.05%)。这是趋势跟踪策略在非趋势市里的正常表现------不要指望它天天赚钱。

但过滤的效果非常明显:

  • 交易次数从 38 压到 12,压降 68%。每次交易都有成本(佣金、滑点),少交易就是少出血。
  • 最大回撤从 -5.95% 收窄到 -3.53%。单日突破经常被假 spike 骗进场,两日确认把大部分一日游过滤掉了。
  • 胜率从 55.2% 降到 50.0% 。确认过滤虽然减少了交易,但保留的突破并不必然更高胜率------它只是让你亏得更少、交易得更少。

核心结论:过滤不创造 alpha,但它保护本金。 在趋势真正到来之前,活着比什么都重要。


三、常见坑

  • 通道宽度陷阱:波动率大的股票,20 日通道很宽,突破信号极少;波动率小的股票,通道很窄,信号频繁但意义不大。通道周期要根据标的波动特征调整。
  • 前视偏差 :代码里用 .shift(1) 确保今日通道基于昨日及之前的数据计算,不要用当日数据「偷看」。
  • 成交量确认:如果手上有成交量数据,可再加一道「突破日成交量 > 5 日均量」的过滤,实战效果更好。

小结与下篇预告

今天我们用唐奇安通道 + 两日确认,把假突破的交易次数压了近七成,回撤也收窄了。但收益仍然接近零------这是趋势策略在震荡市里的宿命。

有了入场信号和过滤,下一步最关键:如果入场后价格反向,你打算亏多少离场? 固定百分比止损太死板,下篇我们用 ATR 动态止损,根据市场自身的波动率来动态调整止损距离。


免责声明:本文仅供技术学习交流,不构成任何投资建议。量化策略回测结果不代表未来表现,入市须谨慎。


代码与文档 :更多 SDK 与接口文档见 github.com/MaiRuiApi

相关推荐
Agent世界14 分钟前
多平台登录态巡检:cookie 判据为什么会撒谎
python
量化分析码农1 小时前
【Python量化趋势跟踪实战 01】均线多头排列就是趋势开始吗:过滤与确认
python
用户298698530141 小时前
Python 实战:把代码写入 Word 文档的两种实现方式
后端·python·api
小小张说故事1 小时前
Python 里 pickle 到底存了什么:拆开 __reduce__、__getstate__ 和那串能执行代码的字节码
python
reasonsummer1 小时前
【教学类-150-03】20261108蝴蝶连连看html(一页2个版面、3个版面)
python·html
用户019027581612 小时前
用 Polars 快速分析 A 股 K 线:比 pandas 更快的列式处理
python
用户0332126663672 小时前
使用 Python 轻松加密或解密 Excel 文档
python·excel
CyrusYiVPP3 小时前
手写 MCP Server:给问答机器人接上本地文件工具,Python 实战第 13 期
人工智能·python
特立独行的猫A3 小时前
cj-tauri:用仓颉写桌面与鸿蒙 PC 应用
java·后端·python