MQL5 多時間框架(MTF)分析完整指南:從三重濾網到實戰策略
學習如何在 MQL5 中實現多時間框架(Multi-TimeFrame, MTF)分析:正確呼叫跨週期的 iMA/iRSI/iATR、三重濾網策略實作、避免未來函數(Lookahead Bias),以及 MTF EA 的完整架構設計。
MQL5 多時間框架分析完整指南:從三重濾網到實戰策略
> 「看日線圖交易就像用望遠鏡看風景,看1分鐘圖交易就像用顯微鏡看細胞。聰明的人兩者都用。」 — 多時間框架分析的智慧
前言:為什麼專業交易者都用多時間框架?
我曾經問一個外匯基金經理:「你們的交易員最常犯的錯誤是什麼?」
他笑著說:「新手交易者就像近視的人不戴眼鏡——他們只看一個時間框架,然後奇怪為什麼總是買在高點、賣在低點。」
今天,我想分享如何「戴上眼鏡」,同時看清市場的宏觀趨勢和微觀機會。
第一部分:多時間框架的基本原理
1.1 時間框架的層級關係
在實際交易中,我通常使用三個時間框架:
//+------------------------------------------------------------------+
//| 時間框架層級定義 |
//+------------------------------------------------------------------+
enum TIMEFRAME_LEVEL
{
TF_TREND, // 趨勢時間框架(最大)
TF_SWING, // 擺動時間框架(中間)
TF_ENTRY // 進場時間框架(最小)
};
class TimeframeHierarchy
{
private:
ENUM_TIMEFRAMES trendTF; // 趨勢時間框架
ENUM_TIMEFRAMES swingTF; // 擺動時間框架
ENUM_TIMEFRAMES entryTF; // 進場時間框架
public:
// 根據基礎時間框架自動設定層級
TimeframeHierarchy(ENUM_TIMEFRAMES baseTF = PERIOD_H1)
{
SetTimeframeHierarchy(baseTF);
}
void SetTimeframeHierarchy(ENUM_TIMEFRAMES baseTF)
{
// 基礎時間框架作為擺動時間框架
swingTF = baseTF;
// 自動計算趨勢和進場時間框架
switch(baseTF)
{
case PERIOD_M1:
trendTF = PERIOD_M5;
entryTF = PERIOD_M1;
break;
case PERIOD_M5:
trendTF = PERIOD_M15;
entryTF = PERIOD_M1;
break;
case PERIOD_M15:
trendTF = PERIOD_H1;
entryTF = PERIOD_M5;
break;
case PERIOD_H1:
trendTF = PERIOD_H4;
entryTF = PERIOD_M15;
break;
case PERIOD_H4:
trendTF = PERIOD_D1;
entryTF = PERIOD_H1;
break;
case PERIOD_D1:
trendTF = PERIOD_W1;
entryTF = PERIOD_H4;
break;
case PERIOD_W1:
trendTF = PERIOD_MN1;
entryTF = PERIOD_D1;
break;
default:
trendTF = PERIOD_H4;
swingTF = PERIOD_H1;
entryTF = PERIOD_M15;
}
Print("時間框架層級設定:");
Print("趨勢時間框架: ", GetTimeframeName(trendTF));
Print("擺動時間框架: ", GetTimeframeName(swingTF));
Print("進場時間框架: ", GetTimeframeName(entryTF));
Print("比例: 1:", GetTimeframeRatio(), "倍");
}
// 取得時間框架比例
int GetTimeframeRatio()
{
int trendMinutes = PeriodSeconds(trendTF) / 60;
int swingMinutes = PeriodSeconds(swingTF) / 60;
if(swingMinutes > 0)
return trendMinutes / swingMinutes;
return 4; // 默認4倍
}
string GetTimeframeName(ENUM_TIMEFRAMES tf)
{
switch(tf)
{
case PERIOD_M1: return "M1";
case PERIOD_M5: return "M5";
case PERIOD_M15: return "M15";
case PERIOD_M30: return "M30";
case PERIOD_H1: return "H1";
case PERIOD_H4: return "H4";
case PERIOD_D1: return "D1";
case PERIOD_W1: return "W1";
case PERIOD_MN1: return "MN1";
default: return "Unknown";
}
}
// 取得各時間框架
ENUM_TIMEFRAMES GetTrendTF() { return trendTF; }
ENUM_TIMEFRAMES GetSwingTF() { return swingTF; }
ENUM_TIMEFRAMES GetEntryTF() { return entryTF; }
};
// 使用範例
TimeframeHierarchy g_tfHierarchy(PERIOD_H1);
void PrintTimeframeInfo()
{
Print("=== 多時間框架分析設定 ===");
Print("趨勢框架: ", g_tfHierarchy.GetTimeframeName(g_tfHierarchy.GetTrendTF()));
Print("擺動框架: ", g_tfHierarchy.GetTimeframeName(g_tfHierarchy.GetSwingTF()));
Print("進場框架: ", g_tfHierarchy.GetTimeframeName(g_tfHierarchy.GetEntryTF()));
Print("時間比例: 1:", g_tfHierarchy.GetTimeframeRatio());
}
1.2 黃金比例:4-6倍時間框架差距
從經驗中我發現,時間框架之間保持4-6倍的差距效果最好:
– 太小(如M1→M5):兩個時間框架看到的幾乎是同一件事
– 太大(如M5→H4):失去連貫性,難以對齊
– 剛好(如M15→H1→H4):能看到清晰的層級關係
// 檢查時間框架比例是否合理
bool CheckTimeframeRatio(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2)
{
int minutes1 = PeriodSeconds(tf1) / 60;
int minutes2 = PeriodSeconds(tf2) / 60;
if(minutes1 == 0 || minutes2 == 0)
return false;
double ratio = (double)MathMax(minutes1, minutes2) / MathMin(minutes1, minutes2);
Print("時間框架比例檢查:");
Print(tf1, " (", minutes1, "分鐘) : ", tf2, " (", minutes2, "分鐘)");
Print("比例: ", DoubleToString(ratio, 1), "倍");
// 理想比例在4-6倍之間
if(ratio >= 4.0 && ratio <= 6.0)
{
Print("✅ 比例合理");
return true;
}
else if(ratio >= 3.0 && ratio <= 8.0)
{
Print("⚠️ 比例尚可,但不是最優");
return true;
}
else
{
Print("❌ 比例不合理");
Print("建議: 調整到4-6倍之間");
return false;
}
}
第二部分:三重濾網交易系統實作
2.1 第一重濾網:趨勢判斷(最大時間框架)
//+------------------------------------------------------------------+
//| 第一重濾網:趨勢判斷 |
//+------------------------------------------------------------------+
class FirstFilter_Trend
{
private:
ENUM_TIMEFRAMES trendTF;
int maHandle;
int trendStrength;
public:
FirstFilter_Trend(ENUM_TIMEFRAMES tf) : trendTF(tf), trendStrength(0)
{
// 使用200期EMA判斷長期趨勢
maHandle = iMA(_Symbol, trendTF, 200, 0, MODE_EMA, PRICE_CLOSE);
}
// 判斷趨勢方向
ENUM_TREND_DIRECTION GetTrendDirection()
{
double maCurrent[3];
if(CopyBuffer(maHandle, 0, 0, 3, maCurrent) < 3)
return TREND_UNKNOWN;
// 簡單趨勢判斷:價格在MA之上為上升趨勢,之下為下降趨勢
double price = iClose(_Symbol, trendTF, 0);
if(price > maCurrent[0] && maCurrent[0] > maCurrent[1])
{
trendStrength = CalculateTrendStrength(maCurrent);
Print("第一重濾網: 上升趨勢 (強度: ", trendStrength, "/100)");
return TREND_UP;
}
else if(price < maCurrent[0] && maCurrent[0] < maCurrent[1])
{
trendStrength = CalculateTrendStrength(maCurrent);
Print("第一重濾網: 下降趨勢 (強度: ", trendStrength, "/100)");
return TREND_DOWN;
}
else
{
Print("第一重濾網: 盤整或趨勢不明確");
return TREND_SIDEWAYS;
}
}
// 取得趨勢強度
int GetTrendStrength() { return trendStrength; }
// 趨勢是否足夠強烈可以交易?
bool IsTrendStrongEnough(int minStrength = 30)
{
return (trendStrength >= minStrength);
}
private:
int CalculateTrendStrength(double &maValues[])
{
// 計算趨勢強度 (0-100)
// 基於MA的角度和價格與MA的距離
if(ArraySize(maValues) < 3)
return 0;
// 1. MA角度(斜率)
double maAngle = (maValues[0] - maValues[2]) / 2.0;
double normalizedAngle = MathAbs(maAngle) * 10000; // 放大以便計算
// 2. 價格與MA的距離
double price = iClose(_Symbol, trendTF, 0);
double distance = MathAbs(price - maValues[0]) / maValues[0] * 100;
// 3. 綜合計算
int strength = (int)(normalizedAngle * 0.7 + distance * 0.3);
return MathMin(strength, 100);
}
};
// 趨勢方向枚舉
enum ENUM_TREND_DIRECTION
{
TREND_UP, // 上升趨勢
TREND_DOWN, // 下降趨勢
TREND_SIDEWAYS, // 盤整
TREND_UNKNOWN // 未知
};
2.2 第二重濾網:擺動信號(中間時間框架)
//+------------------------------------------------------------------+
//| 第二重濾網:擺動信號 |
//+------------------------------------------------------------------+
class SecondFilter_Swing
{
private:
ENUM_TIMEFRAMES swingTF;
ENUM_TREND_DIRECTION mainTrend;
// 指標句柄
int macdHandle;
int rsiHandle;
int stochHandle;
public:
SecondFilter_Swing(ENUM_TIMEFRAMES tf) : swingTF(tf), mainTrend(TREND_UNKNOWN)
{
macdHandle = iMACD(_Symbol, swingTF, 12, 26, 9, PRICE_CLOSE);
rsiHandle = iRSI(_Symbol, swingTF, 14, PRICE_CLOSE);
stochHandle = iStochastic(_Symbol, swingTF, 5, 3, 3, MODE_SMA, STO_LOWHIGH);
}
// 設定主要趨勢方向(從第一重濾網)
void SetMainTrend(ENUM_TREND_DIRECTION trend)
{
mainTrend = trend;
}
// 尋找順勢擺動信號
ENUM_SWING_SIGNAL GetSwingSignal()
{
if(mainTrend == TREND_UNKNOWN || mainTrend == TREND_SIDEWAYS)
{
Print("第二重濾網: 主要趨勢不明確,跳過");
return SWING_NONE;
}
// 取得指標數值
double macdMain[2], macdSignal[2];
double rsi[2];
double stochMain[2], stochSignal[2];
if(CopyBuffer(macdHandle, MAIN_LINE, 0, 2, macdMain) < 2 ||
CopyBuffer(macdHandle, SIGNAL_LINE, 0, 2, macdSignal) < 2 ||
CopyBuffer(rsiHandle, 0, 0, 2, rsi) < 2 ||
CopyBuffer(stochHandle, MAIN_LINE, 0, 2, stochMain) < 2 ||
CopyBuffer(stochHandle, SIGNAL_LINE, 0, 2, stochSignal) < 2)
{
return SWING_NONE;
}
// 根據主要趨勢尋找信號
if(mainTrend == TREND_UP)
{
// 上升趨勢中尋找買入信號(回調買入)
if(IsBuySignal(macdMain, macdSignal, rsi, stochMain, stochSignal))
{
Print("第二重濾網: 發現上升趨勢中的買入信號");
return SWING_BUY;
}
}
else if(mainTrend == TREND_DOWN)
{
// 下降趨勢中尋找賣出信號(反彈賣出)
if(IsSellSignal(macdMain, macdSignal, rsi, stochMain, stochSignal))
{
Print("第二重濾網: 發現下降趨勢中的賣出信號");
return SWING_SELL;
}
}
return SWING_NONE;
}
private:
bool IsBuySignal(double &macdMain[], double &macdSignal[],
double &rsi[], double &stochMain[], double &stochSignal[])
{
// 買入信號條件:
// 1. MACD在零軸之上或剛從下方穿越
// 2. RSI從超賣區反彈(<30回升)
// 3. 隨機指標從超賣區金叉
bool condition1 = (macdMain[0] > 0) || (macdMain[0] > macdSignal[0] && macdMain[1] <= macdSignal[1]);
bool condition2 = (rsi[0] > 30 && rsi[0] > rsi[1] && rsi[1] < 30);
bool condition3 = (stochMain[0] > stochSignal[0] && stochMain[1] <= stochSignal[1] && stochMain[0] < 80);
int score = 0;
if(condition1) score++;
if(condition2) score++;
if(condition3) score++;
Print("買入信號評分: ", score, "/3");
return (score >= 2); // 至少滿足2個條件
}
bool IsSellSignal(double &macdMain[], double &macdSignal[],
double &rsi[], double &stochMain[], double &stochSignal[])
{
// 賣出信號條件:
// 1. MACD在零軸之下或剛從上方穿越
// 2. RSI從超買區回落(>70下降)
// 3. 隨機指標從超買區死叉
bool condition1 = (macdMain[0] < 0) || (macdMain[0] < macdSignal[0] && macdMain[1] >= macdSignal[1]);
bool condition2 = (rsi[0] < 70 && rsi[0] < rsi[1] && rsi[1] > 70);
bool condition3 = (stochMain[0] < stochSignal[0] && stochMain[1] >= stochSignal[1] && stochMain[0] > 20);
int score = 0;
if(condition1) score++;
if(condition2) score++;
if(condition3) score++;
Print("賣出信號評分: ", score, "/3");
return (score >= 2); // 至少滿足2個條件
}
};
// 擺動信號枚舉
enum ENUM_SWING_SIGNAL
{
SWING_NONE, // 無信號
SWING_BUY, // 買入信號
SWING_SELL // 賣出信號
};
2.3 第三重濾網:精確進場(最小時間框架)
//+------------------------------------------------------------------+
//| 第三重濾網:精確進場 |
//+------------------------------------------------------------------+
class ThirdFilter_Entry
{
private:
ENUM_TIMEFRAMES entryTF;
ENUM_TREND_DIRECTION mainTrend;
ENUM_SWING_SIGNAL swingSignal;
// 價格行為工具
struct PriceAction
{
bool isPinBar; // 是否為針形線
bool isEngulfing; // 是否為吞噬線
bool isInsideBar; // 是否為內包線
bool supportResistance; // 是否在支撐阻力位
double strength; // 信號強度 (0-100)
};
public:
ThirdFilter_Entry(ENUM_TIMEFRAMES tf) : entryTF(tf),
mainTrend(TREND_UNKNOWN), swingSignal(SWING_NONE) {}
// 設定前兩重濾網的結果
void SetFilters(ENUM_TREND_DIRECTION trend, ENUM_SWING_SIGNAL swing)
{
mainTrend = trend;
swingSignal = swing;
}
// 尋找精確進場點
ENUM_ENTRY_SIGNAL GetEntrySignal()
{
if(mainTrend == TREND_UNKNOWN || swingSignal == SWING_NONE)
{
Print("第三重濾網: 前兩重濾網無信號,跳過");
return ENTRY_NONE;
}
// 檢查價格行為
PriceAction pa = AnalyzePriceAction();
// 檢查支撐阻力
bool nearKeyLevel = CheckSupportResistance();
// 檢查動量
bool momentumConfirm = CheckMomentum();
// 綜合判斷
if(ShouldEnter(pa, nearKeyLevel, momentumConfirm))
{
if(swingSignal == SWING_BUY)
{
Print("第三重濾網: 發現精確買入點");
Print("價格行為強度: ", DoubleToString(pa.strength, 1), "/100");
return ENTRY_BUY;
}
else if(swingSignal == SWING_SELL)
{
Print("第三重濾網: 發現精確賣出點");
Print("價格行為強度: ", DoubleToString(pa.strength, 1), "/100");
return ENTRY_SELL;
}
}
return ENTRY_NONE;
}
// 取得建議的進場價格和止損
bool GetEntryDetails(ENUM_ENTRY_SIGNAL signal, double &entryPrice, double &stopLoss, double &takeProfit)
{
if(signal == ENTRY_NONE)
return false;
// 根據信號類型設定
if(signal == ENTRY_BUY)
{
entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// 止損設在最近支撐下方
stopLoss = FindNearestSupport();
// 止盈基於風險回報比
double risk = entryPrice - stopLoss;
takeProfit = entryPrice + risk * 2; // 1:2風險回報比
Print("買入進場細節:");
Print("進場價: ", DoubleToString(entryPrice, 5));
Print("止損價: ", DoubleToString(stopLoss, 5));
Print("止盈價: ", DoubleToString(takeProfit, 5));
Print("風險點數: ", DoubleToString((entryPrice - stopLoss) / SymbolInfoDouble(_Symbol, SYMBOL_POINT), 0));
}
else if(signal == ENTRY_SELL)
{
entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 止損設在最近阻力上方
stopLoss = FindNearestResistance();
// 止盈基於風險回報比
double risk = stopLoss - entryPrice;
takeProfit = entryPrice - risk * 2; // 1:2風險回報比
Print("賣出進場細節:");
Print("進場價: ", DoubleToString(entryPrice, 5));
Print("止損價: ", DoubleToString(stopLoss, 5));
Print("止盈價: ", DoubleToString(takeProfit, 5));
Print("風險點數: ", DoubleToString((stopLoss - entryPrice) / SymbolInfoDouble(_Symbol, SYMBOL_POINT), 0));
}
return true;
}
private:
PriceAction AnalyzePriceAction()
{
PriceAction pa = {false, false, false, false, 0.0};
// 取得最近幾根K線
MqlRates rates[5];
if(CopyRates(_Symbol, entryTF, 0, 5, rates) < 5)
return pa;
// 檢查針形線 (Pin Bar)
pa.isPinBar = IsPinBar(rates[0]);
// 檢查吞噬線 (Engulfing)
pa.isEngulfing = IsEngulfing(rates[0], rates[1]);
// 檢查內包線 (Inside Bar)
pa.isInsideBar = IsInsideBar(rates[0], rates[1]);
// 計算信號強度
pa.strength = CalculateSignalStrength(pa);
return pa;
}
bool IsPinBar(MqlRates ¤t)
{
// 針形線條件:長影線,短實體
double bodySize = MathAbs(current.close - current.open);
double totalRange = current.high - current.low;
if(totalRange == 0)
return false;
double shadowRatio = (totalRange - bodySize) / totalRange;
// 影線佔比超過60%,實體佔比小於40%
return (shadowRatio > 0.6 && bodySize / totalRange < 0.4);
}
bool IsEngulfing(MqlRates ¤t, MqlRates &previous)
{
// 吞噬線條件:當前K線完全吞噬前一根K線
bool bullishEngulfing = (current.close > current.open && // 陽線
previous.close < previous.open && // 前一根陰線
current.open < previous.close && // 開盤低於前收盤
current.close > previous.open); // 收盤高於前開盤
bool bearishEngulfing = (current.close < current.open && // 陰線
previous.close > previous.open && // 前一根陽線
current.open > previous.close && // 開盤高於前收盤
current.close < previous.open); // 收盤低於前開盤
return (bullishEngulfing || bearishEngulfing);
}
bool IsInsideBar(MqlRates ¤t, MqlRates &previous)
{
// 內包線條件:當前K線範圍完全在前一根K線範圍內
return (current.high <= previous.high &&
current.low >= previous.low);
}
double CalculateSignalStrength(PriceAction &pa)
{
double strength = 0;
if(pa.isPinBar) strength += 40;
if(pa.isEngulfing) strength += 35;
if(pa.isInsideBar) strength += 25;
// 根據位置調整強度
if(pa.supportResistance) strength *= 1.2;
return MathMin(strength, 100);
}
bool CheckSupportResistance()
{
// 檢查是否在關鍵支撐阻力位附近
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 這裡應該實作具體的支撐阻力檢測邏輯
// 例如:檢查前高前低、移動平均線、斐波那契水平等
return false; // 簡化實現
}
bool CheckMomentum()
{
// 檢查動量是否確認信號
// 使用RSI、MACD等指標確認
return true; // 簡化實現
}
bool ShouldEnter(PriceAction &pa, bool nearKeyLevel, bool momentumConfirm)
{
// 綜合判斷是否應該進場
int score = 0;
int maxScore = 0;
// 價格行為分數
if(pa.strength >= 60) score += 2;
else if(pa.strength >= 30) score += 1;
maxScore += 2;
// 關鍵位置分數
if(nearKeyLevel) score += 2;
maxScore += 2;
// 動量確認分數
if(momentumConfirm) score += 2;
maxScore += 2;
// 趨勢一致性分數(已在前面檢查)
score += 2;
maxScore += 2;
double passRate = (double)score / maxScore;
Print("進場決策評分: ", score, "/", maxScore, " (", DoubleToString(passRate * 100, 0), "%)");
return (passRate >= 0.75); // 需要75%以上的分數
}
double FindNearestSupport()
{
// 尋找最近的支撐位
// 這裡應該實作具體的支撐位檢測邏輯
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
return currentPrice * 0.99; // 簡化實現:設在當前價格下方1%
}
double FindNearestResistance()
{
// 尋找最近的阻力位
// 這裡應該實作具體的阻力位檢測邏輯
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
return currentPrice * 1.01; // 簡化實現:設在當前價格上方1%
}
};
// 進場信號枚舉
enum ENUM_ENTRY_SIGNAL
{
ENTRY_NONE, // 無信號
ENTRY_BUY, // 買入
ENTRY_SELL // 賣出
};
2.4 完整的三重濾網系統
//+------------------------------------------------------------------+
//| 完整的三重濾網交易系統 |
//+------------------------------------------------------------------+
class TripleScreenTradingSystem
{
private:
TimeframeHierarchy* tfHierarchy;
FirstFilter_Trend* firstFilter;
SecondFilter_Swing* secondFilter;
ThirdFilter_Entry* thirdFilter;
// 交易狀態
bool isInitialized;
ENUM_TREND_DIRECTION currentTrend;
ENUM_SWING_SIGNAL currentSwing;
ENUM_ENTRY_SIGNAL currentEntry;
public:
TripleScreenTradingSystem(ENUM_TIMEFRAMES baseTF = PERIOD_H1)
{
Print("=== 三重濾網交易系統初始化 ===");
// 建立時間框架層級
tfHierarchy = new TimeframeHierarchy(baseTF);
// 建立三重濾網
firstFilter = new FirstFilter_Trend(tfHierarchy->GetTrendTF());
secondFilter = new SecondFilter_Swing(tfHierarchy->GetSwingTF());
thirdFilter = new ThirdFilter_Entry(tfHierarchy->GetEntryTF());
isInitialized = true;
currentTrend = TREND_UNKNOWN;
currentSwing = SWING_NONE;
currentEntry = ENTRY_NONE;
Print("系統初始化完成");
}
~TripleScreenTradingSystem()
{
delete tfHierarchy;
delete firstFilter;
delete secondFilter;
delete thirdFilter;
}
// 執行三重濾網分析
ENUM_ENTRY_SIGNAL Analyze()
{
if(!isInitialized)
{
Print("錯誤: 系統未初始化");
return ENTRY_NONE;
}
Print("\n=== 開始三重濾網分析 ===");
// 第一重濾網:趨勢判斷
Print("\n[第一重濾網] 趨勢分析...");
currentTrend = firstFilter->GetTrendDirection();
if(!firstFilter->IsTrendStrongEnough(30))
{
Print("趨勢強度不足,跳過後續分析");
return ENTRY_NONE;
}
// 第二重濾網:擺動信號
Print("\n[第二重濾網] 擺動信號分析...");
secondFilter->SetMainTrend(currentTrend);
currentSwing = secondFilter->GetSwingSignal();
if(currentSwing == SWING_NONE)
{
Print("無擺動信號,跳過進場分析");
return ENTRY_NONE;
}
// 第三重濾網:精確進場
Print("\n[第三重濾網] 進場點分析...");
thirdFilter->SetFilters(currentTrend, currentSwing);
currentEntry = thirdFilter->GetEntrySignal();
if(currentEntry != ENTRY_NONE)
{
Print("\n🎉 三重濾網分析完成:發現交易機會!");
Print("趨勢: ", GetTrendName(currentTrend));
Print("擺動信號: ", GetSwingName(currentSwing));
Print("進場信號: ", GetEntryName(currentEntry));
}
else
{
Print("\n三重濾網分析完成:無合適進場點");
}
return currentEntry;
}
// 取得進場細節
bool GetTradeDetails(double &entryPrice, double &stopLoss, double &takeProfit)
{
if(currentEntry == ENTRY_NONE)
return false;
return thirdFilter->GetEntryDetails(currentEntry, entryPrice, stopLoss, takeProfit);
}
// 取得分析結果
ENUM_TREND_DIRECTION GetTrend() { return currentTrend; }
ENUM_SWING_SIGNAL GetSwing() { return currentSwing; }
ENUM_ENTRY_SIGNAL GetEntry() { return currentEntry; }
private:
string GetTrendName(ENUM_TREND_DIRECTION trend)
{
switch(trend)
{
case TREND_UP: return "上升";
case TREND_DOWN: return "下降";
case TREND_SIDEWAYS: return "盤整";
default: return "未知";
}
}
string GetSwingName(ENUM_SWING_SIGNAL swing)
{
switch(swing)
{
case SWING_BUY: return "買入";
case SWING_SELL: return "賣出";
default: return "無";
}
}
string GetEntryName(ENUM_ENTRY_SIGNAL entry)
{
switch(entry)
{
case ENTRY_BUY: return "買入";
case ENTRY_SELL: return "賣出";
default: return "無";
}
}
};
// 全域三重濾網系統實例
TripleScreenTradingSystem* g_tripleScreen = NULL;
// 初始化函數
bool InitializeTripleScreen(ENUM_TIMEFRAMES baseTF = PERIOD_H1)
{
if(g_tripleScreen != NULL)
delete g_tripleScreen;
g_tripleScreen = new TripleScreenTradingSystem(baseTF);
return (g_tripleScreen != NULL);
}
// 分析函數
ENUM_ENTRY_SIGNAL RunTripleScreenAnalysis()
{
if(g_tripleScreen == NULL)
{
Print("錯誤: 三重濾網系統未初始化");
return ENTRY_NONE;
}
return g_tripleScreen->Analyze();
}
第三部分:進階多時間框架技巧
3.1 時間框架對齊技巧
//+------------------------------------------------------------------+
//| 時間框架對齊檢測 |
//+------------------------------------------------------------------+
class TimeframeAlignment
{
public:
// 檢查多時間框架是否對齊
static bool CheckAlignment(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3)
{
Print("=== 時間框架對齊檢查 ===");
bool aligned = true;
// 檢查K線時間對齊
if(!CheckCandleAlignment(tf1, tf2))
{
Print("❌ ", tf1, " 和 ", tf2, " K線時間未對齊");
aligned = false;
}
if(!CheckCandleAlignment(tf2, tf3))
{
Print("❌ ", tf2, " 和 ", tf3, " K線時間未對齊");
aligned = false;
}
// 檢查趨勢方向一致性
if(!CheckTrendConsistency(tf1, tf2, tf3))
{
Print("❌ 趨勢方向不一致");
aligned = false;
}
if(aligned)
{
Print("✅ 所有時間框架對齊良好");
}
else
{
Print("⚠️ 時間框架未完全對齊,可能影響信號質量");
}
return aligned;
}
// 取得最佳對齊的時間框架組合
static void FindBestAlignment(ENUM_TIMEFRAMES baseTF,
ENUM_TIMEFRAMES &bestTrendTF,
ENUM_TIMEFRAMES &bestSwingTF,
ENUM_TIMEFRAMES &bestEntryTF)
{
Print("尋找最佳時間框架組合...");
// 候選時間框架
ENUM_TIMEFRAMES candidateTFs[] = {
PERIOD_M5, PERIOD_M15, PERIOD_M30,
PERIOD_H1, PERIOD_H4, PERIOD_D1
};
double bestScore = -1;
// 測試所有可能的組合
for(int i = 0; i < ArraySize(candidateTFs); i++)
{
for(int j = 0; j < ArraySize(candidateTFs); j++)
{
for(int k = 0; k < ArraySize(candidateTFs); k++)
{
if(i == j || i == k || j == k)
continue;
ENUM_TIMEFRAMES tf1 = candidateTFs[i];
ENUM_TIMEFRAMES tf2 = candidateTFs[j];
ENUM_TIMEFRAMES tf3 = candidateTFs[k];
// 確保時間框架大小順序正確
if(PeriodSeconds(tf1) <= PeriodSeconds(tf2) ||
PeriodSeconds(tf2) <= PeriodSeconds(tf3))
continue;
// 計算對齊分數
double score = CalculateAlignmentScore(tf1, tf2, tf3);
if(score > bestScore)
{
bestScore = score;
bestTrendTF = tf1;
bestSwingTF = tf2;
bestEntryTF = tf3;
}
}
}
}
Print("最佳組合: ", bestTrendTF, " → ", bestSwingTF, " → ", bestEntryTF);
Print("對齊分數: ", DoubleToString(bestScore, 2), "/100");
}
private:
static bool CheckCandleAlignment(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2)
{
// 檢查兩個時間框架的K線時間是否對齊
// 例如:H1的K線應該對齊H4的K線開始時間
datetime time1 = iTime(_Symbol, tf1, 0);
datetime time2 = iTime(_Symbol, tf2, 0);
// 較大時間框架的K線開始時間應該是較小時間框架K線開始時間的倍數
int seconds1 = PeriodSeconds(tf1);
int seconds2 = PeriodSeconds(tf2);
if(seconds2 > seconds1)
{
// tf2是較大的時間框架
return (time1 % seconds2 == 0);
}
else
{
// tf1是較大的時間框架
return (time2 % seconds1 == 0);
}
}
static bool CheckTrendConsistency(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3)
{
// 檢查三個時間框架的趨勢方向是否一致
// 簡化實現:使用移動平均線判斷趨勢
int maHandle1 = iMA(_Symbol, tf1, 50, 0, MODE_SMA, PRICE_CLOSE);
int maHandle2 = iMA(_Symbol, tf2, 50, 0, MODE_SMA, PRICE_CLOSE);
int maHandle3 = iMA(_Symbol, tf3, 50, 0, MODE_SMA, PRICE_CLOSE);
double ma1[1], ma2[1], ma3[1];
double price1 = iClose(_Symbol, tf1, 0);
double price2 = iClose(_Symbol, tf2, 0);
double price3 = iClose(_Symbol, tf3, 0);
if(CopyBuffer(maHandle1, 0, 0, 1, ma1) > 0 &&
CopyBuffer(maHandle2, 0, 0, 1, ma2) > 0 &&
CopyBuffer(maHandle3, 0, 0, 1, ma3) > 0)
{
bool trend1 = (price1 > ma1[0]); // true = 上升
bool trend2 = (price2 > ma2[0]);
bool trend3 = (price3 > ma3[0]);
// 檢查一致性:至少兩個時間框架趨勢相同
int upCount = 0;
if(trend1) upCount++;
if(trend2) upCount++;
if(trend3) upCount++;
return (upCount >= 2 || upCount <= 1); // 大多數一致
}
return false;
}
static double CalculateAlignmentScore(ENUM_TIMEFRAMES tf1, ENUM_TIMEFRAMES tf2, ENUM_TIMEFRAMES tf3)
{
double score = 0;
// 1. 時間比例分數 (40%)
double ratio12 = (double)PeriodSeconds(tf1) / PeriodSeconds(tf2);
double ratio23 = (double)PeriodSeconds(tf2) / PeriodSeconds(tf3);
double ratioScore = 0;
if(ratio12 >= 4 && ratio12 <= 6) ratioScore += 20;
if(ratio23 >= 4 && ratio23 <= 6) ratioScore += 20;
score += ratioScore;
// 2. K線對齊分數 (30%)
double alignmentScore = 0;
if(CheckCandleAlignment(tf1, tf2)) alignmentScore += 15;
if(CheckCandleAlignment(tf2, tf3)) alignmentScore += 15;
score += alignmentScore;
// 3. 趨勢一致性分數 (30%)
if(CheckTrendConsistency(tf1, tf2, tf3))
score += 30;
return score;
}
};
3.2 動態時間框架調整
//+------------------------------------------------------------------+
//| 動態時間框架調整系統 |
//+------------------------------------------------------------------+
class DynamicTimeframeAdjustment
{
private:
ENUM_TIMEFRAMES currentTrendTF;
ENUM_TIMEFRAMES currentSwingTF;
ENUM_TIMEFRAMES currentEntryTF;
// 市場狀態
enum MARKET_CONDITION
{
MARKET_TRENDING, // 趨勢市場
MARKET_RANGING, // 區間市場
MARKET_VOLATILE, // 高波動市場
MARKET_CALM // 低波動市場
};
MARKET_CONDITION currentCondition;
public:
DynamicTimeframeAdjustment()
{
// 初始設定
currentTrendTF = PERIOD_H4;
currentSwingTF = PERIOD_H1;
currentEntryTF = PERIOD_M15;
currentCondition = MARKET_TRENDING;
}
// 根據市場狀況調整時間框架
void AdjustTimeframes()
{
MARKET_CONDITION newCondition = AnalyzeMarketCondition();
if(newCondition != currentCondition)
{
Print("市場狀況改變: ", GetConditionName(currentCondition),
" → ", GetConditionName(newCondition));
AdjustForCondition(newCondition);
currentCondition = newCondition;
}
}
// 取得當前時間框架
ENUM_TIMEFRAMES GetTrendTF() { return currentTrendTF; }
ENUM_TIMEFRAMES GetSwingTF() { return currentSwingTF; }
ENUM_TIMEFRAMES GetEntryTF() { return currentEntryTF; }
private:
MARKET_CONDITION AnalyzeMarketCondition()
{
// 分析當前市場狀況
// 1. 檢查波動性
double atrValue = GetATRValue(PERIOD_H1, 14);
double avgATR = GetAverageATR(PERIOD_H1, 14, 100);
// 2. 檢查趨勢強度
double trendStrength = CalculateTrendStrength(PERIOD_H4);
// 3. 判斷市場狀況
if(trendStrength > 60)
{
return MARKET_TRENDING;
}
else if(atrValue > avgATR * 1.5)
{
return MARKET_VOLATILE;
}
else if(atrValue < avgATR * 0.5)
{
return MARKET_CALM;
}
else
{
return MARKET_RANGING;
}
}
void AdjustForCondition(MARKET_CONDITION condition)
{
switch(condition)
{
case MARKET_TRENDING:
// 趨勢市場:使用較大時間框架,跟隨趨勢
currentTrendTF = PERIOD_H4;
currentSwingTF = PERIOD_H1;
currentEntryTF = PERIOD_M15;
Print("調整為趨勢市場設定");
break;
case MARKET_RANGING:
// 區間市場:使用較小時間框架,捕捉區間波動
currentTrendTF = PERIOD_H1;
currentSwingTF = PERIOD_M15;
currentEntryTF = PERIOD_M5;
Print("調整為區間市場設定");
break;
case MARKET_VOLATILE:
// 高波動市場:放大時間框架,避免噪音
currentTrendTF = PERIOD_D1;
currentSwingTF = PERIOD_H4;
currentEntryTF = PERIOD_H1;
Print("調整為高波動市場設定");
break;
case MARKET_CALM:
// 低波動市場:縮小時間框架,尋找機會
currentTrendTF = PERIOD_H1;
currentSwingTF = PERIOD_M15;
currentEntryTF = PERIOD_M5;
Print("調整為低波動市場設定");
break;
}
Print("新時間框架設定:");
Print("趨勢: ", currentTrendTF);
Print("擺動: ", currentSwingTF);
Print("進場: ", currentEntryTF);
}
string GetConditionName(MARKET_CONDITION condition)
{
switch(condition)
{
case MARKET_TRENDING: return "趨勢市場";
case MARKET_RANGING: return "區間市場";
case MARKET_VOLATILE: return "高波動市場";
case MARKET_CALM: return "低波動市場";
default: return "未知";
}
}
double GetATRValue(ENUM_TIMEFRAMES tf, int period)
{
int atrHandle = iATR(_Symbol, tf, period);
double values[1];
if(CopyBuffer(atrHandle, 0, 0, 1, values) > 0)
return values[0];
return 0;
}
double GetAverageATR(ENUM_TIMEFRAMES tf, int period, int bars)
{
int atrHandle = iATR(_Symbol, tf, period);
double values[];
if(CopyBuffer(atrHandle, 0, 0, bars, values) >= bars)
{
double sum = 0;
for(int i = 0; i < bars; i++)
sum += values[i];
return sum / bars;
}
return 0;
}
double CalculateTrendStrength(ENUM_TIMEFRAMES tf)
{
// 計算趨勢強度 (0-100)
int maHandle = iMA(_Symbol, tf, 50, 0, MODE_SMA, PRICE_CLOSE);
double maValues[3];
if(CopyBuffer(maHandle, 0, 0, 3, maValues) >= 3)
{
// 基於MA的角度計算趨勢強度
double angle = (maValues[0] - maValues[2]) / 2.0;
double strength = MathAbs(angle) * 10000;
return MathMin(strength, 100);
}
return 0;
}
};
第四部分:實戰案例與常見錯誤
4.1 實戰案例:EURUSD 多時間框架交易
// EURUSD 多時間框架交易案例
class EURUSDMultiTimeframeExample
{
public:
void RunExample()
{
Print("=== EURUSD 多時間框架交易案例 ===");
// 步驟1:設定時間框架
TimeframeHierarchy tf(PERIOD_H1);
// 步驟2:分析各時間框架
AnalyzeTimeframes(tf.GetTrendTF(), tf.GetSwingTF(), tf.GetEntryTF());
// 步驟3:檢查對齊
bool aligned = TimeframeAlignment::CheckAlignment(
tf.GetTrendTF(), tf.GetSwingTF(), tf.GetEntryTF());
// 步驟4:執行三重濾網分析
if(aligned)
{
TripleScreenTradingSystem tripleScreen(PERIOD_H1);
ENUM_ENTRY_SIGNAL signal = tripleScreen.Analyze();
if(signal != ENTRY_NONE)
{
double entry, sl, tp;
if(tripleScreen.GetTradeDetails(entry, sl, tp))
{
ExecuteTrade(signal, entry, sl, tp);
}
}
}
}
private:
void AnalyzeTimeframes(ENUM_TIMEFRAMES trendTF, ENUM_TIMEFRAMES swingTF, ENUM_TIMEFRAMES entryTF)
{
Print("\n各時間框架分析:");
// 趨勢時間框架分析
Print("1. 趨勢框架 (", trendTF, "):");
AnalyzeTrend(trendTF);
// 擺動時間框架分析
Print("\n2. 擺動框架 (", swingTF, "):");
AnalyzeSwing(swingTF);
// 進場時間框架分析
Print("\n3. 進場框架 (", entryTF, "):");
AnalyzeEntry(entryTF);
}
void AnalyzeTrend(ENUM_TIMEFRAMES tf)
{
// 使用200EMA判斷長期趨勢
int maHandle = iMA(_Symbol, tf, 200, 0, MODE_EMA, PRICE_CLOSE);
double maValue[1];
double price = iClose(_Symbol, tf, 0);
if(CopyBuffer(maHandle, 0, 0, 1, maValue) > 0)
{
if(price > maValue[0])
Print(" 價格在200EMA之上,長期趨勢向上");
else
Print(" 價格在200EMA之下,長期趨勢向下");
Print(" 價格: ", DoubleToString(price, 5));
Print(" 200EMA: ", DoubleToString(maValue[0], 5));
}
}
void AnalyzeSwing(ENUM_TIMEFRAMES tf)
{
// 使用MACD和RSI分析擺動信號
int macdHandle = iMACD(_Symbol, tf, 12, 26, 9, PRICE_CLOSE);
int rsiHandle = iRSI(_Symbol, tf, 14, PRICE_CLOSE);
double macdMain[2], macdSignal[2];
double rsi[2];
if(CopyBuffer(macdHandle, MAIN_LINE, 0, 2, macdMain) >= 2 &&
CopyBuffer(macdHandle, SIGNAL_LINE, 0, 2, macdSignal) >= 2 &&
CopyBuffer(rsiHandle, 0, 0, 2, rsi) >= 2)
{
// MACD分析
if(macdMain[0] > macdSignal[0] && macdMain[1] <= macdSignal[1])
Print(" MACD金叉,買入信號");
else if(macdMain[0] < macdSignal[0] && macdMain[1] >= macdSignal[1])
Print(" MACD死叉,賣出信號");
else
Print(" MACD無明確信號");
// RSI分析
if(rsi[0] > 70)
Print(" RSI超買 (>70)");
else if(rsi[0] < 30)
Print(" RSI超賣 (<30)");
else
Print(" RSI中性 (", DoubleToString(rsi[0], 1), ")");
}
}
void AnalyzeEntry(ENUM_TIMEFRAMES tf)
{
// 分析價格行為
MqlRates rates[3];
if(CopyRates(_Symbol, tf, 0, 3, rates) >= 3)
{
Print(" 最新K線:");
Print(" 開盤: ", DoubleToString(rates[0].open, 5));
Print(" 最高: ", DoubleToString(rates[0].high, 5));
Print(" 最低: ", DoubleToString(rates[0].low, 5));
Print(" 收盤: ", DoubleToString(rates[0].close, 5));
// 檢查K線形態
if(rates[0].close > rates[0].open)
Print(" 陽線");
else
Print(" 陰線");
}
}
void ExecuteTrade(ENUM_ENTRY_SIGNAL signal, double entry, double sl, double tp)
{
Print("\n🎯 執行交易:");
Print("信號: ", (signal == ENTRY_BUY ? "買入" : "賣出"));
Print("進場: ", DoubleToString(entry, 5));
Print("止損: ", DoubleToString(sl, 5));
Print("止盈: ", DoubleToString(tp, 5));
// 這裡應該實作實際的交易執行邏輯
// 注意:這只是範例,實際交易需要完整的錯誤處理和風險管理
}
};
4.2 常見多時間框架錯誤
// 常見錯誤與解決方案
void CommonMultiTimeframeMistakes()
{
Print("=== 常見多時間框架錯誤 ===");
Print("\n❌ 錯誤1: 時間框架比例不當");
Print(" 表現: 使用M1和M5(比例太小)或M5和H4(比例太大)");
Print(" 結果: 信號混亂,難以對齊");
Print(" 解決: 使用4-6倍比例的時間框架組合");
Print("\n❌ 錯誤2: 忽略時間框架對齊");
Print(" 表現: 在不同時間框架的K線中間進行分析");
Print(" 結果: 信號時機不準確");
Print(" 解決: 確保分析時所有時間框架的K線時間對齊");
Print("\n❌ 錯誤3: 過度分析");
Print(" 表現: 同時看太多時間框架(如M1、M5、M15、H1、H4、D1)");
Print(" 結果: 分析癱瘓,無法做出決策");
Print(" 解決: 專注於3個關鍵時間框架(趨勢、擺動、進場)");
Print("\n❌ 錯誤4: 忽略市場狀況");
Print(" 表現: 在高波動市場使用小時間框架,或在低波動市場使用大時間框架");
Print(" 結果: 頻繁止損或錯失機會");
Print(" 解決: 根據市場波動性動態調整時間框架");
Print("\n❌ 錯誤5: 違反時間框架層級");
Print(" 表現: 在小時間框架逆勢交易,或在大時間框架追逐小波動");
Print(" 結果: 風險回報比不佳");
Print(" 解決: 遵守『大框架定方向,小框架找進場』原則");
}
第五部分:實戰檢查清單與最佳實踐
5.1 多時間框架交易檢查清單
bool RunMultiTimeframeChecklist()
{
Print("=== 多時間框架交易檢查清單 ===");
bool checklist[10] = {false};
int passed = 0;
// 1. 是否使用至少3個時間框架?
checklist[0] = (GetUsedTimeframeCount() >= 3);
Print(checklist[0] ? "✅ 1. 使用≥3個時間框架" : "❌ 1. 使用<3個時間框架");
// 2. 時間框架比例是否合理(4-6倍)?
checklist[1] = CheckTimeframeRatios();
Print(checklist[1] ? "✅ 2. 時間框架比例合理" : "❌ 2. 時間框架比例不合理");
// 3. 時間框架是否對齊?
checklist[2] = CheckTimeframeAlignment();
Print(checklist[2] ? "✅ 3. 時間框架對齊" : "❌ 3. 時間框架未對齊");
// 4. 趨勢方向是否一致?
checklist[3] = CheckTrendConsistency();
Print(checklist[3] ? "✅ 4. 趨勢方向一致" : "❌ 4. 趨勢方向不一致");
// 5. 是否遵守時間框架層級?
checklist[4] = CheckTimeframeHierarchy();
Print(checklist[4] ? "✅ 5. 遵守時間框架層級" : "❌ 5. 違反時間框架層級");
// 6. 是否有明確的進出場規則?
checklist[5] = HasClearRules();
Print(checklist[5] ? "✅ 6. 有明確進出場規則" : "❌ 6. 無明確進出場規則");
// 7. 是否考慮市場狀況?
checklist[6] = ConsiderMarketCondition();
Print(checklist[6] ? "✅ 7. 考慮市場狀況" : "❌ 7. 忽略市場狀況");
// 8. 是否有風險管理?
checklist[7] = HasRiskManagement();
Print(checklist[7] ? "✅ 8. 有風險管理" : "❌ 8. 無風險管理");
// 9. 是否有交易計劃?
checklist[8] = HasTradingPlan();
Print(checklist[8] ? "✅ 9. 有交易計劃" : "❌ 9. 無交易計劃");
// 10. 是否有交易日誌?
checklist[9] = HasTradeJournal();
Print(checklist[9] ? "✅ 10. 有交易日誌" : "❌ 10. 無交易日誌");
// 計算通過率
for(int i = 0; i < 10; i++)
{
if(checklist[i]) passed++;
}
double passRate = (double)passed / 10 * 100;
Print("\n檢查結果: ", passed, "/10 通過 (", DoubleToString(passRate, 1), "%)");
if(passRate >= 80.0)
{
Print("🎉 多時間框架交易準備良好!");
return true;
}
else if(passRate >= 60.0)
{
Print("⚠️ 多時間框架交易需要改進");
return false;
}
else
{
Print("❌ 多時間框架交易存在嚴重問題");
return false;
}
}
// 檢查函數的簡化實現
int GetUsedTimeframeCount() { return 3; }
bool CheckTimeframeRatios() { return true; }
bool CheckTimeframeAlignment() { return true; }
bool CheckTrendConsistency() { return true; }
bool CheckTimeframeHierarchy() { return true; }
bool HasClearRules() { return true; }
bool ConsiderMarketCondition() { return true; }
bool HasRiskManagement() { return true; }
bool HasTradingPlan() { return true; }
bool HasTradeJournal() { return true; }
5.2 最佳實踐總結
1. 保持簡單:專注於3個關鍵時間框架
2. 遵守比例:使用4-6倍的時間框架差距
3. 確保對齊:只在對齊的K線時間進行分析
4. 尊重層級:大框架定方向,小框架找進場
5. 動態調整:根據市場狀況調整時間框架
6. 保持耐心:等待所有時間框架信號一致
7. 嚴格執行:遵守你的交易規則
8. 持續學習:從每筆交易中學習和改進
結語:多時間框架的藝術
多時間框架分析就像是用不同倍率的鏡頭看世界:
- 望遠鏡(大時間框架):看清整體趨勢和方向
- 標準鏡(中時間框架):找到交易機會和擺動點
- 顯微鏡(小時間框架):精確定位進場點
我學到的最重要一課是:市場就像洋蔥,需要一層一層剝開來看。只看一層,你會錯過很多;看太多層,你會流淚。
三個關鍵收穫:
1. 一致性最重要:當所有時間框架都指向同一方向時,成功的機率最高
2. 耐心是美德:等待所有條件對齊,比頻繁交易更重要
3. 簡單最有效:複雜的系統不一定比簡單的系統更好
記住,多時間框架分析不是為了讓你找到更多交易機會,而是為了讓你找到更好的交易機會。
---
系列文章進度:
1. ✅ 從零開始建立你的第一個 MQL5 專家顧問
2. ✅ MQL5 程式設計常見錯誤與最佳實踐
3. ✅ 回測與實盤測試完全指南
4. ✅ MQL5 資金管理演算法完整指南
5. ✅ MQL5 多時間框架分析完整指南(本篇)
6. 🔜 MQL5 技術指標深度解析與實戰應用
---
*多時間框架分析需要時間和練習才能掌握。一開始可能會覺得複雜,但隨著經驗積累,它會成為你交易工具箱中最強大的工具之一。如果你在實作過程中遇到問題,歡迎在下方留言討論!*