IMSL C Math Library
feynman_kac

   more...
 
This function solves the generalized Feynman-Kac PDE on a rectangular grid using a finite element Galerkin method. Initial and boundary conditions are satisfied. The solution is represented by a series of C2 Hermite quintic splines.
Synopsis
#include <imsl.h>
void imsl_f_feynman_kac (int nxgrid, int ntgrid, int nlbcd, int nrbcd, float xgrid[], float tgrid[], void fcn_fkcfiv(), void fcn_fkbcp(), float y[], float y_prime[],…, 0)
The type double function is imsl_d_feynman_kac.
Required Arguments
int nxgrid (Input)
Number of grid lines in the x-direction. nxgrid must be at least 2.
int ntgrid (Input)
Number of time points where an approximate solution is returned. The value ntgrid must be at least 1.
int nlbcd (Input)
Number of left boundary conditions. It is required that 1 nlbcd ≤ 3.
int  nrbcd (Input)
Number of right boundary conditions. It is required that 1 nrbcd ≤ 3.
float xgrid[] (Input)
Array of length nxgrid containing the breakpoints for the Hermite quintic splines used in the x discretization. The points in xgrid must be strictly increasing. The values xgrid[0] and xgrid[nxgrid-1] are the endpoints of the interval.
float tgrid[] (Input)
Array of length ntgrid containing the set of time points (in time-remaining units) where an approximate solution is returned. The points in tgrid must be positive and strictly increasing.
void fcn_fkcfiv (float x, float t, int *iflag, float *value)
User-supplied function to compute the values of the coefficients for the Feynman-Kac PDE and the initial data function .
float x (Input)
Space variable.
float t (Input)
Time variable.
int *iflag (Input/Output)
On entry, iflag indicates which coefficient or data function is to be computed. The following table shows which value has to be returned by fcn_fkcfiv for all possible values of iflag:
iflag
Computed coefficient
-1

0

1

2

3

For non-zero input values of iflag, note when a coefficient does not depend on t. This is done by setting iflag = 0 after the coefficient is defined. If there is time dependence, the value of iflag should not be changed. This action will usually yield a more efficient algorithm because some finite element matrices do not have to be reassembled for each t value.
float *value (Output)
Value of the coefficient or initial data function. Which value is computed depends on the input value for iflag, see description of iflag.
void fcn_fkbcp (int nbc, float t, int *iflag, float values[])
User-supplied function to define boundary values that the solution of the differential equation must satisfy. There are nlbcd conditions specified at the left end, , and nrbcd conditions at the right end, . The boundary conditions are
int nbc (Input)
Number of boundary conditions. At xmin, nbc=nlbcd, at xmax, nbc = nrbcd.
float t (Input)
Time point of the boundary conditions.
int *iflag (Input/Output)
On entry, iflag indicates whether the coefficients for the left or right boundary conditions are to be computed:
iflag
Computed boundary conditions
1
Left end, x = xmin
0
Right end, x = xmax
If there is no time dependence for one of the boundaries then set iflag = 0 after the array values is defined for either end point. This flag can avoid unneeded continued computation of the finite element matrices.
float values[] (Output)
Array of length 4 * max (nlbcdnrbcd) containing the values of the boundary condition coefficients in its first 4*nbc locations. The coefficients for xmin are stored row-wise according to the following scheme:
The coefficients for xmax are stored similarly.
float y[] (Output)
An array of size (ntgrid+1) by (3 × nxgrid) containing the coefficients of the Hermite representation of the approximate solution for the Feynman-Kac PDE at time points 0, tgrid[0], …, tgrid[ntgrid-1]. The approximate solution is given by
for
The representation for the initial data at t = 0 is
The (ntgrid + 1) by (3 * nxgrid) matrix
is stored row-wise in array y.
After the integration, use row i of array y as input argument coef in function imsl_f_feynman_kac_evaluate to obtain an array of values for f(x, t) or its partials at time point t=0,tgrid[i-1], i=1,…,ntgrid.
The expressions for the basis functions are represented piece-wise and can be found in Hanson, R. (2008) “Integrating Feynman-Kac Equations Using Hermite Quintic Finite Elements.”
float y_prime[] (Output)
An array of size (ntgrid + 1) by (3 × nxgrid) containing the first derivatives of y at time points 0, tgrid[0],,tgrid[ntgrid - 1], i.e.
for
and
The (ntgrid + 1) by (3 *nxgrid) matrix
is stored row-wise in array y_prime.
After the integration, use row i of array y_prime as input argument coef in function imsl_f_feynman_kac_evaluate to obtain an array of values for the partials at time point t=tgrid[i-1], i=1,,ntgrid, and row 0 for the partials at t=0.
Synopsis with Optional Arguments
#include <imsl.h>
void imsl_f_feynman_kac (int nxgrid, int ntgrid, int nlbcd, int nrbcd, float xgrid[], float tgrid[], void fcn_fkcfiv(), void fcn_fkbcp(), float y[], float y_prime[],
IMSL_FCN_FKCFIV_W_DATA, void fcn_fkcfiv(), void *data,
IMSL_FCN_FKBCP_W_DATA, void fcn_fkbcp(), void *data,
IMSL_FCN_INIT, void fcn_fkinit(),
IMSL_FCN_INIT_W_DATA, void fcn_fkinit(), void *data,
IMSL_FCN_FORCE, void fcn_fkforce(),
IMSL_FCN_FORCE_W_DATA, void fcn_fkforce(), void *data,
IMSL_ATOL_RTOL_SCALARS, float atol, float rtol,
IMSL_ATOL_RTOL_ARRAYS, float atol[], float rtol[]
IMSL_NDEGREE, int ndeg,
IMSL_TDEPEND, int tdepend[],
IMSL_MAX_STEP, float max_stepsize,
IMSL_INITIAL_STEPSIZE, float init_stepsize,
IMSL_MAX_NUMBER_STEPS, int max_steps,
IMSL_STEP_CONTROL, int step_control,
IMSL_MAX_BDF_ORDER, int max_bdf_order,
IMSL_T_BARRIER, float t_barrier,
IMSL_ISTATE, int istate[],
IMSL_EVALS, int nval[],
0)
Optional Arguments
IMSL_FCN_FKCFIV_W_DATA, void fcn_fkcfiv(float x, float t, int *iflag, float *value, void *data), void *data (Input)
User-supplied function to compute the values of the coefficients for the Feynman-Kac PDE and the initial data function . This function also accepts a pointer to the object data supplied by the user.
IMSL_FCN_FKBCP_W_DATA, void fcn_fkbcp(int nbc, float t,int *iflag, float values[], void *data), void *data (Input)
User-supplied function to define boundary values that the solution of the differential equation must satisfy. This function also accepts a pointer to data supplied by the user.
IMSL_FCN_INIT, void fcn_fkinit(int nxgrid, int ntgrid, float xgrid[], float tgrid[], float time, float yprime[], float y[], float atol[]float rtol[]) (Input)
User-supplied function for adjustment of initial data or as an opportunity for output during the integration steps. The solution values of the model parameters are presented in the arrays y and yprime at the integration points time = 0, tgrid[j]j=0,…,ntgrid-1. At the initial point, integral least-squares estimates are made for representing the initial data . If this is not satisfactory, fcn_fkinit can change the contents of y[] to match data for any reason.
int nxgrid (Input)
Number of grid lines in the x-direction.
int ntgrid (Input)
Number of time points where an approximate solution is returned.
float xgrid[] (Input)
Vector of length nxgrid containing the breakpoints for the Hermite quintic splines used in the x discretization.
float tgrid[] (Input)
Vector of length ntgrid containing the set of time points (in time-remaining units) where an approximate solution is returned.
float time (Input)
Time variable.
float yprime[] (Input)
Vector of length 3*nxgrid containing the derivative of the coefficients of the Hermite quintic spline at time point time.
float y[] (Input/Output)
Vector of length 3 × nxgrid containing the coefficients of the Hermite quintic spline at time point time.
float atol[] (Input/Output)
Array of length 3 × nxgridcontaining absolute error tolerances.
float rtol[] (Input/Output)
Array of length 3 × nxgrid containing relative error tolerances.
IMSL_FCN_INIT_W_DATA, void fcn_fkinit(int nxgrid, int ntgrid, float xgrid[], float tgrid[], float time, float yprime[], float y[], float atol[], float rtol[], void *data), void *data (Input)
User-supplied function for adjustment of initial data or as an opportunity for output during the integration steps which also accepts a pointer to data supplied by the user. For an explanation of the other arguments of function fcn_fkinit, see optional argument IMSL_FCN_INIT.
IMSL_FCN_FORCE, void fcn_fkforce(int interval, int ndeg, int nxgrid, float y[], float time, float width, float xlocal[], float qw[], float u[], float phi[], float dphi[]) (Input)
Function fcn_fkforce is used in case there is a non-zero term in the Feynman-Kac differential equation. If function fcn_fkforce is not used, it is assumed that is identically zero.
int interval (Input)
Index indicating the bounds xgrid[interval-1] and xgrid[interval] of the integration interval, 1interval  nxgrid-1.
int ndeg (Input)
The degree used for the Gauss-Legendre formulas.
int nxgrid (Input)
Number of grid lines in the x-direction.
float y[] (Input)
Vector of length 3*nxgrid containing the coefficients of the Hermite quintic spline representing the solution of the Feynman-Kac equation at time point time.
float time (Input)
Time variable.
float width (Input)
The interval width, width = xgrid[interval] - xgrid[interval-1].
float xlocal[] (Input)
Array of length ndeg containing the Gauss-Legendre points translated and normalized to the interval [xgrid[interval-1], xgrid[interval]].
float qw[] (Input)
Vector of length ndeg containing the Gauss-Legendre weights.
float u[] (Input)
Array of dimension 12 by ndeg containing the basis function values that define at the Gauss-Legendre points xlocal. Setting u[i+k*ndeg] and xlocal[i], is defined as
float phi[] (Output)
Vector of length 6 containing Gauss-Legendre approximations for the local contributions
where  time and
Vector phi contains elements
for i=0,…,5.
float dphi[] (Output)
Array of dimension 6 by 6 containing a Gauss-Legendre approximation for the Jacobian of the local contributions at  time,
The approximation to this symmetric matrix is stored row-wise, i.e.
for i, j = 0, …, 5.
IMSL_FCN_FORCE_W_DATA, void fcn_fkforce(int interval, int ndeg, int nxgrid, float y[], float time, float width, float  xlocal[], float qw[], float u[], float phi[], float dphi[], void *data), void *data (Input)
Function fcn_fkforce is used in case there is a non-zero term in the Feynman-Kac differential equation. This function also accepts a data pointer to data supplied by the user. For an explanation of the other arguments of function fcn_fkforce, see optional argument IMSL_FCN_FORCE.
IMSL_ATOL_RTOL_SCALARS, float atol, float rtol (Input)
Scalar values that apply to the error estimates of all components of the solution y in the differential equation solver SDASLX. See optional argument IMSL_ATOL_RTOL_ARRAYS if separate tolerances are to be applied to each component of y.
Default: atol and rtol are set to 10-3 in single precision and 10-5 in double precision.
IMSL_ATOL_RTOL_ARRAYS, float atol[], float rtol[], (Input)
Componentwise tolerances are used for the computation of solution y in the differential equation solver SDASLX. Arguments atol and rtol are pointers to arrays of length 3 × nxgrid to be used for the absolute and relative tolerance and to be applied to each component of the solution, y. See optional argument IMSL_ATOL_RTOL_SCALARS if scalar values of absolute and relative tolerances are to be applied to all components.
Default: All elements of atol and rtol are set to 10-3 in single precision and 10-5 in double precision.
IMSL_NDEGREE, int ndeg (Input)
The degree used for the Gauss-Legendre formulas for constructing the finite element matrices. It is required that ndeg ≥ 6.
Default: ndeg = 6.
IMSL_TDEPEND, int tdepend[] (Output)
Vector of length 7 indicating time dependence of the coefficients, boundary conditions and function φ in the Feynman-Kac equation. If tdepend[i] = 0 then argument i is not time dependent, if tdepend[i]=1 then argument i is time dependent.
i
Computed coefficient
0

