免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

自定义指标实战:量化策略想法的完整表达与回测优化

自定义指标实战:量化策略想法的完整表达与回测优化 自己动手做量化也有好几年了回头看最深的感触不是哪套策略赚了多少而是“脑子里的想法能不能被工具完整表达”这件事几乎决定了一个策略的最终形态。水母量化这轮把自定义指标持续优化作为重点推进我理解它的核心目标就一句话让策略想法有更多表达方式。很多刚入门的朋友以为自定义指标就是把均线周期改一改、把MACD参数调一调真正开始做策略之后才会发现内置指标永远是“够用但不完全够用”的状态。当你想表达“连续缩量之后的放量突破”或者“价格创新高但成交量没有跟上”这种量价关系时内置指标根本没有对应的现成结构只能自己造。这篇文章我想把自定义指标这一整套东西完整拆开聊一聊包括功能设计思路、指标语法与计算机制、具体实操落地以及经常踩到的坑。不管你是刚开始写策略的新手还是已经在用自定义指标做因子筛选的老手这篇内容都能给你一些可以直接拿去用的经验。1. 思路拆解自定义指标到底在解决什么问题1.1 内置指标帮你完成80%剩下的20%决定策略质量内置指标指的是平台预置好的均线、MACD、KDJ、布林带、RSI这类经典技术指标。它们稳定、可靠、经过大量市场验证用来写最基础的趋势跟踪、震荡回归完全没有问题。但它们的表达方式是固定的MA就是MAKDJ就是KDJ你能调整的只有周期、算法、超买超卖阈值不能改它的语义。举个例子。你想表达“当价格在20日均线上方但成交量已经萎缩到5日均量0.7倍以下时输出一个潜在缩量回踩的观察信号”。这个逻辑涉及两条均线的位置关系、量能比较、条件组合、跨K线状态判断内置指标没有任何一个能直接给出答案。不是说写不出来而是你得把多个内置指标拼在一起然后用策略代码去算整个过程非常别扭而且可读性很差。我给这一阶段做了个类比内置指标就像一本成语词典你能用的都是现成的固定搭配组合起来能表达的意思有限自定义指标则像是给你一支笔和一套语法规则允许你自己造句、自己定义新词。做策略做到一定深度之后几乎所有真正差异化的地方都藏在那20%的自定义表达里。1.2 从量价关系走向多品种合成指标表达能力的边界在哪自定义指标能覆盖的表达范围其实比大多数人想象中宽。我按应用场景大致列一下单标的量价类量价背离、动量加速度、缩量回踩、通道突破、自定义均线簇。横截面比较类多品种相对强弱、轮动得分、资金流向比较这类逻辑可以同时访问多个品种的行情数据完全靠内置指标做不出来。跨周期组合类日线级别的趋势方向作为大前提配合30分钟级别的入场信号两者合成一个复合指标。事件驱动类比如“涨停后第N天缩量回调到关键均线附近”这类逻辑天然依赖时间序列和事件标记。水母量化在做自定义指标优化的时候没有把这些能力散乱地堆在一起而是拆成了几个层次去打磨。我在实际使用中也觉得这套拆法非常清晰语法层解决“想得到能不能写得出”计算层解决“算得准不准、快不快”输出层解决“算出来的东西怎么被策略用起来”效率层解决“多指标并行、实盘运行时的性能问题”。四层都稳了策略想法才能相对完整地被保留下来而不是写着写着就变形了。1.3 一个数字化指标需要的四层框架这里我把这个框架说得具体一点方便后面理解。语法层是最直观的。好的指标语言必须能自然表达“过去20天内的最高价、最低价、累计成交量、平均持仓成本”这类逻辑。如果你的指标语言里连“序列”这个概念都没有每次只能拿一个孤立数值算来算去那你的想法再牛也很难落地。计算层决定效率。同一段指标逻辑逐根K线循环和预计算向量化之间的速度差距可能达到几十倍。一个设计良好的平台会在回测开始前把指标序列一次性算完然后让回测引擎直接读数组而不是每次迭代都重新算一遍。输出层决定可用性。指标算出来的东西可以是一条连续曲线、一个布尔信号、一组阀值也可以被后续策略直接引用。输出方式越多信号的复用性越强。效率层容易被忽略但实战里特别重要。当你同时运行五六个自定义指标每一个都在实时行情里不断刷新时如果没有合理的缓存和增量计算机制系统很容易变得卡顿。2. 核心机制解析指标语法、计算模型与信号输出2.1 指标语法怎么设计才能“想得到就写得出”一个真正好用的指标语法在我看来有三个基本特征。第一序列是天然数据类型。什么叫序列就是这一根K线上的值、上一根K线上的值、再往前的值连起来就是一条时间序列。均线、最高价、成交量本质上都是序列。如果你的语法里只能写“当前收盘价”不能写“5分钟前的收盘价”那你根本没法表达任何稍微复杂一点的时间关系。第二内置函数要覆盖常规统计和常用序列操作。MA是移动平均HHV是过去N周期最高值LLV是过去N周期最低值REF是引用N根K线之前的值CROSS是判断两条序列是否上穿或下穿。这些函数是造句子用的基础单词越丰富越好。第三变量和表达式可以自由组合。中间计算结果可以命名保存然后被后面的条件引用。写指标本质上和写作文一样先做定义再做推理最后输出结论。拿一个实际例子来说下面是一段相对完整的指标代码用来预警“价格突破近期高点但成交量明显不足”的潜在背离INDICATOR 量价背离预警 INPUT N 20; // 观察窗口 INPUT RATIO 0.8; // 量能萎缩阈值 // 计算基础序列 MA5 : MA(CLOSE, 5); MA20 : MA(CLOSE, 20); VOL_MA : MA(VOLUME, 20); HH : HHV(HIGH, N); VOL_HH : HHV(VOLUME, N); // 条件价格突破近N日高点但量能明显小于前期峰量 IS_BREAK : CLOSE REF(HH, 1); IS_SHRINK : VOLUME REF(VOL_HH, 1) * RATIO; SIGNAL : IS_BREAK AND IS_SHRINK; // 输出 PLOT(SIGNAL, 缩量突破预警); PLOT(MA20, MA20);注意几个细节。HHV(HIGH, N)算出来是一个序列每一根K线上都有一个对应的值代表该K线往前数N根K线里的最高价。REF(HH, 1)取的是上一根K线的HH值这样写是为了避免当前K线自己也被算进“突破前高”的比较里如果直接写CLOSE HH那就永远不会成立因为HH是从当前K线开始算的。这不是什么高深技巧但新手经常在这里栽跟头。2.2 逐K线循环与预计算向量化两种计算模型的取舍指标计算引擎的主流实现方式有两种搞清楚它们对写策略很有帮助。第一种是逐K线驱动。每来一根新K线就执行一遍完整公式用户写的每行代码都在当前K线上求一个值。好处是逻辑和人类思考习惯一致调试的时候你很清楚每一个变量在某一根K线上是什么值。坏处是性能瓶颈明显尤其是当你在循环里反复使用HHV、REF这类跨K线函数时如果不做缓存、每次都重新扫描一整段历史速度会变得非常慢。第二种是预计算向量化。回测开始之前先把指标序列一次性全部算完回测引擎读取信号时直接从数组索引取值复杂度是O(1)。好处是快坏处是灵活性有限如果指标逻辑依赖“当时的持仓状态”或者“动态变化的窗口长度”纯向量化就写不出来必须放到策略层去做逐K线循环。水母量化在自定义指标上采取的是指标层预计算加策略层逐K线两条路结合的方式这也是现在主流量化平台比较成熟的设计。指标负责把行情数据转化成信号特征策略负责基于这些特征做决策。两个层次分开之后最直接的好处是回测速度明显提升因为相同的指标不需要在每个策略、每次调参时都重新算一遍。我自己以前也犯过一个很低级的错误把指标逻辑直接写在策略的主循环里每根K线都去重算MA20和HHV结果一次回测要跑十分钟。把指标逻辑抽出来预计算之后同样的回测只需要十几秒。这个差距就是预计算的价值。2.3 信号输出不只是画一条线要被策略真正“用起来”指标算完之后结果要能被不同方式使用这个能力决定了指标的复用价值。我自己接触过的输出形态大概分为四类。第一类是指标值曲线最传统的一种。MA、MACD这类数值型序列直接画在主图或副图上供人眼观察。第二类是布尔信号输出true或false。比如上面例子里的SIGNAL这个值可以直接作为策略的开仓过滤条件也可以画在图上变成一根根标记线。第三类是多指标合成把多个自定义指标的结果当作策略输入特征类似构造一个小的因子库再在策略层做打分或加权。第四类是警报输出在实时行情里当指标条件成立时触发通知这个对盘中盯盘或者辅助人工决策很有用。输出层的核心设计原则是“指标与策略分离”。很多平台会把指标函数和策略逻辑耦合在同一个代码段里导致你想复用一个指标就得把大段公式复制到新策略里。水母量化的指标结果拼接进策略时是引用式的策略里只写“当某个指标的SIGNAL变为true时买入”逻辑非常清晰。2.4 运行效率的三个优化手段写指标前就该知道性能问题不是只属于大型量化团队个人写策略也会碰得到。这里分享三个实际经验。第一是相同指标参数在一个回测里只算一次。实战中我经常会在同一个回测里尝试多个策略条件如果每个策略都把自己的指标从头算一遍浪费很多时间。现在我已经养成了习惯指标层先算好策略层直接取结果。用参数签名做缓存也是个办法同一个自定义指标固定参数下只算一次后面多策略复用就不会重复计算。第二是实时行情里的增量计算。实时场景中旧K线已经不再变化只有最后一根K线还在跳动。聪明的引擎会只更新最后一根K线上的指标值而不是把整条历史序列全部重算。写指标的人不太能控制引擎层面的算法但如果你发现自己的指标在实盘刷新时很卡优先检查是不是指标内部用了过大的窗口或者跨周期引用太频繁了。第三是内存复用。一个回测跑上万个品种、几百根K线时指标中间变量会占用大量内存。好的平台会主动释放不再使用的中间序列只保留最终输出。这个用户侧控制不了但理解这一点能帮你意识到指标写得再简洁如果平台底层不做内存管理照样会崩。3. 实操复现把一个模糊想法变成可回测的自定义指标3.1 从“感觉”到规则第一步不是写代码而是写伪代码很多人的策略想法一开始都是模糊的比如“股票连续上涨几天之后回调一下如果回调时量很小可能说明抛压不大后面还有机会”。这种描述人听得懂代码听不懂。所以第一步永远是把它翻译成结构化规则我习惯先写伪代码。我举一个实际案例。假设我想表达的策略逻辑是“上涨趋势中的缩量回调是潜在买点”那伪代码可以这样写上涨趋势定义收盘价位于短周期均线之上比如CLOSE大于MA5同时MA5大于MA10。回调定义价格从近期阶段高点回落但跌幅不超过5%不能跌太深跌太深说明趋势可能已经坏了。缩量定义当前成交量小于过去N根K线平均成交量的0.7倍说明抛压很轻。把这三条规则同时满足作为信号输出为true。这样写完之后转化成指标代码就非常顺了。3.2 完整指标代码与参数配置伪代码翻译成水母量化的指标语言大概是下面这个样子INDICATOR 回调缩量机会 INPUT PERIOD_MA 5; INPUT SHRINK_RATIO 0.7; INPUT RETRACE_MAX 0.05; // 趋势方向判断 UP_TREND : CLOSE MA(CLOSE, PERIOD_MA) AND MA(CLOSE, PERIOD_MA) MA(CLOSE, PERIOD_MA*2); // 回调幅度判断 HH : HHV(HIGH, 10); PULLBACK : (HH - CLOSE) / HH RETRACE_MAX; // 量能萎缩判断 VOL_SHRINK : VOLUME MA(VOLUME, PERIOD_MA) * SHRINK_RATIO; // 综合信号 SIGNAL : UP_TREND AND PULLBACK AND VOL_SHRINK; PLOT(SIGNAL, 回调缩量机会);参数含义给一个新读者解释一下。PERIOD_MA控制趋势判断的敏感度设成5意味着这个信号偏向短线设成10的话趋势确认更慢、信号更少。SHRINK_RATIO是量能萎缩比例0.7代表成交量要小于均量的70%数值越接近0条件越严格。RETRACE_MAX是允许的回调幅度0.05代表价格距离最近10根K线的最高点回落不超过5%如果超过5%就认为不是正常回调可能是趋势反转。3.3 回测配置与参数敏感性检查指标写完之后把它接入策略做回测。这里给一个简单的策略框架满足SIGNAL信号时以收盘价买入持有最多5根K线或者价格跌破MA20时提前出场。手续费按万二计算滑点设一个点。我跑了一组不同参数组合的结果差别其实很明显参数组合 (PERIOD_MA / SHRINK_RATIO / RETRACE_MAX)信号次数平均持有期胜率盈亏比最大回撤5 / 0.7 / 0.0583.2天56.25%1.318.4%5 / 0.6 / 0.0552.8天60.00%1.186.9%10 / 0.7 / 0.08124.1天50.00%1.4211.2%10 / 0.6 / 0.08144.6天42.86%1.0513.6%这组结果有一个很值得注意的点参数越严格信号越少胜率看起来越高但总盈利不一定最好。SHRINK_RATIO从0.7调到0.6信号次数直接少了快一半胜率上去了但盈亏比下降说明剩下的信号虽然质量高但盈利空间也变小了。这个现象说明单一指标信号的稳定性是有限的最终能不能赚钱还取决于出场逻辑和仓位管理。3.4 从回测到实时行情接入时最容易忽略的两个问题回测没问题不代表实盘没问题。第一个很常见的问题是实时行情中最后一根K线还没有收盘CLOSE和VOLUME都是动态变化的所以同一个指标信号可能在盘中反复出现又消失回测里则完全不会出现这种抖动。处理方式一般有两种一是强制等到K线收盘后信号才生效二是把盘中触发和收盘确认分成两级。第二个问题是信号重复触发。SIGNAL为true的时候如果连续几根K线都满足条件策略层不做处理的话就会在同一段行情里反复开仓。解决方式是在策略层写一个状态判断只取SIGNAL从false变为true的那根K线触发动作也就是做一次边沿检测。4. 常见问题与排查技巧实录4.1 未来函数指标里最隐蔽也最致命的错误我一直认为未来函数是自定义指标第一杀手因为它不会让你报错只能让你的回测结果异常漂亮实盘却一塌糊涂。所谓未来函数就是计算第N根K线的信号时偷偷用到了第N1根甚至更后面的数据。最容易犯的几个场景我列一下REF方向写反。REF(CLOSE, 1)是取上一根K线的收盘价REF(CLOSE, -1)这类写法本质上是取下一根K线的值平台不一定报错但结果完全不对。跨周期引用没有做时间对齐。比如你在30分钟图上引用日线级别指标如果你直接用了“当天最终收盘价”而当前日线尚未收盘那一根K线上你已经偷看了未来。动态窗口越界。某些平台在K线不足时会把窗口自动扩展到包含未来数据这个问题在回测初期和末尾容易出现最好手动检查第一根信号出现在哪个位置。排查未来函数的一个可靠技巧是把回测区间切一半对比前半段和后半段的信号分布、胜率差异。如果后半段明显好于前半段有可能是数据泄漏或者未来函数导致的因为你的信号在偷偷利用“之后才知道的信息”。4.2 指标噪声和过拟合信号太多没有价值信号太少更危险自定义指标很容易陷入两个极端。阈值设得太紧信号极少回测曲线看起来漂亮得不行但实盘机会根本不够策略空转阈值设得太松信号满天飞胜率跟抛硬币差不多扣掉手续费就是亏损。关键的一个检查方法叫“参数高原”验证。你把某个参数比如SHRINK_RATIO从0.5一路调到0.9画出指标表现曲线。如果0.6到0.75这一段表现都还不错说明逻辑本身是稳健的参数选在这里面哪个值都不太影响大局这就是参数高原。如果只有某一个点特别好比如0.68胜率60%但0.67和0.69都很差那大概率是过拟合你的策略只是在参数搜索里找到了一个随机噪声点实盘基本不会复现。4.3 性能排查加了自定义指标之后回测速度骤降怎么办这个问题几乎所有写自定义指标的人都遇到过。原因多数是同一个指标里的HHV、MA这类跨K线统计函数在逐K线循环中被反复全量计算。比如你有3000根K线HHV(HIGH, 20)在循环里被调用了3000次每次都要扫描最近的20根K线总计算量就是60000个操作。听起来不多但如果你的指标里同时有三个HHV、四个MA再叠加参数优化扫描几千次计算量就会被放大到难以接受。解决办法是前面提到的把指标计算从策略循环里拿出来做预计算。这样HHV就只算一遍策略循环直接读取结果性能差距几十倍。如果必须在策略内部做动态窗口计算尽量把窗口控制在几十根以内。不要在一个几千根K线的回测里做动态O(n²)的窗口扫描那是性能灾难。4.4 指标序列引用错误导致信号整体平移一根K线还有一个很隐蔽的问题画线看起来一切正常但仔细对信号和行情发现信号总是比肉眼看到的形态晚一根K线出现。这通常是因为在判断“突破前高”的时候没有用上一根K线的HH值导致信号产生所依赖的数据包含了当前K线本身的极值计算出来的结果天然就会偏保守或偏延迟。解决方式就是检查REF的使用位置尤其是HHV、LLV这类极值函数是否正确地引用到了前一根K线。5. 后续还能继续优化的方向5.1 从“能用”到“好用”平台与个人都可以持续打磨的细节水母量化这轮把自定义指标持续优化作为重点我觉得方向上还有一些可以做得更深的内容。比如让用户能可视化检查指标中间变量而不仅仅是最终输出调试复杂指标时可以同时看到MA5、MA20、HH和最终信号的叠加排查逻辑出错的效率会高很多。函数库方面也可以继续扩充分位数、协方差、WMA、自适应均线这类偏统计和智能方向的函数能让指标表达更复杂的市场状态。再往下走如果能提供一个“指标信号质量面板”自动统计一个指标的信号频率、连续信号次数、胜率分布就能帮使用者快速判断指标是否值得深入调参。5.2 建立个人策略想法库而不是零散地记公式最后分享一个我个人收益非常高的习惯每写一个自定义指标都给它建一个指标卡片记录五个要素——想法来源为什么会有这个思路、适用市场股票、期货还是数字货币、参数含义每个参数控制什么、历史表现回测表现和实盘感受、失效信号什么时候发现这个指标开始失效了。时间长了这堆指标卡片就成了我自己的策略字典。有了这本字典你后续的优化思路会清晰很多因为你不再零散地记住一两个公式而是系统地管理了“表达策略想法”的词汇表。我自己的体会是自定义指标的真正价值不在于它能帮你找到一个万能信号而在于它能把你对市场的理解固化成可检验的代码。哪怕一个指标最终证明不好用这个“不好用”本身也是有效的结论因为它是被回测验证过的。持续优化的过程本质上是不断把自己脑子里那些模糊的盘感翻译成一个一个可以被独立检验、继续迭代的小模型。只要这个翻译链路足够顺畅你的策略想法就会越积越多最终形成属于你自己的信号体系。
返回列表