核心策略:
多單:布林通道上軌突破(Buy_at_MARKET 市價單)
空單:三黑K型態 + ADX 趨勢增強(Short_at_MARKET 市價單)
交易限制:
時段:08:00 - 次日 04:00
每日限 2 次進場
同根 K 棒不重複下單(BarNumber != OrderBarNo)
同根 K 棒不於平倉後立即進場(BarNumber != CloseOrderNo)
點差 < 商品平均點差 × Point()
進場條件:
多單:UpBreak==true && Close[1]>a_BBUP[1] && Close[1]>Open[1]
空單:Black3Bar_LClose() && a_ADX[1]>a_ADX[2] && Volume[1]>Volume[2]
進場方式: 兩者皆為市價單直接成交
出場條件:
多單:出現空單信號(SE_Cond) 或 觸及停利 或 觸及停損
空單:出現多單信號(LE_Cond) 或 觸及停利 或 觸及停損
停利擴大為 3 倍 TP,讓利潤有更大成長空間
初始停損停利: 進場當根 K 棒立即推送至券商端
換K棒更新指標: ATR、BBAND、ADX、Volatility、OHLC 各週期數據、Day5Range
//+------------------------------------------------------------------+
//| MT5 自動交易程式 (Expert Advisor)
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh> // 引入 MT5 交易函數庫
#include <MagicMT5_函數庫V3.mqh> // 引入自定義函數庫
ENUM_TIMEFRAMES 時間週期 = PERIOD_H2; // EA 主要運行的時間週期。
ENUM_TIMEFRAMES 時間框架 = PERIOD_D1; // 相對大週期,可能用於趨勢判斷或過濾
//+------------------------------------------------------------------+
//| EA 初始化函數,當 EA 載入圖表時執行一次 |
//+------------------------------------------------------------------+
int OnInit()
{
LoadEA = TimeCurrent(); // 記錄 EA 啟動時的當前伺服器時間
return(INIT_SUCCEEDED); // 初始化成功,返回成功代碼
}
//+------------------------------------------------------------------+
//| 報價驅動函數:每當價格跳動(Tick)時即執行一次 |
//+------------------------------------------------------------------+
void OnTick()
{
// 資金風控檢查:若帳戶餘額低於設定的「資金風控」值,則停止執行並報警
if(AccountInfoDouble(ACCOUNT_BALANCE) < 資金風控)
{
Alert("********** 資金不足 *************");
// 執行完整清倉程序:刪除掛單 → 平倉部位 → 移除 EA
// 第一步:刪除所有未成交的掛單
if(total_pending_order_count(Symbol(), MagicNumber,-1) != 0)
{
delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff);
}
// 第二步:平倉所有持倉部位
if(多單部位() > 0 && BarNumber != CloseOrderNo)
LX_CloseByTicket(多單進場單號,Lots) ;
if(空單部位() > 0 && BarNumber != CloseOrderNo)
SX_CloseByTicket(空單進場單號,Lots) ;
// 第三步:強制移除 EA,停止自動交易
ExpertRemove();
return;
}
// 計算當前 K 棒相對於 EA 啟動時間 (LoadEA) 的位置編號
BarNumber = iBarShift(Symbol(),時間週期,LoadEA);
// 計算自上次平倉後經過了多少根 K 棒
BarSinceExit = BarNumber - CloseOrderNo;
// 特殊邏輯:啟動後第一根 K 棒時,直接將 CloseOrderNo 設為 -1
// 不執行測試性開倉,避免不必要的交易紀錄干擾初始狀態
if((BarNumber == 1 && BarNumber != JudgeNo))
{
CloseOrderNo = -1; // 直接設定,不開倉
}
// 換 K 棒偵測:當 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()) ; // 計算當前點差(Ask-Bid 的絕對值)
//+------------------------------------------------------------------+
//| 計算交易手數:根據風險比例動態調整或使用固定手數 |
//+------------------------------------------------------------------+
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)
{
//+------------------------------------------------------------------+
//| 多單進場邏輯區塊 |
//+------------------------------------------------------------------+
// 確保止損點數不低於最小止損要求,避免滑價或點差過大導致無效止損
SL = MathMax(SL,最小止損) ;
set_BuyCondition(); // 載入多單過濾條件,計算 LE_Cond
// 判斷條件:符合進場信號、且點差小於商品平均點差
if(LE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits()))
{
// 確保當前無多空部位、且同一根 K 棒內不重複下單、且非剛平倉的同一根 K 棒
if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo && BarNumber != CloseOrderNo)
{
// 每日進場次數限制:今日下單次數需小於 2 次
if(EntriesToday(MagicNumber,Symbol()) < 2)
{
if(多單下單方式 == 1) // 使用市價單直接買入
{
// 空手狀態下執行市價買入,立即成交
多單進場單號 = Buy_at_MARKET(Symbol(),Lots,TP,SL,"BUY MARKET",MagicNumber) ;
}
} // end of EntriesToday
} // end of 空手且不同根K棒() == true
} // end of LE_Cond == true
//+------------------------------------------------------------------+
//| 空單進場邏輯區塊 |
//+------------------------------------------------------------------+
set_ShortCondition() ; // 載入空單過濾條件,計算 SE_Cond
// 判斷條件:符合進場信號、且點差在合理範圍內
if(SE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits()))
{
// 確保當前無多空部位、且同一根 K 棒內不重複下單、且非剛平倉的同一根 K 棒
if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo && BarNumber != CloseOrderNo)
{
// 每日進場次數限制:今日下單次數需小於 2 次
if(EntriesToday(MagicNumber,Symbol()) < 2)
{
if(空單下單方式 == 1) // 使用市價單直接賣出
{
// 空手狀態下執行市價賣出,立即成交
空單進場單號 = Short_at_MARKET(Symbol(),Lots,TP,SL,"Short Market",MagicNumber) ;
}
} //end of EntriesToday
} // end of 空手且不同根K棒() == true
} // end of SE_Cond == true
} //end of 允許交易時段 == true
// 更新判斷編號,供下一個 Tick 判斷是否已換 K 棒
JudgeNo = iBarShift(Symbol(),時間週期,LoadEA);
} //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棒()
{
// 於商品收盤時間前刪除所有掛單,避免隔夜掛單殘留造成非預期成交
if((getTM_hour(TimeCurrent()) == 商品收盤時間) && (total_pending_order_count(Symbol(), MagicNumber,-1) != 0))
{
delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff);
}
// 更新 K 線數據數組(Bar 級別與 Day 級別)
Set_OHLC_Bar_Series(); // 保留:設定 Bar 層級 OHLC 陣列
Set_OHLC_Day_Series(); // 保留:設定 Day 層級 OHLC 陣列
Get_OHLC_Bar(30) ; // 保留:取得最近 30 根 K 棒數據
Get_OHLC_BigBar(15) ; // 保留:取得最近 15 根大週期 K 棒數據
Get_OHLC_Day(15) ; // 保留:取得最近 15 天數據
set_BarInfo(); // 保留:設定 K 棒相關資訊
set_ATR(); // 保留:計算 ATR 指標(平均真實波幅)
Get_Volatility() ; // 保留:取得波動率數據
// 計算技術指標
set_BBAND(); // 計算布林通道(Bollinger Bands)
set_ADX(); // 計算 ADX 趨勢強度指標(Average Directional Index)
// 計算最近 5 天的平均波動範圍 (Daily Range),用於動態停利計算
Day5Range = ((HighD[1] - LowD[1])+(HighD[2] - LowD[2])+(HighD[3] - LowD[3])+(HighD[4] - LowD[4])+(HighD[5] - LowD[5]))/5;
}
//+------------------------------------------------------------------+
//| 自訂函數:設定允許交易的時段 |
//+------------------------------------------------------------------+
void 交易時段賦值()
{
// 允許交易時段為上午 8:00 至隔日凌晨 4:00(跨日時段)
允許交易時段 = (getTM_hour(TimeCurrent()) >= 8 || (getTM_hour(TimeCurrent()) >= 0 && getTM_hour(TimeCurrent()) < 4));
}
//+------------------------------------------------------------------+
//| 自訂函數:核心監控 - 處理停損、停利(交易時段外亦持續執行) |
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
{
// --- 多單監控區 ---
// 無論是否在交易時段,只要持有多單就持續監控
if(多單部位() > 0)
{
//+------------------------------------------------------------------+
//| 多單出場邏輯 |
//+------------------------------------------------------------------+
// 取得多單進場價格,並計算最小利潤門檻(需覆蓋 3 倍點差)
多單進場價格 = LE_EntryPrice(MagicNumber, 多單進場單號);
// 計算進場後至今出現的最高價(用於移動停損基準)
多單最高價 = iHigh(Symbol(), 時間週期, iHighest(Symbol(), 時間週期, MODE_HIGH, LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期), 1));
多單最高價 = NormalizeDouble(多單最高價, Digits());
//--- 進場當根 K 棒:設定初始停損停利至券商端,確保保護機制立即生效
if(LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期) == 0)
{
double 初始停利 = NormalizeDouble(多單進場價格 + TP * Point(), Digits());
double 初始停損 = NormalizeDouble(多單進場價格 - SL * Point(), Digits());
// 將停損停利推送至券商端(伺服器端保護)
TPSLchange(Symbol(), 多單進場單號, 初始停利, 初始停損, 時間週期);
}
// 從券商端讀取當前實際的 TP/SL,確保與伺服器數據同步
if(PositionSelectByTicket(多單進場單號))
{
多單停利價格 = PositionGetDouble(POSITION_TP);
多單停損價格 = PositionGetDouble(POSITION_SL);
}
//+------------------------------------------------------------------+
//| 出場方式計算 |
//+------------------------------------------------------------------+
//配合反向訊號出場(持倉至對立信號出現時才離場)
// 擴大停利至 3 倍 TP,讓利潤有更大的成長空間
double 新停利 = NormalizeDouble(多單進場價格 + (TP * 3) * Point(), Digits());
// 只在新停利高於當前停利時才更新,保持停利只升不降
if(新停利 > 多單停利價格)
TPSLchange(Symbol(), 多單進場單號, 多單停利價格, 0, 時間週期);
// 出場條件:出現空單信號(反向進場) 或 觸及停利 或 觸及停損
LX_Cond = (SE_Cond || Bid >= 多單停利價格 || (Bid <= 多單停損價格));
//+------------------------------------------------------------------+
//| 多單出場執行 |
//+------------------------------------------------------------------+
// 確認出場條件成立、不在下單同根 K 棒、不在平倉同根 K 棒、且已持倉超過 0 根 K 棒
if(LX_Cond == true && BarNumber != OrderBarNo && BarNumber != CloseOrderNo &&
LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期) > 0)
{
LX_CloseByTicket(多單進場單號, Lots); // 依票號平倉多單
}
} // end of 多單部位() > 0
// --- 空單監控區 ---
// 無論是否在交易時段,只要持有空單就持續監控
if(空單部位() > 0)
{
//+------------------------------------------------------------------+
//| 空單出場邏輯 |
//+------------------------------------------------------------------+
// 取得空單進場價格,並計算最小利潤門檻(需覆蓋 3 倍點差)
空單進場價格 = SE_EntryPrice(MagicNumber, 空單進場單號);
// 計算進場後至今出現的最低價(用於移動停損基準)
空單最低價 = iLow(Symbol(), 時間週期, iLowest(Symbol(), 時間週期, MODE_LOW, SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期), 1));
空單最低價 = NormalizeDouble(空單最低價, Digits());
//--- 進場當根 K 棒:設定初始停損停利至券商端,確保保護機制立即生效
if(SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期) == 0)
{
double 初始停利 = NormalizeDouble(空單進場價格 - TP * Point(), Digits());
double 初始停損 = NormalizeDouble(空單進場價格 + SL * Point(), Digits());
// 將停損停利推送至券商端(伺服器端保護)
TPSLchange(Symbol(), 空單進場單號, 初始停利, 初始停損, 時間週期);
}
// 從券商端讀取當前實際的 TP/SL,確保與伺服器數據同步
if(PositionSelectByTicket(空單進場單號))
{
空單停利價格 = PositionGetDouble(POSITION_TP);
空單停損價格 = PositionGetDouble(POSITION_SL);
}
//+------------------------------------------------------------------+
//| 出場方式計算 |
//+------------------------------------------------------------------+
//配合反向訊號出場(持倉至對立信號出現時才離場)
// 擴大停利至 3 倍 TP,讓利潤有更大的成長空間
double 新停利 = NormalizeDouble(空單進場價格 - (TP * 3) * Point(), Digits());
// 只在新停利低於當前停利時才更新,保持停利只降不升
if(新停利 < 空單停利價格)
TPSLchange(Symbol(), 空單進場單號, 空單停利價格, 0, 時間週期);
// 出場條件:出現多單信號(反向進場) 或 觸及停利 或 觸及停損
SX_Cond = (LE_Cond || Ask <= 空單停利價格 || (Ask >= 空單停損價格));
//+------------------------------------------------------------------+
//| 空單出場執行
//+------------------------------------------------------------------+
// 確認出場條件成立、不在下單同根 K 棒、不在平倉同根 K 棒、且已持倉超過 0 根 K 棒
if(SX_Cond == true && BarNumber != OrderBarNo && BarNumber != CloseOrderNo &&
SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期) > 0)
{
SX_CloseByTicket(空單進場單號, Lots); // 依票號平倉空單
}
} // end of 空單部位() > 0
} // end of 交易時段外也可停損停利()
//+------------------------------------------------------------------+
//| 多單策略模組:定義買入信號
//+------------------------------------------------------------------+
void set_BuyCondition()
{
// 布林通道突破 + K 棒型態確認
// 條件:
// (1) UpBreak 為真(價格向上突破關鍵區間)
// (2) 收盤價高於布林通道上軌(確認上方動能強勁)
// (3) 收盤價高於開盤價(紅 K 棒,多頭型態確認)
// 適用於強勢突破策略,三重條件過濾確保進場品質
LE_Cond = (UpBreak == true) && Close[1] > a_BBUP[1] && Close[1] > Open[1] ;
}
//+------------------------------------------------------------------+
//| 空單策略模組:定義賣出信號
//+------------------------------------------------------------------+
void set_ShortCondition()
{
// 三黑K型態 + ADX 趨勢增強 + 成交量放大確認
// 條件:
// (1) Black3Bar_LClose():出現三根連續收低的黑 K 棒(下跌動能持續)
// (2) a_ADX[1] > a_ADX[2]:ADX 趨勢強度持續上升(趨勢加速中)
// (3) Volume[1] > Volume[2]:成交量放大(下跌有量支撐,賣壓增強)
// 適用於趨勢延續放空策略,確認下跌趨勢有量能配合才進場
SE_Cond = Black3Bar_LClose() && a_ADX[1] > a_ADX[2] && Volume[1] > Volume[2] ;
}
回測結果
測試參數交易商品:XAUUSD(黃金)樣本內區間:2019/1/1 ~ 2024/06/30
交易手數:固定 0.1 手
時間框架: H2圖表
交易模式:波段交易

沒有留言:
張貼留言