第十五節模塊編寫

上節課我們介紹了子函數,通過子函數我們可以將一些功能封裝起來,直接調用,能實現特定功能的子函數我們稱其為模塊。

一款EA往往都是由許多個功能模塊組合起來完成交易功能的,這些模塊中最基本的模塊有K線信號控制、開倉、平倉、掛單模塊,這些模塊的基礎之上,有移動止損、離場、移動掛單、倉位計算模塊等等,基礎的模塊每個EA用的都差不多,基礎之上的模塊根據策略的不同,模塊的功能也不盡相同。這節課我們就先把K線信號控制、開倉、平倉、掛單模塊實現封裝,以便後面的EA編程直接調用。

1 K線信號控制

這個模塊控製程序只在K線開盤的時候運行一次,這使得策略的回測相對來說更加準確和可靠,要實現則個功能很簡單,只要判斷K線的根數有沒有增加就行了,如果K線增加了一根那說明新的K線開盤了。模塊代碼如下:

 int bar=0; int Barjudge() { if(Bars!=bar) { bar=Bars; return(1); } else return(0); }

如果這個函數返回了1,那麼說明有新的K線開盤,採用這個模塊的EA就可以通過模塊的返回值來判斷當下是否運行。注意以上的bar這個變量是個全局變量,需要在主函數前聲明。

2開倉模塊

要實現開倉當然非常簡單,只要一個開倉函數就可以了,但是實際交易的時候可沒有這麼容易,我們會碰到兩個問題。

第一個問題就是手數的問題,我們的EA計算出來的手數有可能會小於最小手數,也有可能會大於最大手數,當小於最小手數時,我們不做單,當大於最大手數時我們把單子拆開來分成幾單來做,這一部分內容可以參考第五節課。

第二個問題就是開倉遇到的問題,比如說由於卡頓導致訂單沒有成交等等,此時我們需要立即再發送開單命令,直到成交為止。

根據以上問題我們把程序寫出來,代碼如下:

 int orderopen(string sym,string direction,double lot,double sl,double tp,int mag,string comment) { int check; double lot_min=MarketInfo(sym,MODE_MINLOT); double lot_max=MarketInfo(sym,MODE_MAXLOT); double lot_last=0; int err=0; int huadian=10; double amount; int i; if(lot<lot_min) { Print('lot is too small'); return(1); } else if(lot>lot_max) { amount=MathCeil(lot/lot_max); lot_last=lot-(amount-1)*lot_max; for(i=(int)amount;i>0;i--) { if (i!=1) { if(direction=='BUY') { do { check=OrderSend(sym,OP_BUY,lot_max,MarketInfo(sym, MODE_ASK),huadian,sl,tp,comment,mag,0,clrBlue); if(check==-1) { err=GetLastError(); } else { break; } }while(err==146 || err==135); } else if(direction=='SELL') { do { check=OrderSend(sym,OP_SELL,lot_max,MarketInfo(sym, MODE_BID),huadian,sl,tp,comment,mag,0,clrRed); if(check==-1) { err=GetLastError(); } else { break; } }while(err==146 || err==135); } } else { if(direction=='BUY') { do { check=OrderSend(sym,OP_BUY,lot_last,MarketInfo(sym, MODE_ASK),huadian,sl,tp,comment,mag,0,clrBlue); if(check==-1) { err=GetLastError(); } else { break; } }while(err==146 || err==135); } else if(direction=='SELL') { do { check=OrderSend(sym,OP_SELL,lot_last,MarketInfo(sym, MODE_BID),huadian,sl,tp,comment,mag,0,clrRed); if(check==-1) { err=GetLastError(); } else { break; } }while(err==146 || err==135); } } } } else { if(direction=='BUY') { do { check=OrderSend(sym,OP_BUY,lot,MarketInfo(sym, MODE_BID),huadian,sl,tp,comment,mag,0,clrBlue); if(check==-1) { err=GetLastError(); } else { break; } }while(err==146 || err==135); } if(direction=='SELL') { do { check=OrderSend(sym,OP_SELL,lot,MarketInfo(sym, MODE_BID),huadian,sl,tp,comment,mag,0,clrRed); if(check==-1) { err=GetLastError(); } else { break; } }while(err==146 || err==135); } } return(0); }

以上程序中對於手數小於最小手數以及手數大於最大手數的情況的處理方式參照第五課,對於卡頓導致訂單沒有成交的問題,我們採用do while循環語句來解決,在程序中如果訂單沒有成交,那麼OrderSend()函數會返回-1,我們將這個返回值賦給check這個變量,如果check等於-1那麼檢查錯誤的原因,然後將這個原因對應的編號賦值給err,如果err=146或者135那麼程序會再循環一次直到check不等於-1跳出循環為止。

3平倉模塊

