Skip to main content

Chapter 2 · Quantitative Finance

Loans and Bonds: The Mechanics of Borrowing and Default Risk

A quantitative guide to loan amortization and bond pricing. Learn to derive payment formulas, simulate default risk, and understand coupon rates using Python.

By Updated 13 min Intermediate

Illustration explaining how loans and bonds work, including interest, repayment, and default risk
On this page

In Chapter 1 we learned to think about a single uncertain bet, meaning its expected value, its variance, and the gap between the two. A loan is where that abstract idea finally meets a contract. It is the simplest instrument in finance and the first thing in this series that we can genuinely put a price on. Given an amount, a rate, and a term, we can ask what the loan costs and what happens if the borrower can’t pay. Once we can answer that, bonds, tranches, and yield curves all turn out to be variations on the same theme.

Picture a young couple, Sara and Tom, who have been dreaming about owning their first home. They have a modest savings account and a lot of determination, and they finally walk into a bank to talk about a mortgage. It is a real turning point for them, because the money they borrow is what lets them move into the house where they plan to raise their family. Loans like this sit right at the core of how a modern economy works. A family buying a house, a company expanding, a government funding a public project, all of them rely on loans to get the upfront capital they need to make something happen. Most people understand the basic idea of borrowing money and paying it back with interest. What tends to get skipped over is the machinery underneath, and how that machinery gets reassembled into more complicated instruments. So in this post we start by breaking down what a loan actually is and how a lender manages the risk of handing over money. From there we build towards bonds, which are really just loans that have been packaged up, traded, and scaled for markets. Getting comfortable with both gives us the mental framework we need before we touch anything more complicated.

How a Loan Repayment Schedule Works

At its core a loan is simple. We borrow some money and pay it back over time, and the repayment schedule is not hard to reason about. Say we take out a mortgage of €300,000€300,000 and plan to repay it in 300300 equal monthly payments at an interest rate of 5%5\% per year, which works out to about 0.417%0.417\% per month.

We can get a rough feel for the monthly payment using nothing but intuition. The lower bound is €300,000∗0.417%=€1250€300,000 * 0.417\% = €1250, since that is what we pay in interest each month before touching the principal at all. The upper bound is €1250+€300,000300=€2250€1250 + \frac{€300,000}{300} = €2250, which would cover the whole loan including interest charged on the full amount the entire time.

The real answer sits somewhere in between. Suppose we estimate the principal portion at around €1000€1000 a month and the interest at around €625€625. We get that interest figure by taking €1250€1250 and halving it, on the reasoning that the interest shrinks steadily towards zero over the life of the loan. That puts our monthly payment near €1625€1625. But is that guess actually fair? Let’s work it out properly.

def loan_payment(X, r, N):
    """
    Compute the fixed payment amount A for a loan.
    X: initial loan amount
    r: interest rate per period (e.g., 0.05 for 5%)
    N: number of payments
    """
    if r == 0:
        return X / N
    return X * (r * (1 + r)**N) / ((1 + r)**N - 1)

def simulate_loan(X, r, N):
    """
    Simulate the loan balance D_k using the difference equation.
    """
    A = loan_payment(X, r, N)
    D = [X]  # D_0 = X

    for k in range(N):
        D_next = (1 + r) * D[-1] - A
        D.append(D_next)

    return A, D

def main():
    # Parameters
    X = 300000.0   # initial amount borrowed
    annual_rate = 0.05
    payments_per_year = 12
    years = 25

    # Convert to rate per period and number of periods
    r = annual_rate / payments_per_year
    N = payments_per_year * years

    A, D = simulate_loan(X, r, N)

    print(f"Loan amount (X): {X:,.2f}")
    print(f"Interest rate per period (r): {r:.6f}")
    print(f"Number of payments (N): {N}")
    print(f"Payment per period (A): {A:,.2f}\n")

    print("k\tOutstanding Balance (D_k)")
    for k, balance in enumerate(D):
        print(f"{k}\t{balance:,.2f}")

if __name__ == "__main__":
    main()

Deriving the Loan Payment Formula

The script gives us a monthly payment of €1753.77, which is close to our guess but not identical. Here is how we get there properly. We start with the amount borrowed, €X€X. We repay it by paying €A€A at time N+ΔTN + \Delta T, then again at N+2ΔTN + 2 \Delta T, and so on until we reach time NΔTN \Delta T, at which point we owe nothing. Let DkD_{k} be the amount we still owe at the kk-th payment. We know that D0=€XD_{0} = €X and DN=0D_{N} = 0, so what we actually have on our hands is a difference equation. To summarise the pieces:

SymbolMeaningUnits
DkD_{k}amount we owe at the KthK_{th} paymentCurrency
AAPayment per periodCurrency
NNNumber of payments
ΔT\Delta TTime between paymentsTime
rrInterest rate per period
NNPrincipal (amount borrowed at time t)Currency

We can look at what happens between two consecutive payments. At the start of period kk you owe DkD_{k}. So during the next time interval ΔT\Delta T:

  • The loan accrues interest at rate rr, so it grows to Dk(1+r)D_k(1+r).
  • Then you make a payment AA, reducing your balance.

Thus the amount owed after the next payment (just after (k+1)ΔT(k+1) \Delta T ) is:

Dk+1=(1+r)Dk−AD_{k+1} = (1+r) D_k - A

Now we need to find a closed form for DkD_k which is a non-homogeneous linear recurrence relation. We know the boundary conditions of our scenario. The initial condition is us owing the full loan at D0=XD_0=X, and the final condition is us owing noting after NN payments at DN=0D_N=0. Knowing this we can solve for AA:

0=(1+r)NX−A(1+r)N−1r0 = (1 + r)^N X - A \frac{(1 + r)^{N}-1}{r}

So,

A=Xr(1+r)N(1+r)N−1A = X \frac{r(1+r)^N}{(1+r)^N-1}

Given the fixed payment per period we will have completely repaid the loan of amount XX after NN equal payments.

Why Early Payments Are Almost All Interest

The formula is exact, but it hides something that every borrower feels each month. The payment itself is fixed, and yet what it is made of shifts dramatically over the life of the loan. Early on, almost the whole payment is interest on a large outstanding balance, and it is only near the end that you are really paying down what you borrowed. Drag the sliders below and watch the orange interest area drain away into the blue principal area. Then drag the default marker. The balance still owed at that point is the lender’s exposure, and it is the number that turns this clean formula into a genuine risk. That risk is what the rest of the chapter is about.

AmortizationSchedule · where the payment goes300 payments
Monthly payment
€1,754
Total interest paid
€226,121
75% of what you borrowed
Total repaid
€526,131

Every monthly payment is the same size, but its makeup is not. Early on you are almost entirely paying interest (orange); only late do you meaningfully pay down what you borrowed (blue). Slide the default marker to see what the lender is still owed if the borrower stops paying — the number that makes a loan risky.

For a €300,000 loan at 5% over 25 years, the fixed €1,754 monthly payment starts as almost pure interest and only crosses over to mostly principal around year 17.
A fixed payment, a shifting split: interest first, principal last. The default marker shows what the lender stands to lose.

What Is a Bond?

If loans are the backbone of personal and corporate finance, bonds are their large-scale, institutional cousin. A loan is usually a direct agreement between one borrower and one lender, and a bond takes that same idea and opens it up to the financial markets. In this section we look at what bonds are, how they work, and why they matter so much for funding governments and corporations. It is one of the building blocks we will keep leaning on as the models get more involved.

Put simply, a bond is a loan that gets sold to an investor. There is a lot going on in one quantitatively. Their value moves with interest rates, they often carry embedded options that let them be repaid or called back early, and sometimes they are issued in a foreign currency, which drags foreign exchange risk into the picture. All of that is interesting, but the single most important risk we need to pin down is whether the borrower can actually repay the money.

Let’s start by unpacking a simple bond. The borrower receives €X€X today, and in exchange promises to repay €X(1+c)€X(1+c) at time TT, where cc is the coupon rate and TT is the bond’s term. The obvious question is what a fair value for cc should be. How much should we be compensated for lending out our money? To answer that we first need the risk-free rate rr, which is the rate that in theory carries no risk at all, because the borrower is certain to repay. Think of the risk-free rate as the floor on any investment. Whatever we put our money into, the expected return has to clear that floor, because if it doesn’t, there is no way to justify taking on the extra risk. In a perfect world with no chance of default, a fair coupon would simply be c=rc = r.

Pricing Default Risk into the Coupon Rate

The real world does have default, so to find a fair cc we need to account for it. A simple model says that at time TT the bond either defaults or is repaid in full. If it defaults, the investor recovers only a fraction RR of what they were owed. So with probability pp the bondholder receives RX(1+c)RX(1+c) at time TT, and with probability 1−p1-p they receive X(1+c)X(1+c). A coupon is fair when, on average, the investor gets back their principal adjusted for the time value of money, and when the lender does no worse than they would have done lending at the risk-free rate. We can write that condition down directly:

pRX(1+c)+(1−p)X(1+c)≥X(1+r)pRX(1+c)+(1-p)X(1+c) \geq X(1+r)

Or,

pR(1+c)+(1−p)(1+c)≥(1+r)pR(1+c)+(1-p)(1+c) \geq (1+r)

Or even simpler:

cfair=r+p(1−R)1−p(1−R)c_{fair} = \frac{r+p(1-R)}{1-p(1-R)}

