# R # Black-Scholes Shiny app — fixed plots for call and put library(shiny) library(plotly) library(ggplot2) library(ggthemes) ##### SERVER ##### server <- function(input, output) { # Generate Black-Scholes values BS = function(S, K, T, v, rf, dv) { d1 = (log(S/K) + (rf - dv + 0.5 * v^2) * T) / (v * sqrt(T)) d2 = d1 - v * sqrt(T) bscall = S * exp(-dv * T) * pnorm(d1) - K * exp(-rf * T) * pnorm(d2) bsput = -S * exp(-dv * T) * pnorm(-d1) + K * exp(-rf * T) * pnorm(-d2) c(bscall, bsput) } # Call option price output$BScall <- renderText({ S = input$stockprice K = input$strike T = input$maturity v = input$volatility rf = input$riskfreerate dv = input$divrate round(BS(S, K, T, v, rf, dv)[1], 4) }) # Put option price output$BSput <- renderText({ S = input$stockprice K = input$strike T = input$maturity v = input$volatility rf = input$riskfreerate dv = input$divrate round(BS(S, K, T, v, rf, dv)[2], 4) }) # Call plot (shows call and put curves across strikes) output$plotCall <- renderPlotly({ S = input$stockprice K = input$strike T = input$maturity v = input$volatility rf = input$riskfreerate dv = input$divrate strikes = seq(K - 30, K + 30) vcall = sapply(strikes, function(k) BS(S, k, T, v, rf, dv)[1]) df = data.frame(strikes = strikes, Call = vcall) p <- ggplot(df, aes(x = strikes)) + geom_line(aes(y = Call, color = "Call")) + labs(title = "Black-Scholes Option Pricing", x = "Strike Price", y = "Option Price") + theme_minimal() + scale_color_manual("", values = c("Call" = "steelblue", "Put" = "firebrick")) plotly::ggplotly(p) }) # Put plot (same curves — kept for tab separation) output$plotPut <- renderPlotly({ S = input$stockprice K = input$strike T = input$maturity v = input$volatility rf = input$riskfreerate dv = input$divrate strikes = seq(K - 30, K + 30) vput = sapply(strikes, function(k) BS(S, k, T, v, rf, dv)[2]) df = data.frame(strikes = strikes, Put = vput) p <- ggplot(df, aes(x = strikes)) + geom_line(aes(y = Put, color = "Put")) + labs(title = "Black-Scholes Option Pricing", x = "Strike Price", y = "Option Price") + theme_minimal() + scale_color_manual("", values = c("Call" = "steelblue", "Put" = "firebrick")) plotly::ggplotly(p) }) } ##### UI ##### ui <- shinyUI(fluidPage( titlePanel("Black-Scholes-Merton (1973)"), sidebarLayout( sidebarPanel( numericInput('stockprice', 'Stock Price', 100), numericInput('strike', 'Strike Price', 100), sliderInput('maturity', 'Maturity (years)', min = 0.1, max = 10, value = 1, step = 0.01), sliderInput('volatility', 'Volatility (annualized)', min = 0.1, max = 2, value = 0.2, step = 0.01), sliderInput('riskfreerate', 'Risk-free Rate (annualized)', min = 0, max = 0.5, value = 0.01, step = 0.01), sliderInput('divrate', 'Dividend Yield (annualized)', min = 0, max = 0.5, value = 0, step = 0.01), hr(), p('Please refer to following for more details:', a("Black-Scholes (1973)", href = "https://en.wikipedia.org/wiki/Black%E2%80%93Scholes_model")), hr() ), mainPanel( h2('European call price'), textOutput("BScall"), hr(), h2('European put price'), textOutput("BSput"), hr(), tabsetPanel( tabPanel("Calls", plotlyOutput("plotCall", width = "100%")), tabPanel("Puts", plotlyOutput("plotPut", width = "100%")) ) ) ) )) ##### Run ##### shinyApp(ui = ui, server = server)