MQL5教程 04 实战开发预备知识、脚本开发实战、指标开发基础

一、实战开发预备知识

1、基本概念

(1)在交易函数中,Position开头的函数为市价单函数,Order开头的函数为挂单函数,History开头的函数为历史单函数。

(2)comment为订单注释,magic(幻数)为订单ID,可以用设定它们来区分订单。

(3)挂单交易中,stop为挂单价格在盈利方向,limit为挂单价格在亏损方向。例如挂买单:当 挂单价格 > ASK时,用stop方式;当 挂单价格 < ASK时,用limit方式。

(4)MqlTradeRequest(交易请求结构体)作用是在发起交易时,向服务器提交的请求,需要装在该结构体中。
   MqlTradeResult(交易请求结果结构体)作用是在发起交易时,服务器对交易请求响应,返回结果装在该结构体中。
   以下是这两个结构体的一些编号属性的解释:
   市价单交易:

在这里插入图片描述


在这里插入图片描述


   挂单交易:

在这里插入图片描述

2、常用取值方法

(1)获取bid、ask值

int OnStart()
  {
   MqlTick A[];
   
   // 数组A的索引顺序默认从过去到现在,
   // 该句代码可以将索引顺序改为从现在到过去
   ArraySetAsSeries(A, true);
   
   // 将ticks值复制到数组A中
   CopyTicks(NULL,A);
   printf(DoubleToString(A[0].bid,Digits()));
   printf(DoubleToString(A[0].ask,Digits()));
   
   return(1);
  }

(2)获取K线高开低收价和开始时间

int OnStart()
  {
   MqlRates rates[];
   ArraySetAsSeries(rates, true);
   CopyRates(NULL,PERIOD_CURRENT,0,100,rates);
   
   printf(DoubleToString(rates[1].open,Digits()));
   printf(DoubleToString(rates[1].close,Digits()));
   printf(DoubleToString(rates[1].high,Digits()));
   printf(DoubleToString(rates[1].low,Digits()));
   printf(TimeToString(rates[1].time));
   
   return(1);
  }

(3)获取系统指标值

int OnStart()
  {
   double buffer[];
   ArraySetAsSeries(buffer,true);
   
   // 获取MA指标句柄
   int ma_h = iMA(NULL,PERIOD_CURRENT,10,0,MODE_SMA,PRICE_CLOSE);
   CopyBuffer(ma_h,0,0,10,buffer);
   printf(DoubleToString(buffer[0],Digits()));
   
   return(1);
  }

(4)一根K线只开一个单

这里只演示开多单。

datetime ot;
int OnInit()
  {
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   
  }

void OnTick()
  {
   datetime t[];
   ArraySetAsSeries(t, true);
   CopyTime(NULL,PERIOD_CURRENT,0,2,t);
  
   if(ot!=t[0])
     {
      // 结构体初始化
      MqlTradeRequest request = {};
      MqlTradeResult  result  = {};
      
      request.action = TRADE_ACTION_DEAL; // 交易操作类型
      request.symbol = Symbol();          // 交易品种
      request.volume = 0.01;              // 0.01手交易量
      request.type   = ORDER_TYPE_BUY;    // 订单类型
      request.price  = SymbolInfoDouble(Symbol(),SYMBOL_ASK); // 持仓价格
      request.deviation = 100;            // 允许滑点数
      request.magic  = 123;               // 订单幻数
      
      if(OrderSend(request, result))      // 执行交易操作操作
        {
         Print(result.deal);  // 交易成功后,返回订单号码
        }
        
      ot = t[0];
     }
  }

(5)获取市价单信息

方法一(推荐):

int OnStart()
  {
   int pt = PositionsTotal(); // 获取市价单总数
   for(int i=0; i<pt; i++)
     {
      // PositionGetTicket()不仅会获取订单号,还有预选功能
      // 订单被预选后,才能进行后续操作
      // PositionGetTicket()获取失败,则返回0
      // PositionGetTicket()的参数为索引值,顺序是按时间从过去到现在
      if(PositionGetTicket(i)>0)
        {
         // PositionGetTicket()预选后,方可使用PositionGetDouble()函数
         double op = PositionGetDouble(POSITION_PRICE_OPEN);
         Print(op);
        }
     }
   
   return(1);
  }

