//@} //! \name Results //@{ /*! Returns the upfront spread that, given the running spread * and the quoted recovery rate, will make the instrument * have an NPV of 0. */ public double fairUpfront() { calculate(); Utils.QL_REQUIRE(fairUpfront_ != null, () => "fair upfront not available"); return(fairUpfront_.Value); }
public DateGeneration.Rule rule() { Utils.QL_REQUIRE(rule_.HasValue, () => "full interface (rule) not available"); return(rule_.Value); }
/// <summary> /// rule based constructor /// </summary> /// <param name="effectiveDate"></param> /// <param name="terminationDate"></param> /// <param name="tenor"></param> /// <param name="calendar"></param> /// <param name="convention"></param> /// <param name="terminationDateConvention"></param> /// <param name="rule"></param> /// <param name="endOfMonth"></param> /// <param name="firstDate"></param> /// <param name="nextToLastDate"></param> public Schedule(Date effectiveDate, Date terminationDate, Period tenor, Calendar calendar, BusinessDayConvention convention, BusinessDayConvention terminationDateConvention, DateGeneration.Rule rule, bool endOfMonth, Date firstDate = null, Date nextToLastDate = null) { calendar_ = calendar ?? new NullCalendar(); firstDate_ = firstDate == effectiveDate ? null : firstDate; nextToLastDate_ = nextToLastDate == terminationDate ? null : nextToLastDate; tenor_ = tenor; convention_ = convention; terminationDateConvention_ = terminationDateConvention; rule_ = rule; endOfMonth_ = allowsEndOfMonth(tenor) && endOfMonth; // sanity checks Utils.QL_REQUIRE(terminationDate != null, () => "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 == null && firstDate == null && rule == DateGeneration.Rule.Backward) { Date evalDate = Settings.evaluationDate(); Utils.QL_REQUIRE(evalDate < terminationDate, () => "null effective date"); int y; if (nextToLastDate != null) { y = (nextToLastDate - evalDate) / 366 + 1; effectiveDate = nextToLastDate - new Period(y, TimeUnit.Years); } else { y = (terminationDate - evalDate) / 366 + 1; effectiveDate = terminationDate - new Period(y, TimeUnit.Years); } // More accurate , is the previous coupon date if (effectiveDate > evalDate) { effectiveDate = effectiveDate - new Period(tenor_.length(), TimeUnit.Months); } else if (effectiveDate + new Period(tenor_.length(), TimeUnit.Months) < evalDate) { effectiveDate = effectiveDate + new Period(tenor_.length(), TimeUnit.Months); } } else { Utils.QL_REQUIRE(effectiveDate != null, () => "null effective date"); } Utils.QL_REQUIRE(effectiveDate < terminationDate, () => "effective date (" + effectiveDate + ") later than or equal to termination date (" + terminationDate + ")" ); if (tenor_.length() == 0) { rule_ = DateGeneration.Rule.Zero; } else { Utils.QL_REQUIRE(tenor.length() > 0, () => "non positive tenor (" + tenor + ") not allowed"); } if (firstDate_ != null) { switch (rule_.Value) { case DateGeneration.Rule.Backward: case DateGeneration.Rule.Forward: Utils.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.Rule.ThirdWednesday: Utils.QL_REQUIRE(IMM.isIMMdate(firstDate_, false), () => "first date (" + firstDate_ + ") is not an IMM date"); break; case DateGeneration.Rule.Zero: case DateGeneration.Rule.Twentieth: case DateGeneration.Rule.TwentiethIMM: case DateGeneration.Rule.OldCDS: case DateGeneration.Rule.CDS: case DateGeneration.Rule.CDS2015: Utils.QL_FAIL("first date incompatible with " + rule_.Value + " date generation rule"); break; default: Utils.QL_FAIL("unknown rule (" + rule_.Value + ")"); break; } } if (nextToLastDate_ != null) { switch (rule_.Value) { case DateGeneration.Rule.Backward: case DateGeneration.Rule.Forward: Utils.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.Rule.ThirdWednesday: Utils.QL_REQUIRE(IMM.isIMMdate(nextToLastDate_, false), () => "next-to-last date (" + nextToLastDate_ + ") is not an IMM date"); break; case DateGeneration.Rule.Zero: case DateGeneration.Rule.Twentieth: case DateGeneration.Rule.TwentiethIMM: case DateGeneration.Rule.OldCDS: case DateGeneration.Rule.CDS: case DateGeneration.Rule.CDS2015: Utils.QL_FAIL("next to last date incompatible with " + rule_.Value + " date generation rule"); break; default: Utils.QL_FAIL("unknown rule (" + rule_.Value + ")"); break; } } // calendar needed for endOfMonth adjustment Calendar nullCalendar = new NullCalendar(); int periods = 1; Date seed = new Date(), exitDate = new Date(); switch (rule_.Value) { case DateGeneration.Rule.Zero: tenor_ = new Period(0, TimeUnit.Years); dates_.Add(effectiveDate); dates_.Add(terminationDate); isRegular_.Add(true); break; case DateGeneration.Rule.Backward: dates_.Add(terminationDate); seed = terminationDate; if (nextToLastDate_ != null) { dates_.Insert(0, nextToLastDate_); Date temp = nullCalendar.advance(seed, -periods * tenor_, convention_, endOfMonth_.Value); if (temp != nextToLastDate_) { isRegular_.Insert(0, false); } else { isRegular_.Insert(0, true); } seed = nextToLastDate_; } exitDate = effectiveDate; if (firstDate_ != null) { exitDate = firstDate_; } while (true) { Date temp = nullCalendar.advance(seed, -periods * tenor_, convention_, endOfMonth_.Value); if (temp < exitDate) { if (firstDate_ != null && (calendar_.adjust(dates_.First(), convention_) != calendar_.adjust(firstDate_, convention_))) { dates_.Insert(0, firstDate_); isRegular_.Insert(0, false); } break; } else { // skip dates that would result in duplicates // after adjustment if (calendar_.adjust(dates_.First(), convention_) != calendar_.adjust(temp, convention_)) { dates_.Insert(0, temp); isRegular_.Insert(0, true); } ++periods; } } if (calendar_.adjust(dates_.First(), convention) != calendar_.adjust(effectiveDate, convention)) { dates_.Insert(0, effectiveDate); isRegular_.Insert(0, false); } break; case DateGeneration.Rule.Twentieth: case DateGeneration.Rule.TwentiethIMM: case DateGeneration.Rule.ThirdWednesday: case DateGeneration.Rule.OldCDS: case DateGeneration.Rule.CDS: case DateGeneration.Rule.CDS2015: Utils.QL_REQUIRE(!endOfMonth, () => "endOfMonth convention incompatible with " + rule_.Value + " date generation rule"); goto case DateGeneration.Rule.Forward; // fall through case DateGeneration.Rule.Forward: if (rule_.Value == DateGeneration.Rule.CDS || rule_.Value == DateGeneration.Rule.CDS2015) { dates_.Add(previousTwentieth(effectiveDate, rule_.Value)); } else { dates_.Add(effectiveDate); } seed = dates_.Last(); if (firstDate_ != null) { dates_.Add(firstDate_); Date temp = nullCalendar.advance(seed, periods * tenor_, convention_, endOfMonth_.Value); if (temp != firstDate_) { isRegular_.Add(false); } else { isRegular_.Add(true); } seed = firstDate_; } else if (rule_.Value == DateGeneration.Rule.Twentieth || rule_.Value == DateGeneration.Rule.TwentiethIMM || rule_.Value == DateGeneration.Rule.OldCDS || rule_.Value == DateGeneration.Rule.CDS || rule_.Value == DateGeneration.Rule.CDS2015) { Date next20th = nextTwentieth(effectiveDate, rule_.Value); if (rule_ == DateGeneration.Rule.OldCDS) { // distance rule inforced in natural days long stubDays = 30; if (next20th - effectiveDate < stubDays) { // +1 will skip this one and get the next next20th = nextTwentieth(next20th + 1, rule_.Value); } } if (next20th != effectiveDate) { dates_.Add(next20th); isRegular_.Add(false); seed = next20th; } } exitDate = terminationDate; if (nextToLastDate_ != null) { exitDate = nextToLastDate_; } if (rule_ == DateGeneration.Rule.CDS2015 && nextTwentieth(terminationDate, rule_.Value) == terminationDate && terminationDate.month() % 2 == 1) { exitDate = nextTwentieth(terminationDate + 1, rule_.Value); } while (true) { Date temp = nullCalendar.advance(seed, periods * tenor_, convention_, endOfMonth_.Value); if (temp > exitDate) { if (nextToLastDate_ != null && (calendar_.adjust(dates_.Last(), convention_) != calendar_.adjust(nextToLastDate_, convention_))) { dates_.Add(nextToLastDate_); isRegular_.Add(false); } break; } else { // skip dates that would result in duplicates // after adjustment if (calendar_.adjust(dates_.Last(), convention_) != calendar_.adjust(temp, convention_)) { dates_.Add(temp); isRegular_.Add(true); } ++periods; } } if (calendar_.adjust(dates_.Last(), terminationDateConvention_.Value) != calendar_.adjust(terminationDate, terminationDateConvention_.Value)) { if (rule_.Value == DateGeneration.Rule.Twentieth || rule_.Value == DateGeneration.Rule.TwentiethIMM || rule_.Value == DateGeneration.Rule.OldCDS || rule_.Value == DateGeneration.Rule.CDS) { dates_.Add(nextTwentieth(terminationDate, rule_.Value)); isRegular_.Add(true); } else if (rule_ == DateGeneration.Rule.CDS2015) { Date tentativeTerminationDate = nextTwentieth(terminationDate, rule_.Value); if (tentativeTerminationDate.month() % 2 == 0) { dates_.Add(tentativeTerminationDate); isRegular_.Add(true); } } else { dates_.Add(terminationDate); isRegular_.Add(false); } } break; default: Utils.QL_FAIL("unknown rule (" + rule_.Value + ")"); break; } // adjustments if (rule_ == DateGeneration.Rule.ThirdWednesday) { for (int i = 1; i < dates_.Count - 1; ++i) { dates_[i] = Date.nthWeekday(3, DayOfWeek.Wednesday, dates_[i].Month, dates_[i].Year); } } if (endOfMonth && calendar_.isEndOfMonth(seed)) { // adjust to end of month if (convention_ == BusinessDayConvention.Unadjusted) { for (int i = 1; i < dates_.Count - 1; ++i) { dates_[i] = Date.endOfMonth(dates_[i]); } } else { for (int i = 1; i < dates_.Count - 1; ++i) { dates_[i] = calendar_.endOfMonth(dates_[i]); } } if (terminationDateConvention_ != BusinessDayConvention.Unadjusted) { dates_[0] = calendar_.endOfMonth(dates_.First()); dates_[dates_.Count - 1] = calendar_.endOfMonth(dates_.Last()); } else { // the termination date is the first if going backwards, // the last otherwise. if (rule_ == DateGeneration.Rule.Backward) { dates_[dates_.Count - 1] = Date.endOfMonth(dates_.Last()); } else { dates_[0] = Date.endOfMonth(dates_.First()); } } } else { // first date not adjusted for CDS schedules if (rule_ != DateGeneration.Rule.OldCDS) { dates_[0] = calendar_.adjust(dates_[0], convention_); } for (int i = 1; i < dates_.Count - 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_.Value != BusinessDayConvention.Unadjusted && rule_.Value != DateGeneration.Rule.CDS && rule_.Value != DateGeneration.Rule.CDS2015) { dates_[dates_.Count - 1] = calendar_.adjust(dates_.Last(), terminationDateConvention_.Value); } } // 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_.Count >= 2 && dates_[dates_.Count - 2] >= dates_.Last()) { isRegular_[isRegular_.Count - 2] = (dates_[dates_.Count - 2] == dates_.Last()); dates_[dates_.Count - 2] = dates_.Last(); dates_.RemoveAt(dates_.Count - 1); isRegular_.RemoveAt(isRegular_.Count - 1); } if (dates_.Count >= 2 && dates_[1] <= dates_.First()) { isRegular_[1] = (dates_[1] == dates_.First()); dates_[1] = dates_.First(); dates_.RemoveAt(0); isRegular_.RemoveAt(0); } Utils.QL_REQUIRE(dates_.Count > 1, () => "degenerate single date (" + dates_[0] + ") schedule" + "\n seed date: " + seed + "\n exit date: " + exitDate + "\n effective date: " + effectiveDate + "\n first date: " + firstDate + "\n next to last date: " + nextToLastDate + "\n termination date: " + terminationDate + "\n generation rule: " + rule_.Value + "\n end of month: " + endOfMonth_.Value); }
public bool isRegular(int i) { Utils.QL_REQUIRE(isRegular_.Count > 0, () => "full interface (isRegular) not available"); Utils.QL_REQUIRE(i <= isRegular_.Count && i > 0, () => "index (" + i + ") must be in [1, " + isRegular_.Count + "]"); return(isRegular_[i - 1]); }
public Period tenor() { Utils.QL_REQUIRE(tenor_ != null, () => "full interface (tenor) not available"); return(tenor_); }
protected override double localVolImpl(double t, double underlyingLevel) { double dr = riskFreeTS_.currentLink().discount(t, true); double dq = dividendTS_.currentLink().discount(t, true); double forwardValue = underlying_.currentLink().value() * dq / dr; // strike derivatives double strike, y, dy, strikep, strikem; double w, wp, wm, dwdy, d2wdy2; strike = underlyingLevel; y = Math.Log(strike / forwardValue); dy = ((Math.Abs(y) > 0.001) ? y * 0.0001 : 0.000001); strikep = strike * Math.Exp(dy); strikem = strike / Math.Exp(dy); w = blackTS_.link.blackVariance(t, strike, true); wp = blackTS_.link.blackVariance(t, strikep, true); wm = blackTS_.link.blackVariance(t, strikem, true); dwdy = (wp - wm) / (2.0 * dy); d2wdy2 = (wp - 2.0 * w + wm) / (dy * dy); // time derivative double dt, wpt, wmt, dwdt; if (t.IsEqual(0.0)) { dt = 0.0001; double drpt = riskFreeTS_.currentLink().discount(t + dt, true); double dqpt = dividendTS_.currentLink().discount(t + dt, true); double strikept = strike * dr * dqpt / (drpt * dq); wpt = blackTS_.link.blackVariance(t + dt, strikept, true); Utils.QL_REQUIRE(wpt >= w, () => "decreasing variance at strike " + strike + " between time " + t + " and time " + (t + dt)); dwdt = (wpt - w) / dt; } else { dt = Math.Min(0.0001, t / 2.0); double drpt = riskFreeTS_.currentLink().discount(t + dt, true); double drmt = riskFreeTS_.currentLink().discount(t - dt, true); double dqpt = dividendTS_.currentLink().discount(t + dt, true); double dqmt = dividendTS_.currentLink().discount(t - dt, true); double strikept = strike * dr * dqpt / (drpt * dq); double strikemt = strike * dr * dqmt / (drmt * dq); wpt = blackTS_.link.blackVariance(t + dt, strikept, true); wmt = blackTS_.link.blackVariance(t - dt, strikemt, true); Utils.QL_REQUIRE(wpt >= w, () => "decreasing variance at strike " + strike + " between time " + t + " and time " + (t + dt)); Utils.QL_REQUIRE(w >= wmt, () => "decreasing variance at strike " + strike + " between time " + (t - dt) + " and time " + t); dwdt = (wpt - wmt) / (2.0 * dt); } if (dwdy.IsEqual(0.0) && d2wdy2.IsEqual(0.0)) // avoid /w where w might be 0.0 { return(Math.Sqrt(dwdt)); } else { double den1 = 1.0 - y / w * dwdy; double den2 = 0.25 * (-0.25 - 1.0 / w + y * y / w / w) * dwdy * dwdy; double den3 = 0.5 * d2wdy2; double den = den1 + den2 + den3; double result = dwdt / den; Utils.QL_REQUIRE(result >= 0.0, () => "negative local vol^2 at strike " + strike + " and time " + t + "; the black vol surface is not smooth enough"); return(Math.Sqrt(result)); } }
public override double swapletRate() { OvernightIndex index = coupon_.index() as OvernightIndex; List <Date> fixingDates = coupon_.fixingDates(); List <double> dt = coupon_.dt(); int n = dt.Count; int i = 0; double compoundFactor = 1.0; // already fixed part Date today = Settings.evaluationDate(); while (fixingDates[i] < today && i < n) { // rate must have been fixed double?pastFixing = IndexManager.instance().getHistory(index.name())[fixingDates[i]]; Utils.QL_REQUIRE(pastFixing != null, () => "Missing " + index.name() + " fixing for " + fixingDates[i].ToString()); compoundFactor *= (1.0 + pastFixing.GetValueOrDefault() * dt[i]); ++i; } // today is a border case if (fixingDates[i] == today && i < n) { // might have been fixed try { double?pastFixing = IndexManager.instance().getHistory(index.name())[fixingDates[i]]; if (pastFixing != null) { compoundFactor *= (1.0 + pastFixing.GetValueOrDefault() * dt[i]); ++i; } else { // fall through and forecast } } catch (Exception) { // fall through and forecast } } // forward part using telescopic property in order // to avoid the evaluation of multiple forward fixings if (i < n) { Handle <YieldTermStructure> curve = index.forwardingTermStructure(); Utils.QL_REQUIRE(!curve.empty(), () => "null term structure set to this instance of" + index.name()); List <Date> dates = coupon_.valueDates(); double startDiscount = curve.link.discount(dates[i]); double endDiscount = curve.link.discount(dates[n]); compoundFactor *= startDiscount / endDiscount; } double rate = (compoundFactor - 1.0) / coupon_.accrualPeriod(); return(coupon_.gearing() * rate + coupon_.spread()); }
public override void calculate(IPricingEngineResults r) { OneAssetOption.Results results = r as OneAssetOption.Results; Utils.QL_REQUIRE(results != null, () => "incorrect results type"); double beginDate, endDate; int dateNumber = stoppingTimes_.Count; bool lastDateIsResTime = false; int firstIndex = -1; int lastIndex = dateNumber - 1; bool firstDateIsZero = false; double firstNonZeroDate = getResidualTime(); double dateTolerance = 1e-6; int j; if (dateNumber > 0) { Utils.QL_REQUIRE(getDividendTime(0) >= 0, () => "first date (" + getDividendTime(0) + ") cannot be negative"); if (getDividendTime(0) < getResidualTime() * dateTolerance) { firstDateIsZero = true; firstIndex = 0; if (dateNumber >= 2) { firstNonZeroDate = getDividendTime(1); } } if (Math.Abs(getDividendTime(lastIndex) - getResidualTime()) < dateTolerance) { lastDateIsResTime = true; lastIndex = dateNumber - 2; } if (!firstDateIsZero) { firstNonZeroDate = getDividendTime(0); } if (dateNumber >= 2) { for (j = 1; j < dateNumber; j++) { Utils.QL_REQUIRE(getDividendTime(j - 1) < getDividendTime(j), () => "dates must be in increasing order: " + getDividendTime(j - 1) + " is not strictly smaller than " + getDividendTime(j)); } } } double dt = getResidualTime() / (timeStepPerPeriod_ * (dateNumber + 1)); // Ensure that dt is always smaller than the first non-zero date if (firstNonZeroDate <= dt) { dt = firstNonZeroDate / 2.0; } setGridLimits(); initializeInitialCondition(); initializeOperator(); initializeBoundaryConditions(); initializeModel(); initializeStepCondition(); prices_ = (SampledCurve)intrinsicValues_.Clone(); if (lastDateIsResTime) { executeIntermediateStep(dateNumber - 1); } j = lastIndex; object temp; do { if (j == dateNumber - 1) { beginDate = getResidualTime(); } else { beginDate = getDividendTime(j + 1); } if (j >= 0) { endDate = getDividendTime(j); } else { endDate = dt; } temp = prices_.values(); model_.rollback(ref temp, beginDate, endDate, timeStepPerPeriod_, stepCondition_); prices_.setValues((Vector)temp); if (j >= 0) { executeIntermediateStep(j); } } while (--j >= firstIndex); temp = prices_.values(); model_.rollback(ref temp, dt, 0, 1, stepCondition_); prices_.setValues((Vector)temp); if (firstDateIsZero) { executeIntermediateStep(0); } results.value = prices_.valueAtCenter(); results.delta = prices_.firstDerivativeAtCenter(); results.gamma = prices_.secondDerivativeAtCenter(); results.additionalResults["priceCurve"] = prices_; }
public override void setupArguments(IPricingEngineArguments args) { base.setupArguments(args); ConvertibleBond.option.arguments moreArgs = args as ConvertibleBond.option.arguments; Utils.QL_REQUIRE(moreArgs != null, "wrong argument type"); moreArgs.conversionRatio = conversionRatio_; Date settlement = bond_.settlementDate(); int n = callability_.Count; moreArgs.callabilityDates.Clear(); moreArgs.callabilityTypes.Clear(); moreArgs.callabilityPrices.Clear(); moreArgs.callabilityTriggers.Clear(); moreArgs.callabilityDates.Capacity = n; moreArgs.callabilityTypes.Capacity = n; moreArgs.callabilityPrices.Capacity = n; moreArgs.callabilityTriggers.Capacity = n; for (int i = 0; i < n; i++) { if (!callability_[i].hasOccurred(settlement, false)) { moreArgs.callabilityTypes.Add(callability_[i].type()); moreArgs.callabilityDates.Add(callability_[i].date()); if (callability_[i].price().type() == Callability.Price.Type.Clean) { moreArgs.callabilityPrices.Add(callability_[i].price().amount() + bond_.accruedAmount(callability_[i].date())); } else { moreArgs.callabilityPrices.Add(callability_[i].price().amount()); } SoftCallability softCall = callability_[i] as SoftCallability; if (softCall != null) { moreArgs.callabilityTriggers.Add(softCall.trigger()); } else { moreArgs.callabilityTriggers.Add(null); } } } List <CashFlow> cashflows = bond_.cashflows(); moreArgs.couponDates.Clear(); moreArgs.couponAmounts.Clear(); for (int i = 0; i < cashflows.Count - 1; i++) { if (!cashflows[i].hasOccurred(settlement, false)) { moreArgs.couponDates.Add(cashflows[i].date()); moreArgs.couponAmounts.Add(cashflows[i].amount()); } } moreArgs.dividends.Clear(); moreArgs.dividendDates.Clear(); for (int i = 0; i < dividends_.Count; i++) { if (!dividends_[i].hasOccurred(settlement, false)) { moreArgs.dividends.Add(dividends_[i]); moreArgs.dividendDates.Add(dividends_[i].date()); } } moreArgs.creditSpread = creditSpread_; moreArgs.issueDate = issueDate_; moreArgs.settlementDate = settlement; moreArgs.settlementDays = settlementDays_; moreArgs.redemption = redemption_; }
public double upfrontNPV() { calculate(); Utils.QL_REQUIRE(upfrontNPV_ != null, () => "upfront NPV not available"); return(upfrontNPV_.Value); }
public override void calculate() { Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); EuropeanExercise exercise = arguments_.exercise as EuropeanExercise; Utils.QL_REQUIRE(exercise != null, () => "not an European Option"); BasketPayoff basket_payoff = arguments_.payoff as BasketPayoff; MinBasketPayoff min_basket = arguments_.payoff as MinBasketPayoff; MaxBasketPayoff max_basket = arguments_.payoff as MaxBasketPayoff; Utils.QL_REQUIRE(min_basket != null || max_basket != null, () => "unknown basket type"); PlainVanillaPayoff payoff = basket_payoff.basePayoff() as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double strike = payoff.strike(); double variance1 = process1_.blackVolatility().link.blackVariance(exercise.lastDate(), strike); double variance2 = process2_.blackVolatility().link.blackVariance(exercise.lastDate(), strike); double riskFreeDiscount = process1_.riskFreeRate().link.discount(exercise.lastDate()); // cannot handle non zero dividends, so don't believe this... double dividendDiscount1 = process1_.dividendYield().link.discount(exercise.lastDate()); double dividendDiscount2 = process2_.dividendYield().link.discount(exercise.lastDate()); double forward1 = process1_.stateVariable().link.value() * dividendDiscount1 / riskFreeDiscount; double forward2 = process2_.stateVariable().link.value() * dividendDiscount2 / riskFreeDiscount; if (max_basket != null) { switch (payoff.optionType()) { // euro call on a two asset max basket case Option.Type.Call: results_.value = euroTwoAssetMaxBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; // euro put on a two asset max basket case Option.Type.Put: results_.value = strike * riskFreeDiscount - euroTwoAssetMaxBasketCall(forward1, forward2, 0.0, riskFreeDiscount, variance1, variance2, rho_) + euroTwoAssetMaxBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; default: Utils.QL_FAIL("unknown option type"); break; } } else if (min_basket != null) { switch (payoff.optionType()) { // euro call on a two asset min basket case Option.Type.Call: results_.value = euroTwoAssetMinBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; // euro put on a two asset min basket case Option.Type.Put: results_.value = strike * riskFreeDiscount - euroTwoAssetMinBasketCall(forward1, forward2, 0.0, riskFreeDiscount, variance1, variance2, rho_) + euroTwoAssetMinBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; default: Utils.QL_FAIL("unknown option type"); break; } } else { Utils.QL_FAIL("unknown type"); } }
public double defaultLegNPV() { calculate(); Utils.QL_REQUIRE(defaultLegNPV_ != null, () => "default-leg NPV not available"); return(defaultLegNPV_.Value); }
public double couponLegNPV() { calculate(); Utils.QL_REQUIRE(couponLegNPV_ != null, () => "coupon-leg NPV not available"); return(couponLegNPV_.Value); }
/*! Returns the running spread that, given the quoted recovery * rate, will make the running-only CDS have an NPV of 0. * * \note This calculation does not take any upfront into * account, even if one was given. */ public double fairSpread() { calculate(); Utils.QL_REQUIRE(fairSpread_ != null, () => "fair spread not available"); return(fairSpread_.Value); }
/*! returns the mean, defined as * \f[ \langle x \rangle = \frac{\sum w_i x_i}{\sum w_i}. \f] */ public double mean() { Utils.QL_REQUIRE(sampleWeight_ > 0.0, () => "sampleWeight_=0, insufficient"); return(sum_ / sampleWeight_); }
public virtual int dayCount(Date d1, Date d2) { Utils.QL_REQUIRE(!empty(), () => "No implementation provided"); return(dayCounter_.dayCount(d1, d2)); }
public override void calculate() { Utils.QL_REQUIRE(arguments_.averageType == Average.Type.Geometric, () => "not a geometric average option"); Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); Date exercise = arguments_.exercise.lastDate(); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double volatility = process_.blackVolatility().link.blackVol(exercise, payoff.strike()); double variance = process_.blackVolatility().link.blackVariance(exercise, payoff.strike()); double riskFreeDiscount = process_.riskFreeRate().link.discount(exercise); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double dividendYield = 0.5 * ( process_.riskFreeRate().link.zeroRate(exercise, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate() + process_.dividendYield().link.zeroRate(exercise, divdc, Compounding.Continuous, Frequency.NoFrequency).rate() + volatility * volatility / 6.0); double t_q = divdc.yearFraction(process_.dividendYield().link.referenceDate(), exercise); double dividendDiscount = Math.Exp(-dividendYield * t_q); double spot = process_.stateVariable().link.value(); Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying"); double forward = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forward, Math.Sqrt(variance / 3.0), riskFreeDiscount); results_.value = black.value(); results_.delta = black.delta(spot); results_.gamma = black.gamma(spot); results_.dividendRho = black.dividendRho(t_q) / 2.0; double t_r = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(t_r) + 0.5 * black.dividendRho(t_q); double t_v = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t_v) / Math.Sqrt(3.0) + black.dividendRho(t_q) * volatility / 6.0; try { results_.theta = black.theta(spot, t_v); } catch (Exception /*Error*/) { results_.theta = null; //Null<Real>(); } }
public virtual double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) { Utils.QL_REQUIRE(!empty(), () => "No implementation provided"); return(dayCounter_.yearFraction(d1, d2, refPeriodStart, refPeriodEnd)); }
public override void initialize(FloatingRateCoupon coupon) { coupon_ = coupon as OvernightIndexedCoupon; Utils.QL_REQUIRE(coupon_ != null, () => "wrong coupon type"); }
public AssetSwap(bool parAssetSwap, Bond bond, double bondCleanPrice, double nonParRepayment, double gearing, IborIndex iborIndex, double spread = 0.0, DayCounter floatingDayCount = null, Date dealMaturity = null, bool payBondCoupon = false) : base(2) { bond_ = bond; bondCleanPrice_ = bondCleanPrice; nonParRepayment_ = nonParRepayment; spread_ = spread; parSwap_ = parAssetSwap; Schedule tempSch = new Schedule(bond_.settlementDate(), bond_.maturityDate(), iborIndex.tenor(), iborIndex.fixingCalendar(), iborIndex.businessDayConvention(), iborIndex.businessDayConvention(), DateGeneration.Rule.Backward, false); // endOfMonth if (dealMaturity == null) { dealMaturity = bond_.maturityDate(); } Utils.QL_REQUIRE(dealMaturity <= tempSch.dates().Last(), () => "deal maturity " + dealMaturity + " cannot be later than (adjusted) bond maturity " + tempSch.dates().Last()); Utils.QL_REQUIRE(dealMaturity > tempSch.dates()[0], () => "deal maturity " + dealMaturity + " must be later than swap start date " + tempSch.dates()[0]); // the following might become an input parameter BusinessDayConvention paymentAdjustment = BusinessDayConvention.Following; Date finalDate = tempSch.calendar().adjust(dealMaturity, paymentAdjustment); Schedule schedule = tempSch.until(finalDate); // bondCleanPrice must be the (forward) clean price // at the floating schedule start date upfrontDate_ = schedule.startDate(); double dirtyPrice = bondCleanPrice_ + bond_.accruedAmount(upfrontDate_); double notional = bond_.notional(upfrontDate_); /* In the market asset swap, the bond is purchased in return for * payment of the full price. The notional of the floating leg is * then scaled by the full price. */ if (!parSwap_) { notional *= dirtyPrice / 100.0; } if (floatingDayCount == null) { legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withGearings(gearing) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); } else { legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withGearings(gearing) .withPaymentDayCounter(floatingDayCount) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); } foreach (CashFlow c in legs_[1]) { c.registerWith(update); } List <CashFlow> bondLeg = bond_.cashflows(); // skip bond redemption int i; for (i = 0; i < bondLeg.Count && bondLeg[i].date() <= dealMaturity; ++i) { // whatever might be the choice for the discounting engine // bond flows on upfrontDate_ must be discarded bool upfrontDateBondFlows = false; if (!bondLeg[i].hasOccurred(upfrontDate_, upfrontDateBondFlows)) { legs_[0].Add(bondLeg[i]); } } // if the first skipped cashflow is not the redemption // and it is a coupon then add the accrued coupon if (i < bondLeg.Count - 1) { Coupon c = bondLeg[i] as Coupon; if (c != null) { CashFlow accruedCoupon = new SimpleCashFlow(c.accruedAmount(dealMaturity), finalDate); legs_[0].Add(accruedCoupon); } } // add the nonParRepayment_ CashFlow nonParRepaymentFlow = new SimpleCashFlow(nonParRepayment_, finalDate); legs_[0].Add(nonParRepaymentFlow); Utils.QL_REQUIRE(!legs_[0].empty(), () => "empty bond leg to start with"); // special flows if (parSwap_) { // upfront on the floating leg double upfront = (dirtyPrice - 100.0) / 100.0 * notional; CashFlow upfrontCashFlow = new SimpleCashFlow(upfront, upfrontDate_); legs_[1].Insert(0, upfrontCashFlow); // backpayment on the floating leg // (accounts for non-par redemption, if any) double backPayment = notional; CashFlow backPaymentCashFlow = new SimpleCashFlow(backPayment, finalDate); legs_[1].Add(backPaymentCashFlow); } else { // final notional exchange CashFlow finalCashFlow = new SimpleCashFlow(notional, finalDate); legs_[1].Add(finalCashFlow); } Utils.QL_REQUIRE(!legs_[0].empty(), () => "empty bond leg"); foreach (CashFlow c in legs_[0]) { c.registerWith(update); } if (payBondCoupon) { payer_[0] = -1.0; payer_[1] = +1.0; } else { payer_[0] = +1.0; payer_[1] = -1.0; } }
public InverseCumulativePoisson(double lambda) { lambda_ = lambda; Utils.QL_REQUIRE(lambda_ > 0.0, () => "lambda must be positive"); }
public double floatingLegNPV() { calculate(); Utils.QL_REQUIRE(legNPV_.Count > 1 && legNPV_[1] != null, () => "floating-leg NPV not available"); return(legNPV_[1].GetValueOrDefault()); }
public IList <bool> isRegular() { Utils.QL_REQUIRE(isRegular_.Count > 0, () => "full interface (isRegular) not available"); return(isRegular_); }
public AssetSwap(bool payBondCoupon, Bond bond, double bondCleanPrice, IborIndex iborIndex, double spread, Schedule floatSchedule = null, DayCounter floatingDayCount = null, bool parAssetSwap = true) : base(2) { bond_ = bond; bondCleanPrice_ = bondCleanPrice; nonParRepayment_ = 100; spread_ = spread; parSwap_ = parAssetSwap; Schedule schedule = floatSchedule; if (floatSchedule == null) { schedule = new Schedule(bond_.settlementDate(), bond_.maturityDate(), iborIndex.tenor(), iborIndex.fixingCalendar(), iborIndex.businessDayConvention(), iborIndex.businessDayConvention(), DateGeneration.Rule.Backward, false); // endOfMonth } // the following might become an input parameter BusinessDayConvention paymentAdjustment = BusinessDayConvention.Following; Date finalDate = schedule.calendar().adjust(schedule.endDate(), paymentAdjustment); Date adjBondMaturityDate = schedule.calendar().adjust(bond_.maturityDate(), paymentAdjustment); Utils.QL_REQUIRE(finalDate == adjBondMaturityDate, () => "adjusted schedule end date (" + finalDate + ") must be equal to adjusted bond maturity date (" + adjBondMaturityDate + ")"); // bondCleanPrice must be the (forward) clean price // at the floating schedule start date upfrontDate_ = schedule.startDate(); double dirtyPrice = bondCleanPrice_ + bond_.accruedAmount(upfrontDate_); double notional = bond_.notional(upfrontDate_); /* In the market asset swap, the bond is purchased in return for * payment of the full price. The notional of the floating leg is * then scaled by the full price. */ if (!parSwap_) { notional *= dirtyPrice / 100.0; } if (floatingDayCount == null) { legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); } else { legs_[1] = new IborLeg(schedule, iborIndex) .withSpreads(spread) .withPaymentDayCounter(floatingDayCount) .withNotionals(notional) .withPaymentAdjustment(paymentAdjustment); } foreach (CashFlow c in legs_[1]) { c.registerWith(update); } List <CashFlow> bondLeg = bond_.cashflows(); foreach (CashFlow c in bondLeg) { // whatever might be the choice for the discounting engine // bond flows on upfrontDate_ must be discarded bool upfrontDateBondFlows = false; if (!(c.hasOccurred(upfrontDate_, upfrontDateBondFlows))) { legs_[0].Add(c); } } Utils.QL_REQUIRE(!legs_[0].empty(), () => "empty bond leg to start with"); // special flows if (parSwap_) { // upfront on the floating leg double upfront = (dirtyPrice - 100.0) / 100.0 * notional; CashFlow upfrontCashFlow = new SimpleCashFlow(upfront, upfrontDate_); legs_[1].Insert(0, upfrontCashFlow); // backpayment on the floating leg // (accounts for non-par redemption, if any) double backPayment = notional; CashFlow backPaymentCashFlow = new SimpleCashFlow(backPayment, finalDate); legs_[1].Add(backPaymentCashFlow); } else { // final notional exchange CashFlow finalCashFlow = new SimpleCashFlow(notional, finalDate); legs_[1].Add(finalCashFlow); } Utils.QL_REQUIRE(!legs_[0].empty(), () => "empty bond leg"); foreach (CashFlow c in legs_[0]) { c.registerWith(update); } if (payBondCoupon) { payer_[0] = -1.0; payer_[1] = +1.0; } else { payer_[0] = +1.0; payer_[1] = -1.0; } }
public BusinessDayConvention terminationDateBusinessDayConvention() { Utils.QL_REQUIRE(terminationDateConvention_.HasValue, () => "full interface (termination date business day convention) not available"); return(terminationDateConvention_.Value); }
public TqrEigenDecomposition(Vector diag, Vector sub, EigenVectorCalculation calc = EigenVectorCalculation.WithEigenVector, ShiftStrategy strategy = ShiftStrategy.CloseEigenValue) { iter_ = 0; d_ = new Vector(diag); int row = calc == EigenVectorCalculation.WithEigenVector ? d_.size() : calc == EigenVectorCalculation.WithoutEigenVector ? 0 : 1; ev_ = new Matrix(row, d_.size(), 0.0); int n = diag.size(); Utils.QL_REQUIRE(n == sub.size() + 1, () => "Wrong dimensions"); Vector e = new Vector(n, 0.0); int i; for (i = 1; i < e.Count; ++i) { e[i] = sub[i - 1]; } for (i = 0; i < ev_.rows(); ++i) { ev_[i, i] = 1.0; } for (int k = n - 1; k >= 1; --k) { while (!offDiagIsZero(k, e)) { int l = k; while (--l > 0 && !offDiagIsZero(l, e)) { ; } iter_++; double q = d_[l]; if (strategy != ShiftStrategy.NoShift) { // calculated eigenvalue of 2x2 sub matrix of // [ d_[k-1] e_[k] ] // [ e_[k] d_[k] ] // which is closer to d_[k+1]. // FLOATING_POINT_EXCEPTION double t1 = Math.Sqrt( 0.25 * (d_[k] * d_[k] + d_[k - 1] * d_[k - 1]) - 0.5 * d_[k - 1] * d_[k] + e[k] * e[k]); double t2 = 0.5 * (d_[k] + d_[k - 1]); double lambda = (Math.Abs(t2 + t1 - d_[k]) < Math.Abs(t2 - t1 - d_[k]))? t2 + t1 : t2 - t1; if (strategy == ShiftStrategy.CloseEigenValue) { q -= lambda; } else { q -= ((k == n - 1) ? 1.25 : 1.0) * lambda; } } // the QR transformation double sine = 1.0; double cosine = 1.0; double u = 0.0; bool recoverUnderflow = false; for (i = l + 1; i <= k && !recoverUnderflow; ++i) { double h = cosine * e[i]; double p = sine * e[i]; e[i - 1] = Math.Sqrt(p * p + q * q); if (e[i - 1].IsNotEqual(0.0)) { sine = p / e[i - 1]; cosine = q / e[i - 1]; double g = d_[i - 1] - u; double t = (d_[i] - g) * sine + 2 * cosine * h; u = sine * t; d_[i - 1] = g + u; q = cosine * t - h; for (int j = 0; j < ev_.rows(); ++j) { double tmp = ev_[j, i - 1]; ev_[j, i - 1] = sine * ev_[j, i] + cosine * tmp; ev_[j, i] = cosine * ev_[j, i] - sine * tmp; } } else { // recover from underflow d_[i - 1] -= u; e[l] = 0.0; recoverUnderflow = true; } } if (!recoverUnderflow) { d_[k] -= u; e[k] = q; e[l] = 0.0; } } } // sort (eigenvalues, eigenvectors), // code taken from symmetricSchureDecomposition.cpp List <KeyValuePair <double, List <double> > > temp = new InitializedList <KeyValuePair <double, List <double> > >(n); List <double> eigenVector = new InitializedList <double>(ev_.rows()); for (i = 0; i < n; i++) { if (ev_.rows() > 0) { eigenVector = ev_.column(i); } temp[i] = new KeyValuePair <double, List <double> >(d_[i], eigenVector); } temp.Sort(KeyValuePairCompare); // first element is positive for (i = 0; i < n; i++) { d_[i] = temp[i].Key; double sign = 1.0; if (ev_.rows() > 0 && temp[i].Value[0] < 0.0) { sign = -1.0; } for (int j = 0; j < ev_.rows(); ++j) { ev_[j, i] = sign * temp[i].Value[j]; } } }
public bool endOfMonth() { Utils.QL_REQUIRE(endOfMonth_.HasValue, () => "full interface (end of month) not available"); return(endOfMonth_.Value); }
/*! returns the maximum sample value */ public double max() { Utils.QL_REQUIRE(samples() > 0, () => "empty sample set"); return(max_); }
//! the settlementDays used for reference date calculation public virtual int settlementDays() { Utils.QL_REQUIRE(settlementDays_ != null, () => "settlement days not provided for this instance"); return(settlementDays_.Value); }
public virtual Date referenceDate() { Utils.QL_REQUIRE(referenceDate_ != null, () => "referenceDate not available for this instance"); return(referenceDate_); }