楼主: fantuanxiaot
26436 129

[学习资源] [转载]基于Matlab和Wind SQL数据库的通用选股策略回测程序   [推广有奖]

已卖:1597份资源

大师

9%

还不是VIP/贵宾

-

威望
7
论坛币
-234454 个
通用积分
225.8477
学术水平
3783 点
热心指数
3819 点
信用等级
3454 点
经验
150360 点
帖子
7597
精华
32
在线时间
1329 小时
注册时间
2013-2-4
最后登录
2025-3-23

初级学术勋章 初级热心勋章 中级热心勋章 中级学术勋章 初级信用勋章 中级信用勋章 高级热心勋章 高级学术勋章 特级学术勋章 特级热心勋章 高级信用勋章 特级信用勋章

楼主
fantuanxiaot 发表于 2015-2-3 20:41:54 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
代码如下:

本帖隐藏的内容

  1. function [y,varargout]=backtestcomplex(x,varargin)
  2. % Created by mjf(Tel: 13718883838) on 2012-07-15
  3. % latest justified by mjf on 2012-09-20
  4. % 输入x是一个excel文件的地址字符串,如‘E:\Top50.xlsx’, excel文件的第一行为表头,包含4列:股票交易代码(SZ000001,SH600001形式),中文简称,选股日期

  5. %(yyyy-mm-dd形式),权重因子
  6. % 输出y是一个矩阵,有portfolioNum+3列,第一列对应交易日期,2:end-2列对应该交易日期组合的复权收盘价;如果第一列的值为0,那么该行对应的是组合的权重因子;
  7. %   第end-1列为该交易日相对于上个交易日组合的收益率;第end列为策略的累计收益率
  8. % vargin是一个数字或者日期字符串,设定回测的结束日期,默认回测到昨天。
  9. % varargout{varout_infoRatio}是一个structure,有两个field:daily和monthly,分别是一个两列的矩阵,第一列对应日期,第二列表示从该日期起到回测结束策略的信息比率
  10. % varargout{varout_maxDrawDown}是一个size(y,1)-1行2列的矩阵,分别对应组合和市场指数从y(1:end-1,1)交易日开始,到y(end,1)交易日结束时的最大回撤
  11. % varargout{varout_index}是一个structure,有HS300和SZZZ两个field,对应相应的市场指数
  12. % varargout{varout_matketCap}是一个两列的矩阵,第一列为日期,第二列为该日期组合的流通市值;
  13. % varargout{varout_FF3},对应Fama-French三因子回归超额收益alpha
  14. % varargout{varout_xlsResult}输出xlsResult在y中每个组合前面加上了“中文简称”,“交易代码”,“windID”三行,方便写为excel文件

  15. % raw=x;
  16. [~,~,raw]=xlsread(x);

  17. tradeCost=0;
  18. refIndexName='HS300'; %计算信息比率的benchmark
  19. risklessReturn=1+4e-2/365;
  20. weightingMethod=1;% 0:等权重;1:流通市值加权

  21. varout_infoRatio=3;
  22. varout_maxDrawDown=2;
  23. varout_index=1;
  24. varout_matketCap=4;
  25. varout_FF3=5;
  26. varout_xlsResult=6;
  27. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  28. % 设定回测结束日期datenum给huicheEndDateNum
  29. if nargin==1 || isempty(varargin)
  30.     huicheEndDateNum=today-1; % 设定回测的结束日期,结束日期设为当天之前的1天,就减去1
  31. elseif isnumeric(varargin{1})
  32.     huicheEndDateNum=today-varargin{1};
  33. elseif ischar(varargin{1})&&length(varargin{1})==6
  34.     huicheEndDateNum=monthEndDateNum(datenum([varargin{1},'01'],'yyyymmdd'));
  35. elseif ischar(varargin{1})&&length(varargin{1})==7
  36.     huicheEndDateNum=monthEndDateNum(datenum([varargin{1},'-01'],'yyyy-mm-dd'));
  37. elseif ischar(varargin{1})&&length(varargin{1})==8
  38.     huicheEndDateNum=datenum(varargin{1},'yyyymmdd');
  39. elseif ischar(varargin{1})&&length(varargin{1})==10
  40.     huicheEndDateNum=datenum(varargin{1},'yyyy-mm-dd');
  41. else
  42.     y='Not correct backtest end DATE format.';
  43.     fprintf('%s\n',y)
  44.     return
  45. end
  46. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  47. format longG
  48. if isnan(raw{1,end})
  49.     raw(:,end)=[];
  50.     if size(raw,2)~=4
  51.         fprintf('Data format may be not correct. Verify it.\n');
  52.         return;
  53.     end
  54. end
  55. raw=[raw,[{'wind证券ID'};cell(size(raw,1)-1,1)]];
  56. % raw是一个5列的cell array,分别是:股票交易代码,中文简称,选股日期,权重因子,wind证券ID
  57. tradeIDCol=1;
  58. tradeCodeBeginBit=3;
  59. chineseNameCol=2;
  60. dateCol=3;
  61. weightingFactorCol=4;
  62. windIDCol=5;

  63. conn=database('wind','userName','passWord');
  64. %将raw第3列的日期格式转变为与wind数据库相同的'yyyymmdd'形式,且以str类型存储
  65. tmp=datenum(raw(2:end,dateCol),'yyyy-mm-dd');
  66. raw(2:end,dateCol)=num2cell(datestr(tmp,'yyyymmdd'),2);
  67. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  68. % 找出每只股票的windID,存入raw的第5列,如果一个组合为空仓,即没有购买股票,这个组合的wingID留空
  69. raw(1,:)=[]; % 去掉raw的表头,即第一行
  70. blankPortfolioRowNum=find(strcmp('AA000000',raw(:,tradeIDCol))); % 约定空仓组合所在行的交易代码为‘AA000000’
  71. blankPortfolio=raw(blankPortfolioRowNum,:);
  72. raw(blankPortfolioRowNum,:)=[];
  73. tradeID=cell2mat(raw(:,tradeIDCol));
  74. tradeID=tradeID(:,tradeCodeBeginBit:end);
  75. tradeID=num2cell(tradeID,2);
  76. windIDAll=strconnec(tradeID);
  77. tmp=fetch(conn,['select f2_1090 from tb_object_1090 where f4_1090=''A'' and f16_1090 in (',windIDAll,') order by f16_1090']);
  78. [~,~,seq]=unique(tradeID);
  79. raw(:,windIDCol)=tmp(seq,:);
  80. raw=[blankPortfolio;raw];
  81. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  82. % 找出各个组合在raw中对应的行范围,存储到portfolioRawRowScope,第一列为组合的开始行,第二列为组合的结束行
  83. raw=sortrows(raw,dateCol);
  84. [~,firstRowNum]=unique(raw(:,dateCol),'first');
  85. [~,lastRowNum]=unique(raw(:,dateCol),'last');
  86. portfolioRawRowScope=[firstRowNum,lastRowNum];
  87. totalPortfolioNum=size(portfolioRawRowScope,1);
  88. maxPortfolioCapacity=max(portfolioRawRowScope(:,2)-portfolioRawRowScope(:,1))+1;
  89. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  90. % y现在有maxPortfolioNum+3行,第一行对应交易日期,2:end-2行对应该交易日期组合的收盘价;
  91. % 如果y第一行的值为0,那么该列对应的是组合的权重因子;end-1行为隔日收益率,end行为累计收益率
  92. approxiamteBackTestMonthNum=ceil((huicheEndDateNum-datenum(raw{1,dateCol},'yyyymmdd'))/28);
  93. y=zeros(maxPortfolioCapacity+3,approxiamteBackTestMonthNum*32);
  94. marketCap=zeros(approxiamteBackTestMonthNum*32,2);
  95. xlsResult=cell(maxPortfolioCapacity+3,approxiamteBackTestMonthNum*35);
  96. yCol=0;
  97. marketCapCol=0;
  98. xlsResultCol=0;
  99. for portfolioSN=1:totalPortfolioNum
  100.     %     确定该组合的结束日期
  101.     if datenum(raw{portfolioRawRowScope(portfolioSN,1),dateCol},'yyyymmdd')>=huicheEndDateNum
  102.         break
  103.     elseif portfolioSN<totalPortfolioNum && datenum(raw{portfolioRawRowScope(portfolioSN+1,1),dateCol},'yyyymmdd')<=huicheEndDateNum
  104.         endDateStr=raw{portfolioRawRowScope(portfolioSN+1,1),dateCol};
  105.     elseif portfolioSN<totalPortfolioNum && datenum(raw{portfolioRawRowScope(portfolioSN+1,1),dateCol},'yyyymmdd')>huicheEndDateNum
  106.         endDateStr=datestr(huicheEndDateNum,'yyyymmdd'); % 最新的组合持有到huicheEndDateNum的时间
  107.     elseif portfolioSN==totalPortfolioNum
  108.         endDateStr=datestr(huicheEndDateNum,'yyyymmdd');
  109.     end
  110.     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  111.     %     一个组合是空仓
  112.     if isempty(raw{portfolioRawRowScope(portfolioSN,1),windIDCol})
  113.         tradeDay=fetch(conn,['select f1_1010 from tb_object_1010 where f1_1010 between ''',...
  114.             raw{portfolioRawRowScope(portfolioSN,1),dateCol},''' and ''',endDateStr,''' order by f1_1010']);
  115.         yCol=yCol+1;
  116.         y(1,yCol+1:yCol+length(tradeDay))=str2double(tradeDay)';
  117.         y(end-1,yCol:yCol+length(tradeDay))=1;
  118.         yCol=yCol+length(tradeDay);
  119.         {portfolioSN,size(portfolioRawRowScope,1),raw{portfolioRawRowScope(portfolioSN,1),dateCol},'short'}
  120.         continue;
  121.     end
  122.     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  123.      
  124.     currentPortfolioCapacity=portfolioRawRowScope(portfolioSN,2)-portfolioRawRowScope(portfolioSN,1)+1; %当前组合中的股票数目
  125.     currentPortfolioWindID=raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),windIDCol);
  126.     currentPortfolioWindID=strconnec(currentPortfolioWindID);
  127.     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  128. %     权重因子:weightingMethod==0:等权重;weightingMethod==1:流通市值加权
  129.     if weightingMethod==0
  130.         raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),weightingFactorCol)={1/currentPortfolioCapacity};
  131.     else
  132. %         此处一个隐藏的bug是当一只股票在对应交易日的流通市值没有数据时,会提示错误而中断程序
  133.         mktCap=fetch(conn,['select f1_5004,f10_5004 from tb_object_5004 where f2_5004=''',...
  134.             raw{portfolioRawRowScope(portfolioSN,1),dateCol},''' and f1_5004 in (',currentPortfolioWindID,')']);
  135.         if size(mktCap,1)~=currentPortfolioCapacity
  136.             no_mktCap=setdiff(raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),windIDCol),mktCap(:,1));
  137.             tmp=containers.Map(raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),windIDCol),...
  138.                 raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),tradeIDCol));
  139.             no_mktCapID=values(tmp,no_mktCap);
  140.             fprintf('Can not fetch the Market Capacity for stocks %s on %s\n',strconnec(no_mktCapID),raw{portfolioRawRowScope(portfolioSN,1),dateCol});
  141.             no_mktCap=fetch(conn,['select f1_5004,f10_5004 from tb_object_5004 where f2_5004<=''',...
  142.                 raw{portfolioRawRowScope(portfolioSN,1),dateCol},''' and f1_5004 in (',strconnec(no_mktCap),') order by f2_5004']);
  143.             [~,tmp]=unique(no_mktCap(:,1),'last');
  144.             no_mktCap=no_mktCap(tmp,:);
  145.             mktCap=[mktCap;no_mktCap];
  146.         end
  147.         cap=cell2mat(mktCap(:,2));
  148.         mktCap=containers.Map(mktCap(:,1),cap/sum(cap));
  149.         raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),weightingFactorCol)=...
  150.             values(mktCap,raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),windIDCol));
  151.     end
  152.     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  153.     %     SQLserver的 BETWEEN……AND关键词给出的结果包含上下范围的边界
  154.     closingPrice=fetch(conn,['select f1_1425,f2_1425,f7_1425 from tb_object_1425 where f1_1425 IN (',currentPortfolioWindID,...
  155.         ') AND (f2_1425 BETWEEN ''',raw{portfolioRawRowScope(portfolioSN,1),dateCol},''' AND ''',endDateStr,''')']);
  156.     % closingPrice是cell array,有三列数据:wind证券ID(char类型),交易时间(char类型),复权收盘价(double类型)
  157.      
  158.     % 如果wind数据库中没有一只股票某个交易日的收盘价数据,得到的tmp中也缺乏该股票这个交易日的数据。
  159.     % 修正办法:让该股票这个交易日的收盘价等于最近有数据的交易日的收盘价。
  160.     % 还有一个bug需要修正:有可能一只股票的数据在组合建立的第一天就没有;此bug已修正
  161.     uniqueWindID=sortrows(raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),windIDCol),1);
  162.     tradeSeriesMatrix=col2matrix(closingPrice);
  163.     noDataWindID=uniqueWindID(find(1-ismember(uniqueWindID,tradeSeriesMatrix(2:end,1))));
  164.     if ~isempty(noDataWindID)
  165.         fprintf('Caution: can not fetch closing price data for stock(s) %s from %s to %s. These datas are set to 1.',...
  166.             cell2mat(strcat(strcat(noDataWindID,',')')),raw{portfolioRawRowScope(portfolioSN,1),dateCol},endDateStr);
  167.         missed=[noDataWindID,num2cell(ones(length(noDataWindID),size(tradeSeriesMatrix,2)-1))]; %所有的交易日内都没有数据,收盘价统一设为1,该股票没有盈亏
  168.         tradeSeriesMatrix=[tradeSeriesMatrix(1,:);sortrows([tradeSeriesMatrix(2:end,:);missed],1)];
  169.     end
  170.     for tradeSeriesMatrixRow=2:size(tradeSeriesMatrix,1)
  171.         for tradeSeriesMatrixCol=3:size(tradeSeriesMatrix,2)
  172.             if isnan(tradeSeriesMatrix{tradeSeriesMatrixRow,tradeSeriesMatrixCol})
  173.                 tradeSeriesMatrix{tradeSeriesMatrixRow,tradeSeriesMatrixCol}=tradeSeriesMatrix{tradeSeriesMatrixRow,tradeSeriesMatrixCol-1};
  174.             end
  175.         end
  176.     end
  177.     %     股票在组合建立当日就没有数据,收盘价设为此后最近有数据交易日的收盘价
  178.     for tradeSeriesMatrixRow=2:size(tradeSeriesMatrix,1)
  179.         lastNanCol=find(isnan(cell2mat(tradeSeriesMatrix(tradeSeriesMatrixRow,2:end))),1,'last')+1;
  180.         tradeSeriesMatrix(tradeSeriesMatrixRow,2:lastNanCol)=tradeSeriesMatrix(tradeSeriesMatrixRow,lastNanCol+1);
  181.     end
  182.     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  183.     totalTradeDate=size(tradeSeriesMatrix,2)-1;
  184.     yCol=yCol+1;

  185.   %对raw排序,因为tradeSeriesMatrix对windid做了排序,不然权重因子对不上,add by van 20121227(van.sun.cn@hotmail.com)

  186.     tmpArray=sortrows(raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),:),windIDCol);
  187.     y(2:currentPortfolioCapacity+1,yCol)=cell2mat(tmpArray(:,weightingFactorCol));
  188.    
  189.     y(1,yCol+1:yCol+totalTradeDate)=str2double(tradeSeriesMatrix(1,2:end));
  190.     y(2:currentPortfolioCapacity+1,yCol+1:yCol+totalTradeDate)=cell2mat(tradeSeriesMatrix(2:end,2:end));
  191.     y(end,yCol:yCol+totalTradeDate)=[1,1,y(2:currentPortfolioCapacity+1,yCol)'*...
  192.         (y(2:currentPortfolioCapacity+1,yCol+2:yCol+totalTradeDate)./repmat(y(2:currentPortfolioCapacity+1,yCol+1),1,totalTradeDate-1))];
  193.     y(end-1,yCol:yCol+totalTradeDate)=[1,1,y(end,yCol+2:yCol+totalTradeDate)./y(end,yCol+1:yCol+totalTradeDate-1)];
  194.     y(end-1,yCol+1)=y(end-1,yCol+1)*(1-tradeCost); % 增加买入交易成本
  195.     y(end-1,yCol+totalTradeDate)=y(end-1,yCol+totalTradeDate)*(1-tradeCost);% 增加卖出交易成本
  196.     yCol=yCol+totalTradeDate;
  197.      
  198.     %%%%%%%%%%%%%%%%%%%%% 流通市值
  199.     if nargout>=varout_matketCap+1
  200.         currentMarketCap=marketCapFun(raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),windIDCol),...
  201.             raw{portfolioRawRowScope(portfolioSN,1),dateCol},endDateStr,conn);
  202.         if size(currentMarketCap,1)<size(tradeSeriesMatrix,2)-1
  203.             for i=1:size(currentMarketCap,1)
  204.                 if ~ismember(num2str(currentMarketCap(i,1)),tradeSeriesMatrix(1,2:end))
  205.                     fprintf('Can not fetch market capitalization data on %d for stocks in portfoio %d when caculating market Cap.\n',...
  206.                         currentMarketCap(i,1),portfolioSN);
  207.                 end
  208.             end
  209.         end
  210.         %前一个组合的结束日期和后一个组合的开始日期在同一个交易日,在该日,策略的流通市值取后一个组合的流通市值,因此前一个组合的流通市值只取1:end-1
  211.         i=1-sign(str2double(tradeSeriesMatrix(1,end))-currentMarketCap(end,1));
  212.         marketCap(marketCapCol+1:marketCapCol+size(currentMarketCap,1)-i,:)=currentMarketCap(1:end-i,:);
  213.         marketCapCol=marketCapCol+size(currentMarketCap,1)-i;
  214.     end
  215.     %%%%%%%%%%%%%%%%%%%%% xlsresult
  216.     if nargout>=varout_xlsResult+1
  217.         xlsResult(1,xlsResultCol+1:xlsResultCol+4)={'中文简称','交易代码','windID','权重因子'};
  218.         xlsResult(2:currentPortfolioCapacity+1,xlsResultCol+1:xlsResultCol+4)=...
  219.             sortrows(raw(portfolioRawRowScope(portfolioSN,1):portfolioRawRowScope(portfolioSN,2),[chineseNameCol,tradeIDCol,windIDCol,weightingFactorCol]),3);
  220.         xlsResult(:,xlsResultCol+5:xlsResultCol+4+totalTradeDate)=num2cell(y(:,yCol+1:yCol+totalTradeDate));
  221.         xlsResult(end-1,xlsResultCol+1:xlsResultCol+4+totalTradeDate)=[1,1,1,num2cell(y(end-1,yCol:yCol+totalTradeDate))];
  222.         xlsResultCol=xlsResultCol+4+totalTradeDate;
  223.     end
  224.     [portfolioSN,size(portfolioRawRowScope,1)]
  225. end
  226. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  227. y(:,yCol+1:end)=[];
  228. if isempty(y)
  229.     fprintf('The backtest end-date is prior to the construction-date of the first portfolio.\n');
  230.     return;
  231. end
  232. y(end,:)=cumprod(y(end-1,:));
  233. y=y';
  234. spaceRow=find(y(:,1)==0);
  235. y([1;spaceRow(2:end);spaceRow(2:end)+1],:)=[]; %将权重因子所在行全部去掉
  236. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  237. % 信息比率information ratio
  238. if nargout>=varout_infoRatio+1
  239.     marketIndex=idx(raw{1,dateCol},datestr(huicheEndDateNum,'yyyymmdd'),conn);
  240.     if strcmp(refIndexName,'HS300')
  241.         refIndex=marketIndex.HS300;
  242.     else
  243.         refIndex=marketIndex.SZZZ;
  244.     end
  245.     %     eval(['refIndex=marketIndex.',refIndexName,';']);
  246.     if any(refIndex(:,1)~=y(:,1))
  247.         fprintf('Caution: Index date does not totally match with y date when caculating Information Ratio.\n');
  248.     end
  249.     refIndex=refIndex(:,2);
  250.     indexDailyReturn=[1;refIndex(2:end)./refIndex(1:end-1)];
  251.      
  252.     yMonth=floor(y(:,1)/100);
  253.     [~,firstRowNum]=unique(yMonth,'first');
  254.     [~,lastRowNum]=unique(yMonth,'last');
  255.     yMonth_YRowScope=[firstRowNum,lastRowNum];
  256.     portfolioMonthlyReturn=zeros(size(yMonth_YRowScope,1),1);
  257.     indexMonthlyReturn=zeros(size(yMonth_YRowScope,1),1);
  258.     for yMonthSN=2:size(yMonth_YRowScope,1)-1
  259.         stgMonthSN_YRow=yMonth_YRowScope(yMonthSN,1):yMonth_YRowScope(yMonthSN,2);
  260.         portfolioMonthlyReturn(yMonthSN)=prod(y(stgMonthSN_YRow,end-1));
  261.         indexMonthlyReturn(yMonthSN)=prod(indexDailyReturn(stgMonthSN_YRow));
  262.     end
  263.     portfolioMonthlyReturn([1,yMonthSN+1:end])=[];
  264.     indexMonthlyReturn([1,yMonthSN+1:end])=[];
  265.     monthlyTrackingError=portfolioMonthlyReturn-indexMonthlyReturn;
  266.     monthlyIR=zeros(size(monthlyTrackingError,1),2);
  267.     for yMonthSN=1:size(monthlyIR,1)-12
  268.         monthlyIR(yMonthSN,:)=[yMonth(yMonth_YRowScope(yMonthSN+1,1)),...
  269.             mean(monthlyTrackingError(yMonthSN:end))/std(monthlyTrackingError(yMonthSN:end))];
  270.     end
  271.     monthlyIR(yMonthSN+1:end,:)=[];
  272.     monthlyIR(:,2)=sqrt(12)*monthlyIR(:,2);
  273.      
  274.     dailyTracingError=y(:,end-1)-indexDailyReturn;
  275.     dailyIR=zeros(size(y,1)-1,2);
  276.     dailyIR(1:end,1)=y(1:end-1,1);
  277.     for yRow=1:size(y,1)-1
  278.         dailyIR(yRow,2)=mean(dailyTracingError(yRow:end))/std(dailyTracingError(yRow:end));
  279.     end
  280.     dailyIR(:,2)=dailyIR(:,2)*sqrt(250);
  281.     varargout{varout_infoRatio}=struct('daily',dailyIR,'monthly',monthlyIR);
  282. end
  283. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  284. % 最大回撤maxDrawDown
  285. if nargout>=varout_maxDrawDown+1
  286.     yRowNum=size(y,1);
  287.     varargout{varout_maxDrawDown}=zeros(yRowNum-1,2); %varargout{varout_infoRatio}
  288.     for i=1:yRowNum-1
  289.         varargout{varout_maxDrawDown}(i,1)=maxdrawdown(y(i:end,end));
  290.         varargout{varout_maxDrawDown}(i,2)=maxdrawdown(refIndex(i:end));
  291.     end
  292. end
  293. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  294. % 市场指数
  295. if nargout>=varout_index+1
  296.     marketIndex=idx(raw{1,dateCol},datestr(huicheEndDateNum,'yyyymmdd'),conn);
  297.     varargout{varout_index}=marketIndex;
  298. end
  299. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  300. % 流通市值
  301. if nargout>=varout_matketCap+1
  302.     varargout{varout_matketCap}=marketCap(1:marketCapCol,:);
  303. end
  304. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  305. % Famma French 三因素回归
  306. if nargout>=varout_FF3+1
  307.     varargout{varout_FF3}=FF3(y(:,[1,end-1]),risklessReturn,conn);
  308. end
  309. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  310. % xlsresult
  311. if nargout>=varout_xlsResult+1
  312.     xlsResult(:,xlsResultCol+1:end)=[];
  313.     xlsResult(end-1:end,1)=[{'隔日收益'};{'累计收益'}];
  314.     xlsResult(end,2:end)=num2cell(cumprod(cell2mat(xlsResult(end-1,2:end))));
  315.     xlsResult=xlsResult';
  316.     varargout{varout_xlsResult}=xlsResult;
  317. end
  318. close(conn);
  319. end

  320. function suby=strconnec(subx)
  321. ssubx=subx(1:end-1);
  322. ssubx=strcat('''',ssubx,''',');
  323. tmp=['''',subx{end},''''];
  324. ssubx=[ssubx;tmp];
  325. suby=cell2mat(ssubx');
  326. end

  327. function sy=monthEndDateNum(sDateNum)
  328. %         输入sDateNum是一个date number, 输出sy给出date number所在月最后一天的datenum
  329. sx=datestr(sDateNum,'yyyymm');
  330. endDay=eomday(str2double(sx(1:4)),str2double(sx(5:6)));
  331. sy=datenum([sx,num2str(endDay)],'yyyymmdd');
  332. end

  333. function y=marketCapFun(windIDCell,fromDate,toDate,conn)
  334. % windIDCell一列的cell
  335. % fromDate, toDate是date sting,'yyyymmdd'形式
  336. % y是一个两列的数值矩阵,给出从fromDate到toDate组合的流通市值之和,第一列为日期,第二列为市值
  337. if isempty(windIDCell{1})
  338.     tradeDay=fetch(conn,['select f1_1010 from tb_object_1010 where f1_1010 between ''',fromDate,''' and ''',toDate,''' order by f1_1010']);
  339.     fprintf('No stock in the portfolio. Market capitalization equals 0.\n');
  340.     y=[str2double(tradeDay),zeros(length(tradeDay),1)];
  341.     return;
  342. end

  343. uniqueWindID=sortrows(windIDCell);
  344. windID=strconnec(uniqueWindID);
  345. marketCap=fetch(conn,['select f1_5004,f2_5004,f10_5004 from tb_object_5004 where f1_5004 in (',windID,') and (f2_5004 between ''',fromDate,''' and ''',toDate,''')']);
  346. tradeSeriesMatrix=col2matrix(marketCap);
  347. noDataWindID=uniqueWindID(find(1-ismember(uniqueWindID,tradeSeriesMatrix(2:end,1))));
  348. if ~isempty(noDataWindID)
  349.     fprintf('Caution: Can not fetch market capitalization data for stock(s) with windID %s from %s to %s. These data are set to 0.',...
  350.         cell2mat(strcat(strcat(noDataWindID,',')')),fromDate,toDate);
  351.     missed=[noDataWindID,num2cell(zeros(length(noDataWindID),size(tradeSeriesMatrix,2)-1))]; %所有的交易日内都没有数据,流通市值统一设为0
  352.     tradeSeriesMatrix=[tradeSeriesMatrix(1,:);srotrows([tradeSeriesMatrix(2:end,:);missed],1)];
  353. end
  354. for tradeSeriesMatrixRow=2:size(tradeSeriesMatrix,1)
  355.     for tradeSeriesMatrixCol=3:size(tradeSeriesMatrix,2)
  356.         if isnan(tradeSeriesMatrix{tradeSeriesMatrixRow,tradeSeriesMatrixCol})
  357.             tradeSeriesMatrix{tradeSeriesMatrixRow,tradeSeriesMatrixCol}=tradeSeriesMatrix{tradeSeriesMatrixRow,tradeSeriesMatrixCol-1};
  358.         end
  359.     end
  360. end
  361. %     股票在组合建立当日就没有数据,收盘价设为此后最近有数据交易日的收盘价
  362. tradeSeriesMatrixData=cell2mat(tradeSeriesMatrix(2:end,2:end));
  363. for tradeSeriesMatrixDataRow=1:size(tradeSeriesMatrixData,1)
  364.     lastNanCol=find(isnan(tradeSeriesMatrixData(tradeSeriesMatrixDataRow,:)),1,'last');
  365.     tradeSeriesMatrixData(tradeSeriesMatrixDataRow,1:lastNanCol)=tradeSeriesMatrixData(tradeSeriesMatrixDataRow,lastNanCol+1);
  366. end
  367. y=[str2double(tradeSeriesMatrix(1,2:end))',sum(tradeSeriesMatrixData)'];
  368. end

  369. function y=col2matrix(x)
  370. % x是一个三列的cell,第一列和第二列为指标,第三列为值,如第一列为证券ID,第二列为一段时间之内的交易日,第三列为一只股票在某个交易日的收盘价
  371. % y是一个cell,有unique(x(:,1))+1行和unique(x(:,2))+1列,y(2:end,1)等于unique(x(:,1)且按降序排列,
  372. % y(1,2:end)等于unique(x(:,2))且按降序排列,y(i,j)的值等于y(i,1)所对应的x第一列指标和y(1,j)所对应的x第二列指标给出的x第三列中的值,若x中没有该值,y(i,j)=NaN

  373. keyWindID=unique(x(:,1));
  374. keyTradeDate=unique(x(:,2));
  375. mapWindID=containers.Map(keyWindID,num2cell(1:length(keyWindID)));
  376. mapTradeDate=containers.Map(keyTradeDate,num2cell(1:length(keyTradeDate)));
  377. tradeDataMatrix=nan(length(keyWindID),length(keyTradeDate));
  378. tradeDataMatrix(sub2ind(size(tradeDataMatrix),cell2mat(values(mapWindID,x(:,1))),cell2mat(values(mapTradeDate,x(:,2)))))=cell2mat(x(:,3));
  379. y=nan(length(keyWindID)+1,length(keyTradeDate)+1);
  380. y=num2cell(y);
  381. y(2:end,1)=keyWindID;
  382. y(1,2:end)=keyTradeDate';
  383. y(2:end,2:end)=num2cell(tradeDataMatrix);
  384. end

  385. function y=idx(fromDate,toDate,conn)
  386. % y是一个是一个structure,每个field是一个数值列向量,给出从fromDate到toDate之间所有交易日的市场指数值
  387. % HS300: S12425; 上证综指:1A0001
  388. y.HS300=fch('S12425');
  389. y.SZZZ=fch('1A0001');
  390.     function suby=fch(subx)
  391.         suby=fetch(conn,['select f2_1425,f7_1425 from tb_object_1425 where f1_1425=''',subx,...
  392.             ''' and (f2_1425 between ''',fromDate,''' and ''',toDate,''') order by f2_1425']);
  393.         suby=[str2double(suby(:,1)),cell2mat(suby(:,2))];
  394.     end
  395. end

  396. function yhat=FF3(x,risklessReturn,conn)
  397. % x是一个两列的cell array,第一列为交易日期,第二列为策略隔日收益率,x等于主程序中y(:,[1,end-1])
  398. % yhat是一个两列的矩阵,第一列是日期,第二列为FF3回归的到的累计收益。在由FF3计算隔日收益时,如果由于数据缺失而无法得到某天的收益,那么该日的收益等于x对应当日的真实收益
  399. xorg=x;
  400. totalDateNum=size(x,1);
  401. SMB=zeros(totalDateNum,2);
  402. HML=zeros(totalDateNum,2);
  403. SMB_HML_sampleRatio=20e-2;

  404. eachFetchDateNum=50;
  405. fetchNum=floor(totalDateNum/eachFetchDateNum)+sign(mod(totalDateNum,eachFetchDateNum));
  406. SMB_HML_num=0;
  407. for fetchSN=1:fetchNum
  408.     [fetchSN,fetchNum,size(x,1)]
  409.     tic
  410.     fromDate_YRow=(fetchSN-1)*eachFetchDateNum+1
  411.     toDate_YRow=sign(fetchNum-fetchSN)*fetchSN*eachFetchDateNum+(1-sign(fetchNum-fetchSN))*totalDateNum
  412.     mktCap_PB=fetch(conn,['select t5.f2_5004,t5.f10_5004,t5.f15_5004,t5.f7_5004 from tb_object_5004 as t5 inner join tb_object_1425 as t1 ',...
  413.         'on (t5.f1_5004=t1.f1_1425 and t5.f2_5004=t1.f2_1425) where (t5.f2_5004 between ''',num2str(x(fromDate_YRow,1)),''' and ''',...
  414.         num2str(x(toDate_YRow,1)),''') and t1.f11_1425=-1 order by t5.f2_5004']);
  415.     if isempty(mktCap_PB)
  416.         fprintf('Caution: fetch no data from %d to %d when caculating FF3.\n',x(fromDate_YRow,1),x(toDate_YRow,1));
  417.         continue;
  418.     end
  419.      
  420.     mktCap_PB=[str2double(mktCap_PB(:,1)),cell2mat(mktCap_PB(:,2:end))];
  421.     %     mktCap_PB_Data中会偶尔掺杂nan,去掉这些行
  422.     nanRow=prod(mktCap_PB,2);
  423.     mktCap_PB(find(isnan(nanRow)),:)=[];
  424.     mktCap_PB=sortrows(mktCap_PB,1);
  425.      
  426.     [~,firstRow]=unique(mktCap_PB(:,1),'first');
  427.     [~,lastRow]=unique(mktCap_PB(:,1),'last');
  428.     [length(firstRow),length(lastRow)]
  429.     for dateSN=1:length(firstRow)
  430.         currentDateData=mktCap_PB(firstRow(dateSN):lastRow(dateSN),:);
  431.         sampleNum=floor(size(currentDateData,1)*SMB_HML_sampleRatio);
  432.         currentDateData=sortrows(currentDateData,2);
  433.         SMB(SMB_HML_num+1,:)=[currentDateData(1,1),mean(currentDateData(1:sampleNum,end)-currentDateData(end-sampleNum+1:end,end))/100];
  434.         currentDateData=sortrows(currentDateData,3);
  435.         HML(SMB_HML_num+1,:)=[currentDateData(1,1),mean(currentDateData(1:sampleNum,end)-currentDateData(end-sampleNum+1:end,end))/100];
  436.         SMB_HML_num=SMB_HML_num+1;
  437.     end
  438.     toc
  439. end
  440. SMB=SMB(1:SMB_HML_num,:);
  441. HML=HML(1:SMB_HML_num,:);

  442. indexHS300=fetch(conn,['select f2_1425,f7_1425 from tb_object_1425 where f1_1425=''S12425'' and (f2_1425 between ''',...
  443.     num2str(x(1,1)),''' and ''',num2str(x(end,1)),''') order by f2_1425']);
  444. indexHS300=[str2double(indexHS300(:,1)),cell2mat(indexHS300(:,2))];
  445. marketReturn=[1;indexHS300(2:end,2)./indexHS300(1:end-1,2)];
  446. marketReturn=[indexHS300(:,1),marketReturn];

  447. interTradeDay=intersect(intersect(x(:,1),marketReturn(:,1)),SMB(:,1));
  448. SMB=SMB(find(ismember(SMB(:,1),interTradeDay)),:);
  449. HML=HML(find(ismember(HML(:,1),interTradeDay)),:);
  450. marketReturn=marketReturn(find(ismember(marketReturn(:,1),interTradeDay)),:);
  451. x=x(find(ismember(x(:,1),interTradeDay)),:);

  452. X=[ones(length(interTradeDay),1),marketReturn(:,2)-risklessReturn,SMB(:,2),HML(:,2)];
  453. whichStats={'beta','yhat','r','rsquare','adjrsquare','tstat','fstat'};
  454. stats=regstats(x(:,end)-risklessReturn,X(:,2:end),[],whichStats);
  455. % regCoef=stats.beta;
  456. % yhat=X*regCoef+risklessReturn;
  457. yhat=stats.yhat+risklessReturn;
  458. yhat=[interTradeDay,yhat];
  459. xorg(find(ismember(xorg(:,1),interTradeDay)),:)=yhat; % 对于缺乏数据而不能给出FF3回归结果的交易日,当日的FF3收益等于组合的真实收益
  460. yhat=sortrows(xorg,1);
  461. yhat(:,2)=cumprod(yhat(:,2));
  462. yhat=struct('yhat',yhat,'stats',stats);
  463. end
复制代码


二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:MATLAB SQL数据库 atlab matla 策略回测 SQL数据库 通用 程序

已有 1 人评分学术水平 热心指数 信用等级 收起 理由
冬致夏陌 + 5 + 5 + 5 精彩帖子

总评分: 学术水平 + 5  热心指数 + 5  信用等级 + 5   查看全部评分

本帖被以下文库推荐

沙发
rendapxj 发表于 2015-2-3 21:43:53

回帖奖励 +2

居然是沙发,谢谢
已有 1 人评分论坛币 收起 理由
fantuanxiaot + 2 精彩帖子

总评分: 论坛币 + 2   查看全部评分

藤椅
feders 发表于 2015-2-4 00:11:34

回帖奖励 +2

来看看

板凳
刘帅123 发表于 2015-2-4 08:26:29
看看!!

报纸
榄外人L 发表于 2015-2-4 08:56:24

回帖奖励 +2

很好的资料,学习
已有 1 人评分论坛币 收起 理由
fantuanxiaot + 2 精彩帖子

总评分: 论坛币 + 2   查看全部评分

地板
mike68097 发表于 2015-2-4 09:14:53
支持!!!!
已有 1 人评分论坛币 收起 理由
fantuanxiaot + 5 精彩帖子

总评分: 论坛币 + 5   查看全部评分

7
fjrong 在职认证  发表于 2015-2-4 09:22:12

回帖奖励 +2

8
fjrong 在职认证  发表于 2015-2-4 09:22:53
已有 1 人评分论坛币 收起 理由
fantuanxiaot + 5 精彩帖子

总评分: 论坛币 + 5   查看全部评分

9
fjrong 在职认证  发表于 2015-2-4 09:23:55

回帖奖励 +2

已有 1 人评分论坛币 收起 理由
fantuanxiaot + 3 精彩帖子

总评分: 论坛币 + 3   查看全部评分

10
刘帅123 发表于 2015-2-4 09:30:28

回帖奖励 +2

have a look!!
已有 1 人评分经验 论坛币 收起 理由
fantuanxiaot + 5 + 2 精彩帖子

总评分: 经验 + 5  论坛币 + 2   查看全部评分

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2025-12-26 14:58