历史版本4 :内部收益率公式IRR 返回文档
编辑时间:
内容长度:图片数:目录数:
修改原因:
1. 描述编辑
场景描述:内部收益率IRR这个公式在可行性评估分析类的财务报表中频繁的被使用,FR中默认没有这个公式,而这个值的计算又不是纯数学计算,需要靠猜测和差值最终得到一个理想的结果。
下面就介绍如何实现。
2.思路编辑
我们可以根据IRR逻辑创建一个自定义函数,然后根据一个数据列的集合来计算IRR内部收益率。
3. 操作步骤编辑
点击菜单服务器>函数管理器,点击"+"号添加一个函数,双击可重命名,重名民为IRR,点击编辑,弹出自定义函数对话框,将代码复制进去,点击编译,若编译成功,点击保存即可。
注:编译成功才能正确保存。
package com.midas.function.excel;
import java.math.BigDecimal;
import com.fr.general.FArray;
import com.fr.general.GeneralUtils;
import com.fr.script.AbstractFunction;
public class IRR extends AbstractFunction {
private static final long serialVersionUID = 7634415917398642321L;
private static final String ERROR_VALUE = "#NUM!";
@Override
public Object run(Object[] args) {
try{
if(1 == args.length){
return run( transArr( (FArray) args[0] ) );
}else if(2 == args.length){
return run( transArr( (FArray) args[0] ), trans( args[1] ) );
}
}catch(Exception e){
System.out.println(e);
}
return ERROR_VALUE;
}
/**
* 将其他类型的数字转换为大数(保证精度)
* @param ele
* @return
*/
private static BigDecimal trans(Object ele){
try{
String val = GeneralUtils.objectToString(ele);
return new BigDecimal(val);
}catch(Exception e){
}
return (BigDecimal) ele;
}
/**
* 将数组转换为大数数组
* @param in
* @return
*/
private static FArray<BigDecimal> transArr(FArray in){
FArray<BigDecimal> rt = new FArray<BigDecimal>();
for(int i=0;i<in.length();i++){
Object ele = in.elementAt(i);
rt.add(trans(ele));
}
return rt;
}
private static BigDecimal run(FArray<BigDecimal> cashflow){
return run( cashflow, new BigDecimal(0.1d) );
}
private static BigDecimal run(FArray<BigDecimal> cashflow,BigDecimal guess){
BigDecimal maxrate = initRateMax(cashflow,guess);
BigDecimal minrate = initRateMin(cashflow,guess);
for( int i=0; i<Init_Max_Loop; i++ ){
BigDecimal testrate = minrate.add(maxrate).divide( new BigDecimal(2d) );
BigDecimal npv = NPV( cashflow, testrate );
if( npv.abs().compareTo(Accuracy) == LESS ){
guess = testrate;
break;
}else if( npv.compareTo(ZERO) == LESS ){
minrate = testrate;
}else{
maxrate = testrate;
}
}
//保留16位小数(足够精度)
return guess.setScale(16,BigDecimal.ROUND_HALF_UP);
}
//最小精度
private static final BigDecimal Accuracy = new BigDecimal(0.00000001d);
//最大循环次数,excel用的是20次,不过精度只到小数点后两位,而且不一定一定能算出值,为了尽可能保证算出结果,我增加到100次,
private static final int Init_Max_Loop = 100;
private static final BigDecimal ZERO = new BigDecimal(0);
private static final BigDecimal ONE = new BigDecimal(1);
private static final BigDecimal Z005 = new BigDecimal(0.005d);
private static final BigDecimal Z2 = new BigDecimal(0.2d);
private static final int GREATER = 1;
private static final int LESS = -1;
/**
* 生成一个使NPV为负数的R作为内部收益率下限值
* @param cashflow
* @param guess
* @return
*/
private static BigDecimal initRateMin(FArray<BigDecimal> cashflow,BigDecimal guess){
for( int i=0; i<Init_Max_Loop; i++ ){
BigDecimal npv = NPV( cashflow, guess );
if( npv.compareTo(ZERO) == LESS ){
return guess;
}
BigDecimal step = guess.abs().multiply( Z2 );
guess = guess.add( step.compareTo( Z005 ) == LESS ? Z005 : step );
}
return guess;
}
/**
* 生成一个使NPV为正数的R作为内部收益率的上限制
* @param cashflow
* @param guess
* @return
*/
private static BigDecimal initRateMax(FArray<BigDecimal> cashflow,BigDecimal guess){
for( int i=0; i<Init_Max_Loop; i++ ){
BigDecimal npv = NPV( cashflow, guess );
if( npv.compareTo(ZERO) == GREATER ){
return guess;
}
BigDecimal step = guess.abs().multiply( Z2 );
guess = guess.subtract( step.compareTo( Z005 ) == LESS ? Z005 : step );
}
return guess;
}
/**
* 算NPV
* @param cashflow
* @param rate
* @return
*/
private static BigDecimal NPV(FArray<BigDecimal> cashflow,BigDecimal rate){
BigDecimal npv = ZERO;
BigDecimal rpowj = ONE;//(1+r)^0
BigDecimal radd1 = rate.add(ONE);//1+r
for( int j=0; j<cashflow.length(); j++ ){
BigDecimal valuej = cashflow.elementAt(j);
npv = npv.add( valuej.divide( rpowj, 10, BigDecimal.ROUND_HALF_DOWN ) );// vj / (1+r)^j
rpowj = rpowj.multiply(radd1); // (1+r)^j
//npv += cashflow.elementAt(j) / Math.pow( 1+rate, j );
}
return npv;
}
}
编译保存后即可使用该IRR函数。
3. 效果预览编辑
(1)excel中的IRR公式效果
(2)FR中的IRR公式效果
模板样式设计:
预览结果:
通过比较我们可以知道,在FR中使用该公式,比excel中更精确,也更稳定。
注:不容易出现算不出结果的情况。
4.移动端效果预览编辑
