Java implements financial formulas in excel tables: Duration/Xirr/Yield

By chance. The source code of others that I picked up is recorded here. Maybe it can still be used in the future.

1. Duration implementation

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DurationFunction {<!-- -->

    public static Double getDuration(String settlementStr,String maturityStr,Double coupon,Double yld,Double frequency,Integer basis) {<!-- -->
        Date settlement = null;
        Date maturity = null;
        try {<!-- -->
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            settlement = simpleDateFormat.parse(settlementStr);
            maturity = simpleDateFormat.parse(maturityStr);
        } catch (ParseException e) {<!-- -->
            return new Double(0.0);
        }

        double m=interval(settlement, maturity,basis, 'm');//The number of months between settlement date and maturity date
        double z=12/frequency;//The number of months in each interest payment period
        double T=Math.ceil(m/z);//Number of interest payments between settlement date and maturity date

        double c=coupon/frequency;
        double y=yld/frequency;

        return new Double((1 + 1/y-(1 + y + T*(c-y))/(c*Math.pow(1 + y, T)-c + y))/frequency);
    }

    public static long interval(Date date1, Date date2, int basis, char mark){<!-- -->
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date1);
        int m1=calendar.get(Calendar.MONTH) + 1;
        int y1=calendar.get(Calendar.YEAR);
        int d1=calendar.get(Calendar.DAY_OF_MONTH);
        calendar.setTime(date2);
        int m2=calendar.get(Calendar.MONTH) + 1;
        int y2=calendar.get(Calendar.YEAR);
        int d2=calendar.get(Calendar.DAY_OF_MONTH);
        if(mark=='d'){<!-- -->
            if(basis==0 || basis==4){<!-- --> //Calculated as 30 days per month

                return ((y2-y1)*12 + m2-m1)*30 + d2-d1;
            }else{<!-- --> //Calculate based on actual days
                long day = 24 * 3600 * 1000;
                long interval = date2.getTime() / day - date1.getTime() / day;
                return interval;
            }
        }else if(mark=='m'){<!-- -->
            int m=(y2-y1)*12 + m2-m1;
            if(d1>d2) m-=1;
            return m;
        }
        return 0;
    }

}

**2. Xirr implementation **

