可直接回测!Ptrade 强势 ETF 周度轮动程序化交易策略全解

0 阅读8分钟

文章简介

本文基于 Ptrade 量化平台,搭建一套可直接回测、仿真运行的周度强势 ETF 轮动策略。核心逻辑:每周筛选 20 日涨幅最强 2 只 ETF,7:3 资金配比买入,每周最后一个交易日全部清仓,循环滚动做趋势短线。全文附带完整可运行代码、函数拆解、节假日自动适配逻辑与实操避坑要点,新手也能快速上手改造。

适用人群:Ptrade 量化学习者、ETF 趋势交易者、程序化自动化交易爱好者

平台: Ptrade Python 量化接口

策略类型:周度趋势轮动、动量策略

一、策略核心逻辑

整套策略极简易懂,完全贴合 A 股 ETF T+1 交易规则,无日内频繁交易风险:

  1. 选股规则:每周首个交易日,遍历 ETF 候选池,计算所有标的近 20 个交易日涨跌幅,取涨幅前 2 名强势标的;
  2. 资金分配:第一名分配 70% 可用资金,第二名分配 30%,可自行修改权重;
  3. 卖出规则:每周最后一个交易日 14:55 统一清仓全部持仓,预留 5 分钟成交时间;
  4. 循环执行:次周重复选股、买入、清仓流程,持续跟踪市场强势板块 ETF;
  5. 特色适配:内置交易日判断函数,自动跳过周末、法定节假日,周一休市则顺延至节后首个交易日。

二、完整策略代码

# 1. 导入依赖库
import datetime
import pandas as pd
from datetime import date
from datetime import timedelta
import numpy as np

# 2. 策略初始化函数,程序启动仅执行一次
def initialize(context):
    # ETF候选池,自行填充沪市/深市ETF代码,示例:["510300.SS","159915.SZ"]
    g.security_list = []
    set_universe(g.security_list)
    
    # 全局缓存变量
    g.trade_days_list = get_trade_days('2015-01-01', '2025-10-31')  # 交易日期总表
    g.selected_stocks = []  # 每周筛选出的2只ETF
    g.weights = [0.7, 0.3]  # 资金分配比例
    g.hold_positions = context.portfolio.positions  # 当前持仓
    
    # 定时任务:每日固定时间执行对应函数
    run_daily(context, buy_function, time='9:31')    # 周初9:31执行买入
    run_daily(context, sell_function, time='14:55')  # 周末14:55执行清仓

# 3. 每日盘前执行:筛选强势ETF、标记本周首尾交易日
def before_trading_start(context, data):
    today = get_trading_day(0)
    # 判断是否本周第一个/最后一个交易日
    g.is_first = is_first_trading_day_of_week(today)
    g.is_last = is_last_trading_day_of_week(today)
    print('{}是否为本周首个交易日: {}'.format(today, g.is_first))
    print('{}是否为本周最后一个交易日: {}'.format(today, g.is_last))

    # 仅周初执行选股逻辑
    if g.is_first:
        returns = {}
        for stock in g.security_list:
            # 获取近20日收盘价
            history = get_history(20, '1d', 'close', stock, fq='pre')
            # 计算20日涨跌幅
            ret = (history.iloc[-1, 0] - history.iloc[0, 0]) / history.iloc[0, 0]
            returns[stock] = ret
        log.info("各ETF20日涨跌幅:", returns)
        # 按涨幅降序排序,取前2只
        sorted_returns = sorted(returns.items(), key=lambda x: x[1], reverse=True)
        g.selected_stocks = [item[0] for item in sorted_returns[:2]]
    else:
        pass

# 4. 买入函数:周初开盘后按比例下单
def buy_function(context):
    if g.is_first:
        total_available_cash = context.portfolio.cash
        for i in range(len(g.selected_stocks)):
            # 获取最新1分钟收盘价
            history_1 = get_history(1, '1m', 'close', g.selected_stocks[i], fq='pre')
            current_price = history_1.iloc[0]
            # 分配对应资金
            buy_cash = total_available_cash * g.weights[i]
            # A股ETF一手100份,计算可买整数手
            amount = int(buy_cash / current_price / 100) * 100
            if amount > 0:
                order(g.selected_stocks[i], amount)
    else:
        log.info('今日不是本周第一个交易日,不执行买入')

# 5. 卖出函数:周末收盘前全部清仓
def sell_function(context):
    if g.is_last:
        g.current_holdings = {}
        # 遍历全部持仓
        for sid, position in context.portfolio.positions.items():
            if position.amount > 0:
                g.current_holdings[sid] = position.amount
        sell_list = list(g.current_holdings.keys())
        # 清仓所有持仓
        for code in sell_list:
            if g.current_holdings[code] > 0:
                order_target(code, 0)
                log.info("卖出{},共{}份".format(code, g.current_holdings[code]))
    else:
        log.info('今日不是本周最后一个交易日,不执行卖出')

# 辅助函数1:判断当日是否为本周第一个交易日(自动适配节假日)
def is_first_trading_day_of_week(today):
    one_day = timedelta(days=1)
    yesterday = today - one_day
    # 向前寻找上一个有效交易日
    while yesterday.strftime('%Y-%m-%d') not in g.trade_days_list:
        yesterday -= one_day
    # 周数不同则代表今天是本周首个交易日
    return today.isocalendar()[1] > yesterday.isocalendar()[1] or today.year > yesterday.year

# 辅助函数2:判断当日是否为本周最后一个交易日(自动适配节假日)
def is_last_trading_day_of_week(today):
    one_day = timedelta(days=1)
    tomorrow = today + one_day
    # 向后寻找下一个有效交易日
    while tomorrow.strftime('%Y-%m-%d') not in g.trade_days_list:
        tomorrow += one_day
    # 周数不同则代表今天是本周最后一个交易日
    return today.isocalendar()[1] < tomorrow.isocalendar()[1] or today.year < tomorrow.year

三、代码逐模块详细解析

3.1 依赖库导入模块

import datetime
import pandas as pd
from datetime import date
from datetime import timedelta
import numpy as np
  • datetime/timedelta:处理日期、交易日偏移、节假日判断;
  • pandas:行情数据表格处理;
  • numpy:数值计算、涨跌幅运算; 所有 Ptrade 策略必备基础库,缺失会导致行情获取、日期计算报错。

3.2 initialize 初始化函数

程序启动仅运行一次,负责全局变量、候选池、定时任务配置:

  1. g.security_list:ETF 标的池,必须手动填充代码,沪市后缀.SS、深市后缀.SZ
  2. get_trade_days:拉取指定区间全部交易日,用于节假日判断;
  3. run_daily:定时触发器,固定时间自动执行买卖函数,无需人工盯盘;

3.3 before_trading_start 盘前选股函数

每个交易日开盘前自动执行,核心完成两件事:

  1. 调用is_first_trading_day_of_week标记周初交易日;
  2. 遍历候选池 ETF,拉取 20 日收盘价计算动量涨幅,降序排序选出前 2 只强势标的存入全局变量;

注意:此函数仅做数据计算,不会发起交易下单。

3.4 buy_function 买入下单函数

仅每周首个交易日 9:31 执行:

  1. 获取账户可用现金,按 7:3 权重分配资金;
  2. 读取实时 1 分钟收盘价,计算可买入份数;
  3. int(xxx/100)*100强制保证下单数量为 100 的整数倍,符合 A 股 ETF 交易规则;
  4. order(标的, 数量):Ptrade 原生限价下单接口。

3.5 sell_function 清仓卖出函数

仅每周最后一个交易日 14:55 执行:

  1. 遍历账户全部持仓,筛选持有数量大于 0 的 ETF;
  2. order_target(code, 0):一键清仓接口,目标持仓设为 0,无需手动计算卖出数量;
  3. 14:55 操作预留 5 分钟收盘缓冲,避免尾盘流动性不足无法成交。

3.6 交易日判断辅助函数

策略核心适配工具,解决节假日、长假休市问题:

  1. is_first_trading_day_of_week:向前回溯查找上一个交易日,对比周数,自动识别节后首个交易日;
  2. is_last_trading_day_of_week:向后查找下一个交易日,识别节前最后一个交易日; 无需手动调整日期,回测、实盘均可自动适配。

四、策略实操关键注意事项

4.1 ETF 候选池配置

g.security_list = []为空列表,必须填充标的,推荐宽基、行业 ETF 混合,建议 20~40 只,

格式区分沪深市场,代码后缀写错会导致行情拉取失败。

4.2 回测时间区间匹配

代码内get_trade_days('2015-01-01', '2025-10-31'),回测起止时间不能超出该区间,可自行修改结束年份拓展数据范围。

4.3 资金权重自定义

g.weights = [0.7, 0.3],风险偏好保守可改为[0.5,0.5]均衡分配,仅支持 2 个数值,对应选出的 2 只 ETF。

4.4 A 股交易规则适配

ETF 实行 T+1 交易,本策略每周买入、次周卖出,完全规避日内交易限制,不存在当日买入无法卖出的逻辑 bug。

4.5 回测优先,谨慎实盘

  1. 先用 Ptrade 仿真终端长期回测,观察不同牛熊周期收益、最大回撤;
  2. 可自行调参:20 日动量周期改为 10/30 日、增减持仓数量;
  3. 实盘前小资金试运行 1~2 个月,验证成交滑点、手续费对收益的影响。

五、策略优化拓展思路

  1. 增加止损逻辑:在每日盘后增加持仓跌幅判断,单只 ETF 回撤超 5% 强制卖出;
  2. 多周期动量过滤:叠加 60 日中长期趋势,仅在大盘上行时执行轮动;
  3. 仓位动态调整:根据市场波动率自动调整总仓位,高位降仓、低位加仓;
  4. 扩大持仓数量:修改代码选取前 3~4 只 ETF,分散单一标的风险;
  5. 过滤低流动性标的:增加日均成交额筛选,剔除成交量低迷的小盘 ETF。

六、总结

本套 Ptrade ETF 周度动量轮动策略代码完整、逻辑轻量化,内置节假日自动适配、合规交易数量处理、定时自动交易能力,新手可直接复用平台回测。 策略核心依靠动量效应捕捉中期强势板块 ETF,每周调仓频率适中,不会产生过高交易手续费,适合中长期自动化定投 + 趋势交易结合的投资者。

风险提示:本文仅为量化代码教学分享,不构成任何投资建议。量化策略存在历史回测不代表未来收益,市场波动、流动性风险、手续费滑点均会影响实际收益,请谨慎使用。