def yield_curve_update(self, date, calendar, day_counter, maturity, risk_free=None, compounding=ql.Continuous, frequency=ql.Once): """ :param date: date-like The date. :param calendar: QuantLib.Calendar The option calendar used to evaluate the model :param day_counter: QuantLib.DayCounter The option day count used to evaluate the model :param maturity: date-like The option maturity. :param risk_free: float, optional An override of the zero rate in case you don't wan't to use the yield curve implied one. :param compounding: QuantLib.Compounding, default=Continuous The compounding used to interpolate the curve. :param frequency: QuantLib.Frequency, default = Once The frequency of the quoted yield. """ ql_date = to_ql_date(date) mat_date = to_ql_date(maturity) if risk_free is not None: zero_rate = risk_free else: zero_rate = self.yield_curve.zero_rate_to_date(date=ql_date, to_date=mat_date, compounding=compounding, frequency=frequency) yield_curve = ql.FlatForward(0, calendar, to_ql_quote_handle(zero_rate), day_counter, compounding) self.risk_free_handle.linkTo(yield_curve)
def new_f(self, **kwargs): # Dates if kwargs.get("bypass_option_default_values", False): return f(self, **kwargs) try: kwargs['date'] = to_ql_date(kwargs['date']) except TypeError: kwargs['date'] = to_ql_date(kwargs['date'][0]) if kwargs.get('base_date', None) is None: kwargs['base_date'] = kwargs['date'] try: kwargs['base_date'] = to_ql_date(kwargs['base_date']) except TypeError: kwargs['base_date'] = to_ql_date(kwargs['base_date'][0]) # Option Setup Arguments if kwargs.get('base_equity_process', None) is None: kwargs['base_equity_process'] = self.base_equity_process else: base_equity_process = kwargs['base_equity_process'] try: kwargs['base_equity_process'] = base_equity_process(calendar=self.calendar, day_counter=self.day_counter) except TypeError: base_equity_process.calendar = self.calendar base_equity_process.day_counter = self.day_counter kwargs['base_equity_process'] = base_equity_process if kwargs.get('risk_free_yield_curve_ts', None) is None: kwargs['risk_free_yield_curve_ts'] = self.risk_free_yield_curve_ts if kwargs.get('underlying_instrument', None) is None: kwargs['underlying_instrument'] = self.underlying_instrument else: self.set_underlying_instrument(underlying_instrument=kwargs['underlying_instrument']) kwargs['underlying_instrument'] = self.underlying_instrument if kwargs.get('engine_name', None) is None: kwargs['engine_name'] = self.engine_name # Date setup search_date = kwargs['base_date'] if kwargs['date'] > kwargs['base_date'] else kwargs['date'] if kwargs.get('last_available', None) is None: kwargs['last_available'] = True last = kwargs['last_available'] # Underlying Timeseries Values underlying = kwargs['underlying_instrument'] if kwargs.get('spot_price', None) is None: spot_prices = getattr(underlying, UNADJUSTED_PRICE) kwargs['spot_price'] = spot_prices(index=search_date, last_available=last) if kwargs.get('dividend_yield', None) is None: dividend_yield = getattr(underlying, DIVIDEND_YIELD) kwargs['dividend_yield'] = dividend_yield(index=search_date, last_available=last) if kwargs.get('dividend_tax', None) is None: kwargs['dividend_tax'] = 0 # Option Timeseries Values if kwargs.get('option_price', None) is None: mid_prices = getattr(self.timeseries, MID_PRICE) kwargs['option_price'] = mid_prices(index=search_date, last_available=last) if kwargs.get('quote', None) is None: kwargs['quote'] = kwargs['option_price'] kwargs['volatility'] = kwargs.get('volatility', None) return f(self, **kwargs)
def zero_rate_to_date(self, date, to_date, compounding, frequency, extrapolate=True, day_counter=None): """ 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. day_counter: QuantLib.DayCounter, optional The day counter for the calculation. Returns ------- scalar Zero rate for `to_date`, implied by the yield curve at `date`. """ date = to_ql_date(date) to_date = to_ql_date(to_date) day_counter = day_counter if day_counter is not None else self.day_counter return self.yield_curve(date).zeroRate(to_date, day_counter, compounding, frequency, extrapolate).rate()
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 underlying_price(self, date, base_date, vol_last_available=False, dvd_tax_adjust=1, last_available=True, exercise_ovrd=None): """ :param date: date-like The date. :param base_date: date-like When date is a future date base_date is the last date on the "present" used to estimate future values. :param vol_last_available: bool, optional Whether to use last available data in case dates are missing in volatility values. :param dvd_tax_adjust: float, default=1 The multiplier used to adjust for dividend tax. For example, US dividend taxes are 30% so you pass 0.7. :param last_available: bool, optional Whether to use last available data in case dates are missing in ``quotes``. :param exercise_ovrd: str, optional Used to force the option model to use a specific type of option. Only working for American and European option types. :return: float The option underlying spot price. """ ql.Settings.instance().evaluationDate = to_ql_date(date) dt_maturity = to_datetime(self.option_maturity) if to_datetime(date) >= dt_maturity: return 0 else: ql.Settings.instance().evaluationDate = to_ql_date(date) if to_datetime(date) > to_datetime(base_date): self.option_engine(date=base_date, vol_last_available=vol_last_available, dvd_tax_adjust=dvd_tax_adjust, last_available=last_available, exercise_ovrd=exercise_ovrd) else: self.option_engine(date=date, vol_last_available=vol_last_available, dvd_tax_adjust=dvd_tax_adjust, last_available=last_available, exercise_ovrd=exercise_ovrd) return self.ql_process.spot_price_handle.value()
def option_exercise_type(exercise_type, date, maturity): if exercise_type.upper() == 'AMERICAN': return ql.AmericanExercise(to_ql_date(date), to_ql_date(maturity)) elif exercise_type.upper() == 'EUROPEAN': return ql.EuropeanExercise(to_ql_date(maturity)) else: raise ValueError('Exercise type not supported')
def security(self, date, maturity=None, yield_curve_time_series=None, *args, **kwargs): """ Parameters ---------- date: Date-Like The evaluation date maturity: QuantLib.Date The maturity date or call date to retrieve from self.bond_components yield_curve_time_series: :py:class:`YieldCurveTimeSeries` The yield curve used for discounting the bond to retrieve the NPV Returns ------- The python object representing a Bond """ date = to_ql_date(date) bond_components = self.bond_components.copy() yield_curve = yield_curve_time_series.yield_curve_handle(date=date) bond_components[self.maturity_date] = ql.FixedRateBond( self.settlement_days, self.face_amount, self.schedule, self.coupons, self.day_counter, self.business_convention, self.redemption, self.issue_date) if maturity is None: maturity = self.maturity_date else: maturity = to_ql_date(maturity) bond_components[maturity].setPricingEngine( ql.DiscountingBondEngine(yield_curve)) return bond_components[maturity]
def __init__(self, timeseries): super().__init__(timeseries=timeseries) self.option_type = self.ts_attributes[OPTION_TYPE] self.strike = float(self.ts_attributes[STRIKE_PRICE]) self.contract_size = float(self.ts_attributes[CONTRACT_SIZE]) self._maturity = to_ql_date(to_datetime(self.ts_attributes[MATURITY_DATE])) self.calendar = to_ql_calendar(self.ts_attributes[CALENDAR]) self.day_counter = to_ql_day_counter(self.ts_attributes[DAY_COUNTER]) self.exercise_type = self.ts_attributes[EXERCISE_TYPE] self.underlying_name = self.ts_attributes[UNDERLYING_INSTRUMENT] try: self.earliest_date = to_ql_date(self.ts_attributes[EARLIEST_DATE]) except KeyError: self.earliest_date = ql.Date.minDate() self.exercise = to_ql_option_exercise_type(self.exercise_type, self.earliest_date, self._maturity) self.payoff = to_ql_option_payoff(self.ts_attributes[PAYOFF_TYPE], to_ql_option_type(self.option_type), self.strike) self.option = to_ql_one_asset_option(self.payoff, self.exercise) self.risk_free_yield_curve_ts = None self.underlying_instrument = None # engine setup self.engine_name = 'FINITE_DIFFERENCES' self.base_equity_process = None self._implied_volatility = dict() self._implied_volatility_prices = dict()
def __init__(self, start_date, end_date, tenor, calendar, day_counter): self.start_date = to_ql_date(start_date) self.end_date = to_ql_date(end_date) self.tenor = ql.PeriodParser.parse(tenor) self.calendar = to_ql_calendar(calendar) self.day_counter = to_ql_day_counter(day_counter) self.schedule = ql.Schedule(self.start_date, self.end_date, self.tenor, self.calendar, ql.Unadjusted, ql.Unadjusted, ql.DateGeneration.Forward, False)
def nth_weekday_of_month(start_year, n_years, frequency, weekday, nth_day, min_date=None, max_date=None): """ Function to get a list of dates following a specific weekday at a specific recurrence inside a month example: the 3rd friday of the month, every 3 months. :param start_year: int The base year for the date calculation :param n_years: The amount of years ahead to forecast :param frequency: str The frequency of the monthly occurrence, ex: ANNUAL, SEMIANNUAL, EVERY_FOUR_MONTH, QUARTERLY, BIMONTHLY, MONTHLY" :param weekday: str The weekday of the recurrence, Monday, Tuesday... :param nth_day: int The nth occurrence inside of the month. :param min_date: Date-like, optional The minimum date of the list :param max_date: Date-like, optional The minimum date of the list :return: list of QuantLib.Dates """ frequency = str(frequency).upper() ql_frequency = to_ql_frequency(frequency) if not 0 < ql_frequency <= 12: raise ValueError( "Only supported frequencies are: ANNUAL, SEMIANNUAL, EVERY_FOUR_MONTH," " QUARTERLY, BIMONTHLY, MONTHLY") weekday = str(weekday).upper() ql_weekday = to_ql_weekday(weekday) nth_day = int(nth_day) dates = list() for j in range(n_years + 1): year = start_year + j for i in range(ql_frequency): month = int((i + 1) * (12 / ql_frequency)) dates.append(ql.Date.nthWeekday(nth_day, ql_weekday, month, year)) if min_date is not None and max_date is None: min_date = to_ql_date(min_date) dates = [date for date in dates if date >= min_date] elif min_date is None and max_date is not None: max_date = to_ql_date(max_date) dates = [date for date in dates if date <= max_date] elif min_date is not None and max_date is not None: min_date = to_ql_date(min_date) max_date = to_ql_date(max_date) dates = [date for date in dates if min_date <= date <= max_date] return dates
def implied_fx_rate_to_date(self, date, to_date): date = to_ql_date(date) to_date = to_ql_date(to_date) to_time = self.day_counter.yearFraction(date, to_date) continuous_rate = self.zero_rate_to_time(date, to_time, ql.Continuous, ql.NoFrequency) fx_spot = self.fx_spot_price[date].value() return fx_spot * np.exp(continuous_rate * to_time)
def update_spread_from_value(self, date, spread, tenor_date, day_counter, frequency, compounding): date = to_ql_date(date) tenor_date = to_ql_date(tenor_date) self.spreads[date] = dict() self.spreads[date][tenor_date] = ql.InterestRate( spread, day_counter, compounding, frequency) return self
def new_f(self, *args, **kwargs): # Just to avoid a bug when passing the dates try: date = to_ql_date(kwargs['date'][0]) except: date = to_ql_date(kwargs['date']) ql.Settings.instance().evaluationDate = date self.add_fixings(date=date) self.link_to_curves(date=date) return f(self, *args, **kwargs)
def base_rate(self, spot, counter_rate, quote, date, counter_rate_day_counter, counter_rate_compounding, counter_rate_frequency, base_rate_day_counter, base_rate_compounding, base_rate_frequency, maturity, *args, **kwargs): """Obtain the interest rate of the base currency. Parameters ---------- spot: scalar Spot exchange rate. counter_rate: scalar Interest rate of the base currency. quote: scalar, optional Quote of the contract. Default: see :py:func:`default_arguments_currency_future`. date: QuantLib.Date, optional Reference date. Default: see :py:func:`default_arguments_currency_future`. counter_rate_day_counter: QuantLib.DayCounter, optional Day counter of the counter currency interest rate. Default: see :py:func:`default_arguments_currency_future`. counter_rate_compounding: QuantLib.Compounding, optional Compounding convention of the counter currency interest rate. Default: see :py:func:`default_arguments_currency_future`. counter_rate_frequency: QuantLib.Frequency, optional Compounding frequency of the counter currency interest rate. Default: see :py:func:`default_arguments_currency_future`. base_rate_day_counter: QuantLib.DayCounter, optional Day base of the base currency interest rate. Default: see :py:func:`default_arguments_currency_future`. base_rate_compounding: QuantLib.Compounding, optional Compounding convention of the base currency interest rate. Default: see :py:func:`default_arguments_currency_future`. base_rate_frequency: QuantLib.Frequency, optional Compounding frequency of the base currency interest rate. Default: see :py:func:`default_arguments_currency_future`. maturity: QuantLib.Date, optional Maturity date of the contract. Default: see :py:func:`default_arguments_currency_future`. Returns ------- scalar Implied interest rate to `maturity` in the base currency. """ date = to_ql_date(date) maturity = to_ql_date(maturity) counter_rate = ql.InterestRate(counter_rate, counter_rate_day_counter, counter_rate_compounding, counter_rate_frequency) compound = spot * counter_rate.compoundFactor(date, maturity) / quote return ql.InterestRate_impliedRate(compound, base_rate_day_counter, base_rate_compounding, base_rate_frequency, date, maturity)
def default_probability_to_date(self, date, to_date): """ The default probability given a date and period :param date: Date of the yield curve. :param to_date: The target date :return: The % chance of default given the date and tenor. """ date = to_ql_date(date) to_date = to_ql_date(to_date) ql.Settings.instance().evaluationDate = date return self.hazard_curve(date).defaultProbability(to_date)
def hazard_rate_to_date(self, date, to_date): """ The hazard rate of a given date :param date: Date of the yield curve. :param to_date: The target date :return: The % chance of default given the date and tenor. """ date = to_ql_date(date) to_date = to_ql_date(to_date) ql.Settings.instance().evaluationDate = date return self.hazard_curve(date).hazardRate(to_date)
def calibrate_swaption_model(date, model_name, term_structure_ts, swaption_vol_ts_collection, solver_name=None, use_scipy=False, fix_mean=False, mean_reversion_value=0.03, **kwargs): """ Calibrate a QuantLib Swaption model. :param date: QuantLib.Date Calibration date. :param model_name: str The model name for calibration :param term_structure_ts: :py:obj:`YieldCurveTimeSeries` Yield curve time series of the curve. :param swaption_vol_ts_collection: :py:obj:`TimeSeriesCollection` Collection of swaption volatility (Black, log-normal) quotes. :param solver_name: str The solver to be used, see :py:func:calibrate_ql_model for options :param use_scipy: bool Whether to use the QuantLib solvers or Scipy solvers, see :py:func:calibrate_ql_model for options :param fix_mean: bool If you want to fix or not the mean reversion of the model :param mean_reversion_value: float Mean reversion value, used when the mean reversion is fixed. :return: QuantLib.CalibratedModel Calibrated model. """ date = to_ql_date(date) model_name = str(model_name).upper() term_structure = term_structure_ts.yield_curve_handle(date=date) ql.Settings.instance().evaluationDate = date if fix_mean: model = to_ql_short_rate_model(model_name=model_name)( term_structure, mean_reversion_value) else: model = to_ql_short_rate_model(model_name=model_name)(term_structure) engine = to_ql_swaption_engine(model_name=model_name, model=model) swaption_helpers = list() swaption_vol = generate_instruments(swaption_vol_ts_collection) for swaption in swaption_vol: swaption.link_to_term_structure(date=date, yield_curve=term_structure_ts) helper = swaption.rate_helper(date=date) helper.setPricingEngine(engine) swaption_helpers.append(helper) return calibrate_ql_model(date=date, model_name=model_name, model=model, helpers=swaption_helpers, solver_name=solver_name, use_scipy=use_scipy, max_iteration=10000, max_stationary_state_iteration=100, **kwargs)
def spread_rate(self, date, last_available=True, **kwargs): """ Returns the QuantLib.InterestRate representing the spread at date This will return the rate with the specific characteristics of the rate as Day Counter, Compounding and Frequency. This way when we ask for a rate to be used as spread we can be sure that the rate being used is correctly adjusted to the base rate characteristics :param date: QuantLib.Date Reference date. :param last_available: bool, optional Whether to use last available quotes if missing data. :param kwargs: :return:QuantLib.InterestRate The QuantLib object with the rate and characteristics """ date = to_ql_date(date) # Returns None if impossible to obtain a rate helper from this time series if self.is_expired(date): return None rate = self.quotes.get_values(index=date, last_available=last_available, fill_value=np.nan) if np.isnan(rate): return None return ql.InterestRate(rate, self.day_counter, self.compounding, self.frequency)
def rate_helper(self, date, last_available=True, spread=None, sigma=None, mean=None, **other_args): """Helper for yield curve construction. :param date: QuantLib.Date Reference date. :param last_available: bool, optional Whether to use last available quotes if missing data. :param spread: float Rate Spread :param sigma: :py:obj:`TimeSeries` The timeseries of the sigma, used for convexity calculation :param mean: :py:obj:`TimeSeries` The timeseries of the mean, used for convexity calculation :return QuantLib.RateHelper Rate helper for yield curve construction. """ # Returns None if impossible to obtain a rate helper from this time series date = to_ql_date(date) if self.is_expired(date, **other_args): return None rate = self.quotes.get_values(index=date, last_available=last_available, fill_value=np.nan) if np.isnan(rate): return None return None
def __init__(self, timeseries, calculate_convexity=True): super().__init__(timeseries, calculate_convexity=calculate_convexity) # Database Attributes self.last_delivery = to_ql_date( to_datetime(self.ts_attributes[LAST_DELIVERY])) self._index_tenor = ql.PeriodParser.parse( self.ts_attributes[INDEX_TENOR]) self.contract_size = float(self.ts_attributes[CONTRACT_SIZE]) self.tick_size = float(self.ts_attributes[TICK_SIZE]) self.tick_value = float(self.ts_attributes[TICK_VALUE]) self.term_number = int(self.ts_attributes[TERM_NUMBER]) self.term_period = to_ql_time_unit(self.ts_attributes[TERM_PERIOD]) self.settlement_days = int(self.ts_attributes[SETTLEMENT_DAYS]) # QuantLib Objects self.index = to_ql_rate_index(self.ts_attributes[INDEX], self._index_tenor) # QuantLib Attributes self._maturity = self.interest_start_date = self.index.valueDate( self.last_delivery) self.calendar = self.index.fixingCalendar() self.day_counter = self.index.dayCounter() self.business_convention = self.index.businessDayConvention() self.fixing_days = self.index.fixingDays() self.month_end = self.index.endOfMonth() self.interest_maturity_date = self.index.maturityDate( self.interest_start_date) self.futures_type = ql.Futures.IMM # Rate Helper self.helper_rate = ql.SimpleQuote(100) self.helper_convexity = ql.SimpleQuote(0)
def update_curves(self, dates): dates = to_list(dates) for date in dates: date = to_ql_date(date) ql.Settings.instance().evaluationDate = date spread_dict = dict() if self.spreads_ts_collection is not None: for ts in self.spreads_ts_collection: maturity = ts.maturity(date=date) rate = ts.spread_rate(date=date).equivalentRate( self.day_counter, self.compounding, self.frequency, date, maturity).rate() spread_dict[maturity] = ql.SimpleQuote(rate) elif self.spreads is not None: # passing the dict with dates and simple quotes directly. spread_dict = self.spreads date_list = list() spread_list = list() for maturity in sorted(spread_dict.keys()): date_list.append(maturity) spread_list.append(ql.QuoteHandle(spread_dict[maturity])) curve_handle = ql.YieldTermStructureHandle( self.yield_curve_time_series.yield_curve(date=date)) spread_curve = ql.SpreadedLinearZeroInterpolatedTermStructure( curve_handle, spread_list, date_list, self.compounding, self.frequency, self.day_counter) spread_curve.enableExtrapolation() self.spreaded_curves[date] = spread_curve
def spreaded_curve(self, date, spreads=None, compounding=None, frequency=None, day_counter=None): ql_date = to_ql_date(date) if compounding is None: compounding = self.compounding if frequency is None: frequency = self.frequency if day_counter is None: day_counter = self.day_counter if spreads is None: spreads_at_date = self.spreads[ql_date] else: spreads_at_date = spreads[ql_date] curve_handle = self.yield_curve_time_series.yield_curve_handle( date=ql_date) yield_curve = ql.ZeroSpreadedTermStructure( curve_handle, ql.QuoteHandle(spreads_at_date), compounding, frequency, day_counter) yield_curve.enableExtrapolation() self.spreaded_curves[ql_date] = yield_curve
def tenor(self, date, *args, **kwargs): """Get tenor of the deposit rate. Parameters ---------- date: QuantLib.Date Reference date. Returns ------- QuantLib.Period The tenor (period) to maturity of the deposit rate. """ try: # If the object has a tenor attribute, return it. assert self._tenor return self._tenor except (AttributeError, AssertionError): # If no tenor, then it must have a maturity. Use it to calculate the tenor. date = to_ql_date(date) if self.is_expired(date): raise ValueError( "The requested date is equal or higher than the instrument's maturity: {}" .format(self.name)) return ql.Period(self._maturity - date, ql.Days)
def rate_helper(self, date, last_available=True, *args, **kwargs): """ Get a Rate helper object for this instrument. Parameters ---------- date: QuantLib.Date Reference date. last_available: bool Whether to use last available quotes if missing data. Returns ------- QuantLib.RateHelper Object used to build yield curves. """ # Returns None if impossible to obtain a rate helper from this time series. if self.is_expired(date): return None rate = self.quotes.get_values(index=date, last_available=last_available, fill_value=np.nan) first_cc_rate = self.first_cc.get_values(index=date, last_available=last_available, fill_value=np.nan) if np.isnan(rate): return None if np.isnan(first_cc_rate): return None date = to_ql_date(date) reference_date = self.reference_date(date) tenor = self.tenor(date) maturity_date = self.calendar.advance(reference_date, tenor) day_counter = self.day_counter implied_rate = (((1 + first_cc_rate * day_counter.yearFraction(date, reference_date)) * (1 + rate * day_counter.yearFraction(reference_date, maturity_date)) - 1) / day_counter.yearFraction(date, maturity_date)) tenor = ql.Period(self.calendar.businessDaysBetween(date, maturity_date), ql.Days) return ql.DepositRateHelper(ql.QuoteHandle(ql.SimpleQuote(implied_rate)), tenor, 0, self.calendar, ql.Unadjusted, False, self.day_counter)
def ql_irr(cash_flow, first_amount, first_date): """ Calculate the IRR from a given cash flow :param cash_flow: list List of QuantLib.SimpleCashFlow (s) :param first_amount: Int The Amount of the first cash flow :param first_date: Date-Like The date of the first cash flow :return: float """ ql.Settings.instance().evaluationDate = to_ql_date(first_date) try: fixed_rate = ql.CashFlows.yieldRate(cash_flow, float(first_amount), ql.Actual365Fixed(), ql.Compounded, ql.Annual, False, ql.Date(), ql.Date(), 1.0e-6, 1000, 0.05) except RuntimeError: try: fixed_rate = ql.CashFlows.yieldRate(cash_flow, float(first_amount), ql.Actual365Fixed(), ql.Compounded, ql.Annual, False, ql.Date(), ql.Date(), 1.0e-6, 1000, -0.1) except RuntimeError: fixed_rate = ql.CashFlows.yieldRate(cash_flow, float(first_amount), ql.Actual365Fixed(), ql.Compounded, ql.Annual, False, ql.Date(), ql.Date(), 1.0e-6, 1000, -0.5) return fixed_rate
def __init__(self, timeseries, *args, **kwargs): super().__init__(timeseries) self.base_currency = to_ql_currency(self.ts_attributes[BASE_CURRENCY]) self.counter_currency = to_ql_currency( self.ts_attributes[COUNTER_CURRENCY]) self.maturity_date = to_ql_date(self.ts_attributes[MATURITY_DATE]) self.quotes = self.timeseries.price.ts_values self.calendar = to_ql_calendar(self.ts_attributes[CALENDAR]) self.base_rate_day_counter = to_ql_day_counter( self.ts_attributes[BASE_RATE_DAY_COUNTER]) self.counter_rate_day_counter = to_ql_day_counter( self.ts_attributes[COUNTER_RATE_DAY_COUNTER]) self.base_rate_compounding = to_ql_compounding( self.ts_attributes[BASE_RATE_COMPOUNDING]) self.counter_rate_compounding = to_ql_compounding( self.ts_attributes[COUNTER_RATE_COMPOUNDING]) self.base_rate_frequency = to_ql_frequency( self.ts_attributes[BASE_RATE_FREQUENCY]) self.counter_rate_frequency = to_ql_frequency( self.ts_attributes[COUNTER_RATE_FREQUENCY]) self.settlement_days = int(self.ts_attributes[SETTLEMENT_DAYS]) self.base_currency_future = BaseCurrencyFuture( self.base_currency, self.counter_currency, self.maturity_date, self.calendar, self.base_rate_day_counter, self.base_rate_compounding, self.base_rate_frequency, self.counter_rate_day_counter, self.counter_rate_compounding, self.counter_rate_frequency, self.settlement_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: date = to_ql_date(date) ql.Settings.instance().evaluationDate = 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(date, helpers, self.day_counter) hazard_dates = hazard_curve.dates() hazard_rates = [ hazard_curve.hazardRate(h_date) for h_date in hazard_dates ] hazard_curve = ql.HazardRateCurve(hazard_dates, hazard_rates, self.day_counter, self.calendar) hazard_curve.enableExtrapolation() self.hazard_curves[date] = hazard_curve
def volatility_update(self, date, calendar, day_counter, ts_option, underlying_name, vol_value=None, last_available=False): """ :param date: date-like The date. :param calendar: QuantLib.Calendar The option calendar used to evaluate the model :param day_counter: QuantLib.DayCounter The option day count used to evaluate the model :param ts_option: py:class:`TimeSeries` The option Timeseries. :param underlying_name: The underlying Timeseries ts_name :param vol_value: float, optional An override of the volatility value in case you don't wan't to use the timeseries one. :param last_available: bool, optional Whether to use last available data in case dates are missing in ``quotes``. :return: """ dt_date = to_datetime(date) vol_updated = True if vol_value is not None: volatility_value = vol_value else: volatility_value = ts_option.ivol_mid.get_values(index=dt_date, last_available=last_available) if np.isnan(volatility_value): volatility_value = 0 vol_updated = False back_constant_vol = ql.BlackConstantVol(0, calendar, to_ql_quote_handle(volatility_value), day_counter) self.volatility_handle.linkTo(back_constant_vol) self.vol_updated[underlying_name][to_ql_date(date)] = vol_updated
def rate_helper(self, date, last_available=True, spread=None, sigma=None, mean=None, **other_args): """Helper for yield curve construction. :param date: QuantLib.Date Reference date. :param last_available: bool, optional Whether to use last available quotes if missing data. :param spread: float Rate Spread :param sigma: :py:obj:`TimeSeries` The timeseries of the sigma, used for convexity calculation :param mean: :py:obj:`TimeSeries` The timeseries of the mean, used for convexity calculation :return QuantLib.RateHelper Rate helper for yield curve construction. """ # Returns None if impossible to obtain a rate helper from this time series date = to_ql_date(date) if self.is_expired(date, **other_args): return None rate = self.quotes.get_values(index=date, last_available=last_available, fill_value=np.nan) if np.isnan(rate): return None time = self.day_counter.yearFraction(date, self.maturity(date)) rate = ql.InterestRate(rate, self.day_counter, self.compounding, self.frequency) self.helper_rate.setValue(rate.equivalentRate(ql.Simple, ql.Annual, time).rate()) return ql.DepositRateHelper(ql.QuoteHandle(self.helper_rate), ql.Period(self.day_counter.dayCount(date, self._maturity), ql.Days), self.fixing_days, self.calendar, self.business_convention, self.month_end, self.day_counter)
def set_coupon_rate_helper(self): """Set Rate Helper if None has been defined yet Returns ------- QuantLib.RateHelper """ for call_date, call_price in self.call_schedule.ts_values.iteritems(): call_date = to_ql_date(call_date) schedule = create_schedule_for_component(call_date, self.schedule, self.calendar, self.business_convention, self.coupon_frequency, self.date_generation, self.month_end) self._clean_price[call_date] = ql.SimpleQuote(100) self._bond_rate_helper[call_date] = ql.FixedRateBondHelper( ql.QuoteHandle(self._clean_price[call_date]), self.settlement_days, self.face_amount, schedule, self.coupons, self.day_counter, self.business_convention, self.redemption, self.issue_date) self._clean_price[self.maturity_date] = ql.SimpleQuote(100) self._bond_rate_helper[self.maturity_date] = ql.FixedRateBondHelper( ql.QuoteHandle(self._clean_price[self.maturity_date]), self.settlement_days, self.face_amount, self.schedule, self.coupons, self.day_counter, self.business_convention, self.redemption, self.issue_date)