MQL4编程入门:从基础语法到EA与自定义指标实战
简介:面向外汇交易者与编程初学者的MT4 MQL4编程入门资料,系统梳理MetaTrader 4平台内置编程语言的基础用法,帮助读者从零开始建立编写自定义指标与Expert Advisor(EA)的能力。内容以文档形式呈现,围绕自定义变量、附加变量、自定义函数、自定义数组等核心概念展开,配有代码示例与中文变量命名说明,可直接对照MetaTrader 4环境练习。资料共1个文件,类型为doc,压缩包约197KB,小巧精炼,适合快速阅读与随时查阅。页面显示已有389人学习浏览,适合初步接触MQL4、希望理解EA构建逻辑的交易者与程序化交易爱好者。通过学习本资料,可掌握变量类型定义、extern参数接口、函数编写方式以及数组索引规则,并在此基础上结合MT4预定义数组读取市场数据,为后续开发个性化交易策略打下基础。内容结构紧凑,从基础语法到实际调用逐步递进,能帮助读者降低入门门槛,更快进入自动化交易实践。
1. 学 MQL4 之前:先确认它替你解决的是哪一类盯盘问题
做外汇或者黄金交易盯盘久了,大概率会碰到这种场景:等一个关键价位,眼睛不敢离开图表,结果起身接杯水行情就冲过去了;或者深夜被一根假突破骗进,止损刚好被打掉。MT4 里的 MQL4 编程,就是把这些“必须盯着才能做”的动作自动化。它允许你写自定义指标,把开高低收、均线、MACD 按自己的想法加工成一条线;也允许你写 EA,把入场、止损、出场交给程序执行,甚至做成定时邮件提醒,把行情直接推到手机上。这套入门教程正是从零开始讲 MQL4 的变量、数组、函数、指标结构,最后落成一个完整实例。想验证策略、不想一直盯盘的人,以及刚接触编程的量化爱好者,都适合顺着章节边抄边改,比自己硬啃帮助文件快得多。
2. 变量、类型与 extern 参数:把交易条件翻译成机器指令
MQL4 程序无论是指标还是 EA,本质上都在处理一组数和一堆状态。价格是 double,均线周期是 int,要不要报警是 bool。先把类型这件事弄清楚,后面写逻辑才不会处处别扭。
2.1 四种基本类型:int、double、string、bool 怎么选
MQL4 的自定义变量在使用前必须先创建,也就是定义变量类型和名称。定义时可以同时赋初值,也可以只定义不赋值,变量名允许使用中文。
int mark; // 整型变量,变量名为 mark bool 是否报警 = true; // 逻辑型变量,赋初值 true这里有几条边界需要记住:
- int 是整数,包含负整数,均线周期、订单数量、循环计数这类整数用 int。
- double 是双精度小数,简单理解就是带小数位的数。价格、指标计算值都归它管,K 线数据 Open、High、Low、Close 全是 double。
- string 是字符串,存文本信息,指标名称、注释、自定义消息都用它。
- bool 是逻辑型,取值只有 true 和 false,也可以写成 1 和 0,1 是真,0 是假,用于开关和状态标记。
类型定义词只在创建变量时使用。单击变量已经定义过后,后面再给它赋值或取值,直接写变量名,不能再带 int、double 这些词。这个细节经常有新手在 start() 里重复写 int,编译时报“重新定义”错误。
变量命名也值得多说一句。MQL4 允许中文命名,老教程里大量使用中文变量,比如“快线”“慢线”“是否报警”,可读性确实好。但我自己的习惯是:全局参数和 extern 变量用中文或带语义的英文,局部临时变量用简短的 i、j、temp。别用 a、b、c 这类单字母做全局变量,隔两周回看代码会非常痛苦。
2.2 extern 附加变量:把参数面板变成你的控制台
extern 是 MQL4 里一个特别实用的关键字。它定义的变量和普通全局变量一样,可以在程序中使用,但多了一个能力:程序加载到图表后,用户不需要打开源码,直接在 MT4 的输入参数界面里修改它的值。
extern int 周期 = 9; // 整型附加变量,默认值 9,允许外部修改MACD(12,26,9) 大家都很熟,这里(12、26、9) 实际上就是三个附加变量的默认值。指标加载后,你会在参数列表里看到“周期”“快线”“慢线”等条目,修改后点击确认,指标就会用新参数重新计算。
把哪些变量设计成 extern,直接关系到指标好不好用。一般我会把周期、均线长度、报警阈值、邮件发送间隔这类“每次加载都想调”的参数做成 extern。程序内部临时用的计数器或中间结果,就不要用 extern,否则参数面板会被一堆无关变量塞满,使用的人反而找不到重点。extern 变量也是全局变量,但它对外暴露,普通全局变量只在程序内部可见,这是两者最大的区别。
2.3 数组:定义、赋值和下标从 0 开始的规矩
数组是一系列同类型变量的集合,可以同时存储多个数值。MQL4 数组的下标从 0 开始,这一点和大多数编程语言一致,但在交易语境里容易和 K 线编号混在一起,需要单独说几遍。
int aa[4] = {4, 3, 2, 1}; // 整型数组,4 个元素aa[0] 的值是 4,aa[1] 的值是 3,aa[2] 的值是 2,aa[3] 的值是 1。创建时可以赋初值,也可以不赋:
int bb[]; // 创建数组 bb,不设定元素个数 int cc[8]; // 创建数组 cc,8 个元素,不赋初值 int dd[3] = {9, 8, 7}; // 创建数组 dd,3 个元素并赋初值 int ee[9] = {0,1,2,3}; // 创建数组 ee,9 个元素,只给前 4 个赋初值初学阶段最容易忽略的是,数组创建之后,赋值必须一个一个来。批量赋值的写法只有创建时这一次机会。
dd[2] = 1; // 把 dd 数组第 3 个元素(下标 2)从 7 改成 1引用数组时也一次只能引用一个元素。如果你想对整列数组做操作,需要配合循环,逐个处理。这正是后面 for 和 while 循环的核心场景。另外,MQL4 的数组越界检查并不总是及时报错,有时编译能过,运行时数据错乱,排查起来很费劲,所以定义数组长度时宁可留出余量,也不要卡着数量写。
3. 行情数据读取:预定义数组和指标函数的正确打开方式
写交易程序,第一步永远是拿价格。MQL4 把 K 线数据做成了预定义数组,把实时报价做成了预定义变量,又提供了一组以 i 开头的函数做跨周期、跨品种取数。这三块内容就是行情数据的全部入口。
3.1 预定义数组:Open、High、Low、Close 的下标规则
图表上每一根 K 线都有开盘价、最高价、最低价、收盘价、成交量和时间,MQL4 用六个预定义数组直接暴露给开发者:
Open[] // 开盘价,double 型 High[] // 最高价,double 型 Low[] // 最低价,double 型 Close[] // 收盘价,double 型 Volume[] // 成交量,double 型 Time[] // K 线时间,datetime 型这几个数组最关键的规则是编号方向:右边第一根 K 线,也就是最新一根,编号为 0;第二根编号 1;第三根编号 2。从右向左数。图表上越往右的 K 线下标越小,这和我们阅读图表的习惯刚好相反。
Open[0]; // 最新一根 K 线的开盘价 High[1]; // 右侧第二根 K 线的最高价 Close[2]; // 右侧第三根 K 线的收盘价 Close[i]; // 右侧第 i+1 根 K 线的收盘价注意这是方括号,数组下标。有初学者写成 Open(0),那就是函数调用语法,在 MQL4 里直接用 Open(0) 编译都会报错。至于为什么不直接给个全是 1 的写法,原因在于循环处理历史数据时,下标 i 本身就是循环变量,从 0 到 N 正好覆盖所有 K 线,不需要额外转换。
判断价格上穿某个价位时,最经典也最抗噪的写法是拿相邻两根 K 线的最高价做对比:
if (High[1] < 1.0000 && High[0] >= 1.0000) { // 最新 K 线最高价已经站上 1.0000 }这个写法比用 Close 判断更抗噪,因为最高价覆盖了这根 K 线试探过的最远位置。反过来,下穿判断则用最低价:
if (Low[1] > 1.0000 && Low[0] <= 1.0000) { // 最新 K 线最低价已经跌破 1.0000 }只要理解 Right-to-Left 下标方向,这两段代码是整套教程里复用率最高的基础逻辑。
3.2 实时行情与交易环境:Ask、Bid、Digits、Point
K 线数组是历史,Ask 和 Bid 才是当下。Ask 是买入价,Bid 是卖出价,类型都是 double,实时变动。挂单、移动止损、判断真实报价时,EA 直接用这两个变量。
double curAsk = Ask; // 当前买入价 double curBid = Bid; // 当前卖出价注意,MT4 图表上显示的价格通常是 Bid,你在图表上看到的那条收盘价线是 Close[],不是 Ask。如果做的是收盘价策略,就用 Close[];如果是即时成交价触发,才需要考虑 Ask/Bid 的区别。
预定义变量里还有三个值得养成习惯使用的:
| 变量 | 类型 | 说明 |
|---|---|---|
| Bars | int | 当前图表 K 线总数 |
| Digits | int | 当前品种小数位,无日元币对 4,有日元币对 2 |
| Point | double | 最小价格单位,无日元币对 0.0001,有日元币对 0.01 |
Digits 和 Point 经常用于显示和计算。显示价格时如果写死小数位数,换到日元对,价格就会缺位或多位,看着像玄学错误。正确的姿势是用 DoubleToStr(Close[0], Digits),让程序自己根据当前品种决定保留几位小数。
3.3 指标函数与跨周期取数:iMA、iCustom 参数拆解
预定义数组只能拿当前图表的数据。想跨周期、跨品种取数,必须用 i 开头的一组函数。它们的基本格式一致,前两个参数是品种和周期,中间是各自特有参数,最后一位是指定位移。
iOpen(symbol, timeframe, shift) // 开盘价 iClose(symbol, timeframe, shift) // 收盘价 iHigh(symbol, timeframe, shift) // 最高价 iLow(symbol, timeframe, shift) // 最低价 iVolume(symbol, timeframe, shift) // 成交量 iTime(symbol, timeframe, shift) // K 线时间timeframe 用内置常量:PERIOD_M1、PERIOD_M5、PERIOD_M15、PERIOD_M30、PERIOD_H1、PERIOD_H4、PERIOD_D1、PERIOD_W1、PERIOD_MN1。不要自己去换算成分钟数,可读性差且容易出错。
shift 的规则和预定义数组下标一致,0 是最新 K 线。示例:
iClose("EURUSD", PERIOD_H4, 2) // 欧元兑美元 4 小时图,右侧第 3 根 K 线的收盘价 iHigh(NULL, 0, i) // 当前品种、当前周期、右侧第 i+1 根 K 线的最高价第一个参数写 NULL,表示让函数自动取当前图表品种;timeframe 写 0,表示当前图表周期。这种写法在自定义指标里特别常见,因为指标加载在哪个品种上就跑哪个品种,不需要写死。
移动平均线函数 iMA 是 7 个参数,这样拆解:
iMA(symbol, timeframe, period, ma_shift, ma_method, applied_price, shift)period 是均线周期,10 日均线的 period 就是 10。ma_shift 是均线整体左右平移的偏移量,通常写 0。ma_method 是平均模式,MODE_SMA 是简单移动平均,MODE_EMA 是指数移动平均,MODE_SMMA 是平滑移动平均,MODE_LWMA 是线性加权移动平均。applied_price 是取哪种价格,PRICE_CLOSE、PRICE_OPEN、PRICE_HIGH、PRICE_LOW、PRICE_MEDIAN 这些都是。最后一个 shift 是位置。
iMA(NULL, 0, 20, 0, MODE_EMA, PRICE_CLOSE, 2)这行代码的意思:当前品种当前周期下,以收盘价计算 20 周期指数移动平均线,取右侧第 3 根 K 线所在位置的值。在实际策略里,这个函数常用于快线和慢线的差值计算,也就是 MACD 白线的核心算法。
自定义指标的值要用 iCustom 读取,语法是:
iCustom(symbol, timeframe, 指标名称, 参数1, 参数2, ..., 指标线编号, shift)指标线编号从 0 开始。如果自定义指标只有一根线,编号写 0;多根线则依次为 0、1、2。示例:
iCustom(NULL, 0, "mymacd", 12, 26, 9, 2, 0)意思是当前图表上调用自定义指标 mymacd,给它传入 12、26、9 三个参数,取第 3 条指标线在最新位置的值。这里有一个隐蔽的坑:如果 mymacd 不存在或者参数个数不一致,iCustom 不会报错,而是静默返回 0。排查这种问题时,先确认指标文件已经编译,再核对参数个数和类型,最后在数据窗口里查看指标当前值是否正常。
4. if 判断与循环取数:从报警条件到批量赋值
策略逻辑离不开判断和循环。把“什么条件下做什么”用 if 写清楚,把“一批数据怎么处理”用循环写清楚,MQL4 的基本语法就通了。
4.1 if/else 报警逻辑:条件、嵌套与分号陷阱
if 语句的规则很直观:条件成立就执行大括号里的语句,不成立就跳过。
if (a == 1) { b = c + 1; }MQL4 里比较用两个等号==,赋值用一个等号=。有初学 EA 的人把 if(Close[0] > Open[0]) 写成 if(Close[0] = Open[0]),编译能过,但意思是把 Open[0] 的值赋给 Close[0],然后判断这个值是否为真,逻辑完全跑偏。遇到这种“明明代码没报错但行为不对”的情况,优先检查比较处是不是写成了单等号。
else 配合 if,构成两路分支:
if (条件) { // 条件成立执行这里 } else { // 条件不成立执行这里 }报警指标里最常见的需求,是防止同样的条件反复报警。典型写法是配合一个状态变量 mark:
int mark = 0; if (High[1] < 1.0000 && High[0] >= 1.0000 && mark != 1) { Alert(Symbol(), "价格向上触及 1.0000"); mark = 1; // 标记:上穿已经报过警 } if (Low[1] > 1.0000 && Low[0] <= 1.0000 && mark != 2) { Alert(Symbol(), "价格向下触及 1.0000"); mark = 2; // 标记:下穿已经报过警 }mark 初值是 0,表示“从未报警”,此时上穿和下穿都可以触发。上穿报警后 mark 变成 1,那么后面再上穿,mark != 1 的判断就为假,不再报警;但下穿仍然可以触发。反过来也一样。这个去重逻辑在实际运行中非常有用,不加的后果是价格在一个位置反复穿越时,报警弹窗把 MT4 刷到卡顿。
分号规则同样值得注意。只有语句结束才加分号,条件、大括号、小括号后面都不加分号。单行语句可以省略大括号,但为了后续扩展代码不踩坑,我一般都会写成带大括号的格式,即使内部只有一行语句。
4.2 while 与 for 循环:给数组批量赋值的两种姿势
K 线图动辄上万根,每一根都有开高低收和若干指标值。逐根手写不现实,循环就是专门解决批量处理问题的。
while 循环的规则:条件成立,就一直执行大括号里的语句,直到条件不成立。
extern int 快线 = 12; extern int 慢线 = 26; double buffer[]; int i = 0; while (i < 1000) { buffer[i] = iMA(NULL, 0, 快线, 0, MODE_EMA, PRICE_CLOSE, i) - iMA(NULL, 0, 慢线, 0, MODE_EMA, PRICE_CLOSE, i); i++; }注意事项集中在三点。第一,buffer 数组与指标线绑定,由 SetIndexBuffer 分配空间,直接赋值没问题;但如果是在普通 EA 脚本里用同样的数组,必须先调用 ArrayResize(buffer, 1000) 分配内存。第二,循环体里的 i++ 不能漏,漏掉就是死循环,MT4 会假死,只能强制重启终端。第三,循环运行 1000 次后 i 变成 1000,条件 i<1000 不成立,退出循环。此时 buffer[0] 到 buffer[999] 都存入了计算值。
for 循环与 while 逻辑完全一致,只是把三个关键步骤集中在一起:
extern int 快线 = 12; extern int 慢线 = 26; double buffer[]; for (int i = 0; i < 1000; i++) { buffer[i] = iMA(NULL, 0, 快线, 0, MODE_EMA, PRICE_CLOSE, i) - iMA(NULL, 0, 慢线, 0, MODE_EMA, PRICE_CLOSE, i); }拆开看 for 的三个部分:int i = 0 是初始变量定义,整个循环只执行一次;i < 1000 是循环条件,每次循环开始前判断;i++ 是每次循环体执行完后的步进。相比 while,for 把变量初始化、条件判断、步进写在同一行,不容易漏掉 i++,因此日常开发里 for 用得更频繁。
4.3 自定义函数:封装可复用的计算逻辑
自定义函数和数学函数本质是一回事。数学里 ft(x, y) = 3x + 2y,写成 MQL4 是:
int ft(int x, int y) { int temp; temp = 3 * x + 2 * y; return (temp); }头部第一行说明三件事:int 是函数返回值的类型,ft 是函数名,x 和 y 是参数。因为变量使用前必须先定义,所以参数也要带类型定义词 int。函数体有三句:定义局部变量 temp,计算 3x+2y 存入 temp,然后 return 结束函数并把 temp 的值返回给调用点。这段代码也可以简写成:
int ft(int x, int y) { return (3 * x + 2 * y); }函数类型必须与返回值匹配。返回整数用 int,返回小数用 double,返回字符串用 string。还有一种类型是 void,表示函数运行结束不需要返回任何值:
void ShowAlarmMessage() { Print("自定义提示消息"); }函数内部定义的变量是局部变量,只在函数运行时存在,函数结束自动注销。上例中的 x、y、temp 三个变量,在外部完全不可见。如果希望多个函数共享数据,就要把变量定义到全局区,也就是文件头部、函数外面的位置。
调用自定义函数,只需要保证函数名正确、参数个数一致、参数类型一致。ft(4, 5) 会按顺序把 4 传给 x、5 传给 y,计算后返回 22。在写自定义指标时,建议把均线差值、突破判断、资金管理这类可复用的逻辑都封装成函数,后续在多个 EA 之间迁移会轻松很多。
5. 避坑排查:新手最容易翻车的 5 个典型问题
把基本语法学会之后,真正耗时的是排错。MQL4 的编译器只检查语法错误,不检查逻辑错误。以下 5 个坑,是入门教程里最容易遇到的“现象、原因、解决”三连。
5.1 数组越界:数据错乱还是程序崩溃
现象:指标加载时日志提示数组越界,或者指标线上某几根 K 线位置突然出现巨大偏移,看起来像线断了一截。
原因:数组长度不够。索引从 0 开始,长度为 N 的数组最大合法下标是 N-1。写循环时条件如果是 i <= limit,而 limit 恰好等于数组长度,最后一次循环就访问了一个不存在的元素。指标缓冲区这类由系统托管的数组,越界访问不一定立刻报错,而是可能读到内存里其它位置的值,造成数据错乱。
解决:循环条件统一写成 i < 个数,不要用 <=。动态数组使用前用 ArrayResize 分配空间。自定义数组的大小不要卡着 K 线数量写,多留几十个元素作为余量。排错时先看日志里数组越界提示来自哪个函数,再查那个函数里对数组长度的控制。
5.2 K 线编号搞反:0 是右边最新一根,不是最老一根
现象:指标结果和手工对照完全对不上,手算这里应该已经突破,程序却没有任何动作。
原因:初学者容易把编号 0 当成图最左边的第一根 K 线,也就是最老的数据。实际上 MT4 预定义数组的编号是从右往左数的,Open[0] 是当前最新一根 K 线的开盘价,Open[1] 才是它左边的那一根。
解决:把图表坐标的“右侧是现在,左侧是历史”这个方向记死在脑子里。取当前 K 线的上一根,下标是 1,再上一根是 2。跨周期函数 iClose("EURUSD", PERIOD_H4, 2) 同样是这个规则,2 代表当前这根 K 线左侧第二根,而不是从最早数过来第二根。
5.3 SetIndexBuffer 没绑好:指标线一直不显示
现象:指标编译通过,加载到图表上却没有任何线条,也没有报错。
原因:定义了一个 double 数组,也在 start() 里给它赋了值,但 init() 里没有调用 SetIndexBuffer 把这个数组和指标线绑定。系统不知道这个数组的数据应该画在哪条线上。另一种情况是绑定了,但绑定顺序和声明不一致,比如声明了 2 条指标线,却在 init() 里先绑定第二条、后绑定第一条。
解决:检查文件头部的 #property indicator_buffers 数量,与 init() 里 SetIndexBuffer 的调用次数和顺序是否一致。检查 #property indicator_color1、indicator_color2 与线条编号是否对应。加载后如果还是不显示,把指标拖到另一个周期再拖回来强制刷新一次。
5.4 double 和 int 混用:精度丢失是最安静的坑
现象:价格和均线差偶尔差 0.0001,或者计算出的结果让人摸不着头脑,比如 5 除 2 得到 2 而不是 2.5。
原因:double 与 int 混合运算时,MQL4 会做隐式类型转换,但把小数赋给 int 变量时,小数部分直接截断,不是四舍五入。更隐蔽的是,两个 int 相除,结果仍然是 int,5/2 返回 2。交易计算里这么一截,止损位就可能偏移几个点。
解决:涉及价格的中间量一律用 double。比较或赋值前用 NormalizeDouble(value, Digits) 统一精度。int 类型只用于计数、周期参数、循环变量,不要参与价格计算。尤其是止损止盈价位,一旦被 int 截断,实盘轻仓可能看不出问题,重仓时差别就大了。
5.5 参数改了没反应:先查 IndicatorCounted 的逻辑
现象:修改 extern 变量后,指标在图表上的数值没有变化,看起来像参数修改没有生效。
原因:start() 里常见这样一行:
int limit = Bars - IndicatorCounted();这个写法的本意是跳过已经计算过的 K 线,只算新增的 K 线,提升性能。但修改 extern 参数后,“已计算”的标记不会自动失效,系统认为这些 K 线已经算过了,直接跳过,于是旧值继续保留。
解决:参数修改后重新编译一次,或者切换时间周期强制刷新。开发调试阶段建议先全量计算,也就是不依赖 IndicatorCounted(),每次 start() 都从 0 开始遍历全部 K 线。逻辑稳定后再改回增量计算,否则很容易出现“改了参数以为生效,实际用的是老数据”的乌龙。
6. 落地验证:定时邮件行情通知的完整实现与调参
这一段的代码是整套教程里最实用的一段。思路是把 MT4 当作邮件客户端,把行情变化通过 E-mail 发到自己的邮箱,再利用邮箱的短信提醒功能推送到手机,相当于让手机变成一台简易行情接收机。老教程里的这个思路到现在依然可用,而且代码足够短,适合验证你对变量、函数、判断的理解。
#property indicator_chart_window extern int 间隔分钟 = 10; // 外部可调参数,默认每 10 分钟一次 datetime NextTime = 0; // 记录下次发送时间 int start() { if (TimeLocal() > NextTime) { SendMail(Symbol() + DoubleToStr(Close[0], Digits), ""); NextTime = TimeLocal() + 间隔分钟 * 60; } return (0); }代码逻辑只有一层判断加两条语句。TimeLocal() 返回从 1970 年 1 月 1 日零时起算的秒数。NextTime 一开始是 0,TimeLocal() 必然大于 0,所以指标加载后会立即发送一次邮件。发送完成后,NextTime 被设置为当前时间加上间隔分钟乘以 60,也就是下一次触发的时间点。之后只有当电脑时间超过 NextTime,才会再次发送。
SendMail 的第一个参数是邮件正文。Symbol() 返回当前商品名称,Close[0] 是最新收盘价,DoubleToStr(Close[0], Digits) 按当前货币对的小数位格式化价格。现在手机邮箱基本都支持新邮件推送,这封邮件到达后,报价就会直接出现在锁屏通知里。
加载验证步骤:把代码保存为 .mq4 文件放入 MQL4 目录的 Indicators 文件夹,在 MT4 导航窗口右键编译。确认编译日志无报错后,把指标拖到图表,在参数框里应能看到“间隔分钟”且默认值为 10。然后在工具、选项、电子邮件里配置发件 SMTP 和收件地址,先点“测试”按钮。第一次测试常常不成功,日志页会显示错误,重启 MT4 后再测试一般就正常了,邮件发送成功后日志会显示发送成功信息。
后续想扩展,可以尝试三件事。一是把固定间隔改成突破触发,在 if 条件里加上均线交叉或者价位穿越判断,满足条件才发邮件。二是发送内容里加上 Ask 和 Bid,让手机通知直接显示可执行的买卖报价。三是把参数“间隔分钟”的范围调大,通常设置为 5 到 30 分钟之间,设置太短会频繁收到邮件,反而失去提醒作用。
这套入门材料最值得动手的地方,就是从这个短小的定时通知开始,先跑通语法,再逐步把交易判断加进去。我自己以前写指标习惯先把整体框架搭好再验证,结果第一次运行一堆小问题来回改。从那以后,不论写什么指标,我都强制自己在最小数据集上先跑一遍,确认语法、确认输出格式、确认参数可调,再逐步叠加逻辑。这个方法,希望能在你学 MQL4 的路上少走些弯路,也祝你写出的第一个指标能顺利画出第一根线。
本文还有配套的精品资源,点击获取