summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--c_layer/curve.cpp137
-rw-r--r--c_layer/curve.hpp61
-rw-r--r--pyisda/_curve.pxd45
-rw-r--r--pyisda/cdsone.pxd2
-rw-r--r--pyisda/cdsone.pyx4
-rw-r--r--pyisda/credit_index.pxd6
-rw-r--r--pyisda/credit_index.pyx128
-rw-r--r--pyisda/curve.pxd39
-rw-r--r--pyisda/curve.pyx342
-rw-r--r--pyisda/legs.pxd2
-rw-r--r--pyisda/legs.pyx11
-rw-r--r--pyisda/optim.pyx2
-rw-r--r--setup.py2
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,
diff --git a/setup.py b/setup.py
index 35005d8..1ccd0e6 100644
--- a/setup.py
+++ b/setup.py
@@ -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++",