def tenor(self, date, *args, **kwargs): """ Parameters ---------- date: datetime-like Returns ------- QuantLib.Period, None Period representing the time to maturity or expiry of the instrument. Returns None if there is no tenor. """ try: return ql.PeriodParser.parse(self.get_attribute(TENOR_PERIOD)) except: try: try: tenor = float(self.get_attribute(TENOR_PERIOD)) if tenor < 1: return ql.Period(int(self.get_attribute(TENOR_PERIOD)*252), ql.Days) # TODO: Amend these approx. tenors return ql.Period(int(tenor), ql.Years) except: date = to_ql_date(date) maturity = to_ql_date(to_datetime(self.get_attribute(MATURITY_DATE))) if date >= maturity: return None calendar = ql.UnitedStates(ql.UnitedStates.NYSE) # TODO: This will have to be parametrized afterwards. return ql.Period(calendar.businessDaysBetween(date, maturity), ql.Days) except: # Impossible to find a tenor or maturity for this TimeSeries. return None
def _discount_factor_to_date(self, date, to_date, zero_rate, compounding, frequency): """ Calculate discount factor to a given date, at a given date, given an interest rate. Parameters ---------- date: QuantLib.Date, (c-vectorized) Reference date of the interest rate. to_date: QuantLib.Date, (c-vectorized) Maturity of the discount rate. zero_rate: scalar, (c-vectorized) Interest rate at `date`, with maturity `to_date`. compounding: QuantLib.Compounding Compounding convention of the interest rate. frequency: QuantLib.Frequency Compounding frequency of the interest rate. Returns ------- scalar The discount rate to `to_date`, equivalent to the given interest rate. """ date = to_ql_date(date) to_date = to_ql_date(to_date) return ql.InterestRate(zero_rate, self.day_counter, compounding, frequency).discountFactor( date, to_date, date, to_date)
def forward_rate_date_to_date(self, date, to_date1, to_date2, compounding, frequency, extrapolate=True): """ Parameters ---------- date: QuantLib.Date, (c-vectorized) Date of the yield curve. to_date1: QuantLib.Date, (c-vectorized) First maturity for the fra. to_date2: QuantLib.Date, (c-vectorized) Second maturity for the fra. compounding: QuantLib.Compounding Compounding convention for the rate. frequency: QuantLib.Frequency Frequency convention for the rate. extrapolate: bool, optional Whether to enable extrapolation. Returns ------- scalar Forward rate between `to_date1` and `to_date2`, implied by the yield curve at `date`. """ to_date1 = to_ql_date(to_date1) to_date2 = to_ql_date(to_date2) return self.yield_curve(date).forwardRate(to_date1, to_date2, self.day_counter, compounding, frequency, extrapolate).rate()
def credit_default_swap(self, date, notional, probability_handle, base_yield_curve_handle, upfront_price=1, last_available=True, *args, **kwargs): """ :param date: pd.Datetime or QuantLib.Date Reference Date :param notional: float Size of the contract :param probability_handle: QuantLib.DefaultProbabilityTermStructureHandle the curve used for the calculation :param base_yield_curve_handle: QuantLib.YieldTermStructureHandle the curve used for the calculation :param upfront_price: float The par value of the upfront payment. :param last_available: bool, optional Whether to use last available quotes if missing data. :return: QuantLib CDS Instrument The CDS Instrument for calculation purposes. """ recovery_rate = self.recovery_rate if 'recovery_rate' in kwargs.keys(): recovery_rate = kwargs['recovery_rate'] # maybe the user passed a quote multiplied by 100, if so we divide by 100 to be correctly used by the CDS. if upfront_price > 1: upfront_price = upfront_price / 100 upfront = 1 - upfront_price rate = self.quotes.get_values(index=date, last_available=last_available, fill_value=np.nan) maturity = to_ql_date(date) + self._tenor schedule = ql.Schedule(to_ql_date(date), maturity, self.coupon_frequency, self.calendar, self.business_convention, ql.Unadjusted, self.date_generation, False) cds = ql.CreditDefaultSwap(ql.Protection.Buyer, notional, upfront, rate, schedule, self.business_convention, self.day_counter) engine = ql.MidPointCdsEngine(probability_handle, recovery_rate, base_yield_curve_handle) cds.setPricingEngine(engine) return cds
def update_spread_from_timeseries(self, date, last_available=True): date = to_ql_date(date) self.spreads[date] = dict() for ts in self.ts_collection: spread = ts.get_values(date, last_available=last_available) try: spread_date_or_tenor = date + ql.Period( ts.ts_attributes[TENOR_PERIOD]) except AttributeError: spread_date_or_tenor = to_ql_date( ts.ts_attributes[MATURITY_DATE]) self.spreads[date][spread_date_or_tenor] = to_ql_quote_handle( spread)
def __init__(self, reference_date, spot, helpers, counter_rate_day_counter, counter_rate_compounding, counter_rate_frequency, base_rate_day_counter, base_rate_compounding, base_rate_frequency): """ Currency curve class analogous to QuantLib yield term structures. Parameters ---------- reference_date: QuantLib.Date The reference date for the curve. spot: scalar Value of the spot exchange rate at `reference_date`. helpers: list of :py:pbj:CurrencyFutureHelper Currency future helpers for construction of the curve. counter_rate_day_counter: QuantLib.DayCounter Day counter for the counter currency interest rates. counter_rate_compounding: QuantLib.Compounding Compounding convention for the counter currency interest rates. counter_rate_frequency: QuantLib.Frequency Compounding frequency for the counter currency interest rates. counter_rate_day_counter: QuantLib.DayCounter Day counter for the counter currency interest rates. counter_rate_compounding: QuantLib.Compounding Compounding convention for the counter currency interest rates. counter_rate_frequency: QuantLib.Frequency Compounding frequency for the counter currency interest rates. """ # TODO: Propose a similar class in QuantLib. self.reference_date = to_ql_date(reference_date) self.spot = spot self.helpers = sorted(helpers, key=attrgetter('maturity')) self.maturity_dates = [self.reference_date] + [ helper.maturity for helper in self.helpers ] self.nodes = {helper.maturity: helper.quote for helper in self.helpers} self.nodes[reference_date] = spot self.max_date = to_ql_date(self.maturity_dates[-1]) self._day_counter = ql.Actual365Fixed() self.counter_rate_day_counter = counter_rate_day_counter self.counter_rate_compounding = counter_rate_compounding self.counter_rate_frequency = counter_rate_frequency self.base_rate_day_counter = base_rate_day_counter self.base_rate_compounding = base_rate_compounding self.base_rate_frequency = base_rate_frequency # These will be initialized when necessary. self._base_rate_curve = None self._counter_rate_curve = None
def forward_rate_date_to_time(self, date, to_date, to_time, compounding, frequency, extrapolate=True): """ Parameters ---------- date: QuantLib.Date, (c-vectorized) Date of the yield curve. to_date: QuantLib.Date, (c-vectorized) First maturity for the fra. to_time: scalar, (c-vectorized) Time in years after `to_date` for the fra. compounding: QuantLib.Compounding Compounding convention for the rate. frequency: QuantLib.Frequency Frequency convention for the rate. extrapolate: bool, optional Whether to enable extrapolation. Returns ------- scalar Forward rate between `to_date` and `to_time`, implied by the yield curve at `date`. """ to_date = to_ql_date(to_date) to_date2 = self.calendar.advance(to_date, to_time) return self.yield_curve(date).forwardRate(to_date, to_date2, self.day_counter, compounding, frequency, extrapolate).rate()
def rate_helper(self, date, last_available=True, *args, **kwargs): """ Rate helper object for yield curve building. Parameters ---------- date: QuantLib.Date Reference date. last_available: bool Whether to use last available information if missing data. Returns ------- QuantLib.RateHelper Rate helper object for yield curve construction. """ # Returns None if impossible to obtain a rate helper from this time series if self.is_expired(date): return None rate = self.get_values(index=date, last_available=last_available, fill_value=np.nan) if np.isnan(rate): return None date = to_ql_date(date) try: tenor = self.tenor(date) except ValueError: # Return none if the deposit rate can't retrieve a tenor (i.e. is expired). return None return ql.OISRateHelper(self.settlement_days, tenor, to_ql_quote_handle(rate), self.overnight_index, ql.YieldTermStructureHandle(), False, 0, self.business_convention)
def exchange_rate_to_date(self, date): """ Exchange rate to a given date, using linear interpolation between nodes. Parameters ---------- date: QuantLib.Date Returns ------- scalar The exchange rate implied by the currency curve, using linear interpolation between nodes. """ date = to_ql_date(date) if date > self.max_date: raise ValueError( "The requested date ({0}) is after the curve's max date ({1})". format(date, self.max_date)) try: # If the date is in the nodes, use it. return self.nodes[date] except KeyError: # Use linear interpolation to obtain the result. lower_date_bound = find_le(self.maturity_dates, date) upper_date_bound = find_gt(self.maturity_dates, date) lower_bound = self.nodes[lower_date_bound] upper_bound = self.nodes[upper_date_bound] lower_interval = self._day_counter.yearFraction( lower_date_bound, date) interval = self._day_counter.yearFraction(lower_date_bound, upper_date_bound) return lower_bound + lower_interval * (upper_bound - lower_bound) / interval
def base_rate_to_date(self, date, to_date, counter_rate): """ Get interest rate of the base currency at a given date, to a given date. Parameters ---------- date: QuantLib.Date Reference date for the currency curve. to_date: QuantLib.Date Maturity date of the interest rate. counter_rate: scalar Interest rate of the counter currency to `to_date`. Returns ------- scalar Interest rate of the base currency to `to_date`. """ to_date = to_ql_date(to_date) currency_curve = self.currency_curve(date) helper = currency_curve.helpers[0] exchange_rate = currency_curve.exchange_rate_to_date(date) return helper.base_rate(quote=exchange_rate, spot=currency_curve.spot, counter_rate=counter_rate, maturity=to_date)
def upfront_npv(self, date, notional, probability_handle, base_yield_curve_handle, upfront_price=1, last_available=True, *args, **kwargs): """ :param date: pd.Datetime or QuantLib.Date Reference Date :param notional: float Size of the contract :param probability_handle: QuantLib.DefaultProbabilityTermStructureHandle the curve used for the calculation :param base_yield_curve_handle: QuantLib.YieldTermStructureHandle the curve used for the calculation :param upfront_price: float The par value of the upfront payment. :param last_available: bool, optional Whether to use last available quotes if missing data. :return: QuantLib CDS Instrument CDS Net Present Value of the coupon Leg. """ ql.Settings.instance().evaluationDate = to_ql_date(date) cds = self.credit_default_swap(date, notional, probability_handle, base_yield_curve_handle, upfront_price, last_available, *args, **kwargs) return cds.upfrontNPV()
def zero_rate_to_date(self, date, to_date, compounding, frequency, extrapolate=True): """ Parameters ---------- date: QuantLib.Date, (c-vectorized) Date of the yield curve. to_date: QuantLib.Date, (c-vectorized) Maturity of the rate. compounding: QuantLib.Compounding Compounding convention for the rate. frequency: QuantLib.Frequency Frequency convention for the rate. extrapolate: bool, optional Whether to enable extrapolation. Returns ------- scalar Zero rate for `to_date`, implied by the yield curve at `date`. """ to_date = to_ql_date(to_date) return self.yield_curve(date).zeroRate(to_date, self.day_counter, compounding, frequency, extrapolate).rate()
def __init__(self, ts_collection, base_yield_curve, calendar, day_counter, keep_only_on_the_run_month=False, ignore_errors=False, **other_rate_helper_args): """Time series of QuantLib YieldTermStructures objects. The QuantLib YieldTermStructure objects are stored in the dict self.yield_curves and are 'lazy' created and stored when requested. Parameters ---------- ts_collection: :py:obj:`TimeSeriesCollection` Collection of instruments for building the yield curves. base_yield_curve: :py:obj: 'YieldCurveTimeSeries' Base yield curve used for discounting the cash flows. calendar: QuantLib.Calendar Calendar for the yield curves. day_counter: QuantLib.DayCounter Day counter for the yield curves. keep_only_on_the_run_month: bool, optional Whether to use only one instrument per month for the yield curves. Defaults to False. ignore_errors: bool, optional Use last available yield curve if building a yield curve fails at a given date. Defaults to False. **other_rate_helper_args: key=value pairs, optional Additional arguments to pass to ``rate_helper`` methods of the instruments in `ts_collection`. """ self.ts_collection = ts_collection self.base_yield_curve = base_yield_curve self.calendar = calendar self.day_counter = day_counter self.keep_only_on_the_run_month = keep_only_on_the_run_month self.hazard_curves = dict() self.ignore_errors = ignore_errors self.other_rate_helper_args = other_rate_helper_args self.issue_dates = dict() # TODO: Remove issue_dates inspection from this class and add an issue_date attribute to all instrument. # classes. # Saving the issue dates. for ts in ts_collection: issue_date = None for issue_attribute in ISSUE_DATE_ATTRIBUTES: try: issue_date = to_ql_date(ts.get_attribute(issue_attribute)) break except AttributeError: continue if issue_date is None: # If impossible to decide the issue_date, it remains equal to "None" as we initialized above. # Then set it to 2000-01-01. issue_date = DEFAULT_ISSUE_DATE self.issue_dates[ts.ts_name] = issue_date
def _get_helpers(self, date): helpers = dict() ql_date = to_ql_date(date) for ts in self.ts_collection: ts_name = ts.ts_name issue_date = self.issue_dates[ts_name] yield_curve_handle = self.base_yield_curve_handle(date) helper = ts.cds_rate_helper( date=date, base_yield_curve_handle=yield_curve_handle, **self.other_rate_helper_args) if helper is not None: tenor = ts.tenor(date) maturity_date = self.calendar.advance(ql_date, tenor) ''' TODO: Wrap this ``ExtRateHelper`` inside a (properly named) class or namedtuple and always return these objects from the instrument classes. This prevents this method from calling ``ts.tenor`` and recalculating the ``maturity_date``, because these were already calculated inside each instrument's ``rate_helper`` method. ''' helper = ExtRateHelper(ts_name=ts_name, issue_date=issue_date, tenor=tenor, maturity_date=maturity_date, helper=helper) # Remove Helpers with the same maturity date (or tenor), keeping the last issued one - This is to avoid # error in QuantLib when trying to instantiate a yield curve with two helpers with same maturity # date or tenor. existing_helper = helpers.get(maturity_date, None) if existing_helper is None: helpers[maturity_date] = helper else: helpers[maturity_date] = max((helper, existing_helper), key=attrgetter('issue_date')) if self.keep_only_on_the_run_month: # If self.keep_only_on_the_run_month is True, then filter the returned helpers so that for each month there # is not more than one helper. This may be used to avoid distortions in curves being generated by a large # amount of TimeSeries. month_end_helpers = dict() for maturity_date, helper in helpers.items(): month_year = self._date_to_month_year(maturity_date) existing_helper = month_end_helpers.get(month_year, None) if existing_helper is None: month_end_helpers[month_year] = helper else: month_end_helpers[month_year] = max( (helper, existing_helper), key=attrgetter('issue_date')) helpers = { ndhelper.maturity_date: ndhelper for ndhelper in month_end_helpers.values() } return helpers
def spread_handle(self, date, last_available=True): date = to_ql_date(date) try: return self.spreads[date] except KeyError: self.update_spread_from_timeseries(date=date, last_available=last_available) return self.spreads[date]
def default_probability(self, date, period): """ The survival probability given a date and period :param date: Date of the yield curve. :param period: The tenor for the maturity. :return: The % chance of default given the date and tenor. """ ql_date = to_ql_date(date) ql_period = ql.Period(period) return self.hazard_curves[date].defaultProbability(ql_date + ql_period)
def net_present_value(self, date, *args, **kwargs): """ Parameters ---------- date: date-like Returns ------- The asset net present value """ date = to_ql_date(date) ql.Settings.instance().evaluationDate = date instrument = self.security(date=date, *args, **kwargs) return instrument.NPV()
def _calculate_cc(self, date, spot_price, currency_curve, DI_curve): maturity_date = self._maturity_on_the_run(date) future_price = currency_curve.exchange_rate_to_date( date, maturity_date) DI = DI_curve.zero_rate_to_date(date, maturity_date, ql.Compounded, ql.Annual) DI_rate = ql.InterestRate(DI, ql.Business252(), ql.Compounded, ql.Annual) date = to_ql_date(date) compound = spot_price * DI_rate.compoundFactor( date, maturity_date) / future_price rate = ql.InterestRate.impliedRate(compound, ql.Actual360(), ql.Simple, ql.Annual, date, maturity_date) return rate.rate()
def implied_term_structure_handle(self, date, future_date): """ A relinkable handle for a yield curve at a given date. Parameters ---------- date: Date of the yield curve. future_date: Date of the Implied Yield Curve Returns ------- QuantLib.RelinkableYieldTermStructureHandle A relinkable handle to the yield term structure object. """ future_date = to_ql_date(future_date) return ql.ImpliedTermStructure(self.yield_curve_handle(date), future_date)
def __init__(self, timeseries): super().__init__(timeseries=timeseries) # TODO: Add support for puttable bonds. # TODO: Here we assume that the call prices are always clean prices. Fix this! # TODO: Implement an option to reduce (some kind of 'telescopic') call dates. # if there are too much. This is useful in case we are treating a callable perpetual bond, for example. called_date = self.ts_attributes[CALLED_DATE] if called_date: self.expire_date = to_ql_date(to_datetime(called_date)) self.callability_schedule = ql.CallabilitySchedule() for call_date, call_price in self.call_schedule.ts_values.iteritems(): # The original bond (with maturity at self.maturity will be added to the components after its # instantiation below. call_date = to_ql_date(to_datetime(call_date)) callability_price = ql.CallabilityPrice(call_price, ql.CallabilityPrice.Clean) self.callability_schedule.append( ql.Callability(callability_price, ql.Callability.Call, call_date)) self.bond_components[call_date] = create_call_component( call_date, call_price, self.schedule, self.calendar, self.business_convention, self.coupon_frequency, self.date_generation, self.month_end, self.settlement_days, self.face_amount, self.coupons, self.day_counter, self.issue_date) self.bond = ql.CallableFixedRateBond(self.settlement_days, self.face_amount, self.schedule, self.coupons, self.day_counter, self.business_convention, self.redemption, self.issue_date, self.callability_schedule) self.bond_components[ self. maturity_date] = self.bond # Add the original bond to bond_components. self._bond_components_backup = self.bond_components.copy()
def discount_to_date(self, date, to_date): """ Parameters ---------- date: QuantLib.Date, (c-vectorized) The date of the yield curve. to_date: QuantLib.Date, (c-vectorized) The maturity for the discount rate. Returns ------- scalar The discount rate for `to_date` implied by the yield curve at `date`. """ to_date = to_ql_date(to_date) return self.yield_curve(date).discount(to_date)
def next_cc_maturity(date): """Next DDI future maturity (DDI future contracts dealt by BMF in Brazil). Parameters ---------- date: date-like Returns ------- QuantLib.Date """ date = to_ql_date(date) calendar = ql.Brazil() return calendar.advance( calendar.endOfMonth(calendar.advance(date, 2, ql.Days)), 1, ql.Days)
def exchange_rate_to_date(self, date, to_date): """ Get exchange rate implied by the curve at a given date, to a given date, with linear interpolation. Parameters ---------- date: QuantLib.Date Reference date of the currency curve. to_date: QuantLib.Date Maturity date for the future exchange rate. Returns ------- scalar Exchange rate to `to_date`, at `date`. """ to_date = to_ql_date(to_date) return self.currency_curve(date).exchange_rate_to_date(to_date)
def ts_to_dict(*args): """ Produce a date-indexed dictionary of tuples from the values of multiple time series. Parameters ---------- args: time series names (each time series represents a parameter). Returns ------- dict Dictionary of parameter tuples, indexed by dates. """ params_df = pd.concat([ts for ts in args], axis=1) params = dict() for data in params_df.itertuples(): params[to_ql_date(data[0])] = tuple(data[i] for i in range(1, len(data))) return params
def maturity(self, date, *args, **kwargs): """Maturity of the "next cupom cambial". Parameters ---------- date: QuantLib.Date Reference date. Returns ------- QuantLib.Date Maturity date. """ date = to_ql_date(date) calendar = self.calendar return calendar.advance( calendar.endOfMonth(calendar.advance(date, 2, ql.Days)), 1, ql.Days)
def update_curves(self, dates): """ Update ``self.yield_curves`` with the yield curves of each date in `dates`. Parameters ---------- dates: list of QuantLib.Dates """ dates = to_list(dates) for date in dates: ql_date = to_ql_date(date) ql.Settings.instance().evaluationDate = ql_date helpers_dict = self._get_helpers(date) # Instantiate the curve helpers = [ndhelper.helper for ndhelper in helpers_dict.values()] # Just bootstraping the nodes yield_curve = ql.PiecewiseLinearZero(ql_date, helpers, self.day_counter) # Get dates and discounts node_dates = yield_curve.dates() # node_discounts = [yield_curve.discount(date) for date in node_dates] node_rates = [ yield_curve.zeroRate(date, self.day_counter, ql.Continuous).rate() for date in node_dates ] # Freezing the curve so that nothing is bothered by changing the singleton (global variable) evaluationDate. # yield_curve = ql.DiscountCurve(node_dates, node_discounts, yield_curve.dayCounter()) yield_curve = ql.MonotonicCubicZeroCurve( node_dates, node_rates, self.day_counter, self.calendar, ql.MonotonicCubic(), ql.Continuous, ) yield_curve.enableExtrapolation() self.yield_curves[date] = yield_curve
def calibrate_swaption_model(date, model_class, term_structure_ts, swaption_vol_ts_collection): """ Calibrate a Hull-White QuantLib model. Parameters ---------- date: QuantLib.Date Calibration date. model_class: QuantLib.Model Model for calibration. term_structure_ts: :py:obj:`YieldCurveTimeSeries` Yield curve time series of the curve. swaption_vol_ts_collection: :py:obj:`TimeSeriesCollection` Collection of swaption volatility (Black, log-normal) quotes. Returns ------- QuantLib.Model Calibrated model. """ # This has only been tested for model_class = HullWhite date = to_ql_date(date) print("Calibrating {0} 1F short rate model for date = {1}".format( model_class, date)) yield_curve = term_structure_ts.yield_curve(date=date) term_structure = ql.YieldTermStructureHandle(yield_curve) ql.Settings.instance().evaluationDate = date model, engine = ql_swaption_engine(model_class=model_class, term_structure=term_structure) swaption_helpers = list() swaption_vol = generate_instruments(swaption_vol_ts_collection) for swaption in swaption_vol: swaption.set_yield_curve(yield_curve=yield_curve) helper = swaption.rate_helper(date=date) helper.setPricingEngine(engine) swaption_helpers.append(helper) optimization_method = ql.LevenbergMarquardt(1.0e-8, 1.0e-8, 1.0e-8) end_criteria = ql.EndCriteria(10000, 100, 1e-6, 1e-8, 1e-8) model.calibrate(swaption_helpers, optimization_method, end_criteria) return model
def tenor(self, date, **kwargs): """Tenor of the "next cupom cambial". Parameters ---------- date: QuantLib.Date Reference date. Returns ------- QuantLib.Period The tenor (period) to maturity. """ date = to_ql_date(date) maturity = self._maturity_on_the_run(date) days = self.calendar.businessDaysBetween(date, maturity) return ql.Period(days, ql.Days)
def spreaded_zero_rate_to_date(self, date, to_date, compounding, frequency, spread_handle, extrapolate=True): """ Parameters ---------- date: QuantLib.Date, (c-vectorized) Date of the yield curve. to_date: QuantLib.Date, (c-vectorized) Maturity of the rate. compounding: QuantLib.Compounding Compounding convention for the rate. frequency: QuantLib.Frequency Frequency convention for the rate. spread_handle: :py:obj:'SpreadHandle' The spread to be added to the yield curve rates. extrapolate: bool, optional Whether to enable extrapolation. Returns ------- scalar Zero rate for `to_date`, implied by the spreaded yield curve at `date`. """ to_date = to_ql_date(to_date) spread_curve = self.spreaded_interpolated_curve( date=date, spread_handle=spread_handle, compounding=compounding, frequency=frequency) return spread_curve.zeroRate(to_date, self.day_counter, compounding, frequency, extrapolate).rate()
def update_curves(self, dates): """ Update ``self.yield_curves`` with the yield curves of each date in `dates`. Parameters ---------- dates: list of QuantLib.Dates """ dates = to_list(dates) for date in dates: ql_date = to_ql_date(date) ql.Settings.instance().evaluationDate = ql_date helpers_dict = self._get_helpers(date) # Instantiate the curve helpers = [ndhelper.helper for ndhelper in helpers_dict.values()] # Just bootstrapping the nodes hazard_curve = ql.PiecewiseFlatHazardRate(ql_date, helpers, self.day_counter) hazard_curve.enableExtrapolation() self.hazard_curves[date] = hazard_curve