2026年5月15日 星期五

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

核心策略概述(商品:富台指 TAIEX)
核心策略
多單:布林下軌反彈 + KD 超賣確認(Buy_at_MARKET 市價買入)
空單:BBUP 倒V型趨勢 + BB 下軌擴張 + 收盤遞減確認(Short_at_MARKET 市價賣出)
👉 [ 點擊此處領取 範例 EA 體驗版]  
交易限制
時段:交易時段(12:00~18:00 或 04:00~10:00)
波段模式:若當日三倍庫存費,23:30 前若有部位則執行避讓出場(不重入)
商品預設停止下單時間:商品收盤整點時刻自動刪除所有掛單
每日限 2 次進場:EntriesToday < 2
同根 K 棒不重複下單:BarNumber != OrderBarNo 且 BarNumber != CloseOrderNo
點差過濾:SP < 商品平均點差 * Point()
資金風控:帳戶餘額 < 資金風控門檻時,強制刪除所有掛單、平倉全部部位並退出 EA

進場條件
多單模組 : 
Low[1] < a_BBDN[1](下影線觸及布林下軌)&& Close[1] > a_BBDN[1](收盤站回下軌之上)&& a_KA2[1] < 30(KD 超賣)
空單模組 : 
BBUP_ATrend()(布林上軌呈倒V型趨勢)&& a_BBDN[1] < a_BBDN[2](BB 下軌向下擴張)&& Close[1] < Close[2] - Range[1]*0.382(收盤遞減超過前根振幅 38.2%)

進場方式
多單下單方式 1:Buy_at_MARKET 市價買入,即時成交
空單下單方式 1:Short_at_MARKET 市價賣出,即時成交

出場條件
多單出場方式 :
 溢價區域 在溢價區域() + MACD 高背離 MACD_HDiv_01() + 已達最小停利(Bid > 進場價 + SP*3)觸發平倉; 
同時以近 2 週高點 MathMax(HighW[1], HighW[2]) 動態上移停利價(停利只向有利方向移動,且須 > 初始 TP 的 30%); 
或直接觸及 多單停損價格 / 多單停利價格 即平倉
 
空單出場方式 : 
折價區域 在折價區域() + MACD 低背離 MACD_LDiv_01() + 已達最小停利(Ask < 進場價 - SP*3)觸發平倉; 
同時以近 2 週低點 MathMin(LowW[1], LowW[2]) 動態下移停利價(停利只向有利方向移動,且須 < 初始 TP 的 30%); 
或直接觸及 空單停損價格 / 空單停利價格 即平倉

停損停利設計
進場首根 K 棒(BarsSinceEntry == 0)立即呼叫 TPSLchange() 推送初始停損停利至券商端
停損停利價格每 Tick 從 PositionGetDouble(POSITION_TP/SL) 動態讀取,反映追蹤止損的即時狀態
多單:初始停利 = 進場價 + TP*Point();初始停損 = 進場價 - SL*Point()
空單:初始停利 = 進場價 - TP*Point();初始停損 = 進場價 + SL*Point()
 
換K棒更新指標
商品收盤整點前自動刪除全部掛單
更新指標:BBAND、MACD、RSI、KD、ATR、波動率、Bar/Day/Week OHLC、近2週高低點(TwoWeekHL)、Day5Range(五日均幅)

//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void OnTick()
  {

   if(!IsMarketOpen(10))
      return;

   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) ;
      ExpertRemove();

      return;
     }