import oracle.jdbc.internal.OracleStruct;
import oracle.sql.ARRAY;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class XirrFunction {<!-- -->


    public final static Double MIN_DISTANCE = 1E-15; // Target accuracy, minimum allowed difference (recommended to be accurate to 8-15 decimal places)
    public final static Double MIN_VALUE = 1E-8; // Minimum range of allowed net present value deviation
    public final static int MAX_ITERATION = 100; // Maximum number of iterations (to prevent stuck, usually 100 loops is enough)
    public final static Double DEFAULT_XIRR_GUESS = 0.1D; //Default Xirr guess value
    public final static Double FULL_YEAR_DAYS = 365.0D; // Number of days in the year


    public static Double xirr_q(ARRAY array) {<!-- -->

        try {<!-- -->
            List<Double> values = new ArrayList<>();
            List<Date> dates = new ArrayList<>();
            Object[] var6 = (Object[]) array.getArray();
            for (int i = 0; i < var6.length; i + + ) {<!-- -->
                OracleStruct oracleStruct = (OracleStruct) var6[i];
                Object[] attributes = oracleStruct.getAttributes();
                //The starting position of the field value array elements is 0, which is different from the field array.
                values.add( ((BigDecimal)attributes[0]).doubleValue());
                dates.add((Date) attributes[1]);

            }
            Double rate = xirr(values.toArray(new Double[0]),
                    dates.toArray(new Date[0]), DEFAULT_XIRR_GUESS);
            return rate;
        } catch (Exception e) {<!-- -->
            return new Double(0.0);
        }
    }

    /**
     * Calculate the rate of return with a net present value of 0<br>
     * Description: Use the iterative half-search myopia value method to calculate the function XIRR. By changing the yield (starting from guess), the calculation result is continuously revised until its accuracy is less than 1E-7.<br>
     * If the function XIRR is evaluated 100 times and no result is found, the error value NaN is returned.
     *
     * @param values cash flow (must have at least one positive cash flow and one negative cash flow)
     * @param dates date
     * @param guess guess value
     * @return rate of return
     */
    public static Double xirr(Double[] values, Date[] dates, double guess) {<!-- -->
        Double result = new Double(0.0); // Return result
        Double irrGuess = DEFAULT_XIRR_GUESS; // Calculate the half amount of the xirr guess value
        Double sumCashFlows = 0.0D; // sum of cash flows
        boolean wasHi = false; // Prevent missing interval identifiers
        Double npv = 0.0D; // Net present value
        int negativeCashFlowCount = 0; // Number of positive cash flows
        int positiveCashFlowCount = 0; //Number of negative cash flows

        if (values == null || values.length == 0) return result;
        if (dates == null || dates.length == 0) return result;
        if (values.length != dates.length) return result;


        for (int i = 0; i < values.length; i + + ) {<!-- -->
            sumCashFlows + = values[i];
            if (values[i] > 0.0) {<!-- -->
                negativeCashFlowCount + + ;
            } else if (values[i] < 0.0) {<!-- -->
                positiveCashFlowCount + + ;
            }
        }

        if (negativeCashFlowCount <= 0 || positiveCashFlowCount <= 0) return result;

        if (!Double.isNaN(guess)) {<!-- -->
            irrGuess = guess;
            if (irrGuess <= 0.0) irrGuess = 0.5;
        }

        Double irr = sumCashFlows < 0 ? -irrGuess : irrGuess;

        for (int i = 0; i <= MAX_ITERATION; i + + ) {<!-- -->

            npv = getXirrNpvValue(irr, values, dates);

            if (Math.abs(npv) < MIN_VALUE) {<!-- -->
                result = irr;
                break;
            }

            if (npv > 0.0) {<!-- -->
                if (wasHi) irrGuess /= 2;
                irr + = irrGuess;
                if (wasHi) {<!-- -->
                    irrGuess -= MIN_DISTANCE;
                    wasHi = false;
                }
            } else {<!-- -->
                irrGuess /= 2;
                irr -= irrGuess;
                wasHi = true;
            }

            if (irrGuess <= MIN_DISTANCE) {<!-- -->
                result = irr;
                break;
            }
        }

        return result;
    }

    /**
     * Calculate the net present value according to the formula
     *
     * @param guess guess value
     * @param values cash flow (must have at least one positive cash flow and one negative cash flow)
     * @param dates date
     * @return net present value
     */
    public static Double getXirrNpvValue(final double guess, Double[] values, Date[] dates) {<!-- -->
        Double result = 0.0D;
        // 0 = sum(values[i] / (1 + rate)^((dates[i] - dates[1]) / 365))
        for (int i = 0; i < dates.length; i + + ) {<!-- -->
            result + = values[i] / Math.pow(1 + guess, getIntervalDays(dates[i], dates[0]) / FULL_YEAR_DAYS);
        }
        return result;
    }

    private static Long getIntervalDays(Date endDate, Date startDate) {<!-- -->
        long day = 24 * 60 * 60 * 1000;

        return (endDate.getTime() - startDate.getTime()) / day;
    }
}

