发新话题
打印

[指标EA编写讨论] mt4编程代码讲解篇

本主题由 xfxyldj 于 2008-4-5 03:47 加入精华 本主题被作者加入到个人文集中 所需阅读权限 1

再来一个MACD交叉信号显示!

后面只解释增加的代码部分!
附件: 您所在的用户组无法下载或查看附件

TOP

1、为这两种箭头指标增加和修改相应的预定义部分。
复制内容到剪贴板
代码:
#property indicator_buffers 5
#property indicator_color4 Yellow
#property indicator_color5 Blue
#property indicator_buffers 5  //指标数量由3增加到5
  为这两种箭头设置预定义颜色
  #property indicator_color4 Yellow
  #property indicator_color5 Blue[/code]
2、定义存放这两种箭头的变量数组。
复制内容到剪贴板
代码:
double UP[];
double DO[];
UP[]存放向上箭头变量 DO[]存放向下箭头变量。
3、为这两个箭头指标在int()函数中设置初始化属性。
复制内容到剪贴板
代码:
   SetIndexStyle(3,DRAW_ARROW);
   SetIndexStyle(4,DRAW_ARROW);
   SetIndexArrow(3,233);
   SetIndexArrow(4,234);
   SetIndexBuffer(3,UP);
   SetIndexBuffer(4,DO);
   SetIndexLabel(3,"BUY_SIGNAL");
   SetIndexLabel(4,"SELL-SIGNAL");
SetIndexArrow(3,233);//设置箭头的样式。3是索引,233是向上的箭头 234是向下的箭头。
   SetIndexArrow()函数定义形式。
   void SetIndexArrow( int index, int code)

   Sets an arrow symbol for indicators line of the DRAW_ARROW type.
   Arrow codes out of range 33 to 255 cannot be used.

   Parameters:
   index   -   Line index. Must lie between 0 and 7.
   code   -   Symbol code from Wingdings font or Array constants.

4、在主函数start()为这两个指标负值。
复制内容到剪贴板
代码:
for(i=0; i<limit; i++)
     {
      if (Buffer1[i+0]>Buffer2[i+0]  &&  Buffer1[i+1]<Buffer2[i+1]) UP[i]=Buffer2[i];
      if (Buffer1[i+0]<Buffer2[i+0]  &&  Buffer1[i+1]>Buffer2[i+1]) DO[i]=Buffer2[i];
      if (Buffer1[i+0]>Buffer2[i+0]  &&  Buffer1[i+1]==Buffer2[i+1]  &&  Buffer1[i+2]<Buffer2[i+2]) UP[i]=Buffer2[i];
      if (Buffer1[i]<Buffer2[i]  &&  Buffer1[i+1]==Buffer2[i+1]  &&  Buffer1[i+2]>Buffer2[i+2]) DO[i]=Buffer2[i];
     }
这两个指标的取值由MACD指标的两条曲线的相互关系决定。
    就是看相邻两点或三个点之间的大小关系(有相等点的时候就要用3点判断)
   就是 0,1,2三点两条线数值的大小。
   不明白的把上面判断条件的i去掉然后画画相互关系关系就明了了。
   虽然MACD看着是两条线,实际是每条K线形成一个点。然后把所有点用光滑线连接起来的。
   当条件满足的时候就将 Buffer2的值赋予相应的指标数组(UP或者DO)。这样mt4就在Buffer2的相应位置画一个箭头。
   对于不满足的位置 就不会对UP[]和DO[]赋值,这时UP[]和DO[]的值默认为空,mt4就不会做任何操作(什么都不画)。
ok这个指标就这样了。

TOP

继续完善这个MACD

