# 多货币对自动交易EA - 完整MQL4源码(支持EURUSD/GBPUSD/USDJPY)
本文提供一个专业的多货币对智能交易系统,只需附加到一个图表即可同时交易EURUSD、GBPUSD和USDJPY三个主流货币对。每个货币对拥有独立的参数设置,支持保本止损、移动止损、每日盈利目标以及可选的对冲保护模式。
策略逻辑
EA在每个货币对上独立运行基于波动率突破的策略。当价格突破移动平均线+ATR滤波器的上轨时开多单,突破下轨时开空单。每个货币对可单独开启/关闭,并可自定义风险系数。
完整MQL4代码
```mql4
//+------------------------------------------------------------------+
//| MultiCurrencyTrader.mq4 |
//| 自主编译 多货币对专用 |
//| |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict
//--- 通用设置
input string GeneralSettings = "=== 通用设置 ===";
input double RiskPercent = 1.0; // 单笔风险(账户余额%)
input int Slippage = 3; // 滑点点数
input int MagicBase = 202412; // 魔术号基数
input bool UseHedgeMode = false; // 启用对冲模式
input double HedgeDrawdownPercent = 8.0; // 触发对冲的回撤%
input int DailyTargetProfit = 0; // 每日盈利目标美元(0=关闭)
input bool CloseAtDayEnd = true; // 日终平仓所有持仓
//--- EURUSD 参数
input string EURUSDSettings = "=== EURUSD 设置 ===";
input bool EnableEURUSD = true; // 启用EURUSD交易
input int EURUSD_MA_Period = 20; // EURUSD MA周期
input int EURUSD_ATR_Period = 14; // EURUSD ATR周期
input double EURUSD_ATR_Multiplier = 1.5; // EURUSD ATR倍数
input int EURUSD_StopLoss = 40; // EURUSD止损点数
input int EURUSD_TakeProfit = 80; // EURUSD止盈点数
input int EURUSD_BreakevenTrigger = 30; // EURUSD保本触发点数
input int EURUSD_TrailingStop = 25; // EURUSD移动止损点数
input double EURUSD_LotMultiplier = 1.0; // EURUSD手数系数
//--- GBPUSD 参数
input string GBPUSDSettings = "=== GBPUSD 设置 ===";
input bool EnableGBPUSD = true; // 启用GBPUSD交易
input int GBPUSD_MA_Period = 20; // GBPUSD MA周期
input int GBPUSD_ATR_Period = 14; // GBPUSD ATR周期
input double GBPUSD_ATR_Multiplier = 1.5; // GBPUSD ATR倍数
input int GBPUSD_StopLoss = 50; // GBPUSD止损点数
input int GBPUSD_TakeProfit = 100; // GBPUSD止盈点数
input int GBPUSD_BreakevenTrigger = 35; // GBPUSD保本触发点数
input int GBPUSD_TrailingStop = 30; // GBPUSD移动止损点数
input double GBPUSD_LotMultiplier = 0.8; // GBPUSD手数系数
//--- USDJPY 参数
input string USDJPYSettings = "=== USDJPY 设置 ===";
input bool EnableUSDJPY = true; // 启用USDJPY交易
input int USDJPY_MA_Period = 20; // USDJPY MA周期
input int USDJPY_ATR_Period = 14; // USDJPY ATR周期
input double USDJPY_ATR_Multiplier = 1.5; // USDJPY ATR倍数
input int USDJPY_StopLoss = 45; // USDJPY止损点数
input int USDJPY_TakeProfit = 90; // USDJPY止盈点数
input int USDJPY_BreakevenTrigger = 32; // USDJPY保本触发点数
input int USDJPY_TrailingStop = 28; // USDJPY移动止损点数
input double USDJPY_LotMultiplier = 0.9; // USDJPY手数系数
//--- 货币对参数结构体
struct PairParams
{
string symbol;
bool enabled;
int maPeriod;
int atrPeriod;
double atrMultiplier;
int stopLoss;
int takeProfit;
int breakevenTrigger;
int trailingStop;
double lotMultiplier;
int magicNumber;
datetime lastBarTime;
};
PairParams pairs[3];
double dailyProfitStart = 0;
bool hedgePositionOpen = false;
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 初始化EURUSD
pairs[0].symbol = "EURUSD";
pairs[0].enabled = EnableEURUSD;
pairs[0].maPeriod = EURUSD_MA_Period;
pairs[0].atrPeriod = EURUSD_ATR_Period;
pairs[0].atrMultiplier = EURUSD_ATR_Multiplier;
pairs[0].stopLoss = EURUSD_StopLoss;
pairs[0].takeProfit = EURUSD_TakeProfit;
pairs[0].breakevenTrigger = EURUSD_BreakevenTrigger;
pairs[0].trailingStop = EURUSD_TrailingStop;
pairs[0].lotMultiplier = EURUSD_LotMultiplier;
pairs[0].magicNumber = MagicBase + 1;
pairs[0].lastBarTime = 0;
// 初始化GBPUSD
pairs[1].symbol = "GBPUSD";
pairs[1].enabled = EnableGBPUSD;
pairs[1].maPeriod = GBPUSD_MA_Period;
pairs[1].atrPeriod = GBPUSD_ATR_Period;
pairs[1].atrMultiplier = GBPUSD_ATR_Multiplier;
pairs[1].stopLoss = GBPUSD_StopLoss;
pairs[1].takeProfit = GBPUSD_TakeProfit;
pairs[1].breakevenTrigger = GBPUSD_BreakevenTrigger;
pairs[1].trailingStop = GBPUSD_TrailingStop;
pairs[1].lotMultiplier = GBPUSD_LotMultiplier;
pairs[1].magicNumber = MagicBase + 2;
pairs[1].lastBarTime = 0;
// 初始化USDJPY
pairs[2].symbol = "USDJPY";
pairs[2].enabled = EnableUSDJPY;
pairs[2].maPeriod = USDJPY_MA_Period;
pairs[2].atrPeriod = USDJPY_ATR_Period;
pairs[2].atrMultiplier = USDJPY_ATR_Multiplier;
pairs[2].stopLoss = USDJPY_StopLoss;
pairs[2].takeProfit = USDJPY_TakeProfit;
pairs[2].breakevenTrigger = USDJPY_BreakevenTrigger;
pairs[2].trailingStop = USDJPY_TrailingStop;
pairs[2].lotMultiplier = USDJPY_LotMultiplier;
pairs[2].magicNumber = MagicBase + 3;
pairs[2].lastBarTime = 0;
dailyProfitStart = AccountBalance();
Print("多货币对EA初始化完成,启用货币对: ",
EnableEURUSD ? "EURUSD " : "",
EnableGBPUSD ? "GBPUSD " : "",
EnableUSDJPY ? "USDJPY" : "");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("多货币对EA已移除");
}
//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 每日盈利目标检查
if(DailyTargetProfit > 0)
{
double currentProfit = AccountProfit();
if(currentProfit >= DailyTargetProfit)
{
CloseAllPositionsAllPairs();
Comment("今日盈利目标已达成: $", currentProfit);
return;
}
}
// 日终平仓(23:55)
if(CloseAtDayEnd && (TimeHour(TimeCurrent()) == 23 && TimeMinute(TimeCurrent()) >= 55))
{
CloseAllPositionsAllPairs();
return;
}
// 对冲模式检查
if(UseHedgeMode && !hedgePositionOpen)
{
CheckAndOpenHedge();
}
// 处理每个货币对
for(int i = 0; i < 3; i++)
{
if(!pairs[i].enabled) continue;
// 检查该货币对的新K线
datetime currentBarTime = iTime(pairs[i].symbol, PERIOD_CURRENT, 0);
if(pairs[i].lastBarTime == currentBarTime) continue;
pairs[i].lastBarTime = currentBarTime;
ProcessPair(i);
}
// 管理所有持仓
ManageAllPositions();
}
//+------------------------------------------------------------------+
//| 处理单个货币对 |
//+------------------------------------------------------------------+
void ProcessPair(int idx)
{
// 已有持仓则不开新单
if(CountPositionsByMagic(pairs[idx].magicNumber) > 0) return;
// 计算指标
double ma = iMA(pairs[idx].symbol, 0, pairs[idx].maPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double atr = iATR(pairs[idx].symbol, 0, pairs[idx].atrPeriod, 1);
double filter = atr * pairs[idx].atrMultiplier;
double close1 = iClose(pairs[idx].symbol, 0, 1);
double close2 = iClose(pairs[idx].symbol, 0, 2);
// 突破信号
bool buySignal = (close1 > ma + filter) && (close2 <= ma + filter);
bool sellSignal = (close1 < ma - filter) && (close2 >= ma - filter);
if(buySignal)
{
OpenOrderForPair(idx, OP_BUY);
}
else if(sellSignal)
{
OpenOrderForPair(idx, OP_SELL);
}
}
//+------------------------------------------------------------------+
//| 为指定货币对开仓 |
//+------------------------------------------------------------------+
void OpenOrderForPair(int idx, int cmd)
{
double lotSize = CalculateLotSize(pairs[idx].symbol, RiskPercent) * pairs[idx].lotMultiplier;
lotSize = NormalizeDouble(lotSize, 2);
if(lotSize < MarketInfo(pairs[idx].symbol, MODE_MINLOT)) lotSize = MarketInfo(pairs[idx].symbol, MODE_MINLOT);
if(lotSize > MarketInfo(pairs[idx].symbol, MODE_MAXLOT)) lotSize = MarketInfo(pairs[idx].symbol, MODE_MAXLOT);
double price = (cmd == OP_BUY) ? MarketInfo(pairs[idx].symbol, MODE_ASK) : MarketInfo(pairs[idx].symbol, MODE_BID);
double sl = 0, tp = 0;
double point = MarketInfo(pairs[idx].symbol, MODE_POINT);
int digitMultiplier = (Digits == 3 || Digits == 5) ? 10 : 1;
double pipValue = point * digitMultiplier;
if(pairs[idx].stopLoss > 0)
{
if(cmd == OP_BUY)
sl = price - pairs[idx].stopLoss * pipValue;
else
sl = price + pairs[idx].stopLoss * pipValue;
}
if(pairs[idx].takeProfit > 0)
{
if(cmd == OP_BUY)
tp = price + pairs[idx].takeProfit * pipValue;
else
tp = price - pairs[idx].takeProfit * pipValue;
}
int ticket = OrderSend(pairs[idx].symbol, cmd, lotSize, price, Slippage, sl, tp,
"MultiCurrency", pairs[idx].magicNumber, 0, clrNONE);
if(ticket > 0)
{
Print("开仓成功 ", pairs[idx].symbol, " ", (cmd == OP_BUY ? "多单" : "空单"),
" 手数: ", lotSize, " 订单号: ", ticket);
}
else
{
Print("开仓失败 ", pairs[idx].symbol, " 错误码: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| 根据风险百分比计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize(string symbol, double riskPercent)
{
double balance = AccountBalance();
double riskAmount = balance * riskPercent / 100.0;
double tickValue = MarketInfo(symbol, MODE_TICKVALUE);
double stopLossPips = 40.0; // 默认止损点数
double lot = riskAmount / (stopLossPips * tickValue);
lot = NormalizeDouble(lot, 2);
double minLot = MarketInfo(symbol, MODE_MINLOT);
double maxLot = MarketInfo(symbol, MODE_MAXLOT);
if(lot < minLot) lot = minLot;
if(lot > maxLot) lot = maxLot;
return lot;
}
//+------------------------------------------------------------------+
//| 按魔术号统计持仓数量 |
//+------------------------------------------------------------------+
int CountPositionsByMagic(int magic)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == magic)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| 统计EA所有持仓总数 |
//+------------------------------------------------------------------+
int CountAllEAPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
for(int j = 0; j < 3; j++)
{
if(OrderMagicNumber() == pairs[j].magicNumber)
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| 管理所有持仓 - 保本止损和移动止损 |
//+------------------------------------------------------------------+
void ManageAllPositions()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
int pairIdx = -1;
for(int j = 0; j < 3; j++)
{
if(OrderMagicNumber() == pairs[j].magicNumber)
{
pairIdx = j;
break;
}
}
if(pairIdx == -1) continue;
double point = MarketInfo(OrderSymbol(), MODE_POINT);
int digitMultiplier = (Digits == 3 || Digits == 5) ? 10 : 1;
double pipValue = point * digitMultiplier;
double openPrice = OrderOpenPrice();
double currentPrice = (OrderType() == OP_BUY) ? MarketInfo(OrderSymbol(), MODE_BID) : MarketInfo(OrderSymbol(), MODE_ASK);
double profitPips = 0;
if(OrderType() == OP_BUY)
profitPips = (currentPrice - openPrice) / pipValue;
else
profitPips = (openPrice - currentPrice) / pipValue;
// 保本止损
if(pairs[pairIdx].breakevenTrigger > 0 && profitPips >= pairs[pairIdx].breakevenTrigger)
{
double breakevenSL = openPrice;
if(OrderStopLoss() == 0 || (OrderType() == OP_BUY && breakevenSL > OrderStopLoss()) ||
(OrderType() == OP_SELL && breakevenSL < OrderStopLoss()))
{
OrderModify(OrderTicket(), openPrice, breakevenSL, OrderTakeProfit(), 0, clrNONE);
}
}
// 移动止损
if(pairs[pairIdx].trailingStop > 0 && profitPips > pairs[pairIdx].trailingStop)
{
double newSL = 0;
if(OrderType() == OP_BUY)
newSL = currentPrice - pairs[pairIdx].trailingStop * pipValue;
else
newSL = currentPrice + pairs[pairIdx].trailingStop * pipValue;
if((OrderType() == OP_BUY && newSL > OrderStopLoss()) ||
(OrderType() == OP_SELL && newSL < OrderStopLoss()))
{
OrderModify(OrderTicket(), openPrice, newSL, OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 平仓所有货币对的所有持仓 |
//+------------------------------------------------------------------+
void CloseAllPositionsAllPairs()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
for(int j = 0; j < 3; j++)
{
if(OrderMagicNumber() == pairs[j].magicNumber)
{
bool closed = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, clrNONE);
if(closed)
Print("已平仓 ", OrderSymbol(), " 订单号: ", OrderTicket());
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查并在回撤超过阈值时开启对冲仓位 |
//+------------------------------------------------------------------+
void CheckAndOpenHedge()
{
double totalProfit = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
for(int j = 0; j < 3; j++)
{
if(OrderMagicNumber() == pairs[j].magicNumber)
{
totalProfit += OrderProfit();
}
}
}
}
double balance = AccountBalance();
double drawdownPercent = (totalProfit < 0) ? (-totalProfit / balance) * 100 : 0;
if(drawdownPercent >= HedgeDrawdownPercent && CountAllEAPositions() > 0)
{
// 在EURUSD上开反向对冲仓位
double hedgeLot = CalculateLotSize("EURUSD", RiskPercent) * 1.5;
hedgeLot = NormalizeDouble(hedgeLot, 2);
double price = MarketInfo("EURUSD", MODE_BID);
double sl = price + 100 * Point * 10;
double tp = price - 80 * Point * 10;
int ticket = OrderSend("EURUSD", OP_SELL, hedgeLot, price, Slippage, sl, tp,
"HedgePosition", MagicBase + 100, 0, clrNONE);
if(ticket > 0)
{
hedgePositionOpen = true;
Print("对冲仓位已开启,当前回撤: ", drawdownPercent, "%");
}
}
}
//+------------------------------------------------------------------+
```
参数详解
| 参数 | 说明 | 推荐值 |
|------|------|--------|
| RiskPercent | 单笔风险占账户余额百分比 | 0.5-2.0 |
| UseHedgeMode | 回撤超过阈值时开启对冲保护 | 保守账户设为true |
| HedgeDrawdownPercent | 触发对冲的回撤百分比 | 8-12 |
| DailyTargetProfit | 每日盈利达到后自动停止 | 50-200美元 |
| CloseAtDayEnd | 每日23:55自动平仓 | true |
| LotMultiplier | 各货币对手数调整系数 | 0.8-1.2 |
货币对兼容性
本EA专门为以下货币对设计和测试:
代码自动处理4位和5位报价经纪商的点数计算。
安装与使用说明
1. 将EA附加到任意一个图表(推荐EURUSD H1周期)
2. EA会自动交易所有启用的货币对
3. 无需附加多个EA实例
4. 标准账户建议设置RiskPercent为1.0
编译注意事项
参考来源
本文EA源码为自主编译,针对多货币对交易进行了专门优化。策略基于波动率突破原理,配合ATR滤波器进行信号过滤。
*如需包含AI信号过滤和投资组合管理的进阶多策略EA,欢迎查看我们的专业EA套件,附赠10+货币对详细回测报告。*