//波段單 三倍庫存費用日 先出場
   if(!當沖)      CheckAndCloseForSwap(23, 30);  // 只避讓,不重入

   BarNumber = iBarShift(Symbol(),時間週期,LoadEA);
   BarSinceExit = BarNumber - CloseOrderNo;

   if((BarNumber == 1 && BarNumber != JudgeNo))
     {
      CloseOrderNo = -1;  // 直接設定,不開倉
     }

   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)) ;
     }

   交易時段外也可停損停利();

   if(允許交易時段 == true)
     {
      double 價格遠距上限 = Close[1] * 突破距離百分比;   // 距市價不超過0.004%
      double 價格遠距下限 = Close[1] * 限價距離百分比;  // 距市價不超過0.002%
      //     SL = MathMax(SL,最小止損*2)  ;
      //+------------------------------------------------------------------+
      //|多單進場
      //+------------------------------------------------------------------+

      set_BuyCondition();
      if(LE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits()))
        {

         if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo && BarNumber != CloseOrderNo)  // OnTradeTransaction 設定

           {
            if(EntriesToday(MagicNumber,Symbol()) < 2)
              {
                  //  空手+市價單買入
                  Print("✅✅✅ 準備開市價多單 | K棒:", BarNumber," 舊單號",OrderBarNo, " 上次平倉單號",CloseOrderNo);
                  多單進場單號 = Buy_at_MARKET(Symbol(),Lots,TP,SL,"BUY MARKET",MagicNumber) ;

              } // end of EntriesToday
           } // end of 空手且不同根K棒() == true
        } // end of LE_Cond == true
      //+------------------------------------------------------------------+
      //|空單進場
      //+------------------------------------------------------------------+
      set_ShortCondition() ;
      if(SE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits()))
        {
         if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo && BarNumber != CloseOrderNo)   // OnTradeTransaction 設定
           {
            if(EntriesToday(MagicNumber,Symbol()) < 2)
              {
                  // 空手+市價賣出
                  Print("✅✅✅ 準備開市價空單 | K棒:", BarNumber);
                  空單進場單號 = 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

//+------------------------------------------------------------------+
//|
//+-------------------------------------------------------------------+
//   交易時段外也可停損停利();
   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 ;
  }

//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void 換K棒()
  {
// 商品收盤時間前刪除所有掛單
   if((getTM_hour(TimeCurrent()) == 商品收盤時間) && (total_pending_order_count(Symbol(), MagicNumber,-1) != 0))
     {
      delete_pending_orders_all(Symbol(), MagicNumber, -1, 0x0000ff);
     }

   Set_OHLC_Bar_Series(); //保留
   Set_OHLC_Day_Series(); //保留
   Get_OHLC_Bar(30) ; //保留
   Get_OHLC_BigBar(15) ; //保留
   Get_OHLC_Day(15) ; //保留
   set_BarInfo(); //保留
   set_ATR(); //保留

   Set_OHLC_Week_Series();
   Get_OHLC_Week(5) ;

   Get_Volatility() ; //保留

   set_BBAND();
   set_MACD();
   set_RSI();
   set_KD() ;

   TwoWeekHL();

   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()) >= 12 || (getTM_hour(TimeCurrent()) >= 4 && getTM_hour(TimeCurrent()) < 10));
  }

