【问题标题】:Calculate bond yield to maturity (YTM) in C++用 C++ 计算债券到期收益率 (YTM)
【发布时间】:2020-02-04 00:17:48
【问题描述】:

我想使用二等分法或割线法计算给定价格的债券到期收益率。我知道网上有 C++ 食谱,但我不知道我自己的代码有什么问题。这两种方法都会创建一个无限循环。当我尝试逐步遍历变量时,迭代解决方案的成熟收益率不断增加到 80% 甚至更高。

#include <iostream>
#include <math.h>

using namespace std;

class bond {
private:
    double principal;
    double coupon;
    double timeToMaturity;

public:
    bond(double principal, double coupon, double timeToMaturity) {
        this->principal = principal;
        this->coupon = coupon;
        this->timeToMaturity = timeToMaturity;
    }

    double getprice(double YTM);
    double getytm(double price);
    double getytmsec(double price);
};

bool isZero(double x)
{
    if (fabs(x) < 1e-10)
        return true;
    return false;
}

double bond::getprice(double YTM) {
    double pvPrincipal;
    double pvCoupon;
    double factor = 0;

    pvPrincipal = this->principal / pow(1 + YTM, this->timeToMaturity);

    for (int i = 0; (this->timeToMaturity - i) > 0; i++) {
        double denom = pow(1 + YTM, this->timeToMaturity - i);
        factor += 1 / denom;
    }

    pvCoupon = this->coupon * factor;

    return pvPrincipal + pvCoupon;
}

// Bisection method
double bond::getytm(double price) {
    double low = 0;
    double high = 1;

    double f0 = getprice(low);
    double f2 = 1;
    double x2 = 0;

    while (!isZero(f2)) {
        x2 = (low + high) / 2;
        f2 = getprice(x2) - price;
        if (f2 < 0) {
            low = x2;
        }
        else {
            high = x2;
        }
    }

    return x2;
}

// Secant method
double bond::getytmsec(double price) {
    double x1 = price;
    double x2 = price + 0.25;
    double f1 = this->getprice(x1);
    double f2 = this->getprice(x2);
    for (; !isZero(f1 - price); x2 = x1, x1 = price) {
        f1 = getprice(x1);
        f2 = getprice(x2);
        price = x1 - f1 * (x1 - x2) / (f1 - f2);
    }
    return price;
}

int main() {
    bond myBond = { 1000, 25, 6 };
    cout << "YTM is " << myBond.getytm(950) << endl;
    cout << "YTM is " << myBond.getytmsec(950) << endl;

    return 0;
}

【问题讨论】:

  • 请发布您的 isZero() 函数源。另外,当你说不起作用时,你的意思是什么。是否返回不正确的结果、崩溃等?
  • getPrice(double) 是做什么的?我怀疑你的问题出在代码上,而不是数学上,但我需要能够编译和运行代码才能看到问题出在哪里。
  • 我建议您将代码复制并粘贴到ideone.com 或类似的在线编码工具中,以便确保它可以编译。有人可能只是盯着看就能看出你的问题,但我看不到。
  • 太棒了!我认为,如果您逐步浏览它,您会发现一旦 f2 小于 0,getytm() 函数就会被捕获。我将查看有关 bisect 方法的在线文档,但自从我这样做以来已经有几年了,好吧几十年了金融的东西。
  • youtube.com/watch?v=L3DuEhHqaWI 当然,他与代码的链接已损坏,但他很好地覆盖了它。我希望这会有所帮助。

标签: c++ finance quantitative-finance computational-finance


【解决方案1】:

如建议的那样,调试此问题的一个好方法是逐步计算。或者,您可以在每次迭代时打印相关值。

问题是:找到函数f(x) = getprice(x) - price的零。

二分法一般是:从区间[low, high]开始,其中f(low)f(high)的符号不同(一个非正,一个非负)。这意味着它包含一个零。然后根据中点处的函数值选择左或右子区间以保持该属性。

在这种情况下,函数是单调且非递增的,因此我们知道f(low) 必须是较大的(非负)数,f(high) 必须是较小的(非正)数。因此,如果f(midpoint)为负数,我们必须选择左子区间,如果f(midpoint)为正数,我们必须选择右子区间。但代码却相反,如果f(midpoint) 为负数,则选择正确的子区间:

    x2 = (low + high) / 2;
    f2 = getprice(x2) - price;
    if (f2 < 0) {
        low = x2;
    }

所以你选择越来越小的右子区间,最终[low, high] = [1, 1],这是一个无限循环。将f2 &lt; 0 替换为f2 &gt; 0

割线法通常涉及采用两个零“估计”x_kx_{k-1},并使用递归来找到更好的“估计”x_{k+1}。循环本质上使用(x_{k-1}, f(x_{k-1})(x_k, f(x_k)) 之间的线,并查看这条线与零交叉的位置。

提供的代码有多个问题。首先,在重要的一步:

    price = x1 - f1 * (x1 - x2) / (f1 - f2);

其中x1x2 是当前和以前的估计值,f1getprice(x1)f2getprice(x2)。重要的是,请注意 f1 不是 f(x1) 其中 f 是我们想要的零的函数。这不是割线公式。第二项的第一部分应该是x1处的函数值,即f1 - price,而不是f1

    ... = x1 - (f1 - price) * (x1 - x2) / (f1 - f2);

其次,您将其分配给price,从而丢失了您在每次迭代中确实需要的price 的实际值。

第三,对产量的初步猜测是priceprice + 0.25。这些与实际值相差甚远,以至于成为问题(零是产量,介于 0 和 1 之间)。试试01

通过不混合许多问题可以避免很多这种情况。您可以从函数的实际标识中分解出找到函数零的逻辑。例如,二等分的一步是:

template<typename Function>
constexpr auto bisection_step(double low, double high, Function f) -> std::pair<double, double>
{
    assert(std::isfinite(high));
    assert(std::isfinite(low));
    assert(low < high);
    assert(f(low) * f(high) <= 0.);

    auto mid = midpoint(low, high);
    if (f(low) * f(mid) <= 0.)
        return {low, mid};
    else
        return {mid, high};
}

这允许您以断言或检查的形式指定假设,以引发异常或返回错误代码。这也使逻辑更清晰,因此不太可能选择错误的子区间。即使有人这样做,断言也会触发。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    相关资源
    最近更新 更多