---
title: "7.4.6 Trading taxes in Spain: the IRPF savings base"
description: "Spain's 2026 savings income base, FIFO, crypto swaps, the two-month rule, loss offsetting and tax treaties, with a tested tax engine in Python."
url: "https://pythonparatrading.com/en/python-trading-course/level-7/trading-taxes-in-spain/"
lang: "en"
image: "https://pythonparatrading.com/og/en/python-trading-course/level-7/trading-taxes-in-spain.png"
---

# 7.4.6 · Trading taxes in Spain

> A year of trades becomes a figure on the Spanish income tax return by following rules that can be programmed: which lot is sold, when a loss does not count, how balances are offset and how much of the tax paid abroad is recovered. This lesson reads those rules of Spain's personal income tax (IRPF) in the BOE and in the rulings of the DGT and turns them into an engine tested against hand-calculated cases. It is not tax advice.

- 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: 65 min
- Prerequisites: [4.4.1 · Choosing a broker from Spain or the EU: regulation, assets, costs and API](https://pythonparatrading.com/en/python-trading-course/level-4/choosing-a-broker-in-the-eu/), [1.2.1 · Stocks: splits, dividends and corporate actions](https://pythonparatrading.com/en/python-trading-course/level-1/stocks-splits-dividends-corporate-actions/), [0.3.1 · Variables, types and operators; floating point and decimal for prices and ticks](https://pythonparatrading.com/en/python-trading-course/level-0/python-variables-types-and-decimal/)

## By the end of this lesson

- Compute the tax on the savings income base with the 2026 scale (Arts. 66 and 76 of the IRPF Law) and distinguish the marginal rate from the average rate.
- Determine capital gains and losses with FIFO over all holdings, also for cryptocurrencies and swaps between them, as the DGT interprets it.
- Apply the two-month rule of Art. 33.5 f): defer a loss when there is a repurchase and include it when the sale is final.
- Offset balances and pending losses with the 25% limit and the four-year period, and limit the double taxation deduction to what the tax treaty allows.

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

