策略概述(交易商品 XAUUSD)
這是一個基於多種技術指標的趨勢跟隨與背離交易策略。策略整合MACD背離、ADX趨勢強度、Force Index動能確認等多項指標來識別進場機會,並使用當沖模式(特定時間強制平倉)。
基礎概念
時間週期: H1(1小時)
風險控制: 動態手數計算(基於風險百分比、帳戶餘額與SL),手數限制在0.01~0.3;
資金風控門檻:低於門檻時停止交易
商品平均點差限制: 進場前檢查點差 < 商品平均點差 * Point()
交易限制: 每日限制1次進場;
交易時段: 當沖模式下排除18:00後至01:00前
當沖設定: 是(預設啟用),特定時間(當沖出場時間或01:00)強制平倉
進出場策略
多單進場條件
LE_Cond = (MACD_HDiv_01() && ADX強勢趨勢());
條件分析:
MACD高點背離: 高點在最近2根K棒內發生,但MACD DIF最大值在更早(>2根),且RSI < 70(避免超買)
ADX強勢趨勢: ADX > 25 且上升(確認趨勢強度)
進場方式: 市價單買入,僅在無部位、點差小、今日未進場、BarSinceExit >1時執行
空單進場條件
SE_Cond = (FORCE_ZeroCross() == -1 && FORCE_VolumePrice());
條件分析:
Force Index零線穿越: FORCE_ZeroCross() == -1
Force Index量價確認: FORCE_VolumePrice()
//+------------------------------------------------------------------+
//| MT5 自動交易程式 (Expert Advisor)
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh> // 引入 MT5 交易函數庫
#include <MagicMT5_函數庫V2.mqh> // 引入自定義函數庫
ENUM_TIMEFRAMES 時間週期 = PERIOD_H2; // 主交易時間框架(小時圖)
ENUM_TIMEFRAMES 時間框架 = PERIOD_D1; // 相對大週期(日線圖)
//+------------------------------------------------------------------+
//| 初始化函數,於EA啟動時執行 |
//+------------------------------------------------------------------+
int OnInit()
{
LoadEA = TimeCurrent(); // 記錄EA載入時間
return(INIT_SUCCEEDED); // 初始化成功
}
//+------------------------------------------------------------------+
//| 每tick執行,核心交易邏輯 |
//+------------------------------------------------------------------+
void OnTick()
{
// 檢查帳戶餘額是否低於資金風控門檻
if(AccountInfoDouble(ACCOUNT_BALANCE) < 資金風控)
{
Alert("********** 資金不足 *************"); // 餘額不足警告
return;
}
BarNumber = iBarShift(Symbol(),時間週期,LoadEA); // 計算從EA載入到當前的K棒數
BarSinceExit = BarNumber-CloseOrderNo ; // 計算上次平倉後的K棒數
if((BarNumber == 1 && BarNumber != JudgeNo)) // 初次執行且非重複判斷
{
多單進場單號 = Buy_at_MARKET(Symbol(),Lots,0,0,"1st_K",MagicNumber) ; // 開啟市價買單(測試用)
LX_CloseByTicket(多單進場單號,Lots) ; // 立即平倉
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA); // 更新平倉K棒編號
}
if(BarNumber != JudgeNo) // 新K棒觸發
{
換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()) ; // 計算點差並標準化
//+------------------------------------------------------------------+
//| 計算交易手數 |
//+------------------------------------------------------------------+
if(動態計算手數 == true) // 若啟用動態手數
{
Lots = get_dynamic_lot_size(是否偶數單,Symbol(),風險百分比,AccountBalance,SL) ; // 計算動態手數
Lots = MathMin(0.3,MathMax(0.01,Lots)) ; // 限制手數範圍0.01~0.3
}
// 當沖模式時間限制
if(當沖 == true)
{
接近收盤 = (getTM_hour(TimeCurrent()) >= 18 || getTM_hour(TimeCurrent()) <= 1) ; // 判斷是否接近收盤(18:00-01:00)
允許交易時段 = (允許交易時段 && !接近收盤) ; // 交易時段需在允許範圍且非收盤時段
}
// 允許交易時段內執行進場邏輯
if(允許交易時段 == true)
{
//+------------------------------------------------------------------+
//| 多單進場邏輯 |
//+------------------------------------------------------------------+
set_BuyCondition(); // 設定多單進場條件
if(LE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits())) // 多單條件成立且點差符合限制
{
if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo) // 無部位且非同一K棒
{
if(BarSinceExit > 1 && EntriesToday(MagicNumber,Symbol()) < 1) // 確保上次平倉後超過1根K棒且當日未進場
{
// 市價買入模式
多單進場單號 = Buy_at_MARKET(Symbol(),Lots,TP,SL,"BUY MARKET",MagicNumber) ; // 開啟市價買單
OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA); // 記錄進場K棒編號
}
}
}
//+------------------------------------------------------------------+
//| 空單進場邏輯 |
//+------------------------------------------------------------------+
set_ShortCondition() ; // 設定空單進場條件
if(SE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits())) // 空單條件成立且點差符合限制
{
if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo) // 無部位且非同一K棒
{
if(BarSinceExit > 1 && EntriesToday(MagicNumber,Symbol()) < 1) // 確保上次平倉後超過1根K棒且當日未進場
{
// 市價賣出模式
空單進場單號 = Short_at_MARKET(Symbol(),Lots,TP,SL,"Short Market",MagicNumber) ; // 開啟市價賣單
OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA); // 記錄進場K棒編號
}
}
}
}
//+------------------------------------------------------------------+
//| 當沖平倉邏輯 |
//+------------------------------------------------------------------+
if(當沖 == true && (getTM_hour(TimeCurrent()) == 當沖出場時間 || getTM_hour(TimeCurrent()) == 1)) // 當沖模式下到達平倉時間
{
if(多單部位() > 0 || 空單部位() > 0) // 若有持倉
{
當沖平倉(); // 執行當沖平倉
}
}
交易時段外也可停損停利(); // 交易時段外繼續監控停損停利
JudgeNo = iBarShift(Symbol(),時間週期,LoadEA); // 更新判斷K棒編號
}
//+------------------------------------------------------------------+
//| 自訂函數庫:多單部位計數 |
//+------------------------------------------------------------------+
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棒時刪除所有掛單
if(total_pending_order_count(Symbol(), MagicNumber,-1) != 0)
{
delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff); // 刪除所有掛單
}
Set_OHLC_Bar_Series(); // 設定K棒開高低收序列
Set_OHLC_Day_Series(); // 設定日線開高低收序列
Get_OHLC_Bar(30) ; // 獲取最近30根K棒數據
Get_OHLC_Day(15) ; // 獲取最近15天日線數據
set_MACD(); // 計算MACD指標
set_RSI(); // 計算RSI指標
set_ADX(); // 計算ADX指標
set_FORCE_INDEX() ; // 計算Force Index指標
Day5Range = ((HighD[1] - LowD[1])+(HighD[2] - LowD[2])+(HighD[3] - LowD[3])+(HighD[4] - LowD[4])+(HighD[5] - LowD[5]))/5; // 計算前5天平均範圍
}
//+------------------------------------------------------------------+
//| 交易時段設定函數 |
//+------------------------------------------------------------------+
void 交易時段賦值()
{
允許交易時段 = true; // 允許交易
}
//+------------------------------------------------------------------+
//| 交易時段外停損停利監控 |
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
{
// 多單出場邏輯
if(多單部位() > 0)
{
多單進場價格 = LE_EntryPrice(MagicNumber,多單進場單號); // 獲取多單進場價格
多單最小停利 = NormalizeDouble(多單進場價格 + SP*3,Digits()) ; // 計算最小停利(進場價+3倍點差)
LX_MinPF = Bid > 多單最小停利 ; // 檢查是否達到最小停利
多單停利價格 = NormalizeDouble(多單進場價格 + TP * Point(),Digits()) ; // 計算停利價格
多單停損價格 = NormalizeDouble(多單進場價格 - SL * Point(),Digits()) ; // 計算停損價格
LX_Cond = ((LX_MinPF == true && CrossUnder(a_OSC[2],0,a_OSC[1],0)) || (Close[1] >= 多單停損價格 && Bid < 多單停損價格)) ; // 出場條件:最小停利+OSC下穿零線 或 觸及停損
if(LX_Cond == true && BarNumber != CloseOrderNo && LE_BarsSinceEntry(MagicNumber,多單進場單號,時間週期) > 0) // 出場條件成立且非同一K棒
{
LX_CloseByTicket(多單進場單號,Lots) ; // 平倉多單
if(多單部位() == 0) // 若無多單部位
{
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 更新平倉K棒編號
}
}
}
// 空單出場邏輯
if(空單部位() > 0)
{
空單進場價格 = SE_EntryPrice(MagicNumber,空單進場單號); // 獲取空單進場價格
空單最小停利 = NormalizeDouble(空單進場價格 - SP*3,Digits()) ; // 計算最小停利(進場價-3倍點差)
SX_MinPF = Ask < 空單最小停利 ; // 檢查是否達到最小停利
空單停利價格 = NormalizeDouble(空單進場價格 - TP * Point(),Digits()) ; // 計算停利價格
空單停損價格 = NormalizeDouble(空單進場價格 + SL * Point(),Digits()) ; // 計算停損價格
SX_Cond = ((SX_MinPF == true && CrossOver(a_OSC[2],0,a_OSC[1],0)) || (Close[1] <= 空單停損價格 && Ask > 空單停損價格)) ; // 出場條件:最小停利+OSC上穿零線 或 觸及停損
if(SX_Cond == true && BarNumber != CloseOrderNo && SE_BarsSinceEntry(MagicNumber,空單進場單號,時間週期) > 0) // 出場條件成立且非同一K棒
{
SX_CloseByTicket(空單進場單號,Lots) ; // 平倉空單
if(空單部位() == 0) // 若無空單部位
{
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 更新平倉K棒編號
}
}
}
}
}
//+------------------------------------------------------------------+
//| 設定多單進場條件 |
//+------------------------------------------------------------------+
void set_BuyCondition()
{
LE_Cond = (MACD_HDiv_01() && ADX強勢趨勢()) ; // MACD高點背離+ADX強勢趨勢
}
//+------------------------------------------------------------------+
//| 設定空單進場條件 |
//+------------------------------------------------------------------+
void set_ShortCondition()
{
SE_Cond = (FORCE_ZeroCross() == -1 && FORCE_VolumePrice()) ; //零線穿越+量價確認
}
//+------------------------------------------------------------------+
//| 當沖平倉函數 |
//+------------------------------------------------------------------+
void 當沖平倉()
{
if((getTM_hour(TimeCurrent()) == 當沖出場時間 || getTM_hour(TimeCurrent()) == 1)) // 到達當沖平倉時間
{
if(多單部位() > 0 && BarNumber != CloseOrderNo) // 有多單且非同一K棒
LX_CloseByTicket(多單進場單號,Lots) ; // 平倉多單
if(空單部位() > 0 && BarNumber != CloseOrderNo) // 有空單且非同一K棒
SX_CloseByTicket(空單進場單號,Lots) ; // 平倉空單
CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ; // 更新平倉K棒編號
}
}
//+------------------------------------------------------------------+
//| 設定開高低收陣列序列 |
//+------------------------------------------------------------------+
double Open[], High[], Low[], Close[],Range[],Body[],UPshadow[],DNshadow[] ;
double OpenD[], HighD[], LowD[], CloseD[] ;
long Volume[],BigVolume[] ;
void Set_OHLC_Bar_Series()
{
ArraySetAsSeries(Open,true); // 設定開盤價陣列為時間序列
ArraySetAsSeries(High,true); // 設定最高價陣列為時間序列
ArraySetAsSeries(Low,true); // 設定最低價陣列為時間序列
ArraySetAsSeries(Close,true); // 設定收盤價陣列為時間序列
ArraySetAsSeries(Volume,true); // 設定成交量陣列為時間序列
}
//+------------------------------------------------------------------+
//| 獲取K棒開高低收數據 |
//+------------------------------------------------------------------+
void Get_OHLC_Bar(int argCount)
{
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) ; // 獲取成交量
}
//+------------------------------------------------------------------+
//| 設定日線開高低收序列 |
//+------------------------------------------------------------------+
void Set_OHLC_Day_Series()
{
ArraySetAsSeries(OpenD,true); // 設定日線開盤價陣列
ArraySetAsSeries(HighD,true); // 設定日線最高價陣列
ArraySetAsSeries(LowD,true); // 設定日線最低價陣列
ArraySetAsSeries(CloseD,true); // 設定日線收盤價陣列
}
//+------------------------------------------------------------------+
//| 獲取日線開高低收數據 |
//+------------------------------------------------------------------+
void Get_OHLC_Day(int argCount)
{
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) ; // 獲取日線收盤價
}
//+------------------------------------------------------------------+
//| 計算MACD指標 |
//+------------------------------------------------------------------+
double a_DIF[], a_MACD[], a_OSC[] ;
void set_MACD()
{
int MacdLen, FastLen, SlowLen ;
int h_DIF,h_MACD ;
MacdLen = MathMax(LenA1,LenB1) ; // 取LenA1,LenB1最大值作為MACD訊號線週期
if(MacdLen > 15)
MacdLen = 15 ; // 限制最大週期為15
FastLen = (int)MathRound(MacdLen*1.33) ; // 快線週期=訊號線*1.33
SlowLen = (int)MathRound(MacdLen*2.66) ; // 慢線週期=訊號線*2.66
h_DIF = iMACD(Symbol(),時間週期,FastLen,SlowLen,MacdLen,PRICE_WEIGHTED); // 創建DIF句柄
ArraySetAsSeries(a_DIF, true); // 設定DIF陣列為時間序列
h_MACD = iMACD(Symbol(),時間週期,FastLen,SlowLen,MacdLen,PRICE_WEIGHTED); // 創建MACD句柄
ArraySetAsSeries(a_MACD, true); // 設定MACD陣列為時間序列
get_IndexData(h_DIF,0,0,10,a_DIF) ; // 獲取最近10根DIF數據
get_IndexData(h_MACD,1,0,10,a_MACD) ; // 獲取最近10根MACD數據
ArraySetAsSeries(a_OSC, true); // 設定OSC陣列為時間序列
ArrayResize(a_OSC,ArraySize(a_DIF)) ; // 調整OSC陣列大小
for(int i = 0;i < ArraySize(a_OSC); i++)
{
a_OSC[i] = a_DIF[i]-a_MACD[i] ; // 計算OSC(DIF-MACD)
}
}
//+------------------------------------------------------------------+
//| 計算RSI指標 |
//+------------------------------------------------------------------+
double a_RSIA[],a_RSIB[] ;
void set_RSI()
{
int h_RSIA,h_RSIB ;
h_RSIA = iRSI(Symbol(),時間週期,LenA1,PRICE_CLOSE) ; // 創建RSI(A組)句柄
ArraySetAsSeries(a_RSIA,true) ; // 設定RSIA陣列為時間序列
get_IndexData(h_RSIA,0,0,5,a_RSIA) ; // 獲取最近5根RSIA數據
h_RSIB = iRSI(Symbol(),時間週期,LenB1,PRICE_CLOSE) ; // 創建RSI(B組)句柄
ArraySetAsSeries(a_RSIB,true) ; // 設定RSIB陣列為時間序列
get_IndexData(h_RSIB,0,0,5,a_RSIB) ; // 獲取最近5根RSIB數據
}
//+------------------------------------------------------------------+
//| 計算ADX指標 |
//+------------------------------------------------------------------+
double a_ADX[], a_PLUSDI[], a_MINUSDI[] ;
void set_ADX()
{
int h_ADX,h_PLUSDI,h_MINUSDI ;
h_ADX = iADX(Symbol(),時間週期,LenA2); // 創建ADX句柄
ArraySetAsSeries(a_ADX, true); // 設定ADX陣列為時間序列
h_PLUSDI = iADX(Symbol(),時間週期,LenA2); // 創建+DI句柄
ArraySetAsSeries(a_PLUSDI, true); // 設定+DI陣列為時間序列
h_MINUSDI = iADX(Symbol(),時間週期,LenA2); // 創建-DI句柄
ArraySetAsSeries(a_MINUSDI, true); // 設定-DI陣列為時間序列
get_IndexData(h_ADX,0,0,5,a_ADX) ; // 獲取最近5根ADX數據
get_IndexData(h_PLUSDI,1,0,5,a_PLUSDI) ; // 獲取最近5根+DI數據
get_IndexData(h_MINUSDI,2,0,5,a_MINUSDI) ; // 獲取最近5根-DI數據
}
//+------------------------------------------------------------------+
//| MACD高點背離判斷函數 |
//+------------------------------------------------------------------+
bool MACD_HDiv_01()
{
if((ArrayMaximum(High) !=0 && ArrayMaximum(High) <= 2) && (ArrayMaximum(a_DIF) > 2) && (a_RSIA[1] < 70)) // 高點在最近2根K棒但DIF最大值更早且RSI<70
{
return true; // 背離成立
}
return false;
}
//+------------------------------------------------------------------+
//| ADX強勢趨勢判斷函數 |
//+------------------------------------------------------------------+
bool ADX強勢趨勢()
{
if(a_ADX[1] > 25 && a_ADX[1] > a_ADX[2]) // ADX>25且上升
{
return true; // 強勢趨勢成立
}
return false;
}
//+------------------------------------------------------------------+
//| 計算Force Index指標 |
//+------------------------------------------------------------------+
double a_FORCE_A[] ; // Force Index A組陣列
void set_FORCE_INDEX()
{
int h_FORCE_A ; // Force Index A組句柄
h_FORCE_A = iForce(Symbol(), 時間週期, LenA1, MODE_EMA, VOLUME_TICK); // 創建A組Force Index(EMA)
ArraySetAsSeries(a_FORCE_A, true); // 設定A組陣列為時間序列
get_IndexData(h_FORCE_A, 0, 0, 10, a_FORCE_A); // 獲取A組數據
}
//+------------------------------------------------------------------+
//| Force Index 零軸交叉檢測函數 |
//+------------------------------------------------------------------+
// 返回值:1=上穿零軸(看漲), -1=下穿零軸(看跌), 0=無交叉
int FORCE_ZeroCross()
{
// 檢測A組上穿零軸(從負轉正)
if(a_FORCE_A[2] < 0 && a_FORCE_A[1] > 0)
{
return 1; // 上穿零軸(看漲信號)
}
// 檢測A組下穿零軸(從正轉負)
if(a_FORCE_A[2] > 0 && a_FORCE_A[1] < 0)
{
return -1; // 下穿零軸(看跌信號)
}
return 0; // 無交叉
}
//+------------------------------------------------------------------+
//| Force Index 量價配合檢測函數 |
//+------------------------------------------------------------------+
// 返回值:true=量價配合良好, false=量價背離
bool FORCE_VolumePrice()
{
double price_change = Close[1] - Close[2]; // 計算價格變化(當前收盤價-前根收盤價)
double force_change = a_FORCE_A[1] - a_FORCE_A[2]; // 計算Force Index變化(當前值-前根值)
// 量價配合判斷:價格變化方向與Force Index變化方向一致
if((price_change > 0 && force_change > 0) || (price_change < 0 && force_change < 0))
{
// 進一步檢查強度是否匹配
double price_strength = MathAbs(price_change) / Close[2]; // 價格變化百分比
double force_strength = MathAbs(force_change) / (MathAbs(a_FORCE_A[2]) + 0.0001); // Force Index變化百分比(避免除零)
// 如果Force Index變化幅度與價格變化幅度相當,表示量價配合良好
if(force_strength > price_strength * 0.5) // Force Index變化至少是價格變化的一半
{
return true; // 量價配合良好
}
}
return false; // 量價背離或配合不佳
}
回測結果
測試參數交易商品:XAUUSD(黃金)
樣本內區間:2019/1/1 ~ 2023/8/31
交易手數:固定0.1手
時間框架:H2 小時圖表
交易模式:當沖交易
測試參數交易商品:XAUUSD(黃金)
樣本內區間:2019/1/1 ~ 2023/8/31
交易手數:固定0.1手
時間框架:H2 小時圖表
交易模式:當沖交易
沒有留言:
張貼留言