如何构建并优化基于成交量的交易系统蔡金资金流指标(Chaikin Money Flow - CMF)(基础篇)
「用 CMF 把成交量翻译成资金方向」
蔡金资金流(CMF)把每日成交量和收盘价在当日振幅里的位置绑定,给出一段时间内资金净流入还是净流出的读数。它在 MT5 里属于内置成交量类指标,默认周期 20,输出在 -1 到 +1 之间摆动,0 轴是多空分水岭。 对黄金和外汇来说,CMF 单独用很容易被震荡市刷掉,但它和价格背离的组合值得盯:当价格创新高而 CMF 未同步抬升,上涨动能可能衰竭;反之价格新低而 CMF 回升,下跌倾向减弱。这类信号在 H1 及以上周期更干净,M1 噪音偏大。 在 MT5 里直接拖入 CMF 后,把周期从 20 改成 14 或 34 是常见调法——14 更灵敏但假信号多,34 滞后但过滤强。外汇与贵金属杠杆高、滑点突发行情多,任何指标信号都只是概率参考,实盘前请用策略测试器跑至少 3 个月 tick 数据验证。
◍ 先认识 CMF 这个成交量指标
本篇要拆的是一个基于成交量的技术指标——蔡金资金流指标(CMF)。我们会从定义、计算方式、自定义代码编写,到把它塞进交易系统做回测优化,一步步过一遍。 这类文章的核心目的很直接:把新工具摊开给你看,既能单独用,也能按它的特性拼进你现有的分析框架里,再跑测试看实不实用。 外汇和贵金属市场高杠杆、高波动,CMF 只是概率层面的辅助信号,任何用它构成的系统都得你在 MT5 上自己验过才作数,盈亏自负。
用 CMF 读穿资金积累与派发
蔡金资金流(CMF)把成交量和收盘价在 K 线中的相对位置揉在一起,用来判断一段周期内资金是在吸筹还是派发。核心逻辑很直白:收盘越贴最高价,越像资金在积累;收盘越贴最低价,越像在分布。配合放大的成交量,这种偏向会被放大成指标的正负值。 计算上分三层。先算资金流乘数:(收盘价-最低价)-(最高价-收盘价) 再除以 (最高价-最低价),这东西天然落在 -1 到 1 之间。乘上当年周期成交量得到资金流成交量,最后取 n 周期的资金流成交量之和除以 n 周期成交量之和,就是 CMF。 出来的曲线绕 0 轴晃,区间锁在 +1 到 -1。上穿 0 可能意味着买盘开始占上风,下穿 0 则倾向卖盘主导;贴着 0 磨往往说明多空没分出明显方向。外汇和贵金属波动大、杠杆高,光看穿越信号容易吃假突破,建议开 MT5 加载默认 CMF 先肉眼校一遍再谈用法。 原文给的下一步是把它改成柱状图或调周期,这其实点出了 MT5 指标可改代码的便利——不用死守折线。
「手搓一个独立窗口的蔡金资金流」
想把蔡金资金流(CMF)塞进自己的交易系统,最干净的做法是在 MT5 里写个自定义指标,而不是依赖内置版本。核心思路不复杂:用 #property 把外观和行为钉死,再用 OnCalculate 跑累加逻辑,编译后就能在独立子窗口里看到蓝柱。 外观层面先定调:indicator_separate_window 让它单独开窗;indicator_buffers 设 4、indicator_plots 设 1,说明我们只画一条图但留了多余缓冲;indicator_type1 用 DRAW_HISTOGRAM,线宽 indicator_width1=3,颜色 clrBlue。水平线放在 0、+0.20、-0.20,样式 STYLE_DOT、黑色、线宽 0——这三条线就是后续判多空惯性的参照。 输入参数只留两个:periods 默认 20,volumeTypeInp 默认 VOLUME_TICK。缓冲区 cmfBuffer 用 SetIndexBuffer(0,cmfBuffer,INDICATOR_DATA) 绑定;IndicatorSetInteger(INDICATOR_DIGITS,5) 控小数位;PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,0) 表示从第 0 根就画;短名拼成 "Chaikin Money Flow(20)" 方便看图识别。 计算部分靠 AccDis 函数吃下 high/low/close/volume,先算资金流乘数再乘量得资金流成交量,循环累加 sumAccDis 与 sumVol,比值写回 cmfBuffer。外汇与贵金属波动受成交量断层影响大,CMF 仅作概率倾向参考,实盘前务必在 MT5 用历史数据验证柱体反应速度。 下面这段是拼好的关键声明与初始化片段,直接丢进 MQ5 文件就能编译出雏形:
class="macro">#class="kw">property description "Chaikin Money Flow" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">4 class="macro">#class="kw">property indicator_plots class="num">1 class="macro">#class="kw">property indicator_width1 class="num">3 class="macro">#class="kw">property indicator_level1 class="num">0 class="macro">#class="kw">property indicator_level2 class="num">0.20 class="macro">#class="kw">property indicator_level3 -class="num">0.20 class="macro">#class="kw">property indicator_levelstyle STYLE_DOT class="macro">#class="kw">property indicator_levelwidth class="num">0 class="macro">#class="kw">property indicator_levelcolor clrBlack class="macro">#class="kw">property indicator_type1 DRAW_HISTOGRAM class="macro">#class="kw">property indicator_color1 clrBlue input class="type">int periods=class="num">20; class=class="str">"cmt">// Periods input ENUM_APPLIED_VOLUME volumeTypeInp=VOLUME_TICK; class=class="str">"cmt">// Volume Type class="type">class="kw">double cmfBuffer[]; SetIndexBuffer(class="num">0,cmfBuffer,INDICATOR_DATA); IndicatorSetInteger(INDICATOR_DIGITS,class="num">5); PlotIndexSetInteger(class="num">0,PLOT_DRAW_BEGIN,class="num">0); IndicatorSetString(INDICATOR_SHORTNAME,"Chaikin Money Flow("+class="type">class="kw">string(periods)+")"); class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[],
◍ CMF 指标里成交量与累积分布的落地写法
上面这段是 Chaikin Money Flow 自定义指标的核心计算段,区分了 tick 成交量与真实成交量两种数据源,由 volumeTypeInp 开关切换。 当 rates_total 小于 periods 时直接返回 0,避免样本不足算出无意义数值;initPos 从 prev_calculated-1 起步,保证增量刷新时不重算整段历史。 内层循环调用 AccDis 函数,用 (2*close-high-low)/(high-low)*volume 把每根 K 线的价量关系折算成累积分布量,再除以周期总成交量得到 CMF 值,写入 cmfBuffer[pos+periods-1]。 指标属性里划了 0、±0.20 三条虚线水平,CMF 突破 +0.20 可能暗示资金流入偏强,跌破 -0.20 则倾向流出;外汇与贵金属波动受杠杆放大,实盘验证前先用 MT5 策略测试器跑一遍。 return 语句返回 rates_total-periods-10,这个值会影响 OnCalculate 下一轮 prev_calculated 的起始位点,调参时别忽略这 10 根缓冲偏移。
const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) if(rates_total<periods) class="kw">return(class="num">0); class="type">int initPos = prev_calculated -class="num">1; if(initPos<class="num">0) initPos = class="num">0; if(volumeTypeInp==VOLUME_TICK) { for(class="type">int pos = initPos;pos<=rates_total-periods;pos++) { class="type">class="kw">double sumAccDis = class="num">0; class="type">long sumVol = class="num">0; for(class="type">int i = class="num">0; i < periods && !IsStopped(); ++i) { class="type">long thisTickVolume = tick_volume[pos+i]; sumVol += thisTickVolume; sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume); } cmfBuffer[pos+periods-class="num">1] = sumAccDis/sumVol; } } else { for(class="type">int pos = initPos;pos<=rates_total-periods;pos++) { class="type">class="kw">double sumAccDis = class="num">0; class="type">long sumVol = class="num">0; for(class="type">int i = class="num">0; i < periods && !IsStopped(); ++i) { class="type">long thisTickVolume = volume[pos+i]; sumVol += thisTickVolume; sumAccDis += AccDis(high[pos+i], low[pos+i], close[pos+i], thisTickVolume); } cmfBuffer[pos+periods-class="num">1] = sumAccDis/sumVol; } } class="kw">return (rates_total-periods-class="num">10); class="type">class="kw">double AccDis(class="type">class="kw">double high,class="type">class="kw">double low,class="type">class="kw">double close,class="type">long volume) { class="type">class="kw">double res=class="num">0; if(high!=low) res=(class="num">2*close-high-low)/(high-low)*volume; class="kw">return(res); } class="macro">#class="kw">property description "Chaikin Money Flow" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">4 class="macro">#class="kw">property indicator_plots class="num">1 class="macro">#class="kw">property indicator_width1 class="num">3 class="macro">#class="kw">property indicator_level1 class="num">0 class="macro">#class="kw">property indicator_level2 class="num">0.20 class="macro">#class="kw">property indicator_level3 -class="num">0.20 class="macro">#class="kw">property indicator_levelstyle STYLE_DOT