In this example, the Feynman-Kac (FK) class is used to solve for the Greeks, i.e. various derivatives of FK solutions applicable to the pricing of options and related financial derivatives. In order to illustrate and verify these calculations, the Greeks are calculated by two methods. The first method involves the FK solution to the diffusion model for call options given in Example 2 for the Black-Scholes (BS) case, i.e. . The second method calculates the Greeks using the closed-form BS evaluations which can be found at http://en.wikipedia.org/wiki/The_Greeks.
This example calculates FK and BS solutions to the BS problem and the following Greeks:
Intrinsic Greeks
include derivatives involving only S
and t
, the intrinsic FK arguments. In the above list, Value, Delta, Gamma, Theta, Charm, Color,
and Speed
are all intrinsic Greeks. As is discussed in Hanson, R. (2008) Integrating Feynman-Kac equations Using Hermite Quintic Finite Elements
, the expansion of the FK solution function in terms of quintic polynomial functions defined on S
-grid subintervals and subject to continuity constraints in derivatives 0, 1, and 2 across the boundaries of these subintervals allows Value, Delta, Gamma, Theta, Charm,
and Color
to be calculated directly by the FK class methods GetSplineCoefficients
, GetSplineCoefficientsPrime
, and GetSplineValue
.
Non-intrinsic Greeks are derivatives of V involving FK parameters other than the intrinsic arguments S and t , such as r and . Non-intrinsic Greeks in the above list include Rho, Vega, Volga, and Vanna . In order to calculate non-intrinsic Greek (parameter) derivatives or intrinsic Greek S -derivatives beyond the second (such as Speed ) or t -derivatives beyond the first, the entire FK solution must be calculated 3 times (for a parabolic fit) or five times (for a quartic fit), at the point where the derivative is to be evaluated and at nearby points located symmetrically on either side.
Using a Taylor series expansion of truncated to m +1 terms (to allow an m -degree polynomial fit of m +1 data points):
we are able to derive the following parabolic (3 point) estimation of first and second derivatives and in terms of the three values: , , and , where and :
Similarly, the quartic (5 point) estimation of and in terms of , , , , and is:
For our example, the quartic estimate does not appear to be significantly better than the parabolic estimate, so we have produced only parabolic estimates by setting variable iquart
to 0. The user may try the example with the quartic estimate simply by setting iquart
to 1.
As is pointed out in Integrating Feynman-Kac equations Using Hermite Quintic Finite Elements
, the quintic polynomial expansion function used by Feynman-Kac only allows for continuous derivatives through the second derivative. While up to fifth derivatives can be calculated from the quintic expansion (indeed class FeynmanKac
method GetSplineValue
will allow the third derivative to be calculated by setting parameter ideriv
to 3
, as is done in this example), the accuracy is compromised by the inherent lack of continuity across grid points (i.e. subinterval boundaries).
The accurate second derivatives in S
returned by FK method GetSplineValue
can be leveraged into a third derivative estimate by calculating three FK second derivative solutions, the first solution for grid and evaluation point sets and the second and third solutions for solution grid and evaluation point sets and , where the solution grid and evaluation point sets are shifted up and down by . In this example, is set to , where is the average value of S
over the range of grid values and . The third derivative solution can then be obtained using the parabolic estimate:
This procedure is implemented in the current example to calculate the Greek Speed
. (For comparison purposes, Speed
is also calculated directly
by setting the GetSplineValue
input S derivative parameter iSDeriv
to 3
. The output from this direct calculation is called "Speed2
".)
The average and maximum relative errors (defined as the absolute value of the difference between the BS and FK values divided by the BS value) for each of the Greeks is given at the end of the output. (These relative error statistics are given for nine combinations of Strike Price and Volatility, but only one of the nine combinations is actually printed in the output.) Both intrinsic and non-intrinsic Greeks have good accuracy (average relative error is in the range 0.01 -- 0.0001) except for Volga
, which has an average relative error of about 0.05. This is probably a result of the fact that Volga
involves differences of differences, which will more quickly erode accuracy than calculations using only one difference to approximate a derivative. Possible ways to improve upon the 2 to 4 significant digits of accuracy achieved in this example include increasing FK integration accuracy by reducing the initial step size (using property InitialStepsize
), by choosing more closely spaced S
and t
grid points (by adjusting method ComputeCoefficients
input parameter arrays xGrid
and tGrid
), and by adjusting so that the central differences used to calculate the derivatives are not too small to compromise accuracy.
using System; using Imsl.Math; using Imsl.Stat; public class FeynmanKacEx5 { private static double[] strikePrices = { 15.0, 20.0, 25.0 }; // The set of sigma values private static double[] sigma = { 0.2, 0.3, 0.4 }; // The set of model diffusion powers: alpha = 2.0 <==> Black Scholes private static double[] alpha = { 2.0, 1.0, 0.0 }; // Time values for the options private static double[] tGrid = { 1.0 / 12.0, 4.0 / 12.0, 7.0 / 12.0 }; // Values of the min and max underlying values modeled private static double xMin = 0.0; private static double xMax = 60.0; // Value of the interest rate and continuous dividend private static double r = 0.05; private static double dividend = 0.0; // Define parameters for the integration step. private static int nXGgrid = 121; private static int nTGrid = 3; private static int nv = 3; private static int nint = nXGgrid - 1; private static int n = 3 * nXGgrid; private static double[] xGrid = new double[nXGgrid]; private static double dx; // Time dependency private static bool[] timeDependence = new bool[7]; // Number of left/right boundary conditions private static int nlbcd = 3; private static int nrbcd = 3; // Values of the underlying price where evaluations are made private static double[] evaluationPoints = { 19.0, 20.0, 21.0 }; private static double epsfrac = .001; //used to calc derivatives private static double dx2 = epsfrac * 0.5 * (xMin + xMax); private static double sqrt2pi = Math.Sqrt(2.0 * Math.PI); private static String[] greekName = new String[]{ " Value", " Delta", " Gamma", " Theta", " Charm", " Color", " Vega", " Volga", " Vanna", " Rho", " Speed", "Speed2" }; // Time values for the options private static double[] rex = new double[greekName.Length]; private static double[] reavg = new double[greekName.Length]; private static int[] irect = new int[greekName.Length]; // Compute Constant Elasticity of Variance Model for Vanilla Call public static void Main(String[] args) { // Define equally-spaced grid of points for the underlying price dx = (xMax - xMin) / ((double)nint); xGrid[0] = xMin; xGrid[nXGgrid - 1] = xMax; for (int i = 2; i <= nXGgrid - 1; i++) { xGrid[i - 1] = xGrid[i - 2] + dx; } Console.Out.WriteLine(" Constant Elasticity of Variance Model" + " for Vanilla Call Option"); Console.Out.WriteLine( " Interest Rate:{0,7:f3} Continuous Dividend:{1,7:f3}", r, dividend); Console.Out.WriteLine( " Minimum and Maximum Prices of Underlying:{0,7:f2}{1,7:f2}", xMin, xMax); Console.Out.WriteLine( " Number of equally spaced spline knots:{0,4:d}", nXGgrid - 1); Console.Out.WriteLine(" Number of unknowns:{0,4:d}", n); Console.Out.WriteLine(); Console.Out.WriteLine( " Time in Years Prior to Expiration: {0,7:f4}{1,7:f4}{2,7:f4}", tGrid[0], tGrid[1], tGrid[2]); // tGrid[] = tau == T - t Console.Out.WriteLine( " Option valued at Underlying Prices:{0,7:f2}{1,7:f2}{2,7:f2}", evaluationPoints[0], evaluationPoints[1], evaluationPoints[2]); Console.Out.WriteLine(); /* * iquart = 0 : derivatives estimated with 3-point fitted parabola * iquart = 1 : derivatives estimated with 5-point fitted quartic * polynomial */ int iquart = 0; if (iquart == 0) { Console.Out.WriteLine( " 3 point (parabolic) estimate of parameter derivatives;"); } else { Console.Out.WriteLine( " 5 point (quartic) estimate of parameter derivatives"); } Console.Out.WriteLine(" epsfrac = {0,11:f8}", epsfrac); //alpha: Black-Scholes for (int i2 = 1; i2 <= 3; i2++) /* Loop over volatility */ for (int i3 = 1; i3 <= 3; i3++) /* Loop over strike price */ calculateGreeks(i2, i3, iquart); Console.Out.WriteLine(); for (int ig = 0; ig < 12; ig++) { reavg[ig] /= irect[ig]; Console.Out.WriteLine(); Console.Out.Write(" Greek: " + greekName[ig] + "; avg rel err: {0,15:f12}; max rel err: {1,15:f12}", reavg[ig], rex[ig]); } Console.Out.WriteLine(); } // end main private static void calculateGreeks( int volatility, int strikePrice, int iquart) { int i1 = 1; int nt = 3; if ((volatility == 1) && (strikePrice == 1)) { Console.Out.WriteLine(); Console.Out.WriteLine(" Strike={0,5:f2}, Sigma=" + "{1,5:f2}, Alpha={2,5:f2}:", strikePrices[strikePrice - 1], sigma[volatility - 1], alpha[i1 - 1]); Console.Out.WriteLine(); Console.Out.WriteLine(" years to expiration: " + " {0,7:f4} {1,7:f4} {2,7:f4}", tGrid[0], tGrid[1], tGrid[2]); Console.Out.WriteLine(); } /* Loop over t derivative index */ for (int iTDeriv = 0; iTDeriv < 2; iTDeriv++) { int iSDerivMax = 4 - iTDeriv; /* Loop over S derivative index */ for (int iSDeriv = 0; iSDeriv < iSDerivMax; iSDeriv++) { int iSigDerivMax = 1; if (iTDeriv == 0) { if (iSDeriv == 0) iSigDerivMax = 3; if (iSDeriv == 1) iSigDerivMax = 2; } //Loop over sigma deriv index for (int iSigDeriv = 0; iSigDeriv < iSigDerivMax; iSigDeriv++) { int iRDerivMax = 1; if (iTDeriv == 0 && iSDeriv == 0 && iSigDeriv == 0) iRDerivMax = 2; // Loop over r derivative index for (int iRDeriv = 0; iRDeriv < iRDerivMax; iRDeriv++) { int ispeedmin = 0; int ispeedmax = 1; if (iTDeriv == 0 && iSDeriv == 2) ispeedmax = 2; if (iTDeriv == 0 && iSDeriv == 3) { ispeedmin = 2; ispeedmax = 3; } // Loop over speed index for (int ispeed = ispeedmin; ispeed < ispeedmax; ispeed++) { // Pass data through into evaluation routines. double[] userData = new double[6]; userData[0] = strikePrices[strikePrice - 1]; userData[1] = xMax; userData[2] = sigma[volatility - 1]; userData[3] = alpha[i1 - 1]; userData[4] = r; userData[5] = dividend; double[][] splineValuesOption = new double[nTGrid][]; for (int i = 0; i < nTGrid; i++) { splineValuesOption[i] = new double[nv]; } double[][] splineValuesOptionP = new double[nTGrid][]; double[][] splineValuesOptionM = new double[nTGrid][]; double[][] splineValuesOptionPP = new double[nTGrid][]; double[][] splineValuesOptionMM = new double[nTGrid][]; double[] xGridP = new double[nXGgrid]; double[] xGridM = new double[nXGgrid]; double[] evaluationPointsP = new double[nv]; double[] evaluationPointsM = new double[nv]; double[] xGridPP = new double[nXGgrid]; double[] xGridMM = new double[nXGgrid]; double[] evaluationPointsPP = new double[nv]; double[] evaluationPointsMM = new double[nv]; double xMaxP = xMax; double xMaxM = xMax; double xMaxPP = xMax, xMaxMM = xMax; // Evaluate FK solution at vector evaluationPoints, at // each time value prior to expiration. if ((iSigDeriv != 1 || iSDeriv == 1 || iRDeriv != 1) && (ispeed == 0 || ispeed == 2)) { FeynmanKac callOption = new FeynmanKac(new MyCoefficients(userData)); //Right boundary condition time-dependent timeDependence[5] = true; callOption.SetTimeDependence(timeDependence); callOption.ComputeCoefficients(nlbcd, nrbcd, new MyBoundaries(userData), xGrid, tGrid); double[] tmpArray = new double[3 * xGrid.Length]; double[,] optionCoefficients; if (iTDeriv == 0) { optionCoefficients = callOption.GetSplineCoefficients(); } else { optionCoefficients = callOption.GetSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < 3 * xGrid.Length; j++) tmpArray[j] = optionCoefficients[i + 1, j]; // FK option values for tau = tGrid[i]: splineValuesOption[i] = callOption.GetSplineValue(evaluationPoints, tmpArray, iSDeriv); } } if (iSigDeriv > 0 || iRDeriv > 0 || ispeed == 1) { Array.Copy(xGrid, 0, xGridP, 0, nXGgrid); Array.Copy(xGrid, 0, xGridM, 0, nXGgrid); Array.Copy(evaluationPoints, 0, evaluationPointsP, 0, nv); Array.Copy(evaluationPoints, 0, evaluationPointsM, 0, nv); if (ispeed == 1) { for (int i = 0; i < nXGgrid; i++) { xGridP[i] = xGrid[i] + dx2; xGridM[i] = xGrid[i] - dx2; } for (int i = 0; i < nv; i++) { evaluationPointsP[i] = evaluationPoints[i] + dx2; evaluationPointsM[i] = evaluationPoints[i] - dx2; } xMaxP = xMax + dx2; xMaxM = xMax - dx2; } userData[1] = xMaxP; // calculate spline values for // sigmaP = sigma[i2-1]*(1. + epsfrac) // or rP = r*(1. + epsfrac) if (iSigDeriv > 0) userData[2] = sigma[volatility - 1] * (1.0 + epsfrac); else if (iRDeriv > 0) userData[4] = r * (1.0 + epsfrac); FeynmanKac callOptionP = new FeynmanKac(new MyCoefficients(userData)); //Right boundary condition time-dependent timeDependence[5] = true; callOptionP.SetTimeDependence(timeDependence); callOptionP.ComputeCoefficients(nlbcd, nrbcd, new MyBoundaries(userData), xGridP, tGrid); double[,] optionCoefficientsP; double[] tmpArray2 = new double[3 * xGrid.Length]; if (iTDeriv == 0) { optionCoefficientsP = callOptionP.GetSplineCoefficients(); } else { optionCoefficientsP = callOptionP.GetSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < 3 * xGrid.Length; j++) tmpArray2[j] = optionCoefficientsP[i + 1, j]; // FK option values for tau = tGrid[i]: splineValuesOptionP[i] = callOptionP.GetSplineValue(evaluationPointsP, tmpArray2, iSDeriv); } userData[1] = xMaxM; // calculate spline values for // sigmaM = sigma[i2-1]*(1. - epsfrac) or // rM = r*(1. - epsfrac): if (iSigDeriv > 0) userData[2] = sigma[volatility - 1] * (1.0 - epsfrac); else userData[4] = r * (1.0 - epsfrac); FeynmanKac callOptionM = new FeynmanKac(new MyCoefficients(userData)); //Right boundary condition time-dependent timeDependence[5] = true; callOptionM.SetTimeDependence(timeDependence); callOptionM.ComputeCoefficients(nlbcd, nrbcd, new MyBoundaries(userData), xGridM, tGrid); double[,] optionCoefficientsM; double[] tmpArray3 = new double[3 * xGrid.Length]; if (iTDeriv == 0) { optionCoefficientsM = callOptionM.GetSplineCoefficients(); } else { optionCoefficientsM = callOptionM.GetSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < 3 * xGrid.Length; j++) tmpArray3[j] = optionCoefficientsM[i + 1, j]; // FK option values for tau = tGrid[i]: splineValuesOptionM[i] = callOptionM.GetSplineValue(evaluationPointsM, tmpArray3, iSDeriv); } if (iquart == 1) { Array.Copy(xGrid, 0, xGridPP, 0, nXGgrid); Array.Copy(xGrid, 0, xGridMM, 0, nXGgrid); Array.Copy(evaluationPoints, 0, evaluationPointsPP, 0, nv); Array.Copy(evaluationPoints, 0, evaluationPointsMM, 0, nv); if (ispeed == 1) { for (int i = 0; i < nXGgrid; i++) { xGridPP[i] = xGrid[i] + 2.0 * dx2; xGridMM[i] = xGrid[i] - 2.0 * dx2; } for (int i = 0; i < nv; i++) { evaluationPointsPP[i] = evaluationPoints[i] + 2.0 * dx2; evaluationPointsMM[i] = evaluationPoints[i] - 2.0 * dx2; } xMaxPP = xMax + 2.0 * dx2; xMaxMM = xMax - 2.0 * dx2; } userData[1] = xMaxPP; if (iSigDeriv > 0) { // calculate spline values for // sigmaPP = sigma[i2-1]*(1. + 2.*epsfrac): userData[2] = sigma[volatility - 1] * (1.0 + 2.0 * epsfrac); } else if (iRDeriv > 0) { // calculate spline values for // rPP = r*(1. + 2.*epsfrac): userData[4] = r * (1.0 + 2.0 * epsfrac); } FeynmanKac callOptionPP = new FeynmanKac(new MyCoefficients(userData)); //Right boundary condition time-dependent timeDependence[5] = true; callOptionPP.SetTimeDependence(timeDependence); callOptionPP.ComputeCoefficients(nlbcd, nrbcd, new MyBoundaries(userData), xGridPP, tGrid); double[] tmpArray4 = new double[3 * xGrid.Length]; double[,] optionCoefficientsPP; if (iTDeriv == 0) { optionCoefficientsPP = callOptionPP.GetSplineCoefficients(); } else { optionCoefficientsPP = callOptionPP.GetSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < 3 * xGrid.Length; j++) tmpArray4[j] = optionCoefficientsPP[i + 1, j]; // FK option values for tau = tGrid[i]: splineValuesOptionPP[i] = callOptionPP.GetSplineValue( evaluationPointsPP, tmpArray4, iSDeriv); } userData[1] = xMaxMM; // calculate spline values for // sigmaMM = sigma[i2-1]*(1. - 2.*epsfrac) or // rMM = r*(1. - 2.*epsfrac) if (iSigDeriv > 0) userData[2] = sigma[volatility - 1] * (1.0 - 2.0 * epsfrac); else if (iRDeriv > 0) userData[4] = r * (1.0 - 2.0 * epsfrac); FeynmanKac callOptionMM = new FeynmanKac(new MyCoefficients(userData)); //Right boundary condition time-dependent timeDependence[5] = true; callOptionMM.SetTimeDependence(timeDependence); callOptionMM.ComputeCoefficients(nlbcd, nrbcd, new MyBoundaries(userData), xGridMM, tGrid); double[] tmpArray5 = new double[3 * xGrid.Length]; double[,] optionCoefficientsMM; if (iTDeriv == 0) { optionCoefficientsMM = callOptionMM.GetSplineCoefficients(); } else { optionCoefficientsMM = callOptionMM.GetSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < 3 * xGrid.Length; j++) tmpArray5[j] = optionCoefficientsMM[i + 1, j]; // FK option values for tau = tGrid[i]: splineValuesOptionMM[i] = callOptionMM.GetSplineValue( evaluationPointsMM,tmpArray5, iSDeriv); } } } if (iSigDeriv == 1 || iRDeriv == 1 || ispeed == 1) { double eps = 0.0, f11 = 0.0, f12 = 0.0; if (iSigDeriv == 1) eps = sigma[volatility - 1] * epsfrac; if (iRDeriv == 1) eps = r * epsfrac; if (ispeed == 1) eps = dx2; for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < nv; j++) { f11 = (splineValuesOptionP[i][j] - splineValuesOptionM[i][j]) / (2.0 * eps); if (iquart == 0) splineValuesOption[i][j] = f11; else { f12 = (splineValuesOptionPP[i][j] - splineValuesOptionMM[i][j]) / (4.0 * eps); splineValuesOption[i][j] = (4.0 * f11 - f12) / 3.0; } } } } if (iSigDeriv == 2) { double eps = sigma[volatility - 1] * epsfrac; double f21 = 0.0; double f22 = 0.0; for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < nv; j++) { f21 = (splineValuesOptionP[i][j] + splineValuesOptionM[i][j] - 2.0 * splineValuesOption[i][j]) / (eps * eps); if (iquart == 0) splineValuesOption[i][j] = f21; else { f22 = (splineValuesOptionPP[i][j] + splineValuesOptionMM[i][j] - 2.0 * splineValuesOption[i][j]) / (4.0 * eps * eps); splineValuesOption[i][j] = (4.0 * f21 - f22) / 3.0; } } } } // Evaluate BS solution at vector evaluationPoints, // at each time value prior to expiration. double[][] BSValuesOption = new double[nTGrid][]; for (int i7 = 0; i7 < nTGrid; i7++) { BSValuesOption[i7] = new double[nv]; } for (int i = 0; i < nTGrid; i++) { /* * Black-Scholes (BS) European call option * value = ValBSEC(S,t) = exp(-q*tau)*S*N01CDF(d1) - * exp(-r*tau)*K*N01CDF(d2), * where: * tau = time to maturity = T - t; * q = annual dividend yield; * r = risk free rate; * K = strike price; * S = stock price; * N01CDF(x) = N(0,1)_CDF(x); * d1 = ( log( S/K ) + * ( r - q + 0.5*sigma**2 )*tau ) / * ( sigma*sqrt(tau) ); * d2 = d1 - sigma*sqrt(tau) */ // BS option values for tau = tGrid[i]: double tau = tGrid[i]; double sigsqtau = Math.Pow(sigma[volatility - 1], 2) * tau; double sqrt_sigsqtau = Math.Sqrt(sigsqtau); double sigsq = sigma[volatility - 1] * sigma[volatility - 1]; for (int j = 0; j < nv; j++) { // Values of the underlying price where // evaluations are made: double S = evaluationPoints[j]; double d1 = (Math.Log(S / strikePrices[strikePrice - 1]) + (r - dividend) * tau + 0.5 * sigsqtau) / sqrt_sigsqtau; double n01pdf_d1 = Math.Exp((-0.5) * d1 * d1) / sqrt2pi; double nu = Math.Exp((-dividend) * tau) * S * n01pdf_d1 * Math.Sqrt(tau); if (iTDeriv == 0) { if (iSDeriv == 0) { double d2 = d1 - sqrt_sigsqtau; if (iSigDeriv == 0) { if (iRDeriv == 0) { BSValuesOption[i][j] = Math.Exp((-dividend) * tau) * S * Cdf.Normal(d1) - Math.Exp((-r) * tau) * strikePrices[strikePrice - 1] * Cdf.Normal(d2); } else { BSValuesOption[i][j] = Math.Exp((-r) * tau) * strikePrices[strikePrice - 1] * tau * Cdf.Normal(d2); } } else if (iSigDeriv == 1) { //greek = Vega BSValuesOption[i][j] = nu; } else if (iSigDeriv == 2) { //greek = Volga BSValuesOption[i][j] = nu * d1 * d2 / sigma[volatility - 1]; } } else if (iSDeriv == 1) { //greek = delta if (iSigDeriv == 0) { BSValuesOption[i][j] = Math.Exp((-dividend) * tau) * Cdf.Normal(d1); } else if (iSigDeriv == 1) { //greek = Vanna BSValuesOption[i][j] = (nu / S) * (1.0 - d1 / sqrt_sigsqtau); } } else if (iSDeriv == 2) { if (ispeed == 0) { //greek = gamma BSValuesOption[i][j] = Math.Exp((-dividend) * tau) * n01pdf_d1 /(S * sqrt_sigsqtau); } else { //greek = speed BSValuesOption[i][j] = (-Math.Exp((-dividend) * tau)) * n01pdf_d1 * (1.0 + d1 / sqrt_sigsqtau) / (S * S * sqrt_sigsqtau); } } else if (iSDeriv == 3 && ispeed == 2) { //greek = speed BSValuesOption[i][j] = (-Math.Exp((-dividend) * tau)) * n01pdf_d1 * (1.0 + d1 / sqrt_sigsqtau) / (S * S * sqrt_sigsqtau); } } else if (iTDeriv == 1) { double d2 = d1 - sqrt_sigsqtau; if (iSDeriv == 0) { //greek = theta BSValuesOption[i][j] = Math.Exp((-dividend) * tau) * S * (dividend * Cdf.Normal(d1) - 0.5 * sigsq * n01pdf_d1 / sqrt_sigsqtau) - r * Math.Exp((-r) * tau) * strikePrices[strikePrice - 1] * Cdf.Normal(d2); } else if (iSDeriv == 1) { //greek = charm BSValuesOption[i][j] = Math.Exp((-dividend) * tau) * ((-dividend) * Cdf.Normal(d1) + n01pdf_d1 * ((r - dividend) * tau - 0.5 * d2 * sqrt_sigsqtau) / (tau * sqrt_sigsqtau)); } else if (iSDeriv == 2) { //greek = color BSValuesOption[i][j] = (-Math.Exp((-dividend) * tau)) * n01pdf_d1 * (2.0 * dividend * tau + 1.0 + d1 * (2.0 * (r - dividend) * tau - d2 * sqrt_sigsqtau) / sqrt_sigsqtau) / (2.0 * S * tau * sqrt_sigsqtau); } } } } double relerrmax = 0.0; int gNi = 3 * iTDeriv + iSDeriv; if (iSigDeriv == 1 && iSDeriv == 0) gNi = 6; //vega if (iSigDeriv == 2 && iSDeriv == 0) gNi = 7; //volga if (iSigDeriv == 1 && iSDeriv == 1) gNi = 8; //vanna if (iRDeriv == 1) gNi = 9; //rho if (ispeed >= 1) gNi = 9 + ispeed; //speed for (int i = 0; i < nv; i++) { for (int j = 0; j < nt; j++) { double sVo = splineValuesOption[j][i]; double BSVo = BSValuesOption[j][i]; //greeks(itd=1) ~ d/dtau = -d/dt for iSDeriv > 0: if (iTDeriv == 1 && iSDeriv > 0) sVo = -sVo; double relerr = Math.Abs((sVo - BSVo) / BSVo); if (relerr > relerrmax) relerrmax = relerr; reavg[gNi] += relerr; irect[gNi] += 1; } } if (relerrmax > rex[gNi]) rex[gNi] = relerrmax; if ((volatility == 1) && (strikePrice == 1)) { for (int i = 0; i < nv; i++) { Console.Out.Write( " underlying price: {0,4:f1}; FK " + greekName[gNi] + ": ", evaluationPoints[i]); for (int j = 0; j < nt; j++) { double sVo = splineValuesOption[j][i]; //greeks(itd=1) ~ d/dtau = -d/dt for isd > 0: if (iTDeriv == 1 && iSDeriv > 0) sVo = -sVo; Console.Out.Write("{0,13:f10} ", sVo); } Console.Out.WriteLine(); Console.Out.Write(" BS " + greekName[gNi] + ": "); for (int j = 0; j < nt; j++) { Console.Out.Write("{0,13:f10} ", BSValuesOption[j][i]); } Console.Out.WriteLine(); } } } } } } } } class MyCoefficients : FeynmanKac.IPdeCoefficients { const double zero = 0.0; const double half = 0.5; double sigma, strikePrice, interestRate; double alpha, dividend; public MyCoefficients(double[] myData) { this.strikePrice = myData[0]; this.sigma = myData[2]; this.alpha = myData[3]; this.interestRate = myData[4]; this.dividend = myData[5]; } // The coefficient sigma(x) public double Sigma(double x, double t) { return (sigma * Math.Pow(x, alpha * half)); } // The coefficient derivative d(sigma) / dx public double SigmaPrime(double x, double t) { return (half * alpha * sigma * Math.Pow(x, alpha * half - 1.0)); } // The coefficient mu(x) public double Mu(double x, double t) { return ((interestRate - dividend) * x); } // The coefficient kappa(x) public double Kappa(double x, double t) { return interestRate; } } class MyBoundaries : FeynmanKac.IBoundaries { double xMax, df, interestRate, strikePrice; public MyBoundaries(double[] myData) { this.strikePrice = myData[0]; this.xMax = myData[1]; this.interestRate = myData[4]; } public void LeftBoundaries(double time, double[,] bndCoeffs) { bndCoeffs[0, 0] = 1.0; bndCoeffs[0, 1] = 0.0; bndCoeffs[0, 2] = 0.0; bndCoeffs[0, 3] = 0.0; bndCoeffs[1, 0] = 0.0; bndCoeffs[1, 1] = 1.0; bndCoeffs[1, 2] = 0.0; bndCoeffs[1, 3] = 0.0; bndCoeffs[2, 0] = 0.0; bndCoeffs[2, 1] = 0.0; bndCoeffs[2, 2] = 1.0; bndCoeffs[2, 3] = 0.0; return; } public void RightBoundaries(double time, double[,] bndCoeffs) { df = Math.Exp(interestRate * time); bndCoeffs[0, 0] = 1.0; bndCoeffs[0, 1] = 0.0; bndCoeffs[0, 2] = 0.0; bndCoeffs[0, 3] = xMax - df * strikePrice; bndCoeffs[1, 0] = 0.0; bndCoeffs[1, 1] = 1.0; bndCoeffs[1, 2] = 0.0; bndCoeffs[1, 3] = 1.0; bndCoeffs[2, 0] = 0.0; bndCoeffs[2, 1] = 0.0; bndCoeffs[2, 2] = 1.0; bndCoeffs[2, 3] = 0.0; return; } public double Terminal(double x) { double zero = 0.0; // The payoff function double val = Math.Max(x - strikePrice, zero); return val; } } }
Constant Elasticity of Variance Model for Vanilla Call Option 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 3 point (parabolic) estimate of parameter derivatives; epsfrac = 0.00100000 Strike=15.00, Sigma= 0.20, Alpha= 2.00: years to expiration: 0.0833 0.3333 0.5833 underlying price: 19.0; FK Value: 4.0623732450 4.2575924184 4.4733805278 BS Value: 4.0623732509 4.2575929678 4.4733814062 underlying price: 20.0; FK Value: 5.0623700127 5.2505145764 5.4492418798 BS Value: 5.0623700120 5.2505143129 5.4492428547 underlying price: 21.0; FK Value: 6.0623699727 6.2485587059 6.4385718831 BS Value: 6.0623699726 6.2485585270 6.4385720688 underlying price: 19.0; FK Rho: 1.2447807022 4.8365676562 8.0884594648 BS Rho: 1.2447806658 4.8365650322 8.0884502627 underlying price: 20.0; FK Rho: 1.2448021850 4.8929216544 8.3041708173 BS Rho: 1.2448021908 4.8929245641 8.3041638392 underlying price: 21.0; FK Rho: 1.2448024992 4.9107294561 8.4114197621 BS Rho: 1.2448024996 4.9107310444 8.4114199038 underlying price: 19.0; FK Vega: 0.0003289870 0.3487168323 1.1153520921 BS Vega: 0.0003295819 0.3487535501 1.1153536190 underlying price: 20.0; FK Vega: 0.0000056652 0.1224632724 0.6032458218 BS Vega: 0.0000056246 0.1224675413 0.6033084039 underlying price: 21.0; FK Vega: 0.0000000623 0.0376974472 0.3028275296 BS Vega: 0.0000000563 0.0376857196 0.3028629419 underlying price: 19.0; FK Volga: 0.0286253687 8.3705172571 16.7944556484 BS Volga: 0.0286064650 8.3691191978 16.8219823169 underlying price: 20.0; FK Volga: 0.0007135181 4.2505026165 12.9315443687 BS Volga: 0.0007186004 4.2519372748 12.9612638820 underlying price: 21.0; FK Volga: 0.0000100364 1.7613083880 8.6626164020 BS Volga: 0.0000097963 1.7617504949 8.6676581034 underlying price: 19.0; FK Delta: 0.9999864098 0.9877532309 0.9652249945 BS Delta: 0.9999863811 0.9877520034 0.9652261127 underlying price: 20.0; FK Delta: 0.9999998142 0.9964646548 0.9842482622 BS Delta: 0.9999998151 0.9964644003 0.9842476147 underlying price: 21.0; FK Delta: 0.9999999983 0.9990831687 0.9932459040 BS Delta: 0.9999999985 0.9990834124 0.9932451927 underlying price: 19.0; FK Vanna: -0.0012418872 -0.3391850563 -0.6388552010 BS Vanna: -0.0012431594 -0.3391932673 -0.6387423326 underlying price: 20.0; FK Vanna: -0.0000244490 -0.1366771953 -0.3945466660 BS Vanna: -0.0000244825 -0.1367114682 -0.3945405194 underlying price: 21.0; FK Vanna: -0.0000002905 -0.0466333335 -0.2187406645 BS Vanna: -0.0000002726 -0.0466323413 -0.2187858632 underlying price: 19.0; FK Gamma: 0.0000543456 0.0144908955 0.0264849216 BS Gamma: 0.0000547782 0.0144911447 0.0264824761 underlying price: 20.0; FK Gamma: 0.0000008315 0.0045912854 0.0129288434 BS Gamma: 0.0000008437 0.0045925328 0.0129280372 underlying price: 21.0; FK Gamma: 0.0000000080 0.0012817012 0.0058860348 BS Gamma: 0.0000000077 0.0012818272 0.0058865489 underlying price: 19.0; FK Speed: -0.0002127758 -0.0157070513 -0.0181086989 BS Speed: -0.0002123854 -0.0156192867 -0.0179536520 underlying price: 20.0; FK Speed: -0.0000037305 -0.0056184183 -0.0098403706 BS Speed: -0.0000037568 -0.0055859333 -0.0097472434 underlying price: 21.0; FK Speed: -0.0000000385 -0.0017185470 -0.0048615664 BS Speed: -0.0000000378 -0.0017082128 -0.0048130214 underlying price: 19.0; FK Speed2: -0.0002310655 -0.0156276977 -0.0179516855 BS Speed2: -0.0002123854 -0.0156192867 -0.0179536520 underlying price: 20.0; FK Speed2: -0.0000043215 -0.0055923924 -0.0097502997 BS Speed2: -0.0000037568 -0.0055859333 -0.0097472434 underlying price: 21.0; FK Speed2: -0.0000000475 -0.0017117661 -0.0048153107 BS Speed2: -0.0000000378 -0.0017082128 -0.0048130214 underlying price: 19.0; FK Theta: -0.7472631891 -0.8301000450 -0.8845209253 BS Theta: -0.7472638978 -0.8301108199 -0.8844992143 underlying price: 20.0; FK Theta: -0.7468881086 -0.7706770630 -0.8152217385 BS Theta: -0.7468880640 -0.7706789470 -0.8152097697 underlying price: 21.0; FK Theta: -0.7468815742 -0.7479185416 -0.7728950748 BS Theta: -0.7468815673 -0.7479153725 -0.7728982104 underlying price: 19.0; FK Charm: -0.0014382828 -0.0879903285 -0.0843323992 BS Charm: -0.0014397520 -0.0879913927 -0.0843403333 underlying price: 20.0; FK Charm: -0.0000284881 -0.0364107814 -0.0547260337 BS Charm: -0.0000285354 -0.0364209077 -0.0547074804 underlying price: 21.0; FK Charm: -0.0000003396 -0.0126436426 -0.0313343015 BS Charm: -0.0000003190 -0.0126437838 -0.0313252716 underlying price: 19.0; FK Color: 0.0051622176 0.0685064195 0.0299871130 BS Color: 0.0051777484 0.0684737183 0.0300398444 underlying price: 20.0; FK Color: 0.0001188761 0.0355826975 0.0274292189 BS Color: 0.0001205713 0.0355891884 0.0274307898 underlying price: 21.0; FK Color: 0.0000015431 0.0143174419 0.0190897160 BS Color: 0.0000015141 0.0143247729 0.0190752019 Greek: Value; avg rel err: 0.000146170676; max rel err: 0.009030693731 Greek: Delta; avg rel err: 0.000035817236; max rel err: 0.001158483024 Greek: Gamma; avg rel err: 0.001088309906; max rel err: 0.044839095787 Greek: Theta; avg rel err: 0.000054196357; max rel err: 0.001412847204 Greek: Charm; avg rel err: 0.001213365580; max rel err: 0.064577964461 Greek: Color; avg rel err: 0.003323775096; max rel err: 0.136355625079 Greek: Vega; avg rel err: 0.001512805322; max rel err: 0.106097327138 Greek: Volga; avg rel err: 0.058535091480; max rel err: 1.639568432709 Greek: Vanna; avg rel err: 0.001061842143; max rel err: 0.065654941418 Greek: Rho; avg rel err: 0.000146868204; max rel err: 0.009438785575 Greek: Speed; avg rel err: 0.007252489626; max rel err: 0.123630427780 Greek: Speed2; avg rel err: 0.008430324710; max rel err: 0.255782408454Link to C# source.