
简介本资源为通达信平台缠论技术分析功能的C DLL模块开源实现面向量化交易开发者、证券软件二次开发工程师及缠论算法研究者提供可嵌入通达信客户端的底层指标计算逻辑。源码基于VC6.0开发完整呈现缠论笔、线段、中枢等核心结构的C建模与递归判定逻辑适合作为学习经典技术分析算法工程化落地的实践范例。压缩包为RAR格式共含若干C源文件.cpp/.h、项目工程文件.dsp/.dsw及编译说明总大小891KB结构紧凑便于快速编译调试与逻辑验证。已有1201人学习下载读者可直接获取可运行的DLL接口定义、缠论各级别走势分解的完整状态机实现、以及适配通达信数据结构的封装层代码对理解技术指标DLL开发规范与缠论自动化识别具有较强参考价值。1. 缠论DLL不是“插件包”而是通达信指标生态里可编译、可调试、可嵌入的C计算内核很多刚接触通达信量化开发的人看到“缠论DLL”第一反应是下载一个现成的.dll文件双击安装——结果发现根本无法加载或者加载后指标不显示、K线不识别、分笔出错。这不是你电脑缺VC运行库也不是通达信版本太低而是从根本上混淆了“DLL”的角色它不是封装好的黑盒工具而是用C实现缠论核心逻辑如分笔、画线、中枢识别、走势类型判定的动态链接库必须与通达信的指标调用协议严格对齐且需在Visual Studio中编译为x86架构、/MT静态链接、无CRT依赖的纯Win32 DLL。这类源码通常由资深交易者或C工程师维护比如标题中提到的southern977和perhapsryf他们公开的代码不是为了“一键导入”而是提供可审计、可定制、可对接实盘信号的底层计算能力。适合三类人想把缠论规则真正落地为自动识别逻辑的量化开发者需要在通达信主图/副图中嵌入高精度缠论结构标记的策略研究员以及正在从公式语言TDX公式转向C高性能计算的进阶用户。它解决的不是“怎么画线”而是“为什么这样画线才符合缠论原文定义”。2. 用Visual Studio 2019编译缠论C源码生成x86通达信兼容DLL的最小可行流程通达信所有版本含最新V7.85仅支持x86架构DLL且明确要求导出函数使用__stdcall调用约定、无C异常传播、不依赖外部DLL如MSVCP140.dll。这意味着你不能直接用CMakeLists.txt生成默认配置的DLL也不能用MinGW或Clang编译。必须用Visual Studio原生工具链并手动关闭所有可能引入运行时依赖的选项。2.1 创建空DLL项目并配置关键编译参数打开Visual Studio 2019 → 新建项目 → “动态链接库(DLL)” → 项目名设为ChanLunCore→ 目标平台选“x86”不是x64→ 点击“创建”。进入项目属性页右键项目 → 属性按顺序修改以下五项配置属性 → 常规 → 字符集设为“使用多字节字符集”不是Unicode配置属性 → C/C → 代码生成 → 运行时库设为/MT多线程静态链接配置属性 → 链接器 → 高级 → 导入库清空该字段避免生成.lib配置属性 → 链接器 → 高级 → 入口点填入DllMain确保Windows加载时能初始化配置属性 → 链接器 → 输入 → 忽略所有默认库设为“是”/NODEFAULTLIB提示/MT是硬性要求。若用/MD动态链接CRT通达信加载时会报OSERROR: [WinError 1114] 动态链接库(DLL)初始化例程失败——这不是DLL损坏而是CRT初始化冲突。/NODEFAULTLIB则防止链接器偷偷拉入kernel32.lib等非必要依赖。2.2 实现通达信DLL标准导出接口GetIndicatorData通达信通过固定函数名调用DLL最核心的是GetIndicatorData。它接收K线数据指针、周期参数、输出缓冲区地址返回计算结果。以下是精简但完整的实现骨架需替换为你实际的缠论逻辑// ChanLunCore.cpp #include stdafx.h #include vector #include algorithm // 通达信要求的导出函数签名必须__stdcall参数顺序不可变 extern C __declspec(dllexport) int __stdcall GetIndicatorData( double* pOpen, // 开盘价数组按时间倒序最新在前 double* pHigh, // 最高价 double* pLow, // 最低价 double* pClose, // 收盘价 double* pVolume, // 成交量 int nCount, // K线总数通常≥500 double* pOutBuffer, // 输出缓冲区长度由调用方分配 int nOutLen, // 输出缓冲区总长度单位double int nParam1, // 用户自定义参数1如分笔阈值 int nParam2, // 用户自定义参数2如中枢震荡次数 int nParam3, // 用户自定义参数3如走势类型过滤开关 char* pStrParam // 字符串参数如“笔起点标记” ) { if (nCount 3 || !pOutBuffer || nOutLen 100) return -1; // 步骤1将倒序K线转为正序缠论计算需从左到右遍历 std::vectordouble open(pOpen, pOpen nCount); std::reverse(open.begin(), open.end()); // 同理处理high/low/close/volume... // 步骤2执行缠论分笔示例简单顶底分型包含处理 std::vectorint biStarts; // 存储笔起点索引正序索引 for (int i 2; i nCount - 2; i) { bool isTop (open[i-2] open[i-1]) (open[i-1] open[i]) (open[i] open[i1]) (open[i1] open[i2]); bool isBottom (open[i-2] open[i-1]) (open[i-1] open[i]) (open[i] open[i1]) (open[i1] open[i2]); if (isTop || isBottom) biStarts.push_back(i); } // 步骤3写入输出缓冲区通达信要求每组数据占3个double[x,y,value] int writePos 0; for (size_t j 0; j biStarts.size() writePos 3 nOutLen; j) { int idx biStarts[j]; // 正序索引 pOutBuffer[writePos] static_castdouble(idx); // X坐标K线索引 pOutBuffer[writePos] open[idx]; // Y坐标价格 pOutBuffer[writePos] 1.0; // value标记类型1笔起点 } return writePos / 3; // 返回有效点数 }参数说明与通达信调用逻辑pOpen等数组是倒序排列最新K线在索引0这是通达信为兼容历史数据设计的约定必须反转后再做缠论分析nOutLen是调用方预分配的double数量不是字节数每个绘图点需3个doubleX,Y,Value因此最大可写点数为nOutLen/3nParam1~3对应通达信公式中的PARAM#1、PARAM#2等在TDX公式里写DRAWICON(REF(C,1)REF(C,2) AND ... , L, 10)时10即传给nParam1pStrParam用于传递字符串指令如DRAWLINE表示启用连线NO_LABEL表示禁用文字标注。2.3 添加DllMain确保线程安全与资源隔离通达信可能在多个窗口同时加载同一DLL必须保证全局状态不冲突。DllMain是唯一安全的初始化入口// DllMain.cpp #include stdafx.h // 每个线程独立的缠论计算上下文避免static变量跨线程污染 __declspec(thread) struct ChanContext { std::vectorint lastBiPoints; int lastCalcTime; } g_threadContext; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 进程加载时可初始化全局只读资源如小数点精度表 break; case DLL_THREAD_ATTACH: // 线程加载时清空本线程上下文 g_threadContext.lastBiPoints.clear(); g_threadContext.lastCalcTime 0; break; case DLL_THREAD_DETACH: // 线程卸载时释放本线程独占资源 g_threadContext.lastBiPoints.clear(); break; case DLL_PROCESS_DETACH: // 进程卸载时释放全局资源如有 break; } return TRUE; }注意通达信指标计算是单线程调用但DllMain仍需按规范实现DLL_THREAD_ATTACH/DETACH否则在多窗口场景下可能出现内存越界。__declspec(thread)是Windows特有语法确保每个线程有独立副本比static更安全。3. 在通达信中注册并调用缠论DLLTDX公式语法与调试定位技巧编译生成的ChanLunCore.dll不能直接拖入通达信目录必须通过TDX公式显式声明调用路径并严格匹配参数类型与返回值。通达信的DLL调用机制是“弱类型”设计错误不会报详细异常只会静默失败或返回0因此调试必须前置。3.1 TDX公式中声明DLL函数并绑定参数在通达信公式编辑器中新建公式输入以下内容注意路径、函数名、参数个数必须与C源码完全一致// 缠论笔识别主图指标 {参数} N1:10; // 分笔波动阈值% N2:3; // 中枢最小段数 N3:1; // 1启用笔标记0仅计算不绘图 // 声明DLL函数路径必须是绝对路径或相对通达信根目录 DLLName:ChanLunCore.dll; FuncName:GetIndicatorData; // 调用DLL参数顺序OPEN,HIGH,LOW,CLOSE,VOLUME, COUNT, OUTPUT_BUFFER, OUTPUT_LEN, PARAM1, PARAM2, PARAM3, STR_PARAM Result : CALLDLL(DLLName, FuncName, OPEN, HIGH, LOW, CLOSE, VOL, DATACOUNT, REF(BI_BUFFER,1), 1000, N1, N2, N3, DRAWICON); // 解析返回结果每个点占3个元素X,Y,Value IF Result 0 THEN BEGIN FOR I : 0 TO Result-1 DO BEGIN X : REF(BI_BUFFER,1)[I*30]; // K线索引正序 Y : REF(BI_BUFFER,1)[I*31]; // 价格 V : REF(BI_BUFFER,1)[I*32]; // 类型 IF V 1 THEN DRAWICON(X, Y, 10); // 画笔起点图标图标ID 10 END; END;关键细节解析REF(BI_BUFFER,1)BI_BUFFER是公式中预定义的ARRAY型变量长度≥1000REF(...,1)取其上一根K线的值确保DLL写入时缓冲区已分配DATACOUNT通达信内置函数返回当前周期K线总数不是BARPOSBARPOS是当前K线索引从0开始DRAWICON字符串参数C代码中用strcmp(pStrParam, DRAWICON)0判断是否启用图标绘制图标ID10对应通达信内置图标库DRAWICON(X,Y,10)在坐标(X,Y)处画一个小三角形常用于标记笔起点。3.2 定位DLL加载失败的三大日志入口当指标不显示、CALLDLL返回-1或0时不要盲目重装VC运行库按以下顺序排查排查层级操作方法典型现象与原因文件层将ChanLunCore.dll复制到TdxW.exe同目录如C:\tdx\Trade\用Dependency Walkerx86版打开DLL检查右侧列表是否只有KERNEL32.dll、USER32.dll等系统DLL若出现MSVCP140.dll、VCRUNTIME140.dll说明未设/MT需回VS重新编译注册层在通达信公式编辑器中点击“测试”按钮观察右下角状态栏是否提示“DLL加载成功”若提示“找不到指定模块”检查DLL路径是否含中文、空格或长路径建议放根目录函数层在C代码GetIndicatorData开头插入OutputDebugStringA(DLL called);用DebugViewSysinternals工具捕获输出若无任何输出说明通达信根本未调用该函数——检查FuncName拼写、大小写、是否多空格提示通达信的CALLDLL函数在x86模式下对函数名大小写敏感getindicatordata和GetIndicatorData被视为不同函数。务必与__declspec(dllexport)声明的名称完全一致。4. 缠论DLL性能优化减少内存拷贝、规避浮点精度陷阱、适配通达信实时行情流通达信实时行情推送频率可达毫秒级而缠论计算尤其中枢递归复杂度为O(n²)若每次Tick都全量重算500根K线CPU占用会飙升至30%以上。必须采用增量计算与缓存策略且警惕通达信数据精度导致的分型误判。4.1 实现K线增量更新机制只重算最后N根通达信在实时行情中DATACOUNT每新来一根K线就1但旧K线数据不变。因此无需每次调用都处理全部K线只需关注新增部分// 在GetIndicatorData中添加增量逻辑 static int s_lastCount 0; static std::vectorint s_cachedBiPoints; if (nCount s_lastCount) { // 新增K线数 int newBars nCount - s_lastCount; // 只对最后200根K线做分笔覆盖至少3个笔的长度 int startIdx std::max(0, nCount - 200); // 从startIdx开始重算复用s_cachedBiPoints中旧结果 std::vectorint newBiPoints CalculateBiFrom(startIdx, nCount, ...); // 合并保留旧缓存中未被新计算覆盖的部分 新增点 s_cachedBiPoints.clear(); s_cachedBiPoints.insert(s_cachedBiPoints.end(), s_cachedBiPoints.begin(), s_cachedBiPoints.begin() (startIdx 0 ? startIdx : 0)); s_cachedBiPoints.insert(s_cachedBiPoints.end(), newBiPoints.begin(), newBiPoints.end()); s_lastCount nCount; } // 输出s_cachedBiPoints...参数选择依据200是经验值缠论一笔至少5根K线一个标准中枢需至少9根3段200根可覆盖约40笔足够应对日线/30分钟线startIdx动态计算避免固定偏移导致漏判如跳空缺口后分型位置偏移缓存std::vector而非全局数组利用RAII自动管理内存防止DllMain中DLL_PROCESS_DETACH时野指针。4.2 处理通达信价格精度陷阱用整数比替代浮点比较通达信内部价格以double存储但实际精度仅到小数点后2~3位取决于品种。直接用比较pHigh[i] pHigh[i-1]在极小波动时可能因浮点误差失效// 错误写法受浮点舍入影响 if (pHigh[i] pHigh[i-1] pLow[i] pLow[i-1]) { /* 上移中枢 */ } // 正确写法转换为整数比较假设价格精度为0.01 long long high1 static_castlong long(pHigh[i-1] * 100 0.5); long long high2 static_castlong long(pHigh[i] * 100 0.5); if (high2 high1) { /* 确认真实上涨 */ }精度映射表通达信常见品种品种类型价格单位推荐放大倍数示例10.234→10234A股股票0.01元×10010.234 * 100 1023截断→ 应0.5四舍五入期货主力0.001元×10002345.678 * 1000 2345678港股通0.001港币×1000同上加密货币0.00000001 BTC×100000000需long long容纳注意0.5是关键。static_castlong long(x * 100)直接截断x10.235会变成1023而非1024加0.5后10.235*1000.51024.0再转long long得正确值。4.3 适配通达信通讯协议接收Level2逐笔委托数据部分高级缠论实现需结合逐笔成交Level2判断买卖力衰竭此时DLL需主动连接通达信行情服务器。通达信提供TDXTcpClient类需引用TdxApi.h但必须在DLL中单独开线程接收不能阻塞GetIndicatorData主线程// 在DllMain DLL_PROCESS_ATTACH中启动接收线程 HANDLE hRecvThread CreateThread(NULL, 0, [](LPVOID) - DWORD { TDXTcpClient client; client.Connect(127.0.0.1, 7709); // 通达信Level2端口 while (g_running) { TdxPacket pkt; if (client.RecvPacket(pkt)) { // 解析逐笔数据更新买卖盘强度缓存 UpdateBuySellStrength(pkt); } } return 0; }, NULL, 0, NULL);安全约束g_running为全局volatile boolDLL_PROCESS_DETACH时置false并WaitForSingleObject(hRecvThread, 1000)TDXTcpClient必须静态链接#pragma comment(lib, TdxApi.lib)避免运行时DLL缺失逐笔数据不参与GetIndicatorData主计算流仅作为pStrParam中USE_LEVEL2开关的辅助信号源。5. 验证缠论DLL输出正确性的三步交叉校验法用Python解析二进制缓冲区通达信不提供DLL内部变量查看功能CALLDLL返回的BI_BUFFER是二进制double数组肉眼无法验证计算是否符合缠论原文定义如“连续三段重叠构成中枢”。必须脱离通达信环境用Python独立解析输出缓冲区与手工标注对比。5.1 提取通达信输出缓冲区到文件在TDX公式中添加调试语句将BI_BUFFER内容导出为二进制文件// 在CALLDLL后添加 IF Result 0 THEN BEGIN // 将BI_BUFFER前Result*3个double写入文件 FileName : C:\tdx\debug\bi_output.bin; FileWrite(FileName, REF(BI_BUFFER,1), Result*3*8); // 8字节/double END;5.2 Python脚本解析并可视化笔结构import numpy as np import matplotlib.pyplot as plt # 读取二进制文件double数组 with open(rC:\tdx\debug\bi_output.bin, rb) as f: data np.frombuffer(f.read(), dtypenp.float64) # 每3个元素为一组[X索引, Y价格, Value类型] points data.reshape(-1, 3) print(f共解析 {len(points)} 个笔点) # 过滤Value1的笔起点 bi_starts points[points[:, 2] 1.0] print(f笔起点{bi_starts.shape[0]} 个索引范围 {bi_starts[:,0].min():.0f} ~ {bi_starts[:,0].max():.0f}) # 绘制验证图需配合原始K线数据 plt.figure(figsize(12,6)) plt.plot(range(int(bi_starts[:,0].max())10), np.random.rand(int(bi_starts[:,0].max())10)*1010, k-, alpha0.3) # 模拟K线 plt.scatter(bi_starts[:,0], bi_starts[:,1], cr, s50, zorder5, label笔起点) plt.xlabel(K线索引正序) plt.ylabel(价格) plt.legend() plt.grid(True) plt.title(缠论DLL输出笔起点验证) plt.show()校验逻辑bi_starts[:,0]应为递增整数序列如[5, 12, 23, 35,...]若出现重复索引或乱序说明C中biStarts向量未按时间顺序pushbi_starts[:,1]价格值应与通达信主图对应K线的LOW或HIGH吻合笔起点必为顶或底分型数量级应合理日线图上500根K线通常产生30~80个笔若仅2~3个大概率是分型识别阈值nParam1设得过大。5.3 对照缠论原文关键条款反向验证最后一步拿输出结果对照《缠论》原文第三课“走势分解”中的硬性条件缠论原文条款DLL应满足的代码逻辑验证失败时的修复方向“笔必须有顶分型和底分型构成”GetIndicatorData中biStarts必须交替出现顶/底标识可用pStrParam传入MARK_TYPE让DLL返回类型在C中增加isTop/isBottom标志位输出时第4个double存类型“中枢至少由三个连续次级别走势重叠”若DLL支持中枢计算pOutBuffer中应有Value2的中枢区间点且X坐标跨度≥9检查中枢识别循环是否用了for (int i0; inCount-8; i)确保覆盖9根K线“走势类型完成需离开中枢”笔终点价格与中枢区间无重叠可用std::set_intersection验证在C中增加中枢区间缓存计算笔终点时做区间判断提示真正的缠论DLL不是“画线工具”而是可验证的数学结构提取器。每一次CALLDLL调用都应能回答“这个笔是否满足原文第X章第Y条定义”——这才是southern977和perhapsryf开源代码的核心价值。本文还有配套的精品资源点击获取