//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
  {
// 交易時段外也可停損停利
   if(多單部位() > 0)
     {
      //+------------------------------------------------------------------+
      //|    多單出場邏輯
      //+------------------------------------------------------------------+
      double 多單最小停利 = 0.0, 多單保本價格 = 0.0 ;
      bool LX_MinPF = false, LongPull = false;

      多單進場價格 = LE_EntryPrice(MagicNumber, 多單進場單號);
      多單最小停利 = NormalizeDouble(多單進場價格 + SP * 3, Digits());
      LX_MinPF = Bid > 多單最小停利;

      // 計算進場後的最高價
      多單最高價 = iHigh(Symbol(), 時間週期, iHighest(Symbol(), 時間週期, MODE_HIGH, LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期), 1));
      多單最高價 = NormalizeDouble(多單最高價, Digits());

      //--- 進場時設定初始停損停利
      if(LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期) == 0)
        {
         double 初始停利 = NormalizeDouble(多單進場價格 + TP * Point(), Digits());
         double 初始停損 = NormalizeDouble(多單進場價格 - SL * Point(), Digits());

         TPSLchange(Symbol(), 多單進場單號, 初始停利, 初始停損, 時間週期);
        }

      // 獲取當前實際的 TP/SL (供 LX_Cond 判斷用)

      if(PositionSelectByTicket(多單進場單號))
        {
         多單停利價格 = PositionGetDouble(POSITION_TP);
         多單停損價格 = PositionGetDouble(POSITION_SL);
        }

      //+------------------------------------------------------------------+
      //|    出場方式計算
      //+------------------------------------------------------------------+
      //--- 多單出場方式 : 溢價區域+MACD高背離 + 近2週高點追蹤停利

         double 近2週高 = NormalizeDouble(MathMax(HighW[1], HighW[2]), Digits());
         double 最小停利 = NormalizeDouble(多單進場價格 + TP * 0.3 * Point(), Digits());
         if(近2週高 > 最小停利 && 近2週高 > 多單停利價格)
           {
            多單停利價格 = 近2週高;
            TPSLchange(Symbol(), 多單進場單號, 多單停利價格, 0, 時間週期);
           }
         LX_Cond = (Bid > 多單最小停利 && 在溢價區域() && MACD_HDiv_01())
                   || (Bid <= 多單停損價格) || (Bid >= 多單停利價格);


      //+------------------------------------------------------------------+
      //|    多單出場執行
      //+------------------------------------------------------------------+
      if(LX_Cond == true && BarNumber != OrderBarNo && BarNumber != CloseOrderNo &&
         LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期) > 0)
        {
         LX_CloseByTicket(多單進場單號, Lots);
        }

     } // end of 多單部位() > 0

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
   if(空單部位() > 0)
     {
      //+------------------------------------------------------------------+
      //|    空單出場邏輯
      //+------------------------------------------------------------------+
      double 空單最小停利 = 0.0, 空單保本價格 = 0.0;
      bool SX_MinPF = false, ShortPull = false;

      空單進場價格 = SE_EntryPrice(MagicNumber, 空單進場單號);
      空單最小停利 = NormalizeDouble(空單進場價格 - SP * 3, Digits());
      SX_MinPF = Ask < 空單最小停利;

      // 計算進場後的最低價
      空單最低價 = iLow(Symbol(), 時間週期, iLowest(Symbol(), 時間週期, MODE_LOW, SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期), 1));
      空單最低價 = NormalizeDouble(空單最低價, Digits());

      //--- 進場時設定初始停損停利
      if(SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期) == 0)
        {
         double 初始停利 = NormalizeDouble(空單進場價格 - TP * Point(), Digits());
         double 初始停損 = NormalizeDouble(空單進場價格 + SL * Point(), Digits());

         TPSLchange(Symbol(), 空單進場單號, 初始停利, 初始停損, 時間週期);
        }

      // 獲取當前實際的 TP/SL (供 SX_Cond 判斷用)
      if(PositionSelectByTicket(空單進場單號))
        {
         空單停利價格 = PositionGetDouble(POSITION_TP);
         空單停損價格 = PositionGetDouble(POSITION_SL);
        }

      //+------------------------------------------------------------------+
      //|    出場方式計算
      //+------------------------------------------------------------------+
      //--- 空單出場方式 : 折價區域+MACD低背離 + 近2週低點追蹤停利

         double 近2週低 = NormalizeDouble(MathMin(LowW[1], LowW[2]), Digits());
         double 最小停利 = NormalizeDouble(空單進場價格 - TP * 0.3 * Point(), Digits());
         if(近2週低 < 最小停利 && 近2週低 < 空單停利價格)
           {
            空單停利價格 = 近2週低;
            TPSLchange(Symbol(), 空單進場單號, 空單停利價格, 0, 時間週期);
           }
         SX_Cond = (Ask < 空單最小停利 && 在折價區域() && MACD_LDiv_01())
                   || (Ask >= 空單停損價格) || (Ask <= 空單停利價格);


      //+------------------------------------------------------------------+
      //|    空單出場執行
      //+------------------------------------------------------------------+
      if(SX_Cond == true && BarNumber != OrderBarNo && BarNumber != CloseOrderNo &&
         SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期) > 0)
        {
         SX_CloseByTicket(空單進場單號, Lots);
        }

     } // end of 空單部位() > 0
  } // end of 交易時段外也可停損停利()

//+------------------------------------------------------------------+
//|多單模組編號
//+------------------------------------------------------------------+
void set_BuyCondition()
  {
// 布林下軌反彈配合KD超賣
      LE_Cond = Low[1] < a_BBDN[1] && Close[1] > a_BBDN[1] && a_KA2[1] < 30;
  }
//+------------------------------------------------------------------+
//| 空單模組編號
//+------------------------------------------------------------------+
void set_ShortCondition()
  {
// BBUP倒V型趨勢 + BB下軌擴張 + 收盤遞減
      SE_Cond = BBUP_ATrend() && a_BBDN[1] < a_BBDN[2] && Close[1] < Close[2]-Range[1]*0.382 ;
  }
回測結果
測試參數交易商品:TAIEX(富台指)
樣本內區間:2024/1/1 ~ 2025/09/30
交易手數:固定 10 手
時間框架: M20圖表
交易模式:波段交易


沒有留言:

張貼留言