[R-SIG-Finance] American option sensitivities

Enrico Schumann enricoschumann at yahoo.de
Fri Feb 10 18:05:04 CET 2012



Am 10.02.2012 15:24, schrieb J Toll:
> On Fri, Feb 10, 2012 at 3:18 AM, Enrico Schumann
> <enricoschumann at yahoo.de>  wrote:
>>
>> Hi all,
>>
>> (comments below)
>>
>> Am 10.02.2012 01:02, schrieb J Toll:
>>
>>> On Thu, Feb 9, 2012 at 5:17 PM, Dirk Eddelbuettel<edd at debian.org>    wrote:
>>>>
>>>>
>>>> On 9 February 2012 at 17:06, J Toll wrote:
>>>> | Hi,
>>>> |
>>>> | I'd like to calculate sensitivities on American options.  I was hoping
>>>> | somebody might be able to summarize of the current state of that
>>>> | functionality within the various R packages.  It's my understanding
>>>> | that the fOptions package can calculate greeks for European options
>>>> | but not American.  RQuantLib appears to have had the ability to
>>>> | calculate greeks for American options at one point, but it appears
>>>> | that functionality was removed in Release 0.1.8 sometime around
>>>> | 2003-11-28.
>>>>
>>>> ... because that functionality was removed upstream by QuantLib.
>>>>
>>>> |
>>>> |
>>>> http://lists.r-forge.r-project.org/pipermail/rquantlib-commits/2010-August/000117.html
>>>> |
>>>> | Additionally, from RQuantLib ?AmericanOptions says,
>>>> |
>>>> | "Note that under the new pricing framework used in QuantLib, binary
>>>> | pricers do not provide analytics for 'Greeks'. This is expected to be
>>>> | addressed in future releases of QuantLib."
>>>> |
>>>> | I haven't found any other packages for calculating option
>>>> | sensitivities.  Are there any other packages?
>>>> |
>>>> | Regarding RQuantLib, is the issue that that functionality hasn't been
>>>> | implemented in R yet, or is it QuantLib that's broken?
>>>>
>>>> There is a third door behind which you find the price: "numerical
>>>> shocks".
>>>>
>>>> Evaluate your american option, then shift the various parameters (spot,
>>>> vol,
>>>> int.rate, time to mat, ...) each by a small amount and calculate the
>>>> change
>>>> in option price -- voila for the approximate change in option value for
>>>> change input.  You can also compute twice at  'x - eps' and 'x + eps'
>>>> etc.
>>>>
>>>> Dirk
>>>
>>>
>>> Dirk,
>>>
>>> Thank you for your response.  I was hoping you might reply.
>>>
>>> I understand the concept of your suggestion, although I don't have any
>>> practical experience implementing it.  I'm guessing this is what's
>>> generally referred to as finite difference methods.  In theory, the
>>> first order greeks should be simple enough, although my impression is
>>> the second or third order greeks may be a bit more challenging.
>>>
>>> I hate to trouble you for more information, but I'm curious why?  Is
>>> this the "standard" method of calculating greeks for American options?
>>>   Has QuantLib decided not to implement this calculation? Just curious.
>>>
>>> Thanks again,
>>>
>>
>> A simple forward difference is
>>
>> [f(x + h) - f(x)] / h
>>
>> 'f' is the option pricing formula; 'x' are the arguments to the formula, and
>> 'h' is a small offset.
>>
>> Numerically, 'h' should not be made too small:
>>
>> (1) Even for smooth functions, we trade off truncation error (which is large
>> when 'h' is large) against roundoff-error (in the extreme, 'x + h' may still
>> be 'x' for a very small 'h').
>>
>> (2) American options are typically valued via finite-difference or tree
>> methods, and hence 'f' is not smooth and any 'bumps' in the function will be
>> magnified by dividing by a very small 'h'. So when 'h' is too small, the
>> results will become nonsensical.
>>
>> Here is an example. As a first test, I use a European option.
>>
>> require("RQuantLib")
>> h<- 1e-4
>> S<- 100
>> K<- 100
>> tau<- 0.5
>> vol<- 0.3
>> C0<- EuropeanOption(type = "call",
>>                      underlying = S, strike = K,
>>                      dividendYield = 0.0,
>>                      riskFreeRate = 0.03, maturity = tau,
>>                      volatility = 0.3)
>> Cplus<- EuropeanOption(type="call",
>>                         underlying = S + h, strike = K,
>>                         dividendYield = 0.0,
>>                         riskFreeRate=0.03, maturity=tau,
>>                         volatility=0.3)
>> Cminus<- EuropeanOption(type="call",
>>                          underlying = S - h, strike=K,
>>                          dividendYield=0.0,
>>                          riskFreeRate=0.03, maturity=tau,
>>                          volatility=0.3)
>>
>> ## a first-order difference: delta
>> (Cplus$value-C0$value)/h
>> ## [1] 0.570159
>> C0$delta
>> ## [1] 0.5701581
>>
>>
>> ## a second-order difference
>> (Cplus$delta-C0$delta)/h
>> ## [1] 0.01851474
>> C0$gamma
>> ## [1] 0.01851475
>>
>>
>>
>>
>> Now for an American option. Here we don't have the delta, so we first need
>> to compute it as well.
>>
>> C0<- AmericanOption(type="put",
>>                      underlying = S, strike=K,
>>                      dividendYield=0.0,
>>                      riskFreeRate=0.03, maturity=tau,
>>                      volatility=vol)
>> Cplus<- AmericanOption(type="put",
>>                         underlying = S + h, strike=K,
>>                         dividendYield=0.0,
>>                         riskFreeRate=0.03, maturity=tau,
>>                         volatility=vol)
>> Cminus<- AmericanOption(type="put",
>>                          underlying = S - h, strike=K,
>>                          dividendYield=0.0,
>>                          riskFreeRate=0.03, maturity=tau,
>>                          volatility=vol)
>>
>> ## a first-order difference: delta
>> (dplus<- (Cplus$value - C0$value)/h)
>> (dminus<-(C0$value - Cminus$value)/h)
>>
>> ## a second-order difference
>> (dplus - dminus)/h
>> ## [1] 0.01905605
>>
>> I ran a little a experiment with different levels of 'h', where you can
>> clearly see when the gamma diverges.
>>
>> |    h |      gamma |
>> |    1 | 0.01905385 |
>> | 0.01 | 0.01905612 |
>> | 1e-4 | 0.01905605 |
>> | 1e-5 | 0.01915801 |
>> | 1e-6 | 0.03463896 |
>> | 1e-8 |   8.881784 |
>>
>>
>> In the literatur, you find a number of tricks to smooth the function, but in
>> my experience, you are fine if you make 'h' small with respect to 'x' --
>> small, not tiny. So if the stock price is 100, a change of 1 or 0.1 is
>> small. (And think of it: even if we found that a change of one-thousandth of
>> a cent led to a meaningful numerical difference; if the stock price never
>> moves by such an amount, such a computation would not be empirically
>> meaningful.)
>>
>>
>> Regards,
>> Enrico
>>
>
> Enrico,
>
> Thank you so much for such a detailed and helpful example.  I had
> found an article on Wikipedia mentioning many of the issues you write
> about regarding selection of h.  In that article, the author/s
> suggest:
>
> "A choice for h which is small without producing a large rounding
> error is sqrt(ε  x) where the machine epsilon ε is typically of the
> order 2.2×10-16."
>
> http://en.wikipedia.org/wiki/Numerical_differentiation
>
> So for R, I suppose that would equate to:
>
> h<- sqrt(.Machine$double.eps * x)
>
> Thanks again for your really helpful example.
>
> Best,
>

