GnmFunc: make this a GObject.
[gnumeric.git] / plugins / fn-derivatives / options.c
blob0f6d3049fadeb1ba07ebbaa23bb47b3a97eac0ef
1 /*
2 * Options pricing
4 * Authors:
5 * Elliot Lee <sopwith@redhat.com> All initial work.
6 * Morten Welinder <terra@gnome.org> Port to new plugin framework.
7 * Cleanup.
8 * Hal Ashburner <hal_ashburner@yahoo.co.uk>
9 * Black Scholes Code re-structure, optional asset leakage paramaters,
10 * American approximations, alternative models to Black-Scholes
11 * and All exotic Options Functions.
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses/>.
26 #include <gnumeric-config.h>
27 #include <gnumeric.h>
28 #include <gnm-i18n.h>
29 #include <goffice/goffice.h>
30 #include <gnm-plugin.h>
32 #include <func.h>
33 #include <mathfunc.h>
34 #include <sf-dpq.h>
35 #include <sf-gamma.h>
36 #include <value.h>
38 #include <math.h>
39 #include <string.h>
41 GNM_PLUGIN_MODULE_HEADER;
43 /* Some common decriptors */
44 #define DEF_ARG_CALL_PUT_FLAG { GNM_FUNC_HELP_ARG, F_("call_put_flag:'c' for a call and 'p' for a put") }
45 #define DEF_ARG_SPOT { GNM_FUNC_HELP_ARG, F_("spot:spot price") }
46 #define DEF_ARG_STRIKE { GNM_FUNC_HELP_ARG, F_("strike:strike price") }
47 #define DEF_ARG_TIME_MATURITY_Y { GNM_FUNC_HELP_ARG, F_("time:time to maturity in years") }
48 #define DEF_ARG_TIME_MATURITY_D { GNM_FUNC_HELP_ARG, F_("time:time to maturity in days") }
49 #define DEF_ARG_TIME_DIVIDEND { GNM_FUNC_HELP_ARG, F_("time_payout:time to dividend payout") }
50 #define DEF_ARG_TIME_EXPIRATION { GNM_FUNC_HELP_ARG, F_("time_exp:time to expiration") }
51 #define DEF_ARG_RATE_RISKFREE { GNM_FUNC_HELP_ARG, F_("rate:risk-free interest rate to the exercise date in percent") }
52 #define DEF_ARG_RATE_ANNUALIZED { GNM_FUNC_HELP_ARG, F_("rate:annualized interest rate") }
53 #define DEF_ARG_RATE_RISKFREE_ANN { GNM_FUNC_HELP_ARG, F_("rate:annualized risk-free interest rate") }
54 #define DEF_ARG_VOLATILITY { GNM_FUNC_HELP_ARG, F_("volatility:annualized volatility of the asset in percent for the period through to the exercise date") }
55 #define DEF_ARG_VOLATILITY_SHORT { GNM_FUNC_HELP_ARG, F_("volatility:annualized volatility of the asset") }
56 #define DEF_ARG_AMOUNT { GNM_FUNC_HELP_ARG, F_("d:amount of the dividend to be paid expressed in currency") }
57 #define DEF_ARG_CC_OPT { GNM_FUNC_HELP_ARG, F_("cost_of_carry:net cost of holding the underlying asset (for common stocks, the risk free rate less the dividend yield), defaults to 0") }
58 #define DEF_ARG_CC { GNM_FUNC_HELP_ARG, F_("cost_of_carry:net cost of holding the underlying asset") }
60 #define DEF_NOTE_UNITS { GNM_FUNC_HELP_NOTE, F_("The returned value will be expressed in the same units as @{strike} and @{spot}.")}
63 typedef enum {
64 OS_Call,
65 OS_Put,
66 OS_Error
67 } OptionSide;
69 typedef enum{
70 OT_Euro,
71 OT_Amer,
72 OT_Error
73 } OptionType;
75 static gnm_float opt_baw_call (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v);
76 static gnm_float opt_baw_put (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v);
77 static gnm_float NRA_c (gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v);
78 static gnm_float NRA_p (gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v);
79 static gnm_float opt_bjer_stens1_c (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v);
80 /* static gnm_float opt_bjer_stens1_p (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); */
81 static gnm_float phi (gnm_float s, gnm_float t, gnm_float gamma, gnm_float H, gnm_float I, gnm_float r, gnm_float b, gnm_float v);
82 static gnm_float CriticalValueOptionsOnOptions (OptionSide side, gnm_float x1, gnm_float x2, gnm_float t,
83 gnm_float r, gnm_float b, gnm_float v);
84 static gnm_float opt_crit_val_chooser (gnm_float s,gnm_float xc,gnm_float xp,gnm_float t,
85 gnm_float tc, gnm_float tp, gnm_float r, gnm_float b, gnm_float v);
88 static OptionSide
89 option_side (char const *s)
91 if (s[0] == 'p' || s[0] == 'P')
92 return OS_Put;
93 else if (s[0] == 'c' || s[0] == 'C')
94 return OS_Call;
95 else
96 return OS_Error;
99 static OptionType
100 option_type (char const *s)
102 if (s[0] == 'a' || s[0] == 'A')
103 return OT_Amer;
104 else if (s[0] == 'e' || s[0] == 'E')
105 return OT_Euro;
106 else
107 return OT_Error;
110 /* The normal distribution function */
111 static gnm_float
112 ncdf (gnm_float x)
114 return pnorm (x, 0.0, 1.0, TRUE, FALSE);
117 static gnm_float
118 npdf (gnm_float x)
120 return dnorm (x, 0.0, 1.0, FALSE);
123 static int
124 Sgn (gnm_float a)
126 if ( a >0)
127 return 1;
128 else if (a < 0)
129 return -1;
130 else
131 return 0;
134 /* The cumulative bivariate normal distribution function */
135 static gnm_float
136 cum_biv_norm_dist1 (gnm_float a, gnm_float b, gnm_float rho)
138 gnm_float rho1, rho2, delta;
139 gnm_float a1, b1, sum = 0.0;
140 int i, j;
142 static const gnm_float x[] = {0.24840615, 0.39233107, 0.21141819, 0.03324666, 0.00082485334};
143 static const gnm_float y[] = {0.10024215, 0.48281397, 1.0609498, 1.7797294, 2.6697604};
144 a1 = a / gnm_sqrt (2.0 * (1 - (rho * rho)));
145 b1 = b / gnm_sqrt (2.0 * (1 - (rho * rho)));
147 if (a <= 0 && b <= 0 && rho <= 0) {
148 for (i = 0; i != 5; ++i) {
149 for (j = 0; j != 5; ++j) {
150 sum = sum + x[i] * x[j] * gnm_exp (a1 * (2.0 * y[i] - a1) + b1 * (2.0 *
151 y[j] - b1) + 2 * rho * (y[i] - a1) * (y[j] - b1));
154 return gnm_sqrt (1.0 - (rho * rho)) / M_PIgnum * sum;
155 } else if (a <= 0 && b >= 0 && rho >= 0)
156 return ncdf (a) - cum_biv_norm_dist1 (a,-b,-rho);
157 else if (a >= 0 && b <= 0 && rho >= 0)
158 return ncdf (b) - cum_biv_norm_dist1 (-a,b,-rho);
159 else if (a >= 0 && b >= 0 && rho <= 0)
160 return ncdf (a) + ncdf (b) - 1.0 + cum_biv_norm_dist1 (-a,-b,rho);
161 else if ((a * b * rho) > 0) {
162 rho1 = (rho * a - b) * Sgn (a) / gnm_sqrt ((a * a) - 2 * rho * a
163 * b + (b * b));
164 rho2 = (rho * b - a) * Sgn (b) / gnm_sqrt ((a * a) - 2 * rho * a
165 * b + (b * b));
166 delta = (1.0 - Sgn (a) * Sgn (b)) / 4.0;
167 return (cum_biv_norm_dist1 (a,0.0,rho1) +
168 cum_biv_norm_dist1 (b,0.0,rho2) -
169 delta);
171 return gnm_nan;
175 static GnmValue *
176 cum_biv_norm_dist(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
178 gnm_float a = value_get_as_float (argv[0]);
179 gnm_float b = value_get_as_float (argv[1]);
180 gnm_float rho = value_get_as_float (argv[2]);
181 gnm_float result = cum_biv_norm_dist1 (a,b,rho);
183 if (gnm_isnan (result))
184 return value_new_error_NUM (ei->pos);
185 else
186 return value_new_float (result);
189 static GnmFuncHelp const help_cum_biv_norm_dist[] = {
190 { GNM_FUNC_HELP_NAME, F_("CUM_BIV_NORM_DIST:cumulative bivariate normal distribution")},
191 { GNM_FUNC_HELP_ARG, F_("a:limit for first random variable")},
192 { GNM_FUNC_HELP_ARG, F_("b:limit for second random variable")},
193 { GNM_FUNC_HELP_ARG, F_("rho:correlation of the two random variables")},
194 { GNM_FUNC_HELP_DESCRIPTION, F_("CUM_BIV_NORM_DIST calculates the probability that two standard "
195 "normal distributed random variables with correlation @{rho} are "
196 "respectively each less than @{a} and @{b}.")},
197 { GNM_FUNC_HELP_EXAMPLES, "=CUM_BIV_NORM_DIST(0,0,0.5)" },
198 { GNM_FUNC_HELP_END}
203 /* the generalized Black and Scholes formula*/
204 static gnm_float
205 opt_bs1 (OptionSide side,
206 gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v,
207 gnm_float b)
209 gnm_float d1 = (gnm_log (s / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
210 gnm_float d2 = d1 - v * gnm_sqrt (t);
212 switch (side) {
213 case OS_Call:
214 return (s * gnm_exp ((b - r) * t) * ncdf (d1) -
215 x * gnm_exp (-r * t) * ncdf (d2));
216 case OS_Put:
217 return (x * gnm_exp (-r * t) * ncdf (-d2) -
218 s * gnm_exp ((b - r) * t) * ncdf (-d1));
219 default:
220 return gnm_nan;
225 static GnmValue *
226 opt_bs (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
228 OptionSide call_put = option_side (value_peek_string (argv[0]));
229 gnm_float s = value_get_as_float (argv[1]);
230 gnm_float x = value_get_as_float (argv[2]);
231 gnm_float t = value_get_as_float (argv[3]);
232 gnm_float r = value_get_as_float (argv[4]);
233 gnm_float v = value_get_as_float (argv[5]);
234 gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0;
235 gnm_float gfresult = opt_bs1 (call_put, s, x, t, r, v, b);
237 if (gnm_isnan (gfresult))
238 return value_new_error_NUM (ei->pos);
239 return value_new_float (gfresult);
242 static GnmFuncHelp const help_opt_bs[] = {
243 { GNM_FUNC_HELP_NAME, F_("OPT_BS:price of a European option")},
244 DEF_ARG_CALL_PUT_FLAG,
245 DEF_ARG_SPOT,
246 DEF_ARG_STRIKE,
247 DEF_ARG_TIME_MATURITY_Y,
248 DEF_ARG_RATE_RISKFREE,
249 DEF_ARG_VOLATILITY,
250 DEF_ARG_CC_OPT,
251 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_BS uses the Black-Scholes model to calculate "
252 "the price of a European option struck at @{strike} "
253 "on an asset with spot price @{spot}.")},
254 DEF_NOTE_UNITS,
255 { GNM_FUNC_HELP_SEEALSO, "OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_VEGA,OPT_BS_GAMMA"},
256 { GNM_FUNC_HELP_END}
259 /* Delta for the generalized Black and Scholes formula */
260 static gnm_float
261 opt_bs_delta1 (OptionSide side,
262 gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
264 gnm_float d1 =
265 (gnm_log (s / x) + (b + (v * v) / 2.0) * t) /
266 (v * gnm_sqrt (t));
268 switch (side) {
269 case OS_Call:
270 return gnm_exp ((b - r) * t) * ncdf (d1);
272 case OS_Put:
273 return gnm_exp ((b - r) * t) * (ncdf (d1) - 1.0);
275 default:
276 return gnm_nan;
281 static GnmValue *
282 opt_bs_delta (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
284 OptionSide call_put = option_side (value_peek_string (argv[0]));
285 gnm_float s = value_get_as_float (argv[1]);
286 gnm_float x = value_get_as_float (argv[2]);
287 gnm_float t = value_get_as_float (argv[3]);
288 gnm_float r = value_get_as_float (argv[4]);
289 gnm_float v = value_get_as_float (argv[5]);
290 gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0;
291 gnm_float gfresult = opt_bs_delta1 (call_put, s, x, t, r, v, b);
293 if (gnm_isnan (gfresult))
294 return value_new_error_NUM (ei->pos);
296 return value_new_float (gfresult);
299 static GnmFuncHelp const help_opt_bs_delta[] = {
300 { GNM_FUNC_HELP_NAME, F_("OPT_BS_DELTA:delta of a European option")},
301 DEF_ARG_CALL_PUT_FLAG,
302 DEF_ARG_SPOT,
303 DEF_ARG_STRIKE,
304 DEF_ARG_TIME_MATURITY_Y,
305 DEF_ARG_RATE_RISKFREE,
306 DEF_ARG_VOLATILITY,
307 DEF_ARG_CC_OPT,
308 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_BS_DELTA uses the Black-Scholes model to calculate "
309 "the 'delta' of a European option struck at @{strike} "
310 "on an asset with spot price @{spot}.")},
311 DEF_NOTE_UNITS,
312 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_VEGA,OPT_BS_GAMMA"},
313 { GNM_FUNC_HELP_END}
317 /* Gamma for the generalized Black and Scholes formula */
318 static gnm_float
319 opt_bs_gamma1 (gnm_float s,gnm_float x,gnm_float t,gnm_float r,gnm_float v,gnm_float b)
321 gnm_float d1;
323 d1 = (gnm_log (s / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
324 return gnm_exp ((b - r) * t) * npdf (d1) / (s * v * gnm_sqrt (t));
328 static GnmValue *
329 opt_bs_gamma (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
331 gnm_float s = value_get_as_float (argv[0]);
332 gnm_float x = value_get_as_float (argv[1]);
333 gnm_float t = value_get_as_float (argv[2]);
334 gnm_float r = value_get_as_float (argv[3]);
335 gnm_float v = value_get_as_float (argv[4]);
336 gnm_float b = argv[5] ? value_get_as_float (argv[5]) : 0.0;
337 gnm_float gfresult = opt_bs_gamma1 (s,x,t,r,v,b);
338 return value_new_float (gfresult);
341 static GnmFuncHelp const help_opt_bs_gamma[] = {
342 { GNM_FUNC_HELP_NAME, F_("OPT_BS_GAMMA:gamma of a European option")},
343 DEF_ARG_SPOT,
344 DEF_ARG_STRIKE,
345 DEF_ARG_TIME_MATURITY_Y,
346 DEF_ARG_RATE_RISKFREE,
347 DEF_ARG_VOLATILITY,
348 DEF_ARG_CC_OPT,
349 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_BS_GAMMA uses the Black-Scholes model to calculate "
350 "the 'gamma' of a European option struck at @{strike} "
351 "on an asset with spot price @{spot}. The gamma of an "
352 "option is the second derivative of its price "
353 "with respect to the price of the underlying asset.")},
354 { GNM_FUNC_HELP_NOTE, F_("Gamma is expressed as the rate of change "
355 "of delta per unit change in @{spot}.")},
356 { GNM_FUNC_HELP_NOTE, F_("Gamma is the same for calls and puts.")},
357 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_VEGA"},
358 { GNM_FUNC_HELP_END}
361 /* theta for the generalized Black and Scholes formula */
362 static gnm_float
363 opt_bs_theta1 (OptionSide side,
364 gnm_float s,gnm_float x,gnm_float t,gnm_float r,gnm_float v,gnm_float b)
366 gnm_float d1 = (gnm_log (s / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
367 gnm_float d2 = d1 - v * gnm_sqrt (t);
369 switch (side) {
370 case OS_Call:
371 return -s * gnm_exp ((b - r) * t) * npdf (d1) * v / (2.0 * gnm_sqrt (t)) -
372 (b - r) * s * gnm_exp ((b - r) * t) * ncdf (d1) - r * x * gnm_exp (-r * t) * ncdf (d2);
373 case OS_Put:
374 return -s * gnm_exp ((b - r) * t) * npdf (d1) * v / (2.0 * gnm_sqrt (t)) +
375 (b - r) * s * gnm_exp ((b - r) * t) * ncdf (-d1) + r * x * gnm_exp (-r * t) * ncdf (-d2);
376 default:
377 return gnm_nan;
381 static GnmValue *
382 opt_bs_theta (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
384 OptionSide call_put = option_side (value_peek_string (argv[0]));
385 gnm_float s = value_get_as_float (argv[1]);
386 gnm_float x = value_get_as_float (argv[2]);
387 gnm_float t = value_get_as_float (argv[3]);
388 gnm_float r = value_get_as_float (argv[4]);
389 gnm_float v = value_get_as_float (argv[5]);
390 gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0;
391 gnm_float gfresult = opt_bs_theta1 (call_put, s, x, t, r, v, b);
393 if (gnm_isnan (gfresult))
394 return value_new_error_NUM (ei->pos);
395 return value_new_float (gfresult);
398 static GnmFuncHelp const help_opt_bs_theta[] = {
399 { GNM_FUNC_HELP_NAME, F_("OPT_BS_THETA:theta of a European option")},
400 DEF_ARG_CALL_PUT_FLAG,
401 DEF_ARG_SPOT,
402 DEF_ARG_STRIKE,
403 DEF_ARG_TIME_MATURITY_Y,
404 DEF_ARG_RATE_RISKFREE,
405 DEF_ARG_VOLATILITY,
406 DEF_ARG_CC_OPT,
407 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_BS_THETA uses the Black-Scholes model to calculate "
408 "the 'theta' of a European option struck at @{strike} "
409 "on an asset with spot price @{spot}. The theta of an "
410 "option is the rate of change of its price with "
411 "respect to time to expiry.")},
412 { GNM_FUNC_HELP_NOTE, F_("Theta is expressed as the negative of the rate of change "
413 "of the option value, per 365.25 days.")},
414 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_VEGA,OPT_BS_GAMMA"},
415 { GNM_FUNC_HELP_END}
419 /* Vega for the generalized Black and Scholes formula */
420 static gnm_float
421 opt_bs_vega1 (gnm_float s, gnm_float x, gnm_float t,
422 gnm_float r, gnm_float v, gnm_float b)
424 gnm_float d1 = (gnm_log (s / x) + (b + (v * v) / 2.0) * t) /
425 (v * gnm_sqrt (t));
426 return s * gnm_exp ((b - r) * t) * npdf (d1) * gnm_sqrt (t);
429 static GnmValue *
430 opt_bs_vega (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
432 gnm_float s = value_get_as_float (argv[0]);
433 gnm_float x = value_get_as_float (argv[1]);
434 gnm_float t = value_get_as_float (argv[2]);
435 gnm_float r = value_get_as_float (argv[3]);
436 gnm_float v = value_get_as_float (argv[4]);
437 gnm_float b = argv[5] ? value_get_as_float (argv[5]) : 0.0;
439 return value_new_float (opt_bs_vega1 (s, x, t, r, v, b));
442 static GnmFuncHelp const help_opt_bs_vega[] = {
443 { GNM_FUNC_HELP_NAME, F_("OPT_BS_VEGA:vega of a European option")},
444 DEF_ARG_SPOT,
445 DEF_ARG_STRIKE,
446 DEF_ARG_TIME_MATURITY_Y,
447 DEF_ARG_RATE_RISKFREE,
448 DEF_ARG_VOLATILITY,
449 DEF_ARG_CC_OPT,
450 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_BS_VEGA uses the Black-Scholes model to calculate "
451 "the 'vega' of a European option struck at @{strike} "
452 "on an asset with spot price @{spot}. The vega of an "
453 "option is the rate of change of its price with "
454 "respect to volatility.")},
455 { GNM_FUNC_HELP_NOTE, F_("Vega is the same for calls and puts.")},
456 /* xgettext:no-c-format */
457 { GNM_FUNC_HELP_NOTE, F_("Vega is expressed as the rate of change "
458 "of option value, per 100% volatility.")},
459 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
460 { GNM_FUNC_HELP_END}
464 /* Rho for the generalized Black and Scholes formula */
465 static gnm_float
466 opt_bs_rho1 (OptionSide side, gnm_float s, gnm_float x,
467 gnm_float t, gnm_float r, gnm_float v, gnm_float b)
469 gnm_float d1 = (gnm_log (s / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
470 gnm_float d2 = d1 - v * gnm_sqrt (t);
471 switch (side) {
472 case OS_Call:
473 if (b != 0)
474 return t * x * gnm_exp (-r * t) * ncdf (d2);
475 else
476 return -t * opt_bs1 (side, s, x, t, r, v, b);
478 case OS_Put:
479 if (b != 0)
480 return -t * x * gnm_exp (-r * t) * ncdf (-d2);
481 else
482 return -t * opt_bs1 (side, s, x, t, r, v, b);
484 default:
485 return gnm_nan;
490 static GnmValue *
491 opt_bs_rho (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
493 OptionSide call_put = option_side (value_peek_string (argv[0]));
494 gnm_float s = value_get_as_float (argv[1]);
495 gnm_float x = value_get_as_float (argv[2]);
496 gnm_float t = value_get_as_float (argv[3]);
497 gnm_float r = value_get_as_float (argv[4]);
498 gnm_float v = value_get_as_float (argv[5]);
499 gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0;
500 gnm_float gfresult = opt_bs_rho1 (call_put, s, x, t, r, v, b);
502 if (gnm_isnan (gfresult))
503 return value_new_error_NUM (ei->pos);
504 return value_new_float (gfresult);
507 static GnmFuncHelp const help_opt_bs_rho[] = {
508 { GNM_FUNC_HELP_NAME, F_("OPT_BS_RHO:rho of a European option")},
509 DEF_ARG_CALL_PUT_FLAG,
510 DEF_ARG_SPOT,
511 DEF_ARG_STRIKE,
512 DEF_ARG_TIME_MATURITY_Y,
513 DEF_ARG_RATE_RISKFREE,
514 DEF_ARG_VOLATILITY,
515 DEF_ARG_CC_OPT,
516 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_BS_RHO uses the Black-Scholes model to calculate "
517 "the 'rho' of a European option struck at @{strike} "
518 "on an asset with spot price @{spot}. The rho of an "
519 "option is the rate of change of its price with "
520 "respect to the risk free interest rate.")},
521 /* xgettext:no-c-format */
522 { GNM_FUNC_HELP_NOTE, F_("Rho is expressed as the rate of change "
523 "of the option value, per 100% change in @{rate}.")},
524 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_THETA,OPT_BS_VEGA,OPT_BS_GAMMA"},
525 { GNM_FUNC_HELP_END}
528 /* Carry for the generalized Black and Scholes formula */
529 static gnm_float
530 opt_bs_carrycost1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
532 gnm_float d1 = (gnm_log (s / x) + (b + (v * v) / 2.0) * t) /
533 (v * gnm_sqrt (t));
535 switch (side) {
536 case OS_Call:
537 return t * s * gnm_exp ((b - r) * t) * ncdf (d1);
538 case OS_Put:
539 return -t * s * gnm_exp ((b - r) * t) * ncdf (-d1);
540 default:
541 return gnm_nan; /*should never get to here*/
545 static GnmValue *
546 opt_bs_carrycost (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
548 OptionSide call_put = option_side (value_peek_string (argv[0]));
549 gnm_float s = value_get_as_float (argv[1]);
550 gnm_float x = value_get_as_float (argv[2]);
551 gnm_float t = value_get_as_float (argv[3]);
552 gnm_float r = value_get_as_float (argv[4]);
553 gnm_float v = value_get_as_float (argv[5]);
554 gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0;
555 gnm_float gfresult = opt_bs_carrycost1 (call_put, s, x, t, r, v, b);
557 if (gnm_isnan (gfresult))
558 return value_new_error_NUM (ei->pos);
559 return value_new_float (gfresult);
563 static GnmFuncHelp const help_opt_bs_carrycost[] = {
564 { GNM_FUNC_HELP_NAME, F_("OPT_BS_CARRYCOST:elasticity of a European option")},
565 DEF_ARG_CALL_PUT_FLAG,
566 DEF_ARG_SPOT,
567 DEF_ARG_STRIKE,
568 DEF_ARG_TIME_MATURITY_Y,
569 DEF_ARG_RATE_RISKFREE,
570 DEF_ARG_VOLATILITY,
571 DEF_ARG_CC_OPT,
572 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_BS_CARRYCOST uses the Black-Scholes model to calculate "
573 "the 'elasticity' of a European option struck at @{strike} "
574 "on an asset with spot price @{spot}. The elasticity of an option "
575 "is the rate of change of its price "
576 "with respect to its @{cost_of_carry}.")},
577 /* xgettext:no-c-format */
578 { GNM_FUNC_HELP_NOTE, F_("Elasticity is expressed as the rate of change "
579 "of the option value, per 100% volatility.")},
580 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
581 { GNM_FUNC_HELP_END}
585 /* Currency Options - Garman and Kohlhagen */
586 static gnm_float
587 opt_garman_kohlhagen1 (OptionSide side,
588 gnm_float s, gnm_float x, gnm_float t,
589 gnm_float r, gnm_float rf, gnm_float v)
591 gnm_float d1 = (gnm_log (s / x) + (r - rf + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
592 gnm_float d2 = d1 - v * gnm_sqrt (t);
593 switch (side) {
594 case OS_Call:
595 return s * gnm_exp (-rf * t) * ncdf (d1) - x * gnm_exp (-r * t) * ncdf (d2);
596 case OS_Put:
597 return x * gnm_exp (-r * t) * ncdf (-d2) - s * gnm_exp (-rf * t) * ncdf (-d1);
598 default:
599 return gnm_nan; /*should never get to here*/
603 static GnmValue *
604 opt_garman_kohlhagen (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
606 OptionSide call_put = option_side (value_peek_string (argv[0]));
607 gnm_float s = value_get_as_float (argv[1]);
608 gnm_float x = value_get_as_float (argv[2]);
609 gnm_float t = value_get_as_float (argv[3]);
610 gnm_float r = value_get_as_float (argv[4]);
611 gnm_float rf = value_get_as_float (argv[5]);
612 gnm_float v = value_get_as_float (argv[6]);
613 gnm_float gfresult = opt_garman_kohlhagen1 (call_put, s, x, t, r, rf, v);
615 if (gnm_isnan (gfresult))
616 return value_new_error_NUM (ei->pos);
617 else
618 return value_new_float (gfresult);
621 static GnmFuncHelp const help_opt_garman_kohlhagen[] = {
622 { GNM_FUNC_HELP_NAME, F_("OPT_GARMAN_KOHLHAGEN:theoretical price of a European currency option")},
623 DEF_ARG_CALL_PUT_FLAG,
624 DEF_ARG_SPOT,
625 DEF_ARG_STRIKE,
626 { GNM_FUNC_HELP_ARG, F_("time:number of days to exercise")},
627 { GNM_FUNC_HELP_ARG, F_("domestic_rate:domestic risk-free interest rate to the exercise date in percent")},
628 { GNM_FUNC_HELP_ARG, F_("foreign_rate:foreign risk-free interest rate to the exercise date in percent")},
629 DEF_ARG_VOLATILITY,
630 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_GARMAN_KOHLHAGEN values the theoretical price of a European "
631 "currency option struck at @{strike} on an asset with spot price @{spot}.")},
632 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
633 { GNM_FUNC_HELP_END}
637 /* French (1984) adjusted Black and scholes model for trading day volatility */
638 static gnm_float
639 opt_french1 (OptionSide side, gnm_float s, gnm_float x, gnm_float tradingt, gnm_float calendart,
640 gnm_float r, gnm_float v, gnm_float b)
642 gnm_float d1 = (gnm_log (s / x) + b * calendart + ((v * v) / 2.0) * tradingt) / (v * gnm_sqrt (tradingt));
643 gnm_float d2 = d1 - v * gnm_sqrt (tradingt);
645 switch (side) {
646 case OS_Call:
647 return s * gnm_exp ((b - r) * calendart) * ncdf (d1) - x * gnm_exp (-r * calendart) * ncdf (d2);
648 case OS_Put:
649 return x * gnm_exp (-r * calendart) * ncdf (-d2) - s * gnm_exp ((b - r) * calendart) * ncdf (-d1);
650 default:
651 return gnm_nan;
656 static GnmValue *
657 opt_french (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
659 OptionSide call_put = option_side (value_peek_string (argv[0]));
660 gnm_float s = value_get_as_float (argv[1]);
661 gnm_float x = value_get_as_float (argv[2]);
662 gnm_float t = value_get_as_float (argv[3]);
663 gnm_float t1 = value_get_as_float (argv[4]);
664 gnm_float r = value_get_as_float (argv[5]);
665 gnm_float v = value_get_as_float (argv[6]);
666 gnm_float b = value_get_as_float (argv[7]);
667 gnm_float gfresult = opt_french1 (call_put, s, x, t, t1, r, v, b);
669 if (gnm_isnan (gfresult))
670 return value_new_error_NUM (ei->pos);
671 else
672 return value_new_float (gfresult);
675 static GnmFuncHelp const help_opt_french[] = {
676 { GNM_FUNC_HELP_NAME, F_("OPT_FRENCH:theoretical price of a European option adjusted for trading day volatility")},
677 DEF_ARG_CALL_PUT_FLAG,
678 DEF_ARG_SPOT,
679 DEF_ARG_STRIKE,
680 { GNM_FUNC_HELP_ARG, F_("time:ratio of the number of calendar days to exercise and the number of calendar days in the year")},
681 { GNM_FUNC_HELP_ARG, F_("ttime:ratio of the number of trading days to exercise and the number of trading days in the year")},
682 DEF_ARG_RATE_RISKFREE,
683 DEF_ARG_VOLATILITY,
684 DEF_ARG_CC_OPT,
685 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_FRENCH values the theoretical price of a "
686 "European option adjusted for trading day volatility, struck at "
687 "@{strike} on an asset with spot price @{spot}.")},
688 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
689 { GNM_FUNC_HELP_END}
692 /* Merton jump diffusion model*/
693 static gnm_float
694 opt_jump_diff1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v,
695 gnm_float lambda, gnm_float gamma)
697 gnm_float delta, sum;
698 gnm_float Z, vi;
699 int i;
701 delta = gnm_sqrt (gamma * (v * v) / lambda);
702 Z = gnm_sqrt ((v * v) - lambda * (delta * delta));
703 sum = 0.0;
704 for (i = 0; i != 11; ++i) {
705 vi = gnm_sqrt ((Z * Z) + (delta * delta) * (i / t));
706 sum = sum + gnm_exp (-lambda * t) * gnm_pow (lambda * t, i) / gnm_fact(i) *
707 opt_bs1 (side, s, x, t, r, vi, r);
709 return sum;
712 static GnmValue *
713 opt_jump_diff (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
715 OptionSide call_put = option_side (value_peek_string (argv[0]));
716 gnm_float s = value_get_as_float (argv[1]);
717 gnm_float x = value_get_as_float (argv[2]);
718 gnm_float t = value_get_as_float (argv[3]);
719 gnm_float r = value_get_as_float (argv[4]);
720 gnm_float v = value_get_as_float (argv[5]);
721 gnm_float lambda = value_get_as_float (argv[6]);
722 gnm_float gamma = value_get_as_float (argv[7]);
723 gnm_float gfresult =
724 opt_jump_diff1 (call_put, s, x, t, r, v, lambda, gamma);
725 return value_new_float (gfresult);
728 static GnmFuncHelp const help_opt_jump_diff[] = {
729 { GNM_FUNC_HELP_NAME, F_("OPT_JUMP_DIFF:theoretical price of an option according to the Jump Diffusion process")},
730 DEF_ARG_CALL_PUT_FLAG,
731 DEF_ARG_SPOT,
732 DEF_ARG_STRIKE,
733 DEF_ARG_TIME_MATURITY_Y,
734 { GNM_FUNC_HELP_ARG, F_("rate:the annualized rate of interest")},
735 DEF_ARG_VOLATILITY,
736 { GNM_FUNC_HELP_ARG, F_("lambda:expected number of 'jumps' per year")},
737 { GNM_FUNC_HELP_ARG, F_("gamma:proportion of volatility explained by the 'jumps'")},
738 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_JUMP_DIFF models the theoretical price of an option according "
739 "to the Jump Diffusion process (Merton).")},
740 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
741 { GNM_FUNC_HELP_END}
746 /* Miltersen schwartz (1997) commodity option model */
747 static gnm_float
748 opt_miltersen_schwartz1 (OptionSide side, gnm_float p_t, gnm_float f_t, gnm_float x, gnm_float t1,
749 gnm_float t2, gnm_float v_s, gnm_float v_e, gnm_float v_f, gnm_float rho_se,
750 gnm_float rho_sf, gnm_float rho_ef, gnm_float kappa_e, gnm_float kappa_f)
752 gnm_float vz, vxz;
753 gnm_float d1, d2;
755 vz = (v_s * v_s) * t1 + 2.0 * v_s * (v_f * rho_sf * 1.0/ kappa_f * (t1 - 1.0/ kappa_f * gnm_exp (-kappa_f * t2) * (gnm_exp (kappa_f * t1) - 1.0))
756 - v_e * rho_se * 1.0/ kappa_e * (t1 - 1.0/ kappa_e * gnm_exp (-kappa_e * t2) * (gnm_exp (kappa_e * t1) - 1.0)))
757 + (v_e * v_e) * 1.0/ (kappa_e * kappa_e) * (t1 + 1.0/ (2.0 * kappa_e) * gnm_exp (-2 * kappa_e * t2) * (gnm_exp (2.0 * kappa_e * t1) - 1.0)
758 - 2.0 * 1.0/ kappa_e * gnm_exp (-kappa_e * t2) * (gnm_exp (kappa_e * t1) - 1.0))
759 + (v_f * v_f) * 1.0/ (kappa_f * kappa_f) * (t1 + 1.0/ (2.0 * kappa_f) * gnm_exp (-2.0 * kappa_f * t2) * (gnm_exp (2.0 * kappa_f * t1) - 1.0)
760 - 2.0 * 1.0/ kappa_f * gnm_exp (-kappa_f * t2) * (gnm_exp (kappa_f * t1) - 1.0))
761 - 2.0 * v_e * v_f * rho_ef * 1.0/ kappa_e * 1.0/ kappa_f * (t1 - 1.0/ kappa_e * gnm_exp (-kappa_e * t2) * (gnm_exp (kappa_e * t1) - 1.0)
762 - 1.0/ kappa_f * gnm_exp (-kappa_f * t2) * (gnm_exp (kappa_f * t1) - 1.0)
763 + 1.0/ (kappa_e + kappa_f) * gnm_exp (-(kappa_e + kappa_f) * t2) * (gnm_exp ((kappa_e + kappa_f) * t1) - 1.0));
765 vxz = v_f * 1.0/ kappa_f * (v_s * rho_sf * (t1 - 1.0/ kappa_f * (1.0 - gnm_exp (-kappa_f * t1)))
766 + v_f * 1.0/ kappa_f * (t1 - 1.0/ kappa_f * gnm_exp (-kappa_f * t2) * (gnm_exp (kappa_f * t1) - 1.0) - 1.0/ kappa_f * (1 - gnm_exp (-kappa_f * t1))
767 + 1.0/ (2.0 * kappa_f) * gnm_exp (-kappa_f * t2) * (gnm_exp (kappa_f * t1) - gnm_exp (-kappa_f * t1)))
768 - v_e * rho_ef * 1.0/ kappa_e * (t1 - 1.0/ kappa_e * gnm_exp (-kappa_e * t2) * (gnm_exp (kappa_e * t1) - 1.0) - 1.0/ kappa_f * (1.0 - gnm_exp (-kappa_f * t1))
769 + 1.0/ (kappa_e + kappa_f) * gnm_exp (-kappa_e * t2) * (gnm_exp (kappa_e * t1) - gnm_exp (-kappa_f * t1))));
771 vz = gnm_sqrt (vz);
773 d1 = (gnm_log (f_t / x) - vxz + (vz * vz) / 2.0) / vz;
774 d2 = (gnm_log (f_t / x) - vxz - (vz * vz) / 2.0) / vz;
776 switch (side) {
777 case OS_Call:
778 return p_t * (f_t * gnm_exp (-vxz) * ncdf (d1) - x * ncdf (d2));
779 case OS_Put:
780 return p_t * (x * ncdf (-d2) - f_t * gnm_exp (-vxz) * ncdf (-d1));
781 default:
782 return gnm_nan;
786 static GnmValue *
787 opt_miltersen_schwartz (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
789 OptionSide call_put = option_side (value_peek_string (argv[0]));
790 gnm_float p_t = value_get_as_float (argv[1]);
791 gnm_float f_t = value_get_as_float (argv[2]);
792 gnm_float x = value_get_as_float (argv[3]);
793 gnm_float t1 = value_get_as_float (argv[4]);
794 gnm_float t2 = value_get_as_float (argv[5]);
795 gnm_float v_s = value_get_as_float (argv[6]);
796 gnm_float v_e = value_get_as_float (argv[7]);
797 gnm_float v_f = value_get_as_float (argv[8]);
798 gnm_float rho_se = value_get_as_float (argv[9]);
799 gnm_float rho_sf = value_get_as_float (argv[10]);
800 gnm_float rho_ef = value_get_as_float (argv[11]);
801 gnm_float kappa_e = value_get_as_float (argv[12]);
802 gnm_float kappa_f = value_get_as_float (argv[13]);
804 gnm_float gfresult =
805 opt_miltersen_schwartz1 (call_put, p_t, f_t, x, t1, t2,
806 v_s, v_e, v_f,
807 rho_se, rho_sf, rho_ef, kappa_e, kappa_f);
809 if (gnm_isnan (gfresult))
810 return value_new_error_NUM (ei->pos);
811 return value_new_float (gfresult);
815 static GnmFuncHelp const help_opt_miltersen_schwartz[] = {
816 { GNM_FUNC_HELP_NAME, F_("OPT_MILTERSEN_SCHWARTZ:theoretical price of options on commodities futures according to Miltersen & Schwartz")},
817 DEF_ARG_CALL_PUT_FLAG,
818 { GNM_FUNC_HELP_ARG, F_("p_t:zero coupon bond with expiry at option maturity")},
819 { GNM_FUNC_HELP_ARG, F_("f_t:futures price")},
820 DEF_ARG_STRIKE,
821 { GNM_FUNC_HELP_ARG, F_("t1:time to maturity of the option")},
822 { GNM_FUNC_HELP_ARG, F_("t2:time to maturity of the underlying commodity futures contract")},
823 { GNM_FUNC_HELP_ARG, F_("v_s:volatility of the spot commodity price")},
824 { GNM_FUNC_HELP_ARG, F_("v_e:volatility of the future convenience yield")},
825 { GNM_FUNC_HELP_ARG, F_("v_f:volatility of the forward rate of interest")},
826 { GNM_FUNC_HELP_ARG, F_("rho_se:correlation between the spot commodity price and the convenience yield")},
827 { GNM_FUNC_HELP_ARG, F_("rho_sf:correlation between the spot commodity price and the forward interest rate")},
828 { GNM_FUNC_HELP_ARG, F_("rho_ef:correlation between the forward interest rate and the convenience yield")},
829 { GNM_FUNC_HELP_ARG, F_("kappa_e:speed of mean reversion of the convenience yield")},
830 { GNM_FUNC_HELP_ARG, F_("kappa_f:speed of mean reversion of the forward interest rate")},
831 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
832 { GNM_FUNC_HELP_END}
835 /* American options */
838 /* American Calls on stocks with known dividends, Roll-Geske-Whaley */
839 static gnm_float opt_rgw1 (gnm_float s, gnm_float x, gnm_float t1, gnm_float t2, gnm_float r, gnm_float d, gnm_float v)
840 /*t1 time to dividend payout
841 t2 time to option expiration */
843 gnm_float sx, i;
844 gnm_float a1, a2, b1, b2;
845 gnm_float HighS, LowS, epsilon;
846 gnm_float ci, infinity;
847 gnm_float gfresult;
849 if (!(s > 0))
850 return gnm_nan;
852 infinity = 100000000;
853 epsilon = 0.00001;
854 sx = s - d * gnm_exp (-r * t1);
855 if (d <= (x * (1.0 - gnm_exp (-r * (t2 - t1)))))
856 /* Not optimal to exercise */
857 return opt_bs1 (OS_Call, sx, x, t2, r, v,0.0);
859 ci = opt_bs1 (OS_Call, s, x, t2 - t1, r, v,0.0);
860 HighS = s;
861 while ((ci - HighS - d + x) > 0.0 && HighS < infinity) {
863 HighS *= 2.0;
864 ci = opt_bs1 (OS_Call, HighS, x, t2 - t1, r, v,0.0);
866 if (HighS > infinity)
867 return opt_bs1 (OS_Call, sx, x, t2, r, v,0.0);
869 LowS = 0.0;
870 i = HighS * 0.5;
871 ci = opt_bs1 (OS_Call, i, x, t2 - t1, r, v, 0.0);
873 /* search algorithm to find the critical stock price i */
874 while (gnm_abs (ci - i - d + x) > epsilon && HighS - LowS > epsilon) {
875 if ((ci - i - d + x) < 0)
876 HighS = i;
877 else
878 LowS = i;
879 i = (HighS + LowS) / 2.0;
880 ci = opt_bs1 (OS_Call, i, x, (t2 - t1), r, v, 0.0);
883 a1 = (gnm_log (sx / x) + (r + (v * v) / 2.0) * t2) / (v * gnm_sqrt (t2));
884 a2 = a1 - v * gnm_sqrt (t2);
885 b1 = (gnm_log (sx / i) + (r + (v * v) / 2.0) * t1) / (v * gnm_sqrt (t1));
886 b2 = b1 - v * gnm_sqrt (t1);
888 gfresult = sx * ncdf (b1) + sx * cum_biv_norm_dist1 (a1, -b1, -gnm_sqrt (t1 / t2))
889 - x * gnm_exp (-r * t2) * cum_biv_norm_dist1 (a2, -b2, -gnm_sqrt (t1 / t2)) - (x - d)
890 * gnm_exp (-r * t1) * ncdf (b2);
891 return gfresult;
895 static GnmValue *
896 opt_rgw(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
898 gnm_float s = value_get_as_float (argv[0]);
899 gnm_float x = value_get_as_float (argv[1]);
900 gnm_float t1 = value_get_as_float (argv[2]);
901 gnm_float t2 = value_get_as_float (argv[3]);
902 gnm_float r = value_get_as_float (argv[4]);
903 gnm_float d = value_get_as_float (argv[5]);
904 gnm_float v = value_get_as_float (argv[6]);
905 gnm_float gfresult = 0.0;
907 gfresult = opt_rgw1 (s, x, t1, t2, r, d, v);
909 return value_new_float (gfresult);
912 static GnmFuncHelp const help_opt_rgw[] = {
913 { GNM_FUNC_HELP_NAME, F_("OPT_RGW:theoretical price of an American option according to the Roll-Geske-Whaley approximation")},
914 DEF_ARG_SPOT,
915 DEF_ARG_STRIKE,
916 DEF_ARG_TIME_DIVIDEND,
917 DEF_ARG_TIME_EXPIRATION,
918 DEF_ARG_RATE_ANNUALIZED,
919 DEF_ARG_AMOUNT,
920 DEF_ARG_VOLATILITY,
921 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
922 { GNM_FUNC_HELP_END}
925 /* the Barone-Adesi and Whaley (1987) American approximation */
926 static GnmValue *
927 opt_baw_amer (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
929 OptionSide call_put = option_side (value_peek_string (argv[0]));
930 gnm_float s = value_get_as_float (argv[1]);
931 gnm_float x = value_get_as_float (argv[2]);
932 gnm_float t = value_get_as_float (argv[3]);
933 gnm_float r = value_get_as_float (argv[4]);
934 gnm_float v = value_get_as_float (argv[5]);
935 gnm_float b = value_get_as_float (argv[6]);
936 gnm_float gfresult;
938 switch (call_put) {
939 case OS_Call:
940 gfresult = opt_baw_call (s, x, t, r, v, b);
941 break;
942 case OS_Put:
943 gfresult = opt_baw_put (s, x, t, r, v, b);
944 break;
945 default:
946 return value_new_error_NUM (ei->pos);
949 if (gnm_isnan (gfresult))
950 return value_new_error_NUM (ei->pos);
952 return value_new_float (gfresult);
955 static GnmFuncHelp const help_opt_baw_amer[] = {
956 { GNM_FUNC_HELP_NAME, F_("OPT_BAW_AMER:theoretical price of an option according to the Barone Adesie & Whaley approximation")},
957 DEF_ARG_CALL_PUT_FLAG,
958 DEF_ARG_SPOT,
959 DEF_ARG_STRIKE,
960 DEF_ARG_TIME_MATURITY_D,
961 DEF_ARG_RATE_RISKFREE_ANN,
962 DEF_ARG_CC,
963 DEF_ARG_VOLATILITY_SHORT,
964 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
965 { GNM_FUNC_HELP_END}
968 /* American call */
969 static gnm_float
970 opt_baw_call (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
972 gnm_float sk, n, k;
973 gnm_float d1, q2, a2;
974 gnm_float gfresult;
975 if (b >= r)
976 gfresult = opt_bs1 (OS_Call, s, x, t, r, v, b);
977 else
979 sk = NRA_c (x, t, r, v, b);
980 n = 2 * b / (v * v);
981 k = 2 * r / ((v * v) * (1.0 - gnm_exp (-r * t)));
982 d1 = (gnm_log (sk / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
983 q2 = (-(n - 1.0) + gnm_sqrt ((n - 1.0) * (n - 1.0) + 4.0 * k)) / 2.0;
984 a2 = (sk / q2) * (1.0 - gnm_exp ((b - r) * t) * ncdf (d1));
985 if (s < sk)
986 gfresult = opt_bs1 (OS_Call, s, x, t, r, v, b) + a2 * gnm_pow (s / sk, q2);
987 else
988 gfresult = s - x;
990 } /*end if statement*/
991 return gfresult;
998 /* Newton Raphson algorithm to solve for the critical commodity price for a Call */
999 static gnm_float
1000 NRA_c (gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
1002 gnm_float n, m;
1003 gnm_float su, si;
1004 gnm_float h2, k;
1005 gnm_float d1, q2, q2u;
1006 gnm_float LHS, RHS;
1007 gnm_float bi, e;
1009 /* Calculation of seed value, si */
1010 n = 2 * b / (v * v);
1011 m = 2 * r / (v * v);
1012 q2u = (-(n - 1.0) + gnm_sqrt (((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0;
1013 su = x / (1.0 - 1.0/ q2u);
1014 h2 = -(b * t + 2.0 * v * gnm_sqrt (t)) * x / (su - x);
1015 si = x + (su - x) * (1.0 - gnm_exp (h2));
1017 k = 2 * r / ((v * v) * (1.0 - gnm_exp (-r * t)));
1018 d1 = (gnm_log (si / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1019 q2 = (-(n - 1.0) + gnm_sqrt (((n - 1.0) * (n - 1.0)) + 4.0 * k)) / 2.0;
1020 LHS = si - x;
1021 RHS = opt_bs1 (OS_Call, si, x, t, r, v, b) + (1.0 - gnm_exp ((b - r) * t) * ncdf (d1)) * si / q2;
1022 bi = gnm_exp ((b - r) * t) * ncdf (d1) * (1.0 - 1.0/ q2)
1023 + (1.0 - gnm_exp ((b - r) * t) * ncdf (d1) / (v * gnm_sqrt (t))) / q2;
1024 e = 0.000001;
1026 /* Newton Raphson algorithm for finding critical price si */
1027 while ((gnm_abs (LHS - RHS) / x) > e)
1029 si = (x + RHS - bi * si) / (1.0 - bi);
1030 d1 = (gnm_log (si / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1031 LHS = si - x;
1032 RHS = opt_bs1 (OS_Call, si, x, t, r, v, b) + (1.0 - gnm_exp ((b - r) * t) * ncdf (d1)) * si / q2;
1033 bi = gnm_exp ((b - r) * t) * ncdf (d1) * (1.0 - 1.0/ q2)
1034 + (1.0 - gnm_exp ((b - r) * t) * npdf (d1) / (v * gnm_sqrt (t))) / q2;
1036 return si;
1039 static gnm_float
1040 opt_baw_put (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
1042 gnm_float sk = NRA_p (x, t, r, v, b);
1043 gnm_float n = 2 * b / (v * v);
1044 gnm_float k = 2 * r / ((v * v) * (1.0 - gnm_exp (-r * t)));
1045 gnm_float d1 = (gnm_log (sk / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1046 gnm_float q1 = (-(n - 1.0) - gnm_sqrt (((n - 1.0) * (n - 1.0)) + 4.0 * k)) / 2.0;
1047 gnm_float a1 = -(sk / q1) * (1.0 - gnm_exp ((b - r) * t) * ncdf (-d1));
1049 if (s > sk)
1050 return opt_bs1 (OS_Put, s, x, t, r, v, b) + a1 * gnm_pow (s/ sk, q1);
1051 else
1052 return x - s;
1055 /* Newton Raphson algorithm to solve for the critical commodity price for a Put*/
1056 static gnm_float
1057 NRA_p (gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
1060 gnm_float n, m;
1061 gnm_float su, si;
1062 gnm_float h1, k;
1063 gnm_float d1, q1u, q1;
1064 gnm_float LHS, RHS;
1065 gnm_float bi, e;
1067 /* Calculation of seed value, si */
1068 n = 2 * b / (v * v);
1069 m = 2 * r / (v * v);
1070 q1u = (-(n - 1.0) - gnm_sqrt (((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0;
1071 su = x / (1.0 - 1.0/ q1u);
1072 h1 = (b * t - 2.0 * v * gnm_sqrt (t)) * x / (x - su);
1073 si = su + (x - su) * gnm_exp (h1);
1075 k = 2 * r / ((v * v) * (1.0 - gnm_exp (-r * t)));
1076 d1 = (gnm_log (si / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1077 q1 = (-(n - 1.0) - gnm_sqrt (((n - 1.0) * (n - 1.0)) + 4.0 * k)) / 2.0;
1078 LHS = x - si;
1079 RHS = opt_bs1 (OS_Put, si, x, t, r, v, b) - (1.0 - gnm_exp ((b - r) * t) * ncdf (-d1)) * si / q1;
1080 bi = -gnm_exp ((b - r) * t) * ncdf (-d1) * (1.0 - 1.0/ q1)
1081 - (1.0 + gnm_exp ((b - r) * t) * npdf (-d1) / (v * gnm_sqrt (t))) / q1;
1082 e = 0.000001;
1084 /* Newton Raphson algorithm for finding critical price si */
1085 while(gnm_abs (LHS - RHS) / x > e) {
1086 si = (x - RHS + bi * si) / (1.0 + bi);
1087 d1 = (gnm_log (si / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1088 LHS = x - si;
1089 RHS = opt_bs1 (OS_Put, si, x, t, r, v, b) - (1.0 - gnm_exp ((b - r) * t) * ncdf (-d1)) * si / q1;
1090 bi = -gnm_exp ((b - r) * t) * ncdf (-d1) * (1.0 - 1.0/ q1)
1091 - (1.0 + gnm_exp ((b - r) * t) * ncdf (-d1) / (v * gnm_sqrt (t))) / q1;
1093 return si;
1096 /* the Bjerksund and stensland (1993) American approximation */
1097 static gnm_float
1098 opt_bjer_stens1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
1100 switch (side) {
1101 case OS_Call:
1102 return opt_bjer_stens1_c (s, x, t, r, v, b);
1103 case OS_Put:
1104 /* Use the Bjerksund and stensland put-call transformation */
1105 return opt_bjer_stens1_c (x, s, t, r - b, v, -b);
1106 default:
1107 return gnm_nan;
1111 static GnmValue *
1112 opt_bjer_stens (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1114 OptionSide call_put = option_side (value_peek_string (argv[0]));
1115 gnm_float s = value_get_as_float (argv[1]);
1116 gnm_float x = value_get_as_float (argv[2]);
1117 gnm_float t = value_get_as_float (argv[3]);
1118 gnm_float r = value_get_as_float (argv[4]);
1119 gnm_float v = value_get_as_float (argv[5]);
1120 gnm_float b = argv[6] ? value_get_as_float (argv[6]):0;
1121 gnm_float gfresult =
1122 opt_bjer_stens1 (call_put, s, x, t, r, v, b);
1123 return value_new_float (gfresult);
1127 static GnmFuncHelp const help_opt_bjer_stens[] = {
1128 { GNM_FUNC_HELP_NAME, F_("OPT_BJER_STENS:theoretical price of American options according to the Bjerksund & Stensland approximation technique")},
1129 DEF_ARG_CALL_PUT_FLAG,
1130 DEF_ARG_SPOT,
1131 DEF_ARG_STRIKE,
1132 DEF_ARG_TIME_MATURITY_D,
1133 DEF_ARG_RATE_RISKFREE_ANN,
1134 DEF_ARG_VOLATILITY_SHORT,
1135 DEF_ARG_CC_OPT,
1136 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1137 { GNM_FUNC_HELP_END}
1140 static gnm_float
1141 opt_bjer_stens1_c (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b)
1143 if (b >= r) /* Never optimal to exersice before maturity */
1144 return opt_bs1 (OS_Call, s, x, t, r, v, b);
1145 else {
1146 gnm_float Beta =
1147 (1.0/ 2.0 - b / (v * v)) +
1148 gnm_sqrt (gnm_pow (b / (v * v) - 1.0/ 2.0, 2) + 2 * r / (v * v));
1149 gnm_float BInfinity = Beta / (Beta - 1.0) * x;
1150 gnm_float B0 = MAX (x, r / (r - b) * x);
1151 gnm_float ht = -(b * t + 2.0 * v * gnm_sqrt (t)) * B0 / (BInfinity - B0);
1152 gnm_float I = B0 + (BInfinity - B0) * (1.0 - gnm_exp (ht));
1153 if (s >= I)
1154 return s - x;
1155 else {
1156 gnm_float alpha = (I - x) * gnm_pow (I ,-Beta);
1157 return alpha * gnm_pow (s ,Beta) -
1158 alpha * phi (s, t, Beta, I, I, r, v, b) +
1159 phi (s, t, 1.0, I, I, r, v, b) -
1160 phi (s, t, 1.0, x, I, r, v, b) -
1161 x * phi (s, t, 0.0, I, I, r, v, b) +
1162 x * phi (s, t, 0.0, x, I, r, v, b);
1167 static gnm_float
1168 phi (gnm_float s, gnm_float t, gnm_float gamma, gnm_float H, gnm_float I, gnm_float r, gnm_float v, gnm_float b)
1170 gnm_float lambda, kappa;
1171 gnm_float d;
1172 gnm_float gfresult;
1174 lambda = (-r + gamma * b + 0.5 * gamma * (gamma - 1.0) * (v * v)) * t;
1175 d = -(gnm_log (s / H) + (b + (gamma - 0.5) * (v * v)) * t) / (v * gnm_sqrt (t));
1176 kappa = 2 * b / (v * v) + (2.0 * gamma - 1.0);
1177 gfresult = gnm_exp (lambda) * gnm_pow (s, gamma) * (ncdf (d) - gnm_pow (I / s, kappa) * ncdf (d - 2.0 * gnm_log (I / s) / (v * gnm_sqrt (t))));
1179 return gfresult;
1183 /* Executive stock options */
1184 static GnmValue *
1185 opt_exec (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1187 OptionSide call_put = option_side (value_peek_string (argv[0]));
1188 gnm_float s = value_get_as_float (argv[1]);
1189 gnm_float x = value_get_as_float (argv[2]);
1190 gnm_float t = value_get_as_float (argv[3]);
1191 gnm_float r = value_get_as_float (argv[4]);
1192 gnm_float v = value_get_as_float (argv[5]);
1193 gnm_float b = value_get_as_float (argv[6]);
1194 gnm_float lambda = value_get_as_float (argv[7]);
1195 gnm_float gfresult =
1196 gnm_exp (-lambda * t) * opt_bs1 (call_put, s, x, t, r, v, b);
1197 return value_new_float (gfresult);
1201 static GnmFuncHelp const help_opt_exec[] = {
1202 { GNM_FUNC_HELP_NAME, F_("OPT_EXEC:theoretical price of executive stock options")},
1203 DEF_ARG_CALL_PUT_FLAG,
1204 DEF_ARG_SPOT,
1205 DEF_ARG_STRIKE,
1206 DEF_ARG_TIME_MATURITY_D,
1207 DEF_ARG_RATE_RISKFREE_ANN,
1208 DEF_ARG_VOLATILITY_SHORT,
1209 DEF_ARG_CC,
1210 { GNM_FUNC_HELP_ARG, F_("lambda:jump rate for executives")},
1211 { GNM_FUNC_HELP_NOTE, F_("The model assumes executives forfeit their options if they leave the company.")},
1212 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1213 { GNM_FUNC_HELP_END}
1220 /* Forward start options */
1221 static GnmValue *
1222 opt_forward_start(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1224 OptionSide call_put = option_side (value_peek_string (argv[0]));
1225 gnm_float s = value_get_as_float (argv[1]);
1226 gnm_float alpha = value_get_as_float (argv[2]);
1227 gnm_float t1 = value_get_as_float (argv[3]);
1228 gnm_float t = value_get_as_float (argv[4]);
1229 gnm_float r = value_get_as_float (argv[5]);
1230 gnm_float v = value_get_as_float (argv[6]);
1231 gnm_float b = value_get_as_float (argv[7]);
1232 gnm_float gfresult =
1233 s * gnm_exp ((b - r) * t1) * opt_bs1 (call_put, 1, alpha, t - t1, r, v, b);
1234 return value_new_float (gfresult);
1239 static GnmFuncHelp const help_opt_forward_start[] = {
1240 { GNM_FUNC_HELP_NAME, F_("OPT_FORWARD_START:theoretical price of forward start options")},
1241 DEF_ARG_CALL_PUT_FLAG,
1242 DEF_ARG_SPOT,
1243 { GNM_FUNC_HELP_ARG, F_("alpha:fraction setting the strike price at the future date @{time_start}")},
1244 { GNM_FUNC_HELP_ARG, F_("time_start:time until the option starts in days")},
1245 DEF_ARG_TIME_MATURITY_D,
1246 DEF_ARG_RATE_RISKFREE_ANN,
1247 DEF_ARG_VOLATILITY_SHORT,
1248 DEF_ARG_CC,
1249 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1250 { GNM_FUNC_HELP_END}
1254 /* time switch options (discrete) */
1255 static GnmValue *
1256 opt_time_switch (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1258 OptionSide call_put = option_side (value_peek_string (argv[0]));
1259 gnm_float s = value_get_as_float (argv[1]);
1260 gnm_float x = value_get_as_float (argv[2]);
1261 gnm_float a = value_get_as_float (argv[3]);
1262 gnm_float t = value_get_as_float (argv[4]);
1263 gnm_float m = value_get_as_float (argv[5]);
1264 gnm_float dt = value_get_as_float (argv[6]);
1265 gnm_float r = value_get_as_float (argv[7]);
1266 gnm_float b = value_get_as_float (argv[8]);
1267 gnm_float v = value_get_as_float (argv[9]);
1269 gnm_float gfresult;
1270 gnm_float sum, d;
1271 int i, n, Z = 0;
1273 switch (call_put) {
1274 case OS_Call: Z = +1; break;
1275 case OS_Put: Z = -1; break;
1276 default: return value_new_error_NUM (ei->pos);
1279 sum = 0.0;
1280 n = t / dt;
1281 for (i = 1; i < n; ++i) {
1282 d = (gnm_log (s / x) + (b - (v * v) / 2.0) * i * dt) / (v * gnm_sqrt (i * dt));
1283 sum = sum + ncdf (Z * d) * dt;
1286 gfresult = a * gnm_exp (-r * t) * sum + dt * a * gnm_exp (-r * t) * m;
1287 return value_new_float (gfresult);
1291 static GnmFuncHelp const help_opt_time_switch[] = {
1292 { GNM_FUNC_HELP_NAME, F_("OPT_TIME_SWITCH:theoretical price of time switch options")},
1293 DEF_ARG_CALL_PUT_FLAG,
1294 DEF_ARG_SPOT,
1295 DEF_ARG_STRIKE,
1296 { GNM_FUNC_HELP_ARG, F_("a:amount received for each time period")},
1297 DEF_ARG_TIME_MATURITY_Y,
1298 { GNM_FUNC_HELP_ARG, F_("m:number of time units the option has already met the condition")},
1299 { GNM_FUNC_HELP_ARG, F_("dt:agreed upon discrete time period expressed as "
1300 "a fraction of a year")},
1301 DEF_ARG_RATE_RISKFREE_ANN,
1302 DEF_ARG_CC,
1303 DEF_ARG_VOLATILITY_SHORT,
1304 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_TIME_SWITCH models the theoretical price of time switch options. (Pechtl 1995). "
1305 "The holder receives @{a} * @{dt} for each period that the asset price was "
1306 "greater than @{strike} (for a call) or below it (for a put).")},
1307 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1308 { GNM_FUNC_HELP_END}
1312 /* simple chooser options */
1313 static GnmValue *
1314 opt_simple_chooser (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1316 gnm_float s = value_get_as_float (argv[0]);
1317 gnm_float x = value_get_as_float (argv[1]);
1318 gnm_float t1 = value_get_as_float (argv[2]);
1319 gnm_float t2 = value_get_as_float (argv[3]);
1320 gnm_float r = value_get_as_float (argv[4]);
1321 gnm_float b = value_get_as_float (argv[5]);
1322 gnm_float v = value_get_as_float (argv[6]);
1324 gnm_float d = (gnm_log (s / x) + (b + (v * v) / 2.0) * t2) / (v * gnm_sqrt (t2));
1325 gnm_float y = (gnm_log (s / x) + b * t2 + (v * v) * t1 / 2.0) / (v * gnm_sqrt (t1));
1326 gnm_float gfresult =
1327 s * gnm_exp ((b - r) * t2) * ncdf ( d) - x * gnm_exp (-r * t2) * ncdf ( d - v * gnm_sqrt (t2)) -
1328 s * gnm_exp ((b - r) * t2) * ncdf (-y) + x * gnm_exp (-r * t2) * ncdf (-y + v * gnm_sqrt (t1));
1330 return value_new_float (gfresult);
1333 static GnmFuncHelp const help_opt_simple_chooser[] = {
1334 { GNM_FUNC_HELP_NAME, F_("OPT_SIMPLE_CHOOSER:theoretical price of a simple chooser option")},
1335 DEF_ARG_CALL_PUT_FLAG,
1336 DEF_ARG_SPOT,
1337 DEF_ARG_STRIKE,
1338 { GNM_FUNC_HELP_ARG, F_("time1:time in years until the holder chooses a put or a call option")},
1339 { GNM_FUNC_HELP_ARG, F_("time2:time in years until the chosen option expires")},
1340 DEF_ARG_CC,
1341 DEF_ARG_VOLATILITY_SHORT,
1342 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1343 { GNM_FUNC_HELP_END}
1347 /* Complex chooser options */
1348 static GnmValue *
1349 opt_complex_chooser(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1351 gnm_float s = value_get_as_float (argv[0]);
1352 gnm_float xc = value_get_as_float (argv[1]);
1353 gnm_float xp = value_get_as_float (argv[2]);
1354 gnm_float t = value_get_as_float (argv[3]);
1355 gnm_float tc = value_get_as_float (argv[4]);
1356 gnm_float tp = value_get_as_float (argv[5]);
1357 gnm_float r = value_get_as_float (argv[6]);
1358 gnm_float b = value_get_as_float (argv[7]);
1359 gnm_float v = value_get_as_float (argv[8]);
1361 gnm_float gfresult;
1363 gnm_float d1, d2, y1, y2;
1364 gnm_float rho1, rho2, I;
1366 I = opt_crit_val_chooser (s, xc, xp, t, tc, tp, r, b, v);
1367 d1 = (gnm_log (s / I) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1368 d2 = d1 - v * gnm_sqrt (t);
1369 y1 = (gnm_log (s / xc) + (b + (v * v) / 2.0) * tc) / (v * gnm_sqrt (tc));
1370 y2 = (gnm_log (s / xp) + (b + (v * v) / 2.0) * tp) / (v * gnm_sqrt (tp));
1371 rho1 = gnm_sqrt (t / tc);
1372 rho2 = gnm_sqrt (t / tp);
1374 gfresult = s * gnm_exp ((b - r) * tc) * cum_biv_norm_dist1 (d1, y1, rho1) - xc * gnm_exp (-r * tc)
1375 * cum_biv_norm_dist1 (d2, y1 - v * gnm_sqrt (tc), rho1) - s * gnm_exp ((b - r) * tp)
1376 * cum_biv_norm_dist1 (-d1, -y2, rho2) + xp * gnm_exp (-r * tp) * cum_biv_norm_dist1 (-d2, -y2 + v * gnm_sqrt (tp), rho2);
1378 return value_new_float (gfresult);
1382 static GnmFuncHelp const help_opt_complex_chooser[] = {
1383 { GNM_FUNC_HELP_NAME, F_("OPT_COMPLEX_CHOOSER:theoretical price of a complex chooser option")},
1384 DEF_ARG_SPOT,
1385 { GNM_FUNC_HELP_ARG, F_("strike_call:strike price, if exercised as a call option")},
1386 { GNM_FUNC_HELP_ARG, F_("strike_put:strike price, if exercised as a put option")},
1387 { GNM_FUNC_HELP_ARG, F_("time:time in years until the holder chooses a put or a call option")},
1388 { GNM_FUNC_HELP_ARG, F_("time_call:time in years to maturity of the call option if chosen")},
1389 { GNM_FUNC_HELP_ARG, F_("time_put:time in years to maturity of the put option if chosen")},
1390 DEF_ARG_RATE_RISKFREE_ANN,
1391 DEF_ARG_CC,
1392 DEF_ARG_VOLATILITY,
1393 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1394 { GNM_FUNC_HELP_END}
1400 /* Critical value complex chooser option */
1401 static gnm_float
1402 opt_crit_val_chooser (gnm_float s,gnm_float xc,gnm_float xp,gnm_float t,
1403 gnm_float tc, gnm_float tp, gnm_float r, gnm_float b, gnm_float v)
1405 gnm_float sv, ci, Pi, epsilon;
1406 gnm_float dc, dp, yi, di;
1408 sv = s;
1409 ci = opt_bs1 (OS_Call, sv, xc, tc - t, r, v, b);
1410 Pi = opt_bs1 (OS_Put, sv, xp, tp - t, r, v, b);
1411 dc = opt_bs_delta1 (OS_Call, sv, xc, tc - t, r, v, b);
1412 dp = opt_bs_delta1 (OS_Put, sv, xp, tp - t, r, v, b);
1413 yi = ci - Pi;
1414 di = dc - dp;
1415 epsilon = 0.001;
1416 /* Newton-Raphson */
1417 while (gnm_abs (yi) > epsilon)
1419 sv = sv - (yi) / di;
1420 ci = opt_bs1 (OS_Call, sv, xc, tc - t, r, v, b);
1421 Pi = opt_bs1 (OS_Put, sv, xp, tp - t, r, v, b);
1422 dc = opt_bs_delta1 (OS_Call, sv, xc, tc - t, r, v, b);
1423 dp = opt_bs_delta1 (OS_Put, sv, xp, tp - t, r, v, b);
1424 yi = ci - Pi;
1425 di = dc - dp;
1428 return sv;
1432 /* Options on options */
1433 static GnmValue *
1434 opt_on_options (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1436 char const *type_flag = value_peek_string (argv[0]);
1437 gnm_float s = value_get_as_float (argv[1]);
1438 gnm_float x1 = value_get_as_float (argv[2]);
1439 gnm_float x2 = value_get_as_float (argv[3]);
1440 gnm_float t1 = value_get_as_float (argv[4]);
1441 gnm_float t2 = value_get_as_float (argv[5]);
1442 gnm_float r = value_get_as_float (argv[6]);
1443 gnm_float b = value_get_as_float (argv[7]);
1444 gnm_float v = value_get_as_float (argv[8]);
1446 gnm_float gfresult;
1448 gnm_float y1, y2, z1, z2;
1449 gnm_float I, rho;
1450 OptionSide call_put;
1452 if (!strcmp (type_flag, "cc") || !strcmp (type_flag, "pc"))
1453 call_put = OS_Call;
1454 else
1455 call_put = OS_Put;
1457 I = CriticalValueOptionsOnOptions (call_put, x1, x2, t2 - t1, r, b, v);
1459 rho = gnm_sqrt (t1 / t2);
1460 y1 = (gnm_log (s / I) + (b + (v * v) / 2.0) * t1) / (v * gnm_sqrt (t1));
1461 y2 = y1 - v * gnm_sqrt (t1);
1462 z1 = (gnm_log (s / x1) + (b + (v * v) / 2.0) * t2) / (v * gnm_sqrt (t2));
1463 z2 = z1 - v * gnm_sqrt (t2);
1465 if (!strcmp (type_flag, "cc"))
1466 gfresult = s * gnm_exp ((b - r) * t2) * cum_biv_norm_dist1 (z1, y1, rho) -
1467 x1 * gnm_exp (-r * t2) * cum_biv_norm_dist1 (z2, y2, rho) - x2 * gnm_exp (-r * t1) * ncdf (y2);
1468 else if (!strcmp (type_flag, "pc"))
1469 gfresult = x1 * gnm_exp (-r * t2) * cum_biv_norm_dist1 (z2, -y2, -rho) -
1470 s * gnm_exp ((b - r) * t2) * cum_biv_norm_dist1 (z1, -y1, -rho) + x2 * gnm_exp (-r * t1) * ncdf (-y2);
1471 else if (!strcmp (type_flag, "cp"))
1472 gfresult = x1 * gnm_exp (-r * t2) * cum_biv_norm_dist1 (-z2, -y2, rho) -
1473 s * gnm_exp ((b - r) * t2) * cum_biv_norm_dist1 (-z1, -y1, rho) - x2 * gnm_exp (-r * t1) * ncdf (-y2);
1474 else if (!strcmp (type_flag, "pp"))
1475 gfresult = s * gnm_exp ((b - r) * t2) * cum_biv_norm_dist1 (-z1, y1, -rho) -
1476 x1 * gnm_exp (-r * t2) * cum_biv_norm_dist1 (-z2, y2, -rho) + gnm_exp (-r * t1) * x2 * ncdf (y2);
1477 else
1478 return value_new_error_VALUE (ei->pos);
1480 return value_new_float (gfresult);
1483 static GnmFuncHelp const help_opt_on_options[] = {
1484 { GNM_FUNC_HELP_NAME, F_("OPT_ON_OPTIONS:theoretical price of options on options")},
1485 { GNM_FUNC_HELP_ARG, F_("type_flag:'cc' for calls on calls, 'cp' for calls on puts, and so on for 'pc', and 'pp'")},
1486 DEF_ARG_SPOT,
1487 { GNM_FUNC_HELP_ARG, F_("strike1:strike price at which the option being valued is struck")},
1488 { GNM_FUNC_HELP_ARG, F_("strike2:strike price at which the underlying option is struck")},
1489 { GNM_FUNC_HELP_ARG, F_("time1:time in years to maturity of the option")},
1490 { GNM_FUNC_HELP_ARG, F_("time2:time in years to the maturity of the underlying option")},
1491 DEF_ARG_RATE_RISKFREE_ANN,
1492 { GNM_FUNC_HELP_ARG, F_("cost_of_carry:net cost of holding the underlying asset of the underlying option")},
1493 { GNM_FUNC_HELP_ARG, F_("volatility:annualized volatility in price of the underlying asset of the underlying option")},
1494 { GNM_FUNC_HELP_NOTE, F_("For common stocks, @{cost_of_carry} is the risk free rate less the dividend yield.")},
1495 { GNM_FUNC_HELP_NOTE, F_("@{time2} \xe2\x89\xa5 @{time1}")},
1496 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1497 { GNM_FUNC_HELP_END}
1501 /* Calculation of critical price options on options */
1502 static gnm_float
1503 CriticalValueOptionsOnOptions (OptionSide side, gnm_float x1, gnm_float x2, gnm_float t,
1504 gnm_float r, gnm_float b, gnm_float v)
1506 gnm_float si, ci, di, epsilon;
1508 si = x1;
1509 ci = opt_bs1 (side, si, x1, t, r, v, b);
1510 di = opt_bs_delta1 (side, si, x1, t, r, v, b);
1512 /* Newton-Raphson algorithm */
1513 epsilon = 0.0001;
1514 while (gnm_abs (ci - x2) > epsilon) {
1515 si = si - (ci - x2) / di;
1516 ci = opt_bs1 (side, si, x1, t, r, v, b);
1517 di = opt_bs_delta1 (side, si, x1, t, r, v, b);
1519 return si;
1522 /* Writer extendible options */
1523 static GnmValue *
1524 opt_extendible_writer (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1526 OptionSide call_put = option_side (value_peek_string (argv[0]));
1527 gnm_float s = value_get_as_float (argv[1]);
1528 gnm_float x1 = value_get_as_float (argv[2]);
1529 gnm_float x2 = value_get_as_float (argv[3]);
1530 gnm_float t1 = value_get_as_float (argv[4]);
1531 gnm_float t2 = value_get_as_float (argv[5]);
1532 gnm_float r = value_get_as_float (argv[6]);
1533 gnm_float b = value_get_as_float (argv[7]);
1534 gnm_float v = value_get_as_float (argv[8]);
1536 gnm_float rho = gnm_sqrt (t1 / t2);
1537 gnm_float z1 = (gnm_log (s / x2) + (b + (v * v) / 2.0) * t2) / (v * gnm_sqrt (t2));
1538 gnm_float z2 = (gnm_log (s / x1) + (b + (v * v) / 2.0) * t1) / (v * gnm_sqrt (t1));
1540 gnm_float gfresult;
1542 switch (call_put) {
1543 case OS_Call:
1544 gfresult = opt_bs1 (call_put, s, x1, t1, r, v, b) +
1545 s * gnm_exp ((b - r) * t2) * cum_biv_norm_dist1 (z1, -z2, -rho) -
1546 x2 * gnm_exp (-r * t2) * cum_biv_norm_dist1 (z1 - gnm_sqrt ((v * v) * t2), -z2 + gnm_sqrt ((v * v) * t1), -rho);
1547 break;
1548 case OS_Put:
1549 gfresult = opt_bs1 (call_put, s, x1, t1, r, v, b) +
1550 x2 * gnm_exp (-r * t2) * cum_biv_norm_dist1 (-z1 + gnm_sqrt ((v * v) * t2), z2 - gnm_sqrt ((v * v) * t1), -rho) -
1551 s * gnm_exp ((b - r) * t2) * cum_biv_norm_dist1 (-z1, z2, -rho);
1552 break;
1553 default:
1554 return value_new_error_NUM (ei->pos);
1557 return value_new_float (gfresult);
1560 static GnmFuncHelp const help_opt_extendible_writer[] = {
1561 { GNM_FUNC_HELP_NAME, F_("OPT_EXTENDIBLE_WRITER:theoretical price of extendible writer options")},
1562 DEF_ARG_CALL_PUT_FLAG,
1563 DEF_ARG_SPOT,
1564 { GNM_FUNC_HELP_ARG, F_("strike1:strike price at which the option is struck")},
1565 { GNM_FUNC_HELP_ARG, F_("strike2:strike price at which the option is re-struck if out of the money at @{time1}")},
1566 { GNM_FUNC_HELP_ARG, F_("time1:initial maturity of the option in years")},
1567 { GNM_FUNC_HELP_ARG, F_("time2:extended maturity in years if chosen")},
1568 DEF_ARG_RATE_RISKFREE_ANN,
1569 DEF_ARG_CC,
1570 DEF_ARG_VOLATILITY_SHORT,
1571 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_EXTENDIBLE_WRITER models the theoretical price of extendible "
1572 "writer options. These are options that have their maturity "
1573 "extended to @{time2} if the option is "
1574 "out of the money at @{time1}.")},
1575 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1576 { GNM_FUNC_HELP_END}
1581 /* Two asset correlation options */
1582 static GnmValue *
1583 opt_2_asset_correlation(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1585 OptionSide call_put = option_side (value_peek_string(argv[0]));
1586 gnm_float s1 = value_get_as_float (argv[1]);
1587 gnm_float s2 = value_get_as_float (argv[2]);
1588 gnm_float x1 = value_get_as_float (argv[3]);
1589 gnm_float x2 = value_get_as_float (argv[4]);
1590 gnm_float t = value_get_as_float (argv[5]);
1591 gnm_float b1 = value_get_as_float (argv[6]);
1592 gnm_float b2 = value_get_as_float (argv[7]);
1593 gnm_float r = value_get_as_float (argv[8]);
1594 gnm_float v1 = value_get_as_float (argv[9]);
1595 gnm_float v2 = value_get_as_float (argv[10]);
1596 gnm_float rho = value_get_as_float (argv[11]);
1598 gnm_float y1 = (gnm_log (s1 / x1) + (b1 - (v1 * v1) / 2.0) * t) / (v1 * gnm_sqrt (t));
1599 gnm_float y2 = (gnm_log (s2 / x2) + (b2 - (v2 * v2) / 2.0) * t) / (v2 * gnm_sqrt (t));
1601 if (call_put == OS_Call) {
1602 return value_new_float (s2 * gnm_exp ((b2 - r) * t)
1603 * cum_biv_norm_dist1 (y2 + v2 * gnm_sqrt (t), y1 + rho * v2 * gnm_sqrt (t), rho)
1604 - x2 * gnm_exp (-r * t) * cum_biv_norm_dist1 (y2, y1, rho));
1605 } else if (call_put == OS_Put) {
1606 return value_new_float (x2 * gnm_exp (-r * t) * cum_biv_norm_dist1 (-y2, -y1, rho)
1607 - s2 * gnm_exp ((b2 - r) * t) * cum_biv_norm_dist1 (-y2 - v2 * gnm_sqrt (t), -y1 - rho * v2 * gnm_sqrt (t), rho));
1608 } else
1609 return value_new_error_NUM (ei->pos);
1612 static GnmFuncHelp const help_opt_2_asset_correlation[] = {
1613 { GNM_FUNC_HELP_NAME, F_("OPT_2_ASSET_CORRELATION:theoretical price of options on 2 assets with correlation @{rho}")},
1614 DEF_ARG_CALL_PUT_FLAG,
1615 { GNM_FUNC_HELP_ARG, F_("spot1:spot price of the underlying asset of the first option")},
1616 { GNM_FUNC_HELP_ARG, F_("spot2:spot price of the underlying asset of the second option")},
1617 { GNM_FUNC_HELP_ARG, F_("strike1:strike prices of the first option")},
1618 { GNM_FUNC_HELP_ARG, F_("strike2:strike prices of the second option")},
1619 DEF_ARG_TIME_MATURITY_Y,
1620 { GNM_FUNC_HELP_ARG, F_("cost_of_carry1:net cost of holding the underlying asset of the first option "
1621 "(for common stocks, the risk free rate less the dividend yield)")},
1622 { GNM_FUNC_HELP_ARG, F_("cost_of_carry2:net cost of holding the underlying asset of the second option "
1623 "(for common stocks, the risk free rate less the dividend yield)")},
1624 DEF_ARG_RATE_RISKFREE_ANN,
1625 { GNM_FUNC_HELP_ARG, F_("volatility1:annualized volatility in price of the underlying asset of the first option")},
1626 { GNM_FUNC_HELP_ARG, F_("volatility2:annualized volatility in price of the underlying asset of the second option")},
1627 { GNM_FUNC_HELP_ARG, F_("rho:correlation between the two underlying assets")},
1628 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_2_ASSET_CORRELATION models the theoretical price of options "
1629 "on 2 assets with correlation @{rho}. The payoff for a call is "
1630 "max(@{spot2} - @{strike2},0) if @{spot1} > @{strike1} or 0 otherwise. "
1631 "The payoff for a put is max (@{strike2} - @{spot2}, 0) if @{spot1} < @{strike1} or 0 otherwise.")},
1632 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1633 { GNM_FUNC_HELP_END}
1637 /* European option to exchange one asset for another */
1638 static GnmValue *
1639 opt_euro_exchange(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1641 gnm_float s1 = value_get_as_float (argv[0]);
1642 gnm_float s2 = value_get_as_float (argv[1]);
1643 gnm_float q1 = value_get_as_float (argv[2]);
1644 gnm_float q2 = value_get_as_float (argv[3]);
1645 gnm_float t = value_get_as_float (argv[4]);
1646 gnm_float r = value_get_as_float (argv[5]);
1647 gnm_float b1 = value_get_as_float (argv[6]);
1648 gnm_float b2 = value_get_as_float (argv[7]);
1649 gnm_float v1 = value_get_as_float (argv[8]);
1650 gnm_float v2 = value_get_as_float (argv[9]);
1651 gnm_float rho = value_get_as_float (argv[10]);
1652 gnm_float v, d1, d2;
1654 v = gnm_sqrt (v1 * v1 + v2 * v2 - 2 * rho * v1 * v2);
1655 d1 = (gnm_log (q1 * s1 / (q2 * s2)) + (b1 - b2 + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1656 d2 = d1 - v * gnm_sqrt (t);
1658 return value_new_float (q1 * s1 * gnm_exp ((b1 - r) * t) * ncdf (d1) -
1659 q2 * s2 * gnm_exp ((b2 - r) * t) * ncdf (d2));
1662 static GnmFuncHelp const help_opt_euro_exchange[] = {
1663 { GNM_FUNC_HELP_NAME, F_("OPT_EURO_EXCHANGE:theoretical price of a European option to exchange assets")},
1664 { GNM_FUNC_HELP_ARG, F_("spot1:spot price of asset 1")},
1665 { GNM_FUNC_HELP_ARG, F_("spot2:spot price of asset 2")},
1666 { GNM_FUNC_HELP_ARG, F_("qty1:quantity of asset 1")},
1667 { GNM_FUNC_HELP_ARG, F_("qty2:quantity of asset 2")},
1668 DEF_ARG_TIME_MATURITY_Y,
1669 DEF_ARG_RATE_RISKFREE_ANN,
1670 { GNM_FUNC_HELP_ARG, F_("cost_of_carry1:net cost of holding asset 1 "
1671 "(for common stocks, the risk free rate less the dividend yield)")},
1672 { GNM_FUNC_HELP_ARG, F_("cost_of_carry2:net cost of holding asset 2 "
1673 "(for common stocks, the risk free rate less the dividend yield)")},
1674 { GNM_FUNC_HELP_ARG, F_("volatility1:annualized volatility in price of asset 1")},
1675 { GNM_FUNC_HELP_ARG, F_("volatility2:annualized volatility in price of asset 2")},
1676 { GNM_FUNC_HELP_ARG, F_("rho:correlation between the prices of the two assets")},
1677 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_EURO_EXCHANGE models the theoretical price of a European "
1678 "option to exchange one asset with quantity @{qty2} and spot "
1679 "price @{spot2} for another with quantity @{qty1} and spot price "
1680 "@{spot1}.")},
1681 { GNM_FUNC_HELP_SEEALSO, "OPT_AMER_EXCHANGE,OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1682 { GNM_FUNC_HELP_END}
1686 /* American option to exchange one asset for another */
1687 static GnmValue *
1688 opt_amer_exchange(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1690 gnm_float s1 = value_get_as_float (argv[0]);
1691 gnm_float s2 = value_get_as_float (argv[1]);
1692 gnm_float q1 = value_get_as_float (argv[2]);
1693 gnm_float q2 = value_get_as_float (argv[3]);
1694 gnm_float t = value_get_as_float (argv[4]);
1695 gnm_float r = value_get_as_float (argv[5]);
1696 gnm_float b1 = value_get_as_float (argv[6]);
1697 gnm_float b2 = value_get_as_float (argv[7]);
1698 gnm_float v1 = value_get_as_float (argv[8]);
1699 gnm_float v2 = value_get_as_float (argv[9]);
1700 gnm_float rho = value_get_as_float (argv[10]);
1701 gnm_float v = gnm_sqrt (v1 * v1 + v2 * v2 - 2 * rho * v1 * v2);
1703 return value_new_float (opt_bjer_stens1 (OS_Call, q1 * s1, q2 * s2, t, r - b2, v,b1 - b2));
1706 static GnmFuncHelp const help_opt_amer_exchange[] = {
1707 { GNM_FUNC_HELP_NAME, F_("OPT_AMER_EXCHANGE:theoretical price of an American option to exchange assets")},
1708 { GNM_FUNC_HELP_ARG, F_("spot1:spot price of asset 1")},
1709 { GNM_FUNC_HELP_ARG, F_("spot2:spot price of asset 2")},
1710 { GNM_FUNC_HELP_ARG, F_("qty1:quantity of asset 1")},
1711 { GNM_FUNC_HELP_ARG, F_("qty2:quantity of asset 2")},
1712 DEF_ARG_TIME_MATURITY_Y,
1713 DEF_ARG_RATE_RISKFREE_ANN,
1714 { GNM_FUNC_HELP_ARG, F_("cost_of_carry1:net cost of holding asset 1 "
1715 "(for common stocks, the risk free rate less the dividend yield)")},
1716 { GNM_FUNC_HELP_ARG, F_("cost_of_carry2:net cost of holding asset 2 "
1717 "(for common stocks, the risk free rate less the dividend yield)")},
1718 { GNM_FUNC_HELP_ARG, F_("volatility1:annualized volatility in price of asset 1")},
1719 { GNM_FUNC_HELP_ARG, F_("volatility2:annualized volatility in price of asset 2")},
1720 { GNM_FUNC_HELP_ARG, F_("rho:correlation between the prices of the two assets")},
1721 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_AMER_EXCHANGE models the theoretical price of an American "
1722 "option to exchange one asset with quantity @{qty2} and spot "
1723 "price @{spot2} for another with quantity @{qty1} and spot price "
1724 "@{spot1}.")},
1725 { GNM_FUNC_HELP_SEEALSO, "OPT_EURO_EXCHANGE,OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1726 { GNM_FUNC_HELP_END}
1730 /* Spread option approximation */
1731 static GnmValue *
1732 opt_spread_approx(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1734 OptionSide call_put_flag = option_side (value_peek_string(argv[0]));
1735 gnm_float f1 = value_get_as_float (argv[1]);
1736 gnm_float f2 = value_get_as_float (argv[2]);
1737 gnm_float x = value_get_as_float (argv[3]);
1738 gnm_float t = value_get_as_float (argv[4]);
1739 gnm_float r = value_get_as_float (argv[5]);
1740 gnm_float v1 = value_get_as_float (argv[6]);
1741 gnm_float v2 = value_get_as_float (argv[7]);
1742 gnm_float rho = value_get_as_float (argv[8]);
1744 gnm_float v = gnm_sqrt (v1 * v1 + gnm_pow ((v2 * f2 / (f2 + x)), 2) - 2 * rho * v1 * v2 * f2 / (f2 + x));
1745 gnm_float F = f1 / (f2 + x);
1747 return value_new_float (opt_bs1 (call_put_flag, F, 1.0, t, r, v, 0.0) * (f2 + x));
1750 static GnmFuncHelp const help_opt_spread_approx[] = {
1751 { GNM_FUNC_HELP_NAME, F_("OPT_SPREAD_APPROX:theoretical price of a European option on the spread between two futures contracts")},
1752 DEF_ARG_CALL_PUT_FLAG,
1753 { GNM_FUNC_HELP_ARG, F_("fut_price1:price of the first futures contract")},
1754 { GNM_FUNC_HELP_ARG, F_("fut_price2:price of the second futures contract")},
1755 DEF_ARG_STRIKE,
1756 DEF_ARG_TIME_MATURITY_Y,
1757 DEF_ARG_RATE_RISKFREE_ANN,
1758 { GNM_FUNC_HELP_ARG, F_("volatility1:annualized volatility in price of the first underlying futures contract")},
1759 { GNM_FUNC_HELP_ARG, F_("volatility2:annualized volatility in price of the second underlying futures contract")},
1760 { GNM_FUNC_HELP_ARG, F_("rho:correlation between the two futures contracts")},
1761 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1762 { GNM_FUNC_HELP_END}
1766 /* Floating strike lookback options */
1767 static GnmValue *
1768 opt_float_strk_lkbk(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1770 OptionSide call_put_flag = option_side (value_peek_string(argv[0]));
1771 gnm_float s = value_get_as_float (argv[1]);
1772 gnm_float s_min = value_get_as_float (argv[2]);
1773 gnm_float s_max = value_get_as_float (argv[3]);
1774 gnm_float t = value_get_as_float (argv[4]);
1775 gnm_float r = value_get_as_float (argv[5]);
1776 gnm_float b = value_get_as_float (argv[6]);
1777 gnm_float v = value_get_as_float (argv[7]);
1779 gnm_float a1, a2, m;
1781 if(OS_Call == call_put_flag)
1782 m = s_min;
1783 else if(OS_Put == call_put_flag)
1784 m = s_max;
1785 else
1786 return value_new_error_NUM (ei->pos);
1788 a1 = (gnm_log (s / m) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1789 a2 = a1 - v * gnm_sqrt (t);
1791 if(OS_Call == call_put_flag)
1792 return value_new_float (s * gnm_exp ((b - r) * t) * ncdf (a1) -
1793 m * gnm_exp (-r * t) * ncdf (a2) +
1794 gnm_exp (-r * t) * (v * v) / (2 * b) * s * (gnm_pow (s / m, (-2 * b / (v * v))) * ncdf (-a1 + 2 * b / v * gnm_sqrt (t)) -
1795 gnm_exp (b * t) * ncdf (-a1)));
1796 else if(OS_Put == call_put_flag)
1797 return value_new_float (m * gnm_exp (-r * t) * ncdf (-a2) -
1798 s * gnm_exp ((b - r) * t) * ncdf (-a1) +
1799 gnm_exp (-r * t) * (v * v) / (2 * b) * s * (-gnm_pow (s / m, ((-2 * b) / (v * v))) * ncdf (a1 - 2 * b / v * gnm_sqrt (t)) +
1800 gnm_exp (b * t) * ncdf (a1)));
1802 return value_new_error_VALUE (ei->pos);
1805 static GnmFuncHelp const help_opt_float_strk_lkbk[] = {
1806 { GNM_FUNC_HELP_NAME, F_("OPT_FLOAT_STRK_LKBK:theoretical price of floating-strike lookback option")},
1807 DEF_ARG_CALL_PUT_FLAG,
1808 DEF_ARG_SPOT,
1809 { GNM_FUNC_HELP_ARG, F_("spot_min:minimum spot price of the underlying asset so far observed")},
1810 { GNM_FUNC_HELP_ARG, F_("spot_max:maximum spot price of the underlying asset so far observed")},
1811 DEF_ARG_TIME_MATURITY_Y,
1812 DEF_ARG_RATE_RISKFREE_ANN,
1813 DEF_ARG_CC,
1814 DEF_ARG_VOLATILITY_SHORT,
1815 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_FLOAT_STRK_LKBK determines the theoretical price of a "
1816 "floating-strike lookback option where the holder "
1817 "of the option may exercise on expiry at the most favourable price "
1818 "observed during the options life of the underlying asset.")},
1819 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1820 { GNM_FUNC_HELP_END}
1824 /* Fixed strike lookback options */
1826 static GnmValue *
1827 opt_fixed_strk_lkbk(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1829 OptionSide call_put_flag = option_side (value_peek_string(argv[0]));
1830 gnm_float s = value_get_as_float (argv[1]);
1831 gnm_float s_min = value_get_as_float (argv[2]);
1832 gnm_float s_max = value_get_as_float (argv[3]);
1833 gnm_float x = value_get_as_float (argv[4]);
1834 gnm_float t = value_get_as_float (argv[5]);
1835 gnm_float r = value_get_as_float (argv[6]);
1836 gnm_float b = value_get_as_float (argv[7]);
1837 gnm_float v = value_get_as_float (argv[8]);
1839 gnm_float d1, d2;
1840 gnm_float e1, e2, m;
1842 if (OS_Call == call_put_flag)
1843 m = s_max;
1844 else if (OS_Put == call_put_flag)
1845 m = s_min;
1846 else
1847 return value_new_error_VALUE (ei->pos);
1849 d1 = (gnm_log (s / x) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1850 d2 = d1 - v * gnm_sqrt (t);
1851 e1 = (gnm_log (s / m) + (b + (v * v) / 2.0) * t) / (v * gnm_sqrt (t));
1852 e2 = e1 - v * gnm_sqrt (t);
1854 if (OS_Call == call_put_flag && x > m)
1855 return value_new_float (s * gnm_exp ((b - r) * t) * ncdf (d1) - x * gnm_exp (-r * t) * ncdf (d2) + s * gnm_exp (-r * t) * (v * v) / (2 * b) * (-gnm_pow ((s / x), (-2 * b / (v * v))) * ncdf (d1 - 2 * b / v * gnm_sqrt (t)) + gnm_exp (b * t) * ncdf (d1)));
1857 else if (OS_Call == call_put_flag && x <= m)
1858 return value_new_float (gnm_exp (-r * t) * (m - x) + s * gnm_exp ((b - r) * t) * ncdf (e1) - gnm_exp (-r * t) * m * ncdf (e2) + s * gnm_exp (-r * t) * (v * v) / (2 * b) * (-gnm_pow ((s / m), (-2 * b / (v * v))) * ncdf (e1 - 2 * b / v * gnm_sqrt (t)) + gnm_exp (b * t) * ncdf (e1)));
1860 else if (OS_Put == call_put_flag && x < m)
1861 return value_new_float (-s * gnm_exp ((b - r) * t) * ncdf (-d1) + x * gnm_exp (-r * t) * ncdf (-d1 + v * gnm_sqrt (t)) + s * gnm_exp (-r * t) * (v * v) / (2 * b) * (gnm_pow ((s / x), (-2 * b / (v * v))) * ncdf (-d1 + 2 * b / v * gnm_sqrt (t)) - gnm_exp (b * t) * ncdf (-d1)));
1863 else if (OS_Put == call_put_flag && x >= m)
1864 return value_new_float (gnm_exp (-r * t) * (x - m) - s * gnm_exp ((b - r) * t) * ncdf (-e1) + gnm_exp (-r * t) * m * ncdf (-e1 + v * gnm_sqrt (t)) + gnm_exp (-r * t) * (v * v) / (2 * b) * s * (gnm_pow ((s / m), (-2 * b / (v * v))) * ncdf (-e1 + 2 * b / v * gnm_sqrt (t)) - gnm_exp (b * t) * ncdf (-e1)));
1866 return value_new_error_VALUE (ei->pos);
1869 static GnmFuncHelp const help_opt_fixed_strk_lkbk[] = {
1870 { GNM_FUNC_HELP_NAME, F_("OPT_FIXED_STRK_LKBK:theoretical price of a fixed-strike lookback option")},
1871 DEF_ARG_CALL_PUT_FLAG,
1872 DEF_ARG_SPOT,
1873 { GNM_FUNC_HELP_ARG, F_("spot_min:minimum spot price of the underlying asset so far observed")},
1874 { GNM_FUNC_HELP_ARG, F_("spot_max:maximum spot price of the underlying asset so far observed")},
1875 DEF_ARG_STRIKE,
1876 DEF_ARG_TIME_MATURITY_Y,
1877 DEF_ARG_RATE_RISKFREE_ANN,
1878 DEF_ARG_CC,
1879 DEF_ARG_VOLATILITY_SHORT,
1880 { GNM_FUNC_HELP_DESCRIPTION, F_("OPT_FIXED_STRK_LKBK determines the theoretical price of a "
1881 "fixed-strike lookback option where the holder "
1882 "of the option may exercise on expiry at the most favourable price "
1883 "observed during the options life of the underlying asset.")},
1884 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1885 { GNM_FUNC_HELP_END}
1890 /* Binomial Tree valuation */
1891 static GnmValue *
1892 opt_binomial(GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1894 OptionType amer_euro_flag = option_type(value_peek_string(argv[0]));
1895 OptionSide call_put_flag = option_side (value_peek_string(argv[1]));
1896 gnm_float n = gnm_floor (value_get_as_float (argv[2]));
1897 gnm_float s = value_get_as_float (argv[3]);
1898 gnm_float x = value_get_as_float (argv[4]);
1899 gnm_float t = value_get_as_float (argv[5]);
1900 gnm_float r = value_get_as_float (argv[6]);
1901 gnm_float v = value_get_as_float (argv[7]);
1902 gnm_float b = argv[8] ? value_get_as_float (argv[8]) : 0;
1904 gnm_float *value_array;
1905 gnm_float u, d, p, dt, Df, temp1, temp2, gf_result;
1906 gint i, j, z;
1908 if (n < 0 || n > 100000)
1909 return value_new_error_NUM (ei->pos);
1911 if (OS_Call == call_put_flag)
1912 z = 1;
1913 else if (OS_Put == call_put_flag)
1914 z = -1;
1915 else
1916 return value_new_error_NUM (ei->pos);
1918 if (OT_Error == amer_euro_flag)
1919 return value_new_error_NUM (ei->pos);
1921 value_array = (gnm_float *) g_try_malloc ((n + 2)* sizeof(gnm_float));
1922 if (value_array == NULL)
1923 return value_new_error_NUM (ei->pos);
1925 dt = t / n;
1926 u = gnm_exp (v * gnm_sqrt (dt));
1927 d = 1.0 / u;
1928 p = (gnm_exp (b * dt) - d) / (u - d);
1929 Df = gnm_exp (-r * dt);
1931 for (i = 0; i <= n; ++i) {
1932 temp1 = z * (s * gnm_pow (u, i) * gnm_pow (d, (n - i)) - x);
1933 value_array[i] = MAX (temp1, 0.0);
1936 for (j = n - 1; j > -1; --j) {
1937 for (i = 0; i <= j; ++i) {
1938 /*if (0==i)g_printerr("secondloop %d\n",j);*/
1939 if (OT_Euro == amer_euro_flag)
1940 value_array[i] = (p * value_array[i + 1] + (1.0 - p) * value_array[i]) * Df;
1941 else if (OT_Amer == amer_euro_flag) {
1942 temp1 = (z * (s * gnm_pow (u, i) * gnm_pow (d, (gnm_abs (i - j))) - x));
1943 temp2 = (p * value_array[i + 1] + (1.0 - p) * value_array[i]) * Df;
1944 value_array[i] = MAX (temp1, temp2);
1948 gf_result = value_array[0];
1949 g_free (value_array);
1950 return value_new_float (gf_result);
1953 static GnmFuncHelp const help_opt_binomial[] = {
1954 { GNM_FUNC_HELP_NAME, F_("OPT_BINOMIAL:theoretical price of either an American or European style option using a binomial tree")},
1955 { GNM_FUNC_HELP_ARG, F_("amer_euro_flag:'a' for an American style option or 'e' for a European style option")},
1956 DEF_ARG_CALL_PUT_FLAG,
1957 { GNM_FUNC_HELP_ARG, F_("num_time_steps:number of time steps used in the valuation")},
1958 DEF_ARG_SPOT,
1959 DEF_ARG_STRIKE,
1960 DEF_ARG_TIME_MATURITY_Y,
1961 DEF_ARG_RATE_RISKFREE_ANN,
1962 DEF_ARG_VOLATILITY_SHORT,
1963 DEF_ARG_CC,
1964 { GNM_FUNC_HELP_NOTE, F_("A larger @{num_time_steps} yields greater accuracy but OPT_BINOMIAL is slower to calculate.")},
1965 { GNM_FUNC_HELP_SEEALSO, "OPT_BS,OPT_BS_DELTA,OPT_BS_RHO,OPT_BS_THETA,OPT_BS_GAMMA"},
1966 { GNM_FUNC_HELP_END}
1971 GnmFuncDescriptor const derivatives_functions [] = {
1972 { "opt_bs",
1973 "sfffff|f",
1974 help_opt_bs, opt_bs, NULL,
1975 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
1977 { "opt_bs_delta",
1978 "sfffff|f",
1979 help_opt_bs_delta, opt_bs_delta, NULL,
1980 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
1982 { "opt_bs_rho",
1983 "sfffff|f",
1984 help_opt_bs_rho, opt_bs_rho, NULL,
1985 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
1987 { "opt_bs_theta",
1988 "sfffff|f",
1989 help_opt_bs_theta, opt_bs_theta, NULL,
1990 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
1992 { "opt_bs_gamma",
1993 "fffff|f",
1994 help_opt_bs_gamma, opt_bs_gamma, NULL,
1995 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
1997 { "opt_bs_vega",
1998 "fffff|f",
1999 help_opt_bs_vega, opt_bs_vega, NULL,
2000 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2002 { "opt_bs_carrycost",
2003 "sfffff|f",
2004 help_opt_bs_carrycost, opt_bs_carrycost, NULL,
2005 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2007 { "cum_biv_norm_dist",
2008 "fff",
2009 help_cum_biv_norm_dist, cum_biv_norm_dist, NULL,
2010 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
2012 { "opt_garman_kohlhagen",
2013 "sffffff",
2014 help_opt_garman_kohlhagen, opt_garman_kohlhagen, NULL,
2015 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2017 { "opt_french",
2018 "sfffffff",
2019 help_opt_french, opt_french, NULL,
2020 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2022 { "opt_jump_diff",
2023 "sfffffff",
2024 help_opt_jump_diff, opt_jump_diff, NULL,
2025 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2027 { "opt_exec",
2028 "sfffffff",
2029 help_opt_exec, opt_exec, NULL,
2030 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2032 { "opt_bjer_stens",
2033 "sffffff",
2034 help_opt_bjer_stens, opt_bjer_stens, NULL,
2035 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2037 { "opt_miltersen_schwartz",
2038 "sfffffffffffff",
2039 help_opt_miltersen_schwartz, opt_miltersen_schwartz, NULL,
2040 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2042 { "opt_baw_amer",
2043 "sffffff",
2044 help_opt_baw_amer, opt_baw_amer, NULL,
2045 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2047 { "opt_rgw",
2048 "fffffff",
2049 help_opt_rgw, opt_rgw, NULL,
2050 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2052 { "opt_forward_start",
2053 "sfffffff",
2054 help_opt_forward_start, opt_forward_start, NULL,
2055 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2057 { "opt_time_switch",
2058 "sfffffffff",
2059 help_opt_time_switch, opt_time_switch, NULL,
2060 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2062 { "opt_simple_chooser",
2063 "fffffff",
2064 help_opt_simple_chooser, opt_simple_chooser, NULL,
2065 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2067 { "opt_complex_chooser",
2068 "fffffffff",
2069 help_opt_complex_chooser, opt_complex_chooser, NULL,
2070 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2072 { "opt_on_options",
2073 "sffffffff",
2074 help_opt_on_options, opt_on_options, NULL,
2075 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2077 { "opt_extendible_writer",
2078 "sffffffff",
2079 help_opt_extendible_writer, opt_extendible_writer, NULL,
2080 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2082 { "opt_2_asset_correlation",
2083 "sfffffffffff",
2084 help_opt_2_asset_correlation, opt_2_asset_correlation, NULL,
2085 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2087 { "opt_euro_exchange",
2088 "fffffffffff",
2089 help_opt_euro_exchange, opt_euro_exchange, NULL,
2090 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2092 { "opt_amer_exchange",
2093 "fffffffffff",
2094 help_opt_amer_exchange, opt_amer_exchange, NULL,
2095 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2097 { "opt_spread_approx",
2098 "sffffffff",
2099 help_opt_spread_approx, opt_spread_approx, NULL,
2100 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2102 { "opt_float_strk_lkbk",
2103 "sfffffff",
2104 help_opt_float_strk_lkbk, opt_float_strk_lkbk, NULL,
2105 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2107 { "opt_fixed_strk_lkbk",
2108 "sffffffff",
2109 help_opt_fixed_strk_lkbk, opt_fixed_strk_lkbk, NULL,
2110 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2113 { "opt_binomial",
2114 "ssffffff|f",
2115 help_opt_binomial, opt_binomial, NULL,
2116 GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC },
2118 { NULL}