以文本方式查看主题

-  金字塔客服中心 - 专业程序化交易软件提供商  (http://www.weistock.com/bbs/index.asp)
--  金字塔软件问题提交  (http://www.weistock.com/bbs/list.asp?boardid=2)
----  [建议]关于编写高效率的程序(说慢的进,客服也请进)  (http://www.weistock.com/bbs/dispbbs.asp?boardid=2&id=51455)

--  作者:klc
--  发布时间:2013/4/26 19:01:29
--  [建议]关于编写高效率的程序(说慢的进,客服也请进)

客服来了也请看完吧,看在我写了那么多的份上,因为后面我有疑问,同时也有建议。

 

由于buy、sell要求逐K线,按我的理解,每收到一次行情数据(如期货每秒会收到两次行情),金字塔都会从第一根K线到最后一根K线逐根调用你的程序,假如你的程序加载要5秒钟,那么每收到一个tick,也同样需要5秒钟时间运算,而tick是每秒两个,你的程序来不及运算所以怎么可能不卡?逐K线的原理就是每次都是从头到尾。

废话少说,说说编写高效率程序的方法吧,以下假设你的程序相当的复杂,需要大量费时的运算:

 

1、勾选“仅刷最后一根K线”,这样每次就只计算一根K线,效率大大提高,勾选之后你一定不能使用未来函数,当然,我从来不使用未来函数。当然还有其它代价,就是全局变量对最后一根K线无效,后面会提到

 

2、少用stkindi,因为每次调用,里面都还有大量运算,就好比嵌套循环一样效率成倍下降,不得以要用时,调用的那个指标也请勾上“仅刷最后一根K线”

 

3、选项中“图形显示X周期”可以设小一点,一般设比你一个屏幕的K线数大一点就可以了,我发现即使图形不显示,但指标仍然会计算的,holding是正确的。显示周期设小好处是节约内存,减少界面的卡

 

4、内存保存K线可以设大点,选项的“内存”页选最大内存使用(用内存空间换效率)

 

5、写的交易程序或者指标,如果最后K线的指标值或交易信号,和N根K线前的指标值、交易信号和K线数据无关,那么点下公式编辑窗口那个“快速”按钮,根据你公式的情况输入,值越小越快,但不要小于你实际需要用到的K线数

 

6、善用跨周期引用行情数据(不是引用指标)而少用统计函数,这个举一个例子说明:

假如交易程序运行在1分钟K线图上,但你需要获得当日的交易额,那么有两种写法:

写法1:callstock(stklabel,vtAMOUNT,6,0);

写法2:sum(AMOUNT,BARSLAST(date<>ref(date,1)));

显然写法1比写法2效率高得多,但写法1有一个问题,那就是对历史上的K线,获得的实际上是收盘时的成交额,而不是盘中对应于1分钟图上的某时刻的成交额,属于使用了“未来数据”(未收盘而使用了收盘数据),可能大大影响你历史信号的可靠性。以下是我程序中的一段代码,供参考:

if ISLASTBAR then
 SH_A_0:=CALLSTOCK(\'SH000001\',vtAMOUNT,6,0);  //当天的数据直接取,免去计算
else
 SH_A_0:=STKINDI(\'SH000001\',\'ac.ac\',0,1,0);          //历史K线也用自定义的指标ac来统计上证的截至到当前分钟的成交额,准确但效率相当低

就是巧妙的用IsLastbar来区分历史数据和当前数据,历史数据我用了我在第2点讲要尽可能不用的低效的stkindi,不过没关系只是在加载程序到图表上时对历史数据进行了一次低效但精准的计算,加载的速度会比较慢,但在实盘时,因为我们勾选了“仅刷最后一根K线”,所以加载后就飞速了。我自己的程序经过使用大量技巧后,基本上在实盘时避免了使用二次调用指标和统计函数,所以我的程序在实盘时都不卡(哪些函数涉及大量运算的统计,看金字塔函数说明)。

 

再举一个避免引用统计的例子,求3天简单ma值,在程序中直接计算:

(CALLSTOCK(stklabel,vtClose,6,0)+CALLSTOCK(stklabel,vtClose,6,0)+CALLSTOCK(stklabel,vtClose,6,0))/3.0;

这样又避免了写调用指标了,因为我怕调用指标时对无关的K线值也进行了反复计算,而我只关心一个值

 

7、对于程序运行过程中值永远不变的,不要定义变量,最好直接用参数替代,理论上给变量赋值也是逐K线运算的也会消耗一点点时间(但可用variable可定义全局变量并在定义时赋值,理论上只是初始化一次而不用每K线赋值的)

 

8、如果必须用到数组,并且数组的值在程序运行过程中也不会发生变化的,为了避免每个K线都赋值,用以下代码:

VARIABLE:Amountper[240]=0.0;
if ISLASTBAR or BARPOS=1 THEN
begin
 Amountper[1]:=1.538;
 Amountper[2]:=0.68;

........

end

加载程序以及策略评测时,只在第一根K线时(BARPOS=1)为这些变量赋值,因此加快了加载程序和评测的速度。

 

9、如果你的变量在程序运行过程中会发生变化,但并不是每根K线变化,而是满足一定周期才变化的,比如我的股指交易合约,每月才变化一次,所以在月初进行切换,并用VARIABLE声明的全局变量来记录当前交易的合约:

VARIABLE:jyhy=\'IF00\',能否交易=false;

if ISLASTBAR or barpos=1 or month()<>ref(month(),1) THEN //最后K、第一根、换月时检查是否需要换合约
begin
 yuefen:=month();
 if (yuefen=12) then
  jyhy:=\'IF01\';
 else if (yuefen>=1 and yuefen<=8) then
  jyhy:=\'IF0\' & NUMTOSTR(yuefen+1,0);
 else if (yuefen>=9 and yuefen<=11) then
  jyhy:=\'IF\' & NUMTOSTR(yuefen+1,0);
 能否交易:=STRCMP(STKLABEL,jyhy)=0;
end

if ISLASTBAR and 能否交易=false and THOLDING=0 and holding=0 and 实盘中 then

begin

  GotoNewCode(); //自定义函数:用VBA代码切换当前图表窗口的交易合约

end

以上代码用了month()<>ref(month(),1),避免了每根K线都去做运算。如果是每天一次运算的就用date()<>ref(date(),1)。以上代码对于越小的周期,节约的运算量越可观。

 

10、第7、8、9点都是节约的加载时间以及加快评测速度,但并不能减少实盘时的运算量,因为我发现一点问题,就是在勾选了“仅刷最后一根K线”之后,不管是VARIABLE还是GLOBALVARIABLE声明的全局变量,到了最后一根K线时,值都变回初始化的值了,而不是保留上次计算的值。当然这个情况只发生在最后一根K线,所以你们看到了我在第7、8、9点中还是用了全局变量并且用他来节约历史数据的计算时间以及加快评测速度(如果你的策略的确很复杂,你测试3年的1分钟周期,可能要花上十分钟)。

解决的办法是有两个,分别是:

10.1、像第8、9点我写的那样,if里面加个IsLastbar or,对最后一根K线,重新计算全局变量的值。当然,代价就是每tick行情都要计算一次。但起码历史数据和评测是快了,并且这也是勾选“仅刷最后一根K线”大大加快了实盘响应速度之后的小小代价;

10.2、使用系统全局变量,就是EXTGB开头的那几个函数(用法自己看函数说明了),当然系统全局变量读写的是硬盘,VARIABLE等定义的变量读写的是内存,所以EXTGB等的操作肯定也不是非常快,所以要衡量你的这个全局变量计算过程是否足够复杂了,如果很复杂,就用EXTGB,不复杂就用10.1的方法。

正确的使用系统全局变量,除了要避免不同策略不要互相冲突外,还有就是最好在历史K线上用VARIABLE,最后K线用EXTGBData等函数,例如:

VARIABLE:myvalue=0;

if barpos=1 THEN
begin
 EXTGBDATASET(\'我的全局变量\',0); //加载时把这个全局变量“初始化”为0
end;

if barpos=1 or month()<>ref(month(),1) then//历史K线,需要改变全局变量的值时,这次没了ISLASTBAR

begin

 myvalue:=。。。。。。。。。。

 。。。。。。。。//这里是复杂运算,每月才执行一次

 if myvalue<>EXTGBDATA(\'我的全局变量\') then

   EXTGBDATASET(\'我的全局变量\',myvalue);

end

if ISLASTBAR then       //最后K线,直接用EXTGBxxx取全局变量
begin
 myvalue:=EXTGBDATA(\'我的全局变量\');

end

 

总结:基本思路都是实盘时每tick只计算一根K线,并且在最后这根K线的计算时避免统计、调用指标、避免循环,只是最后K线不能取VARIABLE变量值感到有一点点不足。

结束前突然想到上面例子有一点点问题,我用month()<>ref(month(),1)、date()<>ref(date(),1)等条件时,并不是只计算一次,而是有一根K线(每天的第1根或每月的第1根)一直在计算,如果是1分钟图,就在那1分钟内,如果是小时图,就在那1小时内!!!运算。

所以,应该再定义一个全局变量,一个标志,使得真正只计算一次,具体大家可以自己研究。

 

我要问客服专家的两个问题是:

1)我在第9点代码中那个绿色的“实盘中”我实际未用,导致评测时也试图调用vba切换图表,造成vba报错,我想问下有没有一个函数来判断是评测时,还是实盘中呢?我自己的基本思路是调用某个账户函数,如果没有值就是评测中,不知可否,现在也不知道哪个函数比较好。

2)金字塔软件能否增加一个新的全局变量品种,可以在仅刷最后K线时也得以保留,但又比EXTGBxxx轻量的?


--  作者:ackvz
--  发布时间:2013/4/26 19:31:37
--  

长期关注此贴

谢谢楼主分享  可以做个提高效率的精华专题啊


--  作者:klc
--  发布时间:2013/4/26 20:42:23
--  
11、减少vba自定义函数的调用,之前写的那个自动切换交易合约的VBA自定义函数,每次行情更新都调用(在VBA中判断是否切换),结果就是很卡,后来改了下代码,不是每次行情更新都调用,而是先在perl公式中写代码判断,然后if 条件成立 then 调用vba自定义函数,一个月也就调用一次,因此也就不卡了
--  作者:wn10000neng
--  发布时间:2013/4/26 20:43:06
--  

看来很有研究,有时候会回来看的。

我也擅长编写金字塔策略,欢迎交流


--  作者:bdggl
--  发布时间:2013/4/26 20:43:32
--  
申精~果断好贴  人工置顶
--  作者:stockwiner
--  发布时间:2013/4/27 8:50:24
--  
 请教楼主:如果不勾选"只刷新最后一根K线",全局变量在最后一根K线就不会重新初始化?
我最近也碰到这个问题,非常烦恼,你的帖子给我很大帮助.

--  作者:klc
--  发布时间:2013/4/27 9:19:19
--  
以下是引用stockwiner在2013-4-27 8:50:24的发言:
 请教楼主:如果不勾选"只刷新最后一根K线",全局变量在最后一根K线就不会重新初始化?
我最近也碰到这个问题,非常烦恼,你的帖子给我很大帮助.

如果不勾选,全局变量在最后一根K线的值仍会保留前一计算值,这个我试过了。有一个变通的方法(但局限性非常大)可能行,那就是不勾选"只刷新最后一根K线",但点“快速”,然后输入1,实际上也是每次只计算最后一根K线,而且至少超全局变量的值应该能保留前一计算值吧,但问题是这样前面的信号没了,比如前面开仓了,后面无法平仓,但我还是用了他,用在一个真的只需要计算最后一根的地方。


--  作者:klc
--  发布时间:2013/4/28 11:59:03
--  各种全局变量、VBA调用效率测试

很高兴被加精了,第一次啊

不过发现分享变成了责任,原文的许多东西是我理论上的假想,为了更精确,决定对一些语句的使用进行效率测试,让你和我都心里有底,以更好的进行选择,这次先测试各种全局变量以及vba调用的执行效率,测试方法很简单,代码如下:

 

GLOBALVARIABLE:a=0;
VARIABLE:b=0;
if islastbar then
begin
 MSGOUT(1,\'1\');
 for i:=1 to 5000000 do
 BEGIN
  a:=a+1;   //测试GLOBALVARIABLE变量读写效率
 end;
 MSGOUT(1,\'2\');
 for i:=1 to 5000000 do
 BEGIN
  b:=b+1;   //测试VARIABLE变量读写效率
 end;
 MSGOUT(1,\'3\');
 for i:=1 to 5000000 do
 BEGIN
  a:=EXTGBDATA(\'test\'); //测试读系统全局变量效率
 end;
 MSGOUT(1,\'4\');
 for i:=1 to 5000000 do
 BEGIN
  EXTGBDATASET(\'test\',a); //测试写系统全局变量效率
 end;

 MSGOUT(1,\'5\');
 for i:=1 to 5000000 do
 BEGIN
  a:=EXTGBDATA(\'test\'); //测试读+写系统全局变量效率

  EXTGBDATASET(\'test\',a); 
 end;
 MSGOUT(1,\'6\');
 for i:=1 to 5000000 do
 BEGIN
  a:=EXTGBstring(\'test\'); //测试读系统全局字符串变量效率
 end;
 MSGOUT(1,\'7\');
 for i:=1 to 5000000 do
 BEGIN
  EXTGBstringset(\'test\',a);//测试写系统全局字符串变量效率
 end;

 MSGOUT(1,\'8\');
 for i:=1 to 5000000 do
 BEGIN
  a:=EXTGBstring(\'test\'); //测试读+写系统全局字符串变量效率

  EXTGBstringset(\'test\',a);
 end;
 MSGOUT(1,\'9\');
 for i:=1 to 5000000 do
 BEGIN
  my_fun();  //my_fun是自定义函数,即vba调用,里面什么事也没干,干调用
 end;
 MSGOUT(1,\'10\');
end;

每种操作都循环500万次,结果如下:

 

   方法  读写500万次  费时 效率评价
VARIABLE变量 读+写 6秒 极快
GLOBALVARIABLE 读+写 7秒 极快   
EXTGBDATA 仅读 9秒 
EXTGBDATASET 仅写 10秒

EXTGBDATA

EXTGBDATASET

读+写 16秒 中等
EXTGBSTRING 仅读 10秒
EXTGBSTRINGSET 仅写 12秒 中等

EXTGBSTRING

EXTGBSTRINGSET

读+写 18秒 较慢
VBA调用 仅接口调用 54秒 很慢

 

由于我没有找到输出毫秒的方法,所以以上测试会有1秒之内的误差,但基本上印证了原文中的假设:VARIABLE和GLOBALVARIABLE最快,读取时间可以忽略不计,EXTGBxxx操作的系统全局变量速度较前两者慢,并且对字符串的操作比对数字的操作更慢,VBA调用最慢,所以应该尽量避免频繁调用。


--  作者:klc
--  发布时间:2013/4/28 16:11:28
--  

澄清一下,测试结果表明从Perl公式调用VBA会很慢,并不是说VBA代码效率很慢,只是切换代码执行环境会比较慢。换句话说,应该避免频繁调用VBA,比如每tick新行情就调用一次VBA、甚至是每tick行情就调用多次VBA函数,就可能会有点卡了。

但如果是满足条件时才调用VBA,不会说0点几秒就调用一次,则不会有很大的影响。


--  作者:sxpms
--  发布时间:2013/5/16 23:31:35
--  

好贴子。

VBA和PEL能否做个比较?