I am glad if it helped. The value for 'h' that the Wikipedia article 
cites is very likely for a forward difference; this is not generally 
appropriate for other differences (eg, central  or higher-order 
differences) -- it will be too small then.

Here is again the example I gave, slightly modified to directly compute 
the second-order derivative.

require("RQuantLib")
h <- 10^(-(0:10))  ## a vector of values
S <- 100
K <- 100
tau <- 0.5
vol <- 0.3
C0 <- EuropeanOption(type = "call",
                      underlying = S, strike = K,
                      dividendYield = 0.0,
                      riskFreeRate = 0.03, maturity = tau,
                      volatility = vol)

gammaVec <- numeric(length(h))
for (i in seq(along.with = h)) {
     Cplus <- EuropeanOption(type="call",
                             underlying = S + h[i], strike = K,
                             dividendYield = 0.0,
                             riskFreeRate = 0.03,
                             maturity = tau,
                             volatility = vol)
     Cminus <- EuropeanOption(type="call",
                              underlying = S - h[i], strike = K,
                              dividendYield = 0.0,
                              riskFreeRate = 0.03, maturity = tau,
                              volatility = vol)

     ## a central difference
     gammaVec[i] <- (Cplus$value + Cminus$value - 2*C0$value)/h[i]^2
}


## the analytic solution
C0$gamma
## [1] 0.01851475

data.frame(h = h, gamma = gammaVec)

        h         gamma
1  1e+00  1.851213e-02
2  1e-01  1.851473e-02
3  1e-02  1.851475e-02
4  1e-03  1.851475e-02
5  1e-04  1.851461e-02
6  1e-05  1.861622e-02
7  1e-06  1.421085e-02
8  1e-07  0.000000e+00
9  1e-08  7.105427e+01
10 1e-09 -1.421085e+04
11 1e-10 -7.105427e+05


The goal in choosing 'h' is to find the 'sweet spot' where the 
approximation error (which is a combination of truncation and roundoff 
error) is smallest. My suggestion is that, if in doubt, to use a larger 
value for 'h' than prescribed on Wikipedia or the literature. (Or, even 
better, to run experiments to determine a good value for 'h' for your 
implementation.)


Regards,
Enrico


>
> James
>
>

-- 
Enrico Schumann
Lucerne, Switzerland
http://nmof.net/



More information about the R-SIG-Finance mailing list