diff options
| -rw-r--r-- | c_layer/curve.cpp | 137 | ||||
| -rw-r--r-- | c_layer/curve.hpp | 61 | ||||
| -rw-r--r-- | pyisda/_curve.pxd | 45 | ||||
| -rw-r--r-- | pyisda/cdsone.pxd | 2 | ||||
| -rw-r--r-- | pyisda/cdsone.pyx | 4 | ||||
| -rw-r--r-- | pyisda/credit_index.pxd | 6 | ||||
| -rw-r--r-- | pyisda/credit_index.pyx | 128 | ||||
| -rw-r--r-- | pyisda/curve.pxd | 39 | ||||
| -rw-r--r-- | pyisda/curve.pyx | 342 | ||||
| -rw-r--r-- | pyisda/legs.pxd | 2 | ||||
| -rw-r--r-- | pyisda/legs.pyx | 11 | ||||
| -rw-r--r-- | pyisda/optim.pyx | 2 | ||||
| -rw-r--r-- | setup.py | 2 |
13 files changed, 495 insertions, 286 deletions
diff --git a/c_layer/curve.cpp b/c_layer/curve.cpp new file mode 100644 index 0000000..2f84d01 --- /dev/null +++ b/c_layer/curve.cpp @@ -0,0 +1,137 @@ +#include <cstring> +#include "curve.hpp" +#include "isda/cxzerocurve.h" +#include <cmath> + +namespace pyisda { + size_t Curve::size() { + return sizeof(int) + sizeof(TDate) + sizeof(double) + + sizeof(long) + sizeof(TRatePt) * ptr->fNumItems; + } + + unsigned char* Curve::serialize(unsigned char* const buf) { + unsigned char* cur = buf; + int num_items = ptr->fNumItems; + std::memcpy(cur, &num_items, sizeof(int)); + cur += sizeof(int); + memcpy(cur, ptr->fArray, sizeof(TRatePt) * num_items); + cur += sizeof(TRatePt) * num_items; + memcpy(cur, &(ptr->fBaseDate), sizeof(TDate)); + cur += sizeof(TDate); + memcpy(cur, &(ptr->fBasis), sizeof(double)); + cur += sizeof(double); + memcpy(cur, &(ptr->fDayCountConv), sizeof(long)); + return cur + sizeof(long); + } + + double Curve::zeroPrice(const TDate date) { + return JpmcdsZeroPrice(ptr, date); + } + + double Curve::zeroPrice(const TDate date1, const TDate date2) { + return JpmcdsForwardZeroPrice(ptr, date1, date2); + } + + double Curve::survivalProb(const TDate start_date, const TDate maturity_date, double eps) { + double lambda1, lambda2; + double t1, t2; + if(start_date == ptr->fBaseDate) { + lambda2 = JpmcdsZeroRate(ptr, maturity_date) * (1 + eps); + t2 = (maturity_date - ptr->fBaseDate) / 365.; + return std::exp(-lambda2 * t2); + } else { + lambda1 = JpmcdsZeroRate(ptr, start_date); + lambda2 = JpmcdsZeroRate(ptr, maturity_date); + t1 = start_date - ptr->fBaseDate; + t2 = maturity_date - ptr->fBaseDate; + return std::exp((lambda1 * t1 - lambda2 * t2) * (1 + eps) / 365.); + } + } + + double Curve::survivalProb(const TDate start_date, const TDate maturity_date) { + double lambda1, lambda2; + double t1, t2; + if(start_date == ptr->fBaseDate) { + lambda2 = JpmcdsZeroRate(ptr, maturity_date); + t2 = (maturity_date - ptr->fBaseDate) / 365.; + return std::exp(-lambda2 * t2); + } else { + lambda1 = JpmcdsZeroRate(ptr, start_date); + lambda2 = JpmcdsZeroRate(ptr, maturity_date); + t1 = start_date - ptr->fBaseDate; + t2 = maturity_date - ptr->fBaseDate; + return std::exp((lambda1 * t1 - lambda2 * t2) / 365.); + } + } + + void Curve::tweak(TCurve* const ptr, double epsilon) { + double h1, h2, t1, t2, c; + h1 = t1 = c = 0; + TRatePt cur; + for(int i = 0; i < ptr->fNumItems; i++) { + cur = ptr->fArray[i]; + h2 = cur.fRate; + t2 = (cur.fDate - ptr->fBaseDate) / 365.; + c += (h2 * t2 - h1 * t1 ) * (1 + epsilon); + cur.fRate = c / t2; + h1 = h2; + t1 = t2; + } + } + + void Curve::tweak(TCurve* const ptr, double epsilon, unsigned long mask) { + double h1, h2, t1, t2, c; + h1 = t1 = c = 0; + TRatePt cur; + for(int i = 0;i < ptr->fNumItems; i++) { + cur = ptr->fArray[i]; + h2 = cur.fRate; + t2 = (cur.fDate - ptr->fBaseDate) / 365.; + c += (h2 * t2 - h1 * t1 ) * (1 + epsilon * (( mask >> i) & 1)); + cur.fRate = c / t2; + h1 = h2; + t1 = t2; + } + } + + void Curve::tweak(double epsilon) { + Curve::tweak(ptr, epsilon); + } + + void Curve::tweak(double epsilon, unsigned long mask) { + Curve::tweak(ptr, epsilon, mask); + } + + size_t YieldCurve::size() { + return Curve::size() + sizeof(TDate) * dates.size(); + } + + unsigned char* YieldCurve::serialize(unsigned char* const buf) { + unsigned char* cur = buf; + cur = Curve::serialize(buf); + size_t size = dates.size(); + memcpy(cur, &size, sizeof(size_t)); + cur += sizeof(size_t); + memcpy(cur, dates.data(), sizeof(TDate) * size); + return cur + sizeof(TDate) * size; + } + + size_t SpreadCurve::size() { + return Curve::size() + recovery_rates.size() * sizeof(double) + 8; + } + + unsigned char* SpreadCurve::serialize(unsigned char* const buf) { + unsigned char* cur = buf; + cur = Curve::serialize(buf); + memcpy(cur, recovery_rates.data(), recovery_rates.size() * sizeof(double)); + cur += recovery_rates.size() * sizeof(double); + if (ticker.length() < 8) { + ticker.copy((char*)cur, ticker.length(), 0); + memset(cur + ticker.length(), 0, 8 - ticker.length()); + } else { + ticker.copy((char*)cur, 8, 0); + } + return cur + 8; + } + +} diff --git a/c_layer/curve.hpp b/c_layer/curve.hpp new file mode 100644 index 0000000..5479e72 --- /dev/null +++ b/c_layer/curve.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include <vector> +#include <string> +#include "isda/bastypes.h" +#include "isda/cdate.h" +#include "isda/tcurve.h" + +namespace pyisda { + class Curve { + + public: + Curve(TCurve* const curve) : ptr(curve) {}; + Curve(const Curve &curve2) : + ptr(JpmcdsCopyCurve(curve2.ptr)) {}; + + ~Curve() { + JpmcdsFreeTCurve(ptr); + } + explicit operator TCurve*() const { return ptr; } + unsigned char* serialize(unsigned char* buf); + double zeroPrice(const TDate date); + double zeroPrice(const TDate date1, const TDate date2); + double survivalProb(const TDate start_date, const TDate maturity_date); + double survivalProb(const TDate start_date, const TDate maturity_date, double eps); + static void tweak(TCurve* ptr, double epsilon); + static void tweak(TCurve* ptr, double epsilon, unsigned long mask); + void tweak(double epsilon); + void tweak(double epsilon, unsigned long mask); + size_t size(); + private: + TCurve* ptr; + }; + + class YieldCurve : public Curve { + public: + YieldCurve(TCurve* const curve, const std::vector<TDate>& dates); + YieldCurve(const YieldCurve &curve2) : + Curve(curve2), + dates(curve2.dates) {}; + size_t size(); + unsigned char* serialize(unsigned char* buf); + std::vector<TDate> dates; + }; + + class SpreadCurve : public Curve { + public: + SpreadCurve(TCurve* const curve, const std::vector<double>& recovery_rates, + std::string ticker); + SpreadCurve(const SpreadCurve &curve2) : + Curve(curve2), + recovery_rates(curve2.recovery_rates), + ticker(curve2.ticker) {}; + + std::vector<double> recovery_rates; + std::string ticker; + size_t size(); + unsigned char* serialize(unsigned char* buf); + }; + +} diff --git a/pyisda/_curve.pxd b/pyisda/_curve.pxd new file mode 100644 index 0000000..a3ae417 --- /dev/null +++ b/pyisda/_curve.pxd @@ -0,0 +1,45 @@ +from .date cimport TDate +from libcpp.string cimport string +from libcpp.vector cimport vector + +cdef extern from "isda/bastypes.h": + ctypedef struct TCurve: + int fNumItems + TRatePt* fArray + TDate fBaseDate + double fBasis + long fDayCountConv + + ctypedef struct TRatePt: + TDate fDate + double fRate + +cdef extern from "c_layer/curve.hpp" namespace "pyisda" nogil: + cdef cppclass Curve: + Curve(TCurve*) + Curve(const TCurve&) + unsigned char* serialize(unsigned char*) + TCurve* operator() + double zeroRate(TDate d) + double zeroRate(TDate d1, TDate d2) + void tweak(double epsilon, unsigned long mask) + void tweak(double epsilon) + + @staticmethod + void tweak(const TCurve*, double epsilon, unsigned long mask) + @staticmethod + void tweak(const TCurve*, double epsilon) + double survivalProb(const TDate, const TDate) + double survivalProb(const TDate, const TDate, double eps) + size_t size() + + cdef cppclass YieldCurve(Curve): + vector[TDate] dates + YieldCurve(const YieldCurve&) + YieldCurve(TCurve* const ptr, vector[TDate]&) + + cdef cppclass SpreadCurve(Curve): + vector[double] recovery_rates + SpreadCurve(const SpreadCurve&) + SpredCurve(TCurve* const ptr, vector[double]&, ticker) + string ticker diff --git a/pyisda/cdsone.pxd b/pyisda/cdsone.pxd index d40a7f2..e6a48f5 100644 --- a/pyisda/cdsone.pxd +++ b/pyisda/cdsone.pxd @@ -1,5 +1,5 @@ from .date cimport TDateInterval -from .curve cimport TCurve +from ._curve cimport TCurve cdef extern from "isda/stub.h" nogil: ctypedef struct TStubMethod: diff --git a/pyisda/cdsone.pyx b/pyisda/cdsone.pyx index 5dd8040..2970825 100644 --- a/pyisda/cdsone.pyx +++ b/pyisda/cdsone.pyx @@ -60,7 +60,7 @@ def upfront_charge(date, value_date, benchmark_start_date, stepin_date, if JpmcdsCdsoneUpfrontCharge(today, value_date_c, benchmark_start_date_c, stepin_date_c, start_date_c, end_date_c, coupon_rate, pay_accrued_on_default, &ivl, &stub, dcc("ACT/360"), b'F', b"None", - yc._thisptr.get(), spread, recovery_rate, + yc.get_curve(), spread, recovery_rate, pay_accrued_at_start, &result) == SUCCESS: return result else: @@ -121,7 +121,7 @@ def spread_from_upfront(date, value_date, benchmark_start_date, stepin_date, if JpmcdsCdsoneSpread(today, value_date_c, benchmark_start_date_c, stepin_date_c, start_date_c, end_date_c, coupon_rate, pay_accrued_on_default, &ivl, &stub, dcc("ACT/360"), b'F', b"None", - yc._thisptr.get(), upfront, recovery_rate, + yc.get_curve(), upfront, recovery_rate, pay_accrued_at_start, &result) == SUCCESS: return result else: diff --git a/pyisda/credit_index.pxd b/pyisda/credit_index.pxd index 4895ecf..3126220 100644 --- a/pyisda/credit_index.pxd +++ b/pyisda/credit_index.pxd @@ -1,6 +1,7 @@ from .legs cimport TContingentLeg, TFeeLeg from .date cimport TDate -from .curve cimport TCurve, shared_ptr +from ._curve cimport Curve +from libcpp.memory cimport shared_ptr from libcpp.vector cimport vector from libcpp.map cimport map from libcpp.string cimport string @@ -8,9 +9,8 @@ from libcpp.string cimport string cdef class CurveList: cdef TDate base_date cdef vector[double] _weights - cdef vector[shared_ptr[TCurve]] _curves + cdef vector[shared_ptr[Curve]] _curves cdef map[string, size_t] tickers - cdef vector[shared_ptr[double]] recovery_rates cdef class CreditIndex(CurveList): cdef TDate start_date diff --git a/pyisda/credit_index.pyx b/pyisda/credit_index.pyx index bec58e2..88c51e6 100644 --- a/pyisda/credit_index.pyx +++ b/pyisda/credit_index.pyx @@ -3,16 +3,17 @@ 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 shared_ptr from cython.operator cimport dereference as deref, preincrement as preinc from cpython cimport PyObject, Py_INCREF from cython.parallel cimport prange, parallel +from . cimport _curve cimport cython from .legs cimport (JpmcdsCdsContingentLegMake, JpmcdsCdsFeeLegMake, JpmcdsContingentLegPV, JpmcdsFeeLegPV, FeeLegAI, JpmcdsFeeLegFree) -from .curve cimport (SpreadCurve, JpmcdsCopyCurve, tweak_curve, YieldCurve, - JpmcdsFreeTCurve, Hash64WithSeed, Hash64, uint64_t, serialize, TRatePt, TCurve_size, survival_prob) +from .curve cimport (SpreadCurve, YieldCurve, Hash64WithSeed, Hash64, uint64_t) +from ._curve cimport TCurve, TRatePt from .date cimport (pydate_to_TDate, TDate_to_pydate, ACT_365F, JpmcdsDtFwdAny, TDateInterval, JpmcdsMakeDateInterval) from .cdsone cimport JpmcdsStringToStubMethod, TStubMethod @@ -22,8 +23,8 @@ 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) +# cdef inline shared_ptr[TCurve] make_shared(TCurve* ptr) nogil: +# return shared_ptr[TCurve](ptr, JpmcdsFreeTCurve) ctypedef TFeeLeg* TFeeLeg_ptr ctypedef TContingentLeg* TContingentLeg_ptr @@ -64,7 +65,7 @@ cdef class CurveList: if value_date is not None: self.base_date = pydate_to_TDate(value_date) else: - self.base_date = sc._thisptr.get().fBaseDate + self.base_date = sc.get_curve().fBaseDate i = 0 cdef int n_skipped = 0 @@ -74,7 +75,6 @@ cdef class CurveList: if it == self.tickers.end(): self.tickers[sc.ticker] = i self._curves.push_back(sc._thisptr) - self.recovery_rates.push_back(sc.recovery_rates) self._weights.push_back(1.) i += 1 else: @@ -264,28 +264,17 @@ cdef class CreditIndex(CurveList): def __hash__(self): cdef: - TCurve* curve = self._curves[0].get() - size_t size = TCurve_size(curve.fNumItems) - size_t buf_size = size + curve.fNumItems * sizeof(double) + 8 + _curve.SpreadCurve* curve = <_curve.SpreadCurve*>self._curves[0].get() + size_t size = curve.size() + size_t buf_size = curve.size() unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor - unsigned char* start = buf + size size_t i uint64_t h = 0 pair[string, size_t] p for p in self.tickers: - cursor = start - curve = self._curves[p.second].get() - serialize(curve, buf) - if p.first.length() < 8: - p.first.copy(<char*>cursor, p.first.length(), 0) - #0 padding - memset(cursor + p.first.length(), 0, 8 - p.first.length()) - else: - p.first.copy(<char*>cursor, 8, 0) - cursor += 8 - memcpy(cursor, self.recovery_rates[p.second].get(), curve.fNumItems * sizeof(double)) + curve = <_curve.SpreadCurve*>self._curves[p.second].get() + curve.serialize(buf); h ^= Hash64(<char*>buf, buf_size) free(buf) @@ -310,7 +299,7 @@ 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 + _curve.SpreadCurve* sc pair[string, size_t] p double recovery_rate @@ -320,26 +309,26 @@ cdef class CreditIndex(CurveList): j = 0 for p in self.tickers: - sc = self._curves[p.second].get() + sc = <_curve.SpreadCurve*>self._curves[p.second].get() tickers.append(p.first) # TODO: pick the actual recovery on the curve # this only works for flat recovery curve - recovery_rate = self.recovery_rates[p.second].get()[0] + recovery_rate = sc.recovery_rates[0] for j in range(self._maturities.size()): JpmcdsContingentLegPV(self.contingent_legs[j], - sc.fBaseDate, + (<TCurve*>sc).fBaseDate, cash_settle_date_c, step_in_date_c, - yc._thisptr.get(), - sc, + yc.get_curve(), + <TCurve*>sc, recovery_rate, &cl_pv[i,j]) JpmcdsFeeLegPV(self.fee_legs[j], - sc.fBaseDate, + (<TCurve*>sc).fBaseDate, step_in_date_c, cash_settle_date_c, - yc._thisptr.get(), - sc, + yc.get_curve(), + <TCurve*>sc, True, &fl_pv[i,j]) i += 1 @@ -380,7 +369,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, + yc.get_curve(), legs, fixed_rate, self._weights, epsilon, mask) if i == -1: free(legs.first) @@ -420,9 +409,8 @@ 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, - self.recovery_rates, fixed_rate, self._weights, - 0., 0) + cash_settle_date_c, yc.get_curve(), legs, + fixed_rate, self._weights, 0., 0) if i == -1: free(legs.first) JpmcdsFeeLegFree(legs.second) @@ -436,9 +424,8 @@ cdef class CreditIndex(CurveList): legs.second = self.fee_legs[i] 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, - self._weights, 0., 0) + carry + cash_settle_date_c, yc.get_curve(), legs, + fixed_rate, self._weights, 0., 0) + carry if i == -1: free(legs.first) JpmcdsFeeLegFree(legs.second) @@ -474,7 +461,7 @@ cdef class CreditIndex(CurveList): cdef: double fl_pv, r = 0 - shared_ptr[TCurve] sc + shared_ptr[Curve] sc i = 0 for sc in self._curves: @@ -482,8 +469,8 @@ cdef class CreditIndex(CurveList): self.base_date, step_in_date_c, cash_settle_date_c, - yc._thisptr.get(), - sc.get(), + yc.get_curve(), + <TCurve*>deref(sc), True, &fl_pv) r += self._weights[i] * fl_pv @@ -506,25 +493,23 @@ cdef class CreditIndex(CurveList): cdef: TDate maturity_c = pydate_to_TDate(maturity) unsigned long mask = fill_mask(maturity_c, self._maturities, self._curves[0]) - TCurve* sc_orig - TCurve *sc_copy + _curve.SpreadCurve *sc_copy size_t i if inplace: for i in range(self._curves.size()): - sc_orig = self._curves[i].get() - tweak_curve(sc_orig, sc_orig, epsilon, mask) + (<_curve.SpreadCurve*>self._curves[i].get()).tweak(epsilon, mask) else: for i in range(self._curves.size()): - sc_orig = self._curves[i].get() - sc_copy = JpmcdsCopyCurve(sc_orig) - tweak_curve(sc_orig, sc_copy, epsilon, mask) - self._curves[i].reset(sc_copy, JpmcdsFreeTCurve) + sc_copy = new _curve.SpreadCurve( + deref(<_curve.SpreadCurve*>(self._curves[i].get()))) + sc_copy.tweak(epsilon, mask) + self._curves[i].reset(sc_copy) def survival_matrix(self, TDate[:] schedule, double epsilon=0.): cdef: - shared_ptr[TCurve] sc + shared_ptr[Curve] sc pair[string, size_t] p size_t i np.npy_intp[2] n @@ -534,20 +519,27 @@ 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: - sc = self._curves[p.second] - tickers[p.second] = p.first - for i in range(n[1]): - sp[p.second, i] = survival_prob(sc.get(), self.base_date, schedule[i], epsilon) + if epsilon == 0.: + for p in self.ticker: + sc = self._curves[p.second] + tickers[p.second] = p.first + for i in range(n[1]): + sp[p.second, i] = sc.get().survivalProb(self.base_date, schedule[i]) + else: + for p in self.tickers: + sc = self._curves[p.second] + tickers[p.second] = p.first + for i in range(n[1]): + sp[p.second, i] = sc.get().survivalProb(self.base_date, schedule[i], epsilon) return sp, tickers cdef unsigned long fill_mask(const TDate maturity, const vector[TDate]& maturities, - const shared_ptr[TCurve]& sc) nogil: + const shared_ptr[Curve]& sc) nogil: cdef: TDate prev_maturity = 0 TDate next_maturity = maturity size_t i - TRatePt* it = sc.get().fArray + TRatePt* it = (<TCurve*>sc.get()).fArray unsigned long mask = 0 for i in range(maturities.size()): @@ -600,21 +592,20 @@ cdef pair[TContingentLeg_ptr, TFeeLeg_ptr] get_legs(TDate maturity, True) return r -cdef double pv(const vector[shared_ptr[TCurve]]& curves, +cdef double pv(const vector[shared_ptr[Curve]]& curves, TDate base_date, TDate step_in_date, TDate cash_settle_date, TCurve* yc, pair[TContingentLeg_ptr, TFeeLeg_ptr]& legs, - const vector[shared_ptr[double]] &recovery_rates, double fixed_rate, vector[double]& weights, double epsilon, unsigned long mask) nogil: cdef: double fl_pv, cl_pv, r = 0 + _curve.SpreadCurve *curve TCurve *tweaked_curve - TCurve *orig_curve size_t i = 0 double* recovery_rate @@ -624,8 +615,8 @@ cdef double pv(const vector[shared_ptr[TCurve]]& curves, cl_pv = 0. for i in prange(curves.size()): - tweaked_curve = curves[i].get() - recovery_rate = recovery_rates[i].get() + curve = <_curve.SpreadCurve*>(curves[i].get()) + recovery_rate = curve.recovery_rates.data() # FIXME: do something better if isnan(deref(recovery_rate)): preinc(recovery_rate) @@ -635,7 +626,7 @@ cdef double pv(const vector[shared_ptr[TCurve]]& curves, cash_settle_date, step_in_date, yc, - tweaked_curve, + <TCurve*>deref(curve), deref(recovery_rate), &cl_pv) JpmcdsFeeLegPV(legs.second, @@ -643,19 +634,18 @@ cdef double pv(const vector[shared_ptr[TCurve]]& curves, step_in_date, cash_settle_date, yc, - tweaked_curve, + <TCurve*>deref(curve), True, &fl_pv) r += weights[i] * (cl_pv - fl_pv * fixed_rate) else: with parallel(): - tweaked_curve = JpmcdsCopyCurve(curves[0].get()) cl_pv = 0. fl_pv = 0. for i in prange(curves.size()): - orig_curve = curves[i].get() - tweak_curve(orig_curve, tweaked_curve, epsilon, mask) - recovery_rate = recovery_rates[i].get() + curve = <_curve.SpreadCurve*>(curves[i].get()) + recovery_rate = curve.recovery_rates.data() + _curve.Curve.tweak(tweaked_curve, epsilon, mask) # FIXME: do something better if isnan(deref(recovery_rate)): preinc(recovery_rate) diff --git a/pyisda/curve.pxd b/pyisda/curve.pxd index cbf826a..2b8ee47 100644 --- a/pyisda/curve.pxd +++ b/pyisda/curve.pxd @@ -3,9 +3,13 @@ from .legs cimport TContingentLeg, TFeeLeg from libcpp.vector cimport vector from libcpp cimport bool from libcpp.string cimport string +from libcpp.memory cimport shared_ptr from libc.string cimport memcpy from libc.stdlib cimport malloc, calloc, free from libc.stdint cimport uint64_t +from libcpp.memory cimport shared_ptr +from ._curve cimport TCurve, TRatePt +from . cimport _curve cdef extern from "isda/zerocurve.h" nogil: ctypedef int TBoolean @@ -27,17 +31,6 @@ cdef extern from "isda/zerocurve.h" nogil: long floatSwapDCC, long badDayConv, char* holidayFile) -cdef extern from "isda/bastypes.h": - ctypedef struct TCurve: - int fNumItems - TRatePt* fArray - TDate fBaseDate - double fBasis - long fDayCountConv - - ctypedef struct TRatePt: - TDate fDate - double fRate cdef extern from "farmhash.h" namespace 'util' nogil: uint64_t Hash64(const char *buff, size_t len) @@ -185,7 +178,7 @@ cdef extern from "isda/cxzerocurve.h" nogil: double JpmcdsForwardZeroPrice(TCurve* curve, TDate startDate, TDate maturityDate) double JpmcdsZeroRate(TCurve* curve, TDate date) -cdef double survival_prob(TCurve* curve, TDate start_date, TDate maturity_date, double eps) nogil +#cdef double survival_prob(TCurve* curve, TDate start_date, TDate maturity_date, double eps) nogil cdef extern from "isda/cfinanci.h": int JpmcdsDiscountToRateYearFrac(double discount, # (I) Discount factor @@ -199,27 +192,15 @@ cdef enum Basis: ANNUAL_BASIS = 1 DISCOUNT_FACTOR = -2 -cdef extern from "<memory>" namespace "std" nogil: - cdef cppclass shared_ptr[T]: - ctypedef void (*D)(T*) - shared_ptr() - shared_ptr(T*, D) - T* get() - void reset(T*, D) - bool operator!() - long use_count() - cdef class Curve: - cdef shared_ptr[TCurve] _thisptr + cdef shared_ptr[_curve.Curve] _thisptr + cdef inline TCurve* get_curve(self) nogil cdef class YieldCurve(Curve): - cdef vector[TDate] dates + cdef _curve.YieldCurve* get_yieldcurve(self) nogil + cdef vector[TDate]& dates(self) cdef class SpreadCurve(Curve): - cdef string ticker - cdef shared_ptr[double] recovery_rates + cdef _curve.SpreadCurve* get_spreadcurve(self) nogil cdef fArray_to_list(TRatePt* fArray, int fNumItems) - -cdef void tweak_curve(const TCurve* sc, TCurve* sc_tweaked, double epsilon, - unsigned long mask) nogil diff --git a/pyisda/curve.pyx b/pyisda/curve.pyx index 1f76c69..577f10c 100644 --- a/pyisda/curve.pyx +++ b/pyisda/curve.pyx @@ -11,6 +11,7 @@ from .cdsone cimport JpmcdsStringToStubMethod, TStubMethod from .legs cimport (JpmcdsCdsContingentLegMake, JpmcdsCdsFeeLegMake, JpmcdsContingentLegPV, JpmcdsFeeLegPV, FeeLegAI, JpmcdsFeeLegFree) +from libcpp.memory cimport make_shared, static_pointer_cast cimport cython cimport numpy as np import numpy as np @@ -31,41 +32,45 @@ 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 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 lambda1, lambda2 - double t1, t2, u - if start_date == curve.fBaseDate: - lambda2 = JpmcdsZeroRate(curve, maturity_date) - t2 = (maturity_date - curve.fBaseDate) / 365. - if eps != 0.: - lambda2 *= (1 + eps) - return exp(-lambda2 * t2) - else: - lambda1 = JpmcdsZeroRate(curve, start_date) - lambda2 = JpmcdsZeroRate(curve, maturity_date) - t1 = (start_date - curve.fBaseDate) / 365. - t2 = (maturity_date - curve.fBaseDate) / 365. - u = lambda1 * t1 - lambda2 * t2 - if eps != 0.: - u *= (1 + eps) - return exp(u) +# cdef double survival_prob(TCurve* curve, TDate start_date, TDate maturity_date, double eps) nogil: +# cdef: +# double lambda1, lambda2 +# double t1, t2, u +# if start_date == curve.fBaseDate: +# lambda2 = JpmcdsZeroRate(curve, maturity_date) +# t2 = (maturity_date - curve.fBaseDate) / 365. +# if eps != 0.: +# lambda2 *= (1 + eps) +# return exp(-lambda2 * t2) +# else: +# lambda1 = JpmcdsZeroRate(curve, start_date) +# lambda2 = JpmcdsZeroRate(curve, maturity_date) +# t1 = (start_date - curve.fBaseDate) / 365. +# t2 = (maturity_date - curve.fBaseDate) / 365. +# u = lambda1 * t1 - lambda2 * t2 +# if eps != 0.: +# u *= (1 + eps) +# return exp(u) cdef class Curve(object): + cdef inline TCurve* get_curve(self) nogil: + return <TCurve*>(deref(self._thisptr)) + def __getstate__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t curve_size = TCurve_size(curve.fNumItems) + _curve.Curve* curve = self._thisptr.get() + size_t curve_size = curve.size() unsigned char* buf = <unsigned char*>malloc(curve_size * sizeof(unsigned char)) - serialize(curve, buf) + curve.serialize(buf) + #FIXME: should I free buf? return <bytes>buf[:curve_size] def __setstate__(self, bytes state): @@ -73,7 +78,7 @@ cdef class Curve(object): TCurve* curve = <TCurve*>malloc(sizeof(TCurve)) unsigned char* cursor = state deserialize(cursor, curve) - self._thisptr = make_shared(curve) + self._thisptr = make_shared[_curve.Curve](curve) @classmethod def from_bytes(cls, object state): @@ -88,15 +93,15 @@ cdef class Curve(object): else: cursor = <bytes?>state deserialize(cursor, curve) - instance._thisptr = make_shared(curve) + instance._thisptr = shared_ptr[_curve.Curve](curve) return instance def __hash__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t curve_size = TCurve_size(curve.fNumItems) - unsigned char* buf = <unsigned char*>malloc(curve_size * sizeof(unsigned char)) - serialize(curve, buf) + _curve.Curve* curve = self._thisptr.get() + size_t curve_size = curve.size() + unsigned char* buf = <unsigned char*>malloc(curve_size * sizeof(unsigned char)) + curve.serialize(buf) cdef uint64_t r = Hash64(<char*>buf, curve_size) free(buf) return r @@ -109,24 +114,24 @@ cdef class Curve(object): dict contains `base_date`, `basis`, `day_count_counvention` and `data` """ - + cdef TCurve* curve = <TCurve*>deref(self._thisptr) 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': self.get_curve().fBasis, + 'day_count_convention': dcc_tostring(self.get_curve().fDayCountConv), + 'data': fArray_to_list(self.get_curve().fArray, self.get_curve().fNumItems)} @cython.boundscheck(False) @cython.cdivision(True) def to_series(self, bint forward=True): - cdef np.npy_intp n = self._thisptr.get().fNumItems + cdef np.npy_intp n = self.get_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 = self.get_curve().fArray cdef double t1, h1, t2, h2 t1 = 0 h1 = 0 - cdef int base_date = self._thisptr.get().fBaseDate + cdef int base_date = self.get_curve().fBaseDate if forward: for i in range(n): h2 = it[i].fRate @@ -147,17 +152,18 @@ cdef class Curve(object): def __iter__(self): cdef: + TCurve* curve = <TCurve*>deref(self._thisptr) size_t i = 0 - TRatePt* it = self._thisptr.get().fArray - for i in range(self._thisptr.get().fNumItems): + TRatePt* it = self.get_curve().fArray + for i in range(self.get_curve().fNumItems): yield (TDate_to_pydate(it[i].fDate), it[i].fRate) def __len__(self): - return self._thisptr.get().fNumItems + return self.get_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[_curve.Curve](deref(self._thisptr.get())) memo[id(self)] = sc return sc @@ -165,21 +171,21 @@ 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 np.npy_intp shape = self.get_curve().fNumItems t1 = 0 h1 = 0 - cdef TCurve* curve = self._thisptr.get() - cdef double* data = <double*>malloc(curve.fNumItems * sizeof(double)) + cdef TCurve* curve = self.get_curve() + 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) @@ -195,7 +201,7 @@ cdef class Curve(object): @property def base_date(self): - return TDate_to_pydate(self._thisptr.get().fBaseDate) + return TDate_to_pydate(self.get_curve().fBaseDate) def __forward_zero_price(self, d2, d1=None): """ computes the forward zero price at a given date. @@ -212,12 +218,10 @@ cdef class Curve(object): raise ValueError('curve is empty') cdef TDate start_date if d1 is None: - start_date = self._thisptr.get().fBaseDate - return JpmcdsForwardZeroPrice(self._thisptr.get(), start_date, - pydate_to_TDate(d2)) + return self._thisptr.get().zeroRate(pydate_to_TDate(d2)) else: - return JpmcdsForwardZeroPrice(self._thisptr.get(), pydate_to_TDate(d1), - pydate_to_TDate(d2)) + return self._thisptr.get().zeroRate(pydate_to_TDate(d1), + pydate_to_TDate(d2)) cdef fArray_to_list(TRatePt* fArray, int fNumItems): cdef size_t i @@ -255,6 +259,13 @@ cdef class YieldCurve(Curve): .. warning:: Instruments need to be sorted by tenor! """ + + cdef _curve.YieldCurve* get_yieldcurve(self): + return <_curve.YieldCurve*> self._thisptr.get() + + cdef vector[TDate]& dates(self): + return (<_curve.YieldCurve*> self._thisptr.get()).dates + def __init__(self, date, str types, list periods, double[:] rates, str mm_dcc, str fixed_swap_period, str float_swap_period, @@ -266,9 +277,9 @@ cdef class YieldCurve(Curve): TDateInterval ivl char* routine = 'zerocurve' TDate value_date = pydate_to_TDate(date) + TDate d - self.dates = vector[TDate](len(periods)) - + cdef vector[TDate] dates cdef TDate settle_date if JpmcdsDateFromBusDaysOffset(value_date, 2, "None", &settle_date) != SUCCESS: @@ -278,19 +289,20 @@ cdef class YieldCurve(Curve): TDateInterval tmp long period_adjust size_t i - char* period_bytes + char* p + TDate t - for i, p in enumerate(periods): - period_bytes = p - if JpmcdsStringToDateInterval(period_bytes, routine, &tmp) != SUCCESS: + for i, p in periods: + if JpmcdsStringToDateInterval(p, routine, &tmp) != SUCCESS: raise ValueError if types[i] == 'M': period_adjust = MODIFIED else: period_adjust = NONE if JpmcdsDateFwdThenAdjust(settle_date, &tmp, period_adjust, - "None", &self.dates[i]) != SUCCESS: + "None", &d) != SUCCESS: raise ValueError('Invalid interval') + dates.push_back(d) cdef char* fixed_bytes = fixed_swap_period cdef char* float_bytes = float_swap_period @@ -305,22 +317,20 @@ cdef class YieldCurve(Curve): if JpmcdsDateIntervalToFreq(&ivl, &float_freq) != SUCCESS: raise ValueError - self._thisptr = make_shared(JpmcdsBuildIRZeroCurve( - value_date, types_bytes, self.dates.data(), - &rates[0], self.dates.size(), dcc(mm_dcc), <long> fixed_freq, - <long> float_freq, dcc(fixed_swap_dcc), dcc(float_swap_dcc), - bad_day_conv, b"None" - )) + self._thisptr = shared_ptr[_curve.Curve](new _curve.YieldCurve( + JpmcdsBuildIRZeroCurve( + value_date, types_bytes, dates.data(), + &rates[0], dates().size(), dcc(mm_dcc), <long> fixed_freq, + <long> float_freq, dcc(fixed_swap_dcc), dcc(float_swap_dcc), + bad_day_conv, b"None"), dates) + ) 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() + _curve.YieldCurve* const curve = <_curve.YieldCurve*>self._thisptr.get() + size_t buf_size = curve.size() unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) - serialize_vector(self.dates, cursor) + curve.serialize(buf) return <bytes>buf[:buf_size] def __setstate__(self, bytes state): @@ -330,16 +340,17 @@ cdef class YieldCurve(Curve): size_t num_instr cursor = deserialize(cursor, curve) - self._thisptr = make_shared(curve) memcpy(&num_instr, cursor, sizeof(size_t)) cursor += sizeof(size_t) - self.dates = vector[TDate](num_instr) - memcpy(self.dates.data(), cursor, num_instr * sizeof(TDate)) + cdef vector[TDate] dates = vector[TDate](num_instr) + memcpy(dates.data(), cursor, num_instr * sizeof(TDate)) + self._thisptr = shared_ptr[_curve.Curve](new _curve.YieldCurve(curve, dates)) def __deepcopy__(self, dict memo): cdef YieldCurve yc = YieldCurve.__new__(YieldCurve) - yc._thisptr = make_shared(JpmcdsCopyCurve(self._thisptr.get())) - yc.dates = vector[TDate](self.dates) + yc._thisptr = shared_ptr[_curve.Curve]( + new _curve.YieldCurve(deref(<_curve.YieldCurve*>self._thisptr.get())) + ) memo[id(self)] = yc return yc @@ -360,25 +371,20 @@ cdef class YieldCurve(Curve): cursor = deserialize(cursor, curve) - instance._thisptr = make_shared(curve) + instance._thisptr = make_shared[_curve.Curve](curve) memcpy(&num_instr, cursor, sizeof(size_t)) - cursor += sizeof(size_t) - instance.dates = vector[TDate](num_instr) - memcpy(instance.dates.data(), cursor, num_instr * sizeof(TDate)) + cdef vector[TDate] dates = vector[TDate](num_instr) + memcpy(dates.data(), cursor, num_instr * sizeof(TDate)) + instance._thisptr = shared_ptr[_curve.Curve]( + new _curve.YieldCurve(curve, dates)) return instance 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() - unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) - size = self.dates.size() - memcpy(cursor, &size, sizeof(size_t)) - cursor += sizeof(size_t) - memcpy(cursor, self.dates.data(), sizeof(TDate) * size) + _curve.YieldCurve* curve = self.get_yieldcurve() + size_t buf_size = curve.size() + unsigned char* buf = <unsigned char*>malloc(curve.size()) + curve.serialize(buf); cdef uint64_t r = Hash64(<char*>buf, buf_size) free(buf) return r @@ -388,17 +394,19 @@ cdef class YieldCurve(Curve): """ build a yield curve from a list of discount factors """ cdef TDate base_date_c = pydate_to_TDate(base_date) cdef YieldCurve yc = YieldCurve.__new__(YieldCurve) - yc.dates = vector[TDate](len(dates)) + cdef vector[TDate] dates_c = vector[TDate](len(dates)) cdef size_t i - cdef double* rates = <double*>malloc(sizeof(double) * yc.dates.size()) + cdef double* rates = <double*>malloc(sizeof(double) * dates_c.size()) for i, d in enumerate(dates): - yc.dates[i] = pydate_to_TDate(d) - JpmcdsDiscountToRateYearFrac(dfs[i], <double>(yc.dates[i]-base_date_c)/365., + dates_c[i] = pydate_to_TDate(d) + JpmcdsDiscountToRateYearFrac(dfs[i], <double>(dates_c[i] - base_date_c)/365., <double>1, &rates[i]) - yc._thisptr = make_shared( + yc._thisptr = shared_ptr[_curve.Curve](_curve.YieldCurve( JpmcdsMakeTCurve(base_date_c, yc.dates.data(), rates, dfs.shape[0], - <double>1, dcc(day_count_conv))) + 1., dcc(day_count_conv)), dates_c) + ) + free(rates) return yc discount_factor = Curve.__forward_zero_price @@ -408,50 +416,53 @@ cdef class YieldCurve(Curve): """ returns the list of instrument dates """ - return [TDate_to_pydate(d) for d in self.dates] + return [TDate_to_pydate(d) for d in self.dates()] + @cython.cdivision(True) def expected_forward_curve(self, forward_date): """ returns the expected forward curve """ - cdef TDate forward_date_c = pydate_to_TDate(forward_date) - cdef YieldCurve yc = YieldCurve.__new__(YieldCurve) - cdef size_t i = 0 - while self.dates[i] < forward_date_c: - i += 1 - yc.dates = vector[TDate](self.dates.size() - i) - cdef double* rates = <double*>malloc(sizeof(double) * yc.dates.size()) - cdef size_t k - cdef double df - for k in range(yc.dates.size()): - yc.dates[k] = self.dates[i] - df = JpmcdsForwardZeroPrice(self._thisptr.get(), forward_date_c, self.dates[i]) - JpmcdsDiscountToRateYearFrac( - df, <double>(self.dates[i] - forward_date_c)/365., - <double>1, &rates[k]) - i += 1 - yc._thisptr = make_shared(JpmcdsMakeTCurve( - forward_date_c, yc.dates.data(), rates, yc.dates.size(), - <double>1, self._thisptr.get().fDayCountConv)) + cdef: + TDate forward_date_c = pydate_to_TDate(forward_date) + YieldCurve yc = YieldCurve.__new__(YieldCurve) + vector[TDate] dates + vector[double] rates + double r + + for d in self.dates(): + if d < forward_date_c: + continue + else: + dates.push_back(d) + df = self._thisptr.get().zeroRate(forward_date_c, d) + JpmcdsDiscountToRateYearFrac( + df, <double>(d - forward_date_c)/365., 1., &r) + rates.push_back(r) + yc._thisptr = shared_ptr[_curve.Curve](new _curve.YieldCurve( + JpmcdsMakeTCurve( + forward_date_c, dates.data(), rates, dates.size(), + 1., self.get_curve().fDayCountConv), dates) + ) return yc -@cython.cdivision(True) -cdef void tweak_curve(const TCurve* sc, TCurve* sc_tweaked, double epsilon, - unsigned long mask) nogil: - ## We want to tweak in the forward space, so we convert the hazard rates - ## into forward rates and then back - cdef double h1, h2, t1, t2, c - h1 = t1 = c = 0 - cdef size_t i +# @cython.cdivision(True) +# cdef void tweak_curve(const TCurve* sc, TCurve* sc_tweaked, double epsilon, +# unsigned long mask) nogil: +# ## We want to tweak in the forward space, so we convert the hazard rates +# ## into forward rates and then back +# cdef double h1, h2, t1, t2, c +# h1 = t1 = c = 0 +# cdef size_t i - for i in range(sc.fNumItems): - h2 = sc.fArray[i].fRate - t2 = (sc.fArray[i].fDate - sc.fBaseDate) / 365. - h = (h2 * t2 - h1 * t1) / (t2 - t1) - if mask == 0 or (mask >> i) & 1: - h *= (1 + epsilon) - c += (t2 - t1) * h - sc_tweaked.fArray[i].fRate = c / t2 - h1 = h2 - t1 = t2 +# for i in range(sc.fNumItems): +# h2 = sc.fArray[i].fRate +# t2 = (sc.fArray[i].fDate - sc.fBaseDate) / 365. +# h = (h2 * t2 - h1 * t1) / (t2 - t1) +# if mask == 0 or (mask >> i) & 1: +# h *= (1 + epsilon) +# c += (t2 - t1) * h +# sc_tweaked.fArray[i].fRate = c / t2 +# h1 = h2 +# t1 = t2 cdef class SpreadCurve(Curve): """ @@ -472,6 +483,9 @@ cdef class SpreadCurve(Curve): Default to True """ + cdef _curve.SpreadCurve* get_spreadcurve(self): + return <_curve.SpreadCurve*> self._thisptr.get() + @cython.boundscheck(False) @cython.wraparound(False) @cython.initializedcheck(False) @@ -585,15 +599,10 @@ cdef class SpreadCurve(Curve): def __getstate__(self): cdef: - TCurve* curve = self._thisptr.get() - size_t size = TCurve_size(curve.fNumItems) - size_t buf_size = size + sizeof(size_t) + self.ticker.length() + \ - curve.fNumItems * sizeof(double) + _curve.SpreadCurve* curve = self.get_spreadcurve() + size_t buf_size = curve.size() unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) - cursor = serialize_string(self.ticker, cursor) - memcpy(cursor, self.recovery_rates.get(), sizeof(double) * curve.fNumItems) + curve.serialize(buf) return <bytes>buf[:buf_size] def __setstate__(self, bytes state): @@ -615,11 +624,8 @@ cdef class SpreadCurve(Curve): 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())) - sc.ticker = self.ticker - sc.recovery_rates= shared_ptr[double](<double*>malloc(size), double_free) - memcpy(sc.recovery_rates.get(), self.recovery_rates.get(), size) + sc._thisptr = shared_ptr[_curve.Curve]( + new _curve.SpreadCurve(deref(self.get_spreadcurve()))) memo[id(self)] = sc return sc @@ -653,15 +659,10 @@ cdef class SpreadCurve(Curve): def __hash__(self): # same code as __getstate__ cdef: - TCurve* curve = self._thisptr.get() - size_t size = TCurve_size(curve.fNumItems) - size_t buf_size = size + sizeof(size_t) + self.ticker.length() + \ - curve.fNumItems * sizeof(double) + _curve.SpreadCurve* curve = self.get_spreadcurve() + size_t buf_size = curve.size() unsigned char* buf = <unsigned char*>malloc(buf_size) - unsigned char* cursor = buf + size - serialize(curve, buf) - cursor = serialize_string(self.ticker, cursor) - memcpy(cursor, self.recovery_rates.get(), curve.fNumItems * sizeof(double)) + curve.serialize(buf) cdef uint64_t r = Hash64(<char*>buf, buf_size) free(buf) return r @@ -704,33 +705,25 @@ cdef class SpreadCurve(Curve): epsilon : double tweaking factor (either additive or multiplicative) multiplicative : bool, optional - do we scale by 1+epsilon or add epsilon (default multiplicative). + do we scale by 1 + epsilon or add epsilon (default multiplicative). mask : array of bool or None If None (default), tweak everything, otherwise only tweak values in the mask. """ cdef: - TCurve* curve_tweaked SpreadCurve sc - int num_items = self._thisptr.get().fNumItems - vector[double] h - vector[double] T - size_t i if not inplace: sc = SpreadCurve.__new__(SpreadCurve) - curve_tweaked = JpmcdsCopyCurve(self._thisptr.get()) - sc._thisptr = make_shared(curve_tweaked) - sc.ticker = self.ticker - memcpy(sc.recovery_rates.get(), self.recovery_rates.get(), - num_items * sizeof(double)) - #sc.recovery_rates = self.recovery_rates + sc._thisptr = shared_ptr[_curve.Curve](new _curve.SpreadCurve( + deref(self.get_spreadcurve())) + ) else: sc = self - curve_tweaked = self._thisptr.get() - if mask != 0: - tweak_curve(self._thisptr.get(), curve_tweaked, epsilon, mask) + sc._thisptr.get().tweak(epsilon, mask) + else: + sc._thisptr.get().tweak(epsilon) return sc @cython.boundscheck(False) @@ -765,8 +758,8 @@ cdef class SpreadCurve(Curve): ACT_360, <long>b'M', b'NONE', - yc._thisptr.get(), - self._thisptr.get(), + <TCurve*>deref(yc._thisptr), + <TCurve*>deref(self._thisptr), &recovery_rates[0], par_spreads) free(end_dates_c) @@ -782,10 +775,11 @@ cdef class SpreadCurve(Curve): @property def recovery_rates(self): - cdef np.npy_intp shape = <np.npy_intp>self._thisptr.get().fNumItems + cdef _curve.SpreadCurve* curve = <_curve.SpreadCurve*>self._thisptr.get() + cdef np.npy_intp shape = <np.npy_intp>curve.recovery_rates.size() cdef np.ndarray[np.float64_t] out = \ np.PyArray_SimpleNewFromData(1, &shape, np.NPY_DOUBLE, - self.recovery_rates.get()) + curve.recovery_rates.data()) return out @cython.cdivision(True) diff --git a/pyisda/legs.pxd b/pyisda/legs.pxd index 77b4230..90750c6 100644 --- a/pyisda/legs.pxd +++ b/pyisda/legs.pxd @@ -1,6 +1,6 @@ from .date cimport TDateInterval from .cdsone cimport TStubMethod -from .curve cimport TCurve +from ._curve cimport TCurve ctypedef int TBoolean ctypedef long TDate diff --git a/pyisda/legs.pyx b/pyisda/legs.pyx index b5bf312..39e60e8 100644 --- a/pyisda/legs.pyx +++ b/pyisda/legs.pyx @@ -1,3 +1,4 @@ +from cython.operator cimport dereference as deref from libc.stdlib cimport free from .date cimport pydate_to_TDate, TDate_to_pydate, dcc from date import dcc_tostring @@ -43,7 +44,7 @@ cdef class ContingentLeg: self._thisptr.endDate, self._thisptr.notional, self._thisptr.protectStart)) - + @property def end_date(self): return TDate_to_pydate(self._thisptr.endDate) @@ -70,7 +71,7 @@ cdef class ContingentLeg: cdef TDate value_date_c = pydate_to_TDate(value_date) cdef double pv if JpmcdsContingentLegPV(self._thisptr, today_c, value_date_c, step_in_date_c, - yc._thisptr.get(), sc._thisptr.get(), + yc.get_curve(), sc.get_curve(), recovery_rate, &pv) == 0: return pv else: @@ -162,8 +163,8 @@ cdef class FeeLeg: result.append((TDate_to_pydate(cf.fDate), cf.fAmount)) return result - def pv(self, today, step_in_date, value_date, YieldCurve yc, SpreadCurve sc, - bint pay_accrued_at_start): + def pv(self, today, step_in_date, value_date, YieldCurve yc not None, + SpreadCurve sc not None, bint pay_accrued_at_start): """ Present Value of FeeLeg cashflows. @@ -185,7 +186,7 @@ cdef class FeeLeg: cdef TDate value_date_c = pydate_to_TDate(value_date) cdef double pv if JpmcdsFeeLegPV(self._thisptr, today_c, step_in_date_c, value_date_c, - yc._thisptr.get(), sc._thisptr.get(), pay_accrued_at_start, &pv) == 0: + yc.get_curve(), sc.get_curve(), pay_accrued_at_start, &pv) == 0: return pv else: raise ValueError diff --git a/pyisda/optim.pyx b/pyisda/optim.pyx index a3a4143..c82e177 100644 --- a/pyisda/optim.pyx +++ b/pyisda/optim.pyx @@ -93,7 +93,7 @@ def init_context(YieldCurve yc not None, trade_date, value_date, start_date, 1) # protect_start = True params.stepinDate = step_in_date_c params.cashSettleDate = value_date_c - params.discountCurve = yc._thisptr.get() + params.discountCurve = yc.get_curve() params.cdsCurve = JpmcdsNewTCurve(trade_date_c, 1, <double>CONTINUOUS, @@ -5,7 +5,7 @@ import numpy all_extensions = Extension( "*", - ["pyisda/*.pyx"], + ["pyisda/*.pyx", "c_layer/curve.cpp"], include_dirs=["c_layer", numpy.get_include()], libraries=["cds", "farmhash"], language="c++", |
