Saltar a contenido

Fuentes de datos: qué hay, de dónde viene y qué no se hace

Todo backtest empieza por una pregunta incómoda: ¿de dónde salen los precios y cuánto me puedo fiar de ellos? Este capítulo recorre las dos fuentes del banco de pruebas (Twelve Data y Yahoo Finance), explica qué activos, marcos temporales y rangos cubren, y —igual de importante— qué defectos traen y qué decisiones se tomaron con ellos.

Qué vas a aprender

  • Qué activos y marcos temporales tiene el proyecto y de qué proveedor viene cada uno.
  • Por qué los ficheros fuente se congelan antes de usarse.
  • Qué significa que Yahoo entregue un OHLC «ajustado por splits» y un adjclose «con dividendos».
  • Qué defectos reales traen los pares FX (OHLC inválido, volumen cero, fechas corridas en domingo).
  • Qué es el universo us_equities_36, el panel 2.690 × 34 y el sesgo de supervivencia.
  • Qué cosas no hace el proyecto con los datos (actualización diaria, corrección de supervivencia…).

1. La idea: un inventario pequeño pero auditado

El banco de pruebas no persigue tener muchos datos, sino datos cuyo origen y cuyos defectos están medidos y escritos. Piensa en un laboratorio: vale más una muestra pequeña con su ficha técnica que un almacén entero sin etiquetar.

flowchart LR
    subgraph Proveedores
        TD[Twelve Data<br/>plan Basic]
        YH[Yahoo Finance<br/>API chart v8]
    end
    TD -->|CSV 1min y diarios| RAW1[data/raw/twelvedata/<br/>+ MANIFEST.json]
    YH -->|CSV diarios| RAW2[data/raw/yahoo/<br/>+ MANIFEST.json]
    RAW1 --> PREP[tools/prep_data.py]
    RAW2 --> PREP
    PREP --> CAN[(data/canonical/<br/>Parquet canónico)]

Hay dos proveedores:

Proveedor Activos Marcos Rango Moneda / calendario
Twelve Data NVDA (XNAS), KO (XNYS) 1min y derivados 2, 5, 15, 30min, 1h, 1d; diario largo 1d_long 1min: 2020-03-24 → 2026-09-14/15; 1d_long: NVDA desde 1999, KO desde 1970 USD · XNYS
Yahoo AMZN, INTC, MSFT, TSLA · BBVA.MC, SAN.MC, TEF.MC · EURUSD=X, GBPEUR=X, USDJPY=X (más 30 valores US añadidos en WP-10a) diario (1d) 2015-01-01/02 → 2026-09-15 USD/EUR/JPY · XNYS, XMAD, FX

El plan Basic de Twelve Data

El plan de desarrollo (docs/PLAN_DESARROLLO.md) registra que la cuenta de Twelve Data es Basic, limitada a 8 créditos por minuto y 3 mercados. Por eso el intradía se limita a dos valores (NVDA y KO) y el universo amplio se construyó con Yahoo.


2. Twelve Data: el intradía de NVDA y KO

Los cuatro CSV fuente, tal como se congelaron:

Fichero fuente Filas Rango Uso
twelvedata_nvda_1min_full.csv 632.095 2020-03-24 → 2026-09-14 freq=1min y los seis derivados
twelvedata_ko_1min_full.csv 526.386 2020-03-24 → 2026-09-15 freq=1min y los seis derivados
twelvedata_nvda_daily.csv 6.953 1999-01-22 → 2026-09-14 freq=1d_long
twelvedata_ko_daily.csv 14.296 1970-01-02 → 2026-09-14 freq=1d_long

Tras la limpieza (capítulo siguiente) quedan 631.735 barras de 1 minuto para NVDA y 526.049 para KO.

2.1 Por qué se congelan los CSV

El fichero original del host seguía creciendo: un job de sincronización le añadía barras. Durante el propio desarrollo el CSV de KO pasó de 518.884 a 526.386 filas en minutos. Sin una copia congelada, ninguna cifra sería reproducible.

Analogía

