Shopping Cart

購物車內沒有任何商品。

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

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

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

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

    稱呼

    電子郵件

    以下非必填,但若您願意分享,我們將能推送更精準的內容給您

    投資經驗

    是否為理工科背景、工程師或有寫程式的經驗?

    有興趣的主題
    量化交易台股期貨海外期貨虛擬貨幣美股

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

    想透過量化交易達成甚麼目的?
    不確定自動交易選股回測投資績效量化自己的投資方法想找現成的策略套用

    還有什麼想詢問的?

    一、策略邏輯

    (一)限制:

    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社群量化交易討論群」無壓力討論與分享!

    加入Discord 「量化交易討論群」即時獲取實用的資源!

    Write Together 101306261122
    Write Together 101306261121
    Marco
    Marco

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

    文章: 5

    3 則留言

    發佈留言

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