十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

通达信缠论笔线段自动画线:基于DLL插件的完整实现方案

通达信缠论笔线段自动画线:基于DLL插件的完整实现方案 简介这是一份通达信缠论笔线段画线公式源码的主图文档专为使用通达信软件进行缠论技术分析的投资者和指标开发者准备。文档详细讲解了基于分型与笔划分的画线公式实现方法包含DINGFEN、DIFEN、ZHUANGZHE等核心变量和函数定义并明确说明了公式使用向后引用未来函数的原因及边界——仅用于走势标注与中枢观察不可直接作为选股依据。压缩包仅含1个doc文件大小215KB文件结构精简、便于直接查阅和复制源码。目前已有1841人学习/下载适合希望通过公式源码直观理解缠论笔、线段及分型判定的初中级使用者。阅读这份文档读者可以快速掌握如何用近似方式识别笔的上下分型顶点理解关键变量对走势状态的判定逻辑并在此基础上结合其他指标完善自己的分析系统。 缠论笔线段自动画线这个需求在通达信用户里一直很旺盛。但市面上流传的“公式源码”绝大多数只能画个大概要么是未来函数重灾区要么是画出来的笔和实际走势对不上。我自己折腾这套主图画线公式有一年多从纯公式语言到DLL插件都试过踩了不少坑这里把我最终落地的方案和完整思路写出来给想自己动手的人一个参考。先说清楚这套东西能干什么它能在通达信主图上自动识别K线的包含关系、划分顶底分型、确认笔的成立再基于笔递归出线段最后把笔、线段、中枢直接画在价格图上红笔绿笔、中枢矩形框一目了然。适合做缠论复盘研究、买卖点标注参考也适合想搞清楚缠论算法到底怎么用代码表达的人。1. 缠论笔线段的工程化难题从读图到画图1.1 为什么靠肉眼画线不靠谱缠论里最基础的操作就是分型、笔、线段。刚开始学的时候拿笔在图上画画完一张图觉得“哦原来走势是这样的”。但画上几十张图之后就会发现问题不同的人对同一段走势可以画出完全不同的笔差一个分型后面线段划分全变。这不是学得不好而是纯视觉判断本身没有可复现的判定标准人在看图的时候会不自觉地“找自己想要的结构”。如果要把缠论用在更系统的复盘里第一步就是把“看图找结构”变成“程序算结构”。也就是说给定一段K线数据程序输出的笔、线段、中枢必须是确定的——同样的输入永远得到同样的输出。这也是我决定自己写画线工具的核心动机让判定标准统一让复盘结果可复现。1.2 缠论自动画线到底要解决哪几个问题把需求拆开看一套可用的自动画线系统要依次解决四个问题K线包含关系的合并。相邻两根K线有包含关系时必须先按方向合并成一根否则后面的分型判断全是错的。顶底分型的识别。合并之后的K线序列里找出所有顶分型和底分型但分型之间不能共用K线。笔的确认。相邻的顶底分型之间满足最低K线数要求且中间没有更极端的价格笔才成立。线段与中枢。用笔作为基本组件按特征序列划分线段再在重叠区间里圈出中枢。这四个问题一环扣一环。包含关系合并不对分型就错分型错笔就错笔错线段和中枢就是空中楼阁。所以写代码的时候必须逐层验证不能跳步。2. 公式语言和DLL的边界选型决定成败2.1 通达信公式语言做不到什么通达信自带的公式语言设计目标是技术指标不是面向对象编程。它能做均线、MACD、KDJ这类基于单根K线的计算也能用HHV、LLV这类函数做滚动窗口统计但缠论笔线段这种需要“回溯、递归、状态记录”的逻辑公式语言写起来非常别扭。具体的问题有三个没有真正的循环和数组。公式语言里虽然有些函数能模拟迭代但模式是固定的很难实现“动态向前找分型”这种需要按需回溯的逻辑。未来函数陷阱。很多人为了在图上画出连续的笔直接在公式里用了ZIG之类的函数这类函数会重绘历史信号你看到的线是包含了未来信息的。复盘时觉得“真准”实盘一看完全不是那么回事。状态管理能力极弱。缠论的笔需要维持“当前是否在向上笔/向下笔”的状态公式语言的函数式思路做这件事很别扭。所以想在通达信里做一套真正可靠的缠论画线纯公式方案基本可以放弃。当然如果你只是想画个视觉参考那用现成的ZIG函数改改也能看但不是本文要讲的正路。2.2 DLL方案的技术栈与整体架构我的最终方案是用通达信的DLL插件机制把缠论计算全部放在C代码里完成公式语言只负责把K线数据传给DLL、把DLL返回的画线数据画出来。整体架构是这样的K线数据来源通达信主图公式通过REF(H, N)、REF(L, N)等函数把当前股票最近500根K线的高低价、开收盘价打包成一维数组传给DLL。DLL计算引擎C代码拿到K线数组后执行包含关系合并、分型识别、笔确认、线段划分、中枢计算最终输出笔的每个端点的价格和序号以及中枢的上下沿区间。主图画线DLL返回结果放在数组里通达信这边用DRAWLINE、STICKLINE、DRAWKLINE之类的绘图函数把笔段和中枢画到主图上。这里最核心的设计思路是DLL负责算公式语言负责画。DLL返回的是纯数据比如“第12根K线是笔的起点价格是3.26第35根K线是笔的终点价格是3.18方向是向下”画线完全交给通达信原生绘图能力这样既保持了计算精度又最大限度利用通达信的图表渲染。2.3 为什么选DLL而不是外部程序可能有人会问为什么不直接在Python里算好再把结果导入通达信两种路子我都试过。外部程序方案的问题在于K线数据同步。Python要拿到和通达信完全一致的行情数据需要另接数据源而且除权数据、复权方式要仔细对齐否则同一只股票在两边看到的图形都对不上。另外“算好再导入”就意味着你得不断生成数据文件、刷新图表整个流程是割裂的。DLL方案的优点在于它跑在通达信内部天然拿到的是通达信当前显示的这幅图的K线数据。K线准计算就准显示什么算的就是什么。复盘的时候切换股票、切换周期DLL自动重新计算体验是实时无缝的。当然DLL方案的入门门槛也更高得会C、得了解通达信DLL接口规范还要会调通公式和DLL的对接。这部分我后面会细说。3. 笔的判定包含关系、分型与顶底确认3.1 K线包含关系合并的算法实现这是整个算法的地基。包含关系的定义很明确一根K线的高低范围完全被另一根K线的高低范围覆盖就说明两根K线存在包含关系。处理包含关系时有两个关键点一是根据“当前方向”决定合并方式二是方向本身来自最近一个非包含关系的方向。具体代码逻辑是这样写的// K线包含处理 void MergeContainBars(vectorKLine klines) { vectorKLine merged; bool direction_up true; // 当前方向默认向上 for (auto k : klines) { if (merged.empty()) { merged.push_back(k); continue; } KLine prev merged.back(); // 判断是否包含 bool contains (k.high prev.high k.low prev.low) || (prev.high k.high prev.low k.low); if (contains) { // 方向向上时合并取高高、低高 // 方向向下时合并取低低、高低 if (direction_up) { prev.high max(prev.high, k.high); prev.low max(prev.low, k.low); } else { prev.high min(prev.high, k.high); prev.low min(prev.low, k.low); } // 合并后不改变方向 } else { // 非包含关系更新方向 direction_up k.high prev.high; merged.push_back(k); } } // merged 就是合并后的K线序列 }这里有一个容易写错的细节合并之后方向不是重新判断而是保持不变。只有遇到非包含K线时才会根据新高/新低来更新方向。原因在于缠论里方向是“延续”的不是每根K线独立判断的。3.2 顶底分型的严格定义合并完K线之后分型判断就简单了。顶分型的定义是中间K线的最高价是三者中最高的且中间K线的最低价也是三者中最高的。底分型则反过来。严格实现时还要处理一个边界问题分型之间的K线不能共用。也就是说第i根K线可以是顶分型的中间K线那它就不能再作为底分型的组成部分。这层约束如果漏掉会出现“一根K线又是顶又是底”的荒谬情况。// 判断分型 struct Fenxing { int index; // 在原始K线上的索引 double value; // 分型对应的价格 bool is_top; // true顶分型false底分型 }; vectorFenxing FindFenxing(const vectorFenxing merged) { vectorFenxing result; for (int i 1; i merged.size() - 1; i) { bool is_top merged[i].high merged[i-1].high merged[i].high merged[i1].high; bool is_bottom merged[i].low merged[i-1].low merged[i].low merged[i1].low; // 分型有效性检查相邻分型不能共用K线 if (!result.empty()) { if (abs(i - result.back().index) 2) { continue; } } if (is_top) { result.push_back({i, merged[i].high, true}); } else if (is_bottom) { result.push_back({i, merged[i].low, false}); } } return result; }3.3 笔的成立条件与严格代码有了分型序列笔的判定核心就是两个条件相邻分型必须一顶一底交替出现且两者之间的K线数足够。K线数要求我通常设为5根以上这个是网上大多数缠论实现里默认的参数处理的是“分型之间隔得太近不具备笔的意义”的问题。这个参数建议开放出来后面画线的时候可以在通达信里直接调。// 笔的确认 struct Bi { int start_index; // 起点K线索引 int end_index; // 终点K线索引 double start_price; // 起点价格 double end_price; // 终点价格 bool direction_up; // true向上笔 }; vectorBi BuildBi(const vectorFenxing fenxings, int min_bars) { vectorBi bis; if (fenxings.size() 2) return bis; const Fenxing* prev fenxings[0]; for (int i 1; i fenxings.size(); i) { const Fenxing curr fenxings[i]; // 必须顶底交替 if (curr.is_top prev-is_top) { // 同类型分型按更极端的价格替换 if (curr.is_top) { if (curr.value prev-value) prev curr; } else { if (curr.value prev-value) prev curr; } continue; } // 检查K线根数是否足够 int bar_gap curr.index - prev-index; if (bar_gap min_bars) { Bi bi; bi.start_index prev-index; bi.end_index curr.index; bi.start_price prev-value; bi.end_price curr.value; bi.direction_up curr.is_top; // 底-顶为向上笔 bis.push_back(bi); prev curr; } } return bis; }这段代码里还有一个重要的处理逻辑当连续出现两个顶分型时保留价格更高的那个连续出现两个底分型时保留价格更低的那个。这个“取极值”规则直接决定了后续线段划分的准确性。3.4 一笔未完的处理策略实际走势中最后一笔往往是没有走完的——当前价格就在最后一根K线上顶分型或底分型还没确认。这时候不能生硬地画一笔到最后一个分型因为图上会多出一段误导性的线段。我的处理方式是最后一笔的终点不固定把它指向最新K线。也就是说程序会返回一个“未完成笔”的标记通达信侧画出来是虚线等下一根K线数据刷新时再重新计算。这样图面上看起来更干净也如实反映了“当前这笔尚未确认”的状态。4. 线段与中枢递归逻辑的落地4.1 线段的特征序列与划分笔确认之后线段划分就是缠论里最难的部分。严格来说线段是由至少三笔构成的但它不是简单地把三笔连起来——线段本身需要满足特征序列的顶底分型要求。特征序列的思路是这样的在一段向上线段中每一笔向下笔的“低点”构成一个序列这个序列如果出现了顶分型就说明向上线段可能要被终结。向下线段同理。划线的实用做法是先找出“特征序列分型”然后以分型为分界点将笔序列切割成线段。这部分的C实现比笔要复杂很多因为存在多义性处理我最终的方案是采用“线段破坏需第二特征序列分型确认”的方式。代码没法在这里完全展开但核心思路可以概括为几句话用已经确认的笔序列作为输入。按方向把笔分成“向上线段”和“向下线段的特征序列”。对特征序列做分型判断遇到分型就认为前一个线段结束新线段从分型点开始。如果分型还没确认保持当前线段延续。4.2 中枢的区间计算中枢的定义比较清晰连续三个次级别走势类型的重叠区间。在笔线段体系里我们简化为“连续三笔的重叠区间”。但要注意真正的缠论中枢应该基于线段来定义用笔来定义属于简化用法复盘时作为参考够用。中枢区间的计算逻辑是取连续三段走势类型比如线段1、2、3。中枢区间的高点是三段中“最低高点”的那一个也就是min(高点1, 高点2, 高点3)。中枢区间的低点是三段中“最高低点”的那一个也就是max(低点1, 低点2, 低点3)。如果高点大于低点说明三段有重叠中枢成立。表达式是中枢高点 min(线段高点列表)中枢低点 max(线段低点列表)。4.3 递归与二次确认这里要特别提醒缠论本身是递归定义的。笔构成线段线段构成更高级别的笔更高级别的笔又构成更高级别的线段。没有递归你的画线永远停留在最低级别。但完全按递归去实现在通达信里会碰到性能问题。500根K线做一两次递归还行做五六次就会明显卡顿。我的做法是默认只做两层结构即“分型→笔→线段”中枢基于线段计算。如果你想做更高级别的中枢可以外接更高周期的K线再算一次比如在30分钟图上画出的线段拿到日线图上就是日线级别的笔。这种跨周期组合比单图递归效率高得多。5. 主图画线输出DLL与绘图API的配合5.1 主图数据输出的格式DLL算完的数据要传回通达信公式。通达信DLL插件常规的做法是设置多个输出数组每个数组对应一个序列。我封装成这样的输出输出1笔方向数组 (1向上笔起点-1向下笔起点0无) 输出2笔起点价格 输出3笔终点价格 输出4中枢上沿 输出5中枢下沿公式侧只需要按这些数组画线即可。比如输出1的第i个值为1输出2和输出3的第i个值分别是该笔的起点价和终点价那在K线i的位置就能画出一条从起点到终点的线段。这只是我的设计方式你也可以把数据揉在同一个数组里用约定好的值来区分类型看个人习惯。5.2 画线数据的组织与颜色区分通达信主图画线我推荐用DRAWLINE和STICKLINE配合的方式。笔的部分用DRAWLINE从起点画到终点红线表示向上笔绿线表示向下笔。线段用更粗的线比如黄色或者白色和笔区分开。中枢矩形用DRAWKLINE或者自定义的STICKLINE组合。公式侧大致长这样// 通达信公式部分示意 上行笔:DRAWLINE(起笔条件, 起笔价格, 终笔条件, 终笔价格, 0) COLORRED; 下行笔:DRAWLINE(起笔条件, 起笔价格, 终笔条件, 终笔价格, 0) COLORGREEN;DLL返回的数据里要把“起笔条件”和“终笔条件”写成布尔值。当第i根K线是某笔起点时起笔条件为TRUE价格为该笔起点价当第i根K线是某笔终点时终笔条件为TRUE价格为该笔终点价。剩下的工作交给DRAWLINE去连线。5.3 参数可调节设计缠论算法里有很多参数会影响结果分型K线根数、笔的最低K线数、是否允许分型共用K线等。硬编码在DLL里的话每次调参都得重新编译很麻烦。我选择了“参数外部化”的方案在通达信公式里设置PARAM参数通过DLL的接口传入计算引擎。公式侧看起来是这样笔最少K线数: 5;用户在通达信里拖动参数条就能实时调整笔的灵敏度DLL收到新参数后自动重算并重绘。这个交互方式在实际使用中非常舒服复盘的时候可以快速对比不同参数下画线的差异。6. 实战中的坑与调试体会6.1 未来函数是最大的敌人前面反复强调过未来函数但实际写代码时太容易踩进去了。最常见的写法来自对“已知答案”的依赖。比如你知道这只股票后面涨了在代码里用HHV(高点, 10)去找“这一段的高点”表面上是找10根K线内的最高点实际却隐藏了“我先知道答案再倒推出高点”的问题。在含未来函数的公式上你永远看到的是“最完美的笔”这种图复盘再漂亮也没有实战参考价值。DLL方案之所以能绕开这个问题是因为你可以严格地按顺序遍历K线从第0根到第N根每一根只使用当前及以前的信息做判断。只要循环顺序不乱就天然不含未来函数。判断一个画线工具是否可信最简单的办法是看第N根K线之后能不能输出第N1根才该有的信息如果能就是未来函数。6.2 数据对齐与除权复权问题DLL拿到的K线数据必须和通达信当前显示的K线一一对应。用REF(H, N)取历史高低点时N的偏移如果错一位所有分型位置都会偏一格笔画出来看着没毛病实际全错位。另外复权方式也会影响画线结果。前复权、后复权、不复权三种模式下K线的高低价是完全不同的。最稳妥的做法是在计算前明确记录当前使用的复权类型复盘时固定用一种复权方式不要来回切换。6.3 不同周期的一致性同一只股票在1分钟、5分钟、30分钟图上用同一套参数算出来的笔是不一样的。这不是bug而是缠论本身的分级特性——不同级别反映的是不同的走势结构。如果你的画线工具在1分钟图和日线图上画出来的笔完全一致那反而要警惕说明算法很可能没有按级别递归只是在用同一套参数做机械扫描。真正的缠论实现不同级别之间应该有一种“自相似”的结构关系这是验证算法是否正确的一个隐含标准。我在写完后做了一组对比测试用同一套源码分别在5分钟和30分钟图上跑同一只股票观察线段划分是否呈现“大级别笔由小级别线段构成”的递进关系。能对上算法基本就是对的。7. 最后的落地体验与调试建议整个项目折腾下来最深的体会有三个。第一缠论自动画线的价值不在“自动”两个字而在可复现。以前和人讨论走势各画各的谁也说服不了谁。有了这套工具之后同样的数据和参数任何人跑出来都是同一张图讨论才有共同基础。第二不要追求一次画对全部结构。先只输出笔验证笔的准确率再把笔连成线段最后才加中枢矩形。每加一层都会暴露前面一层的问题一层层稳扎稳打比憋大招靠谱得多。第三参数别迷信默认值。网上的缠论画线工具默认参数各不相同有的要求分型之间至少隔3根K线有的要5根还有的用百分比过滤毛刺。这些差异不是谁对谁错而是适用场景不同。实盘研究的时候把参数调低可以得到更灵敏的笔调高可以得到更稳健的大级别结构没有绝对标准。如果你是从零开始建议不要直接拿现成的源码跑那样出了问题也不知道怎么改。先自己把包含关系合并写出来用一小段真实的日K数据手工验证一遍再往上加分型、加笔。基础算法自己亲手写过一遍之后后面所有扩展都顺了这是我在这套代码上最大的收获。本文还有配套的精品资源点击获取
返回列表