Time SwaptionVolatilityStructure::swapLength(const Period& p) const { QL_REQUIRE(p.length()>0, "non-positive swap tenor (" << p << ") given"); switch (p.units()) { case Months: return p.length()/12.0; case Years: return static_cast<Time>(p.length()); default: QL_FAIL("invalid Time Unit (" << p.units() << ") for swap length"); } }
AmortizingFixedRateBond::AmortizingFixedRateBond( Natural settlementDays, const Calendar& calendar, Real initialFaceAmount, const Date& startDate, const Period& bondTenor, const Frequency& sinkingFrequency, const Rate coupon, const DayCounter& accrualDayCounter, BusinessDayConvention paymentConvention, const Date& issueDate) : Bond(settlementDays, calendar, issueDate), frequency_(sinkingFrequency), dayCounter_(accrualDayCounter) { QL_REQUIRE(bondTenor.length() > 0, "bond tenor must be positive. " << bondTenor << " is not allowed."); maturityDate_ = startDate + bondTenor; cashflows_ = FixedRateLeg(sinkingSchedule(startDate, bondTenor, sinkingFrequency, calendar)) .withNotionals(sinkingNotionals(bondTenor, sinkingFrequency, coupon, initialFaceAmount)) .withCouponRates(coupon, accrualDayCounter) .withPaymentAdjustment(paymentConvention); addRedemptionsToCashflows(); }
Real days(const Period& p) { if (p.length()==0) return 0.0; switch (p.units()) { case Days: return p.length(); case Weeks: return p.length()*7.0; case Months: QL_FAIL("cannot convert Months into Days"); case Years: QL_FAIL("cannot convert Years into Days"); default: QL_FAIL("unknown time unit (" << Integer(p.units()) << ")"); } }
void SwaptionVolatilityStructure::checkSwapTenor(const Period& swapTenor, bool extrapolate) const { QL_REQUIRE(swapTenor.length() > 0, "non-positive swap tenor (" << swapTenor << ") given"); QL_REQUIRE(extrapolate || allowsExtrapolation() || swapTenor <= maxSwapTenor(), "swap tenor (" << swapTenor << ") is past max tenor (" << maxSwapTenor() << ")"); }
void CallableBondVolatilityStructure::checkRange(const Date& optionDate, const Period& bondTenor, Rate k, bool extrapolate) const { TermStructure::checkRange(timeFromReference(optionDate), extrapolate); QL_REQUIRE(bondTenor.length() > 0, "negative bond tenor (" << bondTenor << ") given"); QL_REQUIRE(extrapolate || allowsExtrapolation() || bondTenor <= maxBondTenor(), "bond tenor (" << bondTenor << ") is past max tenor (" << maxBondTenor() << ")"); QL_REQUIRE(extrapolate || allowsExtrapolation() || (k >= minStrike() && k <= maxStrike()), "strike (" << k << ") is outside the curve domain [" << minStrike() << "," << maxStrike()<< "]"); }
Date Calendar::advance(const Date & d, const Period & p, BusinessDayConvention c, bool endOfMonth) const { return advance(d, p.length(), p.units(), c, endOfMonth); }
void InterpolatedCPICapFloorTermPriceSurface<I2D>:: performCalculations() const { Size nMat = cfMaturities_.size(), ncK = cStrikes_.size(), nfK = fStrikes_.size(), nK = ncK + nfK; Matrix cP(nK, nMat), fP(nK, nMat); Handle<ZeroInflationTermStructure> zts = zii_->zeroInflationTermStructure(); Handle<YieldTermStructure> yts = this->nominalTermStructure(); QL_REQUIRE(!zts.empty(),"Zts is empty!!!"); QL_REQUIRE(!yts.empty(),"Yts is empty!!!"); for (Size i =0; i<nfK; i++){ allStrikes_.push_back(fStrikes_[i]); for (Size j=0; j<nMat; j++) { Period mat = cfMaturities_[j]; Real df = yts->discount(cpiOptionDateFromTenor(mat)); Real atm_quote = zts->zeroRate(cpiOptionDateFromTenor(mat)); Real atm = pow(1.0+atm_quote, mat.length()); Real S = atm * df; Real K_quote = fStrikes_[i]/100.0; Real K = pow(1.0+K_quote, mat.length()); cP[i][j] = fPrice_[i][j] + S - K * df; fP[i][j] = fPrice_[i][j]; } } for (Size i =0; i<ncK; i++){ allStrikes_.push_back(cStrikes_[i]); for (Size j=0; j<nMat; j++) { Period mat = cfMaturities_[j]; Real df = yts->discount(cpiOptionDateFromTenor(mat)); Real atm_quote = zts->zeroRate(cpiOptionDateFromTenor(mat)); Real atm = pow(1.0+atm_quote, mat.length()); Real S = atm * df; Real K_quote = cStrikes_[i]/100.0; Real K = pow(1.0+K_quote, mat.length()); cP[i+nfK][j] = cPrice_[i][j]; fP[i+nfK][j] = cPrice_[i][j] + K * df - S; } } // copy to store cPriceB_ = cP; fPriceB_ = fP; cfMaturityTimes_.clear(); for (Size i=0; i<cfMaturities_.size();i++) { cfMaturityTimes_.push_back(timeFromReference(cpiOptionDateFromTenor(cfMaturities_[i]))); } capPrice_ = interpolator2d_.interpolate(cfMaturityTimes_.begin(),cfMaturityTimes_.end(), allStrikes_.begin(), allStrikes_.end(), cPriceB_ ); capPrice_.enableExtrapolation(); floorPrice_ = interpolator2d_.interpolate(cfMaturityTimes_.begin(),cfMaturityTimes_.end(), allStrikes_.begin(), allStrikes_.end(), fPriceB_ ); floorPrice_.enableExtrapolation(); /* test code - note order of indices for (Size i =0; i<nK; i++){ std::cout << allStrikes_[i] << ": "; Real qK = allStrikes_[i]; for (Size j=0; j<nMat; j++) { Real t = cfMaturityTimes_[j]; std::cout << fP[i][j] << "," << floorPrice_(t,qK) << " | " ; } std::cout << std::endl; } for (Size i =0; i<nK; i++){ std::cout << allStrikes_[i] << ": "; Real qK = allStrikes_[i]; for (Size j=0; j<nMat; j++) { Real t = cfMaturityTimes_[j]; std::cout << cP[i][j] << "," << capPrice_(t,qK) << " | " ; } std::cout << std::endl; } */ }
inline Date Date::operator-(const Period& p) const { return advance(*this,-p.length(),p.units()); }
Schedule::Schedule(Date effectiveDate, const Date& terminationDate, const Period& tenor, const Calendar& cal, BusinessDayConvention convention, BusinessDayConvention terminationDateConvention, DateGeneration::Rule rule, bool endOfMonth, const Date& first, const Date& nextToLast) : tenor_(tenor), calendar_(cal), convention_(convention), terminationDateConvention_(terminationDateConvention), rule_(rule), endOfMonth_(allowsEndOfMonth(tenor) ? endOfMonth : false), firstDate_(first==effectiveDate ? Date() : first), nextToLastDate_(nextToLast==terminationDate ? Date() : nextToLast) { // sanity checks QL_REQUIRE(terminationDate != Date(), "null termination date"); // in many cases (e.g. non-expired bonds) the effective date is not // really necessary. In these cases a decent placeholder is enough if (effectiveDate==Date() && first==Date() && rule==DateGeneration::Backward) { Date evalDate = Settings::instance().evaluationDate(); QL_REQUIRE(evalDate < terminationDate, "null effective date"); Natural y; if (nextToLast != Date()) { y = (nextToLast - evalDate)/366 + 1; effectiveDate = nextToLast - y*Years; } else { y = (terminationDate - evalDate)/366 + 1; effectiveDate = terminationDate - y*Years; } } else QL_REQUIRE(effectiveDate != Date(), "null effective date"); QL_REQUIRE(effectiveDate < terminationDate, "effective date (" << effectiveDate << ") later than or equal to termination date (" << terminationDate << ")"); if (tenor.length()==0) rule_ = DateGeneration::Zero; else QL_REQUIRE(tenor.length()>0, "non positive tenor (" << tenor << ") not allowed"); if (firstDate_ != Date()) { switch (*rule_) { case DateGeneration::Backward: case DateGeneration::Forward: QL_REQUIRE(firstDate_ > effectiveDate && firstDate_ < terminationDate, "first date (" << firstDate_ << ") out of effective-termination date range [" << effectiveDate << ", " << terminationDate << ")"); // we should ensure that the above condition is still // verified after adjustment break; case DateGeneration::ThirdWednesday: QL_REQUIRE(IMM::isIMMdate(firstDate_, false), "first date (" << firstDate_ << ") is not an IMM date"); break; case DateGeneration::Zero: case DateGeneration::Twentieth: case DateGeneration::TwentiethIMM: case DateGeneration::OldCDS: case DateGeneration::CDS: QL_FAIL("first date incompatible with " << *rule_ << " date generation rule"); default: QL_FAIL("unknown rule (" << Integer(*rule_) << ")"); } } if (nextToLastDate_ != Date()) { switch (*rule_) { case DateGeneration::Backward: case DateGeneration::Forward: QL_REQUIRE(nextToLastDate_ > effectiveDate && nextToLastDate_ < terminationDate, "next to last date (" << nextToLastDate_ << ") out of effective-termination date range (" << effectiveDate << ", " << terminationDate << "]"); // we should ensure that the above condition is still // verified after adjustment break; case DateGeneration::ThirdWednesday: QL_REQUIRE(IMM::isIMMdate(nextToLastDate_, false), "next-to-last date (" << nextToLastDate_ << ") is not an IMM date"); break; case DateGeneration::Zero: case DateGeneration::Twentieth: case DateGeneration::TwentiethIMM: case DateGeneration::OldCDS: case DateGeneration::CDS: QL_FAIL("next to last date incompatible with " << *rule_ << " date generation rule"); default: QL_FAIL("unknown rule (" << Integer(*rule_) << ")"); } } // calendar needed for endOfMonth adjustment Calendar nullCalendar = NullCalendar(); Integer periods = 1; Date seed, exitDate; switch (*rule_) { case DateGeneration::Zero: tenor_ = 0*Years; dates_.push_back(effectiveDate); dates_.push_back(terminationDate); isRegular_.push_back(true); break; case DateGeneration::Backward: dates_.push_back(terminationDate); seed = terminationDate; if (nextToLastDate_ != Date()) { dates_.insert(dates_.begin(), nextToLastDate_); Date temp = nullCalendar.advance(seed, -periods*(*tenor_), convention, *endOfMonth_); if (temp!=nextToLastDate_) isRegular_.insert(isRegular_.begin(), false); else isRegular_.insert(isRegular_.begin(), true); seed = nextToLastDate_; } exitDate = effectiveDate; if (firstDate_ != Date()) exitDate = firstDate_; for (;;) { Date temp = nullCalendar.advance(seed, -periods*(*tenor_), convention, *endOfMonth_); if (temp < exitDate) { if (firstDate_ != Date() && (calendar_.adjust(dates_.front(),convention)!= calendar_.adjust(firstDate_,convention))) { dates_.insert(dates_.begin(), firstDate_); isRegular_.insert(isRegular_.begin(), false); } break; } else { // skip dates that would result in duplicates // after adjustment if (calendar_.adjust(dates_.front(),convention)!= calendar_.adjust(temp,convention)) { dates_.insert(dates_.begin(), temp); isRegular_.insert(isRegular_.begin(), true); } ++periods; } } if (calendar_.adjust(dates_.front(),convention)!= calendar_.adjust(effectiveDate,convention)) { dates_.insert(dates_.begin(), effectiveDate); isRegular_.insert(isRegular_.begin(), false); } break; case DateGeneration::Twentieth: case DateGeneration::TwentiethIMM: case DateGeneration::ThirdWednesday: case DateGeneration::OldCDS: case DateGeneration::CDS: QL_REQUIRE(!*endOfMonth_, "endOfMonth convention incompatible with " << *rule_ << " date generation rule"); // fall through case DateGeneration::Forward: if (*rule_ == DateGeneration::CDS) { dates_.push_back(previousTwentieth(effectiveDate, DateGeneration::CDS)); } else { dates_.push_back(effectiveDate); } seed = dates_.back(); if (firstDate_!=Date()) { dates_.push_back(firstDate_); Date temp = nullCalendar.advance(seed, periods*(*tenor_), convention, *endOfMonth_); if (temp!=firstDate_) isRegular_.push_back(false); else isRegular_.push_back(true); seed = firstDate_; } else if (*rule_ == DateGeneration::Twentieth || *rule_ == DateGeneration::TwentiethIMM || *rule_ == DateGeneration::OldCDS || *rule_ == DateGeneration::CDS) { Date next20th = nextTwentieth(effectiveDate, *rule_); if (*rule_ == DateGeneration::OldCDS) { // distance rule inforced in natural days static const BigInteger stubDays = 30; if (next20th - effectiveDate < stubDays) { // +1 will skip this one and get the next next20th = nextTwentieth(next20th + 1, *rule_); } } if (next20th != effectiveDate) { dates_.push_back(next20th); isRegular_.push_back(false); seed = next20th; } } exitDate = terminationDate; if (nextToLastDate_ != Date()) exitDate = nextToLastDate_; for (;;) { Date temp = nullCalendar.advance(seed, periods*(*tenor_), convention, *endOfMonth_); if (temp > exitDate) { if (nextToLastDate_ != Date() && (calendar_.adjust(dates_.back(),convention)!= calendar_.adjust(nextToLastDate_,convention))) { dates_.push_back(nextToLastDate_); isRegular_.push_back(false); } break; } else { // skip dates that would result in duplicates // after adjustment if (calendar_.adjust(dates_.back(),convention)!= calendar_.adjust(temp,convention)) { dates_.push_back(temp); isRegular_.push_back(true); } ++periods; } } if (calendar_.adjust(dates_.back(),terminationDateConvention)!= calendar_.adjust(terminationDate,terminationDateConvention)) { if (*rule_ == DateGeneration::Twentieth || *rule_ == DateGeneration::TwentiethIMM || *rule_ == DateGeneration::OldCDS || *rule_ == DateGeneration::CDS) { dates_.push_back(nextTwentieth(terminationDate, *rule_)); isRegular_.push_back(true); } else { dates_.push_back(terminationDate); isRegular_.push_back(false); } } break; default: QL_FAIL("unknown rule (" << Integer(*rule_) << ")"); } // adjustments if (*rule_==DateGeneration::ThirdWednesday) for (Size i=1; i<dates_.size()-1; ++i) dates_[i] = Date::nthWeekday(3, Wednesday, dates_[i].month(), dates_[i].year()); if (*endOfMonth_ && calendar_.isEndOfMonth(seed)) { // adjust to end of month if (convention == Unadjusted) { for (Size i=1; i<dates_.size()-1; ++i) dates_[i] = Date::endOfMonth(dates_[i]); } else { for (Size i=1; i<dates_.size()-1; ++i) dates_[i] = calendar_.endOfMonth(dates_[i]); } if (terminationDateConvention != Unadjusted) { dates_.front() = calendar_.endOfMonth(dates_.front()); dates_.back() = calendar_.endOfMonth(dates_.back()); } else { // the termination date is the first if going backwards, // the last otherwise. if (*rule_ == DateGeneration::Backward) dates_.back() = Date::endOfMonth(dates_.back()); else dates_.front() = Date::endOfMonth(dates_.front()); } } else { // first date not adjusted for CDS schedules if (*rule_ != DateGeneration::OldCDS) dates_[0] = calendar_.adjust(dates_[0], convention); for (Size i=1; i<dates_.size()-1; ++i) dates_[i] = calendar_.adjust(dates_[i], convention); // termination date is NOT adjusted as per ISDA // specifications, unless otherwise specified in the // confirmation of the deal or unless we're creating a CDS // schedule if (terminationDateConvention != Unadjusted || *rule_ == DateGeneration::Twentieth || *rule_ == DateGeneration::TwentiethIMM || *rule_ == DateGeneration::OldCDS || *rule_ == DateGeneration::CDS) { dates_.back() = calendar_.adjust(dates_.back(), terminationDateConvention); } } // Final safety checks to remove extra next-to-last date, if // necessary. It can happen to be equal or later than the end // date due to EOM adjustments (see the Schedule test suite // for an example). if (dates_.size() >= 2 && dates_[dates_.size()-2] >= dates_.back()) { isRegular_[dates_.size()-2] = (dates_[dates_.size()-2] == dates_.back()); dates_[dates_.size()-2] = dates_.back(); dates_.pop_back(); isRegular_.pop_back(); } if (dates_.size() >= 2 && dates_[1] <= dates_.front()) { isRegular_[1] = (dates_[1] == dates_.front()); dates_[1] = dates_.front(); dates_.erase(dates_.begin()); isRegular_.erase(isRegular_.begin()); } QL_ENSURE(dates_.size()>1, "degenerate single date (" << dates_[0] << ") schedule" << "\n seed date: " << seed << "\n exit date: " << exitDate << "\n effective date: " << effectiveDate << "\n first date: " << first << "\n next to last date: " << nextToLast << "\n termination date: " << terminationDate << "\n generation rule: " << *rule_ << "\n end of month: " << *endOfMonth_); }
bool operator<(const Period& p1, const Period& p2) { // special cases if (p1.length() == 0) return p2.length() > 0; if (p2.length() == 0) return p1.length() < 0; // exact comparisons if (p1.units() == p2.units()) return p1.length() < p2.length(); if (p1.units() == Months && p2.units() == Years) return p1.length() < 12*p2.length(); if (p1.units() == Years && p2.units() == Months) return 12*p1.length() < p2.length(); if (p1.units() == Days && p2.units() == Weeks) return p1.length() < 7*p2.length(); if (p1.units() == Weeks && p2.units() == Days) return 7*p1.length() < p2.length(); // inexact comparisons (handled by converting to days and using limits) std::pair<Integer, Integer> p1lim = daysMinMax(p1); std::pair<Integer, Integer> p2lim = daysMinMax(p2); if (p1lim.second < p2lim.first) return true; else if (p1lim.first > p2lim.second) return false; else QL_FAIL("undecidable comparison between " << p1 << " and " << p2); }