Spaces:
Running
Running
library(shiny) | |
library(shinydashboard) | |
library(shinydashboardPlus) | |
library(shinyWidgets) | |
library(shinycssloaders) | |
library(DT) | |
library(plotly) | |
library(scico) | |
library(ggthemes) | |
library(scales) | |
library(stringr) | |
library(wesanderson) | |
library(data.table) | |
library(dtplyr) | |
library(parallel) | |
library(googlesheets4) | |
# devtools::install_github("woobe/Rnumerai") | |
library(Rnumerai) | |
# Options | |
options(encoding = "UTF-8") | |
# Pre-download all usernames | |
options(timeout = max(1000, getOption("timeout"))) | |
ls_username <- sort(get_leaderboard()$username) | |
# Prepare survey results data | |
if (TRUE) { | |
# Download | |
gs4_deauth() | |
d_survey_raw <- read_sheet(ss = "https://docs.google.com/spreadsheets/d/18AM4RkG5KiK3TlDGMx0z7X5Y5-eQE9kgLXM9ng_yXUk", | |
sheet = "Form responses 1") %>% data.table() | |
# Rename | |
colnames(d_survey_raw) <- c("timestamp", "country", "comments") | |
# Summarise | |
d_survey_summary <- | |
d_survey_raw %>% | |
lazy_dt() %>% | |
group_by(country) %>% | |
summarise(count = n()) %>% | |
arrange(desc(count), country) %>% | |
as.data.table() | |
} | |
# ============================================================================== | |
# Helper Functions | |
# ============================================================================== | |
# Download raw data | |
download_raw_data <- function(model_name) { | |
# Download data from Numerai | |
d_raw <- round_model_performances(model_name) | |
# Remove rows without CORR | |
d_raw <- d_raw[!is.na(d_raw$corrWMetamodel), ] | |
# Add the model name | |
d_raw$model <- model_name | |
# Return | |
return(as.data.table(d_raw)) | |
} | |
# Reformat | |
reformat_data <- function(d_raw) { | |
# Keep some columns only | |
col_keep <- c("model", "roundNumber", | |
"roundOpenTime", "roundResolveTime", | |
"roundResolved", "selectedStakeValue", | |
"corr20V2", "corr20V2Percentile", | |
"fncV3", "fncV3Percentile", | |
"tc", "tcPercentile", | |
"mmc", "mmcPercentile", | |
"corrWMetamodel", | |
"apcwnm", "mcwnm", | |
"roundPayoutFactor", "payout") | |
d_munged <- d_raw[, col_keep, with = FALSE] | |
# Date | |
d_munged[, roundOpenTime := as.Date(roundOpenTime)] | |
d_munged[, roundResolveTime := as.Date(roundResolveTime)] | |
# Reformat percentile | |
d_munged[, corr20V2Percentile := round(corr20V2Percentile * 100, 6)] | |
d_munged[, fncV3Percentile := round(fncV3Percentile * 100, 6)] | |
d_munged[, tcPercentile := round(tcPercentile * 100, 6)] | |
d_munged[, mmcPercentile := round(mmcPercentile * 100, 6)] | |
# Rename columns | |
colnames(d_munged) <- c("model", "round", | |
"date_open", "date_resolved", | |
"resolved", "stake", | |
"corrV2", "corrV2_pct", | |
"fncV3", "fncV3_pct", | |
"tc", "tc_pct", | |
"mmc", "mmc_pct", | |
"corr_meta", | |
"apcwnm", "mcwnm", | |
"pay_ftr", "payout") | |
# Return | |
return(d_munged) | |
} | |
# Generate Colour Palette | |
gen_custom_palette <- function(ls_model) { | |
# Extract info | |
n_limit <- 5 | |
n_coluor <- length(unique(ls_model)) | |
n_pal_rep <- ceiling(n_coluor / n_limit) | |
wes_pal_themes <- rep(c("Cavalcanti1", "Darjeeling1"), n_pal_rep) | |
# Generate | |
custom_palette <- c() | |
for (n_pal in 1:n_pal_rep) { | |
tmp_pal_name <- wes_pal_themes[n_pal] | |
tmp_pal <- wesanderson::wes_palette(name = tmp_pal_name, n = n_limit, type = "continuous") | |
custom_palette <- c(custom_palette, tmp_pal) | |
} | |
# Trim and return | |
return(custom_palette[1:n_coluor]) | |
} | |
# ============================================================================== | |
# UI | |
# ============================================================================== | |
ui <- shinydashboardPlus::dashboardPage( | |
title = "Shiny Numerati", | |
skin = "black-light", | |
options = list(sidebarExpandOnHover = TRUE), | |
header = shinydashboardPlus::dashboardHeader( | |
title = "✨ Shiny Numerati", | |
userOutput("user") | |
), | |
# ============================================================================ | |
# Sidebar | |
# ============================================================================ | |
sidebar = shinydashboardPlus::dashboardSidebar( | |
id = "sidebar", | |
sidebarMenu( | |
menuItem(text = "Start Here", tabName = "start", icon = icon("play")), | |
menuItem(text = "Performance Summary", tabName = "performance", icon = icon("line-chart")), # icon("credit-card") | |
menuItem(text = "Raw Data", tabName = "raw_data", icon = icon("download")), | |
menuItem(text = "Community Events", tabName = "community", icon = icon("users")), | |
menuItem(text = "About", tabName = "about", icon = icon("question-circle")) | |
), | |
minified = TRUE, | |
collapsed = FALSE | |
), | |
# ============================================================================ | |
# Main Body | |
# ============================================================================ | |
body = dashboardBody( | |
tabItems( | |
# ======================================================================== | |
# Start Here | |
# ======================================================================== | |
tabItem(tabName = "start", | |
fluidPage( | |
# ============================================================== | |
# Special script to keep the session alive for a bit longer | |
# ============================================================== | |
tags$head( | |
HTML( | |
" | |
<script> | |
var socket_timeout_interval | |
var n = 0 | |
$(document).on('shiny:connected', function(event) { | |
socket_timeout_interval = setInterval(function(){ | |
Shiny.onInputChange('count', n++) | |
}, 10000) | |
}); | |
$(document).on('shiny:disconnected', function(event) { | |
clearInterval(socket_timeout_interval) | |
}); | |
</script> | |
" | |
) | |
), | |
# ============================================================== | |
# First Page | |
# ============================================================== | |
markdown("# **Shiny Numerati**"), | |
markdown("### Community Dashboard for the Numerai Classic Tournament"), | |
br(), | |
fluidRow( | |
column(6, | |
markdown("## **Step 1: Select Your Models**"), | |
markdown("### First, click this ⬇"), | |
pickerInput(inputId = "model", | |
label = " ", | |
choices = ls_username, # Replace this with your own models if needed | |
multiple = TRUE, | |
width = "100%", | |
options = list( | |
`title` = "---------->>> HERE <<<----------", | |
`header` = "Notes: 1) Use the search box below to find and select your models. 2) Use 'Select All' for quick selection.", | |
size = 20, | |
`actions-box` = TRUE, | |
`live-search` = TRUE, | |
`live-search-placeholder` = "For example: V43_LGBM_CYRUS20", | |
`virtual-scroll` = TRUE, | |
`multiple-separator` = ", ", | |
`selected-text-format`= "count > 3", | |
`count-selected-text` = "{0} models selected (out of {1})", | |
`deselect-all-text` = "Deselect All", | |
`select-all-text` = "Select All" | |
) | |
) | |
), | |
column(6, | |
markdown("## **Step 2: Download Data**"), | |
markdown("### Next, click this ⬇ (it may take a while)"), | |
br(), | |
actionBttn(inputId = "button_download", | |
label = "Download Data from Numerai", | |
color = "primary", | |
icon = icon("cloud-download"), | |
style = "gradient", | |
block = TRUE | |
) | |
) | |
), | |
br(), | |
h3(strong(textOutput(outputId = "text_download"))), | |
verbatimTextOutput(outputId = "print_download"), | |
br(), | |
h3(strong(textOutput(outputId = "text_preview"))), | |
shinycssloaders::withSpinner(DTOutput("dt_model")), | |
br(), | |
h2(strong(textOutput(outputId = "text_next"))), | |
h3(textOutput(outputId = "text_note")), | |
br() | |
) | |
), | |
# ======================================================================== | |
# Payout Summary | |
# ======================================================================== | |
tabItem(tabName = "performance", | |
fluidPage( | |
markdown("# **Performance Summary**"), | |
markdown("### Remember to refresh the charts after making changes to model selection or settings below."), | |
markdown("### **NOTE**: the charts may take a while to render if you have selected a lot of models."), | |
br(), | |
fluidRow( | |
column(8, | |
markdown("## **Step 4: Adjust the Filter**"), | |
sliderInput(inputId = "range_round", | |
label = "Numerai Classic Tournament Rounds", | |
width = "100%", | |
step = 1, | |
min = 168, # first tournament round | |
max = Rnumerai::get_current_round(), # note: daily payouts from round 474 | |
value = c(496, Rnumerai::get_current_round()) | |
) | |
), | |
column(4, | |
markdown("## **Step 5: Generate Summary**"), | |
br(), | |
actionBttn(inputId = "button_filter", | |
label = "Generate / Refresh", | |
color = "primary", | |
icon = icon("refresh"), | |
style = "gradient", | |
block = TRUE) | |
) | |
), # end of fluidRow | |
br(), | |
tabsetPanel(type = "tabs", | |
# First Page - All KPIs | |
tabPanel("KPI (All)", | |
br(), | |
h3(strong(textOutput(outputId = "text_performance_chart"))), | |
h4(textOutput(outputId = "text_performance_chart_note")), | |
br(), | |
# Controls | |
fluidRow( | |
column(6, | |
markdown("#### **Pick ONE of the KPIs:**"), | |
pickerInput( | |
inputId = "kpi_choice", | |
choices = c("Score Multipliers: 0.5 x CORRv2 + 2.0 x MMCv2", | |
"Score Multipliers: 2.0 x CORRv2 + 1.0 x TC", | |
"MMCv2: The Latest and the Greatest MMC", | |
"Percentile: MMCv2", | |
"CORRv2: CORRelation with target cyrus_v4_20", | |
"Percentile: CORRv2", | |
"TC: True Contribtuion to the hedge fund's returns", | |
"Percentile: TC", | |
"FNCv3: Feature Neutral Correlation with respect to the FNCv3 features", | |
"Percentile: FNCv3", | |
"CWMM: Correlation With the Meta Model", | |
"MCWNM: Maximum Correlation With Numerai Models staked at least 10 NMR", | |
"APCWNM: Average Pairwise Correlation With Numerai Models staked at least 10 NMR", | |
"Payout", | |
"Rate of Return (%): Payout / Stake x 100"), | |
multiple = FALSE, | |
width = "95%") | |
), | |
column(2, | |
markdown("#### **Cumulative**"), | |
switchInput( | |
inputId = "kpi_cumulative", | |
onLabel = "Yes", | |
offLabel = "No", | |
value = TRUE) | |
), | |
column(2, | |
markdown("#### **Hide Pending**"), | |
switchInput( | |
inputId = "kpi_hide_pending", | |
onLabel = "Yes", | |
offLabel = "No", | |
value = FALSE) | |
), | |
column(2, | |
markdown("#### **Facet**"), | |
switchInput( | |
inputId = "kpi_facet", | |
onLabel = "Yes", | |
offLabel = "No", | |
value = FALSE) | |
) | |
), | |
h4(strong(textOutput(outputId = "text_performance_chart_data"))), | |
br(), | |
DTOutput("dt_kpi"), | |
br(), | |
br(), | |
h4(strong(textOutput(outputId = "text_performance_chart_title"))), | |
fluidRow(column(12, plotlyOutput("plot_kpi"))), | |
br() | |
), | |
# tabPanel("KPI (C&T)", | |
# | |
# br(), | |
# | |
# h3(strong(textOutput(outputId = "text_performance_models"))), | |
# | |
# h4(textOutput(outputId = "text_performance_models_note")), | |
# | |
# br(), | |
# | |
# fluidRow( | |
# column(width = 6, plotlyOutput("plot_performance_avg")), | |
# column(width = 6, plotlyOutput("plot_performance_sharpe")) | |
# ), | |
# | |
# br(), | |
# br(), | |
# br(), | |
# | |
# fluidRow(DTOutput("dt_performance_summary"), | |
# | |
# br(), | |
# | |
# markdown("#### **Notes**: | |
# | |
# - **avg_corrV2**: Average `CORRv2` | |
# - **sharpe_corrV2**: Sharpe Ratio of `CORRv2` | |
# | |
# - **avg_tc**: Average True Contribution (`TC`) | |
# - **sharpe_tc**: Sharpe Ratio of True Contribution (`TC`) | |
# | |
# - **avg_2C1T**: Average `2xCORRv2 + 1xTC` | |
# - **sharpe_2C1T**: Sharpe Ratio of `2xCORRv2 + 1xTC` | |
# | |
# "), | |
# | |
# br() | |
# ), | |
# | |
# | |
# br() | |
# | |
# ), # End of KPI (C&T) | |
tabPanel("Payout (Overview)", | |
br(), | |
h3(strong(textOutput(outputId = "text_payout_overview"))), | |
br(), | |
fluidRow( | |
class = "text-center", | |
valueBoxOutput("payout_n_round_resolved", width = 3), | |
valueBoxOutput("payout_resolved", width = 3), | |
valueBoxOutput("payout_average_resolved", width = 3), | |
valueBoxOutput("payout_avg_ror_resolved", width = 3), | |
valueBoxOutput("payout_n_round_pending", width = 3), | |
valueBoxOutput("payout_pending", width = 3), | |
valueBoxOutput("payout_average_pending", width = 3), | |
valueBoxOutput("payout_avg_ror_pending", width = 3), | |
valueBoxOutput("payout_n_round", width = 3), | |
valueBoxOutput("payout_total", width = 3), | |
valueBoxOutput("payout_average", width = 3), | |
valueBoxOutput("payout_avg_ror", width = 3) | |
), | |
br(), | |
shinycssloaders::withSpinner(plotlyOutput("plot_payout_net")), | |
br() | |
), | |
tabPanel("Payout Table (Rounds)", | |
br(), | |
h3(strong(textOutput(outputId = "text_payout_rnd"))), | |
br(), | |
DTOutput("dt_payout_summary"), | |
br() | |
), | |
tabPanel("Payout Table (Models)", | |
br(), | |
h3(strong(textOutput(outputId = "text_payout_ind"))), | |
br(), | |
DTOutput("dt_model_payout_summary"), | |
br() | |
), | |
# tabPanel("Payout (Sim)", | |
# | |
# br(), | |
# | |
# h3(strong(textOutput(outputId = "text_payout_sim"))), | |
# | |
# br(), | |
# | |
# # markdown("![new_tc_change](https://i.ibb.co/XjKwtzr/screenshot-2023-10-05-at-10.png)"), | |
# markdown("![new_mmc](https://forum.numer.ai/uploads/default/optimized/2X/0/0b04785bd7167ff261f26325bc926c107398e26a_2_1035x729.jpeg)"), | |
# | |
# | |
# br(), | |
# | |
# markdown("#### **Notes**: | |
# | |
# - **sum_pay**: Sum of Payouts | |
# - **shp_pay**: Sharpe Ratio of Payouts | |
# - **1C3T**: 1xCORRv2 + 3xTC (Original Degen Mode) | |
# - **1C0T**: 1xCORRv2 + 0xTC (Until the End of 2023) | |
# - **2C0T**: 2xCORRv2 + 0xTC (Until the End of 2023) | |
# - **2C1T**: 2xCORRv2 + 1xTC (Until the End of 2023) | |
# - **05C2M**: 0.5xCORRv2 + 2xMMCv2 (**New Payout Mode**) | |
# | |
# | |
# "), | |
# | |
# br(), | |
# | |
# markdown("### **Payout Simulation (Overall)**"), | |
# | |
# DTOutput("dt_payout_sim_overall"), | |
# | |
# br(), | |
# | |
# br(), | |
# | |
# markdown("### **Payout Simulation (Individual Models)**"), | |
# | |
# br(), | |
# | |
# DTOutput("dt_payout_sim_model"), | |
# | |
# br() | |
# | |
# ), # End of Payout Sim | |
tabPanel("Payout Chart (Rounds)", | |
br(), | |
h3(strong(textOutput(outputId = "text_payout_all_models"))), | |
br(), | |
shinycssloaders::withSpinner(plotlyOutput("plot_payout_stacked")), | |
br() | |
), | |
tabPanel("Payout Chart (Models)", | |
br(), | |
h3(strong(textOutput(outputId = "text_payout_ind_models"))), | |
br(), | |
shinycssloaders::withSpinner(plotlyOutput("plot_payout_individual")), | |
br() | |
) | |
# tabPanel("KPI (x~y)", | |
# | |
# br(), | |
# | |
# markdown("![image](https://media.giphy.com/media/cftSzNoCTfSyAWctcl/giphy.gif)"), | |
# | |
# br(), | |
# | |
# fluidRow( | |
# | |
# column(6, | |
# markdown("#### **X-Axis:**"), | |
# pickerInput( | |
# inputId = "kpi_xy_x", | |
# choices = c("CORRv2: CORRelation with target cyrus_v4_20", | |
# "MMCv2: The Latest and the Greatest MMC", | |
# "TC: True Contribtuion to the hedge fund's returns", | |
# "FNCv3: Feature Neutral Correlation with respect to the FNCv3 features", | |
# "Percentile: MMCv2", | |
# "Percentile: CORRv2", | |
# "Percentile: TC", | |
# "Percentile: FNCv3", | |
# "CWMM: Correlation With the Meta Model", | |
# "MCWNM: Maximum Correlation With Numerai Models staked at least 10 NMR", | |
# "APCWNM: Average Pairwise Correlation With Numerai Models staked at least 10 NMR"), | |
# | |
# multiple = FALSE, | |
# width = "95%") | |
# ), | |
# | |
# column(6, | |
# markdown("#### **Y-Axis:**"), | |
# pickerInput( | |
# inputId = "kpi_xy_y", | |
# choices = c("MMCv2: The Latest and the Greatest MMC", | |
# "CORRv2: CORRelation with target cyrus_v4_20", | |
# "TC: True Contribtuion to the hedge fund's returns", | |
# "FNCv3: Feature Neutral Correlation with respect to the FNCv3 features", | |
# "Percentile: MMCv2", | |
# "Percentile: CORRv2", | |
# "Percentile: TC", | |
# "Percentile: FNCv3", | |
# "CWMM: Correlation With the Meta Model", | |
# "MCWNM: Maximum Correlation With Numerai Models staked at least 10 NMR", | |
# "APCWNM: Average Pairwise Correlation With Numerai Models staked at least 10 NMR"), | |
# | |
# multiple = FALSE, | |
# width = "95%") | |
# ), | |
# | |
# column(2, | |
# markdown("#### **Control 1**"), | |
# switchInput( | |
# inputId = "kpi_xy_ctrl_1", | |
# onLabel = "Yes", | |
# offLabel = "No", | |
# value = TRUE) | |
# ), | |
# | |
# column(2, | |
# markdown("#### **Control 2**"), | |
# switchInput( | |
# inputId = "kpi_xy_ctrl_2", | |
# onLabel = "Yes", | |
# offLabel = "No", | |
# value = FALSE) | |
# ), | |
# | |
# column(2, | |
# markdown("#### **Control 3**"), | |
# switchInput( | |
# inputId = "kpi_xy_ctrl_3", | |
# onLabel = "Yes", | |
# offLabel = "No", | |
# value = FALSE) | |
# ), | |
# | |
# column(2, | |
# markdown("#### **Control 4**"), | |
# switchInput( | |
# inputId = "kpi_xy_ctrl_4", | |
# onLabel = "Yes", | |
# offLabel = "No", | |
# value = FALSE) | |
# ), | |
# | |
# column(2, | |
# markdown("#### **Control 5**"), | |
# switchInput( | |
# inputId = "kpi_xy_ctrl_5", | |
# onLabel = "Yes", | |
# offLabel = "No", | |
# value = FALSE) | |
# ), | |
# | |
# column(2, | |
# markdown("#### **Control 6**"), | |
# switchInput( | |
# inputId = "kpi_xy_ctrl_6", | |
# onLabel = "Yes", | |
# offLabel = "No", | |
# value = FALSE) | |
# ) | |
# | |
# | |
# ), | |
# | |
# br() | |
# | |
# ) # end of KPI (x~y) | |
) # end of tabsetPanel | |
) # end of fluidPage | |
), | |
# ======================================================================== | |
# Raw Data | |
# ======================================================================== | |
tabItem(tabName = "raw_data", | |
markdown("# **Download Raw Data**"), | |
markdown("### Wanna run your own analysis? No problem."), | |
markdown("### Remember to select your model(s) first."), | |
br(), | |
fluidRow( | |
column(6, | |
downloadBttn(outputId = "download_raw", | |
label = "Download Raw Data CSV", | |
icon = icon("cloud-download"), | |
style = "gradient", | |
block = T) | |
) | |
) | |
), | |
# ======================================================================== | |
# Community | |
# ======================================================================== | |
tabItem(tabName = "community", | |
fluidPage( | |
markdown("# **Numerai Coummunity**"), | |
br(), | |
tabsetPanel(type = "tabs", | |
# First Page - About | |
tabPanel("About", | |
markdown("## **Around the World with Numeratis**"), | |
markdown("### **Overview** | |
NumerCon (2022) and the time I spent with my fellow Numeratis were the exact “booster jab” I needed to wake the “meetup organizer Joe” up | |
after two crazy years of COVID and hibernation in a man cave. After NumerCon, I am confident that | |
1) many countries are now ready for in-person tech events and | |
2) we need to bring this fantastic in-person experience to more Numeratis around the world. | |
So, fam, here is my [proposal](https://forum.numer.ai/t/proposal-around-the-world-with-numeratis) for **Global Numerai Community Meetups**. | |
"), | |
markdown("### **Goals** | |
- **Demystifying Numerai** - many top data scientists out there are just not too sure about the crypto elements and/or the tournament format. Speaking with and listening to long-term participants can be an effective way to build trust. This was exactly how I learned to trust Numerai after watching Jon’s Office Hours with Arbitrage. | |
- **Knowledge sharing and brainstorming** - although we may not share all our secret sauce, meetups are great opportunities to bounce ideas off each other and may lead to new models with crazily high TC. | |
- **Face-to-face discussions between Numeratis and Numerai team** - a lot more than just asking the team “wen scores” in person, these events can provide a quick and direct feedback loop for both participants and the Numerai team. | |
"), | |
markdown("### **What's Here?** | |
- Materials (slides and videos) from previous events | |
- Survey results for upcoming events | |
- Memes and (de)GenAI stuff from the community | |
### **Enjoy :)** | |
") | |
), | |
# Second Page - Materials | |
tabPanel("Materials", | |
fluidRow( | |
column(10, | |
htmltools::includeMarkdown('https://raw.githubusercontent.com/councilofelders/meetups/master/README.md') | |
)) | |
), | |
# Third Page - Survey Results | |
tabPanel("Survey Results", | |
markdown("## **Survey for Upcoming Events**"), | |
br(), | |
markdown("Hello everyone! | |
We’re reaching out to gather **anonymous data** on your location, which will greatly assist us in planning future Numerai community events. | |
Your support in providing this information would be much appreciated. | |
You can find the latest survey results below. | |
Here is the [**Google Form**](https://forms.gle/1USUa7YCn2EgZG3NA). Please share it with your fellow Numeratis. | |
Thank you! | |
"), | |
fluidRow( | |
column(4, | |
markdown("### **Summary**"), | |
br(), | |
DTOutput("dt_survey_summary") | |
), | |
column(8, | |
markdown("### **Raw Data**"), | |
br(), | |
DTOutput("dt_survey_raw")) | |
) | |
), | |
# 4th Page - Memes | |
tabPanel("Memes", | |
markdown("![image](https://media.giphy.com/media/cftSzNoCTfSyAWctcl/giphy.gif)") | |
) | |
) # end of tabsetPanel | |
) # end of fluidPage | |
), | |
# ======================================================================== | |
# About | |
# ======================================================================== | |
tabItem(tabName = "about", | |
markdown("# **About this App**"), | |
markdown('### Yet another Numerai community dashboard by <b><a href="https://linktr.ee/jofaichow" target="_blank">Jo-fai Chow</a></b>.'), | |
br(), | |
markdown("## **Acknowledgements**"), | |
markdown("- #### This hobby project was inspired by Rajiv's <b><a href='https://huggingface.co/spaces/rajistics/shiny-kmeans' target='_blank'>shiny-kmeans</a></b> on 🤗 Spaces."), | |
markdown('- #### The <b><a href="https://linktr.ee/jofaichow" target="_blank">Rnumerai</a></b> package from Omni Analytics Group.'), | |
br(), | |
markdown("## **Source Code**"), | |
markdown("- #### GitHub: https://github.com/woobe/shiny-numerati"), | |
markdown("- #### Hugging Face: https://huggingface.co/spaces/jofaichow/shiny-numerati/tree/main"), | |
br(), | |
markdown("## **Changelog**"), | |
markdown( | |
" | |
- #### **0.1.0** — First prototype with an interactive table output | |
- #### **0.1.1** — Added a functional `Payout Summary` page | |
- #### **0.1.2** — `Payout Summary` layout updates | |
- #### **0.1.3** — Added `Raw Data` | |
- #### **0.1.4** — Various improvements in `Payout Summary` | |
- #### **0.1.5** — Replaced `corrV1` with `corrV2` | |
- #### **0.1.6** — Added `apcwnm` and `mcwnm` | |
- #### **0.1.7** — Added CoE Meetup GitHub page to `Community` | |
- #### **0.1.8** — Various improvements in `Payout Summary` | |
- #### **0.1.9** — Added `Payout Sim` based on new Corr and TC multipier settings | |
- #### **0.2.0** — Replaced `Payout Summary` with `Performance Summary`. Added KPIs summary | |
- #### **0.2.1** — Added `KPI (All)` | |
- #### **0.2.2** — Sped up chart rendering with `toWebGL()` | |
- #### **0.2.3** — Added new `MMC` - Ref: https://forum.numer.ai/t/changing-scoring-payouts-again-to-mmc-only | |
- #### **0.2.4** — Added `MMC` to `Payout Sim` | |
- #### **0.2.5** — Added more features related to MMC | |
- #### **0.2.6** — Added survey results - Ref: https://forum.numer.ai/t/around-the-world-with-numeratis-survey-for-upcoming-events | |
- #### **0.2.7** — Removed `KPI (C&T)` and `Payout Simulation` | |
"), | |
br(), | |
markdown("## **Session Info**"), | |
verbatimTextOutput(outputId = "session_info"), | |
br(), | |
textOutput("keepAlive") # trick to keep session alive | |
) | |
# ======================================================================== | |
) # end of tabItems | |
), | |
footer = shinydashboardPlus::dashboardFooter( | |
left = "Powered by ❤️, ☕, Shiny, and 🤗 Spaces", | |
right = paste0("Version 0.2.7")) | |
) | |
# ============================================================================== | |
# Server | |
# ============================================================================== | |
server <- function(input, output) { | |
# About Joe | |
output$user <- renderUser({ | |
dashboardUser( | |
name = "JC", | |
image = "https://numerai-public-images.s3.amazonaws.com/profile_images/aijoe_v5_compressed-iJWEo1WeHkpH.jpg", | |
subtitle = "@matlabulous", | |
footer = p('"THE NMR LIFE CHOSE ME."', class = 'text-center') | |
) | |
}) | |
# ============================================================================ | |
# Reactive: Data | |
# ============================================================================ | |
react_ls_model <- eventReactive(input$button_download, {sort(input$model)}) | |
react_d_raw <- eventReactive( | |
input$button_download, | |
{ | |
# Parallelised download | |
d_raw <- rbindlist(mclapply(X = input$model, | |
FUN = download_raw_data, | |
mc.cores = detectCores())) | |
# Return | |
d_raw | |
} | |
) | |
output$dt_model <- DT::renderDT({ | |
# Raw Data | |
d_raw <- react_d_raw() | |
# Reformat | |
d_munged <- reformat_data(d_raw) | |
# Main DT | |
DT::datatable( | |
# Data | |
d_munged, | |
# Other Options | |
rownames = FALSE, | |
extensions = "Buttons", | |
options = | |
list( | |
dom = 'Bflrtip', # https://datatables.net/reference/option/dom | |
buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html | |
order = list(list(0, 'asc'), list(1, 'asc')), | |
pageLength = 10, | |
lengthMenu = c(10, 20, 100, 500, 1000, 50000), | |
columnDefs = list(list(className = 'dt-center', targets = "_all"))) | |
) |> | |
# Reformat individual columns | |
formatRound(columns = c("corrV2", "tc", "fncV3", "corr_meta", "pay_ftr", "mmc"), digits = 4) |> | |
formatRound(columns = c("apcwnm", "mcwnm"), digits = 4) |> | |
formatRound(columns = c("corrV2_pct", "tc_pct", "fncV3_pct", "mmc_pct"), digits = 1) |> | |
formatRound(columns = c("stake", "payout"), digits = 2) |> | |
formatStyle(columns = c("model"), | |
fontWeight = "bold") |> | |
formatStyle(columns = c("stake"), | |
fontWeight = "bold", | |
color = styleInterval(cuts = -1e-15, values = c("#D24141", "#2196F3"))) |> | |
formatStyle(columns = c("corrV2", "fncV3", "mmc"), | |
color = styleInterval(cuts = -1e-15, values = c("#D24141", "black"))) |> | |
formatStyle(columns = c("tc"), | |
color = styleInterval(cuts = -1e-15, values = c("#D24141", "#A278DC"))) |> | |
formatStyle(columns = c("corrV2_pct", "tc_pct", "fncV3_pct", "mmc_pct"), | |
color = styleInterval(cuts = c(1, 5, 15, 85, 95, 99), | |
values = c("#692020", "#9A2F2F", "#D24141", | |
"#D1D1D1", # light grey | |
"#00A800", "#007000", "#003700"))) |> | |
formatStyle(columns = c("payout"), | |
fontWeight = "bold", | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) | |
}) | |
# ============================================================================ | |
# Static: Survey Data | |
# ============================================================================ | |
output$dt_survey_summary <- DT::renderDT({ | |
# Return | |
DT::datatable( | |
# Data | |
d_survey_summary, | |
# Other Options | |
rownames = FALSE, | |
# extensions = "Buttons", | |
options = | |
list( | |
dom = 'Blrtip', # https://datatables.net/reference/option/dom | |
pageLength = 20, | |
lengthMenu = c(10, 20, 100, 500) | |
) | |
) | |
}) | |
output$dt_survey_raw <- DT::renderDT({ | |
# Return | |
DT::datatable( | |
# Data | |
d_survey_raw, | |
# Other Options | |
rownames = FALSE, | |
# extensions = "Buttons", | |
options = | |
list( | |
dom = 'Blrtip', # https://datatables.net/reference/option/dom | |
pageLength = 20, | |
lengthMenu = c(10, 20, 100, 500) | |
) | |
) | |
}) | |
# ============================================================================ | |
# Reactive: Text Printout | |
# ============================================================================ | |
output$print_download <- renderPrint({react_ls_model()}) | |
output$text_download <- renderText({ | |
if (length(react_ls_model()) >= 1) "Your Selection:" else " " | |
}) | |
output$text_preview <- renderText({ | |
if (length(react_ls_model()) >= 1) "Data Preview:" else " " | |
}) | |
output$text_next <- renderText({ | |
if (length(react_ls_model()) >= 1) "Step 3: Performance Summary (see ←)" else " " | |
}) | |
output$text_note <- renderText({ | |
if (length(react_ls_model()) >= 1) "Note: you can also download [Raw Data] and check out our [Community Events] (see ←)" else " " | |
}) | |
# ============================================================================ | |
# Reactive: filtering data for all charts | |
# ============================================================================ | |
react_d_filter <- eventReactive( | |
input$button_filter, | |
{ | |
# Reformat and Filter | |
d_filter <- reformat_data(react_d_raw()) | |
d_filter <- d_filter[round >= input$range_round[1], ] | |
d_filter <- d_filter[round <= input$range_round[2], ] | |
# Return | |
d_filter | |
}) | |
react_d_payout_summary <- eventReactive( | |
input$button_filter, | |
{ | |
# Summarise payout | |
d_smry <- | |
react_d_filter() |> | |
lazy_dt() |> | |
filter(pay_ftr > 0) |> | |
filter(stake > 0) |> | |
group_by(round, date_open, date_resolved, resolved) |> | |
summarise(staked_models = n(), | |
total_stake = sum(stake, na.rm = T), | |
net_payout = sum(payout, na.rm = T)) |> | |
as.data.table() | |
d_smry$rate_of_return <- (d_smry$net_payout / d_smry$total_stake) * 100 | |
# Return | |
d_smry | |
}) | |
react_d_model_payout_summary <- eventReactive( | |
input$button_filter, | |
{ | |
# Get filtered data | |
# d_smry <- as.data.table(react_d_filter() |> filter(pay_ftr > 0)) | |
d_smry <- | |
react_d_filter() |> | |
lazy_dt() |> | |
filter(pay_ftr > 0) |> | |
filter(stake > 0) |> | |
as.data.table() | |
# Calculate rate of return (%) | |
d_smry[, rate_of_return_percent := payout / stake * 100] | |
# Summarise | |
d_smry <- | |
d_smry |> | |
lazy_dt() |> | |
group_by(model) |> | |
summarise(staked_rounds = n(), | |
net_payout = sum(payout, na.rm = T), | |
avg_payout = mean(payout, na.rm = T), | |
avg_rate_of_return_percent = mean(rate_of_return_percent, na.rm = T), | |
sharpe_rate_of_return = mean(rate_of_return_percent, na.rm = T) / sd(rate_of_return_percent, na.rm = T) | |
) |> as.data.table() | |
# Return | |
d_smry | |
}) | |
react_d_payout_sim_model <- eventReactive( | |
input$button_filter, | |
{ | |
# Get filtered data | |
# d_smry <- as.data.table(react_d_filter() |> filter(pay_ftr > 0)) | |
d_payout <- | |
react_d_filter() |> | |
lazy_dt() |> | |
filter(pay_ftr > 0) |> | |
filter(stake > 0) |> | |
as.data.table() | |
# Apply clip to corrV2 | |
d_payout[, corrV2_final := corrV2] | |
d_payout[corrV2 > 0.25, corrV2_final := 0.25] | |
d_payout[corrV2 < -0.25, corrV2_final := -0.25] | |
# Apply clip to tc | |
d_payout[, tc_final := tc] | |
d_payout[tc > 0.25, tc_final := 0.25] | |
d_payout[tc < -0.25, tc_final := -0.25] | |
# Calculate different payout | |
d_payout[, payout_1C0T := (corrV2_final) * stake * pay_ftr] | |
d_payout[, payout_2C0T := (2*corrV2_final) * stake * pay_ftr] | |
d_payout[, payout_2C1T := (2*corrV2_final + tc_final) * stake * pay_ftr] | |
d_payout[, payout_1C3T := (corrV2_final + 3*tc_final) * stake * pay_ftr] | |
d_payout[, payout_05C2M := (0.5*corrV2_final + 2*mmc) * stake * pay_ftr] | |
# Summarise | |
d_payout_smry <- | |
d_payout |> | |
lazy_dt() |> | |
group_by(model) |> | |
summarise( | |
rounds = n(), | |
sum_pay_1C0T = sum(payout_1C0T, na.rm = T), | |
sum_pay_2C0T = sum(payout_2C0T, na.rm = T), | |
sum_pay_2C1T = sum(payout_2C1T, na.rm = T), | |
sum_pay_1C3T = sum(payout_1C3T, na.rm = T), | |
sum_pay_05C2M = sum(payout_05C2M, na.rm = T), | |
shp_pay_1C0T = mean(payout_1C0T, na.rm = T) / sd(payout_1C0T, na.rm = T), | |
shp_pay_2C0T = mean(payout_2C0T, na.rm = T) / sd(payout_2C0T, na.rm = T), | |
shp_pay_2C1T = mean(payout_2C1T, na.rm = T) / sd(payout_2C1T, na.rm = T), | |
shp_pay_1C3T = mean(payout_1C3T, na.rm = T) / sd(payout_1C3T, na.rm = T), | |
shp_pay_05C2M = mean(payout_05C2M, na.rm = T) / sd(payout_05C2M, na.rm = T) | |
) |> | |
as.data.table() | |
# Return | |
d_payout_smry | |
}) | |
react_d_payout_sim_overall <- eventReactive( | |
input$button_filter, | |
{ | |
# Get filtered data | |
# d_payout <- as.data.table(react_d_filter() |> filter(pay_ftr > 0)) | |
d_payout <- | |
react_d_filter() |> | |
lazy_dt() |> | |
filter(pay_ftr > 0) |> | |
filter(stake > 0) |> | |
as.data.table() | |
# Apply clip to corrV2 | |
d_payout[, corrV2_final := corrV2] | |
d_payout[corrV2 > 0.25, corrV2_final := 0.25] | |
d_payout[corrV2 < -0.25, corrV2_final := -0.25] | |
# Apply clip to tc | |
d_payout[, tc_final := tc] | |
d_payout[tc > 0.25, tc_final := 0.25] | |
d_payout[tc < -0.25, tc_final := -0.25] | |
# Calculate different payout | |
d_payout[, payout_1C0T := (corrV2_final) * stake * pay_ftr] | |
d_payout[, payout_2C0T := (2*corrV2_final) * stake * pay_ftr] | |
d_payout[, payout_2C1T := (2*corrV2_final + tc_final) * stake * pay_ftr] | |
d_payout[, payout_1C3T := (corrV2_final + 3*tc_final) * stake * pay_ftr] | |
d_payout[, payout_05C2M := (0.5*corrV2 + 2*mmc) * stake * pay_ftr] | |
# Summarise | |
d_payout_smry <- | |
d_payout |> | |
lazy_dt() |> | |
summarise( | |
sum_pay_1C0T = sum(payout_1C0T, na.rm = T), | |
sum_pay_2C0T = sum(payout_2C0T, na.rm = T), | |
sum_pay_2C1T = sum(payout_2C1T, na.rm = T), | |
sum_pay_1C3T = sum(payout_1C3T, na.rm = T), | |
sum_pay_05C2M = sum(payout_05C2M, na.rm = T), | |
shp_pay_1C0T = mean(payout_1C0T, na.rm = T) / sd(payout_1C0T, na.rm = T), | |
shp_pay_2C0T = mean(payout_2C0T, na.rm = T) / sd(payout_2C0T, na.rm = T), | |
shp_pay_2C1T = mean(payout_2C1T, na.rm = T) / sd(payout_2C1T, na.rm = T), | |
shp_pay_1C3T = mean(payout_1C3T, na.rm = T) / sd(payout_1C3T, na.rm = T), | |
shp_pay_05C2M = mean(payout_05C2M, na.rm = T) / sd(payout_05C2M, na.rm = T) | |
) |> | |
as.data.table() | |
# Return | |
d_payout_smry | |
}) | |
react_d_performance_summary <- eventReactive( | |
input$button_filter, | |
{ | |
# Get filtered data | |
d_pref <- as.data.table(react_d_filter()) | |
# Add 2xCORRv2 + 1xTC | |
d_pref[, twoC_oneT := 2*corrV2 + tc] | |
# Calculate some high level stats | |
d_pref <- | |
d_pref |> | |
lazy_dt() |> | |
group_by(model) |> | |
summarise(total_rounds = n(), | |
avg_corrV2 = mean(corrV2, na.rm = T), | |
sharpe_corrV2 = mean(corrV2, na.rm = T) / sd(corrV2, na.rm = T), | |
# mdd_corrV2 = maxdrawdown(corrV2), | |
avg_tc = mean(tc, na.rm = T), | |
sharpe_tc = mean(tc, na.rm = T) / sd(tc, na.rm = T), | |
# mdd_tc = maxdrawdown(tc), | |
avg_2C1T = mean(twoC_oneT, na.rm = T), | |
sharpe_2C1T = mean(twoC_oneT, na.rm = T) / sd(tc, na.rm = T) | |
# mdd_2C1T = maxdrawdown(twoC_oneT) | |
) |> as.data.table() | |
# Return | |
d_pref | |
}) | |
react_d_kpi <- eventReactive( | |
input$button_filter, | |
{ | |
# Get filtered data | |
d_pref <- as.data.table(react_d_filter()) | |
# Hide Pending? | |
if (input$kpi_hide_pending) d_pref <- d_pref[resolved == TRUE] | |
# Add Rate of Return | |
d_pref[stake >0, rate_of_return := payout / stake * 100] | |
# Extract Raw KPI | |
if (input$kpi_choice == "MMCv2: The Latest and the Greatest MMC") d_pref[, KPI := mmc] | |
if (input$kpi_choice == "CORRv2: CORRelation with target cyrus_v4_20") d_pref[, KPI := corrV2] | |
if (input$kpi_choice == "TC: True Contribtuion to the hedge fund's returns") d_pref[, KPI := tc] | |
if (input$kpi_choice == "FNCv3: Feature Neutral Correlation with respect to the FNCv3 features") d_pref[, KPI := fncV3] | |
if (input$kpi_choice == "CWMM: Correlation With the Meta Model") d_pref[, KPI := corr_meta] | |
if (input$kpi_choice == "MCWNM: Maximum Correlation With Numerai Models staked at least 10 NMR") d_pref[, KPI := mcwnm] | |
if (input$kpi_choice == "APCWNM: Average Pairwise Correlation With Numerai Models staked at least 10 NMR") d_pref[, KPI := apcwnm] | |
# Calculate Score Multiplies | |
if (input$kpi_choice == "Score Multipliers: 0.5 x CORRv2 + 2.0 x MMCv2") d_pref[, KPI := 0.5 * corrV2 + 2.0 * mmc] | |
if (input$kpi_choice == "Score Multipliers: 0.5 x CORRv2") d_pref[, KPI := 0.5 * corrV2] | |
if (input$kpi_choice == "Score Multipliers: 1.5 x CORRv2") d_pref[, KPI := 1.5 * corrV2] | |
if (input$kpi_choice == "Score Multipliers: 2.0 x CORRv2") d_pref[, KPI := 2.0 * corrV2] | |
if (input$kpi_choice == "Score Multipliers: 2.0 x CORRv2 + 0.5 x TC") d_pref[, KPI := 2.0 * corrV2 + 0.5 * tc] | |
if (input$kpi_choice == "Score Multipliers: 2.0 x CORRv2 + 1.0 x TC") d_pref[, KPI := 2.0 * corrV2 + 1.0 * tc] | |
# Extract Percentile | |
if (input$kpi_choice == "Percentile: MMCv2") d_pref[, KPI := mmc_pct] | |
if (input$kpi_choice == "Percentile: CORRv2") d_pref[, KPI := corrV2_pct] | |
if (input$kpi_choice == "Percentile: TC") d_pref[, KPI := tc_pct] | |
if (input$kpi_choice == "Percentile: FNCv3") d_pref[, KPI := fncV3_pct] | |
# Extract Payout info | |
if (input$kpi_choice == "Payout") d_pref[, KPI := payout] | |
if (input$kpi_choice == "Rate of Return (%): Payout / Stake x 100") d_pref[, KPI := rate_of_return] | |
# Remove rows with NA (quick hack for now) | |
d_pref <- d_pref[!is.na(KPI)] | |
# Calculate Cumulative KPI (if needed) | |
if (input$kpi_cumulative) { | |
# Sort before doing cumsum | |
setorderv(d_pref, c("model", "round")) | |
# The data.table way | |
d_pref[, KPI := cumsum(KPI), "model"] | |
} | |
# Trim and sort | |
d_trim <- d_pref[, c("model", "round", "date_resolved", "resolved", "KPI")] | |
setorderv(d_trim, c("model", "round")) | |
# Return | |
d_trim | |
}) | |
# ============================================================================ | |
# Reactive: Payout Value Boxes | |
# ============================================================================ | |
output$text_payout_overview <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "Payout Summary (Overview)" else " " | |
}) | |
output$text_payout_rnd <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "Payout Summary (Tournament Rounds)" else " " | |
}) | |
output$text_payout_ind <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "Payout Summary (Individual Models)" else " " | |
}) | |
output$text_payout_all_models <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "Payout Summary Chart (All Models - Stacked)" else " " | |
}) | |
output$text_payout_ind_models <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "Payout Summary Chart (Individual Models)" else " " | |
}) | |
output$text_payout_sim <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "New Payout Simulation (NOTE: Experimental!)" else " " | |
}) | |
output$text_performance_models <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "KPI Analysis (CORRv2 and TC)" else " " | |
}) | |
output$text_performance_models_note <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "NOTE: You may want to find out which models have high CORRv2 Sharpe and high TC Sharpe." else " " | |
}) | |
output$text_performance_chart <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "KPI Analysis (Model Comparison)" else " " | |
}) | |
output$text_performance_chart_note <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "NOTE: Remember to refresh the chart (Step 5 ↑) after making any changes." else " " | |
}) | |
output$text_performance_chart_title <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "KPI Chart (Remember to Click the 'Refresh' Button ↑↑↑)" else " " | |
}) | |
output$text_performance_chart_data <- renderText({ | |
if (nrow(react_d_filter()) >= 1) "KPI Data" else " " | |
}) | |
# ============================================================================ | |
# Reactive valueBox outputs: Rounds | |
# ============================================================================ | |
output$payout_n_round_resolved <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = nrow(react_d_payout_summary()[resolved == TRUE & total_stake > 0, ]), | |
subtitle = "Staked Rounds (Resolved)", | |
color = "olive") | |
}) | |
output$payout_n_round_pending <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = nrow(react_d_payout_summary()[resolved == FALSE & total_stake > 0, ]), | |
subtitle = "Staked Rounds (Pending)", | |
color = "yellow") | |
}) | |
output$payout_n_round <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = nrow(react_d_payout_summary()[total_stake > 0, ]), | |
subtitle = "Staked Rounds (All)", | |
color = "light-blue") | |
}) | |
# ============================================================================ | |
# Reactive valueBox outputs: Payouts | |
# ============================================================================ | |
output$payout_resolved <- renderValueBox({ | |
valueBox(value = paste(as.character(format(round(sum(react_d_filter()[resolved == T, ]$payout, na.rm = T), 2), nsmall = 2)), "NMR"), | |
subtitle = "Total Payout (Resolved)", | |
color = "olive") | |
}) | |
output$payout_pending <- renderValueBox({ | |
valueBox(value = paste(as.character(format(round(sum(react_d_filter()[resolved == F, ]$payout, na.rm = T), 2), nsmall = 2)), "NMR"), | |
subtitle = "Total Payout (Pending)", | |
color = "yellow") | |
}) | |
output$payout_total <- renderValueBox({ | |
valueBox(value = paste(as.character(format(round(sum(react_d_filter()$payout, na.rm = T), 2), nsmall = 2)), "NMR"), | |
subtitle = "Total Payout (All)", | |
color = "light-blue") | |
}) | |
# ============================================================================ | |
# Reactive valueBox outputs: Average Round Payouts | |
# ============================================================================ | |
output$payout_average_resolved <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = paste(as.character(format(round(mean(react_d_payout_summary()[resolved == T & total_stake > 0, ]$net_payout, na.rm = T), 2), nsmall = 2)), "NMR"), | |
subtitle = "Avg. Round Payout (Resolved)", | |
color = "olive") | |
}) | |
output$payout_average_pending <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = paste(as.character(format(round(mean(react_d_payout_summary()[resolved == F & total_stake > 0, ]$net_payout, na.rm = T), 2), nsmall = 2)), "NMR"), | |
subtitle = "Avg. Round Payout (Pending)", | |
color = "yellow") | |
}) | |
output$payout_average <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = paste(as.character(format(round(mean(react_d_payout_summary()[total_stake > 0, ]$net_payout, na.rm = T), 2), nsmall = 2)), "NMR"), | |
subtitle = "Avg. Round Payout (All)", | |
color = "light-blue") | |
}) | |
# ============================================================================ | |
# Reactive valueBox outputs: Average Rate of Return | |
# ============================================================================ | |
output$payout_avg_ror_resolved <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = paste(as.character(format(round(mean(react_d_payout_summary()[resolved == T & total_stake > 0, ]$rate_of_return), 2), nsmall = 2)), "%"), | |
subtitle = "Avg. Round ROR (Resolved)", | |
color = "olive") | |
}) | |
output$payout_avg_ror_pending <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = paste(as.character(format(round(mean(react_d_payout_summary()[resolved == F & total_stake > 0, ]$rate_of_return), 2), nsmall = 2)), "%"), | |
subtitle = "Avg. Round ROR (Pending)", | |
color = "yellow") | |
}) | |
output$payout_avg_ror <- renderValueBox({ | |
# Use rounds with stake > 0 only | |
valueBox(value = paste(as.character(format(round(mean(react_d_payout_summary()[total_stake > 0, ]$rate_of_return), 2), nsmall = 2)), "%"), | |
subtitle = "Avg. Round ROR (All)", | |
color = "light-blue") | |
}) | |
# ============================================================================ | |
# Reactive: Payout Charts | |
# ============================================================================ | |
# Net Payouts Bar Chart | |
output$plot_payout_net <- renderPlotly({ | |
# Data | |
d_filter <- react_d_payout_summary() | |
# Filter | |
d_filter <- d_filter[total_stake > 0] | |
# Divider (resolved vs pending) | |
x_marker <- max(d_filter[resolved == TRUE]$round) + 0.5 | |
y_marker <- max(d_filter$net_payout) | |
# ggplot | |
p <- ggplot(d_filter, | |
aes(x = round, y = net_payout, fill = net_payout, | |
text = paste("Round:", round, | |
"\nRound Open Date:", date_open, | |
"\nRound Resolved Date:", date_resolved, | |
"\nRound Resolved?:", resolved, | |
"\nPayout:", round(net_payout,2), "NMR"))) + | |
geom_bar(position = "stack", stat = "identity") + | |
theme( | |
panel.border = element_rect(fill = 'transparent', | |
color = "grey", linewidth = 0.25), | |
panel.background = element_rect(fill = 'transparent'), | |
plot.background = element_rect(fill = 'transparent', color = NA), | |
panel.grid.major = element_blank(), | |
panel.grid.minor = element_blank(), | |
strip.background = element_rect(fill = 'transparent'), | |
strip.text = element_text(), | |
strip.clip = "on", | |
legend.background = element_rect(fill = 'transparent'), | |
legend.box.background = element_rect(fill = 'transparent') | |
) + | |
geom_vline(aes(xintercept = x_marker), linewidth = 0.25, color = "grey", linetype = "dashed") + | |
geom_hline(aes(yintercept = 0), linewidth = 0.25, color = "grey") + | |
annotate("text", x = x_marker, y = y_marker*1.2, label = "← Resolved vs. Pending →") + | |
scale_fill_scico(palette = "vikO", direction = -1, midpoint = 0) + | |
# scale_x_date(breaks = breaks_pretty(10), | |
# labels = label_date_short(format = c("%Y", "%b", "%d"), sep = "\n") | |
# ) + | |
xlab("\nTournament Round") + | |
ylab("Round Payout (NMR)") | |
# Generate plotly | |
ggplotly(p, height = 500, tooltip = "text") |> toWebGL() | |
}) | |
# Stacked Bar Chart | |
output$plot_payout_stacked <- renderPlotly({ | |
# Data | |
d_filter <- react_d_filter() | |
# Filter | |
d_filter <- d_filter[stake > 0] | |
# Divider (resolved vs pending) | |
x_marker <- max(d_filter[resolved == TRUE]$round) + 0.5 | |
# ggplot | |
p <- ggplot(d_filter, | |
aes(x = round, y = payout, fill = payout, | |
text = paste("Model:", model, | |
"\nRound:", round, | |
"\nRound Open Date:", date_open, | |
"\nRound Resolved Date:", date_resolved, | |
"\nRound Resolved?:", resolved, | |
"\nPayout:", round(payout,2), "NMR"))) + | |
geom_bar(position = "stack", stat = "identity") + | |
theme( | |
panel.border = element_rect(fill = 'transparent', | |
color = "grey", linewidth = 0.25), | |
panel.background = element_rect(fill = 'transparent'), | |
plot.background = element_rect(fill = 'transparent', color = NA), | |
panel.grid.major = element_blank(), | |
panel.grid.minor = element_blank(), | |
strip.background = element_rect(fill = 'transparent'), | |
strip.text = element_text(), | |
strip.clip = "on", | |
legend.background = element_rect(fill = 'transparent'), | |
legend.box.background = element_rect(fill = 'transparent') | |
) + | |
geom_vline(aes(xintercept = x_marker), linewidth = 0.25, color = "grey", linetype = "dashed") + | |
geom_hline(aes(yintercept = 0), linewidth = 0.25, color = "grey") + | |
scale_fill_scico(palette = "vikO", direction = -1, midpoint = 0) + | |
# scale_x_date(breaks = breaks_pretty(10), | |
# labels = label_date_short(format = c("%Y", "%b", "%d"), sep = "\n") | |
# ) + | |
xlab("\nTournament Round") + | |
ylab("Round Payout (NMR)") | |
# Generate plotly | |
ggplotly(p, height = 500, tooltip = "text") |> toWebGL() | |
}) | |
# Individual | |
output$plot_payout_individual <- renderPlotly({ | |
# Data | |
d_filter <- react_d_filter() | |
# Filter | |
d_filter <- d_filter[stake > 0] | |
# Get the number of unique models | |
n_model <- length(unique(d_filter$model)) | |
# Base plot | |
p <- ggplot(d_filter, | |
aes(x = round, y = payout, fill = payout, | |
text = paste("Model:", model, | |
"\nRound:", round, | |
"\nRound Open Date:", date_open, | |
"\nRound Resolved Date:", date_resolved, | |
"\nRound Resolved:", resolved, | |
"\nPayout:", round(payout,2), "NMR"))) + | |
geom_bar(stat = "identity") + | |
theme( | |
panel.border = element_rect(fill = 'transparent', color = "grey", linewidth = 0.25), | |
panel.background = element_rect(fill = 'transparent'), | |
plot.background = element_rect(fill = 'transparent', color = NA), | |
panel.grid.major = element_blank(), | |
panel.grid.minor = element_blank(), | |
strip.background = element_rect(fill = 'transparent'), | |
strip.text = element_text(), | |
strip.clip = "on", | |
legend.background = element_rect(fill = 'transparent'), | |
legend.box.background = element_rect(fill = 'transparent'), | |
axis.text.x = element_text(angle = 45, hjust = 1) | |
) + | |
geom_hline(aes(yintercept = 0), linewidth = 0.25, color = "grey") + | |
scale_fill_scico(palette = "vikO", direction = -1, midpoint = 0) + | |
scale_x_continuous(breaks = breaks_pretty(5)) + | |
xlab("\nTournament Round") + | |
ylab("Payout (NMR)") | |
# Facet setting | |
# if ((n_model %% 4) == 0) { | |
# p <- p + facet_wrap(. ~ model, ncol = 4, scales = "fixed") | |
# } else if ((n_model %% 5) == 0) { | |
# p <- p + facet_wrap(. ~ model, ncol = 5, scales = "fixed") | |
# } else { | |
# p <- p + facet_wrap(. ~ model, ncol = 6, scales = "fixed") | |
# } | |
p <- p + facet_wrap(. ~ model, ncol = 5, scales = "fixed") # fixed | |
# Dynamic height adjustment | |
height <- 500 # default minimum height | |
if (n_model >= 10) height = 800 | |
if (n_model >= 15) height = 1000 | |
if (n_model >= 20) height = 1200 | |
if (n_model >= 25) height = 1400 | |
if (n_model >= 30) height = 1600 | |
if (n_model >= 35) height = 1800 | |
if (n_model >= 40) height = 2000 | |
if (n_model >= 45) height = 2200 | |
if (n_model >= 50) height = 2400 | |
if (n_model >= 55) height = 2600 | |
if (n_model >= 60) height = 2800 | |
if (n_model >= 65) height = 3000 | |
# Generate plotly | |
ggplotly(p, height = height, tooltip = "text") |> toWebGL() | |
}) | |
# KPI Chart: Avg Corr vs. Avg TC | |
output$plot_performance_avg <- renderPlotly({ | |
# Data | |
d_pref <- react_d_performance_summary() | |
# Plot | |
p_avg <- ggplot(d_pref, | |
aes(x = avg_tc, y = avg_corrV2, | |
text = paste("Model:", model, | |
"\nAverage CORRv2:", round(avg_corrV2, 4), | |
"\nAverage TC:", round(avg_tc, 4)) | |
)) + | |
geom_point() + | |
theme( | |
panel.border = element_rect(fill = 'transparent', color = "grey", linewidth = 0.25), | |
panel.background = element_rect(fill = 'transparent'), | |
plot.background = element_rect(fill = 'transparent', color = NA), | |
panel.grid.major = element_blank(), | |
panel.grid.minor = element_blank(), | |
strip.background = element_rect(fill = 'transparent'), | |
strip.text = element_text(), | |
strip.clip = "on", | |
legend.background = element_rect(fill = 'transparent'), | |
legend.box.background = element_rect(fill = 'transparent'), | |
axis.text.x = element_text(angle = 45, hjust = 1) | |
) + | |
scale_x_continuous(breaks = breaks_pretty(5)) + | |
scale_y_continuous(breaks = breaks_pretty(5)) + | |
xlab("\nAverage TC") + | |
ylab("\nAverage CORRv2") | |
# Add vline and hline if needed | |
if (min(d_pref$avg_corrV2) <0) p_avg <- p_avg + geom_hline(aes(yintercept = 0), linewidth = 0.25, color = "grey", linetype = "dashed") | |
if (min(d_pref$avg_tc) <0) p_avg <- p_avg + geom_vline(aes(xintercept = 0), linewidth = 0.25, color = "grey", linetype = "dashed") | |
# Convert to Plotly | |
ggplotly(p_avg, tooltip = "text") |> toWebGL() | |
}) | |
# KPI Chart: Corr Sharpe vs. TC Sharpe | |
output$plot_performance_sharpe <- renderPlotly({ | |
# Data | |
d_pref <- react_d_performance_summary() | |
# Plot | |
p_sharpe <- ggplot(d_pref, | |
aes(x = sharpe_tc, y = sharpe_corrV2, | |
text = paste("Model:", model, | |
"\nSharpe Ratio of CORRv2:", round(sharpe_corrV2, 4), | |
"\nSharpe Ratio of TC:", round(sharpe_tc, 4)) | |
)) + | |
geom_point() + | |
theme( | |
panel.border = element_rect(fill = 'transparent', color = "grey", linewidth = 0.25), | |
panel.background = element_rect(fill = 'transparent'), | |
plot.background = element_rect(fill = 'transparent', color = NA), | |
panel.grid.major = element_blank(), | |
panel.grid.minor = element_blank(), | |
strip.background = element_rect(fill = 'transparent'), | |
strip.text = element_text(), | |
strip.clip = "on", | |
legend.background = element_rect(fill = 'transparent'), | |
legend.box.background = element_rect(fill = 'transparent'), | |
axis.text.x = element_text(angle = 45, hjust = 1) | |
) + | |
scale_x_continuous(breaks = breaks_pretty(5)) + | |
scale_y_continuous(breaks = breaks_pretty(5)) + | |
xlab("\nSharpe Ratio of TC") + | |
ylab("\nSharpe Ratio of CORRv2") | |
# Add vline and hline if needed | |
if (min(d_pref$sharpe_corrV2) <0) p_sharpe <- p_sharpe + geom_hline(aes(yintercept = 0), linewidth = 0.25, color = "grey", linetype = "dashed") | |
if (min(d_pref$sharpe_tc) <0) p_sharpe <- p_sharpe + geom_vline(aes(xintercept = 0), linewidth = 0.25, color = "grey", linetype = "dashed") | |
# Convert to Plotly | |
ggplotly(p_sharpe, tooltip = "text") |> toWebGL() | |
}) | |
# KPI Chart: All KPIs | |
output$plot_kpi <- renderPlotly({ | |
# Data | |
d_kpi <- react_d_kpi() | |
# Dynamic Labels | |
if (input$kpi_choice == "MMCv2: The Latest and the Greatest MMC") y_label <- "mmc" | |
if (input$kpi_choice == "CORRv2: CORRelation with target cyrus_v4_20") y_label <- "CORRv2" | |
if (input$kpi_choice == "TC: True Contribtuion to the hedge fund's returns") y_label <- "TC" | |
if (input$kpi_choice == "FNCv3: Feature Neutral Correlation with respect to the FNCv3 features") y_label <- "FNCv3" | |
if (input$kpi_choice == "CWMM: Correlation With the Meta Model") y_label <- "CWMM" | |
if (input$kpi_choice == "MCWNM: Maximum Correlation With Numerai Models staked at least 10 NMR") y_label <- "MCWNM" | |
if (input$kpi_choice == "APCWNM: Average Pairwise Correlation With Numerai Models staked at least 10 NMR") y_label <- "APCWNM" | |
if (input$kpi_choice == "Score Multipliers: 0.5 x CORRv2 + 2.0 x MMCv2") y_label <- "0.5 x CORRv2 + 2.0 x MMCv2" | |
if (input$kpi_choice == "Score Multipliers: 0.5 x CORRv2") y_label <- "0.5 x CORRv2" | |
if (input$kpi_choice == "Score Multipliers: 1.5 x CORRv2") y_label <- "1.5 x CORRv2" | |
if (input$kpi_choice == "Score Multipliers: 2.0 x CORRv2") y_label <- "2.0 x CORRv2" | |
if (input$kpi_choice == "Score Multipliers: 2.0 x CORRv2 + 0.5 x TC") y_label <- "2.0 x CORRv2 + 0.5 x TC" | |
if (input$kpi_choice == "Score Multipliers: 2.0 x CORRv2 + 1.0 x TC") y_label <- "2.0 x CORRv2 + 1.0 x TC" | |
if (input$kpi_choice == "Percentile: MMCv2") y_label <- "MMCv2 Percentile" | |
if (input$kpi_choice == "Percentile: CORRv2") y_label <- "CORRv2 Percentile" | |
if (input$kpi_choice == "Percentile: TC") y_label <- "TC Percentile" | |
if (input$kpi_choice == "Percentile: FNCv3") y_label <- "FNCv3 Percentile" | |
if (input$kpi_choice == "Payout") y_label <- "Payout (NMR)" | |
if (input$kpi_choice == "Rate of Return (%): Payout / Stake x 100") y_label <- "Rate of Return (%)" | |
# If cumulative | |
if (input$kpi_cumulative) y_label <- paste("Cumulative", y_label) | |
# Other settings | |
y_min <- min(d_kpi$KPI) * 0.95 | |
if (y_min > 0) y_min <- 0 | |
y_max <- max(d_kpi$KPI) * 1.05 | |
height <- 500 # default minimum height | |
# Plot | |
p <- ggplot(d_kpi, aes(x = round, y = KPI, | |
ymin = y_min, ymax = y_max, | |
color = model)) + | |
geom_line() + | |
theme( | |
panel.border = element_rect(fill = 'transparent', color = "grey", linewidth = 0.25), | |
panel.background = element_rect(fill = 'transparent'), | |
plot.background = element_rect(fill = 'transparent', color = NA), | |
panel.grid.major = element_blank(), | |
panel.grid.minor = element_blank(), | |
strip.background = element_rect(fill = 'transparent'), | |
strip.text = element_text(), | |
strip.clip = "on", | |
legend.background = element_rect(fill = 'transparent'), | |
legend.box.background = element_rect(fill = 'transparent'), | |
axis.text.x = element_text(angle = 45, hjust = 1) | |
) + | |
scale_x_continuous(breaks = breaks_pretty(5)) + | |
scale_y_continuous(breaks = breaks_pretty(5)) + | |
geom_hline(aes(yintercept = 0), linewidth = 0.25, color = "grey", linetype = "dashed") + | |
ylab(y_label) + | |
xlab("\nTournament Round") | |
# Facet wrap? | |
if (input$kpi_facet) { | |
# Extract no. of models | |
n_model <- length(unique(d_kpi$model)) | |
# Add facet_wrap | |
p <- p + facet_wrap(. ~ model, ncol = 5, scales = "fixed") # fixed | |
# Dynamic height adjustment | |
if (n_model >= 10) height = 800 | |
if (n_model >= 15) height = 1000 | |
if (n_model >= 20) height = 1200 | |
if (n_model >= 25) height = 1400 | |
if (n_model >= 30) height = 1600 | |
if (n_model >= 35) height = 1800 | |
if (n_model >= 40) height = 2000 | |
if (n_model >= 45) height = 2200 | |
if (n_model >= 50) height = 2400 | |
if (n_model >= 55) height = 2600 | |
if (n_model >= 60) height = 2800 | |
if (n_model >= 65) height = 3000 | |
} | |
# Convert to Plotly | |
ggplotly(p, height = height) |> toWebGL() | |
}) | |
# ============================================================================ | |
# Reactive: Payout Summary Table | |
# ============================================================================ | |
# Net Round Payout Summary | |
output$dt_payout_summary <- DT::renderDT({ | |
# Generate a new DT | |
DT::datatable( | |
# Data | |
react_d_payout_summary(), | |
# Other Options | |
rownames = FALSE, | |
extensions = "Buttons", | |
options = | |
list( | |
dom = 'Bflrtip', # https://datatables.net/reference/option/dom | |
buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html | |
order = list(list(0, 'asc'), list(1, 'asc')), | |
pageLength = 1000, | |
lengthMenu = c(10, 50, 100, 500, 1000, 10000), | |
columnDefs = list(list(className = 'dt-center', targets = "_all"))) | |
) |> | |
# Reformat individual columns | |
formatRound(columns = c("total_stake", "net_payout", "rate_of_return"), digits = 2) |> | |
formatStyle(columns = c("round"), fontWeight = "bold") |> | |
formatStyle(columns = c("resolved"), | |
target = "row", | |
backgroundColor = styleEqual(c(1,0), c("transparent", "#FFF8E1"))) |> | |
formatStyle(columns = c("total_stake"), | |
fontWeight = "bold", | |
color = styleInterval(cuts = -1e-15, values = c("#D24141", "#2196F3"))) |> | |
formatStyle(columns = c("net_payout"), | |
fontWeight = "bold", | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) |> | |
formatStyle(columns = c("rate_of_return"), | |
fontWeight = "bold", | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) | |
}) | |
# Individual Model Payout Summary | |
output$dt_model_payout_summary <- DT::renderDT({ | |
# Generate a new DT | |
DT::datatable( | |
# Data | |
react_d_model_payout_summary(), | |
# Other Options | |
rownames = FALSE, | |
extensions = "Buttons", | |
options = | |
list( | |
dom = 'Bflrtip', # https://datatables.net/reference/option/dom | |
buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html | |
order = list(list(0, 'asc'), list(1, 'asc')), | |
pageLength = 100, | |
lengthMenu = c(10, 50, 100, 500, 1000), | |
columnDefs = list(list(className = 'dt-center', targets = "_all"))) | |
) |> | |
# Reformat individual columns | |
formatRound(columns = c("net_payout", "avg_payout", | |
"avg_rate_of_return_percent", | |
"sharpe_rate_of_return"), digits = 4) |> | |
# formatStyle(columns = c("model"), fontWeight = "bold") |> | |
formatStyle(columns = c("net_payout", "avg_payout", | |
"avg_rate_of_return_percent", | |
"sharpe_rate_of_return"), | |
# fontWeight = "bold", | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) | |
}) | |
# Payout Sim (Model) | |
output$dt_payout_sim_model <- DT::renderDT({ | |
# Generate a new DT | |
DT::datatable( | |
# Data | |
react_d_payout_sim_model(), | |
# Other Options | |
rownames = FALSE, | |
extensions = "Buttons", | |
options = | |
list( | |
dom = 'Bflrtip', # https://datatables.net/reference/option/dom | |
buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html | |
order = list(list(0, 'asc'), list(1, 'asc')), | |
pageLength = 100, | |
lengthMenu = c(10, 50, 100, 500, 1000), | |
columnDefs = list(list(className = 'dt-center', targets = "_all"))) | |
) |> | |
# Reformat individual columns | |
formatRound(columns = c("sum_pay_1C0T", "sum_pay_2C0T", "sum_pay_2C1T", "sum_pay_1C3T", | |
"sum_pay_05C2M", "shp_pay_05C2M", | |
"shp_pay_1C0T", "shp_pay_2C0T", "shp_pay_2C1T", "shp_pay_1C3T"), | |
digits = 2) |> | |
formatStyle(columns = c("sum_pay_1C0T", "sum_pay_2C0T", "sum_pay_2C1T", "sum_pay_1C3T", | |
"sum_pay_05C2M", "shp_pay_05C2M", | |
"shp_pay_1C0T", "shp_pay_2C0T", "shp_pay_2C1T", "shp_pay_1C3T"), | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) |> | |
formatStyle(columns = c("model", | |
"sum_pay_05C2M", "shp_pay_05C2M" | |
# "sum_pay_2C1T", "sum_pay_1C3T", | |
# "shp_pay_2C1T", "shp_pay_1C3T" | |
), fontWeight = "bold") | |
}) | |
# Payout Sim (Overall) | |
output$dt_payout_sim_overall <- DT::renderDT({ | |
# Generate a new DT | |
DT::datatable( | |
# Data | |
react_d_payout_sim_overall(), | |
# Other Options | |
rownames = FALSE, | |
# extensions = "Buttons", | |
options = | |
list( | |
dom = 't', # https://datatables.net/reference/option/dom | |
# buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html | |
# order = list(list(0, 'asc'), list(1, 'asc')), | |
# pageLength = 10, | |
# lengthMenu = c(10, 50, 100, 500, 1000), | |
columnDefs = list(list(className = 'dt-center', targets = "_all"))) | |
) |> | |
# Reformat individual columns | |
formatRound(columns = c("sum_pay_1C0T", "sum_pay_2C0T", "sum_pay_2C1T", "sum_pay_1C3T", | |
"sum_pay_05C2M", "shp_pay_05C2M", | |
"shp_pay_1C0T", "shp_pay_2C0T", "shp_pay_2C1T", "shp_pay_1C3T"), | |
digits = 2) |> | |
formatStyle(columns = c("sum_pay_1C0T", "sum_pay_2C0T", "sum_pay_2C1T", "sum_pay_1C3T", | |
"sum_pay_05C2M", "shp_pay_05C2M", | |
"shp_pay_1C0T", "shp_pay_2C0T", "shp_pay_2C1T", "shp_pay_1C3T"), | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) |> | |
formatStyle(columns = c("sum_pay_05C2M", "shp_pay_05C2M"), | |
fontWeight = "bold") | |
}) | |
# Performance Summary | |
output$dt_performance_summary <- DT::renderDT({ | |
# Generate a new DT | |
DT::datatable( | |
# Data | |
react_d_performance_summary(), | |
# Other Options | |
rownames = FALSE, | |
extensions = "Buttons", | |
options = | |
list( | |
dom = 'Bflrtip', # https://datatables.net/reference/option/dom | |
buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html | |
order = list(list(0, 'asc'), list(1, 'asc')), | |
pageLength = 10, | |
lengthMenu = c(10, 50, 100, 500, 1000), | |
columnDefs = list(list(className = 'dt-center', targets = "_all"))) | |
) |> | |
# Reformat individual columns | |
formatRound(columns = c("avg_corrV2", "sharpe_corrV2", | |
"avg_tc", "sharpe_tc", | |
"avg_2C1T", "sharpe_2C1T" | |
), | |
digits = 4) |> | |
formatStyle(columns = c("avg_corrV2", "sharpe_corrV2", | |
"avg_tc", "sharpe_tc", | |
"avg_2C1T", "sharpe_2C1T" | |
), | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) | |
}) | |
# KPI Filter | |
output$dt_kpi <- DT::renderDT({ | |
# Generate a new DT | |
DT::datatable( | |
# Data | |
react_d_kpi(), | |
# Other Options | |
rownames = FALSE, | |
extensions = "Buttons", | |
options = | |
list( | |
dom = 'Bflrtip', # https://datatables.net/reference/option/dom | |
buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html | |
order = list(list(0, 'asc'), list(1, 'asc')), | |
pageLength = 5, | |
lengthMenu = c(5, 10, 50, 100, 500, 1000), | |
columnDefs = list(list(className = 'dt-center', targets = "_all"))) | |
) |> | |
# Reformat individual columns | |
formatRound(columns = c("KPI"), digits = 4) |> | |
formatStyle(columns = c("KPI"), | |
color = styleInterval(cuts = c(-1e-15, 1e-15), | |
values = c("#D24141", "#D1D1D1", "#00A800"))) | |
}) | |
# ============================================================================ | |
# Reactive: Model Performance Charts | |
# ============================================================================ | |
# Boxplot - TC Percentile | |
output$plot_boxplot_tcp <- renderPlotly({ | |
# Data | |
d_filter <- react_d_filter() | |
# Order by TC_PCT | |
d_model_order <- with(d_filter, reorder(model, tc_pct, median)) | |
d_filter$model_order <- factor(d_filter$model, levels = levels(d_model_order)) | |
# ggplot2 | |
p <- ggplot(d_filter, aes(x = model_order, y = tc_pct, group = model_order, color = model_order)) + | |
geom_boxplot() + | |
theme( | |
panel.border = element_blank(), | |
panel.background = element_rect(fill = 'transparent'), | |
plot.background = element_rect(fill = 'transparent', color = NA), | |
panel.grid.major.x = element_blank(), | |
panel.grid.major.y = element_line(color = "grey", linewidth = 0.25), | |
panel.grid.minor = element_blank(), | |
strip.background = element_rect(fill = 'transparent'), | |
strip.text = element_text(), | |
strip.clip = "on", | |
legend.position = "none" | |
) + | |
scale_color_manual(values = gen_custom_palette(d_filter$model)) + | |
xlab("Model") + | |
ylab("TC Percentile") + | |
scale_y_continuous(limits = c(0,100), breaks = breaks_pretty(4)) + | |
coord_flip() | |
# Dynamic height adjustment | |
n_model <- length(unique(d_filter$model)) | |
height <- 600 # default | |
if (n_model > 10) height = 800 | |
if (n_model > 15) height = 1000 | |
if (n_model > 20) height = 1200 | |
if (n_model > 25) height = 1400 | |
if (n_model > 30) height = 1600 | |
if (n_model > 35) height = 1800 | |
if (n_model > 40) height = 2000 | |
if (n_model > 45) height = 2200 | |
if (n_model > 50) height = 2400 | |
# Generate plotly | |
ggplotly(p, height = height) | |
}) | |
# ============================================================================ | |
# Reactive: Downloads | |
# ============================================================================ | |
output$download_raw <- downloadHandler( | |
filename = "raw_data.csv", | |
content = function(file) {fwrite(react_d_raw(), file, row.names = FALSE)} | |
) | |
# ============================================================================ | |
# Session Info | |
# ============================================================================ | |
output$session_info <- renderPrint({ | |
sessionInfo() | |
}) | |
# ============================================================================ | |
# Trick to keep session alive | |
# https://tickets.dominodatalab.com/hc/en-us/articles/360015932932-Increasing-the-timeout-for-Shiny-Server | |
# ============================================================================ | |
output$keepAlive <- renderText({ | |
req(input$count) | |
# paste("keep alive ", input$count) | |
" " | |
}) | |
} | |
# ============================================================================== | |
# App | |
# ============================================================================== | |
shinyApp(ui, server) | |