2025年10月20日 星期一

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

核心策略概述 (交易商品 NAS100 )
這是一個基於多重技術指標超賣/超買背離的反轉交易策略。策略整合RVI背離、多重指標鈍化(RSI、KD、Williams %R、CCI)、Keltner通道突破等多項指標來識別進場機會,並使用固定時段交易模式(14:00-22:00)。

交易限制:
資金風控門檻: 低於門檻時停止交易
商品平均點差限制: 進場前檢查點差 < 商品平均點差 * Point()
每日限制1次進場
距離上次平倉需間隔 > 1根K棒
交易時段: 14:00 至 22:00

進出場策略
多單進場條件
LE_Cond = (RVI_HDiv_01() && Close[1] > 流動性池底部價格 && Close[1] > a_KC_UPPER_B[1]);
條件分析:
RVI高點背離 (RVI_HDiv_01())
價格最高點在最近3根K棒內(索引0-2)發生
RVI最高點出現在更早之前(索引>2),顯示動能減弱 目前RVI值 < 0.5(回落狀態)

突破流動性池底部價格
收盤價突破最近20根K棒的最低價(從第5根開始計算)
突破Keltner通道上軌B 確認突破強度

進場方式: 市價單買入,僅在無部位、點差小、最近6筆內有空單平倉、今日未進場、BarSinceExit > 1時執行

空單進場條件
SE_Cond = (多重超賣鈍化() && Close[1] > Close[3] && Close[1] < Open[1]);

條件分析:
多重超賣鈍化 (多重超賣鈍化())
需要至少2個以上指標同時出現低檔鈍化:
RSI低檔鈍化: RSI < 30 且出現 ≥ 4次
KD低檔鈍化: K值 < 30 且出現 ≥ 4次
Williams %R低檔鈍化: WPR < -80 且出現 ≥ 4次
CCI超賣鈍化: CCI < -100 且出現 ≥ 4次

價格回升確認
Close[1] > Close[3] (收盤價高於2根K棒前) 確認超賣後的反彈動作

黑K確認 Close[1] < Open[1] (收盤價低於開盤價) 形成看跌K線型態

進場方式: 市價單賣出,僅在無部位、點差小、今日未進場、BarSinceExit > 1時執行

//+------------------------------------------------------------------+
//| MT5 自動交易程式 (Expert Advisor)                                                 
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>                                             // 引入 MT5 交易函數庫
#include <MagicMT5_函數庫V2.mqh>                            // 引入自定義函數庫

ENUM_TIMEFRAMES 時間週期 = PERIOD_M10; // 主要分析時間框架(10分鐘) ENUM_TIMEFRAMES 時間框架 = PERIOD_H1; // 相對大週期(1 小時)
//+------------------------------------------------------------------+
//| EA 初始化函數                                                     
//| 說明:程式啟動時執行一次,用於初始化變數和顯示商品資訊               
//+------------------------------------------------------------------+
int OnInit()
  {
   // 記錄EA載入時間
   LoadEA = TimeCurrent();
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| EA 主要執行函數                                                   
//| 說明:每個Tick(價格變動)都會執行,包含所有交易邏輯                     
//+------------------------------------------------------------------+
void OnTick()
  {
   // 檢查帳戶餘額是否低於風控設定值
   if(AccountInfoDouble(ACCOUNT_BALANCE) < 資金風控)
     {
      Alert("**********  資金不足 *************");
      return; // 資金不足時停止執行
     }

   // 計算目前K棒編號(從EA載入時間開始算起)
   BarNumber = iBarShift(Symbol(),時間週期,LoadEA);
   
   // 計算距離上次平倉後經過的K棒數量
   BarSinceExit = BarNumber-CloseOrderNo ;
   
   // 當出現新K棒時(BarNumber=1)且未重複判斷時,執行測試交易
   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棒編號改變時,更新指標和交易時段設定
   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()) ; // 計算買賣價差(點差)

//+------------------------------------------------------------------+
//| 動態計算交易手數                                                   
//| 說明:根據風險百分比和停損點數自動計算合適的交易手數                  
//+------------------------------------------------------------------+
   if(動態計算手數 == true)
     {
      // 根據帳戶餘額、風險百分比和停損點數計算手數
      Lots = get_dynamic_lot_size(是否偶數單,Symbol(),風險百分比,AccountBalance,SL) ;
      
      // 限制手數範圍在0.01到0.3之間
      Lots = MathMin(0.3,MathMax(0.01,Lots)) ;
     }

   // 只有在允許交易時段內才執行進場邏輯
   if(允許交易時段 == true)
     {
      //+------------------------------------------------------------------+
      //| 多單進場邏輯                                                      
      //+------------------------------------------------------------------+
      set_BuyCondition(); // 計算多單進場條件
      
      // 多單進場判斷:
      // 1. 多單進場條件成立
      // 2. 點差小於商品平均點差
      // 3. 最近6筆內有空單平倉記錄
      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)
              {

                  // 執行市價多單進場
                  多單進場單號 = Buy_at_MARKET(Symbol(),Lots,TP,SL,"BUY MARKET",MagicNumber) ;
                  
                  // 記錄下單時的K棒編號(避免重複下單)
                  OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA);

              }
           }
        }
        
      //+------------------------------------------------------------------+
      //| 空單進場邏輯                                                   
      //+------------------------------------------------------------------+
      set_ShortCondition() ; // 計算空單進場條件
      
      // 空單進場判斷:
      // 1. 空單進場條件成立
      // 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);

              }
           }
        }
     }

   // 執行停損停利邏輯(即使不在交易時段也可執行)
   交易時段外也可停損停利();
   
   // 更新判斷編號,避免同一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棒時執行的更新函數                                             
