- Open Notes - http://opennotes.ru -

Процедура Oracle SQL для поиска ставки кредита по сроку, платежу и сумме кредита

На просторах Интернета не сложно найти SQL функцию, которая считала бы аннуитетный платёж по данным о величине кредита, процентной ставке и сроку кредита. Но вот готовой функции для другой задачи, а именно поиска процентной ставки по данным об аннуитете, сроке и величине кредита, мне найти не удалось. Решение данной задачи не тривиальное и требует применения численных методов. В Excel есть очень удобная для этого функция СТАВКА(), на аналог которой на языке PHP я наткнулся на StackOverflow. Мне оставалось только переписать код для SQL. Результат можно увидеть ниже.

nper — количество платёжных периодов; pmt — аннуитетный платёж; pv — величина кредита. Для поиска решения используется метод хорд.

create or replace FUNCTION RATE (nper number, pmt number, pv number) RETURN number
  as v_result number;
  fv number:= 0.0;
  s_type int:= 0;
  guess number := 0.1;
  FINANCIAL_MAX_ITERATIONS int := 5000; --количество итераций
  FINANCIAL_PRECISION number:= 1.0e-08; --точность
  y number;
  y0 number;
  y1 number;
  f number;
  i number;
  x0 number;
  x1 number;
BEGIN
v_result := guess;

if (abs(v_result) < FINANCIAL_PRECISION) then
y := pv * (1 + nper * v_result) + pmt * (1 + v_result * s_type) * nper + fv;
else
f := exp(nper * ln(1 + v_result));
y := pv * f + pmt * (1 / v_result + s_type) * (f - 1) + fv;
end if;
y0 := pv + pmt * nper + fv;
y1 := pv * f + pmt * (1 / v_result + s_type) * (f - 1) + fv;

-- поиск корня методом хорд
i := 0;
x0 := 0;
x1 := v_result;
while ((abs(y0 - y1) > FINANCIAL_PRECISION) and (i < FINANCIAL_MAX_ITERATIONS)) loop
     v_result := (y1 * x0 - y0 * x1) / (y1 - y0);
     x0 := x1;
     x1 := v_result;
     if ((nper * abs(pmt)) > (pv - fv)) then
         x1 := abs(x1);
     end if;
     if (abs(v_result) < FINANCIAL_PRECISION) then
         y := pv * (1 + nper * v_result) + pmt * (1 + v_result * s_type) * nper + fv;
     else
         f := exp(nper * ln(1 + v_result));
         y := pv * f + pmt * (1 / v_result + s_type) * (f - 1) + fv;
     end if;
     y0 := y1;
     y1 := y;
     i:=i+1;
end loop;
    return v_result;
END;