===========================================

1 Declaration:** In accordance with the Honor Code, I certify that my answers here are my own work, and I did not make my solutions available to anyone else.

===========================================

2 The Home project objective:

The objective of this project is to price a European up-and-out call option with a barrier active between the moment of pricing and the optoin expiry using the Monte Carlo simulation implemented in C++ via the Rcpp package.

The aim is to analyze the following: 1. The theoretical price of this up-and-out call option for the specified parameters 2. The relationship between the theoretical price of the option & the two factors simultaneously: - volatility (σ) of the underlying instrument returns - time to maturity (T) of this option

3 A brief description:

3.1 What is an up-and-out call Option?

An up-and-out call is a type of barrier option; a path-dependent derivative whose payoff depends on whether the underlying asset price reaches a certain barrier level during the option’s life-time.

up-and-out call: - The price has to increase to reach the barrier - The option is cancelled when the asset price reaches the barrier

\[\Phi_t = \max(S_T - K, 0) \quad \text{if} \quad \max_{0 \leq t \leq T}(S_t) \leq L\]

However, if the barrier \(L\) is breached at any time \(t \in [0, T]\), the option is knocked-out and the payoff becomes zero.

3.2 Why to use the Barrier options?

The Barrier options are popular because of the following reasons:

  1. Lower premiums: The knock-out feature reduces the price compared to vanilla options.
  2. Customized risk profiles: This allow traders to express specific market views.
  3. Very common in both FX & equity markets: Observered & heard that it is extensively used for hedging

4 Model assumptions:

The underlying asset price follows the Geometric Brownian Motion (GBM) under the risk-neutral measure:

\[dS_t = rS_t \, dt + \sigma S_t \, dW_t\]

Also, some Key assumptions:

  • Continuous trading is possible
  • No transaction costs or taxes
  • Constant risk-free rate \(r\)
  • Constant volatility \(\sigma\)
  • and No dividends
  • Continuous/Active barrier monitoring (barrier is checked at each simulation step)

4.1 Below are the Monte Carlo parameters:

  • The number of time steps: 189 (approximately 252 trading days × 0.75 years)
  • The number of simulations: 50,000 for main pricing, 5,000 for sensitivity grid
  • The Random number generation: Box-Muller transform for standard normal variates

5 The Option parameters

# Below are the option parameters derfined in the home assignment
S0      <- 105     # The current spot price
K       <- 110     # The strike price
sigma   <- 0.21    # The annualized volatility rate (i.e. 21%)
r       <- 0.05    # The annualized risk-free rate (i.e. 5%)
T_mat   <- 0.75    # The time to maturity (i.e. 0.75 year = 9 months)

# Selecting the Barrier level wisely, as instructed in the home assignment, chosen to moderately impact the price
# L = 130 represents ~24% increase from S0
# this ensures the option is not always knocked-out
L       <- 130     # This is our Barrier level

# Below are the Monte Carlo parameters
nInt    <- 189     # Number of time steps (~252 * 0.75)
nReps   <- 50000   # Number of simulations

5.1 Here is my rationale for selecting the Barrier level mentioned above as 130$

I chose the barrier level \(L = 130\) because of the following:

  1. It is above the current spot price (\(S_0 = 105\)); required for an “up” barrier
  2. It is above the strike price (\(K = 110\)); so it makes the option meaningful
  3. It represents approximately a 24% increase from the spot price, which seems achievable and not trivial over 9 months (0.75 year) with the 21% annualized volatility
  4. It will moderately impact the price without making it zero.

6 Package Installation

# Install the barrierOptionPricer package from the source
# NOTE: Commented-out this code snippet, as mentioned in the home assignment/project as a requirement
# Please install the package manually before running this document:

# install.packages("barrierOptionPricer_1.0.tar.gz", 
#                  type = "source", 
#                  repos = NULL)
# Let's load the barrierOptionPricer package (assuming it is already installed)
library(barrierOptionPricer)

# loading the visualization packages
library(ggplot2)
library(reshape2)

7 Here are the Monte Carlo Simulation Results

7.1 The Theoretical price calculation

# First we set the seed for reproducibility
set.seed(42)

# Now calculate the up-and-out call price
upOutCallPrice <- getUpAndOutCallPrice(
  nInt    = nInt,
  strike  = K,
  spot    = S0,
  vol     = sigma,
  r       = r,
  expiry  = T_mat,
  barrier = L,
  nReps   = nReps
)

