2025年4月8日 星期二

MT5 外匯商品 交易策略開發 [01]

市佔率穩居首位的外匯交易平台,是由俄羅斯Metaquotes 軟體公司所開發的MetaTrader 4 (MT4)平台,並於2010年推出了MetaTrader 5(MT5)平台,MQL5(MetaQuotes Language 5)是一種強大的編程語言,專為撰寫交易策略、技術指標、腳本和功能庫等自動化交易系統而設計。MQL5 提供了大量預先定義的功能和服務,幫助開發者快速構建複雜的交易機器人(EA)、自定義指標和腳本。讓交易策略的自動化成為可能。無論是專業的交易者還是有志於自動化交易系統開發的程序員,MQL5 都提供了必要的工具和資源,以支持他們的工作。

以往部落格文章主要內容在於應用 MultiCharts 平台開發交易策略,2025開始會應用MT5平台 開發一些外匯商品的交易策略,也期盼在量化交易的推廣上有所助益

為了簡化篇幅 變數的定義 不會放在文章內,並儘可能用 MultiCharts 語法的形式來撰寫程式碼,內容中自定義的函數,大部分會放在個人的函數庫內,主要是策略邏輯的分享,未來也會將網路上或 YT頻道看到的交易邏輯 轉為MT5 的交易策略在部落格發佈

PS: 策略寫法是單向持單 (會先平掉反向單 再進場) 

#include <Trade\Trade.mqh>
#include <MagicMT5_函數庫V1.mqh>

ENUM_TIMEFRAMES  時間週期 = PERIOD_M20;

//+------------------------------------------------------------------+
//|                                                                
//+------------------------------------------------------------------+
int OnInit()
  {
   LoadEA = TimeCurrent();
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//|                                                                 
//+------------------------------------------------------------------+
void OnTick()
  {
   if(AccountInfoDouble(ACCOUNT_BALANCE) <= 資金風控)
     {
      Alert("**********  資金不足 *************");
      return;
     }

   BarNumber = iBarShift(Symbol(),時間週期,LoadEA);   //取得回測區間的 K棒數
   BarSinceExit = BarNumber-CloseOrderNo ;   // 用來取得距離前一筆交易出場後的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);
     }

   if(BarNumber != JudgeNo)
     {
      換K棒();
      允許交易時段 = (getTM_hour(TimeCurrent()) >= 10 || getTM_hour(TimeCurrent()) < 2);
     }

   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(Lots_AutoCal == true)
     {
      Lots = get_dynamic_lot_size(Lots_Even,Symbol(),RiskPercent,AccountBalance,SL) ;
      Lots = MathMin(0.3,MathMax(0.01,Lots)) ;
     }

   if(允許交易時段 == true)
     {
      //+------------------------------------------------------------------+
      //|多單進場
      //+------------------------------------------------------------------+
      LE_Cond = (_3GreatThanBBUp()) ;
      if(LE_Cond == true)
        {
         if(空單部位() > 0 && BarNumber != OrderBarNo)
            sell_order_close_all(Symbol(), MagicNumber, ORDER_TYPE_SELL);  // 空單先全部平倉 
            
         if(多單部位() == 0 && BarNumber != OrderBarNo)
           {
            買入價格 = NormalizeDouble(Highest_OHLC(Symbol(),PERIOD_D1,MODE_OPEN,HBar) + Range[1]*0.5,Digits()) ;
            // 空手+市價單
            if(BarSinceExit > 1)
              {
               多單進場單號 = Buy_at_MARKET(Symbol(),Lots,TP,SL,"BUY MARKET",MagicNumber) ;
               OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA);
              }
           }
        }
      //+------------------------------------------------------------------+
      //|空單進場
      //+------------------------------------------------------------------+
      SE_Cond = (DayBlack3Bar_LClose() && a_RSIA[1] > 50) ;
      if(SE_Cond == true)
        {
         if(多單部位() > 0 && BarNumber != OrderBarNo)
            buy_order_close_all(Symbol(), MagicNumber, ORDER_TYPE_BUY);
            
         if(空單部位() == 0 && BarNumber != OrderBarNo)
           {
            賣出價格 = NormalizeDouble(Lowest_OHLC(Symbol(),PERIOD_D1,MODE_LOW,LBar) - Range[1]*0.5,Digits());
            // 空手+掛單賣出
            if(BarSinceExit > 1)
              {
               if(Close[1] > 賣出價格)
                 {
                  空單進場單號 = Short_at_STOP(Symbol(),MagicNumber,賣出價格,Lots,TP,SL,"Short STOP",3600) ;
                  OrderBarNo = iBarShift(Symbol(),時間週期,LoadEA);

                 }
              }
           }
        }
     }
