---
title: "7.4.2 ESMA and CFDs: leverage and investor protection"
description: "ESMA and Spanish CNMV rules on retail CFDs: initial margin, 50% close-out per account, negative balance and risk warning, simulated with data."
url: "https://pythonparatrading.com/en/python-trading-course/level-7/esma-cfd-leverage-investor-protection/"
lang: "en"
image: "https://pythonparatrading.com/og/en/python-trading-course/level-7/esma-cfd-leverage-investor-protection.png"
---

# 7.4.2 · ESMA and CFDs: leverage, close-out and negative balance

> Since 2018 an EU retail client cannot open a CFD with more leverage than ESMA sets, and in Spain a CNMV resolution with the same rules has applied since 2019. This lesson turns those rules into a margin calculator and an account simulator, and tests them on the series from levels 2 and 3.

- Course: [Level 7: Production, MLOps, regulation, career and capstone](https://pythonparatrading.com/en/python-trading-course/level-7/)
- Module: 7.4 · Regulation, ethics and taxation
- Duration: 60 min
- Prerequisites: [1.2.4 · Forex: pairs, pips, lots, rollover and CFDs](https://pythonparatrading.com/en/python-trading-course/level-1/forex-pairs-pips-lots-and-rollover/), [4.2.6 · Derivatives risk: portfolio Greeks, margin and liquidations](https://pythonparatrading.com/en/python-trading-course/level-4/derivatives-risk-greeks-margin-and-liquidations/), [7.4.1 · MiFID II: clients, suitability and algorithmic trading](https://pythonparatrading.com/en/python-trading-course/level-7/mifid-ii-suitability-algorithmic-trading/)

## By the end of this lesson

- Place ESMA Decision (EU) 2018/796 and the 2019 and 2023 resolutions of the CNMV, Spain's securities regulator: which products they cover, for whom and since when.
- Compute the minimum initial margin of a CFD portfolio by type of underlying according to Annex I of the resolution.
- Simulate the 50% margin close-out per account and compare it with a close-out per position, and measure the probability of a close-out at maximum leverage.
- Explain negative balance protection with a real price gap from the data and compute the risk warning's percentage of losing accounts.

## From the ESMA decision to the CNMV resolution

Lesson 1.2.4 presented the limits on CFDs for retail clients and computed how much movement closes a position in
EURUSD. This lesson goes to the sources and to the detail that decides the outcome of an account: which underlying
falls in which band, how the 50% close-out is measured, what happens with a price gap and where the figure in the
warning comes from. The framework is the EU one as applied in Spain.

The product intervention power came with Regulation (EU) 600/2014 (MiFIR), applicable since January 3, 2018: its
Article 40 allows ESMA to temporarily restrict a product throughout the EU and Article 42 allows each national
authority to do so in its own country. On May 22, 2018 ESMA adopted two decisions: banning binary options for retail
clients from July 2 and restricting CFDs from August 1 (Decision (EU) 2018/796), for three renewable months.
According to the CNMV (Comisión Nacional del Mercado de Valores, Spain's securities market regulator), ESMA renewed
them three times. To give them stability, each authority adopted equivalent national measures; in Spain, the **CNMV
Resolution of June 27, 2019**, whose CFD chapter came into force on August 1, 2019 and still applies “as long as the
CNMV does not consider that there have been changes in market conditions that allow its repeal” (unofficial
translation).

```mermaid
flowchart TB
  A[MiFIR art. 40 and 42] --> B[ESMA 2018/796]
  B --> C[CNMV June 2019]
  C --> D[CNMV July 2023]
```

The 2019 resolution applies to all marketing, distribution and sale of CFDs to **retail clients resident in Spain**,
whatever the country of the firm and whether or not it has a branch there; if another authority imposes something
different, the stricter rule applies. It treats as CFDs, among others, *rolling spot forex* and spread bets, and it
leaves out warrants and turbos. It does not apply to professional clients: that is why lesson 7.4.1 insists on what
is lost by asking for that treatment.

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

**Not advice.** A summary of the rules in force on October 23, 2026, with a link to the text in the BOE (Spain's
official gazette). The series are synthetic (data from levels 2 and 3, which imitate real markets without being them)
and the simulations close out only on daily closing prices; a provider monitors the account continuously. The
probabilities do not describe any real product or provider.

</aside>

## The five conditions of Article 4

Article 4 of the resolution allows a CFD to be sold to a retail client only if **all** of these conditions are met:

| Condition | What it requires | Definition |
|---|---|---|
| a) Initial margin | Charge the client at least the margin in Annex I | Art. 3.c and Annex I |
| b) Margin close-out | Liquidate CFDs when funds plus unrealized net profits fall below half of the total initial margin | Art. 3.e |
| c) Negative balance | The client's liability is limited to the funds in the CFD account | Art. 3.f |
| d) No incentives | No payments or monetary or non-monetary benefits for trading, except information and research tools | Art. 3.b |
| e) Warning | Every communication carries the Annex II warning with the percentage of losing accounts | Annex II |

