A:在实盘操作中,在当前时间点进行选股模型的运算后,并不能立刻以当时的价格达成交易,所以,在高频回测中,有些用户也希望以下一个时点的价格来模拟实盘交易的情况,达到更接近实盘操作的目的。
那么,在回测框架中,如何实现呢?
此时,我们可以利用数量类回测中自定义成交价(即FPriceType设置为-1)的方式来达到目的。
具体步聚为:
第一步:设置成员变量,FPriceType:=-1;将成交价类型设置为自定义。
第二步:在重写gettradeorder方法中,成交价通过秒线获取下一秒的价格,比如:
r[i,'成交价']:=specall(ref(close(),-1),
array(pn_cycle():cy_1s(),pn_date():vEndt,pn_stock():stockid,pn_rate():0));
注:比例类回测中,无法自定义成交价,所以做不了这个实现。
实现范例:
将天软范例回测框架-高频回测:TSFL_TSBackTesting_HighFrequencyTrading变更为开仓与平仓成交价为证券的下一条交易明细的价格,具体实现如下(更改内容,注意标红的地方):
Function TSFL_TSBackTesting_HighFrequencyTrading_Nextclose(Stocks,BegT,EndT,Cycle,MaxN,MaxGR,MaxLR,FeeRate);
Begin
obj := createobject('HighFrequencyTrading');
//********************回测基本设置***************************//
//回测开始时间
obj.FBegT:=BegT;
//回测截止时间
obj.FEndT:=EndT;
//回测周期
obj.FCycle:=Cycle;
//组合类型(数量类组合)
obj.FGroupType:=2;
//成交价类别(用户自定义)
obj.FPriceType:=-1;
//数量类组合开仓数量类别(此处采用固定金额法)
obj.FOpenVolType:=2;
//数量类组合平仓数量类别(此处采用可平仓数量占比法)
obj.FCloseVolType:=2;
//日内止盈止损
obj.FGLType:=1;
//止盈线
obj.FMaxGainRatio:=MaxGR;
//止损线
obj.FMaxLossRatio:=MaxLR;
//费率(%)
obj.FFeeRate:=FeeRate;
//********************用户自定义参数***************************//
//此处的股票池以数组的方式提供,实际可修改为按照选股方法获取
obj.FStockArr := str2array(Stocks);
//最大持有数量
obj.FMaxN := MaxN;
//回测
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;
Type HighFrequencyTrading=class(TSBackTesting)
FStockArr; //备选股票池
FMaxN; //最大持有股票数
FFeeRate; //费率(%)
function GetTradeOrder(vEndT);override;
begin
oV := BackupSystemParameters2();
//当前时间
d := vEndT;
echo datetimetostr(d);
cc := GetHoldData(); //获取当前持仓
cash := GetSurplusFund(); //获取剩余资金
ccstocks := sselect distinct(['代码']) from cc end; //获取持仓中的股票代码
tjy := array();
n:=0;
if istable(ccstocks) then
ccnum := length(ccstocks)
else
ccnum := 0;
newccnum := ccnum;
if newccnum<FMaxN then //当前持仓股票个数小于最大持仓数
begin
setsysparam(pn_cycle(),FCycle);
//判断开仓
for nI:=0 to length(FStockArr)-1 do
begin
stockid := FStockArr[nI];
setsysparam(pn_stock(),stockid);
setsysparam(pn_date(),d);
if not istradeday(d) then continue;
if not (stockid in ccstocks) then //如果持仓中不存在才判断是否满足开仓
begin
setsysparam(pn_rate(),1);
if cross(ma(close(),10),ma(close(),20))=1 then
begin
tjy[n]['截止日'] := d;
tjy[n]['代码'] := stockid;
tjy[n]['名称'] := stockname(stockid);
tjy[n]['方向'] := 1;
tjy[n]['动作'] := 0;
tjy[n]['成交价']:=specall(ref(close(),-1),
array(pn_cycle():cy_1s(),pn_date():d,pn_stock():stockid,pn_rate():0));
//与FOpenVolType关联,为2时,提供金额即可,此处为剩余资金/剩余个数,由基类计算成交量
tjy[n]['资金'] := cash/(FMaxN-ccnum);
n++;
newccnum++;
end
end
if newccnum>=FMaxN then //当持仓的证券个数大于最大持仓个数就终止开仓
break;
end
end;
//平仓,对持仓循环判断是否达到平仓条件
for nJ:=0 to length(cc)-1 do
begin
stockid := cc[nJ]['代码'];
setsysparam(pn_stock(),stockid);
setsysparam(pn_date(),d);
setsysparam(pn_rate(),1);
if cross(ma(close(),20),ma(close(),10))=1 then
begin
tjy[n]['截止日'] := d;
tjy[n]['代码'] := stockid;
tjy[n]['名称'] := stockname(stockid);
tjy[n]['方向'] := 1;
tjy[n]['动作'] := 1;
tjy[n]['成交价']:=specall(ref(close(),-1),
array(pn_cycle():cy_1s(),pn_date():d,pn_stock():stockid,pn_rate():0));
//FCloseVolType为2时,提供平仓比例即可,此处平仓数量占比(%)即全部平仓。也可将FCloseVolType设置为1,此处直接提供成交量
tjy[n]['平仓数量占比(%)']:=100;
n++;
end
end;
//添加费率(%)字段
update tjy set ['费率(%)']=FFeeRate end;
return tjy;
end
End;
成交价验证:更改后执行范例结果:
验证其中交易明细:
买入:
SZ000009在2020-09-15 11:30:00后的价格为:
对比结果:与调仓时间的下一条交易明细价格一致。
卖出:
SZ000009在2020-09-23 14:00:00后的价格为:
对比结果:与调仓时间的下一条交易明细价格一致。