2025年8月25日 星期一

MT5 EA交易策略開發教學[13]

策略概述(交易商品 XAUUSD)
這是一個基於技術指標的波段交易策略,使用M20時間週期進行交易決策,結合多種技術分析工具來識別趨勢轉折和進出場時機。策略採用風險控制機制,限制每日交易次數並設定資金保護。

基礎概念
時間週期: M20(20分鐘)
風險控制:動態手數計算,風險百分比為1%
資金風控門檻:5000
商品平均點差限制:50點
交易限制: 每日限制1次進場
交易時段: 00:00-16:00(可配置)
停利停損: TP=4800點,SL=1480點

進出場策略
多單進場條件
LE_Cond = (Shrink && High[1] > a_BBUP[1] && Close[1] > Open[1]);


條件分析:
布林帶收縮(Shrink): 上下軌同時收縮,表示波動率降低,蓄勢待發
突破確認(High[1] > a_BBUP[1]): 最高價突破布林帶上軌
紅K確認(Close[1] > Open[1]): 收盤價高於開盤價,確認多頭動能

空單進場條件
SE_Cond = (CCI_HDiv_01() && Low[1] < Lowest_OHLC(Symbol(),時間週期,MODE_LOW,10,1) && 多重死叉確認());

條件分析:
CCI高點背離(CCI_HDiv_01): 價格創新高但CCI未創新高,出現背離信號
創10日新低: 最低價跌破10日最低點
多重死叉確認: 至少2個指標同時出現死叉信號
KD死叉(K線下穿D線)
MACD死叉(DIF下穿MACD線)
RVI死叉
ADX DI死叉

進場價格計算
多單進場價格
BuyPrice = LowD[1] + Range[2]*0.236;
計算方式: 前日最低點 + 第2根K棒區間的23.6%

空單進場價格(編號66)
ShortPrice = HighD[1] - Range[2]*0.236;
計算方式: 前日最高點 - 第2根K棒區間的23.6%

//+------------------------------------------------------------------+
//| MT5 自動交易程式 (Expert Advisor)                               
//| 基於技術指標的趨勢跟隨策略                          
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>              // 交易功能函式庫
#include <MagicMT5_函數庫V2.mqh>        // 自定義函式庫

ENUM_TIMEFRAMES 時間週期 = PERIOD_M20;  // 主要分析時間週期:20分鐘
ENUM_TIMEFRAMES 時間框架 = PERIOD_H4;   // 相對大週期:4小時

//+------------------------------------------------------------------+
//| EA初始化函數                                               
//+------------------------------------------------------------------+
int OnInit()
{
   LoadEA = TimeCurrent();             // 記錄EA載入時間
   return(INIT_SUCCEEDED);             // 初始化成功
}

//+------------------------------------------------------------------+
//| 主要交易邏輯函數(每個價格跳動時執行)                          
//+------------------------------------------------------------------+
void OnTick()
{
   // 資金風控檢查
   if(AccountInfoDouble(ACCOUNT_BALANCE) < 資金風控)
   {
      Alert("**********  資金不足 *************");
      return;
   }

   // 計算當前K棒位置和距離上次出場的K棒數
   BarNumber = iBarShift(Symbol(),時間週期,LoadEA);
   BarSinceExit = BarNumber - CloseOrderNo;
   
   // 新K棒時執行一次性交易測試(僅在第1根K棒執行)
   if((BarNumber == 1 && BarNumber != JudgeNo))
   {
      // 測試下單功能:開多單後立即平倉
      多單進場單號 = Buy_at_MARKET(Symbol(),Lots,0,0,"1st_K",MagicNumber);
      LX_CloseByTicket(多單進場單號,Lots);
      CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA);
      FastSma = MathMin(LenA1, LenB1);  // 設定快速均線週期
      SlowSma = MathMax(LenA1, LenB1);  // 設定慢速均線週期
   }

   // 新K棒時更新指標和交易時段
   if(BarNumber != JudgeNo)
   {
      換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)
   {
      //+------------------------------------------------------------------+
      //| 多單進場邏輯                                                      |
      //+------------------------------------------------------------------+
      set_BuyCondition();              // 設定多單進場條件
      if(LE_Cond == true && SP <= 商品平均點差)  // 進場條件成立且點差合理
      {
         // 確保空手且不在同一根K棒下單
         if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo)
         {
 
            // 確保距離上次出場超過1根K棒且當日未超過交易次數限制
            if(BarSinceExit > 1 && EntriesToday(MagicNumber,Symbol()) < 1)
            {

                  多單進場單號 = Buy_at_MARKET(Symbol(),Lots,TP,SL,"BUY MARKET",MagicNumber);
                  OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA);

              } // end of BarSinceExit > 1
           } // end of 空手且不同根K棒() == true
        } // end of LE_Cond == true
      //+------------------------------------------------------------------+
      //| 空單進場邏輯                                                      |
      //+------------------------------------------------------------------+
      set_ShortCondition();            // 設定空單進場條件
      if(SE_Cond == true && SP <= 商品平均點差)  // 進場條件成立且點差合理
      {
         // 確保空手且不在同一根K棒下單
         if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo)
         {

            // 確保距離上次出場超過1根K棒且當日未超過交易次數限制
            if(BarSinceExit > 1 && EntriesToday(MagicNumber,Symbol()) < 1)
            {

                  空單進場單號 = Short_at_MARKET(Symbol(),Lots,TP,SL,"Short Market",MagicNumber);
                  OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA);

              } //end of BarSinceExit > 1
           } // end of 空手且不同根K棒() == true
        } // end of SE_Cond == true
     } //end of 允許交易時段 == true

   // 即使不在交易時段也允許停損停利
   交易時段外也可停損停利();
   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_BarInfo();            // 計算K棒資訊(實體、影線等)
   set_ATR();                // 計算ATR指標

   // 更新各種技術指標
   set_BBAND();              // 布林通道
   set_MACD();               // MACD指標
   set_KD();                 // KD指標

   set_ADX();                // ADX指標
   set_CCI();                // CCI指標
   set_RVI();                // RVI指標
   set_ICHIMOKU();           // 一目均衡表

   // 計算五日平均振幅
   Day5Range = ((HighD[1] - LowD[1]) + (HighD[2] - LowD[2]) + 
                (HighD[3] - LowD[3]) + (HighD[4] - LowD[4]) + 
                (HighD[5] - LowD[5])) / 5;
}

//+------------------------------------------------------------------+
//| 交易時段判斷                                                   
//+------------------------------------------------------------------+
void 交易時段賦值()
{
      允許交易時段 = (getTM_hour(TimeCurrent()) >= 0 && getTM_hour(TimeCurrent()) < 16);
}

//+------------------------------------------------------------------+
//| 停損停利執行(即使在非交易時段也可執行)     
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
{
   // 多單停損停利邏輯
   if(多單部位() > 0)
   {
      double 多單最小停利 = 0.0;
      bool LX_MinPF = false;
      
      // 獲取多單進場價格和計算停損停利價格
      多單進場價格 = LE_EntryPrice(MagicNumber,多單進場單號);
      多單最小停利 = NormalizeDouble(多單進場價格 + SP*3,Digits());  // 最小獲利保護
      LX_MinPF = Bid > 多單最小停利;  // 是否達到最小獲利
      多單停利價格 = NormalizeDouble(多單進場價格 + TP * Point(),Digits());
      多單停損價格 = NormalizeDouble(多單進場價格 - SL * Point(),Digits());

      // 多單出場條件

         // 達到最小獲利且MACD下穿零軸,或觸及停損
         LX_Cond = ((LX_MinPF == true && CrossUnder(a_MACD[2],0,a_MACD[1],0)) || 
                    (Close[1] >= 多單停損價格 && Bid < 多單停損價格));

      // 執行多單平倉
      if(LX_Cond == true && BarNumber != CloseOrderNo && 
         LE_BarsSinceEntry(MagicNumber,多單進場單號,時間週期) > 0)
      {
         LX_CloseByTicket(多單進場單號,Lots);
         if(多單部位() == 0)
         {
            CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA);
         }
      }
   }

   // 空單停損停利邏輯
   if(空單部位() > 0)
   {
      double 空單最小停利 = 0.0;
      bool SX_MinPF = false;
      
      // 獲取空單進場價格和計算停損停利價格
      空單進場價格 = SE_EntryPrice(MagicNumber,空單進場單號);
      空單最小停利 = NormalizeDouble(空單進場價格 - SP*3,Digits());  // 最小獲利保護
      SX_MinPF = Ask < 空單最小停利;  // 是否達到最小獲利
      空單停利價格 = NormalizeDouble(空單進場價格 - TP * Point(),Digits());
      空單停損價格 = NormalizeDouble(空單進場價格 + SL * Point(),Digits());

      // 空單出場條件

         // 達到最小獲利且一目均衡表金叉,或觸及停損
         SX_Cond = ((SX_MinPF == true && CrossOver(a_ICHI_C[2],a_ICHI_B[2],a_ICHI_C[1],a_ICHI_B[1])) || 
                    (Close[1] <= 空單停損價格 && Ask > 空單停損價格));

      // 執行空單平倉
      if(SX_Cond == true && BarNumber != CloseOrderNo && 
         SE_BarsSinceEntry(MagicNumber,空單進場單號,時間週期) > 0)
      {
         SX_CloseByTicket(空單進場單號,Lots);
         if(空單部位() == 0)
         {
            CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA);
         }
      }
   }
}