Annex I sets the margin by type of underlying. The detail is in the lists: there are six “major currencies” (US
dollar, euro, yen, pound, Canadian dollar and Swiss franc), and the 5% indices are ten named ones, with their 2019
names (“DAX30”). The IBEX 35 is not among them. A calculator that applies it as written:

```python
import numpy as np
import pandas as pd

PRINCIPALES = {"USD", "EUR", "JPY", "GBP", "CAD", "CHF"}
INDICES_5 = {"FTSE 100", "CAC 40", "DAX30", "DJIA", "S&P 500",
             "NASDAQ", "NASDAQ 100", "Nikkei 225", "ASX 200",
             "EURO STOXX 50"}

def garantia_inicial(tipo, nombre=""):
    """Fraction of the notional (Annex I of the 2019 Resolution)."""
    if tipo == "divisa":
        a, b = nombre[:3], nombre[3:]
        return 0.0333 if {a, b} <= PRINCIPALES else 0.05
    if tipo == "indice":
        return 0.05 if nombre in INDICES_5 else 0.10
    if tipo == "materia_prima":
        return 0.05 if nombre == "oro" else 0.10
    if tipo == "cripto":
        return 0.50
    return 0.20                 # stocks and any other underlying

cartera = pd.DataFrame([
    ("divisa", "EURUSD", 100_000), ("divisa", "EURTRY", 20_000),
    ("indice", "DAX30", 40_000), ("indice", "IBEX 35", 40_000),
    ("materia_prima", "oro", 20_000),
    ("materia_prima", "brent", 20_000),
    ("accion", "una acción", 10_000), ("cripto", "BTC", 4_000),
], columns=["tipo", "subyacente", "nominal_eur"])
pares = zip(cartera["tipo"], cartera["subyacente"])
cartera["garantia_pct"] = [100 * garantia_inicial(t, s)
                           for t, s in pares]
cartera["garantia_eur"] = (cartera["nominal_eur"]
                           * cartera["garantia_pct"] / 100)
print(cartera.to_string(index=False))
total = cartera["garantia_eur"].sum()
nominal = cartera["nominal_eur"].sum()
print(f"total margin {total:,.0f} EUR for {nominal:,.0f} EUR of "
      f"notional (average leverage {nominal / total:.1f}:1)")
```
```output
         tipo subyacente  nominal_eur  garantia_pct  garantia_eur
       divisa     EURUSD       100000          3.33        3330.0
       divisa     EURTRY        20000          5.00        1000.0
       indice      DAX30        40000          5.00        2000.0
       indice    IBEX 35        40000         10.00        4000.0
materia_prima        oro        20000          5.00        1000.0
materia_prima      brent        20000         10.00        2000.0
       accion una acción        10000         20.00        2000.0
       cripto        BTC         4000         50.00        2000.0
total margin 17,330 EUR for 254,000 EUR of notional (average leverage 14.7:1)
```

The data values of the table stay in Spanish, as in the rest of the course: `divisa` (currency), `indice` (index),
`materia_prima` (commodity), `accion` (stock), `oro` (gold). The same notional in the DAX30 and in the IBEX 35 costs
twice the margin in the second, and the EURTRY pair, with a currency that is not on the list, goes to 5%. The
average leverage of the portfolio, which mixes bands, is none of those in the annex: that is what happens in a real
account.

## What maximum leverage means with data

ESMA did not pick the figures by eye. In its decision it explains that it calibrated the margin of each underlying
according to its volatility, simulating with about ten years of daily prices the probability that a client loses 50%
of their initial investment over an appropriate holding period, with the aim of making that probability consistent
across underlyings. The same exercise can be done with the course data. The series imitate real markets; the band
assigned to each is that of the market it imitates, and this is an assumption of the lesson:

| Series (data) | Imitates | Annex I band | Margin |
|---|---|---|---|
| QEVUSD (`fx3_diario`) | EURUSD | Two major currencies | 3.33% |
| USDQMP (`fx3_diario`) | USDMXN | Other currency | 5% |
| RVE (`futuros3_indices_contado`) | EURO STOXX 50 | Major index | 5% |
| MEA (`futuros2_continuos`) | Gold | Gold | 5% |
| RVH (`futuros3_indices_contado`) | IBEX 35 | Other index | 10% |
| ENA (`futuros2_continuos`) | Light crude | Other commodity | 10% |
| 120 stocks (`universo2_ajustados`) | Stocks | Stocks | 20% |
| XBQ on boreal (`cripto3_diario`) | Bitcoin | Cryptocurrency | 50% |

The experiment: the whole account balance as margin (the maximum leverage allowed), one position opened every
session, long or short, held for 20 sessions. It is closed out if at any daily close the loss reaches half of the
margin, that is, an adverse move of half of the annex percentage:

```python
from numpy.lib.stride_tricks import sliding_window_view

fx = pd.read_parquet("fx3_diario.parquet")
ind = pd.read_csv("futuros3_indices_contado.csv", parse_dates=["fecha"])
fut = pd.read_parquet("futuros2_continuos.parquet")
acc = pd.read_parquet("universo2_ajustados.parquet")
cri = pd.read_parquet("cripto3_diario.parquet")

def serie(df, col_id, valor, col_precio):
    s = df[df[col_id] == valor].set_index("fecha")[col_precio]
    return s.sort_index().dropna()

series = {
    "QEVUSD": (serie(fx, "par", "QEVUSD", "spot"), 0.0333),
    "USDQMP": (serie(fx, "par", "USDQMP", "spot"), 0.05),
    "RVE": (serie(ind, "indice", "RVE", "contado"), 0.05),
    "MEA": (serie(fut, "mercado", "MEA", "cierre_ajustado"), 0.05),
    "RVH": (serie(ind, "indice", "RVH", "contado"), 0.10),
    "ENA": (serie(fut, "mercado", "ENA", "cierre_ajustado"), 0.10),
    "XBQ": (serie(cri[cri["exchange"] == "boreal"], "activo", "XBQ",
                  "cierre_spot"), 0.50),
}

def prob_cierre(precios, garantia, h=20):
    """Fraction of h-session windows with a close-out (long/short mean)."""
    v = sliding_window_view(np.asarray(precios, float), h + 1)
    r = v[:, 1:] / v[:, :1] - 1
    largo = (r <= -0.5 * garantia).any(axis=1).mean()
    corto = (r >= 0.5 * garantia).any(axis=1).mean()
    return (largo + corto) / 2

res = {k: prob_cierre(s, g) for k, (s, g) in series.items()}
por_accion = [prob_cierre(g["cierre_ajustado"], 0.20)
              for _, g in acc.sort_values("fecha").groupby("ticker")
              if len(g) > 21]
res["stocks"] = float(np.mean(por_accion))
prob = pd.Series(res).sort_values(ascending=False)
(100 * prob).round(1)
```
```output
RVE       52.7
ENA       51.6
MEA       49.8
QEVUSD    39.8
USDQMP    36.7
RVH       30.0
stocks    19.3
XBQ        7.4
dtype: float64
```

The figure at the top sorts the result. At maximum leverage with the whole balance committed, a CFD is closed out by
the 50% rule in a large share of the four-week windows, and the share is not the same across underlyings. The single
5% band for major indices, gold and secondary currencies treats series with different volatilities alike, and the
same happens between the IBEX 35 and crude oil at 10%. The cryptocurrency, with the 50% margin, is the one closed out
least often: the band is conservative for its volatility. The figures depend on the synthetic data; what can be
generalized is the method and the order of magnitude.

The way to reduce that probability is decided not by the rule but by the client: not committing the whole balance.
The same account with less effective leverage on the major pair:

```python
qev = series["QEVUSD"][0]
for apal in (30, 20, 10, 5, 2):
    # with effective leverage L, the close-out comes with a fall
    # of 50% of the balance: a move of 0.5 / L
    p = prob_cierre(qev, 1 / apal)
    print(f"{apal:>2}:1  move that closes out {50 / apal:5.2f} %  "
          f"prob. in 20 sessions {100 * p:5.1f} %")
```
```output
30:1  move that closes out  1.67 %  prob. in 20 sessions  39.8 %
20:1  move that closes out  2.50 %  prob. in 20 sessions  23.0 %
10:1  move that closes out  5.00 %  prob. in 20 sessions   4.3 %
 5:1  move that closes out 10.00 %  prob. in 20 sessions   0.4 %
 2:1  move that closes out 25.00 %  prob. in 20 sessions   0.0 %
```

