作者n33222 (满身菜味)
看板ForeignEX
标题[投稿] MQL4初学-含教学
时间Thu Jul 6 22:23:17 2017
HIHI大家好,我又再度出现了,很久没发文前阵子都待在中国,
最近很多人发文有关MT4 EA的文章,看到了一些也想提供一些撰写方式,
我自己本身不是IT出身,CODING在学MQL4之前几乎没学过,所以算是从0学起吧。
但过程我就不多加赘述了,我这篇文章将提供一般的策略如何撰写,
让你不用有求於人,也能轻松写出自己的EA,至於大神们就可以直接跳过了,
若有错误还请多指教。
在本文开始前额外插个小话题:
10/1开始MATE QUOTE将停止更新MT4,并上调MT4对外汇商售价以及维护费,
并且相对调低MT5价格,试图逼所有外汇商改用MT5,目前今年使用MT5券商,
已经增加百家,如果照这样下去,MT5的使用将会越来越大,如果有懂IT的大人,
可以改用MQL5试试看了,我还在学觉得很困难。
另外MT5为人诟病的"不可对锁"这件事情在新版本已经改善,将由外汇商自行决定
是否给投资人对锁模式。
-------------------------本文开始-----------------------------
在MQL4编辑介面新开一个EA,甚麽都不选择的情况下按下一步,
将会出现一个空白的EA编写介面包含以下三个区块:
Oninit(首次启动EA执行内部程序"只执行一次")
OnDeinit(关闭EA时执行内部程序)
Ontick(价格"每次"跳动都会执行里面的程序一次)
在这边我会先给大家一个概念,建议将所有的程序都写在这三个区块以外,
有人可能会觉得神经病那这样怎麽执行,且听老纳娓娓道来。
"所有的程序写成"子函数"并在ONTICK中调用"
"所有的程序写成"子函数"并在ONTICK中调用"
"所有的程序写成"子函数"并在ONTICK中调用"
很重要说三次
解释:有需要用到她的时候再把他叫出来,没需要用到叫他滚边睡。
曾经下载过网路上其他程式的人,不知道有没有这样的经验,
两支EA有的时候回测速度就是有一支比别支慢很多,明明同样时间和历史,
先忽略掉程序本身真的很复杂的部份,大约有70%的情况都是因为,
设计师把所有的程序全部放在ONTICK里面,不管是不是立即要用到的,
这会导致电脑程序的执行非常缓慢,假设你的程序有500行,
也就是说价格每跳动一次你就要执行一次500行的程序,非常占资源与时间,
所以谨记这点。
回到EA部分,一支EA里面最关键的几个要素是:
下单
平仓
我这次就以这两个来做示范,基本上你想要直接复制走也可以,很多IT到最後都是这样,
下单:
下单最重要的就是"条件",连接到刚刚的调用的概念,我会先做一个子函数出来,
void CheckForOpen()
{
if(
这边很多人会直接开始写下单条件)//但我不会这麽做
//我会在外面再增加一个子函数
//bool buy_rule()
//bool sell_rule()
}
为什麽会这样做呢?,当我在设定一个buyrule sell rule 当之後我的下单条件
有所更改,我不用一行一行去找,我只需要去更改这两个子函数内的条件就可以了。
额外说:如果您要问bool是甚麽void是甚麽,可以去估狗 myeatrade的网站,
上面有完整的教科文。
轮到平仓部分,当然也是用同样方式,在外面制作一个子函数来调用
void CheckForclose()
{
一样在开始下单之前设定一个
bool buy_crule() //bool後面的字不一定要跟我一样,我这只是方便我自己看
//你可以改成你自己想要的名子,中文也可以。
bool sell_crule() //同样的
}
这边得解释一下平仓这件事情,在下单中因为你场上没有任何单子,
再加上你的下单策略可能是不需要看场上单子的,
所以你不需要用到"选单子"这件事情,
但在平仓你必须先选择到你要平的部位才能进行砍单。
所以你必须有这一段选单过程:
for(int i= OrdersTotal()-1; i>=0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS))
{
if(OrderMagicNumber() == magic)
(魔术码)
提醒一下(IF里面都是两个==)
{
if(OrderSymbol()==symbol)
(商品确认)
{
if(OrderType() == OP_SELL )
(确认方向)
{
OrderClose(OrderTicket(),OrderLots(),Ask,3,White)
最後在ORDERCLOSE平仓,如果是BUY单就是Bid
如果是sell单就是Ask,这边要小心常错。
}
}
}
}
}
可能有人注意到我的for回圈中是使用i--而非mql4范例中的i++
主要是因为电脑程序的第一个数位都是从0开始,如果你今天是多张单子策略,
他平仓完第一张(在程序编号上是0),前面的单字会往前补,也就是原本编号1的单子
会变成编号0,但这时候for回圈会开始执行编号1的单子,这样一直循环下去,
导致你的仓位会有些部位没平仓到,为了解决这个问题,
最好的方式就是我从最後一张来,一路递减,这样甚麽烦恼都没有。
回到Ontick上我们要调用这两个势必也有些条件,这个条件其实就不用像刚刚那个
再额外设置一个子函数了,直接写在Ontick里即可。
void OnTick()
{
if(CalculateCurrentOrders()==0) //计算场上部位数量的子函数等下附上
CheckForOpen();
if(CalculateCurrentOrders()>=1)
CheckForClose();
//---
}
/*如果if条件式下没有{大括号},则程式只会执行条件式下一句,
所以如果有多行要执行,请加{}并左右对称。
本来我还想提到追踪止损部分,但是因为感冒打得有点累了所以下次再提吧,
最後送给大家一个简易版的程式码,可以直接复制过去的,并附上注解。
int MAGICMA = 378354385;
extern int 停利 =2900;
extern int 停损 =50;
extern int 追踪止损距离 = 500;
extern int 均线数值 = 288;
extern double Lots = 0.1;
int bars = 1;
//+--------------------------买单条件--------------------------------+
bool buy_rule()
{
double ma=iMA(NULL,0,均线数值,0,MODE_SMA,PRICE_CLOSE,0);
if(Open[0]>ma)
return (true);
else
return(false);
}
//+--------------------------卖单条件--------------------------------+
bool sell_rule()
{
double ma=iMA(NULL,0,均线数值,0,MODE_SMA,PRICE_CLOSE,0);
if(Open[0]<ma)
return (true);
else
return(false);
}
bool buy_crule()
{
double ma=iMA(NULL,0,均线数值,0,MODE_SMA,PRICE_CLOSE,0);
if(Open[0]<ma)
return (true);
else
return(false);
}
//+--------------------------卖单条件--------------------------------+
bool sell_crule()
{
double ma=iMA(NULL,0,均线数值,0,MODE_SMA,PRICE_CLOSE,0);
if(Open[0]>ma)
return (true);
else
return(false);
}
//+--------------------------平仓子涵数--------------------------------+
void CheckForClose()
{
double ma=iMA(NULL,0,均线数值,0,MODE_SMA,PRICE_CLOSE,0);
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderMagicNumber()==MAGICMA || OrderSymbol()==Symbol())
if(OrderType()==OP_BUY && buy_crule()==True)
{
OrderClose(OrderTicket(),OrderLots(),Bid,3,White) ;
}
if(OrderType()==OP_SELL && sell_crule() == True)
{
OrderClose(OrderTicket(),OrderLots(),Ask,3,White);
}
}
}
//+--------------------------开新部位--------------------------------+
void CheckForOpen()
{
double ticket;
if(buy_rule()==true && bars!=Bars)
{
ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,Bid-停损*Point,Ask+停
利*Point,"",MAGICMA,0,Blue);
bars=Bars;
}
if(sell_rule()==true && bars!=Bars)
{
ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,Ask+停损*Point,Bid-停
利*Point,"",MAGICMA,0,Red);
bars=Bars;
}
}
int OnInit()
{
bars=Bars;
return(INIT_SUCCEEDED);
}
//+--------------------------计算部位数量--------------------------------+
int CalculateCurrentOrders()
{
int orders = 0;
for(int i=0;i<OrdersTotal();i++)
{
if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
continue;
if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=MAGICMA)
continue;
orders++;
}
return(orders);
}
//关闭EA时所执行之程序
void OnDeinit(const int reason)
{
}
//+--------------------------移动停损--------------------------------+
void modify()
{
double Ma =iMA(NULL,0,均线数值,0,MODE_SMA,PRICE_CLOSE,0);
for(int i=0;i<OrdersTotal();i++)
{
if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
continue;
if(OrderType()==OP_BUY)
{
if(OrderMagicNumber() == MAGICMA )
{
if(追踪止损距离>0 )
{
if(Bid-OrderOpenPrice()>Point*追踪止损距离)
{
OrderModify(OrderTicket(),OrderOpenPrice(),Bid-追踪止
损距离*Point,OrderTakeProfit(),0,Green);
}
}
}
}
if(OrderType()==OP_SELL)
{
if(追踪止损距离>0 )
{
if((OrderOpenPrice()-Ask)>(Point*追踪止损距离))
{
OrderModify(OrderTicket(),OrderOpenPrice(),Ask+追踪止
损距离*Point,OrderTakeProfit(),0,Red);
return;
}
}
}
}
}
//+--------------------------价格跳动执行一次
--------------------------------+
void OnTick()
{
if(CalculateCurrentOrders()<1)
CheckForOpen();
if(CalculateCurrentOrders()>=1)
modify();
CheckForClose() ;
}
/*关於程序中的Bars这边做个说明,Bars在mql4中定义为当前这根k棒,
/*而我自己在设定一个自定义变数bars,在开仓时用到,
/*当bars != Bars 代表着当前这根k棒跟之前纪录的那一根不同了,
/*在ordersend之後 bars=Bars 代表着我这根k棒下过单了,避免重复开单,
/*每更新一根k棒才会下单。
这只是一个很简易的模组版本ea,如果有比较深入的想了解,
可以把问题来信,我看我有没有办法解决,如果无法就说抱歉了,
下次文章:马丁写法。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 220.137.10.223
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/ForeignEX/M.1499351000.A.122.html
※ 编辑: n33222 (220.137.10.223), 07/06/2017 22:31:29
1F:推 henry46277: 子涵数的涵 写错 应该是函 07/06 22:37
不好意思,电脑选字忘记改,已修正
※ 编辑: n33222 (220.137.10.223), 07/06/2017 22:40:46
2F:推 fritrescond: 认真推 07/06 22:54
※ 编辑: n33222 (220.137.10.223), 07/06/2017 23:04:21
3F:推 VCLee: 推.. 07/07 00:10
4F:推 ary9872: 感谢撰文分享 07/07 02:11
5F:推 wwchocolate: 推认真 07/07 03:03
6F:推 liker: 推 07/07 07:04
7F:推 ducati5566: 推! 07/07 08:18
8F:推 hobbylin: 推 07/07 08:21
9F:推 willy84222: 推 07/07 08:53
10F:推 angrywater: 推一下,谢分享 07/07 13:08
11F:推 Theseus914: 推,对新手很有帮助,感谢 07/08 13:11
12F:→ yzfr6: 函数、函式即可,不需要【子】 07/09 01:00
13F:推 Hermanda: 推 超专业 07/09 21:15
14F:推 liuwen: 推推 07/11 22:18
15F:推 roger2232: 感谢分享 07/15 03:11