package com.imsl.test.example.math; import com.imsl.math.*; import com.imsl.stat.*; import java.util.*; /** *

* Solves for the "Greeks" of mathematical finance. *

*

* In this example, the FeynmanKac class is used to calculate the * so-called "Greeks" of mathematical finance. The "Greeks" are defined in terms * of various derivatives of Feynman-Kac solutions and have special * interpretations in 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 Feynman-Kac (FK) * solution to the diffusion model for call options as described in example 2 * {@link FeynmanKacEx2}, for the Black-Scholes case (\( \alpha \rightarrow * 2\)). The second method calculates the Greeks using the closed-form * Black-Scholes (BS) evaluations, which can be found here: * Wikipedia: * The Greeks. *

* *

* Example 5 calculates FK and BS solutions \(V(S, t)\) 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 \(V(S, t)\) 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 \(\sigma\). 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 \(f(\sigma + \epsilon)\) truncated to * m+1 terms (to allow an m-degree polynomial fit of m+1 * data points):

* *

* $$ f(\sigma + \epsilon)\;=\;\sum_{n = 0}^m {\frac{f^{(n)}(\sigma)}{n!} * \epsilon^n} $$

* *

* we are able to derive the following parabolic (3 point) estimation of first * and second derivatives \(f^{(1)}(\sigma)\) and \(f^{(2)}(\sigma)\) in terms * of the three values: \(f(\sigma - \epsilon)\), \(f(\sigma)\), and \(f(\sigma * + \epsilon)\), where \(\epsilon = \epsilon_{frac} \sigma\) and \(0 \lt * \epsilon_{frac} \lt\lt 1\):

* *

* $$ f^{(1)}(\sigma)\; \equiv \;\frac{\partial f(\sigma)}{\partial * \sigma}\;\approx\; f^{[1]}(\sigma, \epsilon)\; \equiv \;\frac{f(\sigma + * \epsilon)-f(\sigma - \epsilon)}{2 \epsilon} $$

* *

* $$ f^{(2)}(\sigma)\; \equiv \;\frac{\partial^2 f(\sigma)}{\partial * \sigma^2}\;\approx\; f^{[2]}(\sigma, \epsilon)\; \equiv \;\frac{f(\sigma + * \epsilon)+f(\sigma - \epsilon)-2f(\sigma)}{\epsilon^2} $$

* *

* Similarly, the quartic (5 point) estimation of \(f^{(1)}(\sigma)\) and * \(f^{(2)}(\sigma)\) in terms of \(f(\sigma - 2\epsilon)\), \(f(\sigma - * \epsilon)\), \(f(\sigma)\), \(f(\sigma + \epsilon)\), and \(f(\sigma + * 2\epsilon)\) is:

* *

* $$ f^{(1)}(\sigma)\;\approx\;\frac{4}{3} f^{[1]}(\sigma, \epsilon) \;-\; * \frac{1}{3}f^{[1]}(\sigma, 2\epsilon) $$

* *

* $$ f^{(2)}(\sigma)\;\approx\;\frac{4}{3} f^{[2]}(\sigma, \epsilon) \;-\; * \frac{1}{3}f^{[2]}(\sigma, 2\epsilon) $$

* *

* 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 Hanson, R. (2008), 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 the 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 \(\{S,\; f^{(2)}(S)\}\) and the second and * third solutions for solution grid and evaluation point sets \(\{S+\epsilon,\; * f^{(2)}(S+\epsilon)\}\) and \(\{S-\epsilon,\; f^{(2)}(S-\epsilon)\}\), where * the solution grid and evaluation point sets are shifted up and down by * \(\epsilon\). In this example, \(\epsilon\) is set to \(\epsilon_{frac} * \bar{S}\), where \(\bar{S}\) is the average value of S over the range * of grid values and \(0 \lt \epsilon_{frac} \lt\lt 1\). The third derivative * solution can then be obtained using the parabolic estimate:

* *

* $$ f^{(3)}(S)\;=\;\frac{\partial f^{(2)}(S)}{\partial * S}\;\approx\;\frac{f^{(2)}(S + \epsilon)-f^{(2)}(S - \epsilon)}{2 \epsilon} * $$

* *

* This procedure is implemented in this example to calculate the Greek * Speed. (For comparison purposes, Speed * is also calculated directly in this example 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 method * setInitialStepsize), by choosing more closely spaced S * and t grid points (by adjusting method * computeCoefficients input parameter arrays xGrid * and tGrid), and by adjusting \(\epsilon_{frac}\) so that the * central differences used to calculate the derivatives are not too small to * compromise accuracy.

* * * @see Code * @see Output * */ 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, xMax = 60.0; // Value of the interest rate and continuous dividend private static double r = 0.05, dividend = 0.0; // Define parameters for the integration step. private static int nXGgrid = 121, nTGrid = 3, nv = 3; private static int nint = nXGgrid - 1, n = 3 * nXGgrid; private static double[] xGrid = new double[nXGgrid]; private static double dx; // Time dependency private static boolean[] timeDependence = new boolean[7]; // Number of left/right boundary conditions private static int nlbcd = 3, 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. * Math.PI); private static String greekName[] = { " Value", " Delta", " Gamma", " Theta", " Charm", " Color", " Vega", " Volga", " Vanna", " Rho", " Speed", "Speed2"}; // Time values for the options private static int nt = 3; 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[]) throws Exception { // 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; } System.out.printf(" Constant Elasticity of Variance Model" + " for Vanilla Call Option%n"); System.out.printf(Locale.ENGLISH, "%7sInterest Rate:%7.3f Continuous Dividend:%7.3f%n", " ", r, dividend); System.out.printf(Locale.ENGLISH, "%7sMinimum and Maximum Prices of Underlying:%7.2f%7.2f%n", " ", xMin, xMax); System.out.printf("%7sNumber of equally spaced spline knots:%4d%n", " ", nXGgrid - 1); System.out.printf("%7sNumber of unknowns:%4d%n%n", " ", n); System.out.printf(Locale.ENGLISH, "%7sTime in Years Prior to Expiration: %7.4f%7.4f%7.4f%n", " ", tGrid[0], tGrid[1], tGrid[2]); // tGrid[] = tau == T - t System.out.printf(Locale.ENGLISH, "%7sOption valued at Underlying Prices:%7.2f%7.2f%7.2f%n%n", " ", evaluationPoints[0], evaluationPoints[1], evaluationPoints[2]); /* * 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) { System.out.printf(Locale.ENGLISH, " 3 point (parabolic) estimate of " + "parameter derivatives;%n"); } else { System.out.printf(Locale.ENGLISH, " 5 point (quartic) estimate of " + "parameter derivatives%n"); } System.out.printf(Locale.ENGLISH, " epsfrac = %11.8f%n", 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); } } System.out.println(); for (int ig = 0; ig < 12; ig++) { reavg[ig] /= irect[ig]; System.out.printf(Locale.ENGLISH, "%n Greek: " + greekName[ig] + "; avg rel err: %15.12f" + "; max rel err: %15.12f", reavg[ig], rex[ig]); } System.out.println(); } // end main private static void calculateGreeks(int volatility, int strikePrice, int iquart) throws Exception { int i1 = 1; int nt = 3; if ((volatility == 1) && (strikePrice == 1)) { System.out.printf(Locale.ENGLISH, "%n" + " Strike=%5.2f, Sigma=" + "%5.2f, Alpha=%5.2f:", strikePrices[strikePrice - 1], sigma[volatility - 1], alpha[i1 - 1]); System.out.printf(Locale.ENGLISH, "%n" + " years to expiration: " + " %7.4f %7.4f %7.4f" + "%n", tGrid[0], tGrid[1], tGrid[2]); } /* 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 methods. 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][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[][] optionCoefficients = new double[tGrid.length + 1][3 * xGrid.length]; if (iTDeriv == 0) { optionCoefficients = callOption. getSplineCoefficients(); } else { optionCoefficients = callOption. getSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { // FK option values for tau = tGrid[i]: splineValuesOption[i] = callOption.getSplineValue( evaluationPoints, optionCoefficients[i + 1], iSDeriv); } } if (iSigDeriv > 0 || iRDeriv > 0 || ispeed == 1) { System.arraycopy(xGrid, 0, xGridP, 0, nXGgrid); System.arraycopy(xGrid, 0, xGridM, 0, nXGgrid); System.arraycopy(evaluationPoints, 0, evaluationPointsP, 0, nv); System.arraycopy(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. + epsfrac); } else if (iRDeriv > 0) { userData[4] = r * (1. + 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 = new double[tGrid.length + 1][3 * xGrid.length]; if (iTDeriv == 0) { optionCoefficientsP = callOptionP. getSplineCoefficients(); } else { optionCoefficientsP = callOptionP. getSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { // FK option values for tau = tGrid[i]: splineValuesOptionP[i] = callOptionP.getSplineValue( evaluationPointsP, optionCoefficientsP[i + 1], 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. - epsfrac); } else { userData[4] = r * (1. - 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 = new double[tGrid.length + 1][3 * xGrid.length]; if (iTDeriv == 0) { optionCoefficientsM = callOptionM. getSplineCoefficients(); } else { optionCoefficientsM = callOptionM. getSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { // FK option values for tau = tGrid[i]: splineValuesOptionM[i] = callOptionM. getSplineValue( evaluationPointsM, optionCoefficientsM[i + 1], iSDeriv); } if (iquart == 1) { System.arraycopy(xGrid, 0, xGridPP, 0, nXGgrid); System.arraycopy(xGrid, 0, xGridMM, 0, nXGgrid); System.arraycopy(evaluationPoints, 0, evaluationPointsPP, 0, nv); System.arraycopy(evaluationPoints, 0, evaluationPointsMM, 0, nv); if (ispeed == 1) { for (int i = 0; i < nXGgrid; i++) { xGridPP[i] = xGrid[i] + 2. * dx2; xGridMM[i] = xGrid[i] - 2. * dx2; } for (int i = 0; i < nv; i++) { evaluationPointsPP[i] = evaluationPoints[i] + 2. * dx2; evaluationPointsMM[i] = evaluationPoints[i] - 2. * dx2; } xMaxPP = xMax + 2. * dx2; xMaxMM = xMax - 2. * dx2; } userData[1] = xMaxPP; if (iSigDeriv > 0) { // calculate spline values for // sigmaPP = sigma[i2-1] // *(1. + 2.*epsfrac): userData[2] = sigma[volatility - 1] * (1. + 2. * epsfrac); } else if (iRDeriv > 0) { // calculate spline values for // rPP = r*(1. + 2.*epsfrac): userData[4] = r * (1. + 2. * 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[][] optionCoefficientsPP = new double[tGrid.length + 1][3 * xGrid.length]; if (iTDeriv == 0) { optionCoefficientsPP = callOptionPP. getSplineCoefficients(); } else { optionCoefficientsPP = callOptionPP. getSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { // FK option values for tau = tGrid[i]: splineValuesOptionPP[i] = callOptionPP.getSplineValue( evaluationPointsPP, optionCoefficientsPP[i + 1], 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. - 2. * epsfrac); } else if (iRDeriv > 0) { userData[4] = r * (1. - 2. * 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[][] optionCoefficientsMM = new double[tGrid.length + 1][3 * xGrid.length]; if (iTDeriv == 0) { optionCoefficientsMM = callOptionMM. getSplineCoefficients(); } else { optionCoefficientsMM = callOptionMM. getSplineCoefficientsPrime(); } for (int i = 0; i < nTGrid; i++) { // FK option values for tau = tGrid[i]: splineValuesOptionMM[i] = callOptionMM.getSplineValue( evaluationPointsMM, optionCoefficientsMM[i + 1], iSDeriv); } } } if (iSigDeriv == 1 || iRDeriv == 1 || ispeed == 1) { double eps = 0., f11 = 0., f12 = 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. * eps); if (iquart == 0) { splineValuesOption[i][j] = f11; } else { f12 = (splineValuesOptionPP[i][j] - splineValuesOptionMM[i][j]) / (4. * eps); splineValuesOption[i][j] = (4. * f11 - f12) / 3.; } } } } if (iSigDeriv == 2) { double eps = sigma[volatility - 1] * epsfrac; double f21 = 0.; double f22 = 0.; for (int i = 0; i < nTGrid; i++) { for (int j = 0; j < nv; j++) { f21 = (splineValuesOptionP[i][j] + splineValuesOptionM[i][j] - 2. * splineValuesOption[i][j]) / (eps * eps); if (iquart == 0) { splineValuesOption[i][j] = f21; } else { f22 = (splineValuesOptionPP[i][j] + splineValuesOptionMM[i][j] - 2. * splineValuesOption[i][j]) / (4. * eps * eps); splineValuesOption[i][j] = (4. * f21 - f22) / 3.; } } } } // Evaluate BS solution at vector evaluationPoints, // at each time value prior to expiration. double[][] BSValuesOption = new double[nTGrid][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. - 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. + 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. + 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. * dividend * tau + 1. + d1 * (2. * (r - dividend) * tau - d2 * sqrt_sigsqtau) / sqrt_sigsqtau) / (2. * S * tau * sqrt_sigsqtau); } } } } double relerrmax = 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], 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++) { System.out.printf(" underlying price:" + " %4.1f; 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; } System.out.printf(Locale.ENGLISH, "%13.10f ", sVo); } System.out.println(); System.out.printf(" " + " BS " + greekName[gNi] + ": "); for (int j = 0; j < nt; j++) { System.out.printf(Locale.ENGLISH, "%13.10f ", BSValuesOption[j][i]); } System.out.println(); } } } } } } } } static class MyCoefficients implements FeynmanKac.PdeCoefficients { final double zero = 0.0, half = 0.5; private double sigma, strikePrice, interestRate; private 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; } } static class MyBoundaries implements FeynmanKac.Boundaries { private 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; } 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; } public double terminal(double x) { double zero = 0.0; // The payoff function double value = Math.max(x - strikePrice, zero); return value; } } }