1

2

3

4
Left boundary conditions
5
Right boundary conditions
6
φ
IMSL_MAX_STEP, float max_stepsize (Input)
This is the maximum step size the integrator may take.
Default: max_stepsize = imsl_f_machine(2), the largest possible machine number.
IMSL_INITIAL_STEPSIZE, float init_stepsize (Input)
The starting step size for the integration. Must be less than zero since the integration is internally done from t=0 to t=tgrid[ntgrid-1] in a negative direction.
Default: init_stepsize =  , where Ɛ is the machine precision.
IMSL_MAX_NUMBER_STEPS, int max_steps (Input)
The maximum number of steps between each output point of the integration.
Default: maxsteps = 500000.
IMSL_STEP_CONTROL, int step_control (Input)
Indicates which step control algorithm is used for the integration. If step_control = 0, then the step control method of Söderlind is used. If step_control = 1, then the method used by the original Petzold code SASSL is used.
Default: step_control = 0.
IMSL_MAX_BDF_ORDER, int max_bdf_order (Input)
Maximum order of the backward differentiation formulas used in the integrator. It is required that 1 max_bdf_order ≤ 5.
Default: max_bdf_order = 5.
IMSL_T_BARRIER, float t_barrier (Input)
This optional argument controls whether the code should integrate past a special point, t_barrier, and then interpolate to get and at the points in tgrid[]. If this optional argument is present, the integrator assumes the equations either change on the alternate sides of t_barrier or they are undefined there. In this case, the code creeps up to t_barrier in the direction of integration. It is required that t_barrier tgrid[ntgrid-1].
Default: t_barrier = tgrid[ntgrid-1].
IMSL_ISTATE, int istate[] (Output)
An array of length 7 whose entries flag the state of computation for the matrices and vectors required in the integration. For each entry, a zero indicates that no computation has been done or there is a time dependence. A one indicates that the entry has been computed and there is no time dependence. The istate entries are as follows:
i
Entry in istate
0
Mass Matrix, M
1
Stiffness matrix, N
2
Bending matrix, R
3
Weighted mass, K
4
Left boundary conditions
5
Right- boundary conditions
6
Forcing term
Default: istate[i] = 0 for I = 0,…,6.
IMSL_EVALS, int nval[] (Output)
Array of length 3 summarizing the number of evaluations required during the integration.
i
nval[i]
0
Number of residual function evaluations of the DAE used in the model.
1
Number of factorizations of the differential matrix associated with solving the DAE.
2
Number of linear system solve steps using the differential matrix.
Description
The generalized Feynman-Kac differential equation has the form
where the initial data satisfies . The derivatives are
The function imsl_f_feynman_kac uses a finite element Galerkin method over the rectangle
in to compute the approximate solution. The interval is decomposed with a grid
On each subinterval the solution is represented by
The values
are time-dependent coefficients associated with each interval. The basis functions are given for
by
The Galerkin principle is then applied. Using the provided initial and boundary conditions leads to an Index 1 differential-algebraic equation for the time-dependent coefficients
This system is integrated using the variable order, variable step algorithm DDASLX/SDASLX noted in Hanson and Krogh, R. (2008) Solving Constrained Differential-Algebraic Systems Using Projections. Solution values and their time derivatives are returned at a grid preceding time T, expressed in units of time remaining. For further details of deriving and solving the system see Hanson, R. (2008) Integrating Feynman-Kac Equations Using Hermite Quintic Finite Elements. To evaluate f or its partial derivatives at any time point in the grid, use the function imsl_f_feynman_kac_evaluate.
Examples
Example 1
The value of the American Option on a Vanilla Put can be no smaller than its European counterpart. That is due to the American Option providing the opportunity to exercise at any time prior to expiration. This example compares this difference – or premium value of the American Option – at two time values using the Black-Scholes model. The example is based on Wilmott et al. (1996, p. 176), and uses the non-linear forcing or weighting term described in Hanson, R. (2008), “Integrating Feynman-Kac Equations Using Hermite Quintic Finite Elements”, for evaluating the price of the American Option. The coefficients, payoff, boundary conditions and forcing term for American and European options are defined by the user functions fkcfiv_put, fkbcp_put and fkforce_put, respectively. One breakpoint is set exactly at the strike price.
The sets of parameters in the computation are:
1. Strike price .
2. Volatility .
3. Times until expiration .
4. Interest rate .
5. .
6. .
The payoff function is the “vanilla option”, .
 