*Fig. — Probability that the 50 % rule closes a CFD within 20 sessions by effective leverage; each line ends at the maximum for its class.* Three lines of the probability of a close-out within 20 sessions against effective leverage, from 1:1 to the permitted maximum. QEVUSD reaches 39.8 % at 30:1, highlighted; RVE 52.7 % at 20:1; RVH 30.0 % at 10:1. At 3:1 or less, all three are at 2 % or below.

The simplification is honest in one sense: with the minimum margin and the whole balance committed, margin and
balance coincide, and the close-out comes with the loss of half of the balance. With less leverage, the provider
would not close out under the 50% rule (the balance is more than enough), but losing 50% of the balance is still the
threshold that matters to the client.

## The 50% close-out: per account, not per position

Article 3.e defines the protection as the liquidation of one or more CFDs “when the sum of funds in the CFD trading
account and the unrealized net profits of all open CFDs connected to that account falls to less than half of the
total initial margin provided for all those open CFDs” (unofficial translation of the CNMV resolution). It is a rule
**per account**. ESMA explains in its decision that the public consultation proposed a rule per position and that it
changed it: a rule per account allows the gains of one position to offset the losses of another, and a provider that
closes out per position also complies, because it is stricter.

The difference matters as soon as there are two positions that hedge each other. A long-short pair: a long CFD on
RVE (major index, 5%) and a short one on RVH (10%), with the same notional, and the account holding exactly the
margin of both. The two series have a daily correlation of 0.73:

```python
p = ind.pivot(index="fecha", columns="indice", values="contado")
par = p[["RVE", "RVH"]].to_numpy()

def ventana(i, regla, h=20):
    nominal, signo = 1.0, np.array([1, -1])     # long RVE, short RVH
    garantia = nominal * np.array([0.05, 0.10])
    fondos = garantia.sum()
    abierta = np.array([True, True])
    cerrada_pnl = np.zeros(2)
    cerro = False
    for t in range(1, h + 1):
        vivo = signo * nominal * (par[i + t] / par[i] - 1)
        pnl = np.where(abierta, vivo, cerrada_pnl)
        if regla == "position":
            corta = abierta & (pnl < -0.5 * garantia)
            cerrada_pnl[corta], abierta[corta] = pnl[corta], False
            cerro |= corta.any()
        if abierta.any() and (fondos + pnl.sum()
                              < 0.5 * garantia[abierta].sum()):
            cerrada_pnl[abierta], abierta[:] = pnl[abierta], False
            cerro = True
    return cerro, pnl.sum() / fondos

for regla in ("account", "position"):
    r = [ventana(i, regla) for i in range(len(par) - 20)]
    cerradas = np.mean([c for c, _ in r])
    final = np.array([x for _, x in r])
    print(f"{regla:9} close-outs {100 * cerradas:5.1f} %   mean "
          f"result {100 * final.mean():5.1f} %   5th percentile "
          f"{100 * np.percentile(final, 5):6.1f} %")
```
```output
account   close-outs   6.6 %   mean result   0.9 %   5th percentile  -51.2 %
position  close-outs  71.8 %   mean result   2.0 %   5th percentile  -58.7 %
```

With the close-out per position, one leg is liquidated in most windows: the 5% leg withstands a 2.5% adverse move
before being closed, and in a hedged pair each leg moves much more than the whole. The worst part is not the
frequency, it is what is left: one leg open without its hedge, an exposure the client had not chosen. That is the
argument ESMA takes from the public consultation. The rule per account looks at what the client can lose, the net,
and barely acts. In an account with a single position, as ESMA says, the two rules are the same.

The CNMV adds a good practice in the preamble of the 2019 resolution: warn the client **before** the 50%, so they can
post more margin or close on their own. And the 2023 resolution specifies that the close-out “must not take place
before that threshold is reached and the client has been informed” (unofficial translation).

```mermaid
stateDiagram-v2
  [*] --> Normal
  Normal --> Warning: funds below threshold
  Warning --> Normal: adds funds or closes
  Warning --> CloseOut: below 50%
  CloseOut --> Normal: balance left
  CloseOut --> Protected: negative balance
  Protected --> [*]
```

## Negative balance protection

The 50% close-out leaves the other half of the margin as a cushion to execute the close-out. That cushion is useless
if the price gaps between two observations: a weekend, a central bank decision, a gap at the open. The currency data
contain one like that. On Friday, October 2, 2015 QEVUSD closed at 1.58226 and on Monday the 5th, at 1.45688:

```python
q = series["QEVUSD"][0]
salto = q.pct_change().abs().idxmax()
antes, despues = q.shift(1)[salto], q[salto]
mov = despues / antes - 1
fondos = 10_000                       # EUR in the CFD account
nominal = fondos / 0.0333             # the whole balance as margin
saldo = fondos + nominal * mov        # long CFD
print(f"{salto.date()}: {antes:.5f} -> {despues:.5f} "
      f"({100 * mov:.2f} %)")
print(f"notional {nominal:,.0f}; balance after the gap {saldo:,.0f} EUR")
print(f"client's loss with protection: {fondos:,.0f} EUR; "
      f"borne by the provider: {-min(saldo, 0):,.0f} EUR")
```
```output
2015-10-05: 1.58226 -> 1.45688 (-7.92 %)
notional 300,300; balance after the gap -13,796 EUR
client's loss with protection: 10,000 EUR; borne by the provider: 13,796 EUR
```

For simplicity the account is in euros and the notional is expressed in euros. A 7.9% fall at 30:1 leverage is a
loss of 238% of the margin: by the time the provider can close out, there is no balance left. Negative balance
protection (Article 3.f) limits the client's liability, for all the CFDs in that account, to the funds deposited in
it. How often this happens in the experiment of the previous section can be counted: the first close-out signal
arrives with the loss already above 100% of the margin.

```python
def saldo_negativo(precios, garantia, h=20):
    v = sliding_window_view(np.asarray(precios, float), h + 1)
    r = v[:, 1:] / v[:, :1] - 1
    out = 0
    for x in (r, -r):                          # long and short
        toca = x <= -0.5 * garantia
        hay = toca.any(axis=1)
        primero = toca.argmax(axis=1)
        perdida = -x[np.arange(len(x)), primero] / garantia
        out += int((hay & (perdida > 1)).sum())
    return out

pd.Series({k: saldo_negativo(s, g) for k, (s, g) in series.items()})
```
```output
QEVUSD     88
USDQMP      4
RVE       167
MEA       116
RVH        16
ENA       232
XBQ         0
dtype: int64
```

These are windows (long and short) whose first close-out signal arrives with the loss already above the margin: 232
in crude oil, 167 in the major index, 88 in the major pair and none in the cryptocurrency, whose 50% margin leaves a
cushion of a 25% move. Gaps are concentrated on a few dates and affect every window that spans them, which is why
the counts are large in some series and small in others. With intraday data and continuous monitoring there would be
fewer cases, not none: the gap between two prices exists at any frequency.

## The risk warning and its percentage

Every communication from a CFD provider to retail clients carries a warning in a fixed format (Annex II). On a website
or a durable medium (unofficial translation of the Spanish text):

> CFDs are complex instruments and come with a high risk of losing money rapidly due to leverage. [provider's
> percentage] % of retail investor accounts lose money when trading CFDs with this provider. You should consider
> whether you understand how CFDs work and whether you can afford to take the high risk of losing your money.

The percentage is not free. Section A.6 defines it: every three months, over the previous 12 months, the proportion
of retail CFD accounts whose net realized and unrealized result on CFDs was negative, **with** all costs (charges,
fees, commissions) and **without** the accounts that had no open CFD, without the result of other products and
without deposits or withdrawals. A provider with no open CFDs in the period uses the standard warning: “Between 74%
and 89% of retail investors lose money when trading CFDs” (unofficial translation), the range from the supervisors'
studies that the resolution itself cites.

A validator on 5,000 synthetic accounts, compared with two tempting and wrong calculations:

```python
rng = np.random.default_rng(2019)
n = 5_000
cuentas = pd.DataFrame({
    "con_cfd_abierto": rng.random(n) < 0.85,
    "realizado": rng.normal(-150, 900, n).round(2),
    "no_realizado": rng.normal(0, 200, n).round(2),
    "costes": rng.gamma(2.0, 180, n).round(2),
    "otros_productos": rng.normal(300, 1500, n).round(2),
    "ingresos": rng.gamma(1.5, 2000, n).round(2),
    "retiradas": rng.gamma(1.2, 900, n).round(2),
})
cuentas.loc[~cuentas["con_cfd_abierto"],
            ["realizado", "no_realizado", "costes"]] = 0.0

def pct_pierden(c):
    """Annex II, section A.6 of the 2019 Resolution."""
    c = c[c["con_cfd_abierto"]]
    neto = c["realizado"] + c["no_realizado"] - c["costes"]
    return 100 * (neto < 0).mean()

c = cuentas
sin_costes = c[c["con_cfd_abierto"]]
mal_1 = 100 * ((sin_costes["realizado"] + sin_costes["no_realizado"])
               < 0).mean()
saldo = (c["realizado"] + c["no_realizado"] - c["costes"]
         + c["otros_productos"] + c["ingresos"] - c["retiradas"])
mal_2 = 100 * (saldo < 0).mean()
print(f"Annex II A.6:                       {pct_pierden(c):5.1f} %")
print(f"without costs:                      {mal_1:5.1f} %")
print(f"change in balance, all accounts:    {mal_2:5.1f} %")
```
```output
Annex II A.6:                        70.3 %
without costs:                       56.0 %
change in balance, all accounts:     29.7 %
```

