tb3中加入了 SetBasePeriod,有用,但不严谨,或者说有更严谨的方式我不了解,
策略是大周期(30分钟)和小周期(5分钟) 当前high同时突破upperband[1]买入,下单价位是max(open,upperband[1]),大周期已经设置了SetBasePeriod(1m)
30分钟大周期,upperband是7721,5分钟小周期,upperband是 7711,open是7712,结果是在10:40的时候,下单买开,价位是7712,但此时大周期其实并没有突破7721这个价位,是在后面突破的,在下面的1分钟图可以看到,10:44才突破7721价位,原因我猜应该是在5分钟这个周期里,直接使用了这个周期末的大周期数据,即判断10:40到10:45大周期突破了,但在10:40小周期,开盘价就买入了,即使我设置了BasePeriod,只是确保没有在10:30和10:35买入,在 tb中,确实会出现10:30就买入的情况。
回测结果也确实偏高,引用了未来数据所致。我认为应该是大周期确定突破后,小周期的下一根K线才能买入,即10:45买入。不知道有没有比较好的方式来规避这个问题,
上图:
5分钟小周期:
一分钟图:
你的意思是不是
如果data0,是30分钟,然后设置setbase为1m分钟,那么一根30分钟周期的bar,会截断为以1m为长度的30份执行30遍onbar域的代码。而你希望能获取这30次onbar执行时,每一次的开高低收。
比如运行到第5份时,通过一些操作,获取第四份时的开高低收等序列数据,是这个意思吗?
回复这个贴子:
是的,我想要的功能就是这样的,能够对这些序列数据回溯,这样就能自己控制取哪个细分时间的大周期数据,因为对于大周期,这整个30M,如果不做处理,那数据就是不变的,如果要做处理,那么处理越细越好,跨周期功能就可以更强大。目前按颗粒度运行,却只有截面,没有序列,不得不说是遗憾。叠加1分钟图层也能实现这点, 但太麻烦了,setbaseperiod的初衷应该就是便捷地细化大周期数据处理吧。
谢谢楼上的提醒,再来讨论一下为什么不同合约不能比价呢?通过比价方式,能避免未来数据,但在合约价差比较大的时候,比价出来的结果会大幅偏离,给个示例如下:
ec000和ec2408,价差很大,在大周期达到条件时, data[1].Sell(0,Min(data[1].Open,Min(data[0].Lowerband[1],data[1].Lowerband[1]))); 比价卖平,那么总是data[1].lowerband[1] 更低,然后又因为因为超出当前data[1]的价格范围,经常成交在data[1]的最低价,图中成交价 4165,和30m的lowerband:3856以及5m的lowerband:4243和 5m的open:4241都相差甚远。
Params
Numeric Length1(10);
Vars
Series<Numeric> Upperband;
Series<Numeric> Lowerband;
Events
OnInit()
{
SubscribeBar(RelativeSymbol,5m,data[0].BeginDateTime,data[0].EndDateTime);
data[0].SetBasePeriod(1m);
}
OnBar(ArrayRef<Integer> indexs)
{
Integer i;
Range[i = 0:DataCount-1]
{
Upperband = Highest(averagefc(High,2), Length1);
Lowerband = lowest(averagefc(Low,2),Length1);
PlotNumeric(Upperband,Upperband[1]);
PlotNumeric(Lowerband,Lowerband[1]);
}
Range[i=1:DataCount-1]
{
//系统入场(买开)
If(data[1].Marketposition == 0 And data[0].High >= data[0].Upperband[1] and data[1].high>=data[1].Upperband[1] And Vol > 0)
{
data[1].Buy(1, max(data[1].Open,max(data[0].Upperband[1],data[1].Upperband[1])));
}
// 系统出场(卖平)
Commentary(data[0].LowerBand[1]:+text(data[0].LowerBand[1]));
Commentary(data[1].LowerBand[1]:+text(data[1].LowerBand[1]));
Commentary(data[1].open:+text(data[1].open));
If(data[1].MarketPosition == 1 And data[0].Low <= data[0].Lowerband[1] and data[1].low <= data[1].Lowerband[1] and data[1].BarsSinceEntry >0 And Vol > 0)
{
data[1].Sell(0,Min(data[1].Open,Min(data[0].Lowerband[1],data[1].Lowerband[1])));
}
}
}
大概知道你什么意思了。
如果你的条件取决于其他合约,那么还是像之前说的,你必须精准计算出所有条件的满足价格,然后再对这些价格取最大值或者最小值。这个价格指的是你出信号的合约的价格。
如果你用000的数据来指导2408合约的信号,那么你就必须计算出历史上在满足000条件时,2408的数据。
然而现在对于大部分平台来说,这个是做不到的。k线图这种数据结构是一种有损失的截面数据存放方式,本身就是只能代表某一时刻的状态。如果你想完全重演实盘时的状态,你可以直接订阅tick截面。
总体来说,我认为现在的跨周期机制没问题,应该还有改进空间,比如我建议的,针对颗粒度对大周期数据的回溯,即除了现在的截面数据,再增加序列数据。
如果只有截面数据,这也是之前其他用户提出的容易出现未来数据的原因,要么不用,用的话,不做一定处理就很容易引用未来数据,而且极难避免
SetBasePeroid 功能越强,可适用的场景才越多,量化平台是工具,各种思路和策略的试验尝试,希望平台功能越完善越强大,才能验证出更多的可能。
因为tb2的跨周期功能不好用,在tb3上试验setbaseperiod,结果也不尽如人意,最终我确定的方式还是在tb2中叠加 1分钟图层配合大周期,希望tb3能继续改进和提升
问题我已经解决了,贴出来做个参考和借鉴吧,通过array保存每个颗粒度的大周期数据,然后删除可能包含的未来数据,相当于用上一个5m bar的大周期数据,而不是当前bar的大周期颗粒数据,成交时间推到了10:45,这个问题,希望官方仔细再复核一下机制,也不止我一人提出这个问题,虽然可以解决,但这样实现代码很不优雅。
再次感谢 kyover的热心解答,提供的setBasePeriod运行原理确实帮了大忙。
Params
Numeric Length1(10);
Vars
Series<Numeric> Upperband;
Series<Numeric> Lowerband;
Natural array<Numeric> HighA;
Natural array<Numeric> LowA;
Natural Numeric HighV;
Natural Numeric LowV;
Events
OnInit()
{
SubscribeBar(Symbol,5m,data[0].BeginDateTime,data[0].EndDateTime);
data[0].SetBasePeriod(1m);
}
OnBar(ArrayRef<Integer> indexs)
{
Integer i;
Range[i = 0:DataCount-1]
{
ArrayPushBack(HighA,data[0].High) ; //按颗粒度保存大周期数据
ArrayPushBack(LowA,data[0].Low) ;//按颗粒度保存大周期数据
if(GetArraySize(HighA)>10)
{
ArrayErase(HighA, 0,GetArraySize(HighA)-10); //数组只保留最新的10个
}
Array<Numeric> HighB;
ArrayCopy(HighA,HighB);
ArrayErase(HighB,5,5); //复制出来的数组删除最新的5个(根据小周期的5m和颗粒度1m决定,这其中有可能含有未来数据),确保只引用当前小周期bar之前的大周期数据
if(GetArraySize(LowA)>10)
{
ArrayErase(LowA, 0,GetArraySize(LowA)-10);
ArrayErase(LowA,5,5);
}
Array<Numeric> LowB;
ArrayCopy(LowA,LowB);
ArrayErase(LowB,5,5);
Integer pos;
HighV=Na1Max(HighB,pos); //找出数组中的最大值(即大周期中,上一个5m周期中的最大值),最终需要的结果数据。
LowV=Na1Min(LowB,pos);//找出数组中的最小值(即大周期中,上一个5m周期中的最小值),最终需要的结果数据。
print(bartime:+DateTimeToString(d+t)+ symbol:+symbol+ data[0].High:+text(data[0].High) +HighV:+text(HighV)+ period:+Frequency);
Upperband = Highest(averagefc(High,2), Length1);
Lowerband = lowest(averagefc(Low,2),Length1);
PlotNumeric(Upperband,Upperband[1]);
PlotNumeric(Lowerband,Lowerband[1]);
}
Range[i=1:DataCount-1]
{
//系统入场(买开),用上述结果数据,即highv和lowv和大周期上轨做比较,以实现大小周期同时符合条件,又规避未来数据。
If(data[1].Marketposition == 0 And HighV >= data[0].Upperband[1] and data[1].high>=data[1].Upperband[1] And Vol > 0)
{
data[1].Buy(1, max(data[1].Open,data[1].Upperband[1]));
print(买入 data[1].open:+text(data[1].Open)+data[1].Upperband[1]+text(data[1].Upperband[1])+ data[1].close:+text(data[1].close) );
}
// 系统出场(卖平)
If(data[1].MarketPosition == 1 And LowV<= data[0].Lowerband[1] and data[1].low <= data[1].Lowerband[1] and data[1].BarsSinceEntry >0 And Vol > 0)
{
data[1].Sell(0,min(data[1].Open,data[1].Lowerband));
}
}
}
提醒一下,你用全局数组来记录bar数据的方式,如果是实盘,可能就无效了
当信号价格的计算里加上对两个图层的上下轨大小判断以后,信号价格就变得正确了。
感谢你的回复,但事实是我前面也说了,同合约没问题,可以通过价格比较,我也说了可以直接用大周期数据来给出成交价格,不同合约呢,怎么办,价格没有相关性,各自的upperband都依赖各自的K线数据,比如888和对应主力,取两个价格的最大值还有用吗?
实际情况逐一讨论,你当前的问题是由于你的策略逻辑有错导致的,如果你觉得不同合约会存在问题,请提交一份代码和对应的样本环境,指出其中的问题,只有拿出实例分析后才能说明问题
你的问题大概看了一下,跟setbaseperiod其实没什么关系,是你自己策略的逻辑有问题
你的开仓条件等效于最新加个突破data0.upperband[1],也要突破data1.upperband[1]。
问题是,你这两个图层上的upperband,是不一样的,你没有发现吗?
那你现在就面临一个问题,比如这两个upperband一个是100,一个是110,如果要满足同时突破他们俩,那意味着你的信号价格一定是他们两者中较大的那个。
而你代码中的信号价格,却只取了data1.upperband[1],为什么就不考虑data0.upperband[1]更高的情况?
实际上你这种逻辑错误不是跨周期特有的。就算单周期的策略,如果存在多个信号条件,比如突破前期高点,并且金叉。由于突破前提高点和金叉没有必然的先后关系,那么就必须计算出高点和金叉时的临界价格,比较哪个高,再用高的作为信号价格,这才是正确的计算方式。
提一个建议,既然以颗粒度运行onbar,可否增加以颗粒度回溯大周期数据的功能,那么就可以使用回溯的大周期数据(不是大周期的整根bar回溯),以确保不出现未来数据。比如上图print出来的结果中,通过增加的功能,我可以把data[0]的high数据回溯5个bar,那么小周期使用的就一定是当前bar以前的大周期数据,而不会取到bar区间的大周期数据。
你的意思是不是
如果data0,是30分钟,然后设置setbase为1m分钟,那么一根30分钟周期的bar,会截断为以1m为长度的30份执行30遍onbar域的代码。而你希望能获取这30次onbar执行时,每一次的开高低收。
比如运行到第5份时,通过一些操作,获取第四份时的开高低收等序列数据,是这个意思吗?
是的,我想要的功能就是这样的,能够对这些序列数据回溯,这样就能自己控制取哪个细分时间的大周期数据,因为对于大周期,这整个30M,如果不做处理,那数据就是不变的,如果要做处理,那么处理越细越好,跨周期功能就可以更强大。目前按颗粒度运行,即只有截面,没有序列,不得不说是遗憾。叠加1分钟图层去也能实现这点, 但太麻烦了,setbaseperiod的初衷应该就是细化大周期数据处理吧。
1m的颗粒度,然后每分钟运行onbar,确保大周期的数据获取,问题是在小周期上,却是以5m做为整体时间处理,那么1m的颗粒度影响不到到5m的小周期,无论这个5m周期中在哪一分钟出信号,统一以周期开始时间处理,这个功能使用起来,确实很容易出现未来数据啊。
回测基本要求就是要在 bar开始时,使用这个bar之前的数据,如果使用了bar区间的数据,闪烁,偷价等等都来了,问题是我不知道在这种跨周期的数据引用中,如何避免这种情况的发生
你这个问题跟回测的颗粒度问题无关,这个机制是没问题的,是你自己的策略逻辑有问题
print出相关数据,我觉得运行机制还是有问题,按照1m的颗粒度,确实每分钟运行了onbar,并且在小周期10:40这个5m bar上运行了5次,在第5次获取到了大周期的high数据为7723,达到判断条件成交,小周期bar时间却是在10:40,取小周期的open价格在10:40成交,那么回测结果就不准确,因为实盘环境中出信号时,已经买不到open的价格,那么只应该当前bar出信号,到下一个bar成交才合理,不知道我说的是否正确,以及我要怎么调整代码,避免出现这种情况。
我可以使用data[0]大周期的high或upperband来作买入价格,但如何不是同一个合约呢,这个示例中,是同一个合约,处理起来没问题,但不同合约就没办法了,所以在跨周期处理上,除了回放,还有没有更好的方式。
5m,1m都不影响结果,都是在10:40成交,如果设置成30m,相当于不启用 SetBasePeriod,则是在10:30成交,目前设过多种设置均无法在10:45成交
我想到的一种解决办法是再叠加一个1分钟的图层,然后在这个图层中,引用30分钟的Upperband,并回溯一个bar,用data[1m].high[1]和data[30m].upperband[1]做判断,不知道可不可行
先提醒一下,你的信号既然是设置在data1上的,那你data0无论怎么设置setbaseperiod都是不影响信号的
我是要用大周期增加条件判断,用小周期成交啊,如果在大周期出信号,没有意义啊。或者针对这种情况,有建议的处理办法吗?
我其实没太看明白你说的东西,以现在的这种描述方式,不太能搞清楚你的问题出在哪里,我只能解释以下setbaseperiod的工作原理。
如果你按1m去设置baseperiod 那就等于用1分钟的颗粒度去跑图上的数据。
如果你是5分钟图层,那么历史一根k线会分别跑5次,分别以5分钟bar走到第一分钟、、第二分钟、第三分钟、第四分钟和第五分钟时的五种状态,注意,是5分钟bar,在这五个时间点的形态数据,去跑onbar里的代码。
我感觉你说的情况应该是不会发生的,我不知道你是怎么得出这个结论的。
如果你想进一步说明问题,请提交一份可复现问题的代码并提供配套的样本环境设置,方便我复现问题并进行诊断分析。
样本ag2408,主数据源30m,2024-6-28 10:30-11:00这个bar,我的问题是应该在10:45成交,而不是10:40成交,10:40就已经预知了10:44大周期突破,以下是对应K线和代码。
Params
Numeric Length1(10);
Vars
Series<Numeric> Upperband;
Series<Numeric> Lowerband;
Events
OnInit()
{
SubscribeBar(Symbol,5m,data[0].BeginDateTime,data[0].EndDateTime);
data[0].SetBasePeriod(1m);
}
OnBar(ArrayRef<Integer> indexs)
{
Integer i;
Range[i = 0:DataCount-1]
{
Upperband = Highest(averagefc(High,2), Length1);
Lowerband = lowest(averagefc(Low,2),Length1);
PlotNumeric(Upperband,Upperband[1]);
PlotNumeric(Lowerband,Lowerband[1]);
}
Range[i=1:DataCount-1]
{
//系统入场(买开)
If(data[1].Marketposition == 0 And data[0].High >= data[0].Upperband[1] and data[1].high>=data[1].Upperband[1] And Vol > 0)
{
data[1].Buy(1, max(data[1].Open,data[1].Upperband[1]));
}
// 系统出场(卖平)
If(data[1].MarketPosition == 1 And data[0].Low <= data[0].Lowerband[1] and data[1].low <= data[1].Lowerband[1] and data[1].BarsSinceEntry >0 And Vol > 0)
{
data[1].Sell(0,min(data[1].Open,data[1].Lowerband[1]));
}
}
}
根据你描述的setbaseperiod原理,我会试着print信息来验证一下运行机制,看看问题在哪,谢谢