【ptrade】放量突破 回测实录

10_放量突破 · 回测实录

成交量放大到20日均量的1.5倍以上,且收盘价突破20日最高价,才买入。

策略类型 :趋势跟踪 / 量价

回测批次 :2026-10-04(本地 QuantStudio 回测引擎)

数据来源 :QuantStudio 本地行情库(etf_daily / stock_daily)


一、策略思路

在03号海龟突破的基础上加了一个"成交量确认"条件。逻辑是:价格突破如果只是缩量创新高,很可能是主力在自弹自唱、没有真实买盘接手;只有放量突破,才说明有增量资金真正进场。出场仍然用跌破10日最低价。多了一道过滤,信号数量会明显减少,但每个信号的"含金量"更高。

关键参数

参数 取值
VOL_MA_PERIOD 20
VOL_MULTIPLIER 1.5
ENTRY_LOOKBACK 20
EXIT_LOOKBACK 10
MAX_HOLDINGS 3
EXPOSURE 0.98

二、回测设置

项目 设置
策略文件 10_放量突破.py
回测区间 2024-01-01 ~ 2026-09-02
初始资金 100000 元
佣金费率 0.00035(最低 5.0 元)
印花税率 0.001
过户费率 1e-05
滑点 0.0
撮合价格 close
基准 000300
交易日数 647

三、回测结果

把这张图和03号海龟突破放在一起看:交易的回合数应该更少,胜率可能更高,但总收益不一定更好------"过滤"永远是一个取舍,它筛掉噪音的同时也会筛掉机会。

绩效指标

指标 数值
累计收益 53.70%
年化收益 18.07%
最大回撤 19.16%
年化波动率 23.04%
夏普比率 0.680
索提诺比率 1.116
卡玛比率 0.943
回合胜率 48.94%
日胜率 50.39%
盈亏比 222.2%
盈利回合数 23
亏损回合数 24
平均持仓天数 57.1
成交笔数 104

「盈亏比」= 平均盈利 ÷ 平均亏损 > 100%,表示平均每笔赚的比亏的多。

「回合」指一次完整的买入→卖出(round_trips),「成交笔数」是买卖双方分开计数的原始成交记录条数。
⚠️ 本窗口基准数据不完整 :引擎的基准序列尾部为占位填充值,据此算出的沪深300收益为 0.00%,该数字不可信;净值图中的基准曲线已截断至有效区间(至 2026-07-31)。因此本策略的 α、β、超额收益、信息比率等相对指标一律不可引用,只能用绝对收益与回撤来判断。

分年度收益

年份 区间收益 交易日
2024 18.75% 242
2025 27.41% 243
2026 2.51% 162

四、交易明细

  • 完整交易回合数:47 个(盈利 23 / 亏损 24)
  • 单笔平均盈亏:+1128.55 元 ;最大单笔盈利 +31047.40 元 ;最大单笔亏损 -6264.01 元
  • 持仓天数:平均 57.1 天 ,最长 521 天 ,最短 7 天

盈利最多的 5 个回合

买入日 卖出日 标的 盈亏(元) 持仓天数
2025-06-06 2025-10-14 515880.SH +31047.40 130
2024-09-25 2024-12-17 159915.SZ +10505.15 83
2024-09-25 2024-11-25 512880.SH +9968.27 61
2024-09-25 2024-11-26 515880.SH +6294.94 62
2026-07-02 2026-08-25 512170.SH +5598.01 54

亏损最多的 5 个回合

买入日 卖出日 标的 盈亏(元) 持仓天数
2026-03-12 2026-04-03 516160.SH -6264.01 22
2024-12-27 2025-01-03 515880.SH -4048.41 7
2024-06-17 2024-07-25 515880.SH -3604.92 38
2024-12-23 2025-01-03 512480.SH -3563.91 11
2026-03-03 2026-03-16 512660.SH -2282.59 13

五、完整回测代码

文件名:10_放量突破.py(共 115 行)

引擎:QuantStudio 本地回测;全部为公开 API,无平台专有调用。

python 复制代码
# -*- coding: utf-8 -*-
"""
10_放量突破策略 (QuantStudio 本地回测版)
========================================
核心逻辑:成交量 > 20日均量×1.5 且 收盘价突破20日最高价 → 买入。
跌破10日最低价 → 卖出。
量价齐升是趋势启动的经典信号。
多标的独立判断,最多持3只。
"""

import numpy as np

ETF_POOL = [
    '510300.SS', '159915.SZ', '510500.SS', '512880.SS',
    '512800.SS', '512690.SS', '512170.SS', '512480.SS',
    '515880.SS', '512660.SS', '516160.SS', '518880.SS',
]

VOL_MA_PERIOD = 20      # 成交量均线周期
VOL_MULTIPLIER = 1.5    # 放量倍数
ENTRY_LOOKBACK = 20      # 突破周期
EXIT_LOOKBACK = 10       # 出场周期
MAX_HOLDINGS = 3
EXPOSURE = 0.98