# calculating the vanilla call price for the comparison
vanillaCallPrice <- getVanillaCallPrice(
  nInt    = nInt,
  strike  = K,
  spot    = S0,
  vol     = sigma,
  r       = r,
  expiry  = T_mat,
  barrier = L,
  nReps   = nReps
)

# calculating the barrier discount
barrierDiscount <- upOutCallPrice / vanillaCallPrice * 100

7.2 Results & summary

cat("=" , rep("=", 60), "\n", sep = "")
## =============================================================
cat("         OPTION pricing results\n")
##          OPTION pricing results
cat("=" , rep("=", 60), "\n", sep = "")
## =============================================================
cat("\nOption parameters:\n")
## 
## Option parameters:
cat("  - Spot price (S0):        $", S0, "\n")
##   - Spot price (S0):        $ 105
cat("  - Strike price (K):       $", K, "\n")
##   - Strike price (K):       $ 110
cat("  - Barrier level (L):      $", L, "\n")
##   - Barrier level (L):      $ 130
cat("  - Volatility (sigma):     ", sigma * 100, "%\n")
##   - Volatility (sigma):      21 %
cat("  - Risk-free rate (r):     ", r * 100, "%\n")
##   - Risk-free rate (r):      5 %
cat("  - Time to maturity (T):   ", T_mat, " years (", T_mat * 12, " months)\n")
##   - Time to maturity (T):    0.75  years ( 9  months)
cat("\nMonte Carlo Parameters:\n")
## 
## Monte Carlo Parameters:
cat("  - Time Steps:             ", nInt, "\n")
##   - Time Steps:              189
cat("  - Number of Simulations:  ", format(nReps, big.mark = ","), "\n")
##   - Number of Simulations:   50,000
cat("\n" , rep("-", 60), "\n", sep = "")
## 
## ------------------------------------------------------------
cat("PRICING RESULTS:\n")
## PRICING RESULTS:
cat(rep("-", 60), "\n", sep = "")
## ------------------------------------------------------------
cat("\n  Up-and-Out Call Price:    $", round(upOutCallPrice, 4), "\n")
## 
##   Up-and-Out Call Price:    $ 1.3916
cat("  Vanilla Call Price:       $", round(vanillaCallPrice, 4), "\n")
##   Vanilla Call Price:       $ 7.1226
cat("  Barrier Discount:         ", round(barrierDiscount, 2), "% of vanilla\n")
##   Barrier Discount:          19.54 % of vanilla
cat("\n" , rep("=", 60), "\n", sep = "")
## 
## ============================================================

8 Analysing the sensitivity

8.1 Relationship for Volatility and Time to maturity

Here we are analyzing how the up-and-out Call price varies with the following parameters:

  • Volatility (σ): ranging from 10% to 40%
  • Time to maturity (T): ranging from 0.1 to 1.5 years
# Defining the parameter ranges for sensitivity analysis
vol_range    <- seq(0.10, 0.40, by = 0.025)    # i.e. 10% to 40%
expiry_range <- seq(0.10, 1.50, by = 0.10)     # i.e. 0.1 to 1.5 years

# Number of simulations for the grid (have reduced it for computational efficiency)
nReps_grid <- 5000

cat("Sensitivity analysis grid is here:\n")
## Sensitivity analysis grid is here:
cat("  - Volatility range:  ", min(vol_range)*100, "% to ", max(vol_range)*100, "%\n")
##   - Volatility range:   10 % to  40 %
cat("  - Maturity range:    ", min(expiry_range), " to ", max(expiry_range), " years\n")
##   - Maturity range:     0.1  to  1.5  years
cat("  - Grid points:       ", length(vol_range), " x ", length(expiry_range), " = ", 
    length(vol_range) * length(expiry_range), "\n")
##   - Grid points:        13  x  15  =  195
# Here is the function to price up-and-out call for the sensitivity analysis
priceUpOutCall <- function(vol, expiry) {
  # Adjusting the time steps proportionally to the expiry time
  nSteps <- max(50, round(252 * expiry))
  
  price <- getUpAndOutCallPrice(
    nInt    = nSteps,
    strike  = K,
    spot    = S0,
    vol     = vol,
    r       = r,
    expiry  = expiry,
    barrier = L,
    nReps   = nReps_grid
  )
  
  return(price)
}

