===========================================
===========================================
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
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.
The Barrier options are popular because of the following reasons:
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:
# 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 simulationsI chose the barrier level \(L = 130\) because of the following:
# 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)# 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## =============================================================
## OPTION pricing results
## =============================================================
##
## Option parameters:
## - Spot price (S0): $ 105
## - Strike price (K): $ 110
## - Barrier level (L): $ 130
## - Volatility (sigma): 21 %
## - Risk-free rate (r): 5 %
## - Time to maturity (T): 0.75 years ( 9 months)
##
## Monte Carlo Parameters:
## - Time Steps: 189
## - Number of Simulations: 50,000
##
## ------------------------------------------------------------
## PRICING RESULTS:
## ------------------------------------------------------------
##
## Up-and-Out Call Price: $ 1.3916
## Vanilla Call Price: $ 7.1226
## Barrier Discount: 19.54 % of vanilla
##
## ============================================================
Here we are analyzing how the up-and-out Call price varies with the following parameters:
# 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:
## - Volatility range: 10 % to 40 %
## - 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
# 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)# 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)# 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)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.
The relationship between volatility and the up-and-out call price is definitely complex and quite non-monotonic:
So, this ends up creating a characteristic hump-shaped pattern, which is definitely a distinctive feature of knock-out barrier options.
For up-and-out options, the longer the time to maturity has a competitive effects:
However, the net effect depends on the volatility level and the proximity to the barrier set.
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.
The barrier option is quite cheaper because:
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:
A summary of the key insights:
## 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)