def initialize(context):
    set_benchmark('000300.SS')
    run_daily(context, trade, time='09:30')
    log.info('10_放量突破初始化: 池=%d, 量能均线=%d日, 放量倍数=%.1f, 突破=%d日',
             len(ETF_POOL), VOL_MA_PERIOD, VOL_MULTIPLIER, ENTRY_LOOKBACK)


def _get_breakout_signal(code):
    """返回 (是否放量突破, 突破幅度, 是否跌破出场线)"""
    try:
        n = max(VOL_MA_PERIOD, ENTRY_LOOKBACK, EXIT_LOOKBACK) + 2
        h = get_history(n, '1d', field=['close', 'high', 'low', 'volume'],
                        security_list=code, fq='pre', include=False, is_dict=True)
        if h is None or code not in h:
            return False, 0.0, False
        df = h[code]
        closes = np.asarray(df['close'], dtype=float)
        highs = np.asarray(df['high'], dtype=float)
        lows = np.asarray(df['low'], dtype=float)
        volumes = np.asarray(df['volume'], dtype=float)

        if len(closes) < n:
            return False, 0.0, False

        current_close = float(closes[-1])
        current_vol = float(volumes[-1])

        # 成交量均线(不含当日)
        vol_ma = float(np.mean(volumes[-(VOL_MA_PERIOD + 1):-1]))
        volume_breakout = current_vol > vol_ma * VOL_MULTIPLIER if vol_ma > 0 else False

        # 20日最高价(不含当日)
        entry_high = float(np.max(highs[-(ENTRY_LOOKBACK + 1):-1]))
        price_breakout = current_close > entry_high

        # 10日最低价(不含当日)
        exit_low = float(np.min(lows[-(EXIT_LOOKBACK + 1):-1]))
        price_breakdown = current_close < exit_low

        breakout_amp = (current_close - entry_high) / entry_high if entry_high > 0 else 0
        is_breakout = volume_breakout and price_breakout

        return is_breakout, breakout_amp, price_breakdown
    except Exception:
        return False, 0.0, False


def trade(context):
    held = list(context.portfolio.positions.keys())

    # 1. 跌破出场线卖出
    for code in held:
        _, _, breakdown = _get_breakout_signal(code)
        if breakdown:
            order_target_value(code, 0)
            log.info('放量突破策略卖出 %s: 跌破%d日低点', code, EXIT_LOOKBACK)

    # 2. 放量突破买入
    held = list(context.portfolio.positions.keys())
    if len(held) >= MAX_HOLDINGS:
        return

    candidates = []
    for code in ETF_POOL:
        if code in held:
            continue
        is_breakout, amp, _ = _get_breakout_signal(code)
        if is_breakout:
            candidates.append((code, amp))

    if not candidates:
        return

    # 按突破幅度排序
    candidates.sort(key=lambda x: x[1], reverse=True)
    slots = MAX_HOLDINGS - len(held)
    buy_list = candidates[:slots]

    total_value = context.portfolio.total_value
    per_value = total_value * EXPOSURE / MAX_HOLDINGS
    for code, amp in buy_list:
        order_target_value(code, per_value)
        log.info('放量突破买入 %s: 突破幅度=%.2f%%, 目标市值=%.0f',
                 code, amp * 100, per_value)


def handle_data(context, data):
    pass

六、风险提示

放量本身是双刃剑:突破日的巨大成交量,也可能是主力借突破出货。此外成交量数据受停牌复牌、ETF申赎影响,会出现假放量。过滤条件越多,策略对参数的敏感度就越高,1.5倍这个阈值不宜随意调整。


免责声明:本文所有数字来自本地历史回测,回测收益不代表未来表现,也不构成任何投资建议或个股/基金推荐。策略代码仅供研究学习,实盘执行还需额外考虑滑点、冲击成本、流动性、涨跌停与平台差异等因素。投资有风险,决策需谨慎。

相关推荐
梦帮科技2 小时前
【3.0修订版】# 创作者激励池与空投分发:AirdropDistributor 与 20% 生态分配
去中心化·区块链·智能合约·同态加密·共识算法·信任链·分布式账本
RunsenLIu2 小时前
【ptrade】网格交易 回测实录
区块链
梦帮科技3 小时前
【3.0修订版】跨链桥架构与资产守恒:BSCBridge、BSCMirrorBridge 与 Lock-and-Mint 模型
架构·去中心化·区块链·智能合约·共识算法·信任链
梦帮科技1 天前
【3.0修订版】创世与节点工程:genesis.json 与 node.toml 解剖
数据结构·算法·json·去中心化·区块链·智能合约·信任链
梦帮科技1 天前
【3.0修订版】 RNS 代币架构:ERC20 五件套扩展与六钱包分配
数据结构·后端·算法·架构·node.js·区块链·php
怒放de生命20102 天前
区块链为啥死磕去中心化”
去中心化·区块链
微三云马玮均—GEO源码系统 私有化部署3 天前
消费返物业费:消费+服务趋势的必然产物!
大数据·人工智能·物联网·区块链·生活
55873 生态系统3 天前
第 22 篇|社区聊天|55873 文明共建者的日常交流与协作界面
人工智能·区块链·55873全域文明生态体系·55873操作系统·55873社区聊天