c++ - Quantlib 求解器未产生与 BondFunctions::yield 相同的到期 yield

标签 c++ yield solver quantlib

我想充分了解 Quantlib 的求解器如何在给定预测期限结构和贴现期限结构的情况下计算 float 利率债券的 Z 价差。为此,我首先创建了一个简单的辅助类来计算债券的到期 yield ,我将使用它与求解器(我选择 Brent)一起将 yield 计算与 BondFunctions::yield 进行比较。尽管如此,对于三个样本债券,我得到了两个不同的结果,但我不明白为什么。

首先,我创建了一个辅助类来使用 quantlib 的求解器以数字方式计算债券的到期 yield

class ytmsolver{
private:
    const Real obsPrice;
    const Bond& bondObject;
    const Date& date;
    DayCounter dayCounter;
    Compounding compounding;
    Frequency frequency;
public:
    //constructor
    ytmsolver(const Bond &bond, Real &price, Date &settlementDate, DayCounter& dc, Compounding& comp, 
            Frequency& freq):bondObject(bond),obsPrice(price),date(settlementDate), dayCounter(dc),
            compounding(comp),frequency(freq){};

    //overloaded operator to be used in the solver
    Real operator()(const Rate& rate)const{
        return (bondObject.cleanPrice(rate,dayCounter,compounding,frequency)-obsPrice);
    }

};

然后我创建了一个 float 利率债券工厂,该工厂创建了一个带有索引的 float 利率、一个预测期限结构(为了计算简单起见假设是固定的)和一个定价引擎。