如果你挂过上面的指标你就会发现假突破的箭头不会消失。因为没有赋空值语句。
那么我们加上去。需要增加两行代码。
UP[ i ]=EMPTY_VALUE;
DO[ i ]=EMPTY_VALUE;
for语句循环体变成如下。
复制内容到剪贴板
代码:
   for(i=0; i<limit; i++)
     {
      UP[i]=EMPTY_VALUE;
      DO[i]=EMPTY_VALUE;
      if (Buffer1[i]>Buffer2[i]  &&  Buffer1[i+1]<Buffer2[i+1])
      UP[i]=Buffer2[i];
      if (Buffer1[i]<Buffer2[i]  &&  Buffer1[i+1]>Buffer2[i+1])
      DO[i]=Buffer2[i];
      if (Buffer1[i]>Buffer2[i]  &&  Buffer1[i+1]==Buffer2[i+1]  &&  Buffer1[i+2]<Buffer2[i+2])
      UP[i]=Buffer2[i];
      if (Buffer1[i]<Buffer2[i]  &&  Buffer1[i+1]==Buffer2[i+1]  &&  Buffer1[i+2]>Buffer2[i+2])
      DO[i]=Buffer2[i];
     }
EMPTY_VALUE是mt4内置常量,表示空值。没有任何值当然就不会画箭头了。

现在我们希望还能有个报警最好。
对于报警只需要判断最后三个点的位置关系。代码如下。
复制内容到剪贴板
代码:
if (Buffer1[0]>Buffer2[0]  &&  Buffer1[1]<Buffer2[1])
   Alert("MACD金叉");
   if (Buffer1[0]<Buffer2[0]  &&  Buffer1[1]>Buffer2[1])
   Alert("MACD死叉");
   if (Buffer1[0]>Buffer2[0]  &&  Buffer1[1]==Buffer2[1]  &&  Buffer1[2]<Buffer2[2])
   Alert("MACD金叉");
   if (Buffer1[0]<Buffer2[0]  &&  Buffer1[1]==Buffer2[1]  &&  Buffer1[2]>Buffer2[2])
   Alert("MACD死叉");
这几行代码添加在上面for循环体的外面。一定不要放在上面的循环体内。
Alert()是内置的报警函数。当执行这条语句的时候会弹出报警窗口,并发出叮的声音报警。
这个函数的参数是字符串变量,或者常量。

[ 本帖最后由 xfxyldj 于 2007-9-17 10:31 编辑 ]

TOP

MT4自带的MACD交易程序分析。

接下来我们研究下mt4自带的MACD交易系统。
分析下其流程。
我们先来确认下其交易的规则。

入场点规则。
一、买单入场规则
     1、MACD快线在零轴下方3个点(也就是0轴下方金叉)。
     2、MACD金叉(MACD参数12,26,9)。
     3、MA26均线方向向上。  
二、卖单入场规则
     1、MACD快线在零轴上方3个点(也就是0轴上方金叉)。     
     2、MACD死叉(MACD参数12,26,9)。
     3、MA26均线方向向下。  
出场规则。
一、止赢止损出场。
二、买单出场条件
    1、MACD在零轴上方死叉
    2、并且MACD快线在零轴上方2个点
三、卖单出场条件
    1、MACD在零轴下方金叉
    2、并且MACD快线在零轴下方2个点
并有移动止损功能。

[ 本帖最后由 xfxyldj 于 2007-9-19 03:03 编辑 ]
附件: 您所在的用户组无法下载或查看附件

TOP

逐条解释这个EA代码。
先定义几个用户变量并赋默认值。(加载EA的时候用户可修改的)
复制内容到剪贴板
代码:
extern double TakeProfit = 50;  //止赢点数变量  初值50点
extern double Lots = 0.1;       //仓位的大小,  初值0.1
extern double TrailingStop = 30;//移动止损点数  初值30
extern double MACDOpenLevel=3;  //开仓对MACD数值的过滤 初值3个点
extern double MACDCloseLevel=2; //平仓对MACD数值的过滤 初值2个点
extern double MATrendPeriod=26; //过滤移动平均线的参数  初值26。
这部分在代码中作了解释。
接下来我们看下对于信号判断前的准备
复制内容到剪贴板
代码:
double MacdCurrent, MacdPrevious, SignalCurrent;
   double SignalPrevious, MaCurrent, MaPrevious;
   int cnt, ticket, total;
   if(Bars<100)
     {
      Print("bars less than 100");
      return(0);  
     }
   if(TakeProfit<10)
     {
      Print("TakeProfit less than 10");
      return(0);  // check TakeProfit
     }
   MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0);
   MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
   SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);
   SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);
   MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