Both errors lower the figure. Forgetting costs flips the sign of accounts that make a small profit before
commissions and financing. Using the change in balance of all accounts mixes in deposits, other products and accounts
without CFDs, and turns into “winners” accounts that only received a transfer. The annex's method exists so that the
figure is comparable across providers.

## What the CNMV added in 2023 and what none of this covers

The CNMV Resolution of July 11, 2023, applicable since August 3, 2023, does two things. For CFDs it bans marketing
communications aimed at retail clients in Spain, including those that redirect to a provider's website or form and
public offers of free or token-price courses, seminars or demo accounts; sponsorship and brand advertising with that
effect; and practices such as paying clients for bringing in clients, rewarding the sales network for client
acquisitions or client losses, call centers that promote these products and deposits by credit card. And for **other
leveraged instruments** (those whose maximum risk is not known at subscription or can exceed the initial outlay, such
as futures or written options) it requires an initial margin and the 50% close-out for retail clients. The margin is
the **lower** of two amounts: the one in the 2019 Annex I according to the underlying, or the one required by the
trading venue; if the underlying is a crypto-asset that is not a MiFID financial instrument, the annex amount with no
reduction.

```python
esp = pd.read_csv("futuros2_especificaciones.csv")
ultimo = fut[fut["fecha"] == fut["fecha"].max()].set_index("mercado")
esp = esp.set_index("mercado").loc[["IDA", "MEA", "ENA", "DIB", "BOC"]]
esp["nominal"] = ultimo["cierre"] * esp["multiplicador"]
esp["centro_pct"] = 100 * esp["margen_inicial"] / esp["nominal"]
# assumptions: IDA as a major index; DIB, a non-major currency;
# a bond future is an "other underlying"
esp["anexo_pct"] = [5.0, 5.0, 10.0, 5.0, 20.0]
esp["exigible_pct"] = esp[["centro_pct", "anexo_pct"]].min(axis=1)
esp[["nombre", "nominal", "centro_pct", "anexo_pct",
     "exigible_pct"]].round(2)
```
```output
                       nombre   nominal  centro_pct  anexo_pct  exigible_pct
mercado
IDA      Índice Aldebarán 500  389500.0        7.01        5.0          5.00
MEA                       Oro   41300.0        5.81        5.0          5.00
ENA              Crudo ligero   42670.0       12.89       10.0         10.00
DIB         Divisa Kestra/USD   60390.0        3.31        5.0          3.31
BOC            Bono a 10 años   95937.5        3.86       20.0          3.86
```

In three of the five level 2 futures (index, gold and crude oil) the annex figure is lower than the market margin and
is the one required; in the currency and the bond the market margin applies, lower than the annex's 5% and 20%. The
columns are `centro_pct` (trading venue margin), `anexo_pct` (Annex I) and `exigible_pct` (the one required). The
resolution sets the minimum the provider must ask of the retail client. Its clearest practical effect is the 50%
close-out, which a market's margin does not impose on the end client.

What none of these measures covers: professional clients; retail clients who trade with unauthorized firms (lesson
4.4.1); and market risk itself. They limit how much can be lost through leverage, not whether the strategy makes
money.

## Exercises

1. Repeat the experiment at the top with windows of 5 and 60 sessions. How does the ranking of the underlyings
   change? What does it tell you about the “appropriate holding period” ESMA used?

**Solution.**

```python
tabla = pd.DataFrame({
    h: {k: 100 * prob_cierre(s, g, h=h)
        for k, (s, g) in series.items()}
    for h in (5, 20, 60)
}).round(1)
tabla.columns = [f"{h} sessions" for h in tabla.columns]
tabla.sort_values("20 sessions", ascending=False)
```
```output
        5 sessions  20 sessions  60 sessions
RVE           22.3         52.7         70.0
ENA           21.0         51.6         70.7
MEA           18.7         49.8         69.3
QEVUSD        10.8         39.8         62.5
USDQMP         8.1         36.7         59.8
RVH            6.1         30.0         54.9
XBQ            0.3          7.4         36.2
```

