A:在回测时有时会遇到需要处理调仓数据量特别大的情况,比如对上千个代码几年时间中每日做大量的调仓操作,
会导致产生的缓存数据特别大甚至超过阈值(16G)导致运行程序报错,参考:FAQ:
Q:运行程序报错:instruction:usercall: memory overflow
针对这样的情况,可以对数据进行分段回测清算,把每段回测数据导出到本地后释放缓存,最后统一计算收益。
为保证清算过程的连续性,实现中需要
注意以下事项:
1.每段回测开始时需要把上一段最后一日的持仓明细作为初始持仓,可赋值给成员变量FInitPosition;
2.每段回测开始时需要把上一段最后一日的可用资金作为初始资金,赋值给成员变量FIniCash。
实现范例:
回测数据:日期区间20181001-20240930对沪深300指数成份股进行等权重配比回测
回测模型:
附件:TSFL_TSBackTesting_ManyPosition.fun
Function TSFL_TSBackTesting_ManyPosition(begt,endt,index,IniCash,FeeRate,holddata);
Begin
obj := createobject('PercentPortfolio');
//********************回测基本设置***************************//
//回测开始时间
obj.FBegT:=begt;
//回测截止时间
obj.FEndT:=endt;
//调仓周期(以日线为例)
obj.FCycle:=cy_day();
//组合类别(比例类组合)
obj.FGroupType:=1;
//基准代码
obj.FIndexId:=Index;
//初始资金
obj.FIniCash:=IniCash;
//资金配比方式
obj.FRateType:=-1;
//成交价类别
obj.FPriceType:=2;
//成交量取整模式
obj.FVolModType:=0;
//费率
obj.FFeeRate:=FeeRate;
//初始持仓
obj.FInitPosition:=holddata;
//回测
obj.BackTest();
//获取返回结果(返回结果可根据需要选择)
return array(
//---组合基础
"交易明细":obj.GetTradeData(BegT,EndT),
"资产配置":obj.GetAssetData(BegT,EndT),
"持仓明细":obj.GetHoldData(BegT,EndT),
//---组合盈亏、交易
"组合盈亏":obj.GetGainandLoss(BegT,EndT),
"交易汇总":obj.GetTradingAmount(BegT,EndT),
"组合盈亏(按证券)":obj.GetGainandLossBySecurity(BegT,EndT),
"交易汇总(按证券)":obj.GetTradingAmountBySecurity(BegT,EndT),
//---组合收益
"区间组合收益率": obj.GetPortfolioReturn(BegT,EndT),
"组合和基准收益率序列":obj.GetPortfolioReturn2(BegT,EndT),
"阶段收益":obj.GetTrailingReturn(EndT),
"滚动收益":obj.GetRollingReturn(BegT,EndT,cy_month()),
//----组合评价
"风险回报":obj.GetReturnandRisk(BegT,EndT),
"相对回报":obj.GetRelativePerformance(BegT,EndT),
);
End;
//新建类PercentPortfolio继承回测基类TSBackTesting
Type PercentPortfolio=class(TSBackTesting)
FFeeRate; //费率
//重写GetTradeOrder,自定义配比
function GetTradeOrder(vEndT);override;
begin
echo datetostr(vendt);
stocks:=GetBKByDate(FIndexId,vEndT);
bl:=100/length(stocks);
t:=array();
for i,stock in stocks do
begin
t[i,"截止日"]:=vEndT;
t[i,"代码"]:=stock;
t[i,"方向"]:=1;
t[i,"比例(%)"]:=bl;
t[i,"开仓费率(%)"]:=FFeeRate;
t[i,"平仓费率(%)"]:=FFeeRate;
end
return t;
end
End;
对照组:直接对样例数据做整体回测
实现代码:
begt:=20181001t;
endt:=20240930t;
index:="SH000300";
IniCash:=10^6;
FeeRate:=0.05;
holddata:=array();
result:=TSFL_TSBackTesting_ManyPosition(begt,endt,Index,IniCash,FeeRate,holddata);
return result;
执行结果:
结果说明:
1.由于进行了大量的调仓操作,执行过程产生的缓存数据已经非常大了;
2.区间组合收益率:37.45
3.区间交易明细434483条
4.区间交易明细436500条
实验组:对样例数据经行分段清算,每段数据导出到本地后释放缓存,最后计算组合收益
实现代码:
index:="SH000300";
IniCash:=10^6;
FeeRate:=0.05;
holddata:=array();
//对回测区间进行分段
timearr:=array(("开始日":20181001t,"截止日":20200930t),
("开始日":20201001t,"截止日":20220930t),
("开始日":20221001t,"截止日":20240930t));
path:="C:\\Users\\86152\\Desktop\\TSDN\\data\\TSBackTesting\\大量调仓";
rt:=array();
for i in timearr do
begin
begt:=timearr[i,"开始日"];
endt:=timearr[i,"截止日"];
r:=TSFL_TSBackTesting_ManyPosition(begt,endt,Index,IniCash,FeeRate,holddata);
//导出每段回测数据到本地
rdo2 ExportFile(ftStream(),"",path$datetoint(begt)$"-"$datetoint(endt)$".stm",r);
zcpz:=r["资产配置"];
Iniday:=MaxValue(zcpz[:,"截止日"]);
//设置下段回测的初始资金
IniCash:=vselect ["可用资金"] from zcpz where ["截止日"]=iniday end;
//设置下段回测的初始持仓
holddata:=select * from r["持仓明细"] where ["截止日"]=iniday end;
rt[i,"收益率(%)"]:=r["区间组合收益率"];
//释放内存
r:=array();
end
//返回组合收益率
return pf_CumulativeReturn(rt,"收益率(%)");
//执行结果:37.45
本地结果:
结果说明:
1.组合收益率与对照组一致:37.45
2.交易明细数据量与对照组一致:146514+144145+143824=434483
3.持仓明细数据量与对照组一致:146100+145500+144900=436500
4.进行分段操作中间释放内存可避免单次执行内存溢出的情况