In [1]:
using Dates, DelimitedFiles, Statistics, LinearAlgebra, StatsBase
include("jlFiles/printmat.jl")
include("jlFiles/printTable.jl")
include("jlFiles/OlsGMFn.jl")
Out[1]:
In [2]:
using Plots
#pyplot(size=(600,400))
gr(size=(480,320))
default(fmt = :svg)
In [3]:
x = readdlm("Data/SP500RfPs.csv",',',skipstart=1)
SP = x[:,2] #S&P 500 level
R = (SP[2:end]./SP[1:end-1] .- 1) * 100 #returns, %
T = length(R)
dN = Date.(x[2:end,1],"d/m/y") #convert to Date, 2:end as for R
println("Number of days in the sample: ",T)
The $s$th autocorrelation is
$\rho_s = \text{Corr}(R_t,R_{t-s})$
In large samples, $\sqrt{T}\hat{\rho}_{s}\sim N(0,1)$ if the true value is $\rho_s=0$ for all $s$
In [4]:
plags = 1:5
ρ = autocor(R,plags)
printblue("autocorrelations and t-stats:")
printTable([ρ sqrt(T)*ρ],["ρ","t-stat"],string.(plags))
An AR(1) is
$R_{t}=c+a_{1}R_{t-1}+\varepsilon_{t}$.
It can be estimated by OLS.
An asymmetric AR(1)
$R_{t} =\alpha+\beta Q_{t-1}R_{t-1}+\gamma(1-Q_{t-1})R_{t-1}+\varepsilon _{t}$,
where $Q_{t-1}=1 \ \text{ if } \ R_{t-1}<0$ and zero otherwise
In [5]:
y = R[2:end]
Tb = size(y,1)
x = [ones(Tb) R[1:end-1]]
(b,_,_,Covb,) = OlsGMFn(y,x)
Stdb = sqrt.(diag(Covb))
tstat = b./Stdb
printblue("Results from AR(1):")
printTable([b tstat],["coef","t-stat"],["constant","slope"])
In [6]:
Q = R[1:end-1] .< 0
x = [ones(Tb) Q.*R[1:end-1] (1.0.-Q).*R[1:end-1]]
(b,_,_,Covb,) = OlsGMFn(y,x)
Stdb = sqrt.(diag(Covb))
tstat = b./Stdb
printblue("Results from AR(1) with dummies: [intercept, slope neg, slope pos]")
printTable([b tstat],["coef","t-stat"],["constant","slope (down)","slope (up)"])
Do recursive estimation (longer and longer sample) an predict one period ahead (outside of the sample). Define an "out-of-sample" $R^2$ as
$R_{OS}^2 = 1- \frac{\text{MSE(forecasting model)}}{\text{MSE(benchmark forecast)}}$
In [7]:
y = R[2:end]
Tb = size(y,1)
x = [ones(Tb) Q.*R[1:end-1] (1.0.-Q).*R[1:end-1]]
(ϵ,e) = (zeros(Tb),zeros(Tb))
for t = 100:Tb
local b
b, = OlsGMFn(y[1:t-1],x[1:t-1,:])
ϵ[t] = y[t] - x[t,:]'b #forecast error, model
e[t] = y[t] - mean(y[1:t-1]) #forecast error, historical average
end
(ϵ,e) = (ϵ[100:end],e[100:end])
MSE_Model = mean(ϵ.^2) #MSE for out-of-sample period
MSE_Bench = mean(e.^2)
printlnPs("MSE of AR(1) model and historical mean: ",[MSE_Model MSE_Bench])
printlnPs("out-of-sample R2: ",1-MSE_Model/MSE_Bench)
In [8]:
xTicksLoc = Dates.value.([Date(1980);Date(1990);Date(2000);Date(2010)])
xTicksLab = ["1980";"1990";"2000";"2010"]
plot( dN[101:end],[cumsum(ϵ.^2) cumsum(e.^2)],
linecolor = [:blue :red],
linestyle = [:solid :dash],
label = ["AR(1) model" "historical mean"],
legend = :topleft,
xticks = (xTicksLoc,xTicksLab),
title = "Accumulation of square forecast errors" )
Out[8]:
The Mariano-Diebold and Clark-West tests compare the prediction errors of two models ($e$: benchmark; $\epsilon$: your model). Notice that MD test is not well suited for nested model (your model is an augmented version of the baseline model). Use the Clark-West in that case.
In [9]:
function MDCW(e,ϵ)
g = hcat(e.^2 - ϵ.^2, #Mariano-Diebold
2*e.*(e - ϵ)) #Clark&West
return g
end
Out[9]:
In [10]:
g = MDCW(e,ϵ)
Tg = size(g,1)
μ = mean(g,dims=1)
Stdμ = std(g,dims=1)/sqrt(Tg)
tstats = μ./Stdμ
println("t-stats for Mariano-Diebold and Clark_West tests")
printmat(tstats)
In [11]:
x = readdlm("Data/MomentumSR.csv",',')
dN = Date.(x[:,1],"yyyy-mm-dd") #Julia dates
y = convert.(Float64,x[:,2:end])
(Rm,Rf,R) = (y[:,1],y[:,2],y[:,3:end])
println("\nThe first few rows of dN, Rm and Rf")
printmat([dN[1:4] Rm[1:4] Rf[1:4]])
println("size of dN, Rm, Rf, R")
println(size(dN),"\n",size(Rm),"\n",size(Rf),"\n",size(R))
(T,n) = size(R); #number of periods and assets
In [12]:
Rp = fill(NaN,T)
for t = 2:T #loop over periods, save portfolio returns
#local sort1,w #only needed in REPL/script
sort1 = sortperm(R[t-1,:]) #sort1[1] is index of worst asset
w = zeros(n)
w[sort1[1:5]] .= -1/5
w[sort1[end-4:end]] .= 1/5
Rp[t] = w'R[t,:]
end
Rp = Rp[2:end]; #cut out t=1
Calculate the mean (excess) return, its standard deviation and the Sharpe ratio. Annualize by assuming 250 trading days per year. Compare with the excess return on passively holding an equity market index.
In [13]:
μ = mean(Rp) #strategy
σ = std(Rp)
Rme = Rm - Rf #market
μm = mean(Rme[2:end])
σm = std(Rme[2:end])
printblue("Annualised results:")
result = [μ*250;σ*sqrt(250);μ/σ*sqrt(250)]
resultm = [μm*250;σm*sqrt(250);μm/σm*sqrt(250)]
printTable([result resultm],["Strategy","market"],["mean","std","SR"])
In [ ]: