核心策略概述 (交易商品 NAS100 )
這是一個基於FVG(Fair Value Gap,公允價值缺口)突破與布林帶收縮的趨勢跟隨策略。策略使用MACD背離和布林帶突破來識別進出場機會,並採用當沖模式在特定時間強制平倉。
多單策略
多單進場條件
MACD OSC上升: a_OSC[1] > a_OSC[2](動能增強)
FVG突破: Close[1] > FVG_多頭上緣價格(價格突破公允價值缺口上緣)
連續創高: High[1] > High[2] 且 High[1] > High[3](確認上漲趨勢)
進場方式:市價單買入(Buy_at_MARKET)
僅在無部位、點差合理、今日未進場、BarSinceExit > 1 時執行
正常出場: 達到最小停利(3倍點差)且發生MACD高點背離
MACD高點背離判斷:
價格高點在最近2根K棒內(ArrayMaximum(High) <= 2)
MACD DIF最大值在更早前(ArrayMaximum(a_DIF) > 2)
RSI < 70(避免超買區)
空單策略
空單進場條件
空單出場條件
ENUM_TIMEFRAMES 時間週期 = PERIOD_H1; // 主要分析時間框架(1小時)
ENUM_TIMEFRAMES 時間框架 = PERIOD_D1; // 相對大週期(日線)
條件分析:
布林帶收縮: Shrink == true(波動率降低,蓄勢待發)
向下突破下軌: Low[1] < a_BBDN[1](價格突破布林帶下軌)
黑K確認: Close[1] < Open[1](收盤低於開盤,空方力量確認)
進場方式市價單賣出(Short_at_MARKET)
僅在無部位、點差合理、今日未進場、BarSinceExit > 1 時執行
條件分析:
正常出場: 達到最小停利(3倍點差)且出現多單進場訊號(LE_Cond)
停損出場: K棒收盤突破停損價,且當前價格已漲破停損價
//+------------------------------------------------------------------+
//| MT5 自動交易程式 (Expert Advisor)
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh> // 引入 MT5 交易函數庫
#include <MagicMT5_函數庫V2.mqh> // 引入自定義函數庫
//+------------------------------------------------------------------+
//| EA初始化函數 - 當EA載入到圖表時執行一次 |
//+------------------------------------------------------------------+
int OnInit()
{
// 記錄EA載入的時間戳記,用於計算K棒編號
LoadEA = TimeCurrent();
// 顯示交易商品的基本資訊
// SYMBOL_VOLUME_MIN: 最小交易手數(例如0.01)
// Point(): 最小價格變動單位(例如0.00001)
Alert(" 最小手數=", string(SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN)),
" point=", string(Point()));
// SYMBOL_TRADE_CONTRACT_SIZE: 合約規格(1手代表多少單位,例如100000)
// SYMBOL_TRADE_TICK_VALUE: 1跳點的價值(美元)
Alert(" 合約規格=", string(SymbolInfoDouble(Symbol(), SYMBOL_TRADE_CONTRACT_SIZE)),
" 1 跳點可換美元 = ", string(SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE)));
return(INIT_SUCCEEDED); // 初始化成功
}
//+------------------------------------------------------------------+
//| Tick事件處理函數 - 每次價格跳動時執行 |
//+------------------------------------------------------------------+
void OnTick()
{
//=================================================================
// 資金風控檢查
//=================================================================
// 檢查帳戶餘額是否低於設定的資金風控門檻
if(AccountInfoDouble(ACCOUNT_BALANCE) < 資金風控)
{
Alert("********** 資金不足 *************");
return; // 資金不足,停止執行所有交易邏輯
}
//=================================================================
// K棒編號計算與第一根K棒測試
//=================================================================
// 計算從EA載入到現在經過了幾根K棒
BarNumber = iBarShift(Symbol(), 時間週期, LoadEA);
// 計算從上次平倉到現在經過了幾根K棒
BarSinceExit = BarNumber - CloseOrderNo;
// 第一根K棒特殊處理:測試進出場功能
if((BarNumber == 1 && BarNumber != JudgeNo))
{
// 開立多單並立即平倉(測試用)
多單進場單號 = Buy_at_MARKET(Symbol(), Lots, 0, 0, "1st_K", MagicNumber);
LX_CloseByTicket(多單進場單號, Lots);
// 開立空單並立即平倉(測試用)
空單進場單號 = Short_at_MARKET(Symbol(), Lots, 0, 0, "1st_K", MagicNumber);
SX_CloseByTicket(空單進場單號, Lots);
// 記錄平倉時的K棒編號
CloseOrderNo = iBarShift(Symbol(), 時間週期, LoadEA);
// 計算快慢均線週期(取較小值和較大值)
FastSma = MathMin(LenA1, LenB1);
SlowSma = MathMax(LenA1, LenB1);
}
//=================================================================
// 新K棒時更新指標與交易時段
//=================================================================
// 當K棒編號改變時(新K棒產生),執行更新
if(BarNumber != JudgeNo)
{
換K棒(); // 更新OHLC資料、計算指標、計算FVG價格
交易時段賦值(); // 判斷當前是否在允許交易的時段
}
//=================================================================
// 取得當前市場資訊
//=================================================================
Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); // 當前賣出價(Ask價)
Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); // 當前買入價(Bid價)
AccountBalance = AccountInfoDouble(ACCOUNT_BALANCE); // 帳戶餘額
Tickvalue = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE); // 跳點價值
SP = NormalizeDouble(MathAbs(Ask - Bid), Digits()); // 當前點差(Spread)
//=================================================================
// 動態計算交易手數
//=================================================================
if(動態計算手數 == true)
{
// 根據風險百分比、帳戶餘額、停損點數動態計算手數
Lots = get_dynamic_lot_size(是否偶數單, Symbol(), 風險百分比, AccountBalance, SL);
// 限制手數範圍在 0.01 ~ 0.3 之間
Lots = MathMin(0.3, MathMax(0.01, Lots));
}
//=================================================================
// 當沖模式 - 判斷是否接近收盤
//=================================================================
if(當沖 == true)
{
// 當沖模式:18:00 ~ 01:00 視為接近收盤,不允許新倉進場
接近收盤 = (getTM_hour(TimeCurrent()) >= 18 || getTM_hour(TimeCurrent()) <= 1);
允許交易時段 = (允許交易時段 && !接近收盤);
}
//=================================================================
// 進場邏輯(僅在允許交易時段執行)
//=================================================================
if(允許交易時段 == true)
{
//===============================================================
// 8.1 多單進場邏輯
//===============================================================
set_BuyCondition(); // 計算多單進場條件(LE_Cond)
// 檢查多單進場條件:
// 1. LE_Cond == true(符合多單進場策略)
// 2. 當前點差 < 設定的商品平均點差(避免點差過大時進場)
if(LE_Cond == true && SP < NormalizeDouble(商品平均點差 * Point(), Digits()))
{
// 檢查部位狀態:
// 1. 多單部位() == 0(目前沒有多單)
// 2. 空單部位() == 0(目前沒有空單)
// 3. BarNumber != OrderBarNo(不是在同一根K棒重複進場)
if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo)
{
// 檢查進場限制:
// 1. BarSinceExit > 1(距離上次出場至少1根K棒)
// 2. EntriesToday < 1(今天進場次數少於1次)
if(BarSinceExit > 1 && EntriesToday(MagicNumber, Symbol()) < 1)
{
// 執行市價單買入
// 參數:商品、手數、停利點數、停損點數、註解、魔術編號
多單進場單號 = Buy_at_MARKET(Symbol(), Lots, TP, SL, "BUY MARKET", MagicNumber);
// 記錄下單時的K棒編號,避免同一根K棒重複進場
OrderBarNo = iBarShift(Symbol(), 時間週期, LoadEA);
}
}
}
//===============================================================
// 8.2 空單進場邏輯
//===============================================================
set_ShortCondition(); // 計算空單進場條件(SE_Cond)
// 檢查空單進場條件:
// 1. SE_Cond == true(符合空單進場策略)
// 2. 當前點差 < 設定的商品平均點差
if(SE_Cond == true && SP < NormalizeDouble(商品平均點差 * Point(), Digits()))
{
// 檢查部位狀態:無任何部位且不在同一根K棒重複進場
if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo)
{
// 檢查進場限制:距離上次出場至少1根K棒且今天進場次數少於1次
if(BarSinceExit > 1 && EntriesToday(MagicNumber, Symbol()) < 1)
{
// 執行市價單賣出
空單進場單號 = Short_at_MARKET(Symbol(), Lots, TP, SL, "Short Market", MagicNumber);
// 記錄下單時的K棒編號
OrderBarNo = iBarShift(Symbol(), 時間週期, LoadEA);
}
}
}
}
//=================================================================
// 當沖強制平倉
//=================================================================
// 當沖模式下,在指定的出場時間或凌晨1點強制平倉所有部位
if(當沖 == true && (getTM_hour(TimeCurrent()) == 當沖出場時間 || getTM_hour(TimeCurrent()) == 1))
{
// 如果有任何部位(多單或空單),執行當沖平倉
if(多單部位() > 0 || 空單部位() > 0)
{
當沖平倉();
}
}
//=================================================================
// 停損停利檢查(交易時段外也執行)
//=================================================================
// 無論是否在交易時段,都要檢查並執行停損停利
交易時段外也可停損停利();
//=================================================================
// 更新K棒判斷編號
//=================================================================
// 記錄當前K棒編號,用於下次Tick判斷是否換K棒
JudgeNo = iBarShift(Symbol(), 時間週期, LoadEA);
} // end of OnTick
//+------------------------------------------------------------------+
//| 自訂函數庫
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 函數:多單部位()
//| 功能:計算當前多單(買單)的數量
//| 回傳:多單部位數量
//+------------------------------------------------------------------+
int 多單部位()
{
int count;
// 使用get_TradeCounts函數計算指定商品、魔術編號、買單類型的部位數
count = get_TradeCounts(Symbol(), MagicNumber, POSITION_TYPE_BUY);
return count;
}
//+------------------------------------------------------------------+
//| 函數:空單部位() |
//| 功能:計算當前空單(賣單)的數量 |
//| 回傳:空單部位數量 |
//+------------------------------------------------------------------+
int 空單部位()
{
int count;
// 使用get_TradeCounts函數計算指定商品、魔術編號、賣單類型的部位數
count = get_TradeCounts(Symbol(), MagicNumber, POSITION_TYPE_SELL);
return count;
}
//+------------------------------------------------------------------+
//| 函數:換K棒() |
//| 功能:當新K棒產生時,更新所有資料與指標 |
//+------------------------------------------------------------------+
void 換K棒()
{
//=================================================================
// 1. 刪除所有掛單(限價單、停損單等)
//=================================================================
// 檢查是否有未成交的掛單
if(total_pending_order_count(Symbol(), MagicNumber, -1) != 0)
{
// 刪除所有掛單(新K棒時清空,避免過期訂單)
delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff);
}
//=================================================================
// 2. 設定OHLC陣列序列並取得資料
//=================================================================
Set_OHLC_Bar_Series(); // 設定K棒OHLC陣列為時間序列(索引0為最新)
Set_OHLC_Day_Series(); // 設定日線OHLC陣列為時間序列
Get_OHLC_Bar(30); // 取得最近30根K棒的OHLC資料
Get_OHLC_Day(15); // 取得最近15天的日線OHLC資料
//=================================================================
// 3. 計算技術指標
//=================================================================
set_BBAND(); // 計算布林帶(Bollinger Bands)
set_MACD(); // 計算MACD指標
set_RSI(); // 計算RSI指標
//=================================================================
// 4. 計算FVG(Fair Value Gap)價格
//=================================================================
計算FVG價格(); // 計算多頭與空頭FVG的上下緣價格
//=================================================================
// 5. 計算近5日平均波動
//=================================================================
// Day5Range:過去5天的平均日內波動(High-Low平均值)
Day5Range = ((HighD[1] - LowD[1]) +
(HighD[2] - LowD[2]) +
(HighD[3] - LowD[3]) +
(HighD[4] - LowD[4]) +
(HighD[5] - LowD[5])) / 5;
}
//+------------------------------------------------------------------+
//| 函數:交易時段賦值() |
//| 功能:根據交易時段編號判斷是否允許交易 |
//+------------------------------------------------------------------+
void 交易時段賦值()
{
// 交易時段:只允許8點之後交易
允許交易時段 = (getTM_hour(TimeCurrent()) >= 8);
// 可擴充其他交易時段編號的判斷邏輯
}
//+------------------------------------------------------------------+
//| 函數:當沖平倉()
//| 功能:當沖模式下的強制平倉邏輯
//+------------------------------------------------------------------+
void 當沖平倉()
{
// 檢查是否到達當沖出場時間或凌晨1點
if((getTM_hour(TimeCurrent()) == 當沖出場時間 || getTM_hour(TimeCurrent()) == 1))
{
//===============================================================
// 平倉多單部位
//===============================================================
// 條件:1. 有多單部位 2. 不在同一根K棒重複平倉
if(多單部位() > 0 && BarNumber != CloseOrderNo)
LX_CloseByTicket(多單進場單號, Lots); // 使用票號平倉多單
//===============================================================
// 平倉空單部位
//===============================================================
// 條件:1. 有空單部位 2. 不在同一根K棒重複平倉
if(空單部位() > 0 && BarNumber != CloseOrderNo)
SX_CloseByTicket(空單進場單號, Lots); // 使用票號平倉空單
//===============================================================
// 記錄平倉時的K棒編號
//===============================================================
CloseOrderNo = iBarShift(Symbol(), 時間週期, LoadEA);
}
}
//+------------------------------------------------------------------+
//| 函數:交易時段外也可停損停利() |
//| 功能:無論是否在交易時段,都執行停損停利檢查 |
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
{
//=================================================================
// 多單出場邏輯
//=================================================================
if(多單部位() > 0)
{
//===============================================================
// 計算多單相關價格
//===============================================================
double 多單最小停利 = 0.0; // 最小停利價格
bool LX_MinPF = false; // 是否達到最小停利
// 取得多單進場價格
多單進場價格 = LE_EntryPrice(MagicNumber, 多單進場單號);
// 計算最小停利價格 = 進場價 + 3倍點差
// 目的:避免價格剛微幅上漲就出場,確保至少賺到點差成本
多單最小停利 = NormalizeDouble(多單進場價格 + SP * 3, Digits());
LX_MinPF = Bid > 多單最小停利; // 當前Bid價是否高於最小停利
// 計算停利價格 = 進場價 + TP點數
多單停利價格 = NormalizeDouble(多單進場價格 + TP * Point(), Digits());
// 計算停損價格 = 進場價 - SL點數
多單停損價格 = NormalizeDouble(多單進場價格 - SL * Point(), Digits());
//===============================================================
// 多單出場方式 :MACD背離 + 停損
//===============================================================
// 出場條件(兩種情況任一成立):
// 1. 達到最小停利 且 發生MACD高點背離
// 2. K棒收盤價突破停損價 且 當前Bid價已跌破停損價
LX_Cond = ((LX_MinPF == true && (MACD_HDiv_01() == true)) ||
(Close[1] >= 多單停損價格 && Bid < 多單停損價格));
//===============================================================
// 執行多單出場
//===============================================================
// 出場條件:
// 1. LX_Cond == true(符合出場條件)
// 2. BarNumber != CloseOrderNo(不在同一根K棒重複平倉)
// 3. 進場時間 > 0根K棒(確保不是剛進場就出場)
if(LX_Cond == true && BarNumber != CloseOrderNo &&
LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期) > 0)
{
// 使用票號平倉多單
LX_CloseByTicket(多單進場單號, Lots);
// 如果多單完全平倉,記錄平倉時的K棒編號
if(多單部位() == 0)
{
CloseOrderNo = iBarShift(Symbol(), 時間週期, LoadEA);
}
}
} // end of 多單部位() > 0
//=================================================================
// 空單出場邏輯
//=================================================================
if(空單部位() > 0)
{
//===============================================================
// 計算空單相關價格
//===============================================================
double 空單最小停利 = 0.0; // 最小停利價格
bool SX_MinPF = false; // 是否達到最小停利
// 取得空單進場價格
空單進場價格 = SE_EntryPrice(MagicNumber, 空單進場單號);
// 計算最小停利價格 = 進場價 - 3倍點差
空單最小停利 = NormalizeDouble(空單進場價格 - SP * 3, Digits());
SX_MinPF = Ask < 空單最小停利; // 當前Ask價是否低於最小停利
// 計算停利價格 = 進場價 - TP點數
空單停利價格 = NormalizeDouble(空單進場價格 - TP * Point(), Digits());
// 計算停損價格 = 進場價 + SL點數
空單停損價格 = NormalizeDouble(空單進場價格 + SL * Point(), Digits());
//===============================================================
// 空單出場方式 :多單訊號出現 + 停損
//===============================================================
// 出場條件(兩種情況任一成立):
// 1. 達到最小停利 且 出現多單進場訊號(LE_Cond)
// 2. K棒收盤價突破停損價 且 當前Ask價已漲破停損價
SX_Cond = ((SX_MinPF == true && LE_Cond) ||
(Close[1] <= 空單停損價格 && Ask > 空單停損價格));
//===============================================================
// 執行空單出場
//===============================================================
// 出場條件:
// 1. SX_Cond == true(符合出場條件)
// 2. BarNumber != CloseOrderNo(不在同一根K棒重複平倉)
// 3. 進場時間 > 0根K棒
if(SX_Cond == true && BarNumber != CloseOrderNo &&
SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期) > 0)
{
// 使用票號平倉空單
SX_CloseByTicket(空單進場單號, Lots);
// 如果空單完全平倉,記錄平倉時的K棒編號
if(空單部位() == 0)
{
CloseOrderNo = iBarShift(Symbol(), 時間週期, LoadEA);
}
}
} // end of 空單部位() > 0
} // end of 交易時段外也可停損停利()
//+------------------------------------------------------------------+
//| 多單進場條件模組 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 函數:set_BuyCondition() |
//| 功能:根據多單模組編號設定多單進場條件(LE_Cond) |
//+------------------------------------------------------------------+
void set_BuyCondition()
{
// 進場條件(所有條件必須同時成立):
// 1. a_OSC[1] > a_OSC[2]:MACD柱狀圖上升(動能增強)
// 2. Close[1] > FVG_多頭上緣價格:收盤價突破多頭FVG上緣
// 3. High[1] > High[2]:最高價高於前一根K棒
// 4. High[1] > High[3]:最高價高於前三根K棒(連續創高確認)
LE_Cond = (a_OSC[1] > a_OSC[2] &&
Close[1] > FVG_多頭上緣價格 &&
High[1] > High[2] &&
High[1] > High[3]);
// 可擴充其他多單模組編號的進場條件
}
//+------------------------------------------------------------------+
//| 空單進場條件模組 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 函數:set_ShortCondition() |
//| 功能:根據空單模組編號設定空單進場條件(SE_Cond) |
//+------------------------------------------------------------------+
void set_ShortCondition()
{
// 進場條件(所有條件必須同時成立):
// 1. Shrink:布林帶處於收縮狀態(波動率降低)
// 2. Low[1] < a_BBDN[1]:最低價突破布林帶下軌
// 3. Close[1] < Open[1]:收盤價低於開盤價(黑K確認)
SE_Cond = (Shrink &&
Low[1] < a_BBDN[1] &&
Close[1] < Open[1]);
// 可擴充其他空單模組編號的進場條件
}
//+------------------------------------------------------------------+
//| OHLC資料處理區塊 |
//+------------------------------------------------------------------+
// 宣告K棒價格陣列變數
double Open[]; // 開盤價陣列
double High[]; // 最高價陣列
double Low[]; // 最低價陣列
double Close[]; // 收盤價陣列
double Range[]; // 波動範圍陣列(High - Low)
double Body[]; // K棒實體陣列(|Close - Open|)
double UPshadow[]; // 上影線陣列
double DNshadow[]; // 下影線陣列
// 宣告日線價格陣列變數
double OpenD[]; // 日線開盤價陣列
double HighD[]; // 日線最高價陣列
double LowD[]; // 日線最低價陣列
double CloseD[]; // 日線收盤價陣列
// 宣告成交量陣列變數
long Volume[]; // 成交量陣列
long BigVolume[]; // 大成交量陣列
//+------------------------------------------------------------------+
//| 函數:Set_OHLC_Bar_Series() |
//| 功能:設定K棒OHLC陣列為時間序列(索引0為最新資料) |
//+------------------------------------------------------------------+
void Set_OHLC_Bar_Series()
{
// ArraySetAsSeries:將陣列設為時間序列
// true:索引0代表最新資料,索引越大代表越舊的資料
ArraySetAsSeries(Open, true);
ArraySetAsSeries(High, true);
ArraySetAsSeries(Low, true);
ArraySetAsSeries(Close, true);
ArraySetAsSeries(Volume, true);
}
//+------------------------------------------------------------------+
//| 函數:Get_OHLC_Bar() |
//| 功能:取得指定數量的K棒OHLC資料 |
//| 參數:argCount - 要取得的K棒數量 |
//+------------------------------------------------------------------+
void Get_OHLC_Bar(int argCount)
{
// 使用自訂函數取得OHLC資料並存入對應陣列
get_OpenData(Symbol(), 時間週期, argCount, Open); // 取得開盤價
get_HighData(Symbol(), 時間週期, argCount, High); // 取得最高價
get_LowData(Symbol(), 時間週期, argCount, Low); // 取得最低價
get_CloseData(Symbol(), 時間週期, argCount, Close); // 取得收盤價
get_VolumeData(Symbol(), 時間週期, argCount, Volume); // 取得成交量
}
//+------------------------------------------------------------------+
//| 函數:Set_OHLC_Day_Series() |
//| 功能:設定日線OHLC陣列為時間序列 |
//+------------------------------------------------------------------+
void Set_OHLC_Day_Series()
{
// 將日線資料陣列設為時間序列格式
ArraySetAsSeries(OpenD, true);
ArraySetAsSeries(HighD, true);
ArraySetAsSeries(LowD, true);
ArraySetAsSeries(CloseD, true);
}
//+------------------------------------------------------------------+
//| 函數:Get_OHLC_Day() |
//| 功能:取得指定天數的日線OHLC資料 |
//| 參數:argCount - 要取得的天數 |
//+------------------------------------------------------------------+
void Get_OHLC_Day(int argCount)
{
// 使用PERIOD_D1(日線週期)取得日線OHLC資料
get_OpenData(Symbol(), PERIOD_D1, argCount, OpenD); // 取得日線開盤價
get_HighData(Symbol(), PERIOD_D1, argCount, HighD); // 取得日線最高價
get_LowData(Symbol(), PERIOD_D1, argCount, LowD); // 取得日線最低價
get_CloseData(Symbol(), PERIOD_D1, argCount, CloseD); // 取得日線收盤價
}
//+------------------------------------------------------------------+
//| 技術指標計算區塊 |
//+------------------------------------------------------------------+
//=================================================================
// 布林帶(Bollinger Bands)相關變數與函數
//=================================================================
bool UpBreak; // 向上突破布林帶上軌標記
bool DnBreak; // 向下突破布林帶下軌標記
bool Shrink; // 布林帶收縮標記
double a_BBUP[]; // 布林帶上軌陣列
double a_BBDN[]; // 布林帶下軌陣列
//+------------------------------------------------------------------+
//| 函數:set_BBAND() |
//| 功能:計算布林帶指標並判斷突破與收縮狀態 |
//+------------------------------------------------------------------+
void set_BBAND()
{
int BBLen; // 布林帶週期
int h_BBUP; // 布林帶上軌指標句柄
int h_BBDN; // 布林帶下軌指標句柄
bool ShrinkUp; // 上軌收縮標記
bool ShrinkDn; // 下軌收縮標記
//===============================================================
// 1. 計算布林帶週期(取LenA2和LenB2的較大值)
//===============================================================
BBLen = MathMax(LenA2, LenB2);
//===============================================================
// 2. 建立布林帶指標句柄並取得資料
//===============================================================
// 布林帶上軌:iBands(商品, 週期, BB週期, 偏移, 標準差倍數, 價格類型)
h_BBUP = iBands(Symbol(), 時間週期, BBLen, 0, 2, PRICE_CLOSE);
ArraySetAsSeries(a_BBUP, true); // 設為時間序列
// 布林帶下軌
h_BBDN = iBands(Symbol(), 時間週期, BBLen, 0, 2, PRICE_CLOSE);
ArraySetAsSeries(a_BBDN, true);
// get_IndexData(句柄, 緩衝區索引, 起始位置, 數量, 目標陣列)
get_IndexData(h_BBUP, 1, 0, 10, a_BBUP); // 索引1:上軌資料
get_IndexData(h_BBDN, 2, 0, 10, a_BBDN); // 索引2:下軌資料
//===============================================================
// 3. 判斷布林帶突破狀態
//===============================================================
// 向上突破:最近兩根K棒的最高價都突破上軌
UpBreak = (High[1] > a_BBUP[1]) && (High[2] > a_BBUP[2]);
// 向下突破:最近兩根K棒的最低價都突破下軌
DnBreak = Low[1] < a_BBDN[1] && Low[2] < a_BBDN[2];
//===============================================================
// 4. 判斷布林帶收縮狀態
//===============================================================
// 上軌收縮判斷:
// 1. a_BBUP[1] > a_BBUP[2] > a_BBUP[3](上軌持續下降)
// 2. a_BBUP[5] > a_BBUP[4] > a_BBUP[3](前期也在下降)
ShrinkUp = a_BBUP[1] > a_BBUP[2] && a_BBUP[2] > a_BBUP[3] &&
a_BBUP[5] > a_BBUP[4] && a_BBUP[4] > a_BBUP[3];
// 下軌收縮判斷:
// 1. a_BBDN[1] < a_BBDN[2] < a_BBDN[3](下軌持續上升)
// 2. a_BBDN[5] < a_BBDN[4] < a_BBDN[3](前期也在上升)
ShrinkDn = a_BBDN[1] < a_BBDN[2] && a_BBDN[2] < a_BBDN[3] &&
a_BBDN[5] < a_BBDN[4] && a_BBDN[4] < a_BBDN[3];
// 布林帶收縮 = 上軌收縮 或 下軌收縮(波動率降低)
Shrink = ShrinkUp || ShrinkDn;
}
//=================================================================
// MACD指標相關變數與函數
//=================================================================
double a_DIF[]; // MACD DIF線陣列(快線 - 慢線)
double a_MACD[]; // MACD訊號線陣列(DIF的移動平均)
double a_OSC[]; // MACD柱狀圖陣列(DIF - MACD)
//+------------------------------------------------------------------+
//| 函數:set_MACD() |
//| 功能:計算MACD指標(DIF、訊號線、柱狀圖) |
//+------------------------------------------------------------------+
void set_MACD()
{
int MacdLen; // MACD訊號線週期
int FastLen; // MACD快線週期
int SlowLen; // MACD慢線週期
int h_DIF; // DIF指標句柄
int h_MACD; // 訊號線指標句柄
//===============================================================
// 1. 計算MACD各週期參數
//===============================================================
MacdLen = MathMax(LenA1, LenB1); // 取LenA1和LenB1的較大值
// 限制MacdLen最大為15(避免週期過長)
if(MacdLen > 15)
MacdLen = 15;
// 根據MacdLen計算快慢線週期
FastLen = (int)MathRound(MacdLen * 1.33); // 快線 = 基準 × 1.33
SlowLen = (int)MathRound(MacdLen * 2.66); // 慢線 = 基準 × 2.66
//===============================================================
// 2. 建立MACD指標句柄並取得資料
//===============================================================
// iMACD(商品, 週期, 快線週期, 慢線週期, 訊號線週期, 價格類型)
h_DIF = iMACD(Symbol(), 時間週期, FastLen, SlowLen, MacdLen, PRICE_WEIGHTED);
ArraySetAsSeries(a_DIF, true);
h_MACD = iMACD(Symbol(), 時間週期, FastLen, SlowLen, MacdLen, PRICE_WEIGHTED);
ArraySetAsSeries(a_MACD, true);
// 取得MACD資料
get_IndexData(h_DIF, 0, 0, 10, a_DIF); // 索引0:DIF線
get_IndexData(h_MACD, 1, 0, 10, a_MACD); // 索引1:訊號線
//===============================================================
// 3. 計算MACD柱狀圖(OSC = DIF - MACD)
//===============================================================
ArraySetAsSeries(a_OSC, true); // 設為時間序列
ArrayResize(a_OSC, ArraySize(a_DIF)); // 調整陣列大小
// 逐一計算每根K棒的OSC值
for(int i = 0; i < ArraySize(a_OSC); i++)
{
a_OSC[i] = a_DIF[i] - a_MACD[i]; // OSC = DIF - 訊號線
}
}
//=================================================================
// RSI指標相關變數與函數
//=================================================================
double a_RSIA[]; // RSI指標A陣列(週期LenA1)
double a_RSIB[]; // RSI指標B陣列(週期LenB1)
//+------------------------------------------------------------------+
//| 函數:set_RSI() |
//| 功能:計算RSI指標(兩條不同週期的RSI) |
//+------------------------------------------------------------------+
void set_RSI()
{
int h_RSIA; // RSI A指標句柄
int h_RSIB; // RSI B指標句柄
//===============================================================
// 計算RSI A(週期LenA1)
//===============================================================
// iRSI(商品, 週期, RSI週期, 價格類型)
h_RSIA = iRSI(Symbol(), 時間週期, LenA1, PRICE_CLOSE);
ArraySetAsSeries(a_RSIA, true); // 設為時間序列
get_IndexData(h_RSIA, 0, 0, 5, a_RSIA); // 取得5根K棒的RSI資料
//===============================================================
// 計算RSI B(週期LenB1)
//===============================================================
h_RSIB = iRSI(Symbol(), 時間週期, LenB1, PRICE_CLOSE);
ArraySetAsSeries(a_RSIB, true);
get_IndexData(h_RSIB, 0, 0, 5, a_RSIB);
}
//+------------------------------------------------------------------+
//| 進出場條件判斷函數 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 函數:MACD_HDiv_01() |
//| 功能:判斷MACD高點背離(價格創高但MACD未創高) |
//| 回傳:true - 發生高點背離;false - 未發生背離 |
//+------------------------------------------------------------------+
bool MACD_HDiv_01()
{
//=================================================================
// 背離條件(所有條件必須同時成立)
//=================================================================
// 1. ArrayMaximum(High) != 0 && ArrayMaximum(High) <= 2
// 價格最高點在最近2根K棒內(索引0或1或2)
// 2. ArrayMaximum(a_DIF) > 2
// MACD DIF最大值在更早前(索引大於2)
// 3. a_RSIA[1] < 70
// RSI未超買(避免在極端超買區進場)
// 解釋:價格創新高,但MACD動能反而在減弱 = 背離訊號
if((ArrayMaximum(High) != 0 && ArrayMaximum(High) <= 2) &&
(ArrayMaximum(a_DIF) > 2) &&
(a_RSIA[1] < 70))
{
return true; // 發生高點背離
}
return false; // 未發生背離
}
//+------------------------------------------------------------------+
//| FVG(Fair Value Gap)公允價值缺口相關變數與函數 |
//+------------------------------------------------------------------+
// FVG上下緣價格變數
double FVG_多頭上緣價格; // 多頭FVG上緣價格(Low[1])
double FVG_多頭下緣價格; // 多頭FVG下緣價格(High[3])
double FVG_空頭上緣價格; // 空頭FVG上緣價格(Low[3])
double FVG_空頭下緣價格; // 空頭FVG下緣價格(High[1])
//+------------------------------------------------------------------+
//| 函數:計算FVG價格() |
//| 功能:計算多頭與空頭FVG的上下緣價格 |
//| 說明:FVG是指價格快速移動留下的"空白區域",市場可能回補此缺口 |
//+------------------------------------------------------------------+
void 計算FVG價格()
{
//=================================================================
// 多頭FVG檢測(價格向上跳空)
//=================================================================
// 條件1:Low[1] > High[3](當前K棒最低價 > 前3根K棒最高價)
// 條件2:缺口大小 > 前3根K棒平均波動的1/4(過濾小缺口)
if(Low[1] > High[3] && (Low[1] - High[3]) > (Range[3] + Range[4] + Range[5]) / 4)
{
FVG_多頭上緣價格 = Low[1]; // 多頭FVG上緣 = 當前K棒最低價
FVG_多頭下緣價格 = High[3]; // 多頭FVG下緣 = 前3根K棒最高價
// 結果:在Low[1]與High[3]之間形成向上跳空缺口
}
//=================================================================
// 空頭FVG檢測(價格向下跳空)
//=================================================================
// 條件1:High[1] < Low[3](當前K棒最高價 < 前3根K棒最低價)
// 條件2:缺口大小 > 前3根K棒平均波動的1/4
else if(High[1] < Low[3] && (Low[3] - High[1]) > (Range[3] + Range[4] + Range[5]) / 4)
{
FVG_空頭上緣價格 = Low[3]; // 空頭FVG上緣 = 前3根K棒最低價
FVG_空頭下緣價格 = High[1]; // 空頭FVG下緣 = 當前K棒最高價
// 結果:在Low[3]與High[1]之間形成向下跳空缺口
}
}
回測結果
測試參數交易商品:NAS100(那斯達克指數)
樣本內區間:2019/1/1 ~ 2023/8/31
交易手數:固定 1 手
時間框架:H1 小時圖表
交易模式:當沖交易
測試參數交易商品:NAS100(那斯達克指數)
樣本內區間:2019/1/1 ~ 2023/8/31
交易手數:固定 1 手
時間框架:H1 小時圖表
交易模式:當沖交易
沒有留言:
張貼留言