把指标实现为类:以峰谷指标与 ATR 为例
一、为什么把指标做成类
断线或历史同步中断时,prev_calculated 会被清零,触发针对整个历史的重算。影响因素越多(大周期、复杂计算、多品种、机器差),重算代价越大。把指标封装成类、缓存中间结果,是缓解此问题的工程化手段。
二、prev_calculated 与重算陷阱
三、峰谷指标类
峰谷(ZigZag)指标维护一个极值点序列 ZZBar。类持有 m_prev_calculated 与缓冲,只对新柱计算,避免每 tick 全量重算。
class CCustZigZagPPC
{
int m_prev_calculated;
ZZBar m_bars[]; // 极值点序列
public:
bool Init(void);
int Calculate(const int rates_total, const int prev_calculated);
};
struct ZZBar { double price; int index; int type; };
四、ATR 类示例
ATR 类封装 iATR 句柄与缓冲,对外暴露 Calc(pos) 增量计算,便于在多个指标间复用。
class CCustATR
{
double m_buf[];
public:
bool Init(void) { /* 建句柄 */ return true; }
bool Calc(int pos)
{
m_buf[pos] = iATR(_Symbol, PERIOD_CURRENT, 14, pos);
return true;
}
};
五、在 OnCalculate 中调用
主指标把子指标组合进来,统一用 prev_calculated 控制增量区间,既清晰又可复用。
int OnCalculate(const int rates_total,
const int prev_calculated, ...)
{
if(!atr.Calc(rates_total-1)) return 0;
if(!zz.Calculate(rates_total, prev_calculated)) return 0;
return rates_total;
}
六、为什么把指标写成类
把指标逻辑封装成类(而非散函数或脚本),好处是状态内聚:缓冲、参数、中间变量都是成员,初始化一次、全程复用,调用方只拿到清晰的接口(如 Calculate(shift) 返回算好的值)。
类还能继承与组合:一个基类管生命周期,具体指标子类填计算,新增指标只需写子类。比起每次复制粘贴一套缓冲管理代码,类结构可维护性高一个量级。
七、类的生命周期
类的 OnInit 里创建缓冲、注册参数;OnCalculate 里被调用时算指定 shift 的值并写缓冲;OnDeinit 里释放。把「创建-计算-释放」收进类,调用方不必关心内部,指标之间也不互相污染状态。
class CMyIndicator
{
int m_handle;
public:
bool Init() { m_handle = iMA(...); return m_handle!=INVALID_HANDLE; }
double Calc(int shift) { return CopyBuffer(m_handle,0,shift,1,buf)>0 ? buf[0] : 0; }
};
八、复用与测试
类化后,同一个指标能在 EA、另一个指标、脚本里直接 new 出来用,不用重写。配合单体测试(喂已知数据断言输出),指标逻辑的错误能在最早被发现,而不是等到回测曲线诡异才查。
这也是专业代码和脚本的区别:把可复用的计算资产沉淀成类库,越写越省事、越稳。
九、常见指标类错误
两个高频错:一是缓冲索引错位,写错 buf 号导致画错线或读错值;二是 OnCalculate 里没正确处理 prev_calculated,重复计算或漏算新 bar。两者都会让指标时好时坏。
调试时 Comment 打印缓冲首尾值、用数据窗口核对实时数值,能快速定位是计算错还是绘制错。指标类写稳了,调用它的 EA 才可信。
十、指标类也是可测试资产
把指标类当「一等公民」测试:喂已知序列,断言输出符合公式;改了计算逻辑重跑测试。指标是 EA 的眼睛,眼睛坏了 EA 全程瞎做,所以指标的测试优先级不亚于信号逻辑。
沉淀指标类库后,新策略可以直接复用,不必每个 EA 重写一遍缓冲管理。越往后写,你不是在「从零造轮子」,而是在「组合已有资产」,速度和理解都复利增长。
把指标封装成类后,务必在析构函数里释放指标句柄与缓冲区,否则反复 new/delete 会造成句柄泄漏、跑久了终端变卡。多实例共用时给每个实例的缓冲起独立前缀,避免指标名冲突互相覆盖。类名与对外接口尽量稳定,下游策略引用后才不会被你一次重构全部打断。
写单元测试覆盖核心计算逻辑,比靠肉眼看曲线可靠得多。指标算错往往不抛异常,只在回测里悄悄输出错误信号,等实盘发现时已经亏了一截,所以类内部的关键运算必须可测可验。