# Here is the function for the vanilla call (a comparison)
priceVanillaCall <- function(vol, expiry) {
  nSteps <- max(50, round(252 * expiry))
  
  price <- getVanillaCallPrice(
    nInt    = nSteps,
    strike  = K,
    spot    = S0,
    vol     = vol,
    r       = r,
    expiry  = expiry,
    barrier = L,
    nReps   = nReps_grid
  )
  
  return(price)
}
# Setting the seed for reproducibility
set.seed(123)

# Creating the price matrix
price_matrix <- matrix(NA, nrow = length(vol_range), ncol = length(expiry_range))
rownames(price_matrix) <- paste0(vol_range * 100, "%")
colnames(price_matrix) <- paste0("T=", expiry_range)

cat("Calculating prices for the ", length(vol_range) * length(expiry_range), " combinations...\n")
## Calculating prices for the  195  combinations...
# Now, calculating the prices for each of the combination
for (i in seq_along(vol_range)) {
  for (j in seq_along(expiry_range)) {
    price_matrix[i, j] <- priceUpOutCall(vol_range[i], expiry_range[j])
  }
}

cat("The pricing is now complete\n")
## The pricing is now complete

8.2 Let’s prepare some surface visualizations

# creating the data frame for plotting
plot_data <- expand.grid(
  Volatility = vol_range * 100,
  Maturity = expiry_range
)
plot_data$Price <- as.vector(price_matrix)

# Creating the filled contour plot (it's a pseudo-3D)
ggplot(plot_data, aes(x = Maturity, y = Volatility, fill = Price)) +
  geom_tile() +
  geom_contour(aes(z = Price), color = "white", alpha = 0.5, bins = 12) +
  scale_fill_gradientn(
    colors = c("#440154", "#3b528b", "#21918c", "#5ec962", "#fde725"),
    name = "Price ($)"
  ) +
  labs(
    title = "up-and-out call option price surface",
    subtitle = paste0("S₀ = $", S0, ", K = $", K, ", L = $", L, ", r = ", r*100, "%"),
    x = "The time to maturity (in years)",
    y = "volatility (in %)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    plot.subtitle = element_text(size = 12),
    axis.title = element_text(size = 12),
    legend.title = element_text(size = 10)
  ) +
  # Marking the assignment parameters:
  annotate("point", x = T_mat, y = sigma * 100, 
           color = "red", size = 4, shape = 17) +
  annotate("text", x = T_mat + 0.15, y = sigma * 100 + 1, 
           label = "assignment\nparameters", color = "red", hjust = 0, size = 3)

8.3 For different maturities, let’s look at the Price vs Volatility:

# selecting the specific maturities to plot
# NOTE: Must use values that exist in expiry_range = 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5
maturities_to_plot <- c(0.3, 0.5, 0.7, 1.0, 1.3)

# filtering the data
vol_data <- plot_data[plot_data$Maturity %in% maturities_to_plot, ]
vol_data$Maturity_Label <- paste0("T = ", vol_data$Maturity, " years")

ggplot(vol_data, aes(x = Volatility, y = Price, 
                     color = factor(Maturity), group = Maturity)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 2) +
  scale_color_viridis_d(name = "Time to\nMaturity", option = "turbo") +
  labs(
    title = "Up-and-Out Call: Price vs Volatility",
    subtitle = "For the different times to maturity",
    x = "Volatility (in %)",
    y = "Option price (in $)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 14, face = "bold"),
    legend.position = "right"
  ) +
  geom_vline(xintercept = sigma * 100, linetype = "dashed", color = "gray40", alpha = 0.7)

8.4 For different volatilities, let’s look at the Price vs Time to maturity:

# selecting the specific volatilities to plot (as percentages matching plot_data$Volatility)
# NOTE: Must use values that exist in vol_range * 100 = 10, 12.5, 15, 17.5, 20, 22.5, 25, 27.5, 30, 32.5, 35, 37.5, 40
vols_to_plot_pct <- c(10, 15, 20, 25, 30, 35)

# filtering the data using rounded values to avoid floating-point comparison issues
expiry_data <- plot_data[round(plot_data$Volatility, 1) %in% vols_to_plot_pct, ]

ggplot(expiry_data, aes(x = Maturity, y = Price, 
                        color = factor(Volatility), group = Volatility)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 2) +
  scale_color_viridis_d(name = "Volatility\n(%)", option = "turbo") +
  labs(
    title = "Up-and-Out Call Price vs Time to Maturity",
    subtitle = "For the different volatility levels",
    x = "Time to maturity (in years)",
    y = "Option price (in $)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 14, face = "bold"),
    legend.position = "right"
  ) +
  geom_vline(xintercept = T_mat, linetype = "dashed", color = "gray40", alpha = 0.7)

