jofaichow commited on
Commit
8b0b5c8
1 Parent(s): 6493f1e

0.1.1 - payout summary

Browse files
Files changed (2) hide show
  1. Dockerfile +4 -3
  2. app/app.R +328 -19
Dockerfile CHANGED
@@ -4,11 +4,12 @@ FROM rocker/r-ver
4
  RUN R -q -e "install.packages(c('shiny', 'rmarkdown'))"
5
 
6
  # additional shiny functionality
7
- RUN R -q -e "install.packages(c('shinydashboard', 'shinydashboardPlus', 'shinyWidgets'))"
8
- RUN R -q -e "install.packages(c('fresh', 'shinycssloaders'))"
9
 
10
  # other R packages
11
- RUN R -q -e "install.packages(c('data.table', 'DT', 'Rnumerai'))"
 
12
 
13
  # copy the app to the image
14
  WORKDIR /shinyapp
 
4
  RUN R -q -e "install.packages(c('shiny', 'rmarkdown'))"
5
 
6
  # additional shiny functionality
7
+ RUN R -q -e "install.packages(c('shinydashboard', 'shinydashboardPlus'))"
8
+ RUN R -q -e "install.packages(c('shinyWidgets,'shinycssloaders'))"
9
 
10
  # other R packages
11
+ RUN R -q -e "install.packages(c('DT, 'plotly', 'scico', 'ggthemes'))"
12
+ RUN R -q -e "install.packages(c('data.table', 'dtplyr', 'Rnumerai'))"
13
 
14
  # copy the app to the image
15
  WORKDIR /shinyapp
app/app.R CHANGED
@@ -3,19 +3,26 @@ library(shinydashboard)
3
  library(shinydashboardPlus)
4
  library(shinyWidgets)
5
  library(shinycssloaders)
 
 
 
 
6
  library(data.table)
 
7
  library(Rnumerai)
8
- library(DT)
9
 
10
 
11
  # ==============================================================================
12
- # Leaderboard
13
  # ==============================================================================
14
 
15
  # Download latest leaderboard from Numerai and get a list of all models
16
  d_lb <- get_leaderboard()
17
  ls_model <- sort(d_lb$username)
18
 
 
 
 
19
 
20
  # ==============================================================================
21
  # Helper Functions
@@ -94,23 +101,35 @@ ui <- shinydashboardPlus::dashboardPage(
94
  userOutput("user")
95
  ),
96
 
 
 
 
 
 
97
  sidebar = shinydashboardPlus::dashboardSidebar(
98
  id = "sidebar",
99
  sidebarMenu(
100
  menuItem(text = "Start Here", tabName = "start", icon = icon("play")),
101
- menuItem(text = "Performance", tabName = "performance", icon = icon("line-chart")),
102
- menuItem(text = "Payout", tabName = "payout", icon = icon("credit-card")),
103
  menuItem(text = "About", tabName = "about", icon = icon("question-circle"))
104
  ),
105
  minified = TRUE,
106
  collapsed = FALSE
107
  ),
