Enter the annualized spot rates for each period separated by commas.
For a 2.5-year Semi-Annual bond, enter 5 rates (6mo, 1yr, 1.5yr, 2yr, 2.5yr).
Calculated Bond Price:–
Total Periods:–
Periodic Coupon Payment:–
Valuation Status:–
About Spot Rate Bond Pricing
Unlike simple Yield to Maturity (YTM) calculations which assume a single discount rate for all cash flows, the Spot Rate Bond Calculator determines the arbitrage-free value of a bond by discounting each individual cash flow by the specific spot rate (zero-coupon rate) corresponding to its timing.
Why Use Spot Rates?
The term structure of interest rates (the yield curve) is rarely flat. Interest rates for money lent for 6 months are usually different from rates for money lent for 10 years. By using spot rates:
Accuracy: You account for the specific market cost of capital for each specific time period.
Arbitrage-Free: This method ensures the bond price is consistent with the prices of zero-coupon bonds trading in the market (strips).
Better Valuation: It reveals whether a bond is trading cheap or expensive relative to the theoretical theoretical spot curve.
How It Works
The calculation involves the following steps:
Identify Cash Flows: Determine the periodic coupon payments and the final principal repayment.
Match Spot Rates: Assign the correct annualized spot rate ($S_t$) to each period $t$.
Discount: Calculate the Present Value (PV) of each cash flow using the formula:
PV = Cash Flow / (1 + Spot Rate / Frequency)^Period
Sum: Add all Present Values together to get the total Bond Price.
Example Calculation
Consider a $1,000 Face Value bond with a 5% annual coupon paid annually, maturing in 2 years.
function calculateSpotPrice() {
// Clear previous results and errors
document.getElementById('errorBox').style.display = 'none';
document.getElementById('result-box').style.display = 'none';
// Get Inputs
var faceValueInput = document.getElementById('faceValue').value;
var couponRateInput = document.getElementById('couponRate').value;
var freqInput = document.getElementById('paymentFreq').value;
var spotRatesString = document.getElementById('spotRates').value;
// Validation
if (!faceValueInput || !couponRateInput || !spotRatesString) {
showError("Please fill in all fields.");
return;
}
var faceValue = parseFloat(faceValueInput);
var couponRate = parseFloat(couponRateInput) / 100; // Convert to decimal
var frequency = parseInt(freqInput);
if (isNaN(faceValue) || isNaN(couponRate)) {
showError("Please enter valid numbers for Face Value and Coupon Rate.");
return;
}
// Parse Spot Rates
// Remove spaces and split by comma
var ratesArray = spotRatesString.split(',').map(function(item) {
return parseFloat(item.trim());
});
// Check for invalid entries in the rates array
for (var i = 0; i < ratesArray.length; i++) {
if (isNaN(ratesArray[i])) {
showError("Invalid spot rate format. Please use numbers separated by commas (e.g., 2.5, 3.0).");
return;
}
}
if (ratesArray.length === 0) {
showError("Please enter at least one spot rate.");
return;
}
// Calculation Logic
var totalPeriods = ratesArray.length;
var periodicCoupon = (faceValue * couponRate) / frequency;
var totalPrice = 0;
// Loop through each period defined by the number of spot rates entered
for (var t = 1; t <= totalPeriods; t++) {
var spotRateAnnual = ratesArray[t-1] / 100; // Get rate for this period (index is t-1)
var periodicSpotRate = spotRateAnnual / frequency; // Adjust for frequency
var cashFlow = periodicCoupon;
// If it's the last period, add the Face Value
if (t === totalPeriods) {
cashFlow += faceValue;
}
// Discount Factor calculation
var discountFactor = Math.pow(1 + periodicSpotRate, t);
var presentValue = cashFlow / discountFactor;
totalPrice += presentValue;
}
// Determine Status (Premium, Discount, Par)
var status = "";
if (Math.abs(totalPrice – faceValue) faceValue) {
status = "Trading at Premium";
} else {
status = "Trading at Discount";
}
// Display Results
document.getElementById('finalPrice').innerText = formatCurrency(totalPrice);
document.getElementById('totalPeriods').innerText = totalPeriods;
document.getElementById('periodicPayment').innerText = formatCurrency(periodicCoupon);
document.getElementById('bondStatus').innerText = status;
document.getElementById('result-box').style.display = 'block';
}
function showError(msg) {
var errorBox = document.getElementById('errorBox');
errorBox.innerText = msg;
errorBox.style.display = 'block';
}
function formatCurrency(num) {
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(num);
}