内部收益率公式IRR

编辑
  • 文档创建者:迈达斯之手
  • 浏览次数:2359次
  • 编辑次数:6次
  • 最近更新:susie 于 2019-03-28
  • 1. 描述

    场景描述:内部收益率IRR这个公式在可行性评估分析类的财务报表中频繁的被使用,FR中默认没有这个公式,而这个值的计算又不是纯数学计算,需要靠猜测和差值最终得到一个理想的结果。

    下面就介绍如何实现。

    2.思路

    我们可以根据IRR逻辑创建一个自定义函数,然后根据一个数据列的集合来计算IRR内部收益率。

    3. 操作步骤

    在一般情况下,若您希望在FineReport设计器基础上进行二次开发,我们可以在Eclipse或idea中通过调用FineReport启动类来启动设计器,以便于开发过程中的调试和二次开发的准备。

    注意需要引入FineReport设计器下的jar包,详细点击Eclipse中启动设计器

    3.1 完整代码如下

    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;
            }
            
    }
    3.2 编译自定义函数
    将编译后的IRR.class放到FineReport的安装目录WEB-INF下面的classes目录下,因为IRR.java属于包com.fr.function,所以IRR.class需要放到%FR_HOME%\webapps\webroot\WEB-INF\classes\com\fr\function目录下。
    3.3 注册自定义函数
    生成该函数的类后需要在设计器中进行注册,才可以使用该函数。打开服务器>函数管理器,选择刚刚定义好了IRR类,如下图:


    3.4 使用自定义函数
    注册好自定义函数后,制作报表时便可直接使用了,使用方法与内置的函数是相同的。

    (1)excel中的IRR公式效果


    (2)FR中的IRR公式效果
    模板样式设计:


    预览结果:

    通过比较我们可以知道,在FR中使用该公式,比excel中更精确,也更稳定。

    注:不容易出现算不出结果的情况。

    4.移动端效果预览



    附件列表


    主题: 二次开发
    如果您认为本文档还有待完善,请编辑

    文档内容仅供参考,如果你需要获取更多帮助,付费/准付费客户请咨询帆软技术支持
    关于技术问题,您还可以前往帆软社区,点击顶部搜索框旁边的提问按钮
    若您还有其他非技术类问题,可以联系帆软传说哥(qq:1745114201

    此页面有帮助吗?只是浏览 [ 去社区提问 ]