多品種 EA 開發:同時交易多個貨幣對
📌 本文重點
學習如何開發可同時交易多個貨幣對的 MQL5 EA:多品種指標初始化、Magic Number 管理、跨品種持倉查詢與資金分配。
學習如何開發可同時交易多個貨幣對的 MQL5 EA:多品種指標初始化、Magic Number 管理、跨品種持倉查詢與資金分配。
多品種 EA 的設計思路
一個 EA 可以在單一圖表上同時監控並交易多個品種。關鍵是為每個品種建立獨立的指標句柄,並用不同的 Magic Number 區分持倉。
#include <Trade\Trade.mqh>
CTrade g_trade;
input string InpSymbols = "EURUSD,GBPUSD,USDJPY,AUDUSD"; // 交易品種(逗號分隔)
input int InpFastPeriod= 10;
input int InpSlowPeriod= 30;
input double InpRiskPct = 0.5; // 每個品種風險(帳戶餘額%)
input int InpBaseMagic = 30000; // 基礎 Magic Number(每個品種 +1)
// 每個品種的數據結構
struct SymbolData
{
string symbol;
int fastHandle;
int slowHandle;
int atrHandle;
int magic;
datetime lastBar;
};
SymbolData g_symbols[];
int g_symbolCount = 0;
int OnInit()
{
g_trade.SetDeviations(10);
// 解析品種列表
string symbolList[];
int count = StringSplit(InpSymbols, ',', symbolList);
ArrayResize(g_symbols, count);
g_symbolCount = 0;
for (int i = 0; i < count; i++)
{
string sym = symbolList[i];
StringTrimLeft(sym); StringTrimRight(sym);
if (!SymbolSelect(sym, true)) { Print("無法選擇品種: ", sym); continue; }
g_symbols[g_symbolCount].symbol = sym;
g_symbols[g_symbolCount].fastHandle = iMA(sym, _Period, InpFastPeriod, 0, MODE_SMA, PRICE_CLOSE);
g_symbols[g_symbolCount].slowHandle = iMA(sym, _Period, InpSlowPeriod, 0, MODE_SMA, PRICE_CLOSE);
g_symbols[g_symbolCount].atrHandle = iATR(sym, _Period, 14);
g_symbols[g_symbolCount].magic = InpBaseMagic + i;
g_symbols[g_symbolCount].lastBar = 0;
if (g_symbols[g_symbolCount].fastHandle == INVALID_HANDLE ||
g_symbols[g_symbolCount].slowHandle == INVALID_HANDLE)
{
Print("指標初始化失敗: ", sym);
continue;
}
Print("已初始化品種: ", sym, " Magic=", g_symbols[g_symbolCount].magic);
g_symbolCount++;
}
if (g_symbolCount == 0) return INIT_FAILED;
EventSetTimer(1);
return INIT_SUCCEEDED;
}
void OnDeinit(const int r)
{
EventKillTimer();
for (int i = 0; i < g_symbolCount; i++)
{
IndicatorRelease(g_symbols[i].fastHandle);
IndicatorRelease(g_symbols[i].slowHandle);
IndicatorRelease(g_symbols[i].atrHandle);
}
}
void OnTimer()
{
for (int i = 0; i < g_symbolCount; i++)
ProcessSymbol(g_symbols[i]);
}
void ProcessSymbol(SymbolData &sd)
{
// 只在新K線時處理
datetime curBar = iTime(sd.symbol, _Period, 0);
if (curBar == sd.lastBar) return;
sd.lastBar = curBar;
double fast[3], slow[3], atr[2];
ArraySetAsSeries(fast, true); ArraySetAsSeries(slow, true); ArraySetAsSeries(atr, true);
if (CopyBuffer(sd.fastHandle, 0, 0, 3, fast) < 3) return;
if (CopyBuffer(sd.slowHandle, 0, 0, 3, slow) < 3) return;
if (CopyBuffer(sd.atrHandle, 0, 0, 2, atr) < 2) return;
bool golden = fast[2]<=slow[2] && fast[1]>slow[1];
bool death = fast[2]>=slow[2] && fast[1]<slow[1];
int pos = CountPositions(sd.symbol, sd.magic);
if (golden && pos == 0)
{
double ask = SymbolInfoDouble(sd.symbol, SYMBOL_ASK);
double pt = SymbolInfoDouble(sd.symbol, SYMBOL_POINT);
double sl = ask - atr[1] * 2;
double tp = ask + atr[1] * 4;
int slPips = (int)((ask-sl)/pt);
g_trade.SetExpertMagicNumber(sd.magic);
g_trade.Buy(CalcLots(sd.symbol, slPips), sd.symbol, ask, sl, tp, "Multi "+sd.symbol);
Print("開倉: ", sd.symbol, " Magic=", sd.magic);
}
if (death && pos > 0)
{
g_trade.SetExpertMagicNumber(sd.magic);
for (int i=PositionsTotal()-1; i>=0; i--)
if (PositionGetSymbol(i)==sd.symbol && PositionGetInteger(POSITION_MAGIC)==sd.magic)
g_trade.PositionClose(PositionGetInteger(POSITION_TICKET));
}
}
int CountPositions(string sym, int magic)
{
int n=0;
for (int i=PositionsTotal()-1; i>=0; i--)
if (PositionGetSymbol(i)==sym && PositionGetInteger(POSITION_MAGIC)==magic) n++;
return n;
}
double CalcLots(string sym, int slPips)
{
double r=AccountInfoDouble(ACCOUNT_BALANCE)*InpRiskPct/100.0;
double tv=SymbolInfoDouble(sym,SYMBOL_TRADE_TICK_VALUE);
double ts=SymbolInfoDouble(sym,SYMBOL_TRADE_TICK_SIZE);
double pv=tv*(ts/SymbolInfoDouble(sym,SYMBOL_POINT));
if(pv<=0||slPips<=0) return SymbolInfoDouble(sym,SYMBOL_VOLUME_MIN);
double l=r/(slPips*pv);
double mn=SymbolInfoDouble(sym,SYMBOL_VOLUME_MIN),mx=SymbolInfoDouble(sym,SYMBOL_VOLUME_MAX),st=SymbolInfoDouble(sym,SYMBOL_VOLUME_STEP);
return NormalizeDouble(MathRound(MathMax(MathMin(l,mx),mn)/st)*st,2);
}
本文由 James Lee 撰寫。內容僅供教育目的,不構成投資建議。