**This is not tax advice.** This lesson explains general rules of the IRPF (*Impuesto sobre la Renta de las Personas
Físicas*, Spain's personal income tax) for residents of the common-regime territory (the Basque Country and Navarre have
their own *foral* tax rules) and turns them into educational code. It does not cover business activities, companies,
derivatives, non-residents or many particular cases. The figures were checked in the BOE (Spain's official gazette), the
AEAT (Spanish Tax Agency) and the DGT (*Dirección General de Tributos*, the Finance Ministry's directorate that issues
binding tax rulings) on October 23, 2026; the rules change. Quotations from Spanish laws and rulings are unofficial
English translations. For a real tax return, the source is the AEAT and, if needed, an adviser.

</aside>

## The savings base and its scale

The IRPF separates two tax bases. The **general** base covers employment, business activities and other income; the
**savings** base covers, among others, income from movable capital (dividends and interest, Art. 25) and capital gains
and losses arising from the transfer of assets (Art. 46)[^1]. An individual who buys and sells shares, ETFs or
cryptocurrencies, and who receives dividends, is taxed on them in the savings base.

The scale has two equal halves: the state half (Art. 66.1) and the regional half (Art. 76), both set in the state law.
Since January 1, 2025, after Law 7/2024, the top bracket went from 28% to 30%, and the BOE consolidated text shows no
later changes to those two articles: the 2025 scale is the 2026 scale[^1].

| Taxable savings base | State | Regional | Total | Cumulative tax at the top |
|---|---|---|---|---|
| Up to €6,000 | 9.5% | 9.5% | 19% | €1,140 |
| €6,000 to €50,000 | 10.5% | 10.5% | 21% | €10,380 |
| €50,000 to €200,000 | 11.5% | 11.5% | 23% | €44,880 |
| €200,000 to €300,000 | 13.5% | 13.5% | 27% | €71,880 |
| Over €300,000 | 15% | 15% | 30% | — |

The cumulative tax column is not a calculation of our own: it comes from the scale in Art. 66.2, which applies the full
rates to the taxpayers resident abroad of Arts. 8.2 and 10.1, and it is useful for checking the code.

*Fig. — Spanish income tax rates on the savings base by bracket (common territory).* Stepped bars with the rate of each savings tax-base bracket: 19% up to 6,000 euros, 21% up to 50,000, 23% up to 200,000, 27% up to 300,000 and 30% above, the top bracket, highlighted.

The scale applies to the part of the savings base that exceeds the personal and family allowance (Art. 66.1). Here we
assume the general base absorbs it entirely, which is the usual case with a salary; otherwise, the tax on savings goes
down. With amounts in `Decimal` (lesson 0.3.1), so that the cents add up:

```python
from decimal import Decimal as D, ROUND_HALF_UP

# 2026 savings scale: (up to, state Art. 66, regional Art. 76)
TRAMOS = [
    (D(6000), D("0.095"), D("0.095")),
    (D(50000), D("0.105"), D("0.105")),
    (D(200000), D("0.115"), D("0.115")),
    (D(300000), D("0.135"), D("0.135")),
    (None, D("0.15"), D("0.15")),
]


def c2(x):
    """Rounds to cents, as on the tax return."""
    return D(x).quantize(D("0.01"), rounding=ROUND_HALF_UP)


def cuota_ahorro(base):
    """Gross tax on the savings base and its breakdown by bracket."""
    base, desde, filas = D(base), D(0), []
    for hasta, estatal, autonomico in TRAMOS:
        if base <= desde:
            break
        tope = base if hasta is None else min(base, hasta)
        tipo = estatal + autonomico
        filas.append((desde, tope, tipo, c2((tope - desde) * tipo)))
        if hasta is None:
            break
        desde = hasta
    return sum((f[3] for f in filas), D(0)), filas


total, filas = cuota_ahorro(75000)
for desde, hasta, tipo, cuota in filas:
    print(f"{desde:>7,.0f} to {hasta:>7,.0f}  {tipo:.0%}"
          f"  {cuota:>9,.2f}")
print(f"{'Gross tax':>28}  {total:>9,.2f}")
```
```output
      0 to   6,000  19%   1,140.00
  6,000 to  50,000  21%   9,240.00
 50,000 to  75,000  23%   5,750.00
                   Gross tax  16,130.00
```

The tax on €75,000 is not 23% of 75,000, but each bracket at its rate. The difference between the rate on the last euro
and the rate on the whole base matters when estimating what a strategy returns after tax:

```python
print(" base       marginal average")
for base in (6000, 20000, 50000, 120000, 300000, 1000000):
    cuota, filas = cuota_ahorro(base)
    print(f"{base:>9,}  {filas[-1][2]:>8.0%}  {cuota / base:>7.2%}")
```
```output
 base       marginal average
    6,000       19%   19.00%
   20,000       21%   20.40%
   50,000       21%   20.76%
  120,000       23%   22.07%
  300,000       27%   23.96%
1,000,000       30%   28.19%
```

The figure at the top draws both curves. To decide whether to realize a gain this year or the next, the marginal rate
is what counts; to estimate a year's total tax, the average rate. With a base of €300,000, the average rate is 23.96%,
and every additional euro is already taxed at 30%.

## Acquisition value, FIFO and platforms

A capital gain or loss is the difference between the transfer value and the acquisition value, and both include the
expenses and taxes inherent to the transaction: the purchase commission adds to the cost and the sale commission is
subtracted from the proceeds (Art. 35)[^1]. The practical problem is which shares are sold when they were bought in
several purchases at different prices. For homogeneous securities, Art. 37.2 settles it: “the securities transferred by
the taxpayer shall be deemed to be those acquired first”. It is FIFO, and it is mandatory: you cannot pick the most
expensive lot to declare less.

For cryptocurrencies the law says nothing, but the DGT applies the same criterion. In ruling V1604-18 it concludes that
in partial sales of bitcoin “the ‘bitcoin’ transferred must be considered to be those acquired first”, and that buying
and selling them on different exchanges “is not a circumstance that alters homogeneity”. Rulings V0975-22 and V1550-25
repeat it; the latter, from September 2025, adds that the criterion is limited to cryptocurrencies, not other
crypto-assets, whose homogeneity would require its own analysis[^2].

The lesson's trade log is synthetic and built so that every rule has at least one case that tests it. The assets are
fictitious except BTC and ETH, and the amounts are euros already net of commissions (in a dollar account, each purchase
and each sale is converted at the exchange rate of its date). The trade types in the log keep their Spanish keys:
`compra` (purchase), `venta` (sale), `permuta` (swap) and `dividendo` (dividend):

```python
from dataclasses import dataclass, field
from datetime import date


@dataclass
class Op:
    fecha: date
    tipo: str        # compra, venta, permuta or dividendo
    activo: str
    cantidad: D
    importe: D       # euros net of commissions
    sitio: str       # broker or exchange
    clase: str       # "valor" (listed security) or "cripto"
    datos: dict = field(default_factory=dict)


def op(f, tipo, activo, cant, imp, sitio="B1", clase="valor", **k):
    return Op(date.fromisoformat(f), tipo, activo, D(cant), D(imp),
              sitio, clase, k)


def registro_sintetico():
    """Fictitious trades with the cases that must come out right."""
    return [
        op("2019-05-06", "compra", "ETFX", 1000, "40005"),
        op("2025-01-15", "compra", "GAMMA", 300, "6005"),
        op("2025-02-10", "compra", "BTC", "0.1", "9009", "EX1",
           "cripto"),
        op("2025-03-10", "compra", "ACME", 100, "5005"),
        op("2025-06-02", "compra", "BETA", 200, "8005"),
        op("2025-09-15", "compra", "ACME", 100, "6005"),
        op("2025-11-03", "compra", "DELTA", 100, "8005"),
        op("2025-12-01", "compra", "BTC", "0.1", "8008", "EX2",
           "cripto"),
        op("2026-02-20", "venta", "ACME", 150, "13495"),
        op("2026-03-02", "venta", "DELTA", 100, "5995"),
        op("2026-03-15", "venta", "BTC", "0.1", "7492.5", "EX2",
           "cripto"),
        op("2026-03-20", "compra", "DELTA", 100, "5805"),
        op("2026-04-01", "compra", "EPSI", 400, "4005"),
        op("2026-04-15", "dividendo", "ACME", 0, "500",
           retencion_es=D(95)),
        op("2026-05-11", "venta", "BETA", 200, "5995"),
        op("2026-05-20", "venta", "EPSI", 200, "1795"),
        op("2026-06-10", "venta", "ETFX", 1000, "98995"),
        op("2026-06-25", "compra", "BETA", 200, "6205"),
        op("2026-06-30", "dividendo", "USAA", 0, "800", "B2",
           retencion_ext=D(120), tipo_convenio=D("0.15")),
        op("2026-07-15", "venta", "DELTA", 100, "6595"),
        op("2026-08-10", "permuta", "BTC", "0.05", "4600", "EX1",
           "cripto", recibe="ETH", cantidad_recibida=D("1.5"),
           valor_recibido=D(4575)),
        op("2026-09-01", "venta", "GAMMA", 300, "4495"),
        op("2026-09-30", "dividendo", "USBB", 0, "400", "B2",
           retencion_ext=D(120), tipo_convenio=D("0.15")),
        op("2026-10-20", "compra", "GAMMA", 100, "1405"),
        op("2026-11-05", "venta", "ETH", "0.5", "1648.35", "EX1",
           "cripto"),
    ]


REG = registro_sintetico()
print(len(REG), "trades;",
      sum(o.tipo == "venta" for o in REG), "sales")
```
```output
25 trades; 9 sales
```

The engine walks through the log by date and keeps a queue of lots per asset. Each sale consumes the oldest lots; each
lot stores, besides its cost, the deferred loss it carries under the two-month rule, which is explained further down:

```python
from collections import defaultdict, deque
from dateutil.relativedelta import relativedelta

DOS_MESES = relativedelta(months=2)


@dataclass
class Lote:
    origen: int        # id of the purchase that created it
    cantidad: D
    coste: D           # acquisition value of what is left
    diferida: D = D(0)  # deferred loss it carries (Art. 33.5)


class Motor:
    """Capital gains and losses for the tax year with FIFO and repurchases."""

    def __init__(self, registro, ejercicio, por_sitio=False):
        self.reg = sorted(registro, key=lambda o: o.fecha)
        self.ejercicio, self.por_sitio = ejercicio, por_sitio
        self.lotes = defaultdict(deque)
        self.libre = {id(o): o.cantidad for o in self.reg
                      if o.tipo == "compra"}  # not yet a repurchase
        self.por_llegar = defaultdict(D)  # deferred loss of future purchases
        self.gp, self.rcm, self.diferidas = [], [], []

    def clave(self, o, activo=None):
        activo = activo or o.activo
        return (activo, o.sitio) if self.por_sitio else activo

    def comprar(self, o, activo, cantidad, coste):
        lote = Lote(id(o), cantidad, coste,
                    self.por_llegar.pop(id(o), D(0)))
        self.lotes[self.clave(o, activo)].append(lote)

    def sacar_fifo(self, o):
        """Consumes the oldest lots (cost and deferred loss)."""
        cola, falta = self.lotes[self.clave(o)], o.cantidad
        coste = diferida = D(0)
        while falta > 0:
            if not cola:
                raise ValueError(f"{o.fecha} {o.activo}: no shares")
            lote = cola[0]
            u = min(falta, lote.cantidad)
            parte = u / lote.cantidad
            coste += lote.coste * parte
            diferida += lote.diferida * parte
            lote.coste -= lote.coste * parte
            lote.diferida -= lote.diferida * parte
            lote.cantidad -= u
            falta -= u
            if lote.cantidad == 0:
                cola.popleft()
        return coste, diferida

    def compras(self, o, desde, hasta):
        return [c for c in self.reg if c.tipo == "compra"
                and c.activo == o.activo and desde <= c.fecha <= hasta
                and c is not o]

    def saldo(self, activo, antes_de):
        s = D(0)
        for c in self.reg:
            if c.activo == activo and c.fecha < antes_de:
                s += c.cantidad if c.tipo == "compra" else -c.cantidad
        return s

    def recompras(self, o):
        """Units of homogeneous purchases that count as a repurchase."""
        d = o.fecha
        antes = [c for c in self.compras(o, d - DOS_MESES, d)
                 if c.fecha < d]
        despues = [c for c in self.compras(o, d, d + DOS_MESES)
                   if c.fecha > d]
        quedan = sum(l.cantidad for l in self.lotes[self.clave(o)])
        inicio = self.saldo(o.activo, o.fecha - DOS_MESES)
        if len(antes) == 1 and inicio == 0:
            antes = []  # single purchase and no shares: not a repurchase
        usadas, falta, tope = [], o.cantidad, quedan
        previas = {id(c) for c in antes}
        for c in antes + despues:
            u = min(self.libre[id(c)], falta)
            if id(c) in previas:
                u = min(u, tope)
                tope -= u
            if u > 0:
                usadas.append((c, u))
                self.libre[id(c)] -= u
                falta -= u
        return usadas

    def vender(self, o, valor, nota):
        coste, liberada = self.sacar_fifo(o)
        resultado = valor - coste
        if liberada and self.compras(o, o.fecha - DOS_MESES,
                                     o.fecha + DOS_MESES):
            raise NotImplementedError(
                f"{o.activo} {o.fecha}: non-final sale of shares"
                " with a deferred loss; review by hand")
        if resultado < 0 and o.clase == "valor":
            usadas = self.recompras(o)
            n = sum((u for _, u in usadas), D(0))
            diferida = resultado * n / o.cantidad
            for c, u in usadas:
                self.asignar(c, resultado * u / o.cantidad)
            if n:
                self.diferidas.append(
                    (o.fecha, o.activo, c2(-diferida)))
                nota += f" (defers {n} of {o.cantidad})"
            resultado -= diferida
        self.anotar(o, resultado, nota)
        if liberada:
            self.anotar(o, -liberada, "deferred loss now included")

    def asignar(self, compra, perdida):
        for cola in self.lotes.values():
            for lote in cola:
                if lote.origen == id(compra):
                    lote.diferida += -perdida
                    return
        self.por_llegar[id(compra)] += -perdida

    def anotar(self, o, importe, nota):
        if o.fecha.year == self.ejercicio:
            self.gp.append((o.fecha, o.activo, c2(importe), nota))

    def procesar(self):
        for o in self.reg:
            if o.tipo == "compra":
                self.comprar(o, o.activo, o.cantidad, o.importe)
            elif o.tipo == "venta":
                self.vender(o, o.importe, "sale")
            elif o.tipo == "permuta":
                d = o.datos
                valor = max(o.importe, d["valor_recibido"])  # 37.1.h
                self.vender(o, valor, f"swap for {d['recibe']}")
                self.comprar(o, d["recibe"], d["cantidad_recibida"],
                             valor)
            elif o.tipo == "dividendo":
                if o.fecha.year == self.ejercicio:
                    self.rcm.append(o)
        return self


m = Motor(REG, 2026).procesar()
for fecha, activo, importe, nota in m.gp:
    print(f"{fecha}  {activo:<6}{importe:>10,.2f}  {nota}")
```
```output
2026-02-20  ACME    5,487.50  sale
2026-03-02  DELTA       0.00  sale (defers 100 of 100)
2026-03-15  BTC    -1,516.50  sale
2026-05-11  BETA        0.00  sale (defers 200 of 200)
2026-05-20  EPSI     -207.50  sale
2026-06-10  ETFX   58,990.00  sale
2026-07-15  DELTA     790.00  sale
2026-07-15  DELTA  -2,010.00  deferred loss now included
2026-08-10  BTC       596.00  swap for ETH
2026-09-01  GAMMA  -1,006.67  sale (defers 100 of 300)
2026-11-05  ETH       115.02  sale
```

The ACME gains come from FIFO: the 150 shares sold are the 100 from March 2025 (€5,005) and half of the September lot
(€3,002.50), so the gain is 13,495 − 8,007.50 = €5,487.50. The ETFX gain, €58,990, comes from an ETF bought in 2019:
the holding period does not reduce the gain.

The mistake of keeping a FIFO per exchange, which is what someone does when adding up each platform's reports
separately:

```python
def resultado(motor, activo):
    return sum(i for _, a, i, _ in motor.gp if a == activo)


bien = Motor(REG, 2026).procesar()
mal = Motor(REG, 2026, por_sitio=True).procesar()
print("BTC, FIFO over all holdings:", resultado(bien, "BTC"))
print("BTC, FIFO per exchange:     ", resultado(mal, "BTC"))
```
```output
BTC, FIFO over all holdings: -920.50
BTC, FIFO per exchange:      -420.00
```

The March sale was made on the second exchange, but the units sold for tax purposes are the first ones bought, on the
first exchange. Counting per exchange declares €500.50 less loss in 2026 and leaves a wrong cost in the portfolio for
the following years.

## Cryptocurrencies: every swap is a sale

Exchanging one crypto-asset for another is not neutral. The DGT, in ruling V0999-18, classifies the exchange between
different virtual currencies as a **swap** (*permuta*) that generates a capital gain or loss in the savings base, valued
under Art. 37.1 h): the difference between the acquisition value of what is delivered and **the higher** of the market
value of what is delivered and of what is received. That same value becomes the acquisition value of what is
received[^3].

In the log, on August 10, 0.05 BTC (market value €4,600) are delivered in exchange for 1.5 ETH (€4,575). The FIFO cost of
those 0.05 BTC is half of the December lot, €4,004, so the gain is 4,600 − 4,004 = €596 and the ETH enter the portfolio
with a cost of €4,600. The later sale of 0.5 ETH for €1,648.35 gives 1,648.35 − 4,600/3 = €115.02. The swap never went
through euros and is taxed just like a sale.

Two limits of the engine are worth stating. The first: the two-month rule in the next section speaks of “securities or
units admitted to trading”, and a cryptocurrency is not one; point e) of the same Art. 33.5 deals with the repurchase of
“assets” within one year. The rulings read for this lesson do not settle how it applies to cryptocurrencies, so the
engine applies no repurchase rule to the `cripto` class and leaves it as an explicit decision. The second: staking,
airdrops, lending or DeFi have their own treatments that the engine does not model.

## The two-month rule

Art. 33.5 f) says that losses from transferring securities admitted to trading are not computed “when the taxpayer has
acquired homogeneous securities within the two months before or after those transfers”, and that those losses “shall be
included as the securities or units that remain in the taxpayer's assets are transferred”. For unlisted securities,
point g) extends the period to one year[^1]. The purpose, in the DGT's words, is not to allow the loss “as long as the
taxpayer's assets remain constant”.

```mermaid
flowchart TB
  V[Sale at a loss] --> C{Listed security?}
  C -- no --> G[Unlisted: 1 year]
  C -- yes --> R{Purchase ±2 months?}
  R -- no --> I[Included]
  R -- yes --> D[Deferred]
  D --> T[Final sale]
  T --> I
```

The detail lies in the DGT's doctrine, which ruling V5316-26 (July 2026) summarizes as a procedure[^4]:

- **Purchases after** the sale, within the two months: they prevent computing the loss on the repurchased units.
- **Purchases before**, within the two months: they only count as a repurchase up to the balance of shares left after
  the sale. And they do not count if there was **a single purchase** in that period and no shares were held when it
  began.
- The same purchase cannot block two losses: purchases already used as a repurchase do not count again.
- The deferred loss is included when the repurchased shares are sold in a **final** sale, with no repurchase within the
  two months. Ruling V0754-26 applies it by units: it includes the loss on 185 of the 207 shares of a sale, and V0089-26
  does not allow a loss to be computed because more shares than those sold had been bought in the previous two months.

The engine codes those four rules in `recompras` and `vender`, and the log has one case for each: BETA (full later
repurchase), GAMMA (partial later repurchase, 100 of 300), EPSI (a single prior purchase with no shares at the start of
the period, the same pattern as V5316-26) and DELTA (deferred loss released by selling in July with no repurchase). The
result:

```python
print("Deferred losses in 2026:")
for fecha, activo, importe in m.diferidas:
    print(f"  {fecha} {activo:<6}{importe:>9,.2f}")
print("Still held with a deferred loss:")
for activo, cola in m.lotes.items():
    for lote in cola:
        if lote.diferida:
            print(f"  {activo:<6}{lote.cantidad:>5} shares"
                  f"{c2(lote.diferida):>9,.2f}")
```
```output
Deferred losses in 2026:
  2026-03-02 DELTA  2,010.00
  2026-05-11 BETA   2,010.00
  2026-09-01 GAMMA    503.33
Still held with a deferred loss:
  GAMMA   100 shares   503.33
  BETA    200 shares 2,010.00
```

Of the three losses deferred in 2026, DELTA's has already been included in the same year: the line “deferred loss now
included” in the previous table. Those of BETA and GAMMA remain inside the repurchased lots and will be included in the
year in which they are finally sold. An engine without this rule would have declared €2,513.33 more in losses in 2026
(BETA's €2,010 and GAMMA's €503.33).

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

The window is two months on each side, so a December sale can be blocked by a purchase in January or February of the
following year. The log that feeds the calculation for a tax year has to include those two months, and the engine
refuses (it raises `NotImplementedError`) when facing a sale of shares with a deferred loss that again has purchases
around it: it is a case in which the doctrine chains repurchases and it is best reviewed by hand.

</aside>

## Dividends, withholdings and tax treaties

Dividends are income from movable capital (Art. 25.1) and go to the savings base at their gross amount, before
withholdings. A Spanish payer withholds 19% on account (Art. 101.4), which is deducted at the end as a payment on
account[^1]. A foreign dividend usually arrives with a withholding from the source country, and two pieces come into
play:

- **The tax treaty.** The one between Spain and the US, as amended by the 2013 Protocol (published in the BOE on
  October 23, 2019), allows the US to tax a dividend received by a resident of Spain at up to 15% of the gross amount;
  with lower rates only for companies with significant holdings and pension funds (Art. 10)[^5]. To apply that rate, the
  broker needs the IRS form **W-8BEN**, which certifies the beneficial owner's residence; without it, the general
  withholding on those payments is 30%. The form is generally valid until the last day of the third calendar year after
  the year it was signed[^6].
- **The international double taxation deduction** (Art. 80). The lower of two amounts is deducted: what was actually
  paid abroad and the result of applying the average effective savings rate to the part of the base obtained abroad.
  The AEAT specifies that this average rate is that of the undivided savings base and is expressed with two
  decimals[^7]. And the DGT understands as the amount paid the one the other State **may levy under the treaty**
  (V5381-26)[^8]: whatever was over-withheld is not deducted in Spain and can only be recovered by claiming it from the
  source country.

The log has a Spanish dividend (€500, €95 withheld) and two US dividends of €800 and €400, with €120 withheld on each:
the first at 15%, with a W-8BEN; the second at 30%, without one.

```python
def rendimientos(motor):
    bruto = sum((o.importe for o in motor.rcm), D(0))
    ext = [o for o in motor.rcm if "retencion_ext" in o.datos]
    base_ext = sum((o.importe for o in ext), D(0))
    retenido = sum((o.datos["retencion_ext"] for o in ext), D(0))
    admisible = sum((min(o.datos["retencion_ext"],
                         c2(o.importe * o.datos["tipo_convenio"]))
                     for o in ext), D(0))
    ret_es = sum((o.datos.get("retencion_es", D(0))
                  for o in motor.rcm), D(0))
    return dict(bruto=bruto, base_ext=base_ext, retenido_ext=retenido,
                admisible=admisible, retencion_es=ret_es)


for k, v in rendimientos(m).items():
    print(f"{k:<13}{v:>8,.2f}")
```
```output
bruto        1,700.00
base_ext     1,200.00
retenido_ext   240.00
admisible      180.00
retencion_es    95.00
```

The keys are the gross dividends (`bruto`), the foreign-source base (`base_ext`), the tax withheld abroad
(`retenido_ext`), the part of it that is deductible (`admisible`) and the Spanish withholding (`retencion_es`). Of the
€240 withheld in the US, only €180 are deductible: €120 from the first dividend and €60 (15% of 400) from the second.
Since the treaty rate (15%) is lower than the lowest average rate of the savings base (19%), with US dividends the
binding limit is almost always the treaty's, not the average rate's.

## Offsetting losses and the final assessment

Art. 49 builds the savings base from two balances calculated separately: income from movable capital (RCM) and capital
gains and losses (GP). If one is negative, it offsets the positive balance of the other **up to 25%** of that positive
balance, and whatever is left is offset “in the following four years in the same order”[^1]. The AEAT guide details
the order with balances from previous years: first each pending item against its own balance for the year, with no
limit; then the rest, crosswise, with a 25% that is shared with the offsetting of the year itself[^9].

```mermaid
flowchart TB
  A[RCM balance] --> X{25% cross-offset}
  B[GP balance] --> X
  X --> P[Own carryforwards]
  P --> Q[Carryforwards, crosswise]
  Q --> BA[Savings base]
  BA --> E[Scale 19 to 30%]
```

The best test for a function like this is a case solved by the tax administration itself. The 2025 income tax guide
includes one: income from movable capital of −€800, gains of €5,600, losses of €1,600 and pending items from 2021 and
2022. Its solution gives a savings base of €200:

```python
def compensar(rcm, gp, pend_rcm, pend_gp, ejercicio):
    """Art. 49 of the IRPF Law, in the order of the AEAT guide."""
    rcm, gp = D(rcm), D(gp)
    pend_rcm, pend_gp = dict(pend_rcm), dict(pend_gp)
    vivas = range(ejercicio - 4, ejercicio)
    caducadas = {k: v for p in (pend_rcm, pend_gp)
                 for k, v in p.items() if k not in vivas}
    pend_rcm = {k: D(v) for k, v in pend_rcm.items() if k in vivas}
    pend_gp = {k: D(v) for k, v in pend_gp.items() if k in vivas}
    lim_gp, lim_rcm = D("0.25") * max(gp, 0), D("0.25") * max(rcm, 0)
    # Phase 1: balances of the year, crosswise with the 25%
    if rcm < 0 < gp:
        x = min(-rcm, lim_gp)
        rcm, gp, lim_gp = rcm + x, gp - x, lim_gp - x
    if gp < 0 < rcm:
        x = min(-gp, lim_rcm)
        gp, rcm, lim_rcm = gp + x, rcm - x, lim_rcm - x
    nuevas_rcm, nuevas_gp = -min(rcm, 0), -min(gp, 0)
    rcm, gp = max(rcm, 0), max(gp, 0)

    def gastar(pend, saldo, tope=None):
        for año in sorted(pend):
            x = min(pend[año], saldo,
                    saldo if tope is None else tope)
            pend[año] -= x
            saldo -= x
            if tope is not None:
                tope -= x
        return saldo, tope

    # Phase 2, step 1: each pending item against its own balance
    rcm, _ = gastar(pend_rcm, rcm)
    gp, _ = gastar(pend_gp, gp)
    # Phase 2, step 2: the rest, crosswise and within the remaining 25%
    gp, lim_gp = gastar(pend_rcm, gp, lim_gp)
    rcm, lim_rcm = gastar(pend_gp, rcm, lim_rcm)
    if nuevas_rcm:
        pend_rcm[ejercicio] = nuevas_rcm
    if nuevas_gp:
        pend_gp[ejercicio] = nuevas_gp
    limpia = lambda p: {k: c2(v) for k, v in p.items() if v}
    return dict(base=c2(rcm + gp), rcm=c2(rcm), gp=c2(gp),
                pend_rcm=limpia(pend_rcm), pend_gp=limpia(pend_gp),
                caducadas=limpia(caducadas))


# Worked example from the AEAT guide (2025 income tax, chapter 12)
aeat = compensar(rcm=-800, gp=5600 - 1600,
                 pend_rcm={2021: 500}, pend_gp={2021: 700, 2022: 2100},
                 ejercicio=2025)
print("Savings base:", aeat["base"])
print("Negative RCM from 2021 still pending:", aeat["pend_rcm"][2021])
```
```output
Savings base: 200.00
Negative RCM from 2021 still pending: 300.00
```

It matches: €800 of negative income for the year offset within the limit of €1,000 (25% of 4,000), €2,800 of pending
losses against the gains and only €200 of the €500 of negative income from 2021, because the 25% was almost used up.
The remaining €300 carry over to the next year.

With all the pieces, the assessment of the savings base of the synthetic log, with pending losses from 2021 (€500) and
2022 (€1,000):

```python
def liquidar(motor, pend_rcm, pend_gp):
    r = rendimientos(motor)
    gp = sum((i for _, _, i, _ in motor.gp), D(0))
    comp = compensar(r["bruto"], gp, pend_rcm, pend_gp,
                     motor.ejercicio)
    base = comp["base"]
    cuota, filas = cuota_ahorro(base)
    tme = (cuota / base * 100).quantize(D("0.01")) if base else D(0)
    ddi = min(r["admisible"], c2(r["base_ext"] * tme / 100))
    liquida = cuota - ddi
    return dict(saldo_gp=gp, saldo_rcm=r["bruto"], base=base,
                filas=filas, cuota=cuota, tme=tme, ddi=ddi,
                liquida=liquida, retenciones=r["retencion_es"],
                diferencial=liquida - r["retencion_es"],
                pendientes=comp["pend_gp"], caducadas=comp["caducadas"])


liq = liquidar(m, pend_rcm={}, pend_gp={2021: 500, 2022: 1000})
for k in ("saldo_gp", "saldo_rcm", "base"):
    print(f"{k:<12}{liq[k]:>11,.2f}")
for desde, hasta, tipo, cuota in liq["filas"]:
    print(f"  {desde:>7,.0f} to {hasta:>9,.2f}"
          f"  {tipo:.0%}{cuota:>10,.2f}")
print(f"{'cuota':<12}{liq['cuota']:>11,.2f}")
print(f"{'TME':<12}{liq['tme']:>10} %")
for k in ("ddi", "liquida", "retenciones", "diferencial"):
    print(f"{k:<12}{liq[k]:>11,.2f}")
for k in ("caducadas", "pendientes"):
    años = ", ".join(f"{a}: {v:,.2f}" for a, v in liq[k].items())
    print(f"{k:<12}{años or 'none'}")
```
```output
saldo_gp      61,237.85
saldo_rcm      1,700.00
base          61,937.85
        0 to  6,000.00  19%  1,140.00
    6,000 to 50,000.00  21%  9,240.00
   50,000 to 61,937.85  23%  2,745.71
cuota         13,125.71
TME              21.19 %
ddi              180.00
liquida       12,945.71
retenciones       95.00
diferencial   12,850.71
caducadas   2021: 500.00
pendientes  none
```

The labels are the keys of the result: GP and RCM balances, base, gross tax (`cuota`), average effective rate (`TME`),
double taxation deduction (`ddi`), net tax (`liquida`), Spanish withholdings (`retenciones`), the amount payable
(`diferencial`) and the losses that expire (`caducadas`) or remain pending (`pendientes`). The 2021 losses expire: their
period ended in 2025. The 2022 losses are offset in full against the 2026 gains, the last year in which they could be
used. The savings base, €61,937.85, spans three brackets; the average rate is 21.19%, and the double taxation
deduction, €180 (the treaty limit, lower than 21.19% of 1,200).

A tax engine without tests is an opinion. The ones that follow pin down every figure calculated by hand in the previous
sections, in pytest style (bare `assert` statements inside `test_` functions), and run in a loop so as not to depend on
a separate file:

```python
def gp_de(motor, activo):
    return [i for _, a, i, _ in motor.gp if a == activo]


def test_escala_coincide_con_el_boe():
    # cumulative tax from the table in Art. 66.2 (state + regional)
    for base, cuota in [(6000, 1140), (50000, 10380),
                        (200000, 44880), (300000, 71880)]:
        assert cuota_ahorro(base)[0] == cuota


def test_fifo_ignora_el_exchange():
    assert gp_de(m, "BTC")[0] == D("-1516.50")    # 7,492.5 - 9,009
    assert gp_de(mal, "BTC")[0] == D("-515.50")   # the mistake


def test_permuta_usa_el_mayor_valor():
    assert gp_de(m, "BTC")[1] == D("596.00")      # 4,600 - 8,008/2
    assert gp_de(m, "ETH") == [D("115.02")]       # 1,648.35 - 4,600/3


def test_recompra_en_dos_meses_difiere_la_perdida():
    assert gp_de(m, "BETA") == [D("0.00")]        # 2,010 deferred
    assert gp_de(m, "GAMMA") == [D("-1006.67")]   # 1/3 deferred


def test_unica_compra_previa_sin_titulos_no_es_recompra():
    assert gp_de(m, "EPSI") == [D("-207.50")]


def test_venta_definitiva_libera_la_perdida():
    assert gp_de(m, "DELTA") == [D("0.00"), D("790.00"),
                                 D("-2010.00")]


def test_compensacion_como_el_manual_de_la_aeat():
    assert aeat["base"] == 200
    assert aeat["pend_rcm"] == {2021: 300}


def test_limite_del_25_por_ciento():
    r = compensar(rcm=1000, gp=-600, pend_rcm={}, pend_gp={},
                  ejercicio=2026)
    assert r["base"] == 750 and r["pend_gp"] == {2026: 350}


def test_dividendos_y_convenio():
    assert rendimientos(m)["admisible"] == 180   # 120 + 15% of 400
    assert liq["ddi"] == 180


def test_liquidacion_completa():
    assert liq["base"] == D("61937.85")
    # 1,140 + 9,240 + 23% of 11,937.85
    assert liq["cuota"] == D("13125.71")
    assert liq["diferencial"] == D("12850.71")


pruebas = [f for n, f in list(globals().items())
           if n.startswith("test_")]
for prueba in pruebas:
    prueba()
print(f"{len(pruebas)} tests passed")
```
```output
10 tests passed
```

## Forms 720 and 721: reporting is not taxation

Lesson 4.4.1 explains when Forms 720 (*Modelo 720*, assets and rights located abroad) and 721 (*Modelo 721*, virtual
currencies held abroad) must be filed: a threshold of €50,000 per block on December 31, a new return when the increase
exceeds €20,000 and a filing window from January 1 to March 31 (Royal Decree 1065/2007, Arts. 42 ter and 42 quater;
Orders HAP/72/2013 and HFP/886/2023; Form 721 was filed for the first time for tax year 2023)[^10]. What matters here is
their relationship with the tax:

- **They assess no tax.** They are information returns. The gains, losses and dividends from those assets are declared
  in the IRPF like any others, with the rules of this lesson.
- **The 2012 regime was corrected in 2022.** The judgment of the Court of Justice of the EU of January 27, 2022 (case
  C-788/19) declared aspects of the regime associated with Form 720 contrary to EU law; Law 5/2022 consequently amended
  the eighteenth additional provision of the General Tax Law (*Ley General Tributaria*) and Art. 39 of the IRPF
  Law[^10].
- **What remains.** Under the current Art. 39, assets whose ownership does not match the declared income or wealth are
  unjustified capital gains that are included in the **general base** of the period in which they are discovered,
  unless the taxpayer proves they held them since before the limitation period. A complete trade history, like the log
  that feeds the engine, is also the proof of origin.

What the lesson's engine does not do, so that nobody mistakes it for a tax return program: it does not compute the
general base or the personal allowance, it does not model derivatives, securities lending, spin-offs, capital
increases, subscription rights or investment funds (which have their own transfer regime), it does not convert
currencies and it knows nothing about the *foral* regimes or non-residents.

## Exercises

1. Repeat the assessment assuming the broker also had the W-8BEN for the USBB dividend (15% withholding). Does what you
   pay in Spain change? How much does not having filed it cost?

**Solution.**

```python
con_w8 = [o if o.activo != "USBB" else
          op("2026-09-30", "dividendo", "USBB", 0, "400", "B2",
             retencion_ext=D(60), tipo_convenio=D("0.15"))
          for o in REG]
m2 = Motor(con_w8, 2026).procesar()
liq2 = liquidar(m2, pend_rcm={}, pend_gp={2021: 500, 2022: 1000})
filas = [("Withheld in the US", rendimientos(m)["retenido_ext"],
          rendimientos(m2)["retenido_ext"]),
         ("Deduction in Spain", liq["ddi"], liq2["ddi"]),
         ("Amount payable", liq["diferencial"], liq2["diferencial"])]
print(f"{'':<20}{'no W-8BEN':>12}{'with W-8BEN':>12}")
for nombre, sin, con in filas:
    print(f"{nombre:<20}{sin:>12,.2f}{con:>12,.2f}")
```
```output
                       no W-8BEN with W-8BEN
Withheld in the US        240.00      180.00
Deduction in Spain        180.00      180.00
Amount payable         12,850.71   12,850.71
```

What you pay in Spain does not change: in both cases €180 are deducted. What changes is the amount withheld in the US:
€60 more without the form, which are not recovered on the Spanish return and can only be claimed from the US tax
administration. It is 15% of every dividend received without a valid W-8BEN.

2. Write a pre-trade check: before selling a listed security at a loss, list the purchases of the previous two months
   and the date until which it is best not to buy it back. Test it with GAMMA on November 10, 2026.

**Solution.**

```python
def aviso_recompra(registro, activo, fecha):
    f = date.fromisoformat(fecha)
    previas = [o for o in registro if o.tipo == "compra"
               and o.activo == activo and f - DOS_MESES <= o.fecha < f]
    return previas, f + DOS_MESES


previas, hasta = aviso_recompra(REG, "GAMMA", "2026-11-10")
for o in previas:
    print(f"Prior purchase: {o.fecha} {o.cantidad} shares")
print("No repurchases until", hasta, "to avoid deferring the loss")
```
```output
Prior purchase: 2026-10-20 100 shares
No repurchases until 2027-01-10 to avoid deferring the loss
```

The October 20 purchase falls within the previous two months, so a sale at a loss on November 10 would be deferred in
the part that purchase covers (up to the balance left after the sale). It is a warning, not the full calculation: the
single-purchase exception and the purchases already used as a repurchase are decided by `Motor.recompras`. In a real
system, the check goes into the pre-trade risk control (lesson 4.5.1), next to the limits.

3. You sell some listed shares at a loss on December 15, 2026 and buy them back on January 20, 2027. On which tax return
   does the loss appear, and what data does the engine need to know it?

**Solution.**

It does not appear on the 2026 return: there is a homogeneous purchase within the following two months, so the loss is
deferred and will be included in the year in which the shares repurchased in January are finally sold. To know it, the
engine has to read trades up to February 15, 2027 when computing 2026. A year-end close that only looks at the calendar
year would declare a loss that cannot be computed.

[^1]: Law 35/2006 on Personal Income Tax (IRPF), Arts. 25, 33.5, 35, 37, 46, 49, 66, 76, 80 and 101.4; Arts. 66 and 76
    as worded by Law 7/2024 (effective from January 1, 2025). Consolidated text in the
    [BOE](https://www.boe.es/buscar/act.php?id=BOE-A-2006-20764) (in Spanish; quotations are unofficial translations),
    last update published on October 7, 2026, accessed on October 23, 2026.
[^2]: DGT, binding rulings [V1604-18](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V1604-18)
    (June 11, 2018), [V0975-22](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V0975-22) (May 4, 2022) and
    [V1550-25](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V1550-25) (September 3, 2025), in Spanish, read
    on October 23, 2026.
[^3]: DGT, binding ruling [V0999-18](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V0999-18)
    (April 18, 2018), in Spanish, read on October 23, 2026.
[^4]: DGT, binding rulings [V5316-26](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V5316-26)
    (July 28, 2026), [V0754-26](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V0754-26) (April 6, 2026) and
    [V0089-26](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V0089-26) (January 20, 2026), in Spanish, read
    on October 23, 2026.
[^5]: Convention between the Kingdom of Spain and the United States of America for the avoidance of double taxation, of
    February 22, 1990, Arts. 10 and 24, consolidated text with the Protocol of January 14, 2013 in the
    [BOE](https://www.boe.es/buscar/act.php?id=BOE-A-1990-30940) (Protocol:
    [BOE-A-2019-15166](https://www.boe.es/diario_boe/txt.php?id=BOE-A-2019-15166)), in Spanish, accessed on October 23,
    2026.
[^6]: IRS, [Instructions for Form W-8BEN](https://www.irs.gov/instructions/iw8ben) (October 2021 revision) and
    [NRA withholding](https://www.irs.gov/individuals/international-taxpayers/nra-withholding), accessed on October 23,
    2026.
[^7]: AEAT, 2025 income tax practical guide (*Manual práctico de Renta 2025*), chapter 18, [international double
    taxation
    deduction](https://sede.agenciatributaria.gob.es/Sede/ayuda/manuales-videos-folletos/manuales-practicos/irpf-2025/c18-cuota-liquida-resultante-autoliquidacion/deducciones-cuota-liquida-total/deduccion-doble-imposicion-internacional/objeto-regimen-general-deduccion.html)
    (in Spanish), accessed on October 23, 2026.
[^8]: DGT, binding ruling [V5381-26](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V5381-26)
    (July 28, 2026), in Spanish, read on October 23, 2026. Its case involves a different treaty (Panama); the criterion
    cited is the general one on Art. 80.
[^9]: AEAT, 2025 income tax practical guide, chapter 12: [inclusion and offsetting in the savings
    base](https://sede.agenciatributaria.gob.es/Sede/ayuda/manuales-videos-folletos/manuales-practicos/irpf-2025/c12-integracion-compensacion-rentas/reglas-integracion-compensacion-rentas/integracion-compensacion-rentas-base-imponible-ahorro.html)
    and [worked
    example](https://sede.agenciatributaria.gob.es/Sede/ayuda/manuales-videos-folletos/manuales-practicos/irpf-2025/c12-integracion-compensacion-rentas/caso-practico.html)
    (in Spanish), accessed on October 23, 2026.
[^10]: Royal Decree 1065/2007, Arts. 42 ter and 42 quater, consolidated text in the
    [BOE](https://www.boe.es/buscar/act.php?id=BOE-A-2007-15984); Order HAP/72/2013
    ([BOE-A-2013-954](https://www.boe.es/eli/es/o/2013/01/30/hap72/con)); Order HFP/886/2023
    ([BOE-A-2023-17429](https://www.boe.es/eli/es/o/2023/07/26/hfp886)); Law 5/2022, preamble and fourth and fifth final
    provisions ([BOE-A-2022-3712](https://www.boe.es/eli/es/l/2022/03/09/5)). All in Spanish, accessed on October 23,
    2026.

## Self-assessment

1. With a taxable savings income base of €75,000 in 2026, what is the gross tax liability (state plus regional)?

   - A. €14,250: 19% of the whole base
   - B. €16,130: 1,140 + 9,240 + 5,750, each bracket at its rate
   - C. €17,250: 23% of the whole base, because the last euro is taxed at 23%
   - D. €22,500: 30%, the top rate

   *Answer B: €16,130: 1,140 + 9,240 + 5,750, each bracket at its rate.* The scale is progressive by brackets: €6,000 at 19%, €44,000 at 21% and the remaining €25,000 at 23%. The marginal rate is 23%, but the average rate is 21.51%. Applying the marginal rate to the whole base is the most common mistake when estimating a strategy's tax.

2. On May 11, 2026 you sell at a loss listed shares you had held since 2025, and on June 25 you buy the same shares. What happens to the loss?

   - A. It is included in 2026 because the sale came before the purchase
   - B. It is lost forever
   - C. It is included in 2026 if the repurchase is through another broker
   - D. It is not computed in 2026: it will be included when you finally sell the repurchased shares

   *Answer D: It is not computed in 2026: it will be included when you finally sell the repurchased shares.* Art. 33.5 f) prevents computing the loss if homogeneous securities are acquired within the two months before or after the sale, and says it will be included as the securities that remain are transferred. That is what the engine does with BETA: €2,010 deferred that travel with the repurchased lot.

3. You bought 0.1 BTC on one exchange in February 2025 and another 0.1 BTC on another exchange in December. In March 2026 you sell 0.1 BTC on the second one. Which acquisition value do you use?

   - A. The February 2025 one: FIFO over all BTC, without distinguishing the platform
   - B. The December one, because the sale was made on that exchange
   - C. The average of the two
   - D. Whichever you choose, as long as you are consistent

   *Answer A: The February 2025 one: FIFO over all BTC, without distinguishing the platform.* The DGT applies the criterion of Art. 37.2 to homogeneous cryptocurrencies: the units acquired first are the ones sold, and the platform does not break homogeneity (V1604-18, V0975-22, V1550-25). In the lesson's trade log, FIFO per exchange gives a loss of €515.50 instead of €1,516.50.

4. A €400 dividend from a US company arrives with 30% withheld because the broker did not have the W-8BEN form. How much can you deduct in Spain for double taxation?

   - A. The €120 withheld
   - B. Nothing, because you did not file the W-8BEN
   - C. At most €60: the 15% the tax treaty allows; the excess is claimed from the US tax administration
   - D. 19% of the dividend

   *Answer C: At most €60: the 15% the tax treaty allows; the excess is claimed from the US tax administration.* Art. 80 of the IRPF Law deducts what was actually paid abroad, capped at the average rate, and the DGT understands as paid the amount the other State may levy under the treaty (V5381-26). The treaty with the US caps the tax on an individual's dividends at 15%.

5. In 2026 the balance of capital gains and losses is –€600 and the balance of dividends and interest is +€1,000. What happens?

   - A. The 600 are offset: the savings base is €400
   - B. €250 are offset (25% of 1,000); the remaining 350 are carried forward for up to four years
   - C. Nothing is offset: gains and investment income never mix
   - D. They are offset against the general base

   *Answer B: €250 are offset (25% of 1,000); the remaining 350 are carried forward for up to four years.* Art. 49 allows the negative balance of one block to be offset against the positive balance of the other, up to 25% of that positive balance. Whatever is left is offset in the following four years in the same order. It is the test `test_limite_del_25_por_ciento`.

## Key points

- Spain's 2026 savings income base is taxed by brackets at 19, 21, 23, 27 and 30% (half state, half regional, Arts. 66 and 76; common-regime territory). With a base of €300,000, the average rate is 23.96%.
- FIFO is the legal rule for homogeneous securities (Art. 37.2) and the one the DGT applies to cryptocurrencies, over all units and without separating by platform. A swap between cryptocurrencies is a transfer: it is valued at the higher of the two market values.
- A loss on listed securities is not computed if there are homogeneous purchases two months before or after; it is included when the repurchased shares are sold. An engine that does not model this brings forward losses that are not deductible.
- Dividends and interest on one side and capital gains and losses on the other: each negative balance offsets the other's positive balance only up to 25%, and what is pending expires after four years.
- The double taxation deduction is the lower of what was paid abroad (within what the treaty allows: 15% for US dividends) and the average rate applied to that income. Without a W-8BEN, the US withholds 30% and the difference is not recovered in Spain.
- Forms 720 and 721 (Modelo 720 and 721) report, they do not assess tax: they do not change the taxation of gains, but failing to file them leaves assets unaccounted for.

## Resources

### Documentation

- [Law 35/2006 on Personal Income Tax (IRPF), consolidated text (BOE, in Spanish)](https://www.boe.es/buscar/act.php?id=BOE-A-2006-20764) · boe.es
- [AEAT (Spanish Tax Agency), 2025 income tax practical guide: inclusion and offsetting of income in the savings base (in Spanish)](https://sede.agenciatributaria.gob.es/Sede/ayuda/manuales-videos-folletos/manuales-practicos/irpf-2025/c12-integracion-compensacion-rentas/reglas-integracion-compensacion-rentas/integracion-compensacion-rentas-base-imponible-ahorro.html) · sede.agenciatributaria.gob.es
- [DGT, binding ruling V1550-25: FIFO for cryptocurrencies without separating by platform (in Spanish)](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V1550-25) · petete.tributos.hacienda.gob.es
- [DGT, binding ruling V5316-26: how the two-month rule is applied (in Spanish)](https://petete.tributos.hacienda.gob.es/consultas/?num_consulta=V5316-26) · petete.tributos.hacienda.gob.es
- [Spain–US double taxation treaty, consolidated text with the 2013 Protocol (BOE, in Spanish)](https://www.boe.es/buscar/act.php?id=BOE-A-1990-30940) · boe.es
- [IRS: Instructions for Form W-8BEN](https://www.irs.gov/instructions/iw8ben) · irs.gov

Previous lesson: [7.4.5 · Market abuse: spoofing, layering and the bot that imitates them by accident](https://pythonparatrading.com/en/python-trading-course/level-7/market-abuse-spoofing-layering-bots/)

Next lesson: [7.4.7 · Ethics: personal data, artificial intelligence and selling signals](https://pythonparatrading.com/en/python-trading-course/level-7/ethics-personal-data-ai-trading-signals/)