3. Yield implementation

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class YieldFunction {<!-- -->

// public static void main(String[] args) throws Exception {<!-- -->
// Double yield = yield("2008-02-15","2016-11-15", 0.0575, 95.04287, 100, 2,2);
// System.out.println(yield);
// }
    public static Double getYield(String settlementStr, String maturityStr, Double rate, Double pr,
                               Double redemption, Integer frequency, Integer basis) {<!-- -->
        Date settlement = null;
        Date maturity = null;
        try {<!-- -->
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            settlement = simpleDateFormat.parse(settlementStr);
            maturity = simpleDateFormat.parse(maturityStr);
            if (settlement.compareTo(maturity) == 1) {<!-- -->
                throw new Exception();
            }
            if (rate < 0 || pr <= 0 || redemption <= 0) {<!-- -->
                throw new Exception();
            }
        } catch (Exception e) {<!-- -->
            return new Double(0.0);
        }

        double m = interval(settlement, maturity, basis, 'm');
        int z = 12 / frequency;
        int n = new Double(Math.ceil(m / z)).intValue();

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(maturity);
        calendar.add(Calendar.MONTH, -n * z);
        Date end = new Date();
        end.setTime(calendar.getTimeInMillis());
        Date start = new Date();
        if (settlement.compareTo(end) == 1) {<!-- -->
            start.setTime(end.getTime());
            calendar.add(Calendar.MONTH, z);
            end.setTime(calendar.getTimeInMillis());
        } else {<!-- -->
            calendar.add(Calendar.MONTH, -z);
            start.setTime(calendar.getTimeInMillis());
        }
        double dsr = interval(settlement, maturity, basis, 'd');

        double E = interval(start, end, basis, 'd');

        double A = interval(start, settlement, basis, 'd');
        if (n <= 1) {<!-- -->
            return new Double((redemption / 100.0 + rate / frequency - pr / 100.0 - A * rate / E / frequency) * frequency * E / dsr / (pr / 100.0 + A * rate / E / frequency));
        }
        long dsc = interval(settlement, end, basis, 'd');

        double lguess = 0.1;
        double guess = 0.1;
        double lvalue = 0;
        double step = 0.01;
        for (int i = 1; i <= 100; i + + ) {<!-- -->
            double tmp1 = redemption / Math.pow(1.0 + guess / frequency, n - 1.0 + dsc / E);
            double tmp2 = 0;
            for (double k = 1; k <= n; k + + ) {<!-- -->
                tmp2 + = 100.0 * rate / frequency / Math.pow(1 + guess / frequency, k - 1.0 + dsc / E);
            }
            double tmp3 = 100.0 * rate * A / frequency / E;
            double value = tmp1 + tmp2 - tmp3 - pr;
            if (value < 0.0000001 & amp; & amp; value > -0.0000001) break;
            else if ((lvalue > 0.0000001 & amp; & amp; value < -0.0000001) || (lvalue < -0.0000001 & amp; & amp; value > 0.0000001)) {<!-- -->
                double temp1 = value;
                double temp2 = guess;
                if (value > lvalue) {<!-- -->
                    double tmp = value;
                    value = lvalue;
                    lvalue = tmp;
                    tmp = guess;
                    guess = guess;
                    lguess = tmp;
                }
                guess = lvalue * (guess - lguess) / (lvalue - value) + lguess;
                step = step / 10;
                lvalue = temp1;
                lguess = temp2;
                continue;
            } else if (value > 0.0000001) {<!-- -->
                lguess = guess;
                lvalue = value;
                guess + = step;
            } else if (value < -0.0000001) {<!-- -->
                lguess = guess;
                lvalue = value;
                guess -= step;
            }
            if (guess == -1) {<!-- -->
                guess + = step / 2;
                step = step / 10;
            }
        }
        return new Double(guess);
    }

    public static long interval(Date date1, Date date2, int basis, char mark) {<!-- -->
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date1);
        int m1 = calendar.get(Calendar.MONTH) + 1;
        int y1 = calendar.get(Calendar.YEAR);
        int d1 = calendar.get(Calendar.DAY_OF_MONTH);
        calendar.setTime(date2);
        int m2 = calendar.get(Calendar.MONTH) + 1;
        int y2 = calendar.get(Calendar.YEAR);
        int d2 = calendar.get(Calendar.DAY_OF_MONTH);
        if (mark == 'd') {<!-- -->
            if (basis == 0 || basis == 4) {<!-- --> //Calculated as 30 days per month

                return ((y2 - y1) * 12 + m2 - m1) * 30 + d2 - d1;
            } else {<!-- --> //Calculate based on actual days
                long day = 24 * 3600 * 1000;
                long interval = date2.getTime() / day - date1.getTime() / day;
                return interval;
            }
        } else if (mark == 'm') {<!-- -->
            int m = (y2 - y1) * 12 + m2 - m1;
            if (d1 > d2) m -= 1;
            return m;
        }
        return 0;
    }
}