我在协调现金流的到期收益率计算时遇到问题。
在代码中,我采用日期和金额向量来创建 BondLeg,并为其分配 dirty_value。
我使用 ql.Cashflows.yieldRate 来计算年复利和连续复利到期收益率。
我使用与计算收益率相同的日数基础生成日期的yearFraction。
然后,我使用 PYOMO 非线性优化求解器 (ipopt) 使用年份分数、现金流量和 dirty_value 来求解到期收益率。
PYOMO 年复利收益率与 Quantlib 收益率相差在 1bp 以内。然而,Quantlib 连续复合收益率比 PYOMO 收益率高 13bp。
我的 Jupyter Notebook 中的代码如下:
import numpy as np
import QuantLib as ql
import pyomo.environ as pyo
from pyomo.opt import SolverFactory
ql.Settings.instance().evaluationDate = ql.Date(30,6,2023)
dates = [ql.Date(14,11,2023),
ql.Date(14,11,2024),
ql.Date(14,11,2025),
ql.Date(16,11,2026),
ql.Date(15,11,2027),
ql.Date(14,11,2028),
ql.Date(14,11,2029),
ql.Date(14,11,2030),
ql.Date(14,11,2031),
ql.Date(15,11,2032)]
amounts = [84070,
84070,
84070,
84537.05555555558,
65131.50532953604,
65131.50532953604,
65312.930135468014,
65312.930135468014,
65312.930135468014,
1065494.3549413998]
dirty_val = 1050876.388888889
BondLeg = ql.Leg([ql.SimpleCashFlow(amt, dt) for dt,amt in zip(dates,amounts)])
ql_ra = ql.CashFlows.yieldRate(BondLeg, dirty_val, ql.Actual365Fixed(), ql.Compounded, ql.Annual, True)
ql_rc = ql.CashFlows.yieldRate(BondLeg, dirty_val, ql.Actual365Fixed(), ql.Compounded, ql.Continuous, True)
year_fracs = [ql.Actual365Fixed().yearFraction(ql.Date(30,6,2023),dt) for dt in dates]
periods = np.array([frac for frac in year_fracs])
p_int = np.int_(periods)
p_frac = periods - p_int
amount_vec = np.array(amounts)
model_ra = pyo.ConcreteModel()
# Decision Variables
model_ra.ra = pyo.Var(domain = pyo.NonNegativeReals, initialize=0.05)
ra = model_ra.ra
# Objective Function
def ra_objective_rule(model_ra):
return (amount_vec.dot(1/(((1+ra)**p_int)* (1+(ra*p_frac)))) - dirty_val)**2
model_ra.objf = pyo.Objective(rule=ra_objective_rule, sense=pyo.minimize)
# Constraints
Solver = SolverFactory('ipopt')
results_ra = Solver.solve(model_ra)
print(results_ra)
print(f'Objective Function = {model_ra.objf()}')
print(f'ra = {ra()}')
model_rc = pyo.ConcreteModel()
# Decision Variables
model_rc.rc = pyo.Var(domain = pyo.NonNegativeReals, initialize=0.05)
rc = model_rc.rc
# Objective Function
def rc_objective_rule(model_rc):
return (amount_vec.dot(np.array([pyo.exp(-p*rc) for p in periods])) - dirty_val)**2
model_rc.objf = pyo.Objective(rule=rc_objective_rule, sense=pyo.minimize)
# Constraints
Solver = SolverFactory('ipopt')
results_rc = Solver.solve(model_rc)
print(results_rc)
print(f'Objective Function = {model_rc.objf()}')
print(f'rc = {rc()}')
print(f'QuantLib - Annually Compounded Rate : {ql_ra:.4%}')
print(f'QuantLib - Continuously Compounded Rate : {ql_rc:.4%}')
print(f'PYOMO - Annually Compounded Rate : {ra.value:.4%}')
print(f'PYOMO - Continuously Compounded Rate : {rc.value:.4%}')
QuantLib - 年复合利率:7.3623% QuantLib - 连续复利率:7.2315%
PYOMO - 年复合利率:7.3526% PYOMO - 连续复利率:7.1039%
[从 QuantLib 邮件列表复制答案,以防有人通过搜索到达这里]
上面代码中的调用,
ql_rc = ql.CashFlows.yieldRate(BondLeg, dirty_val, ql.Actual365Fixed(), ql.Compounded, ql.Continuous, True)
应该是:
ql_rc = ql.CashFlows.yieldRate(BondLeg, dirty_val, ql.Actual365Fixed(), ql.Continuous, ql.NoFrequency, True)
也就是说,
ql.Continuous
不是与 ql.Compounded
关联的频率,而是其本身的复合方法。第二次调用给出了您期望的结果。
不幸的是,SWIG 将 C++ 枚举导出为没有特定类型的数值常量,因此 Python 无法警告不匹配情况。第一个调用将
ql.Continuous
的数值 2 解释为半年复利频率。