bool ZeroInflationIndex::needsForecast(const Date& fixingDate) const { // 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::instance().evaluationDate(); Date todayMinusLag = today - availabilityLag_; Date historicalFixingKnown = inflationPeriod(todayMinusLag, frequency_).first-1; Date latestNeededDate = fixingDate; if (interpolated_) { // might need the next one too std::pair<Date,Date> p = inflationPeriod(fixingDate, frequency_); if (fixingDate > p.first) latestNeededDate += Period(frequency_); } if (latestNeededDate <= historicalFixingKnown) { // the fixing date is well before the availability lag, so // we know that fixings were provided. return false; } 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 Real f = timeSeries()[latestNeededDate]; return (f == Null<Real>()); } }
Rate ZeroInflationIndex::fixing(const Date& aFixingDate, bool /*forecastTodaysFixing*/) const { if (!needsForecast(aFixingDate)) { std::pair<Date,Date> lim = inflationPeriod(aFixingDate, frequency_); const TimeSeries<Real>& ts = timeSeries(); Real pastFixing = ts[lim.first]; QL_REQUIRE(pastFixing != Null<Real>(), "Missing " << name() << " fixing for " << lim.first); Real theFixing = pastFixing; if (interpolated_) { // fixings stored on first day of every period if (aFixingDate == lim.first) { // we don't actually need the next fixing theFixing = pastFixing; } else { Real pastFixing2 = ts[lim.second+1]; QL_REQUIRE(pastFixing2 != Null<Real>(), "Missing " << name() << " fixing for " << lim.second+1); // Use lagged period for interpolation std::pair<Date, Date> reference_period_lim = inflationPeriod(aFixingDate + zeroInflationTermStructure()->observationLag(), frequency_); // now linearly interpolate Real daysInPeriod = reference_period_lim.second + 1 - reference_period_lim.first; theFixing = pastFixing + (pastFixing2 - pastFixing)*(aFixingDate - lim.first) / daysInPeriod; } } return theFixing; } else { return forecastFixing(aFixingDate); } }
Rate ZeroInflationIndex::forecastFixing(const Date& fixingDate) const { // the term structure is relative to the fixing value at the base date. Date baseDate = zeroInflation_->baseDate(); QL_REQUIRE(!needsForecast(baseDate), name() << " index fixing at base date is not available"); Real baseFixing = fixing(baseDate); Date effectiveFixingDate; if (interpolated()) { effectiveFixingDate = fixingDate; } else { // start of period is the convention // so it's easier to do linear interpolation on fixings effectiveFixingDate = inflationPeriod(fixingDate, frequency()).first; } // no observation lag because it is the fixing for the date // but if index is not interpolated then that fixing is constant // for each period, hence the t uses the effectiveFixingDate // However, it's slightly safe to get the zeroRate with the // fixingDate to avoid potential problems at the edges of periods Time t = zeroInflation_->dayCounter().yearFraction(baseDate, effectiveFixingDate); bool forceLinearInterpolation = false; Rate zero = zeroInflation_->zeroRate(fixingDate, Period(0,Days), forceLinearInterpolation); // Annual compounding is the convention for zero inflation rates (or quotes) return baseFixing * std::pow(1.0 + zero, t); }
Rate ZeroInflationIndex::fixing(const Date& aFixingDate, bool /*forecastTodaysFixing*/) const { if (!needsForecast(aFixingDate)) { const TimeSeries<Real>& ts = timeSeries(); Real pastFixing = ts[aFixingDate]; QL_REQUIRE(pastFixing != Null<Real>(), "Missing " << name() << " fixing for " << aFixingDate); Real theFixing = pastFixing; if (interpolated_) { // fixings stored flat & for every day std::pair<Date,Date> lim = inflationPeriod(aFixingDate, frequency_); if (aFixingDate == lim.first) { // we don't actually need the next fixing theFixing = pastFixing; } else { Date fixingDate2 = aFixingDate + Period(frequency_); Real pastFixing2 = ts[fixingDate2]; QL_REQUIRE(pastFixing2 != Null<Real>(), "Missing " << name() << " fixing for " << fixingDate2); // now linearly interpolate Real daysInPeriod = lim.second+1 - lim.first; theFixing = pastFixing + (pastFixing2-pastFixing)*(aFixingDate-lim.first)/daysInPeriod; } } return theFixing; } else { return forecastFixing(aFixingDate); } }
bool MultiplicativePriceSeasonality::isConsistent(const InflationTermStructure& iTS) const { // 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() == Daily) return true; if(Size(this->frequency()) == seasonalityFactors().size()) return true; // how many years do you need to test? Size nTest = seasonalityFactors().size() / this->frequency(); // ... relative to the start of the inflation curve std::pair<Date,Date> lim = inflationPeriod(iTS.baseDate(), iTS.frequency()); Date curveBaseDate = lim.second; Real factorBase = this->seasonalityFactor(curveBaseDate); Real eps = 0.00001; for (Size i = 1; i < nTest; i++) { Real factorAt = this->seasonalityFactor(curveBaseDate+Period(i,Years)); QL_REQUIRE(std::fabs(factorAt-factorBase)<eps,"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; }
Real CPICashFlow::amount() const { Real I0 = baseFixing(); Real I1; // what interpolation do we use? Index / flat / linear if (interpolation() == CPI::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 std::pair<Date,Date> dd = inflationPeriod(fixingDate(), frequency()); Real indexStart = index()->fixing(dd.first); if (interpolation() == CPI::Linear) { Real indexEnd = index()->fixing(dd.second+Period(1,Days)); // linear interpolation //std::cout << indexStart << " and " << indexEnd << std::endl; I1 = indexStart + (indexEnd - indexStart) * (fixingDate() - dd.first) / ( (dd.second+Period(1,Days)) - dd.first); // 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); }
Rate MultiplicativePriceSeasonality::correctYoYRate(const Date &d, const Rate r, const InflationTermStructure& iTS) const { std::pair<Date,Date> lim = inflationPeriod(iTS.baseDate(), iTS.frequency()); Date curveBaseDate = lim.second; return seasonalityCorrection(r, d, iTS.dayCounter(), curveBaseDate, false); }
Rate CPICoupon::indexFixing(const Date &d) const { // you may want to modify the interpolation of the index // this gives you the chance Rate I1; // what interpolation do we use? Index / flat / linear if (observationInterpolation() == CPI::AsIndex) { I1 = cpiIndex()->fixing(d); } else { // work out what it should be std::pair<Date,Date> dd = inflationPeriod(d, cpiIndex()->frequency()); Real indexStart = cpiIndex()->fixing(dd.first); if (observationInterpolation() == CPI::Linear) { Real indexEnd = cpiIndex()->fixing(dd.second+Period(1,Days)); // linear interpolation I1 = indexStart + (indexEnd - indexStart) * (d - dd.first) / (Real)( (dd.second+Period(1,Days)) - dd.first); // 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; } } return I1; }
Rate ZeroInflationIndex::fixing(const Date& aFixingDate, bool /*forecastTodaysFixing*/) const { // Stored fixings are always non-interpolated. // If an interpolated fixing is required then // the availability lag + one inflation period // must have passsed to use historical fixings // (because you need the next one to interpolate). // The interpolation is calculated (linearly) on demand. Date today = Settings::instance().evaluationDate(); Date todayMinusLag = today - availabilityLag_; std::pair<Date,Date> lim = inflationPeriod(todayMinusLag, frequency_); Date historicalFixingKnown = lim.first-1; Date fixingDateNeeded = aFixingDate; if (interpolated_) { // need the next one too fixingDateNeeded = fixingDateNeeded + Period(frequency_); } if (fixingDateNeeded <= historicalFixingKnown) { Real pastFixing = IndexManager::instance().getHistory(name())[aFixingDate]; QL_REQUIRE(pastFixing != Null<Real>(), "Missing " << name() << " fixing for " << aFixingDate); Real theFixing = pastFixing; if (interpolated_) { // fixings stored flat & for every day Date fixingDate2 = aFixingDate + Period(frequency_); Real pastFixing2 = IndexManager::instance().getHistory(name())[fixingDate2]; QL_REQUIRE(pastFixing2 != Null<Real>(), "Missing " << name() << " fixing for " << fixingDate2); // now linearly interpolate std::pair<Date,Date> lim = inflationPeriod(aFixingDate, frequency_); Real daysInPeriod = lim.second+1 - lim.first; theFixing = pastFixing + (pastFixing2-pastFixing)*(aFixingDate-lim.first)/daysInPeriod; } return theFixing; } else { return forecastFixing(aFixingDate); } }
// start of curve data static Date initialDate(const ZeroInflationTermStructure* t) { if (t->indexIsInterpolated()) { return t->referenceDate() - t->observationLag(); } else { return inflationPeriod(t->referenceDate() - t->observationLag(), t->frequency()).first; } }
InterpolatedYoYInflationCurve<Interpolator>:: InterpolatedYoYInflationCurve(const Date& referenceDate, const Calendar& calendar, const DayCounter& dayCounter, const Period& lag, Frequency frequency, bool indexIsInterpolated, const Handle<YieldTermStructure>& yTS, const std::vector<Date>& dates, const std::vector<Rate>& rates, const Interpolator& interpolator) : YoYInflationTermStructure(referenceDate, calendar, dayCounter, rates[0], lag, frequency, indexIsInterpolated, yTS), InterpolatedCurve<Interpolator>(std::vector<Time>(), rates, interpolator), dates_(dates) { QL_REQUIRE(dates_.size()>1, "too few dates: " << dates_.size()); // check that the data starts from the beginning, // i.e. referenceDate - lag, at least must be in the relevant // period std::pair<Date,Date> lim = inflationPeriod(yTS->referenceDate() - this->observationLag(), frequency); QL_REQUIRE(lim.first <= dates_[0] && dates_[0] <= lim.second, "first data date is not in base period, date: " << dates_[0] << " not within [" << lim.first << "," << lim.second << "]"); QL_REQUIRE(this->data_.size() == dates_.size(), "indices/dates count mismatch: " << this->data_.size() << " vs " << dates_.size()); this->times_.resize(dates_.size()); this->times_[0] = timeFromReference(dates_[0]); for (Size i = 1; i < dates_.size(); i++) { QL_REQUIRE(dates_[i] > dates_[i-1], "dates not sorted"); // YoY inflation data may be positive or negative // but must be greater than -1 QL_REQUIRE(this->data_[i] > -1.0, "year-on-year inflation data < -100 %"); // this can be negative this->times_[i] = timeFromReference(dates_[i]); QL_REQUIRE(!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(this->times_.begin(), this->times_.end(), this->data_.begin()); this->interpolation_.update(); }
Rate YoYInflationTermStructure::yoyRate(const Date &d, const Period& instObsLag, bool forceLinearInterpolation, bool extrapolate) const { Period useLag = instObsLag; if (instObsLag == Period(-1,Days)) { useLag = observationLag(); } Rate yoyRate; if (forceLinearInterpolation) { std::pair<Date,Date> dd = inflationPeriod(d-useLag, frequency()); dd.second = dd.second + Period(1,Days); Real dp = dd.second - dd.first; Real dt = (d-useLag) - dd.first; // if we are interpolating we only check the exact point // this prevents falling off the end at curve maturity InflationTermStructure::checkRange(d, extrapolate); Time t1 = timeFromReference(dd.first); Time t2 = timeFromReference(dd.second); Rate y1 = yoyRateImpl(t1); Rate y2 = yoyRateImpl(t2); yoyRate = y1 + (y2-y1) * (dt/dp); } else { if (indexIsInterpolated()) { InflationTermStructure::checkRange(d-useLag, extrapolate); Time t = timeFromReference(d-useLag); yoyRate = yoyRateImpl(t); } else { std::pair<Date,Date> dd = inflationPeriod(d-useLag, frequency()); InflationTermStructure::checkRange(dd.first, extrapolate); Time t = timeFromReference(dd.first); yoyRate = yoyRateImpl(t); } } if (hasSeasonality()) { yoyRate = seasonality()->correctYoYRate(d-useLag, yoyRate, *this); } return yoyRate; }
Time inflationYearFraction(Frequency f, bool indexIsInterpolated, const DayCounter &dayCounter, const Date &d1, const Date &d2) { Time t=0; if (indexIsInterpolated) { // N.B. we do not use linear interpolation between flat // fixing forecasts for forecasts. This avoids awkwardnesses // when bootstrapping the inflation curve. t = dayCounter.yearFraction(d1, d2); } else { // I.e. fixing is constant for the whole inflation period. // Use the value for half way along the period. // But the inflation time is the time between period starts std::pair<Date,Date> limD1 = inflationPeriod(d1, f); std::pair<Date,Date> limD2 = inflationPeriod(d2, f); t = dayCounter.yearFraction(limD1.first, limD2.first); } return t; }
Real YoYInflationIndex::forecastFixing(const Date& fixingDate) const { Date d; if (interpolated()) { d = fixingDate; } else { // if the value is not interpolated use the starting value // by internal convention this will be consistent std::pair<Date,Date> lim = inflationPeriod(fixingDate, frequency_); d = lim.first; } return yoyInflation_->yoyRate(d,0*Days); }
Date YoYOptionletVolatilitySurface::baseDate() const { // 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 yoy term structure. if (indexIsInterpolated()) { return referenceDate() - observationLag(); } else { return inflationPeriod(referenceDate() - observationLag(), frequency()).first; } }
void InflationIndex::addFixing(const Date& fixingDate, Real fixing, bool forceOverwrite) { std::pair<Date,Date> lim = inflationPeriod(fixingDate, frequency_); Size n = lim.second - lim.first + 1; std::vector<Date> dates(n); std::vector<Rate> rates(n); for (Size i=0; i<n; ++i) { dates[i] = lim.first + i; rates[i] = fixing; } Index::addFixings(dates.begin(), dates.end(), rates.begin(), forceOverwrite); }
Real MultiplicativePriceSeasonality::seasonalityFactor(const Date &to) const { Date from = seasonalityBaseDate(); Frequency factorFrequency = frequency(); Size nFactors = seasonalityFactors().size(); Period factorPeriod(factorFrequency); Size which = 0; if (from==to) { which = 0; } else { // days, weeks, months, years are the only time unit possibilities Integer diffDays = std::abs(to - from); // in days Integer dir = 1; if(from > to)dir = -1; Integer diff; if (factorPeriod.units() == Days) { diff = dir*diffDays; } else if (factorPeriod.units() == Weeks) { diff = dir * (diffDays / 7); } else if (factorPeriod.units() == Months) { std::pair<Date,Date> lim = inflationPeriod(to, factorFrequency); diff = diffDays / (31*factorPeriod.length()); Date go = from + dir*diff*factorPeriod; while ( !(lim.first <= go && go <= lim.second) ) { go += dir*factorPeriod; diff++; } diff=dir*diff; } else if (factorPeriod.units() == Years) { QL_FAIL("seasonality period time unit is not allowed to be : " << factorPeriod.units()); } else { 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]; }
//! needed for total variance calculations Time YoYOptionletVolatilitySurface::timeFromBase(const Date &maturityDate, const Period& obsLag) const { Period useLag = obsLag; if (obsLag==Period(-1,Days)) { useLag = observationLag(); } Date useDate; if (indexIsInterpolated()) { useDate = maturityDate - useLag; } else { useDate = inflationPeriod(maturityDate - useLag, frequency()).first; } // This assumes that the inflation term structure starts // as late as possible given the inflation index definition, // which is the usual case. return dayCounter().yearFraction(baseDate(), useDate); }
Volatility YoYOptionletVolatilitySurface::volatility(const Date& maturityDate, Rate strike, const Period &obsLag, bool extrapolate) const { Period useLag = obsLag; if (obsLag==Period(-1,Days)) { useLag = observationLag(); } if (indexIsInterpolated()) { YoYOptionletVolatilitySurface::checkRange(maturityDate-useLag, strike, extrapolate); Time t = timeFromReference(maturityDate-useLag); return volatilityImpl(t,strike); } else { std::pair<Date,Date> dd = inflationPeriod(maturityDate-useLag, frequency()); YoYOptionletVolatilitySurface::checkRange(dd.first, strike, extrapolate); Time t = timeFromReference(dd.first); return volatilityImpl(t,strike); } }
Rate YoYInflationIndex::fixing(const Date& fixingDate, bool /*forecastTodaysFixing*/) const { Date today = Settings::instance().evaluationDate(); Date todayMinusLag = today - availabilityLag_; std::pair<Date,Date> lim = inflationPeriod(todayMinusLag, frequency_); Date lastFix = lim.first-1; Date flatMustForecastOn = lastFix+1; Date interpMustForecastOn = lastFix+1 - Period(frequency_); if (interpolated() && fixingDate >= interpMustForecastOn) { return forecastFixing(fixingDate); } if (!interpolated() && fixingDate >= flatMustForecastOn) { return forecastFixing(fixingDate); } // four cases with ratio() and interpolated() const TimeSeries<Real>& ts = timeSeries(); if (ratio()) { if(interpolated()){ // IS ratio, IS interpolated std::pair<Date,Date> lim = inflationPeriod(fixingDate, frequency_); Date fixMinus1Y=NullCalendar().advance(fixingDate, -1*Years, ModifiedFollowing); std::pair<Date,Date> limBef = inflationPeriod(fixMinus1Y, frequency_); Real dp= lim.second + 1 - lim.first; Real dpBef=limBef.second + 1 - limBef.first; Real dl = fixingDate-lim.first; // potentially does not work on 29th Feb Real dlBef = fixMinus1Y - limBef.first; // get the four relevant fixings // recall that they are stored flat for every day Rate limFirstFix = ts[lim.first]; QL_REQUIRE(limFirstFix != Null<Rate>(), "Missing " << name() << " fixing for " << lim.first ); Rate limSecondFix = ts[lim.second+1]; QL_REQUIRE(limSecondFix != Null<Rate>(), "Missing " << name() << " fixing for " << lim.second+1 ); Rate limBefFirstFix = ts[limBef.first]; QL_REQUIRE(limBefFirstFix != Null<Rate>(), "Missing " << name() << " fixing for " << limBef.first ); Rate limBefSecondFix = IndexManager::instance().getHistory(name())[limBef.second+1]; QL_REQUIRE(limBefSecondFix != Null<Rate>(), "Missing " << name() << " fixing for " << limBef.second+1 ); Real linearNow = limFirstFix + (limSecondFix-limFirstFix)*dl/dp; Real linearBef = limBefFirstFix + (limBefSecondFix-limBefFirstFix)*dlBef/dpBef; Rate wasYES = linearNow / linearBef - 1.0; return wasYES; } else { // IS ratio, NOT interpolated Rate pastFixing = ts[fixingDate]; QL_REQUIRE(pastFixing != Null<Rate>(), "Missing " << name() << " fixing for " << fixingDate); Date previousDate = fixingDate - 1*Years; Rate previousFixing = ts[previousDate]; QL_REQUIRE(previousFixing != Null<Rate>(), "Missing " << name() << " fixing for " << previousDate ); return pastFixing/previousFixing - 1.0; } } else { // NOT ratio if (interpolated()) { // NOT ratio, IS interpolated std::pair<Date,Date> lim = inflationPeriod(fixingDate, frequency_); Real dp= lim.second + 1 - lim.first; Real dl = fixingDate-lim.first; Rate limFirstFix = ts[lim.first]; QL_REQUIRE(limFirstFix != Null<Rate>(), "Missing " << name() << " fixing for " << lim.first ); Rate limSecondFix = ts[lim.second+1]; QL_REQUIRE(limSecondFix != Null<Rate>(), "Missing " << name() << " fixing for " << lim.second+1 ); Real linearNow = limFirstFix + (limSecondFix-limFirstFix)*dl/dp; return linearNow; } else { // NOT ratio, NOT interpolated // so just flat Rate pastFixing = ts[fixingDate]; QL_REQUIRE(pastFixing != Null<Rate>(), "Missing " << name() << " fixing for " << fixingDate); return pastFixing; } } // QL_FAIL("YoYInflationIndex::fixing, should never get here"); }