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
adjcloseañ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 = 34en 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
adjclosecon 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_36y 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¶
- 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. - Calcula qué fracción del retorno total de TEF.MC llegó como dividendos entre 2015 y 2026 a
partir de su
adj_factorinicial (0,443214). ¿Qué pasaría si corrieras una estrategia de largo plazo conraw_split_adjusted? - Abre
catalog/universe_v1.jsony busca el camposurvivorship_bias. Redacta la advertencia que acompañaría a un resultado de Q02. - ¿Por qué KO tiene un hueco de 271 sesiones en
1dpero ninguno en1d_long? Pista: piensa de qué fichero fuente sale cada marco.