在 Quantlib 中為 FixedRateBond 定價:收益率與 TermStructure
我正在嘗試使用兩種方法在 QuantLib 中為一個簡單的美國國庫定價。第一個方法呼叫 FixedRatebond.dirtyPrice(…),傳入 YTM 和其他參數。
第二種方法涉及使用與方法 #1 相同的 YTM 和參數建構 FlatForward YieldTermStructure。
這兩種方法給我的髒價格略有不同,我希望它們是相同的。誰能解釋我做錯了什麼,或者解釋為什麼使用這兩種方法的骯髒價格應該不同?
這是一些範常式式碼(完全披露:此程式碼是從這裡抄襲的):
try { // date set up Calendar calendar = TARGET(); Date settlementDate(28, January, 2011); // the settlement date must be a business day settlementDate = calendar.adjust(settlementDate); // Evaluation date Integer fixingDays = 1; Natural settlementDays = 1; Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days); Settings::instance().evaluationDate() = todaysDate; // bond set up Real faceAmount = 100.0; Real redemption = 100.0; Date issueDate(27, January, 2011); Date maturity(31, August, 2020); Real couponRate = 0.03625; Real yield = 0.034921; RelinkableHandle<YieldTermStructure> discountingTermStructure; boost::shared_ptr<YieldTermStructure> flatTermStructure( new FlatForward( settlementDate, yield, ActualActual(ActualActual::Bond), Compounding::Compounded, Semiannual)); discountingTermStructure.linkTo(flatTermStructure); // Pricing engine boost::shared_ptr<PricingEngine> bondEngine( new DiscountingBondEngine(discountingTermStructure)); // Rate Schedule fixedBondSchedule( issueDate, maturity, Period(Semiannual), UnitedStates(UnitedStates::GovernmentBond), BusinessDayConvention::Unadjusted, BusinessDayConvention::Unadjusted, DateGeneration::Rule::Backward, false); FixedRateBond fixedRateBond( settlementDays, faceAmount, fixedBondSchedule, std::vector<Rate>(1, couponRate), ActualActual(ActualActual::Bond), BusinessDayConvention::Unadjusted, redemption, issueDate); //Calculate pricing without term structure Real cp = fixedRateBond.cleanPrice(yield, fixedRateBond.dayCounter(), Compounding::Compounded, Semiannual); Real dp = fixedRateBond.dirtyPrice(yield, fixedRateBond.dayCounter(), Compounding::Compounded, Semiannual); Rate ytm = fixedRateBond.yield(cp, fixedRateBond.dayCounter(), Compounding::Compounded, Semiannual); Real accrued = fixedRateBond.accruedAmount(); fixedRateBond.setPricingEngine(bondEngine); // write column headings Size widths[] = { 18, 15, 15}; std::cout << std::setw(widths[0]) << " " << std::setw(widths[1]) << "Without TS" << std::setw(widths[2]) << "With TS" << std::endl; Size width = widths[0] + widths[1] + widths[2]; std::string rule(width, '-'), dblrule(width, '='); std::cout << rule << std::endl; std::cout << std::setw(widths[0]) << "Clean Price" << std::setw(widths[1]) << std::setprecision (8) << cp << std::setw(widths[2]) << std::setprecision (8) << fixedRateBond.cleanPrice() << std::endl; std::cout << std::setw(widths[0]) << "Dirty Price" << std::setw(widths[1]) << std::setprecision (8) << dp << std::setw(widths[2]) << std::setprecision (8) << fixedRateBond.dirtyPrice() << std::endl; std::cout << std::setw(widths[0]) << "Accrued" << std::setw(widths[1]) << std::setprecision (8) << accrued << std::setw(widths[2]) << std::setprecision (8) << fixedRateBond.accruedAmount() << std::endl; return 0; } catch (std::exception& e) { std::cerr << e.what() << std::endl; return 1; } catch (...) { std::cerr << "unknown error" << std::endl; return 1; }
}
計日慣例。你不能和他們一起生活,你不能沒有他們。
價格不同的原因是定價引擎無法正確計算第一張優惠券的折扣時間,因此適用於優惠券金額的折扣因子略有不同。請坐,這需要一些解釋。
最終,這兩種方法都通過將優惠券金額相加來計算臟價,每個優惠券金額都根據其支付日期進行折扣。它們的不同之處在於折扣因子的計算。
正如可以預期的那樣,採用收益率和相應約定的方法通過組合收益率來計算折扣。折扣 $ D_1 $ 第一張票券是通過在票券的剩餘期限內累積收益而獲得的;折扣 $ D_2 $ 對於第二個息票,通過在其生命週期內累積並將其與之前的結果相乘;以此類推,直到最後一張優惠券。問題是,為了根據通過的 act/act(b) 天數約定正確計算累積時間,您還需要一個由優惠券頻率給出的參考期,在本例中為 6 個月。因此,例如,第一張優惠券的累積時間(從 2011 年 1 月 28 日到 2011 年 2 月 28 日)必須計算為:
dayCounter.yearFraction(Date(28,January,2011), Date(28,February,2011), Date(28,August,2010), Date(28,February,2011));
返回
0.08423913043478261
(第三個和第四個日期是 6 個月參考期的開始和結束日期)。如果優惠券是年度的,結果會略有不同:上面的參考開始日期是 2010 年 2 月 28 日,返回0.08493150684931507
. 取收益方法的重載dirtyPrice
使用票券資訊選擇正確的參考期,並根據上面的第一個值計算第一個折扣因子。結果是0.997087920498809
。相反,依賴定價引擎的方法使用收益率期限結構
t
,並且只要求t.discount(coupon.date())
提供每張優惠券:該discount
方法的介面不允許傳遞有關參考期的任何額外資訊。在內部,期限結構能做的最好的事情就是計算dayCounter.yearFraction(Date(28,January,2011), Date(28,February,2011));
預設情況下,將通過的相同兩個日期作為參考期的開始和結束,使上述等價於
dayCounter.yearFraction(Date(28,January,2011), Date(28,February,2011), Date(28,January,2011), Date(28,February,2011));
這個返回
0.08333333333333333
,第一張優惠券對應的折扣係數是0.9971191880350325
。這種差異解釋了價格的變化(其他息票無效:它們都是正常的 6 個月息票,並且對它們而言,參考期等於息票的壽命,使得 T
0.5
在兩種情況下都等於)。返回的價格dirtyPrice()
是101.08980828425344
; 返回的dirtyPrice(yield, ...)
是101.08663832294855
; 如果我們校正上述不同的折扣因子,我們得到:101.08980828425344 * 0.997087920498809 / 0.9971191880350325 = 101.08663832294862
調和價格。
最後一點:這只發生在那些需要參考期的天數約定中。如果您嘗試使用不支持的程式碼(例如 act/360),這兩種方法將為您提供相同的價格。
更新(2019 年 9 月):
現在可以創建
ActualActual
使用正確參考期間進行計算的日計數器實例。將其實例化為:DayCounter dayCounter = ActualActual(ActualActual::Bond, fixedBondSchedule);
並將其用於貼現曲線和債券。當詢問年份分數時,它將從時間表中檢索正確的參考期並使用它們。這將給出正確的結果。