#include <imsl.h>
#include <stdio.h>
#include <math.h>
 
#define max(A,B) ((A) >= (B) ? (A) : (B))
#define NXGRID 61
#define NTGRID 2
#define NV 9
 
void fkcfiv_put(float, float, int *, float *);
void fkbcp_put(int, float, int *, float[]);
void fkforce_put(int, int, int, float[], float, float, float[],
float[], float[], float[],float[], void *);
 
int main()
{
/* Compute American Option Premium for Vanilla Put */
/* The strike price */
float KS = 10.0;
 
/* The sigma value */
float sigma = 0.4;
 
/* Time values for the options */
int nt = 2;
float time[] = { 0.25, 0.5};
 
/* Values of the underlying where evaluations are made */
float xs[] = { 0.0, 2.0, 4.0, 6.0, 8.0, 10.0,
12.0, 14.0, 16.0 };
 
/* Value of the interest rate */
float r = 0.1;
 
/* Values of the min and max underlying values modeled */
float x_min=0.0, x_max=30.0;
 
/* Define parameters for the integration step. */
int nint=NXGRID-1, n=3*NXGRID;
float xgrid[NXGRID];
float ye[(NTGRID+1)*3*NXGRID],yeprime[(NTGRID+1)*3*NXGRID];
float ya[(NTGRID+1)*3*NXGRID], yaprime[(NTGRID+1)*3*NXGRID];
float fe[NTGRID*NV], fa[NTGRID*NV];
float dx;
int i;
int nlbcd = 2, nrbcd = 3;
float atol = 0.2e-2;
 
/* Array for user-defined data */
float usr_data[3];
 
/* Define an equally-spaced grid of points for the
underlying price */
dx = (x_max-x_min)/((float) nint);
xgrid[0]=x_min;
xgrid[NXGRID-1]=x_max;
 
for (i=2; i<=NXGRID-1; i++) xgrid[i-1]=xgrid[i-2]+dx;
usr_data[0] = KS;
usr_data[1] = r;
usr_data[2] = atol;
 
imsl_f_feynman_kac(NXGRID, nt, nlbcd, nrbcd, xgrid, time,
fkcfiv_put, fkbcp_put, ye, yeprime,
IMSL_ATOL_RTOL_SCALARS, 0.5e-2, 0.5e-2,
0);
 
imsl_f_feynman_kac(NXGRID, nt, nlbcd, nrbcd, xgrid, time,
fkcfiv_put, fkbcp_put, ya, yaprime,
IMSL_FCN_FORCE_W_DATA, fkforce_put, usr_data,
IMSL_ATOL_RTOL_SCALARS, 0.5e-2, 0.5e-2,
0);
 
/* Evaluate solutions at vector of points XS(:), at each
time value prior to expiration. */
for (i=0; i<nt; i++)
{
imsl_f_feynman_kac_evaluate (NV, NXGRID, xgrid, xs, &ye[(i+1)*n],
IMSL_RETURN_USER, &fe[i*NV], 0);
imsl_f_feynman_kac_evaluate (NV, NXGRID, xgrid, xs, &ya[(i+1)*n],
IMSL_RETURN_USER, &fa[i*NV], 0);
}
 
printf("\nAmerican Option Premium for Vanilla Put, "
"3 and 6 Months Prior to Expiry\n");
printf("%7sNumber of equally spaced spline knots:%4d\n", " ",
NXGRID);
printf("%7sNumber of unknowns:%4d\n", " ", n);
printf("%7sStrike=%6.2f, sigma=%5.2f, Interest Rate=%5.2f\n\n",
" ",KS,sigma,r);
printf("%7s%10s%20s%20s\n", " ","Underlying","European","American");
for (i=0; i<NV; i++)
printf("%7s%10.4f%10.4f%10.4f%10.4f%10.4f\n", " ", xs[i], fe[i],
fe[i+NV], fa[i], fa[i+NV]);
}
 
/* These functions define the coefficients, payoff, boundary conditions
and forcing term for American and European Options. */
void fkcfiv_put(float x, float tx, int *iflag, float *value)
{
/* The sigma value */
float sigma=0.4;
 
/* Value of the interest rate and continuous dividend */
float strike_price=10.0, interest_rate=0.1, dividend=0.0;
float zero=0.0;
 
switch (*iflag)
{
case 0:
/* The payoff function */
*value = max(strike_price - x, zero);
break;
case -1:
/* The coefficient derivative d sigma/ dx */
*value = sigma;
break;
case 1:
/* The coefficient sigma(x) */
*value = sigma*x;
break;
case 2:
/* The coefficient mu(x) */
*value = (interest_rate - dividend) * x;
break;
case 3:
/* The coefficient kappa(x) */
*value = interest_rate;
break;
}
 
/* Note that there is no time dependence */
*iflag = 0;
return;
}
 
void fkbcp_put(int nbc, float tx, int *iflag, float val[])
{
switch (*iflag)
{
case 1:
val[0] = 0.0; val[1] = 1.0; val[2] = 0.0;
val[3] = -1.0; val[4] = 0.0; val[5] = 0.0;
val[6] = 1.0; val[7] = 0.0;
break;
case 2:
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = 0.0; val[4] = 0.0; val[5] = 1.0;
val[6] = 0.0; val[7] = 0.0; val[8] = 0.0;
val[9] = 0.0; val[10] = 1.0; val[11] = 0.0;
break;
}
/* Note no time dependence */
*iflag = 0;
return;
}
 