FloatingRateBond flatTermStructureFloaterFactory(Natural indexTenor, Frequency freq, Date tradeDate,
    Date settlementDate,Natural settlementDays, Real faceAmount, const Schedule &schedule, 
    const Calendar& calendar,const Real &currentLiborFixing,const Real& lastResetDateLiborFixing, 
    const DayCounter &accrualDayCounter, 
    BusinessDayConvention paymentConvention=Following, Natural fixingDays=Null< Natural >(), 
    const std::vector< Real > &gearings=std::vector< Real >(1, 1.0), 
    const std::vector< Spread > &spreads=std::vector< Spread >(1, 0.0), 
    const std::vector< Rate > &caps=std::vector< Rate >(), 
    const std::vector< Rate > &floors=std::vector< Rate >(), 
    bool inArrears=false, Real redemption=100.0, const Date &issueDate=Date()){



//***********Term structure declaration***********  

//term structure for the cash flows using a libor index
RelinkableHandle<YieldTermStructure> liborTermStructure;

//Libor index which is tied to the Frequency of payments or index tenor 
boost::shared_ptr<IborIndex> libor(new USDLibor(Period(indexTenor,Months),liborTermStructure)); 

//term structure to forecast rest of cash flows
boost::shared_ptr<YieldTermStructure> flatforecast(
        new FlatForward(settlementDate, currentLiborFixing, accrualDayCounter, Simple, freq));
    liborTermStructure.linkTo(flatforecast);

//Relinkable handle to assign to the price engine.
RelinkableHandle<YieldTermStructure> discountingTermStructure;

//***********Bond object creation***********
FloatingRateBond floatingRateBondInstance(settlementDays, faceAmount,
                      schedule, libor, accrualDayCounter,
                      paymentConvention, fixingDays,
                      // gearings
                      gearings,
                      // spreads
                      spreads); 

//*********Finds the last reset date****************
Date lastResetDate;
Leg cashflows=floatingRateBondInstance.cashflows();
/*
Finds the last reset date by browsing through the cashflow dates and offsetting them by
the number of fixing days and a provided calendar. 
(ONLY WORKS WITH BONDS WITH THE SAME INDEX AS PERIODICITY)

If this date is provided by the flat file then this search is completely unnecessary 
*/
 for (Size i=0; i<cashflows.size()-1; i++) {
        //Takes the lastResetDate to be the las ocurred date prior the the tradeDate
        if ((cashflows[i]->hasOccurred(tradeDate, true))) {
            lastResetDate=calendar.advance(cashflows[i]->date(),-fixingDays, Days,paymentConvention); 
            //cout<<lastResetDate<<endl;    //used to print the dates as a debug method.            
        }
    }

    cout<<"lastResetDate: "<<lastResetDate<<endl; //prints it to ensure that its correct.


//*********Adds the previous libor rate associated to the last reset date*************
libor->addFixing(lastResetDate, lastResetDateLiborFixing);  //last reset date minus fixing days 


//***********Bond Engine declaration*********** 
    boost::shared_ptr<PricingEngine> bondEngine(new DiscountingBondEngine (discountingTermStructure));
    floatingRateBondInstance.setPricingEngine(bondEngine);  //setting the pricing engine for the bond 

return floatingRateBondInstance;    

在此之后,我创建了一个简单的函数,它调用 Quantlib 的债券函数并计算债券的 yield (我将其用作计算给定 float 利率债券 yield 的一种方法。

Real priceToYieldFlatTermStructure(const Bond& bond,
                          Real cleanPrice,
                          const DayCounter& dayCounter,
                          Compounding compounding,
                          Frequency frequency,
                          Date settlement,
                          Real accuracy=1e-50,
                          Size maxIterations=10000){

//Calls the bond function yield which takes a bond, a clean price, a day count, a compounding,
//a frequency of payments, a settlement date, a degree of accuracy and the number of max iterations to
//reach the yield.
Real irr=BondFunctions::yield(bond,cleanPrice,dayCounter,compounding,frequency,
        settlement,accuracy,maxIterations);
return irr;

然后我尝试使用 Quantlib 的求解器在给定干净价格的情况下获得这些债券的 yield ,我使用以下代码得到了不同的结果:

int main(){
try {
    Brent solver;
    Real accuracy=1e-30, guess=0.00, min=-1.0, max=0.5;

    cout<<"*******************************************"<<endl;
    cout<<"Bond # 1: US4042Q0HC65"<<endl;
    cout<<"*******************************************"<<endl;
    //***********Input declaration***********
    Natural settlementDays = 3;
    Natural fixingdays=2;
    Natural indexTenor=6;
    Date tradeDate(02,Mar,2015);
    Date issueDate(9,Aug,2006);
    Date maturityDate(22,Aug,2016);
    Real resetMargin=0.016;
    Real indexMultiplier=1.0;
    Frequency frequency=Semiannual;
    Calendar holidayCalendar=UnitedStates(UnitedStates::NYSE);
    BusinessDayConvention businessDayConvention= BusinessDayConvention(ModifiedFollowing);
    DayCounter dayCounter=Actual360();
    Real lastResetDateLiborFixing=0.003853;
    Real currentLiborFixing=0.003842;
    Real redemption=100;
    string settlementcode="BDY"; //internal settlementcode
    string settlementvalue="3";  //internal settlementvalue
    Date settlementDate=getSettlementDate(tradeDate,holidayCalendar,settlementcode,settlementvalue); //function call to get the settlement date (this is working properly)
    cout<<"settlementDate :"<<settlementDate<<endl;
    Compounding compounding=Compounded;
    Real faceAmount = redemption;
    Real obsprice=101.431;  
    Schedule schedule(issueDate, maturityDate, Period(frequency),
                  holidayCalendar, businessDayConvention, businessDayConvention,
                  DateGeneration::Backward, true);


    //***********Bond creation to be priced***********  
    FloatingRateBond floatingRateBondInstance1=flatTermStructureFloaterFactory(indexTenor,frequency,tradeDate,settlementDate,
            settlementDays,faceAmount,schedule,holidayCalendar,currentLiborFixing,lastResetDateLiborFixing,
            dayCounter,businessDayConvention,fixingdays,std::vector<Real>(1, indexMultiplier),
            std::vector<Rate>(1, resetMargin));

    Real ytm=priceToYieldFlatTermStructure(floatingRateBondInstance1,obsprice,dayCounter,compounding,frequency,settlementDate); 

    //***********Bond pricing, yield and discount marging computation***********    
    cout<<"Clean price: "<<floatingRateBondInstance1.cleanPrice(ytm,dayCounter,compounding,frequency,settlementDate)<<endl;
    cout<<"Dirty price: "<<floatingRateBondInstance1.dirtyPrice(ytm,dayCounter,compounding,frequency,settlementDate)<<endl;
    cout<<"Accrued interest: "<<floatingRateBondInstance1.accruedAmount(settlementDate)<<endl;

    cout<<"Yield: "<<ytm*100<<"%"<<endl;
    cout<<"Discount Margin: "<<(ytm-currentLiborFixing)*100<<"%"<<endl;

    //***************solver testing***************
    Real irr=solver.solve(ytmsolver(floatingRateBondInstance1,obsprice,settlementDate,dayCounter,
    compounding,frequency),accuracy,guess,min,max);

    cout<<"irr: "<<irr*100<<"%"<<endl;
    cout<<"*******************************************"<<endl;
    cout<<"Bond # 2: US4042Q0HB82"<<endl;
    cout<<"*******************************************"<<endl;

    //***********Input declaration***********
    indexTenor=6;
    issueDate=Date(27,Jul,2006);
    maturityDate=Date(20,Jul,2016);
    resetMargin=0.0151;
    indexMultiplier=1.0;
    frequency=Semiannual;
    holidayCalendar=TARGET();
    //holidayCalendar=UnitedStates(UnitedStates::NYSE); //not counting martin luther king day, jan 15,15 as last reset date
    businessDayConvention=BusinessDayConvention(ModifiedFollowing);
    dayCounter=Actual360();
    lastResetDateLiborFixing=0.003549;
    currentLiborFixing=0.003842;
    redemption=100;
    settlementcode="BDY"; //internal settlement code
    settlementvalue="3";  //internal settlement value
    settlementDate=getSettlementDate(tradeDate,holidayCalendar,settlementcode,settlementvalue); //function call to get the settlement date (this is working properly)
    cout<<"settlementDate :"<<settlementDate<<endl;
    compounding=Compounded;
    faceAmount = redemption;
    obsprice=100.429; 
    schedule=Schedule(issueDate, maturityDate, Period(frequency),
                  holidayCalendar, businessDayConvention, businessDayConvention,
                  DateGeneration::Backward, true);


    //***********Bond creation to be priced***********  

    FloatingRateBond floatingRateBondInstance2=flatTermStructureFloaterFactory(indexTenor,frequency,tradeDate,settlementDate,
            settlementDays,faceAmount,schedule,holidayCalendar,currentLiborFixing,lastResetDateLiborFixing,
            dayCounter,businessDayConvention,fixingdays,std::vector<Real>(1, indexMultiplier),
            std::vector<Rate>(1, resetMargin));

    ytm=priceToYieldFlatTermStructure(floatingRateBondInstance2,obsprice,dayCounter,compounding,frequency,settlementDate);  

    //***********Bond pricing, yield and discount marging computation***********    
    cout<<"Clean price: "<<floatingRateBondInstance2.cleanPrice(ytm,dayCounter,compounding,frequency,settlementDate)<<endl;
    cout<<"Dirty price: "<<floatingRateBondInstance2.dirtyPrice(ytm,dayCounter,compounding,frequency,settlementDate)<<endl;
    cout<<"Accrued interest: "<<floatingRateBondInstance2.accruedAmount(settlementDate)<<endl;

    cout<<"Yield: "<<ytm*100<<"%"<<endl;
    cout<<"Discount Margin: "<<(ytm-currentLiborFixing)*100<<"%"<<endl;


    //***************solver testing***************
    irr=solver.solve(ytmsolver(floatingRateBondInstance2,obsprice,settlementDate,dayCounter,
    compounding,frequency),accuracy,guess,min,max);

    cout<<"irr: "<<irr*100<<"%"<<endl;

    cout<<"*******************************************"<<endl;
    cout<<"Bond # 3: US59022CCT80"<<endl;
    cout<<"*******************************************"<<endl;
    //***********Input declaration***********
    indexTenor=3;
    tradeDate=Date(10,Jun,2015);
    issueDate=Date(02,May,2007);
    maturityDate=Date(02,May,2017);
    resetMargin=0.0055;
    indexMultiplier=1.0;
    frequency=Quarterly;
    holidayCalendar=UnitedStates(UnitedStates::NYSE); //not counting martin luther kind day, jan 15,15 as last reset date
    businessDayConvention=BusinessDayConvention(ModifiedFollowing);
    dayCounter=Actual360();
    lastResetDateLiborFixing=0.0027875;
    currentLiborFixing=0.0028785;
    redemption=100;
    settlementcode="BDY";  //internal settlement code
    settlementvalue="3";   //internal settlement value
    settlementDate=getSettlementDate(tradeDate,holidayCalendar,settlementcode,settlementvalue); //function call to get the settlement date (this is working properly)
    cout<<"settlementDate :"<<settlementDate<<endl;
    compounding=Compounded;
    faceAmount = redemption;
    obsprice=99.794;
    schedule=Schedule(issueDate, maturityDate, Period(frequency),
                  holidayCalendar, businessDayConvention, businessDayConvention,
                  DateGeneration::Backward, true);


    //***********Bond pricing, yield and discount marging computation***********    
    FloatingRateBond floatingRateBondInstance3=flatTermStructureFloaterFactory(indexTenor,frequency,tradeDate,settlementDate,
            settlementDays,faceAmount,schedule,holidayCalendar,currentLiborFixing,lastResetDateLiborFixing,
            dayCounter,businessDayConvention,fixingdays,std::vector<Real>(1, indexMultiplier),
            std::vector<Rate>(1, resetMargin));

    ytm=priceToYieldFlatTermStructure(floatingRateBondInstance3,obsprice,dayCounter,compounding,frequency,settlementDate);  
    //***********Bond pricing, yield and discount marging computation***********    
    cout<<"Clean price: "<<floatingRateBondInstance3.cleanPrice(ytm,dayCounter,compounding,frequency,settlementDate)<<endl;
    cout<<"Dirty price: "<<floatingRateBondInstance3.dirtyPrice(ytm,dayCounter,compounding,frequency,settlementDate)<<endl;
    cout<<"Accrued interest: "<<floatingRateBondInstance3.accruedAmount(settlementDate)<<endl;

    cout<<"Yield: "<<ytm*100<<"%"<<endl;
    cout<<"Discount Margin: "<<(ytm-currentLiborFixing)*100<<"%"<<endl;

    //***************solver testing***************
    irr=solver.solve(ytmsolver(floatingRateBondInstance3,obsprice,settlementDate,dayCounter,
    compounding,frequency),accuracy,guess,min,max);

    cout<<"irr: "<<irr*100<<"%"<<endl;

    return 0;

} catch (exception& e) {
    cerr << e.what() << endl;
    return 1;
} catch (...) {
    cerr << "unknown error" << endl;
    return 1;
}

最后我得到以下结果:


键#1:US4042Q0HC65

结算日期:2015年3月5日 最后重置日期:2015 年 2 月 19 日 净价:101.431 脏价:101.486 应计利息:0.0551472 产率:1.01286% 折扣 margin :0.628665% 内部 yield :0.72216%


债券#2:US4042Q0HB82

结算日期:2015年3月5日 最后重置日期:2015 年 1 月 16 日 净价:100.429 脏价:100.657 应计利息:0.227932 yield :1.57325% 折扣 margin :1.18905% 内部 yield :1.47977%


债券#3:US59022CCT80

结算日期:2015年6月15日 最后重置日期:2015 年 4 月 30 日 净价:99.794 脏价:99.8907 应计利息:0.0966875 产率:0.945517% 折扣 margin :0.657667% 内部 yield :0.949541%

我这里做错了什么?我不明白为什么求解器没有返回与 yield 的债券函数相同的数字。关于为什么或我在这里做错了什么有什么想法吗?

最佳答案

我假设返回 QuantLib BondFunctions::yield 函数的 yield 是正确的,因为当您将它传递给债券的 cleanPrice 方法时,您取回您用作输入的观察到的价格。

这让我们猜测您的函数中有什么问题。通过查看您的 ytmsolver 类,我注意到您没有像在 main 中那样将结算日期传递给债券对象的 cleanPrice 方法 重新定价代码时。

如果缺少结算日期,该方法会假定它是今天的结算日期,即从今天算起的三个工作日。这明显晚于您想要的和您在 main 函数中输出的结算日期,因此您得到错误的 yield 。一旦您将结算日期传递给 cleanPrice,求解器就会返回预期值。

关于c++ - Quantlib 求解器未产生与 BondFunctions::yield 相同的到期 yield ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31193013/

相关文章:

c++ - 关于摆脱样板代码的想法

c++ - 强制成员函数实现的开销

javascript - 如何从yield* 中获取当前字符串的长度

c - C 中的回溯数独求解器

c++ - 有什么有效的方法可以动态更改 boost 中的 compress_matrix 吗?

javascript - JS 中的数独解算器

c++ - 将 unsigned int 与 std::string::size_type 进行比较是否安全

c++ - 使用 init.d 将 C++ 程序作为服务运行

python - yield as assignment 有什么作用? myVar = (产量)

Scala - try-catch inside for loop with yield