Lección 1.3.8 · 50 min
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.
- Módulo
- 1.3 · Fuentes de datos de mercado
- Duración
- 50 min
Lecciones del nivel 1
1.1 · Cómo funcionan los mercados
1.2 · Clases de activos
- 1.2.1 Acciones: splits, dividendos y eventos corporativos
- 1.2.2 ETFs: réplica, tracking error, apalancados y UCITS
- 1.2.3 Futuros: especificaciones, margen, roll y contango
- 1.2.4 Forex: pares, pips, lotes, rollover y CFD
- 1.2.5 Opciones: call, put, valor intrínseco y valor temporal
- 1.2.6 Cripto: spot, perpetuos, funding y custodia
- 1.2.7 Renta fija y tipos de interés; CFDs y productos apalancados
1.3 · Fuentes de datos de mercado
- 1.3.1 Tipos de datos de mercado: EOD, barras, ticks, libro y referencia
- 1.3.2 Fuentes de datos gratuitas
- 1.3.3 APIs de datos retail y prosumer
- 1.3.4 Proveedores de datos profesionales
- 1.3.5 Datos de cripto y on-chain
- 1.3.6 Brókeres como fuente de datos
- 1.3.7 Licencias y términos de uso de datos
- 1.3.8 Cómo elegir proveedor de datos
1.4 · Ingeniería de datos financieros
- 1.4.1 Descarga por REST y websockets: paginación, límites y reintentos
- 1.4.2 Limpieza de datos: faltantes, ticks erróneos, huecos y zonas horarias
- 1.4.3 Ajustes por splits y dividendos: precios ajustados y sin ajustar
- 1.4.4 Universos sin sesgo de supervivencia: deslistados, fusiones y cambios de símbolo
- 1.4.5 Construcción de barras: tiempo, ticks, volumen, dólar y desequilibrio
- 1.4.6 Series continuas de futuros: reglas de roll, ajuste por diferencia y ajuste por ratio
- 1.4.7 Almacenamiento: CSV, Parquet, particionado, DuckDB, TimescaleDB y ArcticDB
- 1.4.8 Pipeline reproducible: descarga, validación, actualización incremental y tests de calidad
1.5 · Datos fundamentales y macro
1.6 · Análisis exploratorio financiero
1.7 · Análisis técnico programado
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:
Fuente 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.
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()57 tickers, 2766 sesiones
evento
exclusion 7
deslistado_quiebra 4
fusion 4
cambio_simbolo 2
Name: count, dtype: int64Con 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.
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()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.
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()}{'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.
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) final anual_pct
completo 1.74 5.18
sin_deslistadas 2.21 7.49
miembros_actuales 3.13 10.94La 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.
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.
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)}")completo 40 de 40
sin_deslistadas 30 de 40
miembros_actuales 23 de 40Un 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:
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}")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-31Un 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:
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)fecha
2021-08-23 -75.19
2021-06-02 -12.21
2025-03-26 -12.10
Name: cierre, dtype: float64Una 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:
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) rent_pct evento
fecha
2021-08-02 23.94 False
2021-08-23 -75.19 TrueDe 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:
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%}")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:
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").Tfecha 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.2Con 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:
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}) diario_acciones intradia_futuros
B 3.94 NaN
C 3.75 4.5Para 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:
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()}) 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 0Cada 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
- 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
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")30 tickers; 6.05% anualSin 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.
- En la matriz de decisión, sube el peso del coste en el uso
diario_accionesde 4 a 8. ¿Cambia el ganador? ¿Qué pasa si además quitas el criterio eliminatorio?
Solución
usos.loc["coste", "diario_acciones"] = 8
pesos = usos / usos.sum()
print(puntuar("diario_acciones"))
print((notas @ pesos["diario_acciones"]).round(2))B 3.95
C 3.20
dtype: float64
A 3.40
B 3.95
C 3.20
dtype: float64Con 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.
- 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.
Respuesta plegada en cada pregunta
Autoevaluación
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?
- ANinguno, porque usas los miembros de cada día
- BSobrestimas la rentabilidad: faltan justo los valores que quebraron o fueron absorbidos, que suelen salir tras caer
- CSubestimas la rentabilidad, porque faltan las OPV
- DSolo afecta a la volatilidad, no a la rentabilidad
Ver respuesta
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 %.
En una serie diaria aparece una caída del 75,2 % en un día, sin noticias. ¿Qué es lo más probable?
- AUna quiebra
- BUn split de 4 por 1 en datos sin ajustar
- CUn error de redondeo
- DUn dividendo extraordinario
Ver respuesta
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.
¿Por qué la matriz de decisión empieza por criterios eliminatorios y no por la suma ponderada?
- APorque es más rápido
- BPorque 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
- CPorque los pesos son siempre iguales
- DPorque la suma ponderada no funciona con pandas
Ver respuesta
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.
Un proveedor ofrece dos años de historia gratis. ¿Qué problema tiene para validar un sistema diario?
- ANinguno, dos años son 500 sesiones
- BQue 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 %)
- CQue los datos gratis siempre están mal
- DQue el formato es distinto
Ver respuesta
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.
Un valor cambia de símbolo en 2018. El proveedor indexa los precios por el símbolo actual. ¿Qué pasa con su historia anterior?
- ANada, el símbolo da igual
- BDepende: 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
- CSe convierte en un valor deslistado
- DSe ajusta como un split
Ver respuesta
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.