rm(list=ls()) library(highlight) library(knitr) library(ggplot2) library(dplyr) library(tidyr) ##homedir <- '/home/rmcd/tex/d67/Rtutorial/' options(digits=4) figsize <- 4.5 opts_chunk$set(size='footnotesize', prompt=FALSE, comment=NA, fig.align='center', fig.width = figsize, fig.height=figsize, out.width='3.75in') opts_knit$set(highlight = TRUE, eval.after='fig.cap', prompt=TRUE, renderer=renderer_latex(document=FALSE), size='footnotesize') curr <- function(amt) formatC(amt, format='f', digits=2)
library(derivmkts) library(mnormt) library(markdown) opts_chunk$set(collapse=TRUE)
This vignette is an overview to the functions in the derivmkts package, which was conceived as a companion to my book Derivatives Markets @mcdonald:derivs:13. The material has an educational focus. There are other option pricing packages for R, but this package has several distinguishing features:
simprice
function produces simulated asset pricesquincunx
function illustrates the workings of a
quincunx (Galton board).Table \ref{tab:bslist} lists the Black-Scholes related functions in
the package.^[See @black/scholes:73 and
@merton:73-bell.] The functions bscall
, bsput
, and
bsopt
provide basic pricing of European calls and puts. There are
also options with binary payoffs: cash-or-nothing and asset-or-nothing
options. All of these functions are vectorized. The function bsopt
by default provides option greeks. Here are some examples:
s <- 100; k <- 100; r <- 0.08; v <- 0.30; tt <- 2; d <- 0 bscall(s, k, v, r, tt, d) bsput(s, c(95, 100, 105), v, r, tt, d)
\begin{table}[tp] \centering \begin{tabular}{cp{4in}} Function& Description \ \hline bscall & European call\ bsput & European put\ bsopt & European call and put and associated Greeks: delta, gamma, vega, theta, rho, psi, and elasticity \ assetcall & Asset-or-nothing call\ assetput & Asset-or-nothing put\ cashcall & Cash-or-nothing call\ cashput & Cash-or-nothing put \end{tabular} \caption{Black-Scholes related option pricing functions} \label{tab:bslist} \end{table}
There are pricing functions for the following barrier options:^[See @merton:73-bell, p. 175, for the first derivation of a barrier option pricing formula and @mcdonald:derivs:13, Chapter 14, for an overview.]
Naming for the barrier options generally follows the convention
[u|d][i|o][call|put]
which means that the option is up'' or
down'', in'' or
out'', and a
call or put.^[This naming convention differs from that in
@mcdonald:derivs:13, in which names are callupin
,
callupout
, etc. Thus, I have made both names
available for these functions.] An up-and-in call, for example,
would be denoted by uicall
. For binary options, we add the
underlying, which is either the asset or \$1: cash:
[asset|cash][u|d][i|o][call|put]
H <- 115 bscall(s, c(80, 100, 120), v, r, tt, d) ## Up-and-in call uicall(s, c(80, 100, 120), v, r, tt, d, H) bsput(s, c(80, 100, 120), v, r, tt, d) ## Up-and-out put uoput(s, c(80, 100, 120), v, r, tt, d, H)
The functions callperpetual
and putperetual
price infinitely-lived American
options.^[@merton:73-bell derived the price of a
perpetual American put.] The pricing formula assumes that all inputs
(risk-free rate, volatility, dividend yield) are fixed. This is of
course usual with the basic option pricing formulas, but it is more of
a conceptual stretch for an infinitely-lived option than for a 3-month
option.
In order for the option to have a determined value, the dividend yield on the underlying asset must be positive if the option is a call. If this is not true, the call is never exercised and the price is undefined.^[A well-known result (see @merton:73-bell) is that a standard American call is never exercised before expiration if the dividend yield is zero and the interest rate is non-negative. A perpetual call with $\delta=0$ and $r>0$ would thus never be exercised. The limit of the option price as $\delta\to 0$ is $s$, so in this case the function returns the stock price as the option value.] Similarly, the risk-free rate must be positive if the option is a put.
By default, the perpetual pricing formulas return the price. By
setting showbarrier=TRUE
, the function returns both the option price
and the stock price at which the option is optimally exercised (the
``barrier''). Here are some examples:
s <- 100; k <- 100; r <- 0.08; v <- 0.30; tt <- 2; d <- 0.04 callperpetual(s, c(95, 100, 105), v, r, d) callperpetual(s, c(95, 100, 105), v, r, d, showbarrier=TRUE)
Options greeks are mathematical derivatives of the option price with
respect to inputs; see @mcdonald:derivs:13, Chapters 12 and
13, for a discussion of the greeks for vanilla
options. Greeks for vanilla and barrier options can be computed using
the greeks
function, which is a wrapper for any pricing function
that returns the option price and which uses the default naming of
inputs.^[In this version of the package, I have two
alternative functions that return Greeks:
bsopt
function by default
produces prices and Greeks for European calls and puts.greeks2
function takes as arguments the name of the pricing
function and then inputs as a list.These may be deprecated in the future. greeks2
is
more cumbersome to use but may be more robust. I welcome feedback on
these functions and what you find useful.
]
H <- 105 greeks(uicall(s, k, v, r, tt, d, H))
The value of this approach is that you can easily compute Greeks for
spreads and custom pricing functions. Here are two examples. First,
the value at time 0 of a prepaid contract that pays $S_{T}^{a}$ at
time $T$ is given by the powercontract()
function:
powercontract <- function(s, v, r, tt, d, a) { price <- exp(-r*tt)*s^a* exp((a*(r-d) + 1/2*a*(a-1)*v^2)*tt) }
We can easily compute the Greeks for a power contract:
greeks(powercontract(s=40, v=.08, r=0.08, tt=0.25, d=0, a=2))
Second, consider a bull spread in which we buy a call with a strike of
$k_{1}$ and sell a call with a strike of $k_2$. We can create a
function that computes the
value of the spread, and then compute the greeks for the spread by
using this newly-created function together with greeks()
:
bullspread <- function(s, v, r, tt, d, k1, k2) { bscall(s, k1, v, r, tt, d) - bscall(s, k2, v, r, tt, d) } greeks(bullspread(39:41, .3, .08, 1, 0, k1=40, k2=45))
The Greeks function is vectorized, so you can create vectors of greek values with a single call. This example plots, for a bull spread, the gamma as a function of the stock price; see Figure \ref{fig:bullgamma}.
sseq <- seq(1, 100, by=0.5) greeks(bullspread(sseq, .3, .08, 1, 0, k1=40, k2=45), initcaps = TRUE, long = TRUE) %>% filter(greek == 'Gamma' ) %>% ggplot(aes(x = s, y = value)) + geom_line()
This code produces the plots in Figure \ref{fig:allgreeks}:
k <- 100; r <- 0.08; v <- 0.30; tt <- 2; d <- 0 S <- seq(.5, 200, by=.5) Call <- greeks(bscall(S, k, v, r, tt, d), long = TRUE) Put <- greeks(bsput(S, k, v, r, tt, d), long = TRUE) ggplot(rbind(Call, Put), aes(x = s, y = value, color = funcname )) + geom_line() + facet_wrap(~ greek, scales = 'free_y') + scale_color_discrete(name = 'Option', labels = c('Call','Put' )) + scale_x_continuous('Stock', breaks =c(0, 100, 200) ) + scale_y_continuous('Value')
There are two functions related to binomial option pricing:^[See @cox/ross/rubinstein:79, @rendleman/bartter:79-jf, and @mcdonald:derivs:13, Chapter 11]
binomopt computes prices of American and European calls and puts. The function has three optional parameters that control output:
returnparams=TRUE
will return as a vector the option
pricing inputs, computed parameters, and risk-neutral probability.
returngreeks=TRUE
will return as a vector the price,
delta, gamma, and theta at the initial node.
returntrees=TRUE
will return as a list the price, greeks, the
full stock price tree, the exercise status (TRUE
or FALSE
) at
each node, and the replicating portfolio at each node.
binomplot displays the asset price tree, the corresponding probability of being at each node, and whether or not the option is exercised at each node. This function is described in more detail in Section \ref{sec:binomplot}.
Here are examples of pricing, illustrating the default of just returning the price, and the ability to return the price plus parameters, as well as the price, the parameters, and various trees:
s <- 100; k <- 100; r <- 0.08; v <- 0.30; tt <- 2; d <- 0.03 binomopt(s, k, v, r, tt, d, nstep=3) binomopt(s, k, v, r, tt, d, nstep=3, returnparams=TRUE) binomopt(s, k, v, r, tt, d, nstep=3, putopt=TRUE) binomopt(s, k, v, r, tt, d, nstep=3, returntrees=TRUE, putopt=TRUE)
There are analytical functions for valuing geometric Asian options and
Monte Carlo routines for valuing arithmetic Asian
options.^[See @kemna/vorst:90.] Be aware that the greeks()
function at this time will not work with the Monte Carlo valuation for
arithmetic Asian options. I plan to address this in a future
release.^[As the functions are currently written, each
invocation of the pricing function will start with a different random
number seed, resulting in price variation that is due solely to random
variation. Moreover, random number generation changes the random
number seed globally. In a future release I hope to address this by
saving and restoring the seed within the greeks function. For the
curious, a Stackoverflow
post
discusses this issue.]
Geometric Asian options can be valued using the Black-Scholes formulas for vanilla calls and puts, with modified inputs. The functions return both call and put prices with a named vector:
s <- 100; k <- 100; r <- 0.08; v <- 0.30; tt <- 2; d <- 0.03; m <- 3 geomavgpricecall(s, 98:102, v, r, tt, d, m) geomavgpricecall(s, 98:102, v, r, tt, d, m, cont=TRUE) geomavgstrikecall(s, k, v, r, tt, d, m)
Monte Carlo valuation is used to price arithmetic Asian options. For
efficiency, the function arithasianmc
returns call and put
prices for average price and average strike options. By default the
number of simulations is 1000. Optionally the function returns the
standard deviation of each estimate
arithasianmc(s, k, v, r, tt, d, 3, numsim=5000, printsds=TRUE)
The function arithavgpricecv
uses the control variate
method to reduce the variance in the simulation. At the moment this
function prices only calls, and returns both the price and the
regression coefficient used in the control variate correction:
arithavgpricecv(s, k, v, r, tt, d, 3, numsim=5000)
A compound option is an option where the underlying asset is an option.^[See @geske:79 and @mcdonald:derivs:13, Chapter 14.] The terminology associated with compound options can be confusing, so it may be easiest to start with an example.
Figure \ref{fig:compoundopt} is a timeline for a compound option that is an option to buy a put. The compound option expires at $t_{1}$ and the put expires at $t_{2}$. The owner of the compound option only acquires the put if at time $t_{1}$ it is worth at least $k_{co}$, and only exercises the put if at time $t_{2}$ the stock price is less than $k_{uo}$.
\begin{figure} \centering \begin{tikzpicture} \newcommand{\start}{0} \newcommand{\finish}{9} \newcommand{\midpt}{(\finish/2-\start/2} \newcommand{\tickheight}{0.3} \draw (\start,0) -- (\finish,0); \draw (0,\tickheight) -- (0, -\tickheight) node[below] {\begin{tabular}{c} Time 0 \ Buy compound option \end{tabular}}; \draw (\midpt,\tickheight) -- (\midpt, -\tickheight) node[below] {\begin{tabular}{c} Time $t_{1}$ \ Compound exercise decision \ Pay $k_{co}$ to buy put? \end{tabular} }; \draw (\finish, \tickheight) -- (\finish, -\tickheight) node[below] { \begin{tabular}{c} Time $t_{2}$ \ Put exercise decision\ Sell stock for $k_{uo}$? \end{tabular} }; \end{tikzpicture} \caption{The timeline for a compound option: a call to buy a put. The compound option expires at time $t_{1}$ and the underlying asset is a put option that expires at time $t_{2}$. At time $t_{1}$, the owner decides whether to pay $k_{co}$ to buy a put option which has time to expiration $t_{2} - t_{1}$. At time $t_{2}$ the owner decides whether to exercise the put, selling the stock for the strike price of $k_{uo}$.} \label{fig:compoundopt} \end{figure}
Based on the example, you can see that there are three prices associated with a compound option:
The definition of a compound option therefore requires that we specify
whether the compound option is a put or a call
the strike price at which you can exercise the underlying option ($k_{uo}$)
the strike price at which you can exercise the compound option ($k_{co}$)
the date at which you can exercise the compound option (first exercise date, $t_{1}$)
the date at which you can exercise the underlying option expires, $t_{2}>t_{1}$.
Given these possibilities, you can have a call on a call, a put on a call, a call on a put, and a put on a put. The valuation procedure require calculating the underlying asset price at which you are indifferent about acquiring the underlying option.
The price calculation requires computing the stock price above or below which you would optimally exercise the option at time $t_{1}$.
As an example, consider the following inputs for a call option to buy a call option:
s <- 100; kuo <- 95; v <- 0.30; r <- 0.08; t1 <- 0.50; t2 <- 0.75; d <- 0 kco <- 3.50 calloncall(s, kuo, kco, v, r, t1, t2, d, returnscritical=TRUE)
With these parameters, after 6 months ($t_{1}=0.5$), the compound
option buyer decides whether to pay \$r formatC(kco, format='f',
digits=2)
to acquire a 3-month call on the underlying asset. (The
volatility of the underlying asset is r v
.) It will be
worthwhile to pay the compound strike, \$r curr(kco)
, as long as
the underlying asset price exceeds r calloncall(s, kuo, kco, v,
r, t1, t2, d, returnscritical=TRUE)['scritical']
.
Similarly, there is a put on the call, and a call and put on the corresponding put:
putoncall(s, kuo, kco, v, r, t1, t2, d, returnscritical=TRUE) callonput(s, kuo, kco, v, r, t1, t2, d, returnscritical=TRUE) putonput(s, kuo, kco, v, r, t1, t2, d, returnscritical=TRUE)
\label{sec:jumps}
The mertonjump
function returns call and put prices for a
stock that can jump discretely.^[See @merton/jfe/1976.]
A poisson process controls the
occurrence of a jump and the size of the jump is lognormally
distributed. The parameter lambda
is the mean number of
jumps per year, the parameter alphaj
is the log of the
expected jump, and sigmaj
is the standard deviation of the
log of the jump. The jump amount is thus drawn from the distribution
\begin{equation}
Y \sim \mathcal{N}(\alpha_{J} - 0.5\sigma^{2}{J}, \sigma{J}^{2} )
\end{equation}
mertonjump(s, k, v, r, tt, d, lambda=0.5, alphaj=-0.2, vj=0.3) c(bscall(s, k, v, r, tt, d), bsput(s, k, v, r, tt, d))
The simple bond functions provided in this version compute the present
value of cash flows (bondpv
), the IRR of the bond (bondyield
),
Macaulay duration (duration
), and convexity (convexity
).
coupon <- 8; mat <- 20; yield <- 0.06; principal <- 100; modified <- FALSE; freq <- 2 price <- bondpv(coupon, mat, yield, principal, freq) price bondyield(price, coupon, mat, principal, freq) duration(price, coupon, mat, principal, freq, modified) convexity(price, coupon, mat, principal, freq)
The function simprice
provides a flexible way to generate prices
that can be used for Monte Carlo valuation or just to illustrate
sample paths. This function implements "naive" Monte Carlo, not making
use of any variance reduction techniques. When supplied with a
covariance matrix, simprice
produces correlated asset prices.
For more information on Monte Carlo see @mcdonald:derivs:13, Chapter
19, and @glasserman:mc:04, especially Chapter 4.
A simple example is to generate 5 random sample paths of daily stock prices for a year; see Figure \ref{fig:fivepaths}:
s0 <- 100; v <- 0.3; r <- 0.10; d <- 0; tt <- 1 trials <- 5; periods <- 365; set.seed(1) s <- simprice(s0 = s0, v = v, r = r, tt = tt, d = d, trials = trials, periods = periods, jump = FALSE, long = TRUE) ggplot(s, aes(x = period, y = price, color = trial, group = trial)) + geom_line()
We can do the same exercise adding jumps; see Figure \ref{fig:fivejumpers}.
s0 <- 100; v <- 0.3; r <- 0.10; d <- 0; tt <- 1 trials <- 5; periods <- 365; jump <- TRUE; lambda <- 2; alphaj <- -0.1; vj <- 0.2; set.seed(1) s <- simprice(s0 = s0, v = v, r = r, tt = tt, d = d, trials = trials, periods = periods, jump = jump, alphaj = alphaj, lambda = lambda, vj = vj, long = TRUE) ggplot(s, aes(x = period, y = price, color = trial, group = trial)) + geom_line()
\newpage
Here is an example using a three-asset covariance matrix, showing that
the simulated series have the correct variance structure. We have
three correlated stocks with standard deviations of 60%, 20%, and
45%., with the covariance matrix given by v
. Compare the estimated
covariance matrix with v
:
set.seed(1) vi <- diag(3) tt <- 2; periods <- tt*365 diag(vi) <- c(.6, .2, .45) vc <- matrix(c(1, .4, -.3, .4, 1, .25, -.3, .25, 1), nrow = 3) v <- vi %*% vc %*% vi s <- simprice(s0 = s0, v = v, r = r, tt = tt, d = d, trials = 1, periods = periods, jump = FALSE, long = TRUE) threestocks <- s %>% filter(trial == 1) %>% group_by(asset) %>% mutate(ret = log(price/lag(price)), row = row_number()) %>% select(asset, period, ret) %>% spread(key = asset, value = ret ) var(threestocks[2:4], na.rm = TRUE)*365 v
Several functions provide visual illustrations of some aspects of the material.
The quincunx is a physical device the illustrates the central limit theorem. A ball rolls down a pegboard and strikes a peg, falling randomly either to the left or right. As it continues down the board it continues to strike a series of pegs, randomly falling left or right at each. The balls collect in bins and create an approximate normal distribution.
The quincunx function allows the user to simulate a quincunx, observing the path of each ball and watching the height of each bin as the balls accumulate. More interestingly, the quincunx function permits altering the probability that the ball will fall to the right.
Figure \ref{fig:quincunx} illustrates the function after dropping 200 balls down 20 levels of pegs with a 70\% probability that each ball will fall right:
par(mar=c(2,2,2,2)) quincunx(n=20, numballs=200, delay=0, probright=0.7)
\label{sec:binomplot}
The binomplot
function calls binomopt
to compute the option price
and the various trees, which it then uses in plotting:
The first plot, figure \ref{fig:binomplot1}, is basic:
s0 <- 100; k <- 100; v <- 0.3; r <- 0.08; tt <- 2; d <- 0 binomplot(s0, k, v, r, tt, d, nstep=6, american=TRUE, putopt=TRUE)
The second plot, figure \ref{fig:binomplot2}, adds a display of stock prices and arrows connecting the nodes.
```r except that values and arrows are added to the plot.'} binomplot(s0, k, v, r, tt, d, nstep=6, american=TRUE, putopt=TRUE, plotvalues=TRUE, plotarrows=TRUE)
As a final example, consider an American call when the dividend yield is positive and `nstep` has a larger value. Figure \ref{fig:binomplot3} shows the plot, with early exercise evident. ```r d <- 0.06 binomplot(s0, k, v, r, tt, d, nstep=40, american=TRUE)
The large value of nstep
creates a high maximum terminal stock
price, which makes details hard to discern in the boundary region
where exercise first occurrs. We can zoom in on that region by
selecting values for ylimval
; the result is in Figure \ref{fig:binomplot4}.
d <- 0.06 binomplot(s0, k, v, r, tt, d, nstep=40, american=TRUE, ylimval=c(75, 225))
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.