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

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

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

立即訂閱電子報,掌握最新資訊!

    電子郵件

    有興趣的主題
    量化交易金融知識台灣股市國內期貨海外期貨虛擬貨幣

    有興趣的量化交易軟體/平台
    不清楚MultiChartsTradingViewPythonXQMT4MT5

    還有什麼詢問的?

    一、策略邏輯

    (一)限制:

    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 則留言

    發佈留言

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