平倉模塊主要解決的是平台因為卡頓無法平倉的問題,思路和開倉模塊一樣,代碼如下:

 void ordercl(string sym,int ticket) { int i; bool result=false; double price; double lot; int err=0; int huadian=10; for(i=OrdersTotal()-1;i>=0;i--) { if(OrderSelect(i,SELECT_BY_POS)) { if(OrderSymbol()==sym && OrderTicket()==ticket) { if(OrderType()==OP_SELL) { lot=OrderLots(); price=MarketInfo(sym,MODE_ASK); do { result=OrderClose(ticket,lot,price,huadian); if(result==false) { err=GetLastError(); } }while(err==146 || err==135); } else if(OrderType()==OP_BUY) { lot=OrderLots(); price=MarketInfo(sym,MODE_BID); do { result=OrderClose(ticket,lot,price,huadian); if(result==false) { err=GetLastError(); } }while(err==146 || err==135); } if(result==false) { err=GetLastError(); Print('order close failed,err='+(string)err); Print('Ticket Number='+(string)OrderTicket()); } } } } }

以上平倉模塊中我們需要輸入的參數有需要平倉的訂單對應的品種和訂單號,實際上如果只根據訂單號來平倉也是可以的,這裡增加品種的識別是為了防止

4掛單模塊

掛單模塊一般來說不會存在延遲的問題,所以它只需要解決拆單的問題,但是需要解決價格錯誤的問題,比如說掛突破買單,當掛單的價格小於當前買價時,掛單是掛不了的,所以要排除這種價格錯誤的情況。代碼如下:

 int pendingorder(string sym,string direction,double price,double lot,double sl, double tp,int mag,string comment) { int check; double lot_min=MarketInfo(sym,MODE_MINLOT); double lot_max=MarketInfo(sym,MODE_MAXLOT); double lot_last=0; int huadian=10; double amount; int i; if(lot<lot_min) { Print('lot is too small'); return(1); } if(direction=='BUYSTOP' && price<MarketInfo(sym,MODE_ASK)+ MarketInfo(sym,MODE_SPREAD)*MarketInfo(sym,MODE_POINT)) { Print('Price is too low for BUYSTOP order'); return(1); } else if(direction=='BUYLIMIT' && price>MarketInfo(sym,MODE_ASK)- MarketInfo(sym,MODE_SPREAD)*MarketInfo(sym,MODE_POINT)) { Print('Price is too high for BUYLIMIT order'); return(1); } else if(direction=='SELLSTOP' && price>MarketInfo(sym,MODE_BID)- MarketInfo(sym,MODE_SPREAD)*MarketInfo(sym,MODE_POINT)) { Print('Price is too high for SELLSTOP order'); return(1); } else if(direction=='SELLLIMIT' && price<MarketInfo(sym,MODE_BID)+ MarketInfo(sym,MODE_SPREAD)*MarketInfo(sym,MODE_POINT)) { Print('Price is too low for SELLLIMIT order'); return(1); } if(lot>lot_max) { amount=MathCeil(lot/lot_max); lot_last=lot-(amount-1)*lot_max; for(i=(int)amount;i>0;i--) { if (i!=1) { if(direction=='BUYSTOP') { check=OrderSend(sym,OP_BUYSTOP,lot_max,price,huadian,sl,tp, comment,mag,0,clrBlue); } else if(direction=='BUYLIMIT') { check=OrderSend(sym,OP_BUYLIMIT,lot_max,price,huadian,sl,tp, comment,mag,0,clrBlue); } else if(direction=='SELLSTOP') { check=OrderSend(sym,OP_SELLSTOP,lot_max,price,huadian,sl,tp, comment,mag,0,clrRed); } else if(direction=='SELLLIMIT') { check=OrderSend(sym,OP_SELLLIMIT,lot_max,price,huadian,sl,tp, comment,mag,0,clrRed); } } else { if(direction=='BUYSTOP') { check=OrderSend(sym,OP_BUYSTOP,lot_last,price,huadian,sl,tp, comment,mag,0,clrBlue); } else if(direction=='BUYLIMIT') { check=OrderSend(sym,OP_BUYLIMIT,lot_last,price,huadian,sl,tp, comment,mag,0,clrBlue); } else if(direction=='SELLSTOP') { check=OrderSend(sym,OP_SELLSTOP,lot_last,price,huadian,sl,tp, comment,mag,0,clrRed); } else if(direction=='SELLLIMIT') { check=OrderSend(sym,OP_SELLLIMIT,lot_last,price,huadian,sl,tp, comment,mag,0,clrRed); } } } } else { if(direction=='BUYSTOP') { check=OrderSend(sym,OP_BUYSTOP,lot,price,huadian,sl,tp, comment,mag,0,clrBlue); } else if(direction=='BUYLIMIT') { check=OrderSend(sym,OP_BUYLIMIT,lot,price,huadian,sl,tp, comment,mag,0,clrBlue); } else if(direction=='SELLSTOP') { check=OrderSend(sym,OP_SELLSTOP,lot,price,huadian,sl,tp, comment,mag,0,clrRed); } else if(direction=='SELLLIMIT') { check=OrderSend(sym,OP_SELLLIMIT,lot,price,huadian,sl,tp, comment,mag,0,clrRed); } } return(0); }

以上代碼中,我們對輸入的一些錯誤進行了處理,輸出了問題以備交易者參考,接下來我們將大訂單拆開來,分解成了小單子再根據掛單的種類掛上去,雖然程序看起來有點多,實際上都是對掛單類型的識別,原理比較簡單。

那麼以上四個模塊就是我們以後可以使用的通用模塊了,接下來的課會講解幾個EA的編程,在這個過程中我們會反復用到這幾個模塊,以上內容希望大家好好理解和掌握。