//+------------------------------------------------------------------+
//| 多單進場條件設定
//+------------------------------------------------------------------+
void set_BuyCondition()
{
      // 布林帶收縮 + 突破上軌 + 紅K確認
      LE_Cond = (Shrink && High[1] > a_BBUP[1] && Close[1] > Open[1]);
}

//+------------------------------------------------------------------+
//| 空單進場條件設定
//+------------------------------------------------------------------+
void set_ShortCondition()
{
      // CCI高點背離 + 創10日新低 + 多重死叉確認
      SE_Cond = (CCI_HDiv_01() && Low[1] < Lowest_OHLC(Symbol(),時間週期,MODE_LOW,10,1) && 多重死叉確認());
}


//+------------------------------------------------------------------+
//| 進出場條件判斷函數                                                  |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| ADX DI死叉檢測函數                                                 |
//+------------------------------------------------------------------+
bool ADX_DI死叉()
{
   // 檢測+DI下穿-DI的死叉信號
   if(a_PLUSDI[2] >= a_MINUSDI[2] && a_PLUSDI[1] < a_MINUSDI[1])
   {
      return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| RVI死叉檢測函數                                                    |
//+------------------------------------------------------------------+
bool RVI_死叉()
{
   // 檢測RVI主線下穿信號線的死叉信號
   if(a_RVI_1[2] >= a_RVI_2[2] && a_RVI_1[1] < a_RVI_2[1])
   {
      return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 多重死叉確認函數                                                    |
//+------------------------------------------------------------------+
bool 多重死叉確認()
{
   int CrossDN_count = 0;  // 死叉信號計數器
   
   // 檢查KD死叉
   if(a_KA2[1] < a_DA2[1] && a_KA2[2] >= a_DA2[2])
      CrossDN_count++;
   
   // 檢查MACD死叉
   if(a_DIF[1] < a_MACD[1] && a_DIF[2] >= a_MACD[2])
      CrossDN_count++;
   
   // 檢查RVI死叉
   if(RVI_死叉())
      CrossDN_count++;
   
   // 檢查ADX DI死叉
   if(ADX_DI死叉())
      CrossDN_count++;

   // 至少2個指標同時出現死叉才確認信號
   if(CrossDN_count >= 2)
   {
      return true;
   }
   return false;
}


回測結果
測試參數交易商品:XAUUSD(黃金)
樣本內區間:2019/1/1 ~ 2023/7/31
交易手數:固定0.1手
時間框架:20分鐘圖表
交易模式:波段交易

現在就加入《外匯實戰 MT5 EA基礎班》,用量化思維,開啟你的交易新紀元!


★ ~~~ 早鳥優惠 ( 2025/11/13 前報名) ~~~ ★ 
🎁限量加碼 贈送 實戰EA *2
(XAUUSD *1 + NAS100 * 1)
可攜伴2位參加文創手作聖誕樹 DIY才藝班





沒有留言:

張貼留言