//+------------------------------------------------------------------+
//| TradeByATR.mq5 |
//| Copyright 2022, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
input double lots=0.1; // 交易量手数
input double kATR=3; // ATR中的信号蜡烛长度
input int ATRperiod=20; // ATR指标周期
input int holdbars=8; // 保持持仓的柱形图数量
input int slippage=10; // 可允许滑移
input bool revers=false; //反向信号?
input ulong EXPERT_MAGIC=0; // EA幻数
//--- 用于存储ATR指标句柄
int atr_handle;
//--- 在这里,我们将存储ATR最后值和蜡烛体
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//+------------------------------------------------------------------+
//| EA交易初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 初始化全局变量
last_atr=0;
last_body=0;
//--- 设置正确的交易量
double min_lot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
trade_lot=lots>min_lot? lots:min_lot;
//--- 创建ATR指标句柄
atr_handle=iATR(_Symbol,_Period,ATRperiod);
if(atr_handle==INVALID_HANDLE)
{
PrintFormat("%s: failed to create iATR, error code %d",__FUNCTION__,GetLastError());
return(INIT_FAILED);
}
//--- EA成功初始化
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA交易去初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- 通知EA操作结束代码
Print(__FILE__,": Deinitialization reason code = ",reason);
}
//+------------------------------------------------------------------+
//| EA报价函数 |
//+------------------------------------------------------------------+
void OnTick()
{
//--- 交易信号
static int signal=0; // +1表示买入信号, -1 表示卖出信号
//--- 检查和关闭‘holdbars’柱形图之前开仓的旧持仓
ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- 检查新柱形图
if(isNewBar())
{
//--- 检查信号是否存在
signal=CheckSignal();
}
//--- 如果持有单边持仓,跳过信号-等候直至关闭
if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
{
signal=0;
return; // 退出NewTick事件处理程序,并在新柱形图出现之前不要进入市场
}
//--- 对于锁仓账户,每个持仓都是单独持仓和平仓
if(signal!=0)
{
//--- 买入信号
if(signal>0)
{
PrintFormat("%s: Buy signal! Revers=%s",__FUNCTION__,string(revers));
if(Buy(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
//--- 卖出信号
if(signal<0)
{
PrintFormat("%s: Sell signal! Revers=%s",__FUNCTION__,string(revers));
if(Sell(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
}
//--- OnTick函数结束
}
//+------------------------------------------------------------------+
//| 检查新的交易信号 |
//+------------------------------------------------------------------+
int CheckSignal()
{
//--- 0 意味着没有信号
int res=0;
//--- 获得倒数第二个完整柱形图的ATR值(柱形图索引是2)
double atr_value[1];
if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
{
last_atr=atr_value[0];
//--- 获得最后关闭的柱形图的MqlRates类型数组数据
MqlRates bar[1];
if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
{
//--- 计算最后完整柱形图的柱体大小
last_body=bar[0].close-bar[0].open;
//--- 如果最后柱形图的柱体(索引为1)超过的之前ATR的值(柱形图索引为2),那么会收到一个交易信号
if(MathAbs(last_body)>kATR*last_atr)
res=last_body>0?1:-1; // 向上蜡烛图的正值
}
else
PrintFormat("%s: Failed to receive the last bar! Error",__FUNCTION__,GetLastError());
}
else
PrintFormat("%s: Failed to receive ATR indicator value! Error",__FUNCTION__,GetLastError());
//--- 如果启用了反向交易模式
res=revers?-res:res; // 在必要时反转信号(返回-1而不是1,反之亦然)
//--- 返回一个交易信号值
return (res);
}
//+------------------------------------------------------------------+
//| 当新柱形图出现时返回'true' |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
{
static datetime bartime=0; //存储当前柱形图的开盘时间
//--- 获得零柱的开盘时间
datetime currbar_time=iTime(_Symbol,_Period,0);
//--- 如果开盘时间更改,则新柱形图出现
if(bartime!=currbar_time)
{
bartime=currbar_time;
lastbar_timeopen=bartime;
//--- 在日志中显示新柱形图开盘时间的数据
if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
{
//--- 显示新柱形图开盘时间的信息
PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol,
StringSubstr(EnumToString(_Period),7),
TimeToString(TimeCurrent(),TIME_SECONDS));
//--- 获取关于最后报价的数据
MqlTick last_tick;
if(!SymbolInfoTick(Symbol(),last_tick))
Print("SymbolInfoTick() failed, error = ",GetLastError());
//--- 显示最后报价的时间,精确至毫秒
PrintFormat("Last tick was at %s.%03d",
TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
}
//--- 我们有一个新柱形图
return (true);
}
//--- 没有新柱形图
return (false);
}
//+------------------------------------------------------------------+
//| 以指定交易量和市场价买入 |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- 以市场价买入
return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| 以指定交易量和市场价卖出 |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- 以市场价卖出
return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| 在柱形图中根据持有时间平仓 |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong magicnumber=0)
{
int total=PositionsTotal(); // 持仓数量
//--- 重复持仓
for(int i=total-1; i>=0; i--)
{
//--- 持仓参数
ulong position_ticket=PositionGetTicket(i); // 持仓单号
string position_symbol=PositionGetString(POSITION_SYMBOL); //交易品种
ulong magic=PositionGetInteger(POSITION_MAGIC); // 持仓幻数
datetime position_open=(datetime)PositionGetInteger(POSITION_TIME); // 持仓开盘时间
int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1; // 开仓之前有多少柱形图
//--- 如果持仓的生命周期很长,则幻数和交易品种匹配
if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
{
int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // 小数位数
double volume=PositionGetDouble(POSITION_VOLUME); // 持仓交易量
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // 持仓类型
string str_type=StringSubstr(EnumToString(type),14);
StringToLower(str_type); //纠正消息格式的小写文本案例
PrintFormat("Close position #%d %s %s %.2f",
position_ticket,position_symbol,str_type,volume);
//--- 设置一个订单类型并发送交易请求
if(type==POSITION_TYPE_BUY)
MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
else
MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
}
}
}
//+------------------------------------------------------------------+
//| 准备和发送交易请求 |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
{
//--- 声明和初始化结构
MqlTradeRequest request={};
MqlTradeResult result={};
double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
if(type==ORDER_TYPE_BUY)
price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- 请求参数
request.action =TRADE_ACTION_DEAL; // 交易操作类型
request.position =pos_ticket; // 关闭情况下的持仓单号
request.symbol =Symbol(); // 交易品种
request.volume =volume; // 交易量
request.type =type; // 订单类型
request.price =price; // 交易价格
request.deviation=slip; // 可允许的价格偏差
request.magic =magicnumber; // 订单幻数
request.type_filling=ORDER_FILLING_IOC;
//--- 发送请求
if(!OrderSend(request,result))
{
//--- 显示数据失败
PrintFormat("OrderSend %s %s %.2f at %.5f error %d",
request.symbol,EnumToString(type),volume,request.price,GetLastError());
return (false);
}
//--- 通知成功操作
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
return (true);
}
|