[原創]教你寫一個金字塔不卡的復雜圖表策略[金字塔模型]
前言:
這是一個代碼框架,其中部分靈感來自阿火的策略、guotx2010的VBA教程和王峰的一句話(如果你注重效率,那么金字塔提供的全局變量數據庫的速度完全可以超越INI文件的),所以在此向各位前輩致謝!
適用本框架的前提:
不使用金字塔規定不能用于if ... then中的函數(如統計函數、未來函數等)、采用走完K線、且K線走完后信號就固定下來的(即未來不會發生改變)、使用新圖表交易函數、勾選“僅刷最后一根K線”
步驟:
第一步:創建3個自定義函數,創建方法在此不詳述,VBA代碼如下:
\'定義4個動態數組保存信號和信號發生日期和時間
dim dates()
dim times()
dim values()
dim SigCounts()
SigCount = 0
Function INSERTSIG(Formula,SIGNUM,D,T,H)
\'通過VBA數組記錄信號以及信號發生的時間,當最新信號發出時執行一次
INSERTSIG=0
On Error Resume Next
dates(SIGNUM).AddBack(D)
if err.number<>0 then
INSERTSIG=1
exit function
end if
times(SIGNUM).AddBack(T)
values(SIGNUM).AddBack(H)
if dates(SIGNUM).Count>SigCounts(SIGNUM) and SigCounts(SIGNUM)>0 then
dates(SIGNUM).RemoveAt(0)
times(SIGNUM).RemoveAt(0)
values(SIGNUM).RemoveAt(0)
end if
End Function
Function READSIG(Formula,SIGNUM)
\'將數組信號發生時間轉換為K線位置,并記錄到單值全局變量系統中,供Perl公式讀取,每產生一次新K線時執行一次
READSIG=0
On Error Resume Next
cc = times(SIGNUM).Count
if err.number<>0 then
READSIG = 1
exit function
end if
iGlobal=document.ExtDataNum
for i=iGlobal to 0 step -1
iKeyValue=document.GetExtDataByIndex(i,sKeyName)
if (strComp(left(sKeyName,5),"HH" & right(Formatnumber(1000+SIGNUM,0,0,0,0),3))=0) or (strComp(left(sKeyName,5),"PP" & right(Formatnumber(1000+SIGNUM,0,0,0,0),3))=0) then
call document.RemoveExtData(i)
end if
next
Set History = Formula.ParentGrid.GetHistoryData()
next_sig_pos = 0
For i = times(SIGNUM).Count-1 To 0 step -1
str = Formatnumber(19000000+dates(SIGNUM).GetAt(i),0,0,0,0) & Formatnumber(1000000+times(SIGNUM).GetAt(i),0,0,0,0)
str = mid(str,1,4) & "-" & mid(str,5,2) & "-" & mid(str,7,2) & " " & mid(str,10,2) & ":" & mid(str,12,2) & ":" & mid(str,14,2)
bi = History.GetPosFromDate(str) + 1
Document.SetExtData "PP" & right(Formatnumber(1000+SIGNUM,0,0,0,0),3) & Formatnumber(bi,0,0,0,0),next_sig_pos
Document.SetExtData "HH" & right(Formatnumber(1000+SIGNUM,0,0,0,0),3) & Formatnumber(bi,0,0,0,0),values(SIGNUM).GetAt(i)
next_sig_pos = bi
Next
Document.SetExtData "PP" & right(Formatnumber(1000+SIGNUM,0,0,0,0),3) & "1",next_sig_pos
End Function
Function INIT_SIG(Formula,SigNum,Count)
\'初始化數組,加載公式時或其他必要時間(例如加載新品種時)運行一次
INIT_SIG=0
On Error Resume Next
Set dates(SigNum) = nothing
Set times(SigNum) = nothing
Set values(SigNum) = nothing
if err.number<>0 or SigCount<SigNum+1 then
ReDim Preserve dates(SigNum+1)
ReDim Preserve times(SigNum+1)
ReDim Preserve values(SigNum+1)
ReDim Preserve SigCounts(SigNum+1)
SigCount = SigNum+1
end if
Set dates(SigNum) = CreateObject("Stock.Array")
Set times(SigNum) = CreateObject("Stock.Array")
Set values(SigNum) = CreateObject("Stock.Array")
dates(SigNum).RemoveAll
times(SigNum).RemoveAll
values(SigNum).RemoveAll
SigCounts(SigNum) = Count
End Function
第二步:Perl公式代碼修改為以下框架:
///////////////固定的開頭,您僅可以修改“保留信號數”以及“策略號”/////////////////////////////////////////////////
GLOBALVARIABLE:d=0,t=0,next_sig_pos=0,保留信號數=20,策略號=0;
mylot:holding,NODRAW;
if BARPOS=1 then
begin
if EXTGBDATA(\'股指合約切換\')=1 then
begin
d:=0;
t:=0;
//EXTGBDATASET(\'股指合約切換\',0);
end;
if d=0 and t=0 then
begin
xxx:=INIT_SIG(策略號,保留信號數);
end
else begin
xxx:=round(READSIG(策略號));
if xxx=1 then
begin
d:=0;
t:=0;
next_sig_pos=0;
xxx:=INIT_SIG(策略號,保留信號數);
end
else
next_sig_pos:=1;
end;
end;
if barpos=next_sig_pos then
begin
myholding:=round(extgbdata(\'HH\' & strright(numtostr(1000+策略號,0),3) & numtostr(barpos,0)))-holding;
next_sig_pos:=round(extgbdata(\'PP\' & strright(numtostr(1000+策略號,0),3) & numtostr(barpos,0)));
if myholding>0 then
begin
pc:=min(abs(min(holding,0)),myholding);
kc:=myholding-pc;
sellshort(pc>0 and holding<0,pc,market);
buy(kc>0 and holding>=0,kc,market);
end
else if myholding<0 then
begin
pc:=min(max(holding,0),abs(myholding));
kc:=abs(myholding)-pc;
sell(pc>0 and holding>0,pc,market);
buyshort(kc>0 and holding<=0,kc,market);
end;
end;
if date()<d or (date()=d and time()<=t) or ISLASTBAR then exit;
//////////////////////////////////////////////////////////////////////////////////////////////////////
//這里本應省略N行代碼,這是您原來的策略代碼,為了使您馬上能測試,我隨便寫了個簡單的策略,請不要照用
issell:=close<open and CALLSTOCK(STKLABEL,vtCLOSE,1,-1)<CALLSTOCK(STKLABEL,vtOpen,1,-1);//2連陰空
isbuy:=close>open and CALLSTOCK(STKLABEL,vtCLOSE,1,-1)>CALLSTOCK(STKLABEL,vtOpen,1,-1);//2連陽多
sell(holding>0 and issell,1,market);
SELLSHORT(holding<0 and isbuy,1,market);
buyshort(holding=0 and issell,-1,market);
buy(holding=0 and isbuy,1,market);
j:=0;
for i:=1 to 3000 do //////////這里加了個循環3000次,目的是故意拖慢效率
j:=j+1;
//您的策略代碼可以非常復雜,唯一需要注意的是請保證后面的結束語句能被執行,即至少產生交易信號時不要使用exit
/////////////////固定的結束語句,請原封不動//////////////////////////////////////////////////////////
d:=date();
t:=time();
if (mylot<>holding) then xxx:=round(INSERTSIG(策略號,d,t,holding));
一般策略卡的原因:
為了優化代碼執行效率,使得特別復雜的策略運行起來也不會卡,我專門研究了金字塔公式的執行過程,發現“逐K線計算”+“僅刷最后一根K線”模式的運行原理是這樣的:
加載公式到圖表,或公式被初次stkindi:從第1根K線(barpos=1)逐根計算至最后一根K線(barpos=DATACOUNT且islastbar為true)
收到新的行情但沒有產生新的K線:僅就最后一根K線進行計算
收到新的行情并且產生新的K線(即新K線收到第一筆行情):從第1根K線(barpos=1)逐根計算至最后一根K線(barpos=DATACOUNT且islastbar為true)
由于以上原因,所以勾選“僅刷最后一根K線”后,一般的公式就應該不怎么卡了,但如果你的公式表現還是卡,那就是兩個原因了:
1、盡管每次只計算最后一根K線,但你的代碼對最后K線計算過程非常復雜,導致剛計算完甚至還沒來得及計算完,又收到新的行情了,你的cpu一直處于高度緊張狀態
2、當新K線產生時,雖然你勾選了“僅刷最后一根K線”,但新K線收到第一筆行情時,仍會從barpos=1計算至lastbar,所以你如果用1分鐘周期,那么當計算量非常大時,1分鐘會卡一次
我的優化原理:
1、加載公式時,除了你代碼自身優化外,我沒什么能幫你的,所以本策略不能使你的公式加載更快
2、收到新行情但未產生新K線時,我建議你的是采用走完K線模式,所以最后K線完全可以不計算,而只在K線走完時計算倒數第2根K線,所以我遇到islastbar直接exit,這樣在一根K線未走完時,是完全沒有任何計算的
3、只在產生新K線時對倒數第2根K線進行計算,如果該K線產生交易信號,那么調用VBA記錄交易該信號以及產生的K線日期和時間
4、產生新K線時,由于金字塔要求從barpos=1開始重新刷新所有K線,第3點我已經為您記錄了交易信號以及其產生的日期和時間,所以,我會在金字塔重新刷新第一根K線前(barpos=1),再次調用VBA,把所有交易信號(信號產生時間轉換為K線序號)寫入單值全局變量數據庫
5、金字塔重新刷新所有K線時,我幫你直接從單值全局變量數據庫取信號刷新到歷史K線上,而不需要重新計算,直到倒數第2根
如果你的代碼不復雜,就不要用這個了,反而弄復雜了,計算量越大才越有效,我示范的代碼加了個循環3000次,可以看出一點都不卡,如果不優化,就很卡了
為了寫得通用,我設了兩個參數,解釋一下
保留信號數=20 //如果設為0表示顯示所有交易信號,如果設20表示僅顯示最后20個信號,設得值越小肯定速度越快,建議設置5~20個左右
策略號=0 //策略號不能重復,如果你有4個策略同時跑,那么請設置不同的策略號,策略號的設置范圍為0~999,如果兩個圖表策略設置相同策略號,將產生沖突
最好不要使用ref等金字塔提示不能用在if ... then語句中的函數,如果你要引用前面的周期,建議改為callstock(stklabel,.....)等表達方式,因為金字塔沒有提示在if then中不能用callstock
假設:你的策略非常復雜,每計算一個K線需要0.2秒,使用一分鐘K線周期,每秒收到兩個tick行情,您的圖表一共有5千根K線,歷史上一共產生了200個交易信號
那么,我可以為您做個優化前后的對比:
優化前:每收到一個tick行情需要計算0.1秒,每秒需要計算兩次,即0.2秒;每根K線走完需要進行一次5千根K線的循環計算,即500秒(那肯定是卡死了),您以前唯一可以優化的是點那個“快速”按鈕,假如您輸入的是100根K線,那么您的程序1分鐘也會卡住10秒鐘,如果你的信號跨度比較大,比如有可能持倉超過100根K線,那么此方法還行不通了
優化后:一分鐘時間內只需要調用最多兩次VBA,寫和讀200次單值全局變量數據庫,200個交易指令,一次完整的K線運算,具體如下:
收到新行情時,直接退出,不消耗cpu;每走完一根K線時,需要調用一次VBA(消耗0.00001秒),把歷史上的200個交易信號寫入單值全局變量數據庫,需要寫200次全局變量(需要0.000002*200=0.0004秒),以上過程加上數據處理的時間(主要是將信號產生的日期和時間轉換為K線序號),我算他0.1秒是綽綽有余了。另外需要0.1秒計算倒數第二根K線,如果這根K線產生了信號,那么需要再次調用VBA把信號記錄(這個記錄操作我也算他0.1秒)。再加上金字塔本身循環5000個K線所需的時間(但沒有任何代碼去處理他),這個沒測過,應該很快( m.kzuj.com.cn )
也就是,最多消耗0.3秒左右即可完成,比之前的大大提高了。
注:關于調用VBA和單值全局變量數據庫的操作速度,可參考我另外一個帖子的測試結果,結果顯示 GLOBALVARIABLE快于 單值全局變量數據庫 快于VBA調用,所以我寫程序的時候盡可能把VBA調用次數減少到最低,并且能用GLOBALVARIABLE就不用EXTGBxxx
{別忘了將本網告訴您身邊的朋友,向朋友傳達有用資料,也是一種人情,你朋友會感謝你的。}
有思路,想編寫各種指標公式,程序化交易模型,選股公式,預警公式的朋友
可聯系技術人員 QQ: 1145508240 進行 有償 編寫!(不貴!點擊查看價格!)
相關文章
-
沒有相關內容