開始寫一支完整的交易策略-MT4 EA語法教學(四)

前幾篇文章中已經有提到 EA 基礎語法的使用,本篇文章用以下的程式直接來說明一支完整交易策略該具備的要素,包含策略邏輯、進出場停利停損設置訂單管理等功能。

如果要免費使用 MT4 或進行模擬交易,可以使用 OANDA 安達,進入外匯市場的首選經紀商完整介紹!


追蹤量化通的粉絲專頁量化通QuantPass」即時獲取實用的資源!

一、策略邏輯

(一)限制:

  1. 場上僅能有一張單
  2. 僅在 K棒開盤時第一個 tick 做進出場判斷

(二) 進場條件:前一根 K棒往下收過當前 MA 即作空、往上收過作多
(三) 出場條件:

  1. 設置固定停利停損
  2. 多單:前一根 K棒往下收過當前 MA 即平倉
  3. 空單:前一根 K棒往上收過當前 MA 即平倉

Mt4 101209161710
Mt4 101209161711

二、完整程式碼與講解

(一)定義該策略的魔術碼:

這裡使用 #define 來定義 MAGICMA 這個常數,因為魔術碼不會變動。

#define MAGICMA  20131111

(二)策略參數定義

這幾行定義了基礎手數(Lots)、最大風險(MaximumRisk)、減少因子(DecreaseFactor)、MA參數(MovingPeriod、MovingShift)、停利停損點數(TP、SL) 。

input double Lots           =0.1;
input double MaximumRisk    =0.02;
input double DecreaseFactor =3;
input int    MovingPeriod   =12;
input int    MovingShift    =6;
input int    TP             =300;
input int    SL             =100;

(三)自定義函式:

計算目前持倉單量與方向,若有1張多單回傳”1″,有2張空單回傳”-2″ 。

int CalculateCurrentOrders() {
    int buys=0,sells=0; // 初始化多空單量

    // 走訪所有持倉訂單
    for(int i=0;i<OrdersTotal();i++) {
         // 使用OrderSelect選取訂單,若選取失敗則使用break語法退出訂單走訪
         if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
         // 判斷選取的訂單貨幣對與魔術碼是否跟該EA一致
    if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA) {
             // 選取的訂單為多單則將buys加一
             if(OrderType()==OP_BUY)  buys++;
            // 選取的訂單為空單則將sells加一
             if(OrderType()==OP_SELL) sells++;
         }
    }

    // 判斷持倉多空方向並回傳
    if(buys>0) return(buys);    
else return(-sells);
}

(四)自定義函式:計算最佳下單手數

double LotsOptimized() {
    double lot=Lots;
    int orders=HistoryTotal();     // 取得歷史訂單數
    int losses=0;                        // 初始化虧損訂單數

    // 用最大風險(MaximumRisk)參數取得下單手數
    lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);

    // 當減少因子大於0時計算虧損訂單數,用虧損訂單數計算出下單手數
    if(DecreaseFactor>0) {
        // 走訪歷史訂單
        for(int i=orders-1;i>=0;i–) {
            if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) {
                Print(“Error in history!”);
                break;
            }
            // 若選取的訂單不是當前貨幣兌或為掛單則使用continue語法跳過計算
            if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)
                continue;

        // 當該訂單有盈利時跳出迴圈
             if(OrderProfit()>0) break;
    // 當該訂單虧損時將losses加一
            if(OrderProfit()<0) losses++;
     }

       // 有虧損訂單則重新計算下單手數
       if(losses>1) lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);    
 }

    // 回傳下單手數,最小值為0.1
    if(lot<0.1) lot=0.1;
    return(lot);
}

(五)自定義函式:進場判斷

