若一本关于MQL语言的书籍,未能涵盖自定义指标与脚本的开发,那它无疑是不完整的。MetaTrader平台内置的指标虽多,但远非全部。幸运的是,MQL语言赋予了我们创造专属分析工具的强大能力。今天,我们将学习如何为我们的交易系统,打造独一无二的视觉化指标和便捷化脚本。
第一部分:自定义指标 —— 将你的策略“画”在图上
一个EA再智能,也只是在后台默默运算。而一个自定义指标,则能将你独特的交易逻辑、对市场的理解,直观地画在图表上,让你一目了然。
我们将亲手构建一个简单但非常实用的指标:一个以EMA为中轨的改良版布林带。为什么?因为很多交易员(包括我)都更偏爱EMA的灵敏性,而非标准布林带所使用的SMA(简单移动平均线)。我们不只是在编程,更是在升级一个经典工具。
核心概念:指标缓冲区(Buffers) —— 我们的“颜料桶”
要往图表上画线,我们首先需要准备好“颜料”。在MQL4中,指标缓冲区就是我们的“颜料桶”。
- 每个自定义指标最多可以拥有8个颜料桶(缓冲区索引号从0到7)。
- 每个桶里可以装一种“颜料”(一条指标线的数据)。
- 比如,0号桶装蓝色颜料(中轨线),1号桶装红色颜料(上轨),2号桶装绿色颜料(下轨)。
在OnInit()
函数中,我们告诉MT4每个桶里是什么颜色、画成什么样式(SetIndexStyle
),以及这个桶将由哪个数组来填装颜料(SetIndexBuffer
)。在OnTick()
(或OnCalculate
)函数中,我们的代码就是那位辛勤的“画家”,负责计算数据,并将“颜料”填入每个桶中。
第一步:使用向导,搭建指标框架
通过MetaEditor的“新建”向导,我们可以快速生成一个自定义指标的基本模板。在向导中,我们设定指标包含3条线。以下是向导生成的初始代码(为简洁起见,暂时省略了 start() 函数部分):
#property copyright "tudoajiaoyi" // 版权声明
#property link "http://www.eamql5.com" // 开发者链接
#property indicator_chart_window // 指示指标在主图表窗口显示
#property indicator_buffers 3 // 指示指标使用3个缓冲区
#property indicator_color1 DeepSkyBlue // 第1条线的颜色
#property indicator_color2 DeepSkyBlue // 第2条线的颜色
#property indicator_color3 DeepSkyBlue // 第3条线的颜色
//---- 定义指标缓冲区数组
double ExtMapBuffer1[]; // 第1个缓冲区数组 (稍后会重命名)
double ExtMapBuffer2[]; // 第2个缓冲区数组
double ExtMapBuffer3[]; // 第3个缓冲区数组
int init()
{
//---- 指标设置
SetIndexStyle(0, DRAW_LINE);
SetIndexBuffer(0, ExtMapBuffer1);
// 设置第1号缓冲区的绘图风格为直线,并将其与ExtMapBuffer2数组绑定
SetIndexStyle(1, DRAW_LINE);
SetIndexBuffer(1, ExtMapBuffer2);
// 设置第2号缓冲区的绘图风格为直线,并将其与ExtMapBuffer3数组绑定
SetIndexStyle(2, DRAW_LINE);
SetIndexBuffer(2, ExtMapBuffer3);
//----
return(0); // 初始化成功
}
#property
indicator_c
hart_window
: 告诉MT4,把指标画在主图表上。如果要画在下方的副图(像MACD那样),则用#property indicator_separate_window
。#property
indicator
_buffers
3
: 声明我们将使用3个“颜料桶”。SetInde
xBuffer(0,
ExtMapBuffer1)
: 这是核心,它将0号“颜料桶”和ExtMapBuffer1
这个数组“绑定”在了一起。未来,我们往ExtMapBuffer1
数组里填的数据,就会自动显示为0号桶所代表的那条线。
第二步:为“颜料桶”取个好名字,并添加说明
向导生成的ExtMapBuffer1
这种名字毫无意义。一个专业的开发者,会立刻给它们换上具有描述性的名称,并用SetIndexLabel()
为每条线添加“标签”,方便使用者(也方便未来的自己)理解。
//---- 定义指标缓冲区数组
double EMA[]; // 用于存储指数移动平均线 (中轨)
double UpperBand[]; // 用于存储布林带上轨
double LowerBand[]; // 用于存储布林带下轨
int init()
{
// 设置第0号缓冲区 (EMA - 中轨)
SetIndexStyle(0, DRAW_LINE); // 绘制为直线
SetIndexBuffer(0, EMA); // 绑定到EMA数组
SetIndexLabel(0, "EMA (中轨)"); // 设置数据窗口标签
// 设置第1号缓冲区 (UpperBand - 上轨)
SetIndexStyle(1, DRAW_LINE);
SetIndexBuffer(1, UpperBand);
SetIndexLabel(1, "上轨");
// 设置第2号缓冲区 (LowerBand - 下轨)
SetIndexStyle(2, DRAW_LINE);
SetIndexBuffer(2, LowerBand);
SetIndexLabel(2, "下轨");
//----
return(0); // 初始化成功
}
学长建议:清晰的命名和标签,是你为自己的指标写的最好的“说明书”。当其他EA要通过iCustom()
函数调用你的指标数据时,正是通过缓冲区索引号(0, 1, 2)来获取对应数据的。你的清晰标签,能让调用者一目了然,0号是中轨,1号是上轨。
第三步:核心计算逻辑 —— start()
函数与增量计算
指标的核心计算,都发生在start()
函数(在新版MQL中也叫OnCalculate()
)中。
IndicatorCounted()
—— 聪明的画家 想象一下,一幅有2000根K线的壁画,每次价格跳动一下,我们就要把整幅画全部重画一遍,这是极其低效的。IndicatorCounted()
函数就是让我们的“画家”变聪明的关键,它会告诉我们:“这幅画已经画了多少根K线了?”。这样,我们的代码就只需要计算和绘制那些新出现的、尚未被画过的K线,极大地提升了效率。这就是增量计算。
int counted_bars = IndicatorCounted();
int limit;
if(counted_bars > 0)
{
counted_bars--;
}
limit = Bars - counted_bars - 1;
for(int i = limit; i >= 0; i--)
{
// 在此执行指标的具体计算逻辑,并将结果存入对应的缓冲区数组,例如 EMA[i], UpperBand[i] 等
}
第四步:填充计算逻辑,并使其“可定制”
现在,我们在循环中,调用MQL4内置的指标函数,来计算我们需要的值,并填入“颜料桶”。更进一步,我们将所有关键参数都设置为extern
外部变量,让用户可以自由定制。
// 外部输入参数
extern int BandsPeriod = 20; // 布林带周期
extern int BandsShift = 0; // 均线位移
extern int BandsMethod = MODE_EMA; // 均线计算方法
extern int BandsPrice = PRICE_CLOSE;
extern double Deviations = 2.0;
// 在 start() 函数或 OnCalculate() 函数中:
// (counted_bars 和 limit 的计算如前所述)
for(int i = limit; i >= 0; i--)
{
// 计算EMA (或其他类型的均线)
EMA[i] = iMA(NULL, 0, BandsPeriod, BandsShift, BandsMethod, BandsPrice, i);
// 计算标准差
double StdDev = iStdDev(NULL, 0, BandsPeriod, BandsShift, BandsMethod, BandsPrice, i);
// 计算上下轨,并应用标准差倍数
UpperBand[i] = EMA[i] + (StdDev * Deviations);
LowerBand[i] = EMA[i] - (StdDev * Deviations);
}
至此,一个功能强大、高度可定制的、远胜于平台自带的“EMA布林带”指标就诞生了!
第二部分:脚本(Scripts) —— 交易员的“一键宏”
脚本是一种“召之即来,挥之即去”的一次性MQL程序。当它被加载到图表上时,会自动执行一次,然后就结束使命。
它就像是为我们交易员量身定做的“快捷键”或“宏命令”,专门用于自动化处理那些繁琐、重复性的操作:
- 一键平掉所有盈利的订单。
- 一键将所有持仓单设置到保本损。
- 一键删除所有挂单。
脚本的“启动确认”
标签为了规范脚本的执行,我们通常会在脚本开头,加入下面两个预处理指令之一:
#property
show_con
firm
: 如果你的脚本是“一键平仓”这种不可逆的危险操作,用它。执行前会弹窗,让你“确认执行?”。#prope
rty
show_inputs
: 如果你的脚本需要用户临时输入参数(比如一个“快速挂单”脚本需要用户输入价格和手数),用它。执行前会自动弹出参数设置窗口。
#property show_confirm // 确认提示框
#property show_inputs // 参数设置对话框
脚本的生命周期非常简单:init()
-> start()
-> deinit()
,三个函数都只执行一次。并且,同一个图表在同一时间,只能运行一个脚本。所有脚本文件,都存放在\experts\scripts
子文件夹中。
评论前必须登录!
立即登录 注册