方法二:

int OnStart()
  {
   // PositionSelect()为预选订单,通常是选择最早开立的订单
   if(PositionSelect(Symbol()) == true)
     {
      double op = PositionGetDouble(POSITION_PRICE_OPEN);
      Print(op);
     }
   
   return(1);
  }

(6)获取挂单信息

方法一(推荐):

int OnStart()
  {
   int ordertotal = OrdersTotal();
   for(int i=0; i<ordertotal; i++)
     {
      if(OrderGetTicket(i)>0)
        {
         double op = OrderGetDouble(ORDER_PRICE_OPEN);
         Print(op);
        }
     }
   
   return(1);
  }

方法二:

int OnStart()
  {
   if(OrderSelect(150578850873) == true)
     {
      double op = OrderGetDouble(ORDER_PRICE_OPEN);
      Print(op);
     }
   
   return(1);
  }

(7)获取历史订单信息

方法类似上两小节。
需要注意的是,历史订单分成交订单等模式,需要使用对应的函数来获取信息。

二、脚本开发实战

1、给脚本设置快捷键

在MT5导航栏中,选定脚本,鼠标右击 → 设置热键

2、运行时显示输入参数界面

// 运行时显示输入参数界面,脚本默认不显示
#property script_show_inputs

input double lots = 0.1;

int OnStart()
  {
   return(1);
  }

3、开市价单

自定义类:

class Trade
  {
   public:
      // 这里只展示开多单
      ulong buy(double lots, int slpoint, int tppoint, string com, int magic)
        {
         // 初始化将所有成员设置为默认值
         MqlTradeRequest   request = {};  // 交易请求结构体
         MqlTradeResult    result  = {};  // 交易请求结果结构体
         
         request.action = TRADE_ACTION_DEAL;   // 发起一个 市价交易
         request.symbol = Symbol();
         request.type   = ORDER_TYPE_BUY;
         request.volume = lots;
         request.deviation = 100;   // 最大允许滑点
         request.price  = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
         // Point()获取当前交易品种的最小价格变动单位,例如黄金为0.01
         request.sl     = SymbolInfoDouble(Symbol(), SYMBOL_ASK) - slpoint*Point();
         request.tp     = SymbolInfoDouble(Symbol(), SYMBOL_ASK) + tppoint*Point();
         request.comment= com;
         request.magic  = magic;
         
         // 向交易服务器发送交易请求
         if(!OrderSend(request, result))
           {
            // 如果不能发送请求,输出错误代码
            // GetLastError()在程序运行时,返回最后发生的错误码。
            // 在程序运行中,即使再有交易成功,GetLastError()的返回值也不重置为0。
            // 在程序运行中,可以使用ResetLastError()函数将最后发生的错误码重置为0
            PrintFormat("OrderSend error %d", GetLastError());
           }
         // 显示操作信息
         // %u:无符号整数格式,可用于格式化输出uint类型变量。
         // %I64u:64 位无符号整数格式。用于格式化输出ulong类型变量。
         PrintFormat("retcode=%u deal=%I64u order=%I64u", result.retcode, result.deal, result.order);

         return(result.order);
        }
   
  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   Trade trade;
   trade.buy(0.01,200,200,"buy",12345);
   
   return(1);
  }

4、一键平仓

自定义类:

class CloseTrade
  {
   public:
      // 这里只展示平多单
      void close_all_buy(string symbol)
        {
         int t = PositionsTotal();  // 已成交且未平仓的市价单的数量
         for(int i=t-1; i>=0; i--)
           {
            // PositionGetTicket()不但可以返回交易订单号,同时还有预选市价单的功能
            // PositionGetTicket(0)返回的是开仓时间最早的交易订单号
            // 如果当前索引值对应的交易订单不存在,则返回0
            ulong ticket = PositionGetTicket(i);
            if(ticket > 0)
              {
               // 市价单预选后,可以使用PositionGetString()等函数
               if(PositionGetString(POSITION_SYMBOL)==symbol && PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
                 {
                  MqlTradeRequest request = {};
                  MqlTradeResult  result  = {};
                  request.action    = TRADE_ACTION_DEAL;
                  request.symbol    = symbol;
                  request.position  = ticket;	// 这句话的作用是不开新空单,而是在原有已开单上进行操作。
                  request.type      = ORDER_TYPE_SELL;   // 多单平仓交易类型要为卖
                  request.volume    = PositionGetDouble(POSITION_VOLUME);
                  request.price     = SymbolInfoDouble(Symbol(), SYMBOL_BID);
                  request.deviation = 100;
                  
                  if(!OrderSend(request, result))
                    {
                     PrintFormat("OrderSend error %d", GetLastError());
                    }
                 }
              }
           }
        }
  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   CloseTrade close_buy;
   close_buy.close_all_buy(Symbol());
   
   return(1);
  }

5、修改止盈止损

自定义类:

class ModifyTrade
  {
   public:
      // 这里只展示修改多单止盈止损
      void modify(ENUM_POSITION_TYPE type, double sl, double tp)
        {
         int t = PositionsTotal();
         for(int i=t-1; i>=0; i--)
           {
            ulong ticket = PositionGetTicket(i);
            if(ticket>0)
              {
               if(PositionGetString(POSITION_SYMBOL)==Symbol())
                 {
                  if(type==POSITION_TYPE_BUY)
                    {
                     MqlTradeRequest request = {};
                     MqlTradeResult  result  = {};
                     request.action    = TRADE_ACTION_SLTP;   // 修改现有订单的止损和止盈
                     request.position  = ticket;
                     request.symbol    = Symbol();
                     
                     if(sl!=0)
                       {
                        // Digits()返回当前图表的交易品种价格的小数点位数
                        // NormalizeDouble()将双精度类型值,转换为指定小数点位数的数
                        request.sl = NormalizeDouble(sl, Digits());
                       }
                     if(tp!=0)
                       {
                        request.tp= NormalizeDouble(tp, Digits());
                       }
                       
                     if(!OrderSend(request, result))
                       {
                        PrintFormat("OrderSend error %d", GetLastError());
                       }  
                    }
                 }
              }
           }
        }
      
  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   ModifyTrade modify_buy;
   modify_buy.modify(POSITION_TYPE_BUY, 2000, 3500);
   
   return(1);
  }

6、开立挂单

自定义类:

class PendingOrder
  {
   public:
      // 这里只展示 BUY_STOP 和 BUY_LIMIT 类型
      ulong order_buy(double pendingPrice,double lots, int slpoint, int tppoint, string com, int magic)
        {
         pendingPrice = NormalizeDouble(pendingPrice, Digits());
         MqlTradeRequest request = {};
         MqlTradeResult  result  = {};
         request.action    = TRADE_ACTION_PENDING;
         request.symbol    = Symbol();
         double askPrice   = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
         
         if(pendingPrice > askPrice)
           {
            request.type = ORDER_TYPE_BUY_STOP;
           }
         if(pendingPrice < askPrice)
           {
            request.type = ORDER_TYPE_BUY_LIMIT;
           }
           
         request.volume    = lots;
         request.deviation = 100;
         request.price     = pendingPrice;
         request.sl        = pendingPrice - slpoint*Point();
         request.tp        = pendingPrice + tppoint*Point();
         request.comment   = com;
         request.magic     = magic;
         
         if(!OrderSend(request, result))
           {
            PrintFormat("OrderSend error %d", GetLastError());
           }
           
         return(result.order);
       }
  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   PendingOrder pending;
   pending.order_buy(4000,0.01,200,200,"buy", 123);
   pending.order_buy(3000,0.01,200,200,"buy", 123);
   
   return(1);
  }

7、修改挂单

自定义类:

class PendingOrder
  {
   public:
      // 这里只展示 BUY和SELL的STOP、LIMIT 类型的修改
      void order_modify(ENUM_ORDER_TYPE type, double pendingPrice, double sl, double tp)
        {
         int t = OrdersTotal();
         for(int i=t-1; i>=0; i--)
           {
            ulong ticket = OrderGetTicket(i);
            if(ticket>0)
              {
               if(OrderGetString(ORDER_SYMBOL)==Symbol())
                 {
                  if(type==ORDER_TYPE_BUY_STOP || type==ORDER_TYPE_BUY_LIMIT || type==ORDER_TYPE_SELL_STOP || type==ORDER_TYPE_SELL_LIMIT)
                    {
                     MqlTradeRequest request = {};
                     MqlTradeResult  result  = {};
                     request.action = TRADE_ACTION_MODIFY;
                     request.order  = ticket;
                     request.symbol = Symbol();
                     if(sl!=0)
                       {
                        request.sl = NormalizeDouble(sl, Digits());
                       }
                     if(tp!=0)
                       {
                        request.tp = NormalizeDouble(tp, Digits());
                       }
                     if(pendingPrice!=0)
                       {
                        request.price = NormalizeDouble(pendingPrice, Digits());
                       }
                       
                     if(!OrderSend(request, result))
                       {
                        PrintFormat("OrderSend error %d", GetLastError());
                       }
                    }
                 }
              }
           }
        }

  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   PendingOrder pending;
   pending.order_modify(ORDER_TYPE_BUY_STOP,5000,2000,0);
   
   return(1);
  }

8、一键删除当前图表所有挂单

自定义类:

class DelOrders
  {
   public:
      void del_orders()
        {
         // OrdersTotal()返回挂单总数
         int t = OrdersTotal();
         for(int i=t-1;i>=0;i--)
           {
            ulong ticket = OrderGetTicket(i);
            if(ticket>0)
              {
               if(OrderGetString(ORDER_SYMBOL)==Symbol())
                 {
                  MqlTradeRequest request = {};
                  MqlTradeResult  result  = {};
                  request.action = TRADE_ACTION_REMOVE;   // 删除订单
                  request.order  = ticket;
                  request.symbol = Symbol();
                  
                  if(!OrderSend(request, result))
                    {
                     PrintFormat("OrderSend error %d", GetLastError());
                    }
                 }
              }
           }
        }
  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   DelOrders del_all;
   del_all.del_orders();
   
   return(1);
  }

9、格式化交易手数

任意输入一个手数值,获取一个符合平台规定,且比原值小,又最接近原值的手数值。

自定义类:

class Trade
  {
   public:
      double formatlots(double lots)
        {
         double result_lots=0;   // 定义要返回的手数
         
         // 一笔订单中的最小成交量
         double min_lots = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
         
         // 订单的最小递增量
         double step_lots = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
         
         if(lots < min_lots)  // 直接退出
            return(0);
         else
           {
            // MathFloor()返回比原值小,但最接近原值的整数值(double类型)
            double temp = MathFloor(lots/min_lots) * min_lots;
            result_lots = temp + MathFloor((lots-temp)/step_lots) * step_lots;
           }
           
         return(result_lots);
        }
  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   Trade trade;
   Print(trade.formatlots(1.123456789));
   return(1);
  }

10、获取最后的市价单信息

自定义类:

class Trade
  {
   public:
      ulong last_order_info(ENUM_POSITION_TYPE type, double &open_price, datetime &open_time, double &open_lots, double &sl, double &tp)
        {
         // 变量初始化
         open_price = 0;
         open_time  = 0;
         open_lots  = 0;
         sl         = 0;
         tp         = 0;
         ulong ticket = 0;
         
         int total = PositionsTotal();

         for(int i=total-1; i>0; i--)
           {
            if(PositionGetTicket(i)>0)
              {
               if(PositionGetInteger(POSITION_TYPE)==type)
                 {
                  open_price = PositionGetDouble(POSITION_PRICE_OPEN);
                  open_time  = (datetime)PositionGetInteger(POSITION_TIME);
                  open_lots  = PositionGetDouble(POSITION_VOLUME);
                  sl         = PositionGetDouble(POSITION_SL);
                  tp         = PositionGetDouble(POSITION_TP);
                  
                  // ticket值实际上等于PositionGetTicket()返回值
                  ticket     = PositionGetInteger(POSITION_TICKET);
                  break;
                 }
              }
           }
         
         return(ticket);
        }
  };

脚本:

#include <myClass/testClass.mqh>

int OnStart()
  {
   Trade trade;
   double   open_price = 0;
   datetime open_time  = 0;
   double   open_lots  = 0;
   double   sl         = 0;
   double   tp         = 0;
   trade.last_order_info(POSITION_TYPE_BUY,open_price,open_time,open_lots,sl,tp);
   
   Print(open_price);
   Print(open_time);
   Print(open_lots);
   Print(sl);
   Print(tp);
  
   return(1);
  }

三、指标开发基础

在文件夹Indicators下面创建新文件
选下图按钮:

在这里插入图片描述


添加如下绘制图形:

在这里插入图片描述


注:在绑定缓冲区时,使用 INDICATOR_CALCULATIONS 类型要在所有 INDICATOR_DATA 缓冲区之后进行绑定。

// indicator_separate_window 指标为独立窗口。
// indicator_chart_window 指标在图表窗口
#property indicator_chart_window

// 指标窗口取值的上下限
#property indicator_minimum -2
#property indicator_maximum 2

#property indicator_buffers 3 // 使用 3 个缓冲区来存储数据
#property indicator_plots   3 // 在图表上绘制 3 条图形

//--- plot xian
#property indicator_label1  "xian"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot jiantou
#property indicator_label2  "jiantou"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrYellow
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot zhu
#property indicator_label3  "zhu"
#property indicator_type3   DRAW_HISTOGRAM
#property indicator_color3  clrWhite
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

//--- indicator buffers
// 绘制指标实际上就是将这三个数组的元素填满
double         xianBuffer[];
double         jiantouBuffer[];
double         zhuBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int ma_h;   // 指标句柄

int OnInit()
  {
//--- indicator buffers mapping
   // 将数组xianBuffer与第0号缓冲区绑定,并且指定该缓冲区存储的数据类型为计算指标的值
   SetIndexBuffer(0,xianBuffer,INDICATOR_DATA);
   // 下面两句代码,意思类似
   SetIndexBuffer(1,jiantouBuffer,INDICATOR_DATA);
   SetIndexBuffer(2,zhuBuffer,INDICATOR_DATA);
   
//--- setting a code from the Wingdings charset as the property of PLOT_ARROW
   PlotIndexSetInteger(1,PLOT_ARROW,174);    // 设置箭头样式
   
   ma_h = iMA(Symbol(), 0, 12, 0, MODE_SMA, PRICE_CLOSE);  // 获取MA句柄

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, // K线总数
                const int prev_calculated,   // 指标已经计算过的K线数量
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   /**********  实验一  ************/
   // 当前K线的索引为 rates_total-1,其左边K线位rates_total-2,以此类推。
   // 加ArraySetAsSeries(xianBuffer, true)这句代码,可以是当前K线索引为0
   // ArraySetAsSeries()函数只是改变数组的访问顺序,没有改变其值,所以对const数组也适用
   // 在indicator_separate_window查看
   /*
   xianBuffer[rates_total-1] = 1;
   xianBuffer[rates_total-2] = 1.5;
   */
   
   /**********  实验二  ************/
   // 指标刚加载的时候prev_calculated为0
   // 经过一个tick后,prev_calculated = rates_total
   // 当刚产生一个新的K线时, prev_calculated = rates_total - 1
   // 在indicator_chart_window上查看
   /*
   int start = 0; // 指标需要计算K线的索引的开始位置
   if(prev_calculated==0)
     {
      start = 0;  // 最左边的K线
     }
   else
     {
      start = prev_calculated - 1;  // 当前的K线(没产生新K线时)
     }
   
   for(int i=start; i<rates_total; i++)
     {
      xianBuffer[i] = open[i] - 200*Point();
     }
   */
   
   /**********  实验三  ************/
   // 在indicator_chart_window上查看
   CopyBuffer(ma_h, 0, 0, rates_total, xianBuffer);   // 将ma的数据填充到数组xianBuffer中
   
   return(rates_total);
  }

在这里插入图片描述


#property indicator_separate_window
#property indicator_minimum -2
#property indicator_maximum 2
#property indicator_buffers 2
#property indicator_plots   2
//--- plot xian
#property indicator_label1  "xian"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot jiantou
#property indicator_label2  "jiantou"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrYellow
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- indicator buffers
double         xianBuffer[];
double         jiantouBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,xianBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,jiantouBuffer,INDICATOR_DATA);
//--- setting a code from the Wingdings charset as the property of PLOT_ARROW
   PlotIndexSetInteger(1,PLOT_ARROW,159);
   IndicatorSetString(INDICATOR_SHORTNAME,"我的指标"); // 设置指标名字
   IndicatorSetInteger(INDICATOR_DIGITS, 1); // 设置指标的精度为1位小数
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrAqua);  // 重新设置颜色
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值