if(Bars<100)
     {
      Print("bars less than 100");
      return(0);  
     }
这个判断的作用是,如果当前图表的K线条数小于100
则1、Print("bars less than 100");//输出bars less than 100字符串。
      Print( ...) 函数用来输出一个字符串,也就是一句话。
  2、return();这个语句结束这个主函数,后面的程序代码不再执行。
if(TakeProfit<10)
     {
      Print("TakeProfit less than 10");
      return(0);  // check TakeProfit
     }
这个判断是如果止赢小于10则 输出TakeProfit less than 10 并推出主函数。
这个程序要求止赢大于10,也就是禁止拔头皮吧。
    MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0);
   MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
   SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);
   SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);
   MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
   MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
这个6条语句取得了MACD两条线及26号均线的当前值,和前一点的数值。
    MacdCurrent=MACD快线的当前值。
   MacdPrevious=MACD快线的前一点值。
   SignalCurrent=MACD慢线的当前值。
   SignalPrevious=MACD慢线的前一点值。
   MaCurrent=26EMA均线的当前值。
   MaPrevious=26EMA均线的前一点值。
iMACD()内置指标函数前面介绍过。这里不再重复。下面详细说下iMA()内置指标函数
看下函数的定义。
double iMA( string symbol, int timeframe, int period, int ma_shift, int ma_method, int applied_price, int shift)
Calculates the Moving average indicator and returns its value.
Parameters:
symbol   -   Symbol the data of which should be used to calculate indicator. NULL means the current symbol.
timeframe   -   Timeframe. It can be any of Timeframe enumeration values. 0 means the current chart timeframe.
period   -   Averaging period for calculation.
ma_shift   -   MA shift. Indicators line offset relate to the chart by timeframe.
ma_method   -   MA method. It can be any of the Moving Average method enumeration value.
applied_price   -   Applied price. It can be any of Applied price enumeration values.
shift   -   Index of the value taken from the indicator buffer (shift relative to the current bar the given amount of periods ago).
简单翻译下。(我挂着金山词霸翻译,并不是原味的翻译,英文好的看上面的原文)
symbol   -   货币标识 (NULL表示使用当前图表的货币标识)
timeframe   - 时间周期,0表示使用的是图表的当前周期(小时图,日图等选择).
period   -   均线周期,这里使用了用户变量MATrendPeriod=26.
ma_shift   -  均线平移。0表示不平移.
ma_method   -   均线模式(这里使用了EMA模式“MODE_EMA”)一共有4种模式。添加均线的时候可以看到。.
applied_price   -   应用的价格模式。这里用的是收盘价“PRICE_CLOSE”。.
shift   -   索引号。0当前这条,1向前数1条,依此类推).
基本的准备就绪了。
附件: 您所在的用户组无法下载或查看附件
本帖最近评分记录
  • 学汇做人 威望 +1 一分虽不多予以致敬意,支持继续 偶想学写EA ... 2007-9-22 21:38

TOP

下单

现在解决下第一部分。下单操作。
复制内容到剪贴板
代码:
total=OrdersTotal();
   if(total<1)
     {
      if(AccountFreeMargin()<(1000*Lots))
        {
         Print("We have no money. Free Margin = ", AccountFreeMargin());
         return(0);  
        }
      if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
         MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious)
        {
         ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
         if(ticket>0)
           {
            if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice());
           }
         else Print("Error opening BUY order : ",GetLastError());
         return(0);
        }
      if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
         MacdCurrent>(MACDOpenLevel*Point) && MaCurrent<MaPrevious)
        {
         ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,Bid-TakeProfit*Point,"macd sample",16384,0,Red);
         if(ticket>0)
           {
            if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("SELL order opened : ",OrderOpenPrice());
           }
         else Print("Error opening SELL order : ",GetLastError());
         return(0);
        }
      return(0);
     }
分步拆解下。
total=OrdersTotal();//取得已成交的订单数。OrdersTotal()函数返回总订单数。
if(total<1)这个判断语句检查下现存的订单数。小于1就是没有订单。然后再判断是否需要下单。
复制内容到剪贴板
代码:
if(AccountFreeMargin()<(1000*Lots))
        {
         Print("We have no money. Free Margin = ", AccountFreeMargin());
         return(0);  
        }
