Created
February 19, 2018 23:00
-
-
Save Demandrel/11f077867bbbb9d0433becb9457d7742 to your computer and use it in GitHub Desktop.
Momentum builder for cryptocurrencies in R
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Loading libraries | |
library("tidyverse") | |
library("jsonlite") | |
library("shiny") | |
library("ggplot2") | |
library("tidyquant") | |
library("PortfolioAnalytics") | |
library("scales") | |
# Loading source files | |
setwd("/Users/Pro/Documents/02 SCOLAIRE/01 FAC/02 Master 2 - EF/Semester 2/Quant. Methods/") | |
options(encoding = 'UTF-8') | |
source("utilitaire.R") | |
source("ui.R") | |
server = function(input, output) { | |
output$stratReturns = renderPlot({ | |
prices = getHistoricalPrice(input$ticker) | |
returns = getStratRes(prices, as.numeric(input$lookback), as.logical(input$isMomentum), as.logical(input$isShortSells)) | |
ggplot(data = returns, aes(x = time, y = return_momentum)) + | |
geom_line(size = 1, color = "steelblue") + | |
labs(title = "Rendements de la stratégie", | |
y = "Rendements", | |
x = "Année") + | |
theme_alphaplot() | |
}) | |
output$stratCumReturns = renderPlot({ | |
prices = getHistoricalPrice(input$ticker) | |
returns = getStratRes(prices, as.numeric(input$lookback), as.logical(input$isMomentum), as.logical(input$isShortSells)) | |
ggplot(data = returns, aes(x = time, y = cum_return_momentum)) + | |
geom_line(size = 1, color = "steelblue") + | |
labs(title = "Rendements cumulés de la stratégie", | |
y = "Rendements cumulés", | |
x = "Année") + | |
theme_alphaplot() | |
}) | |
output$riskStrat = renderPlot({ | |
data = getHistoricalPrice(input$ticker) | |
returns = getStratRes(data, as.numeric(input$lookback), as.logical(input$isMomentum), as.logical(input$isShortSells)) | |
risks = getPerformanceMetrics(returns[c(1,7)]) | |
ggplot(data = risks, aes(y = risk_value , x = mesure)) + | |
geom_bar(stat = "identity", fill = "steelblue") + | |
geom_text(aes(label = round(risk_value, 2)), vjust = -0.5, color = "black", size = 4.5) + | |
geom_hline(yintercept = 0, size = 1) + | |
labs(title = paste("Mesures de risques de la stratégie sur le", input$ticker), | |
subtitle = "", | |
y = "Valeur de la mesure", | |
x = "Mesures de risque") + | |
theme_alphaplot() + | |
theme(axis.text.x = element_text(face = "bold", color = "black", size = 12, angle = 90)) + | |
theme(axis.line = element_line(colour = "steelblue", size = 1, linetype = "solid")) | |
}) | |
} | |
shinyApp(ui, server) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ui = fluidPage( | |
# App title ---- | |
titlePanel("Momentum builder"), | |
# Sidebar layout with input and output definitions ---- | |
sidebarLayout( | |
# Sidebar panel for inputs ---- | |
sidebarPanel( | |
selectInput(inputId = "ticker", | |
label = "Choisir une cryptomonnaie :", | |
choices = list("Bitcoin (BTC)" = "BTC", | |
"Ethereum (ETH)" = "ETH", | |
"Ripple (XRP)" = "XRP", | |
"Bitcoin Cash (BCH)" = "BCH", | |
"Monero (XMR)" = "XMR", | |
"EOS (EOS)" = "EOS", | |
"IOTA (IOT)" = "IOT", | |
"Litecoin (LTC)" = "LTC", | |
"NEO (NEO)" = "NEO", | |
"OmiseGo (OMG)" = "OMG"), | |
selected = "BTC" | |
), | |
radioButtons("isMomentum", | |
label = "Type de stratégie :", | |
choices = list("Momentum" = TRUE, "Contrarian" = FALSE), | |
selected = TRUE | |
), | |
radioButtons("isShortSells", | |
label = "Ventes à découverts :", | |
choices = list("Oui" = TRUE, "Non" = FALSE), | |
selected = FALSE | |
), | |
selectInput(inputId = "lookback", | |
label = "Période de lookback :", | |
choices = list("1 jour" = 1, | |
"7 jours" = 7, | |
"30 jours" = 30, | |
"3 mois" = 90, | |
"6 mois" = 180, | |
"1 an" = 350), | |
selected = 7 | |
), | |
selectInput(inputId = "rebalance", | |
label = "Période de rebalancement du portefeuille :", | |
choices = list("Tous les jours (1 jour)" = 1, | |
"Toutes les semaines (7 jours)" = 7, | |
"Tous les mois (30 jours)" = 30, | |
"Tous les trois mois (90 jours)" = 90), | |
selected = 7 | |
), | |
sliderInput("transactionCosts", | |
label = "Coûts de transactions (en points de base) :", | |
min = 0, | |
max = 200, | |
value = 0 | |
), | |
actionButton("action", | |
label = "Tester cette stratégie", | |
icon("check-circle"), | |
width = "400px", | |
style ="color: #fff; background-color: #337ab7; border-color: #2e6da4" | |
) | |
), | |
# Main panel for displaying outputs ---- | |
mainPanel( | |
plotOutput(outputId = "stratCumReturns"), | |
plotOutput(outputId = "riskStrat") | |
) | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Librairies indispensables au fonctionnement de ce code, chargées depuis le fichier principal | |
#library("tidyverse") | |
#library("jsonlite") | |
#library("ggplot2") | |
#library("tidyquant") | |
#library("PortfolioAnalytics") | |
# Permet d'obtenir les prix de cloture dans un dataframe | |
getHistoricalPrice = function(ticker) { | |
api_base = "https://min-api.cryptocompare.com/data/histoday" | |
#Ticker de la crypto dont on veut les données | |
#Construction de la requete | |
url_request = paste(api_base, "?fsym=", ticker, "&tsym=USD&allData=True", sep = "") | |
#Récupération des données au format CHLO | |
request = fromJSON(url_request) | |
#On extrait les réponses au format dataframe du résultat de la requete | |
data = request[[4]] | |
data[[1]] = as.Date.POSIXct(data[[1]]) | |
return(data[, c(1, 2)]) | |
} | |
#Calcul les rendements au format xts a partir d'un dataframe | |
DfToTS = function (data) { | |
#Data doit être un dataframe avec les dates en colonne 1 et les prix en colonne 2 | |
data_xts = xts(data[[2]], order.by = data[[1]]) | |
return(data_xts) | |
} | |
# Calul les retours et signaux de la stratégie | |
getStratRes = function(data, loockback = 7, momentum = TRUE, short = FALSE) { | |
#signal1 = si return_n_days > 0 et signal2 sinon | |
#Signal sans vente à découvert | |
signal1 = as.numeric(momentum) | |
signal2 = as.numeric(!momentum) | |
#Si ventes à découvert et momentum classique | |
if(short == TRUE && momentum == TRUE) { | |
signal1 = 1 | |
signal2 = -1 | |
} | |
if(short == TRUE && momentum == FALSE) { | |
signal1 = -1 | |
signal2 = 1 | |
} | |
#On calcule les rendements à 1 jours, n jours, et le signal | |
res = mutate(data, | |
return_01d = close / lag(close, 1) - 1, | |
return_n_days = close / lag(close, loockback) - 1, | |
signal = ifelse(lag(return_n_days, 1) > 0.00, signal1, signal2)) | |
res = na.omit(res) | |
#On remplace les signaux 0 en -1 si les ventes à découvert sont autorisées : | |
res = filter(res, !is.na(signal)) %>% | |
mutate(return_buyhold = cumprod(1 + return_01d) - 1, | |
return_momentum = return_01d * signal, | |
cum_return_momentum = cumprod(1 + return_01d * signal) - 1) | |
return(res) | |
} | |
# Calcul ratio de Sharpe avec semi-volatilité négative | |
# i.e. on n'emputera pas la performance avec une volatilité positive | |
SharpeMaison = function(asset_returns) { | |
# Taux sans risque rf, hypothese: | |
rf = 0.05 | |
SemiVolNeg = SemiDeviation(asset_returns) | |
returns = Return.annualized(asset_returns) | |
SR = (returns-rf)/SemiVolNeg | |
#Sk = skewness(asset_returns) | |
#Ku = kurtosis(asset_returns) | |
#AdjSRatioMaison = SR * (1+ (Sk/6)*SR - ((Ku-3)/24)*SR^2) | |
return(SR) | |
} | |
# permet de retourner le tableau des indicateurs de performance d'un portefeuille | |
#Data doit être un dataframe avec les dates en colonne 1 et les rendements en colonne 2 | |
getPerformanceMetrics = function(data) { | |
asset_returns = xts(data[2], order.by = data[[1]]) | |
#Calcul des indicateurs statistiques : | |
asset_Return.annualized = Return.annualized(asset_returns) | |
asset_sd_annualized = StdDev.annualized(asset_returns) | |
asset_skewness = skewness(asset_returns) | |
asset_kurtosis = kurtosis(asset_returns)/3 | |
#Calcul des indicateurs de performance absolue : | |
CrazyBastardManagerSharpeRatio = 7 | |
asset_sharpe_ratio = SharpeMaison(asset_returns)/CrazyBastardManagerSharpeRatio | |
asset_sortino = SortinoRatio(asset_returns) | |
asset_drRatio = DRatio(asset_returns) | |
asset_SkewnessKurtosisRatio = SkewnessKurtosisRatio(asset_returns) | |
#On agrege les mesures dans un vecteur | |
risk_resume = c( | |
asset_Return.annualized, | |
asset_sd_annualized, | |
asset_skewness, | |
asset_kurtosis, | |
asset_sharpe_ratio, | |
asset_sortino, | |
asset_drRatio, | |
asset_SkewnessKurtosisRatio | |
) | |
risk_resume = data.frame( | |
risk_value = risk_resume, | |
mesure = c( | |
"Retour annualisé", | |
"Eccart type", | |
"Skewness", | |
"Kurtosis / Norm.Kurt.(3)", | |
"Ratio de Sharpe / 7", | |
"ratio de Sortino", | |
"drRation", | |
"Ratio skewness/Kurtosis" | |
) | |
) | |
return(risk_resume) | |
} | |
#Theme graphiques | |
theme_alphaplot = function(base_size = 11, | |
base_family = "") { | |
half_line <- base_size / 2 | |
theme( | |
line = element_line( | |
colour = "steelblue", | |
size = 0.5, | |
linetype = 1, | |
lineend = "butt" | |
), | |
rect = element_rect( | |
fill = "white", | |
colour = "black", | |
size = 0.5, | |
linetype = 1 | |
), | |
text = element_text( | |
family = base_family, | |
face = "plain", | |
colour = "black", | |
size = base_size, | |
lineheight = 0.9, | |
hjust = 0.5, | |
vjust = 0.5, | |
angle = 0, | |
margin = margin(), | |
debug = FALSE | |
), | |
axis.line = element_blank(), | |
axis.text = element_text(face = "bold", colour = "#535353"), | |
axis.text.x = element_text(margin = margin(t = 0.8 * half_line / 2), vjust = 1), | |
axis.text.y = element_text(margin = margin(r = 0.8 * half_line / 2), hjust = 1), | |
axis.ticks = element_blank(), | |
axis.ticks.length = unit(half_line / 2, "pt"), | |
axis.title.x = element_text( | |
face = "bold", | |
colour = "#535353", | |
margin = margin(t = 0.8 * half_line, b = 0.8 * half_line / 2) | |
), | |
axis.title.y = element_text( | |
face = "bold", | |
colour = "#535353", | |
angle = 90, | |
margin = margin(r = 0.8 * half_line, l = 0.8 * half_line / 2) | |
), | |
legend.background = element_rect(fill = "#F0F0F0", colour = NA), | |
legend.spacing = unit(0.2, "cm"), | |
legend.key = element_rect(fill = "#F0F0F0", colour = "#F0F0F0"), | |
legend.key.size = unit(1.2, "lines"), | |
legend.key.height = NULL, | |
legend.key.width = NULL, | |
legend.text = element_text(size = rel(0.8)), | |
legend.text.align = NULL, | |
legend.title = element_text(hjust = 0), | |
legend.title.align = NULL, | |
legend.position = "right", | |
legend.direction = NULL, | |
legend.justification = "center", | |
legend.box = NULL, | |
panel.background = element_rect(fill = "#F0F0F0", colour = NA), | |
panel.border = element_rect(fill = NA, colour = "#F0F0F0"), | |
panel.grid.major = element_line(colour = "#D0D0D0", size = 0.60), | |
panel.grid.minor = element_line(colour = "#D0D0D0", size = 0.60), | |
panel.spacing = unit(half_line, "pt"), | |
panel.ontop = FALSE, | |
strip.background = element_rect(fill = "grey85", colour = NA), | |
strip.text = element_text(colour = "grey10", size = rel(0.8)), | |
strip.text.x = element_text(margin = margin(t = half_line, b = half_line)), | |
strip.text.y = element_text( | |
angle = -90, | |
margin = margin(l = half_line, r = half_line) | |
), | |
strip.switch.pad.grid = unit(0.1, "cm"), | |
strip.switch.pad.wrap = unit(0.1, "cm"), | |
plot.background = element_rect(fill = "#F0F0F0", colour = NA), | |
plot.title = element_text( | |
face = "bold", | |
hjust = 0, | |
vjust = 2, | |
colour = "#3C3C3C", | |
size = rel(1.5), | |
margin = margin(b = half_line * 2.2) | |
), | |
plot.margin = margin(half_line, half_line * 3, half_line, half_line), | |
complete = TRUE | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment