public ZeroCouponInflationSwapHelper( Handle <Quote> quote, Period swapObsLag, // lag on swap observation of index Date maturity, Calendar calendar, // index may have null calendar as valid on every day BusinessDayConvention paymentConvention, DayCounter dayCounter, ZeroInflationIndex zii) : base(quote) { swapObsLag_ = swapObsLag; maturity_ = maturity; calendar_ = calendar; paymentConvention_ = paymentConvention; dayCounter_ = dayCounter; zii_ = zii; if (zii_.interpolated()) { // if interpolated then simple earliestDate_ = maturity_ - swapObsLag_; latestDate_ = maturity_ - swapObsLag_; } else { // but if NOT interpolated then the value is valid // for every day in an inflation period so you actually // get an extended validity, however for curve building // just put the first date because using that convention // for the base date throughout KeyValuePair <Date, Date> limStart = Utils.inflationPeriod(maturity_ - swapObsLag_, zii_.frequency()); earliestDate_ = limStart.Key; latestDate_ = limStart.Key; } // check that the observation lag of the swap // is compatible with the availability lag of the index AND // it's interpolation (assuming the start day is spot) if (zii_.interpolated()) { Period pShift = new Period(zii_.frequency()); if ((swapObsLag_ - pShift) <= zii_.availabilityLag()) { throw new ApplicationException( "inconsistency between swap observation of index " + swapObsLag_ + " index availability " + zii_.availabilityLag() + " index period " + pShift + " and index availability " + zii_.availabilityLag() + " need (obsLag-index period) > availLag"); } } Settings.registerWith(update); }
public InterpolatedYoYInflationCurve(Date referenceDate, Calendar calendar, DayCounter dayCounter, Period lag, Frequency frequency, bool indexIsInterpolated, Handle <YieldTermStructure> yTS, List <Date> dates, List <double> rates, Interpolator interpolator) : base(referenceDate, calendar, dayCounter, rates[0], lag, frequency, indexIsInterpolated, yTS) { times_ = new List <double>(); dates_ = dates; data_ = rates; interpolator_ = interpolator ?? FastActivator <Interpolator> .Create(); Utils.QL_REQUIRE(dates_.Count > 1, () => "too few dates: " + dates_.Count); // check that the data starts from the beginning, // i.e. referenceDate - lag, at least must be in the relevant // period KeyValuePair <Date, Date> lim = Utils.inflationPeriod(yTS.link.referenceDate() - this.observationLag(), frequency); Utils.QL_REQUIRE(lim.Key <= dates_[0] && dates_[0] <= lim.Value, () => "first data date is not in base period, date: " + dates_[0] + " not within [" + lim.Key + "," + lim.Value + "]"); Utils.QL_REQUIRE(this.data_.Count == dates_.Count, () => "indices/dates count mismatch: " + this.data_.Count + " vs " + dates_.Count); this.times_ = new InitializedList <double>(dates_.Count); this.times_[0] = timeFromReference(dates_[0]); for (int i = 1; i < dates_.Count; i++) { Utils.QL_REQUIRE(dates_[i] > dates_[i - 1], () => "dates not sorted"); // YoY inflation data may be positive or negative // but must be greater than -1 Utils.QL_REQUIRE(this.data_[i] > -1.0, () => "year-on-year inflation data < -100 %"); // this can be negative this.times_[i] = timeFromReference(dates_[i]); Utils.QL_REQUIRE(!Utils.close(this.times_[i], this.times_[i - 1]), () => "two dates correspond to the same time " + "under this curve's day count convention"); } this.interpolation_ = this.interpolator_.interpolate(times_, times_.Count, data_); this.interpolation_.update(); }
public Date initialDate(ZeroInflationTermStructure t) { if (t.indexIsInterpolated()) { return(t.referenceDate() - t.observationLag()); } else { return(Utils.inflationPeriod(t.referenceDate() - t.observationLag(), t.frequency()).Key); } }
public double zeroRate(Date d, Period instObsLag, bool forceLinearInterpolation, bool extrapolate) { Period useLag = instObsLag; if (instObsLag == new Period(-1, TimeUnit.Days)) { useLag = observationLag(); } double zeroRate; if (forceLinearInterpolation) { KeyValuePair <Date, Date> dd = Utils.inflationPeriod(d - useLag, frequency()); Date ddValue = dd.Value + new Period(1, TimeUnit.Days); double dp = ddValue - dd.Key; double dt = d - dd.Key; // if we are interpolating we only check the exact point // this prevents falling off the end at curve maturity base.checkRange(d, extrapolate); double t1 = timeFromReference(dd.Key); double t2 = timeFromReference(ddValue); zeroRate = zeroRateImpl(t1) + zeroRateImpl(t2) * (dt / dp); } else { if (indexIsInterpolated()) { base.checkRange(d - useLag, extrapolate); double t = timeFromReference(d - useLag); zeroRate = zeroRateImpl(t); } else { KeyValuePair <Date, Date> dd = Utils.inflationPeriod(d - useLag, frequency()); base.checkRange(dd.Key, extrapolate); double t = timeFromReference(dd.Key); zeroRate = zeroRateImpl(t); } } if (hasSeasonality()) { zeroRate = seasonality().correctZeroRate(d - useLag, zeroRate, this); } return(zeroRate); }
public override Date maxDate() { Date d; if (indexIsInterpolated()) { d = dates_.Last(); } else { d = Utils.inflationPeriod(dates_.Last(), frequency()).Value; } return(d); }
public override void addFixing(Date fixingDate, double fixing, bool forceOverwrite) { KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_); int n = lim.Value - lim.Key + 1; List <Date> dates = new List <Date>(n); List <double> rates = new List <double>(n); for (int i = 0; i < n; ++i) { dates.Add(lim.Key + i); rates.Add(fixing); } base.addFixings(dates, rates, forceOverwrite); }
public virtual Date baseDate() { // Depends on interpolation, or not, of observed index // and observation lag with which it was built. // We want this to work even if the index does not // have a term structure. if (indexIsInterpolated()) { return(referenceDate() - observationLag()); } else { return(Utils.inflationPeriod(referenceDate() - observationLag(), frequency()).Key); } }
private double forecastFixing(Date fixingDate) { Date d; if (interpolated()) { d = fixingDate; } else { // if the value is not interpolated use the starting value // by internal convention this will be consistent KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_); d = lim.Key; } return(yoyInflation_.link.yoyRate(d, new Period(0, TimeUnit.Days))); }
private bool needsForecast(Date fixingDate) { // Stored fixings are always non-interpolated. // If an interpolated fixing is required then // the availability lag + one inflation period // must have passed to use historical fixings // (because you need the next one to interpolate). // The interpolation is calculated (linearly) on demand. Date today = Settings.evaluationDate(); Date todayMinusLag = today - availabilityLag_; Date historicalFixingKnown = Utils.inflationPeriod(todayMinusLag, frequency_).Key - 1; Date latestNeededDate = fixingDate; if (interpolated_) { // might need the next one too KeyValuePair <Date, Date> p = Utils.inflationPeriod(fixingDate, frequency_); if (fixingDate > p.Key) { latestNeededDate = latestNeededDate + new Period(frequency_); } } if (latestNeededDate <= historicalFixingKnown) { // the fixing date is well before the availability lag, so // we know that fixings were provided. return(false); } else if (latestNeededDate > today) { // the fixing can't be available, no matter what's in the // time series return(true); } else { // we're not sure, but the fixing might be there so we // check. Todo: check which fixings are not possible, to // avoid using fixings in the future return(!timeSeries().ContainsKey(latestNeededDate)); } }
protected override double seasonalityCorrection(double rate,Date atDate,DayCounter dc,Date curveBaseDate,bool isZeroRate) { double indexFactor = this.seasonalityFactor(atDate); // Getting seasonality correction double f = 0; if (isZeroRate) { KeyValuePair<Date,Date> lim = Utils.inflationPeriod(curveBaseDate, Frequency.Monthly); double timeFromCurveBase = dc.yearFraction(lim.Key, atDate); f = Math.Pow(indexFactor, 1/timeFromCurveBase); } else { Utils.QL_FAIL("Seasonal Kerkhof model is not defined on YoY rates"); } return (rate + 1)*f - 1; }
//! redefined to use baseFixing() and interpolation public override double amount() { double I0 = baseFixing(); double I1; // what interpolation do we use? Index / flat / linear if (interpolation() == InterpolationType.AsIndex) { I1 = index().fixing(fixingDate()); } else { // work out what it should be //std::cout << fixingDate() << " and " << frequency() << std::endl; //std::pair<Date,Date> dd = inflationPeriod(fixingDate(), frequency()); //std::cout << fixingDate() << " and " << dd.first << " " << dd.second << std::endl; // work out what it should be KeyValuePair <Date, Date> dd = Utils.inflationPeriod(fixingDate(), frequency()); double indexStart = index().fixing(dd.Key); if (interpolation() == InterpolationType.Linear) { double indexEnd = index().fixing(dd.Value + new Period(1, TimeUnit.Days)); // linear interpolation //std::cout << indexStart << " and " << indexEnd << std::endl; I1 = indexStart + (indexEnd - indexStart) * (fixingDate() - dd.Key) / ((dd.Value + new Period(1, TimeUnit.Days)) - dd.Key); // can't get to next period's value within current period } else { // no interpolation, i.e. flat = constant, so use start-of-period value I1 = indexStart; } } if (growthOnly()) { return(notional() * (I1 / I0 - 1.0)); } else { return(notional() * (I1 / I0)); } }
/*! \warning the forecastTodaysFixing parameter (required by * the Index interface) is currently ignored. */ public override double fixing(Date aFixingDate, bool forecastTodaysFixing = false) { if (!needsForecast(aFixingDate)) { KeyValuePair <Date, Date> lim = Utils.inflationPeriod(aFixingDate, frequency_); Utils.QL_REQUIRE(IndexManager.instance().getHistory(name()).value().ContainsKey(lim.Key), () => "Missing " + name() + " fixing for " + lim.Key); double?pastFixing = IndexManager.instance().getHistory(name()).value()[lim.Key]; double?theFixing = pastFixing; if (interpolated_) { // fixings stored on first day of every period if (aFixingDate == lim.Key) { // we don't actually need the next fixing theFixing = pastFixing; } else { Utils.QL_REQUIRE(IndexManager.instance().getHistory(name()).value().ContainsKey(lim.Value + 1), () => "Missing " + name() + " fixing for " + (lim.Value + 1)); double?pastFixing2 = IndexManager.instance().getHistory(name()).value()[lim.Value + 1]; // Use lagged period for interpolation KeyValuePair <Date, Date> reference_period_lim = Utils.inflationPeriod(aFixingDate + zeroInflationTermStructure().link.observationLag(), frequency_); // now linearly interpolate double daysInPeriod = reference_period_lim.Value + 1 - reference_period_lim.Key; theFixing = pastFixing + (pastFixing2 - pastFixing) * (aFixingDate - lim.Key) / daysInPeriod; } } return(theFixing.GetValueOrDefault()); } else { return(forecastFixing(aFixingDate)); } }
public double volatility(Date maturityDate, double strike, Period obsLag, bool extrapolate) { Period useLag = obsLag; if (obsLag == new Period(-1, TimeUnit.Days)) { useLag = observationLag(); } if (indexIsInterpolated()) { checkRange(maturityDate - useLag, strike, extrapolate); double t = timeFromReference(maturityDate - useLag); return(volatilityImpl(t, strike)); } else { KeyValuePair <Date, Date> dd = Utils.inflationPeriod(maturityDate - useLag, frequency()); checkRange(dd.Key, strike, extrapolate); double t = timeFromReference(dd.Key); return(volatilityImpl(t, strike)); } }
public override bool isConsistent(InflationTermStructure iTS) { // If multi-year is the specification consistent with the term structure start date? // We do NOT test daily seasonality because this will, in general, never be consistent // given weekends, holidays, leap years, etc. if (this.frequency() == Frequency.Daily) { return(true); } if ((int)this.frequency() == seasonalityFactors().Count) { return(true); } // how many years do you need to test? int nTest = seasonalityFactors().Count / (int)this.frequency(); // ... relative to the start of the inflation curve KeyValuePair <Date, Date> lim = Utils.inflationPeriod(iTS.baseDate(), iTS.frequency()); Date curveBaseDate = lim.Value; double factorBase = this.seasonalityFactor(curveBaseDate); double eps = 0.00001; for (int i = 1; i < nTest; i++) { double factorAt = this.seasonalityFactor(curveBaseDate + new Period(i, TimeUnit.Years)); if (Math.Abs(factorAt - factorBase) >= eps) { throw new ApplicationException("seasonality is inconsistent with inflation " + "term structure, factors " + factorBase + " and later factor " + factorAt + ", " + i + " years later from inflation curve " + " with base date at " + curveBaseDate); } } return(true); }
/*! \warning the forecastTodaysFixing parameter (required by * the Index interface) is currently ignored. */ public override double fixing(Date aFixingDate, bool forecastTodaysFixing) { if (!needsForecast(aFixingDate)) { if (!IndexManager.instance().getHistory(name()).value().ContainsKey(aFixingDate)) { throw new ApplicationException("Missing " + name() + " fixing for " + aFixingDate); } double pastFixing = IndexManager.instance().getHistory(name()).value()[aFixingDate]; double theFixing = pastFixing; if (interpolated_) { // fixings stored flat & for every day Date fixingDate2 = aFixingDate + new Period(frequency_); if (!IndexManager.instance().getHistory(name()).value().ContainsKey(fixingDate2)) { throw new ApplicationException("Missing " + name() + " fixing for " + fixingDate2); } double pastFixing2 = IndexManager.instance().getHistory(name()).value()[fixingDate2]; // now linearly interpolate KeyValuePair <Date, Date> lim2 = Utils.inflationPeriod(aFixingDate, frequency_); double daysInPeriod = lim2.Value + 1 - lim2.Key; theFixing = pastFixing + (pastFixing2 - pastFixing) * (aFixingDate - lim2.Key) / daysInPeriod; } return(theFixing); } else { return(forecastFixing(aFixingDate)); } }
public override void calculate() { double npv = 0.0; // what is the difference between the observationLag of the surface // and the observationLag of the cap/floor? // TODO next line will fail if units are different Period lagDiff = arguments_.observationLag - priceSurf_.link.observationLag(); // next line will fail if units are different if Period() is not well written Utils.QL_REQUIRE(lagDiff >= new Period(0, TimeUnit.Months), () => "InterpolatingCPICapFloorEngine: " + "lag difference must be non-negative: " + lagDiff); // we now need an effective maturity to use in the price surface because this uses // maturity of calibration instruments as its time axis, N.B. this must also // use the roll because the surface does Date effectiveMaturity = arguments_.payDate - lagDiff; // what interpolation do we use? Index / flat / linear if (arguments_.observationInterpolation == InterpolationType.AsIndex) { // same as index means we can just use the price surface // since this uses the index if (arguments_.type == Option.Type.Call) { npv = priceSurf_.link.capPrice(effectiveMaturity, arguments_.strike); } else { npv = priceSurf_.link.floorPrice(effectiveMaturity, arguments_.strike); } } else { KeyValuePair <Date, Date> dd = Utils.inflationPeriod(effectiveMaturity, arguments_.infIndex.link.frequency()); double priceStart = 0.0; if (arguments_.type == Option.Type.Call) { priceStart = priceSurf_.link.capPrice(dd.Key, arguments_.strike); } else { priceStart = priceSurf_.link.floorPrice(dd.Key, arguments_.strike); } // if we use a flat index vs the interpolated one ... if (arguments_.observationInterpolation == InterpolationType.Flat) { // then use the price for the first day in the period because the value cannot change after then npv = priceStart; } else { // linear interpolation will be very close double priceEnd = 0.0; if (arguments_.type == Option.Type.Call) { priceEnd = priceSurf_.link.capPrice((dd.Value + new Period(1, TimeUnit.Days)), arguments_.strike); } else { priceEnd = priceSurf_.link.floorPrice((dd.Value + new Period(1, TimeUnit.Days)), arguments_.strike); } npv = priceStart + (priceEnd - priceStart) * (effectiveMaturity - dd.Key) / ((dd.Value + new Period(1, TimeUnit.Days)) - dd.Key); // can't get to next period' } } results_.value = npv; }
public double fixing(Date fixingDate, bool forecastTodaysFixing) { Date today = Settings.evaluationDate(); Date todayMinusLag = today - availabilityLag_; KeyValuePair <Date, Date> limm = Utils.inflationPeriod(todayMinusLag, frequency_); Date lastFix = limm.Key - 1; Date flatMustForecastOn = lastFix + 1; Date interpMustForecastOn = lastFix + 1 - new Period(frequency_); if (interpolated() && fixingDate >= interpMustForecastOn) { return(forecastFixing(fixingDate)); } if (!interpolated() && fixingDate >= flatMustForecastOn) { return(forecastFixing(fixingDate)); } // four cases with ratio() and interpolated() if (ratio()) { if (interpolated()) { // IS ratio, IS interpolated KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_); Date fixMinus1Y = new NullCalendar().advance(fixingDate, new Period(-1, TimeUnit.Years), BusinessDayConvention.ModifiedFollowing); KeyValuePair <Date, Date> limBef = Utils.inflationPeriod(fixMinus1Y, frequency_); double dp = lim.Value + 1 - lim.Key; double dpBef = limBef.Value + 1 - limBef.Key; double dl = fixingDate - lim.Key; // potentially does not work on 29th Feb double dlBef = fixMinus1Y - limBef.Key; // get the four relevant fixings // recall that they are stored flat for every day double limFirstFix = IndexManager.instance().getHistory(name()).value()[lim.Key]; if (limFirstFix == null) { throw new ApplicationException("Missing " + name() + " fixing for " + lim.Key); } double limSecondFix = IndexManager.instance().getHistory(name()).value()[lim.Value + 1]; if (limSecondFix == null) { throw new ApplicationException("Missing " + name() + " fixing for " + lim.Value + 1); } double limBefFirstFix = IndexManager.instance().getHistory(name()).value()[limBef.Key]; if (limBefFirstFix == null) { throw new ApplicationException("Missing " + name() + " fixing for " + limBef.Key); } double limBefSecondFix = IndexManager.instance().getHistory(name()).value()[limBef.Value + 1]; if (limBefSecondFix == null) { throw new ApplicationException("Missing " + name() + " fixing for " + limBef.Value + 1); } double linearNow = limFirstFix + (limSecondFix - limFirstFix) * dl / dp; double linearBef = limBefFirstFix + (limBefSecondFix - limBefFirstFix) * dlBef / dpBef; double wasYES = linearNow / linearBef - 1.0; return(wasYES); } else { // IS ratio, NOT interpolated double pastFixing = IndexManager.instance().getHistory(name()).value()[fixingDate]; if (pastFixing == null) { throw new ApplicationException("Missing " + name() + " fixing for " + fixingDate); } Date previousDate = fixingDate - new Period(1, TimeUnit.Years); double previousFixing = IndexManager.instance().getHistory(name()).value()[previousDate]; if (previousFixing == null) { throw new ApplicationException("Missing " + name() + " fixing for " + previousDate); } return(pastFixing / previousFixing - 1.0); } } else { // NOT ratio if (interpolated()) { // NOT ratio, IS interpolated KeyValuePair <Date, Date> lim = Utils.inflationPeriod(fixingDate, frequency_); double dp = lim.Value + 1 - lim.Key; double dl = fixingDate - lim.Key; double limFirstFix = IndexManager.instance().getHistory(name()).value()[lim.Key]; if (limFirstFix == null) { throw new ApplicationException("Missing " + name() + " fixing for " + lim.Key); } double limSecondFix = IndexManager.instance().getHistory(name()).value()[lim.Value + 1]; if (limSecondFix == null) { throw new ApplicationException("Missing " + name() + " fixing for " + lim.Value + 1); } double linearNow = limFirstFix + (limSecondFix - limFirstFix) * dl / dp; return(linearNow); } else { // NOT ratio, NOT interpolated // so just flat double pastFixing = IndexManager.instance().getHistory(name()).value()[fixingDate]; if (pastFixing == null) { throw new ApplicationException("Missing " + name() + " fixing for " + fixingDate); } return(pastFixing); } } // QL_FAIL("YoYInflationIndex::fixing, should never get here"); }
public override double correctYoYRate(Date d, double r, InflationTermStructure iTS) { KeyValuePair<Date,Date> lim = Utils.inflationPeriod(iTS.baseDate(), iTS.frequency()); Date curveBaseDate = lim.Value; return seasonalityCorrection(r, d, iTS.dayCounter(), curveBaseDate, false); }
//! The factor returned is NOT normalized relative to ANYTHING. public virtual double seasonalityFactor(Date to) { Date from = seasonalityBaseDate(); Frequency factorFrequency = frequency(); int nFactors = seasonalityFactors().Count; Period factorPeriod = new Period(factorFrequency); int which = 0; if (from == to) { which = 0; } else { // days, weeks, months, years are the only time unit possibilities int diffDays = Math.Abs(to - from); // in days int dir = 1; if (from > to) { dir = -1; } int diff = 0; if (factorPeriod.units() == TimeUnit.Days) { diff = dir * diffDays; } else if (factorPeriod.units() == TimeUnit.Weeks) { diff = dir * (diffDays / 7); } else if (factorPeriod.units() == TimeUnit.Months) { KeyValuePair <Date, Date> lim = Utils.inflationPeriod(to, factorFrequency); diff = diffDays / (31 * factorPeriod.length()); Date go = from + dir * diff * factorPeriod; while (!(lim.Key <= go && go <= lim.Value)) { go += dir * factorPeriod; diff++; } diff = dir * diff; } else if (factorPeriod.units() == TimeUnit.Years) { Utils.QL_FAIL("seasonality period time unit is not allowed to be : " + factorPeriod.units()); } else { Utils.QL_FAIL("Unknown time unit: " + factorPeriod.units()); } // now adjust to the available number of factors, direction dependent if (dir == 1) { which = diff % nFactors; } else { which = (nFactors - (-diff % nFactors)) % nFactors; } } return(seasonalityFactors()[which]); }