diff options
| -rw-r--r-- | pyisda/curve.pxd | 7 | ||||
| -rw-r--r-- | pyisda/curve.pyx | 232 |
2 files changed, 131 insertions, 108 deletions
diff --git a/pyisda/curve.pxd b/pyisda/curve.pxd index 12ed440..c7362c0 100644 --- a/pyisda/curve.pxd +++ b/pyisda/curve.pxd @@ -47,7 +47,7 @@ cdef inline size_t TCurve_size(int num_items) nogil: return sizeof(int) + sizeof(TDate) + sizeof(double) + \ sizeof(long) + sizeof(TRatePt) * num_items -cdef inline void serialize(const TCurve* curve, unsigned char* buf) nogil: +cdef inline unsigned char* serialize(const TCurve* curve, unsigned char* buf) nogil: memcpy(buf, &(curve.fNumItems), sizeof(curve.fNumItems)) buf += sizeof(curve.fNumItems) memcpy(buf, curve.fArray, sizeof(TRatePt) * curve.fNumItems) @@ -57,6 +57,7 @@ cdef inline void serialize(const TCurve* curve, unsigned char* buf) nogil: memcpy(buf, &(curve.fBasis), sizeof(double)) buf += sizeof(double) memcpy(buf, &(curve.fDayCountConv), sizeof(long)) + return buf + sizeof(long) cdef inline void serialize_vector(const vector[TDate]& v, unsigned char* cursor) nogil: cdef size_t size = v.size() @@ -211,12 +212,14 @@ cdef extern from "<memory>" namespace "std" nogil: cdef class Curve: cdef shared_ptr[TCurve] _thisptr + cdef size_t size(self) + cdef const TCurve* curve(self) cdef class YieldCurve(Curve): cdef vector[TDate] dates cdef class SpreadCurve(Curve): - cdef string ticker + cdef public string ticker cdef shared_ptr[double] recovery_rates cdef TDate defaulted diff --git a/pyisda/curve.pyx b/pyisda/curve.pyx index b435eeb..a575a96 100644 --- a/pyisda/curve.pyx +++ b/pyisda/curve.pyx @@ -60,10 +60,9 @@ cdef class Curve(object): def __getstate__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t curve_size = TCurve_size(curve.fNumItems) + size_t curve_size = self.size() unsigned char* buf = <unsigned char*>malloc(curve_size * sizeof(unsigned char)) - serialize(curve, buf) + serialize(self.curve(), buf) cdef bytes r = buf[:curve_size] free(buf) return r @@ -75,6 +74,9 @@ cdef class Curve(object): deserialize(cursor, curve) self._thisptr = make_shared(curve) + cdef size_t size(self): + return TCurve_size(self._thisptr.get().fNumItems) + @classmethod def from_bytes(cls, object state): cdef: @@ -93,8 +95,8 @@ cdef class Curve(object): def __hash__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t curve_size = TCurve_size(curve.fNumItems) + const TCurve* curve = self.curve() + size_t curve_size = self.size() unsigned char* buf = <unsigned char*>malloc(curve_size * sizeof(unsigned char)) serialize(curve, buf) cdef uint64_t r = Hash64(<char*>buf, curve_size) @@ -109,24 +111,31 @@ cdef class Curve(object): dict contains `base_date`, `basis`, `day_count_counvention` and `data` """ - + cdef const TCurve* curve = self.curve() return {'base_date': self.base_date, - 'basis': self._thisptr.get().fBasis, - 'day_count_convention': dcc_tostring(self._thisptr.get().fDayCountConv), - 'data': fArray_to_list(self._thisptr.get().fArray, self._thisptr.get().fNumItems)} + 'basis': curve.fBasis, + 'day_count_convention': dcc_tostring(curve.fDayCountConv), + 'data': fArray_to_list(curve.fArray, curve.fNumItems)} + + cdef const TCurve* curve(self): + if not self._thisptr: + raise ValueError("Curve is not initialized") + else: + return self._thisptr.get() @cython.boundscheck(False) @cython.cdivision(True) def to_series(self, bint forward=True): - cdef np.npy_intp n = self._thisptr.get().fNumItems + cdef const TCurve* curve = self.curve() + cdef np.npy_intp n = curve.fNumItems cdef np.ndarray[np.float64_t,ndim=1] h = np.PyArray_EMPTY(1, &n, np.NPY_DOUBLE, 0) cdef np.ndarray[np.int64_t,ndim=1] d = np.PyArray_EMPTY(1, &n, np.NPY_INT64, 0) cdef size_t i - cdef TRatePt* it = self._thisptr.get().fArray + cdef TRatePt* it = curve.fArray cdef double t1, h1, t2, h2 t1 = 0 h1 = 0 - cdef int base_date = self._thisptr.get().fBaseDate + cdef int base_date = curve.fBaseDate if forward: for i in range(n): h2 = it[i].fRate @@ -150,16 +159,16 @@ cdef class Curve(object): def __iter__(self): cdef: size_t i = 0 - TRatePt* it = self._thisptr.get().fArray - for i in range(self._thisptr.get().fNumItems): + TRatePt* it = self.curve().fArray + for i in range(self.curve().fNumItems): yield (TDate_to_pydate(it[i].fDate), it[i].fRate) def __len__(self): - return self._thisptr.get().fNumItems + return self.curve().fNumItems def __deepcopy__(self, dict memo): cdef Curve sc = Curve.__new__(Curve) - sc._thisptr = make_shared(JpmcdsCopyCurve(self._thisptr.get())) + sc._thisptr = make_shared(JpmcdsCopyCurve(<TCurve*>self.curve())) memo[id(self)] = sc return sc @@ -167,21 +176,22 @@ cdef class Curve(object): @cython.cdivision(True) def forward_hazard_rates(self): cdef double t1, h1, t2, h2 - cdef np.npy_intp shape = self._thisptr.get().fNumItems + cdef const TCurve* curve = self.curve() + cdef np.npy_intp shape = curve.fNumItems t1 = 0 h1 = 0 - cdef TCurve* curve = self._thisptr.get() - cdef double* data = <double*>malloc(curve.fNumItems * sizeof(double)) + + cdef double* data = <double*>malloc(shape * sizeof(double)) cdef size_t i if <Basis>curve.fBasis == Basis.CONTINUOUS: - for i in range(curve.fNumItems): + for i in range(shape): h2 = curve.fArray[i].fRate t2 = (curve.fArray[i].fDate - curve.fBaseDate)/365. data[i] = (h2 * t2 - h1 * t1) / (t2 - t1) h1 = h2 t1 = t2 elif <Basis>curve.fBasis == Basis.ANNUAL_BASIS: - for i in range(curve.fNumItems): + for i in range(shape): h2 = log1p(curve.fArray[i].fRate) t2 = (curve.fArray[i].fDate - curve.fBaseDate)/365. data[i] = (h2 * t2 - h1 * t1) / (t2 - t1) @@ -197,7 +207,7 @@ cdef class Curve(object): @property def base_date(self): - return TDate_to_pydate(self._thisptr.get().fBaseDate) + return TDate_to_pydate(self.curve().fBaseDate) def __forward_zero_price(self, d2, d1=None): """ computes the forward zero price at a given date. @@ -210,15 +220,12 @@ cdef class Curve(object): ------- float """ - if not self._thisptr: - raise ValueError('curve is empty') - cdef TDate start_date + cdef TCurve* curve = <TCurve*>self.curve() if d1 is None: - start_date = self._thisptr.get().fBaseDate - return JpmcdsForwardZeroPrice(self._thisptr.get(), start_date, + return JpmcdsForwardZeroPrice(curve, curve.fBaseDate, pydate_to_TDate(d2)) else: - return JpmcdsForwardZeroPrice(self._thisptr.get(), pydate_to_TDate(d1), + return JpmcdsForwardZeroPrice(curve, pydate_to_TDate(d1), pydate_to_TDate(d2)) cdef fArray_to_list(TRatePt* fArray, int fNumItems): @@ -314,14 +321,16 @@ cdef class YieldCurve(Curve): bad_day_conv, b"None" )) + cdef size_t size(self): + return Curve.size(self) + sizeof(size_t) + sizeof(TDate) * self.dates.size() + def __getstate__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t size = TCurve_size(curve.fNumItems) - size_t buf_size = size + sizeof(size_t) + sizeof(TDate) * self.dates.size() + const TCurve* curve = self.curve() + size_t buf_size = self.size() unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) + unsigned char* cursor = serialize(curve, buf) + serialize_vector(self.dates, cursor) cdef bytes r = buf[:buf_size] free(buf) @@ -373,12 +382,12 @@ cdef class YieldCurve(Curve): def __hash__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t size = TCurve_size(curve.fNumItems) - size_t buf_size = size + sizeof(size_t) + sizeof(TDate) * self.dates.size() + const TCurve* curve = self.curve() + size_t buf_size = self.size() + size_t size unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) + unsigned char* cursor = serialize(curve, buf) + size = self.dates.size() memcpy(cursor, &size, sizeof(size_t)) cursor += sizeof(size_t) @@ -516,6 +525,7 @@ cdef class SpreadCurve(Curve): cdef double* tenors_c = NULL cdef TCurve* curve = NULL cdef TCurve* new_curve = NULL + cdef double* rates = NULL cdef unsigned int includes = 0 cdef size_t i cdef bint freeup = False @@ -527,12 +537,6 @@ cdef class SpreadCurve(Curve): self.defaulted = -1 else: self.defaulted = pydate_to_TDate(defaulted) - self.recovery_rates = shared_ptr[double]( - <double*>malloc(sizeof(double)), double_free) - self.recovery_rates.get()[0] = recovery_rates[0] - if ticker: - self.ticker = ticker - return if isinstance(end_dates, list): n_dates = len(end_dates) @@ -564,55 +568,72 @@ cdef class SpreadCurve(Curve): if JpmcdsStringToStubMethod(b"f/s", &stub_type) != 0: raise ValueError("can't convert stub") with nogil: - curve = JpmcdsCleanSpreadCurve(today_c, - yc._thisptr.get(), - start_date_c, - step_in_date_c, - cash_settle_date_c, - n_dates, - end_dates_c, - &coupon_rates[0], - &upfront_rates[0], - includes, - &recovery_rates[0], - pay_accrued_on_default, - NULL, - ACT_360, - &stub_type, - <long>'M', - b'NONE') + if defaulted is None: + curve = JpmcdsCleanSpreadCurve(today_c, + yc._thisptr.get(), + start_date_c, + step_in_date_c, + cash_settle_date_c, + n_dates, + end_dates_c, + &coupon_rates[0], + &upfront_rates[0], + includes, + &recovery_rates[0], + pay_accrued_on_default, + NULL, + ACT_360, + &stub_type, + <long>'M', + b'NONE') + else: + rates = <double*>malloc(n_dates * sizeof(double)) + for i in range(n_dates): + rates[i] = 1000 + curve = JpmcdsMakeTCurve(today_c, + end_dates_c, + rates, + n_dates, + 5000., + ACT_360) + if curve is not NULL: + if fill_curve and curve.fNumItems != n_dates: + new_curve = _fill_curve(curve, end_dates_c, n_dates) + JpmcdsFreeTCurve(curve) + curve = new_curve + if freeup: + free(end_dates_c) + self._thisptr = make_shared(curve) + self.recovery_rates = shared_ptr[double]( + <double*>malloc(curve.fNumItems * sizeof(double)), + double_free) + memcpy(<void*>self.recovery_rates.get(), &recovery_rates[0], + curve.fNumItems * sizeof(double)) + + if ticker: + self.ticker = ticker + if curve is NULL: if freeup: free(end_dates_c) raise ValueError("Didn't init the survival curve properly") - else: - if fill_curve and curve.fNumItems != n_dates: - new_curve = _fill_curve(curve, end_dates_c, n_dates) - JpmcdsFreeTCurve(curve) - curve = new_curve - if freeup: - free(end_dates_c) - self._thisptr = make_shared(curve) - self.recovery_rates = shared_ptr[double]( - <double*>malloc(curve.fNumItems * sizeof(double)), - double_free) - memcpy(<void*>self.recovery_rates.get(), &recovery_rates[0], - curve.fNumItems * sizeof(double)) - if ticker: - self.ticker = ticker + survival_probability = Curve.__forward_zero_price + 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) + def __getstate__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t size = TCurve_size(curve.fNumItems) + const TCurve* curve = self.curve() + size_t buf_size = self.size() size_t size_recovery = curve.fNumItems * sizeof(double) - size_t buf_size = size + sizeof(size_t) + self.ticker.length() + \ - size_recovery + sizeof(TDate) unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) + unsigned char* cursor = serialize(curve, buf) + cursor = serialize_string(self.ticker, cursor) memcpy(cursor, self.recovery_rates.get(), size_recovery) cursor += size_recovery @@ -625,29 +646,30 @@ cdef class SpreadCurve(Curve): cdef: TCurve* curve = <TCurve*>malloc(sizeof(TCurve)) unsigned char* cursor = state - size_t ticker_length, size - double* temp + size_t ticker_length, recovery_size + cursor = deserialize(cursor, curve) self._thisptr = make_shared(curve) memcpy(&ticker_length, cursor, sizeof(size_t)) cursor += sizeof(size_t) self.ticker = string(<char*>cursor, ticker_length) cursor += ticker_length - size = curve.fNumItems * sizeof(double) - self.recovery_rates = shared_ptr[double](<double*>malloc(size), + recovery_size = curve.fNumItems * sizeof(double) + self.recovery_rates = shared_ptr[double](<double*>malloc(recovery_size), double_free) - memcpy(self.recovery_rates.get(), cursor, size) - cursor += size + memcpy(self.recovery_rates.get(), cursor, recovery_size) + cursor += recovery_size memcpy(&self.defaulted, cursor, sizeof(TDate)) def __deepcopy__(self, dict memo): cdef SpreadCurve sc = SpreadCurve.__new__(SpreadCurve) - cdef size_t size = self._thisptr.get().fNumItems * sizeof(double) - sc._thisptr = make_shared(JpmcdsCopyCurve(self._thisptr.get())) + cdef const TCurve* curve = self.curve() + cdef size_t size = curve.fNumItems * sizeof(double) + sc._thisptr = make_shared(JpmcdsCopyCurve(<TCurve*>curve)) sc.ticker = self.ticker - sc.recovery_rates= shared_ptr[double](<double*>malloc(size), double_free) + sc.recovery_rates = shared_ptr[double](<double*>malloc(size), double_free) memcpy(sc.recovery_rates.get(), self.recovery_rates.get(), size) - memcpy(&sc.defaulted, &self.defaulted, sizeof(TDate)) + sc.defaulted = self.defaulted memo[id(self)] = sc return sc @@ -666,7 +688,7 @@ cdef class SpreadCurve(Curve): SpreadCurve instance = SpreadCurve.__new__(SpreadCurve) unsigned char* cursor TCurve* curve = <TCurve*>malloc(sizeof(TCurve)) - size_t ticker_length, size + size_t ticker_length, recovery_size Py_buffer* py_buf if PyMemoryView_Check(state): @@ -681,25 +703,23 @@ cdef class SpreadCurve(Curve): cursor += sizeof(size_t) instance.ticker = string(<char*>cursor, ticker_length) cursor += ticker_length - size = curve.fNumItems * sizeof(double) - instance.recovery_rates = shared_ptr[double]( - <double*>malloc(size), double_free) - memcpy(instance.recovery_rates.get(), cursor, size) - cursor += size + recovery_size = curve.fNumItems * sizeof(double) + instance.recovery_rates = shared_ptr[double](<double*>malloc(recovery_size), + double_free) + memcpy(instance.recovery_rates.get(), cursor, recovery_size) + cursor += recovery_size memcpy(&instance.defaulted, cursor, sizeof(TDate)) return instance def __hash__(self): # same code as __getstate__ cdef: - TCurve* curve = self._thisptr.get() - size_t size = TCurve_size(curve.fNumItems) + const TCurve* curve = self.curve() + size_t buf_size = self.size() size_t size_recovery = curve.fNumItems * sizeof(double) - size_t buf_size = size + sizeof(size_t) + self.ticker.length() + \ - size_recovery + sizeof(TDate) unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) + unsigned char* cursor = serialize(curve, buf) + cursor = serialize_string(self.ticker, cursor) memcpy(cursor, self.recovery_rates.get(), size_recovery) cursor += size_recovery @@ -823,7 +843,7 @@ cdef class SpreadCurve(Curve): @property def recovery_rates(self): - cdef np.npy_intp shape = <np.npy_intp>self._thisptr.get().fNumItems + cdef np.npy_intp shape = self.curve().fNumItems cdef np.ndarray[np.float64_t] out = \ np.PyArray_SimpleNewFromData(1, &shape, np.NPY_DOUBLE, self.recovery_rates.get()) @@ -831,7 +851,7 @@ cdef class SpreadCurve(Curve): @cython.cdivision(True) @cython.boundscheck(False) -cdef TCurve* _fill_curve(TCurve* sc, TDate* end_dates, int n_dates): +cdef TCurve* _fill_curve(TCurve* sc, TDate* end_dates, int n_dates) nogil: cdef: size_t i TDate base_date = sc.fBaseDate |
