3.5 交易成本
没有考虑交易成本的回测业绩是不真实的。第2 章谈到了佣金、流动性成本、机会成本、市场冲击及滑价等交易成本,并且举了几个如何在策略回测中处理交易成本的例子。一项高夏普比率的策略,在考虑交易成本后变得无利可图,这是完全可能的,见例3. 7 。
例3.7 交易成本与均值回归模型
麻省理工学院的Amir Khandani 和Andrew Lo 提出了一个简单的均值回归模型〈可参见web. mit. edu/alo/www/Papers/august07. pdf}。策略非常简单z 买入前一交易日日收益最差的股票,卖空前一交易日日收益最好的股暴。在不考虑交易成本的情况下, 1995 年以来,这一简单策咯的业绩一直非常出色( 2006 年的夏普比卒为4. 47 )。我们要研究的是,假定每笔交易成本为5 个基点时,策咯业绩如何变化。〈此处的一笔交易是指一笔买入或一笔卖出,而不是一笔来回交易。〉这个例子不仅向我们展示了交易成本对业绩的影响,也展示了交易多只股票时MATLAB 在模型回测中的强大功能。用Excel 对模型在若干年中的交易进行回测需要设置一大堆标签,十分不便。但使用MATLAB 也会遇到一个问题,即如何在几百个数据标签中检索历史数据,尤其是元存活偏差的数据。此处暂不考虑存活偏差的问题,因为获取无偏差数据十分昂贵,我们只要记得,所得的预测业绩相对实际业绩被高估。
对股票选择策略进行回测时,遇到的第一个问题永远是z 哪些股票可用于回测?标准普尔500 指数成分股通常是首选,因为它的流动性是最好的。标准普尔500 指数的当前成分股可从标准普尔网站下载( www.standardandpoors. com)。由于成分股一直在变化,你下载的名单可能会与我的不同。你可在epchan. com/book/SP500 20071121. xis 找到我的名单。下载所有这些股票的历史数据,最简便的方法是购买一份HQuotePro 的拷贝(可在HQuote. com 下载)。这个软件可以使你很方便地剪切粘贴所需数据的所有标签列。使用软件检索和支新2001 年1 月1 日至今的所有数据,只选择日期、开盘价、最高价、最低价、收盘价和成交量,不含数据名称栏,保存为文件“Export. txt”。通过以下MATLAB 程序将其处理为运算时可使用的数据,在本地目录下保存为二进制文件“SPX_20071123. mat”。
clear;
inputFile=’Export. txt' ; ,
outputFile= 'SPX_2007 l l 23
[mysym, mytday, myop, my hi, my lo, mycl, myvol] = ..?
textread ( inputFile,’%s %u %f %f %f %f %u’,’delimiter’,’,’>;
%找到不重复的股票集合stocks=unique(mysym);
%找到不重复的日期集合
tday= unique( mytday);
op=NaNOength(tday), length(stocks> g
hi= NaN (length ( tday) , length (stocks)) ;
lo= NaN (length ( tday) , length (stocks> s
cl=NaNOength(tday), length(stocks));
vol=NaNOength(tday), length(stocks> g
for s= 1: length(stocks)stk=stocks{s};
%找到当前股票的下标
idxA=strmatch(stk, mysym,’exact’>;
%找到当前日期集的下标[foo,
idxtA, idxtB]=intersect(mytday(idxA), tday);
%获取当前股票的价格
op(idxtB, s) =myop(idxA(idxtA> g
hi(idxtB, s) = myhi(idxA(idxtA> 3
lo( idxtB, s) = mylo(idxA (idxtA> g
cl(idxtB, s) = mycl(idxA(idxtA));
vol(idxtB, s) = myvol(idxA(idxtA> z
end
save(outputFile ,’ tday’,’ stocks,,’。p’,’ hi’,’ lo’,’ cl’,' vol');
接下来,用这些历史数据来回测不考虑交易成本的均值回归策略z
clear;
startDate= 20060101;
endDate=20061231;
load('SPX 20071123’,’tday', 'stocks', 'cl') ;
%日收益率dailyret= (cl- lagl (cl>. /lagHcD;
%等权重的市场指数收益率
marketDailyret= smartmean(dailyret, 2);
%股票的权重与其离市场收益率的距离的负数成正比
weights= ???
一( dailyret- rep mat ( marketDailyret, [ 1 size( dailyret, 2) J>./
repmat(smartsum(isfinite(cl), 2), [1 size(dailyret, 2) ]) ;
%则除没有有效价格或日收益率的股票
weights(~isfinite(cl) I ~ isfiniteOagl (cl>)=O;
dailypnl =smarts um ( lagl (weights ).骨dailyret, 2);
%删除考虑期外的盈亏
dailypnl( tday < startDate I tday > endDate) = [];
%夏普比率应该是0.25
sharpe= sqrt(252 )铃smartmean( dailypnl, 1) I smartstd ( dailypnl, 1)
这个程序文件包括在epchan. com/book/example3_7. mo 注意, 2006年的夏普比率只有0.25 ,而非原作者所报告的4.47 。业绩的显著降低,是因为本例的回测使用的是标准普尔500 指数所有成分股。如采读了作者的文章,你就会发现绝大部分收益来自小盘股和微盘股。
在上面的MATLAB 程序中,我使用了三个新函数“ smarts um ”、“ smartmean”和“ smartstd”,其功能与“ sum”、“ mean”和“ std”三个常用函数的功能相似,但能自动忽略数据中所有NaN 元素。这些函数在进行回测时非常有用,因为股票的价格序列经常是断断续续的。这些文件在epchan. com/book 都能找到。
”Smartsum. m”
function y = smartsum(x, dim)
% y = smartsum(x, dim)
%对dim 维求和,忽略NaN
hasDa ta= isfini te ( x) ;
x(~hasData) =O;
y=sum(x,dim);
y(all(~hasData, dim>=NaN;
”smartmean. m
function y = smartmean 仗, dim)
% y = smartmean(x, dim)
%对dim 维求均值,忽咯NaN
hasData=isfinite(x);
x(~hasData) = 0;
y=sum(x,dim). /sum(hasData, dim);
y(all(~hasData, dim>=NaN; %若y 的所有元素都是NaN ,则y 取值
为NaN
” smartstd. m”
function y = smartstd(x, dim)
%y = smartstd(x, dim)
%对dim 维求标准差,忽略NaN 和Inf
hasData=isfinite(x);
x(~hasData) = 0;
y=std(x);y(all(~hasData, dim>=NaN;
现在考虑每笔交易成本为5 个基点时的回测。
%减去交易成本后的盈亏
onewaytcost=O. 0005;
%假设交易成本为5 个基点
%剧除考虑期外的权重
weights( tday < startDate I tday > endDate, : ) = [];
%权重变化时产生交易成本
dailypnlminustcost = ..?
dailypnl - smartsum(abs(weights-lagl(weights)), 2 )*.onewaytcost;
%夏普比率应该为一3. 19
sharpeminustcost= sqrt(252 )*
smartmean( dailypnlminustcost, 1) I ...smartstd(dailypnlminustcost, 1)
现在,这一策略的业绩非常差!