void CheckForOpen() {
     double ma;
     int res;
      double sl;
     double tp; 

    // 當前K棒成交量大於1時代表不是開盤第一個tick所以直接退出函式
    if(Volume[0]>1) return;

    // 取得最新K棒MA數值
    ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);

     // 前一根K棒往下收過當前MA
      if(Open[1]>ma && Close[1]<ma) { 
        // 下單
        res=OrderSend(
            Symbol(), // 當前EA掛載的商品
            OP_SELL, // 市價空單
            LotsOptimized(), // 透過LotsOptimized函式取得下單手數
            Bid, // 買價
            3, // 滑點
            0, // 停損,這裡先不設停損,確定成交後再修改訂單設置停損
            0, // 停利,這裡先不設停利,確定成交後再修改訂單設置停利
            “”, // 備註
            MAGICMA, // 魔術碼
            0, 
            Red
       );

        // 下單回傳值不為-1則表示下單成功,且該值為訂單編號
        if(res != -1) {
            // 使用訂單編號選取訂單
     OrderSelect(res, SELECT_BY_TICKET);
            // 用進場價計算停利停損
         tp = NormalizeDouble(OrderOpenPrice()-TP*Point, Digits);
         sl = NormalizeDouble(OrderOpenPrice()+SL*Point, Digits);

           // 修改訂單設置停利停損
           OrderModify(res, OrderOpenPrice(), sl, tp, 0);
       }
        return;
    }

    // 前一根K棒往上收過當前MA
    if(Open[1]<ma && Close[1]>ma) {
        res=OrderSend(Symbol(),
            OP_BUY, // 市價多單
            LotsOptimized(),
            Ask, // 賣價
            3,0,0,””,MAGICMA,0,Blue);
        if(res != -1) {
     OrderSelect(res, SELECT_BY_TICKET);
     tp = NormalizeDouble(OrderOpenPrice()+TP*Point, Digits);
     sl = NormalizeDouble(OrderOpenPrice()-SL*Point, Digits);
     OrderModify(res, OrderOpenPrice(), sl, tp, 0);
        }
        return;
    }
}

(六)自定義函式:出場判斷

void CheckForClose() {
    double ma;

    // 當前K棒成交量大於1時代表不是開盤第一個tick所以直接退出函式
    if(Volume[0]>1) return;

    // 取得最新K棒MA數值 
    ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);

    // 走訪持倉訂單
    for(int i=0;i<OrdersTotal();i++) {
        // 選取訂單
        if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
        // 該訂單並非該EA下的或不是EA掛載的商品則跳過
        if(OrderMagicNumber()!=MAGICMA || OrderSymbol()!=Symbol()) continue;

        // 若該訂單為多單
        if(OrderType()==OP_BUY) {
            // 多單出場條件前一根K棒往下收過當前MA
            if(Open[1]>ma && Close[1]<ma) {
                // 平倉
                if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,White))
                Print(“OrderClose error “,GetLastError());
            }
            break;
        }
        // 若該訂單為空單
        if(OrderType()==OP_SELL) {
            // 空單出場條件前一根K棒往上收過當前MA
            if(Open[1]<ma && Close[1]>ma) {
                if(!OrderClose(OrderTicket(),OrderLots(),Ask,3,White))
                    Print(“OrderClose error “,GetLastError());
            }
            break;
        }
    }

(七)EA運行主函式:

}
//+— — — — — — — — — — — — — — +
// | OnTick function            |
//+— — — — — — — — — — — — — — +
void OnTick() {
    // 若圖表K棒數量小於100或不允許EA自動交易則退出
    if(Bars<100 || IsTradeAllowed()==false)
        return;

    // 透過呼叫自定義函式CalculateCurrentOrders計算目前持倉單量與方向,為0表示無持倉可以進入下單判斷
    if(CalculateCurrentOrders()==0) CheckForOpen();
    // 有單則進入出場判斷
    else CheckForClose();
}
//+— — — — — — — — — — — — — — +

三、總結

以上是一個包含各種基礎語法運用的 EA 範例,這其實就是 MT4 內建的「Moving Average」EA,我們在這只是加上了獨立的停利與停損,模仿是學習最好的途徑,去理解他人的做法並從中擷取有用的技巧,祝各位能成功寫出自己覺得滿意的 EA。


量化通粉絲社群,一起討論程式交易!

加入LINE社群量化交易討論群」無壓力討論與分享!

加入臉書社團「程式交易 Taiwan」即時獲取實用的資源!

Marco
Marco

8年經驗法人級EA開發者,已開發過至少百隻EA、腳本、外部套件等相關MT4/MT5程式,期間曾開發過跨交易所/經紀商對沖避險EA、動態權重馬丁策略、多商品網格策略、也專門開發設計各式網站、金流電商等,將程式與交易視為志業,持續精進自己的人。

文章: 5

1 則留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *