Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
sudo apt-get install libquantlib0-dev

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:
# Publish built docs to gh-pages branch.
- name: Checkout documentation branch
if: matrix.python-version == '3.11'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: gh-pages
path: gh-pages
Expand Down
38 changes: 38 additions & 0 deletions quantlib/instruments/_assetswap.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from libcpp cimport bool
from quantlib.types cimport Real, Spread
from quantlib.time._schedule cimport Schedule
from ._bond cimport Bond
from quantlib.handle cimport shared_ptr
from quantlib.time._date cimport Date
from quantlib.time._daycounter cimport DayCounter
from .._cashflow cimport Leg
from ._swap cimport Swap
from quantlib.indexes._ibor_index cimport IborIndex

cdef extern from "ql/instruments/assetswap.hpp" namespace "QuantLib" nogil:
cdef cppclass AssetSwap(Swap):
AssetSwap(bool payBondCoupon,
shared_ptr[Bond] bond,
Real bondCleanPrice,
const shared_ptr[IborIndex]& iborIndex,
Spread spread,
Schedule floatSchedule,
const DayCounter& floatingDayCounter,
bool parAssetSwap, # = true
Real gearing, # 1.0
Real nonParRepayment, # = Null<Real>(),
Date dealMaturity) # = Date()
Spread fairSpread() except +
Real floatingLegBPS() except +
Real floatingLegNPV() except +
Real fairCleanPrice() except +
Real fairNonParRepayment() except +

bool parSwap()
Spread spread()
Real cleanPrice()
Real nonParRepayment()
const shared_ptr[Bond]& bond()
bool payBondCoupon()
const Leg& bondLeg()
const Leg& floatingLeg()
31 changes: 31 additions & 0 deletions quantlib/instruments/_zerocouponswap.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from quantlib.types cimport Natural, Rate, Real
from quantlib.handle cimport shared_ptr
from quantlib.time._calendar cimport BusinessDayConvention, Calendar
from quantlib.time._date cimport Date
from quantlib.time._daycounter cimport DayCounter
from quantlib.indexes._ibor_index cimport IborIndex
from .swap cimport Type
from ._swap cimport Swap

cdef extern from "ql/instruments/zerocouponswap.hpp" namespace "QuantLib" nogil:
cdef cppclass ZeroCouponSwap(Swap):
ZeroCouponSwap(Type type,
Real baseNominal,
const Date& startDate,
const Date& maturityDate,
Real fixedPayment,
shared_ptr[IborIndex] iborIndex,
const Calendar& paymentCalendar,
BusinessDayConvention paymentConvention,# = Following,
Natural paymentDelay) # = 0

ZeroCouponSwap(Type type,
Real baseNominal,
const Date& startDate,
const Date& maturityDate,
Rate fixedRate,
const DayCounter& fixedDayCounter,
shared_ptr[IborIndex] iborIndex,
const Calendar& paymentCalendar,
BusinessDayConvention paymentConvention,# = Following,
Natural paymentDelay) # = 0)
2 changes: 2 additions & 0 deletions quantlib/instruments/api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .assetswap import AssetSwap
from .bonds import FixedRateBond, ZeroCouponBond, FloatingRateBond
from .bondforward import BondForward
from .credit_default_swap import CreditDefaultSwap, PricingModel
Expand All @@ -15,3 +16,4 @@
from .overnightindexfuture import OvernightIndexFuture
from .overnightindexedswap import OvernightIndexedSwap
from .make_ois import MakeOIS
from .zerocouponswap import ZeroCouponSwap
4 changes: 4 additions & 0 deletions quantlib/instruments/assetswap.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .swap cimport Swap

cdef class AssetSwap(Swap):
pass
69 changes: 69 additions & 0 deletions quantlib/instruments/assetswap.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from cython.operator cimport dereference as deref
from libcpp cimport bool
from quantlib.types cimport Real, Spread
from quantlib.handle cimport shared_ptr, static_pointer_cast
from quantlib.indexes.ibor_index cimport IborIndex
from quantlib.indexes cimport _ibor_index as _ib
from quantlib.time.schedule cimport Schedule
from quantlib.time.date cimport Date
from quantlib.time.daycounter cimport DayCounter
from quantlib.utilities.null cimport Null
from quantlib.cashflow cimport Leg
from quantlib._cashflow cimport Leg as QlLeg
from . cimport _assetswap as _asw
from .bond cimport Bond
from ._bond cimport Bond as QlBond

cdef class AssetSwap(Swap):
def __init__(
self, bool pay_bond_coupon, Bond bond, Real bond_clean_price,
IborIndex ibor_index, Spread spread, Schedule float_schedule=Schedule.__new__(Schedule),
DayCounter floating_day_counter=DayCounter(), bool par_asset_swap=True,
Real gearing=1.0, Real non_par_repayment=Null[Real](), Date deal_maturity=Date()
):
self._thisptr.reset(
new _asw.AssetSwap(pay_bond_coupon,
static_pointer_cast[QlBond](bond._thisptr),
bond_clean_price,
static_pointer_cast[_ib.IborIndex](ibor_index._thisptr),
spread,
float_schedule._thisptr,
deref(floating_day_counter._thisptr),
par_asset_swap,
gearing,
non_par_repayment,
deal_maturity._thisptr
)
)

@property
def fair_spread(self):
return (<_asw.AssetSwap*>self._thisptr.get()).fairSpread()

@property
def floating_leg_BPS(self):
return (<_asw.AssetSwap*>self._thisptr.get()).floatingLegBPS()

@property
def floating_leg_NPV(self):
return (<_asw.AssetSwap*>self._thisptr.get()).floatingLegNPV()

@property
def fair_clean_price(self):
return (<_asw.AssetSwap*>self._thisptr.get()).fairCleanPrice()

@property
def fair_non_par_repayment(self):
return (<_asw.AssetSwap*>self._thisptr.get()).fairNonParRepayment()

@property
def bond_leg(self):
cdef Leg leg = Leg.__new__(Leg)
leg._thisptr = (<_asw.AssetSwap*>self._thisptr.get()).bondLeg()
return leg

@property
def floating_leg(self):
cdef Leg leg = Leg.__new__(Leg)
leg._thisptr = (<_asw.AssetSwap*>self._thisptr.get()).floatingLeg()
return leg
4 changes: 4 additions & 0 deletions quantlib/instruments/zerocouponswap.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .swap cimport Swap, Type

cdef class ZeroCouponSwap(Swap):
pass
24 changes: 24 additions & 0 deletions quantlib/instruments/zerocouponswap.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from quantlib.types cimport Real, Natural
from quantlib.handle cimport static_pointer_cast
from quantlib.time.date cimport Date
from quantlib.time.calendar cimport Calendar
from quantlib.time.businessdayconvention cimport BusinessDayConvention, Following
from quantlib.indexes.ibor_index cimport IborIndex
from quantlib.indexes cimport _ibor_index as _ib
from . cimport _zerocouponswap as _zcs

cdef class ZeroCouponSwap(Swap):
def __init__(self, Type type, Real nominal, Date start_date, Date maturity, Real fixed_payment, IborIndex ibor_index, Calendar payment_calendar, BusinessDayConvention payment_convention=Following, Natural payment_delay=0):
self._thisptr.reset(
new _zcs.ZeroCouponSwap(
type,
nominal,
start_date._thisptr,
maturity._thisptr,
fixed_payment,
static_pointer_cast[_ib.IborIndex](ibor_index._thisptr),
payment_calendar._thisptr,
payment_convention,
payment_delay
)
)
10 changes: 6 additions & 4 deletions test/test_asian.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from quantlib.quotes import SimpleQuote
from quantlib.termstructures.volatility.api import BlackConstantVol

import contextlib
import unittest

from .utilities import flat_rate
Expand All @@ -42,9 +43,11 @@ class AsianOptionTestCase(unittest.TestCase):
"""

def setUp(self):

self.settings = Settings()

stack = contextlib.ExitStack()
self.settings = stack.enter_context(Settings())
self.addCleanup(stack.close)

self.calendar = NullCalendar()

self.today = Date(6, June, 2021)
Expand Down Expand Up @@ -96,10 +99,9 @@ def setUp(self):

self.payoff = PlainVanillaPayoff(self.option_type, self.strike)


def test_analytic_cont_geom_av_price(self):
"""
"Testing analytic continuous geometric average-price Asians...")
Testing analytic continuous geometric average-price Asians

data from "Option Pricing Formulas", Haug, pag.96-97
"""
Expand Down
59 changes: 59 additions & 0 deletions test/test_assetswap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import unittest

from quantlib.time.api import Date, Period, Annual, TARGET, Unadjusted, Schedule, DateGeneration, Actual365Fixed, Semiannual, ActualActual, Following
from quantlib.instruments.api import FixedRateBond, AssetSwap
from quantlib.pricingengines.api import DiscountingBondEngine, DiscountingSwapEngine
from quantlib.termstructures.yields.api import HandleYieldTermStructure
from quantlib.indexes.api import Euribor
from .utilities import flat_rate

class TestMarketASWSpread(unittest.TestCase):
def setUp(self):
self.term_structure = HandleYieldTermStructure()
self.today = Date(24, 4, 2007)
self.ibor_index = Euribor(Period(Semiannual), self.term_structure)
self.term_structure.link_to(flat_rate(0.05, Actual365Fixed(), self.today))
self.spread = 0.0
self.face_amount = 100.0
self.settlement_days = 2

def test_market_vs_par_asset_swap(self):
"""Testing relationship between market asset swap and par asset swap..."""

pay_fixed_rate = True
par_asset_swap = True
mkt_asset_swap = False
fixed_bond_schedule1 = Schedule.from_rule(Date(4, 1, 2005),
Date(4, 1, 2037),
Period(Annual),
TARGET(),
Unadjusted, Unadjusted,
DateGeneration.Backward, False)
fixed_bond1 = FixedRateBond(self.settlement_days, self.face_amount, fixed_bond_schedule1,
[0.04], ActualActual(ActualActual.ISDA), Following,
100.0, Date(4, 1, 2005))
bond_engine = DiscountingBondEngine(self.term_structure)
swap_engine = DiscountingSwapEngine(self.term_structure)
fixed_bond1.set_pricing_engine(bond_engine)
fixed_bond_mkt_price1 = 89.22 # market price observed on June 7th 2007
fixed_bond_mkt_full_price1 = fixed_bond_mkt_price1 + fixed_bond1.accrued_amount()
fixed_bond_par_asset_swap1 = AssetSwap(pay_fixed_rate,
fixed_bond1, fixed_bond_mkt_price1,
self.ibor_index,
self.spread,
floating_day_counter=self.ibor_index.day_counter,
par_asset_swap=True)
fixed_bond_par_asset_swap1.set_pricing_engine(swap_engine)
fixed_bond_par_asset_swap_spread1 = fixed_bond_par_asset_swap1.fair_spread
fixed_bond_mkt_asset_swap1 = AssetSwap(pay_fixed_rate,
fixed_bond1,
fixed_bond_mkt_price1,
self.ibor_index,
self.spread,
floating_day_counter=self.ibor_index.day_counter,
par_asset_swap=False)
fixed_bond_mkt_asset_swap1.set_pricing_engine(swap_engine)
fixed_bond_mkt_asset_swap_spread1 = fixed_bond_mkt_asset_swap1.fair_spread

self.assertAlmostEqual(fixed_bond_mkt_asset_swap_spread1,
100 * fixed_bond_par_asset_swap_spread1 / fixed_bond_mkt_full_price1)