这个判断体检查账户资金是否足够。AccountFreeMargin()函数返回账户的可用保证金额度。
如果其值小于 仓位乘以1000的值说明资金不够。
接下来是买卖订单的建立。因为是对称的,只分析买入订单的代码部分。
复制内容到剪贴板
代码:
if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
         MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious)
        {
         ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
         if(ticket>0)
           {
            if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice());
           }
         else Print("Error opening BUY order : ",GetLastError());
         return(0);
        }
首先判断当前价格下是否有买入信号产生。
if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious &&
         MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious)

这个if语句,MACD快线当前值小于0,&& MACD快线当前值大于MACD慢线当前值 && MACD快线前一点值小于MACD慢线前一点值。(这两个语句即判断了一个交叉。) && MACD快线当前值的绝对值大于3个点差。(Point,mt4预定以变量。其值是当前货币的小数位。例如英镑的Point=0.0001,日元的Point=0.01) && MA26均线方向向上。(后一点大于前一点)。
MathAbs()是取绝对值函数。这个位置也可以用MacdCurrent<MACDOpenLevel*Point 代替。
如果所有的条件都成立则执行下单函数。OrderSend().Mt4的所有订单都是调用这个函数实现的。挂Stop、Limit单以及即时订单。全部由这个函数完成。
如果下单成功则返回订单号,如果不成功返回-1。
程序把其返回值赋给ticket,接着判断ticket的值来检查是否成功。
GetLastError()函数返回错误代码。
看下OrderSend()函数定义。参数比较多。
int OrderSend( string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)
参数意义:
symbol   -   货币标识.
cmd   -   订单类型(OP_BUY=0,OP_SELL=1,OP_BUYLIMIT=2,OP_SELLLIMIT=3,OP_BUYSTOP=4,OP_SELLSTOP=5
volume   -   头寸的大小。(多少手).
price   -   开仓价格.
slippage   -   允许的最大滑点数(有的时候价格波动比较快,这个参数决定开仓价格的波动范围,price正负slippage)
stoploss   -   止损价格,0表示不设.
takeprofit   -   止赢价格,0表示不设.
comment   -   为订单加一个注释。字符串类型的,“”双括号表示不加.
magic   -   为订单加一个子定义的标识好,正整数。对这个订单的后续的处理需要用到。.
expiration   -   过期时间,(仅挂单需要)
arrow_color   -   在图表上标记此订单的箭头颜色,可不加。

下了订单后检查这个订单
OrderSelect()函数用于选取已存在的订单。函数定义如下。
OrderSelect( int index, int select, int pool=MODE_TRADES)
参数说明
index   -   订单的索引号.
select   -   选择索引号的类型。两个参数可选SELECT_BY_POS或SELECT_BY_TICKET常用后一个。
pool   -   这个参数不知道什么意思,一般不用。可选参数两个MODE_TRADES,MODE_HISTORY
             英文说明:Optional order pool index. Used when the selected parameter is SELECT_BY_POS. It can be any of the following values:
MODE_TRADES (default)- order selected from trading pool(opened and pending orders),
MODE_HISTORY - order selected from history pool (closed and canceled order).
英语差,请见谅!
如果找到了订单则输出订单的标识号吗。
否则输出错误号码。
OK差不多了。啰里啰唆的。希望能看明白。

[ 本帖最后由 xfxyldj 于 2007-9-25 06:41 编辑 ]

TOP

非常好,不知是否可以在MACD交叉信号显示版中加柱状颜色变化。谢谢!

TOP

这是我想要的结果,代码已改好,多谢!

[ 本帖最后由 dolphin 于 2007-9-26 14:15 编辑 ]
附件: 您所在的用户组无法下载或查看附件

TOP

实在对不起,最近事情很多。帖子更新慢了。
今天分析下最后一段。
这一段if镶嵌比较多。
用代码拆分的方式来分析。
全代码
for(cnt=0;cnt<total;cnt++)
     {
      OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
      if(OrderType()<=OP_SELL &&   // check for opened position
         OrderSymbol()==Symbol())  // check for symbol

        {
         if(OrderType()==OP_BUY)   // long position is opened
           {

            if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
               MacdCurrent>(MACDCloseLevel*Point))
                {
                 OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
                 return(0); // exit
                }
            if(TrailingStop>0)  
              {                 
               if(Bid-OrderOpenPrice()> Point*TrailingStop)
                 {
                  if(OrderStopLoss()<Bid-Point*TrailingStop)
                    {
                     OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
                     return(0);
                    }
                 }
              }
           }
         else
           {

            if(MacdCurrent<0 && MacdCurrent>SignalCurrent &&
               MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
              {
               OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
               return(0);
              }

            if(TrailingStop>0)  
              {                 
               if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
                 {
                  if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                    {
                     OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                     return(0);
                    }
                 }
              }
           }
        }
     }
这是全部的代码。这部分代码主要是处理移动止损和关仓操作。
代码最外围的是for语句用来遍历全部未了解的订单。并进行相应的判断。
for(cnt=0;cnt<total;cnt++)
     {
     }这个代码个人觉得不好。这样搜索有时候会出错。最好的语句个人认为是for(cnt=total;cnt>=0;cnt--)
因为在递增搜索的过程中如果有订单被止损或取消会影响语句的执行。
for语句内的分为两部分。主要是针对买单和卖单的处理。
首先要挑选出符合条件的买卖单然后进行操作。
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if(OrderType()<=OP_SELL && // check for opened position
OrderSymbol()==Symbol()) // check for symbol
{
}

这部分代码实现订单的选择。
OrderType()<=OP_SELL 代码限定了被选中的是已经成交的订单OP_SELL=1因此使这个语句成立的只有OP_BUY和OP_SELL两种类型的订单。
OrderType()函数返回订单的类型。判断的订单是被OrderSelect()选中的订单。
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);语句选择订单列表中编号为cnt的订单。
OrderSymbol()==Symbol()代码用来判断是否是要操作的货币兑。OrderSymbol()函数返回订单的货币标示。也是对OrderSelect()选中的订单进行操作。
接下来是对不同订单进行操作,也就是分买卖订单进行操作。
if(OrderType()==OP_BUY)   // long position is opened
           {
           }
