从零到实盘:一个PTrade双均线策略的完整AI开发与调优日记

1. 策略构思与需求拆解

那天早上喝咖啡时,我突然想到一个简单的交易想法:能不能用两条均线的交叉来捕捉趋势?这个念头看似简单,但真正落地需要解决一系列问题。首先得明确策略的具体参数——监控哪些股票?用多少天的均线?交易频率如何?

经过反复推敲,我确定了策略的核心要素:

  • 监控标的:沪深300成分股,确保流动性充足
  • 数据周期:日线级别收盘价
  • 均线参数:短期5日均线(MA5)和长期20日均线(MA20)
  • 交易信号:MA5上穿MA20买入,下穿卖出
  • 资金管理:每次交易使用可用资金的20%
# 策略基础参数示例
g.stock_pool = ['600519.SS', '000858.SZ']  # 示例股票池
g.ma_short = 5  # 短期均线周期
g.ma_long = 20  # 长期均线周期
g.position_ratio = 0.2  # 单次交易仓位比例

2. 代码生成与框架搭建

借助AI工具,我快速生成了策略的基础框架。PTrade的量化引擎采用事件驱动模式,需要特别注意各个事件的触发时机和适用场景。

2.1 初始化设置

在initialize函数中,我们需要完成以下关键配置:

  1. 设置交易手续费(佣金和印花税)
  2. 初始化全局变量
  3. 设置股票池和基准
def initialize(context):
    # 设置交易成本
    set_commission(commission_ratio=0.0003, min_commission=5)
    set_slippage(fixed=0.02)
    
    # 初始化全局变量
    g.stock_pool = get_index_stocks('000300.XSHG')  # 沪深300成分股
    g.ma_short = 5
    g.ma_long = 20
    g.position_ratio = 0.2
    
    # 设置基准
    set_benchmark('000300.XSHG')
    log.info("策略初始化完成")

2.2 盘中交易逻辑

handle_data函数是策略的核心,需要处理以下关键点:

  • 获取历史数据计算均线
  • 生成交易信号
  • 执行买卖操作
def handle_data(context, data):
    current_time = context.blotter.current_dt.time()
    # 仅在收盘前30分钟执行
    if not (current_time.hour == 14 and current_time.minute >= 30):
        return
    
    for stock in g.stock_pool:
        # 获取历史数据
        hist = get_history(g.ma_long+1, '1d', 'close', stock)
        if hist is None or len(hist) < g.ma_long+1:
            continue
            
        # 计算均线
        ma_short = hist['close'][-g.ma_short:].mean()
        ma_long = hist['close'][-g.ma_long:].mean()
        current_price = data[stock]['close']
        
        # 获取持仓信息
        position = get_position(stock)
        holding_amount = position.amount if position else 0
        
        # 生成交易信号
        if ma_short > ma_long and holding_amount == 0:
            # 买入信号
            cash = context.portfolio.cash * g.position_ratio
            order_value(stock, cash)
            log.info("买入 %s @%.2f MA5:%.2f MA20:%.2f" % 
                    (stock, current_price, ma_short, ma_long))
        elif ma_short < ma_long and holding_amount > 0:
            # 卖出信号
            order_target(stock, 0)
            log.info("卖出 %s @%.2f MA5:%.2f MA20:%.2f" % 
                    (stock, current_price, ma_short, ma_long))

3. 调试与问题修复

第一次回测就遇到了几个典型问题,记录下解决过程:

3.1 数据获取异常

问题现象:回测时报错"KeyError: 'close'" 原因分析:某些股票可能停牌或无数据 解决方案:增加数据有效性检查

# 修改后的数据获取逻辑
hist = get_history(g.ma_long+1, '1d', 'close', stock)
if hist is None or len(hist) < g.ma_long+1:
    continue
if 'close' not in hist.columns:
    continue

3.2 交易时间控制

问题现象:策略在非交易时段也执行 优化方案:精确控制交易执行时间

# 时间控制优化
current_time = context.blotter.current_dt.time()
if not (current_time.hour == 14 and current_time.minute >= 30):
    return

3.3 资金管理改进

初始版本存在全仓进出的风险,增加了仓位控制:

