C++:实现量化bonds 债券测试实例

C++:实现量化bonds 债券测试实例


#include "bonds.hpp"
#include "utilities.hpp"
#include <ql/cashflows/iborcoupon.hpp>
#include <ql/instruments/bonds/fixedratebond.hpp>
#include <ql/instruments/bonds/floatingratebond.hpp>
#include <ql/instruments/bonds/zerocouponbond.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/calendars/unitedkingdom.hpp>
#include <ql/time/calendars/australia.hpp>
#include <ql/time/calendars/brazil.hpp>
#include <ql/time/calendars/southafrica.hpp>
#include <ql/time/calendars/nullcalendar.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/business252.hpp>
#include <ql/indexes/ibor/usdlibor.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/utilities/dataformatters.hpp>
#include <ql/time/schedule.hpp>
#include <ql/cashflows/fixedratecoupon.hpp>
#include <ql/cashflows/simplecashflow.hpp>
#include <ql/cashflows/couponpricer.hpp>
#include <ql/cashflows/cashflows.hpp>
#include <ql/pricingengines/bond/discountingbondengine.hpp>
#include <ql/pricingengines/bond/bondfunctions.hpp>
#include <ql/termstructures/credit/flathazardrate.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/currencies/europe.hpp>
#include <ql/pricingengines/bond/riskybondengine.hpp>

using namespace QuantLib;
using namespace boost::unit_test_framework;

#define ASSERT_CLOSE(name, settlement, calculated, expected, tolerance)  \
    if (std::fabs(calculated-expected) > tolerance) {
      \
    BOOST_ERROR("Failed to reproduce " << name << " at " << settlement \
                << "\n    calculated: " << std::setprecision(8) << calculated \
                << "\n    expected:   " << std::setprecision(8) << expected); \
    }

namespace bonds_test {
   

    struct CommonVars {
   
        // common data
        Calendar calendar;
        Date today;
        Real faceAmount;

        // cleanup
        SavedSettings backup;

        // setup
        CommonVars() {
   
            calendar = TARGET();
            today = calendar.adjust(Date::todaysDate());
            Settings::instance().evaluationDate() = today;
            faceAmount = 1000000.0;
        }
    };

    void checkValue(Real value, Real expectedValue, Real tolerance, const std::string& msg) {
   
        if (std::fabs(value - expectedValue) > tolerance) {
   
            BOOST_ERROR(msg
                        << std::fixed
                        << "\n    calculated: " << value
                        << "\n    expected:   " << expectedValue
                        << "\n    tolerance:  " << tolerance
                        << "\n    error:      " << value - expectedValue);
        }
    }
}


void BondTest::testYield() {
   

    BOOST_TEST_MESSAGE("Testing consistency of bond price/yield calculation...");

    using namespace bonds_test;

    CommonVars vars;

    Real tolerance = 1.0e-7;
    Size maxEvaluations = 100;

    Integer issueMonths[] = {
    -24, -18, -12, -6, 0, 6, 12, 18, 24 };
    Integer lengths[] = {
    3, 5, 10, 15, 20 };
    Natural settlementDays = 3;
    Real coupons[] = {
    0.02, 0.05, 0.08 };
    Frequency frequencies[] = {
    Semiannual, Annual };
    DayCounter bondDayCount = Thirty360(Thirty360::BondBasis);
    BusinessDayConvention accrualConvention = Unadjusted;
    BusinessDayConvention paymentConvention = ModifiedFollowing;
    Real redemption = 100.0;

    Rate yields[] = {
    0.03, 0.04, 0.05, 0.06, 0.07 };
    Compounding compounding[] = {
    Compounded, Continuous };

    for (int issueMonth : issueMonths) {
   
        for (int length : lengths) {
   
            for (Real& coupon : coupons) {
   
                for (auto& frequencie : frequencies) {
   
                    for (auto& n : compounding) {
   

                        Date dated = vars.calendar.advance(vars.today, issueMonth, Months);
                        Date issue = dated;
                        Date maturity = vars.calendar.advance(issue, length, Years);

                        Schedule sch(dated, maturity, Period(frequencie), vars.calendar,
                                     accrualConvention, accrualConvention, DateGeneration::Backward,
                                     false);

                        FixedRateBond bond(settlementDays, vars.faceAmount, sch,
                                           std::vector<Rate>(1, coupon), bondDayCount,
                                           paymentConvention, redemption, issue);

                        for (Real m : yields) {
   

                            Real price =
                                BondFunctions::cleanPrice(bond, m, bondDayCount, n, frequencie);

                            Rate calculated = BondFunctions::yield(
                                bond, price, bondDayCount, n, frequencie, Date(), tolerance,
                                maxEvaluations, 0.05, Bond::Price::Clean);

                            if (std::fabs(m - calculated) > tolerance) {
   
                                // the difference might not matter
                                Real price2 = BondFunctions::cleanPrice(
                                    bond, calculated, bondDayCount, n, frequencie);
                                if (std::fabs(price - price2) / price > tolerance) {
   
                                    BOOST_ERROR("\nyield recalculation failed:"
                                                "\n    issue:     "
                                                << issue << "\n    maturity:  " << maturity
                                                << "\n    coupon:    " << io::rate(coupon)
                                                << "\n    frequency: " << frequencie
                                                << "\n    yield:   " << io::rate(m)
                                                << (n == Compounded ? " compounded" : " continuous")
                                                << std::setprecision(7) << "\n    clean price:   "
                                                << price << "\n    yield': " << io::rate(calculated)
                                                << "\n    clean price': " << price2);
                                }
                            }

                            price = BondFunctions::dirtyPrice(bond, m, bondDayCount, n, frequencie);

                            calculated = BondFunctions::yield(
                                bond, price, bondDayCount, n, frequencie, Date(), tolerance,
                                maxEvaluations, 0.05, Bond::Price::Dirty);

                            if (std::fabs(m - calculated) > tolerance) {
   
                                // the difference might not matter
                                Real price2 = BondFunctions::dirtyPrice(
                                    bond, calculated, bondDayCount, n, frequencie);
                                if (std::fabs(price - price2) / price > tolerance) {
   
                                    BOOST_ERROR("\nyield recalculation failed:"
                                                "\n    issue:     "
                                                << issue << "\n    maturity:  " << maturity
                                                << "\n    coupon:    " << io::rate(coupon)
                                                << "\n    frequency: " << frequencie
                                                << "\n    yield:   " << io::rate(m)
                                                << (n == Compounded ? " compounded" : " continuous")
                                                << std::setprecision(7) << "\n    dirty price:   "
                                                << price << "\n    yield': " << io::rate(calculated)
                                                << "\n    dirty price': " << price2);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

void BondTest::testAtmRate() {
   

    BOOST_TEST_MESSAGE("Testing consistency of bond price/ATM rate calculation...");

    using namespace bonds_test;

    CommonVars vars;

    Real tolerance = 1.0e-7;

    Integer issueMonths[] = {
    -24, -18, -12, -6, 0, 6, 12, 18, 24 };
    Integer lengths[] = {
    3, 5, 10, 15, 20 };
    Natural settlementDays = 3;
    Real coupons[] = {
    0.02, 0.05, 0.08 };
    Frequency frequencies[] = {
    Semiannual, Annual };
    DayCounter bondDayCount = Thirty360(Thirty360::BondBasis);
    BusinessDayConvention accrualConvention = Unadjusted;
    BusinessDayConvention paymentConvention = ModifiedFollowing;
    Real redemption = 100.0;
    Handle<YieldTermStructure> disc(flatRate(vars.today,0.03,Actual360()));
    ext::shared_ptr<PricingEngine> bondEngine(new DiscountingBondEngine(disc));

    for (int issueMonth : issueMonths) {
   
        for (int length : lengths) {
   
            for (Real& coupon : coupons) {
   
                for (auto& frequencie : frequencies) {
   
                    Date dated = vars.calendar.advance(vars.today, issueMonth, Months);
                    Date issue = dated;
                    Date maturity = vars.calendar.advance(issue, length, Years);

                    Schedule sch(dated, maturity, Period(frequencie), vars.calendar,
                                 accrualConvention, accrualConvention, DateGeneration::Backward,
                                 false);

                    FixedRateBond bond(settlementDays, vars.faceAmount, sch,
                                       std::vector<Rate>(1, coupon), bondDayCount,
                                       paymentConvention, redemption, issue);

                    bond.setPricingEngine(bondEngine);
                    Real price = bond.cleanPrice();
                    Rate calculated =
                        BondFunctions::atmRate(bond, **disc, bond.settlementDate(), price);

                    if (std::fabs(coupon - calculated) > tolerance) {
   
                        BOOST_ERROR("\natm rate recalculation failed:"
                                    "\n today:           "
                                    << vars.today << "\n settlement date: " << bond.settlementDate()
                                    << "\n issue:           " << issue << "\n maturity:        "
                                    << maturity << "\n coupon:          " << io::rate(coupon)
                                    << "\n frequency:       " << frequencie
                                    << "\n clean price:     " << price
                                    << "\n dirty price:     " << price + bond.accruedAmount()
                                    << "\n atm rate:        " << io::rate(calculated));
                    }
                }
            }
        }
    }
}

void BondTest::testZspread() {
   

    BOOST_TEST_MESSAGE("Testing consistency of bond price/z-spread calculation...");

    using namespace bonds_test;

    CommonVars vars;

    Real tolerance = 1.0e-7;
    Size maxEvaluations = 100;

    Handle<YieldTermStructure> discountCurve(
                                       flatRate(vars.today,0.03,Actual360()));

    Integer issueMonths[] = {
    -24, -18, -12, -6, 0, 6, 12, 18, 24 };
    Integer lengths[] = {
    3, 5, 10, 15, 20 };
    Natural settlementDays = 3;
    Real coupons[] = {
    0.02, 0.05, 0.08 };
    Frequency frequencies[] = {
    Semiannual, Annual };
    DayCounter bondDayCount = Thirty360(Thirty360::BondBasis);
    BusinessDayConvention accrualConvention = Unadjusted;
    BusinessDayConvention paymentConvention = ModifiedFollowing;
    Real redemption = 100.0;

    Spread spreads[] = {
    -0.01, -0.005, 0.0, 0.005, 0.01 };
    Compounding compounding[] = {
    Compounded, Continuous };

    for (int issueMonth : issueMonths) {
   
        for (int length : lengths) {
   
            for (Real& coupon : coupons) {
   
                for (auto& frequencie : frequencies) {
   
                    for (auto& n : compounding) {
   

                        Date dated = vars.calendar.advance(vars.today, issueMonth, Months);
                        Date issue = dated;
                        Date maturity = vars.calendar.advance(issue, length, Years);

                        Schedule sch(dated, maturity, Period(frequencie), vars.calendar,
                                     accrualConvention, accrualConvention, DateGeneration::Backward,
                                     false);

                        FixedRateBond bond(settlementDays, vars.faceAmount, sch,
                                           std::vector<Rate>(1, coupon), bondDayCount,
                                           paymentConvention, redemption, issue);

                        for (Real spread : spreads) {
   

                            Real price = BondFunctions::cleanPrice(bond, *discountCurve, spread,
                                                                   bondDayCount, n, frequencie);
                            Spread calculated = BondFunctions::zSpread(
                                bond, price, *discountCurve, bondDayCount, n, frequencie, Date(),
                                tolerance, maxEvaluations);

                            if (std::fabs(spread - calculated) > tolerance) {
   
                                // the difference might not matter
                                Real price2 = BondFunctions::cleanPrice(
                                    bond, *discountCurve, calculated, bondDayCount, n, frequencie);
                                if (std::fabs(price - price2) / price > tolerance) {
   
                                    BOOST_ERROR("\nZ-spread recalculation failed:"
                                                "\n    issue:     "
                                                << issue << "\n    maturity:  " << maturity
                                                << "\n    coupon:    " << io::rate(coupon)
                                                << "\n    frequency: " << frequencie
                                                << "\n    Z-spread:  " << io::rate(spread)
                                                << (n == Compounded ? " compounded"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

源代码大师

赏点狗粮吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值