在MT4安裝后,默認會給用戶提供幾個例子程序,這些程序對于新學習EA開發過程中有很大的幫助,下面我們就來對MACD Sample這個例子EA來做個解讀,來看看別人是如何開發一個EA的。
首先是注釋,在MQL語言中所有//的這一行就是注釋行,系統本身不會去執行它,它只是用來解釋當前代碼的含義的,我們在代碼中加入這些注釋為了是讓我們能夠清楚的閱讀代碼的含義,在程序中加入詳細的注釋是一個很好的編程習慣,我們鼓勵大家多加入注釋。很多人覺得注釋這東西因為沒用在寫代碼中覺得很麻煩而忽略它,但是即使是自己編寫的程序如果不加注釋過幾天就會忘記意思,還要再一行一行地看這些代碼,所以注釋是非常重要的。
下面這些就是定義變量,我們發現前面加上了extern關鍵字,如果在定義變量的前面有這個詞說明這個變量將會被作為EA運行的參數,舉個例子,比如我要編寫一個EA,在使用過程中我想改變它的止盈和止損值,如果我程序都是事先編好的止盈止損,那么以后要改動它必須要改代碼,這樣不僅麻煩也不靈活,如果我把這些信息作為EA運行參數,那么在EA運行中就可以隨時調整它了。
extern double TakeProfit = 50;
extern double Lots = 0.1;
extern double TrailingStop = 30;
extern double MACDOpenLevel=3;
extern double MACDCloseLevel=2;
extern double MATrendPeriod=26;
上面定義了6個變量并且都是當作參數,這里設定變量名稱的時候盡量使用用戶可以理解的詞匯,在EA的參數設置里這些變量名就是參數的名字。值得注意的是EA里變量名稱是可以用中文的。
int start()
start()函數是EA運行的核心,MQL語言規定了幾個默認函數,其中EA第一次運行時會調用init()函數,在這個函數里我們可以放入一些需要初始化的信息,start()函數中放我們EA的核心代碼,每次一個TICK(換句話說就是新報價)來到后,系統會自動調用start()函數。deinit()函數是當EA關閉的時候調用的,這里放一些我們程序停止后需要“善后”的代碼。start()函數是必須要有的,其它兩個函數如果不需要可以不用寫。
if(Bars
{
? ? ? ? Print("bars less than 100");
? ? ? ? return(0); ?
}
上面代碼的意思是如果當前圖表中的k線少于100根將會在日志信息里輸出提示信息并且結束start()函數的執行。return的意思是返回,如果在程序中判斷出有錯誤,下面的代碼無法繼續執行了,我們調用return()函數讓他退出start()函數的執行。
if(TakeProfit
{
? ? ? ? Print("TakeProfit less than 10");
? ? ? ? return(0); ?// check TakeProfit
}
上面的代碼意思是如果參數里的TakeProfit變量小于10也提示一條信息并結束執行,TakeProfit從字面的意思中我們可以知道是止盈的意思,有些平臺會限制下單時的止盈點數不得小于某個點,如果小于某值會在下單時報錯,為了避免這種錯誤我們會限制參數中止盈的設定。
其實這里可以調用MarketInfo()函數得到我們當前平臺中允許的止盈止損最小值從而根據平臺的不同自動計算出最小的止盈點數,詳細情況請參閱文檔MarketInfo()函數的描述。
MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0);
MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1);
SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);
SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);
MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0);
MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
以上語句是調用了MQL的一些內置指標函數,在MQL語言中,對于常用的指標如均線,MACD,KD等MQL已經提供給我們現成的函數,我們只要調用他即可得到指標的值(相見文檔技術指標部分:http://docs.mql4.com/cn/indicators)。
就上面的代碼,MacdCurrent的值是參數為12,26,9的MACD主線當前K線的值,MacdPrevious則是MacdCurrent前一根K線的值,SignalCurrent和SignalPrevious則是相同參數信號線的當前值和前一根值。
后兩個是調用均線指標函數,這里的均線周期參數則是使用了EA的參數變量MATrendPeriod,這樣寫是個好習慣,把調用指標的參數放到EA參數里,這樣可以隨時在運行中調整這些參數方便我們改變策略。MaCurrent和MaPrevious是得到26均線的當前K線值和前一根的值。
total=OrdersTotal();
if(total
上面的代碼就是判斷我們當前是否有單子在做,他調用了OrdersTotal()函數,它可以計算當前賬戶中一共還沒有平倉的單子和掛單的個數,如果它小于1說明當前沒有任何單子,這種判斷方法只是一種簡單的判斷法,如果這個EA在運行過程中人為也去下單則EA永遠不會下單了,如果要更加精確的計算這個EA所下的單子數量還需要利用其他方法,這些技巧我們會在以后的文章中介紹。
if(AccountFreeMargin()
{
? ? ? ? Print("We have no money. Free Margin = ", AccountFreeMargin());
? ? ? ? return(0); ?
} ? ?
上面的代碼是計算當前的剩余保證金是否小于1000,如果太少錢會不夠用,所以會輸出下當前的保證金還剩多少并退出。
if(MacdCurrentSignalCurrent && MacdPrevious(MACDOpenLevel*Point) && MaCurrent>MaPrevious)
{
? ? ? ? ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point,"macd sample",16384,0,Green);
? ? ? ? if(ticket>0)
? ? ? ? {
? ? ? if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice());
? ? ? ? }
? ? ? ? else Print("Error opening BUY order : ",GetLastError());?
? ? ? ? return(0);?
}
上面這段就是多單開倉部分了,條件是這樣:如果當前MACD主線在0軸以下,MACD“金叉”,MACD的主線不在0軸附近(這塊是EA的參數來指定0軸附近多少點)并且還要當前的均線是上升的。
這里最精彩的部分在于如何判斷MACD“金叉”,如何判斷MACD值不在0軸附近和均線目前是上升的還是下降的。
“金叉”的判斷是EA里用的比較多的,這里我們用了判斷大小的方法就能很容易的計算它,首先得到MACD兩根線當前的值和上一根K線的MACD值,如果上一根K線的MACD主線大于信號線并且當前的MACD主線小于信號線那么就相當于這兩根線做了一個“交叉”,因此我們可以認為MACD“金叉”了。從這里我們也能看出來用計算機的方法來解決我們人類所認知的問題靠的都是這種具體數值的計算,所以計算機還是比較“死板”的,如果兩根線“扭”在了一起那么用計算機程序很難判斷出來,這些就是目前計算機程序的缺點。

0軸附近這種判斷方法這里利用了一點數學方面的知識,不過不用擔心都是很簡單的算法。把MACD值做絕對值運算然后判斷是否大于指定的值,因為MACD會是負值做絕對值運算后直接判斷是否大于設定的值就行了,這塊相當于是簡化了判斷語句的條件。
均線的上升和下降判斷和“金叉”的算法差不多,得到當前均線值和前一根線的均線值,如果前一根均線值小于當前值那么就說明均線是上升的。
if(MacdCurrent>0&&MacdCurrentSignalPrevious&& MacdCurrent>(MACDOpenLevel*Point) && MaCurrent
{
? ? ? ? ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,Bid-TakeProfit*Point,"macd sample",16384,0,Red);
? ? ? ? if(ticket>0)
? ? ? ? {
? ? ? ??if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("SELL order opened : ",OrderOpenPrice());
? ? ? ? }
? ? ? ? else
? ? ? ? ? ? ? ? Print("Error opening SELL order : ",GetLastError());?
? ? ? ? return(0);
這段代碼就是空單的進場條件,和上面的正相反。值得說明是這兩個下單代碼中會遇到下單失敗的情況,因為當用OrderSend()函數下單后會返回一個大于0的整數訂單號數值,利用這一點就可以很容易的知道下單是否成功了。
下面的代碼是平倉和移動止損部分,這段代碼比較難懂,但是卻是非常重要的部分,因為在編寫EA中這些操作會經常遇到,讓我們來一點一點的拆解開來理解下它們的含義。
for(cnt=0; cnt
當前存在的訂單中我們要判斷是否到達平倉的條件,所以第一步我們首先要對所有在下的單子進行一次遍歷,一個一個的去判斷它們是否達到平倉條件。
此代碼中利用了一個循環語句從第一單開始一單一單的循環,這里值得注意的是所有單子都是按照下單的先后順序存放的,第一張單子的編號是0而不是1,這是編程語言中一般都采取的方法,我們在編寫程序的時候一定要注意它的值要從0開始。
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
上面是選擇訂單操作,當循環一次訂單后,必須調用OrderSelect()函數來鎖定這一訂單,這樣下面的操作才可以正常運行。這里最容易出錯的地方是函數的第二個參數如果查一下文檔我們會發現它有兩個選項:SELECT_BY_POS和SELECT_BY_TICKET。第一種方式是根據訂單的位置進行選定操作,這個例子中就是使用了這種方式,第二種方式是根據訂單號來進行選定操作,因為我們并不知道所有單子的訂單號是多少所以我們只能使用第一種方式來選擇訂單,剛才說過訂單是按照下單的先后順序來存放的,因此如果是第一個單子那么就是0,如果是第二個單子就是1,最后一個單子是總單子數減一。
if(OrderType()
? ? ? ? OrderSymbol()==Symbol()) ?// check for symbol
上面的代碼段又運用了一個小技巧,它首先調用了OrderType()函數來得到所選定的訂單是多單還是空單,但是我們查下這個函數的定義(http://docs.mql4.com/cn/constants/trading)發現多單的值是0,空單的值是1,那么如果OrderType()函數小于等于空單的值那么相當于在判斷當前訂單是否是非掛單。
第二個條件是判斷當前單子的貨幣對是否和當前圖表相同,這個判斷是為了防止我們處理訂單過程中誤操作了其他不是EA所下的單子。
if(OrderType()==OP_BUY) ? // long position is opened
{
? ? ? ? // should it be closed?
? ? ? ? if(MacdCurrent>0 && MacdCurrentSignalPrevious && MacdCurrent>(MACDCloseLevel*Point))
? ? ? ? {
? ? ? ? ? ? ? ? OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
? ? ? ? ? ? ? ? return(0); // exit
? ? ? ? }
多單的平倉部分代碼,這里其實就是去掉均線條件的空單下單信號,平倉操作中一定要注意在平倉完成后必須終止這個遍歷訂單的循環,因為平倉后會打亂所有單子的順序,造成誤操作其他訂單。
我們在這里舉個例子就能明白為什么要這么做:比如當前有三個單子沒有平倉,按照順序排列序號是0、1、2,如果第二個單子平倉后第三個單子序號就會提前,這樣當下一輪循環執行到OrderSelect()函數后會因為沒有這個編號而出現錯誤。
if(TrailingStop>0) ?
{ ? ? ? ? ? ? ? ??
? ? ? ? if(Bid-OrderOpenPrice()>Point*TrailingStop)
? ? ? ? {
? ? ? ? ? ? ? ? if(OrderStopLoss()
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
? ? ? ? ? ? ? ? ? ? ?return(0);
? ? ? ? ? ? ? ? }
? ? ? ? }
} ? ??
這段代碼就是多單的移動止損部分,當參數TrailingStop大于0的時候EA就開啟了移動止損功能(默認設定是30,也就是說默認情況下是開啟移動止損的),我們就用我們這個例子EA的默認參數30點來說明,當單子的盈利大于30點并且單子的止損點和當前價位相差30點以上時,修改訂單的止損到當前價格以下30點位置。
我們在上面的程序里屢次發現作者使用Point變量來計算點位,這個變量是MT4運行環境中自動設定的值,它在MQL語言中叫做預定義變量(關于預定義變量可以參考這里:http://docs.mql4.com/cn/predefined/variables),Point告訴我們當前貨幣對的價格最小點值是多少,舉個例子:歐元對美元的價格總是X.XXXX這種形式,它的Point值就是0.0001,當我們想設定當價格大于30點這種情況時,我們只要用30乘以Point就可以計算這個貨幣對的實際30點值。不過Point常量在很多平臺中不能正確的來實現它本身的功能了,原因是很多平臺已經改為小數點后5位,這樣Point值變成了0.00001,我們直接用他來乘以點位得到是卻是實際點位的十分之一,這樣會在EA的運行中出現致命的邏輯錯誤。因此如果是5位的平臺,需要在那些點位的值上乘以10來修正這個問題。關于Point的完美解決方法我們將在后續的文章中繼續討論。
以上就是MT4例子EA的解讀,這個程序雖然比較復雜但是它卻是一個很好的例子,里面涉及到了我們在寫EA程序過程中常用到的一些功能,對于初學EA程序的人來說幫助很大,我們也可以修改這個程序的開倉、平倉部分直接變成我們自己的邏輯。
來源:外匯邦
評論前必須登錄!
立即登錄 注冊