前言:
  这是一个代码框架,其中部分灵感来自阿火的策略、guotx2010的VBA教程和王峰的一句话(如果你注重效率,那么金字塔提供的全局变量数据库的速度完全可以超越INI文件的),所以在此向各位前辈致谢!
  
  适用本框架的前提:
  不使用金字塔规定不能用于if ... then中的函数(如统计函数、未来函数等)、采用走完K线、且K线走完后信号就固定下来的(即未来不会发生改变)、使用新图表交易函数、勾选ldquo;
  仅刷最后一根K线rdquo;
  
  步骤:
  
  第一步:创建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.number0 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.number0 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.number0 or SigCount0 then
  begin
  pc:=min(abs(min(holding,0)),myholding);
  kc:=myholding-pc;
  sellshort(pc>0 and holding0 and holding>=0,kc,market);
  end
  else if myholding0 and holding>0,pc,market);
  buyshort(kc>0 and holdingopen and CALLSTOCK(STKLABEL,vtCLOSE,1,-1)>CALLSTOCK(STKLABEL,vtOpen,1,-1);
  //2连阳多
  sell(holding>0 and issell,1,market);
  SELLSHORT(holdingholding) then xxx:=round(INSERTSIG(策略号,d,t,holding));
  
  一般策略卡的原因:
  为了优化代码执行效率,使得特别复杂的策略运行起来也不会卡,我专门研究了金字塔公式的执行过程,发现ldquo;
  逐K线计算rdquo;
  +ldquo;
  仅刷最后一根K线rdquo;
  模式的运行原理是这样的:
  加载公式到图表,或公式被初次stkindi:从第1根K线(barpos=1)逐根计算至最后一根K线(barpos=DATACOUNT且islastbar为true)
  收到新的行情但没有产生新的K线:仅就最后一根K线进行计算
  收到新的行情并且产生新的K线(即新K线收到第一笔行情):从第1根K线(barpos=1)逐根计算至最后一根K线(barpos=DATACOUNT且islastbar为true)
  由于以上原因,所以勾选ldquo;
  仅刷最后一根K线rdquo;
  后,一般的公式就应该不怎么卡了,但如果你的公式表现还是卡,那就是两个原因了:
  1、尽管每次只计算最后一根K线,但你的代码对最后K线计算过程非常复杂,导致刚计算完甚至还没来得及计算完,又收到新的行情了,你的cpu一直处于高度紧张状态
  2、当新K线产生时,虽然你勾选了ldquo;
  仅刷最后一根K线rdquo;
  ,但新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秒(那肯定是卡死了),您以前唯一可以优化的是点那个ldquo;
  快速rdquo;
  按钮,假如您输入的是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