# Installation et chargement des packages nécessaires
library(quantmod)
## Warning: le package 'quantmod' a été compilé avec la version R 4.2.3
## Le chargement a nécessité le package : xts
## Warning: le package 'xts' a été compilé avec la version R 4.2.3
## Le chargement a nécessité le package : zoo
## Warning: le package 'zoo' a été compilé avec la version R 4.2.3
##
## Attachement du package : 'zoo'
## Les objets suivants sont masqués depuis 'package:base':
##
## as.Date, as.Date.numeric
## Le chargement a nécessité le package : TTR
## Warning: le package 'TTR' a été compilé avec la version R 4.2.3
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
library(PerformanceAnalytics)
## Warning: le package 'PerformanceAnalytics' a été compilé avec la version R
## 4.2.3
##
## Attachement du package : 'PerformanceAnalytics'
## L'objet suivant est masqué depuis 'package:graphics':
##
## legend
library(ggplot2)
## Warning: le package 'ggplot2' a été compilé avec la version R 4.2.3
library(tidyverse)
## Warning: le package 'tidyverse' a été compilé avec la version R 4.2.3
## Warning: le package 'tibble' a été compilé avec la version R 4.2.3
## Warning: le package 'tidyr' a été compilé avec la version R 4.2.3
## Warning: le package 'readr' a été compilé avec la version R 4.2.3
## Warning: le package 'purrr' a été compilé avec la version R 4.2.3
## Warning: le package 'dplyr' a été compilé avec la version R 4.2.3
## Warning: le package 'stringr' a été compilé avec la version R 4.2.3
## Warning: le package 'forcats' a été compilé avec la version R 4.2.3
## Warning: le package 'lubridate' a été compilé avec la version R 4.2.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ lubridate 1.9.3 ✔ tibble 3.2.1
## ✔ purrr 1.0.2 ✔ tidyr 1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::first() masks xts::first()
## ✖ dplyr::lag() masks stats::lag()
## ✖ dplyr::last() masks xts::last()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(TTR)
library(knitr)
library(rmarkdown)
# Définition des paramètres
montant_initial <- 10000
poids <- c(0.15, 0.20, 0.30, 0.35)
tickers <- c("AI.PA", "OR.PA", "SU.PA", "MC.PA") # Air Liquide, L'Oréal, Schneider, LVMH
date_debut <- Sys.Date() - years(3)
date_fin <- Sys.Date()
stop_loss_pct <- 0.05 # Stop loss à 5%
trailing_stop_pct <- 0.08 # Trailing stop à 8%
# Téléchargement des données
getSymbols(tickers, from = date_debut, to = date_fin, src = "yahoo")
## [1] "AI.PA" "OR.PA" "SU.PA" "MC.PA"
getSymbols("^FCHI", from = date_debut, to = date_fin, src = "yahoo")
## [1] "FCHI"
# Fonction originale pour appliquer le stop loss
apply_stop_loss <- function(prices) {
prices_clean <- na.omit(as.numeric(prices))
n <- length(prices_clean)
positions <- numeric(n)
positions[1] <- 1
current_stop <- prices_clean[1] * (1 - stop_loss_pct)
trailing_stop <- prices_clean[1] * (1 - trailing_stop_pct)
highest_price <- prices_clean[1]
in_trade <- TRUE
wait_days <- 0
for(i in 2:n) {
if(in_trade) {
if(prices_clean[i] > highest_price) {
highest_price <- prices_clean[i]
trailing_stop <- highest_price * (1 - trailing_stop_pct)
}
if(prices_clean[i] < current_stop || prices_clean[i] < trailing_stop) {
positions[i] <- 0
in_trade <- FALSE
wait_days <- 5
cat("Stop triggered at price:", prices_clean[i], "\n")
} else {
positions[i] <- 1
}
} else {
if(wait_days > 0) {
positions[i] <- 0
wait_days <- wait_days - 1
} else {
positions[i] <- 1
in_trade <- TRUE
current_stop <- prices_clean[i] * (1 - stop_loss_pct)
highest_price <- prices_clean[i]
trailing_stop <- highest_price * (1 - trailing_stop_pct)
cat("Re-entry at price:", prices_clean[i], "\n")
}
}
}
xts(positions, order.by=index(prices))
}
# Création du portefeuille
portfolio_data <- list()
for(i in 1:length(tickers)) {
portfolio_data[[i]] <- Ad(get(tickers[i]))
}
portfolio_prices <- do.call(merge, portfolio_data)
colnames(portfolio_prices) <- tickers
# Calcul des rendements de base
returns <- na.omit(Return.calculate(portfolio_prices))
# Application des stop loss pour chaque titre
stop_loss_signals <- matrix(NA, nrow=nrow(returns), ncol=ncol(returns))
colnames(stop_loss_signals) <- colnames(returns)
for(i in 1:ncol(portfolio_prices)) {
signals <- apply_stop_loss(portfolio_prices[,i])
stop_loss_signals[,i] <- as.numeric(signals[index(returns)])
}
## Stop triggered at price: 107.8007
## Re-entry at price: 118.2816
## Stop triggered at price: 121.3158
## Re-entry at price: 115.1044
## Stop triggered at price: 107.4191
## Re-entry at price: 110.4371
## Stop triggered at price: 109.8405
## Re-entry at price: 107.4191
## Stop triggered at price: 101.5059
## Re-entry at price: 103.1729
## Stop triggered at price: 136.3538
## Re-entry at price: 142.7507
## Stop triggered at price: 159.28
## Re-entry at price: 164.76
## Stop triggered at price: 161.5
## Re-entry at price: 160.24
## Stop triggered at price: 309.9063
## Re-entry at price: 339.7831
## Stop triggered at price: 322.7444
## Re-entry at price: 319.3474
## Stop triggered at price: 302.5901
## Re-entry at price: 313.8893
## Stop triggered at price: 295.552
## Re-entry at price: 311.0166
## Stop triggered at price: 295.2168
## Re-entry at price: 302.2071
## Stop triggered at price: 323.848
## Re-entry at price: 330.6467
## Stop triggered at price: 311.9742
## Re-entry at price: 312.836
## Stop triggered at price: 296.9404
## Re-entry at price: 304.601
## Stop triggered at price: 288.2266
## Re-entry at price: 332.083
## Stop triggered at price: 315.9959
## Re-entry at price: 324.3268
## Stop triggered at price: 392.9566
## Re-entry at price: 396.2559
## Stop triggered at price: 385.5702
## Re-entry at price: 385.9149
## Stop triggered at price: 412.4567
## Re-entry at price: 434.6159
## Stop triggered at price: 406.4491
## Re-entry at price: 409.1575
## Stop triggered at price: 410.05
## Re-entry at price: 405.45
## Stop triggered at price: 381.05
## Re-entry at price: 376.65
## Stop triggered at price: 365.2
## Re-entry at price: 370.5
## Stop triggered at price: 368.2
## Re-entry at price: 358.7
## Stop triggered at price: 337.7
## Re-entry at price: 329.8
## Stop triggered at price: 333.95
## Stop triggered at price: 127.3353
## Re-entry at price: 122.3332
## Stop triggered at price: 134.961
## Re-entry at price: 139.057
## Stop triggered at price: 131.2048
## Re-entry at price: 123.6356
## Stop triggered at price: 115.0472
## Re-entry at price: 124.5017
## Stop triggered at price: 115.5397
## Re-entry at price: 110.5952
## Stop triggered at price: 120.4263
## Re-entry at price: 114.3422
## Stop triggered at price: 114.4388
## Re-entry at price: 109.2432
## Stop triggered at price: 126.3366
## Re-entry at price: 127.9397
## Stop triggered at price: 139.5478
## Re-entry at price: 144.7434
## Stop triggered at price: 137.4232
## Re-entry at price: 145.9988
## Stop triggered at price: 137.964
## Re-entry at price: 146.192
## Stop triggered at price: 150.6679
## Re-entry at price: 151.5546
## Stop triggered at price: 143.5936
## Re-entry at price: 140.9531
## Stop triggered at price: 216.05
## Re-entry at price: 203.5
## Stop triggered at price: 245
## Re-entry at price: 238.8
## Stop triggered at price: 603.2485
## Re-entry at price: 548.1318
## Stop triggered at price: 583.9908
## Re-entry at price: 576.2183
## Stop triggered at price: 539.1793
## Re-entry at price: 551.7495
## Stop triggered at price: 527.7604
## Re-entry at price: 531.1188
## Stop triggered at price: 621.3179
## Re-entry at price: 617.4796
## Stop triggered at price: 578.8091
## Re-entry at price: 583.4151
## Stop triggered at price: 795.5875
## Re-entry at price: 784.5851
## Stop triggered at price: 788.6745
## Re-entry at price: 789.8429
## Stop triggered at price: 739.1146
## Re-entry at price: 710.3912
## Stop triggered at price: 680.3048
## Re-entry at price: 701.6282
## Stop triggered at price: 657.2288
## Re-entry at price: 646.5184
## Stop triggered at price: 668.0336
## Re-entry at price: 635.3532
## Stop triggered at price: 784.9173
## Re-entry at price: 780.0103
## Stop triggered at price: 738.4807
## Re-entry at price: 745.6138
## Stop triggered at price: 704.7963
## Re-entry at price: 719.7561
## Stop triggered at price: 680.5237
## Re-entry at price: 683.7931
## Stop triggered at price: 646.5421
## Re-entry at price: 624.6473
## Stop triggered at price: 613.4521
## Re-entry at price: 602.4551
## Stop triggered at price: 631.8795
## Re-entry at price: 610.2819
## Stop triggered at price: 567.0866
## Re-entry at price: 570.0588
## Stop triggered at price: 690.1
## Re-entry at price: 685.2
# Création des rendements avec stop loss
returns_with_stops <- returns * stop_loss_signals
# Calcul des rendements pondérés
portfolio_returns_no_stop <- Return.portfolio(returns, weights = poids)
portfolio_returns_with_stops <- Return.portfolio(returns_with_stops, weights = poids)
# Synchronisation avec le CAC40
cac40_returns <- Return.calculate(Ad(FCHI))
cac40_returns <- cac40_returns[index(returns)]
# Comparaison finale
comparison <- merge(portfolio_returns_no_stop, portfolio_returns_with_stops, cac40_returns)
colnames(comparison) <- c("Sans_Stop", "Avec_Stop", "CAC40")
# Calcul des performances cumulées
cumul_returns <- cumprod(1 + comparison)
# Fonction de calcul des statistiques de performance
performance_stats <- function(returns, cumul_rets) {
returns_num <- as.numeric(returns)
total_return <- (tail(as.numeric(cumul_rets), 1) - 1) * 100
vol <- sd(returns_num, na.rm = TRUE) * sqrt(252) * 100
sharpe <- (mean(returns_num, na.rm = TRUE) - 0.02/252) / sd(returns_num, na.rm = TRUE) * sqrt(252)
dd <- cumul_rets/cummax(cumul_rets) - 1
max_dd <- min(dd) * 100
c("Rendement total (%)" = total_return,
"Volatilité ann. (%)" = vol,
"Ratio de Sharpe" = sharpe,
"Max Drawdown (%)" = max_dd)
}
# Calcul des statistiques pour chaque stratégie
stats_sans_stop <- performance_stats(comparison$Sans_Stop, cumul_returns$Sans_Stop)
stats_avec_stop <- performance_stats(comparison$Avec_Stop, cumul_returns$Avec_Stop)
stats_cac40 <- performance_stats(comparison$CAC40, cumul_returns$CAC40)
# Création du tableau de performance
perf_table <- data.frame(
"Sans Stop-Loss" = stats_sans_stop,
"Avec Stop-Loss" = stats_avec_stop,
"CAC40" = stats_cac40
)
# Statistiques de trading
trades_count <- colSums(abs(diff(stop_loss_signals))) / 2
# Affichage des résultats
cat("\n=== STATISTIQUES DE PERFORMANCE ===\n")
##
## === STATISTIQUES DE PERFORMANCE ===
print(round(perf_table, 2))
## Sans.Stop.Loss Avec.Stop.Loss CAC40
## Rendement total (%) 34.33 114.98 17.37
## Volatilité ann. (%) 21.94 18.97 16.58
## Ratio de Sharpe 0.46 1.32 0.28
## Max Drawdown (%) -18.57 -11.22 -18.67
cat("\n=== STATISTIQUES DE TRADING ===\n")
##
## === STATISTIQUES DE TRADING ===
trades_summary <- data.frame(
Titre = names(trades_count),
NbTrades = trades_count,
TradesParMois = round(trades_count / (nrow(comparison)/21), 1),
"% du Total" = round(trades_count/sum(trades_count)*100, 1)
)
print(trades_summary)
## Titre NbTrades TradesParMois X..du.Total
## AI.PA AI.PA 8.0 0.2 12.6
## OR.PA OR.PA 19.5 0.5 30.7
## SU.PA SU.PA 15.0 0.4 23.6
## MC.PA MC.PA 21.0 0.6 33.1
# Graphique
plot_data <- data.frame(
Date = index(cumul_returns),
Sans_Stop = coredata(cumul_returns[,1]),
Avec_Stop = coredata(cumul_returns[,2]),
CAC40 = coredata(cumul_returns[,3])
)
ggplot(plot_data, aes(x = Date)) +
geom_line(aes(y = Sans_Stop, color = "Sans Stop-Loss"), linewidth = 1) +
geom_line(aes(y = Avec_Stop, color = "Avec Stop-Loss"), linewidth = 1) +
geom_line(aes(y = CAC40, color = "CAC40"), linewidth = 1) +
labs(title = "Performance comparée: Stratégies vs CAC40",
x = "Date",
y = "Valeur relative (base 1)",
color = "Stratégies") +
theme_minimal() +
scale_color_manual(values = c("Sans Stop-Loss" = "blue",
"Avec Stop-Loss" = "green",
"CAC40" = "red")) +
theme(legend.position = "bottom")
