MSc in Quantitative Investing - Mathematical Finance &
Risk Analysis
Εργαστήριο Ανάλυσης Δεδομένων και Υπολογιστικής Χρηματοοικονομικής
Διδάσκουσα: Δήμητρα Κυριακοπούλου
Θέλουμε να δούμε πώς θα είχε αποδώσει ένα χαρτοφυλάκιο που μοιράζει το κεφάλαιο σε τρεις τίτλους με σταθερά βάρη, σε πέντε διαδοχικές περιόδους. Το ερώτημα είναι ποια είναι η απόδοση του χαρτοφυλακίου σε κάθε περίοδο, πόσο «κουνιέται» και τι αξία θα είχε στο τέλος μία αρχική μονάδα επενδυμένη σε αυτό.
Τα δεδομένα δίνονται στην εκφώνηση και γράφονται κατευθείαν εδώ, οπότε δεν χρειάζεται κανένα εξωτερικό αρχείο CSV. Οι αποδόσεις είναι σε δεκαδική μορφή: 0.020 σημαίνει 2%.
# Πίνακας αποδόσεων: 5 περίοδοι (γραμμές) x 3 τίτλοι (στήλες).
# Με byrow = TRUE κάθε γραμμή του c() γεμίζει μία περίοδο.
returns <- matrix(
c( 0.020, 0.010, -0.010,
0.015, -0.020, 0.010,
-0.020, -0.005, 0.015,
0.030, 0.010, 0.020,
0.010, -0.005, 0.025),
nrow = 5, byrow = TRUE
)
colnames(returns) <- c("Asset_A", "Asset_B", "Asset_C")
rownames(returns) <- paste0("Period_", 1:5)
# Βάρη χαρτοφυλακίου με ονόματα, στην ίδια σειρά με τις στήλες.
weights <- c(Asset_A = 0.50, Asset_B = 0.30, Asset_C = 0.20)
returns
## Asset_A Asset_B Asset_C
## Period_1 0.020 0.010 -0.010
## Period_2 0.015 -0.020 0.010
## Period_3 -0.020 -0.005 0.015
## Period_4 0.030 0.010 0.020
## Period_5 0.010 -0.005 0.025
weights
## Asset_A Asset_B Asset_C
## 0.5 0.3 0.2
dim(returns) # 5 γραμμές, 3 στήλες
## [1] 5 3
nrow(returns) # πλήθος περιόδων
## [1] 5
ncol(returns) # πλήθος τίτλων
## [1] 3
# Indexing σε πίνακα: [γραμμή, στήλη]. Αν αφήσουμε κενό, παίρνουμε όλα.
returns[3, ] # όλες οι αποδόσεις της 3ης περιόδου
## Asset_A Asset_B Asset_C
## -0.020 -0.005 0.015
returns[, "Asset_B"] # όλη η ιστορία του Asset_B
## Period_1 Period_2 Period_3 Period_4 Period_5
## 0.010 -0.020 -0.005 0.010 -0.005
Κάθε γραμμή είναι μία περίοδος και δείχνει τι έκαναν και οι τρεις τίτλοι μαζί εκείνη την περίοδο. Κάθε στήλη είναι ένας τίτλος και δείχνει τη χρονική πορεία των αποδόσεών του. Έχουμε λοιπόν 5 περιόδους και 3 τίτλους, τα Asset_A, Asset_B και Asset_C, με βάρη 50%, 30% και 20% αντίστοιχα.
# Μέσος και τυπική απόκλιση ανά στήλη, δηλαδή ανά τίτλο.
asset_means <- colMeans(returns)
asset_sds <- apply(returns, 2, sd) # 2 = εφαρμογή ανά στήλη
asset_means
## Asset_A Asset_B Asset_C
## 0.011 -0.002 0.012
asset_sds
## Asset_A Asset_B Asset_C
## 0.01884144 0.01254990 0.01350926
# Τα βάρη πρέπει να αθροίζονται σε 1 (όλο το κεφάλαιο επενδεδυμένο).
# Αντί για == ελέγχουμε ότι η απόσταση από το 1 είναι πολύ μικρή, γιατί ο
# υπολογιστής κάνει μικρά σφάλματα στρογγυλοποίησης στους δεκαδικούς.
sum(weights)
## [1] 1
abs(sum(weights) - 1) < 1e-8
## [1] TRUE
Το Asset_C έχει την υψηλότερη μέση απόδοση, ενώ το Asset_B είναι ο μόνος τίτλος με αρνητικό μέσο. Τα βάρη αθροίζονται σε 1, άρα όλο το κεφάλαιο είναι τοποθετημένο στους τρεις τίτλους.
Η απόδοση του χαρτοφυλακίου στην περίοδο \(t\) είναι ο σταθμισμένος μέσος των αποδόσεων των τίτλων:
\[R_{p,t} = \sum_{i=1}^{N} w_i R_{i,t}\]
όπου \(w_i\) το βάρος του τίτλου \(i\) και \(R_{i,t}\) η απόδοσή του στην περίοδο \(t\). Αν γράψουμε τις αποδόσεις ως πίνακα \(R\) (5 γραμμές x 3 στήλες) και τα βάρη ως διάνυσμα \(w\), τότε όλες οι περίοδοι υπολογίζονται μαζί:
\[R_p = Rw\]
Αυτό ακριβώς κάνει το returns %*% weights στην R. Ο
πολλαπλασιασμός πινάκων παίρνει κάθε γραμμή (περίοδο), την
πολλαπλασιάζει στοιχείο προς στοιχείο με τα βάρη και αθροίζει. Βγαίνει
δηλαδή το \(\sum_i w_i R_{i,t}\) για
κάθε \(t\), χωρίς loop.
# Επιστρέφει την απόδοση του χαρτοφυλακίου σε κάθε περίοδο.
# Πριν υπολογίσει, ελέγχει ότι τα βάρη "ταιριάζουν" με τον πίνακα.
portfolio_return <- function(returns, weights) {
# Έλεγχος 1: ένα βάρος για κάθε τίτλο (στήλη).
if (length(weights) != ncol(returns)) {
stop("Το πλήθος των βαρών πρέπει να είναι ίσο με το πλήθος των τίτλων")
}
# Έλεγχος 2: άθροισμα βαρών περίπου 1, με ανοχή 1e-8.
if (abs(sum(weights) - 1) > 1e-8) {
stop("Τα βάρη του χαρτοφυλακίου πρέπει να αθροίζονται σε 1")
}
# R_p = R w. Το %*% δίνει πίνακα 5x1, οπότε τον κάνουμε απλό διάνυσμα.
as.vector(returns %*% weights)
}
Η συνάρτηση ορίζεται εδώ, πριν χρησιμοποιηθεί, ώστε το Knit να
δουλεύει από κενό Environment. Τα stop() σταματούν την
εκτέλεση με κατανοητό μήνυμα αντί να βγάλουν σιωπηλά λάθος
αποτέλεσμα.
portfolio_returns <- portfolio_return(returns, weights)
portfolio_returns
## [1] 0.0110 0.0035 -0.0085 0.0220 0.0085
# Η 1η περίοδος "με το χέρι": βάρη επί αποδόσεις της 1ης γραμμής.
first_period_manual <- sum(weights * returns[1, ])
first_period_manual
## [1] 0.011
# Σύγκριση με ανοχή αντί για ==.
abs(portfolio_returns[1] - first_period_manual) < 1e-8
## [1] TRUE
# Και για όλες τις περιόδους, με loop: σε κάθε γραμμή το ίδιο sum().
manual_all <- numeric(nrow(returns))
for (t in seq_along(manual_all)) {
manual_all[t] <- sum(weights * returns[t, ])
}
# Η μέγιστη απόλυτη διαφορά πρέπει να είναι πρακτικά μηδέν.
max(abs(portfolio_returns - manual_all))
## [1] 1.734723e-18
Ο υπολογισμός της συνάρτησης συμφωνεί με τον χειροκίνητο, άρα το
%*% κάνει πράγματι τον σταθμισμένο μέσο που γράψαμε
παραπάνω.
Οι δύο κλήσεις μένουν σε σχόλια. Αν τρέξουν, η συνάρτηση σταματά με σφάλμα (αυτό θέλουμε), αλλά τότε θα σταματούσε και το Knit.
# Δοκιμή 1: λάθος πλήθος βαρών (2 βάρη για 3 τίτλους).
# Ελέγχει τη συνθήκη length(weights) != ncol(returns).
# portfolio_return(returns, c(0.60, 0.40))
# Δοκιμή 2: σωστό πλήθος, αλλά άθροισμα 0.9 αντί για 1.
# Ελέγχει τη συνθήκη abs(sum(weights) - 1) > 1e-8.
# portfolio_return(returns, c(0.50, 0.30, 0.10))
portfolio_mean <- mean(portfolio_returns) # μέση απόδοση ανά περίοδο
portfolio_sd <- sd(portfolio_returns) # μεταβλητότητα
negative_periods <- sum(portfolio_returns < 0) # TRUE = 1, FALSE = 0
# Σωρευτική αξία 1 μονάδας: κάθε απόδοση εφαρμόζεται στην αξία της
# προηγούμενης περιόδου (ανατοκισμός), γι' αυτό cumprod.
cumulative_value <- cumprod(1 + portfolio_returns)
portfolio_mean
## [1] 0.0073
portfolio_sd
## [1] 0.01112767
negative_periods
## [1] 1
cumulative_value
## [1] 1.011000 1.014539 1.005915 1.028045 1.036783
Η μέση απόδοση δείχνει τι κέρδισε κατά μέσο όρο το χαρτοφυλάκιο σε μία περίοδο. Η τυπική απόκλιση μετρά πόσο απλώνονται οι αποδόσεις γύρω από αυτόν τον μέσο, δηλαδή πόσο ασταθής ήταν η πορεία. Το πλήθος αρνητικών περιόδων μετρά πόσες φορές χάσαμε χρήματα, ενώ η σωρευτική αξία δείχνει σε τι ποσό έφτασε 1 μονάδα που επενδύθηκε στην αρχή και έμεινε μέσα σε όλες τις περιόδους.
returns_table <- data.frame(
Period = rownames(returns),
Portfolio_Return = portfolio_returns,
Cumulative_Value = cumulative_value
)
knitr::kable(
returns_table,
digits = 4,
caption = "Πίνακας 1: Αποδόσεις και σωρευτική αξία του χαρτοφυλακίου ανά περίοδο"
)
| Period | Portfolio_Return | Cumulative_Value |
|---|---|---|
| Period_1 | 0.0110 | 1.0110 |
| Period_2 | 0.0035 | 1.0145 |
| Period_3 | -0.0085 | 1.0059 |
| Period_4 | 0.0220 | 1.0280 |
| Period_5 | 0.0085 | 1.0368 |
summary_table <- data.frame(
Mean_Return = portfolio_mean,
Volatility = portfolio_sd,
Negative_Periods = negative_periods
)
knitr::kable(
summary_table,
digits = 4,
caption = "Πίνακας 2: Συνοπτικά μέτρα επίδοσης του χαρτοφυλακίου"
)
| Mean_Return | Volatility | Negative_Periods |
|---|---|---|
| 0.0073 | 0.0111 | 1 |
Και οι δύο πίνακες φτιάχνονται από R objects. Αν αλλάξουν τα δεδομένα ή τα βάρη, ξαναβγαίνουν σωστοί με ένα νέο Knit.
Η μέση απόδοση του χαρτοφυλακίου είναι 0.73% ανά περίοδο, με τυπική απόκλιση 1.11%. Αρνητικές περιόδους είχαμε 1 στις 5, την περίοδο 3. Η χαμηλότερη απόδοση ήταν -0.85% και η υψηλότερη 2.2%. Στο τέλος, 1 μονάδα έγινε 1.0368, δηλαδή συνολική μεταβολή 3.68%.
# Βάζουμε μπροστά την αρχική αξία 1 (περίοδος 0) για να φαίνεται η αφετηρία.
wealth_path <- c(1, cumulative_value)
periods <- 0:length(portfolio_returns)
plot(
periods,
wealth_path,
type = "l",
main = "Σωρευτική αξία αρχικής μονάδας",
xlab = "Περίοδος",
ylab = "Αξία χαρτοφυλακίου"
)
abline(h = 1, lty = 2) # το σημείο εκκίνησης για σύγκριση
Το γράφημα δείχνει πώς εξελίσσεται η αξία 1 μονάδας περίοδο με περίοδο. Η διακεκομμένη γραμμή στο 1 είναι η αρχική αξία, οπότε ό,τι είναι πάνω της σημαίνει κέρδος σε σχέση με την αφετηρία.
Η μέση απόδοση του χαρτοφυλακίου είναι θετική (0.73% ανά περίοδο), άρα σε αυτό το δείγμα ο συνδυασμός των τριών τίτλων κέρδισε κατά μέσο όρο. Η τυπική απόκλιση (1.11%) είναι μεγαλύτερη από τη μέση απόδοση, που σημαίνει ότι οι αποδόσεις διαφέρουν αισθητά από περίοδο σε περίοδο και η πορεία δεν είναι ομαλή. Ζημία είχαμε σε 1 από τις 5 περιόδους (την 3η), και η επόμενη περίοδος την κάλυψε γρήγορα.
Να τονίσω ότι η διαφοροποίηση βοήθησε: το Asset_B είχε αρνητικό μέσο, όμως με βάρος μόνο 30% και μαζί με τα A και C το χαρτοφυλάκιο έμεινε θετικό.
Τέλος, οι πέντε περίοδοι είναι απλώς εκπαιδευτικό δείγμα. Τα αποτελέσματα περιγράφουν μόνο αυτό που συνέβη εδώ και δεν αρκούν για να βγάλουμε συμπεράσματα για τη μελλοντική επίδοση του χαρτοφυλακίου ή για να εκτιμήσουμε αξιόπιστα τον ενδεχόμενο κίνδυνο.