# 资金管理参数
g.position_ratio = 0.2  # 单次交易仓位比例
g.max_position = 0.8  # 最大总仓位

# 交易逻辑调整
available_cash = min(context.portfolio.cash, 
                    context.portfolio.total_value * g.max_position - 
                    context.portfolio.positions_value)
cash_to_use = available_cash * g.position_ratio

4. 参数优化与压力测试

策略基本运行稳定后,开始对关键参数进行优化测试。通过网格搜索法,评估不同参数组合的表现。

4.1 均线周期组合测试

测试了多种均线组合,部分结果如下:

短期均线 长期均线 年化收益 最大回撤 胜率
5 20 18.7% 22.3% 52%
8 21 16.2% 19.8% 54%
10 30 14.5% 18.2% 56%
13 26 17.1% 20.5% 53%

提示:参数优化时需注意避免过度拟合,建议保留部分样本外数据用于验证

4.2 不同市场环境测试

为了评估策略的适应性,特别测试了以下市场场景:

  1. 单边上涨市(2014-2015牛市)
  2. 震荡市(2016-2017年)
  3. 单边下跌市(2018年)
  4. 剧烈波动市(2020年疫情期间)
# 压力测试代码片段
def stress_test(context, data):
    # 模拟极端行情
    if context.current_dt.year == 2018:
        g.position_ratio = 0.1  # 熊市降低仓位
    elif context.current_dt.year == 2020 and context.current_dt.month in [2,3]:
        g.ma_short = 3  # 波动加大时缩短均线周期
        g.ma_long = 10

5. 模拟盘部署与实盘准备

经过充分回测后,开始将策略部署到PTrade模拟环境。这个过程有几个关键步骤:

5.1 模拟盘配置

  1. 设置初始资金(建议与实盘计划一致)
  2. 配置交易时段和频率
  3. 设置风控参数(单笔最大交易量、最大持仓等)

5.2 监控与日志分析

建立了完善的日志监控系统,重点关注:

  • 信号生成准确性
  • 实际成交与预期的偏差
  • 资金曲线变化
# 增强的日志记录
def after_trading_end(context, data):
    log.info("当日净值: %.2f 现金: %.2f 持仓价值: %.2f" % (
        context.portfolio.total_value,
        context.portfolio.cash,
        context.portfolio.positions_value
    ))
    for stock in context.portfolio.positions:
        pos = context.portfolio.positions[stock]
        log.info("持仓 %s: %d股 成本价: %.2f 当前价: %.2f 盈亏: %.2f%%" % (
            stock, pos.amount, pos.avg_cost, 
            data[stock]['close'],
            (data[stock]['close']/pos.avg_cost-1)*100
        ))

5.3 实盘过渡计划

制定了一个渐进式的实盘过渡方案:

  1. 第一阶段(1个月):模拟盘运行,每日复盘
  2. 第二阶段(1个月):小资金实盘(1-5万)
  3. 第三阶段:根据表现逐步放大资金规模

6. 策略持续优化方向

在策略运行过程中,发现了几个值得进一步优化的点:

  1. 动态参数调整:根据市场波动率自动调整均线周期
  2. 多时间框架确认:加入周线级别的趋势过滤
  3. 止损机制:设置移动止损保护盈利
  4. 仓位动态管理:根据信号强度调整仓位大小
# 动态参数调整示例
def adjust_parameters(context, data):
    # 计算近期波动率
    hist = get_history(30, '1d', 'close', '000300.XSHG')
    returns = hist['close'].pct_change()
    volatility = returns.std() * np.sqrt(252)  # 年化波动率
    
    # 根据波动率调整参数
    if volatility > 0.3:
        g.ma_short = 3
        g.ma_long = 10
    elif volatility < 0.15:
        g.ma_short = 10
        g.ma_long = 30
    else:
        g.ma_short = 5
        g.ma_long = 20

经过三个月的完整周期测试,这个双均线策略在模拟环境中表现稳定,年化收益达到15-20%,最大回撤控制在25%以内。最重要的是,通过这个项目我建立了一套完整的策略开发流程,从构思到实盘的每个环节都有了清晰的认识和实操经验。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