void fkforce_put(int interval, int ndeg, int nxgrid,
float y[], float time, float width, float xlocal[],
float qw[], float u[], float phi[],float dphi[],
void *data_ptr)
{
int i, j, k, l;
const int local=6;
float yl[6], bf[6];
float value, strike_price, interest_rate, zero=0.0, one=1.0;
float rt, mu;
float *data = NULL;
 
data = (float *) data_ptr;
 
for (i=0; i<local; i++)
{
yl[i] = y[3*interval-3+i];
phi[i] = zero;
}
 
strike_price = data[0];
interest_rate = data[1];
value = data[2];
mu=2.0;
 
/* This is the local definition of the forcing term */
for (j=1; j<=local; j++){
for (l=1; l<=ndeg; l++)
{
bf[0] = u[(l-1)];
bf[1] = u[(l-1)+ndeg];
bf[2] = u[(l-1)+2*ndeg];
bf[3] = u[(l-1)+6*ndeg];
bf[4] = u[(l-1)+7*ndeg];
bf[5] = u[(l-1)+8*ndeg];
 
rt = 0.0;
for (k=0; k<local; k++)
rt += yl[k]*bf[k];
rt = value/(rt + value - (strike_price-xlocal[l-1]));
phi[j-1] += qw[l-1] * bf[j-1] * pow(rt,mu);
}
}
 
for (i=0; i<local; i++)
phi[i] = -phi[i]*width*interest_rate*strike_price;
 
/* This is the local derivative matrix for the forcing term */
for (i=0; i<local*local; i++)
dphi[i] = zero;
 
for (j=1; j<=local; j++){
for (i=1; i<=local; i++){
for (l=1; l<=ndeg; l++)
{
bf[0] = u[(l-1)];
bf[1] = u[(l-1)+ndeg];
bf[2] = u[(l-1)+2*ndeg];
bf[3] = u[(l-1)+6*ndeg];
bf[4] = u[(l-1)+7*ndeg];
bf[5] = u[(l-1)+8*ndeg];
 
rt = 0.0;
for (k=0; k<local; k++)
rt += yl[k]*bf[k];
rt = one/(rt + value-(strike_price-xlocal[l-1]));
dphi[i-1+(j-1)*local] += qw[l-1] * bf[i-1] * bf[j-1]
* pow(rt, mu+1.0);
}
}
}
 
for (i=0; i<local*local; i++)
dphi[i] = mu * dphi[i] * width * pow(value, mu) *
interest_rate * strike_price;
return;
}
Output
 
American Option Premium for Vanilla Put, 3 and 6 Months Prior to Expiry
Number of equally spaced spline knots: 61
Number of unknowns: 183
Strike= 10.00, sigma= 0.40, Interest Rate= 0.10
 
Underlying European American
0.0000 9.7534 9.5136 10.0000 10.0000
2.0000 7.7536 7.5138 8.0000 8.0000
4.0000 5.7537 5.5156 6.0000 6.0000
6.0000 3.7615 3.5683 4.0000 4.0000
8.0000 1.9070 1.9168 2.0170 2.0865
10.0000 0.6529 0.8548 0.6771 0.9030
12.0000 0.1632 0.3371 0.1680 0.3519
14.0000 0.0372 0.1270 0.0373 0.1321
16.0000 0.0088 0.0483 0.0084 0.0501
Example 2
In Beckers (1980) there is a model for a Stochastic Differential Equation of option pricing. The idea is a “constant elasticity of variance diffusion (or CEV) class”
The Black-Scholes model is the limiting case . A numerical solution of this diffusion model yields the price of a call option. Various values of the strike price , time values, and power coefficient are used to evaluate the option price at values of the underlying price. The sets of parameters in the computation are:
1. power .
2. strike price .
3. volatility .
4. times until expiration .
5. underlying prices .
6. interest rate .
7. .
8. .
With this model the Feynman-Kac differential equation is defined by identifying:
The payoff function is the “vanilla option”, .
 
#include <imsl.h>
#include <stdio.h>
#include <math.h>
 
#define max(A,B) ((A) >= (B) ? (A) : (B))
#define NXGRID 121
#define NTGRID 3
#define NV 3
 
void fcn_fkcfiv(float, float, int *, float *, void *);
void fcn_fkbcp(int, float, int *, float[], void *);
 
int main()
{
/* Compute Constant Elasticity of Variance Model for Vanilla Call */
/* The set of strike prices */
float KS[] = { 15.0, 20.0, 25.0};
/* The set of sigma values */
float sigma[] = { 0.2, 0.3, 0.4};
/* The set of model diffusion powers */
float alpha[] = { 2.0, 1.0, 0.0};
/* Time values for the options */
int nt = 3;
float time[] = { 1.0/12.0, 4.0/12.0, 7.0/12.0 };
 
/* Values of the underlying where evaluations are made */
float xs[] = { 19.0, 20.0, 21.0};
 
/* Value of the interest rate and continuous dividend */
float r=0.05, dividend=0.0;
 
/* Values of the min and max underlying values modeled */
float x_min=0.0, x_max=60.0;
/* Define parameters for the integration step. */
int nint = NXGRID-1, n=3*NXGRID;
float xgrid[NXGRID], y[(NTGRID+1)*3*NXGRID];
float yprime[(NTGRID+1)*3*NXGRID], f[NTGRID*NV];
float dx;
 
/* Number of left/right boundary conditions */
int nlbcd = 3, nrbcd = 3;
float usr_data[6];
int i,i1,i2,i3,j;
 
/* Define equally-spaced grid of points for the underlying price */
dx = (x_max-x_min)/((float) nint);
xgrid[0] = x_min;
xgrid[NXGRID-1] = x_max;
for (i=2; i<=NXGRID-1; i++)
xgrid[i-1] = xgrid[i-2]+dx;
printf("%2sConstant Elasticity of Variance Model for Vanilla Call\n",
" ");
printf("%7sInterest Rate:%7.3f\tContinuous Dividend:%7.3f\n",
" ", r, dividend);
printf("%7sMinimum and Maximum Prices of Underlying:%7.2f%7.2f\n",
" ",x_min, x_max);
printf("%7sNumber of equally spaced spline knots:%4d \n", " ",
NXGRID-1);
printf("%7sNumber of unknowns:%4d\n\n", " ",n);
printf("%7sTime in Years Prior to Expiration:%7.4f%7.4f%7.4f\n",
" ",time[0], time[1], time[2]);
printf("%7sOption valued at Underlying Prices:%7.2f%7.2f%7.2f\n\n",
" ", xs[0], xs[1], xs[2]);
for (i1=1; i1<=3; i1++) /* Loop over power */
for (i2=1; i2<=3; i2++) /* Loop over volatility */
for (i3=1; i3<=3; i3++) /* Loop over strike price */
{
/* Pass data through into evaluation functions. */
usr_data[0] = KS[i3-1];
usr_data[1] = x_max;
usr_data[2] = sigma[i2-1];
usr_data[3] = alpha[i1-1];
usr_data[4] = r;
usr_data[5] = dividend;
imsl_f_feynman_kac(NXGRID, nt, nlbcd, nrbcd, xgrid, time,
NULL, NULL, y, yprime,
IMSL_FCN_FKCFIV_W_DATA, fcn_fkcfiv, usr_data,
IMSL_FCN_FKBCP_W_DATA, fcn_fkbcp, usr_data, 0);
/* Evaluate solution at vector of points xs, at each time
Value prior to expiration. */
for (i=0; i<NTGRID; i++)
imsl_f_feynman_kac_evaluate (NV, NXGRID, xgrid, xs,
&y[(i+1)*n], IMSL_RETURN_USER, &f[i*NV], 0);
printf("%2sStrike=%5.2f, Sigma=%5.2f, Alpha=%5.2f\n",
" ", KS[i3-1], sigma[i2-1], alpha[i1-1]);
for (i=0; i<NV; i++)
{
printf("%23sCall Option Values%2s", " ", " ");
for (j=0; j<nt; j++) printf("%7.4f ", f[j*NV+i]);
printf("\n");
}
printf("\n");
}
}
 
