A:目前天软回测框架已支持用户通过传入外部证券及对应区间每个交易日价格的数据进行回测。
用户需要提前准备好组合数据,数据包括外部证券编码(区别于天软代码),截止日(每个交易日),价格等。
其实现步骤如下:
第一步:搭建回测实例,比如实现每月再平衡逻辑,则建议使用比例类组合,通过设置调仓周期来控制再平衡频率
第二步:在回测开始前,导入外部数据,并标准化数据,即需包括
1、【截止日】字段,类型为天软日期类型
2、【代码】字段,证券编码,由外部给数据的券,需要用户自己编制证券编码,要与天软已有代码进行区别,比如"DIY001"
3、【价格】字段,用于当日清算,一般如收盘价
4、其它字段,比如用户需要指定成交价时,也需要通过该数据输入,若使用收盘价成交,则可直接通过上述的【价格】字段,无须另外再加。
数据demo如:
附件:外部价格数据test.xlsx
第三步:私有化【个券获取价格的配置:TPC_func_UserSet】 后重写
该函数私有化后函数名依旧是TPC_func_UserSet(如果提示则选择是,进行覆盖),
需要实现,将所有外部自定义个券编码及获取指定日价格的模型进行配置,方便框架调用,如:
Function TPC_func_UserSet();
Begin
{说明:用户重写个券获取价格的配置
使用说明:
1:字段
StockID :证券代码
UserFuncname :用户价格模型名称
BegTDate :价格使用开始日
EndTDate :价格使用截止日
注意:若无个券需要配置,请返回空数组!!
}
return array(('StockID':'DIY001','BegTDate':20010101T,'EndTDate':20990101T,'UserFuncname':'TSP_SetCashPrice_FAQ'),
('StockID':'DIY002','BegTDate':20010101T,'EndTDate':20990101T,'UserFuncname':'TSP_SetCashPrice_FAQ'),
);
//注:UserFuncname字段中存放获取该券在指定日价格的模型,该价格用于每日清算
End;
其中,DIY001与DIY002是需要通过外部数据传入的个券,而对应的TSP_SetCashPrice_FAQ则是获取该券价格的模型。
第四步:私有化【价格获取逻辑:TSP_SetCashPrice】后重命名(比如TSP_SetCashPrice_FAQ)并进行实现价格的获取
该函数命名需要与第三步中配置的UserFuncname对应,这里对应函数名是哪个,上面对应券的UserFuncname就配置成哪个。
如命名为TSP_SetCashPrice_FAQ,且该函数有三个参数:StockID,EndT,ISJS
功能需要实现:
1、指定券在指定日的价格;
2、当指定日该券数据不存在时,也需要返回值(如下面示例使用最近价格替代的方式)
实现示例如下:
Function TSP_SetCashPrice_FAQ(StockID,EndT,ISJS);
Begin
UData:=getsysparam("TSP_SetCashPrice_FAQ");
if ifnil(UData) then raise "没有传入系统参数TSP_SetCashPrice_FAQ";
v:= vselect ["价格"] from UData where ["截止日"]=Endt and ["代码"]=StockID end;
if not ifnil(v) then return v; //如果指定日有价格,则返回
else //指定日无价格,则取最近一个日期的
begin
tt:=select * from UData where ["代码"]=StockID and ["截止日"]<=Endt order by ["截止日"] desc end;
if istable(tt) then return tt[0,"价格"];
else return 0; //没找到价格数据,返回0
end
End;
其中:示例中UData:=getsysparam("TSP_SetCashPrice_FAQ");表示价格数据需要通过系统参数传入。
用户也可以改用其它方式输入,函数参数是固定的,用户不能变动。
第五步:重写回测实例的GetTradeOrder方法,实现再平衡逻辑
在该方法中,实现返回每次调仓后的组合个股配置比例。
必须返回字段:截止日","代码","比例(%)"
具体可参考下面回测完整实例:
Function FAQ_UserDataDoBackTest();
Begin
LJ:="E:\\xxx\\外部价格数据test.xlsx";//外部数据存放路径
r:=Rdo2 Importfile(ftXLS(),"",LJ,UData);
// return UData;//若导入后数据类型不符合要求,建议在这里进行标准化处理
//----将数据存到系统参数中,方便获取指定券在指定日价格
Setsysparam("TSP_SetCashPrice_FAQ",UData);//传入第四步中需要的数据
//--------------------回测--------------------------------
begt:=20250101T;
endt:=20251231T;
obj := createobject('PercentPortfolio');
//********************回测基本设置***************************//
//回测开始时间
obj.FBegT:=begt;
//回测截止时间
obj.FEndT:=endt;
//调仓周期(以月线为例)
obj.FCycle:=cy_month(); //------每月进行调仓-资产再平衡
//组合类别(比例类组合)
obj.FGroupType:=1;
//基准代码
obj.FIndexId:="SH000300";
//初始资金
obj.FIniCash:=10000000;
//资金配比方式
obj.FRateType:=-1;//---当指定券是用户自己输入时,需要用户自己输出比例(%)列
//成交价类别
obj.FPriceType:=2;
//成交量取整模式
obj.FVolModType:=-1;
//是否分红再投资
obj.FDividendType:=0;
//是否参与配股
obj.FAllotmentType:=0;
//********************用户自定义参数***************************//
obj.Ft:=UData; //传入用户组合数据
//回测
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)
Ft;
function GetTradeOrder(vEndT);override;
begin
{必须输出"截止日","代码","比例(%)"字段}
t:= select ["截止日"],["代码"],1 as "方向",0.1 as "开仓费率(%)",0.1 as "平仓费率(%)" from Ft where ["截止日"]=vEndt end;
//每个调仓日进行等权重配比,实现再平衡
t:=select *,1/length(t) *100 as "比例(%)" from t end;
return t;
end
End;
示例结果展示:
