Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.creditcloud.wealthproduct.model.utils; import com.creditcloud.common.utils.DateUtils; import com.creditcloud.common.utils.ValidateResult; import com.creditcloud.model.constant.NumberConstant; import com.creditcloud.model.constant.TimeConstant; import com.creditcloud.model.enums.loan.RepaymentMethod; import static com.creditcloud.model.enums.loan.RepaymentMethod.BulletRepayment; import static com.creditcloud.model.enums.loan.RepaymentMethod.EqualInstallment; import static com.creditcloud.model.enums.loan.RepaymentMethod.EqualInterest; import static com.creditcloud.model.enums.loan.RepaymentMethod.EqualPrincipal; import static com.creditcloud.model.enums.loan.RepaymentMethod.MonthlyInterest; import static com.creditcloud.model.enums.loan.RepaymentMethod.YearlyInterest; import com.creditcloud.model.enums.loan.RepaymentPeriod; import com.creditcloud.model.loan.Duration; import com.creditcloud.model.loan.Repayment; import com.creditcloud.model.loan.RepaymentRule; import com.creditcloud.wealthproduct.model.WealthProductRepaymentDetail; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; import org.joda.time.LocalDate; /** * ?? * * copy fromcom.creditcloud.common.calculator.LoanCalculator * * @author suetming <suetming.ma at creditcloud.com> */ public class WealthProductCalculator { private static final BigDecimal ZERO = new BigDecimal(0).setScale(2); private static final BigDecimal ONE = new BigDecimal(1).setScale(2); private static final BigDecimal monthsPerYear = new BigDecimal(TimeConstant.MONTHS_PER_YEAR); private static final BigDecimal daysPerYear = new BigDecimal(TimeConstant.DAYS_PER_YEAR); /** * rate is in format like 2400 which is actually 24.00% */ private static final BigDecimal rateScale = new BigDecimal(10000); private static final MathContext mc = new MathContext(16, NumberConstant.ROUNDING_MODE); /** * @deprecated analyze??? * * @param asOfDate * @param nextKMonth * @return */ public static LocalDate countDueDate(final LocalDate asOfDate, int nextKMonth) { final int[][] leap = { { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; int year = asOfDate.getYear(); int month = asOfDate.getMonthOfYear(); int day = asOfDate.getDayOfMonth(); int i = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 0 : 1; int nextYear = year + (month + nextKMonth - 1) / 12; int nextMonth = (month + nextKMonth - 1) % 12; int j = ((nextYear % 4 == 0 && nextYear % 100 != 0) || (nextYear % 400 == 0)) ? 0 : 1; int maxNextDay = leap[j][nextMonth]; int maxCurrentDay = leap[i][month - 1]; LocalDate local; if (day < maxCurrentDay && day < maxNextDay) { local = new LocalDate(nextYear, nextMonth + 1, day); } else { local = new LocalDate(nextYear, nextMonth + 1, maxNextDay); } return local; } /** * * @deprecated analyze??? * * @param amount * @param duration * @param rate * @param method * @param asOfDate * @return */ public static WealthProductRepaymentDetail analyzeNew(final int amount, final Duration duration, final int rate, final RepaymentMethod method, final LocalDate asOfDate) { LocalDate local; WealthProductRepaymentDetail model = analyze(amount, duration, rate, method, asOfDate); List<Repayment> list = model.getRepayments(); if (list != null) { for (int i = 0; i < list.size(); i++) { local = countDueDate(asOfDate, i + 1); list.get(i).setDueDate(local); } } return model; } /** * * @param amount * @param duration * @param rate 2400 means 24.00% * @param method * @param asOfDate * @return */ public static WealthProductRepaymentDetail analyze(final int amount, final Duration duration, final int rate, final RepaymentMethod method, final LocalDate asOfDate) { return analyze(BigDecimal.valueOf(amount), duration, rate, method, asOfDate); } /** * ???. * * @param amount * @param duration * @param rate 2400 means 24.00% * @param method * @param asOfDate ?131?: 228(29)?331?430 * @param period * @return */ public static WealthProductRepaymentDetail analyze(final BigDecimal amount, final Duration duration, final int rate, final RepaymentMethod method, final LocalDate asOfDate, final RepaymentPeriod period) { if (period == null) { return analyze(amount, duration, rate, method, asOfDate); } if (!method.isExtensible()) { throw new IllegalArgumentException(method + "is not extensible repayment."); } if (!method.equals(BulletRepayment)) {//??? //periodRepaymentMethod??? ValidateResult validateResult = validatePeriodAndDuration(period, duration); if (!validateResult.isSuccess()) { throw new IllegalArgumentException(validateResult.getMessage()); } } WealthProductRepaymentDetail result = null; //principal BigDecimal principal = amount; //now get rates BigDecimal rateYear = new BigDecimal(rate).divide(rateScale, mc); BigDecimal rateMonth = rateYear.divide(monthsPerYear, mc); BigDecimal ratePeriod = rateMonth.multiply(new BigDecimal(period.getMonthsOfPeriod())); //dealing with different methods BigDecimal interest, amortizedInterest, amortizedPrincipal, outstandingPrincipal; int tenure; switch (method) { case BulletRepayment: analyze(amount, duration, rate, method, asOfDate); break; case MonthlyInterest: //period? tenure = duration.getTotalMonths() / period.getMonthsOfPeriod(); amortizedInterest = principal.multiply(ratePeriod).setScale(2, NumberConstant.ROUNDING_MODE); interest = amortizedInterest.multiply(new BigDecimal(tenure)); //create result result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //add amortized items for (int i = 0; i < tenure; i++) { if (i < tenure - 1) { //only interest, no principal result.getRepayments().add(new Repayment(ZERO, amortizedInterest, principal, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } else { //last ONE we pay off the principal as well as interest result.getRepayments().add(new Repayment(principal, amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } } break; case EqualInstallment: //period?? //times of repayments in months tenure = (duration.getYears() * 12 + duration.getMonths()) / period.getMonthsOfPeriod(); BigDecimal[] is = new BigDecimal[tenure + 1]; for (int i = 0; i <= tenure; i++) { is[i] = ratePeriod.add(ONE).pow(i); } BigDecimal baseInterest = principal.multiply(ratePeriod); //calc installment BigDecimal installment = baseInterest.multiply(is[tenure]).divide(is[tenure].subtract(ONE), mc); installment = installment.setScale(2, NumberConstant.ROUNDING_MODE); //reset total interest interest = ZERO; //create loanDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items outstandingPrincipal = principal; for (int i = 0; i < tenure; i++) { amortizedInterest = baseInterest.subtract(installment, mc).multiply(is[i]).add(installment, mc) .setScale(2, NumberConstant.ROUNDING_MODE); amortizedPrincipal = installment.subtract(amortizedInterest); outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); if (i == tenure - 1) { //last ONE we need to fix the rounding error and let the oustanding principal be ZERO result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipal), amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } else { result.getRepayments().add(new Repayment(amortizedPrincipal, amortizedInterest, outstandingPrincipal, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } interest = interest.add(amortizedInterest); } //fix interest result.setInterest(interest); break; case EqualPrincipal: //period? //times of repayments in months tenure = (duration.getYears() * 12 + duration.getMonths()) / period.getMonthsOfPeriod(); //calc amortized principal first amortizedPrincipal = principal.divide(new BigDecimal(tenure), mc).setScale(2, NumberConstant.ROUNDING_MODE); //calc by each month BigDecimal[] interests = new BigDecimal[tenure]; BigDecimal[] outstandingPrincipals = new BigDecimal[tenure]; outstandingPrincipal = principal; interest = ZERO; for (int i = 0; i < tenure; i++) { interests[i] = outstandingPrincipal.multiply(ratePeriod, mc).setScale(2, NumberConstant.ROUNDING_MODE); interest = interest.add(interests[i]); outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); outstandingPrincipals[i] = outstandingPrincipal; } //create LoanDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items for (int i = 0; i < tenure; i++) { if (i == tenure - 1) { result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipals[i]), interests[i], ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } else { result.getRepayments().add(new Repayment(amortizedPrincipal, interests[i], outstandingPrincipals[i], DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } } break; case EqualInterest: //period? //times of repayments in months tenure = (duration.getYears() * 12 + duration.getMonths()) / period.getMonthsOfPeriod(); //calc amortized principal and interest amortizedPrincipal = principal.divide(new BigDecimal(tenure), mc).setScale(2, NumberConstant.ROUNDING_MODE); amortizedInterest = principal.multiply(ratePeriod).setScale(2, NumberConstant.ROUNDING_MODE); interest = amortizedInterest.multiply(new BigDecimal(tenure), mc).setScale(2, NumberConstant.ROUNDING_MODE); //create LoanDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items outstandingPrincipal = principal; for (int i = 0; i < tenure; i++) { outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); if (i == tenure - 1) { result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipal), amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } else { result.getRepayments().add(new Repayment(amortizedPrincipal, amortizedInterest, outstandingPrincipal, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } } break; } return result; } /** * ???. * * @param amount * @param duration * @param rate 2400 means 24.00% * @param method * @param asOfDate ?131?: 228(29)?331?430 * @return */ public static WealthProductRepaymentDetail analyze(final BigDecimal amount, final Duration duration, final int rate, final RepaymentMethod method, final LocalDate asOfDate) { return analyze(amount, duration, rate, method, asOfDate, NumberConstant.ROUNDING_MODE); } public static WealthProductRepaymentDetail analyze(final BigDecimal amount, final Duration duration, final int rate, final RepaymentMethod method, final RepaymentRule repaymentRule, final LocalDate asOfDate) { return analyze(amount, duration, rate, method, repaymentRule, asOfDate, NumberConstant.ROUNDING_MODE); } public static WealthProductRepaymentDetail analyze(final BigDecimal amount, final Duration duration, final int rate, final RepaymentMethod method, final LocalDate asOfDate, final RoundingMode rm) { return analyze(amount, duration, rate, method, RepaymentRule.DEFAULT, asOfDate, NumberConstant.ROUNDING_MODE); } public static WealthProductRepaymentDetail analyze(final BigDecimal amount, final Duration duration, final int rate, final RepaymentMethod method, final RepaymentRule repaymentRule, final LocalDate asOfDate, final RoundingMode rm) { RoundingMode roundingMode = rm; if (roundingMode == null) { roundingMode = NumberConstant.ROUNDING_MODE; } WealthProductRepaymentDetail result = null; //principal BigDecimal principal = amount; //now get rates BigDecimal rateYear = new BigDecimal(rate).divide(rateScale, mc); BigDecimal rateMonth = rateYear.divide(monthsPerYear, mc); BigDecimal rateDay = rateYear.divide(new BigDecimal(repaymentRule.getDaysOfYear()), mc); //dealing with different methods BigDecimal interest, amortizedInterest, amortizedPrincipal, outstandingPrincipal; int tenure; switch (method) { case BulletRepayment: //?0?,???? if (duration.getDays() > 0) { interest = principal.multiply(rateDay).multiply(new BigDecimal(duration.getTotalDays())); } else { //add yearly interest interest = principal.multiply(rateYear).multiply(new BigDecimal(duration.getYears())); //add monthly interest interest = interest .add(principal.multiply(rateMonth).multiply(new BigDecimal(duration.getMonths()))); } //ceilling the interest interest = interest.setScale(2, roundingMode); //create result result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //?0?,???? if (duration.getDays() > 0) { result.getRepayments().add(new Repayment(principal, interest, ZERO, DateUtils.offset(asOfDate, duration.getTotalDays()))); } else { result.getRepayments() .add(new Repayment(principal, interest, ZERO, DateUtils.offset(asOfDate, duration))); } break; case MonthlyInterest: //in this case we don't need to worry about duration.days since that must be 0 amortizedInterest = principal.multiply(rateMonth).setScale(2, roundingMode); interest = amortizedInterest.multiply(new BigDecimal(duration.getTotalMonths())); //create result result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //add amortized items for (int i = 0; i < duration.getTotalMonths(); i++) { if (i < duration.getTotalMonths() - 1) { //only interest, no principal result.getRepayments().add(new Repayment(ZERO, amortizedInterest, principal, DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } else { //last ONE we pay off the principal as well as interest result.getRepayments().add(new Repayment(principal, amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } } break; case EqualInstallment: //times of repayments in months tenure = duration.getYears() * 12 + duration.getMonths(); //pre-calc BigDecimal[] is = new BigDecimal[tenure + 1]; for (int i = 0; i <= tenure; i++) { is[i] = rateMonth.add(ONE).pow(i); } BigDecimal baseInterest = principal.multiply(rateMonth); //calc installment BigDecimal installment = baseInterest.multiply(is[tenure]).divide(is[tenure].subtract(ONE), mc); installment = installment.setScale(2, roundingMode); //reset total interest interest = ZERO; //create loanDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items outstandingPrincipal = principal; for (int i = 0; i < tenure; i++) { amortizedInterest = baseInterest.subtract(installment, mc).multiply(is[i]).add(installment, mc) .setScale(2, roundingMode); amortizedPrincipal = installment.subtract(amortizedInterest); outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); if (i == tenure - 1) { //last ONE we need to fix the rounding error and let the oustanding principal be ZERO result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipal), amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } else { result.getRepayments().add(new Repayment(amortizedPrincipal, amortizedInterest, outstandingPrincipal, DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } interest = interest.add(amortizedInterest); } //fix interest result.setInterest(interest); break; case EqualPrincipal: //times of repayments in months tenure = duration.getYears() * 12 + duration.getMonths(); //calc amortized principal first amortizedPrincipal = principal.divide(new BigDecimal(tenure), mc).setScale(2, roundingMode); //calc by each month BigDecimal[] interests = new BigDecimal[tenure]; BigDecimal[] outstandingPrincipals = new BigDecimal[tenure]; outstandingPrincipal = principal; interest = ZERO; for (int i = 0; i < tenure; i++) { interests[i] = outstandingPrincipal.multiply(rateMonth, mc).setScale(2, roundingMode); interest = interest.add(interests[i]); outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); outstandingPrincipals[i] = outstandingPrincipal; } //create LoanDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items for (int i = 0; i < tenure; i++) { if (i == tenure - 1) { result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipals[i]), interests[i], ZERO, DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } else { result.getRepayments().add(new Repayment(amortizedPrincipal, interests[i], outstandingPrincipals[i], DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } } break; case EqualInterest: //times of repayments in months tenure = duration.getYears() * 12 + duration.getMonths(); //calc amortized principal and interest amortizedPrincipal = principal.divide(new BigDecimal(tenure), mc).setScale(2, roundingMode); amortizedInterest = principal.multiply(rateMonth).setScale(2, roundingMode); interest = amortizedInterest.multiply(new BigDecimal(tenure), mc).setScale(2, roundingMode); //create LoanDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items outstandingPrincipal = principal; for (int i = 0; i < tenure; i++) { outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); if (i == tenure - 1) { result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipal), amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } else { result.getRepayments().add(new Repayment(amortizedPrincipal, amortizedInterest, outstandingPrincipal, DateUtils.offset(asOfDate, new Duration(0, i + 1, 0)))); } } break; case YearlyInterest: //times of repayments. tenure = duration.getYears(); //calc amortized interest and interest amortizedInterest = principal.multiply(rateYear).setScale(2, roundingMode); interest = amortizedInterest.multiply(new BigDecimal(tenure), mc).setScale(2, roundingMode); //create LoanDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //add amortized items for (int i = 0; i < tenure; i++) { if (i < tenure - 1) { //only interest, no principal result.getRepayments().add(new Repayment(ZERO, amortizedInterest, principal, DateUtils.offset(asOfDate, new Duration(i + 1, 0, 0)))); } else { //last ONE we pay off the principal as well as interest result.getRepayments().add(new Repayment(principal, amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(i + 1, 0, 0)))); } } break; } return result; } /** * ???. * * @param amount * @param duration * @param rate 2400 means 24.00% * @param method * @param asOfDate ?131?: 228(29)?331?430 * @param period * @param repayDateOfMonth * @return */ public static WealthProductRepaymentDetail analyze(final BigDecimal amount, final Duration duration, final int rate, final RepaymentMethod method, final LocalDate asOfDate, final RepaymentPeriod period, final int repayDateOfMonth) { if (period == null) { return analyze(amount, duration, rate, method, asOfDate); } if (repayDateOfMonth == 0) { return analyze(amount, duration, rate, method, asOfDate, period); } if (!method.isExtensible()) { throw new IllegalArgumentException(method + "is not extensible repayment."); } if (!method.equals(BulletRepayment)) {//??? //periodRepaymentMethod??? ValidateResult validateResult = validatePeriodAndDuration(period, duration); if (!validateResult.isSuccess()) { throw new IllegalArgumentException(validateResult.getMessage()); } } WealthProductRepaymentDetail result = null; //principal BigDecimal principal = amount; //now get rates BigDecimal rateYear = new BigDecimal(rate).divide(rateScale, mc); BigDecimal rateMonth = rateYear.divide(monthsPerYear, mc); BigDecimal ratePeriod = rateMonth.multiply(new BigDecimal(period.getMonthsOfPeriod())); //?? int fixDays = repayDateOfMonth - asOfDate.getDayOfMonth(); int fixMonth = asOfDate.getMonthOfYear() - 1; //31??31? LocalDate fixAsOfDate = new LocalDate(asOfDate.getYear(), 1, repayDateOfMonth); if (fixDays < 0) { fixAsOfDate = fixAsOfDate.plusMonths(1); } //dealing with different methods BigDecimal interest, amortizedInterest, amortizedPrincipal, outstandingPrincipal; int tenure; switch (method) { case BulletRepayment: //??? analyze(amount, duration, rate, method, asOfDate); break; case MonthlyInterest: //period? tenure = duration.getTotalMonths() / period.getMonthsOfPeriod(); amortizedInterest = principal.multiply(ratePeriod).setScale(2, NumberConstant.ROUNDING_MODE); interest = amortizedInterest.multiply(new BigDecimal(tenure)); //create result result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //add amortized items for (int i = 0; i < tenure; i++) { if (i < tenure - 1) { //only interest, no principal result.getRepayments() .add(new Repayment(ZERO, amortizedInterest, principal, DateUtils.offset(fixAsOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod() + fixMonth, 0)))); } else { //last ONE we pay off the principal as well as interest result.getRepayments().add(new Repayment(principal, amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } } break; case EqualInstallment: //period?? //times of repayments in months tenure = (duration.getYears() * 12 + duration.getMonths()) / period.getMonthsOfPeriod(); BigDecimal[] is = new BigDecimal[tenure + 1]; for (int i = 0; i <= tenure; i++) { is[i] = ratePeriod.add(ONE).pow(i); } BigDecimal baseInterest = principal.multiply(ratePeriod); //calc installment BigDecimal installment = baseInterest.multiply(is[tenure]).divide(is[tenure].subtract(ONE), mc); installment = installment.setScale(2, NumberConstant.ROUNDING_MODE); //reset total interest interest = ZERO; //create WealthProductRepaymentDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items outstandingPrincipal = principal; for (int i = 0; i < tenure; i++) { amortizedInterest = baseInterest.subtract(installment, mc).multiply(is[i]).add(installment, mc) .setScale(2, NumberConstant.ROUNDING_MODE); amortizedPrincipal = installment.subtract(amortizedInterest); outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); if (i == tenure - 1) { //last ONE we need to fix the rounding error and let the oustanding principal be ZERO result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipal), amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } else { result.getRepayments() .add(new Repayment(amortizedPrincipal, amortizedInterest, outstandingPrincipal, DateUtils.offset(fixAsOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod() + fixMonth, 0)))); } interest = interest.add(amortizedInterest); } //fix interest result.setInterest(interest); break; case EqualPrincipal: //period? //times of repayments in months tenure = (duration.getYears() * 12 + duration.getMonths()) / period.getMonthsOfPeriod(); //calc amortized principal first amortizedPrincipal = principal.divide(new BigDecimal(tenure), mc).setScale(2, NumberConstant.ROUNDING_MODE); //calc by each month BigDecimal[] interests = new BigDecimal[tenure]; BigDecimal[] outstandingPrincipals = new BigDecimal[tenure]; outstandingPrincipal = principal; interest = ZERO; for (int i = 0; i < tenure; i++) { interests[i] = outstandingPrincipal.multiply(ratePeriod, mc).setScale(2, NumberConstant.ROUNDING_MODE); interest = interest.add(interests[i]); outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); outstandingPrincipals[i] = outstandingPrincipal; } //create WealthProductRepaymentDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items for (int i = 0; i < tenure; i++) { if (i == tenure - 1) { result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipals[i]), interests[i], ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } else { result.getRepayments() .add(new Repayment(amortizedPrincipal, interests[i], outstandingPrincipals[i], DateUtils.offset(fixAsOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod() + fixMonth, 0)))); } } break; case EqualInterest: //period? //times of repayments in months tenure = (duration.getYears() * 12 + duration.getMonths()) / period.getMonthsOfPeriod(); //calc amortized principal and interest amortizedPrincipal = principal.divide(new BigDecimal(tenure), mc).setScale(2, NumberConstant.ROUNDING_MODE); amortizedInterest = principal.multiply(ratePeriod).setScale(2, NumberConstant.ROUNDING_MODE); interest = amortizedInterest.multiply(new BigDecimal(tenure), mc).setScale(2, NumberConstant.ROUNDING_MODE); //create WealthProductRepaymentDetail result = new WealthProductRepaymentDetail(principal, interest, duration, method, new ArrayList<Repayment>()); //deal with amortized items outstandingPrincipal = principal; for (int i = 0; i < tenure; i++) { outstandingPrincipal = outstandingPrincipal.subtract(amortizedPrincipal); if (i == tenure - 1) { result.getRepayments().add(new Repayment(amortizedPrincipal.add(outstandingPrincipal), amortizedInterest, ZERO, DateUtils.offset(asOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod(), 0)))); } else { result.getRepayments() .add(new Repayment(amortizedPrincipal, amortizedInterest, outstandingPrincipal, DateUtils.offset(fixAsOfDate, new Duration(0, (i + 1) * period.getMonthsOfPeriod() + fixMonth, 0)))); } } break; } return result; } /** * ? * * @param amount ? * @param rate 240024% * @param duration ? * @return */ public static BigDecimal quickInterest(int amount, int rate, Duration duration) { return quickInterest(new BigDecimal(amount), rate, duration); } /** * ? * * @param amount ? * @param rate 240024% * @param duration ? * @return */ public static BigDecimal quickInterest(BigDecimal amount, int rate, Duration duration) { //rates BigDecimal rateYear = new BigDecimal(rate).divide(rateScale, mc); BigDecimal rateMonth = rateYear.divide(monthsPerYear, mc); BigDecimal rateDay = rateYear.divide(daysPerYear, mc); //calc BigDecimal interest = amount.multiply(rateYear).multiply(new BigDecimal(duration.getYears())); //add monthly interest interest = interest.add(amount.multiply(rateMonth).multiply(new BigDecimal(duration.getMonths()))); //add daily interest interest = interest.add(amount.multiply(rateDay).multiply(new BigDecimal(duration.getDays()))); //return return interest.setScale(2, NumberConstant.ROUNDING_MODE); } public static ValidateResult validatePeriodAndDuration(RepaymentPeriod period, Duration duration) { ValidateResult result = new ValidateResult(false, "", ""); if (period == null) { result.setSuccess(true); return result; } if (duration == null) { result.setMessage("???"); return result; } if (duration.getDays() > 0) { result.setMessage("??RepaymentPeriod???"); return result; } int totalMonths = duration.getTotalMonths(); int monthsOfPeriod = period.getMonthsOfPeriod(); switch (period) { case Monthly: result.setSuccess(true); return result; default: if (totalMonths % monthsOfPeriod == 0) { result.setSuccess(true); } else { result.setMessage(String.format(" [%s] ? [%s] ??", period.getKey(), totalMonths)); } return result; } } }