At this coupon the lender breaks even on average, once you account for both the time value of money and the risk of default. But breaking even is not much of a pitch. Who lends money purely to come out flat? In practice investors demand a coupon well above the fair value, both to be paid for the risk and to give themselves a real shot at making money. The gap between the fair coupon and the coupon actually paid usually comes down to negotiation between the borrower and the lender.

Simulating Default Risk Across a Bond Portfolio

There is another way to deal with the risk that doesn’t involve raising the coupon at all. Instead of leaning on a single borrower, we can spread the money across many different borrowers, each with their own independent chance of defaulting. To see what that does to our risk, let’s simulate ten separate loans and watch how the outcomes spread out. We will use the following fixed parameters:

ParamValue
X€1000
r3%
p5%
R0
import random
import math

def main():
    trials = 100
    num_loans = 10
    X = 1000.0
    r = 0.03
    p = 0.05
    R = 0.0

    # Coupon rate
    c = (r + p * (1 - R)) / (1 - p * (1 - R))

    returns = []

    for _ in range(trials):
        portfolio_return = 0.0

        for _ in range(num_loans):
            defaults = random.random() < p  # Bernoulli(p)

            if defaults:
                portfolio_return += 0.0
            else:
                portfolio_return += X * (1.0 + c)

        total_initial = num_loans * X
        yearly_return = (portfolio_return - total_initial) / total_initial
        returns.append(yearly_return)

    # Statistics
    mean = sum(returns) / trials
    best = max(returns)
    worst = min(returns)

    variance = sum((r - mean) ** 2 for r in returns) / trials
    stddev = math.sqrt(variance)

    # Output
    print(f"Bond Portfolio Simulation Results after {trials} trials with coupon: {c * 100:.2f}%")
    print("--------------------------------------------")
    print(f"Average return:      {mean * 100:.2f} %")
    print(f"Best-case return:    {best * 100:.2f} %")
    print(f"Worst-case return:   {worst * 100:.2f} %")
    print(f"Standard deviation:  {stddev * 100:.2f} %")

if __name__ == "__main__":
    main()

The result tells a clear story. On average our return beats the risk-free rate, though not by a huge margin. The bigger change is at the bottom end. With a single borrower, the worst case is losing everything, a 100% loss. Spreading the same money across ten borrowers pulls that worst case up to around -24%. That is the diversification argument from Chapter 1 showing up again, and it naturally raises the question of what else we could do. Tools like collateral or credit default swaps can add further layers of protection against the worst outcomes, and folding those in would give us even more control over the risk we carry.

From One Loan to a Pool of Them

Loans and bonds can look like everyday, almost boring financial tools, but underneath they run on clear mathematical structure and carefully managed risk. By pulling apart how a simple loan works, how the payment is determined, and how interest reshapes the repayment over time, we have laid the groundwork for the larger and more complex instruments still to come.

Bonds follow the same principle as a loan and then scale it up to the markets. Their pricing hangs on a handful of factors we touched on here, including credit risk, recovery assumptions, and how exposure is diversified across many borrowers. As the simulation showed, spreading risk across a portfolio of independent loans cuts the worst-case outcomes dramatically, which is exactly why diversification sits so close to the heart of modern finance.

In the end, understanding loans and bonds buys us more than the ability to calculate a payment or a yield. It builds the mental framework we need to make sense of the financial system as a whole. With that in place, we can start reaching for more complicated instruments.

Next up is Chapter 3: Life in the Tranches. We take the pool of loans we just built, cut its cash flows into layers of priority, and engineer a “safe” senior slice out of genuinely risky collateral. This is the exact machine that turned subprime mortgages into AAA-rated bonds. We will build the loss waterfall step by step, and then we will break it.

⚠️ Financial Education Disclaimer

This post discusses the mathematical foundations of loans, mortgages, and bonds for educational and research purposes only.

  • Not Financial Advice: The calculations, Python simulations, and scenarios (including the example mortgage) are illustrative. They do not constitute professional financial advice or a recommendation for any specific loan product.
  • Model Simplifications: These models assume fixed interest rates and specific compounding frequencies. Real-world loans may include variable rates, taxes, insurance (PITI), and origination fees not captured in these scripts.
  • Market Risk: Bond pricing and default probabilities (pp) are subject to extreme market volatility. Past performance and statistical averages do not guarantee future results.
  • Consult a Professional: Always speak with a certified financial advisor or mortgage specialist before entering into a significant debt obligation.
Subscribe / Stdin

Get the next part

New parts of Finding P values and false hope land in your inbox the day they publish. No spam, unsubscribe in one click.

We send your address to Kit, our email provider, and ask you to confirm by email before anything is sent. See our Privacy Statement.