//+------------------------------------------------------------------+
//|   多單平倉                                                             
//+------------------------------------------------------------------+
   if(多單部位() > 0)
     {
      多單進場價格 = LE_EntryPrice(MagicNumber,多單進場單號);
      多單停利價格 = 多單進場價格 + TP * Point() ;
      多單停損價格 = 多單進場價格 - SL * Point() ;
      LX_Cond = ((Close[1] <= 多單停利價格 && Bid > 多單停利價格) || (Close[1] >= 多單停損價格 && Bid < 多單停損價格)) ;

      //---------------------------------------------------------多單出場

      if(LX_Cond == true && BarNumber != CloseOrderNo)
        {
         LX_CloseByTicket(多單進場單號,Lots) ;

         if(多單部位() == 0)
           {
            CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ;
           }
        }
     } //  end of  多單部位() > 0
//+------------------------------------------------------------------+
//|   空單平倉
//+------------------------------------------------------------------+
   if(空單部位() > 0)
     {
      空單進場價格 = SE_EntryPrice(MagicNumber,空單進場單號);
      bool SXcond51,SXcond52 ;
      空單停利價格 = MathMin(a_ICHI_SA[1], a_ICHI_SB[1]) - Range[1] * 2;
      空單停損價格 = MathMax(a_ICHI_SA[1], a_ICHI_SB[1]) + Range[1] * 2;
      SXcond51 = (空單進場價格 > 空單停利價格 && Close[1] >= 空單停利價格 && Ask < 空單停利價格) ;
      SXcond52 = (空單進場價格 < 空單停損價格 && Close[1] <= 空單停損價格 && Ask > 空單停損價格) ;
      SX_Cond = (SXcond51 == true || SXcond52 == true) ;
      //---------------------------------------------------------空單出場

      if(SX_Cond == true && BarNumber != CloseOrderNo)
        {
         SX_CloseByTicket(空單進場單號,Lots) ;

         if(空單部位() == 0)
           {
            CloseOrderNo = iBarShift(Symbol(),時間週期,LoadEA) ;
           }
        }
     } // end of 空單部位() > 0
   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棒()
  {
// 新K棒刪除所有掛單
   if(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(10) ;
   Get_OHLC_Day(10) ;

   set_BarInfo();
   set_BBAND();
   set_RSI();
   set_ICHIMOKU();

  }


//+--------------------------------------------------------+
//| 設定 開高低收 陣列序列
//+--------------------------------------------------------+
//********BarPrice Array variables
double Open[], High[], Low[], Close[],Range[],Body[],UPshadow[],DNshadow[] ;
double OpenD[], HighD[], LowD[], CloseD[] ;

//+------------------------------------------------------------------+
//|  陣列序列設定                                                                  |
//+------------------------------------------------------------------+
void Set_OHLC_Bar_Series()
  {
   ArraySetAsSeries(Open,true);
   ArraySetAsSeries(High,true);
   ArraySetAsSeries(Low,true);
   ArraySetAsSeries(Close,true);
  }
//+------------------------------------------------------------------+
//|  K棒 開高低收                                                                |
//+------------------------------------------------------------------+
void Get_OHLC_Bar(int argCount)
  {
   get_OpenData(Symbol(),時間週期,argCount,Open) ;
   get_HighData(Symbol(),時間週期,argCount,High) ;
   get_LowData(Symbol(),時間週期,argCount,Low) ;
   get_CloseData(Symbol(),時間週期,argCount,Close) ;
  }
//+------------------------------------------------------------------+
//|   陣列序列設定                                                               |
//+------------------------------------------------------------------+
void Set_OHLC_Day_Series()
  {
   ArraySetAsSeries(OpenD,true);
   ArraySetAsSeries(HighD,true);
   ArraySetAsSeries(LowD,true);
   ArraySetAsSeries(CloseD,true);
  }
//+------------------------------------------------------------------+
//|  取得日K 開高低收                                                                |
//+------------------------------------------------------------------+
void Get_OHLC_Day(int argCount)
  {
   get_OpenData(Symbol(),PERIOD_D1,argCount,OpenD) ;
   get_HighData(Symbol(),PERIOD_D1,argCount,HighD) ;
   get_LowData(Symbol(),PERIOD_D1,argCount,LowD) ;
   get_CloseData(Symbol(),PERIOD_D1,argCount,CloseD) ;
  }

//+------------------------------------------------------------------+
//|  計算K棒 振幅 實體 上影線 下影線                                                             
//+------------------------------------------------------------------+
void set_BarInfo()
  {
   ArrayResize(Range,ArraySize(Open)) ;
   ArraySetAsSeries(Range,true) ;
   ArrayResize(Body,ArraySize(Open)) ;
   ArraySetAsSeries(Body,true) ;
   ArrayResize(UPshadow,ArraySize(Open)) ;
   ArraySetAsSeries(UPshadow,true) ;
   ArrayResize(DNshadow,ArraySize(Open)) ;
   ArraySetAsSeries(DNshadow,true) ;

   for(int i = 0; i < ArraySize(Open)-1; i=i+1)
     {
      Range[i] = High[i]-Low[i];
      Body[i] = MathAbs(Close[i]-Open[i]);
      UPshadow[i] = High[i]-MathMax(Close[i],Open[i]);
      DNshadow[i] = MathMin(Close[i],Open[i])-Low[i];
     }
  }

//+------------------------------------------------------------------+
//|  計算布林通道
//+------------------------------------------------------------------+
bool UpBreak,DnBreak,Shrink ;
double a_BBUP[],a_BBDN[] ;
void set_BBAND()
  {
   int BBLen,h_BBUP,h_BBDN ;
   bool ShrinkUp,ShrinkDn ;

   BBLen = MathMax(LenA2,LenB2) ;
   h_BBUP = iBands(Symbol(),時間週期,BBLen,0,2,PRICE_CLOSE);
   ArraySetAsSeries(a_BBUP, true);
   h_BBDN = iBands(Symbol(),時間週期,BBLen,0,2,PRICE_CLOSE);
   ArraySetAsSeries(a_BBDN, true);
   get_IndexData(h_BBUP,1,0,10,a_BBUP) ;
   get_IndexData(h_BBDN,2,0,10,a_BBDN) ;

   UpBreak = (High[1] > a_BBUP[1]) && (High[2] > a_BBUP[2]) ;
   DnBreak = Low[1]< a_BBDN[1] && Low[2] < a_BBDN[2] ;
   ShrinkUp = a_BBUP[1] > a_BBUP[2] && a_BBUP[2] > a_BBUP[3] && a_BBUP[5] > a_BBUP[4] && a_BBUP[4] > a_BBUP[3] ;
   ShrinkDn = a_BBDN[1] < a_BBDN[2] && a_BBDN[2] < a_BBDN[3] && a_BBDN[5] < a_BBDN[4] && a_BBDN[4] < a_BBDN[3] ;
   Shrink = ShrinkUp || ShrinkDn ;

  }

//+------------------------------------------------------------------+
//|   計算 RSI 值                                                          
//+------------------------------------------------------------------+
double a_RSIA[],a_RSIB[] ;
void set_RSI()
  {
   int h_RSIA,h_RSIB ;
   h_RSIA = iRSI(Symbol(),時間週期,LenA1,PRICE_CLOSE) ;
   ArraySetAsSeries(a_RSIA,true) ;
   get_IndexData(h_RSIA,0,0,5,a_RSIA) ;

   h_RSIB = iRSI(Symbol(),時間週期,LenB1,PRICE_CLOSE) ;
   ArraySetAsSeries(a_RSIB,true) ;
   get_IndexData(h_RSIB,0,0,5,a_RSIB) ;

  }

//+------------------------------------------------------------------+
//|   計算一目均衡表區間價格                                                      
//+------------------------------------------------------------------+
double a_ICHI_B[],a_ICHI_C[],a_ICHI_SA[],a_ICHI_SB[],a_ICHI_LC[] ;
void set_ICHIMOKU()
  {
   int h_ICHI_C,h_ICHI_B,h_ICHI_SA,h_ICHI_SB,h_ICHI_LC ;
   h_ICHI_C = iIchimoku(Symbol(),時間週期,9,26,52);
   ArraySetAsSeries(a_ICHI_C, true);
   h_ICHI_B = iIchimoku(Symbol(),時間週期,9,26,52);
   ArraySetAsSeries(a_ICHI_B, true);
   h_ICHI_SA = iIchimoku(Symbol(),時間週期,9,26,52);
   ArraySetAsSeries(a_ICHI_SA, true);
   h_ICHI_SB = iIchimoku(Symbol(),時間週期,9,26,52);
   ArraySetAsSeries(a_ICHI_SB, true);
   h_ICHI_LC = iIchimoku(Symbol(),時間週期,9,26,52);
   ArraySetAsSeries(a_ICHI_LC, true);

   get_IndexData(h_ICHI_C,0,0,5,a_ICHI_C) ;
   get_IndexData(h_ICHI_B,0,0,5,a_ICHI_B) ;
   get_IndexData(h_ICHI_SA,0,0,5,a_ICHI_SA) ;
   get_IndexData(h_ICHI_SB,0,0,5,a_ICHI_SB) ;
   get_IndexData(h_ICHI_LC,0,0,5,a_ICHI_LC) ;
  }

//+------------------------------------------------------------------+
//|  日K連三黑 且 收盤近三根低                                                 
//+------------------------------------------------------------------+
bool DayBlack3Bar_LClose()
  {
   int  j = 0;
   for(int i = 1; i <= 3; i=i+1)
     {
      if(CloseD[i] < OpenD[i])
        {
         j = j + 1;

        }
     }
   if((j == 3 && Close[1] <= MathMin(Close[2], Close[3])))
     {
      return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//|  連三根收盤價大於 布林通道上界                                                          
//+------------------------------------------------------------------+
bool _3GreatThanBBUp()
  {
   int  j = 0;
   for(int i = 1; i <= 3; i=i+1)
     {
      if(Close[i] > a_BBUP[i])
        {
         j = j + 1;

        }
     }
   if(j == 3)
     {
      return true;
     }
   return false;
  }

交易商品 XAUUSDXXX 樣本內區間 2019/1/1 ~2022/12/31 交易手數 固定 0.1手







沒有留言:

張貼留言