void fcn_fkcfiv(float x, float tx, int *iflag, float *value,
void *data_ptr)
{
float sigma, strike_price, interest_rate;
float alpha, dividend, zero=0.0, half=0.5;
float *data = NULL;
data = (float *)data_ptr;
strike_price = data[0];
sigma = data[2];
alpha = data[3];
interest_rate = data[4];
dividend = data[5];
switch (*iflag)
{
case 0:
/* The payoff function */
*value = max(x - strike_price, zero);
break;
 
case -1:
/* The coefficient derivative d sigma/ dx */
*value = half * alpha * sigma * pow(x, alpha*half-1.0);
break;
 
case 1:
/* The coefficient sigma(x) */
*value = sigma * pow(x, alpha*half);
break;
case 2:
/* The coefficient mu(x) */
*value = (interest_rate - dividend) * x;
break;
case 3:
/* The coefficient kappa(x) */
*value = interest_rate;
break;
}
 
data = NULL;
 
/* Note that there is no time dependence */
*iflag = 0;
 
return;
}
 
 
void fcn_fkbcp(int nbc, float tx, int *iflag, float val[],
void *data_ptr)
{
float x_max, df, interest_rate, strike_price;
float *data = NULL;
data = (float *)data_ptr;
strike_price = data[0];
x_max = data[1];
interest_rate = data[4];
 
switch (*iflag)
{
case 1:
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = 0.0; val[4] = 0.0; val[5] = 1.0;
val[6] = 0.0; val[7] = 0.0; val[8] = 0.0;
val[9] = 0.0; val[10] = 1.0; val[11] = 0.0;
 
/* Note no time dependence at left end */
*iflag = 0;
break;
case 2:
df = exp(interest_rate*tx);
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = x_max - df*strike_price; val[4] = 0.0;
val[5] = 1.0; val[6] = 0.0; val[7] = 1.0;
val[8] = 0.0; val[9] = 0.0; val[10] = 1.0;
val[11] = 0.0;
break;
}
data = NULL;
return;
}
Output
 
Constant Elasticity of Variance Model for Vanilla Call
Interest Rate: 0.050 Continuous Dividend: 0.000
Minimum and Maximum Prices of Underlying: 0.00 60.00
Number of equally spaced spline knots: 120
Number of unknowns: 363
 
Time in Years Prior to Expiration: 0.0833 0.3333 0.5833
Option valued at Underlying Prices: 19.00 20.00 21.00
 
Strike=15.00, Sigma= 0.20, Alpha= 2.00
Call Option Values 4.0624 4.2577 4.4730
Call Option Values 5.0624 5.2507 5.4491
Call Option Values 6.0624 6.2487 6.4386
 
Strike=20.00, Sigma= 0.20, Alpha= 2.00
Call Option Values 0.1310 0.5956 0.9699
Call Option Values 0.5028 1.0889 1.5100
Call Option Values 1.1979 1.7485 2.1751
 
Strike=25.00, Sigma= 0.20, Alpha= 2.00
Call Option Values 0.0000 0.0113 0.0742
Call Option Values 0.0000 0.0372 0.1614
Call Option Values 0.0007 0.1025 0.3132
 
Strike=15.00, Sigma= 0.30, Alpha= 2.00
Call Option Values 4.0637 4.3399 4.6623
Call Option Values 5.0626 5.2945 5.5788
Call Option Values 6.0624 6.2709 6.5241
 
Strike=20.00, Sigma= 0.30, Alpha= 2.00
Call Option Values 0.3103 1.0274 1.5500
Call Option Values 0.7315 1.5422 2.1024
Call Option Values 1.3758 2.1689 2.7385
 
Strike=25.00, Sigma= 0.30, Alpha= 2.00
Call Option Values 0.0006 0.1112 0.3547
Call Option Values 0.0038 0.2170 0.5552
Call Option Values 0.0184 0.3857 0.8225
 
Strike=15.00, Sigma= 0.40, Alpha= 2.00
Call Option Values 4.0759 4.5136 4.9673
Call Option Values 5.0664 5.4199 5.8321
Call Option Values 6.0635 6.3577 6.7294
 
Strike=20.00, Sigma= 0.40, Alpha= 2.00
Call Option Values 0.5116 1.4645 2.1286
Call Option Values 0.9623 1.9957 2.6944
Call Option Values 1.5815 2.6110 3.3230
 
Strike=25.00, Sigma= 0.40, Alpha= 2.00
Call Option Values 0.0083 0.3288 0.7790
Call Option Values 0.0285 0.5169 1.0657
Call Option Values 0.0813 0.7688 1.4103
 
Strike=15.00, Sigma= 0.20, Alpha= 1.00
Call Option Values 4.0624 4.2479 4.4311
Call Option Values 5.0624 5.2479 5.4311
Call Option Values 6.0624 6.2479 6.4311
 
Strike=20.00, Sigma= 0.20, Alpha= 1.00
Call Option Values 0.0000 0.0241 0.1061
Call Option Values 0.1498 0.4102 0.6483
Call Option Values 1.0832 1.3313 1.5772
 
Strike=25.00, Sigma= 0.20, Alpha= 1.00
Call Option Values 0.0000 -0.0000 0.0000
Call Option Values 0.0000 0.0000 0.0000
Call Option Values -0.0000 0.0000 0.0000
 
Strike=15.00, Sigma= 0.30, Alpha= 1.00
Call Option Values 4.0624 4.2477 4.4310
Call Option Values 5.0624 5.2477 5.4310
Call Option Values 6.0624 6.2477 6.4310
 
Strike=20.00, Sigma= 0.30, Alpha= 1.00
Call Option Values 0.0016 0.0812 0.2214
Call Option Values 0.1981 0.4981 0.7535
Call Option Values 1.0836 1.3441 1.6018
 
Strike=25.00, Sigma= 0.30, Alpha= 1.00
Call Option Values -0.0000 0.0000 0.0000
Call Option Values -0.0000 0.0000 0.0000
Call Option Values -0.0000 0.0000 0.0005
 
Strike=15.00, Sigma= 0.40, Alpha= 1.00
Call Option Values 4.0624 4.2479 4.4312
Call Option Values 5.0624 5.2479 5.4312
Call Option Values 6.0624 6.2479 6.4312
 
Strike=20.00, Sigma= 0.40, Alpha= 1.00
Call Option Values 0.0072 0.1556 0.3445
Call Option Values 0.2501 0.5919 0.8720
Call Option Values 1.0867 1.3783 1.6577
 
Strike=25.00, Sigma= 0.40, Alpha= 1.00
Call Option Values -0.0000 0.0000 0.0001
Call Option Values 0.0000 0.0000 0.0007
Call Option Values 0.0000 0.0002 0.0059
 
Strike=15.00, Sigma= 0.20, Alpha= 0.00
Call Option Values 4.0625 4.2479 4.4311
Call Option Values 5.0625 5.2479 5.4312
Call Option Values 6.0623 6.2479 6.4312
 
Strike=20.00, Sigma= 0.20, Alpha= 0.00
Call Option Values 0.0001 0.0001 0.0002
Call Option Values 0.0816 0.3316 0.5748
Call Option Values 1.0818 1.3308 1.5748
 
