探索创建多彩烛条的选项(基础篇)
◍ 给烛条上色这件事本身没那么简单
MT5 默认烛条只有涨绿跌红两种状态,想做多色分类(比如按波动率、按时段、按订单流)得自己改绘图逻辑。2021-10-29 那篇社区示例被阅 2072 次、评 16 条,说明自定义彩条是实盘前绕不开的底层改造。 外汇与贵金属杠杆高、滑点突变频繁,单靠颜色切换不能替你判断方向,只能把已有信号可视化。开 MT5 新建指标把下面代码贴进去,先跑 EURUSD 的 M15 看渲染是否跟手。
「为什么要用颜色压缩多线信号」
把多条价格线直接叠在图上看,往往比看裸 K 还累。原文里搭了个三置换线模型:黑线是价格本身,蓝线是向前偏移 10 根的 1 周期均线,红线是向前偏移 20 根的 1 周期均线。光分辨颜色不够,还得看三线之间的穿插关系,决策混乱和误判的概率明显上升。 方向走势指数(DMI)也是同类坑——线多、关系多,肉眼跟踪成本太高。变量越多,解释错误越频繁,交易动作就容易前后不一致。 上色规则本质是一种信息压缩:把 N 条线的关系合成成少数几种颜色,直接回答“现在该多、该空还是观望”。原文用绿=做多、红=做空、黄=中性,一张表就替代了三线纠缠。 需要提醒的是,外汇与贵金属杠杆高、波动快,颜色信号只是把信息变易读,不代表方向必然成立;真要上线前请在 MT5 里用历史数据自己验一遍触发频率。
用向导搭出第一套烛条缓冲区
想在 MT5 里自己画烛条,最省事的办法是用 MQL 向导生成指标骨架:命名时顺手把版权和链接改成自己的,OnCalculate 直接接收开盘、最高、最低、收盘四个价格序列。绘图类型选 DRAW_CANDLES,标签写成“Candle”,这个名字既出现在导航树也会绑到缓冲区。 向导默认开 4 个缓冲区 CandleBuffer1~4,分别装 Open/High/Low/Close,数量由 #property indicator_buffers 4 写死,后续要加成交量之类再改这个数。编译后指标进文件浏览器,但拖到图上一根都出不来——因为缓冲区还没填值。 填值在 OnCalculate 里做:用 rate_total 拿到当前总蜡烛数,prev_calculated 是上一次算到的根数。若本次 n+1 根,只算最后一根即可,已赋值的旧缓冲不用重算。下面这段是向导产出的初始化壳子,四个 SetIndexBuffer 把数组绑成指标数据线,编译后你就能在 MT5 里看到全红蜡烛——颜色是 indicator_color1 写死的 clrRed,想绿涨红跌得自己改属性或留给用户界面调。外汇和贵金属波动剧烈,自绘指标仅作结构验证,实盘信号需结合多周期确认。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Candles.mq5 | class=class="str">"cmt">//| Copyright class="num">2021, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">4 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//--- plot Candle class="macro">#class="kw">property indicator_label1 "Candle" class="macro">#class="kw">property indicator_type1 DRAW_CANDLES class="macro">#class="kw">property indicator_color1 clrRed class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double CandleBuffer1[]; class="type">class="kw">double CandleBuffer2[]; class="type">class="kw">double CandleBuffer3[]; class="type">class="kw">double CandleBuffer4[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- indicator buffers mapping SetIndexBuffer(class="num">0,CandleBuffer1,INDICATOR_DATA); SetIndexBuffer(class="num">1,CandleBuffer2,INDICATOR_DATA); SetIndexBuffer(class="num">2,CandleBuffer3,INDICATOR_DATA); SetIndexBuffer(class="num">3,CandleBuffer4,INDICATOR_DATA); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 把四价写进缓冲区才算跑通
指标声明了 4 个缓冲区(indicator_buffers 4),对应开高低收四价,这是 MT5 自定义指标能往主图绘制的硬前提。若缓冲区数量与后续赋值数组不匹配,编译不会报错但图表上什么都不会画。 下面这段 OnCalculate 才是真正干活的循环。prev_calculated 是上一次计算到的位置,新 tick 进来不用从头刷,直接从 start 接着写,省 CPU。 int start = prev_calculated; if(start>=rates_total) start = rates_total-1; for(int i = start; i<rates_total; i++) { CandleBuffer1[i]=open[i]; CandleBuffer2[i]=high[i]; CandleBuffer3[i]=low[i]; CandleBuffer4[i]=close[i]; } return(rates_total); 逐行看:start 初始化为已计算根数;若终端重启导致 prev_calculated 大于等于总根数,则回退到倒数第 1 根重算;for 从 start 扫到 rates_total-1,把每根 K 线的 open/high/low/close 分别塞进四个 buffer;最后返回 rates_total,告诉内核下次从最新根继续。 你开 MT5 新建指标,把这四个 buffer 绑好再贴这段代码,主图就能复刻基础蜡烛。外汇和贵金属杠杆高,这类底层绘制逻辑只解决“看得见”,不涉及任何方向判断,实盘仍需自担风险。
class="type">int OnCalculate(class="kw">const class="type">int rates_total, class="kw">const class="type">int prev_calculated, class="kw">const class="type">class="kw">datetime &time[], class="kw">const class="type">class="kw">double &open[], class="kw">const class="type">class="kw">double &high[], class="kw">const class="type">class="kw">double &low[], class="kw">const class="type">class="kw">double &close[], class="kw">const class="type">long &tick_volume[], class="kw">const class="type">long &volume[], class="kw">const class="type">int &spread[]) { class=class="str">"cmt">//--- class=class="str">"cmt">//--- class="kw">return value of prev_calculated for next call class="kw">return(rates_total); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">4 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(class="kw">const class="type">int rates_total, class="kw">const class="type">int prev_calculated, class="kw">const class="type">class="kw">datetime &time[], class="kw">const class="type">class="kw">double &open[], class="kw">const class="type">class="kw">double &high[], class="kw">const class="type">class="kw">double &low[], class="kw">const class="type">class="kw">double &close[], class="kw">const class="type">long &tick_volume[], class="kw">const class="type">long &volume[], class="kw">const class="type">int &spread[]) { class=class="str">"cmt">//--- class="type">int start = prev_calculated; if(start>=rates_total) start = rates_total-class="num">1; for(class="type">int i = start; i<rates_total; i++) { CandleBuffer1[i]=open[i]; CandleBuffer2[i]=high[i]; CandleBuffer3[i]=low[i]; CandleBuffer4[i]=close[i]; } class=class="str">"cmt">//--- class="kw">return value of prev_calculated for next call class="kw">return(rates_total); } class=class="str">"cmt">//--- plot Candle