因此,我们的 ASP.NET4 Web 应用程序中有一个费率计算器类,它使用 Microsoft.VisualBasic.Financial.Rate 来计算名义费率(基于输入参数)。
我们注意到,对于较高的 NPer 值(付款期总数,例如 50 年 x 每月付款 = 600),该函数会抛出异常:无法使用提供的参数计算利率。
四处搜索我们没有找到任何解决方案,所以我在这里发布解决方案。我们的一个要求是维护一个尽可能接近实现与上述相同算法的函数,因为我们需要产生完全相同的输出。
因此,我们的 ASP.NET4 Web 应用程序中有一个费率计算器类,它使用 Microsoft.VisualBasic.Financial.Rate 来计算名义费率(基于输入参数)。
我们注意到,对于较高的 NPer 值(付款期总数,例如 50 年 x 每月付款 = 600),该函数会抛出异常:无法使用提供的参数计算利率。
四处搜索我们没有找到任何解决方案,所以我在这里发布解决方案。我们的一个要求是维护一个尽可能接近实现与上述相同算法的函数,因为我们需要产生完全相同的输出。
回答我自己的问题,对于遇到此问题的任何未来编码人员 - 我们使用 dotPeek 反编译模块,生成以下内容:
public static double Rate(double NPer, double Pmt, double PV, double FV = 0.0, DueDate Due = DueDate.EndOfPeriod, double Guess = 0.1)
{
if (NPer <= 0.0)
throw new ArgumentException(Utils.GetResourceString("Rate_NPerMustBeGTZero"));
double Rate1 = Guess;
double num1 = Financial.LEvalRate(Rate1, NPer, Pmt, PV, FV, Due);
double Rate2 = num1 <= 0.0 ? Rate1 * 2.0 : Rate1 / 2.0;
double num2 = Financial.LEvalRate(Rate2, NPer, Pmt, PV, FV, Due);
int num3 = 0;
do
{
if (num2 == num1)
{
if (Rate2 > Rate1)
Rate1 -= 1E-05;
else
Rate1 -= -1E-05;
num1 = Financial.LEvalRate(Rate1, NPer, Pmt, PV, FV, Due);
if (num2 == num1)
throw new ArgumentException(Utils.GetResourceString("Financial_CalcDivByZero"));
}
double Rate3 = Rate2 - (Rate2 - Rate1) * num2 / (num2 - num1);
double num4 = Financial.LEvalRate(Rate3, NPer, Pmt, PV, FV, Due);
if (Math.Abs(num4) < 1E-07)
return Rate3;
double num5 = num4;
num1 = num2;
num2 = num5;
double num6 = Rate3;
Rate1 = Rate2;
Rate2 = num6;
checked { ++num3; }
}
while (num3 <= 39);
throw new ArgumentException(Utils.GetResourceString("Financial_CannotCalculateRate"));
}
private static double LEvalRate(double Rate, double NPer, double Pmt, double PV, double dFv, DueDate Due)
{
if (Rate == 0.0)
return PV + Pmt * NPer + dFv;
double num1 = Math.Pow(Rate + 1.0, NPer);
double num2 = Due == DueDate.EndOfPeriod ? 1.0 : 1.0 + Rate;
return PV * num1 + Pmt * num2 * (num1 - 1.0) / Rate + dFv;
}
我们可以看到如果超过 num3 就会抛出错误,因为它的硬限制是 39。我们稍微整理了代码,并将限制增加到 100:
private static double CalculateUpfrontNominalRate(double numberOfPeriods, double payment, double presentValue, double futureValue = 0.0, DueDate Due = DueDate.EndOfPeriod, double Guess = 0.1)
{
if (numberOfPeriods <= 0.0)
{
throw new ArgumentException("CalculateUpfrontNominalRate: Number of periods must be greater than zero");
}
var rateUpperBoundary = Guess;
var lEvalRate1 = LEvalRate(rateUpperBoundary, numberOfPeriods, payment, presentValue, futureValue, Due);
var rateLowerBoundary = lEvalRate1 <= 0.0 ? rateUpperBoundary * 2.0 : rateUpperBoundary / 2.0;
var lEvalRate2 = LEvalRate(rateLowerBoundary, numberOfPeriods, payment, presentValue, futureValue, Due);
for (var i = 0; i < 100; i++)
{
if (lEvalRate2 == lEvalRate1)
{
if (rateLowerBoundary > rateUpperBoundary)
rateUpperBoundary -= 1E-05;
else
rateUpperBoundary -= -1E-05;
lEvalRate1 = LEvalRate(rateUpperBoundary, numberOfPeriods, payment, presentValue, futureValue, Due);
if (lEvalRate2 == lEvalRate1)
{
throw new ArgumentException("CalculateUpfrontNominalRate: Inputs will cause a divsion by zero");
}
}
double temporaryRate = rateLowerBoundary - (rateLowerBoundary - rateUpperBoundary) * lEvalRate2 / (lEvalRate2 - lEvalRate1);
double lEvalRate3 = LEvalRate(temporaryRate, numberOfPeriods, payment, presentValue, futureValue, Due);
if (Math.Abs(lEvalRate3) < 1E-07)
{
return temporaryRate;
}
lEvalRate1 = lEvalRate2;
lEvalRate2 = lEvalRate3;
rateUpperBoundary = rateLowerBoundary;
rateLowerBoundary = temporaryRate;
}
throw new ArgumentException("CalculateUpfrontNominalRate: The maximum number of iterations has been exceeded, unable to calculate rate");
}
private static double LEvalRate(double Rate, double NPer, double Pmt, double PV, double dFv, DueDate Due)
{
if (Rate == 0.0)
return PV + Pmt * NPer + dFv;
double num1 = Math.Pow(Rate + 1.0, NPer);
double num2 = Due == DueDate.EndOfPeriod ? 1.0 : 1.0 + Rate;
return PV * num1 + Pmt * num2 * (num1 - 1.0) / Rate + dFv;
}
更改迭代计算的计数并不能解决所有情况的问题
对于您发布的示例计算,速率算法在 40 次迭代中找不到正确到小数点后 7 位的速率(精确到小数点后 7 位)
这是你反编译的vb.net金融函数的编程代码吗
如果是这样,程序员在使用 Secant 方法对 RATE 函数进行编码方面做得很差
有比糟糕的正割法更好的算法来计算利率
如果您认为通过将迭代次数更改为 100 已经解决了问题,请尝试使用各种数据测试代码,看看是否可以在所有情况下获得 RATE
我在使用 Rate 函数时遇到了同样的问题。发现您的“猜测”对于确保收敛非常重要 - 如文档中的备注中所述:
备注:
确保您对用于指定guess 和nper 的单位保持一致。如果您以 12% 的年利率每月支付四年期贷款,请使用 12%/12 进行猜测,使用 4*12 进行 nper。如果您对同一笔贷款进行年度还款,请使用 12% 的猜测值和 4 的 nper。