MT4平台上mql4实现的基于macd指标的智能交易EA
MT4平台上mql4实现的基于macd指标的智能交易EA
屌丝命苦,拼爹拼不过,拼后台没有,技术宅一枚,情商有问题,不会见人说人话见鬼说鬼话,所以在国庆熬着混着,工作也没啥大起色,想想就郁闷,难不成一辈子就只能这样了?
苦思冥想,想得一条路,那就是程序化交易--现在程序化交易正有越来越火的趋势,国外已经程序化了很久,国内各大交易所也正在半掩半遮的开展,卷商、私人公司陆陆续续都在开展,心水啊,想着先研究研究,熟了之后也是碗饭啊,不行就靠着给人写策略也能吃口饭不至于饿死吧,想想还有点小鸡冻啊。
说干就干啊,原来做过外汇的模拟盘,用过MT4软件,软件自带MQL4语言,程序化已经支持的非常好了,还带有例子,还有高大上的名字叫EA,于是咱也高大上了一把,这年头都得将B格嘛。另外,选择外汇是因为外汇受认为操控的可能性比较小,外汇交易每天几万亿美元,不是个人能左右的了的。外汇里面,EUR/USD又是流通量最大的,所以就选择这个汇对。
废话了一大堆,直接在例子上改,开始是基于均线的MASample,运行了几个礼拜,不是很好,又在MACDSample上改了改,运行了个把月,基本上盈亏平衡,也算是个好开端吧。下面就是我该的macdSample的代码。
MQL4是一个类似c的语言,很多都可以按照c的逻辑来写。我定义的一些全局变量
1 input double TakeProfit =1000;
2 input double Lots =0.1;
3 input double TrailingStop =150;
4 input double MACDOpenLevel =5;
5 input double MACDCloseLevel=3;
6 input int MATrendPeriod =60;
7 input int MATrendPeriodFast =26;
8 input double MaximumRisk =0.08;
9 input double DecreaseFactor=3;
10 input int CheckPeriod = 36;
根据历史盈亏状况改变下单数量的函数,思路是:在最大下单数量的基础上*风险系数,如果有损失,损失次数/3,直到最小下单数量。不过最近也没用这个函数,主要是函数总是在几次损失后下最小的数量单子,但是行情总是在几次震荡才启动,所以总是错失良机,现在基本上都是下固定数量。
1 //+------------------------------------------------------------------+
2 //| Calculate optimal lot size |
3 //+------------------------------------------------------------------+
4 double LotsOptimized()
5 {
6 double lot=Lots;
7 int orders=HistoryTotal(); // history orders total
8 int losses=0; // number of losses orders without a break
9 Print("value of Lots:",lot);
10 //--- select lot size
11 lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);
12 //--- calcuulate number of losses orders without a break
13 if(DecreaseFactor>0)
14 {
15 for(int i=orders-1;i>=0;i--)
16 {
17 if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false)
18 {
19 Print("Error in history!");
20 break;
21 }
22 if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)
23 continue;
24 //---
25 if(OrderProfit()>0) break;
26 if(OrderProfit()<0) losses++;
27 }
28 if(losses>1)
29 lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
30 }
31 //--- return lot size
32 if(lot<0.1) lot=0.1;
33 Print("lot:",lot);
34 return(lot);
35 }
下面的函数是判断盘整行情的,主要是因为往往美国收市后,整个市场就波动很小了,等待第二天才会开始新一轮行情。
1 //判断从第i个行情之前的一段时间内存在盘整行情
2 //盘整行情的定义:行情波动不大
3 bool hasConsolidation(int i)
4 { double macd;
5 int count,j;
6 for(j=0;j<2*CheckPeriod;j++)
7 {
8 macd=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,i+j);
9 if(MathAbs(macd)<=(MACDOpenLevel*Point))
10 {
11 count=count+1;
12 if(count>=CheckPeriod)
13 {
14 Print("isConsolidation:",2*CheckPeriod,"个周期内有连续半数以上波动过小");
15 return true;
16 }
17 }else{
18 count = 0;
19 }
20 }
21 return false;
22 }
MACD的基础理论来了,金叉和死叉的应用(关于MACD以及金叉死叉,这里不解释,请各位自行百度或者知乎),我这里判断一段时间内是否有金叉或者死叉,太长时间也没啥意义,我主要是基于5分钟线做日内交易。
1 //检查一小时内是否有金叉
2 bool CheckForGoldInHour()
3 {
4 double MacdCurrent,MacdPre,SignalCurrent,SignalPre;
5 for(int i=0;i<CheckPeriod;i++)
6 {
7 MacdCurrent=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,i);
8 MacdPre=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,i+1);
9 SignalCurrent=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,i);
10 SignalPre=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,i+1);
11
12 //在0轴下的金叉
13 if(CheckForGold(MacdCurrent,MacdPre,SignalCurrent,SignalPre))
14 return true;
15 }
16
17 return false;
18 }
19
20 bool CheckForDeathInHour()
21 {
22 double MacdCurrent,MacdPre,SignalCurrent,SignalPre;
23 for(int i=0;i<CheckPeriod;i++)
24 {
25 MacdCurrent=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,i);
26 MacdPre=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,i+1);
27 SignalCurrent=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,i);
28 SignalPre=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,i+1);
29
30 //在0轴上的死叉.
31 if(CheckForDeath(MacdCurrent,MacdPre,SignalCurrent,SignalPre))
32 return true;
33 }
34
35 return false;
36 }
37
38 bool CheckForGold(double MacdCurrent,double MacdPrevious,double SignalCurrent,double SignalPrevious)
39 {
40 if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
41 MathAbs(MacdCurrent)>(MACDOpenLevel*Point))
42 {
43 Print("在0轴下的金叉");
44 return true;
45 }
46
47 return false;
48 }
49
50 bool CheckForDeath(double MacdCurrent,double MacdPrevious,double SignalCurrent,double SignalPrevious)
51 {
52 if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
53 MacdCurrent>(MACDOpenLevel*Point))
54 {
55 Print("在0轴上的死叉");
56 return true;
57 }
58
59 return false;
60 }
所有的策略最后都归结到一点,你是看多还是看空,看多,平掉空单,开多单,看空,平掉多单,开空单,简单的逻辑。
1 double Macd0,Macd1,Macd2;
2 double Signal0,Signal1,Signal2;
3 double Ma0,Ma1,Ma2;
4 double highLevel;
5
6 // 看前三个macd值和signal,都是向上的,并且均线向上
7 bool CheckForLong()
8 {
9 Macd0=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,0);
10 Macd1=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,1);
11 Macd2=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,2);
12
13
14 Signal0=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,0);
15 Signal1=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,1);
16 Signal2=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,2);
17
18
19
20 if(Macd1>=Macd0 || Macd2>=Macd1 || Signal1>=Signal0 || Signal2>=Signal1)
21 {
22 Print("CheckForLong:macd或者signal不是向上");
23 return false;
24 }
25
26 highLevel = (Macd0+Macd1+Macd2-Signal0-Signal1-Signal2)/3;
27
28 if(highLevel<MACDOpenLevel*Point)//Macd0<=(Signal0+MACDOpenLevel*Point) || Macd1<=(Signal1+MACDOpenLevel*Point) || Macd2<=(Signal2+MACDOpenLevel*Point))
29 {
30 Print("CheckForLong:macd不满足开仓必须高于信号足够的量");
31 Print("相差",highLevel/Point,"个Point");
32 return false;
33 }
34
35 //均线变动比较慢,暂时取两个
36 Ma0=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
37 Ma1=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
38 //Ma2=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,2);
39 if(Ma0<Ma1)// || Ma1<Ma2)
40 {
41 Print("CheckForLong:均线向下,没有向上");
42 return false;
43 }
44
45 if(!CheckForGoldInHour())
46 {
47 Print("CheckForLong:有效时间内没有检测到0轴下的金叉");
48 if(hasConsolidation(0))
49 {
50 Print("CheckForLong:检测到盘整行情,现在已经确定走势,可以开仓。");
51 return true;
52 }
53 return false;
54 }
55
56 //if(Macd0>Macd1 && Macd1>Macd2 && Signal0>Signal1 && Signal1>Signal2 &&
57 // Macd0>(Signal0+MACDOpenLevel*Point) && Macd1>(Signal1+MACDOpenLevel*Point) && Macd2>(Signal2+MACDOpenLevel*Point)
58 // && Ma0>Ma1)
59 //{
60 //检查一小时内有金叉
61 // if(CheckForGoldInHour())
62 // return true;
63 //}
64
65 //Print("CheckForLong:long");
66 return true;
67 }
这里面加入了均线的判断,macd本质上就是依据两条不同步调(比如我选择的26日均线和60日均线)的均线,如果快的均线离慢均线越来越近,则代表行情可能结束。但是在实际情况中,经常会是,快均线在超过慢均线快速上涨之后,慢慢回落到慢均线,等于慢均线快接近的时候,又快速拉升,慢均线就像支撑线。但是如果仅仅就macd的理论,行情在两均线靠近时就已经要反转了,而这往往是错误的判断。所以macd往往要配合均线。我这里采用26日均线来判断趋势,对于看涨函数,只有在均线是向上的时候才会开仓。同理,下面的short判断也是要看均线向下才开仓。
bool CheckForShort()
{
Macd0=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,0);
Macd1=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,1);
Macd2=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_MAIN,2);
Signal0=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,0);
Signal1=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,1);
Signal2=iMACD(NULL,0,MATrendPeriodFast,MATrendPeriod,9,PRICE_CLOSE,MODE_SIGNAL,2);
//Ma2=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
if(Macd0>=Macd1 || Macd1>=Macd2 || Signal0>=Signal1 || Signal1>=Signal2)
{
Print("CheckForShort:macd或者signal不是向下");
return false;
}
highLevel = (Signal0+Signal1+Signal2-Macd0-Macd1-Macd2)/3;
if(highLevel<MACDOpenLevel*Point)//Signal0<=(Macd0+MACDOpenLevel*Point)
|| Signal1<=(Macd1+MACDOpenLevel*Point) ||
Signal2<=(Macd2+MACDOpenLevel*Point))
{
Print("CheckForShort:macd不满足开仓必须低于信号足够的数量");
Print("相差",highLevel/Point,"个Point");
return false;
}
//均线变动比较慢
Ma0=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
Ma1=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
//Ma2=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,2);
if(Ma0>Ma1) //|| Ma1>Ma2)
{
Print("CheckForShort:均线向上,没有向下");
return false;
}
if(!CheckForDeathInHour())
{
Print("CheckForShort:有效时间内没有检测到0轴上的死叉");
if(hasConsolidation(0))
{
Print("CheckForShort:检测到盘整行情,现在已经确定走势,可以开仓。");
return true;
}
return false;
}
//if(Macd0<Macd1 && Macd1<Macd2 && Signal0<Signal1 && Signal1<Signal2 &&
// Signal0>(Macd0+MACDOpenLevel*Point) &&
Signal1>(Macd1+MACDOpenLevel*Point) &&
Signal2>(Macd2+MACDOpenLevel*Point)
// && Ma0<Ma1)
//{
//检查一小时内有金叉
// if(CheckForDeathInHour())
// return true;
//}
//Print("CheckForShort:not short");
return true;
}
下面就是ontick函数,相当于c语言的main函数,主要是根据多空的判断来开仓和平仓,还有就是移动止损,有点复杂,但是其实也就是一句话的事,就是采用60日均线来作为移动止损点,默认止损是120点,之后取120点和60日均线的大值(要有足够的行情波动空间)。
1 //+------------------------------------------------------------------+
2 //| |
3 //+------------------------------------------------------------------+
4 void OnTick(void)
5 {
6 //double MacdCurrent,MacdPrevious;
7 //double SignalCurrent,SignalPrevious;
8 double MaCurrent;
9 int cnt,ticket,total;
10 double stopLost;
11 //---
12 // initial data checks
13 // it is important to make sure that the expert works with a normal
14 // chart and the user did not make any mistakes setting external
15 // variables (Lots, StopLoss, TakeProfit,
16 // TrailingStop) in our case, we check TakeProfit
17 // on a chart of less than 100 bars
18 //---
19 if(Bars<100)
20 {
21 Print("bars less than 100");
22 return;
23 }
24 if(TakeProfit<10)
25 {
26 Print("TakeProfit less than 10");
27 return;
28 }
29 //--- to simplify the coding and speed up access data are put into internal variables
30 //MacdCurrent=iMACD(NULL,0,26,60,9,PRICE_CLOSE,MODE_MAIN,0);
31 //MacdPrevious=iMACD(NULL,0,26,60,9,PRICE_CLOSE,MODE_MAIN,1);
32 //SignalCurrent=iMACD(NULL,0,26,60,9,PRICE_CLOSE,MODE_SIGNAL,0);
33 //SignalPrevious=iMACD(NULL,0,26,60,9,PRICE_CLOSE,MODE_SIGNAL,1);
34 MaCurrent=iMA(NULL,0,MATrendPeriod*2,0,MODE_EMA,PRICE_CLOSE,0);
35 //MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
36
37 //Print("Ask:",Ask," Bid:",Bid," diff point:",(Bid-Ask)/Point);
38
39 total=OrdersTotal();
40 if(total<1)
41 {
42 //--- no opened orders identified
43 if(AccountFreeMargin()<(1000*Lots))
44 {
45 Print("We have no money. Free Margin = ",AccountFreeMargin());
46 return;
47 }
48 //--- check for long position (BUY) possibility
49 if(CheckForLong())
50 {
51 ticket=OrderSend(Symbol(),OP_BUY,10,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
52 if(ticket>0)
53 {
54 if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
55 Print("BUY order opened : ",OrderOpenPrice());
56 }
57 else
58 Print("Error opening BUY order : ",GetLastError());
59 return;
60 }
61 //--- check for short position (SELL) possibility
62 if(CheckForShort()) //&& MaCurrent<MaPrevious)
63 {
64 ticket=OrderSend(Symbol(),OP_SELL,10,Bid,3,0,Bid-TakeProfit*Point,"macd sample",16384,0,Red);
65 if(ticket>0)
66 {
67 if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
68 Print("SELL order opened : ",OrderOpenPrice());
69 }
70 else
71 Print("Error opening SELL order : ",GetLastError());
72 }
73 //--- exit from the "no opened orders" block
74 return;
75 }
76 //--- it is important to enter the market correctly, but it is more important to exit it correctly...
77 for(cnt=0;cnt<total;cnt++)
78 {
79 if(!OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES))
80 continue;
81 if(OrderType()<=OP_SELL && // check for opened position
82 OrderSymbol()==Symbol()) // check for symbol
83 {
84 //--- long position is opened
85 if(OrderType()==OP_BUY)
86 {
87 //--- should it be closed?
88 if(CheckForShort())
89 {
90 //--- close order and exit
91 if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet))
92 Print("OrderClose error ",GetLastError());
93 return;
94 }
95 //--- check for trailing stop
96 if(TrailingStop>0)
97 {
98 if(Bid-OrderOpenPrice()>Point*TrailingStop)
99 {
100 if(OrderStopLoss()<Bid-Point*TrailingStop)
101 {
102 //当前卖价减去TrailingStop作为移动止损线
103 stopLost = Bid-Point*TrailingStop;
104 //如果移动止损值高于均线,以均线为止损线。
105 if(stopLost>MaCurrent)
106 {
107 stopLost = MaCurrent;
108 }
109
110 //--- modify order and exit
111 if(!OrderModify(OrderTicket(),OrderOpenPrice(),stopLost,OrderTakeProfit(),0,Green))
112 Print("OrderModify error ",GetLastError());
113 return;
114 }
115 }
116
117 if(Bid<OrderOpenPrice())
118 {
119
120 if((OrderOpenPrice()-Bid)>Point*TrailingStop)
121 {
122 if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet))
123 Print("OrderClose error ",GetLastError());
124 return;
125 }
126
127 if((OrderStopLoss()<OrderOpenPrice()-Point*TrailingStop)||(OrderStopLoss()==0))
128 {
129 //--- modify order and exit
130 if(!OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-Point*TrailingStop,OrderTakeProfit(),0,Green))
131 Print("OrderModify error ",GetLastError());
132 return;
133 }
134 }
135
136 }
137 }
138 else // go to short position
139 {
140 //--- should it be closed?
141 if(CheckForLong())
142 {
143 //--- close order and exit
144 if(!OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet))
145 Print("OrderClose error ",GetLastError());
146 return;
147 }
148 //--- check for trailing stop
149 if(TrailingStop>0)
150 {
151 if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
152 {
153 if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
154 {
155
156 stopLost = Ask+Point*TrailingStop;
157 if(Ask+Point*TrailingStop<MaCurrent)
158 {
159 stopLost = MaCurrent;
160 }
161
162 //--- modify order and exit
163 if(!OrderModify(OrderTicket(),OrderOpenPrice(),stopLost,OrderTakeProfit(),0,Red))
164 Print("OrderModify error ",GetLastError());
165 return;
166 }
167 }
168
169 if(OrderOpenPrice()<Ask)
170 {
171 if((Ask-OrderOpenPrice())>Point*TrailingStop)
172 {
173 if(!OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet))
174 Print("OrderClose error ",GetLastError());
175 return;
176 }
177 if((OrderStopLoss()>(OrderOpenPrice()+Point*TrailingStop)) || (OrderStopLoss()==0))
178 {
179 //--- modify order and exit
180 if(!OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+Point*TrailingStop,OrderTakeProfit(),0,Red))
181 Print("OrderModify error ",GetLastError());
182 }
183 return;
184 }
185 }
186 }
187 }
188 }
189 //---
190 }
昨天采用这套策略,抓到一个大行情,模拟盘赚了1万,实盘做的小,500美金的账号,每次下单0.01手,昨天一笔赚了10刀,把我昨天手动亏损的全赚回来了。
当时也是看模拟盘,模拟的不错,一激动入了金,结果发现,外汇还真不是好的发财门道。目前电脑做的比我好多了
稿源:勤快学QKXue.NET