---
title: "1.2.7 Fixed income, rates and leveraged products"
description: "Bonds, yield, duration and convexity; the yield curve and its inversion; interest rate futures; and CFDs and leveraged products for EU retail clients."
url: "https://pythonparatrading.com/en/python-trading-course/level-1/fixed-income-rates-and-leveraged-products/"
lang: "en"
image: "https://pythonparatrading.com/og/en/python-trading-course/level-1/fixed-income-rates-and-leveraged-products.png"
---

# 1.2.7 · Fixed income and interest rates; CFDs and leveraged products

> The interest rate is the price of money and it shows up in every other lesson: in the carry of futures, the forex rollover or options parity. This lesson studies it in its own market, the bonds and yield curve of a fictitious country, and ends with CFDs, the most common leveraged product among retail traders in Europe.

- Course: [Level 1: Financial data, markets and analysis](https://pythonparatrading.com/en/python-trading-course/level-1/)
- Module: 1.2 · Asset classes
- Duration: 60 min
- Prerequisites: [1.1.5 · Costs, settlement, leverage and margin](https://pythonparatrading.com/en/python-trading-course/level-1/trading-costs-settlement-leverage-and-margin/), [1.2.3 · Futures: specifications, margin, roll and contango](https://pythonparatrading.com/en/python-trading-course/level-1/futures-contract-specs-margin-and-roll/)

## By the end of this lesson

- Compute a bond's price from its cash flows and its yield, and the yield from the price.
- Measure with modified duration, DV01 and convexity how much a bond's price changes when rates move.
- Read a yield curve, measure its 2s10s slope and find the periods of inverted curve in the data.
- Explain how a short-term interest rate future and a bond future work, and how CFDs for retail clients work and what ESMA limits.

## The price of money

The interest rate shows up, in one form or another, in every market in this module: in the theoretical price of a
future (lesson 1.2.3), in the rollover of a forex position (1.2.4), in options put-call parity (1.2.5) and in the
financing of any leveraged position (1.1.5). Its own market is **fixed income**: the bonds that governments and
companies issue to borrow. A stock trading system does not need to trade bonds, but it does need to understand what
moves rates, because they move the value of everything else.

```mermaid
flowchart TB
  BC[Central bank] --> TO[Policy rate]
  TO --> EX[Expectations]
  EX --> CU[Yield curve]
  CU --> DE[Value of cash flows]
  CU --> CA[Futures carry]
  CU --> FI[Financing]
```

The central bank sets the **policy rate**, the very short-term rate. Rates at longer tenors depend on what the market
expects that policy rate to do in the future, plus a **term premium** that compensates for the risk of lending long.
Those rates are the ones used to discount future cash flows (which is why stock valuations rise and fall), to compute
the carry of futures and to charge financing on margin positions.

## Inside a bond

A fixed-coupon **bond** pays periodic interest (the **coupon**, as a percentage of face value) and repays the face
value at maturity. Its price is the present value of those cash flows, discounted at the **yield to maturity** $y$, the
single rate that equates price and cash flows. With annual coupons $C$, face value 100 and $n$ years:

$$
P = \sum_{t=1}^{n} \frac{C}{(1 + y)^t} + \frac{100}{(1 + y)^n}
$$

The example is a 10-year bond of Valdoria, the fictitious country in the level's data, with a 3% annual coupon:

```python
import numpy as np
import pandas as pd
from scipy.optimize import brentq

def flujos_bono(cupon, anos, nominal=100.0):
    """Annual cash flows: coupons and, in the last year, face value."""
    t = np.arange(1, anos + 1)
    f = np.full(anos, cupon * nominal)
    f[-1] += nominal
    return t, f

def precio_bono(y, cupon, anos):
    t, f = flujos_bono(cupon, anos)
    return float((f / (1 + y) ** t).sum())

t, f = flujos_bono(0.03, 10)
print("cash flows:", f)
for y in (0.02, 0.03, 0.04):
    precio = precio_bono(y, 0.03, 10)
    print(f"yield {y:.0%}: price {precio:.3f}")
```
```output
cash flows: [  3.   3.   3.   3.   3.   3.   3.   3.   3. 103.]
yield 2%: price 108.983
yield 3%: price 100.000
yield 4%: price 91.889
```

If the market yield matches the coupon, the bond trades **at par** (100). If market rates are higher than its coupon,
nobody buys it at 100: its price falls until buying it yields the same as a new bond (it trades **below par**). If they
are lower, it rises (**above par**). Price and yield always move in opposite directions.

In the market the price is observed, and the yield is inferred by solving the equation the other way around. It has no
closed-form solution, but it does have a single root, which is found numerically:

```python
def rentabilidad(precio, cupon, anos):
    return brentq(lambda y: precio_bono(y, cupon, anos) - precio,
                  -0.05, 0.5)

curva = pd.read_csv("curva_tipos_diaria.csv", parse_dates=["fecha"],
                    index_col="fecha")
y10 = curva.loc["2025-12-31", "10a"] / 100
p_hoy = precio_bono(y10, 0.03, 10)
print(f"Valdoria 10-year on 31-12-2025: {y10:.3%}")
print(f"Price of the 3% bond: {p_hoy:.3f}")
print(f"Yield inferred from that price: "
      f"{rentabilidad(p_hoy, 0.03, 10):.3%}")
```
```output
Valdoria 10-year on 31-12-2025: 3.547%
Price of the 3% bond: 95.462
Yield inferred from that price: 3.547%
```

With the Valdoria 10-year at 3.547% at the end of 2025, the 3% bond trades at 95.462: below par, because its coupon is
lower than what the market pays. Starting from the price, the same yield is recovered. Here the bond is fictitious and
is valued with the curve's yield; with real bonds you also have to take into account accrued interest (the interest
accumulated since the last coupon, which the buyer pays on top of the “clean” price) and each market's day count
convention.

## Duration, DV01 and convexity

For a trader, the important question is how much the price changes if rates move. The first-order answer is the
**modified duration** $D_{mod}$:

$$
\frac{\Delta P}{P} \approx -D_{mod}\,\Delta y + \tfrac{1}{2}\,\text{Conv}\,(\Delta y)^2
$$

(Macaulay) duration is the average time of the cash flows, weighted by their present value; modified duration divides
it by $1 + y$. **DV01** is the price change for one basis point (0.01%) of yield, in money, and **convexity** corrects
for the curvature of the relation, which duration treats as if it were a straight line:

```python
def sensibilidades(y, cupon, anos):
    t, f = flujos_bono(cupon, anos)
    vp = f / (1 + y) ** t
    p = vp.sum()
    macaulay = (t * vp).sum() / p
    d_mod = macaulay / (1 + y)
    conv = (t * (t + 1) * vp).sum() / (p * (1 + y) ** 2)
    return p, macaulay, d_mod, conv

p, mac, dmod, conv = sensibilidades(y10, 0.03, 10)
print(f"Macaulay duration: {mac:.2f} years")
print(f"Modified duration: {dmod:.2f}")
print(f"Convexity:         {conv:.1f}")
print(f"DV01 per 1 M of face value: {dmod * p / 100 * 1e6 * 1e-4:,.0f}")
```
```output
Macaulay duration: 8.75 years
Modified duration: 8.45
Convexity:         85.7
DV01 per 1 M of face value: 807
```

The 10-year bond has a Macaulay duration of almost nine years: the coupons bring part of the money forward and pull the
average time below maturity. With a modified duration of 8.45, each basis point rise in yield takes 0.085% off the
price, 807 units per million of face value. For small moves duration is enough; for large ones, convexity matters:

```python
filas = []
for dy in (-0.02, -0.01, -0.001, 0.001, 0.01, 0.02):
    exacto = precio_bono(y10 + dy, 0.03, 10) / p - 1
    solo_dur = -dmod * dy
    con_conv = solo_dur + 0.5 * conv * dy ** 2
    filas.append({"cambio_pb": round(dy * 1e4),
                  "exacto_%": 100 * exacto,
                  "duracion_%": 100 * solo_dur,
                  "dur_conv_%": 100 * con_conv})
pd.DataFrame(filas).set_index("cambio_pb").round(3)
```
```output
           exacto_%  duracion_%  dur_conv_%
cambio_pb
-200         18.756      16.904      18.618
-100          8.897       8.452       8.881
-10           0.850       0.845       0.849
 10          -0.841      -0.845      -0.841
 100         -8.039      -8.452      -8.023
 200        -15.312     -16.904     -15.190
```

With 10 basis points, duration alone is off by less than a hundredth of a percentage point. With 200, by more than a
point and a half (−16.904% against −15.312% for a rise), and always in the same direction: the real price rises more
than duration says when rates fall and falls less when they rise. That asymmetry, convexity, is a favorable property
for the bondholder, and the figure draws it: the curve of price against yield always lies above the tangent line.

*Fig. — Price of a fictitious 10-year bond with a 3% annual coupon by yield, and the tangent line given by duration at the 3.547% Valdoria 10-year yield at the end of 2025.* Convex, decreasing curve: the price falls from 130 at zero yield to 100 at 3% and 66.45 at 8%. At the highlighted point, 95.46 at 3.547%, a dashed tangent line marks the duration approximation; the curve lies above it on both sides.

## The Valdoria yield curve

Each tenor has its yield. Joined together, they form the **yield curve**. The data are Valdoria's daily sovereign curve
between 2019 and 2025 at five tenors: 3 months, 2, 5, 10 and 30 years. The curve comes from expectations about
Valdoria's policy rate (`macro_vintages.csv`) plus a term premium, the same structure attributed to real curves.

```python
print(curva.shape)
fechas = ["2019-06-03", "2021-06-01", "2023-06-01", "2025-12-31"]
curva.loc[fechas]
```
```output
(1760, 5)
               3m     2a     5a    10a    30a
fecha
2019-06-03  2.322  1.425  2.100  2.659  2.972
2021-06-01  0.156  1.524  2.371  2.484  2.858
2023-06-01  4.992  4.305  3.450  3.363  3.337
2025-12-31  3.403  3.243  3.254  3.547  3.576
```

Four snapshots with four shapes. In June 2019, a positive slope from 2 years on, although with the 3-month above the
2-year: the market was already pricing in some cut to the policy rate. In June 2021, with the policy rate close to zero
after the 2020 crisis, the short end is pinned at zero and the curve is very steep. In June 2023, after the rate hikes,
the curve is **inverted**: the 3-month yields almost 5% and the 30-year, 3.3%. At the end of 2025, with cuts already
under way, the curve is almost flat again with a positive slope. The figure at the top draws the 2021 and 2023 curves
next to the end-of-2025 one.

## The slope and the inversion

The shape of the curve is summed up by its **slope**, usually the difference between the 10-year and the 2-year (the
**2s10s**), in basis points. Positive, the curve is normal; negative, inverted. An inverted curve says the market
expects future cuts to the policy rate: high short-term rates today and lower ones tomorrow. That is why many analysts
follow it as a leading signal of a slowdown; but, if the relation exists, it is statistical, with long and variable
lags, and it does not work as a clock.

```python
pendiente = 100 * (curva["10a"] - curva["2a"])  # basis points
invertida = pendiente < 0
print(f"Sessions with an inverted curve: {invertida.sum()} "
      f"of {len(curva)}")
tramos = (invertida != invertida.shift()).cumsum()
periodos = (pendiente[invertida].groupby(tramos[invertida])
            .agg(inicio=lambda s: s.index[0].date(),
                 fin=lambda s: s.index[-1].date(),
                 sesiones="size", minimo="min"))
periodos.reset_index(drop=True).round(1)
```
```output
Sessions with an inverted curve: 671 of 1760
       inicio         fin  sesiones  minimo
0  2022-06-06  2025-01-13       654   -98.6
1  2025-01-16  2025-01-27         7    -6.9
2  2025-01-31  2025-02-06         5    -6.2
3  2025-02-10  2025-02-11         2    -2.8
4  2025-02-14  2025-02-14         1    -0.1
5  2025-02-21  2025-02-24         2    -3.3
```

*Fig. — Valdoria curve 2s10s slope (10-year minus 2-year), last value of each week, 2019–2025. Shaded, the inverted-curve period.* Weekly line of the slope from 2019 to 2025: between +41 and +188 basis points from 2019 to 2021, peaking in 2020; it falls in 2022, turns negative in June 2022 and reaches a minimum of -98.6 basis points in late December; it stays negative until January 2025, with a few more days in February, and ends 2025 around +30. The inverted period, June 2022 to February 2025, is highlighted: 671 sessions.

The Valdoria curve inverts in June 2022, when the central bank raises rates quickly, hits its low of almost −100 basis
points at the end of 2022 and stays inverted for 654 consecutive sessions, until January 13, 2025; after that the
slope oscillates around zero, with five short inverted stretches (from one to seven sessions) until February 24. In
total, 671 sessions. The slope turns positive again when the central bank starts cutting rates and the 2-year falls
ahead of the 10-year.

The short end of the curve follows the policy rate very closely, because nobody lends for three months at a rate very
different from what the central bank pays for overnight money:

```python
macro = pd.read_csv("macro_vintages.csv",
                    parse_dates=["fecha_publicacion"])
oficial = (macro[macro["serie"] == "tipo_oficial"]
           .set_index("fecha_publicacion")["valor"])
fin_mes = curva["3m"].resample("ME").last()
fin_mes.index = fin_mes.index.normalize()
comp = pd.DataFrame({"oficial": oficial.resample("ME").last(),
                     "curva_3m": fin_mes}).dropna()
print(f"Monthly correlation: {comp.corr().iloc[0, 1]:.3f}")
comp.loc["2022-03":"2022-09"].round(3)
```
```output
Monthly correlation: 0.987
            oficial  curva_3m
2022-03-31     0.35     0.908
2022-04-30     0.35     1.311
2022-05-31     0.85     1.908
2022-06-30     1.60     2.269
2022-07-31     2.35     2.790
2022-08-31     2.35     3.242
2022-09-30     3.10     3.725
```

The 3-month runs ahead of the policy rate: it rises before each hike, because the market prices it in. It is the small
version of what the whole curve does, and the reason rate data are a **leading indicator** of central bank decisions
(lesson 1.5.2).

## Interest rate futures

Rates are also traded with futures, and they are among the most liquid contracts in the world. There are two
families:

- **Short-term interest rate futures**: they settle against the interest rate for a period (three months, for
  example). By convention they are quoted as **100 minus the rate**: a price of 96.25 implies a rate of 3.75%. That
  way, as with a bond, the price falls when the market expects higher rates. The strip of expiries of these futures is
  a curve of policy rate expectations.
- **Bond futures**: they refer to a notional bond and settle by delivering real bonds from a list. Eurex's Euro-Bund,
  for example, has a face value of 100,000 EUR on a notional German bond with a 6% coupon and a remaining life of
  between 8.5 and 10.5 years, with a tick of 0.01% (10 EUR)[^1]. Its price moves with the German 10-year yield
  according to the duration of the cheapest-to-deliver bond.

[^1]: Eurex, fixed income futures factsheet (link in the resources): contract value, notional coupon, remaining life of
the underlying and minimum price change of the Euro-Bund.

```python
# short-term future: 100 - rate, fictitious 1 M, 3-month contract
precio_stir = 100 - curva.loc["2025-12-31", "3m"]
print(f"Short-term future implied by the 3-month: {precio_stir:.3f}")
print(f"Basis point value: {1e6 * 1e-4 * 0.25:.0f} per contract")

# bond future: 6% notional, 10 years, 100,000 face value
p6, _, dmod6, _ = sensibilidades(y10, 0.06, 10)
print(f"6% notional bond: price {p6:.2f}, D_mod {dmod6:.2f}")
dv01_6 = dmod6 * p6 / 100 * 1e5 * 1e-4
print(f"DV01 of 100,000 face value: {dv01_6:.0f}")
```
```output
Short-term future implied by the 3-month: 96.597
Basis point value: 25 per contract
6% notional bond: price 120.35, D_mod 7.75
DV01 of 100,000 face value: 93
```

A short-term contract of one million and three months moves 25 units per basis point: 1,000,000 × 0.0001 × 0.25. A
6% 10-year notional bond, with a yield of 3.547%, trades well above par, at 120.35, with a modified duration of 7.75,
lower than that of the 3% bond (high coupons bring the cash flows forward); 100,000 of face value moves about 93 units
per basis point. With those numbers you can compute how many contracts hedge a bond portfolio or how much rate risk a
position carries. (The price of a real bond future also depends on the cheapest-to-deliver bond and its conversion
factor; here the order of magnitude is enough.)

## CFDs and leveraged products for retail clients

A **CFD** (contract for difference) is a contract between the client and a provider in which the difference in the
price of an underlying (a stock, an index, a currency, a bond, a commodity) between the opening and the closing of the
position is exchanged. The client does not buy the underlying: they open a position with the provider, who is their
counterparty, and post margin.

```mermaid
flowchart TB
  C[Retail client] -- margin --> P[CFD provider]
  P -- price and P&L --> C
  P --> D{Hedges?}
  D -- yes --> M[Real market]
  D -- no --> L[Own book]
```

Two consequences. First: the provider can hedge the position in the market or keep it, and in that case it gains what
the client loses, a conflict of interest that regulation requires to be managed. Second: CFDs with no expiry charge
**financing** every night on the notional of long positions (and pay or charge something on shorts), with a spread
over a reference rate. It is the same idea as perpetuals funding (lesson 1.2.6) or the forex rollover (1.2.4), and on
positions held for weeks it weighs heavily.

In 2018 ESMA restricted CFDs for retail clients across the EU because, according to the national authorities'
analyses, between 74% and 89% of retail accounts lost money, with average losses per client of 1,600 to 29,000
euros[^2]. The measures, product intervention under MiFIR (temporary and renewable every three months), were five:

| Measure | What it requires |
|---|---|
| Leverage limits on opening | From 30:1 to 2:1 depending on the underlying (full table in lesson 1.2.4) |
| Margin close-out | Close positions when margin falls to 50% of the required minimum, per account |
| Negative balance protection | The client cannot lose more than what was deposited in the account |
| Incentives | Bonuses and gifts for trading are banned |
| Risk warning | Standardized, with the percentage of the provider's accounts that lose money |

[^2]: ESMA, press release of March 27, 2018 (link in the resources).

```python
nocional = 10_000  # long CFD on a major index
apal = 20          # ESMA limit for major indices: 20:1
margen = nocional / apal
cierre = 0.5 * margen
print(f"Margin: {margen:.0f}; close-out if losses reach "
      f"{margen - cierre:.0f} ({(margen - cierre) / nocional:.1%} "
      "of the notional)")

# financing: reference rate + 2.5% (assumed spread)
tipo_ref = curva.loc["2025-12-31", "3m"] / 100
anual = nocional * (tipo_ref + 0.025)
print(f"Financing of a long: {anual / 360:.2f} per day, "
      f"{anual:.0f} per year ({anual / margen:.0%} of the margin)")
```
```output
Margin: 500; close-out if losses reach 250 (2.5% of the notional)
Financing of a long: 1.64 per day, 590 per year (118% of the margin)
```

With the maximum leverage for a major index, an adverse move of 2.5% is enough for the provider to close the
position. And financing, with a spread of 2.5 points over the short-term rate (an assumption: each provider publishes
its own), costs 590 a year: 118% of the margin tied up. That is why the CFD is a tool for positions held for days, not
months, and why a backtest with CFDs that does not include daily financing overstates the result. A future on the same
index carries the cost of money inside its price (the basis from lesson 1.2.3), without an added spread.

<aside class="callout" data-kind="riesgo">

Negative balance protection prevents losing more than what was deposited, but it does not prevent losing all of it,
and quickly: at 20:1, a 5% adverse move wipes out the entire margin. The percentage of accounts that lose money, which
every CFD provider in the EU is required to display, is the most useful figure on its website.

</aside>

There are other leveraged products for retail clients: exchange-listed warrants and **turbos** (with a barrier that,
when touched, cancels the product), leveraged and inverse ETFs (lesson 1.2.2) and options (1.2.5). Their risk is not
in the product's name but in its effective leverage and in how the capital is lost: all at once when a barrier is
touched, little by little through financing or through the decay of the daily rebalancing.

## Typical mistakes with rate data

- **Price and yield.** A series of yields is not a series of prices: if it rises, the bond loses. For the result of a
  bond position you have to convert to price (with duration or by revaluing the bond) and add the coupons.
- **Units.** Rates in percent (3.547), as a decimal (0.03547) or in basis points (354.7). Half the mistakes in a fixed
  income calculation are a factor of 100.
- **Constant maturity curves.** The “10-year” of a curve is a fixed tenor recomputed every day with different bonds; it
  is not a bond you can hold in a portfolio.
- **Conventions.** Annual or semiannual coupons, day count basis (Act/360, Act/365, 30/360) and accrued interest
  change the figures. Before comparing two sources, you need to know which convention each one uses.

## Exercises

1. Compute the price, modified duration and DV01 per million of a **2-year** bond with a 3% coupon using the Valdoria
   2-year yield of December 31, 2025. Compare its DV01 with that of the 10-year bond: how many millions of face value
   of the 2-year carry the same rate risk as one million of the 10-year?

**Solution.**

```python
y2 = curva.loc["2025-12-31", "2a"] / 100
p2, _, dmod2, _ = sensibilidades(y2, 0.03, 2)
dv01_2 = dmod2 * p2 / 100 * 1e6 * 1e-4
dv01_10 = dmod * p / 100 * 1e6 * 1e-4
print(f"2-year: price {p2:.3f}, D_mod {dmod2:.2f}, "
      f"DV01 {dv01_2:.0f} per million")
print(f"2-year face value equivalent to 1 M of the 10-year: "
      f"{dv01_10 / dv01_2:.2f} M")
```
```output
2-year: price 99.537, D_mod 1.91, DV01 190 per million
2-year face value equivalent to 1 M of the 10-year: 4.25 M
```

The 2-year bond has a duration of less than two years and a DV01 of about 190 units per million, against 810 for the
10-year: you need a little over four million of the 2-year to have the same risk per basis point. It is the
calculation behind a slope trade (buying the 10-year and selling the 2-year, or the other way around) that is neutral
to a parallel shift of the curve.

2. Compute the average 2s10s of each year and the number of sessions with an inverted curve in each year. In which year
   is the inversion deepest on average?

**Solution.**

```python
por_ano = pd.DataFrame({
    "pendiente_media_pb": pendiente.groupby(pendiente.index.year)
                                    .mean(),
    "sesiones_invertida": invertida.groupby(invertida.index.year)
                                     .sum(),
})
por_ano.round(1)
```
```output
       pendiente_media_pb  sesiones_invertida
fecha
2019                141.4                   0
2020                151.1                   0
2021                 92.2                   0
2022                -23.0                 145
2023                -75.9                 250
2024                -28.2                 252
2025                 14.4                  24
```

The deepest inversion on average is 2023's, −75.9 basis points, with all 250 sessions of the year inverted. 2022 has
145 inverted sessions, from June, and the absolute low; 2024, all 252 sessions of the year, although with a smaller
average inversion (−28.2). In 2025 only 24 inverted sessions remain and the average slope turns positive again.

3. A friend wants to hold a long CFD on an index for three months because “it's cheaper than a fund”. What costs and
   risks would you explain, using the data in this lesson?

**Solution.**

The CFD has no management fee, but it charges financing every night on the entire notional: with a short-term rate of
3.4% and a spread of 2.5 points, around 6% a year of the notional, 1.5% in three months, much more than the annual fee
of an index fund. It also pays the spread when opening and closing. With leverage, a small adverse move triggers the
50% margin close-out and locks in the loss, and the provider is the counterparty: if it goes bankrupt, there are no
securities in your friend's name. A fund or an ETF (lesson 1.2.2) gives the same exposure without financing or the risk
of a forced close-out; if they want leverage, a future carries the cost of money in its price without a spread.

## Self-assessment

1. Market interest rates rise. What happens to the price of an already issued fixed-coupon bond?

   - A. It rises, because it pays more interest
   - B. It does not change, because the coupon is fixed
   - C. It depends only on the issuer
   - D. It falls: its fixed coupons are worth less discounted at a higher rate

   *Answer D: It falls: its fixed coupons are worth less discounted at a higher rate.* The price is the present value of the cash flows. With a higher discount rate, each cash flow is worth less today. A new bond would pay a higher coupon; the old one only sells if its price falls until its yield matches.

2. A bond has a modified duration of 8.5 and trades at 95. If its yield rises 10 basis points, roughly how much does its price change?

   - A. −8.5%
   - B. −0.085%
   - C. −0.85%
   - D. +0.85%

   *Answer C: −0.85%.* The relative price change is −modified duration × change in yield: −8.5 × 0.001 = −0.85%. In price, about 0.81 points on 95.

3. What does it mean for the yield curve to be inverted (negative 2s10s)?

   - A. That 2-year bonds yield more than 10-year ones: the market expects policy rates to fall
   - B. That 10-year bonds yield more than 2-year ones
   - C. That the central bank has raised long-term rates
   - D. That inflation is negative

   *Answer A: That 2-year bonds yield more than 10-year ones: the market expects policy rates to fall.* Longer-term rates reflect expectations about future short-term rates plus a term premium. If the 2-year is above the 10-year, the market is pricing in future cuts to the policy rate, typically because it expects a slowdown.

4. A short-term interest rate future trades at 96.25. What interest rate does it imply?

   - A. 96.25%
   - B. 3.75%
   - C. 6.25%
   - D. 0.9625%

   *Answer B: 3.75%.* By convention, these futures are quoted as 100 minus the rate: 100 − 96.25 = 3.75%. If the market expects higher rates, the future's price falls, like a bond's.

5. According to the national authorities' analyses cited by ESMA in 2018, what percentage of retail CFD accounts lost money?

   - A. Between 10% and 20%
   - B. Around 50%
   - C. More than 99%
   - D. Between 74% and 89%

   *Answer D: Between 74% and 89%.* In March 2018 ESMA cited that, according to the national authorities' analyses, between 74% and 89% of retail CFD accounts lost money, with average losses per client of 1,600 to 29,000 euros. That is why it limited leverage and required every provider to publish that percentage.

## Key points

- A bond's price is the present value of its coupons and its principal. Yield and price move in opposite directions.
- Modified duration gives the price's sensitivity to rates (−D × Δy) and DV01, how much is gained or lost per basis point. Convexity corrects the approximation for large moves.
- The yield curve reflects expectations about the policy rate plus a term premium. In Valdoria it was inverted for 671 sessions, from June 2022 to February 2025.
- Short-term interest rate futures are quoted as 100 minus the rate; bond futures, on a notional bond (Eurex's Bund, 100,000 EUR with a 6% coupon).
- A CFD is a contract with the provider as counterparty: it replicates the underlying's price with margin and charges financing every night. ESMA limits its leverage for retail clients and requires close-out at 50% of margin.

## Resources

### Research

- [Central Banks and Monetary Policy in Python](https://pythonparatrading.com/en/monetary-policy-in-python/) · Research-0010 · 14 min
- [Impact of Seasonal Factor on US Bonds](https://pythonparatrading.com/en/seasonality-usa-yields/) · Research-0017 · 2 min

### Documentation

- [ESMA: ban on binary options and restriction of CFDs for retail clients (March 27, 2018)](https://www.esma.europa.eu/press-news/esma-news/esma-agrees-prohibit-binary-options-and-restrict-cfds-protect-retail-investors) · esma.europa.eu
- [Eurex: fixed income futures factsheet (Euro-Bund, Bobl, Schatz, Buxl)](https://www.eurex.com/resource/blob/5161678/f3adab3e23bc3a57a9a70daa3a8e7d48/data/factsheet-fixed-income-futures.pdf) · eurex.com

### Books

- [Frank J. Fabozzi and Francesco A. Fabozzi, Bond Markets, Analysis, and Strategies (MIT Press, 10th ed., 2021)](https://www.penguinrandomhouse.com/books/679151/bond-markets-analysis-and-strategies-tenth-edition-by-frank-j-fabozzi-and-francesco-a-fabozzi/) · penguinrandomhouse.com

### Data

- [Level 1 data: daily yield curve of Valdoria, 2019-2025 (CSV)](https://pythonparatrading.com/curso/datos/nivel-1/curva_tipos_diaria.csv) · Download · curva_tipos_diaria.csv

Previous lesson: [1.2.6 · Crypto: spot, perpetuals, funding and custody](https://pythonparatrading.com/en/python-trading-course/level-1/crypto-spot-perpetuals-and-funding/)

Next lesson: [1.3.1 · Types of market data: EOD, bars, ticks, order book and reference](https://pythonparatrading.com/en/python-trading-course/level-1/types-of-market-data/)
