MSc in Quantitative Investing - Mathematical Finance & Risk Analysis
Εργαστήριο Ανάλυσης Δεδομένων και Υπολογιστικής Χρηματοοικονομικής
Διδάσκουσα: Δήμητρα Κυριακοπούλου

Objective

Θέλουμε να δούμε πώς θα είχε αποδώσει ένα χαρτοφυλάκιο που μοιράζει το κεφάλαιο σε τρεις τίτλους με σταθερά βάρη, σε πέντε διαδοχικές περιόδους. Το ερώτημα είναι ποια είναι η απόδοση του χαρτοφυλακίου σε κάθε περίοδο, πόσο «κουνιέται» και τι αξία θα είχε στο τέλος μία αρχική μονάδα επενδυμένη σε αυτό.

Data

Τα δεδομένα δίνονται στην εκφώνηση και γράφονται κατευθείαν εδώ, οπότε δεν χρειάζεται κανένα εξωτερικό αρχείο 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, άρα όλο το κεφάλαιο είναι τοποθετημένο στους τρεις τίτλους.

Method

Μαθηματικη σχέση

Η απόδοση του χαρτοφυλακίου στην περίοδο \(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 μονάδα που επενδύθηκε στην αρχή και έμεινε μέσα σε όλες τις περιόδους.

Results

Πίνακες

returns_table <- data.frame(
  Period            = rownames(returns),
  Portfolio_Return  = portfolio_returns,
  Cumulative_Value  = cumulative_value
)

knitr::kable(
  returns_table,
  digits    = 4,
  caption   = "Πίνακας 1: Αποδόσεις και σωρευτική αξία του χαρτοφυλακίου ανά περίοδο"
)
Πίνακας 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: Συνοπτικά μέτρα επίδοσης του χαρτοφυλακίου"
)
Πίνακας 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 είναι η αρχική αξία, οπότε ό,τι είναι πάνω της σημαίνει κέρδος σε σχέση με την αφετηρία.

Interpretation

Η μέση απόδοση του χαρτοφυλακίου είναι θετική (0.73% ανά περίοδο), άρα σε αυτό το δείγμα ο συνδυασμός των τριών τίτλων κέρδισε κατά μέσο όρο. Η τυπική απόκλιση (1.11%) είναι μεγαλύτερη από τη μέση απόδοση, που σημαίνει ότι οι αποδόσεις διαφέρουν αισθητά από περίοδο σε περίοδο και η πορεία δεν είναι ομαλή. Ζημία είχαμε σε 1 από τις 5 περιόδους (την 3η), και η επόμενη περίοδος την κάλυψε γρήγορα.

Να τονίσω ότι η διαφοροποίηση βοήθησε: το Asset_B είχε αρνητικό μέσο, όμως με βάρος μόνο 30% και μαζί με τα A και C το χαρτοφυλάκιο έμεινε θετικό.

Τέλος, οι πέντε περίοδοι είναι απλώς εκπαιδευτικό δείγμα. Τα αποτελέσματα περιγράφουν μόνο αυτό που συνέβη εδώ και δεν αρκούν για να βγάλουμε συμπεράσματα για τη μελλοντική επίδοση του χαρτοφυλακίου ή για να εκτιμήσουμε αξιόπιστα τον ενδεχόμενο κίνδυνο.