Strike=25.00, Sigma= 0.20, Alpha= 0.00
Call Option Values 0.0000 -0.0000 -0.0000
Call Option Values 0.0000 -0.0000 -0.0000
Call Option Values -0.0000 0.0000 -0.0000
 
Strike=15.00, Sigma= 0.30, Alpha= 0.00
Call Option Values 4.0624 4.2479 4.4311
Call Option Values 5.0625 5.2479 5.4311
Call Option Values 6.0623 6.2479 6.4311
 
Strike=20.00, Sigma= 0.30, Alpha= 0.00
Call Option Values 0.0000 -0.0000 0.0029
Call Option Values 0.0895 0.3326 0.5753
Call Option Values 1.0826 1.3306 1.5749
 
Strike=25.00, Sigma= 0.30, Alpha= 0.00
Call Option Values 0.0000 -0.0000 -0.0000
Call Option Values 0.0000 -0.0000 -0.0000
Call Option Values 0.0000 -0.0000 -0.0000
 
Strike=15.00, Sigma= 0.40, Alpha= 0.00
Call Option Values 4.0624 4.2479 4.4312
Call Option Values 5.0624 5.2479 5.4312
Call Option Values 6.0624 6.2479 6.4312
 
Strike=20.00, Sigma= 0.40, Alpha= 0.00
Call Option Values -0.0000 0.0001 0.0111
Call Option Values 0.0985 0.3383 0.5781
Call Option Values 1.0830 1.3306 1.5749
 
Strike=25.00, Sigma= 0.40, Alpha= 0.00
Call Option Values 0.0000 0.0000 -0.0000
Call Option Values 0.0000 -0.0000 -0.0000
Call Option Values 0.0000 -0.0000 -0.0000
Example 3
This example evaluates the price of a European Option using two payoff strategies: Cash-or-Nothing and Vertical Spread. In the first case the payoff function is
The value B is regarded as the bet on the asset price, see Wilmott et al. (1995, p. 39-40). The second case has the payoff function
Both problems use the same boundary conditions. Each case requires a separate integration of the Black-Scholes differential equation, but only the payoff function evaluation differs in each case. The sets of parameters in the computation are:
1. Strike and bet prices K1={10.0}, K2 = {15.0}, and B = {2.0}.
2. Volatility σ = {0.4}.
3. Times until expiration = {1/4, 1/2}.
4. Interest rate r = 0.1.
5. xmin = 0, xmax = 30.
6. nx =61, n = 3 × nx = 183.
 
#include <imsl.h>
#include <stdio.h>
#include <math.h>
 
#define max(A,B) ((A) >= (B) ? (A) : (B))
#define NXGRID 61
#define NTGRID 2
#define NV 12
 
void fkcfiv_call(float, float, int *, float *, void *);
void fkbcp_call(int, float, int *, float[], void *);
 
int main()
{
int i;
/* The strike price */
float KS1 = 10.0;
/* The spread value */
float KS2 = 15.0;
/* The Bet for the Cash-or-Nothing Call */
float bet = 2.0;
/* The sigma value */
float sigma = 0.4;
 
/* Time values for the options */
int nt = 2;
float time[] = {0.25, 0.5};
/* Values of the underlying where evaluations are made */
float xs[NV];
/* Value of the interest rate and continuous dividend */
float r = 0.1, dividend=0.0;
/* Values of the min and max underlying values modeled */
float x_min=0.0, x_max=30.0;
/* Define parameters for the integration step. */
int nint=NXGRID-1, n=3*NXGRID;
float xgrid[NXGRID];
float yb[(NTGRID+1)*3*NXGRID], ybprime[(NTGRID+1)*3*NXGRID];
float yv[(NTGRID+1)*3*NXGRID], yvprime[(NTGRID+1)*3*NXGRID];
float fb[NTGRID*NV], fv[NTGRID*NV];
float dx;
/* Number of left/right boundary conditions. */
int nlbcd = 3, nrbcd = 3;
 
/* Structure for the evaluation functions. */
struct {
int idope[1];
float rdope[7];
} usr_data;
/* Define an equally-spaced grid of points for the underlying
price */
dx = (x_max-x_min)/((float)(nint));
xgrid[0]=x_min;
xgrid[NXGRID-1]=x_max;
for (i=2; i<=NXGRID-1; i++)
xgrid[i-1] = xgrid[i-2]+dx;
 
for (i=1; i<=NV; i++)
xs[i-1] = 2.0+(i-1)*2.0;
usr_data.rdope[0] = KS1;
usr_data.rdope[1] = bet;
usr_data.rdope[2] = KS2;
usr_data.rdope[3] = x_max;
usr_data.rdope[4] = sigma;
usr_data.rdope[5] = r;
usr_data.rdope[6] = dividend;
/* Flag the difference in payoff functions */
/* 1 for the Bet, 2 for the Vertical Spread */
usr_data.idope[0] = 1;
imsl_f_feynman_kac(NXGRID, NTGRID, nlbcd, nrbcd, xgrid, time,
NULL, NULL, yb, ybprime,
IMSL_FCN_FKCFIV_W_DATA, fkcfiv_call, &usr_data,
IMSL_FCN_FKBCP_W_DATA, fkbcp_call, &usr_data,
0);
 
usr_data.idope[0] = 2;
imsl_f_feynman_kac(NXGRID, NTGRID, nlbcd, nrbcd, xgrid, time,
NULL, NULL, yv, yvprime,
IMSL_FCN_FKCFIV_W_DATA, fkcfiv_call, &usr_data,
IMSL_FCN_FKBCP_W_DATA, fkbcp_call, &usr_data,
0);
 
/* Evaluate solutions at vector of points XS(:), at each time value
prior to expiration. */
for (i=0; i<NTGRID; i++)
{
imsl_f_feynman_kac_evaluate (NV, NXGRID, xgrid, xs, &yb[(i+1)*n],
IMSL_RETURN_USER, &fb[i*NV], 0);
imsl_f_feynman_kac_evaluate (NV, NXGRID, xgrid, xs, &yv[(i+1)*n],
IMSL_RETURN_USER, &fv[i*NV], 0);
}
 
printf("%2sEuropean Option Value for A Bet\n", " ");
printf("%3sand a Vertical Spread, 3 and 6 Months Prior to Expiry\n",
" ");
printf("%5sNumber of equally spaced spline knots:%4d\n", " ",
NXGRID);
printf("%5sNumber of unknowns:%4d\n", " ", n);
printf("%5sStrike=%5.2f, Sigma=%5.2f, Interest Rate=%5.2f\n",
" ", KS1, sigma, r);
printf("%5sBet=%5.2f, Spread Value=%5.2f\n\n", " ", bet, KS2);
printf("%17s%18s%18s\n", "Underlying", "A Bet", "Vertical Spread");
for (i=0; i<NV; i++)
printf("%8s%9.4f%9.4f%9.4f%9.4f%9.4f\n", " ", xs[i], fb[i],
fb[i+NV], fv[i], fv[i+NV]);
}
 
 
/* These functions define the coefficients, payoff, boundary conditions
and forcing term for American and European Options. */
 