Congelar el CSV es como fotocopiar un documento antes de citarlo: si el original cambia mañana, tu cita sigue apuntando a lo que leíste. El MANIFEST.json de data/raw/twelvedata/ guarda el sha256, las filas y el rango de cada fichero.

2.2 Defectos conocidos de Twelve Data

Defecto Dónde Qué se hizo
20 filas con OHLC inválido NVDA 1min Reparadas (sub-tick) y marcadas is_synthetic=True
464 filas con OHLC inválido KO 1min Reparadas; una excede el tick por 0,000002 USD (residuo binario)
Barras de volumen cero rellenando sesiones cortas NVDA 2021-11-26 y 2023-07-03 (179 barras cada una) Fuera de sesión: se excluyen
Una barra de pre-mercado a las 08:20 NVDA 2022-06-03, volumen 0 Se excluye
Hueco de cobertura de 271 sesiones KO entre 2025-07-29 y 2026-08-25 Se declara (gap_sessions_before, aviso DATA_GAP_SESSIONS, D-17); no se rellena

El hueco de KO

KO no es una serie continua en el marco 1d derivado del intradía: al proveedor le faltan 13 meses. Cualquier estudio que cruce esa fecha debe declararlo. Curiosamente, en 1d_long (el diario del proveedor) KO no tiene ese hueco: son fuentes distintas.


3. Yahoo: diez series diarias (WP-01b)

Fichero Filas Rango Activo
yahoo_daily_AMZN.csv 2.942 2015-01-02 → 2026-09-15 AMZN
yahoo_daily_INTC.csv 2.942 2015-01-02 → 2026-09-15 INTC
yahoo_daily_MSFT.csv 2.942 2015-01-02 → 2026-09-15 MSFT
yahoo_daily_TSLA.csv 2.942 2015-01-02 → 2026-09-15 TSLA
yahoo_daily_BBVA_MC.csv 2.993 2015-01-02 → 2026-09-15 BBVA.MC
yahoo_daily_SAN_MC.csv 2.994 2015-01-02 → 2026-09-15 SAN.MC
yahoo_daily_TEF_MC.csv 2.993 2015-01-02 → 2026-09-15 TEF.MC
yahoo_daily_EURUSD_X.csv 3.046 2015-01-01 → 2026-09-15 EURUSD=X
yahoo_daily_GBPEUR_X.csv 3.047 2015-01-01 → 2026-09-15 GBPEUR=X
yahoo_daily_USDJPY_X.csv 3.046 2015-01-01 → 2026-09-15 USDJPY=X

Columnas de cada CSV: date,open,high,low,close,adjclose,volume.

3.1 Dos precios distintos: splits y dividendos

Aquí aparece el primer concepto fino. Yahoo entrega dos series de precio:

  • El OHLC ya viene ajustado por splits (si una acción se divide 10 a 1, los precios antiguos se dividen entre 10 para que la serie sea continua).
  • El adjclose añade además los dividendos (retorno total).
flowchart TD
    A[Precio negociado real] -->|ajuste por splits| B[OHLC de Yahoo<br/>= raw_split_adjusted]
    B -->|ajuste por dividendos| C[adjclose<br/>= total_return_adjusted]
    B --> D[adj_factor = adj_close / close]

El canónico guarda siempre el OHLC del proveedor más adj_close y adj_factor = adj_close / close, y cada corrida elige su base de precio (price_basis): raw_split_adjusted (por defecto) o total_return_adjusted. Lo verás en detalle en el capítulo siguiente.

Serie adj_factor inicial Saltos de dividendo
AMZN, TSLA 1,000000 0
INTC 0,767229 39
MSFT 0,847031 47
BBVA.MC 0,541159 26
SAN.MC 0,645629 30
TEF.MC 0,443214 23

Por qué importa

Un factor inicial de 0,54 (BBVA.MC) significa que buena parte del retorno total llegó como dividendos. Pedir la serie sin dividendos a una estrategia de largo plazo es tirar ese retorno. Pedir con dividendos a un instrumento que no tiene adj_close (NVDA, KO, los pares FX) lanza un error: nunca se sustituye en silencio.

