How can I implement a function in Java that performs the exact same calculation as Excel's YIELD function ( the docs). I looked at the specification of the YIELD function and tried to implement it myself, but I get different results when using the following inputs:
Settlement: 21/04/2025
Maturity: 08/12/2027
Rate: 0.06875
Price: 94.28
Redemption: 100
Frequency: 1
Gives 9.499084% but I expected 9.723637 %
Here is MY code:
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public static BigDecimal rendementTitre(
LocalDate settlementDate,
LocalDate maturityDate,
BigDecimal taux,
BigDecimal prix,
BigDecimal valeurNominale,
int frequence,
MathContext mc,
BigDecimal tol,
int maxIter
) {
long daysBetween = ChronoUnit.DAYS.between(settlementDate, maturityDate);
BigDecimal years = new BigDecimal(daysBetween).divide(BigDecimal.valueOf(365), mc);
int n = years.multiply(BigDecimal.valueOf(frequence)).setScale(0, RoundingMode.HALF_UP).intValue();
BigDecimal coupon = valeurNominale.multiply(taux).divide(BigDecimal.valueOf(frequence), mc);
// Initial guess
BigDecimal y = taux;
for (int iter = 0; iter < maxIter; iter++) {
BigDecimal f = bondPrice(y, n, coupon, valeurNominale, frequence, mc).subtract(prix, mc);
BigDecimal fPrime = bondPriceDerivative(y, n, coupon, valeurNominale, frequence, mc);
if (fPrime.abs().compareTo(new BigDecimal("1E-10")) < 0) break;
BigDecimal yNew = y.subtract(f.divide(fPrime, mc), mc);
if (y.subtract(yNew).abs().compareTo(tol) < 0) {
System.out.println("Rendement : " + yNew + " YY%");
return yNew.multiply(BigDecimal.valueOf(100)).setScale(6, RoundingMode.HALF_UP); // % avec 6 décimales
}
y = yNew;
}
throw new RuntimeException("La convergence n'a pas été atteinte");
}
private static BigDecimal bondPrice(BigDecimal y, int n, BigDecimal C, BigDecimal V, int f, MathContext mc) {
BigDecimal total = BigDecimal.ZERO;
BigDecimal onePlusRate = BigDecimal.ONE.add(y.divide(BigDecimal.valueOf(f), mc));
for (int i = 1; i <= n; i++) {
BigDecimal denom = onePlusRate.pow(i, mc);
total = total.add(C.divide(denom, mc), mc);
}
BigDecimal lastDenom = onePlusRate.pow(n, mc);
total = total.add(V.divide(lastDenom, mc), mc);
return total;
}
private static BigDecimal bondPriceDerivative(BigDecimal y, int n, BigDecimal C, BigDecimal V, int f, MathContext mc) {
BigDecimal total = BigDecimal.ZERO;
BigDecimal onePlusRate = BigDecimal.ONE.add(y.divide(BigDecimal.valueOf(f), mc));
for (int i = 1; i <= n; i++) {
BigDecimal denom = onePlusRate.pow(i + 1, mc);
BigDecimal term = BigDecimal.valueOf(-i).multiply(C).divide(BigDecimal.valueOf(f), mc).divide(denom, mc);
total = total.add(term, mc);
}
BigDecimal lastTerm = BigDecimal.valueOf(-n).multiply(V).divide(BigDecimal.valueOf(f), mc)
.divide(onePlusRate.pow(n + 1, mc), mc);
total = total.add(lastTerm, mc);
return total;
}
public static void main(String[] args) {
LocalDate settlement = LocalDate.now();
LocalDate maturity = LocalDate.of(2027, 12, 8);
BigDecimal taux = new BigDecimal("0.06875");
BigDecimal prix = new BigDecimal("94.28");
BigDecimal valeurNominale = new BigDecimal("100");
MathContext mc = new MathContext(20, RoundingMode.HALF_UP);
BigDecimal tol = new BigDecimal("1E-8");
BigDecimal rendement = rendementTitre(
settlement,
maturity,
taux,
prix,
valeurNominale,
2, // Semestriel
mc,
tol,
100
);
System.out.println("Rendement : " + rendement + " %");
}
}
with this exemple the consol print 9.499084 %
but the Excel show 9.723637%
YieldToMaturity
do it? From JavaMoney project.