void fkcfiv_call(float x, float tx, int *iflag, float *value,
void *data_ptr)
{
float sigma, strike_price, interest_rate;
float spread, bet, dividend, zero=0.0;
float *data_real = NULL;
int *data_int = NULL;
 
struct struct_data {
int idope[1];
float rdope[7];
};
struct struct_data *data = NULL;
 
data = data_ptr;
data_real = data->rdope;
data_int = data->idope;
strike_price = data_real[0];
bet = data_real[1];
spread = data_real[2];
sigma = data_real[4];
interest_rate = data_real[5];
dividend = data_real[6];
 
switch (*iflag)
{
case 0:
/* The payoff function - Use flag passed to decide which */
switch (data_int[0])
{
case 1:
/* After reaching the strike price the payoff jumps
from zero to the bet value. */
*value = zero;
if (x > strike_price) *value = bet;
break;
case 2:
/* Function is zero up to strike price.
Then linear between strike price and spread.
Then has constant value Spread-Strike Price after
the value Spread. */
*value = max(x-strike_price, zero)-max(x-spread, zero);
break;
}
break;
case -1:
/* The coefficient derivative d sigma/ dx */
*value = sigma;
break;
case 1:
/* The coefficient sigma(x) */
*value = sigma*x;
break;
case 2:
/* The coefficient mu(x) */
*value = (interest_rate - dividend)*x;
break;
case 3:
/* The coefficient kappa(x) */
*value = interest_rate;
break;
}
/* Note that there is no time dependence */
*iflag = 0;
 
data_real = NULL;
data_int = NULL;
data = NULL;
 
return;
}
 
void fkbcp_call(int nbc, float tx, int *iflag, float val[],
void *data_ptr)
{
float strike_price, spread, bet, interest_rate, df;
int *data_int = NULL;
float *data_real = NULL;
 
struct struct_data {
int idope[1];
float rdope[7];
};
 
struct struct_data *data = NULL;
 
data = data_ptr;
data_int = data->idope;
data_real = data->rdope;
strike_price = data_real[0];
bet = data_real[1];
spread = data_real[2];
interest_rate = data_real[5];
switch (*iflag)
{
case 1:
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = 0.0; val[4] = 0.0; val[5] = 1.0;
val[6] = 0.0; val[7] = 0.0; val[8] = 0.0;
val[9] = 0.0; val[10] = 1.0; val[11] = 0.0;
/* Note no time dependence in case (1) for IFLAG */
*iflag = 0;
break;
case 2:
/* This is the discount factor using the risk-free
interest rate */
df = exp(interest_rate*tx);
/* Use flag passed to decide on boundary condition */
switch (data_int[0])
{
case 1:
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = bet*df;
break;
case 2:
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = (spread-strike_price)*df;
break;
}
val[4] = 0.0; val[5] = 1.0; val[6] = 0.0;
val[7] = 0.0; val[8] = 0.0; val[9] = 0.0;
val[10] = 1.0; val[11] = 0.0;
break;
}
data_real = NULL;
data_int = NULL;
data = NULL;
 
return;
}
Output
 
European Option Value for A Bet
and a Vertical Spread, 3 and 6 Months Prior to Expiry
Number of equally spaced spline knots: 61
Number of unknowns: 183
Strike=10.00, Sigma= 0.40, Interest Rate= 0.10
Bet= 2.00, Spread Value=15.00
 
Underlying A Bet Vertical Spread
2.0000 0.0000 0.0000 0.0000 0.0000
4.0000 0.0000 0.0014 0.0000 0.0006
6.0000 0.0110 0.0722 0.0039 0.0446
8.0000 0.2690 0.4304 0.1479 0.3831
10.0000 0.9948 0.9781 0.8909 1.1927
12.0000 1.6095 1.4287 2.1911 2.2274
14.0000 1.8654 1.6924 3.4255 3.1551
16.0000 1.9337 1.8177 4.2264 3.8263
18.0000 1.9476 1.8700 4.6264 4.2492
20.0000 1.9501 1.8903 4.7911 4.4922
22.0000 1.9505 1.8979 4.8497 4.6232
24.0000 1.9506 1.9007 4.8685 4.6909
Example 4
This example evaluates the price of a convertible bond. Here, convertibility means that the bond may, at any time of the holder’s choosing, be converted to a multiple of the specified asset. Thus a convertible bond with price returns an amount at time unless the owner has converted the bond to units of the asset at some time prior to . This definition, the differential equation and boundary conditions are given in Chapter 18 of Wilmott et al. (1996). Using a constant interest rate and volatility factor, the parameters and boundary conditions are:
1. Bond face value , conversion factor
2. Volatility
3. Times until expiration
4. Interest rate , dividend
5.
6.
7. Boundary conditions
8. Terminal data
9. Constraint for bond holder
Note that the error tolerance is set to a pure absolute error of value . The free boundary constraint is achieved by use of a non-linear forcing term in the function fkforce_cbond. The terminal conditions are provided with the user function fkinit_cbond.
 
#include <imsl.h>
#include <stdio.h>
#include <math.h>
 
#define max(A,B) ((A) >= (B) ? (A) : (B))
#define NXGRID 61
#define NTGRID 2
#define NV 13
 
void fkcfiv_cbond(float, float, int *, float *, void *);
void fkbcp_cbond(int, float, int *, float[], void *);
void fkforce_cbond(int, int, int, float[], float, float,
float[], float[], float[], float[], float[], void *);
void fkinit_cbond(int, int, float[], float[], float,
float[], float[], float[], float[], void *);
 
int main()
{
int i;
/* Compute value of a Convertible Bond */
 
/* The face value */
float KS = 1.0e0;
 
/* The sigma or volatility value */
float sigma = 0.25e0;
 
/* Time values for the options */
float time[] = { 0.5, 1.0};
 
/* Values of the underlying where evaluation are made */
float xs[NV];
 
/* Value of the interest rate, continuous dividend and factor */
float r = 0.1, dividend=0.02, factor = 1.125;
 
/* Values of the min and max underlying values modeled */
float x_min = 0.0, x_max = 4.0;
 
/* Define parameters for the integration step. */
int nint = NXGRID-1, n=3*NXGRID;
 
float xgrid[NXGRID];
float y[(NTGRID+1)*3*NXGRID], yprime[(NTGRID+1)*3*NXGRID];
float f[(NTGRID+1)*NV], dx;
 
/* Array for user-defined data */
float usr_data[8];
float atol;
 
/* Number of left/right boundary conditions. */
int nlbcd = 3, nrbcd = 3;
 
/*
* Define an equally-spaced grid of points for the
* underlying price
*/
dx = (x_max-x_min)/((float) nint);
xgrid[0] = x_min;
xgrid[NXGRID-1] = x_max;
 
for (i=2; i<=NXGRID-1; i++) xgrid[i-1] = xgrid[i-2] + dx;
for (i=1; i<=NV; i++) xs[i-1] = (i-1)*0.25;
 
/* Pass the data for evaluation */
usr_data[0] = KS;
usr_data[1] = x_max;
usr_data[2] = sigma;
usr_data[3] = r;
usr_data[4] = dividend;
usr_data[5] = factor;
 
/* Use a pure absolute error tolerance for the integration */
atol = 1.0e-3;
usr_data[6] = atol;
 
/* Compute value of convertible bond */
imsl_f_feynman_kac(NXGRID, NTGRID, nlbcd, nrbcd, xgrid, time,
NULL, NULL, y, yprime,
IMSL_FCN_FKCFIV_W_DATA, fkcfiv_cbond, usr_data,
IMSL_FCN_FKBCP_W_DATA, fkbcp_cbond, usr_data,
IMSL_FCN_INIT_W_DATA, fkinit_cbond, usr_data,
IMSL_FCN_FORCE_W_DATA, fkforce_cbond, usr_data,
IMSL_ATOL_RTOL_SCALARS, 1.0e-3, 0.0e0,
0);
 
/*
* Evaluate and display solutions at vector of points XS(:),
* at each time value prior to expiration.
*/
for (i=0; i<=NTGRID; i++)
imsl_f_feynman_kac_evaluate (NV, NXGRID, xgrid, xs, &y[i*n],
IMSL_RETURN_USER, &f[i*NV], 0);
 
printf("%2sConvertible Bond Value, 0+, 6 and 12 Months Prior "
"to Expiry\n", " ");
printf("%5sNumber of equally spaced spline knots:%4d\n", " ",
NXGRID);
printf("%5sNumber of unknowns:%4d\n", " ",n);
printf("%5sStrike=%5.2f, Sigma=%5.2f\n", " ", KS, sigma);
printf("%5sInterest Rate=%5.2f, Dividend=%5.2f, Factor=%6.3f\n\n",
" ", r, dividend, factor);
printf("%15s%18s\n", "Underlying", "Bond Value");
for (i=0; i<NV; i++)
printf("%7s%8.4f%8.4f%8.4f%8.4f\n",
" ", xs[i], f[i], f[i+NV], f[i+2*NV]);
}
 
