对于实时行情判断是否到达涨跌停价可以获取Tick数据来检查判断,但是对于历史行情就没有办法获得当时的Tick来进行判断。拿股票来举个例子, 策略运行在一个历史涨停日的1分钟Bar上时,怎么直到当前Bar已经到达了涨停价? 如下图所示:
考虑过的方法有:
请问:有什么通用性和可靠性强的方式来判断在小周期的历史Bar上判断价格是否到达了涨跌停价?
我想了一下,还是对涨跌停数据加一层保护比较好,因为超过3天更早的涨跌停数据你已经无法取到了,只能从现在开始每天收盘执行1次收盘作业,慢慢开始积累,所以另外1个回测和实盘使用的策略当中,要对取到的数据加以识别,如果是因为拿不到涨跌停数据的话,要设为无效值,以便特别处理,回测和实盘取涨跌停数据的 代码片段修改版:
Vars
Dic<Numeric> LimitUpDic("LIMIT_UP");
Dic<Numeric> LimitDownDic("LIMIT_DOWN");
Global Bool bOnceGetFlag(False);
Global Numeric gDailyLimitUpPrice; //当下Bar的涨停价
Global Numeric gDailyLimitDownPrice; //当下Bar的跌停价
Events
OnBarOpen(ArrayRef<Integer> indexes){
If(!bOnceGetFlag && LimitUpDic[0] != InvalidNumeric){
bOnceGetFlag = True;
gDailyLimitUpPrice = LimitUpDic[0];
gDailyLimitDownPrice = LimitDownDic[0];
}Else If(TrueDate(0) != TrueDate(1)){
If(LimitUpDic[0] != InvalidNumeric){
gDailyLimitUpPrice = LimitUpDic[0];
gDailyLimitDownPrice = LimitDownDic[0];
}Else{
gDailyLimitUpPrice = InvalidNumeric;
gDailyLimitDownPrice = InvalidNumeric;
}
}
}
兄弟,我觉得这个问题上,恰恰是Dic数据类型的一个最典型的解决方案的应用场景,真是无巧不成书啊,此时此刻是你应该想起用Dic的时候,而不能错付她啊,因为这个使用场景就是机缘和缘分来了,解决思路:
(1)其实Tick数据的历史行情是可以订阅的,我测试了一下3天不到Tick数据TB服务器是允许你订阅的。所以,每天收盘作业执行一遍,就可以保存对应品种历史的涨跌停价格,下面是收盘作业的策略代码:
原来我采用偷懒的方法的,但是后来测试小周期获取历史Bar的涨跌停数据的时候,大部分没有问题,但是如果小周期是Tick级别的时候,Dic回溯就会发生1天的错位,所以我还是用完整的写法,偷懒的方法我注释了,没有删除:
Vars
Dic<Numeric> LimitUpDic("LIMIT_UP", True); //涨停价
Dic<Numeric> LimitDownDic("LIMIT_DOWN", True);//跌停价
Global Bool bOnceSetFlag(False);
Events
OnBar(ArrayRef<Integer> indexs){
If(!bOnceSetFlag){
bOnceSetFlag = True;
Tick td;
GetTick(td);
//LimitUpDic[0] = td.limitUp;
//LimitDownDic[0] = td.limitDown;
SetDicValue("LIMIT_UP", Symbol, Date + 0.18, td.limitUp, True);
SetDicValue("LIMIT_DOWN", Symbol, Date + 0.18, td.limitDown, True);
}Else If(TrueDate(0) != TrueDate(1)){
Tick td;
GetTick(td);
//LimitUpDic[0] = td.limitUp;
//LimitDownDic[0] = td.limitDown;
SetDicValue("LIMIT_UP", Symbol, Date + 0.18, td.limitUp, True);
SetDicValue("LIMIT_DOWN", Symbol, Date + 0.18, td.limitDown, True);
}
}
(3)小周期读取对应Bar的涨跌停价格,我专门放在OnBarOpen里面另外加了TrueDate的控制,毕竟1天之内涨跌停价格都是固定不变的,还是读Dic不是写Dic所以没有性能问题:
Vars
Dic<Numeric> LimitUpDic("LIMIT_UP");
Dic<Numeric> LimitDownDic("LIMIT_DOWN");
Global Bool bOnceGetFlag(False);
Global Numeric gDailyLimitUpPrice; //当下Bar的涨停价
Global Numeric gDailyLimitDownPrice; //当下Bar的跌停价
Events
OnBarOpen(ArrayRef<Integer> indexes){
If(!bOnceGetFlag && LimitUpDic[0] != InvalidNumeric){
bOnceGetFlag = True;
gDailyLimitUpPrice = LimitUpDic[0];
gDailyLimitDownPrice = LimitDownDic[0];
}Else If(TrueDate(0) != TrueDate(1) && LimitUpDic[0] != InvalidNumeric){
gDailyLimitUpPrice = LimitUpDic[0];
gDailyLimitDownPrice = LimitDownDic[0];
}
}
所以Dic可以构建你自己的私域基础数据,以后你每天收盘的时候固定执行一遍收盘作业就可以,用涨跌停数据进行历史回测了。
确实没错,但是只能记录现在开始的数据,历史数据没有。
目前历史数据确实缺失这块,想要自己造轮子,也存在各种问题。
谢谢提示,看来你是真的很喜欢Dic😁. 这个问题我已经解决了,在策略算法里增加了一些容错。
谈不上喜欢,最近看你频繁发帖问关于实盘情况下大批量数据读写Dic数据的算力性能问题嘛,还搞了很多技术测试,然后我不是不建议你这么干嘛,说你实盘情况下读写Dic类型数据,而且还不是一丢丢的数据,还是个二维数据,而且二维数据还随着tick行情会不断增长,我就直截了当告诉你,这个想法一开始就是错了,因为应用场景错了,无巧不成书,今天你问题中的应用场景刚好挺适合的。其实我看你问题中的提到了使用 最高价 等于 最低价 来判断是否 涨停 或 跌停,也是很常用的办法,无非就是你使用的K线周期不能太小,否则这个判断条件很多时候都不准,另外自己估算 当日涨跌停价格的规则,期货与股票不同,期货涨跌停价格的基准价在很多交易所规定的前1日的结算价,而不是收盘价。所以,问题来了,结算价这个数据同样不是标准的K线数据,回测的时候要么你自己策略里面专门订阅一个特别小的周期比如1分钟,专门用来估算历史的结算价(1分钟周期算出来的结算价比较准),然后得到结算价以后按照交易所的合约属性规则的涨跌停百分比,算出涨跌停价(可能也会有截断误差),你算法里面稍微左右留点余地范围,配合最高价==最低价的2个条件一起判断,也是一种解决办法。当然结算价也可以按照上面提示的办法,存入dic数据,而且历史结算价可以一次性算很久,因为1分钟数据订阅1年应该没有问题的,这样你的回测策略就不用为了算结算价专门订阅大量1分钟数据了。直接拿现场算好的结果判断涨跌停就OK了。不过我贴在上面的操作代码是有Bug的,我自己刚才已经发现了,有点粗糙。
我真的是老糊涂,其实每天的结算价很好算的,不用订阅1分钟周期,订阅1日周期的,最多5万根,基本上可以把一个品种的历史数据都拿到了,然后直接用TurnOver/Vol就是当天的结算价,这个不就是结算价的定义嘛。但是又想起一个新问题,就是合约属性之一的涨跌停百分比值,这个TB的基础数据没有,但是交易所历史上可能会修改涨跌停的百分比数值,这样你回测的时候虽然有了结算价 ,但是没有历史合约属性值,那当前的数值测历史回测,判断涨跌停可能又会不准确了。
谢谢老师关注并回答我的帖子👍。不过我遇到的性能问题不是实盘下写的Dic时遇到的,是在加载历史Bar的时候遇到的.
可能我的帖子里面没有说的那么清楚,让老师误会了。最初了解可能的解决方案的时候看到过别人的帖子,刘老师和王老师回复过非持久化的dick直走内存,不走硬盘,我就想着内存里面怎么折腾应该不会消耗那么多的资源,所以就直接在策略的原型代码里跑了一下几万根,然后就发现跟自己想的不一样,出于好奇心就多做了一些性能测试做对比,发了个帖子出来。在生产代码里做了改进只在重大变化时更新后几万根历史Bar也没问题,只要不去搞得那么频繁,不要把数据弄那么多, 非持续化Dic的性能还是很好的😎。
因为刚开始用tb3开发不太熟悉, 为一个专用于量化的开发工具也没有Debugger和Profiler这样的工具来辅助,这点也完全可以理解。只是对于我这样的新手来说,边学习边干对于一些需求和想法只能先从PoC尝试实现,然后做成原型,最后再来做生产代码,对某一个特定问题的技术交流时不太方便把所有的策略代码都贴上去,其它干预因素太多。直接问很细致的技术问题可能会让热心的老师感觉问题问的很突兀或者产生序误解。不过不管怎么样,还是很感谢老师的热心指点,社区里有您这样的老师,我们这些菜鸟才能受益。👍
老师您提的这个通过订阅日线来计算结算价这个思路很不错,我先记下了,也许以后用的上。
另外给老师多补充一点背景信息,我这个帖子问的这个问题其实真正想解决的是当遇上持续的涨停这种极端情况,在小周期里面行情被长时间封死在一个价格,可能会导致行情分析的逻辑在这种情况下可能出问题,当时想了两个思路,一个是看能不能判断k线是否处于涨停来做一些调整,这个实盘很容易,但是历史上不知道怎么做。另一个就是调整一下行情分析的逻辑,来处理这种极端情况,把代码做的更强壮一点。
考虑到在历史行情上判断k线是否处在涨停做其他功能的时候可能会用的上,所以我就自己做二问一,目前是用的第二种方法解决的。
>不过我遇到的性能问题不是实盘下写的Dic时遇到的,是在加载历史Bar的时候遇到的.
这个写DIC的策略不实盘用的,不会有出性能问题。要是除了性能问题,即使几万根Bar的数据,上限最多也只能订阅5万根Bar,大概率是的你算法本身出了问题,比如你每次OnBar、OnBarOpen、OnBarClose都写dic了,可能会卡死,但是这个是你工程算法本身要考虑的问题。很简单,既然都历史Bar,有必要每根Bar上次都写Dic吗?很简单的一个反问问题。所以你自己如果不能描述清楚,又想真正解决问题,最好的做好就是贴上完整源代码,瞅瞅。退一步讲你的问题压根不涉及交易策略。
>不过我遇到的性能问题不是实盘下写的Dic时遇到的,是在加载历史Bar的时候遇到的.
而且还有一个地方也很奇怪的,就是我记得你还有过一个需求点,就是跨策略单元之间共享传输数据的时候,想到用dic读写来操作实现的,这个需求点不是妥妥的在实盘场景下才有的吗?跑历史Bar所有K线加载的完成后,基本上策略也算执行到头了,这个时候,在多个处于不再驱动OnBar的策略之间,用dic来共享传输数据,没有实际意义啊?2个策略的历史数据加载完全是独立、异步各管各完成的,没法像实盘那样 还有交易所的Tick来同步不同策略,这种状态下所谓的 共享数据,没有实际物理意义。
你说的没错,结算价好处理。但是你可能没做过相关的数据处理,其实停板价格非常麻烦,不仅时可能修改停板幅度,而且每个交易所的取整算法,也是不一样的,有的四舍五入,有点向上,有的向下,五花八门,甚至同一个交易所还有可能出现品种不同所以取整算法也不同的事情。
而且还有一个地方也很奇怪的,就是我记得你还有过一个需求点,就是跨策略单元之间共享传输数据的时候,想到用dic读写来操作实现的,这个需求点不是妥妥的在实盘场景下才有的吗?
Ans: 这个是在做PoC的时候遇到的,当时还在验证初步想法能不能实现的阶段。 不确定的技术点都验证完了才开始做一个相对功能完整的原型,原型跑通了才来搞生产代码。
目前来说没有
停板价格不是标准数据,交易所没给。
现在能提供的办法只有第二种,确实也有可能规则变化的情况,那只能说情况较少,只能忍忍了
谢谢老师回复, 我先做个近似的,确保遇到特殊情况能容错就行。