From a8af544372950e71c8dcccb7e5034e8f42e6f3af Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Sun, 3 Mar 2019 17:40:16 -0500 Subject: wip --- c_layer/survival_curve.hpp | 88 ++++++++++++++++++++++++++++++++++++ pyisda/credit_index.pxd | 4 +- pyisda/credit_index.pyx | 107 +++++++++++++++++++++----------------------- pyisda/curve.pxd | 48 ++++++++++++++++---- pyisda/curve.pyx | 109 ++++++++++++++++++++++++++------------------- 5 files changed, 244 insertions(+), 112 deletions(-) create mode 100644 c_layer/survival_curve.hpp diff --git a/c_layer/survival_curve.hpp b/c_layer/survival_curve.hpp new file mode 100644 index 0000000..7032687 --- /dev/null +++ b/c_layer/survival_curve.hpp @@ -0,0 +1,88 @@ +#include + +struct CurveName { + enum class __attribute__ ((__packed__)) Seniority { + Senior, + Subordinated + }; + + enum class __attribute__ ((__packed__)) DocClause { + XR14, + MR14, + MM14, + CR14 + }; + void serialize(unsigned char* buf) { + memcpy(buf, &seniority, sizeof(Seniority)); + buf += sizeof(Seniority); + memcpy(buf, &doc_clause, sizeof(DocClause)); + buf += sizeof(DocClause); + strcpy((char*)buf, ticker.c_str()); + }; + + std::string full_ticker() { + std::string r = ticker; + return r.append("_").append(str_seniority()). + append("_").append(str_doc_clause()); + } + + void serialize(unsigned char* buf, size_t num) { + memcpy(buf, &seniority, sizeof(Seniority)); + buf += sizeof(Seniority); + memcpy(buf, &doc_clause, sizeof(DocClause)); + buf += sizeof(DocClause); + strncpy((char*)buf, ticker.c_str(), num); + }; + + CurveName(std::string& ticker, Seniority seniority, DocClause doc_clause) : + ticker(ticker), + seniority(seniority), + doc_clause(doc_clause) {}; + + CurveName(const unsigned char* buf) { + memcpy(&seniority, buf, sizeof(Seniority)); + buf += sizeof(Seniority); + memcpy(&doc_clause, buf, sizeof(DocClause)); + buf += sizeof(DocClause); + ticker = std::string((char*)buf); + } + + CurveName() {}; + + size_t size() { + return sizeof(Seniority) + sizeof(DocClause) + ticker.length() + 1; + }; + + bool operator<(const CurveName &other) const { + + return ticker < other.ticker || + ((ticker == other.ticker) && (seniority < other.seniority)) || + ((ticker == other.ticker) && (seniority == other.seniority) && (doc_clause < other.doc_clause)); + } + + std::string str_seniority() const { + switch (seniority) { + case Seniority::Senior: + return "Senior"; + case Seniority::Subordinated: + return "Subordinated"; + }; + } + + std::string str_doc_clause() const { + switch (doc_clause) { + case DocClause::XR14: + return "XR14"; + case DocClause::MR14: + return "MR14"; + case DocClause::MM14: + return "MM14"; + case DocClause::CR14: + return "CR14"; + } + } + + std::string ticker; + Seniority seniority; + DocClause doc_clause; +}; diff --git a/pyisda/credit_index.pxd b/pyisda/credit_index.pxd index 43c1eb5..eadb8f8 100644 --- a/pyisda/credit_index.pxd +++ b/pyisda/credit_index.pxd @@ -1,6 +1,6 @@ from .legs cimport TContingentLeg, TFeeLeg from .date cimport TDate -from .curve cimport TCurve, TRatePt, shared_ptr +from .curve cimport TCurve, TRatePt, shared_ptr, CurveName from libcpp.vector cimport vector from libcpp.map cimport map from libcpp.string cimport string @@ -9,7 +9,7 @@ cdef class CurveList: cdef TDate base_date cdef vector[double] _weights cdef vector[shared_ptr[TCurve]] _curves - cdef map[string, size_t] tickers + cdef map[CurveName, size_t] names cdef vector[shared_ptr[double]] recovery_rates cdef vector[TDate] defaulted diff --git a/pyisda/credit_index.pyx b/pyisda/credit_index.pyx index d2dde30..74bd409 100644 --- a/pyisda/credit_index.pyx +++ b/pyisda/credit_index.pyx @@ -3,7 +3,7 @@ from libc.stdlib cimport malloc, free from libc.math cimport nan, isnan from libc.string cimport memcpy, memset from libcpp.pair cimport pair -from libcpp.memory cimport unique_ptr +from libcpp.memory cimport make_shared from cython.operator cimport dereference as deref, preincrement as preinc from cpython cimport PyObject, Py_INCREF from cython.parallel cimport prange, parallel @@ -12,8 +12,9 @@ cimport cython from .legs cimport (JpmcdsCdsContingentLegMake, JpmcdsCdsFeeLegMake, JpmcdsContingentLegPV, JpmcdsFeeLegPV, FeeLegAI, JpmcdsFeeLegFree) from .curve cimport (SpreadCurve, JpmcdsCopyCurve, tweak_curve, YieldCurve, - JpmcdsFreeTCurve, survival_prob, Hash64WithSeed, - Hash64, uint64_t, TCurve_size, serialize) + JpmcdsFreeTCurve, survival_prob, Hash64WithSeed, + Hash64, uint64_t, TCurve_size, serialize, CurveName, + Seniority, DocClause, get_TCurve) from .date cimport (pydate_to_TDate, TDate_to_pydate, JpmcdsDtFwdAny, TDateInterval, JpmcdsMakeDateInterval) from .cdsone cimport JpmcdsStringToStubMethod, TStubMethod @@ -23,8 +24,6 @@ np.import_array() import pandas as pd import warnings -cdef inline shared_ptr[TCurve] make_shared(TCurve* ptr) nogil: - return shared_ptr[TCurve](ptr, JpmcdsFreeTCurve) ctypedef TFeeLeg* TFeeLeg_ptr ctypedef TContingentLeg* TContingentLeg_ptr @@ -54,7 +53,7 @@ cdef class CurveList: cdef: SpreadCurve sc size_t i - map[string, size_t].iterator it + map[CurveName, size_t].iterator it size_t n = len(curves) double w @@ -70,9 +69,9 @@ cdef class CurveList: cdef double total_weight = 0. for w, sc in curves: if sc is not None: - it = self.tickers.find(sc.ticker) - if it == self.tickers.end(): - self.tickers[sc.ticker] = i + it = self.names.find(deref(sc.name)) + if it == self.names.end(): + self.names[deref(sc.name)] = i self._curves.push_back(sc._thisptr) self.recovery_rates.push_back(sc.recovery_rates) self._weights.push_back(w) @@ -90,32 +89,35 @@ cdef class CurveList: for i in range(self._weights.size()): self._weights[i] /= total_weight - def __getitem__(self, str ticker): + def __getitem__(self, tuple name): cdef: - string ticker_cpp = ticker - map[string, size_t].iterator got = \ - self.tickers.find(ticker_cpp) + string ticker_cpp = name[0] + CurveName.Seniority seniority = (name[1]) + CurveName.DocClause doc_clause = (name[2]) + shared_ptr[CurveName] name_cpp = make_shared[CurveName](ticker_cpp, seniority, doc_clause) + map[CurveName, size_t].iterator got = \ + self.names.find(deref(name_cpp)) SpreadCurve sc - if got == self.tickers.end(): - raise KeyError(ticker) + if got == self.names.end(): + raise KeyError(name) else: sc = SpreadCurve.__new__(SpreadCurve) sc._thisptr = self._curves[deref(got).second] sc.recovery_rates = self.recovery_rates[deref(got).second] - sc.ticker = ticker_cpp + sc.name = name_cpp sc.defaulted = self.defaulted[deref(got).second] return sc def items(self): cdef: SpreadCurve sc - pair[string, size_t] p + pair[CurveName, size_t] p - for p in self.tickers: + for p in self.names: sc = SpreadCurve.__new__(SpreadCurve) sc._thisptr = self._curves[p.second] - sc.ticker = p.first + sc.name = make_shared[CurveName](p.first) sc.recovery_rates = self.recovery_rates[p.second] sc.defaulted = self.defaulted[p.second] yield (sc.ticker, self._weights[p.second], sc) @@ -131,11 +133,11 @@ cdef class CurveList: @property def tickers(self): - cdef np.npy_intp shape = self.tickers.size() - cdef pair[string, size_t] p + cdef np.npy_intp shape = self.names.size() + cdef pair[CurveName, size_t] p cdef np.ndarray out = np.PyArray_EMPTY(1, &shape, np.NPY_OBJECT, 1) - for p in self.tickers: - out[p.second] = p.first + for p in self.names: + out[p.second] = p.first.ticker return out @property @@ -154,12 +156,12 @@ cdef class CurveList: cdef: list r = [] SpreadCurve sc - pair[string, size_t] p + pair[CurveName, size_t] p - for p in self.tickers: + for p in self.names: sc = SpreadCurve.__new__(SpreadCurve) sc._thisptr = self._curves[p.second] - sc.ticker = p.first + sc.name = make_shared[CurveName](p.first) sc.recovery_rates = self.recovery_rates[p.second] sc.defaulted = self.defaulted[p.second] r.append((self._weights[p.second], sc)) @@ -168,7 +170,7 @@ cdef class CurveList: @curves.setter def curves(self, list l not None): self._curves.clear() - self.tickers.clear() + self.names.clear() self._weights.clear() self.recovery_rates.clear() self.defaulted.clear() @@ -265,30 +267,23 @@ cdef class CreditIndex(CurveList): def __hash__(self): cdef: TCurve* curve = self._curves[0].get() - size_t size = TCurve_size(curve.fNumItems) + size_t size = TCurve_size(curve) size_t size_recovery = curve.fNumItems * sizeof(double) - size_t buf_size = size + size_recovery + 8 + sizeof(TDate) + size_t buf_size = size + size_recovery + sizeof(TDate) + \ + sizeof(CurveName.Seniority) + sizeof(DocClause) + 14 unsigned char* buf = malloc(buf_size) unsigned char* cursor - unsigned char* start = buf + size - size_t i uint64_t h = 0 - pair[string, size_t] p + pair[CurveName, size_t] p - for p in self.tickers: - cursor = start + for p in self.names: curve = self._curves[p.second].get() - serialize(curve, buf) - if p.first.length() < 8: - p.first.copy(cursor, p.first.length(), 0) - #0 padding - memset(cursor + p.first.length(), 0, 8 - p.first.length()) - else: - p.first.copy(cursor, 8, 0) - cursor += 8 + cursor = serialize(curve, buf) memcpy(cursor, self.recovery_rates[p.second].get(), size_recovery) cursor += size_recovery memcpy(cursor, &self.defaulted[p.second], sizeof(TDate)) + cursor += sizeof(TDate) + p.first.serialize(cursor, 14) h ^= Hash64(buf, buf_size) free(buf) @@ -313,8 +308,8 @@ cdef class CreditIndex(CurveList): list tickers = [] np.ndarray[np.int64_t,ndim=1] d = np.PyArray_EMPTY(1, &n[1], np.NPY_INT64, 0) TDate maturity - TCurve* sc - pair[string, size_t] p + const TCurve* sc + pair[CurveName, size_t] p double recovery_rate for maturity in self._maturities: @@ -322,9 +317,9 @@ cdef class CreditIndex(CurveList): j += 1 j = 0 - for p in self.tickers: + for p in self.names: sc = self._curves[p.second].get() - tickers.append(p.first) + tickers.append(p.first.ticker) # TODO: pick the actual recovery on the curve # this only works for flat recovery curve recovery_rate = self.recovery_rates[p.second].get()[0] @@ -333,7 +328,7 @@ cdef class CreditIndex(CurveList): sc.fBaseDate, cash_settle_date_c, step_in_date_c, - yc._thisptr.get(), + get_TCurve(yc), sc, recovery_rate, &cl_pv[i,j]) @@ -341,7 +336,7 @@ cdef class CreditIndex(CurveList): sc.fBaseDate, step_in_date_c, cash_settle_date_c, - yc._thisptr.get(), + get_TCurve(yc), sc, True, &fl_pv[i,j]) @@ -383,7 +378,7 @@ cdef class CreditIndex(CurveList): raise ValueError("maturity is not correct") with nogil: r = pv(self._curves, self.base_date, step_in_date_c, cash_settle_date_c, - yc._thisptr.get(), legs, self.recovery_rates, fixed_rate, + get_TCurve(yc), legs, self.recovery_rates, fixed_rate, self._weights, epsilon, mask) if i == -1: free(legs.first) @@ -423,7 +418,7 @@ cdef class CreditIndex(CurveList): legs.first = self.contingent_legs[i] legs.second = self.fee_legs[i] old_pv = pv(self._curves, self.base_date, step_in_date_c, - cash_settle_date_c, yc._thisptr.get(), legs, + cash_settle_date_c, get_TCurve(yc), legs, self.recovery_rates, fixed_rate, self._weights, 0., 0) if i == -1: @@ -440,7 +435,7 @@ cdef class CreditIndex(CurveList): cdef r = old_pv - pv(self._curves, self.base_date, step_in_date_c, cash_settle_date_c, - yc._thisptr.get(), legs, self.recovery_rates, fixed_rate, + get_TCurve(yc), legs, self.recovery_rates, fixed_rate, self._weights, 0., 0) + carry if i == -1: free(legs.first) @@ -563,7 +558,7 @@ cdef class CreditIndex(CurveList): def survival_matrix(self, TDate[:] schedule, double epsilon=0.): cdef: shared_ptr[TCurve] sc - pair[string, size_t] p + pair[CurveName, size_t] p size_t i np.npy_intp[2] n n[0] = self._curves.size() @@ -572,9 +567,9 @@ cdef class CreditIndex(CurveList): np.ndarray[np.float64_t, ndim=2] sp = np.PyArray_EMPTY(2, n, np.NPY_DOUBLE, 1) np.ndarray tickers = np.PyArray_EMPTY(1, n, np.NPY_OBJECT, 1) - for p in self.tickers: + for p in self.names: sc = self._curves[p.second] - tickers[p.second] = p.first + tickers[p.second] = p.first.ticker for i in range(n[1]): sp[p.second, i] = survival_prob(sc.get(), self.base_date, schedule[i], epsilon) return sp, tickers @@ -642,11 +637,11 @@ cdef double pv(const vector[shared_ptr[TCurve]]& curves, TDate base_date, TDate step_in_date, TDate cash_settle_date, - TCurve* yc, + const TCurve* yc, pair[TContingentLeg_ptr, TFeeLeg_ptr]& legs, const vector[shared_ptr[double]] &recovery_rates, double fixed_rate, - vector[double]& weights, + const vector[double]& weights, double epsilon, unsigned long mask) nogil: cdef: diff --git a/pyisda/curve.pxd b/pyisda/curve.pxd index c885d10..de79d38 100644 --- a/pyisda/curve.pxd +++ b/pyisda/curve.pxd @@ -65,12 +65,6 @@ cdef inline void serialize_vector(const vector[TDate]& v, unsigned char* cursor) cursor += sizeof(size_t) memcpy(cursor, v.data(), sizeof(TDate) * v.size()) -cdef inline unsigned char* serialize_string(const string& s, unsigned char* cursor) nogil: - cdef size_t size = s.length() - memcpy(cursor, &size, sizeof(size_t)) - cursor += sizeof(size_t) - cursor += s.copy(cursor, s.length(), 0) - return cursor cdef inline unsigned char* deserialize(unsigned char* buf, TCurve* curve) nogil: memcpy(&curve.fNumItems, buf, sizeof(curve.fNumItems)) @@ -210,20 +204,56 @@ cdef extern from "" namespace "std" nogil: shared_ptr() shared_ptr(T*, D) T* get() + T& operator*() void reset(T*, D) bool operator!() long use_count() + cdef shared_ptr[T] make_shared[T](...) + +cdef extern from "survival_curve.hpp" nogil: + cdef cppclass CurveName: + enum Seniority: + Senior + Subordinated + + enum DocClause: + XR14 + MR14 + MM14 + CR14 + + string ticker + Seniority seniority + DocClause doc_clause + CurveName(string&, Seniority, DocClause) + CurveName(const unsigned char* buf) + void serialize(unsigned char* buf) + void serialize(unsigned char* buf, size_t) + string full_ticker() + size_t size() + +cpdef enum DocClause: + XR14 + MR14 + MM14 + CR14 + +cpdef enum Seniority: + Senior + Subordinated + +cdef inline const TCurve* get_TCurve(Curve c) nogil: + return c._thisptr.get() cdef class Curve: cdef shared_ptr[TCurve] _thisptr - cdef size_t size(self) - cdef const TCurve* curve(self) + cdef size_t size(self) nogil cdef class YieldCurve(Curve): cdef vector[TDate] dates cdef class SpreadCurve(Curve): - cdef public string ticker + cdef shared_ptr[CurveName] name cdef shared_ptr[double] recovery_rates cdef TDate defaulted diff --git a/pyisda/curve.pyx b/pyisda/curve.pyx index a9cbbcf..cb472e2 100644 --- a/pyisda/curve.pyx +++ b/pyisda/curve.pyx @@ -1,3 +1,4 @@ +from cython.operator import dereference as deref from libc.math cimport log1p, log, exp, isnan from .date cimport (JpmcdsStringToDateInterval, pydate_to_TDate, dcc, TMonthDayYear, JpmcdsDateIntervalToFreq, JpmcdsDateFwdThenAdjust, TDate_to_pydate, @@ -29,14 +30,11 @@ cdef extern from "numpy/arrayobject.h": cdef int SUCCESS = 0 -cdef inline shared_ptr[TCurve] make_shared(TCurve* ptr) nogil: - return shared_ptr[TCurve](ptr, JpmcdsFreeTCurve) - cdef inline void double_free(double* ptr) nogil: free(ptr) -cdef double survival_prob(TCurve* curve, TDate start_date, TDate maturity_date, double eps) nogil: +cdef double survival_prob(const TCurve* curve, TDate start_date, TDate maturity_date, double eps) nogil: cdef: double lambda1, lambda2 double t1, t2, u @@ -151,7 +149,7 @@ cdef class Curve(object): if isinstance(self, YieldCurve): name = 'forward_rates' elif isinstance(self, SpreadCurve): - name = (self).ticker + name = (self).name.get().ticker if name == "": name = "hazard_rates" return pd.Series(h, index=d.view('M8[D]'), name=name) @@ -455,19 +453,13 @@ cdef void tweak_curve(const TCurve* sc, TCurve* sc_tweaked, double epsilon, h1 = t1 = c = 0 cdef size_t i - if mask == 0: - for i in range(sc.fNumItems): - h2 = sc.fArray[i].fRate - t2 = (sc.fArray[i].fDate - sc.fBaseDate) / 365. - c += h2 * t2 - h1 * t1 - sc_tweaked.fArray[i].fRate = c / t2 - h1 = h2 - t1 = t2 + if mask == 0 or epsilon == 0.: + return else: for i in range(sc.fNumItems): h2 = sc.fArray[i].fRate t2 = (sc.fArray[i].fDate - sc.fBaseDate) / 365. - c += (h2 * t2 - h1 * t1) * ( 1 + epsilon * ((mask >> i) & 1)) + c += (h2 * t2 - h1 * t1) * (1 + epsilon * ((mask >> i) & 1)) sc_tweaked.fArray[i].fRate = c / t2 h1 = h2 t1 = t2 @@ -498,12 +490,15 @@ cdef class SpreadCurve(Curve): cash_settle_date, end_dates, double[:] coupon_rates, double[:] upfront_rates, double[:] recovery_rates, bint pay_accrued_on_default=True, - str ticker=None, bint fill_curve=True, defaulted=None): + str ticker="", Seniority seniority=Senior, + DocClause doc_clause=XR14, + bint fill_curve=True, defaulted=None): cdef TDate today_c = pydate_to_TDate(today) cdef TDate step_in_date_c cdef TDate cash_settle_date_c cdef TDate start_date_c + cdef string ticker_cpp = ticker if start_date is None: start_date_c = _previous_twentieth(today_c, True) @@ -605,15 +600,16 @@ cdef class SpreadCurve(Curve): curve = new_curve if freeup: free(end_dates_c) - self._thisptr = make_shared(curve) + self._thisptr.reset(curve, JpmcdsFreeTCurve) self.recovery_rates = shared_ptr[double]( malloc(curve.fNumItems * sizeof(double)), double_free) memcpy(self.recovery_rates.get(), &recovery_rates[0], curve.fNumItems * sizeof(double)) - if ticker: - self.ticker = ticker + self.name = make_shared[CurveName](ticker_cpp, + seniority, + doc_clause) if curve is NULL: if freeup: @@ -626,7 +622,7 @@ cdef class SpreadCurve(Curve): cdef size_t size(self): cdef TCurve* curve = self._thisptr.get() return Curve.size(self) + curve.fNumItems * sizeof(double) + \ - sizeof(size_t) + self.ticker.length() + sizeof(TDate) + sizeof(size_t) + sizeof(TDate) + 16 def __getstate__(self): cdef: @@ -635,37 +631,35 @@ cdef class SpreadCurve(Curve): size_t size_recovery = curve.fNumItems * sizeof(double) unsigned char* buf = malloc(buf_size) unsigned char* cursor = serialize(curve, buf) - - cursor = serialize_string(self.ticker, cursor) memcpy(cursor, self.recovery_rates.get(), size_recovery) cursor += size_recovery memcpy(cursor, &self.defaulted, sizeof(TDate)) - cdef bytes r = buf[:buf_size] + strcpy(cursor, self.ticker.c_str()) free(buf) - return r + return buf[:buf_size] def __setstate__(self, bytes state): cdef: TCurve* curve = malloc(sizeof(TCurve)) unsigned char* cursor = state size_t ticker_length, recovery_size + double* recovery_rates cursor = deserialize(cursor, curve) self._thisptr = make_shared(curve) - memcpy(&ticker_length, cursor, sizeof(size_t)) - cursor += sizeof(size_t) - self.ticker = string(cursor, ticker_length) - cursor += ticker_length recovery_size = curve.fNumItems * sizeof(double) - self.recovery_rates = shared_ptr[double](malloc(recovery_size), - double_free) - memcpy(self.recovery_rates.get(), cursor, recovery_size) + recovery_rates = malloc(recovery_size) + memcpy(recovery_rates, cursor, recovery_size) + cursor += recovery_size + self.recovery_rates.reset(recovery_rates, double_free) cursor += recovery_size memcpy(&self.defaulted, cursor, sizeof(TDate)) + cursor += sizeof(TDate) + self.ticker = string(cursor) def __deepcopy__(self, dict memo): cdef SpreadCurve sc = SpreadCurve.__new__(SpreadCurve) - cdef const TCurve* curve = self.curve() + cdef TCurve* curve = self._thisptr.get() cdef size_t size = curve.fNumItems * sizeof(double) sc._thisptr = make_shared(JpmcdsCopyCurve(curve)) sc.ticker = self.ticker @@ -690,7 +684,7 @@ cdef class SpreadCurve(Curve): SpreadCurve instance = SpreadCurve.__new__(SpreadCurve) unsigned char* cursor TCurve* curve = malloc(sizeof(TCurve)) - size_t ticker_length, recovery_size + size_t recovery_size Py_buffer* py_buf if PyMemoryView_Check(state): @@ -701,38 +695,38 @@ cdef class SpreadCurve(Curve): cursor = deserialize(cursor, curve) instance._thisptr = make_shared(curve) - memcpy(&ticker_length, cursor, sizeof(size_t)) - cursor += sizeof(size_t) - instance.ticker = string(cursor, ticker_length) - cursor += ticker_length recovery_size = curve.fNumItems * sizeof(double) instance.recovery_rates = shared_ptr[double](malloc(recovery_size), - double_free) + double_free) memcpy(instance.recovery_rates.get(), cursor, recovery_size) cursor += recovery_size memcpy(&instance.defaulted, cursor, sizeof(TDate)) + cursor += sizeof(TDate) + instance.name = make_shared[CurveName](cursor) return instance def __hash__(self): # same code as __getstate__ cdef: - const TCurve* curve = self.curve() + const TCurve* curve = get_TCurve(self) size_t buf_size = self.size() size_t size_recovery = curve.fNumItems * sizeof(double) unsigned char* buf = malloc(buf_size) unsigned char* cursor = serialize(curve, buf) - - cursor = serialize_string(self.ticker, cursor) memcpy(cursor, self.recovery_rates.get(), size_recovery) cursor += size_recovery memcpy(cursor, &self.defaulted, sizeof(TDate)) + cursor += sizeof(TDate) + self.name.get().serialize(cursor) cdef uint64_t r = Hash64(buf, buf_size) free(buf) return r @classmethod def from_flat_hazard(cls, base_date, double rate, Basis basis=CONTINUOUS, - str day_count_conv='Actual/365F'): + str day_count_conv='Actual/365F', double recov=0.4, + str ticker="", Seniority sen=Senior, + DocClause doc=XR14): """ Alternative constructor for flat hazard rate Curve. @@ -751,9 +745,17 @@ cdef class SpreadCurve(Curve): cdef TDate base_date_c = pydate_to_TDate(base_date) cdef SpreadCurve sc = SpreadCurve.__new__(SpreadCurve) cdef TDate max_date = 200000 # can go higher but this should be more than enough - - sc._thisptr = make_shared(JpmcdsMakeTCurve(base_date_c, &max_date, &rate, 1, - basis, dcc(day_count_conv))) + cdef string ticker_cpp = ticker + cdef double* recovery_rates + + sc._thisptr.reset(JpmcdsMakeTCurve(base_date_c, &max_date, &rate, 1, + basis, dcc(day_count_conv)), + JpmcdsFreeTCurve) + recovery_rates = malloc(sizeof(double)) + recovery_rates[0] = recov + sc.recovery_rates.reset(recovery_rates, double_free) + sc.name = make_shared[CurveName](ticker_cpp, (sen), + (doc)) return sc @cython.boundscheck(False) @@ -851,12 +853,29 @@ cdef class SpreadCurve(Curve): @property def recovery_rates(self): - cdef np.npy_intp shape = self.curve().fNumItems + cdef np.npy_intp shape = get_TCurve(self).fNumItems cdef np.ndarray[np.float64_t] out = \ np.PyArray_SimpleNewFromData(1, &shape, np.NPY_DOUBLE, self.recovery_rates.get()) return out + @property + def ticker(self): + return self.name.get().ticker + + @property + def full_ticker(self): + return self.name.get().full_ticker() + + @property + def seniority(self): + return Seniority(self.name.get().seniority) + + @property + def doc_clause(self): + return DocClause(self.name.get().doc_clause) + + @cython.cdivision(True) @cython.boundscheck(False) cdef TCurve* _fill_curve(const TCurve* sc, const TDate* end_dates, int n_dates) nogil: -- cgit v1.2.3-70-g09d2