/*
* These functions define the coefficients, payoff, boundary conditions
* and forcing term.
*/
 
void fkcfiv_cbond(float x, float tx, int *iflag, float *value,
void *data_ptr)
{
float sigma, strike_price, interest_rate;
float dividend, factor, zero=0.0;
float *data = NULL;
 
data = (float *) data_ptr;
strike_price = data[0];
sigma = data[2];
interest_rate = data[3];
dividend = data[4];
factor = data[5];
 
switch(*iflag)
{
case 0:
/* The payoff function - */
*value = max(factor * x, strike_price);
break;
case -1:
/* The coefficient derivative d sigma/ dx */
*value = sigma;
break;
case 1:
/* The coefficient sigma(x) */
*value = sigma*x;
break;
case 2:
/* The coefficient mu(x) */
*value = (interest_rate - dividend) * x;
break;
case 3:
/* The coefficient kappa(x) */
*value = interest_rate;
break;
}
/* Note that there is no time dependence */
*iflag = 0;
}
void fkbcp_cbond(int nbc, float tx, int *iflag, float val[],
void *data_ptr)
{
float interest_rate, strike_price, dp, factor, x_max;
float *data = NULL;
 
data = (float *) data_ptr;
switch (*iflag)
{
case 1:
strike_price = data[0];
interest_rate = data[3];
dp = strike_price * exp(tx*interest_rate);
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = dp; val[4] = 0.0; val[5] = 1.0;
val[6] = 0.0; val[7] = 0.0; val[8] = 0.0;
val[9] = 0.0; val[10] = 1.0; val[11] = 0.0;
break;
case 2:
x_max = data[1];
factor = data[5];
val[0] = 1.0; val[1] = 0.0; val[2] = 0.0;
val[3] = factor*x_max; val[4] = 0.0; val[5] = 1.0;
val[6] = 0.0; val[7] = factor; val[8] = 0.0;
val[9] = 0.0; val[10] = 1.0; val[11] = 0.0;
/* Note no time dependence */
*iflag = 0;
break;
}
return;
}
void fkforce_cbond(int interval, int ndeg, int nxgrid, float y[],
float time, float width, float xlocal[], float qw[],
float u[], float phi[], float dphi[], void *data_ptr)
{
int i, j, k, l;
const int local=6;
 
float yl[6], bf[6];
float value, strike_price, interest_rate, zero=0.e0;
float one=1.0e0, rt, mu, factor;
float *data = NULL;
 
data = (float *) data_ptr;
 
for (i=0; i<local; i++)
{
yl[i] = y[3*interval-3+i];
phi[i] = zero;
}
for (i=0; i<local*local; i++)
dphi[i] = zero;
value = data[6];
strike_price = data[0];
interest_rate = data[3];
factor = data[5];
mu = 2.0;
 
/*
* This is the local definition of the forcing term -
* It "forces" the constraint f >= factor*x.
*/
for (j=1; j<=local; j++)
for (l=1; l<=ndeg; l++)
{
bf[0] = u[(l-1)];
bf[1] = u[(l-1)+ndeg];
bf[2] = u[(l-1)+2*ndeg];
bf[3] = u[(l-1)+6*ndeg];
bf[4] = u[(l-1)+7*ndeg];
bf[5] = u[(l-1)+8*ndeg];
rt = 0.0;
for (k=0; k<local; k++)
rt += yl[k]*bf[k];
 
rt = value/(rt + value - factor * xlocal[l-1]);
phi[j-1] += qw[l-1] * bf[j-1] * pow(rt,mu);
}
for (i=0; i<local; i++)
phi[i] = -phi[i]*width*factor*strike_price;
 
/*
* This is the local derivative matrix for the forcing term
*/
for (j=1; j<=local; j++)
for (i=1; i<=local; i++)
for (l=1; l<=ndeg; l++)
{
bf[0] = u[(l-1)];
bf[1] = u[(l-1)+ndeg];
bf[2] = u[(l-1)+2*ndeg];
bf[3] = u[(l-1)+6*ndeg];
bf[4] = u[(l-1)+7*ndeg];
bf[5] = u[(l-1)+8*ndeg];
 
rt = 0.0;
for (k=0; k<local; k++) rt += yl[k]*bf[k];
 
rt = one/(rt + value - factor * xlocal[l-1]);
dphi[i-1+(j-1)*local] += qw[l-1] * bf[i-1] *
bf[j-1] * pow(value*rt, mu) * rt;
}
for (i=0; i<local*local; i++)
dphi[i] = -mu * dphi[i] * width * factor * strike_price;
return;
}
 
void fkinit_cbond(int nxgrid, int ntgrid, float xgrid[], float tgrid[],
float time, float yprime[], float y[], float atol[],
float rtol[], void *data_ptr)
{
int i;
float *data = NULL;
 
data = (float *) data_ptr;
 
if (time == 0.0)
{
/* Set initial data precisely. */
for (i=1; i<=nxgrid; i++)
{
if (xgrid[i-1] * data[5] < data[0])
{
y[3*i-3] = data[0];
y[3*i-2] = 0.0;
y[3*i-1] = 0.0;
}
else
{
y[3*i-3] = xgrid[i-1] * data[5];
y[3*i-2] = data[5];
y[3*i-1] = 0.0;
}
}
}
return;
}
Output
 
Convertible Bond Value, 0+, 6 and 12 Months Prior to Expiry
Number of equally spaced spline knots: 61
Number of unknowns: 183
Strike= 1.00, Sigma= 0.25
Interest Rate= 0.10, Dividend= 0.02, Factor= 1.125
 
Underlying Bond Value
0.0000 1.0000 0.9512 0.9048
0.2500 1.0000 0.9512 0.9049
0.5000 1.0000 0.9513 0.9065
0.7500 1.0000 0.9737 0.9605
1.0000 1.1250 1.1416 1.1464
1.2500 1.4063 1.4117 1.4121
1.5000 1.6875 1.6922 1.6922
1.7500 1.9688 1.9731 1.9731
2.0000 2.2500 2.2540 2.2540
2.2500 2.5312 2.5349 2.5349
2.5000 2.8125 2.8160 2.8160
2.7500 3.0938 3.0970 3.0970
3.0000 3.3750 3.3781 3.3781
Fatal Errors
IMSL_STOP_USER_FCN
Request from user supplied function to stop algorithm. User flag = "#".