108
 
 
 
 
 
 
109
  body = dashboardBody(
110
 
111
  tabItems(
112
 
113
  # ========================================================================
 
 
114
 
115
  tabItem(tabName = "start",
116
 
@@ -186,20 +205,99 @@ ui <- shinydashboardPlus::dashboardPage(
186
  )
187
  ),
188
 
 
 
189
  # ========================================================================
190
 
191
- tabItem(tabName = "performance",
192
  fluidPage(
193
- markdown("![image](https://media.giphy.com/media/cftSzNoCTfSyAWctcl/giphy.gif)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  )
 
195
  ),
196
 
197
- tabItem(tabName = "payout",
 
 
 
 
 
198
  fluidPage(
199
  markdown("![image](https://media.giphy.com/media/cftSzNoCTfSyAWctcl/giphy.gif)")
200
  )
201
  ),
202
 
 
 
 
203
  # ========================================================================
204
 
205
  tabItem(tabName = "about",
@@ -207,7 +305,7 @@ ui <- shinydashboardPlus::dashboardPage(
207
  markdown('#### Yet another Numerai community dashboard by <b><a href="https://linktr.ee/jofaichow" target="_blank">Jo-fai Chow</a></b>.'),
208
 
209
  br(),
210
- markdown("## **Acknowledgement**"),
211
  markdown("#### This hobby project was inspired by Rajiv's <a href='https://huggingface.co/spaces/rajistics/shiny-kmeans' target='_blank'>shiny-kmeans</a> on 🤗 Spaces."),
212
 
213
  br(),
@@ -215,6 +313,7 @@ ui <- shinydashboardPlus::dashboardPage(
215
  markdown(
216
  "
217
  - #### **0.1.0** — First prototype with an interactive table output
 
218
  "),
219
  br(),
220
  markdown("## **Session Info**"),
@@ -229,12 +328,11 @@ ui <- shinydashboardPlus::dashboardPage(
229
 
230
  footer = shinydashboardPlus::dashboardFooter(
231
  left = "Powered by ❤️, ☕, Shiny, and 🤗 Spaces",
232
- right = paste0("Version 0.1.0"))
233
 
234
  )
235
 
236
 
237
-
238
  # ==============================================================================
239
  # Server
240
  # ==============================================================================
@@ -253,23 +351,23 @@ server <- function(input, output) {
253
 
254
 
255
  # ============================================================================
256
- # Reactive --> Download Model Data
257
  # ============================================================================
258
 
259
- react_download <- eventReactive(input$button_download, {sort(input$model)})
260
 
261
- output$print_download <- renderPrint({react_download()})
262
 
263
  output$text_download <- renderText({
264
- if (length(react_download()) >= 1) "Your Selection:" else " "
265
  })
266
 
267
  output$text_preview <- renderText({
268
- if (length(react_download()) >= 1) "Data Preview:" else " "
269
  })
270
 
271
  output$text_next <- renderText({
272
- if (length(react_download()) >= 1) "⬅ [Coming Soon] Performance and Payout Charts 📈📊🔥" else " "
273
  })
274
 
275
  react_d_model <- eventReactive(
@@ -289,6 +387,10 @@ server <- function(input, output) {
289
  }
290
  )
291
 
 
 
 
 
292
  output$dt_model <- DT::renderDT({
293
 
294
  DT::datatable(
@@ -330,10 +432,217 @@ server <- function(input, output) {
330
  formatStyle(columns = c("corr_pct", "tc_pct", "fncv3_pct"),
331
  color = styleInterval(cuts = c(1, 5, 15, 85, 95, 99),
332
  values = c("#692020", "#9A2F2F", "#D24141",
333
- "#D1D1D1", # light grey
334
- "#00A800", "#007000", "#003700"))) |>
335
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  formatStyle(columns = c("payout"),
 
 
 
 
 
337
  fontWeight = "bold",
338
  color = styleInterval(cuts = c(-1e-15, 1e-15),
339
  values = c("#D24141", "#D1D1D1", "#00A800")))
 
3
  library(shinydashboardPlus)
4
  library(shinyWidgets)
5
  library(shinycssloaders)
6
+ library(DT)
7
+ library(plotly)
8
+ library(scico)
9
+ library(ggthemes)
10
  library(data.table)
11
+ library(dtplyr)
12
  library(Rnumerai)
 
13
 
14
 
15
  # ==============================================================================
16
+ # Tournament Information
17
  # ==============================================================================
18
 
19
  # Download latest leaderboard from Numerai and get a list of all models
20
  d_lb <- get_leaderboard()
21
  ls_model <- sort(d_lb$username)
22
 
23
+ # Round info
24
+ d_comp <- get_competitions()
25
+
26
 
27
  # ==============================================================================
28
  # Helper Functions
 
101
  userOutput("user")
102
  ),
103
 
104
+
105
+ # ============================================================================
106
+ # Sidebar
107
+ # ============================================================================
108
+
109
  sidebar = shinydashboardPlus::dashboardSidebar(
110
  id = "sidebar",
111
  sidebarMenu(
112
  menuItem(text = "Start Here", tabName = "start", icon = icon("play")),
113
+ menuItem(text = "Payout Summary", tabName = "payout", icon = icon("credit-card")),
114
+ menuItem(text = "Model Performance", tabName = "performance", icon = icon("line-chart")),
115
  menuItem(text = "About", tabName = "about", icon = icon("question-circle"))
116
  ),
117
  minified = TRUE,
118
  collapsed = FALSE
119
  ),
120
 
121
+
122
+ # ============================================================================
123
+ # Main
124
+ # ============================================================================
125
+
126
  body = dashboardBody(
127
 
128
  tabItems(
129
 
130
  # ========================================================================
131
+ # Start Here
132
+ # ========================================================================
133
 
134
  tabItem(tabName = "start",
135
 
 
205
  )
206
  ),
207
 
208
+ # ========================================================================
209
+ # Payout Summary
210
  # ========================================================================
211
 
212
+ tabItem(tabName = "payout",
213
  fluidPage(
214
+
215
+ markdown("# **Payout Summary**"),
216
+ markdown("### Remember to refresh the charts after making changes to model selection or settings below"),
217
+ br(),
218
+
219
+ fluidRow(
220
+
221
+ column(6,
222
+
223
+ markdown("## **Step 1 - Define the Range**"),
224
+
225
+ sliderInput(inputId = "range_round",
226
+ label = "Numerai Classic Tournament Rounds",
227
+ width = "100%",
228
+ min = min(d_comp$number),
229
+ max = max(d_comp$number),
230
+ # note: daily rounds from round 339
231
+ value = c(394, max(d_comp$number))
232
+ )
233
+ ),
234
+
235
+ column(6,
236
+
237
+ markdown("## **Step 2 - Visualise**"),
238
+ br(),
239
+ actionBttn(inputId = "button_filter",
240
+ label = "Create / Refresh Charts",
241
+ color = "primary",
242
+ icon = icon("refresh"),
243
+ style = "gradient",
244
+ block = TRUE)
245
+ )
246
+ ),
247
+
248
+ br(),
249
+
250
+ tabsetPanel(type = "tabs",
251
+
252
+ tabPanel("All Models",
253
+
254
+ br(),
255
+
256
+ h3(strong(textOutput(outputId = "text_payout"))),
257
+
258
+ fluidRow(
259
+ # class = "text-center",
260
+ valueBoxOutput("payout_confirmed", width = 3),
261
+ valueBoxOutput("payout_pending", width = 3),
262
+ valueBoxOutput("payout_total", width = 3),
263
+ valueBoxOutput("payout_average", width = 3)
264
+ ),
265
+
266
+ br(),
267
+
268
+ shinycssloaders::withSpinner(plotlyOutput("plot_payout_stacked"))
269
+
270
+ ),
271
+
272
+ tabPanel("Individual Models",
273
+ br(),
274
+ shinycssloaders::withSpinner(plotlyOutput("plot_payout_individual"))),
275
+
276
+ tabPanel("Summary Table",
277
+ br(), br(),
278
+ shinycssloaders::withSpinner(DTOutput("dt_payout_summary"))
279
+ )
280
+
281
+ )
282
+
283
  )
284
+
285
  ),
286
 
287
+
288
+ # ========================================================================
289
+ # Model Performance
290
+ # ========================================================================
291
+
292
+ tabItem(tabName = "performance",
293
  fluidPage(
294
  markdown("![image](https://media.giphy.com/media/cftSzNoCTfSyAWctcl/giphy.gif)")
295
  )
296
  ),
297
 
298
+
299
+ # ========================================================================
300
+ # About
301
  # ========================================================================
302
 
303
  tabItem(tabName = "about",
 
305
  markdown('#### Yet another Numerai community dashboard by <b><a href="https://linktr.ee/jofaichow" target="_blank">Jo-fai Chow</a></b>.'),
306
 
307
  br(),
308
+ markdown("## **Acknowledgements**"),
309
  markdown("#### This hobby project was inspired by Rajiv's <a href='https://huggingface.co/spaces/rajistics/shiny-kmeans' target='_blank'>shiny-kmeans</a> on 🤗 Spaces."),
310
 
311
  br(),
 
313
  markdown(
314
  "
315
  - #### **0.1.0** — First prototype with an interactive table output
316
+ - #### **0.1.1** — Added a functional `Payout Summary`
317
  "),
318
  br(),
319
  markdown("## **Session Info**"),
 
328
 
329
  footer = shinydashboardPlus::dashboardFooter(
330
  left = "Powered by ❤️, ☕, Shiny, and 🤗 Spaces",
331
+ right = paste0("Version 0.1.1"))
332
 
333
  )
334
 
335
 
 
336
  # ==============================================================================
337
  # Server
338
  # ==============================================================================
 
351
 
352
 
353
  # ============================================================================
354
+ # Reactive: Data
355
  # ============================================================================
356
 
357
+ react_ls_model <- eventReactive(input$button_download, {sort(input$model)})
358
 
359
+ output$print_download <- renderPrint({react_ls_model()})
360
 
361
  output$text_download <- renderText({
362
+ if (length(react_ls_model()) >= 1) "Your Selection:" else " "
363
  })
364
 
365
  output$text_preview <- renderText({
366
+ if (length(react_ls_model()) >= 1) "Data Preview:" else " "
367
  })
368
 
369
  output$text_next <- renderText({
370
+ if (length(react_ls_model()) >= 1) "⬅ [NEW] Payout Summary 📈📊🔥" else " "
371
  })
372
 
373
  react_d_model <- eventReactive(
 
387
  }
388
  )
389
 
390
+ # ============================================================================
391
+ # Reactive: DataTable
392
+ # ============================================================================
393
+
394
  output$dt_model <- DT::renderDT({
395
 
396
  DT::datatable(
 
432
  formatStyle(columns = c("corr_pct", "tc_pct", "fncv3_pct"),
433
  color = styleInterval(cuts = c(1, 5, 15, 85, 95, 99),
434
  values = c("#692020", "#9A2F2F", "#D24141",
435
+ "#D1D1D1", # light grey
436
+ "#00A800", "#007000", "#003700"))) |>
437
+
438
+ formatStyle(columns = c("payout"),
439
+ fontWeight = "bold",
440
+ color = styleInterval(cuts = c(-1e-15, 1e-15),
441
+ values = c("#D24141", "#D1D1D1", "#00A800")))
442
+
443
+ })
444
+
445
+
446
+ # ============================================================================
447
+ # Reactive: filtering data for all charts
448
+ # ============================================================================
449
+
450
+ react_d_filter <- eventReactive(
451
+ input$button_filter,
452
+ {
453
+
454
+ # Model data
455
+ d_filter <- react_d_model()
456
+
457
+ # Filtering
458
+ d_filter <- d_filter[pay_ftr > 0, ] # ignoring the new daily rounds for now
459
+ d_filter <- d_filter[round >= input$range_round[1], ]
460
+ d_filter <- d_filter[round <= input$range_round[2], ]
461
+
462
+ # Return
463
+ d_filter
464
+
465
+ })
466
+
467
+
468
+ # ============================================================================
469
+ # Reactive: Payout Value Boxes
470
+ # ============================================================================
471
+
472
+ output$text_payout <- renderText({
473
+ if (nrow(react_d_filter()) >= 1) "Payouts in NMR" else " "
474
+ })
475
+
476
+ output$payout_confirmed <- renderValueBox({
477
+ valueBox(value = round(sum(react_d_filter()[resolved == TRUE, ]$payout, na.rm = T), 2),
478
+ subtitle = "Confirmed",
479
+ icon = icon("check"),
480
+ color = "green")
481
+ })
482
+
483
+ output$payout_pending <- renderValueBox({
484
+ valueBox(value = round(sum(react_d_filter()[resolved == FALSE, ]$payout, na.rm = T), 2),
485
+ subtitle = "Pending",
486
+ icon = icon("clock"),
487
+ color = "yellow")
488
+ })
489
+
490
+ output$payout_total <- renderValueBox({
491
+ valueBox(value = round(sum(react_d_filter()$payout, na.rm = T), 2),
492
+ subtitle = "Confirmed + Pending",
493
+ icon = icon("plus"),
494
+ color = "aqua")
495
+ })
496
+
497
+ output$payout_average <- renderValueBox({
498
+ valueBox(value = round((sum(react_d_filter()$payout, na.rm = T) / length(unique(react_d_filter()$round))), 2),
499
+ subtitle = "Round Average",
500
+ icon = icon("credit-card"),
501
+ color = "light-blue")
502
+ })
503
+
504
+
505
+ # ============================================================================
506
+ # Reactive: Payout Charts
507
+ # ============================================================================
508
+
509
+ # Stacked Bar Chart
510
+ output$plot_payout_stacked <- renderPlotly({
511
+
512
+ p <- ggplot(react_d_filter(), aes(x = round, y = payout, fill = payout,
513
+ text = paste("Model:", model,
514
+ "\nRound:", round,
515
+ "\nResolved:", resolved,
516
+ "\nPayout:", round(payout,2), "NMR"))) +
517
+ geom_bar(position = "stack", stat = "identity") +
518
+ theme(
519
+ panel.border = element_rect(fill = 'transparent',
520
+ color = "grey", linewidth = 0.25),
521
+ panel.background = element_rect(fill = 'transparent'),
522
+ plot.background = element_rect(fill = 'transparent', color = NA),
523
+ panel.grid.major = element_blank(),
524
+ panel.grid.minor = element_blank(),
525
+ strip.background = element_rect(fill = 'transparent'),
526
+ strip.text = element_text(),
527
+ strip.clip = "on",
528
+ legend.background = element_rect(fill = 'transparent'),
529
+ legend.box.background = element_rect(fill = 'transparent')
530
+ ) +
531
+ geom_hline(aes(yintercept = 0), linewidth = 0.25, color = "grey") +
532
+ scale_fill_scico(palette = "vikO", direction = -1, midpoint = 0) +
533
+ xlab("Tournament Round") +
534
+ ylab("Payout (NMR)")
535
+
536
+ # Generate plotly
537
+ ggplotly(p, tooltip = "text")
538
+
539
+ })
540
+
541
+
542
+ # Individual
543
+ output$plot_payout_individual <- renderPlotly({
544
+
545
+ # Get the number of unique models
546
+ n_model <- length(unique(react_d_filter()$model))
547
+
548
+ # Base plot
549
+ p <- ggplot(react_d_filter(), aes(x = round, y = payout, fill = payout,
550
+ text = paste("Round:", round,
551
+ "\nResolved:", resolved,
552
+ "\nPayout:", round(payout,2), "NMR"))) +
553
+ geom_bar(stat = "identity") +
554
+ theme(
555
+ panel.border = element_rect(fill = 'transparent',
556
+ color = "grey", linewidth = 0.25),
557
+ panel.background = element_rect(fill = 'transparent'),
558
+ plot.background = element_rect(fill = 'transparent', color = NA),
559
+ panel.grid.major = element_blank(),
560
+ panel.grid.minor = element_blank(),
561
+ strip.background = element_rect(fill = 'transparent'),
562
+ strip.text = element_text(),
563
+ strip.clip = "on",
564
+ legend.background = element_rect(fill = 'transparent'),
565
+ legend.box.background = element_rect(fill = 'transparent')
566
+ ) +
567
+ geom_hline(aes(yintercept = 0), size = 0.25, color = "grey") +
568
+ scale_fill_scico(palette = "vikO", direction = -1, midpoint = 0) +
569
+ xlab("Tournament Round") +
570
+ ylab("Confirmed / Pending Payout (NMR)")
571
+
572
+ # Facet setting
573
+ if (n_model %% 5 == 0) {
574
+ p <- p + facet_wrap(. ~ model, ncol = 5)
575
+ } else {
576
+ p <- p + facet_wrap(. ~ model)
577
+ }
578
+
579
+ # Dynamic height adjustment
580
+ height <- 600 # default
581
+ if (n_model > 10) height = 800
582
+ if (n_model > 15) height = 1000
583
+ if (n_model > 20) height = 1200
584
+ if (n_model > 25) height = 1400
585
+ if (n_model > 30) height = 1600
586
+ if (n_model > 35) height = 1800
587
+ if (n_model > 40) height = 2000
588
+ if (n_model > 45) height = 2200
589
+ if (n_model > 50) height = 2400
590
+
591
+ # Generate plotly
592
+ ggplotly(p, height = height, tooltip = "text")
593
+
594
+ })
595
+
596
+
597
+ # ============================================================================
598
+ # Reactive: Payout Summary Table
599
+ # ============================================================================
600
+
601
+ output$dt_payout_summary <- DT::renderDT({
602
+
603
+ # Summarise payout
604
+ d_smry <-
605
+ react_d_filter() |> lazy_dt() |>
606
+ group_by(round, resolved) |>
607
+ summarise(stake = sum(stake, na.rm = T),
608
+ payout = sum(payout, na.rm = T)) |>
609
+ as.data.table()
610
+ d_smry$rate_of_return <- (d_smry$payout / d_smry$stake) * 100
611
+
612
+ # Generate a new DT
613
+ DT::datatable(
614
+
615
+ # Data
616
+ d_smry,
617
+
618
+ # Other Options
619
+ rownames = FALSE,
620
+ extensions = "Buttons",
621
+ options =
622
+ list(
623
+ dom = 'Bflrtip', # https://datatables.net/reference/option/dom
624
+ buttons = list('csv', 'excel', 'copy', 'print'), # https://rstudio.github.io/DT/003-tabletools-buttons.html
625
+ order = list(list(0, 'asc'), list(1, 'asc')),
626
+ pageLength = 20,
627
+ lengthMenu = c(5, 10, 20, 100, 500, 1000),
628
+ columnDefs = list(list(className = 'dt-center', targets = "_all")))
629
+ ) |>
630
+
631
+ # Reformat individual columns
632
+ formatRound(columns = c("stake", "payout", "rate_of_return"), digits = 2) |>
633
+
634
+ formatStyle(columns = c("round"), fontWeight = "bold") |>
635
+
636
+ formatStyle(columns = c("stake"),
637
+ fontWeight = "bold",
638
+ color = styleInterval(cuts = -1e-15, values = c("#D24141", "#2196F3"))) |>
639
+
640
  formatStyle(columns = c("payout"),
641
+ fontWeight = "bold",
642
+ color = styleInterval(cuts = c(-1e-15, 1e-15),
643
+ values = c("#D24141", "#D1D1D1", "#00A800"))) |>
644
+
645
+ formatStyle(columns = c("rate_of_return"),
646
  fontWeight = "bold",
647
  color = styleInterval(cuts = c(-1e-15, 1e-15),
648
  values = c("#D24141", "#D1D1D1", "#00A800")))