All probabilities rise with the horizon, because a longer path has more chances to touch the threshold, but not in
the same proportion: those already closed out often in 5 sessions (RVE, ENA, MEA, around 20%) approach a ceiling of
70% at 60 sessions, and the cryptocurrency goes from almost never (0.3%) to just over a third (36.2%). The ranking
barely changes, but the distances do. That is why a calibration “consistent across underlyings” depends on the chosen
horizon, and a margin that looks ample for a week is not for a quarter.

2. Modify `ventana` so that the account holds twice the funds of the required margin. How many close-outs are there
   now under each rule?

**Solution.**

```python
def ventana_holgada(i, regla, h=20, colchon=2.0):
    nominal, signo = 1.0, np.array([1, -1])
    garantia = nominal * np.array([0.05, 0.10])
    fondos = colchon * garantia.sum()
    abierta = np.array([True, True])
    cerrada_pnl, cerro = np.zeros(2), False
    for t in range(1, h + 1):
        vivo = signo * nominal * (par[i + t] / par[i] - 1)
        pnl = np.where(abierta, vivo, cerrada_pnl)
        if regla == "position":
            corta = abierta & (pnl < -0.5 * garantia)
            cerrada_pnl[corta], abierta[corta] = pnl[corta], False
            cerro |= corta.any()
        if abierta.any() and (fondos + pnl.sum()
                              < 0.5 * garantia[abierta].sum()):
            cerrada_pnl[abierta], abierta[:] = pnl[abierta], False
            cerro = True
    return cerro

for regla in ("account", "position"):
    c = np.mean([ventana_holgada(i, regla)
                 for i in range(len(par) - 20)])
    print(f"{regla:9} close-outs {100 * c:5.1f} %")
```
```output
account   close-outs   0.0 %
position  close-outs  71.8 %
```

With twice the funds, the close-out per account disappears (0.0%): the account has to lose one and a half times the
margin before reaching the threshold. The close-out per position does not change at all, because it looks at each
position against its own margin and does not see the account's funds. Adding more balance protects against the
close-out only if the rule is per account, which is the minimum the rule requires; it is worth knowing which one your
provider applies.

3. A provider advertises on a social network: “Trade the IBEX with 20:1 leverage and get a 100 EUR bonus”. Point out
   which parts of the 2019 and 2023 resolutions that ad breaches if it is aimed at retail clients in Spain.

**Solution.**

Three. The IBEX 35 is not on the list of 5% indices: its minimum margin is 10%, 10:1 leverage (Annex I), so 20:1
breaches Article 4.a. The bonus is a monetary benefit tied to trading CFDs, banned by Article 4.d. And since the 2023
resolution any CFD marketing communication aimed at retail clients in Spain is banned, including those on social
networks; even if it were not, the ad would need the Annex II risk warning with the provider's percentage.

## Self-assessment

1. A retail client resident in Spain has 10,000 EUR in their CFD account and opens a CFD on the IBEX 35. What is the maximum notional?

   - A. 200,000 EUR: indices have 20:1 leverage
   - B. 100,000 EUR: the IBEX 35 is not on the list of major indices, so the margin is 10% (10:1)
   - C. 300,000 EUR: 30:1
   - D. 50,000 EUR: 5:1, like stocks

   *Answer B: 100,000 EUR: the IBEX 35 is not on the list of major indices, so the margin is 10% (10:1).* Annex I gives 5% only to ten named indices (FTSE 100, CAC 40, DAX30, DJIA, S&P 500, NASDAQ, NASDAQ 100, Nikkei 225, ASX 200 and EURO STOXX 50). All other equity indices go to 10%.

2. An account holds a long CFD on one index and a short one on another, correlated index. When must the provider close positions under the CNMV resolution?

   - A. When the sum of the account's funds and the unrealized net profits of all its CFDs falls below half of the total initial margin
   - B. When either of the two loses half of its margin
   - C. At the end of every session, if there are losses
   - D. Never: the client decides

   *Answer A: When the sum of the account's funds and the unrealized net profits of all its CFDs falls below half of the total initial margin.* The close-out protection is measured per account. ESMA explains in its decision that a close-out per position also complies (it is stricter), but the required minimum is per account. In the lesson, with the long-short pair, the per-position close-out triggers in 72% of the 20-session windows and the per-account one in 7%.