//| 說明:每當新K棒產生時,更新所有技術指標和K棒數據                    
//+------------------------------------------------------------------+
void 換K棒()
  {
   // 新K棒產生時刪除所有未成交的掛單
   if(total_pending_order_count(Symbol(), MagicNumber,-1) != 0)
     {
      delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff);
     }
     
   // 設定K棒和日線數據的陣列序列(必要步驟)
   Set_OHLC_Bar_Series();
   Set_OHLC_Day_Series();
   
   // 獲取最近30根K棒和15天的OHLC數據
   Get_OHLC_Bar(30) ;
   Get_OHLC_Day(15) ;

   // 更新所有技術指標
   set_MACD();          // 更新MACD指標
   set_RSI();           // 更新RSI指標
   set_KD() ;           // 更新KD隨機指標
   set_CCI();           // 更新CCI指標
   set_RVI();           // 更新RVI指標
   set_WILLIAMS_PR() ;  // 更新威廉指標
   set_KELTNER_CHANNEL() ; // 更新肯特納通道

   // 計算最近5日的平均波動範圍
   Day5Range = ((HighD[1] - LowD[1])+(HighD[2] - LowD[2])+(HighD[3] - LowD[3])+(HighD[4] - LowD[4])+(HighD[5] - LowD[5]))/5;
  }

//+------------------------------------------------------------------+
//| 交易時段判斷                                                     
//| 說明:根據時間設定允許交易的時段                                    
//+------------------------------------------------------------------+
void 交易時段賦值()
  {
   // 允許交易時間為14:00到22:00
      允許交易時段 = (getTM_hour(TimeCurrent()) >= 14 && getTM_hour(TimeCurrent()) < 22);
  }

