news 2026/9/6 20:01:29

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
通达信缠论笔线段自动画线:基于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(vector<KLine>& klines) { vector<KLine> 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=底分型 }; vector<Fenxing> FindFenxing(const vector<Fenxing>& merged) { vector<Fenxing> result; for (int i = 1; i < merged.size() - 1; i++) { bool is_top = merged[i].high > merged[i-1].high && merged[i].high > merged[i+1].high; bool is_bottom = merged[i].low < merged[i-1].low && merged[i].low < merged[i+1].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=向上笔 }; vector<Bi> BuildBi(const vector<Fenxing>& fenxings, int min_bars) { vector<Bi> 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线之后能不能输出第N+1根才该有的信息,如果能,就是未来函数。

6.2 数据对齐与除权复权问题

DLL拿到的K线数据必须和通达信当前显示的K线一一对应。用REF(H, N)取历史高低点时,N的偏移如果错一位,所有分型位置都会偏一格,笔画出来看着没毛病,实际全错位。

另外复权方式也会影响画线结果。前复权、后复权、不复权三种模式下,K线的高低价是完全不同的。最稳妥的做法是在计算前明确记录当前使用的复权类型,复盘时固定用一种复权方式,不要来回切换。

6.3 不同周期的一致性

同一只股票在1分钟、5分钟、30分钟图上,用同一套参数算出来的笔是不一样的。这不是bug,而是缠论本身的分级特性——不同级别反映的是不同的走势结构。

如果你的画线工具在1分钟图和日线图上画出来的笔完全一致,那反而要警惕,说明算法很可能没有按级别递归,只是在用同一套参数做机械扫描。真正的缠论实现,不同级别之间应该有一种“自相似”的结构关系,这是验证算法是否正确的一个隐含标准。

我在写完后做了一组对比测试:用同一套源码,分别在5分钟和30分钟图上跑同一只股票,观察线段划分是否呈现“大级别笔由小级别线段构成”的递进关系。能对上,算法基本就是对的。

7. 最后的落地体验与调试建议

整个项目折腾下来,最深的体会有三个。

第一,缠论自动画线的价值不在“自动”两个字,而在可复现。以前和人讨论走势,各画各的,谁也说服不了谁。有了这套工具之后,同样的数据和参数,任何人跑出来都是同一张图,讨论才有共同基础。

第二,不要追求一次画对全部结构。先只输出笔,验证笔的准确率;再把笔连成线段;最后才加中枢矩形。每加一层都会暴露前面一层的问题,一层层稳扎稳打比憋大招靠谱得多。

第三,参数别迷信默认值。网上的缠论画线工具默认参数各不相同,有的要求分型之间至少隔3根K线,有的要5根,还有的用百分比过滤毛刺。这些差异不是谁对谁错,而是适用场景不同。实盘研究的时候,把参数调低可以得到更灵敏的笔,调高可以得到更稳健的大级别结构,没有绝对标准。

如果你是从零开始,建议不要直接拿现成的源码跑,那样出了问题也不知道怎么改。先自己把包含关系合并写出来,用一小段真实的日K数据手工验证一遍,再往上加分型、加笔。基础算法自己亲手写过一遍之后,后面所有扩展都顺了,这是我在这套代码上最大的收获。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 19:53:21

IOPaint AI去水印完整指南:免费开源上手

IOPaint AI去水印完整指南&#xff1a;免费开源上手 【免费下载链接】IOPaint Image inpainting tool powered by SOTA AI Model. Remove any unwanted object, defect, people from your pictures or erase and replace(powered by stable diffusion) any thing on your pictu…

作者头像 李华
网站建设 2026/9/6 19:53:18

Stewart平台六自由度主动隔振仿真复现:建模、控制与优化

简介&#xff1a;一份聚焦六自由度隔振平台优化与控制技术研究的论文复现资料&#xff0c;面向机械工程与控制工程背景的研究人员和工程师&#xff0c;解决控制力矩陀螺引发的卫星微振动问题&#xff0c;基于Stewart机构开展主被动联合隔振方案设计与仿真。内容完整覆盖主被动隔…

作者头像 李华
网站建设 2026/9/6 19:53:09

基于STM32的智能家居护眼台灯设计与实现

简介&#xff1a;这是一份基于STM32的智能家居护眼台灯设计与实现的电子信息专业论文写作模板&#xff0c;内含完整论文结构&#xff0c;涵盖摘要、绪论、系统分析、硬件选型、算法设计等章节&#xff0c;适合具备单片机基础的电子信息类学生、嵌入式初学者及智能硬件爱好者用于…

作者头像 李华