3. The example account has 10,000 EUR and a long CFD of about 300,000 EUR notional on the major pair (the whole balance as margin) when the price falls 7.9% between two closes. What does negative balance protection cover?

   - A. Nothing: the client owes the provider the difference
   - B. It returns half of the loss to the client
   - C. It only applies if the client requests it in writing
   - D. It limits the client's loss to the funds in their CFD account: the balance is left at zero and the provider bears the rest

   *Answer D: It limits the client's loss to the funds in their CFD account: the balance is left at zero and the provider bears the rest.* Negative balance protection limits the retail client's aggregate liability for all CFDs in the account to the funds in that account (Article 3.f). In the lesson, the gap leaves the account at −13,796 EUR; with the protection, the client loses 10,000 and the provider bears the rest.

4. To compute the risk warning's percentage of losing accounts, which accounts and which amounts are used?

   - A. All open accounts, with the change in balance, including deposits and withdrawals
   - B. Only the accounts closed during the year
   - C. Accounts with at least one CFD open in the 12 months, with the net realized and unrealized result of their CFDs, costs included and without deposits, withdrawals or other products; it is recalculated every three months
   - D. A fixed figure of 74% to 89%

   *Answer C: Accounts with at least one CFD open in the 12 months, with the net realized and unrealized result of their CFDs, costs included and without deposits, withdrawals or other products; it is recalculated every three months.* It is the method of section A.6 of Annex II. The 74% to 89% range is the standard warning, for a provider that has had no open CFDs in the last calculation period.

5. What did the CNMV resolution of July 11, 2023 add?

   - A. It banned CFD advertising aimed at retail clients in Spain and extended the initial margin and the 50% close-out to other leveraged instruments, such as futures and options
   - B. It raised the maximum leverage of currency CFDs
   - C. It removed negative balance protection
   - D. It repealed the 2019 resolution

   *Answer A: It banned CFD advertising aimed at retail clients in Spain and extended the initial margin and the 50% close-out to other leveraged instruments, such as futures and options.* The first part bans marketing communications, sponsorship and practices such as paying to acquire clients or accepting deposits by credit card; the second applies the minimum margin and the 50% close-out to other instruments whose maximum risk is not known or can exceed the initial outlay.

## Key points

- ESMA restricted CFDs for retail clients with Decision (EU) 2018/796 (from August 1, 2018, renewed three times); in Spain the CNMV Resolution of June 27, 2019 has applied the same rules since August 1, 2019, with no end date.
- Minimum initial margin: 3.33% on pairs of two major currencies; 5% on ten major indices, gold and other pairs; 10% on other commodities and indices; 20% on stocks and everything else; 50% on cryptocurrencies.
- The 50% close-out is measured per account: funds plus unrealized net profits against half of the total initial margin. At maximum leverage with the whole balance committed, in the course data it triggers in 7% (cryptocurrency) to 53% (major index) of the 20-session windows.
- Negative balance protection limits the loss to the funds in the CFD account; a gap between two closes can take the balance below zero before the close-out acts.
- The warning carries the percentage of the provider's retail accounts that lose money, calculated every three months over 12 months with costs and without deposits or withdrawals; since 2023 the CNMV also bans CFD advertising to retail clients.

## Resources

### Research

- [Uses of volatility](https://pythonparatrading.com/en/uses-of-volatility/) · Research-0003 · 5 min

### Documentation

- [CNMV Resolution of June 27, 2019 on binary options and CFDs (BOE-A-2019-9737), in the BOE (Spain's official gazette)](https://www.boe.es/diario_boe/txt.php?id=BOE-A-2019-9737) · boe.es
- [CNMV Resolution of July 11, 2023 on CFDs and other leveraged products (BOE-A-2023-16394), in the BOE](https://www.boe.es/diario_boe/txt.php?id=BOE-A-2023-16394) · boe.es
- [ESMA Decision (EU) 2018/796 temporarily restricting CFDs for retail clients, in the BOE (in Spanish)](https://www.boe.es/buscar/doc.php?id=DOUE-L-2018-80955) · boe.es
- [Regulation (EU) 600/2014 (MiFIR), Articles 40 to 42: product intervention, in the BOE (in Spanish)](https://www.boe.es/buscar/doc.php?id=DOUE-L-2014-81281) · boe.es

### Data

- [Level 3 data: daily currencies (Parquet)](https://pythonparatrading.com/curso/datos/nivel-3/fx3_diario.parquet) · Download · fx3_diario.parquet

Previous lesson: [7.4.1 · MiFID II: clients, suitability and algorithmic trading](https://pythonparatrading.com/en/python-trading-course/level-7/mifid-ii-suitability-algorithmic-trading/)

Next lesson: [7.4.3 · PRIIPs, the KID and US ETFs](https://pythonparatrading.com/en/python-trading-course/level-7/priips-kid-us-etfs/)
