From f29e7d446c7b5b75bda353fb82d48cd9b4a7322f Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Thu, 14 Aug 2025 17:14:36 -0400 Subject: [PATCH 1/7] fix examples --- examples/american_option.py | 14 ++++++------ examples/basic_example.py | 6 +++--- examples/option_valuation.py | 4 ++-- examples/traits_example.py | 42 ++++++++++++++++++++---------------- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/examples/american_option.py b/examples/american_option.py index 343b110e8..60a5aa8d4 100644 --- a/examples/american_option.py +++ b/examples/american_option.py @@ -7,15 +7,15 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ from quantlib.instruments.api import AmericanExercise, VanillaOption, OptionType -from quantlib.instruments.payoffs import PlainVanillaPayoff +from quantlib.payoffs import PlainVanillaPayoff from quantlib.pricingengines.api import BaroneAdesiWhaleyApproximationEngine from quantlib.pricingengines.api import FdBlackScholesVanillaEngine from quantlib.processes.black_scholes_process import BlackScholesMertonProcess from quantlib.quotes import SimpleQuote from quantlib.settings import Settings from quantlib.time.api import Actual365Fixed, Date, May, TARGET -from quantlib.termstructures.volatility.api import BlackConstantVol -from quantlib.termstructures.yields.api import HandleYieldTermStructure, FlatForward +from quantlib.termstructures.volatility.api import BlackConstantVol, HandleBlackVolTermStructure +from quantlib.termstructures.yields.api import RelinkableHandleYieldTermStructure, FlatForward from quantlib.methods.finitedifferences.solvers.fdmbackwardsolver \ import FdmSchemeDesc @@ -25,7 +25,7 @@ def main(): Settings.instance().evaluation_date = todays_date settlement_date = Date(17, May, 1998) - risk_free_rate = HandleYieldTermStructure() + risk_free_rate = RelinkableHandleYieldTermStructure() risk_free_rate.link_to( FlatForward( reference_date=settlement_date, @@ -43,9 +43,11 @@ def main(): # market data underlying = SimpleQuote(36.0) - volatility = BlackConstantVol(todays_date, TARGET(), 0.20, + volatility = HandleBlackVolTermStructure( + BlackConstantVol(todays_date, TARGET(), 0.20, Actual365Fixed()) - dividend_yield = HandleYieldTermStructure() + ) + dividend_yield = RelinkableHandleYieldTermStructure() dividend_yield.link_to( FlatForward( reference_date=settlement_date, diff --git a/examples/basic_example.py b/examples/basic_example.py index 82d2cbbd8..35329c290 100644 --- a/examples/basic_example.py +++ b/examples/basic_example.py @@ -9,7 +9,7 @@ from quantlib.quotes import SimpleQuote from quantlib.settings import Settings from quantlib.time.api import TARGET, Actual365Fixed, today -from quantlib.termstructures.yields.api import FlatForward, HandleYieldTermStructure +from quantlib.termstructures.yields.api import FlatForward, RelinkableHandleYieldTermStructure from quantlib.termstructures.volatility.api import BlackConstantVol @@ -36,8 +36,8 @@ underlyingH = SimpleQuote(underlying) # bootstrap the yield/dividend/vol curves -flat_term_structure = HandleYieldTermStructure() -flat_dividend_ts = HandleYieldTermStructure() +flat_term_structure = RelinkableHandleYieldTermStructure() +flat_dividend_ts = RelinkableHandleYieldTermStructure() flat_term_structure.link_to( FlatForward( diff --git a/examples/option_valuation.py b/examples/option_valuation.py index 62d316469..2f28c63b7 100644 --- a/examples/option_valuation.py +++ b/examples/option_valuation.py @@ -44,7 +44,7 @@ from quantlib.termstructures.yields.api import ( PiecewiseYieldCurve, DepositRateHelper, BootstrapTrait, HandleYieldTermStructure ) -from quantlib.termstructures.volatility.api import BlackConstantVol +from quantlib.termstructures.volatility.api import BlackConstantVol, HandleBlackVolTermStructure from quantlib.termstructures.yields.api import SwapRateHelper def dividendOption(): @@ -169,7 +169,7 @@ def dividendOption(): print('Creating process') - bsProcess = BlackScholesProcess(underlying_priceH, HandleYieldTermStructure(riskFreeTS), flatVolTS) + bsProcess = BlackScholesProcess(underlying_priceH, HandleYieldTermStructure(riskFreeTS), HandleBlackVolTermStructure(flatVolTS)) # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/examples/traits_example.py b/examples/traits_example.py index 06b136204..d8e117064 100644 --- a/examples/traits_example.py +++ b/examples/traits_example.py @@ -13,9 +13,8 @@ from quantlib.quotes import SimpleQuote from quantlib.settings import Settings from quantlib.time.api import TARGET, Actual365Fixed, today, Date as QlDate -from quantlib.termstructures.yields.api import FlatForward -from quantlib.termstructures.volatility.equityfx.black_vol_term_structure \ - import BlackConstantVol +from quantlib.termstructures.yields.api import FlatForward, HandleYieldTermStructure +from quantlib.termstructures.volatility.api import BlackConstantVol settings = Settings.instance() @@ -32,7 +31,7 @@ class OptionValuation(HasTraits): # options parameters - option_type = Enum(Put, Call) + option_type = Enum(OptionType.Put, OptionType.Call) underlying = Float(36) strike = Float(40) dividend_yield = Range(0.0, 0.5) @@ -50,12 +49,6 @@ class OptionValuation(HasTraits): ### Traits view ########################################################## - traits_view = View( - Item('option_type', editor=EnumEditor(values={Put:'Put', Call:'Call'})), - 'underlying', 'strike', 'dividend_yield', 'risk_free_rate', - 'volatility', 'maturity', - HGroup( Item('option_npv', label='Option value')) - ) ### Private protocol ##################################################### @@ -73,16 +66,20 @@ def _get_option_npv(self): underlyingH = SimpleQuote(self.underlying) # bootstrap the yield/dividend/vol curves - flat_term_structure = FlatForward( - reference_date = settlement_date, - forward = self.risk_free_rate, - daycounter = self.daycounter + flat_term_structure = HandleYieldTermStructure( + FlatForward( + reference_date = settlement_date, + forward = self.risk_free_rate, + daycounter = self.daycounter + ) ) - flat_dividend_ts = FlatForward( - reference_date = settlement_date, - forward = self.dividend_yield, - daycounter = self.daycounter + flat_dividend_ts = HandleYieldTermStructure( + FlatForward( + reference_date = settlement_date, + forward = self.dividend_yield, + daycounter = self.daycounter + ) ) flat_vol_ts = BlackConstantVol( @@ -105,10 +102,17 @@ def _get_option_npv(self): return european_option.net_present_value +traits_view = View( + Item('option_type', editor=EnumEditor(values={OptionType.Put:'Put', OptionType.Call:'Call'})), + 'underlying', 'strike', 'dividend_yield', 'risk_free_rate', + 'volatility', 'maturity', + HGroup( Item('option_npv', label='Option value')) +) + if __name__ == '__main__': model = OptionValuation() - model.configure_traits() + model.configure_traits(view=traits_view) ### EOF ####################################################################### From caf6ce6635d26588f1835b29e49d2442c55cf6c2 Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Tue, 19 Aug 2025 10:32:28 -0400 Subject: [PATCH 2/7] simplify script --- examples/data/df_SPX_24jan2011.pkl | Bin 94568 -> 86721 bytes examples/scripts/SPX_Options.py | 56 +++++++++++++---------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/examples/data/df_SPX_24jan2011.pkl b/examples/data/df_SPX_24jan2011.pkl index c3e7682e262e656b1d6b8c55c84f3ceae8ab8b6c..fe81e3ee2e823fba22c6aac79e9540927843cd5c 100644 GIT binary patch literal 86721 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 literal 94568 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 diff --git a/examples/scripts/SPX_Options.py b/examples/scripts/SPX_Options.py index 41fd8458b..3b5f9aad4 100644 --- a/examples/scripts/SPX_Options.py +++ b/examples/scripts/SPX_Options.py @@ -1,4 +1,3 @@ -from __future__ import print_function # -*- coding: utf-8 -*- # 3 @@ -6,11 +5,11 @@ # Standardized Option Quotes Data Format # ====================================== -# +# # To facilitate model calibration, a standard input format has been defined, which contains all the # necessary data. The data is held in a [Panda](http://pandas.pydata.org) table, with one row per quote and # 8 columns, as follows: -# +# # * dtTrade: Quote date, or time stamp # * Strike: Ditto # * dtExpiry: Option expiry date @@ -19,23 +18,23 @@ # * Type: European/American # * PBid: Bid price # * PAsk: Ask price -# -# Note that we do not include the dividend yield nor the risk-free rate in the data set: The +# +# Note that we do not include the dividend yield nor the risk-free rate in the data set: The # implied forward price and risk-free rate are estimated from the call/put parity. -# +# # SPX Option Data Processing # -------------------------- -# +# # As an illustration, we provide below the procedure for converting raw SPX option data, as published by the [CBOE](http://www.cboe.com/DelayedQuote/QuoteTableDownload.aspx), into the standard input format. -# +# # ### SPX Utility functions -# +# # These functions parse the SPX option names, and extract expiry date and strike. # -import pandas -import dateutil, datetime +import pandas as pd +import dateutil import re def ExpiryMonth(s): @@ -58,26 +57,26 @@ def parseSPX(s): """ Parse an SPX quote string, return expiry date and strike """ - tokens = spx_symbol.split(s) + tokens = spx_symbol.split(s.iloc[0]) if len(tokens) == 1: - return {'dtExpiry': None, 'strike': -1} + return {'Strike': -1, 'dtExpiry': None} year = 2000 + int(tokens[1]) day = int(tokens[2]) month = ExpiryMonth(tokens[3]) strike = float(tokens[4]) - dtExpiry = datetime.date(year, month, day) + dtExpiry = pd.Timestamp(year=year, month=month, day=day) - return ({'dtExpiry': dtExpiry, 'strike': strike}) + return {'Strike': strike, 'dtExpiry': dtExpiry} # # ### Reading the SPX raw data file -# -# The csv file downloaded from the CBOE site can be converted into a standard panda table by the following function. +# +# The csv file downloaded from the CBOE site can be converted into a standard panda table by the following function. # @@ -93,12 +92,12 @@ def read_SPX_file(option_data_file): lineTwo = fid.readline() dt = lineTwo.split('@')[0] - dtTrade = dateutil.parser.parse(dt).date() + dtTrade = pd.to_datetime(dt) print('Dt Calc: %s Spot: %f' % (dtTrade, spot)) # read all option price records as a data frame - df = pandas.io.parsers.read_csv(option_data_file, header=0, sep=',', skiprows=[0,1]) + df = pd.read_csv(option_data_file, header=0, sep=',', skiprows=[0,1]) # split and stack calls and puts call_df = df[['Calls', 'Bid', 'Ask']] @@ -109,16 +108,13 @@ def read_SPX_file(option_data_file): put_df = put_df.rename(columns = {'Puts':'Spec', 'Bid.1':'PBid', 'Ask.1':'PAsk'}) put_df['Type'] = 'P' - - df_all = call_df.append(put_df, ignore_index=True) + df_all = pd.concat([call_df, put_df], ignore_index=True) # parse Calls and Puts columns for strike and contract month # insert into data frame - - cp = [parseSPX(s) for s in df_all['Spec']] - df_all['Strike'] = [x['strike'] for x in cp] - df_all['dtExpiry'] = [x['dtExpiry'] for x in cp] - + df_all = pd.concat([df_all, + df_all[["Spec"]].apply(parseSPX, axis="columns", result_type="expand")], + axis=1) del df_all['Spec'] df_all = df_all[(df_all['Strike'] > 0) & (df_all['PBid']>0) \ @@ -126,18 +122,14 @@ def read_SPX_file(option_data_file): df_all['dtTrade'] = dtTrade df_all['Spot'] = spot - return df_all -option_data_file = \ - '../data/SPX-Options-24jan2011.csv' - if __name__ == '__main__': + option_data_file = '../data/SPX-Options-24jan2011.csv' df_SPX = read_SPX_file(option_data_file) print('%d records processed' % len(df_SPX)) # save a csv file and pickled data frame df_SPX.to_csv('../data/df_SPX_24jan2011.csv', index=False) - df_SPX.to_pickle('../data/df_SPX_24jan2011.pkl', protocol=4) + df_SPX.to_pickle('../data/df_SPX_24jan2011.pkl') print('File saved') - From bd76a30310bc894cf44e605f5fb348e2eb86ffc0 Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Wed, 20 Aug 2025 11:46:59 -0400 Subject: [PATCH 3/7] better docstring --- quantlib/models/shortrate/onefactor_model.pyx | 2 +- quantlib/models/shortrate/twofactor_model.pyx | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/quantlib/models/shortrate/onefactor_model.pyx b/quantlib/models/shortrate/onefactor_model.pyx index c5202bf12..36e3fabbb 100644 --- a/quantlib/models/shortrate/onefactor_model.pyx +++ b/quantlib/models/shortrate/onefactor_model.pyx @@ -32,7 +32,7 @@ cdef class ShortRateDynamics: return self._thisptr.get().shortRate(t, variable) cdef class OneFactorModel(ShortRateModel): - + """Single-factor short-rate model abstract class""" @property def dynamics(self): """short-rate dynamics diff --git a/quantlib/models/shortrate/twofactor_model.pyx b/quantlib/models/shortrate/twofactor_model.pyx index 027b7277c..3a0c3c394 100644 --- a/quantlib/models/shortrate/twofactor_model.pyx +++ b/quantlib/models/shortrate/twofactor_model.pyx @@ -6,15 +6,34 @@ cimport quantlib._stochastic_process as _sp from quantlib.stochastic_process cimport StochasticProcess1D cdef class ShortRateDynamics: + r"""Class describing the dynamics of the two state variables + + We assume here that the short-rate is a function of two state + variables :math:`x` and :math:`y`. + + .. math:: + r_t = f(t, x_t, y_t) + of two state variables :math:`x_t` and :math:`y_t`. These stochastic + processes satisfy + + .. math:: + x_t = \mu_x(t, x_t)dt + \sigma_x(t, x_t) dW_t^x\\ + y_t = \mu_y(t,y_t)dt + \sigma_y(t, y_t) dW_t^y + + where :math:`W^x` and :math:`W^y` are two brownian motions satisfying + + .. math:: + dW^x_t dW^y_t = \rho dt + """ @property - def process_x(self): + def x_process(self): cdef StochasticProcess1D sp = StochasticProcess1D.__new__(StochasticProcess1D) sp._thisptr = static_pointer_cast[_sp.StochasticProcess](self._thisptr.get().xProcess()) return sp @property - def process_x(self): + def y_process(self): cdef StochasticProcess1D sp = StochasticProcess1D.__new__(StochasticProcess1D) sp._thisptr = static_pointer_cast[_sp.StochasticProcess](self._thisptr.get().yProcess()) return sp @@ -24,7 +43,7 @@ cdef class ShortRateDynamics: @property def correlation(self): - """Correlation :math:`rho` between the two brownian motions""" + """Correlation :math:`\\rho` between the two brownian motions""" return self._thisptr.get().correlation() cdef class TwoFactorModel(ShortRateModel): From 2b29030acc05227a26b6d72e948c44d42bbb2cb7 Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Mon, 29 Sep 2025 16:20:20 -0400 Subject: [PATCH 4/7] docstrings improvement --- quantlib/instrument.pyx | 15 +++------ quantlib/instruments/bond.pyx | 21 ++++++++++--- quantlib/models/equity/heston_model.pyx | 3 +- quantlib/models/shortrate/twofactor_model.pyx | 8 +++++ .../models/shortrate/twofactormodels/g2.pyx | 16 ++++++++++ quantlib/option.pyx | 6 ++-- quantlib/payoffs.pyx | 17 ++++------ quantlib/termstructure.pyx | 20 +++++++++--- .../credit/flat_hazard_rate.pyx | 19 ++++++------ .../credit/interpolated_hazardrate_curve.pyx | 20 ++++++------ .../termstructures/default_term_structure.pyx | 31 +++++++++++++++++-- 11 files changed, 120 insertions(+), 56 deletions(-) diff --git a/quantlib/instrument.pyx b/quantlib/instrument.pyx index 456fb2757..3159ec421 100644 --- a/quantlib/instrument.pyx +++ b/quantlib/instrument.pyx @@ -26,8 +26,8 @@ cdef class Instrument(Observable): return self._thisptr.get().NPV() @property - def error_estimate(self) -> Real: - """error estimate on the NPV when available""" + def error_estimate(self): + """:obj:`Real`: error estimate on the NPV when available""" return self._thisptr.get().errorEstimate() property npv: @@ -36,16 +36,11 @@ cdef class Instrument(Observable): return self._thisptr.get().NPV() @property - def is_expired(self) -> bool: - """whether the instrument might ave value greater than zero.""" + def is_expired(self): + """:obj:`bool`: whether the instrument might have value greater than zero.""" return self._thisptr.get().isExpired() @property def valuation_date(self): - """the date the net present value refers to. - - Returns - ------- - valuation_date: :class:`~quantlib.time.date.Date` - """ + """:class:`~quantlib.time.date.Date`: the date the net present value refers to.""" return date_from_qldate(self._thisptr.get().valuationDate()) diff --git a/quantlib/instruments/bond.pyx b/quantlib/instruments/bond.pyx index 4e678c6d5..ecbd1b5a2 100644 --- a/quantlib/instruments/bond.pyx +++ b/quantlib/instruments/bond.pyx @@ -47,32 +47,43 @@ cdef class Bond(Instrument): @property def settlement_days(self): + """:obj:`int`""" return self.as_ptr().settlementDays() @property def calendar(self): + """:class:`quantlib.time.date.calendar.Calendar`""" cdef Calendar c = Calendar.__new__(Calendar) c._thisptr = self.as_ptr().calendar() return c @property def start_date(self): - """ Bond start date""" + """:class:`~quantlib.time.date.Date`: Bond start date""" return date_from_qldate(self.as_ptr().startDate()) @property def maturity_date(self): - """ Bond maturity date""" + """:class:`~quantlib.time.date.Date`: Bond maturity date""" return date_from_qldate(self.as_ptr().maturityDate()) @property def issue_date(self): - """ Bond issue date""" + """:class:`~quantlib.time.date.Date`: Bond issue date""" return date_from_qldate(self.as_ptr().issueDate()) def settlement_date(self, Date from_date=Date()): - """ Returns the bond settlement date after the given date.""" + """Returns the bond settlement date after the given date. + + Parameters + ---------- + from_date : :class:`quantlib.time.date.Date` + + Returns + ------- + :class:`quantlib.time.date.Date` + """ return date_from_qldate(self.as_ptr().settlementDate(from_date._thisptr)) def clean_price(self, *args): @@ -122,7 +133,7 @@ cdef class Bond(Instrument): @property def cashflows(self): - """ cash flow stream as a :class:`~quantlib.cashflow.Leg`.""" + """:class:`~quantlib.cashflow.Leg`: cash flow stream""" cdef Leg leg = Leg.__new__(Leg) leg._thisptr = self.as_ptr().cashflows() return leg diff --git a/quantlib/models/equity/heston_model.pyx b/quantlib/models/equity/heston_model.pyx index f657710dd..c0fda9f2f 100644 --- a/quantlib/models/equity/heston_model.pyx +++ b/quantlib/models/equity/heston_model.pyx @@ -73,8 +73,9 @@ cdef class HestonModel: process._thisptr)) ) + @property def process(self): - """underlying process""" + """:class:`~quantlib.processes.heston_process.HestonProcess: unnderlying process""" cdef HestonProcess process = HestonProcess.__new__(HestonProcess) process._thisptr = static_pointer_cast[_sp.StochasticProcess]( self._thisptr.get().process()) diff --git a/quantlib/models/shortrate/twofactor_model.pyx b/quantlib/models/shortrate/twofactor_model.pyx index 3a0c3c394..110b5fe54 100644 --- a/quantlib/models/shortrate/twofactor_model.pyx +++ b/quantlib/models/shortrate/twofactor_model.pyx @@ -28,12 +28,14 @@ cdef class ShortRateDynamics: @property def x_process(self): + """Risk-neutral dynamics of the first state variable x""" cdef StochasticProcess1D sp = StochasticProcess1D.__new__(StochasticProcess1D) sp._thisptr = static_pointer_cast[_sp.StochasticProcess](self._thisptr.get().xProcess()) return sp @property def y_process(self): + """Risk-neutral dynamics of the second state variable y""" cdef StochasticProcess1D sp = StochasticProcess1D.__new__(StochasticProcess1D) sp._thisptr = static_pointer_cast[_sp.StochasticProcess](self._thisptr.get().yProcess()) return sp @@ -50,6 +52,12 @@ cdef class TwoFactorModel(ShortRateModel): @property def dynamics(self): + """short-rate dynamics + + Returns + ------- + dynamics : :class:`~quantlib.models.shortrate.twofactor_model.ShortRateDynamics` + """ cdef ShortRateDynamics dyn = ShortRateDynamics.__new__(ShortRateDynamics) dyn._thisptr = (<_tfm.TwoFactorModel*>self._thisptr.get()).dynamics() return dyn diff --git a/quantlib/models/shortrate/twofactormodels/g2.pyx b/quantlib/models/shortrate/twofactormodels/g2.pyx index bdaae62dc..17ae4b2fd 100644 --- a/quantlib/models/shortrate/twofactormodels/g2.pyx +++ b/quantlib/models/shortrate/twofactormodels/g2.pyx @@ -1,8 +1,24 @@ +"""Two-factor additive Gaussian Model G2++""" from quantlib.types cimport Real from quantlib.handle cimport HandleYieldTermStructure from . cimport _g2 cdef class G2(TwoFactorModel): + r"""Two-factor additive gaussian model class. + + This class implements a two-additive-factor model defined by + + .. math:: + dr_t = \varphi(t) + x_t + y_t + + where :math:`x_t` and :math:`y_t` are defined by + + .. math:: + dx_t = -a x_t dt + \sigma dW^1_t, x_0 = 0\\ + dy_t = -b y_t dt + \sigma dW^2_t, y_0 = 0 + + and :math:`dW^1_t dW^2_t = \rho dt`. + """ def __init(self, HandleYieldTermStructure h, Real a=0.1, diff --git a/quantlib/option.pyx b/quantlib/option.pyx index 64584082e..1599e7c8b 100644 --- a/quantlib/option.pyx +++ b/quantlib/option.pyx @@ -20,13 +20,15 @@ cdef class Option(Instrument): ) @property - def exercise(self) -> Exercise: + def exercise(self): + """:class:`~quantlib.exercise.Exercise`""" cdef Exercise ex = Exercise.__new__(Exercise) ex._thisptr = (<_option.Option*>self._thisptr.get()).exercise() return ex @property - def payoff(self) -> Payoff: + def payoff(self): + """:class:`~quantlib.payoffs.Payoff`""" cdef Payoff po = Payoff.__new__(Payoff) po._thisptr = (<_option.Option*>self._thisptr.get()).payoff() return po diff --git a/quantlib/payoffs.pyx b/quantlib/payoffs.pyx index e4594a699..cd76fc146 100644 --- a/quantlib/payoffs.pyx +++ b/quantlib/payoffs.pyx @@ -29,10 +29,9 @@ cdef class PlainVanillaPayoff(StrikedTypePayoff): Parameters ---------- - - option_type: :class:`~quantlib.option.OptionType` + option_type : :class:`~quantlib.option.OptionType` The type of option, can be either `Call` or `Put` - strike: double + strike : double The strike value """ @@ -45,14 +44,10 @@ cdef class PlainVanillaPayoff(StrikedTypePayoff): ) ) - property option_type: - """ Exposes the internal option type. - - The type can be converted to str using the OptionType enum. - - """ - def __get__(self): - return _get_payoff(self).optionType() + @property + def option_type(self): + """:class:`~quantlib.option.OptionType`""" + return _get_payoff(self).optionType() property strike: def __get__(self): diff --git a/quantlib/termstructure.pyx b/quantlib/termstructure.pyx index 66362a4a7..cccce8660 100644 --- a/quantlib/termstructure.pyx +++ b/quantlib/termstructure.pyx @@ -6,6 +6,7 @@ from quantlib.time.calendar cimport Calendar cimport quantlib.time._daycounter as _dc cdef class TermStructure(Observable): + """Basic term-structure functionality""" def __init__(self): raise NotImplementedError("Abstract Class") @@ -19,39 +20,48 @@ cdef class TermStructure(Observable): return static_pointer_cast[QlObservable](self._thisptr) def time_from_reference(self, Date dt): - """date/time conversion""" + """date/time conversion + + Returns + ------- + :obj:`Time` + + """ return self.as_ptr().timeFromReference(dt._thisptr) @property def reference_date(self): - """ the date at which discount = 1.0 and/or variance = 0.0""" + """:class:`~quantlib.time.date.Date`: the date at which discount = 1.0 and/or variance = 0.0 + """ cdef QlDate ref_date = self.as_ptr().referenceDate() return date_from_qldate(ref_date) @property def max_date(self): - """the latest date for which the curve can return values""" + """:class:`~quantlib.time.date.Date`: the latest date for which the curve can return values""" cdef QlDate max_date = self.as_ptr().maxDate() return date_from_qldate(max_date) @property def max_time(self): - """the latest time for which the curve can return values""" + """:obj:`Time`: the latest time for which the curve can return values""" return self.as_ptr().maxTime() @property def day_counter(self): + """:class:`~quantlib.time.daycounter.DayCounter`: day counter""" cdef DayCounter dc = DayCounter.__new__(DayCounter) dc._thisptr = new _dc.DayCounter(self.as_ptr().dayCounter()) return dc @property def settlement_days(self): - """ number of settlement days used for reference date calculation""" + """:obj:`int`: number of settlement days used for reference date calculation""" return self.as_ptr().settlementDays() @property def calendar(self): + """:class:`~quantlib.time.calendar.Calendar`: calendar""" cdef Calendar instance = Calendar.__new__(Calendar) instance._thisptr = self.as_ptr().calendar() return instance diff --git a/quantlib/termstructures/credit/flat_hazard_rate.pyx b/quantlib/termstructures/credit/flat_hazard_rate.pyx index dee766e52..60d245202 100644 --- a/quantlib/termstructures/credit/flat_hazard_rate.pyx +++ b/quantlib/termstructures/credit/flat_hazard_rate.pyx @@ -14,16 +14,15 @@ from quantlib.quote cimport Quote cdef class FlatHazardRate(DefaultProbabilityTermStructure): """Flat hazard rate curve - Parameters - ---------- - - settlement_days : int - number of days from evaluation date - calendar: :class:`~quantlib.time.calendar.Calendar` - calendar used to compute the reference date - hazard_rate: float or :class:`~quantlib.quote.Quote` - the flat hazard rate - day_counter: :class:`~quantlib.time.daycounter.DayCounter` + Parameters + ---------- + settlement_days : int + number of days from evaluation date + calendar : :class:`~quantlib.time.calendar.Calendar` + calendar used to compute the reference date + hazard_rate : float or :class:`~quantlib.quote.Quote` + the flat hazard rate + day_counter : :class:`~quantlib.time.daycounter.DayCounter` DayCounter for the curve """ diff --git a/quantlib/termstructures/credit/interpolated_hazardrate_curve.pyx b/quantlib/termstructures/credit/interpolated_hazardrate_curve.pyx index b59793fab..8a476dda8 100644 --- a/quantlib/termstructures/credit/interpolated_hazardrate_curve.pyx +++ b/quantlib/termstructures/credit/interpolated_hazardrate_curve.pyx @@ -17,16 +17,16 @@ cimport quantlib.time._calendar as _calendar cdef class InterpolatedHazardRateCurve(DefaultProbabilityTermStructure): """DefaultProbabilityTermStructure based on interpolation of hazard rates - Parameters - ---------- - interpolator : int {Linear, LogLinear, BackwardFlat} - can be one of Linear, LogLinear, BackwardFlat - dates : :obj:`list` of :class:`~quantlib.time.date.Date` - list of dates - hazard_rates: :obj:`list` of float - corresponding list of hazard rates - day_counter: :class:`~quantlib.time.daycounter.DayCounter` - cal: :class:`~quantlib.time.calendar.Calendar` + Parameters + ---------- + interpolator : int {Linear, LogLinear, BackwardFlat} + can be one of Linear, LogLinear, BackwardFlat + dates : :obj:`list` of :class:`~quantlib.time.date.Date` + list of dates + hazard_rates : :obj:`list` of float + corresponding list of hazard rates + day_counter : :class:`~quantlib.time.daycounter.DayCounter` + cal : :class:`~quantlib.time.calendar.Calendar` """ def __init__(self, Interpolator interpolator, list dates, vector[Rate] hazard_rates, diff --git a/quantlib/termstructures/default_term_structure.pyx b/quantlib/termstructures/default_term_structure.pyx index d0bbd010b..b0e1b59c8 100644 --- a/quantlib/termstructures/default_term_structure.pyx +++ b/quantlib/termstructures/default_term_structure.pyx @@ -11,22 +11,49 @@ cdef class DefaultProbabilityTermStructure(TermStructure): return <_dts.DefaultProbabilityTermStructure*>self._thisptr.get() def survival_probability(self, d, bool extrapolate = False): + """Survival probability + + This returns the survival probability from the reference + date until a given date or time. In the former case, the time + is calculated as a fraction of year from the reference date. + + Parameters + ---------- + d : :class:`~quantlib.time.date.Date` or float + extrapolate : bool + + """ + if isinstance(d, Date): return self.as_dts_ptr().survivalProbability( (d)._thisptr, extrapolate) elif isinstance(d, float) or isinstance(d, int): return self.as_dts_ptr().survivalProbability(