/** * The bucked CS01 (or credit DV01) by a shift of each the market spread in turn. This takes an * extraneous yield curve, a set of reference CDSs (marketCDSs) and their market quotes and * bootstraps a credit (hazard) curve - the target CDS is then priced with this credit curve. This * is then repeated with each market spreads bumped in turn by some amount. The result is the * array of differences (bumped minus base price) is divided by the bump amount.<br> * This can take quotes as ParSpread, PointsUpFront or QuotedSpread (or some mix). For * par-spreads, these are bumped and a new credit curve built; for quoted-spreads, there are * bumped and a new curve build be first converting to PUF; and finally for PUF, these are * converted to quoted spreads, bumped and converted back to build the credit curve. * * @param cds analytic description of a CDS traded at a certain time - it is this CDS that we are * calculation CDV01 for * @param cdsCoupon the coupon of the traded CDS (expressed as <b>fractions not basis points</b>) * @param yieldCurve The yield (or discount) curve * @param marketCDSs The market CDSs - these are the reference instruments used to build the * credit curve * @param quotes The quotes for the market CDSs - these can be ParSpread, PointsUpFront or * QuotedSpread (or any mixture of these) * @param fracBumpAmount The fraction bump amount, so a 1pb bump is 1e-4 * @return The bucketed credit DV01 */ public double[] bucketedCS01FromPillarQuotes( CdsAnalytic cds, double cdsCoupon, IsdaCompliantYieldCurve yieldCurve, CdsAnalytic[] marketCDSs, CdsQuoteConvention[] quotes, double fracBumpAmount) { ArgChecker.notNull(cds, "cds"); ArgChecker.noNulls(marketCDSs, "curvePoints"); ArgChecker.noNulls(quotes, "quotes"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small"); int n = marketCDSs.length; ArgChecker.isTrue(n == quotes.length, "speads length does not match curvePoints"); IsdaCompliantCreditCurve baseCurve = _curveBuilder.calibrateCreditCurve(marketCDSs, quotes, yieldCurve); double basePrice = _pricer.pv(cds, yieldCurve, baseCurve, cdsCoupon); double[] res = new double[n]; for (int i = 0; i < n; i++) { CdsQuoteConvention[] bumpedQuotes = bumpQuoteAtIndex(marketCDSs, quotes, yieldCurve, fracBumpAmount, i); IsdaCompliantCreditCurve bumpedCurve = _curveBuilder.calibrateCreditCurve(marketCDSs, bumpedQuotes, yieldCurve); double price = _pricer.pv(cds, yieldCurve, bumpedCurve, cdsCoupon); res[i] = (price - basePrice) / fracBumpAmount; } return res; }
/** * The bucked CS01 (or credit DV01) on a set of CDSS by bumping each quoted (or flat) spread in * turn. This takes an extraneous yield curve, a set of reference CDSs (marketCDSs) and their * quoted (or flat) spreads (expressed as <b>fractions not basis points</b>) and bootstraps a * credit (hazard) curve - the target CDS is then priced with this credit curve. This is then * repeated with each market spreads bumped in turn. The result is the vector of differences * (bumped minus base price) divided by the bump amount.<br> * For small bumps (<1e-4) this approximates $$\frac{\partial V}{\partial S_i}$$ for a flat curve * where $$S_i$$ * * @param cds a set of analytic description of CDSs traded at a certain times * @param dealSpread The <b>fraction</b> spread of the CDS * @param yieldCurve The yield (or discount) curve * @param marketCDSs The market CDSs - these are the reference instruments used to build the * credit curve * @param quotedSpreads The <b>fractional</b> spreads of the market CDSs * @param fracBumpAmount The fraction bump amount, so a 1pb bump is 1e-4 * @param shiftType ABSOLUTE or RELATIVE * @return The bucked CS01 for a set of CDSs */ public double[][] bucketedCS01FromQuotedSpreads( CdsAnalytic[] cds, double dealSpread, IsdaCompliantYieldCurve yieldCurve, CdsAnalytic[] marketCDSs, double[] quotedSpreads, double fracBumpAmount, ShiftType shiftType) { ArgChecker.noNulls(cds, "cds"); ArgChecker.noNulls(marketCDSs, "curvePoints"); ArgChecker.notEmpty(quotedSpreads, "spreads"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.notNull(shiftType, "shiftType"); ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small"); int nMarketCDSs = marketCDSs.length; ArgChecker.isTrue( nMarketCDSs == quotedSpreads.length, "speads length does not match curvePoints"); CdsPriceType priceType = CdsPriceType.DIRTY; double[] premiums = new double[nMarketCDSs]; Arrays.fill(premiums, dealSpread); // assume the premiums of all CDS are equal int nTradeCDSs = cds.length; double[] puf = _pufConverter.quotedSpreadsToPUF(marketCDSs, premiums, yieldCurve, quotedSpreads); // TODO not needed IsdaCompliantCreditCurve baseCurve = _curveBuilder.calibrateCreditCurve(marketCDSs, premiums, yieldCurve, puf); double[] basePrices = new double[nTradeCDSs]; for (int j = 0; j < nTradeCDSs; j++) { basePrices[j] = _pricer.pv(cds[j], yieldCurve, baseCurve, dealSpread, priceType); } double[] bumpedPUF = new double[nMarketCDSs]; double[][] res = new double[nTradeCDSs][nMarketCDSs]; for (int i = 0; i < nMarketCDSs; i++) { // Outer loop is over bumps System.arraycopy(puf, 0, bumpedPUF, 0, nMarketCDSs); double bumpedSpread = bumpedSpread(quotedSpreads[i], fracBumpAmount, shiftType); bumpedPUF[i] = _pufConverter.quotedSpreadToPUF(marketCDSs[i], premiums[i], yieldCurve, bumpedSpread); // TODO a lot of unnecessary recalibration here IsdaCompliantCreditCurve bumpedCurve = _curveBuilder.calibrateCreditCurve(marketCDSs, premiums, yieldCurve, bumpedPUF); for (int j = 0; j < nTradeCDSs; j++) { double price = _pricer.pv(cds[j], yieldCurve, bumpedCurve, dealSpread, priceType); res[j][i] = (price - basePrices[j]) / fracBumpAmount; } } return res; }
/** * Make a set of CDS by specifying all dates. * * @param tradeDate the trade date * @param stepinDate (aka Protection Effective sate or assignment date). Date when party assumes * ownership. This is usually T+1. This is when protection (and risk) starts in terms of the * model. Note, this is sometimes just called the Effective Date, however this can cause * confusion with the legal effective date which is T-60 or T-90. * @param valueDate the valuation date. The date that values are PVed to. Is is normally today + 3 * business days. Aka cash-settle date. * @param accStartDate this is when the CDS nominally starts in terms of premium payments. i.e. * the number of days in the first period (and thus the amount of the first premium payment) * is counted from this date. * @param maturities The maturities of the CDSs. For a standard CDS these are IMM dates * @return an array of CDS analytic descriptions */ public CdsAnalytic[] makeCds( LocalDate tradeDate, LocalDate stepinDate, LocalDate valueDate, LocalDate accStartDate, LocalDate[] maturities) { ArgChecker.noNulls(maturities, "maturities"); int n = maturities.length; CdsAnalytic[] cds = new CdsAnalytic[n]; for (int i = 0; i < n; i++) { cds[i] = new CdsAnalytic( tradeDate, stepinDate, valueDate, accStartDate, maturities[i], _payAccOnDefault, _couponInterval, _stubType, _protectStart, _recoveryRate, _businessdayAdjustmentConvention, _calendar, _accrualDayCount, _curveDayCount); } return cds; }
/** * Puts all the specified dates and values into this builder. * * <p>The date and value collections must be the same size. * * <p>The date-value pairs are added one by one. If a date is duplicated it will overwrite an * earlier entry. * * @param dates the dates to be added * @param values the values to be added * @return this builder */ public LocalDateDoubleTimeSeriesBuilder putAll( Collection<LocalDate> dates, Collection<Double> values) { ArgChecker.noNulls(dates, "dates"); ArgChecker.noNulls(values, "values"); ArgChecker.isTrue( dates.size() == values.size(), "Arrays are of different sizes - dates: {}, values: {}", dates.size(), values.size()); Iterator<LocalDate> itDate = dates.iterator(); Iterator<Double> itValue = values.iterator(); for (int i = 0; i < dates.size(); i++) { put(itDate.next(), itValue.next()); } return this; }
/** * The bucked CS01 (or credit DV01) by shifting each market par-spread in turn. This takes an * extraneous yield curve, a set of reference CDSs (marketCDSs) and their par-spreads (expressed * as <b>fractions not basis points</b>) and bootstraps a credit (hazard) curve - the target CDS * is then priced with this credit curve. This is then repeated with each market spreads bumped in * turn. The result is the vector of differences (bumped minus base price) divided by the bump * amount.<br> * For small bumps (<1e-4) this approximates $$\frac{\partial V}{\partial S_i}$$ where $$S_i$$ is * the spread of the $$1^{th}$$ market CDS<br> * * @param cds analytic description of a CDS traded at a certain time * @param cdsCoupon The <b>fraction</b> spread of the CDS * @param yieldCurve The yield (or discount) curve * @param marketCDSs The market CDSs - these are the reference instruments used to build the * credit curve * @param marketParSpreads The <b>fractional</b> par-spreads of the market CDSs * @param fracBumpAmount The fraction bump amount, so a 1pb bump is 1e-4 * @param shiftType ABSOLUTE or RELATIVE * @return The credit CS01 */ public double[] bucketedCS01FromParSpreads( CdsAnalytic cds, double cdsCoupon, IsdaCompliantYieldCurve yieldCurve, CdsAnalytic[] marketCDSs, double[] marketParSpreads, double fracBumpAmount, ShiftType shiftType) { ArgChecker.notNull(cds, "cds"); ArgChecker.noNulls(marketCDSs, "curvePoints"); ArgChecker.notEmpty(marketParSpreads, "spreads"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.notNull(shiftType, "shiftType"); ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small"); int n = marketCDSs.length; ArgChecker.isTrue(n == marketParSpreads.length, "speads length does not match curvePoints"); CdsPriceType priceType = CdsPriceType.DIRTY; IsdaCompliantCreditCurve baseCurve = _curveBuilder.calibrateCreditCurve(marketCDSs, marketParSpreads, yieldCurve); double basePrice = _pricer.pv(cds, yieldCurve, baseCurve, cdsCoupon, priceType); double[] res = new double[n]; for (int i = 0; i < n; i++) { double[] temp = makeBumpedSpreads(marketParSpreads, fracBumpAmount, shiftType, i); IsdaCompliantCreditCurve bumpedCurve = _curveBuilder.calibrateCreditCurve(marketCDSs, temp, yieldCurve); double price = _pricer.pv(cds, yieldCurve, bumpedCurve, cdsCoupon, priceType); res[i] = (price - basePrice) / fracBumpAmount; } return res; }
/** * The CS01 (or credit DV01) by a parallel shift of the market par spreads (CDS par spread curve). * This takes an extraneous yield curve, a set of reference CDSs (marketCDSs) and their * par-spreads (expressed as <b>fractions not basis points</b>) and bootstraps a credit (hazard) * curve - the target CDS is then priced with this credit curve. This is then repeated with the * market spreads bumped in parallel by some amount. The result is the difference (bumped minus * base price) is divided by the bump amount.<br> * For small bumps (<1e-4) this approximates $$\frac{\partial V}{\partial S}$$<br> * Credit DV01 is (often) defined as -( V(S + 1bp) - V(s)) - to achieve this use fracBumpAmount = * 1e-4 and shiftType ABSOLUTE * * @param cds analytic description of a CDS traded at a certain time * @param cdsFracSpread The <b>fraction</b> spread of the CDS * @param yieldCurve The yield (or discount) curve * @param marketCDSs The market CDSs - these are the reference instruments used to build the * credit curve * @param parSpreads The <b>fractional</b> spreads of the market CDSs * @param fracBumpAmount The fraction bump amount, so a 1pb bump is 1e-4 * @param shiftType ABSOLUTE or RELATIVE * @return The credit DV01 */ public double parallelCS01FromParSpreads( CdsAnalytic cds, double cdsFracSpread, IsdaCompliantYieldCurve yieldCurve, CdsAnalytic[] marketCDSs, double[] parSpreads, double fracBumpAmount, ShiftType shiftType) { ArgChecker.notNull(cds, "cds"); ArgChecker.noNulls(marketCDSs, "curvePoints"); ArgChecker.notEmpty(parSpreads, "spreads"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.notNull(shiftType, "shiftType"); ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small"); int n = marketCDSs.length; ArgChecker.isTrue(n == parSpreads.length, "speads length does not match curvePoints"); double[] bumpedSpreads = makeBumpedSpreads(parSpreads, fracBumpAmount, shiftType); double diff = fdCreditDV01( cds, cdsFracSpread, marketCDSs, bumpedSpreads, parSpreads, yieldCurve, CdsPriceType.DIRTY); return diff / fracBumpAmount; }
/** * Make a set of CDSs with a common trade date and maturities dates the given periods after the * next IMM date (after the trade-date). * * @param tradeDate the trade date * @param accStartDate this is when the CDS nominally starts in terms of premium payments. For a * standard CDS this is the previous IMM date, and for a `legacy' CDS it is T+1 * @param tenors the tenors (lengths) of the CDSs * @return an array of CDS analytic descriptions */ public CdsAnalytic[] makeImmCds(LocalDate tradeDate, LocalDate accStartDate, Period[] tenors) { ArgChecker.notNull(tradeDate, "tradeDate"); ArgChecker.notNull(accStartDate, "effectiveDate"); ArgChecker.noNulls(tenors, "tenors"); LocalDate nextIMM = ImmDateLogic.getNextIMMDate(tradeDate); LocalDate[] maturities = ImmDateLogic.getIMMDateSet(nextIMM, tenors); return makeCds(tradeDate, accStartDate, maturities); }
/** * Set up a strip of on-the-run indexes represented as a single name CDSs (i.e. by CdsAnalytic). * The index roll dates (when new indices are issued) are 20 Mar & Sep, and the index is defined * to have a maturity that is its nominal tenor plus 3M on issuance, so a 5Y index on the * 6-Feb-2014 will have a maturity of 20-Dec-2018 (5Y3M on the issue date of 20-Sep-2013). The * accrual start date will be the previous IMM date (before the trade date), business-day * adjusted. <b>Note</b> it payment interval is changed from the default of 3M, this will produce * a (possibly incorrect) non-standard first coupon. * * @param tradeDate the trade date * @param tenors the nominal lengths of the indexes * @return an array of CDS analytic descriptions */ public CdsAnalytic[] makeCdx(LocalDate tradeDate, Period[] tenors) { ArgChecker.notNull(tradeDate, "tradeDate"); ArgChecker.noNulls(tenors, "tenors"); LocalDate effectiveDate = _businessdayAdjustmentConvention.adjust(ImmDateLogic.getPrevIMMDate(tradeDate), _calendar); LocalDate mid = ImmDateLogic.getNextIndexRollDate(tradeDate).minusMonths(3); LocalDate[] maturities = ImmDateLogic.getIMMDateSet(mid, tenors); return makeCds(tradeDate, effectiveDate, maturities); }
/** * The bucked CS01 (or credit DV01) by shifting each implied par-spread in turn. This takes an * extraneous yield curve, a set of pillar CDSs and their corresponding par-spread, and a set of * bucket CDSs (CDSs with maturities equal to the bucket points). A credit curve is bootstrapped * from the pillar CDSs - this is then used to imply spreads at the bucket maturities. These * spreads form pseudo market spreads to bootstraps a new credit (hazard) curve - the target CDS * is then priced with this credit curve. This is then repeated with each spreads bumped in turn. * The result is the vector of differences (bumped minus base price) divided by the bump amount. * * @param cds analytic description of a CDS traded at a certain time * @param cdsCoupon The <b>fraction</b> spread of the CDS * @param bucketCDSs these are the reference instruments that correspond to maturity buckets * @param yieldCurve The yield (or discount) curve * @param pillarCDSs These are the market CDSs used to build the credit curve * @param pillarSpreads These are the par-spreads of the market (pillar) CDSs * @param fracBumpAmount The fraction bump amount, so a 1pb bump is 1e-4 * @return The credit DV01 */ public double[] bucketedCS01FromParSpreads( CdsAnalytic cds, double cdsCoupon, CdsAnalytic[] bucketCDSs, IsdaCompliantYieldCurve yieldCurve, CdsAnalytic[] pillarCDSs, double[] pillarSpreads, double fracBumpAmount) { ArgChecker.noNulls(pillarCDSs, "pillarCDSs"); ArgChecker.notEmpty(pillarSpreads, "pillarSpreads"); IsdaCompliantCreditCurve creditCurve = _curveBuilder.calibrateCreditCurve(pillarCDSs, pillarSpreads, yieldCurve); return bucketedCS01FromCreditCurve( cds, cdsCoupon, bucketCDSs, yieldCurve, creditCurve, fracBumpAmount); }
/** * Make a set of standard CDS represented as a MultiCdsAnalytic instance. * * @param tradeDate the trade date * @param accStartDate the accrual start date * @param tenors the tenors (length) of the CDS * @return a set of CDS represented as a MultiCdsAnalytic */ public MultiCdsAnalytic makeMultiImmCds( LocalDate tradeDate, LocalDate accStartDate, Period[] tenors) { ArgChecker.noNulls(tenors, "tenors"); int n = tenors.length; int immNMonths = (int) DEFAULT_COUPON_INT.toTotalMonths(); int[] matIndices = new int[n]; for (int i = 0; i < n; i++) { int months = (int) tenors[i].toTotalMonths(); if (months % immNMonths != 0) { throw new IllegalArgumentException( "tenors index " + i + " is not a multiple of " + DEFAULT_COUPON_INT.toString()); } matIndices[i] = months / immNMonths; } return makeMultiImmCds(tradeDate, accStartDate, matIndices); }
/** * The bucked CS01 (or credit DV01) by shifting each implied par-spread in turn. This takes an * extraneous yield curve and a credit curve and a set of bucket CDSs (CDSs with maturities equal * to the bucket points). Par-spreads at the bucket maturities are implied from the credit curve. * These spreads form pseudo market spreads to bootstraps a new credit (hazard) curve - the target * CDS is then priced with this credit curve. This is then repeated with each spreads bumped in * turn. The result is the vector of differences (bumped minus base price) divided by the bump * amount. * * @param cds analytic description of a CDS traded at a certain time * @param cdsCoupon The <b>fraction</b> spread of the CDS * @param bucketCDSs these are the reference instruments that correspond to maturity buckets * @param yieldCurve The yield (or discount) curve * @param creditCurve the credit curve * @param fracBumpAmount The fraction bump amount, so a 1pb bump is 1e-4 * @return The credit DV01 */ public double[] bucketedCS01FromCreditCurve( CdsAnalytic cds, double cdsCoupon, CdsAnalytic[] bucketCDSs, IsdaCompliantYieldCurve yieldCurve, IsdaCompliantCreditCurve creditCurve, double fracBumpAmount) { ArgChecker.notNull(cds, "cds"); ArgChecker.noNulls(bucketCDSs, "bucketCDSs"); ArgChecker.notNull(creditCurve, "creditCurve"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small"); int n = bucketCDSs.length; double[] impSpreads = new double[n]; double[] t = new double[n]; for (int i = 0; i < n; i++) { impSpreads[i] = _pricer.parSpread(bucketCDSs[i], yieldCurve, creditCurve); t[i] = bucketCDSs[i].getProtectionEnd(); if (i > 0) { ArgChecker.isTrue(t[i] > t[i - 1], "buckets must be assending"); } } int index = Arrays.binarySearch(t, cds.getProtectionEnd()); if (index < 0) { index = -1 - index; } index = Math.min(index, n - 1); IsdaCompliantCreditCurve baseCurve = _curveBuilder.calibrateCreditCurve(bucketCDSs, impSpreads, yieldCurve); double basePrice = _pricer.pv(cds, yieldCurve, baseCurve, cdsCoupon); double[] res = new double[n]; for (int i = 0; i <= index; i++) { // don't bother calculating where there is no sensitivity double[] bumpedSpreads = makeBumpedSpreads(impSpreads, fracBumpAmount, ShiftType.ABSOLUTE, i); IsdaCompliantCreditCurve bumpedCurve = _curveBuilder.calibrateCreditCurve(bucketCDSs, bumpedSpreads, yieldCurve); double price = _pricer.pv(cds, yieldCurve, bumpedCurve, cdsCoupon); res[i] = (price - basePrice) / fracBumpAmount; } return res; }
public double parallelCS01FromCreditCurve( CdsAnalytic cds, double cdsCoupon, CdsAnalytic[] pillarCDSs, IsdaCompliantYieldCurve yieldCurve, IsdaCompliantCreditCurve creditCurve, double fracBumpAmount) { ArgChecker.notNull(cds, "cds"); ArgChecker.noNulls(pillarCDSs, "pillarCDSs"); ArgChecker.notNull(creditCurve, "creditCurve"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.isTrue(Math.abs(fracBumpAmount) > 1e-10, "bump amount too small"); int n = pillarCDSs.length; double[] impSpreads = new double[n]; double[] t = new double[n]; for (int i = 0; i < n; i++) { impSpreads[i] = _pricer.parSpread(pillarCDSs[i], yieldCurve, creditCurve); t[i] = pillarCDSs[i].getProtectionEnd(); if (i > 0) { ArgChecker.isTrue(t[i] > t[i - 1], "pillars must be assending"); } } IsdaCompliantCreditCurve baseCurve = _curveBuilder.calibrateCreditCurve(pillarCDSs, impSpreads, yieldCurve); double basePrice = _pricer.pv(cds, yieldCurve, baseCurve, cdsCoupon); double[] bumpedSpreads = makeBumpedSpreads(impSpreads, fracBumpAmount, ShiftType.ABSOLUTE); IsdaCompliantCreditCurve bumpedCurve = _curveBuilder.calibrateCreditCurve(pillarCDSs, bumpedSpreads, yieldCurve); double price = _pricer.pv(cds, yieldCurve, bumpedCurve, cdsCoupon); double res = (price - basePrice) / fracBumpAmount; return res; }
/** * Adds measures to the pricing rule. * * @param measures measures handled by the pricing rule * @return this builder */ public PricingRuleBuilder<T> addMeasures(Measure... measures) { ArgChecker.noNulls(measures, "measures"); this.measures.addAll(Arrays.asList(measures)); return this; }
/** {@inheritDoc} */ @Override public IsdaCompliantCreditCurve calibrateCreditCurve( CdsAnalytic[] cds, double[] premiums, IsdaCompliantYieldCurve yieldCurve, double[] pointsUpfront) { ArgChecker.noNulls(cds, "null CDSs"); ArgChecker.notEmpty(premiums, "empty fractionalSpreads"); ArgChecker.notEmpty(pointsUpfront, "empty pointsUpfront"); ArgChecker.notNull(yieldCurve, "null yieldCurve"); int n = cds.length; ArgChecker.isTrue(n == premiums.length, "Number of CDSs does not match number of spreads"); ArgChecker.isTrue( n == pointsUpfront.length, "Number of CDSs does not match number of pointsUpfront"); double proStart = cds[0].getEffectiveProtectionStart(); for (int i = 1; i < n; i++) { ArgChecker.isTrue( proStart == cds[i].getEffectiveProtectionStart(), "all CDSs must has same protection start"); ArgChecker.isTrue( cds[i].getProtectionEnd() > cds[i - 1].getProtectionEnd(), "protection end must be ascending"); } // use continuous premiums as initial guess double[] guess = new double[n]; double[] t = new double[n]; for (int i = 0; i < n; i++) { t[i] = cds[i].getProtectionEnd(); guess[i] = (premiums[i] + pointsUpfront[i] / t[i]) / cds[i].getLGD(); } IsdaCompliantCreditCurve creditCurve = new IsdaCompliantCreditCurve(t, guess); for (int i = 0; i < n; i++) { Pricer pricer = new Pricer(cds[i], yieldCurve, t, premiums[i], pointsUpfront[i]); Function1D<Double, Double> func = pricer.getPointFunction(i, creditCurve); switch (getArbHanding()) { case Ignore: { try { double[] bracket = BRACKER.getBracketedPoints( func, 0.8 * guess[i], 1.25 * guess[i], Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); double zeroRate = bracket[0] > bracket[1] ? ROOTFINDER.getRoot(func, bracket[1], bracket[0]) : ROOTFINDER.getRoot(func, bracket[0], bracket[1]); // Negative guess handled creditCurve = creditCurve.withRate(zeroRate, i); } catch ( MathException e) { // handling bracketing failure due to small survival probability if (Math.abs(func.evaluate(creditCurve.getZeroRateAtIndex(i - 1))) < 1.e-12) { creditCurve = creditCurve.withRate(creditCurve.getZeroRateAtIndex(i - 1), i); } else { throw new MathException(e); } } break; } case Fail: { double minValue = i == 0 ? 0.0 : creditCurve.getRTAtIndex(i - 1) / creditCurve.getTimeAtIndex(i); if (i > 0 && func.evaluate(minValue) > 0.0) { // can never fail on the first spread StringBuilder msg = new StringBuilder(); if (pointsUpfront[i] == 0.0) { msg.append("The par spread of " + premiums[i] + " at index " + i); } else { msg.append( "The premium of " + premiums[i] + "and points up-front of " + pointsUpfront[i] + " at index " + i); } msg.append( " is an arbitrage; cannot fit a curve with positive forward hazard rate. "); throw new IllegalArgumentException(msg.toString()); } guess[i] = Math.max(minValue, guess[i]); double[] bracket = BRACKER.getBracketedPoints( func, guess[i], 1.2 * guess[i], minValue, Double.POSITIVE_INFINITY); double zeroRate = ROOTFINDER.getRoot(func, bracket[0], bracket[1]); creditCurve = creditCurve.withRate(zeroRate, i); break; } case ZeroHazardRate: { double minValue = i == 0 ? 0.0 : creditCurve.getRTAtIndex(i - 1) / creditCurve.getTimeAtIndex(i); if (i > 0 && func.evaluate(minValue) > 0.0) { // can never fail on the first spread creditCurve = creditCurve.withRate(minValue, i); } else { guess[i] = Math.max(minValue, guess[i]); double[] bracket = BRACKER.getBracketedPoints( func, guess[i], 1.2 * guess[i], minValue, Double.POSITIVE_INFINITY); double zeroRate = ROOTFINDER.getRoot(func, bracket[0], bracket[1]); creditCurve = creditCurve.withRate(zeroRate, i); } break; } default: throw new IllegalArgumentException("unknow case " + getArbHanding()); } } return creditCurve; }
/** * Puts all the entries from the supplied map into this builder. * * <p>If a date is duplicated it will overwrite an earlier entry. * * @param map the map of points to be added * @return this builder */ public LocalDateDoubleTimeSeriesBuilder putAll(Map<LocalDate, Double> map) { ArgChecker.noNulls(map, "map"); map.entrySet().forEach(e -> put(e.getKey(), e.getValue())); return this; }
/** * The difference in PV between two market spreads. * * @param cds analytic description of a CDS traded at a certain time * @param cdsFracSpread The <b>fraction</b> spread of the CDS * @param priceType Clean or dirty price * @param yieldCurve The yield (or discount) curve * @param marketCDSs The market CDSs - these are the reference instruments used to build the * credit curve * @param marketFracSpreads The <b>fractional</b> spreads of the market CDSs * @param fracDeltaSpreads Non-negative shifts * @param fdType The finite difference type (forward, central or backward) * @return The difference in PV between two market spreads */ public double finiteDifferenceSpreadSensitivity( CdsAnalytic cds, double cdsFracSpread, CdsPriceType priceType, IsdaCompliantYieldCurve yieldCurve, CdsAnalytic[] marketCDSs, double[] marketFracSpreads, double[] fracDeltaSpreads, FiniteDifferenceType fdType) { ArgChecker.notNull(cds, "cds"); ArgChecker.noNulls(marketCDSs, "curvePoints"); ArgChecker.notEmpty(marketFracSpreads, "spreads"); ArgChecker.notEmpty(fracDeltaSpreads, "deltaSpreads"); ArgChecker.notNull(yieldCurve, "yieldCurve"); ArgChecker.notNull(priceType, "priceType"); int n = marketCDSs.length; ArgChecker.isTrue(n == marketFracSpreads.length, "speads length does not match curvePoints"); ArgChecker.isTrue( n == fracDeltaSpreads.length, "deltaSpreads length does not match curvePoints"); for (int i = 0; i < n; i++) { ArgChecker.isTrue(marketFracSpreads[i] > 0, "spreads must be positive"); ArgChecker.isTrue(fracDeltaSpreads[i] >= 0, "deltaSpreads must none negative"); ArgChecker.isTrue( fdType == FiniteDifferenceType.FORWARD || fracDeltaSpreads[i] < marketFracSpreads[i], "deltaSpread must be less spread, unless forward difference is used"); } switch (fdType) { case CENTRAL: return fdCentral( cds, cdsFracSpread, marketCDSs, marketFracSpreads, fracDeltaSpreads, yieldCurve, priceType); case FORWARD: return fdForward( cds, cdsFracSpread, marketCDSs, marketFracSpreads, fracDeltaSpreads, yieldCurve, priceType); case BACKWARD: return fdBackwards( cds, cdsFracSpread, marketCDSs, marketFracSpreads, fracDeltaSpreads, yieldCurve, priceType); default: throw new IllegalArgumentException("unknown type " + fdType); } }