ORACLE SQL 實現IRR的計算

1、IRR計算的原理:java

  內部收益率(Internal Rate of Return (IRR)),就是資金流入現值總額與資金流出現值總額相等、淨現值等於零時的折現率。數組

用公式 標識:-200+[30/(1+IRR)+30/(1+IRR)^2+....+30/(1+IRR)^10]=0。屢次方程求解。函數

在計算機界求解高次方程的作法一般是利用牛頓插值法(Newton-Raphson)來實現,也有翻譯牛頓迭代的。oop

 

2、關於牛頓迭代:spa

設r是  的根,選取 做爲r的初始近似值,過點  作曲線  的切線L,L的方程爲  ,求出L與x軸交點的橫座標翻譯

   ,稱x1爲r的一次近似值。過點   作曲線   的切線,並求該切線與x軸交點的橫座標   ,稱  爲r的二次近似值。重複以上過程,得r的近似值序列,其中,
   稱爲r的   次近似值。
 
3、Java的實現方式:
import java.math.BigDecimal;

public class IRRUtilMath2 {

    
    public static double irr(double[] income) {
        return irr(income, 0.1D);
    }
 
    public static double irr(double[] values, double guess) {
        int maxIterationCount = 20;
        double absoluteAccuracy = 1.0E-007D;
 
        double x0 = guess;
 
        int i = 0;
        while (i < maxIterationCount) {
            double fValue = 0.0D;
            double fDerivative = 0.0D;
            for (int k = 0; k < values.length; k++) {
                fValue += values[k] / Math.pow(1.0D + x0, k);
                fDerivative += -k * values[k] / Math.pow(1.0D + x0, k + 1);
            }
            double x1 = x0 - fValue / fDerivative;
            if (Math.abs(x1 - x0) <= absoluteAccuracy) {
                return x1;
            }
            x0 = x1;
            i++;
        }
        return (0.0D / 0.0D);
    }
 
    public static void main(String[] args) {
        double[] income = {-359900,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,19413.67,18241.01,0,0};
        double ret = irr(income,0.00001d)*12 ;
        System.out.println(new BigDecimal(ret));
 
    }
}

4、根據Java版實現的SQL版:3d

--定義type
create or replace type typ_cashflow_array is varray(60) of number;

--實現函數
function IRR_ZEN(p_amount_array in typ_cashflow_array, p_guess in number)
    RETURN NUMBER is 
    rtn_err number := -9999999;
    maxIterationCount number := 20;
    absoluteAccuracy number := 0.0000001;
    x0 number := p_guess;
    x1 number := 0;
    i_num integer := 0; 
    
    fValue number := 0.0;
    fDerivative number := 0.0;
    
    BEGIN
      --x0 :=p_guess;
       while (i_num < maxIterationCount) loop
            fValue := 0.0;
            fDerivative := 0.0;
            for k in 1..p_amount_array.count loop
                fValue :=fValue+p_amount_array(k)/power(1.0 + x0, k);
                fDerivative :=fDerivative+(-k *p_amount_array(k)/power(1.0 + x0, k + 1));
            end loop;
            x1 := x0 - fValue / fDerivative;
            if (abs(x1 - x0) <= absoluteAccuracy) then
                return x1;
            end if;
            x0 := x1;
            i_num := i_num+1;
        end loop; 
        return (0.0/0.0);
    EXCEPTION
      WHEN OTHERS THEN
        return rtn_err; 
  END IRR_ZEN;

 

5、關於函數的調用:code

  方法一,將現金流組成字符串,而後用函數拆解字符串。具體實現方式是orm

function IRR(in_varray in varchar2) return number
    is 
    v_irr number;
    v_amount_array  typ_cashflow_array;
     
    begin
      v_amount_array :=typ_cashflow_array();
      declare
        v_varray_str varchar2(1000);
        v_length number;
        v_split varchar2(2);
        v_cnt integer; 
      begin 
          v_varray_str:=ltrim(rtrim(in_varray));
          v_length:=0;
          v_split :=',';
          v_cnt :=1;
        ---劈開字符串,爲數據賦值
        while instr(v_varray_str,v_split)<>0 loop
         v_length:=v_length+1; 
         v_amount_array.extend;
         v_amount_array(v_cnt) :=to_number(substr(v_varray_str,1,instr(v_varray_str,v_split)-1));
         v_varray_str:=substr(v_varray_str,instr(v_varray_str,v_split)+length(v_split),length(v_varray_str));
         v_cnt :=v_cnt+1;
        end loop;
        
        --循環的末尾追加最後一個數字
        v_amount_array.extend;
        v_amount_array(v_cnt) :=to_number(v_varray_str);
         
        
      end; 
    v_irr :=IRR_ZEN(p_amount_array => v_amount_array, p_guess =>0.1 );
    return v_irr;
  end ;

調用的時候:blog

select LES_FIN_TO_DW.IRR(listagg(t.fee_amt,',') within group( order by t.pay_pd))*12  from 
F_LES_PAY_SCH_IRR_CAL_CASHFLOW t
where t.pay_sch_id=180605104121724
order by t.pay_pd;

方法二:  直接使用類型轉換公式初始化數組

select les_fin_to_dw.IRR_ZEN(cast(collect(fee_amt) as typ_cashflow_array),0.1)*12
from(
select t.fee_amt
from F_LES_PAY_SCH_IRR_CAL_CASHFLOW t
where t.pay_sch_id=171218104072346
order by t.pay_pd
);

方法一的弊端:

listagg可鏈接的最大字節長度是4000byte.當金額比較大,現金流週期比較長的時候可能會有問題。

 方法二的弊端:

 現金流的輸入是有順序的,必須先排序。

 
參考資料:
  https://baike.baidu.com/item/%E7%89%9B%E9%A1%BF%E8%BF%AD%E4%BB%A3%E6%B3%95/10887580?fr=aladdin
相關文章
相關標籤/搜索