文章目录
一、实战开发预备知识
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);
}