🚀 AI 一键生成 ptrade 策略代码
立即体验

ptrade 平台如何自定义编写 EXPMA 与 MACD 指标计算公式?(附 Python 源码)

ptrade | 发布时间: 2026/7/22
以下内容由 EasyQuant 生成。

问题描述

请根据以下 SEO 主题生成完整页面内容:

Title: ptrade 指标进阶:如何手动计算精准的 EXPMA 与 MACD 指标?

Question: 怎么在ptrade里自定义编写MACD和EXPMA指标的平滑系数计算公式?

解决方案

在 ptrade 量化交易平台中,虽然系统提供了内置的技术指标计算函数(如 get_MACD),但在实际策略开发中,内置函数往往由于历史数据长度不足或平滑基期选择不同,导致计算出的指标数值与券商软件终端存在微小偏差。为了追求极致的信号精准度,量化交易员通常会选择手动编写 EXPMA 与 MACD 的平滑系数计算公式

本文将为您详细解析如何在 ptrade 中通过 Python 自定义实现这两个核心指标。


一、 EXPMA(指数平滑移动平均线)计算原理

EXPMA 的核心在于平滑系数 $\alpha$ 的计算。其公式为:
$$\alpha = \frac{m}{N + 1}$$
其中:

  • $N$ 为时间周期(如 12 日、26 日)。
  • $m$ 为平滑因子,通常默认取值为 2.0

当日的 EXPMA 值计算公式为:
$$\text{EXPMA}{\text{today}} = \alpha \times \text{Price}{\text{today}} + (1 - \alpha) \times \text{EXPMA}_{\text{yesterday}}$$


二、 MACD 指标计算步骤

MACD 是基于两条不同周期的 EXPMA(EMA)线计算而来的:

  1. 计算 12 日 EMA 与 26 日 EMA:作为快速与慢速平滑值。
  2. 计算差离值 DIF
    $$\text{DIF} = \text{EMA}{(12)} - \text{EMA}{(26)}$$
  3. 计算 DEA(DIF 的 9 日 EMA)
    $$\text{DEA}{\text{today}} = \beta \times \text{DIF}{\text{today}} + (1 - \beta) \times \text{DEA}_{\text{yesterday}}$$
    (其中 $\beta = \frac{2}{9 + 1} = 0.2$)
  4. 计算 MACD 柱状图 (BAR)
    $$\text{MACD BAR} = 2 \times (\text{DIF} - \text{DEA})$$

三、 ptrade 自定义 Python 源码实现

以下是一个完整的 ptrade 策略示例,展示了如何手动编写算法函数,并在 handle_data 中进行实时计算与交易信号判断:

# -*- coding: utf-8 -*-

# 1. 指数平滑均线基础函数
def f_expma(N, m, EXPMA_prev, price):
    """
    N: 时间周期
    m: 平滑因子系数,默认2.0
    EXPMA_prev: 前一日的EXPMA值
    price: 当日收盘价
    """
    alpha = m / (N + 1)
    EXPMA_curr = alpha * price + (1 - alpha) * EXPMA_prev
    return EXPMA_curr

# 2. 自定义MACD计算函数
def calculate_custom_macd(N1, N2, N3, m, EMA12_prev, EMA26_prev, DEA_prev, current_price):
    """
    手动递推计算当日的 EMA12, EMA26, DIF, DEA 和 MACD BAR
    """
    # 计算今日的快速与慢速EMA
    EMA12_curr = f_expma(N1, m, EMA12_prev, current_price)
    EMA26_curr = f_expma(N2, m, EMA26_prev, current_price)
    
    # 计算DIF
    DIF_curr = EMA12_curr - EMA26_curr
    
    # 计算DEA (DIF的N3日平滑)
    beta = m / (N3 + 1)
    DEA_curr = beta * DIF_curr + (1 - beta) * DEA_prev
    
    # 计算MACD柱状值
    BAR_curr = 2 * (DIF_curr - DEA_curr)
    
    return EMA12_curr, EMA26_curr, DIF_curr, DEA_curr, BAR_curr

def initialize(context):
    # 初始化股票池
    g.security = '600570.SS'  # 恒生电子
    set_universe(g.security)
    
    # 初始化指标全局变量
    g.is_initialized = False
    g.EMA12 = None
    g.EMA26 = None
    g.DIF = None
    g.DEA = None

def handle_data(context, data):
    security = g.security
    
    # 过滤停牌
    if data[security].is_open <= 0:
        return
        
    # 获取历史收盘价(获取最近2天数据用于递推)
    close_data = get_history(2, '1d', field='close', security_list=security)
    if close_data is None or len(close_data) < 2:
        return
        
    prev_close = close_data[security]['close'][-2]
    curr_close = close_data[security]['close'][-1]
    
    # 首次运行初始化基期价格
    if not g.is_initialized:
        g.EMA12 = prev_close
        g.EMA26 = prev_close
        g.DIF = 0.0
        g.DEA = 0.0
        g.is_initialized = True
        
    # 设定标准MACD参数 (12, 26, 9)
    N1, N2, N3 = 12, 26, 9
    m = 2.0
    
    # 保存昨日值用于金叉死叉对比
    DIF_prev = g.DIF
    DEA_prev = g.DEA
    
    # 计算今日最新指标值
    g.EMA12, g.EMA26, g.DIF, g.DEA, bar = calculate_custom_macd(
        N1, N2, N3, m, g.EMA12, g.EMA26, g.DEA, curr_close
    )
    
    # 获取当前账户现金与持仓
    cash = context.portfolio.cash
    position = get_position(security).amount
    
    # 策略逻辑:DIF 向上突破 DEA(金叉),且两者均大于0 -> 买入
    if g.DIF > 0 and g.DEA > 0 and DIF_prev < DEA_prev and g.DIF > g.DEA:
        if cash > curr_close * 100:
            order(security, 100)
            log.info(f"【买入信号】DIF金叉DEA,买入 {security} 100股")
            
    # 策略逻辑:DIF 向下突破 DEA(死叉) -> 卖出平仓
    elif DIF_prev > DEA_prev and g.DIF < g.DEA and position > 0:
        order_target(security, 0)
        log.info(f"【卖出信号】DIF死叉DEA,清仓 {security}")

四、 进阶避坑指南

  1. 基期初始化问题:EMA 是一个无限级数递推指标。在策略启动的第一天,由于没有历史 EMA 累积值,通常将启动前一日的收盘价(prev_close)直接作为 EMA 的初始值。为了使计算结果与真实行情软件完全一致,建议在回测或实盘初始化时,先拉取至少 100 天以上 的历史数据进行预热计算。
  2. 数据精度处理:ptrade 平台中股票委托价格精度为两位小数,而计算出的 EMA 与 MACD 往往是高精度浮点数。在根据指标计算限价单价格时,务必使用 round(price, 2) 进行截断,避免因精度问题导致柜台废单。