欢迎使用金字塔普通技术服务论坛,您可以在相关区域发表技术支持贴。
我司技术服务人员将优先处理 VIP客服论坛 服务贴,普通区问题处理速度慢,请耐心等待。谢谢您对我们的支持与理解。


金字塔客服中心 - 专业程序化交易软件提供商金字塔软件交易策略发布专区 → [原创]教你写一个不卡的复杂图表策略

   

欢迎使用金字塔普通技术服务论坛,您可以在相关区域发表技术支持贴。
我司技术服务人员将优先处理 VIP客服论坛 服务贴,普通区问题处理速度慢,请耐心等待。谢谢您对我们的支持与理解。    


  共有20677人关注过本帖树形打印复制链接

主题:[原创]教你写一个不卡的复杂图表策略

帅哥哟,离线,有人找我吗?
klc
  1楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:黑侠 帖子:993 积分:1787 威望:0 精华:5 注册:2012/11/28 17:37:20
[原创]教你写一个不卡的复杂图表策略  发帖心情 Post By:2013/5/2 0:56:52 [只看该作者]

前言

这是一个代码框架,其中部分灵感来自阿火的策略、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次,可以看出一点都不卡,如果不优化,就很卡了


版主评定:好评,获得50个金币奖励好评,获得50个金币奖励
(理由:好文章)
 回到顶部
帅哥哟,离线,有人找我吗?
ackvz
  2楼 | QQ | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:黑侠 帖子:618 积分:2624 威望:0 精华:0 注册:2010/4/25 15:17:16
  发帖心情 Post By:2013/5/2 8:36:11 [只看该作者]

图片点击可在新窗口打开查看  版主速来加精啊!

 

这种总结 真是太有用了


 回到顶部
帅哥哟,离线,有人找我吗?
klc
  3楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:黑侠 帖子:993 积分:1787 威望:0 精华:5 注册:2012/11/28 17:37:20
  发帖心情 Post By:2013/5/2 11:46:46 [只看该作者]

昨晚赶着1点前发贴,没写完

 

为了写得通用,我设了两个参数,解释一下

保留信号数=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线所需的时间(但没有任何代码去处理他),这个没测过,应该很快

也就是,最多消耗0.3秒左右即可完成,比之前的大大提高了。

注:关于调用VBA和单值全局变量数据库的操作速度,可参考我另外一个帖子的测试结果,结果显示 GLOBALVARIABLE快于 单值全局变量数据库 快于VBA调用,所以我写程序的时候尽可能把VBA调用次数减少到最低,并且能用GLOBALVARIABLE就不用EXTGBxxx

 

 


[本帖被加为精华]
 回到顶部
帅哥哟,离线,有人找我吗?
sxpms
  4楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:论坛游侠 帖子:109 积分:360 威望:0 精华:0 注册:2011/7/9 18:05:33
  发帖心情 Post By:2013/5/22 0:01:54 [只看该作者]

然而,仅仅用于图标,还是有些遗憾了


 回到顶部
帅哥哟,离线,有人找我吗?
klc
  5楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:黑侠 帖子:993 积分:1787 威望:0 精华:5 注册:2012/11/28 17:37:20
  发帖心情 Post By:2013/5/28 21:52:39 [只看该作者]

楼上的,就是图表的,才有必要优化,后台的应该不存在反复计算的问题吧?

 回到顶部
帅哥哟,离线,有人找我吗?
wb5272
  6楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:新手上路 帖子:57 积分:294 威望:0 精华:0 注册:2012/3/15 15:32:30
  发帖心情 Post By:2013/6/4 8:08:22 [只看该作者]

高手啊

 回到顶部
帅哥哟,离线,有人找我吗?
绿草地77
  7楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:论坛游民 帖子:126 积分:0 威望:0 精华:0 注册:2013/6/8 9:24:22
  发帖心情 Post By:2013/6/24 12:16:57 [只看该作者]

谢谢分享


 回到顶部
帅哥哟,离线,有人找我吗?
绿草地77
  8楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:论坛游民 帖子:126 积分:0 威望:0 精华:0 注册:2013/6/8 9:24:22
  发帖心情 Post By:2013/6/24 19:26:57 [只看该作者]

慢了五秒下单,算不算卡呢?


 回到顶部
帅哥哟,离线,有人找我吗?
klc
  9楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:黑侠 帖子:993 积分:1787 威望:0 精华:5 注册:2012/11/28 17:37:20
  发帖心情 Post By:2013/7/1 15:50:44 [只看该作者]

以下是引用绿草地77在2013/6/24 19:26:57的发言:

慢了五秒下单,算不算卡呢?

不一定是卡,只要你人工在金字塔上操作,觉得金字塔界面操作无影响,就没问题阿


 回到顶部
帅哥哟,离线,有人找我吗?
klc
  10楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:黑侠 帖子:993 积分:1787 威望:0 精华:5 注册:2012/11/28 17:37:20
  发帖心情 Post By:2013/7/25 19:09:15 [只看该作者]

======在 2013/7/25 15:28:08 您来信中写道:======

小弟拜读了兄的一系列文章,非常有帮助,感谢兄的分享!

 

因为我也是准备应用图表交易,需要提高速度,在看兄的“不卡的图表”时,有点疑问,对于新K线时,兄保存交易信号到全局变量,再在刷新时把交易信号信息赋给图表。

 

小弟的问题是:

(1)新K线时,金字塔从首根K线开始的逐K线刷新的目的何在? 取得holding值? 我看兄的代码好像和holding没关系?

金字塔从首根K线刷新的目的应该是金字塔设计者才知道目的,我只是通过研究知道是这样的。我的代码多处用到holding啊,比如公式最后是:

/////////////////固定的结束语句,请原封不动//////////////////////////////////////////////////////////

d:=date();
t:=time();
if (mylot<>holding) then xxx:=round(INSERTSIG(策略号,d,t,holding));

就是当holding变化时,就把holding变化的日期(d)时间(t)和holding的值通过InsertSig这个自定义函数传给vba去保存起来啊。

(2)兄为什么要保留图表的历史交易信号信息,如果已经交易了,这些信息如果不是策略里必须,与当前的交易判断没有关系啊? 如果不需要保留该类信息,不是速度更快,就不需要那个insert函数与read函数了呀

的确没有必要保存所有的信号,如你说的,已经交易了,就不是必要的。所以我在代码中有一个“保留信号数=20”,你设置这个就可以保留不超过20次的信号。当然保留得越少,效率越高,但多保留几个信号,你就可以发挥图表的优势,因为图表交易的一个好处是你可以看到历史上的信号,比较直观。

另外,历史信号也不是一点用处就没有的,金字塔说的,最新的信号是建立在前一个信号的基础上的,比如如果你的策略前面有两次开多1手的信号,那么没有前面两个buy,当前的holding就不会是2,那么也就不可能产生平仓的信号了。看你的策略,你可以保存少一点信号来进一步提高效率,但我测试的情况看,多保留几个信号,也不会太降低效率,因为我写的代码已经是效率比较高的。在重现历史信号的时候,计算量是很低的。

 

不太理解,望兄指点下,多谢兄了!

 


======================================

 回到顶部
总数 18 1 2 下一页