核心策略概述 (商品 NAS100)
這是一個基於布林通道(Bollinger Bands)與價格型態的反轉交易策略,整合多日高低點分析與三均線系統來識別進場時機,採用當沖模式並設有保本機制。
風險控制:
低於資金風控門檻時停止交易並退出EA
商品平均點差限制:130點(進場前檢查點差)
交易限制:
每日限制1次進場
進場間隔:BarSinceExit > 1(需距離上次平倉至少2根K棒)
進出場策略
多單進場條件
LE_Cond = (布林下軌反轉型態 || 多日低點突破後反彈)
條件A - 布林下軌反轉型態:
收盤價 < 布林下軌
紅K(收>開)且實體小於總區間33%
下影線 > 上影線的3倍(長下影線)
條件B - 多日低點突破:
Low[1] < Low[2]、Low[3]、Low4
High[1] > High2
紅K收盤
空單進場條件
SE_Cond = Black3Bar_LClose()
三黑創低收型態:
連續3根黑K(收<開)
第3根收盤價 ≤ Min(第2根收盤,第1根收盤)
形成下降趨勢的連續陰線
進場方式: 市價單賣出,僅在無部位、點差合格、今日未進場且BarSinceExit>1時執行
多單出場策略(出場方式9)
停利: 進場價 + 61200點
停損: 進場價 - 15000點
主要出場條件:
LX_Cond = (有最小獲利 && 收盤下穿7MA) || 觸及停損價
保本機制(保藍設定):
當最高價 > 進場價 + TP*0.5 時啟動
保本價格 = 進場價 + TP*0.168 (約10,281點)
若價格回落跌破保本價則平倉
最小獲利要求:
Bid > 進場價 + 點差*3 才允許出場
配合7期均線(a_Avg7)下穿訊號
空單出場策略(出場方式30)
停利: 進場價 - 61200點 或 3日最低價(DayL)
停損: 進場價 + 15000點
主要出場條件:
SX_Cond = (達到3日低點停利 || 觸及停損價)
保本機制(保藍設定):
當最低價 < 進場價 - TP*0.5 時啟動
保本價格 = 進場價 - TP*0.168 (約10,281點)
若價格反彈突破保本價則平倉
動態停利:
優先使用3日最低點(DayL)作為停利目標
若價格到達後反彈則平倉
//+------------------------------------------------------------------+
//| MT5 自動交易程式 (Expert Advisor)
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh> // 引入 MT5 交易函數庫
#include <MagicMT5_函數庫V2.mqh> // 引入自定義函數庫
ENUM_TIMEFRAMES 時間週期 = PERIOD_H4; // EA 主要運行的時間週期 (H4: 4 小時)。
ENUM_TIMEFRAMES 時間框架 = PERIOD_D1; // 相對大週期,可能用於趨勢判斷或過濾 (D1: 日線)。
//+------------------------------------------------------------------+
//| 初始化函式 (EA 載入時執行一次)
//+------------------------------------------------------------------+
int OnInit()
{
LoadEA = TimeCurrent(); // 記錄 EA 載入的當前時間。
return(INIT_SUCCEEDED); // 初始化成功。
}
//+------------------------------------------------------------------+
//| OnTick 函式 (每次報價變動時執行)
//+------------------------------------------------------------------+
void OnTick()
{
// 資金風控檢查:如果帳戶淨值低於設定的資金風控值,則執行緊急處理。
if(AccountInfoDouble(ACCOUNT_BALANCE) < 資金風控)
{
Alert("********** 資金不足 *************"); // 彈出警報。
// 刪除所有掛單
if(total_pending_order_count(Symbol(), MagicNumber,-1) != 0) // 檢查是否有掛單
{
delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff); // 刪除所有由本 EA 下的掛單
}
// 持倉部位平倉 (確保不在同一根 K 棒重複平倉)
if(多單部位() > 0 && BarNumber != CloseOrderNo)
LX_CloseByTicket(多單進場單號,Lots) ; // 平倉多單
if(空單部位() > 0 && BarNumber != CloseOrderNo)
SX_CloseByTicket(空單進場單號,Lots) ; // 平倉空單
ExpertRemove(); // 退出 EA (從圖表中移除)
return; // 結束 OnTick 函式
}
// 計算 K 棒序號,LoadEA 時間在新 K 棒出現時會移動
BarNumber = iBarShift(Symbol(),時間週期,LoadEA);
// 計算距離上次平倉過了多少根 K 棒
BarSinceExit = BarNumber-CloseOrderNo ;
// 檢查是否為新 K 棒 (BarNumber == 1) 且 JudgeNo 還未更新
if((BarNumber == 1 && BarNumber != JudgeNo))
{
// 在新 K 棒開始時,進行一次測試性的下單與平倉(通常用於初始化交易相關的函式庫或獲取初始部位資訊)
多單進場單號 = 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 棒序號為當前 K 棒
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA);
// 根據 LenA1 和 LenB1 設置快慢均線週期
FastSma = MathMin(LenA1, LenB1);
SlowSma = MathMax(LenA1, LenB1);
}
// 檢查是否為新 K 棒
if(BarNumber != JudgeNo)
{
換K棒(); // 執行換 K 棒時的邏輯(如刪單、指標計算)
交易時段賦值(); // 重新檢查並設定當前是否允許交易
}
// 獲取最新的行情和帳戶資訊
Ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK); // 獲取賣價
Bid=SymbolInfoDouble(Symbol(),SYMBOL_BID); // 獲取買價
AccountBalance=AccountInfoDouble(ACCOUNT_BALANCE); // 獲取帳戶淨值
Tickvalue=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE); // 獲取最小跳動點價值
SP = NormalizeDouble(MathAbs(Ask-Bid),Digits()) ; // 計算當前點差 (SPread)
//+------------------------------------------------------------------+
//| 計算交易手數
//+------------------------------------------------------------------+
if(動態計算手數 == true)
{
// 根據風險百分比和停損點數 (SL) 計算動態手數
Lots = get_dynamic_lot_size(是否偶數單,Symbol(),風險百分比,AccountBalance,SL) ;
// 將計算出的手數限制在 0.01 到 0.3 之間
Lots = MathMin(0.3,MathMax(0.01,Lots)) ;
}
// 當沖模式下的交易時段調整
if(當沖 == true)
{
// 判斷是否接近收盤時段 (例如 18點到次日 1點)
接近收盤 = (getTM_hour(TimeCurrent()) >= 18 || getTM_hour(TimeCurrent()) <= 1) ;
// 在當沖模式下,如果接近收盤,則不允許交易
允許交易時段 = (允許交易時段 && !接近收盤) ;
}
// 檢查是否在允許的交易時段內
if(允許交易時段 == true)
{
//+------------------------------------------------------------------+
//|多單進場邏輯
//+------------------------------------------------------------------+
set_BuyCondition(); // 呼叫函式檢查多單進場條件 (LE_Cond)
// 檢查多單進場條件成立、點差小於平均點差、且最近幾筆內有空單平倉(防止立即反向開倉或特殊邏輯)
if(LE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits()) && 最近幾筆內有空單平倉(Symbol(),6) == true)
{
// 確保當前空手且不在同一根 K 棒重複下單
if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo)
{
// 確保距離上次平倉已超過 1 根 K 棒,且當日進場次數小於 1 (限制每日一次)
if(BarSinceExit > 1 && EntriesToday(MagicNumber,Symbol()) < 1)
{
// 執行市價單買入(Long Entry Market)
多單進場單號 = Buy_at_MARKET(Symbol(),Lots,TP,SL,"BUY MARKET",MagicNumber) ;
OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA); // 記錄下單 K 棒序號
} // end of BarSinceExit > 1
} // end of 空手且不同根K棒() == true
} // end of LE_Cond == true
//+------------------------------------------------------------------+
//|空單進場邏輯
//+------------------------------------------------------------------+
set_ShortCondition() ; // 呼叫函式檢查空單進場條件 (SE_Cond)
// 檢查空單進場條件成立、點差小於平均點差
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 Entry Market)
空單進場單號 = Short_at_MARKET(Symbol(),Lots,TP,SL,"Short Market",MagicNumber) ;
OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA); // 記錄下單 K 棒序號
} //end of BarSinceExit > 1
} // end of 空手且不同根K棒() == true
} // end of SE_Cond == true
} //end of 允許交易時段 == true
//+------------------------------------------------------------------+
//| 當沖強制平倉邏輯
//+------------------------------------------------------------------+
// 檢查是否為當沖模式,且當前時間達到設定的出場時間 (當沖出場時間 或 1點)
if(當沖 == true && (getTM_hour(TimeCurrent()) == 當沖出場時間 || getTM_hour(TimeCurrent()) == 1))
{
if(多單部位() > 0 || 空單部位() > 0) // 如果有持倉
{
當沖平倉(); // 執行當沖平倉函式
}
}
交易時段外也可停損停利(); // 執行停損停利及保本邏輯(在非交易時段也檢查)
JudgeNo = iBarShift(Symbol(),時間週期,LoadEA); // 更新 K 棒判斷變數
} //end of onTick
//+------------------------------------------------------------------+
//| 自訂函數庫 |
//+------------------------------------------------------------------+
// 函式:多單部位數量
int 多單部位()
{
int count;
// 呼叫函式庫獲取指定商品、魔術數字的多單部位數量
count = get_TradeCounts(Symbol(), MagicNumber,POSITION_TYPE_BUY) ;
return count ;
}
//+------------------------------------------------------------------+
//| 函式:空單部位數量
//+------------------------------------------------------------------+
int 空單部位()
{
int count;
// 呼叫函式庫獲取指定商品、魔術數字的空單部位數量
count = get_TradeCounts(Symbol(), MagicNumber,POSITION_TYPE_SELL) ;
return count ;
}
//+------------------------------------------------------------------+
//| 函式:換 K 棒時的處理邏輯
//+------------------------------------------------------------------+
void 換K棒()
{
// 新 K 棒開始時,刪除所有掛單(避免在換 K 棒後誤觸發)
if(total_pending_order_count(Symbol(), MagicNumber,-1) != 0)
{
delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff); // 刪除本 EA 的所有掛單
}
// 呼叫函式庫設定/獲取 OHLC K 棒序列資料(保留:表示該函式未在當前程式碼中定義,來自 MagicMT5_函數庫V2)
Set_OHLC_Bar_Series(); //保留
Set_OHLC_Day_Series(); //保留
Get_OHLC_Bar(30) ; //保留 (獲取最近 30 根 K 棒資料)
Get_OHLC_Day(15) ; //保留 (獲取最近 15 天 K 棒資料)
set_BarInfo(); //保留 (設定 K 棒相關資訊)
set_BBAND(); // 呼叫函式庫計算布林通道 (BBAND)
set_ThreeLine() ; // 呼叫函式庫計算三線指標 (ThreeLine)
ThreeDayHL(); // 呼叫函式庫計算三日高低點
FixBarHL(); // 呼叫函式庫修正/計算 K 棒高低點
// 計算近五日平均振幅 (使用 DayD[i] 陣列的高點和低點)
Day5Range = ((HighD[1] - LowD[1])+(HighD[2] - LowD[2])+(HighD[3] - LowD[3])+(HighD[4] - LowD[4])+(HighD[5] - LowD[5]))/5;
}
//+------------------------------------------------------------------+
//| 函式:根據交易時段編號設定是否允許交易
//+------------------------------------------------------------------+
void 交易時段賦值()
{
// 設置允許交易的時段:(4 點到 8 點) 或 (12 點到 16 點)
允許交易時段 = (getTM_hour(TimeCurrent()) >= 4 && getTM_hour(TimeCurrent()) < 8) || (getTM_hour(TimeCurrent()) >= 12 && getTM_hour(TimeCurrent()) < 16);
}
//+------------------------------------------------------------------+
//| 函式:當沖強制平倉邏輯
//+------------------------------------------------------------------+
void 當沖平倉()
{
// 檢查是否達到當沖出場時間 (當沖出場時間 或 1點)
if((getTM_hour(TimeCurrent()) == 當沖出場時間 || getTM_hour(TimeCurrent()) == 1))
{
// 平倉多單 (確保不在同一根 K 棒重複平倉)
if(多單部位() > 0 && BarNumber != CloseOrderNo)
LX_CloseByTicket(多單進場單號,Lots) ;
// 平倉空單 (確保不在同一根 K 棒重複平倉)
if(空單部位() > 0 && BarNumber != CloseOrderNo)
SX_CloseByTicket(空單進場單號,Lots) ;
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 記錄平倉 K 棒序號
}
}
//+------------------------------------------------------------------+
//| 函式:停損停利及保本邏輯(在交易時段外也應檢查)
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
{
// 交易時段外也可停損停利
if(多單部位() > 0) // 如果有多單部位
{
// 多單進場價格 = get_position_info_double((ulong)多單進場單號, POSITION_PRICE_OPEN); // 獲取開倉價格 (備註掉的寫法)
//+------------------------------------------------------------------+
//| Buy Exit Method (多單出場邏輯)
//+------------------------------------------------------------------+
double 多單最小停利 = 0.0, 多單保本價格 = 0.0 ; // 最小停利和保本價格變數
bool LX_MinPF = false, LongPull = false ; // 最小獲利旗標和拉回旗標
多單進場價格 = LE_EntryPrice(MagicNumber,多單進場單號); // 獲取多單進場價格
多單最小停利 = NormalizeDouble(多單進場價格 + SP*3,Digits()) ; // 計算最小停利價格 (進場價 + 3 倍點差)
LX_MinPF = Bid > 多單最小停利 ; // 檢查當前買價是否高於最小停利價格
// LX_MinPF = true ; // 最小停利旗標直接設為 true (備註掉的寫法)
多單停利價格 = NormalizeDouble(多單進場價格 + TP * Point(),Digits()) ; // 計算實際停利價格
多單停損價格 = NormalizeDouble(多單進場價格 - SL * Point(),Digits()) ; // 計算實際停損價格
//多單保本出場邏輯
if(保藍設定 == true)
{
// 獲取多單進場後達到的最高價
多單最高價 = iHigh(Symbol(),時間週期, iHighest(Symbol(),時間週期,MODE_HIGH,LE_BarsSinceEntry(MagicNumber,多單進場單號,時間週期),1));
多單最高價 = NormalizeDouble(多單最高價,Digits()) ;
// 如果最高價達到停利點數 TP 的 50%
if(多單最高價 > 多單進場價格+TP * Point()*0.5)
{
LongPull = true ; // 設定拉回旗標為 true
多單保本價格 = NormalizeDouble(多單進場價格+TP * Point()*0.168,Digits()) ; // 計算保本價格 (進場價 + TP 的 16.8%)
// 保本出場條件:已達一定獲利 (LongPull=true),且上一根收盤價高於保本價,但當前買價 Bid 跌破保本價
LX_Cond = (LongPull == true && Close[1] >= 多單保本價格 && Bid < 多單保本價格);
}
else
LX_Cond = false ; // 否則保本出場條件不成立
// 執行保本平倉
if(LX_Cond == true && BarNumber != CloseOrderNo && LE_BarsSinceEntry(MagicNumber,多單進場單號,時間週期) > 0)
{
LX_CloseByTicket(多單進場單號,Lots) ; // 平倉
if(多單部位() == 0) // 如果平倉成功
{
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 記錄平倉 K 棒序號
}
}
}
// 出場條件:(最小獲利達標且快線向下穿越慢線) 或 (K 棒收盤價高於停損價但當前買價 Bid 跌破停損價)
LX_Cond = ((LX_MinPF == true && CrossUnder(Close[2],a_Avg7[2],Close[1],a_Avg7[1])) || (Close[1] >= 多單停損價格 && Bid < 多單停損價格)) ;
//---------------------------------------------------------多單出場
// 執行主要出場條件平倉
if(LX_Cond == true && BarNumber != CloseOrderNo && LE_BarsSinceEntry(MagicNumber,多單進場單號,時間週期) > 0)
{
LX_CloseByTicket(多單進場單號,Lots) ; // 平倉
if(多單部位() == 0) // 如果平倉成功
{
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 記錄平倉 K 棒序號
}
}
} // end of 多單部位() > 0
if(空單部位() > 0) // 如果有空單部位
{
//+------------------------------------------------------------------+
//| Short Exit Method (空單出場邏輯)
//+------------------------------------------------------------------+
double 空單最小停利 = 0.0, 空單保本價格 = 0.0 ; // 最小停利和保本價格變數
bool SX_MinPF = false, ShortPull = false ; // 最小獲利旗標和拉回旗標
空單進場價格 = SE_EntryPrice(MagicNumber,空單進場單號); // 獲取空單進場價格
空單最小停利 = NormalizeDouble(空單進場價格 - SP*3,Digits()) ; // 計算最小停利價格 (進場價 - 3 倍點差)
SX_MinPF = Ask < 空單最小停利 ; // 檢查當前賣價是否低於最小停利價格
// SX_MinPF = true ; // 最小停利旗標直接設為 true (備註掉的寫法)
空單停利價格 = NormalizeDouble(空單進場價格 - TP * Point(),Digits()) ; // 計算實際停利價格
空單停損價格 = NormalizeDouble(空單進場價格 + SL * Point(),Digits()) ; // 計算實際停損價格
//空單保本出場邏輯
if(保藍設定 == true)
{
// 獲取空單進場後達到的最低價
空單最低價 = iLow(Symbol(),時間週期, iLowest(Symbol(),時間週期,MODE_LOW,SE_BarsSinceEntry(MagicNumber,空單進場單號,時間週期),1));
空單最低價 = NormalizeDouble(空單最低價,Digits()) ;
// 如果最低價達到停利點數 TP 的 50%
if(空單最低價 < 空單進場價格-TP * Point()*0.5)
{
ShortPull = true ; // 設定拉回旗標為 true
空單保本價格 = NormalizeDouble(空單進場價格-TP * Point()*0.168,Digits()) ; // 計算保本價格 (進場價 - TP 的 16.8%)
// 保本出場條件:已達一定獲利 (ShortPull=true),且上一根收盤價低於保本價,但當前賣價 Ask 升破保本價
SX_Cond = (ShortPull == true && Close[1] <= 空單保本價格 && Ask > 空單保本價格);
}
else
SX_Cond = false ; // 否則保本出場條件不成立
// 執行保本平倉
if(SX_Cond == true && BarNumber != CloseOrderNo && SE_BarsSinceEntry(MagicNumber,空單進場單號,時間週期) > 0)
{
SX_CloseByTicket(空單進場單號,Lots) ; // 平倉
if(空單部位() == 0) // 如果平倉成功
{
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 記錄平倉 K 棒序號
}
}
}
bool SXcond301 ;
空單停利價格 = NormalizeDouble(DayL,Digits()) ; // 將停利價格設定為當日最低價 (DayL 應為自訂函式庫中的變數)
// 條件 30-1:開倉價高於停利價 (當日低點),且上一根收盤價低於停利價,但當前賣價 Ask 升破停利價
SXcond301 = (空單進場價格 > 空單停利價格 && Close[1] >= 空單停利價格 && Ask < 空單停利價格) ;
// 總出場條件:條件 30-1 成立 或 (K 棒收盤價低於停損價但當前賣價 Ask 升破停損價)
SX_Cond = (SXcond301 == true || (Close[1] <= 空單停損價格 && Ask > 空單停損價格)) ;
//---------------------------------------------------------空單出場
// 執行主要出場條件平倉
if(SX_Cond == true && BarNumber != CloseOrderNo && SE_BarsSinceEntry(MagicNumber,空單進場單號,時間週期) > 0)
{
SX_CloseByTicket(空單進場單號,Lots) ; // 平倉
if(空單部位() == 0) // 如果平倉成功
{
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 記錄平倉 K 棒序號
}
}
} // end of 空單部位() > 0
} // end of 交易時段外也可停損停利()
//+------------------------------------------------------------------+
//| 函式:多單進場條件設定
//+------------------------------------------------------------------+
void set_BuyCondition()
{
// 多單進場條件 :
// (1) 上一根收盤價低於布林通道下軌 (a_BBDN[1])
// (2) K 棒有實體 (Body[1] != 0) 且收陽線 (Close[1] > Open[1])
// (3) 實體長度小於總振幅的 33% (Body[1] < Range[1] * 0.33)
// (4) 下影線長度大於上影線的 3 倍 (DNshadow[1] > UPshadow[1] * 3) (可能是錘子線或長下影線)
// --- 或 ---
// (5) 上一根低點低於前 2、3、4 根低點,且高點高於前 2 根高點,且收陽線 (Close[1] > Open[1]) (可能是吞噬或 V 型反轉)
LE_Cond = ((Close[1] < a_BBDN[1] && (Body[1] != 0 && Close[1] > Open[1]) && (Body[1] < Range[1] * 0.33) && (DNshadow[1] > UPshadow[1] * 3))
|| (Low[1] < Low[4] && Low[1] < Low[3] && Low[1] < Low[2] && High[1] > High[2] && Close[1] > Open[1])) ;
}
//+------------------------------------------------------------------+
//| 函式:空單進場條件設定
//+------------------------------------------------------------------+
void set_ShortCondition()
{
// 空單進場條件 :呼叫 Black3Bar_LClose() 函式,通常表示 "三黑創低收+高點下降+跌破第四根低點"
SE_Cond = (Black3Bar_LClose()) ; // 三黑創低收+高點下降+跌破第四根低點 (條件名稱)
}
回測結果
測試參數交易商品:NAS100(那斯達克指數)樣本內區間:2019/1/1 ~ 2023/9/30
交易手數:固定 1 手
時間框架:H4 小時圖表
交易模式:當沖交易
沒有留言:
張貼留言