9 Now, some analysis and the interpretations

9.1 Let’s look at some of the key findings:

9.1.1 1. Based on the surface plot:

The 3D-surface plot revealed a non-linear relationship unique to the barrier options. While increasing time to maturity (\(T\)) generally increases the option price (due to higher time value), increasing volatility (\(\sigma\)) has a dual effect. Initially, higher volatility increases the probability of the option ending in-the-money. However, at very high volatility levels, the probability of the asset price hitting the barrier (\(L=130\)) increases drastically, causing the option price to drop. This created the visible ‘hump’ shape in the surface.

9.1.2 2. The effect of volatility on up-and-out call price

The relationship between volatility and the up-and-out call price is definitely complex and quite non-monotonic:

  • At the low volatility: Increasing σ tends to increase the option price, which is similar to the vanilla calls
  • However, at the high volatility: The price may decrease because the higher volatility increases the probability of breaching the set barrier (resulting the knock-out)

So, this ends up creating a characteristic hump-shaped pattern, which is definitely a distinctive feature of knock-out barrier options.

9.1.3 3. THe effect of the Time to maturity

For up-and-out options, the longer the time to maturity has a competitive effects:

  • The Positive effect: The more time for the underlying to appreciate
  • The Negative effect: The more time means more opportunities to hit the barrier

However, the net effect depends on the volatility level and the proximity to the barrier set.

9.1.4 4. The barrier discount

The up-and-out call is worth 19.5% of the vanilla call price. This discount reflects the probability that the barrier will be breached during the option’s life.

9.2 Here is some of the inutition on the economics:

The barrier option is quite cheaper because:

  1. The holder loses the upside if the underlying price rises too much (i.e. above L)
  2. the higher volatility means more knock-out risk, and not just more potential gain
  3. This asymmetry makes barrier options quite useful for traders with specific price range views

10 So, we can conclude:

This home project assignment successfully implemented a Monte Carlo pricer for the European up-and-out call, active barrier options using Rcpp.

The Final results summary:

  1. The theoretical price: $1.3916 for the specified parameters
  2. The barrier discount: 19.5% of vanilla call (reflects knock-out risk)
  3. The sensitivity analysis: The 3D surface shows how price varies with volatility and maturity simultaneously

A summary of the key insights:

  • Significant computational efficiency achieved with RCPP.
  • The barrier options exhibit non-monotonic behavior with respect to volatility, AND
  • The barrier level choice significantly impacts the option value

11 Session Information

sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26100)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=English_United States.utf8 
## [2] LC_CTYPE=English_United States.utf8   
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C                          
## [5] LC_TIME=English_United States.utf8    
## 
## time zone: Europe/London
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] reshape2_1.4.5          ggplot2_4.0.1           barrierOptionPricer_1.0
## 
## loaded via a namespace (and not attached):
##  [1] vctrs_0.7.0        cli_3.6.5          knitr_1.51         rlang_1.1.7       
##  [5] xfun_0.56          stringi_1.8.7      otel_0.2.0         S7_0.2.1          
##  [9] jsonlite_2.0.0     labeling_0.4.3     glue_1.8.0         isoband_0.3.0     
## [13] plyr_1.8.9         htmltools_0.5.9    sass_0.4.10        scales_1.4.0      
## [17] rmarkdown_2.30     grid_4.5.1         evaluate_1.0.5     jquerylib_0.1.4   
## [21] fastmap_1.2.0      yaml_2.3.12        lifecycle_1.0.5    stringr_1.6.0     
## [25] compiler_4.5.1     RColorBrewer_1.1-3 Rcpp_1.1.1         rstudioapi_0.18.0 
## [29] farver_2.1.2       digest_0.6.39      viridisLite_0.4.2  R6_2.6.1          
## [33] magrittr_2.0.4     bslib_0.9.0        withr_3.0.2        tools_4.5.1       
## [37] gtable_0.3.6       cachem_1.1.0

=====================================

Report generated on 2026-01-25 00:43:46.885783

For Applied Finance RCPP Home Assignment (2025/2026)

By Ramik Sharma (477656) | Masters in Data Science and Business Analytics | University of Warsaw (Department of Economics)