---
title: "1.3.8 Cómo elegir proveedor de datos"
description: "Criterios para elegir proveedor de datos: cobertura, historia, deslistadas, ajustes, licencia y coste, medidos en un universo y con una matriz ponderada."
url: "https://pythonparatrading.com/curso-python-trading/nivel-1/como-elegir-proveedor-de-datos/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-1/como-elegir-proveedor-de-datos.png"
---

# 1.3.8 · Cómo elegir proveedor de datos

> Un proveedor no se elige por el precio ni por la comodidad de su API, sino por los sesgos que mete en tus resultados. Esta lección mide esos sesgos sobre un índice ficticio de 40 valores y termina con una matriz de decisión ponderada y una auditoría que puedes pasar a cualquier muestra.

- Curso: [Nivel 1: Datos financieros, mercados y análisis](https://pythonparatrading.com/curso-python-trading/nivel-1/)
- Módulo: 1.3 · Fuentes de datos de mercado
- Duración: 50 min
- Requisitos: [1.3.1 · Tipos de datos de mercado: EOD, barras, ticks, libro y referencia](https://pythonparatrading.com/curso-python-trading/nivel-1/tipos-de-datos-de-mercado/), [1.3.2 · Fuentes de datos gratuitas](https://pythonparatrading.com/curso-python-trading/nivel-1/fuentes-de-datos-gratuitas/), [1.3.7 · Licencias y términos de uso de datos](https://pythonparatrading.com/curso-python-trading/nivel-1/licencias-y-terminos-de-uso-de-datos/)

## Al terminar esta lección

- Enumerar los criterios que separan a un proveedor útil de uno peligroso: cobertura, profundidad histórica, deslistadas, ajustes corporativos, identificadores, licencia y coste.
- Medir con pandas cuánto infla la rentabilidad de un índice un proveedor sin valores deslistados o sin historia de miembros.
- Detectar en una muestra de precios si el proveedor ajusta splits y dividendos.
- Construir una matriz de decisión con criterios eliminatorios y pesos según el uso, y ver cómo cambia la elección.

## Elegir proveedor es elegir sesgos

Las lecciones anteriores del módulo recorren las fuentes: gratuitas (1.3.2), APIs retail (1.3.3), profesionales
(1.3.4), cripto (1.3.5), brókeres (1.3.6) y sus licencias (1.3.7). Todas venden «datos de mercado», pero no venden lo
mismo. Dos proveedores pueden dar el mismo cierre de hoy y, a la vez, resultados de backtest con diez años de
diferencia en rentabilidad, porque uno conserva las empresas que quebraron y el otro no, o porque uno ajusta los splits
y el otro deja una caída falsa del 75 %.

La pregunta útil no es «¿cuál es el mejor proveedor?», sino «¿qué necesita mi sistema y qué defectos no puedo
permitirme?». El orden de trabajo de esta lección es el de la figura:

```mermaid
flowchart TB
  U[Uso del sistema] --> R[Requisitos]
  R --> E{¿Eliminatorios?}
  E -- no --> X[Descartado]
  E -- sí --> P[Matriz ponderada]
  P --> M[Auditar muestra]
  M --> D[Documentar]
```

Primero se escribe qué hace el sistema (activo, frecuencia, horizonte, si opera en real). De ahí salen los requisitos.
Unos son **eliminatorios**: si el proveedor no los cumple, da igual el precio. El resto se pondera. Y antes de pagar
se prueba una muestra, porque la página de un proveedor describe lo que vende, no los defectos que tiene.

## Los criterios, uno por uno

| Criterio | Qué preguntar | Error típico si falla |
|---|---|---|
| Cobertura | ¿Qué mercados, activos y centros? ¿Cinta consolidada o solo la bolsa principal? | Volúmenes que no cuadran; activos que faltan (lección 1.1.1) |
| Profundidad histórica | ¿Desde cuándo, y con qué granularidad en cada tramo? | Backtest de un único régimen de mercado |
| Deslistadas | ¿Incluye quiebras, fusiones y exclusiones, con su último precio? | Sesgo de supervivencia (lección 1.4.4) |
| Miembros de índices | ¿Da la composición histórica, con fechas de alta y baja? | Universo de hoy aplicado al pasado |
| Ajustes corporativos | ¿Ajusta splits y dividendos? ¿Por qué método? ¿Se puede pedir sin ajustar? | Saltos falsos; rentabilidades sin dividendos (lección 1.4.3) |
| Identificadores | ¿Hay un identificador permanente además del símbolo? | Historias cortadas por un cambio de símbolo |
| Point-in-time | ¿Guarda las versiones de cada dato con su fecha de publicación? | Anticipación con fundamentales y macro (lecciones 1.5.1 y 1.5.2) |
| Zona horaria y sesiones | ¿En qué zona vienen las marcas de tiempo? ¿Incluye fuera de hora? | Barras desplazadas una hora dos veces al año |
| Latencia y fiabilidad | ¿Tiempo real, diferido, fin de día? ¿Qué pasa si la API cae? | Señales con datos de ayer en un sistema en real |
| Licencia | ¿Uso personal o profesional? ¿Puedes publicar resultados o derivados? | Incumplir el contrato (lección 1.3.7) |
| Coste total | Cuota, coste por uso, límites de peticiones y horas de limpieza | Un dato «gratis» que cuesta semanas de trabajo |

No todos pesan igual en todos los sistemas. Para un estudio de acciones a diario, las deslistadas y los ajustes son
eliminatorios y la latencia casi no importa. Para un sistema intradía de futuros, los ajustes por dividendos no
existen, pero sí la serie continua (lección 1.4.6), la latencia y las marcas de tiempo exactas.

## El universo del curso como banco de pruebas

Para medir los sesgos hace falta saber la verdad. El universo del curso es un índice ficticio de 40 valores de 2015 a
2025 con todo lo que suele faltar en un proveedor barato: los precios de los 57 tickers que han pasado por el índice
(también los quebrados, los absorbidos y los excluidos) y las fechas de alta y baja de cada miembro.

```python
import pandas as pd

precios = pd.read_parquet("universo_precios.parquet")
miembros = pd.read_csv("universo_miembros.csv",
                       parse_dates=["alta", "baja"])
eventos = pd.read_csv("universo_eventos.csv")

ancho = precios.pivot(index="fecha", columns="ticker",
                      values="cierre")
print(ancho.shape[1], "tickers,", len(ancho), "sesiones")
eventos["evento"].value_counts()
```
```output
57 tickers, 2766 sesiones
evento
exclusion             7
deslistado_quiebra    4
fusion                4
cambio_simbolo        2
Name: count, dtype: int64
```

Con las fechas de alta y baja se construye la máscara de pertenencia: `True` si el valor era miembro ese día. La
cartera de cada sesión se decide con los miembros de la sesión **anterior** (`shift(1)`), que es lo que se sabía al
abrir.

```python
fechas = ancho.index
es_miembro = pd.DataFrame(False, index=fechas,
                          columns=ancho.columns)
for t, alta, baja in miembros.itertuples(index=False):
    fin = baja if pd.notna(baja) else pd.Timestamp.max
    es_miembro.loc[(fechas >= alta) & (fechas < fin), t] = True

peso = es_miembro.shift(1, fill_value=False)
rent = ancho.pct_change(fill_method=None)
es_miembro.sum(axis=1).unique()
```
```output
array([40])
```

Exactamente 40 miembros cada día, como dice la regla del índice.

## Tres proveedores, tres rentabilidades

Con estos datos se pueden simular tres proveedores que venden el «mismo» histórico:

- **Completo.** Tiene los 57 tickers y la historia de miembros. Es la verdad del universo.
- **Sin deslistadas.** Tiene la historia de miembros, pero solo los tickers que cotizan hoy: faltan los quebrados, los
  absorbidos y los símbolos antiguos. Es el defecto más frecuente en fuentes gratuitas.
- **Solo miembros actuales.** Ni deslistadas ni historia de miembros: el usuario toma los 40 miembros de hoy y les
  aplica toda la historia. Es lo que se hace sin darse cuenta cuando se descarga «el índice» de una lista actual.

```python
cotiza_hoy = ancho.columns[ancho.iloc[-1].notna()]
actuales = miembros.loc[miembros["baja"].isna(), "ticker"]

proveedores = {
    "completo": rent.where(peso),
    "sin_deslistadas": rent[cotiza_hoy].where(peso[cotiza_hoy]),
    "miembros_actuales": rent[actuales],
}
{k: v.shape[1] for k, v in proveedores.items()}
```
```output
{'completo': 57, 'sin_deslistadas': 47, 'miembros_actuales': 40}
```

La cartera es equiponderada y se rebalancea cada día: la rentabilidad del índice es la media de las rentabilidades de
los miembros que el proveedor tiene. Si un miembro no tiene precio, simplemente no entra en la media, que es lo que
haría cualquier código que no compruebe la cobertura.

```python
def anual(r_dia):
    curva = (1 + r_dia.iloc[1:].fillna(0)).cumprod()
    anos = (curva.index[-1] - curva.index[0]).days / 365.25
    return curva, curva.iloc[-1] ** (1 / anos) - 1

curvas, tabla = {}, {}
for nombre, r in proveedores.items():
    curvas[nombre], cagr = anual(r.mean(axis=1))
    tabla[nombre] = {"final": curvas[nombre].iloc[-1],
                     "anual_pct": 100 * cagr}
pd.DataFrame(tabla).T.round(2)
```
```output
                   final  anual_pct
completo            1.74       5.18
sin_deslistadas     2.21       7.49
miembros_actuales   3.13      10.94
```

La figura de la cabecera compara los tres proveedores. El mismo índice, la misma regla, los mismos once años. Con el proveedor completo, 1 dólar se convierte en 1,74 (un
5,18 % anual). Sin las deslistadas, en 2,21 (7,49 %). Con los miembros actuales, en 3,13 (10,94 %): más del doble de
rentabilidad anual. Nada de eso es ventaja del sistema: es el proveedor.

La razón es la que explica la lección 1.4.4. Los valores que salen del índice lo hacen tras caer (por capitalización,
por quiebra) y los que entran, tras subir. Un proveedor sin deslistadas borra a los perdedores del pasado; usar los
miembros actuales, además, mete en el pasado a los ganadores que todavía no estaban.

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

Un sistema de selección de acciones (momentum, valor, calidad) probado con un universo de supervivientes tiende a
parecer mejor de lo que es, y el error no se ve en el código: está en lo que falta en los datos. Para un estudio de
acciones, las deslistadas y la historia de miembros son criterios eliminatorios.

</aside>

No basta con que el proveedor diga que tiene deslistadas. Hay que comprobar que tiene las que importan para tu
universo: los miembros del índice al empezar la historia.

```python
ini = miembros.loc[miembros["alta"] == "2015-01-02", "ticker"]
for nombre, r in proveedores.items():
    tiene = ini.isin(r.columns).sum()
    print(f"{nombre:18} {tiene} de {len(ini)}")
```
```output
completo           40 de 40
sin_deslistadas    30 de 40
miembros_actuales  23 de 40
```

Un proveedor de solo miembros actuales tiene 23 de los 40 valores con los que empezaba el índice: el 42 % del
universo de 2015 ha desaparecido de sus datos o nunca estuvo. El proveedor sin deslistadas tiene 30: le faltan los que
quebraron, los absorbidos y los dos símbolos antiguos.

## Identificadores: el símbolo no es la empresa

Esos dos símbolos antiguos son otro criterio. En el universo hay dos cambios de símbolo: ZXUV pasa a llamarse ZXMA en
2018 y ZXLU pasa a ZXBI en 2022. Son la misma empresa, pero los datos los guardan como dos series:

```python
cambios = eventos[eventos["evento"] == "cambio_simbolo"]
for _, ev in cambios.iterrows():
    for t in (ev["ticker"], ev["ticker_nuevo"]):
        s = ancho[t].dropna()
        print(f"{t}: {s.index[0]:%Y-%m-%d} a "
              f"{s.index[-1]:%Y-%m-%d}")
```
```output
ZXUV: 2015-01-02 a 2018-06-01
ZXMA: 2018-06-04 a 2025-12-31
ZXLU: 2015-01-02 a 2022-01-07
ZXBI: 2022-01-10 a 2025-12-31
```

Un proveedor que indexa por símbolo y no enlaza los dos tramos da a ZXMA una historia que empieza en 2018: una
empresa que parece haber salido a bolsa ese año. Los proveedores serios mantienen un **identificador permanente**
(propio o un estándar público) que no cambia con el símbolo, y una tabla de símbolos con fechas de validez. Al
evaluarlos hay que pedir un ejemplo de cambio de símbolo y ver cómo lo devuelven.

## Ajustes corporativos: saber qué te dan

El segundo criterio eliminatorio para acciones son los ajustes. Un proveedor puede dar precios brutos (los que se
negociaron), ajustados por splits, ajustados por splits y dividendos, o las tres cosas. Lo peligroso no es ninguna de
ellas: es no saber cuál es. La serie diaria de NOVA está **sin ajustar**; si se toma como si estuviera ajustada:

```python
nova = pd.read_csv("nova_diario_sin_ajustar.csv",
                   parse_dates=["fecha"], index_col="fecha")
r_nova = nova["cierre"].pct_change()
(100 * r_nova).nsmallest(3).round(2)
```
```output
fecha
2021-08-23   -75.19
2021-06-02   -12.21
2025-03-26   -12.10
Name: cierre, dtype: float64
```

Una caída del 75,19 % en un día, sin quiebra ni noticia. La auditoría consiste en cruzar los saltos grandes con los
eventos corporativos:

```python
ev_nova = pd.read_csv("nova_eventos.csv", parse_dates=["fecha_ex"])

def saltos_con_evento(r, eventos, umbral=0.20):
    grandes = r[r.abs() > umbral]
    return pd.DataFrame({
        "rent_pct": (100 * grandes).round(2),
        "evento": grandes.index.isin(eventos["fecha_ex"]),
    })

saltos_con_evento(r_nova, ev_nova)
```
```output
            rent_pct  evento
fecha
2021-08-02     23.94   False
2021-08-23    -75.19    True
```

De los dos movimientos de más del 20 %, solo el del 2021-08-23 cae en una fecha de evento: el split 4 por 1. El otro,
una subida del 23,94 % el 2021-08-02, no coincide con ningún evento: es un movimiento de mercado y hay que dejarlo. Un
filtro que borrara todo salto grande quitaría también ese, que es real. Si se divide el cierre anterior por el ratio
del split, el salto desaparece:

```python
dia = pd.Timestamp("2021-08-23")
previo = nova["cierre"].shift(1).loc[dia]
print(f"Cierre previo {previo:.2f}, cierre {nova.loc[dia, 'cierre']}")
print(f"Rentabilidad real: "
      f"{nova.loc[dia, 'cierre'] / (previo / 4) - 1:.2%}")
```
```output
Cierre previo 240.00, cierre 59.55
Rentabilidad real: -0.75%
```

El ajuste completo (splits y dividendos, método multiplicativo) es la lección 1.4.3. Para elegir proveedor basta con
saber tres cosas: si ajusta, por qué método, y si da también los precios brutos y la tabla de eventos para poder
rehacer el ajuste. Un proveedor que solo da precios ajustados obliga a fiarse de su ajuste; uno que da brutos y eventos
permite comprobarlo.

## Profundidad: regímenes, no filas

Muchos planes gratuitos o baratos limitan la historia a unos pocos años. Dos años diarios son unas 500 sesiones, que
parecen muchas. El problema es que pueden ser un solo tipo de mercado:

```python
pit = proveedores["completo"].mean(axis=1).iloc[1:]
por_ano = (1 + pit).groupby(pit.index.year).prod() - 1
(100 * por_ano).round(1).to_frame("rent_pct").T
```
```output
fecha     2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  2025
rent_pct -24.6  18.7  10.5  -9.7  30.1  -9.7  49.3 -29.9  14.2  34.7   3.2
```

*Fig. — Rentabilidad de cada año del índice del curso con el proveedor completo: un plan con dos años de historia solo ve 2024 y 2025.* Barras verticales de 2015 a 2025: −24,6, +18,7, +10,5, −9,7, +30,1, −9,7, +49,3, −29,9, +14,2, +34,7 y +3,2 %. Una marca ámbar sobre 2024 y 2025 indica lo que vería un plan de dos años de historia: dos años positivos y ningún año bajista.

Con solo 2024 y 2025 se verían dos años positivos (+34,7 % y +3,2 %) y ningún año bajista. La historia completa tiene
cuatro años negativos, entre ellos 2015 (−24,6 %) y 2022 (−29,9 %). Un sistema que solo se ha visto en dos años
alcistas no se ha probado en una caída. Para un sistema diario, la profundidad útil es la que cubre varios regímenes:
alcista, bajista, lateral, tipos bajos y altos. Para un sistema intradía, el número de sesiones importa menos que la
variedad de condiciones (días de noticias, vencimientos, cierres de media sesión).

## La matriz de decisión

Con los criterios claros, la elección se puede escribir en pandas. Tres perfiles genéricos, inspirados en las
categorías del módulo (no son proveedores concretos y las notas son ilustrativas):

- **A**: fuente gratuita no oficial, fin de día, sin deslistadas, licencia personal.
- **B**: API retail de pago, deslistadas y ajustes, cuota mensual.
- **C**: proveedor profesional, cobertura amplia y tick, precio alto por uso.

Notas de 1 a 5 por criterio y dos usos distintos, con sus pesos. Primero los eliminatorios, después la suma ponderada:

```python
notas = pd.DataFrame({
    "cobertura":    [3, 4, 5],
    "profundidad":  [3, 4, 5],
    "deslistadas":  [1, 4, 5],
    "ajustes":      [3, 4, 4],
    "intradia":     [1, 3, 5],
    "licencia":     [1, 3, 4],
    "coste":        [5, 4, 1],
}, index=["A", "B", "C"])

usos = pd.DataFrame({
    "diario_acciones":  [2, 3, 3, 3, 0, 1, 4],
    "intradia_futuros": [2, 2, 0, 0, 4, 1, 1],
}, index=notas.columns)
pesos = usos / usos.sum()

eliminatorio = {"diario_acciones": ("deslistadas", 3),
                "intradia_futuros": ("intradia", 4)}

def puntuar(uso):
    criterio, minimo = eliminatorio[uso]
    validos = notas[notas[criterio] >= minimo]
    return (validos @ pesos[uso]).round(2)

pd.DataFrame({u: puntuar(u) for u in usos})
```
```output
   diario_acciones  intradia_futuros
B             3.94               NaN
C             3.75               4.5
```

*Fig. — Matriz de decisión: nota ponderada de tres perfiles de proveedor para dos usos, después de aplicar los criterios eliminatorios (esquema).* Barras horizontales. Para un sistema diario de acciones, el perfil B saca 3,94 (destacado, gana) y C 3,75; A queda eliminado por no tener valores deslistados. Para un sistema intradía de futuros, C saca 4,50 y A y B quedan eliminados por su nota en datos intradía.

Para el sistema diario de acciones, A queda fuera antes de sumar nada: sin deslistadas no hay estudio válido, por
barato que sea. Entre B y C gana B (3,94 frente a 3,75): C es mejor en casi todo, pero el coste pesa mucho y el
intradía no cuenta. Para el sistema intradía de futuros, A y B quedan fuera por el criterio de datos intradía (`NaN` en
la tabla) y C gana solo, con 4,5. Con los
mismos proveedores, la elección cambia con el uso; por eso los pesos se escriben antes de mirar los precios, no
después.

La matriz no es un algoritmo para decidir sino una forma de hacer explícitas las prioridades. Si al ver el resultado
dan ganas de cambiar los pesos, conviene preguntarse si el peso estaba mal o si el resultado no gusta.

## Auditar una muestra antes de pagar

La mayoría de proveedores dejan probar: un plan gratuito, un periodo de prueba o una muestra. Esa muestra se pasa por
una auditoría automática. Con el universo del curso como ejemplo:

```python
def auditar(r_dia, universo_inicial, cotiza_hoy):
    tiene = r_dia.columns
    perdidos = sorted(set(universo_inicial) - set(tiene))
    muertos = set(tiene) - set(cotiza_hoy)
    return {
        "tickers": len(tiene),
        "faltan_del_inicio": len(perdidos),
        "deslistados_incluidos": len(muertos),
        "primer_dia": f"{r_dia.index[0]:%Y-%m-%d}",
        "saltos_75pct": int((r_dia.abs() > 0.70).sum().sum()),
    }

pd.DataFrame({k: auditar(v, ini, cotiza_hoy)
              for k, v in proveedores.items()})
```
```output
                         completo sin_deslistadas miembros_actuales
tickers                        57              47                40
faltan_del_inicio               0              10                17
deslistados_incluidos          10               0                 0
primer_dia             2015-01-02      2015-01-02        2015-01-02
saltos_75pct                    0               0                 0
```

Cada fila es una pregunta que se hace a la muestra: cuántos valores del universo inicial faltan, si hay alguno que ya
no cotiza (si no hay ninguno, la base no tiene deslistadas; en la completa son 10, los 8 quebrados o absorbidos y los
2 símbolos antiguos), desde cuándo empieza y si hay saltos imposibles. Aquí
ningún proveedor tiene saltos del 70 % porque `universo_precios` ya viene ajustado; los quebrados caen hasta cerca de
cero en varias sesiones, no de golpe. La lista completa para cualquier proveedor:

| Comprobación | Cómo |
|---|---|
| Deslistadas | Pedir 3 o 4 empresas que quebraron o fueron absorbidas en tu mercado y ver si están, hasta su último día |
| Miembros históricos | Pedir la composición de un índice en una fecha antigua y contarla |
| Cambios de símbolo | Pedir un valor que cambió de símbolo y ver si la historia está unida |
| Ajustes | Buscar un split conocido en los precios y ver si hay salto; pedir brutos y eventos |
| Huecos | Comparar las fechas con el calendario del mercado (lección 1.1.2) |
| Zona horaria | Mirar la hora de la primera barra intradía en invierno y en verano |
| Volumen | Comparar el volumen de un día con otra fuente: ¿consolidado o solo la bolsa principal? |
| Licencia | Leer qué se puede publicar o compartir antes de construir nada encima (lección 1.3.7) |

Y una última recomendación: guardar, junto a cada conjunto de datos, de qué proveedor viene, con qué parámetros se
pidió y cuándo. Si dentro de un año el proveedor corrige su histórico, sabrás por qué tu backtest ha cambiado (lección
1.4.8).

## Ejercicios

1. Repite la rentabilidad anual del proveedor «sin deslistadas» quitando también los valores que entran en el índice
   después de 2015 (las OPV y los sustitutos). ¿Sube o baja respecto al 7,49 %? ¿Por qué?

**Solución.**

```python
viejos = [t for t in cotiza_hoy if t in set(ini)]
r_viejos = rent[viejos].where(peso[viejos])
_, cagr = anual(r_viejos.mean(axis=1))
print(len(viejos), "tickers;", f"{cagr:.2%} anual")
```
```output
30 tickers; 6.05% anual
```

Sin las altas posteriores, quedan solo los miembros iniciales que siguen cotizando. La rentabilidad baja respecto al
proveedor sin deslistadas, porque las altas entraron tras subir con fuerza y siguieron aportando; pero sigue por encima
del índice completo, porque faltan los perdedores. Cada cosa que falta en los datos mueve el resultado, y no siempre en
la misma dirección.

2. En la matriz de decisión, sube el peso del coste en el uso `diario_acciones` de 4 a 8. ¿Cambia el ganador? ¿Qué
   pasa si además quitas el criterio eliminatorio?

**Solución.**

```python
usos.loc["coste", "diario_acciones"] = 8
pesos = usos / usos.sum()
print(puntuar("diario_acciones"))
print((notas @ pesos["diario_acciones"]).round(2))
```
```output
B    3.95
C    3.20
dtype: float64
A    3.40
B    3.95
C    3.20
dtype: float64
```

Con el coste a 8, B sigue ganando (3,95) y C cae a 3,20. Sin el eliminatorio, A, el proveedor sin deslistadas, saca
3,40 gracias al precio y queda por delante del profesional. Es justo lo que el filtro previo evita: un defecto que
invalida el estudio no se compensa con un descuento.

3. Escribe los requisitos y los criterios eliminatorios de un sistema que opera un ETF europeo a diario con datos de
   cierre, y di qué tres comprobaciones de la auditoría harías primero.

**Solución.**

Requisitos: cierres diarios de la bolsa europea donde cotiza el ETF, en su divisa, con su calendario de festivos;
historia suficiente para varios regímenes; precio ajustado por los repartos si el ETF es de distribución (o el valor
liquidativo, lección 1.2.2). Eliminatorios: que tenga ese ETF en su bolsa (no solo la versión de EE. UU.), los repartos
y una licencia que permita el uso previsto. Las deslistadas pesan menos que en un estudio de acciones porque el
universo es un único producto; vuelven a pesar si el sistema elige entre muchos ETFs, porque también se cierran. Las
tres primeras comprobaciones: fechas frente al calendario de esa bolsa (lección 1.1.2), repartos frente a los
documentos de la gestora, y la hora y zona de las marcas de tiempo.

## Autoevaluación

1. Un proveedor solo tiene los valores que cotizan hoy. Con él reconstruyes un índice equiponderado con sus miembros de cada día. ¿Qué error cometes?

   - A. Ninguno, porque usas los miembros de cada día
   - B. Sobrestimas la rentabilidad: faltan justo los valores que quebraron o fueron absorbidos, que suelen salir tras caer
   - C. Subestimas la rentabilidad, porque faltan las OPV
   - D. Solo afecta a la volatilidad, no a la rentabilidad

   *Respuesta B: Sobrestimas la rentabilidad: faltan justo los valores que quebraron o fueron absorbidos, que suelen salir tras caer.* La lista de miembros es correcta, pero los que desaparecieron no tienen precios y el promedio se hace sin ellos. En el índice del curso, la rentabilidad anual pasa del 5,18 % al 7,49 %.

2. En una serie diaria aparece una caída del 75,2 % en un día, sin noticias. ¿Qué es lo más probable?

   - A. Una quiebra
   - B. Un split de 4 por 1 en datos sin ajustar
   - C. Un error de redondeo
   - D. Un dividendo extraordinario

   *Respuesta B: Un split de 4 por 1 en datos sin ajustar.* Una caída de casi exactamente 1 − 1/4 coincide con un split 4 por 1. Si la serie no está ajustada y el backtest la toma como rentabilidad, un sistema corto «ganaría» un 75 % ese día. Por eso hay que cruzar los saltos con los eventos corporativos.

3. ¿Por qué la matriz de decisión empieza por criterios eliminatorios y no por la suma ponderada?

   - A. Porque es más rápido
   - B. Porque una nota alta en precio o en comodidad no compensa un defecto que invalida el resultado, como no tener deslistadas para un estudio de acciones
   - C. Porque los pesos son siempre iguales
   - D. Porque la suma ponderada no funciona con pandas

   *Respuesta B: Porque una nota alta en precio o en comodidad no compensa un defecto que invalida el resultado, como no tener deslistadas para un estudio de acciones.* La suma ponderada permite compensar: mucho de un criterio tapa poco de otro. Hay defectos que no se compensan; se filtran antes.

4. Un proveedor ofrece dos años de historia gratis. ¿Qué problema tiene para validar un sistema diario?

   - A. Ninguno, dos años son 500 sesiones
   - B. Que dos años suelen ser un único régimen de mercado: en el índice del curso, 2024 y 2025 dan +34,7 % y +3,2 %, y no hay ningún año bajista como 2022 (−29,9 %)
   - C. Que los datos gratis siempre están mal
   - D. Que el formato es distinto

   *Respuesta B: Que dos años suelen ser un único régimen de mercado: en el índice del curso, 2024 y 2025 dan +34,7 % y +3,2 %, y no hay ningún año bajista como 2022 (−29,9 %).* La profundidad no se mide en filas sino en regímenes. Con dos años alcistas no se puede saber cómo se comporta el sistema en una caída.

5. Un valor cambia de símbolo en 2018. El proveedor indexa los precios por el símbolo actual. ¿Qué pasa con su historia anterior?

   - A. Nada, el símbolo da igual
   - B. Depende: si el proveedor no enlaza los dos símbolos con un identificador permanente, la historia antigua queda con el símbolo viejo o se pierde
   - C. Se convierte en un valor deslistado
   - D. Se ajusta como un split

   *Respuesta B: Depende: si el proveedor no enlaza los dos símbolos con un identificador permanente, la historia antigua queda con el símbolo viejo o se pierde.* En el universo del curso, ZXUV pasa a ZXMA el 2018-06-04: son dos series separadas. Un proveedor con identificador permanente las une; uno que solo conoce el símbolo actual da a ZXMA una historia que empieza en 2018.

## Ideas clave

- Elegir proveedor es elegir qué sesgos entran en tus resultados: las cifras del backtest son tan buenas como los datos.
- Para acciones, lo eliminatorio es tener los valores deslistados y la historia de miembros. En el índice del curso, sin ellos la rentabilidad anual pasa del 5,18 % al 7,49 % o al 10,94 %.
- Hay que saber si los precios vienen ajustados, por qué método y si se pueden pedir sin ajustar. Un split sin ajustar es una caída falsa del 75 %.
- La profundidad útil se mide en regímenes de mercado, no en filas.
- Una matriz de decisión empieza por los criterios eliminatorios y solo después pondera; los pesos dependen del uso.
- Antes de pagar, se audita una muestra: deslistadas, saltos frente a eventos, huecos, zona horaria y licencia.

## Recursos

### Research

- [OpenBB Platform: datos de mercado gratuitos](https://pythonparatrading.com/openbb-datos-de-mercado-gratuitos/) · Research-0014 · 10 min
- [Big Data para trading](https://pythonparatrading.com/big-data-para-trading/) · Research-0019 · 16 min

### Documentación

- [Sesgo de supervivencia y point-in-time (lección 1.4.4 del curso)](https://pythonparatrading.com/curso-python-trading/nivel-1/sesgo-de-supervivencia-y-point-in-time/) · pythonparatrading.com

### Datos

- [Datos del nivel 1: precios del universo ficticio de 40 valores (Parquet)](https://pythonparatrading.com/curso/datos/nivel-1/universo_precios.parquet) · Descarga · universo_precios.parquet
- [Datos del nivel 1: miembros del índice con altas y bajas (CSV)](https://pythonparatrading.com/curso/datos/nivel-1/universo_miembros.csv) · Descarga · universo_miembros.csv
- [Datos del nivel 1: eventos corporativos de NOVA (CSV)](https://pythonparatrading.com/curso/datos/nivel-1/nova_eventos.csv) · Descarga · nova_eventos.csv

Lección anterior: [1.3.7 · Licencias y términos de uso de datos](https://pythonparatrading.com/curso-python-trading/nivel-1/licencias-y-terminos-de-uso-de-datos/)

Lección siguiente: [1.4.1 · Descarga por REST y websockets: paginación, límites y reintentos](https://pythonparatrading.com/curso-python-trading/nivel-1/descarga-rest-websockets-y-reintentos/)
