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; } }