//+------------------------------------------------------------------+
//| 停損停利執行函數                                                   |
//| 說明:即使不在交易時段也執行停損停利,保護帳戶資金                     |
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
  {
   // 處理多單部位的停損停利
   if(多單部位() > 0)
     {
      //+------------------------------------------------------------------+
      //| 多單出場方法設定                                                  |
      //+------------------------------------------------------------------+
      double 多單最小停利 = 0.0;
      bool LX_MinPF = false ;
      
      // 取得多單進場價格
      多單進場價格 = LE_EntryPrice(MagicNumber,多單進場單號);
      
      // 計算最小停利價格(進場價格+3倍點差)
      多單最小停利 = NormalizeDouble(多單進場價格 + SP*3,Digits()) ;
      LX_MinPF = Bid > 多單最小停利 ; // 判斷是否達到最小停利
      
      // 計算多單停利價格(進場價格+停利點數)
      多單停利價格 = NormalizeDouble(多單進場價格 + TP * Point(),Digits()) ;
      
      // 計算多單停損價格(進場價格-停損點數)
      多單停損價格 = NormalizeDouble(多單進場價格 - SL * Point(),Digits()) ;

      // 多單出場方式13:動態停利(加上5日平均波動)

         // 停利價格加上5日平均波動範圍
         多單停利價格 = NormalizeDouble(多單進場價格 + (TP+Day5Range) * Point(),Digits()) ;
         
         // 多單出場條件:價格突破停利或停損價格
         LX_Cond = ((Close[1] <= 多單停利價格 && Bid > 多單停利價格) || (Close[1] >= 多單停損價格 && Bid < 多單停損價格)) ;

      // 執行多單平倉
      // 條件:1.出場條件成立 2.非同一K棒重複平倉 3.持倉超過0根K棒
      if(LX_Cond == true && BarNumber != CloseOrderNo && LE_BarsSinceEntry(MagicNumber,多單進場單號,時間週期) > 0)
        {
         // 執行平倉
         LX_CloseByTicket(多單進場單號,Lots) ;

         // 如果多單已全部平倉,記錄平倉K棒編號
         if(多單部位() == 0)
           {
            CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ;
           }
        }
     }

   // 處理空單部位的停損停利
   if(空單部位() > 0)
     {
      //+------------------------------------------------------------------+
      //| 空單出場方法設定                                                  |
      //+------------------------------------------------------------------+
      double 空單最小停利 = 0.0;
      bool SX_MinPF = false ;
      
      // 取得空單進場價格
      空單進場價格 = SE_EntryPrice(MagicNumber,空單進場單號);
      
      // 計算最小停利價格(進場價格-3倍點差)
      空單最小停利 = NormalizeDouble(空單進場價格 - SP*3,Digits()) ;
      SX_MinPF = Ask < 空單最小停利 ; // 判斷是否達到最小停利
      
      // 計算空單停利價格(進場價格-停利點數)
      空單停利價格 = NormalizeDouble(空單進場價格 - TP * Point(),Digits()) ;
      
      // 計算空單停損價格(進場價格+停損點數)
      空單停損價格 = NormalizeDouble(空單進場價格 + SL * Point(),Digits()) ;

      // 空單出場方式11:MACD黃金交叉或觸及停損

         // 空單出場條件:1.達到最小停利且MACD向上交叉0軸 2.價格突破停損
         SX_Cond = ((SX_MinPF == true && CrossOver(a_MACD[2],0,a_MACD[1],0)) || (Close[1] <= 空單停損價格 && Ask > 空單停損價格)) ;
        }

      // 執行空單平倉
      // 條件:1.出場條件成立 2.非同一K棒重複平倉 3.持倉超過0根K棒
      if(SX_Cond == true && BarNumber != CloseOrderNo && SE_BarsSinceEntry(MagicNumber,空單進場單號,時間週期) > 0)

         // 執行平倉
         SX_CloseByTicket(空單進場單號,Lots) ;

         // 如果空單已全部平倉,記錄平倉K棒編號
         if(空單部位() == 0)
           {
            CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ;
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| 多單進場條件設定                                                 
//| 說明:根據多單模組編號設定不同的進場條件                           
//+------------------------------------------------------------------+
void set_BuyCondition()
  {
   // 條件1:RVI高點背離(價格創新高但RVI未創新高)
   // 條件2:收盤價突破流動性池底部價格
   // 條件3:收盤價突破Keltner通道上軌B
      LE_Cond = (RVI_HDiv_01() && Close[1] > 流動性池底部價格 && Close[1] > a_KC_UPPER_B[1]) ;
  }

//+------------------------------------------------------------------+
//| 空單進場條件設定                                                  
//| 說明:根據空單模組編號設定不同的進場條件                     
//+------------------------------------------------------------------+
void set_ShortCondition()
  {
   // 條件1:多重超賣鈍化(多個指標同時顯示超賣且持續)
   // 條件2:價格回升(收盤價高於2根K棒前)
   // 條件3:形成黑K(收盤價低於開盤價)
      SE_Cond = (多重超賣鈍化() && Close[1] > Close[3] && Close[1] < Open[1]) ;
  }


回測結果
測試參數交易商品:NAS100(那斯達克指數)
樣本內區間:2019/1/1 ~ 2023/8/31
交易手數:固定 1 手
時間框架:M10 分鐘圖表
交易模式:波段交易

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


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







沒有留言:

張貼留言