adjclose no es point-in-time

Yahoo recalcula hacia atrás el adjclose cada vez que hay un dividendo nuevo. El valor que ves hoy para 2016 no es el que se podía conocer en 2016. El proyecto lo declara en docs/DATOS_POR_ESTRATEGIA.md; no lo corrige.

3.2 Los pares FX: tres defectos serios

Los tres pares de divisas son la parte más «sucia» del inventario.

a) Fechas corridas: filas en domingo. Los CSV traían 360 filas en domingo y solo 251 en viernes. La causa no era el mercado, sino el descargador: Yahoo sella las barras FX a la medianoche de Londres, y el script formateaba la fecha en UTC. En horario de verano británico la medianoche de Londres son las 23:00 UTC del día anterior, así que la fila salía etiquetada un día antes. La política (DEV-01B-01) es re-fechar, no descartar: se reconstruyen entre 1.786 y 1.799 filas por serie y quedan cero sábados, cero domingos y cero duplicados.

b) OHLC inválido de cientos de ticks.

Serie Tick Inválidas antes Tras redondear Violación máxima
EURUSD=X 0,00001 87 86 306 ticks
GBPEUR=X 0,00001 55 55 90 ticks
USDJPY=X 0,001 189 189 578 ticks
7 series de acciones 0 0

Se reparan y se marcan is_synthetic=True, declarando que aquí la reparación mueve el precio más de un tick (DEV-01B-07).

c) Volumen siempre cero. Las 9.139 filas de los tres pares traen volume = 0. El canónico conserva el 0 y declara volume_available: false en el catálogo, en vez de convertirlo a NaN (DEV-01B-06): un NaN rompería la validación y confundiría «no se negoció» con «no lo sabemos».

Consecuencia para las estrategias

Estrategias que usan volumen (P13, VWAP de sesión) no aplican a FX. Las que usan el rango high/low (P06–P11) funcionan sobre FX solo con el OHLC reparado, con reserva. La tabla completa está en docs/DATOS_POR_ESTRATEGIA.md.


4. El universo ampliado us_equities_36 (WP-10a)

Con solo 6 valores US, las estrategias de momento transversal (Q02, Q05, Q06) exigían «mínimo 10 elegibles» y quedaban siempre planas por construcción. La decisión D-23 encargó una descarga única (no actualización diaria) de 30 valores US diarios.

  • 30/30 descargados sin fallo, en 358 s, con tools/download_yahoo.py.
  • Todos con exactamente 2.942 filas, 2015-01-02 → 2026-09-15.
  • El grupo nuevo us_equities_36 = los 30 + NVDA, KO, AMZN, INTC, MSFT, TSLA.
Venue Valores
XNAS (12) AAPL, ADBE, AMD, COST, CSCO, GOOGL, HON, META, NFLX, PEP, QCOM, WMT
XNYS (18) BAC, CAT, CVX, DIS, GS, HD, IBM, JNJ, JPM, MA, MCD, MRK, ORCL, PFE, PG, UNH, V, XOM

El MIC no se adivina

El encargo daba HON, PEP y WMT como XNYS; Yahoo los devuelve como Nasdaq (NMS). Gana lo medido: el catálogo declara XNAS y guarda de dónde sale (yahoo_exchange_name).

4.1 El panel 2.690 × 34

align_panel sobre las 34 columnas de Yahoo (los 30 nuevos + AMZN, INTC, MSFT, TSLA), 2016-01-01 → 2026-09-15:

  • 2.690 fechas × 34 activos, same_calendar() = True, cobertura 1,000000.
  • Construido en 0,63 s.
  • n_eligible = 34 en todas las fechas: el mínimo de 10 de Q02 se cumple con margen 3,4×.

¿Y NVDA y KO? Usan freq=1d_long y el panel exige un único marco, así que no caben en la misma matriz: por eso Q01–Q03 corren sobre las 34 columnas Yahoo (deuda declarada).

4.2 Sesgo de supervivencia: declarado, no corregido