else
           {
           }
前面已经对订单类型进行了筛选。因此这里如果(if)不是买单(OP_BUY)就是(else)卖单(OP_SELL)。
接下来分析一个买单的操作。卖单的类似。
对卖单的操作有两个。关仓和移动止损。
关仓(平仓)代码
if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious &&
               MacdCurrent>(MACDCloseLevel*Point))
                {
                 OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
                 return(0); // exit
                }
他的判断代码类似于建仓的判断。这里用到了OrderClose()函数。对于这些内置函数后面帖子将详细说明。各位先看下系统的帮助了解下。
接下来是移动止损的操作部分
if(TrailingStop>0)  
              {                 
               if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
                 {
                  if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                    {
                     OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                     return(0);
                    }
                 }
              }

这里面镶嵌了3个if的判断语句。
第一个TrailingStop>0移动止损的设置大于0。
第二个OrderOpenPrice()-Ask)>(Point*TrailingStop) 当前买价与开仓价格之差大于移动止损设置置
第三个OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0)止损价格没有被移动或者没有设置移动止损。
当三个条件都成立的时候实行修改移动止损的操作。
OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
语句来实现。OrderModify()订单修改函数。
这个程序基本就这样了。
个人认为代码里面有很多不足的地方。
以后的帖子将对一些mt4内置的函数进行一些分析讲解。
最近比较忙,实在对不起各位。

[ 本帖最后由 xfxyldj 于 2007-10-15 03:20 编辑 ]

TOP

引用:
原帖由 xfxyldj 于 2007-10-15 03:15 发表
个人认为代码里面有很多不足的地方
非常精彩!
愿听听您说说有什么“个人认为代码里面有很多不足的地方”?


[ 本帖最后由 求是 于 2007-10-15 21:20 编辑 ]

TOP

发新话题