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軸交點的橫座標翻譯
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.當金額比較大,現金流週期比較長的時候可能會有問題。
方法二的弊端:
現金流的輸入是有順序的,必須先排序。