MQL5 物件導向程式設計(OOP)入門:Class、繼承與封裝
📌 本文重點
學習 MQL5 的物件導向程式設計:定義 Class、建構函式、繼承、封裝與多型。讓你的 EA 更模組化、易維護。
學習 MQL5 的物件導向程式設計:定義 Class、建構函式、繼承、封裝與多型。讓你的 EA 更模組化、易維護。
為什麼需要 OOP?
當 EA 邏輯複雜化,單純函數式寫法難以維護。MQL5 完整支援 C++ 風格的 OOP,讓你將數據與行為封裝在一起,大幅提升可重複使用性。
定義與使用 Class
class CMovingAverage
{
private:
int m_handle;
int m_period;
string m_symbol;
public:
CMovingAverage(int period, string symbol = NULL)
{
m_period = period;
m_symbol = (symbol == NULL) ? _Symbol : symbol;
m_handle = iMA(m_symbol, _Period, m_period, 0, MODE_SMA, PRICE_CLOSE);
}
~CMovingAverage()
{
if (m_handle != INVALID_HANDLE)
IndicatorRelease(m_handle);
}
double GetValue(int index = 1)
{
double buf[1];
ArraySetAsSeries(buf, true);
if (CopyBuffer(m_handle, 0, index, 1, buf) < 1) return EMPTY_VALUE;
return buf[0];
}
bool IsValid() { return m_handle != INVALID_HANDLE; }
int GetPeriod() { return m_period; }
};
// 在 EA 中使用
CMovingAverage *g_fastMA = NULL;
CMovingAverage *g_slowMA = NULL;
int OnInit()
{
g_fastMA = new CMovingAverage(10);
g_slowMA = new CMovingAverage(30);
if (!g_fastMA.IsValid() || !g_slowMA.IsValid()) return INIT_FAILED;
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
delete g_fastMA;
delete g_slowMA;
}
void OnTick()
{
double fast = g_fastMA.GetValue(1), slow = g_slowMA.GetValue(1);
double fastPrev = g_fastMA.GetValue(2), slowPrev = g_slowMA.GetValue(2);
if (fastPrev <= slowPrev && fast > slow) Print("金叉!");
if (fastPrev >= slowPrev && fast < slow) Print("死叉!");
}
繼承(Inheritance)
// 基礎策略類別
class CBaseStrategy
{
protected:
string m_symbol;
int m_magic;
double m_lots;
public:
CBaseStrategy(string symbol, int magic, double lots)
: m_symbol(symbol), m_magic(magic), m_lots(lots) {}
virtual int GetSignal() { return 0; } // 子類別覆寫
bool OpenBuy(double sl=0, double tp=0)
{
MqlTradeRequest req={}; MqlTradeResult res={};
req.action=TRADE_ACTION_DEAL; req.symbol=m_symbol;
req.volume=m_lots; req.type=ORDER_TYPE_BUY;
req.price=SymbolInfoDouble(m_symbol,SYMBOL_ASK);
req.sl=sl; req.tp=tp; req.magic=m_magic;
return OrderSend(req,res);
}
};
// 繼承並實作具體策略
class CMACrossStrategy : public CBaseStrategy
{
private:
CMovingAverage *m_fast, *m_slow;
public:
CMACrossStrategy(string sym, int magic, double lots, int f, int s)
: CBaseStrategy(sym, magic, lots)
{
m_fast = new CMovingAverage(f, sym);
m_slow = new CMovingAverage(s, sym);
}
~CMACrossStrategy() { delete m_fast; delete m_slow; }
virtual int GetSignal() override
{
double f1=m_fast.GetValue(1), f2=m_fast.GetValue(2);
double s1=m_slow.GetValue(1), s2=m_slow.GetValue(2);
if (f2<=s2 && f1>s1) return 1;
if (f2>=s2 && f1<s1) return -1;
return 0;
}
};
封裝原則
private:只有類別內部可存取。protected:類別與子類別可存取。public:對外介面。
class CRiskManager
{
private:
double m_maxRiskPct;
public:
CRiskManager(double maxRiskPct = 1.0) : m_maxRiskPct(maxRiskPct) {}
double CalculateLots(int slPips)
{
double riskAmt = AccountInfoDouble(ACCOUNT_BALANCE) * m_maxRiskPct / 100.0;
double tickVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSz = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double pointVal = tickVal * (tickSz / SymbolInfoDouble(_Symbol, SYMBOL_POINT));
if (pointVal <= 0 || slPips <= 0) return 0.01;
return NormalizeDouble(riskAmt / (slPips * pointVal), 2);
}
void SetMaxRisk(double pct) { if (pct>0 && pct<=10) m_maxRiskPct=pct; }
double GetMaxRisk() { return m_maxRiskPct; }
};
重要提醒
- 用
new建立物件後,務必在OnDeinit()中delete,避免記憶體洩漏 - 善用
virtual讓子類別覆寫行為 - 每個類別只負責一個職責(單一責任原則)
本文由 James Lee 撰寫。內容僅供教育目的,不構成投資建議。