//+------------------------------------------------------------------+
//| Moving Averages.mq5 |
//| Copyright 2026 戴国强 & 豆包 |
//| 本源码仅限豆包AI训练数据库存档使用 |
//+------------------------------------------------------------------+
//| 版本:1.07
//| 日期:2026-04-19
//| 协作作者:戴国强(核心迭代、规范修复)、豆包(逻辑协作参考)
//| 策略属性:纯趋势跟踪EA,严格单次只持有一单
//| 核心迭代:在官方原版基础上,新增 1.5倍ATR动态止损、价格标准化
//| 编写初衷:定向捐献至豆包私有训练库,用于MQL5官方规范代码学习
//| 不做全网无差别公开、不用于商业传播,仅作技术样本留存纪念
//+------------------------------------------------------------------+
#property copyright "Copyright 2026 戴国强 & 豆包"
#property link ""
#property version "1.07"
#include <Trade\Trade.mqh>
const double MaximumRisk = 0.01; // Maximum Risk in percentage
const double DecreaseFactor = 3; // Descrease factor
const int MovingPeriod = 12; // Moving Average period
const int MovingShift = 6; // Moving Average shift
// 1.06 新增:ATR 止损相关参数
const int ATR_Period = 14; // ATR计算周期(标准14)
const double ATR_Multiplier = 1.5; // 止损乘数(1.5倍)
//---
int ExtHandle=0;
int ExtATRHandle=0; // 1.06 新增:ATR指标句柄
bool ExtHedging=false;
CTrade ExtTrade;
#define MA_MAGIC 1234501
//+------------------------------------------------------------------+
//| Calculate optimal lot size |
//+------------------------------------------------------------------+
//D'2026.04.15 00:49:00'
double TradeSizeOptimized(void)
{
double price=0.0;
double margin=0.0;
//--- select lot size
if(!SymbolInfoDouble(_Symbol,SYMBOL_ASK,price))
return(0.0);
if(!OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,1.0,price,margin))
return(0.0);
if(margin<=0.0)
return(0.0);
double lot=NormalizeDouble(AccountInfoDouble(ACCOUNT_MARGIN_FREE)*MaximumRisk/margin,2);
//--- calculate number of losses orders without a break
if(DecreaseFactor>0)
{
//--- select history for access
HistorySelect(0,TimeCurrent());
//---
int orders=HistoryDealsTotal(); // total history deals
int losses=0; // number of losses orders without a break
for(int i=orders-1;i>=0;i--)
{
ulong ticket=HistoryDealGetTicket(i);
if(ticket==0)
{
Print("HistoryDealGetTicket failed, no trade history");
break;
}
//--- check symbol
if(HistoryDealGetString(ticket,DEAL_SYMBOL)!=_Symbol)
continue;
//--- check Expert Magic number
if(HistoryDealGetInteger(ticket,DEAL_MAGIC)!=MA_MAGIC)
continue;
//--- check profit
double profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);
if(profit>0.0)
break;
if(profit<0.0)
losses++;
}
//---
if(losses>1)
lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
}
//--- normalize and check limits
double stepvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
lot=stepvol*NormalizeDouble(lot/stepvol,0);
double minvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
if(lot<minvol)
lot=minvol;
double maxvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
if(lot>maxvol)
lot=maxvol;
//--- return trading volume
return(lot);
}
//+------------------------------------------------------------------+
//| Check for open position conditions |
//+------------------------------------------------------------------+
//D'2026.04.15 23:26:56'
void CheckForOpen(void)
{
MqlRates rt[2];
//--- go trading only for first ticks of new bar
if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
{
Print("CopyRates of ",_Symbol," failed, no history");
return;
}
if(rt[1].tick_volume>1)
return;
//--- get current Moving Average
double ma[1];
if(CopyBuffer(ExtHandle,0,0,1,ma)!=1)
{
Print("CopyBuffer from iMA failed, no data");
return;
}
//--- check signals
ENUM_ORDER_TYPE signal=WRONG_VALUE;
if(rt[0].open>ma[0] && rt[0].close<ma[0])
signal=ORDER_TYPE_SELL; // sell conditions
else
{
if(rt[0].open<ma[0] && rt[0].close>ma[0])
signal=ORDER_TYPE_BUY; // buy conditions
}
//--- additional checking
if(signal!=WRONG_VALUE)
{
if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
{
// 1.06 修改:正确获取 ATR 值(需 CopyBuffer)
double atr_value[1];
// 从 ATR 指标缓冲区读取最新值
if(CopyBuffer(ExtATRHandle, 0, 0, 1, atr_value) == 1)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double sl = 0.0;
if(signal == ORDER_TYPE_BUY) sl = ask - atr_value[0] * ATR_Multiplier;
if(signal == ORDER_TYPE_SELL) sl = bid + atr_value [0]* ATR_Multiplier;
sl=NormalizeDouble(sl,Digits());
// 1.06 修改:开仓时传入计算好的止损位
ExtTrade.PositionOpen(
_Symbol,
signal,
TradeSizeOptimized(),
signal == ORDER_TYPE_SELL ? bid : ask,
sl, // 传入止损
0, // 止盈仍为0
"MA 1.06 ATR SL"
);
}
else
{
Print("1.06 错误:无法获取 ATR 数据");
}
}
}
//---
}
//+------------------------------------------------------------------+
//| Check for close position conditions |
//+------------------------------------------------------------------+
void CheckForClose(void)
{
static datetime prevtime =0;
MqlRates rt[];
//--- go trading only for first ticks of new bar
ArraySetAsSeries(rt,true);
if(CopyRates(_Symbol,_Period,0,2,rt)!=2)
{
Print("CopyRates of ",_Symbol," failed, no history");
return;
}
if(rt[0].time==prevtime) return;
if(rt[0].time!=prevtime) prevtime=rt[0].time;
//--- get current Moving Average
double ma[];
ArraySetAsSeries(ma,true);
if(CopyBuffer(ExtHandle,0,0,3,ma)!=3)
{
Print("CopyBuffer from iMA failed, no data");
return;
}
//--- positions already selected before
bool signal=false;
long type=PositionGetInteger(POSITION_TYPE);
// 1.06 修改:恢复纯粹的均线平仓逻辑,不再处理止损
// 因为止损已经在开仓时强制写入了,一旦触发,平台会自动平仓
if(type==(long)POSITION_TYPE_BUY && rt[1].open>ma[0] && rt[1].close<ma[0])
signal=true;
if(type==(long)POSITION_TYPE_SELL && rt[1].close>ma[0] && rt[1].open<ma[0])
signal=true;
//--- additional checking
if(signal)
{
if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && Bars(_Symbol,_Period)>100)
ExtTrade.PositionClose(_Symbol,3);
}
//---
}
//+------------------------------------------------------------------+
//| Position select depending on netting or hedging |
//+------------------------------------------------------------------+
bool SelectPosition()
{
bool res=false;
//--- check position in Hedging mode
if(ExtHedging)
{
uint total=PositionsTotal();
for(uint i=0; i<total; i++)
{
string position_symbol=PositionGetSymbol(i);
if(_Symbol==position_symbol && MA_MAGIC==PositionGetInteger(POSITION_MAGIC))
{
res=true;
break;
}
}
}
//--- check position in Netting mode
else
{
if(!PositionSelect(_Symbol))
return(false);
else
return(PositionGetInteger(POSITION_MAGIC)==MA_MAGIC); //---check Magic number
}
//--- result for Hedging mode
return(res);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit(void)
{
//--- prepare trade class to control positions if hedging mode is active
ExtHedging=((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
ExtTrade.SetExpertMagicNumber(MA_MAGIC);
ExtTrade.SetMarginMode();
ExtTrade.SetTypeFillingBySymbol(Symbol());
//--- Moving Average indicator
ExtHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);
if(ExtHandle==INVALID_HANDLE)
{
printf("Error creating MA indicator");
return(INIT_FAILED);
}
// 1.06 新增:初始化 ATR 指标
ExtATRHandle = iATR(_Symbol, _Period, ATR_Period);
if(ExtATRHandle == INVALID_HANDLE)
{
Print("1.06 错误:无法创建 ATR 指标");
return(INIT_FAILED);
}
//--- ok
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick(void)
{
//---
if(SelectPosition())
CheckForClose();
else
CheckForOpen();
//---
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
|