/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.capfloor; import static com.opengamma.strata.basics.currency.Currency.USD; import static com.opengamma.strata.basics.date.DayCounts.ACT_ACT_ISDA; import static com.opengamma.strata.basics.index.IborIndices.USD_LIBOR_3M; import java.time.LocalDate; import java.time.Period; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.List; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.date.BusinessDayAdjustment; import com.opengamma.strata.basics.date.BusinessDayConventions; import com.opengamma.strata.basics.schedule.Frequency; import com.opengamma.strata.basics.schedule.PeriodicSchedule; import com.opengamma.strata.basics.schedule.RollConventions; import com.opengamma.strata.basics.schedule.StubConvention; import com.opengamma.strata.basics.value.ValueSchedule; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.collect.array.DoubleMatrix; import com.opengamma.strata.collect.tuple.Pair; import com.opengamma.strata.market.curve.CurveMetadata; import com.opengamma.strata.market.curve.Curves; import com.opengamma.strata.market.curve.InterpolatedNodalCurve; import com.opengamma.strata.market.curve.interpolator.CurveExtrapolators; import com.opengamma.strata.market.curve.interpolator.CurveInterpolators; import com.opengamma.strata.pricer.rate.ImmutableRatesProvider; import com.opengamma.strata.product.capfloor.IborCapFloorLeg; import com.opengamma.strata.product.capfloor.ResolvedIborCapFloorLeg; import com.opengamma.strata.product.common.PayReceive; import com.opengamma.strata.product.swap.IborRateCalculation; /** * Caplet stripping setup. */ public class CapletStrippingSetup { private static final ReferenceData REF_DATA = ReferenceData.standard(); private static final double[] FWD_CURVE_NODES = new double[] { 0.0438356164383561, 0.0876712328767123, 0.172602739726027, 0.254794520547945, 0.506849315068493, 0.758904109589041, 1.00547945205479, 2.01369863013698, 3.01020285949547, 4.00547945205479, 5.00547945205479, 6.00547945205479, 7.01839958080694, 8.01095890410959, 9.00821917808219, 10.0082191780821, 15.0074706190583, 20.0082191780821, 25.0109589041095, 30.0136986301369}; private static final double[] FWD_CURVE_VALUES = new double[] { 0.00184088091044285, 0.00201024117395892, 0.00241264832694067, 0.00280755413825359, 0.0029541307818572, 0.00310125437814943, 0.00320054435838637, 0.00377914611772073, 0.00483320020067661, 0.00654829256979543, 0.00877749583222556, 0.0112470678648412, 0.0136301644164456, 0.0157618031582798, 0.0176836551757772, 0.0194174141169365, 0.0254011614777518, 0.0282527762712854, 0.0298620063409043, 0.031116719228976}; private static final CurveMetadata FWD_META = Curves.zeroRates("fwdCurve", ACT_ACT_ISDA); protected static final InterpolatedNodalCurve FWD_CURVE = InterpolatedNodalCurve.of(FWD_META, DoubleArray.ofUnsafe(FWD_CURVE_NODES), DoubleArray.ofUnsafe(FWD_CURVE_VALUES), CurveInterpolators.LINEAR, CurveExtrapolators.LINEAR, CurveExtrapolators.LINEAR); private static final double[] DIS_CURVE_NODES = new double[] { 0.00273972602739726, 0.0876712328767123, 0.172602739726027, 0.254794520547945, 0.345205479452054, 0.424657534246575, 0.506849315068493, 0.758904109589041, 1.00547945205479, 2.00547945205479, 3.01020285949547, 4.00547945205479, 5.00547945205479, 10.0054794520547}; private static final double[] DIS_CURVE_VALUES = new double[] { 0.00212916045658802, 0.00144265912946933, 0.00144567477491987, 0.00135441424749791, 0.00134009103595346, 0.00132773752749976, 0.00127592397233014, 0.00132302501180961, 0.00138688847322639, 0.00172748279241698, 0.00254381216780551, 0.00410024606039574, 0.00628782387356631, 0.0170033466745807}; private static final CurveMetadata DIS_META = Curves.zeroRates("dscCurve", ACT_ACT_ISDA); private static final InterpolatedNodalCurve DIS_CURVE = InterpolatedNodalCurve.of( DIS_META, DoubleArray.ofUnsafe(DIS_CURVE_NODES), DoubleArray.ofUnsafe(DIS_CURVE_VALUES), CurveInterpolators.DOUBLE_QUADRATIC, CurveExtrapolators.LINEAR, CurveExtrapolators.LINEAR); protected static final LocalDate CALIBRATION_DATE = LocalDate.of(2016, 3, 3); protected static final ZonedDateTime CALIBRATION_TIME = CALIBRATION_DATE.atTime(10, 0).atZone(ZoneId.of("America/New_York")); protected static final ImmutableRatesProvider RATES_PROVIDER = ImmutableRatesProvider.builder(CALIBRATION_DATE) .discountCurve(USD, DIS_CURVE) .iborIndexCurve(USD_LIBOR_3M, FWD_CURVE) .build(); private static final double[] CAP_BLACK_STRIKES = new double[] { 0.005, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05, 0.055, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12}; private static final int[] CAP_BLACK_END_TIMES = new int[] {1, 2, 3, 4, 5, 7, 10}; private static final int NUM_BLACK_MATURITIES = CAP_BLACK_END_TIMES.length; protected static final int NUM_BLACK_STRIKES = CAP_BLACK_STRIKES.length; private static final double[] CAP_NORMAL_STRIKES = new double[] { 0.0025, 0.005, 0.0075, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05}; private static final int[] CAP_NORMAL_END_TIMES = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20}; private static final int NUM_NORMAL_MATURITIES = CAP_NORMAL_END_TIMES.length; protected static final int NUM_NORMAL_STRIKES = CAP_NORMAL_STRIKES.length; private static final ResolvedIborCapFloorLeg[][] CAPS_BLACK = new ResolvedIborCapFloorLeg[NUM_BLACK_STRIKES][NUM_BLACK_MATURITIES]; private static final ResolvedIborCapFloorLeg[][] CAPS_NORMAL = new ResolvedIborCapFloorLeg[NUM_NORMAL_STRIKES][NUM_NORMAL_MATURITIES]; private static final LocalDate BASE_DATE = USD_LIBOR_3M.getEffectiveDateOffset().adjust(CALIBRATION_DATE, REF_DATA); static { LocalDate startDate = BASE_DATE.plus(USD_LIBOR_3M.getTenor()); for (int i = 0; i < NUM_BLACK_MATURITIES; ++i) { for (int j = 0; j < NUM_BLACK_STRIKES; ++j) { CAPS_BLACK[j][i] = IborCapFloorLeg.builder() .calculation(IborRateCalculation.of(USD_LIBOR_3M)) .capSchedule(ValueSchedule.of(CAP_BLACK_STRIKES[j])) .notional(ValueSchedule.ALWAYS_1) .paymentSchedule( PeriodicSchedule.of( startDate, BASE_DATE.plusYears(CAP_BLACK_END_TIMES[i]), Frequency.P3M, BusinessDayAdjustment.of(BusinessDayConventions.MODIFIED_FOLLOWING, USD_LIBOR_3M.getFixingCalendar()), StubConvention.NONE, RollConventions.NONE)) .payReceive(PayReceive.RECEIVE) .build() .resolve(REF_DATA); } } for (int i = 0; i < NUM_NORMAL_MATURITIES; ++i) { for (int j = 0; j < NUM_NORMAL_STRIKES; ++j) { CAPS_NORMAL[j][i] = IborCapFloorLeg.builder() .calculation(IborRateCalculation.of(USD_LIBOR_3M)) .capSchedule(ValueSchedule.of(CAP_NORMAL_STRIKES[j])) .notional(ValueSchedule.ALWAYS_1) .paymentSchedule( PeriodicSchedule.of( startDate, BASE_DATE.plusYears(CAP_NORMAL_END_TIMES[i]), Frequency.P3M, BusinessDayAdjustment.of(BusinessDayConventions.MODIFIED_FOLLOWING, USD_LIBOR_3M.getFixingCalendar()), StubConvention.NONE, RollConventions.NONE)) .payReceive(PayReceive.RECEIVE) .build() .resolve(REF_DATA); } } } // cap vol at each strike - NaN represents missing data private static final double[][] CAP_BLACK_VOLS = new double[][] { {0.7175, 0.7781, 0.8366, Double.NaN, 0.8101, 0.7633, 0.714}, {Double.NaN, Double.NaN, 0.7523, 0.7056, 0.66095, 0.5933, 0.5313}, {Double.NaN, 0.78086, 0.73987, 0.667, 0.61469, 0.53502, 0.4691}, {Double.NaN, Double.NaN, Double.NaN, 0.63455, 0.56975, 0.48235, 0.4369}, {Double.NaN, 0.80496, 0.73408, 0.6081, 0.5472, 0.46445, 0.38854}, {Double.NaN, Double.NaN, Double.NaN, 0.61055, 0.52865, 0.432, 0.365}, {0.96976, 0.82268, 0.73761, Double.NaN, 0.5168, 0.4183, 0.35485}, {0.98927, 0.8376, 0.7274, 0.5983, 0.5169, 0.4083, 0.3375}, {1.00627, 0.83618, Double.NaN, 0.6003, Double.NaN, Double.NaN, 0.34498}, {1.02132, 0.8391, 0.7226, 0.5921, 0.4984, 0.3914, 0.32155}, {1.0255, 0.8406, 0.7196, 0.5962, 0.5035, 0.3873, 0.3227}, {1.0476, 0.8411, 0.7072, 0.58845, 0.50055, 0.3856, 0.3135}, {1.0467, Double.NaN, 0.70655, 0.5855, 0.50165, 0.3824, 0.3093}, {Double.NaN, Double.NaN, 0.70495, 0.58455, 0.499, 0.3802, 0.316}, {Double.NaN, 0.8458, 0.70345, 0.58335, 0.4984, 0.38905, 0.3164}, {Double.NaN, 0.8489, 0.70205, 0.58245, 0.4999, 0.39155, 0.3239}, {Double.NaN, Double.NaN, 0.7009, 0.5824, 0.5059, 0.4005, 0.3255}, {Double.NaN, Double.NaN, 0.6997, 0.5818, 0.5059, 0.4014, 0.3271}}; private static final double[][] CAP_BLACK_VOLS_INVALID = new double[][] { {0.7175, Double.NaN, 0.8366, Double.NaN, 0.8101, 0.7633, 0.714}, {Double.NaN, Double.NaN, 0.7523, 0.7056, 0.66095, 0.5933, 0.5313}, {Double.NaN, Double.NaN, 0.73987, 0.667, 0.61469, 0.53502, 0.4691}, {Double.NaN, Double.NaN, Double.NaN, 0.63455, 0.56975, 0.48235, 0.4369}, {Double.NaN, Double.NaN, 0.73408, 0.6081, 0.5472, 0.46445, 0.38854}, {Double.NaN, Double.NaN, Double.NaN, 0.61055, 0.52865, 0.432, 0.365}, {0.96976, Double.NaN, 0.73761, Double.NaN, 0.5168, 0.4183, 0.35485}, {0.98927, Double.NaN, 0.7274, 0.5983, 0.5169, 0.4083, 0.3375}, {1.00627, Double.NaN, Double.NaN, 0.6003, Double.NaN, Double.NaN, 0.34498}, {1.02132, Double.NaN, 0.7226, 0.5921, 0.4984, 0.3914, 0.32155}, {1.0255, Double.NaN, 0.7196, 0.5962, 0.5035, 0.3873, 0.3227}, {1.0476, Double.NaN, 0.7072, 0.58845, 0.50055, 0.3856, 0.3135}, {1.0467, Double.NaN, 0.70655, 0.5855, 0.50165, 0.3824, 0.3093}, {Double.NaN, Double.NaN, 0.70495, 0.58455, 0.499, 0.3802, 0.316}, {Double.NaN, Double.NaN, 0.70345, 0.58335, 0.4984, 0.38905, 0.3164}, {Double.NaN, Double.NaN, 0.70205, 0.58245, 0.4999, 0.39155, 0.3239}, {Double.NaN, Double.NaN, 0.7009, 0.5824, 0.5059, 0.4005, 0.3255}, {Double.NaN, Double.NaN, 0.6997, 0.5818, 0.5059, 0.4014, 0.3271}}; private static final double[][] CAP_NORMAL_VOLS = new double[][] { {0.5463, 0.5827, 0.5589, 0.5339, 0.5224, 0.5685, 0.6396, 0.7184, 0.8029, 0.8908, 0.9804, 1.0705, 1.1608}, {0.6679, 0.7129, 0.6801, 0.6483, 0.6308, 0.6654, 0.7263, 0.7939, 0.8673, 0.9448, 1.0249, 1.1062, 1.1881}, {0.7402, 0.78, 0.7501, 0.7206, 0.7024, 0.7249, 0.7727, 0.8272, 0.8876, 0.9525, 1.0206, 1.0907, 1.1619}, {0.7864, 0.8236, 0.7962, 0.7693, 0.7514, 0.7649, 0.8019, 0.8454, 0.8943, 0.9479, 1.0051, 1.0649, 1.1264}, {0.8147, 0.8509, 0.8261, 0.8015, 0.7844, 0.7917, 0.8203, 0.8551, 0.8948, 0.9388, 0.9863, 1.0366, 1.0889}, {0.8318, 0.8676, 0.845, 0.8226, 0.8064, 0.8093, 0.8319, 0.8608, 0.8948, 0.9334, 0.9759, 1.0215, 1.0693}, {0.8354, 0.8722, 0.8514, 0.8308, 0.8154, 0.8151, 0.8324, 0.8562, 0.8851, 0.9189, 0.957, 0.9985, 1.0426}, {0.8374, 0.8753, 0.8559, 0.8368, 0.822, 0.8193, 0.8328, 0.8528, 0.8784, 0.9091, 0.9445, 0.9837, 1.0256}, {0.8385, 0.8776, 0.8593, 0.8412, 0.8271, 0.8225, 0.8331, 0.8503, 0.8734, 0.9021, 0.9357, 0.9733, 1.014}, {0.818, 0.8549, 0.8395, 0.8242, 0.8119, 0.8057, 0.8122, 0.8252, 0.8442, Double.NaN, 0.9, Double.NaN, 0.973}, {0.799, 0.8346, 0.8217, 0.8087, 0.7979, 0.7904, 0.7935, 0.803, 0.8192, 0.842, 0.8708, 0.9041, 0.9409}, {0.7555, 0.7827, 0.7733, 0.7638, 0.7557, 0.749, 0.7502, 0.7577, 0.7721, 0.7935, 0.821, 0.8532, 0.8887}}; private static final double[][] CAP_NORMAL_EQUIV_VOLS = new double[][] { {0.0029237822734902524, 0.0034136169936817033, 0.004111899099433724, Double.NaN, 0.005158919846139429, 0.005880028654583701, 0.006602746804835127}, {Double.NaN, Double.NaN, 0.005555184661720685, 0.006060308783872109, 0.0064681825858204185, 0.007001834697977297, 0.007358023852028733}, {Double.NaN, 0.006482680497799072, 0.0069882805083619345, 0.007385823271192304, 0.007781995222353636, 0.00817176527480969, 0.008305654885373529}, {Double.NaN, Double.NaN, Double.NaN, 0.008463042042139829, 0.008721826257219168, 0.00890430941028613, 0.009204081837445803}, {Double.NaN, 0.009203852197689514, 0.009553295146839048, 0.009395277309796696, 0.009686438334724271, 0.009868950606606874, 0.00945527391744324}, {Double.NaN, Double.NaN, Double.NaN, 0.010577695837496428, 0.01054951965092822, 0.010372321165352374, 0.009951221255227807}, {0.012850988453383937, 0.011715502361745867, 0.011927433447893793, Double.NaN, 0.011406457995250105, 0.011085647553150682, 0.010618460011235616}, {0.014345934996892178, 0.013026457768347524, 0.012872814762320993, 0.012511992391841475, 0.012411501522441871, 0.011783024185340241, 0.010987968181224094}, {0.015814601727181077, 0.01409230561581168, Double.NaN, 0.013545642107440487, Double.NaN, Double.NaN, 0.011999970920509464}, {0.0172609696455313, 0.015198228467757167, 0.014862741487700476, 0.014349870473796244, 0.013886237101534537, 0.013037525141382612, 0.012007974059737487}, {0.01852996833366485, 0.016262287516603396, 0.015798297092384346, 0.01536970407073983, 0.014889560180401738, 0.013705316464075669, 0.012746046992219326}, {0.020108449603030307, 0.017290371749777253, 0.016515852031022907, 0.016107954766077858, 0.015676079961874777, 0.014412163759150191, 0.013087486389562407}, {0.02243813236022633, Double.NaN, 0.018351490528564194, 0.017785737349513116, 0.017367352778322714, 0.015758634740652772, 0.014188523203432134}, {Double.NaN, Double.NaN, 0.020100151305047054, 0.019441849545595233, 0.018882166214436967, 0.017060460613144583, 0.015683090275069138}, {Double.NaN, 0.023194060100211675, 0.021794416400983882, 0.021034125786394464, 0.020400475279279406, 0.018757169214515566, 0.01687872530111318}, {Double.NaN, 0.025120151928656194, 0.023442818163966796, 0.022587053666820456, 0.021949259948182788, 0.02018230434867113, 0.0183957935728747}, {Double.NaN, Double.NaN, 0.025056683365416332, 0.024128415181588148, 0.023645308722883313, 0.021888760133392547, 0.019611322604198053}, {Double.NaN, Double.NaN, 0.026632254625197683, 0.02561633184998322, 0.02508697262418348, 0.023212771122734382, 0.020807499010350925}}; //------------------------------------------------------------------------- protected static DoubleMatrix createFullBlackDataMatrix() { DoubleMatrix matrix = DoubleMatrix.ofUnsafe(CAP_BLACK_VOLS); return matrix.transpose(); } protected static DoubleMatrix createFullBlackDataMatrixInvalid() { DoubleMatrix matrix = DoubleMatrix.ofUnsafe(CAP_BLACK_VOLS_INVALID); return matrix.transpose(); } protected static DoubleMatrix createBlackDataMatrixForStrike(int strikeIndex) { int nExpiry = CAP_BLACK_VOLS[0].length; double[][] res = new double[nExpiry][1]; for (int i = 0; i < nExpiry; ++i) { res[i][0] = CAP_BLACK_VOLS[strikeIndex][i]; } return DoubleMatrix.ofUnsafe(res); } protected static DoubleMatrix createFullFlatBlackDataMatrix() { DoubleMatrix matrix = DoubleMatrix.filled(NUM_BLACK_STRIKES, NUM_BLACK_MATURITIES, 0.5); return matrix.transpose(); } protected static ImmutableList<Period> createBlackMaturities() { Builder<Period> builder = ImmutableList.builder(); for (int i = 0; i < NUM_BLACK_MATURITIES; ++i) { builder.add(Period.ofYears(CAP_BLACK_END_TIMES[i])); } return builder.build(); } protected static DoubleArray createBlackStrikes() { return DoubleArray.copyOf(CAP_BLACK_STRIKES); } //------------------------------------------------------------------------- protected static DoubleMatrix createFullNormalDataMatrix() { DoubleMatrix matrix = DoubleMatrix.ofUnsafe(CAP_NORMAL_VOLS); return matrix.transpose(); } protected static ImmutableList<Period> createNormalMaturities() { Builder<Period> builder = ImmutableList.builder(); for (int i = 0; i < NUM_NORMAL_MATURITIES; ++i) { builder.add(Period.ofYears(CAP_NORMAL_END_TIMES[i])); } return builder.build(); } protected static DoubleArray createNormalStrikes() { return DoubleArray.copyOf(CAP_NORMAL_STRIKES); } //------------------------------------------------------------------------- protected static DoubleMatrix createFullNormalEquivDataMatrix() { DoubleMatrix matrix = DoubleMatrix.ofUnsafe(CAP_NORMAL_EQUIV_VOLS); return matrix.transpose(); } protected static ImmutableList<Period> createNormalEquivMaturities() { Builder<Period> builder = ImmutableList.builder(); for (int i = 0; i < NUM_BLACK_MATURITIES; ++i) { builder.add(Period.ofYears(CAP_BLACK_END_TIMES[i])); } return builder.build(); } protected static DoubleArray createNormalEquivStrikes() { return DoubleArray.copyOf(CAP_BLACK_STRIKES); } //------------------------------------------------------------------------- protected static Pair<List<ResolvedIborCapFloorLeg>, List<Double>> getCapsBlackVols(int strikeIndex) { ResolvedIborCapFloorLeg[] caps = CAPS_BLACK[strikeIndex]; double[] vols = CAP_BLACK_VOLS[strikeIndex]; Builder<ResolvedIborCapFloorLeg> capBuilder = ImmutableList.builder(); Builder<Double> volBuilder = ImmutableList.builder(); int nVols = vols.length; for (int i = 0; i < nVols; ++i) { if (Double.isFinite(vols[i])) { capBuilder.add(caps[i]); volBuilder.add(vols[i]); } } return Pair.of(capBuilder.build(), volBuilder.build()); } protected static Pair<List<ResolvedIborCapFloorLeg>, List<Double>> getCapsFlatBlackVols(int strikeIndex) { ResolvedIborCapFloorLeg[] caps = CAPS_BLACK[strikeIndex]; double[] vols = createFullFlatBlackDataMatrix().columnArray(strikeIndex); Builder<ResolvedIborCapFloorLeg> capBuilder = ImmutableList.builder(); Builder<Double> volBuilder = ImmutableList.builder(); int nVols = vols.length; for (int i = 0; i < nVols; ++i) { if (Double.isFinite(vols[i])) { capBuilder.add(caps[i]); volBuilder.add(vols[i]); } } return Pair.of(capBuilder.build(), volBuilder.build()); } protected static Pair<List<ResolvedIborCapFloorLeg>, List<Double>> getCapsNormalVols(int strikeIndex) { ResolvedIborCapFloorLeg[] caps = CAPS_NORMAL[strikeIndex]; double[] vols = CAP_NORMAL_VOLS[strikeIndex]; Builder<ResolvedIborCapFloorLeg> capBuilder = ImmutableList.builder(); Builder<Double> volBuilder = ImmutableList.builder(); int nVols = vols.length; for (int i = 0; i < nVols; ++i) { if (Double.isFinite(vols[i])) { capBuilder.add(caps[i]); volBuilder.add(vols[i]); } } return Pair.of(capBuilder.build(), volBuilder.build()); } protected static Pair<List<ResolvedIborCapFloorLeg>, List<Double>> getCapsNormalEquivVols(int strikeIndex) { ResolvedIborCapFloorLeg[] caps = CAPS_BLACK[strikeIndex]; double[] vols = CAP_NORMAL_EQUIV_VOLS[strikeIndex]; Builder<ResolvedIborCapFloorLeg> capBuilder = ImmutableList.builder(); Builder<Double> volBuilder = ImmutableList.builder(); int nVols = vols.length; for (int i = 0; i < nVols; ++i) { if (Double.isFinite(vols[i])) { capBuilder.add(caps[i]); volBuilder.add(vols[i]); } } return Pair.of(capBuilder.build(), volBuilder.build()); } //------------------------------------------------------------------------- // print for debugging protected void print(IborCapletFloorletVolatilityCalibrationResult res, DoubleArray strikes, double maxTime) { System.out.println("Print in CapletStrippingSetup \n"); System.out.println("Chi-square: " + res.getChiSquare()); IborCapletFloorletVolatilities vols = res.getVolatilities(); final int nSamples = 51; final int nStrikeSamples = 51; System.out.print("\n"); for (int i = 0; i < nStrikeSamples; i++) { System.out.print("\t" + (strikes.get(0) + (strikes.get(strikes.size() - 1) - strikes.get(0)) * i) / (nStrikeSamples - 1)); } System.out.print("\n"); for (int index = 0; index < nSamples; index++) { final double t = 0.25 + index * maxTime / (nSamples - 1); double forward = FWD_CURVE.yValue(t); System.out.print(t); for (int i = 0; i < nStrikeSamples; i++) { double strike = (strikes.get(0) + (strikes.get(strikes.size() - 1) - strikes.get(0)) * i) / (nStrikeSamples - 1); System.out.print("\t" + vols.volatility(t, strike, forward)); } System.out.print("\n"); } } }