FAQ > 金融建模 > 建模问题 > 回测框架相关问题

Q:回测过程中需要调仓数据量特别大时如何处理?    

  • 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.进行分段操作中间释放内存可避免单次执行内存溢出的情况