initial
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,51 @@
|
||||
clear;
|
||||
|
||||
lookback=20;
|
||||
|
||||
load('inputData_AUDCAD_20120426', 'hhmm', 'tday', 'cl');
|
||||
idx=find(hhmm==1659);
|
||||
dailyCl=cl(idx);
|
||||
tday=tday(idx);
|
||||
|
||||
% Annualized interest rates in percent, updated monthly
|
||||
aud=load('AUD_interestRate', 'yyyy', 'mm', 'rates');
|
||||
cad=load('CAD_interestRate', 'yyyy', 'mm', 'rates');
|
||||
|
||||
aud_dailyRates=zeros(size(tday));
|
||||
for i=1:length(aud.mm)
|
||||
idx=find(aud.mm(i)==month(num2str(tday), 'yyyymmdd') & aud.yyyy(i)==year(num2str(tday), 'yyyymmdd'));
|
||||
if (~isempty(idx))
|
||||
aud_dailyRates(idx)=aud.rates(i);
|
||||
end
|
||||
end
|
||||
aud_dailyRates=aud_dailyRates/365/100;
|
||||
% Triple rollover interest on Wednesdays for AUD
|
||||
isWednesday=weekday(datenum(num2str(tday), 'yyyymmdd'))==4;
|
||||
aud_dailyRates(isWednesday)=3*aud_dailyRates(isWednesday);
|
||||
|
||||
cad_dailyRates=zeros(size(tday));
|
||||
for i=1:length(cad.mm)
|
||||
idx=find(cad.mm(i)==month(num2str(tday), 'yyyymmdd') & cad.yyyy(i)==year(num2str(tday), 'yyyymmdd'));
|
||||
if (~isempty(idx))
|
||||
cad_dailyRates(idx)=cad.rates(i);
|
||||
end
|
||||
end
|
||||
cad_dailyRates=cad_dailyRates/365/100;
|
||||
% Triple rollover interest on Thursdays for CAD
|
||||
isThursday=weekday(datenum(num2str(tday), 'yyyymmdd'))==5;
|
||||
cad_dailyRates(isThursday)=3*cad_dailyRates(isThursday);
|
||||
|
||||
ma=movingAvg(dailyCl, lookback);
|
||||
mstd=movingStd(dailyCl, lookback);
|
||||
z=(dailyCl-ma)./mstd;
|
||||
|
||||
% Unlevered return of a linear mean-reverting strategy.
|
||||
ret=lag(-sign(z), 1).*(log(dailyCl)+lag(-log(dailyCl)+log(1+aud_dailyRates)-log(1+cad_dailyRates), 1));
|
||||
% ret=lag(-sign(z), 1).*(log(dailyCl)+lag(-log(dailyCl), 1));
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.061564 Sharpe=0.541802
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
clear;
|
||||
|
||||
usdcad=load('../Data/inputData_USDCAD_20120426', 'tday', 'hhmm', 'cl');
|
||||
audusd=load('../Data/inputData_AUDUSD_20120426', 'tday', 'hhmm', 'cl');
|
||||
|
||||
firstDate=20090101;
|
||||
|
||||
idx=find(usdcad.tday>firstDate & usdcad.hhmm==1659);
|
||||
usdcad.tday=usdcad.tday(idx);
|
||||
usdcad.hhmm=usdcad.hhmm(idx);
|
||||
usdcad.cl=usdcad.cl(idx);
|
||||
|
||||
idx=find(audusd.tday>firstDate & audusd.hhmm==1659);
|
||||
audusd.tday=audusd.tday(idx);
|
||||
audusd.hhmm=audusd.hhmm(idx);
|
||||
audusd.cl=audusd.cl(idx);
|
||||
|
||||
tday=audusd.tday;
|
||||
|
||||
% Need to invert currency pair so that each unit has same capital in local
|
||||
% currency
|
||||
cad=1./usdcad.cl;
|
||||
aud=audusd.cl;
|
||||
|
||||
y=[ aud cad ];
|
||||
trainlen=250;
|
||||
lookback=20;
|
||||
hedgeRatio=NaN(size(y));
|
||||
numUnits=NaN(size(y, 1), 1);
|
||||
|
||||
for t=trainlen+1:size(y, 1)
|
||||
res=johansen(y(t-trainlen:t-1, :), 0, 1);
|
||||
hedgeRatio(t, :)=res.evec(:, 1)';
|
||||
|
||||
% yport is the market value of a unit portfolio of AUDUSD and CADUSD expressed in US$.
|
||||
yport=sum(y(t-lookback+1:t, :).*repmat(hedgeRatio(t, :), [lookback 1]), 2);
|
||||
ma=mean(yport);
|
||||
mstd=std(yport);
|
||||
zScore=(yport(end)-ma)/mstd;
|
||||
|
||||
% numUnits are number of units of unit portfolio of AUDUSD and CADUSD
|
||||
numUnits(t)=-(yport(end)-ma)/mstd;
|
||||
|
||||
end
|
||||
|
||||
|
||||
% positions are market values of AUDUSD and CADUSD in portfolio expressed
|
||||
% in US$.
|
||||
positions=repmat(numUnits, [1 size(y, 2)]).*hedgeRatio.*y;
|
||||
|
||||
% daily P&L of portfolio in US$.
|
||||
pnl=sum(lag(positions, 1).*(y-lag(y, 1))./lag(y, 1), 2);
|
||||
ret=pnl./sum(abs(lag(positions, 1)), 2);
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
|
||||
plot(cumprod(1+ret(trainlen+1:end))-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret(trainlen+1:end)).^(252/length(ret(trainlen+1:end)))-1, sqrt(252)*mean(ret(trainlen+1:end))/std(ret(trainlen+1:end)));
|
||||
% APR=0.112410 Sharpe=1.610890
|
||||
|
||||
|
||||
% Kelly leverage
|
||||
f=mean(ret(trainlen+1:end))/std(ret(trainlen+1:end))^2;
|
||||
fprintf(1, 'f=%f\n', f);
|
||||
% f=23.845328
|
||||
|
||||
ret=ret(trainlen+1:end);
|
||||
save('../Data/AUDCAD_unequal_ret', 'ret');
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
clear;
|
||||
lookback1=30;
|
||||
lookback2=40;
|
||||
|
||||
|
||||
load('inputDataOHLCDaily_20120504', 'syms', 'tday', 'cl');
|
||||
cl=cl(:, strcmp('CL', syms));
|
||||
|
||||
% longs= cl < lag(cl, lookback1) & cl > lag(movingAvg(cl, lookback2), 1);
|
||||
% shorts=cl > lag(cl, lookback1) & cl < lag(movingAvg(cl, lookback2), 1);
|
||||
|
||||
% longs= cl > lag(cl, lookback2);
|
||||
% shorts= cl < lag(cl, lookback2);
|
||||
longs= cl < backshift(lookback1, cl) & cl > backshift(lookback2, cl);
|
||||
shorts=cl > backshift(lookback1, cl) & cl < backshift(lookback2, cl);
|
||||
% longs= cl < lag(cl, lookback1) ;
|
||||
% shorts=cl > lag(cl, lookback1) ;
|
||||
|
||||
positions=zeros(size(cl));
|
||||
positions(longs)=1;
|
||||
positions(shorts)=-1;
|
||||
|
||||
|
||||
% ret=lag(positions, 1).*(cl-lag(cl, 1))./lag(cl, 1);
|
||||
ret=backshift(1, positions).*(cl-backshift(1, cl))./backshift(1, cl);
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
plot(cumprod(1+ret)-1, 'r'); % Cumulative compounded return
|
||||
dateaxis('X', 12, datenum(num2str(tday(1)), 'yyyymmdd'));
|
||||
hold on;
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.117600 Sharpe=1.100368
|
||||
|
||||
% Momentum only
|
||||
longs= cl > backshift(lookback2, cl);
|
||||
shorts=cl < backshift(lookback2, cl);
|
||||
positions=zeros(size(cl));
|
||||
positions(longs)=1;
|
||||
positions(shorts)=-1;
|
||||
ret=backshift(1, positions).*(cl-backshift(1, cl))./backshift(1, cl);
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
plot(cumprod(1+ret)-1, 'g'); % Cumulative compounded return
|
||||
|
||||
% Reversal only
|
||||
longs= cl < backshift(lookback1, cl);
|
||||
shorts=cl > backshift(lookback1, cl);
|
||||
positions=zeros(size(cl));
|
||||
positions(longs)=1;
|
||||
positions(shorts)=-1;
|
||||
ret=backshift(1, positions).*(cl-backshift(1, cl))./backshift(1, cl);
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
plot(cumprod(1+ret)-1, 'k'); % Cumulative compounded return
|
||||
|
||||
legend('Combo', 'Momentum', 'Reversal');
|
||||
|
||||
longs= cl < backshift(lookback1, cl) | cl > backshift(lookback2, cl);
|
||||
shorts=cl > backshift(lookback1, cl) | cl < backshift(lookback2, cl);
|
||||
% longs= cl < lag(cl, lookback1) ;
|
||||
% shorts=cl > lag(cl, lookback1) ;
|
||||
|
||||
positions=zeros(size(cl));
|
||||
positionsL=zeros(size(cl));
|
||||
positionsS=zeros(size(cl));
|
||||
positionsL(longs)=1;
|
||||
positionsS(shorts)=-1;
|
||||
positions=positionsL+positionsS;
|
||||
|
||||
|
||||
% ret=lag(positions, 1).*(cl-lag(cl, 1))./lag(cl, 1);
|
||||
ret=backshift(1, positions).*(cl-backshift(1, cl))./backshift(1, cl);
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
plot(cumprod(1+ret)-1, 'c'); % Cumulative compounded return
|
||||
legend('ComboAND', 'Momentum', 'Reversal', 'ComboOR');
|
||||
|
||||
hold off;
|
||||
@@ -0,0 +1,31 @@
|
||||
clear;
|
||||
|
||||
% gc=load('//dellquad/Futures_data/inputData_GC_1600_20100802', 'tday', 'hhmm', 'cl');
|
||||
gc=load('inputData_GC_1600_20100802', 'tday', 'hhmm', 'cl');
|
||||
gld=load('inputData_ETF');
|
||||
|
||||
gld.cl=gld.cl(:, strcmp('GLD', gld.syms));
|
||||
|
||||
[tday idx1 idx2]=intersect(gc.tday, gld.tday);
|
||||
|
||||
gc.cl=gc.cl(idx1);
|
||||
gld.cl=gld.cl(idx2);
|
||||
|
||||
% Long GLD and short GC
|
||||
ret=(gld.cl-lag(gld.cl, 1))./lag(gld.cl, 1)-(gc.cl-lag(gc.cl, 1))./lag(gc.cl, 1);
|
||||
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
cumret=cumprod(1+ret)-1;
|
||||
|
||||
plot(cumret);
|
||||
|
||||
riskFreeRate=0.02/252;
|
||||
fprintf(1, 'Avg Ann Ret=%7.4f Sharpe ratio=%4.2f \n',252*smartmean(ret), sqrt(252)*smartmean(ret-riskFreeRate)/smartstd(ret-riskFreeRate));
|
||||
fprintf(1, 'APR=%10.4f\n', prod(1+ret).^(252/length(ret))-1);
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'Max DD =%f Max DDD in days=%i\n\n', maxDD, round(maxDDD));
|
||||
%
|
||||
% Avg Ann Ret= 0.0190 Sharpe ratio=-.07
|
||||
% APR= 0.0191
|
||||
% Max DD =-0.008247 Max DDD in days=91
|
||||
@@ -0,0 +1,102 @@
|
||||
clear;
|
||||
% Daily data on EWA-EWC
|
||||
load('inputData_ETF', 'tday', 'syms', 'cl');
|
||||
idxA=find(strcmp('EWA', syms));
|
||||
idxC=find(strcmp('EWC', syms));
|
||||
|
||||
x=cl(:, idxA);
|
||||
y=cl(:, idxC);
|
||||
|
||||
% Augment x with ones to accomodate possible offset in the regression
|
||||
% between y vs x.
|
||||
|
||||
x=[x ones(size(x))];
|
||||
|
||||
delta=0.0001; % delta=1 gives fastest change in beta, delta=0.000....1 allows no change (like traditional linear regression).
|
||||
|
||||
yhat=NaN(size(y)); % measurement prediction
|
||||
e=NaN(size(y)); % measurement prediction error
|
||||
Q=NaN(size(y)); % measurement prediction error variance
|
||||
|
||||
% For clarity, we denote R(t|t) by P(t).
|
||||
% initialize R, P and beta.
|
||||
R=zeros(2);
|
||||
P=zeros(2);
|
||||
beta=NaN(2, size(x, 1));
|
||||
Vw=delta/(1-delta)*eye(2);
|
||||
Ve=0.001;
|
||||
|
||||
|
||||
% Initialize beta(:, 1) to zero
|
||||
beta(:, 1)=0;
|
||||
|
||||
% Given initial beta and R (and P)
|
||||
for t=1:length(y)
|
||||
if (t > 1)
|
||||
beta(:, t)=beta(:, t-1); % state prediction. Equation 3.7
|
||||
R=P+Vw; % state covariance prediction. Equation 3.8
|
||||
end
|
||||
|
||||
yhat(t)=x(t, :)*beta(:, t); % measurement prediction. Equation 3.9
|
||||
|
||||
Q(t)=x(t, :)*R*x(t, :)'+Ve; % measurement variance prediction. Equation 3.10
|
||||
|
||||
|
||||
% Observe y(t)
|
||||
e(t)=y(t)-yhat(t); % measurement prediction error
|
||||
|
||||
K=R*x(t, :)'/Q(t); % Kalman gain
|
||||
|
||||
beta(:, t)=beta(:, t)+K*e(t); % State update. Equation 3.11
|
||||
P=R-K*x(t, :)*R; % State covariance update. Euqation 3.12
|
||||
|
||||
end
|
||||
|
||||
|
||||
plot(beta(1, :)');
|
||||
|
||||
figure;
|
||||
|
||||
plot(beta(2, :)');
|
||||
|
||||
figure;
|
||||
|
||||
plot(e(3:end), 'r');
|
||||
|
||||
hold on;
|
||||
plot(sqrt(Q(3:end)));
|
||||
|
||||
y2=[x(:, 1) y];
|
||||
|
||||
longsEntry=e < -sqrt(Q); % a long position means we should buy EWC
|
||||
longsExit=e > -sqrt(Q);
|
||||
|
||||
shortsEntry=e > sqrt(Q);
|
||||
shortsExit=e < sqrt(Q);
|
||||
|
||||
numUnitsLong=NaN(length(y2), 1);
|
||||
numUnitsShort=NaN(length(y2), 1);
|
||||
|
||||
numUnitsLong(1)=0;
|
||||
numUnitsLong(longsEntry)=1;
|
||||
numUnitsLong(longsExit)=0;
|
||||
numUnitsLong=fillMissingData(numUnitsLong); % fillMissingData can be downloaded from epchan.com/book2. It simply carry forward an existing position from previous day if today's positio is an indeterminate NaN.
|
||||
|
||||
numUnitsShort(1)=0;
|
||||
numUnitsShort(shortsEntry)=-1;
|
||||
numUnitsShort(shortsExit)=0;
|
||||
numUnitsShort=fillMissingData(numUnitsShort);
|
||||
|
||||
numUnits=numUnitsLong+numUnitsShort;
|
||||
positions=repmat(numUnits, [1 size(y2, 2)]).*[-beta(1, :)' ones(size(beta(1, :)'))].*y2; % [hedgeRatio -ones(size(hedgeRatio))] is the shares allocation, [hedgeRatio -ones(size(hedgeRatio))].*y2 is the dollar capital allocation, while positions is the dollar capital in each ETF.
|
||||
pnl=sum(lag(positions, 1).*(y2-lag(y2, 1))./lag(y2, 1), 2); % daily P&L of the strategy
|
||||
ret=pnl./sum(abs(lag(positions, 1)), 2); % return is P&L divided by gross market value of portfolio
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
figure;
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.262252 Sharpe=2.361162
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
clear;
|
||||
|
||||
% 1 minute data on EWA-EWC
|
||||
load('inputData_ETF', 'tday', 'syms', 'cl');
|
||||
idxG=find(strcmp('GLD', syms));
|
||||
idxU=find(strcmp('USO', syms));
|
||||
|
||||
x=cl(:, idxG);
|
||||
y=cl(:, idxU);
|
||||
|
||||
% lookback period for calculating the dynamically changing hedge ratio
|
||||
lookback=20; % Lookback set arbitrarily short
|
||||
hedgeRatio=NaN(size(x, 1), 1);
|
||||
for t=lookback:size(hedgeRatio, 1)
|
||||
regression_result=ols(log(y(t-lookback+1:t)), [log(x(t-lookback+1:t)) ones(lookback, 1)]);
|
||||
hedgeRatio(t)=regression_result.beta(1);
|
||||
end
|
||||
|
||||
y2=[x y];
|
||||
|
||||
yport=sum([-hedgeRatio ones(size(hedgeRatio))].*log(y2), 2); % The net market value of the portfolio is same as the "spread"
|
||||
hedgeRatio(1:lookback)=[]; % Removed because hedge ratio is indterminate
|
||||
yport(1:lookback)=[];
|
||||
y2(1:lookback, :)=[];
|
||||
plot(yport);
|
||||
|
||||
%
|
||||
numUnits=-(yport-movingAvg(yport, lookback))./movingStd(yport, lookback); % units invested in portfolio. movingAvg and movingStd are functions from epchan.com/book2
|
||||
positions=repmat(numUnits, [1 size(y2, 2)]).*[-hedgeRatio ones(size(hedgeRatio))]; % [hedgeRatio -ones(size(hedgeRatio))] is the dollar capital allocation, while positions is the dollar capital in each ETF.
|
||||
pnl=sum(lag(positions, 1).*(y2-lag(y2, 1))./lag(y2, 1), 2); % daily P&L of the strategy
|
||||
ret=pnl./sum(abs(lag(positions, 1)), 2); % return is P&L divided by gross market value of portfolio
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
figure;
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.088863 Sharpe=0.504153
|
||||
@@ -0,0 +1,40 @@
|
||||
clear;
|
||||
|
||||
% 1 minute data on GLD-USO
|
||||
load('inputData_ETF', 'tday', 'syms', 'cl');
|
||||
idxG=find(strcmp('GLD', syms));
|
||||
idxU=find(strcmp('USO', syms));
|
||||
|
||||
x=cl(:, idxG);
|
||||
y=cl(:, idxU);
|
||||
|
||||
% lookback period for calculating the dynamically changing hedge ratio
|
||||
lookback=20; % Lookback set arbitrarily short
|
||||
hedgeRatio=NaN(size(x, 1), 1);
|
||||
for t=lookback:size(hedgeRatio, 1)
|
||||
regression_result=ols(y(t-lookback+1:t), [x(t-lookback+1:t) ones(lookback, 1)]);
|
||||
hedgeRatio(t)=regression_result.beta(1);
|
||||
end
|
||||
|
||||
y2=[x y];
|
||||
|
||||
yport=sum([-hedgeRatio ones(size(hedgeRatio))].*y2, 2); % The net market value of the portfolio is same as the "spread"
|
||||
hedgeRatio(1:lookback)=[]; % Removed because hedge ratio is indterminate
|
||||
yport(1:lookback)=[];
|
||||
y2(1:lookback, :)=[];
|
||||
plot(yport);
|
||||
|
||||
% Apply a simple linear mean reversion strategy to GLD-USO
|
||||
|
||||
numUnits=-(yport-movingAvg(yport, lookback))./movingStd(yport, lookback); % movingAvg and movingStd are functions from epchan.com/book2
|
||||
positions=repmat(numUnits, [1 size(y2, 2)]).*[-hedgeRatio ones(size(hedgeRatio))].*y2; % [hedgeRatio -ones(size(hedgeRatio))] is the shares allocation, [hedgeRatio -ones(size(hedgeRatio))].*y2 is the dollar capital allocation, while positions is the dollar capital in each ETF.
|
||||
pnl=sum(lag(positions, 1).*(y2-lag(y2, 1))./lag(y2, 1), 2); % daily P&L of the strategy
|
||||
ret=pnl./sum(abs(lag(positions, 1)), 2); % return is P&L divided by gross market value of portfolio
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
figure;
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.108335 Sharpe=0.589651
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
clear;
|
||||
|
||||
% 1 minute data on EWA-EWC
|
||||
load('inputData_ETF', 'tday', 'syms', 'cl');
|
||||
idxG=find(strcmp('GLD', syms));
|
||||
idxU=find(strcmp('USO', syms));
|
||||
|
||||
x=cl(:, idxG);
|
||||
y=cl(:, idxU);
|
||||
|
||||
lookback=20; % Lookback is set arbitrarily
|
||||
ratio=y./x;
|
||||
ratio(1:lookback)=[]; % Removed to have same test set as price spread and log price spread strategies
|
||||
x(1:lookback)=[];
|
||||
y(1:lookback)=[];
|
||||
plot(ratio);
|
||||
|
||||
|
||||
%
|
||||
% Apply a simple linear mean reversion strategy to GLD-USO
|
||||
numUnits=-(ratio-movingAvg(ratio, lookback))./movingStd(ratio, lookback); % units invested in the portfolio. movingAvg and movingStd are functions from epchan.com/book2
|
||||
positions=repmat(numUnits, [1 2]).*[-ones(size(x, 1), 1) ones(size(x, 1), 1)]; % positions in dollar invested
|
||||
pnl=sum(lag(positions, 1).*([x y]-lag([x y], 1))./lag([x y], 1), 2); % daily P&L of the strategy
|
||||
ret=pnl./sum(abs(lag(positions, 1)), 2);
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
figure;
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
|
||||
% APR=-0.141522 Sharpe=-0.746663
|
||||
@@ -0,0 +1,86 @@
|
||||
clear;
|
||||
|
||||
load('../Data/inputDataOHLCDaily_20120511', 'syms', 'tday', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataOHLCDaily_20120815', 'syms', 'tday', 'cl');
|
||||
idx=strmatch('TU', syms, 'exact');
|
||||
|
||||
tday=tday(:, idx);
|
||||
cl=cl(:, idx);
|
||||
|
||||
% Correlation tests
|
||||
for lookback=[1 5 10 25 60 120 250]
|
||||
for holddays=[1 5 10 25 60 120 250]
|
||||
ret_lag=(cl-backshift(lookback, cl))./backshift(lookback, cl);
|
||||
ret_fut=(fwdshift(holddays, cl)-cl)./cl;
|
||||
badDates=any([isnan(ret_lag) isnan(ret_fut)], 2);
|
||||
ret_lag(badDates)=[];
|
||||
ret_fut(badDates)=[];
|
||||
|
||||
if (lookback >= holddays)
|
||||
indepSet=[1:holddays:length(ret_lag)];
|
||||
else
|
||||
indepSet=[1:lookback:length(ret_lag)];
|
||||
end
|
||||
|
||||
ret_lag=ret_lag(indepSet);
|
||||
ret_fut=ret_fut(indepSet);
|
||||
|
||||
[cc, pval]=corrcoef(ret_lag, ret_fut);
|
||||
% fprintf(1, 'lookback=%3i holddays=%3i cc=%7.4f pval=%6.4f\n', lookback, holddays, cc(1, 2), pval(1, 2));
|
||||
fprintf(1, '%3i\t%3i\t%7.4f\t%6.4f\n', lookback, holddays, cc(1, 2), pval(1, 2));
|
||||
end
|
||||
end
|
||||
|
||||
% Hurst exponent and Variance ratio test
|
||||
H=genhurst(log(cl), 2);
|
||||
fprintf(1, 'H2=%f\n', H);
|
||||
|
||||
% Variance ratio test from Matlab Econometrics Toolbox
|
||||
[h,pValue]=vratiotest(log(cl));
|
||||
|
||||
fprintf(1, 'h=%i\n', h); % h=1 means rejection of random walk hypothesis, 0 means it is a random walk.
|
||||
fprintf(1, 'pValue=%f\n', pValue); % pValue is essentially the probability that the null hypothesis (random walk) is true.
|
||||
|
||||
lookback=250;
|
||||
holddays=25;
|
||||
|
||||
longs=cl > backshift(lookback, cl) ;
|
||||
shorts=cl < backshift(lookback, cl) ;
|
||||
|
||||
pos=zeros(length(cl), 1);
|
||||
|
||||
for h=0:holddays-1
|
||||
long_lag=backshift(h, longs);
|
||||
long_lag(isnan(long_lag))=false;
|
||||
long_lag=logical(long_lag);
|
||||
|
||||
short_lag=backshift(h, shorts);
|
||||
short_lag(isnan(short_lag))=false;
|
||||
short_lag=logical(short_lag);
|
||||
|
||||
pos(long_lag)=pos(long_lag)+1;
|
||||
pos(short_lag)=pos(short_lag)-1;
|
||||
end
|
||||
|
||||
ret=(backshift(1, pos).*(cl-backshift(1, cl))./backshift(1, cl))/holddays;
|
||||
|
||||
ret(isnan(ret))=0;
|
||||
idx=find(tday==20090102);
|
||||
% idx=1;
|
||||
|
||||
cumret=cumprod(1+ret(idx:end))-1;
|
||||
|
||||
plot(cumret);
|
||||
|
||||
fprintf(1, 'Avg Ann Ret=%7.4f Ann Volatility=%7.4f Sharpe ratio=%4.2f \n',252*smartmean(ret(idx:end)), sqrt(252)*smartstd(ret(idx:end)), sqrt(252)*smartmean(ret(idx:end))/smartstd(ret(idx:end)));
|
||||
fprintf(1, 'APR=%10.4f\n', prod(1+ret(idx:end)).^(252/length(ret(idx:end)))-1);
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'Max DD =%f Max DDD in days=%i\n\n', maxDD, round(maxDDD));
|
||||
fprintf(1, 'Kelly f=%f\n', mean(ret(idx:end))/std(ret(idx:end))^2);
|
||||
|
||||
% Avg Ann Ret= 0.0167 Sharpe ratio=1.04
|
||||
% APR= 0.0167
|
||||
% Max DD =-0.024847 Max DDD in days=343
|
||||
% Kelly f=64.919535
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
clear;
|
||||
|
||||
% load('../Data/inputDataOHLCDaily_20120511', 'syms', 'tday', 'cl');
|
||||
load('inputDataOHLCDaily_20120511', 'syms', 'tday', 'cl');
|
||||
idx=strmatch('TU', syms, 'exact');
|
||||
|
||||
tday=tday(:, idx);
|
||||
cl=cl(:, idx);
|
||||
|
||||
lookback=250;
|
||||
holddays=25;
|
||||
|
||||
longs=cl > backshift(lookback, cl) ;
|
||||
shorts=cl < backshift(lookback, cl) ;
|
||||
|
||||
pos=zeros(length(cl), 1);
|
||||
|
||||
for h=0:holddays-1
|
||||
long_lag=backshift(h, longs);
|
||||
long_lag(isnan(long_lag))=false;
|
||||
long_lag=logical(long_lag);
|
||||
|
||||
short_lag=backshift(h, shorts);
|
||||
short_lag(isnan(short_lag))=false;
|
||||
short_lag=logical(short_lag);
|
||||
|
||||
pos(long_lag)=pos(long_lag)+1;
|
||||
pos(short_lag)=pos(short_lag)-1;
|
||||
end
|
||||
|
||||
marketRet=(cl-backshift(1, cl))./backshift(1, cl);
|
||||
marketRet(~isfinite(marketRet))=0;
|
||||
|
||||
ret=backshift(1, pos).*marketRet/holddays;
|
||||
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
% Gaussian hypothesis test
|
||||
fprintf(1, 'Gaussian Test statistic=%4.2f\n', mean(ret)/std(ret)*sqrt(length(ret)));
|
||||
% Gaussian Test statistic=2.93
|
||||
|
||||
% Randomized market returns hypothesis test
|
||||
moments={mean(marketRet), std(marketRet), skewness(marketRet), kurtosis(marketRet)};
|
||||
numSampleAvgretBetterOrEqualObserved=0;
|
||||
for sample=1:10000
|
||||
marketRet_sim=pearsrnd(moments{:}, length(marketRet), 1);
|
||||
cl_sim=cumprod(1+marketRet_sim)-1;
|
||||
|
||||
longs_sim=cl_sim > backshift(lookback, cl_sim) ;
|
||||
shorts_sim=cl_sim < backshift(lookback, cl_sim) ;
|
||||
|
||||
pos_sim=zeros(length(cl_sim), 1);
|
||||
|
||||
for h=0:holddays-1
|
||||
long_sim_lag=backshift(h, longs_sim);
|
||||
long_sim_lag(isnan(long_sim_lag))=false;
|
||||
long_sim_lag=logical(long_sim_lag);
|
||||
|
||||
short_sim_lag=backshift(h, shorts_sim);
|
||||
short_sim_lag(isnan(short_sim_lag))=false;
|
||||
short_sim_lag=logical(short_sim_lag);
|
||||
|
||||
pos_sim(long_sim_lag)=pos_sim(long_sim_lag)+1;
|
||||
pos_sim(short_sim_lag)=pos_sim(short_sim_lag)-1;
|
||||
end
|
||||
|
||||
|
||||
ret_sim=backshift(1, pos_sim).*marketRet_sim/holddays;
|
||||
ret_sim(~isfinite(ret_sim))=0;
|
||||
|
||||
if (mean(ret_sim)>= mean(ret))
|
||||
numSampleAvgretBetterOrEqualObserved=numSampleAvgretBetterOrEqualObserved+1;
|
||||
end
|
||||
end
|
||||
|
||||
fprintf(1, 'Randomized prices: p-value=%f\n', numSampleAvgretBetterOrEqualObserved/10000);
|
||||
% p-value=0.027500
|
||||
|
||||
|
||||
% Randomized entry trades hypothesis test
|
||||
|
||||
numSampleAvgretBetterOrEqualObserved=0;
|
||||
for sample=1:100000
|
||||
P=randperm(length(longs));
|
||||
longs_sim=longs(P);
|
||||
shorts_sim=shorts(P);
|
||||
|
||||
pos_sim=zeros(length(cl), 1);
|
||||
|
||||
for h=0:holddays-1
|
||||
long_sim_lag=backshift(h, longs_sim);
|
||||
long_sim_lag(isnan(long_sim_lag))=false;
|
||||
long_sim_lag=logical(long_sim_lag);
|
||||
|
||||
short_sim_lag=backshift(h, shorts_sim);
|
||||
short_sim_lag(isnan(short_sim_lag))=false;
|
||||
short_sim_lag=logical(short_sim_lag);
|
||||
|
||||
pos(long_sim_lag)=pos(long_sim_lag)+1;
|
||||
pos(short_sim_lag)=pos(short_sim_lag)-1;
|
||||
end
|
||||
|
||||
ret_sim=backshift(1, pos_sim).*marketRet/holddays;
|
||||
|
||||
ret_sim(isnan(ret_sim))=0;
|
||||
|
||||
|
||||
if (mean(ret_sim)>= mean(ret))
|
||||
numSampleAvgretBetterOrEqualObserved=numSampleAvgretBetterOrEqualObserved+1;
|
||||
end
|
||||
|
||||
end
|
||||
fprintf(1, 'Randomized trades: p-value=%f\n', numSampleAvgretBetterOrEqualObserved/100000);
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
clear;
|
||||
load('inputDataOHLCDaily_20120517', 'syms', 'tday', 'cl');
|
||||
|
||||
idxV=find(strcmp('VX', syms));
|
||||
idxE=find(strcmp('ES', syms));
|
||||
|
||||
VX=cl(:, idxV);
|
||||
tdayV=tday(:, idxV);
|
||||
|
||||
ES=cl(:, idxE);
|
||||
tdayE=tday(:, idxE);
|
||||
|
||||
[tday idxV idxE]=intersect(tdayV, tdayE);
|
||||
VX=VX(idxV);
|
||||
ES=ES(idxE);
|
||||
|
||||
scatter(VX, ES);
|
||||
|
||||
post200808=find(tday>=20080801);
|
||||
|
||||
hedgeRatio=regress(50*ES(post200808), [1000*VX(post200808) ones(length(post200808), 1)]);
|
||||
@@ -0,0 +1,88 @@
|
||||
clear;
|
||||
entryThreshold=0.1;
|
||||
onewaytcost=1/10000;
|
||||
load('inputDataDaily_VX_20120507', 'tday', 'contracts', 'cl');
|
||||
|
||||
% VIX Index
|
||||
[num txt]=xlsread('../Data/VIX.csv');
|
||||
VIX=num(:, end);
|
||||
|
||||
tday_VIX=str2double(cellstr(datestr(datenum(txt(2:end, 1), 'yyyy-mm-dd'), 'yyyymmdd')));
|
||||
[tday idx1 idx2]=intersect(tday_VIX, tday);
|
||||
VIX=VIX(idx1);
|
||||
VX=cl(idx2, :);
|
||||
|
||||
es=load('inputDataOHLCDaily_20120507', 'syms', 'tday', 'cl');
|
||||
ES=es.cl(:, strcmp('ES', es.syms));
|
||||
tday_ES=es.tday(:, strcmp('ES', es.syms));
|
||||
|
||||
[tday idx1 idx2]=intersect(tday, tday_ES);
|
||||
VIX=VIX(idx1);
|
||||
VX=VX(idx1, :);
|
||||
ES=ES(idx2);
|
||||
|
||||
isExpireDate=false(size(VX));
|
||||
isExpireDate=isfinite(VX) & ~isfinite(fwdshift(1, VX));
|
||||
|
||||
% Define front month as 40 days to 10 days before expiration
|
||||
numDaysStart=40;
|
||||
numDaysEnd=10;
|
||||
|
||||
positions=[zeros(size(VX)) zeros(size(ES))];
|
||||
|
||||
for c=1:length(contracts)-1
|
||||
expireIdx=find(isExpireDate(:, c));
|
||||
if (c==1)
|
||||
startIdx=expireIdx-numDaysStart;
|
||||
endIdx=expireIdx-numDaysEnd;
|
||||
else % ensure next front month contract doesn't start until current one ends
|
||||
startIdx=max(endIdx+1, expireIdx-numDaysStart);
|
||||
endIdx=expireIdx-numDaysEnd;
|
||||
end
|
||||
|
||||
if (~isempty(expireIdx))
|
||||
idx=startIdx:endIdx;
|
||||
% dailyRoll=(VX(idx, c)-VIX(idx))./[expireIdx-startIdx:-1:expireIdx-endIdx]';
|
||||
dailyRoll=(VX(idx, c)-VIX(idx))./[expireIdx-startIdx+1:-1:expireIdx-endIdx+1]';
|
||||
% positions(idx(dailyRoll > entryThreshold), c)=-1*0.3906;
|
||||
positions(idx(dailyRoll > entryThreshold), c)=-1;
|
||||
positions(idx(dailyRoll > entryThreshold), end)=-1;
|
||||
|
||||
% Entry level filter
|
||||
% positions(idx(dailyRoll > entryThreshold & VX(idx, c) > 21), c)=-1*0.3906;
|
||||
% positions(idx(dailyRoll > entryThreshold & VX(idx, c) > 21), end)=-1;
|
||||
|
||||
% positions(idx(dailyRoll < -entryThreshold), c)=1*0.3906;
|
||||
positions(idx(dailyRoll < -entryThreshold), c)=1;
|
||||
positions(idx(dailyRoll < -entryThreshold), end)=1;
|
||||
|
||||
% Entry level filter
|
||||
% positions(idx(dailyRoll < -entryThreshold & VX(idx, c) < 34), c)=1*0.3906;
|
||||
% positions(idx(dailyRoll < -entryThreshold & VX(idx, c) < 34), end)=1;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
y=[VX*1000 ES*50];
|
||||
|
||||
ret=smartsum(lag(positions).*(y-lag(y, 1)), 2)./smartsum(abs(lag(positions.*y)), 2)-...
|
||||
onewaytcost*smartsum(abs(positions.*y-lag(positions.*y)), 2)./smartsum(abs(lag(positions.*y)), 2);
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
idx=find(tday >= 20080804);
|
||||
|
||||
cumret=cumprod(1+ret(idx(501:end)))-1;
|
||||
plot(cumret); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret(idx(501:end))).^(252/length(ret(idx(501:end))))-1, sqrt(252)*mean(ret(idx(501:end)))/std(ret(idx(501:end))));
|
||||
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'maxDD=%f maxDDD=%i\n', maxDD, maxDDD);
|
||||
% APR=0.069065 Sharpe=1.002020
|
||||
% maxDD=-0.075683 maxDDD=259
|
||||
%
|
||||
|
||||
%
|
||||
% If use hedge ratio [1, 1]
|
||||
% APR=0.293906 Sharpe=3.263952
|
||||
% maxDD=-0.035798 maxDDD=59
|
||||
@@ -0,0 +1,71 @@
|
||||
clear;
|
||||
|
||||
load('inputData_ETF', 'syms', 'tday', 'cl');
|
||||
|
||||
uso=cl(:, strcmp('USO', syms));
|
||||
xle=cl(:, strcmp('XLE', syms));
|
||||
tday_ETF=tday;
|
||||
|
||||
load('inputDataDaily_CL_20120502', 'tday', 'contracts', 'cl');
|
||||
|
||||
ratioMatrix=(fwdshift(1, cl')./cl')'; % back/front
|
||||
|
||||
ratio=NaN(size(ratioMatrix, 1), 1);
|
||||
isExpireDate=false(size(ratio));
|
||||
|
||||
isExpireDate=isfinite(cl) & ~isfinite(fwdshift(1, cl));
|
||||
|
||||
% Define front month as 40 days to 10 days before expiration
|
||||
numDaysStart=40;
|
||||
numDaysEnd=10;
|
||||
|
||||
for c=1:length(contracts)-1
|
||||
expireIdx=find(isExpireDate(:, c));
|
||||
if (c==1)
|
||||
startIdx=expireIdx-numDaysStart;
|
||||
endIdx=expireIdx-numDaysEnd;
|
||||
else % ensure next front month contract doesn't start until current one ends
|
||||
startIdx=max(endIdx+1, expireIdx-numDaysStart);
|
||||
endIdx=expireIdx-numDaysEnd;
|
||||
end
|
||||
|
||||
if (~isempty(expireIdx))
|
||||
ratio(startIdx:endIdx)=ratioMatrix(startIdx:endIdx, c);
|
||||
end
|
||||
end
|
||||
|
||||
[tday idxA idxB]=intersect(tday_ETF, tday);
|
||||
uso=uso(idxA);
|
||||
xle=xle(idxA);
|
||||
ratio=ratio(idxB);
|
||||
|
||||
positions=zeros(length(tday), 2);
|
||||
|
||||
% Contango, negative roll return, buy spot, short future
|
||||
contango=find(ratio > 1);
|
||||
positions(contango, :)=repmat([-1 1], [length(contango) 1]);
|
||||
% Backwardation, positive roll return, short spot, long future
|
||||
backwardation=find(ratio < 1);
|
||||
positions(backwardation, :)=repmat([1 -1], [length(backwardation) 1]);
|
||||
|
||||
ret=smartsum(lag(positions, 1).*([uso xle]-lag([uso xle], 1))./lag([uso xle], 1), 2)/2;
|
||||
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
cumret=cumprod(1+ret)-1;
|
||||
|
||||
plot(cumret);
|
||||
|
||||
fprintf(1, 'Avg Ann Ret=%7.4f Sharpe ratio=%4.2f \n',252*smartmean(ret), sqrt(252)*smartmean(ret)/smartstd(ret));
|
||||
fprintf(1, 'APR=%10.4f\n', prod(1+ret).^(252/length(ret))-1);
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'Max DD =%f Max DDD in days=%i\n\n', maxDD, round(maxDDD));
|
||||
% Avg Ann Ret= 0.1592 Sharpe ratio=1.05
|
||||
% APR= 0.1591
|
||||
% Max DD =-0.192321 Max DDD in days=487
|
||||
|
||||
|
||||
% isContango=zeros(size(ratio));
|
||||
% isContango(ratio > 1)=1;
|
||||
%
|
||||
% hold on; plot(isContango, 'r'); hold on;
|
||||
@@ -0,0 +1,74 @@
|
||||
%port_trade.m
|
||||
clear;
|
||||
|
||||
load('../Data/inputDataOHLCDaily_20120424');
|
||||
|
||||
idxStart=find(tday==20070103);
|
||||
idxEnd=find(tday==20111230);
|
||||
|
||||
tday=tday(idxStart:idxEnd);
|
||||
cl=cl(idxStart:idxEnd, :);
|
||||
op=op(idxStart:idxEnd, :);
|
||||
|
||||
% cl is a TxN array of closing prices, where T is the number of trading
|
||||
% days, and N is the number of stocks in the S&P 500
|
||||
ret=(cl-lag(cl, 1))./lag(cl, 1); % daily returns
|
||||
|
||||
marketRet=smartmean(ret, 2); % equal weighted market index return
|
||||
|
||||
weights=-(ret-repmat(marketRet, [1 size(ret, 2)]));
|
||||
weights=weights./repmat(smartsum(abs(weights), 2), [1 size(weights, 2)]);
|
||||
|
||||
dailyret=smartsum(backshift(1, weights).*ret, 2); % Capital is always one
|
||||
|
||||
dailyret(isnan(dailyret))=0;
|
||||
|
||||
plot(cumprod(1+dailyret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+dailyret).^(252/length(dailyret))-1, sqrt(252)*mean(dailyret)/std(dailyret));
|
||||
% APR=13.7%, Sharpe=1.3
|
||||
|
||||
% daily pnl with transaction costs deducted
|
||||
% onewaytcost=0.0005; % assume 5 basis points
|
||||
%
|
||||
% dailyretMinustcost=dailyret - ...
|
||||
% smartsum(abs(weights./cl-backshift(1, weights)./backshift(1, cl)).*backshift(1, cl), 2).*onewaytcost./smartsum(abs(weights), 2); % transaction costs are only incurred when the weights change
|
||||
%
|
||||
% annavgretMinustcost=252*smartmean(dailyretMinustcost, 1)*100
|
||||
%
|
||||
% sharpeMinustcost=sqrt(252)*smartmean(dailyretMinustcost, 1)/smartstd(dailyretMinustcost, 1)
|
||||
%
|
||||
% % switch to use open prices
|
||||
%
|
||||
ret=(op-backshift(1, cl))./backshift(1, cl); % daily returns
|
||||
|
||||
marketRet=smartmean(ret, 2); % equal weighted market index return
|
||||
|
||||
weights=-(ret-repmat(marketRet, [1 size(ret, 2)])); % weight of a stock is proportional to the negative distance to the market index.
|
||||
weights=weights./repmat(smartsum(abs(weights), 2), [1 size(weights, 2)]);
|
||||
|
||||
dailyret=smartsum(weights.*(cl-op)./op, 2)./smartsum(abs(weights), 2);
|
||||
dailyret(isnan(dailyret))=0;
|
||||
|
||||
plot(cumprod(1+dailyret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+dailyret).^(252/length(dailyret))-1, sqrt(252)*mean(dailyret)/std(dailyret));
|
||||
% APR=0.731553 Sharpe=4.713284
|
||||
|
||||
% annavgret=252*smartmean(dailyret, 1)*100
|
||||
%
|
||||
% sharpe=sqrt(252)*smartmean(dailyret, 1)/smartstd(dailyret,1) % Sharpe ratio should be about 0.25
|
||||
%
|
||||
% % daily pnl with transaction costs deducted
|
||||
% onewaytcost=0.0005; % assume 5 basis points
|
||||
%
|
||||
% dailyretMinustcost=dailyret - ...
|
||||
% smartsum(abs(weights./cl-backshift(1, weights)./backshift(1, cl)).*backshift(1, cl), 2).*onewaytcost./smartsum(abs(weights), 2); % transaction costs are only incurred when the weights change
|
||||
%
|
||||
% annavgretMinustcost=252*smartmean(dailyretMinustcost, 1)*100
|
||||
%
|
||||
% sharpeMinustcost=sqrt(252)*smartmean(dailyretMinustcost, 1)/smartstd(dailyretMinustcost, 1)
|
||||
%
|
||||
% % kelly optimal leverage
|
||||
%
|
||||
% f=smartmean(dailyretMinustcost, 1)/smartstd(dailyretMinustcost, 1)^2
|
||||
@@ -0,0 +1,4 @@
|
||||
function y=backshift(day,x)
|
||||
% y=backshift(day,x)
|
||||
assert(day>=0);
|
||||
y=[NaN(day,size(x,2), size(x, 3));x(1:end-day,:, :)];
|
||||
@@ -0,0 +1,43 @@
|
||||
clear;
|
||||
|
||||
topN=10; % Max number of positions
|
||||
entryZscore=1;
|
||||
lookback=20; % for MA
|
||||
|
||||
load('../Data/inputDataOHLCDaily_20120424', 'stocks', 'tday', 'op', 'hi', 'lo', 'cl');
|
||||
|
||||
stdretC2C90d=backshift(1, smartMovingStd(calculateReturns(cl, 1), 90));
|
||||
buyPrice=backshift(1, lo).*(1-entryZscore*stdretC2C90d);
|
||||
|
||||
retGap=(op-backshift(1, lo))./backshift(1, lo);
|
||||
|
||||
pnl=zeros(length(tday), 1);
|
||||
|
||||
positionTable=zeros(size(cl));
|
||||
|
||||
ma=backshift(1, smartMovingAvg(cl, lookback));
|
||||
|
||||
for t=2:size(cl, 1)
|
||||
hasData=find(isfinite(retGap(t, :)) & op(t, :) < buyPrice(t, :) & op(t, :) > ma(t, :));
|
||||
|
||||
[foo idxSort]=sort(retGap(t, hasData), 'ascend');
|
||||
positionTable(t, hasData(idxSort(1:min(topN, length(idxSort)))))=1;
|
||||
end
|
||||
|
||||
retO2C=(cl-op)./op;
|
||||
|
||||
|
||||
pnl=smartsum(positionTable.*(retO2C), 2);
|
||||
ret=pnl/topN;
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
fprintf(1, '%i - %i\n', tday(1), tday(end));
|
||||
fprintf(1, 'APR=%10.4f\n', prod(1+ret).^(252/length(ret))-1);
|
||||
|
||||
fprintf(1, 'Sharpe=%4.2f\n', mean(ret)*sqrt(252)/std(ret));
|
||||
% APR=8.7%, Sharpe=1.5
|
||||
|
||||
cumret=cumprod(1+ret)-1; % compounded ROE
|
||||
|
||||
plot(cumret);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
clear;
|
||||
|
||||
% 1 minute data on GLD-USO
|
||||
load('inputData_ETF', 'tday', 'syms', 'cl');
|
||||
idxG=find(strcmp('GLD', syms));
|
||||
idxU=find(strcmp('USO', syms));
|
||||
|
||||
x=cl(:, idxG);
|
||||
y=cl(:, idxU);
|
||||
|
||||
lookback=20; % Lookback set arbitrarily short
|
||||
hedgeRatio=NaN(size(x, 1), 1);
|
||||
for t=lookback:size(hedgeRatio, 1)
|
||||
regression_result=ols(y(t-lookback+1:t), [x(t-lookback+1:t) ones(lookback, 1)]);
|
||||
hedgeRatio(t)=regression_result.beta(1);
|
||||
end
|
||||
|
||||
y2=[x y];
|
||||
|
||||
yport=sum([-hedgeRatio ones(size(hedgeRatio))].*y2, 2); % The net market value of the portfolio is same as the "spread"
|
||||
hedgeRatio(1:lookback)=[]; % Removed because hedge ratio is indterminate
|
||||
yport(1:lookback)=[];
|
||||
y2(1:lookback, :)=[];
|
||||
|
||||
% Bollinger band strategy
|
||||
entryZscore=1;
|
||||
exitZscore=0;
|
||||
|
||||
MA=movingAvg(yport, lookback);
|
||||
MSTD=movingStd(yport, lookback);
|
||||
zScore=(yport-MA)./MSTD;
|
||||
|
||||
longsEntry=zScore < -entryZscore; % a long position means we should buy EWC
|
||||
longsExit=zScore > -exitZscore;
|
||||
|
||||
shortsEntry=zScore > entryZscore;
|
||||
shortsExit=zScore < exitZscore;
|
||||
|
||||
numUnitsLong=NaN(length(yport), 1);
|
||||
numUnitsShort=NaN(length(yport), 1);
|
||||
|
||||
numUnitsLong(1)=0;
|
||||
numUnitsLong(longsEntry)=1;
|
||||
numUnitsLong(longsExit)=0;
|
||||
numUnitsLong=fillMissingData(numUnitsLong); % fillMissingData can be downloaded from epchan.com/book2. It simply carry forward an existing position from previous day if today's positio is an indeterminate NaN.
|
||||
|
||||
numUnitsShort(1)=0;
|
||||
numUnitsShort(shortsEntry)=-1;
|
||||
numUnitsShort(shortsExit)=0;
|
||||
numUnitsShort=fillMissingData(numUnitsShort);
|
||||
|
||||
numUnits=numUnitsLong+numUnitsShort;
|
||||
positions=repmat(numUnits, [1 size(y2, 2)]).*[-hedgeRatio ones(size(hedgeRatio))].*y2; % [hedgeRatio -ones(size(hedgeRatio))] is the shares allocation, [hedgeRatio -ones(size(hedgeRatio))].*y2 is the dollar capital allocation, while positions is the dollar capital in each ETF.
|
||||
pnl=sum(lag(positions, 1).*(y2-lag(y2, 1))./lag(y2, 1), 2); % daily P&L of the strategy
|
||||
ret=pnl./sum(abs(lag(positions, 1)), 2); % return is P&L divided by gross market value of portfolio
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
figure;
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.178249 Sharpe=0.964673
|
||||
|
||||
% Save this for future use
|
||||
% save('bollinger', 'hedgeRatio', 'MA', 'MSTD');
|
||||
@@ -0,0 +1,33 @@
|
||||
function [maxDD maxDDD]=calculateMaxDD(cumret)
|
||||
% [maxDD maxDDD]=calculateMaxDD(cumret)
|
||||
% calculation of maximum drawdown and maximum drawdown duration based on
|
||||
% cumulative COMPOUNDED returns. cumret must be a compounded cumulative return.
|
||||
% Same as calculateMaxDD_cpd for backward compatibility.
|
||||
% written by:
|
||||
% Ernest Chan
|
||||
%
|
||||
% Author of “Quantitative Trading:
|
||||
% How to Start Your Own Algorithmic Trading Business”
|
||||
%
|
||||
% ernest@epchan.com
|
||||
% www.epchan.com
|
||||
|
||||
highwatermark=zeros(size(cumret)); % initialize high watermarks to zero.
|
||||
|
||||
drawdown=zeros(size(cumret)); % initialize drawdowns to zero.
|
||||
|
||||
drawdownduration=zeros(size(cumret)); % initialize drawdown duration to zero.
|
||||
|
||||
for t=2:length(cumret)
|
||||
highwatermark(t)=max(highwatermark(t-1), cumret(t));
|
||||
drawdown(t)=(1+cumret(t))./(1+highwatermark(t))-1; % drawdown on each day
|
||||
if (drawdown(t)==0)
|
||||
drawdownduration(t)=0;
|
||||
else
|
||||
drawdownduration(t)=drawdownduration(t-1)+1;
|
||||
end
|
||||
end
|
||||
|
||||
maxDD=min(drawdown); % maximum drawdown
|
||||
|
||||
maxDDD=max(drawdownduration); % maximum drawdown duration
|
||||
@@ -0,0 +1,9 @@
|
||||
function rlag=calculateReturns(prices, lag)
|
||||
% rlag=calculateReturns(prices, lag) returns the lagged returns based on
|
||||
% the price series
|
||||
|
||||
% rlag=log(prices)-backshift(lag, log(prices));
|
||||
prevPrices=backshift(lag, prices);
|
||||
rlag=(prices-prevPrices)./prevPrices;
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
clear;
|
||||
|
||||
% load('inputDataDaily_VX_20120507', 'tday', 'contracts', 'cl');
|
||||
load('//dellquad/Futures_data/inputDataDaily_CL_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_TU_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_BR_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_HG_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_C2_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_HO2_20120813', 'tday', 'contracts', 'cl');
|
||||
|
||||
% Find spot prices
|
||||
spotIdx=find(strcmp(contracts, '0000$'));
|
||||
spot=cl(:, spotIdx);
|
||||
cl(:, spotIdx)=[];
|
||||
contracts(spotIdx)=[];
|
||||
|
||||
% T=[1:length(spot)]';
|
||||
% isBadData=~isfinite(spot);
|
||||
% spot(isBadData)=[];
|
||||
% T(isBadData)=[];
|
||||
% res=ols(log(spot), [T ones(size(T, 1), 1)]);
|
||||
%
|
||||
% fprintf(1, 'Average annualized spot return=%f\n', 252*smartmean(res.beta(1)));
|
||||
|
||||
|
||||
|
||||
% Fitting gamma to forward curve
|
||||
gamma=NaN(size(tday));
|
||||
for t=1:length(tday)
|
||||
|
||||
FT=cl(t, :)';
|
||||
idx=find(isfinite(FT));
|
||||
idxDiff=fwdshift(1, idx)-idx; % ensure consecutive months futures
|
||||
if (length(idx) >= 5 && all(idxDiff(1:4)==1))
|
||||
FT=FT(idx(1:5)); % only uses the nearest 5 contracts
|
||||
T=[1:length(FT)]';
|
||||
% scatter(T, log(FT));
|
||||
res=ols(log(FT), [T ones(size(T, 1), 1)]);
|
||||
gamma(t)=-12*res.beta(1);
|
||||
end
|
||||
end
|
||||
gamma=fillMissingData(gamma);
|
||||
|
||||
% plot(gamma);
|
||||
|
||||
|
||||
%print -r300 -djpeg fig5_4
|
||||
% hold on;
|
||||
|
||||
% fprintf(1, 'Average annualized roll return=%f\n', smartmean(gamma));
|
||||
isGoodData=find(isfinite(gamma));
|
||||
results=adf(gamma(isGoodData), 0, 1);
|
||||
prt(results);
|
||||
|
||||
gammalag=lag(gamma(isGoodData), 1);
|
||||
deltaGamma=gamma(isGoodData)-gammalag;
|
||||
deltaGamma(1)=[];
|
||||
gammalag(1)=[];
|
||||
regress_results=ols(deltaGamma, [gammalag ones(size(gammalag))]);
|
||||
halflife=-log(2)/regress_results.beta(1);
|
||||
|
||||
fprintf(1, 'halflife=%f days\n', halflife);
|
||||
% halflife=36.394034 days
|
||||
|
||||
|
||||
lookback=round(halflife);
|
||||
ma=movingAvg(gamma, lookback);
|
||||
mstd=movingStd(gamma, lookback);
|
||||
zScore=(gamma-ma)./mstd;
|
||||
|
||||
% linear mean reversion strategy
|
||||
isExpireDate=false(size(cl));
|
||||
positions=zeros(size(cl));
|
||||
|
||||
isExpireDate=isfinite(cl) & ~isfinite(fwdshift(1, cl));
|
||||
|
||||
holddays=3*21;
|
||||
numDaysStart=holddays+10;
|
||||
numDaysEnd=10;
|
||||
spreadMonth=12; % No. months between far and near contracts.
|
||||
for c=1:length(contracts)-spreadMonth
|
||||
expireIdx=find(isExpireDate(:, c));
|
||||
expireIdx=expireIdx(end); % There may be some missing data earlier on
|
||||
if (c==1)
|
||||
startIdx=max(1, expireIdx-numDaysStart);
|
||||
endIdx=expireIdx-numDaysEnd;
|
||||
else % ensure next front month contract doesn't start until current one ends
|
||||
myStartIdx=endIdx+1;
|
||||
myEndIdx=expireIdx-numDaysEnd;
|
||||
if (myEndIdx-myStartIdx >= holddays)
|
||||
startIdx=myStartIdx;
|
||||
endIdx=myEndIdx;
|
||||
else
|
||||
startIdx=NaN;
|
||||
end
|
||||
end
|
||||
|
||||
if (~isempty(expireIdx) & endIdx > startIdx)
|
||||
positions(startIdx:endIdx, c)=-1; % Presume we long spread (long back contract, short front contract)
|
||||
positions(startIdx:endIdx, c+spreadMonth)=1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
positions(isnan(zScore), :)=0;
|
||||
positions(zScore > 0, :)=-positions(zScore > 0, :);
|
||||
% positions(zScore > 1, :)=-positions(zScore > 1, :);
|
||||
|
||||
ret=smartsum(lag(positions).*(cl-lag(cl, 1))./lag(cl, 1), 2)/2;
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
idx=find(tday==20080102);
|
||||
% idx=1;
|
||||
|
||||
cumret=cumprod(1+ret(idx:end))-1;
|
||||
plot(cumret); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret(idx:end)).^(252/length(ret(idx:end)))-1, sqrt(252)*mean(ret(idx:end))/std(ret(idx:end)));
|
||||
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'maxDD=%f maxDDD=%i\n', maxDD, maxDDD);
|
||||
% APR=0.083406 Sharpe=1.288661
|
||||
% maxDD=-0.053222 maxDDD=206
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
clear;
|
||||
|
||||
% 1 minute data on EWA-EWC
|
||||
load('inputData_ETF', 'tday', 'syms', 'cl');
|
||||
idxA=find(strcmp('EWA', syms));
|
||||
idxC=find(strcmp('EWC', syms));
|
||||
|
||||
x=cl(:, idxA);
|
||||
y=cl(:, idxC);
|
||||
|
||||
plot(x);
|
||||
hold on;
|
||||
plot(y, 'g');
|
||||
|
||||
legend('EWA', 'EWC');
|
||||
figure;
|
||||
|
||||
scatter(x, y);
|
||||
|
||||
figure;
|
||||
|
||||
regression_result=ols(y, [x ones(size(x))]);
|
||||
hedgeRatio=regression_result.beta(1);
|
||||
|
||||
plot(y-hedgeRatio*x);
|
||||
|
||||
% Assume a non-zero offset but no drift, with lag=1.
|
||||
results=cadf(y, x, 0, 1); % cadf is a function in the jplv7 (spatial-econometrics.com) package. We pick y to be the dependent variable again.
|
||||
|
||||
% Print out results
|
||||
prt(results);
|
||||
|
||||
% Output:
|
||||
% Augmented DF test for co-integration variables: variable 1,variable 2
|
||||
% CADF t-statistic # of lags AR(1) estimate
|
||||
% -3.64346635 1 -0.020411
|
||||
%
|
||||
% 1% Crit Value 5% Crit Value 10% Crit Value
|
||||
% -3.880 -3.359 -3.038
|
||||
|
||||
% Combine the two time series into a matrix y2 for input into Johansen test
|
||||
y2=[y, x];
|
||||
results=johansen(y2, 0, 1); % johansen test with non-zero offset but zero drift, and with the lag k=1.
|
||||
|
||||
% Print out results
|
||||
prt(results);
|
||||
|
||||
% Output:
|
||||
% Johansen MLE estimates
|
||||
% NULL: Trace Statistic Crit 90% Crit 95% Crit 99%
|
||||
% r <= 0 variable 1 19.983 13.429 15.494 19.935
|
||||
% r <= 1 variable 2 3.983 2.705 3.841 6.635
|
||||
%
|
||||
% NULL: Eigen Statistic Crit 90% Crit 95% Crit 99%
|
||||
% r <= 0 variable 1 16.000 12.297 14.264 18.520
|
||||
% r <= 1 variable 2 3.983 2.705 3.841 6.635
|
||||
|
||||
|
||||
% Adding IGE to the portfolio
|
||||
|
||||
idxI=find(strcmp('IGE', syms));
|
||||
z=cl(:, idxI);
|
||||
y3=[y2, z];
|
||||
|
||||
results=johansen(y3, 0, 1); % johansen test with non-zero offset but zero drift, and with the lag k=1.
|
||||
|
||||
% Print out results
|
||||
prt(results);
|
||||
|
||||
% Output:
|
||||
% Johansen MLE estimates
|
||||
% NULL: Trace Statistic Crit 90% Crit 95% Crit 99%
|
||||
% r <= 0 variable 1 34.429 27.067 29.796 35.463
|
||||
% r <= 1 variable 2 17.532 13.429 15.494 19.935
|
||||
% r <= 2 variable 3 4.471 2.705 3.841 6.635
|
||||
%
|
||||
% NULL: Eigen Statistic Crit 90% Crit 95% Crit 99%
|
||||
% r <= 0 variable 1 16.897 18.893 21.131 25.865
|
||||
% r <= 1 variable 2 13.061 12.297 14.264 18.520
|
||||
% r <= 2 variable 3 4.471 2.705 3.841 6.635
|
||||
|
||||
results.eig % Display the eigenvalues
|
||||
|
||||
% ans =
|
||||
%
|
||||
% 0.0112
|
||||
% 0.0087
|
||||
% 0.0030
|
||||
|
||||
results.evec % Display the eigenvectors
|
||||
|
||||
% ans =
|
||||
%
|
||||
% -1.0460 -0.5797 -0.2647
|
||||
% 0.7600 -0.1120 -0.0790
|
||||
% 0.2233 0.5316 0.0952
|
||||
|
||||
yport=sum(repmat(results.evec(:, 1)', [size(y3, 1) 1]).*y3, 2); % (net) market value of portfolio
|
||||
|
||||
% Find value of lambda and thus the halflife of mean reversion by linear regression fit
|
||||
ylag=lag(yport, 1); % lag is a function in the jplv7 (spatial-econometrics.com) package.
|
||||
deltaY=yport-ylag;
|
||||
deltaY(1)=[]; % Regression functions cannot handle the NaN in the first bar of the time series.
|
||||
ylag(1)=[];
|
||||
regress_results=ols(deltaY, [ylag ones(size(ylag))]); % ols is a function in the jplv7 (spatial-econometrics.com) package.
|
||||
halflife=-log(2)/regress_results.beta(1);
|
||||
|
||||
fprintf(1, 'halflife=%f days\n', halflife);
|
||||
|
||||
% halflife=22.662578 days
|
||||
%
|
||||
% Apply a simple linear mean reversion strategy to EWA-EWC-IGE
|
||||
lookback=round(halflife); % setting lookback to the halflife found above
|
||||
|
||||
numUnits =-(yport-movingAvg(yport, lookback))./movingStd(yport, lookback); % capital invested in portfolio in dollars. movingAvg and movingStd are functions from epchan.com/book2
|
||||
positions=repmat(numUnits, [1 size(y3, 2)]).*repmat(results.evec(:, 1)', [size(y3, 1) 1]).*y3; % results.evec(:, 1)' can be viewed as the capital allocation, while positions is the dollar capital in each ETF.
|
||||
pnl=sum(lag(positions, 1).*(y3-lag(y3, 1))./lag(y3, 1), 2); % daily P&L of the strategy
|
||||
ret=pnl./sum(abs(lag(positions, 1)), 2); % return is P&L divided by gross market value of portfolio
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
figure;
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.125739 Sharpe=1.391310
|
||||
@@ -0,0 +1,32 @@
|
||||
clear;
|
||||
|
||||
load('inputDataOHLCDaily_20120517', 'tday', 'syms', 'cl');
|
||||
idx=strmatch('TU', syms, 'exact');
|
||||
|
||||
tday=tday(:, idx);
|
||||
cl=cl(:, idx);
|
||||
|
||||
% Correlation tests
|
||||
for lookback=[1 5 10 25 60 120 250]
|
||||
for holddays=[1 5 10 25 60 120 250]
|
||||
ret_lag=(cl-backshift(lookback, cl))./backshift(lookback, cl);
|
||||
ret_fut=(fwdshift(holddays, cl)-cl)./cl;
|
||||
badDates=any([isnan(ret_lag) isnan(ret_fut)], 2);
|
||||
ret_lag(badDates)=[];
|
||||
ret_fut(badDates)=[];
|
||||
|
||||
if (lookback >= holddays)
|
||||
indepSet=[1:holddays:length(ret_lag)];
|
||||
else
|
||||
indepSet=[1:lookback:length(ret_lag)];
|
||||
end
|
||||
|
||||
ret_lag=ret_lag(indepSet);
|
||||
ret_fut=ret_fut(indepSet);
|
||||
|
||||
[cc, pval]=corrcoef(ret_lag, ret_fut);
|
||||
% fprintf(1, 'lookback=%3i holddays=%3i cc=%7.4f pval=%6.4f\n', lookback, holddays, cc(1, 2), pval(1, 2));
|
||||
fprintf(1, '%3i\t%3i\t%7.4f\t%6.4f\n', lookback, holddays, cc(1, 2), pval(1, 2));
|
||||
end
|
||||
end
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,55 @@
|
||||
clear;
|
||||
|
||||
% load('inputDataDaily_VX_20120507', 'tday', 'contracts', 'cl');
|
||||
load('//dellquad/Futures_data/inputDataDaily_CL_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_TU_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_BR_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_HG_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_C2_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('inputDataDaily_C2_20120813', 'tday', 'contracts', 'cl');
|
||||
% load('//dellquad/Futures_data/inputDataDaily_HO2_20120813', 'tday', 'contracts', 'cl');
|
||||
|
||||
% Find spot prices
|
||||
spotIdx=find(strcmp(contracts, '0000$'));
|
||||
spot=cl(:, spotIdx);
|
||||
cl(:, spotIdx)=[];
|
||||
contracts(spotIdx)=[];
|
||||
|
||||
T=[1:length(spot)]';
|
||||
isBadData=~isfinite(spot);
|
||||
spot(isBadData)=[];
|
||||
T(isBadData)=[];
|
||||
res=ols(log(spot), [T ones(size(T, 1), 1)]);
|
||||
|
||||
fprintf(1, 'Average annualized spot return=%f\n', 252*smartmean(res.beta(1)));
|
||||
|
||||
|
||||
|
||||
% Fitting gamma to forward curve
|
||||
gamma=NaN(size(tday));
|
||||
for t=1:length(tday)
|
||||
|
||||
FT=cl(t, :)';
|
||||
idx=find(isfinite(FT));
|
||||
idxDiff=fwdshift(1, idx)-idx; % ensure consecutive months futures
|
||||
if (length(idx) >= 5 && all(idxDiff(1:4)==1))
|
||||
FT=FT(idx(1:5)); % only uses the nearest 5 contracts
|
||||
T=[1:length(FT)]';
|
||||
% scatter(T, log(FT));
|
||||
res=ols(log(FT), [T ones(size(T, 1), 1)]);
|
||||
gamma(t)=-12*res.beta(1);
|
||||
end
|
||||
end
|
||||
isBadData=find(isnan(gamma));
|
||||
gamma(isBadData)=[];
|
||||
tday(isBadData)=[];
|
||||
|
||||
plot(gamma);
|
||||
|
||||
|
||||
%print -r300 -djpeg fig5_4
|
||||
% hold on;
|
||||
|
||||
fprintf(1, 'Average annualized roll return=%f\n', smartmean(gamma));
|
||||
|
||||
% save('C2_gamma', 'tday', 'gamma');
|
||||
@@ -0,0 +1,36 @@
|
||||
function my_prices=fillMissingData(prices, varargin)
|
||||
% my_prices=fillMissingData(prices) fills missing price with previous
|
||||
% day's price
|
||||
% my_prices=fillMissingData(prices, tday, cday) fills missing prices with previous day's price including non-trading days
|
||||
% as specified in cday
|
||||
|
||||
|
||||
if (nargin == 1)
|
||||
my_prices=prices;
|
||||
for t=2:size(my_prices, 1)
|
||||
missData=~isfinite(my_prices(t, :, :));
|
||||
my_prices(t, missData)=my_prices(t-1, missData);
|
||||
end
|
||||
else
|
||||
% We deal only with 2 dim prices array here.
|
||||
tday=varargin{1};
|
||||
cday=varargin{2};
|
||||
my_prices=NaN*zeros(size(cday, 1), size(prices, 2));
|
||||
|
||||
tdayIdx=find(tday==cday(1));
|
||||
if (~isempty(tdayIdx))
|
||||
my_prices(1, :)=prices(tdayIdx, :);
|
||||
end
|
||||
|
||||
for t=2:size(my_prices, 1)
|
||||
tdayIdx=find(tday==cday(t));
|
||||
if (~isempty(tdayIdx))
|
||||
my_prices(t, :)=prices(tdayIdx, :);
|
||||
|
||||
missData=find(~isfinite(my_prices(t, :)));
|
||||
my_prices(t, missData)=my_prices(t-1, missData);
|
||||
else
|
||||
my_prices(t, :)=my_prices(t-1, :);
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
function y=fwdshift(day,x)
|
||||
assert(day>=0);
|
||||
|
||||
|
||||
y=[x(day+1:end,:, :); NaN*ones(day,size(x,2), size(x, 3))];
|
||||
@@ -0,0 +1,35 @@
|
||||
clear;
|
||||
|
||||
entryZscore=0.1;
|
||||
|
||||
data=load('inputDataOHLCDaily_20120517', 'syms', 'tday', 'op', 'hi', 'lo', 'cl');
|
||||
idx=find(strcmp('FSTX', data.syms));
|
||||
|
||||
op=data.op(:, idx);
|
||||
hi=data.hi(:, idx);
|
||||
lo=data.lo(:, idx);
|
||||
cl=data.cl(:, idx);
|
||||
|
||||
stdretC2C90d=backshift(1, smartMovingStd(calculateReturns(cl, 1), 90));
|
||||
|
||||
longs= op >= backshift(1, hi).*(1+entryZscore*stdretC2C90d);
|
||||
shorts=op <= backshift(1, lo).*(1-entryZscore*stdretC2C90d);
|
||||
|
||||
positions=zeros(size(cl));
|
||||
|
||||
positions(longs)=1;
|
||||
positions(shorts)=-1;
|
||||
|
||||
ret=positions.*(op-cl)./op;
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
fprintf(1, '%s APR=%10.4f Sharpe=%4.2f\n', data.syms{idx}, prod(1+ret).^(252/length(ret))-1, mean(ret)*sqrt(252)/std(ret));
|
||||
% APR= 0.1327 Sharpe=1.44
|
||||
cumret=cumprod(1+ret)-1; % compounded ROE
|
||||
|
||||
plot(cumret);
|
||||
|
||||
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'Max DD =%f Max DDD in days=%i\n\n', maxDD, round(maxDDD));
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
clear;
|
||||
|
||||
|
||||
stks=load('../Data/inputDataOHLCDaily_20120424', 'stocks', 'tday','cl');
|
||||
etf=load('../Data/inputData_ETF', 'tday', 'syms', 'cl');
|
||||
|
||||
% Ensure data have same dates
|
||||
[tday idx1 idx2]=intersect(stks.tday, etf.tday);
|
||||
stks.cl=stks.cl(idx1, :);
|
||||
etf.cl=etf.cl(idx2, :);
|
||||
|
||||
% Use SPY
|
||||
idxS=find(strcmp('SPY', etf.syms));
|
||||
etf.cl=etf.cl(:, idxS);
|
||||
|
||||
trainDataIdx=find(tday>=20070101 & tday<=20071231);
|
||||
testDataIdx=find(tday > 20071231);
|
||||
|
||||
isCoint=false(size(stks.stocks));
|
||||
for s=1:length(stks.stocks)
|
||||
% Combine the two time series into a matrix y2 for input into Johansen test
|
||||
y2=[stks.cl(trainDataIdx, s), etf.cl(trainDataIdx)];
|
||||
badData=any(isnan(y2), 2);
|
||||
y2(badData, :)=[]; % remove any missing data
|
||||
|
||||
if (size(y2, 1) > 250)
|
||||
results=johansen(y2, 0, 1); % johansen test with non-zero offset but zero drift, and with the lag k=1.
|
||||
if (results.lr1(1) > results.cvt(1, 1))
|
||||
isCoint(s)=true;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
length(find(isCoint))
|
||||
% 98: there are 98 stocks that are cointegrating with SPY
|
||||
|
||||
% Form a long-only portfolio with all stocks that cointegrate with SPY, with equal
|
||||
% capital allocation
|
||||
yN=stks.cl(trainDataIdx, isCoint);
|
||||
logMktVal_long=sum(log(yN), 2); % The net market value of the long-only portfolio is same as the "spread"
|
||||
|
||||
% Confirm that the portfolio cointegrates with SPY
|
||||
ytest=[logMktVal_long, log(etf.cl(trainDataIdx))];
|
||||
results=johansen(ytest, 0, 1); % johansen test with non-zero offset but zero drift, and with the lag k=1.
|
||||
prt(results);
|
||||
|
||||
% Output:
|
||||
% Johansen MLE estimates
|
||||
% NULL: Trace Statistic Crit 90% Crit 95% Crit 99%
|
||||
% r <= 0 variable 1 15.869 13.429 15.494 19.935
|
||||
% r <= 1 variable 2 6.197 2.705 3.841 6.635
|
||||
%
|
||||
% NULL: Eigen Statistic Crit 90% Crit 95% Crit 99%
|
||||
% r <= 0 variable 1 9.671 12.297 14.264 18.520
|
||||
% r <= 1 variable 2 6.197 2.705 3.841 6.635
|
||||
|
||||
results.evec
|
||||
%
|
||||
% ans =
|
||||
%
|
||||
% 1.0939 -0.2799
|
||||
% -105.5600 56.0933
|
||||
|
||||
|
||||
% Apply linear mean-reversion model on test set
|
||||
yNplus=[stks.cl(testDataIdx, isCoint), etf.cl(testDataIdx)]; % Array of stock and ETF prices
|
||||
weights=[repmat(results.evec(1, 1), size(stks.cl(testDataIdx, isCoint))), ...
|
||||
repmat(results.evec(2, 1), size(etf.cl(testDataIdx)))]; % Array of log market value of stocks and ETF's
|
||||
|
||||
logMktVal=smartsum(weights.*log(yNplus), 2); % Log market value of long-short portfolio
|
||||
|
||||
lookback=5;
|
||||
numUnits=-(logMktVal-movingAvg(logMktVal, lookback))./movingStd(logMktVal, lookback); % capital invested in portfolio in dollars. movingAvg and movingStd are functions from epchan.com/book2
|
||||
positions=repmat(numUnits, [1 size(weights, 2)]).*weights; % positions is the dollar capital in each stock or ETF.
|
||||
pnl=smartsum(lag(positions, 1).*(log(yNplus)-lag(log(yNplus), 1)), 2); % daily P&L of the strategy
|
||||
ret=pnl./smartsum(abs(lag(positions, 1)), 2); % return is P&L divided by gross market value of portfolio
|
||||
ret(isnan(ret))=0;
|
||||
|
||||
figure;
|
||||
plot(cumprod(1+ret)-1); % Cumulative compounded return
|
||||
|
||||
fprintf(1, 'APR=%f Sharpe=%f\n', prod(1+ret).^(252/length(ret))-1, sqrt(252)*mean(ret)/std(ret));
|
||||
% APR=0.044930 Sharpe=1.319397
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,61 @@
|
||||
clear;
|
||||
|
||||
load('inputDataOHLCDaily_stocks_20120424');
|
||||
|
||||
lookback=252;
|
||||
holddays=25;
|
||||
topN=50;
|
||||
|
||||
% idxStart=find(tday==20100104);
|
||||
% idxEnd=find(tday==20120424);
|
||||
idxStart=find(tday==20070515);
|
||||
idxEnd=find(tday==20071231);
|
||||
% idxStart=find(tday==20080102);
|
||||
% idxEnd=find(tday==20091231);
|
||||
% tday=tday(idxStart:idxEnd);
|
||||
% cl=cl(idxStart:idxEnd, :);
|
||||
% op=op(idxStart:idxEnd, :);
|
||||
|
||||
% cl is a TxN array of closing prices, where T is the number of trading
|
||||
% days, and N is the number of stocks in the S&P 500
|
||||
ret=(cl- backshift(lookback,cl))./backshift(lookback,cl); % daily returnslongs=false(size(ret));
|
||||
shorts=false(size(ret));
|
||||
|
||||
positions=zeros(size(ret));
|
||||
for t=lookback+1:length(tday)
|
||||
[foo idx]=sort(ret(t, :), 'ascend');
|
||||
nodata=find(isnan(ret(t, :)));
|
||||
idx=setdiff(idx, nodata, 'stable');
|
||||
longs(t, idx(end-topN+1:end))=true;
|
||||
shorts(t, idx(1:topN))=true;
|
||||
end
|
||||
|
||||
for h=0:holddays-1
|
||||
long_lag=backshift(h, longs);
|
||||
long_lag(isnan(long_lag))=false;
|
||||
long_lag=logical(long_lag);
|
||||
|
||||
short_lag=backshift(h, shorts);
|
||||
short_lag(isnan(short_lag))=false;
|
||||
short_lag=logical(short_lag);
|
||||
|
||||
positions(long_lag)=positions(long_lag)+1;
|
||||
positions(short_lag)=positions(short_lag)-1;
|
||||
end
|
||||
|
||||
dailyret=smartsum(backshift(1, positions).*(cl-lag(cl))./lag(cl), 2)/(2*topN)/holddays;
|
||||
|
||||
dailyret(isnan(dailyret))=0;
|
||||
|
||||
cumret=cumprod(1+dailyret(idxStart:idxEnd))-1;
|
||||
|
||||
plot(cumret);
|
||||
tday=tday([idxStart:idxEnd]);
|
||||
|
||||
fprintf(1, 'Avg Ann Ret=%7.4f Sharpe ratio=%4.2f \n',252*smartmean(dailyret(idxStart:idxEnd)), sqrt(252)*smartmean(dailyret(idxStart:idxEnd))/smartstd(dailyret(idxStart:idxEnd)));
|
||||
fprintf(1, 'APR=%10.4f\n', prod(1+dailyret(idxStart:idxEnd)).^(252/length(dailyret(idxStart:idxEnd)))-1);
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'Max DD =%f Max DDD in days=%i\n\n', maxDD, round(maxDDD));
|
||||
% Avg Ann Ret= 0.0315 Sharpe ratio=0.40
|
||||
% APR= 0.0288
|
||||
% Max DD =-0.066923 Max DDD in days=182
|
||||
@@ -0,0 +1,81 @@
|
||||
clear;
|
||||
|
||||
load('../Data/AUDCAD_unequal_ret', 'ret');
|
||||
|
||||
moments={mean(ret), std(ret), skewness(ret), kurtosis(ret)};
|
||||
[ret_sim, type]=pearsrnd(moments{:}, 100000, 1);
|
||||
|
||||
g=inline('sum(log(1+f*R))/length(R)', 'f', 'R');
|
||||
% g=inline('prod(1+f*R)^(1/length(R))-1', 'f', 'R');
|
||||
|
||||
myf=0:23;
|
||||
myg=NaN(24, 1);
|
||||
for f=myf
|
||||
myg(f+1)=g(f, ret_sim);
|
||||
end
|
||||
|
||||
plot(myf, myg);
|
||||
|
||||
minusG=@(f)-g(f, ret);
|
||||
minusGsim=@(f)-g(f, ret_sim);
|
||||
|
||||
optimalF=fminbnd(minusGsim, 0, 20); % optimal leverage based on simulated returns
|
||||
fprintf(1, 'Optimal leverage=%f optimal growth rate=%f\n', optimalF, -minusGsim(optimalF));
|
||||
|
||||
minR=min(ret_sim); % minimum return in simulated series
|
||||
fprintf(1, 'minR=%f\n', minR);
|
||||
|
||||
maxDD=calculateMaxDD(cumprod(1+optimalF*ret_sim)-1); % max drawdown with optimal leverage
|
||||
fprintf(1, 'f=%i maxDD with optimal leverage=%f\n', optimalF, maxDD);
|
||||
|
||||
maxDD=calculateMaxDD(cumprod(1+optimalF/2*ret_sim)-1); % max drawdown with half of optimal leverage
|
||||
fprintf(1, 'f=%i maxDD with half of optimal leverage=%f\n', optimalF/2, maxDD);
|
||||
|
||||
maxDD=calculateMaxDD(cumprod(1+optimalF/7*ret_sim)-1); % max drawdown with 1/7 of optimal leverage
|
||||
fprintf(1, 'f=%i maxDD with 1/7 of optimal leverage=%f\n', optimalF/7, maxDD);
|
||||
|
||||
maxDD=calculateMaxDD(cumprod(1+optimalF/1.4*ret)-1); % max drawdown with 1/1.4 of optimal leverage for historical returns
|
||||
fprintf(1, 'f=%i maxDD for historical returns=%f\n', optimalF/1.4, maxDD);
|
||||
|
||||
|
||||
D=0.5;
|
||||
fprintf(1, 'Growth rate on simulated returns using D=%3.1f of optimal leverage on full account=%f\n', D, -minusGsim(optimalF*D));
|
||||
fprintf(1, 'MaxDD on simulated returns using D of optimal leverage on full account=%f\n', calculateMaxDD(cumprod(1+optimalF*D*ret_sim)-1));
|
||||
|
||||
% CPPI
|
||||
g_cppi=0;
|
||||
% g_debug=0;
|
||||
drawdown=0;
|
||||
for t=1:length(ret_sim)
|
||||
g_cppi=g_cppi+log(1+ret_sim(t)*D*optimalF*(1+drawdown));
|
||||
% g_cppi=(1+g_cppi)*(1+r(t)*D*optimalF*(1+drawdown))-1;
|
||||
% g_debug=g_debug+log(1+r(t)*D*optimalF)
|
||||
|
||||
% if (g_cppi/t >= 1)
|
||||
% keyboard;
|
||||
% end
|
||||
|
||||
drawdown=min(0, (1+drawdown)*(1+ret_sim(t))-1);
|
||||
end
|
||||
g_cppi=g_cppi/length(ret_sim);
|
||||
% g_cppi=(1+g_cppi)^(1/length(r))-1;
|
||||
|
||||
fprintf(1, 'Growth rate on simulated returns using CPPI=%f\n', g_cppi);
|
||||
|
||||
fprintf(1, 'Growth rate on historical returns using D of optimal leverage on full account=%f\n', -minusG(optimalF*D));
|
||||
fprintf(1, 'MaxDD on historical returns using D of optimal leverage on full account=%f\n', calculateMaxDD(cumprod(1+optimalF*D*ret)-1));
|
||||
|
||||
% CPPI
|
||||
g_cppi=0;
|
||||
drawdown=0;
|
||||
for t=1:length(ret)
|
||||
% g_cppi=(1+g_cppi)*(1+ret(t)*D*optimalF*(1+drawdown))-1;
|
||||
g_cppi=g_cppi+log(1+r(t)*D*optimalF*(1+drawdown));
|
||||
|
||||
drawdown=min(0, (1+drawdown)*(1+ret(t))-1);
|
||||
end
|
||||
g_cppi=g_cppi/length(ret);
|
||||
% g_cppi=(1+g_cppi)^(1/length(ret))-1;
|
||||
|
||||
fprintf(1, 'Growth rate on historical returns using CPPI=%f\n', g_cppi);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
function [mvavg] = movingAvg(x, T)
|
||||
% [mvavg]=movingAvg(x, T). create moving average series over T days. mvavg
|
||||
% has T-1 NaN in beginning.
|
||||
|
||||
assert(T>0);
|
||||
|
||||
mvavg = zeros(size(x,1)-T+1, size(x, 2));
|
||||
|
||||
for i=0:T-1
|
||||
mvavg = mvavg + x(1+i:end-T+1+i, :);
|
||||
end
|
||||
|
||||
mvavg = mvavg / T;
|
||||
|
||||
mvavg=[NaN*ones(T-1, size(x,2)); mvavg];
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
function sd=movingStd(x, T, varargin)
|
||||
% calculate standard deviation of x over T days. Expect T-1
|
||||
% NaN in the beginning of the series
|
||||
% [mvstd]=movingStd(x, lookback, period) creates moving std of lookback
|
||||
% periods. I.e. data is sampled every period.
|
||||
% This uses std which normalizes by N-1.
|
||||
|
||||
sd=NaN*ones(size(x));
|
||||
|
||||
if (nargin == 2)
|
||||
for t=T:size(x, 1)
|
||||
% for t=T:length(x)
|
||||
sd(t, :)=std(x(t-T+1:t, :));
|
||||
end
|
||||
else
|
||||
period=varargin{1};
|
||||
for t=T*period:size(x, 1)
|
||||
sd(t, :)=std(x(t-T*period+1:t, :));
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,69 @@
|
||||
function [earnann]=parseEaringsCalendarFromEarningsDotCom(prevDate,todayDate, allsyms)
|
||||
% [earnann]==parseEaringsCalendarFromEarningsDotCom(prevDate,todayDate, allsyms)
|
||||
|
||||
% allsyms=regexprep(allsyms, '\.', ''); % for earnings.com, BF.B is BFB
|
||||
allsyms=regexprep(allsyms, '-', ''); % for earnings.com, BF.B is BFB
|
||||
|
||||
earnann=zeros(size(allsyms));
|
||||
|
||||
prevEarningsFile=urlread(['http://www.earnings.com/earning.asp?date=', num2str(prevDate), '&client=cb']);
|
||||
todayEarningsFile=urlread(['http://www.earnings.com/earning.asp?date=', num2str(todayDate), '&client=cb']);
|
||||
|
||||
% patternSym='finance.yahoo.com/q\?s=[\w-%\.=&]*">([\w-\.]+)</a>';
|
||||
% patternSym='<a\s+href="http://finance.yahoo.com/q\?s=[\w%\.=&]*">([\w-\.]+)</a>';
|
||||
% patternSym='<a href="http://finance.yahoo.com/q\?s=.+">([\w-\.]+)</a>';
|
||||
% patternTime='<small>([\w:\s]+)</small>';
|
||||
|
||||
prevd=day(datenum(num2str(prevDate), 'yyyymmdd'));
|
||||
todayd=day(datenum(num2str(todayDate), 'yyyymmdd'));
|
||||
|
||||
prevmmm=datestr(datenum(num2str(prevDate), 'yyyymmdd'), 'mmm');
|
||||
todaymmm=datestr(datenum(num2str(todayDate), 'yyyymmdd'), 'mmm');
|
||||
|
||||
patternSym='<a\s+href="company.asp\?ticker=([%\*\w\._/-]+)&coid';
|
||||
|
||||
% prevDate
|
||||
patternPrevDateTime=['<td align="center"><nobr>', num2str(prevd), '-', num2str(prevmmm), '([ :\dABPMCO]*)</nobr>'];
|
||||
|
||||
symA=regexp(prevEarningsFile, patternSym , 'tokens');
|
||||
timeA=regexp(prevEarningsFile, patternPrevDateTime, 'tokens');
|
||||
|
||||
symsA=[symA{:}];
|
||||
timeA=[timeA{:}];
|
||||
|
||||
assert(length(symsA)==length(timeA));
|
||||
|
||||
isAMC=~cellfun('isempty', regexp(timeA, 'AMC'));
|
||||
|
||||
patternPM='[ ]+\d:\d\d[ ]+PM'; % e.g. ' 6:00 PM'
|
||||
|
||||
isAMC2=~cellfun('isempty', regexp(timeA, patternPM));
|
||||
|
||||
symsA=symsA(isAMC | isAMC2);
|
||||
|
||||
[foo, idxA, idxALL]=intersect(symsA, allsyms);
|
||||
earnann(idxALL)=1;
|
||||
|
||||
% today
|
||||
patternTodayDateTime=['<td align="center"><nobr>', num2str(todayd), '-', num2str(todaymmm), '([ :\dABPMCO]*)</nobr>'];
|
||||
|
||||
symA=regexp(todayEarningsFile, patternSym , 'tokens');
|
||||
timeA=regexp(todayEarningsFile, patternTodayDateTime, 'tokens');
|
||||
|
||||
symsA=[symA{:}];
|
||||
timeA=[timeA{:}];
|
||||
|
||||
symsA=symsA(1:length(timeA));
|
||||
|
||||
assert(length(symsA)==length(timeA));
|
||||
|
||||
isBMO=~cellfun('isempty', regexp(timeA, 'BMO'));
|
||||
|
||||
patternAM='[ ]+\d:\d\d[ ]+AM'; % e.g. ' 8:00 AM'
|
||||
|
||||
isBMO2=~cellfun('isempty', regexp(timeA, patternAM));
|
||||
|
||||
symsA=symsA(isBMO | isBMO2);
|
||||
|
||||
[foo, idxA, idxALL]=intersect(symsA, allsyms);
|
||||
earnann(idxALL)=1;
|
||||
@@ -0,0 +1,36 @@
|
||||
clear;
|
||||
|
||||
load('inputDataOHLCDaily_stocks_20120424', 'tday', 'stocks', 'op', 'cl');
|
||||
|
||||
e=load('earnannFile', 'earnann', 'tday');
|
||||
|
||||
[tday idx1 idx2]=intersect(tday, e.tday);
|
||||
op=op(idx1, :);
|
||||
cl=cl(idx1, :);
|
||||
earnann=e.earnann(idx2, :);
|
||||
|
||||
lookback=90;
|
||||
|
||||
retC2O=(op-backshift(1, cl))./backshift(1, cl);
|
||||
stdC2O=smartMovingStd(retC2O, lookback);
|
||||
|
||||
positions=zeros(size(cl));
|
||||
|
||||
longs=retC2O >= 0.5*stdC2O & earnann;
|
||||
shorts=retC2O <= -0.5*stdC2O & earnann;
|
||||
|
||||
positions(longs)=1;
|
||||
positions(shorts)=-1;
|
||||
|
||||
ret=smartsum(positions.*(cl-op)./op, 2)/30;
|
||||
cumret=cumprod(1+ret)-1;
|
||||
|
||||
plot(cumret);
|
||||
|
||||
fprintf(1, 'Avg Ann Ret=%7.4f Sharpe ratio=%4.2f \n',252*smartmean(ret), sqrt(252)*smartmean(ret)/smartstd(ret));
|
||||
fprintf(1, 'APR=%10.4f\n', prod(1+ret).^(252/length(ret))-1);
|
||||
[maxDD maxDDD]=calculateMaxDD(cumret);
|
||||
fprintf(1, 'Max DD =%f Max DDD in days=%i\n\n', maxDD, round(maxDDD));
|
||||
% Avg Ann Ret= 0.0667 Sharpe ratio=1.49
|
||||
% APR= 0.0680
|
||||
% Max DD =-0.026052 Max DDD in days=109
|
||||
@@ -0,0 +1,35 @@
|
||||
function [mvavg] = smartMovingAvg(x, T, varargin)
|
||||
% [mvavg]=movingAvg(x, lookback). create moving average series over T days. mvavg
|
||||
% has T-1 NaN in beginning. Ignore over days with NaN.
|
||||
% [mvavg]=movingAvg(x, lookback, period) creates moving avg of lookback
|
||||
% periods. I.e. data is sampled every period.
|
||||
|
||||
assert(T>0);
|
||||
|
||||
mvavg=zeros(size(x));
|
||||
|
||||
goodDays=isfinite(x);
|
||||
|
||||
xx=x;
|
||||
xx(~goodDays)=0;
|
||||
|
||||
numGoodDays=zeros(size(x));
|
||||
|
||||
if (nargin == 2)
|
||||
for i=0:T-1
|
||||
mvavg=mvavg+backshift(i, xx);
|
||||
numGoodDays=numGoodDays+isfinite(backshift(i, x));
|
||||
end
|
||||
else
|
||||
period=varargin{1};
|
||||
for i=0:T-1
|
||||
mvavg=mvavg+backshift(i*period, xx);
|
||||
numGoodDays=numGoodDays+isfinite(backshift(i*period, x));
|
||||
end
|
||||
end
|
||||
|
||||
nonzeroDays=numGoodDays>0;
|
||||
mvavg(nonzeroDays)=mvavg(nonzeroDays) ./ numGoodDays(nonzeroDays);
|
||||
mvavg(~nonzeroDays)=NaN;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
function sd=smartMovingStd(x, T, varargin)
|
||||
% calculate standard deviation of x over T days. Expect T-1
|
||||
% NaN in the beginning of the series
|
||||
% [mvstd]=smartMovingStd(x, lookback, period) creates moving std of lookback
|
||||
% periods. I.e. data is sampled every period.
|
||||
% This version is inefficient. See implementation of smartMovingCorrcoef
|
||||
% for faster implementation.
|
||||
|
||||
sd=NaN*ones(size(x));
|
||||
|
||||
if (nargin == 2)
|
||||
for t=T:size(x, 1)
|
||||
% for t=T:length(x)
|
||||
sd(t, :)=smartstd(x(t-T+1:t, :));
|
||||
end
|
||||
else
|
||||
period=varargin{1};
|
||||
for t=T*period:size(x, 1)
|
||||
sd(t, :)=smartstd(x(t-T*period+1:t, :));
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
function y = smartmean(x,dim)
|
||||
%SMARTMEAN Average or mean value ignoring NaN.
|
||||
%
|
||||
% Same as MEAN except that it returns the mean of the finite elements
|
||||
% instead of propagating NaN and Inf
|
||||
% Returns NaN if there are no finite elements
|
||||
%
|
||||
% For vectors, MEAN(X) is the mean value of the elements in X. For
|
||||
% matrices, MEAN(X) is a row vector containing the mean value of
|
||||
% each column. For N-D arrays, MEAN(X) is the mean value of the
|
||||
% elements along the first non-singleton dimension of X.
|
||||
%
|
||||
% MEAN(X,DIM) takes the mean along the dimension DIM of X.
|
||||
%
|
||||
% Example: If X = [0 1 2
|
||||
% 3 4 5]
|
||||
%
|
||||
% then mean(X,1) is [1.5 2.5 3.5] and mean(X,2) is [1
|
||||
% 4]
|
||||
%
|
||||
% See also MEDIAN, STD, MIN, MAX, COV.
|
||||
|
||||
% Copyright (c) 1984-98 by The MathWorks, Inc.
|
||||
% $Revision: 5.13 $ $Date: 1997/11/21 23:23:55 $
|
||||
|
||||
if nargin==1,
|
||||
% Determine which dimension SUM will use
|
||||
dim = min(find(size(x)~=1));
|
||||
if isempty(dim), dim = 1; end
|
||||
k=isfinite(x);
|
||||
x(~k)=0;
|
||||
warning off
|
||||
y=sum(x)./sum(k,dim);
|
||||
y(all(~isfinite(x)))=NaN;
|
||||
warning on
|
||||
else
|
||||
k=isfinite(x);
|
||||
x(~k)=0;
|
||||
warning off
|
||||
y=sum(x,dim)./sum(k,dim);
|
||||
y(all(~isfinite(x), dim))=NaN;
|
||||
warning on
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
function y = smartstd(x,dim)
|
||||
%SMARTSTD Standard deviation of finite elements.
|
||||
%
|
||||
% Same as STD except that it ignores NaN and Inf instead of
|
||||
% propagating them
|
||||
%
|
||||
% Normalizes by N, not N-1
|
||||
|
||||
if nargin<2,
|
||||
dim = min(find(size(x)~=1));
|
||||
if isempty(dim), dim = 1; end
|
||||
end
|
||||
|
||||
tile=ones(1,max(ndims(x),dim));
|
||||
tile(dim)=size(x,dim);
|
||||
|
||||
xc=x-repmat(smartmean(x,dim),tile); % Remove mean
|
||||
y=sqrt(smartmean(conj(xc).*xc,dim)); % normalize by N
|
||||
|
||||
% y=sqrt(smartsum(conj(xc).*xc,dim)/(smartsum(abs(sign(xc)), dim)-1)); %normalize by N-1
|
||||
@@ -0,0 +1,33 @@
|
||||
function y = smartsum(x,dim)
|
||||
%SMARTSUM Sum ignoring NaN.
|
||||
%
|
||||
% Same as SUM except that it returns the sum of the finite elements
|
||||
% instead of propagating NaN and Inf
|
||||
% Returns NaN if no element is finite
|
||||
|
||||
if nargin==1,
|
||||
% Determine which dimension SUM will use
|
||||
dim = min(find(size(x)~=1));
|
||||
if isempty(dim), dim = 1; end
|
||||
k=isfinite(x);
|
||||
% x(~k)=-9999*ones(size(x(~k)));
|
||||
% warning off
|
||||
% y=sum(x.*k)./(sum(k,dim)>0);
|
||||
% warning on
|
||||
x(~k)=0;
|
||||
y=sum(x, dim);
|
||||
y(sum(k, dim)==0)=NaN;
|
||||
|
||||
else
|
||||
% k=isfinite(x);
|
||||
% x(~k)=-9999*ones(size(x(~k)));
|
||||
% warning off
|
||||
% y=sum(x.*k,dim)./(sum(k,dim)>0);
|
||||
% warning on
|
||||
|
||||
k=isfinite(x);
|
||||
x(~k)=0;
|
||||
y=sum(x, dim);
|
||||
y(sum(k, dim)==0)=NaN;
|
||||
|
||||
end
|
||||
@@ -0,0 +1,59 @@
|
||||
clear;
|
||||
|
||||
% 1 minute data on USDCAD
|
||||
load('inputData_USDCAD', 'tday', 'hhmm', 'cl');
|
||||
|
||||
% Select the daily close at 16:59 ET.
|
||||
y=cl(hhmm==1659);
|
||||
|
||||
plot(y);
|
||||
|
||||
% Assume a non-zero offset but no drift, with lag=1.
|
||||
results=adf(y, 0, 1); % adf is a function in the jplv7 (spatial-econometrics.com) package.
|
||||
|
||||
% Print out results
|
||||
prt(results);
|
||||
|
||||
% Augmented DF test for unit root variable: variable 1
|
||||
% ADF t-statistic # of lags AR(1) estimate
|
||||
% -1.840744 1 0.994120
|
||||
%
|
||||
% 1% Crit Value 5% Crit Value 10% Crit Value
|
||||
% -3.458 -2.871 -2.594
|
||||
|
||||
% Find Hurst exponent
|
||||
|
||||
H=genhurst(log(y), 2);
|
||||
fprintf(1, 'H2=%f\n', H);
|
||||
|
||||
% Variance ratio test from Matlab Econometrics Toolbox
|
||||
[h,pValue]=vratiotest(log(y));
|
||||
|
||||
|
||||
fprintf(1, 'h=%i\n', h); % h=1 means rejection of random walk hypothesis, 0 means it is a random walk.
|
||||
fprintf(1, 'pValue=%f\n', pValue); % pValue is essentially the probability that the null hypothesis (random walk) is true.
|
||||
|
||||
|
||||
% Output:
|
||||
% h=0
|
||||
% pValue=0.367281
|
||||
|
||||
% Find value of lambda and thus the halflife of mean reversion by linear regression fit
|
||||
ylag=lag(y, 1); % lag is a function in the jplv7 (spatial-econometrics.com) package.
|
||||
deltaY=y-ylag;
|
||||
deltaY(1)=[]; % Regression functions cannot handle the NaN in the first bar of the time series.
|
||||
ylag(1)=[];
|
||||
regress_results=ols(deltaY, [ylag ones(size(ylag))]); % ols is a function in the jplv7 (spatial-econometrics.com) package.
|
||||
halflife=-log(2)/regress_results.beta(1);
|
||||
|
||||
fprintf(1, 'halflife=%f days\n', halflife);
|
||||
|
||||
% halflife=115.209794 days
|
||||
|
||||
% Apply a simple linear mean reversion strategy to USDCAD
|
||||
lookback=round(halflife); % setting lookback to the halflife found above
|
||||
mktVal=-(y-movingAvg(y, lookback))./movingStd(y, lookback); % capital in number of shares invested in USDCAD. movingAvg and movingStd are functions from epchan.com/book2
|
||||
pnl=lag(mktVal, 1).*(y-lag(y, 1))./lag(y, 1); % daily P&L of the strategy
|
||||
pnl(isnan(pnl))=0;
|
||||
figure;
|
||||
plot(cumsum(pnl)); % Cumulative P&L
|
||||
Reference in New Issue
Block a user