2026年4月20日 星期一

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

核心策略概述(商品 XAUUSD)

核心策略
多單:DeMarK 看漲背離 + 量增確認(Buy_at_STOP 突破掛單)
空單:布林下軌跌破 + 一目均衡表空頭趨勢確認(Short_at_LIMIT 限價掛單

交易限制
時段:12:00 以後 或 04:00~10:00(交易時段編號 18)
當沖模式:接近收盤時段(>=21:00 或 <=01:00)且禁止新倉,另加 !商品預設停止下單時間 過濾
商品預設停止下單時間:23:50 以後 或 00:00 整 或 01:00~01:06 封鎖所有下單與平倉
每日限 2 次進場:EntriesToday < 2
同根 K 棒不重複下單:BarNumber != OrderBarNo
同根 K 棒不於平倉後立即進場:BarNumber != CloseOrderNo
點差過濾:SP < 商品平均點差 * Point()

進場條件
多單模組 :DEMARK_LDiv_02()(TD 序列看漲背離)+ Volume[1] > Volume[2](量增確認)
空單模組 :Close[1] < a_BBDN[1](收盤跌破布林下軌)+ ICHI_Trend() == -1(一目均衡表空頭方向)

進場方式
多單下單方式 :Buy_at_STOP 突破掛單,有效期 3600 秒
掛單價:a_KC_UPPER[1] + a_StdDEV[2]*0.5(肯特納上軌 + 標準差緩衝)
距市價須 > 最小止損*2,且 < 價格遠距上限(Close[1]*突破距離百分比)

空單下單方式 :Short_at_LIMIT 限價掛單,有效期 3600 秒
掛單價:POC_價格 - a_ATR[1]*1.618(POC 下方黃金比例緩衝)
距市價須 > 最小止損*2,且 < 價格遠距下限(Close[1]*限價距離百分比)
額外防呆:賣出價格 > 0(防 POC 未初始化)

出場條件
多單出場方式 :CDP 的 AH 值動態上移停利(不低於進場價 TP*0.3 最低門檻,停利只向上調整)+ 流星線確認() && Bid > NH(過熱反轉訊號)+ 已達最小停利;或直接觸及 多單停損價格 / 多單停利價格

空單出場方式 :創新低時(Close[1] < Low[2] && Low[1] == 空單最低價)以 Ask*RatioTP / Ask*RatioSL 比例重算停損停利;停損停利只向有利方向移動,觸及即平倉
停損停利設計
進場首根 K 棒(BarsSinceEntry == 0)立即呼叫 TPSLchange() 推送初始停損停利至券商端
停損停利價格每 Tick 從 PositionGetDouble(POSITION_TP/SL) 動態讀取,反映追蹤止損的即時狀態

換K棒更新指標
收盤時間前自動刪除全部掛單
更新指標:BBAND、CDP、DeMarK、ICHIMOKU、Keltner Channel、POC、ATR、波動率、Bar/Day OHLC

//+------------------------------------------------------------------+
//| 報價驅動函數:每當價格跳動(Tick)時即執行一次                         |
//+------------------------------------------------------------------+
void OnTick()
  {
   // 商品預設停止下單時間:
   // 晚上 23:50 後(避開日結算前流動性不足時段)
   // 或午夜 0 點整,或凌晨 1 點且分鐘數 ≤ 6 分(避開收盤後低流動性時段)
   商品預設停止下單時間 = (getTM_hour(TimeCurrent()) == 23 && getTM_min(TimeCurrent()) >= 50)
                                    || (getTM_hour(TimeCurrent()) == 0) || (getTM_hour(TimeCurrent()) == 1 && getTM_min(TimeCurrent()) <= 6);

   // 鎖時間(到期保護機制,目前已停用,可視需求啟用)
   /*
      if(TimeLocal()>D'2026.05.31 23:59') //到期時間
        {
         Alert("使用時間已經過期喔!");

         ExpertRemove();
         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) ;
      // 第三步:強制移除 EA,停止自動交易
      ExpertRemove();

      return;
     }

   // 計算當前 K 棒相對於 EA 啟動時間 (LoadEA) 的位置編號
   BarNumber = iBarShift(Symbol(),時間週期,LoadEA);
   // 計算自上次平倉後經過了多少根 K 棒
   BarSinceExit = BarNumber - CloseOrderNo;

   // 特殊邏輯:當處於啟動後的第一根 K 棒且尚未標記過 JudgeNo 時執行
   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)
     {
      // 判斷當前時間是否為接近收盤時段(21:00 以後或凌晨 1 點以前)
      接近收盤 = (getTM_hour(TimeCurrent()) >= 21 || getTM_hour(TimeCurrent()) <= 1) ;
      // 若接近收盤或處於預設停止下單時間,則禁止新倉進場(僅允許平倉)
      允許交易時段 = (允許交易時段 && !接近收盤 && !商品預設停止下單時間) ;
     }

   // 無論是否在交易時段內,皆持續監控部位的停損與停利(保護部位)
   // 注意:此呼叫置於進場判斷之前,確保每個 Tick 都優先處理風控
   交易時段外也可停損停利();

   // 交易時段判斷:只有在允許交易的時段內才執行進場邏輯
   if(允許交易時段 == true)
     {
      // 計算掛單距市價的合理距離上下限,防止掛單價格偏離市價過遠
      double 價格遠距上限 = Close[1] * 突破距離百分比;  // 突破掛單距市價不超過指定百分比(如 0.4%)
      double 價格遠距下限 = Close[1] * 限價距離百分比;  // 限價掛單距市價不超過指定百分比(如 0.2%)
      //     SL = MathMax(SL,最小止損*2)  ;

      //+------------------------------------------------------------------+
      //| 多單進場邏輯區塊                                                  |
      //+------------------------------------------------------------------+
      set_BuyCondition(); // 載入多單過濾條件

      // 判斷條件:符合進場信號、點差在合理範圍內、且非預設停止下單時間
      if(LE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits()) && !商品預設停止下單時間)
        {
         // 確保當前無多空部位,且同一根 K 棒內不重複下單(同時排除剛平倉的 K 棒)
         // OnTradeTransaction 設定
         if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo && BarNumber != CloseOrderNo)
           {
            // 計算買入價格(依據多單價格模組設定)
            買入價格 = Get_BuyPrice() ;

            // 每日進場次數限制(最多 2 次)
            if(EntriesToday(MagicNumber,Symbol()) < 2)
              {
               // 下單方式 :空手狀態下執行 STOP 掛單突破買入
                  // 三重價格過濾:
                  // (1) 掛單價必須高於市價,且距離超過最小止損距離的 2 倍(確保不立即成交)
                  // (2) 掛單距市價不超過收盤價的指定百分比(防止追高過遠)
                  // (3) 上根 K 棒收盤價低於掛單價,且非商品收盤時間(避開低流動性時段)
                  if(買入價格 > Ask + (最小止損*Point()) * 2
                     && 買入價格 < Ask + 價格遠距上限
                     && Close[1] < 買入價格 && getTM_hour(TimeCurrent()) != 商品收盤時間)
                    {
                     // 確認目前無其他掛單,避免重複建立相同方向掛單
                     if(total_pending_order_count(Symbol(), MagicNumber,-1) == 0)
                       {
                        // 輸出除錯資訊:確認下單前的關鍵數值
                        Print("✅✅✅ 準備開突破多單 | K棒:", BarNumber,
                              " | 掛單價:", 買入價格,
                              " | 距市價%:", DoubleToString((買入價格 - Ask) / Close[1] * 100, 3));
                        多單進場單號 = Buy_at_STOP(Symbol(),MagicNumber,買入價格,Lots,TP,SL,"BUY STOP",3600) ;
                       }
                    }
              } // end of EntriesToday
           } // end of 空手且不同根K棒() == true
        } // end of LE_Cond == true

      //+------------------------------------------------------------------+
      //| 空單進場邏輯區塊                                                  |
      //+------------------------------------------------------------------+
      set_ShortCondition() ; // 載入空單過濾條件

      // 判斷條件:符合進場信號、點差在合理範圍內、且非預設停止下單時間
      if(SE_Cond == true && SP < NormalizeDouble(商品平均點差*Point(),Digits()) && !商品預設停止下單時間)
        {
         // 確保當前無多空部位,且同一根 K 棒內不重複下單(同時排除剛平倉的 K 棒)
         // OnTradeTransaction 設定
         if(多單部位() == 0 && 空單部位() == 0 && BarNumber != OrderBarNo && BarNumber != CloseOrderNo)
           {
            // 計算賣出價格(依據空單價格模組設定)
            賣出價格 = Get_ShortPrice();

            // 每日進場次數限制(最多 2 次)
            if(EntriesToday(MagicNumber,Symbol()) < 2)
              {
               // 下單方式 :空手狀態下執行 LIMIT 掛單限價賣出
                  // 四重價格過濾:
                  // (1) 掛單價必須高於市價,且距離超過最小止損距離的 2 倍(確保不立即成交)
                  // (2) 掛單距市價不超過收盤價的指定百分比(防止距離過遠)
                  // (3) 掛單價必須大於 0(防止 POC 未初始化時產生無效掛單)
                  // (4) 上根 K 棒收盤價低於掛單價,且非商品收盤時間(避開低流動性時段)
                  if(賣出價格 > Bid + (最小止損*Point()) * 2
                     && 賣出價格 < Bid + 價格遠距下限
                     && 賣出價格 > 0
                     && Close[1] < 賣出價格 && getTM_hour(TimeCurrent()) != 商品收盤時間)
                    {
                     // 確認目前無其他掛單,避免重複建立相同方向掛單
                     if(total_pending_order_count(Symbol(), MagicNumber,-1) == 0)
                       {
                        // 輸出除錯資訊:確認下單前的關鍵數值
                        Print("✅✅✅ 準備開限價空單 | K棒:", BarNumber,
                              " | 掛單價:", 賣出價格,
                              " | 距市價%:", DoubleToString((賣出價格 - Bid) / Close[1] * 100, 3));
                        // 執行 LIMIT 掛單賣出(限價賣出),有效期 3600 秒(1 小時)
                        空單進場單號 = Short_at_LIMIT(Symbol(),MagicNumber,賣出價格,Lots,TP,SL,"Short LIMIT",3600) ;
                       }
                    }
              } // end of EntriesToday
           } // end of 空手且不同根K棒() == true
        } // end of SE_Cond == true
     } // end of 允許交易時段 == true

//+------------------------------------------------------------------+
//| 當沖平倉檢查:若啟用當沖模式,在指定時間或凌晨 1:05 後強制平倉          |
//+------------------------------------------------------------------+
   if(當沖 == true && (getTM_hour(TimeCurrent()) == 當沖出場時間 || (getTM_hour(TimeCurrent()) == 1 && getTM_min(TimeCurrent()) > 5)))
     {
      // 檢查是否有持倉部位需要平倉
      if(多單部位() > 0 || 空單部位() > 0)
        {
         當沖平倉(); // 執行強制平倉程序
        }
     }

   // 更新判斷編號,用於判斷下次 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_CDP();             // 計算 CDP 指標(逆勢操作指標,取得 AH/NH/NL/AL 關鍵價位)
   set_DEMARK();          // 計算 DeMarK 指標(識別潛在反轉的 TD 序列)
   set_ICHIMOKU();        // 計算一目均衡表(判斷趨勢方向與支撐壓力)

   // ***************
   set_KELTNER_CHANNEL() ; // 計算肯特納通道(Keltner Channel),作為進場價格基準
   計算POC() ;              // 計算成交量最大價格(Point of Control),作為空單掛單依據

   // 計算最近 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 交易時段賦值()
  {
   // 交易時段編號 :允許午後 12:00 以後,或早上 4:00 至 10:00 之間交易
      允許交易時段 = (getTM_hour(TimeCurrent()) >= 12 || (getTM_hour(TimeCurrent()) >= 4 && getTM_hour(TimeCurrent()) < 10));
  }

//+------------------------------------------------------------------+
//| 自訂函數:當沖模式下的強制平倉程序                                   |
//+------------------------------------------------------------------+
void 當沖平倉()
  {
   // 檢查是否到達當沖出場時間或凌晨 1 點(跨日強制平倉保護)
   if((getTM_hour(TimeCurrent()) == 當沖出場時間 || getTM_hour(TimeCurrent()) == 1))
     {
      // 若持有多單、不在同一根 K 棒內重複平倉、且非預設停止下單時間,執行多單平倉
      if(多單部位() > 0 && BarNumber != CloseOrderNo && !商品預設停止下單時間)
         LX_CloseByTicket(多單進場單號,Lots) ;
      // 若持有空單、不在同一根 K 棒內重複平倉、且非預設停止下單時間,執行空單平倉
      if(空單部位() > 0 && BarNumber != CloseOrderNo && !商品預設停止下單時間)
         SX_CloseByTicket(空單進場單號,Lots) ;
      //     CloseOrderNo = BarNumber ;
     }
  }

//+------------------------------------------------------------------+
//| 自訂函數:核心監控 - 處理停損、停利、移動止損(交易時段外亦持續執行)  |
//+------------------------------------------------------------------+
void 交易時段外也可停損停利()
  {
   // --- 多單監控區 ---
   // 無論是否在交易時段,只要持有多單且非停止下單時間,就持續監控
   if(多單部位() > 0 && !商品預設停止下單時間)
     {
      //+------------------------------------------------------------------+
      //|    多單出場邏輯                                                    |
      //+------------------------------------------------------------------+
      double 多單最小停利 = 0.0, 多單保本價格 = 0.0 ;
      bool LX_MinPF = false, LongPull = false;

      // 取得多單進場價格並計算最小停利門檻(需覆蓋 3 倍點差成本)
      多單進場價格 = LE_EntryPrice(MagicNumber, 多單進場單號);
      多單最小停利 = NormalizeDouble(多單進場價格 + SP * 3, Digits());
      LX_MinPF = Bid > 多單最小停利; // 判斷是否已達最小利潤

      // 計算進場後出現的最高價(從進場 K 棒後開始計算,用於移動止損)
      多單最高價 = iHigh(Symbol(), 時間週期, iHighest(Symbol(), 時間週期, MODE_HIGH, LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期), 1));
      多單最高價 = NormalizeDouble(多單最高價, Digits());

      // 進場首根 K 棒時:呼叫 TPSLchange 在券商端設定初始停損停利掛單
      // 避免因網路中斷導致停損保護失效
      if(LE_BarsSinceEntry(MagicNumber, 多單進場單號, 時間週期) == 0)
        {
         double 初始停利 = NormalizeDouble(多單進場價格 + TP * Point(), Digits());
         double 初始停損 = NormalizeDouble(多單進場價格 - SL * Point(), Digits());

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

      // 從券商端讀取目前實際的停損停利價格(以反映後續可能的移動止損修改)
      if(PositionSelectByTicket(多單進場單號))
        {
         多單停利價格 = PositionGetDouble(POSITION_TP);
         多單停損價格 = PositionGetDouble(POSITION_SL);
        }

      //+------------------------------------------------------------------+
      //|    出場方式計算                                                    |
      //+------------------------------------------------------------------+
      // 出場方式 :CDP AH 動態停利 + 流星線型態確認
      // 條件說明:
      // (1) 以 CDP 的 AH 值動態更新停利,確保停利不低於進場價 TP*0.3 倍的最低門檻
      //     且只允許停利往有利方向調整(只上移,不下移)
      // (2) 達最小獲利 + 流星線出現 + 價格突破 NH(過熱訊號)→ 立即平倉
      // (3) OR:直接觸及券商端停損價(止損)
      // (4) OR:直接觸及券商端停利價(止盈)

         double 新停利 = NormalizeDouble(AH, Digits());
         double 最小停利 = NormalizeDouble(多單進場價格 + TP * 0.3 * Point(), Digits());
         // 停利只能往有利方向(上方)移動,且不低於最小停利門檻
         if(新停利 > 最小停利 && 新停利 > 多單停利價格)
           {
            多單停利價格 = 新停利;
            TPSLchange(Symbol(), 多單進場單號, 多單停利價格, 0, 時間週期); // 修改券商端停利,停損維持不變(傳 0)
           }
         LX_Cond = (Bid > 多單最小停利 && 流星線確認() && Bid > NH)
                   || (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 && !商品預設停止下單時間)
     {
      //+------------------------------------------------------------------+
      //|    空單出場邏輯                                                    |
      //+------------------------------------------------------------------+
      double 空單最小停利 = 0.0, 空單保本價格 = 0.0;
      bool SX_MinPF = false, ShortPull = false;

      // 取得空單進場價格並計算最小停利門檻(需覆蓋 3 倍點差成本)
      空單進場價格 = SE_EntryPrice(MagicNumber, 空單進場單號);
      空單最小停利 = NormalizeDouble(空單進場價格 - SP * 3, Digits());
      SX_MinPF = Ask < 空單最小停利; // 判斷是否已達最小利潤

      // 計算進場後出現的最低價(從進場 K 棒後開始計算,用於移動止損)
      空單最低價 = iLow(Symbol(), 時間週期, iLowest(Symbol(), 時間週期, MODE_LOW, SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期), 1));
      空單最低價 = NormalizeDouble(空單最低價, Digits());

      // 進場首根 K 棒時:呼叫 TPSLchange 在券商端設定初始停損停利掛單
      // 避免因網路中斷導致停損保護失效
      if(SE_BarsSinceEntry(MagicNumber, 空單進場單號, 時間週期) == 0)
        {
         double 初始停利 = NormalizeDouble(空單進場價格 - TP * Point(), Digits());
         double 初始停損 = NormalizeDouble(空單進場價格 + SL * Point(), Digits());

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

      // 從券商端讀取目前實際的停損停利價格(以反映後續可能的移動止損修改)
      if(PositionSelectByTicket(空單進場單號))
        {
         空單停利價格 = PositionGetDouble(POSITION_TP);
         空單停損價格 = PositionGetDouble(POSITION_SL);
        }

      //+------------------------------------------------------------------+
      //|    出場方式計算                                                    |
      //+------------------------------------------------------------------+
      // 出場方式 :創新低時比例式移動停損停利
      // 邏輯說明:
      // - 當上根 K 棒收盤價突破前兩根低點(創新低),且上根低點即為進場後最低價時,
      //   以當前 Ask 價格乘以比例係數重新計算停利(RatioTP)與停損(RatioSL)
      // - 停利與停損只允許往有利方向移動(只下移,不上移),鎖定既有獲利空間
      // - 更新後立即同步至券商端,確保即使網路中斷也有停損保護

         if(Close[1] < Low[2] && Low[1] == 空單最低價)
           {
            double 新停利 = NormalizeDouble(Ask * (1 - RatioTP), Digits());
            double 新停損 = NormalizeDouble(Ask * (1 + RatioSL), Digits());
            // 停利只能往有利方向(下方)移動
            if(新停利 < 空單停利價格)
               空單停利價格 = 新停利 ;
            // 停損只能往有利方向(下方)移動,保護空單獲利
            if(新停損 < 空單停損價格)
               空單停損價格 = 新停損 ;

            TPSLchange(Symbol(), 空單進場單號, 空單停利價格, 空單停損價格, 時間週期);
           }
         // 出場條件:Ask 觸及追蹤停損線(止損)或 Ask 觸及停利價(止盈)
         SX_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 交易時段外也可停損停利()


//+------------------------------------------------------------------+
//| 依編號取得多單買入價格                                               |
//+------------------------------------------------------------------+
double Get_BuyPrice()
  {
   double BuyPrice = 0.0;

   // 多單價格編號 :肯特納通道上軌 + 標準差擴張緩衝
   // 計算公式:KC 上軌(前 1 根)+ 前 2 根標準差的 0.5 倍
   // 適用於通道突破策略,在動態上軌之上加上波動緩衝設定掛單,過濾假突破

      BuyPrice = a_KC_UPPER[1] + a_StdDEV[2]*0.5;

   // 回傳經過正規化處理的價格(依商品點位精度)
   return NormalizeDouble(BuyPrice,Digits()) ;
  } // end of get BuyPrice

//+------------------------------------------------------------------+
//| 依編號取得空單賣出價格                                               |
//+------------------------------------------------------------------+
double Get_ShortPrice()
  {
   double ShortPrice = 0.0;

   // 空單價格編號 :突破進場 - POC 價格下方 ATR*1.618 處設掛單
   // 計算公式:成交量最大價格(POC)- ATR 的 1.618 倍(黃金比例緩衝)
   // 適用於 POC 下方跌破確認策略,確保價格已有效跌穿支撐後才成交

     { 計算POC(); ShortPrice = POC_價格 - a_ATR[1] * 1.618; }

   // 回傳經過正規化處理的價格(依商品點位精度)
   return NormalizeDouble(ShortPrice,Digits()) ;
  } // end of get ShortPrice

//+------------------------------------------------------------------+
//| 多單策略模組:定義買入信號                                           |
//+------------------------------------------------------------------+
void set_BuyCondition()
  {
   // 多單模組編號 :DeMarK 看漲背離 + 量增確認
   // 條件說明:
   // (1) DEMARK_LDiv_02():觸發 DeMarK 看漲背離訊號(TD 序列識別潛在底部反轉)
   // (2) Volume[1] > Volume[2]:上根 K 棒成交量大於前一根,確認買方動能增強
   // 適用於底部反轉策略:TD 序列底部配合量增,視為多頭啟動訊號
      LE_Cond = DEMARK_LDiv_02() && Volume[1] > Volume[2];
  }

//+------------------------------------------------------------------+
//| 空單策略模組:定義賣出信號                                           |
//+------------------------------------------------------------------+
void set_ShortCondition()
  {
   // 空單模組編號 :收盤跌破布林下軌 + 一目均衡表空頭趨勢確認
   // 條件說明:
   // (1) Close[1] < a_BBDN[1]:上根 K 棒收盤價跌破布林通道下軌(超賣突破,動能向下)
   // (2) ICHI_Trend() == -1:一目均衡表判斷當前趨勢為空頭方向
   // 適用於趨勢追蹤策略:布林下軌突破且一目均衡表確認空頭,雙重過濾提高勝率

      SE_Cond = Close[1] < a_BBDN[1] && ICHI_Trend() == -1;
  }
回測結果
測試參數交易商品:XAUUSD(黃金)
樣本內區間:2020/1/1 ~ 2024/09/30
交易手數:固定 0.1 手
時間框架: M20圖表
交易模式:當沖交易



沒有留言:

張貼留言