Skip to content

Instantly share code, notes, and snippets.

@Demandrel
Created February 19, 2018 23:00
Show Gist options
  • Save Demandrel/11f077867bbbb9d0433becb9457d7742 to your computer and use it in GitHub Desktop.
Save Demandrel/11f077867bbbb9d0433becb9457d7742 to your computer and use it in GitHub Desktop.
Momentum builder for cryptocurrencies in R
# 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)
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")
)
)
)
#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