Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
braverock
GitHub Repository: braverock/portfolioanalytics
Path: blob/master/sandbox/paper_analysis/R_interpretation/performanceanalysis_tactical.R
1433 views
1
2
# ----------------------------------------------------------------------------------
3
# In this script the out-of-sample returns of the optimized portfolios is analyzed
4
#
5
# ----------------------------------------------------------------------------------
6
7
setwd("c:/Documents and Settings/Administrator/Desktop/risk budget programs")
8
9
# Optimized portfolio you want to analyse out-of-sample performance through (Component) Sharpe ratios
10
11
riskcrit = mincriterion = percriskcontribcriterion ="mES"; # "mES" "StdDev" "GES"
12
estyears = 8;
13
frequency = "quarterly" ;yearly = F;
14
CC = TRUE
15
# Load additional programs to interpret the data
16
17
library(zoo); library("PerformanceAnalytics"); source("R_interpretation/pfolioreturn.R");
18
source("R_Allocation/estimators.R"); library(zoo);
19
library(reldist) ; library(sandwich); library(zoo);
20
21
histVaR = function( series ){ return(-quantile(series,probs=0.05) ) }
22
histCVaR = function( series ){ series = as.numeric(series) ; q = as.numeric(histVaR(series)) ; return( -mean( series[series<(-q)] )) }
23
24
# Define optimization criteria
25
26
names = c( "EqualWeight" , "MinRisk" , "MinRisk_PositionLimit" , "MinRisk_RiskLimit" ,
27
"MinRiskConc" , "MinRiskConc_PositionLimit", "EqualRisk" ,
28
"MinRisk_ReturnTarget", "MinRiskConc_ReturnTarget" )
29
if(CC){ names= paste( names, "_CC", sep="") }
30
31
namelabels = c( "Equal Weight" , "Min CVaR" , "Min CVaR + Position Limit" , "Min CVaR + CVaR Alloc Limit" ,
32
"Min CVaR Conc" , "Min CVaR Conc + 40% Position Limit", "Min CVaR + ERC constraint" , "Min CVaR + Return Target" , "Min CVaR Conc + Return Target" )
33
34
if(mincriterion=="mES"){ sel = c(1,2:4,7,5:6) }else{ sel = c(1,2:4,7,5:6) }
35
36
#sel = c(1,2,5)
37
names = names[sel]; namelabels = namelabels[sel];
38
39
criteria = paste( rep("weights/",length(names) ) , rep(mincriterion,length(names) ) , "/", names , sep="")
40
criteria[ criteria == "weights/StdDev/EqualWeight" ] = "weights/mES/EqualWeight"
41
42
# Load the data
43
44
firstyear = 1976 ; firstquarter = 1; lastyear = 2010; lastquarter = 2;
45
data = read.table( file= paste("data/","data.txt",sep="") ,header=T)
46
date = as.Date(data[,1],format="%Y-%m-%d")
47
48
nominalreturns = T;
49
if(nominalreturns){ monthlyR = zoo( data[,2:ncol(data)] , order.by = date ) }else{
50
monthlyR = zoo( data[,2:(ncol(data)-1)] , order.by = date ) - zoo( data[,ncol(data)] , order.by = date )
51
}
52
monthlyR = monthlyR[,1:4]
53
54
# estimation periods
55
ep = endpoints(monthlyR,on='quarters')
56
# select those for estimation period
57
ep.start = ep[1:(length(ep)-estyears*4)]+1
58
from = time(monthlyR)[ep.start]
59
from.estimation = seq( as.Date(paste(firstyear,"-01-01",sep="")), as.Date(paste(lastyear-estyears,"-07-01",sep="")), by="3 month")
60
ep.end = ep[(1+estyears*4):length(ep)]
61
to.estimation = time(monthlyR)[ep.end]
62
cPeriods = length(from);
63
64
# Define rebalancing periods:
65
66
ep = endpoints(monthlyR,on='quarters')
67
# select those for estimation period
68
ep.start = ep[(1+estyears*4):(length(ep)-1)]+1
69
from = time(monthlyR)[ep.start]
70
from = seq( as.Date( paste( as.character(firstyear+estyears),"-01-01",sep="")), as.Date("2010-04-01"), by="3 month")
71
ep.end = ep[(1+estyears*4):(length(ep)-1)]+3
72
to = time(monthlyR)[ep.end]
73
74
oosdates = time( window (monthlyR , start = from[1] , end = to[ length(to) ] ) )
75
76
makeTacticalWeights = TRUE
77
78
if(makeTacticalWeights){
79
80
# Compute daily out of sample returns, accounting for compounding effects
81
Returns.rebalancing( R = monthlyR , criteria = criteria, from = from, to = to , folder="/oosreturns/" )
82
83
load(paste(getwd(),"/","/oosreturns/", "simplereturns.Rdata" ,sep="") )
84
85
colnames(simplereturns) = names; date = time(simplereturns)
86
87
library("TTR")
88
weights_MinRisk = read.csv( file = paste("weights/", riskcrit , "/", "MinRisk_CC", ".csv" , sep="") );
89
90
names_alternatives = c( "EqualWeight" , "MinRisk_PositionLimit" , "MinRisk_RiskLimit" ,
91
"EqualRisk" , "MinRiskConc" , "MinRiskConc_PositionLimit")
92
if(CC){ names_alternatives = paste( names_alternatives, "_CC", sep="") }
93
94
for(name in names_alternatives){
95
weights_alternative = read.csv( file = paste("weights/", riskcrit , "/", name , ".csv" , sep="") );
96
weights_tact = weights_alternative
97
z = simplereturns[,name]
98
cz = cumprod( 1+z ) ; smaz = SMA(cz , n = 10)
99
risky = 1*(cz > smaz ) ; risky[is.na(risky)] = 0;
100
# end of rebal period value
101
risky = risky[to]
102
# we shift since for first rebal period no obs
103
risky = as.numeric(c( 0 , risky[-1] ))
104
weights_tact[ risky==0 , ] = weights_MinRisk[ risky==0 , ]
105
write.table( weights_tact , file = paste("weights/", riskcrit , "/", "tact_" , name , ".csv" , sep=""),
106
append = FALSE, quote = TRUE, sep = ",", eol = "\n", na = "NA", dec = ".", row.names = TRUE,col.names = TRUE, qmethod = "escape")
107
}
108
109
}
110
111
newnames = paste( rep( "tact_" , length(names_alternatives) ) , names_alternatives, sep="" )
112
tact_criteria = paste( rep("weights/",length(newnames) ) , rep(mincriterion,length(newnames) ) , "/", newnames , sep="")
113
114
Returns.rebalancing( R = monthlyR , criteria = tact_criteria, from = from, to = to , folder="/oosreturns/" )
115
load(paste(getwd(),"/","/oosreturns/", "simplereturns.Rdata" ,sep="") )
116
colnames(simplereturns) = names_alternatives; date = time(simplereturns)
117
118
119
120
# Bear periods
121
sp500 = window (monthlyR , start = from[1] , end = to[ length(to) ] )[,2]
122
bear = c(1:length(sp500))[sp500<mean(sp500)]
123
bear = c(1:length(sp500))[sp500<(-0.12)]
124
m.bear.dates = list();
125
i=1;
126
for( b in bear){
127
m.bear.dates[[i]] = c( b-0.5, b+0.5)
128
i = i + 1;
129
}
130
131
# http://www.aheadofthecurve-thebook.com/charts.html
132
# Vertical yellow bars in most charts denote bear markets (declines in the S&P 500 Index of 12% or more).
133
# IMPORTANT: The leading edge (left side) of the vertical yellow bars are thus stock market peaks,
134
# and the trailing edge (right side) are stock market troughs.
135
136
#source( paste(getwd(),"/R_interpretation/findDrawdowns.R",sep="") )
137
out = table.Drawdowns(sp500,top=10)
138
start.bear = out$From[out$Depth<(-0.12)]
139
end.bear = out$Trough[out$Depth<(-0.12)]
140
start.bear.index = c(1:length(sp500))[ time(sp500) ]
141
m.bear.dates = list()
142
v.bear.dates = c()
143
for( i in 1:length(start.bear) ){
144
m.bear.dates[[i]] = c( as.yearmon(start.bear[i]) , as.yearmon(end.bear[i]) )
145
v.bear.dates = c( v.bear.dates , seq(start.bear[i],end.bear[i],"days") )
146
}
147
v.bear.dates = as.Date( v.bear.dates )
148
149
# Table of summary statistics on out-of-sample returns
150
151
library(PerformanceAnalytics)
152
library(zoo)
153
oosreturns = simplereturns;
154
#zoo(simplereturns[,c(1:length(sel))],order.by = seq.Date(as.Date(from[1])+31, as.Date(tail(to,1)) + 1, by ="month") - 1)
155
156
v.nobear.dates = as.Date(setdiff( time(oosreturns) , v.bear.dates ))
157
158
# Mean, Standard Deviation, CVaR
159
histVaR = function( series ){ return(-quantile(series,probs=0.05) ) }
160
histCVaR = function( series ){ series = as.numeric(series) ; q = as.numeric(histVaR(series)) ; return( -mean( series[series<(-q)] )) }
161
162
163
########################################################################
164
165
166
oosreturns = window(oosreturns , start=as.Date("1984-01-01") , end=tail(time(oosreturns),1) )
167
Tstart = 1+(8-estyears)*4
168
#Tstart = 1
169
170
out_full = out_bear = out_bull = c()
171
172
print("Full period") #median, skew; exkur; histVaR
173
out_full = rbind( out_full , apply( oosreturns , 2 , 'mean' )*100*12 )
174
out_full = rbind( out_full , apply( oosreturns , 2 , 'sd' )*100*sqrt(12))
175
out_full = rbind( out_full , apply( oosreturns , 2 , 'skewness' ))
176
out_full = rbind( out_full , apply( oosreturns , 2 , 'kurtosis' ))
177
out_full = rbind( out_full , 100*apply( oosreturns , 2 , 'histCVaR'))
178
out_full = rbind( out_full , -100*apply( oosreturns , 2 , 'ES') )
179
180
181
oosreturns_bear = oosreturns[ v.bear.dates ]
182
oosreturns_bull = oosreturns[ v.nobear.dates ]
183
184
print("Bear market")
185
out_bear = rbind( out_bear , apply( oosreturns_bear , 2 , 'mean' )*100*12);
186
out_bear = rbind( out_bear , apply( oosreturns_bear , 2 , 'sd' )*100*sqrt(12))
187
out_bear = rbind( out_bear , apply( oosreturns_bear , 2 , 'skewness' ))
188
out_bear = rbind( out_bear , apply( oosreturns_bear , 2 , 'kurtosis' ))
189
out_bear = rbind( out_bear , 100*apply( oosreturns_bear , 2 , 'histCVaR'))
190
out_bear = rbind( out_bear , -100*apply( oosreturns_bear , 2 , 'ES'))
191
192
print("Bull market")
193
out_bull = rbind( out_bull , apply( oosreturns_bull , 2 , 'mean' )*100*12 ) ;
194
out_bull = rbind( out_bull , apply( oosreturns_bull , 2 , 'sd' )*100*sqrt(12))
195
out_bull = rbind( out_bull , apply( oosreturns_bull , 2 , 'skewness' ))
196
out_bull = rbind( out_bull , apply( oosreturns_bull , 2 , 'kurtosis' ))
197
out_bull = rbind( out_bull , 100*apply( oosreturns_bull , 2 , 'histCVaR'))
198
out_bull = rbind( out_bull , -100*apply( oosreturns_bull , 2 , 'ES'))
199
200
201
202
203
# Portfolio turnover per strategy:
204
205
pfgini = c();
206
turnover = c();
207
208
209
# Compute for each rebalancing period, the cumulative return:
210
211
cumR = c()
212
oosR = window (monthlyR , start = from[1] , end = to[ length(to) ] )
213
cRebalancing = length(from)
214
215
for( i in 1:cRebalancing ){
216
sel = seq( (i-1)*3+1 , i*3 )
217
cumR = rbind( cumR , apply((1+oosR[sel,]),2,'cumprod')[3,] )
218
}
219
220
# Load portfolio weights:
221
222
for( strat in 1:length(tact_criteria) ){
223
criterion = tact_criteria[strat];
224
wstart = read.csv( file = paste( criterion,".csv",sep=""),header = TRUE, sep = ",", na.strings = "NA", dec = ".")
225
wend = (wstart[Tstart:cRebalancing,]*cumR)/rowSums( wstart[Tstart:cRebalancing,]*cumR )
226
out = rowSums( abs( wstart[(Tstart+1):cRebalancing,]-wend[Tstart:(cRebalancing-1),] ))
227
turnover = cbind( turnover , out )
228
pfgini = c( pfgini , mean(apply( wstart, 1 , 'gini' )) )
229
}
230
231
pfturnover = colMeans( turnover )
232
out_full = rbind( out_full , pfgini , pfturnover*100 )
233
234
#out_full = rbind( out_full , turnover*100 )
235
236
library(xtable)
237
238
xtable(out_full)
239
xtable(out_bear)
240
xtable(out_bull)
241
242
# DRAWDOWNS
243
244
for( i in 1:length(tact_criteria) ){ # Print the drawdowns
245
print( tact_criteria[i] )
246
out = table.Drawdowns(oosreturns[,i],top=10)
247
out = out[out$Depth<=(-0.1),]
248
print( out )
249
}
250
251
# Risk concentration
252
253
outloss = outriskconc = c();
254
255
for( strat in 1:length(tact_criteria) ){
256
criterion = tact_criteria[strat];
257
#print( criterion );
258
weightedR = c(); portfolioVaR = c();
259
weights = read.csv( file = paste( criterion,".csv",sep=""),header = TRUE, sep = ",", na.strings = "NA", dec = ".")
260
261
# Step 1: compute for each optimal weight the corresponding historical quantile
262
263
for (row in 1:length(from)){
264
# For the determination of the historical quantile all returns preceding the rebalancing period are taken
265
previousR = window(monthlyR, start = time(monthlyR)[1] , end = as.Date(from[row]-1)) ;
266
pfoosR = rowSums( matrix( rep( as.numeric(weights[row,]),nrow(previousR)) , nrow = nrow(previousR) )*previousR )
267
# The weighted returns need the returns of the rebalancing period
268
Rrebalperiod = window(monthlyR, start = as.Date(from[row]) , end = as.Date(to[row])) ;
269
weightedR = rbind( weightedR , matrix( rep( as.numeric(weights[row,]),nrow(Rrebalperiod)) , nrow = nrow(Rrebalperiod) )*Rrebalperiod );
270
portfolioVaR = c( portfolioVaR , histVaR( pfoosR ) ) ;
271
}
272
273
# Step 2: compute the mean squared weighted return over months with beyond VaR losses
274
275
series = rowSums(weightedR) ;
276
#out = mean(weightedR[series<(-portfolioVaR),]^2);
277
downsidelosses = weightedR[series<(-portfolioVaR),]
278
#downsidelosses = weightedR[series<=-0.10,]
279
vES = rowSums(downsidelosses)
280
281
#print("Total portfolio loss")
282
out = apply( -downsidelosses , 1 , 'sum')
283
outloss = cbind( outloss , c( median(out) , max(out) ) )
284
#print("Max percentage loss")
285
out = apply( downsidelosses/ apply( downsidelosses , 1 , 'sum') , 1 , 'max')
286
outriskconc = cbind( outriskconc , c( mean(out) , max(out) ) )
287
}
288
289
xtable(outloss)
290
xtable(outriskconc)
291
292