Lee esto antes de publicar un resultado de cartera

Los 36 valores se eligieron en 2026 entre los que seguían cotizando. No hay altas ni bajas: la máscara point-in-time del universo es trivialmente True. Eso sesga al alza cualquier retorno transversal, porque excluye por construcción a los que quebraron o salieron del índice entre 2015 y 2026. Más valores no lo arreglan: 36 supervivientes están tan sesgados como 6.

La regla: todo resultado de Q02/Q05/Q06 sobre us_equities_36 se publica con esta advertencia. El campo survivorship_bias del grupo en catalog/universe_v1.json la lleva escrita, y un test exige que siga ahí.


5. Qué NO se hace con los datos

Tan importante como lo que hay es lo que no hay:

No se hace Por qué / estado
Actualización diaria Fuera de alcance (D-15). El proyecto es de backtesting; los datos se congelan.
Corregir el sesgo de supervivencia Exigiría un universo histórico con altas y bajas; fuera de alcance (D-23).
Rellenar huecos (forward-fill) Prohibido: una vela plana inventada tiene volatilidad cero y mejora el Sharpe artificialmente.
Convertir divisas Un panel puede mezclar monedas pero no las convierte (fx_to_base ≠ 1 se rechaza).
Futuros, libro L1 No existen en el inventario (Q09/Q10 fuera de alcance).
Paridad publicada sobre BME/FX Los calendarios existen y son ejecutables, pero sin corrida de paridad publicada.

6. Cómo repetir la descarga de Yahoo

# los 30 valores de WP-10a (~6 min: la pausa de 12 s entre símbolos es obligatoria)
docker compose --profile dev run --rm -T dev \
    python tools/download_yahoo.py --out data/raw/yahoo

# un subconjunto
docker compose --profile dev run --rm -T dev \
    python tools/download_yahoo.py --symbols AAPL,MSFT --out data/raw/yahoo

Propiedades que la hacen repetible: es idempotente (no vuelve a bajar un símbolo con el mismo rango y sha256), reintenta con espera creciente (12, 24, 36 s) y escribe a un temporal con replace atómico.

Curiosidad: el User-Agent importa al revés de lo que parece (DEV-10A-01)

Con un Mozilla/5.0 escueto, Yahoo responde 200. Con un User-Agent completo de Chrome, el mismo servidor devuelve 429 Too Many Requests en la primera llamada. No es límite de tasa: es una regla de filtrado del borde. Por eso la cadena se deja tal cual.


Resumen

  • Dos proveedores: Twelve Data (intradía NVDA/KO, plan Basic) y Yahoo (diarios de 40 series: 34 US, 3 BME, 3 FX).
  • Los CSV fuente se congelan con su manifiesto de hashes: sin eso nada es reproducible.
  • Yahoo da el OHLC ajustado por splits y adjclose con dividendos; la base de precio se elige por corrida y nunca se sustituye en silencio.
  • FX trae fechas corridas (re-fechadas), OHLC inválido de cientos de ticks (reparado y marcado) y volumen cero (conservado y declarado).
  • us_equities_36 y su panel 2.690 × 34 habilitan Q02, con sesgo de supervivencia declarado.
  • No hay actualización diaria, ni relleno de huecos, ni conversión de divisas.

Para practicar

  1. En docs/DATOS.md §12.2, localiza la fecha en que empiezan las filas de domingo de EURUSD. Explica con tus palabras por qué coincide con el cambio al horario de verano británico.
  2. Calcula qué fracción del retorno total de TEF.MC llegó como dividendos entre 2015 y 2026 a partir de su adj_factor inicial (0,443214). ¿Qué pasaría si corrieras una estrategia de largo plazo con raw_split_adjusted?
  3. Abre catalog/universe_v1.json y busca el campo survivorship_bias. Redacta la advertencia que acompañaría a un resultado de Q02.
  4. ¿Por qué KO tiene un hueco de 271 sesiones en 1d pero ninguno en 1d_long? Pista: piensa de qué fichero fuente sale cada marco.