Lección 1.3.2 · 50 min
Fuentes de datos gratuitas
Para aprender y para investigar hay mucho dato gratuito, pero no todo es igual: unas fuentes son oficiales y estables, otras dependen de una web que no las ofrece como servicio. Esta lección recorre siete, con sus reglas de acceso leídas en su propia documentación, y procesa el formato de respuesta de cada API.
- 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
Dos clases de «gratis»
Hay mucho dato financiero gratuito, y casi todo el que aprende empieza por él. Pero bajo la misma palabra hay dos cosas muy distintas:
- Servicios oficiales: un banco central, un regulador o una agencia estadística publica sus datos con una API documentada, unas condiciones de uso y una intención de mantenerla. FRED (Reserva Federal de San Luis), EDGAR (SEC), el BCE, el Banco de España y la CNMV son de esta clase.
- Herramientas no oficiales: una librería lee los datos de una web que no los ofrece como servicio para terceros. yfinance es la más conocida. Funciona, y mucho, hasta el día en que la web cambia.
La diferencia importa para un sistema. Un servicio oficial puede cambiar de versión, pero lo anuncia y documenta. Una herramienta no oficial puede dejar de funcionar sin aviso, cambiar el formato o devolver límites de peticiones que nadie ha documentado.
Fuente Mermaid
flowchart LR
Q{¿Qué necesitas?} --> P[Precios]
Q --> M[Macro y tipos]
Q --> E[Empresas]
P --> Y[yfinance]
M --> F[FRED]
M --> B[BCE]
M --> BE[Banco de España]
E --> S[SEC EDGAR]
E --> C[CNMV]La matriz de la cabecera resume qué tipo de dato da cada una. Los huecos importan tanto como los cuadros: ninguna fuente oficial gratuita da precios de acciones con historia larga y ajustes corporativos fiables. Para eso están los proveedores de las lecciones 1.3.3 y 1.3.4.
yfinance: útil para aprender, frágil para operar
yfinance es una librería de código abierto (licencia Apache) que descarga precios, dividendos, splits y algunos datos de empresa de Yahoo Finance. Su README es explícito1Nota 1 · README de yfinance y documentación, consultados el 21/10/2026; versión 1.7.0 en PyPI.: no está afiliada, respaldada ni revisada por Yahoo; usa las APIs que Yahoo expone públicamente; está pensada para investigación y educación; y hay que consultar los términos de uso de Yahoo para saber qué derechos hay sobre los datos descargados, recordando que la API de Yahoo Finance es solo para uso personal. La lección 1.3.7 explica qué significa eso al publicar o compartir resultados.
import yfinance as yf # uv add yfinance (necesita red)
aapl = yf.Ticker("AAPL")
diario = aapl.history(period="1mo")
varios = yf.download(["MSFT", "AAPL"], start="2025-01-01",
end="2025-12-31", auto_adjust=True)Tres cosas que conviene saber antes de usarla:
- Ajusta por defecto.
downloadtieneauto_adjust=True: los precios vienen ajustados por dividendos y splits. Si se comparan con precios negociados de otra fuente, no cuadran (lecciones 1.3.8 y 1.4.3). - Límites no documentados. El README no habla de límites de peticiones, pero el código define una excepción
YFRateLimitError(«Too Many Requests») que se lanza cuando Yahoo responde 429. Una descarga de cientos de tickers seguidos puede cortarse a mitad. - Versiones frecuentes. La versión 1.7.0 es del 26 de agosto de 2026, tras otras dos en julio y agosto. Cuando Yahoo
cambia algo, la librería tiene que adaptarse; fija la versión en el
pyproject.toml(lección 0.2.2).
FRED: macro y tipos, con versiones
FRED, de la Reserva Federal de San Luis, reúne series económicas de muchos organismos: tipos de interés, inflación, empleo, tipos de cambio. Su API exige una clave gratuita (una cadena de 32 caracteres) en cada petición y admite hasta 120 peticiones por minuto; por encima responde 429, y no respetar el límite puede acabar en un bloqueo temporal2Nota 2 · FRED API: series/observations, API key y errores («Up to 120 requests per minute»), consultados el 21/10/2026..
import os
from urllib.parse import urlencode
from urllib.request import urlopen
params = {"series_id": "DGS10", "file_type": "json",
"observation_start": "2025-01-01",
"api_key": os.environ["FRED_API_KEY"]}
url = ("https://api.stlouisfed.org/fred/series/observations?"
+ urlencode(params))
with urlopen(url, timeout=10) as r:
datos = r.read()La respuesta JSON tiene metadatos de la consulta (realtime_start, realtime_end, observation_start, count,
offset, limit…) y una lista observations con un diccionario por fecha: date, value y el periodo de tiempo
real en que ese valor era el vigente. El detalle que más errores causa: value llega como texto. Una muestra
ilustrativa con esa estructura:
import json
import pandas as pd
fred_txt = """{
"realtime_start": "2026-10-21", "realtime_end": "2026-10-21",
"observation_start": "2025-01-01", "count": 3, "offset": 0,
"limit": 100000,
"observations": [
{"realtime_start": "2026-10-21", "realtime_end": "2026-10-21",
"date": "2025-01-02", "value": "4.57"},
{"realtime_start": "2026-10-21", "realtime_end": "2026-10-21",
"date": "2025-01-03", "value": "4.60"},
{"realtime_start": "2026-10-21", "realtime_end": "2026-10-21",
"date": "2025-01-06", "value": "4.62"}
]
}"""
fred = json.loads(fred_txt)
obs = pd.DataFrame(fred["observations"])
print(obs.dtypes.to_dict())
obs["date"] = pd.to_datetime(obs["date"])
obs["value"] = pd.to_numeric(obs["value"], errors="coerce")
obs[["date", "value"]]{'realtime_start': <StringDtype(na_value=nan)>, 'realtime_end': <StringDtype(na_value=nan)>, 'date': <StringDtype(na_value=nan)>, 'value': <StringDtype(na_value=nan)>}
date value
0 2025-01-02 4.57
1 2025-01-03 4.60
2 2025-01-06 4.62Sin to_numeric, obs["value"].mean() fallaría y obs["value"] + obs["value"] concatenaría cadenas. Con
errors="coerce", cualquier valor que no sea un número pasa a NaN en lugar de romper la conversión.
Los dos campos realtime_* son la puerta a una de las funciones más valiosas de FRED: los vintages. Las series
macro se revisan (el PIB, el empleo); realtime_start, realtime_end y vintage_dates permiten pedir los datos tal
como existían en una fecha pasada, la base de ALFRED, el archivo de versiones de FRED. Con una muestra ilustrativa de
una serie revisada:
versiones = pd.DataFrame({
"date": ["2025-03-01", "2025-03-01", "2025-03-01"],
"value": [151.0, 228.0, 205.0],
"realtime_start": ["2025-04-04", "2025-05-02", "2025-06-06"],
"realtime_end": ["2025-05-01", "2025-06-05", "9999-12-31"],
})
def conocido_el(v, fecha):
desde, hasta = v["realtime_start"], v["realtime_end"]
vigente = (desde <= fecha) & (hasta >= fecha)
return v.loc[vigente, ["date", "value"]]
for f in ["2025-04-10", "2025-05-20", "2026-01-01"]:
print(f, conocido_el(versiones, f).to_dict("records"))2025-04-10 [{'date': '2025-03-01', 'value': 151.0}]
2025-05-20 [{'date': '2025-03-01', 'value': 228.0}]
2026-01-01 [{'date': '2025-03-01', 'value': 205.0}]El mismo dato de marzo valía 151 para quien lo miraba en abril, 228 en mayo y 205 desde junio. Un backtest que use la
serie de hoy está usando en abril un 205 que nadie conocía. Las fechas se comparan como texto porque tienen formato ISO
(AAAA-MM-DD), que ordena igual que las fechas; 9999-12-31 marca «vigente todavía». La lección 1.5.2 trabaja esto con
datos macro completos.
Un aviso de licencia: FRED reúne series de muchos organismos, y las que llevan aviso de copyright son de terceros con restricciones propias; para usarlas más allá del uso personal hay que pedir permiso a su dueño3Nota 3 · FRED: Legal Notices, Information and Disclaimers y FRED API Terms of Use..
SEC EDGAR: fundamentales y documentos de EE. UU.
EDGAR es el sistema de la SEC donde las empresas cotizadas de EE. UU. presentan sus informes (10-K, 10-Q, 8-K…). Sus
APIs JSON en data.sec.gov no piden clave ni autenticación4Nota 4 · SEC: EDGAR Application Programming Interfaces.; las dos más útiles:
https://data.sec.gov/submissions/CIK##########.json: el historial de presentaciones de una empresa.https://data.sec.gov/api/xbrl/companyfacts/CIK##########.json: todos los datos contables XBRL de la empresa.
########## es el CIK (identificador central de la SEC) con diez dígitos, ceros a la izquierda incluidos. A cambio
de no pedir clave, la SEC fija reglas de acceso justo5Nota 5 · SEC: Accessing EDGAR Data, consultada el 21/10/2026.: un máximo actual de 10 peticiones por segundo y declarar un
User-Agent con el nombre de quien pide y un correo de contacto, con este formato de ejemplo:
User-Agent: Sample Company Name AdminContact@<sample company domain>.com. Las peticiones sin él pueden recibir un 403.
def url_submissions(cik):
return f"https://data.sec.gov/submissions/CIK{int(cik):010d}.json"
CABECERAS = {
"User-Agent": "Mi Proyecto de Research [email protected]",
"Accept-Encoding": "gzip, deflate",
}
print(url_submissions(320193))
print(url_submissions("0000320193"))https://data.sec.gov/submissions/CIK0000320193.json
https://data.sec.gov/submissions/CIK0000320193.jsonimport gzip
import json
from urllib.request import Request, urlopen
req = Request(url_submissions(320193), headers=CABECERAS)
with urlopen(req, timeout=10) as r:
crudo = r.read()
if r.headers.get("Content-Encoding") == "gzip":
crudo = gzip.decompress(crudo)
sub = json.loads(crudo)La respuesta de submissions tiene datos de la empresa (cik, name, tickers, exchanges…) y, en
filings.recent, las presentaciones recientes en formato por columnas: un diccionario de listas paralelas
(accessionNumber, filingDate, reportDate, form, primaryDocument…), no una lista de registros. Es justo lo que
espera pd.DataFrame. Con una muestra ilustrativa de esa estructura, de una empresa ficticia:
sub = {
"cik": "0000000042", "name": "NOVA CORP", "tickers": ["NOVA"],
"filings": {"recent": {
"accessionNumber": ["0000000042-25-000031",
"0000000042-25-000027",
"0000000042-25-000012"],
"filingDate": ["2025-11-05", "2025-10-24", "2025-08-06"],
"reportDate": ["2025-09-30", "", "2025-06-30"],
"form": ["10-Q", "8-K", "10-Q"],
"primaryDocument": ["nova-10q.htm", "nova-8k.htm",
"nova-10q.htm"],
}},
}
pres = pd.DataFrame(sub["filings"]["recent"])
pres["filingDate"] = pd.to_datetime(pres["filingDate"])
pres[pres["form"].isin(["10-K", "10-Q"])][
["form", "reportDate", "filingDate"]] form reportDate filingDate
0 10-Q 2025-09-30 2025-11-05
2 10-Q 2025-06-30 2025-08-06Las dos fechas son la clave para usar fundamentales sin anticipación: reportDate es el final del periodo (30 de
septiembre) y filingDate, el día en que el informe se hizo público (5 de noviembre). Un backtest solo puede usar las
cifras del trimestre desde la segunda (lección 1.5.1).
Respetar el ritmo de EDGAR
Diez peticiones por segundo parecen muchas hasta que se descargan miles de documentos en un bucle. El bucle más simple dispara las peticiones tan rápido como responde la red y supera el límite en el primer segundo. La solución es un limitador de ritmo que espera lo necesario entre peticiones. Para ejecutarlo sin red, con un reloj virtual en milisegundos enteros (así no hay errores de redondeo en las comparaciones) en el que cada petición tarda 20 ms en responder:
Fuente Mermaid
flowchart LR
P[Petición] --> L{¿10 en 1 s?}
L -- sí --> W[Esperar]
W --> L
L -- no --> S[data.sec.gov]from collections import deque
def simular(n, por_segundo=None, latencia_ms=20):
t, hechas, horas = 0, deque(), [] # milisegundos enteros
for _ in range(n):
if por_segundo is not None:
while hechas and t - hechas[0] >= 1000:
hechas.popleft()
if len(hechas) >= por_segundo:
t = hechas[0] + 1000
hechas.popleft()
hechas.append(t)
horas.append(t)
t += latencia_ms
return pd.Series(horas)
def peor_segundo(ms):
return max(((ms >= h) & (ms < h + 1000)).sum() for h in ms)
sin_limite, con_limite = simular(100), simular(100, por_segundo=10)
print("sin limitador:", f"{sin_limite.iloc[-1] / 1000:.2f} s,",
"máximo en un segundo:", peor_segundo(sin_limite))
print("con limitador:", f"{con_limite.iloc[-1] / 1000:.2f} s,",
"máximo en un segundo:", peor_segundo(con_limite))sin limitador: 1.98 s, máximo en un segundo: 50
con limitador: 9.18 s, máximo en un segundo: 10Sin limitador, las 100 peticiones salen en 2 segundos y en el peor segundo hay 50: cinco veces el máximo. Con el limitador tardan 9,18 segundos y nunca hay más de 10 en ninguna ventana de un segundo. Es más lento, pero es la diferencia entre una descarga que termina y una IP bloqueada. La lección 1.4.1 generaliza el limitador y le añade reintentos.
BCE y Banco de España: datos europeos sin clave
El Portal de Datos del BCE ofrece un servicio web SDMX 2.1 con punto de entrada en data-api.ecb.europa.eu/service/.
Cada serie se identifica por una clave que une los valores de sus dimensiones con puntos: el tipo de cambio de
referencia diario del dólar frente al euro es EXR/D.USD.EUR.SP00.A (conjunto EXR; frecuencia D, divisa USD,
divisa de referencia EUR, tipo SP00, variante A). Admite csvdata y jsondata, entre otros formatos, y
parámetros como startPeriod o lastNObservations6Nota 6 · ECB Data Portal: API, Overview y Data.. No pide clave.
url = ("https://data-api.ecb.europa.eu/service/data/EXR/"
"D.USD.EUR.SP00.A?lastNObservations=3&format=csvdata")
bce = pd.read_csv(url)La respuesta CSV trae una fila por observación y más de treinta columnas: las dimensiones de la clave, la fecha
(TIME_PERIOD), el valor (OBS_VALUE) y muchos metadatos (estado de la observación, título, unidad, decimales…). Una
muestra con las columnas principales y los tres valores que devolvía la consulta anterior el 21/10/2026:
from io import StringIO
bce_csv = """KEY,FREQ,CURRENCY,TIME_PERIOD,OBS_VALUE,OBS_STATUS,TITLE
EXR.D.USD.EUR.SP00.A,D,USD,2026-10-16,1.1253,A,US dollar/Euro
EXR.D.USD.EUR.SP00.A,D,USD,2026-10-19,1.1225,A,US dollar/Euro
EXR.D.USD.EUR.SP00.A,D,USD,2026-10-20,1.1215,A,US dollar/Euro
"""
bce = pd.read_csv(StringIO(bce_csv), parse_dates=["TIME_PERIOD"])
eurusd = bce.set_index("TIME_PERIOD")["OBS_VALUE"].rename("EURUSD")
print(eurusd)
print(f"Variación: {eurusd.pct_change().iloc[-1]:.3%}")TIME_PERIOD
2026-10-16 1.1253
2026-10-19 1.1225
2026-10-20 1.1215
Name: EURUSD, dtype: float64
Variación: -0.089%El tipo de referencia del BCE es un dato del día (el propio título de la serie lo sitúa a las 14:15 CET), no un precio negociable: sirve para valorar, para convertir divisas en un informe o para macro, no para un sistema intradía de divisas (lección 1.2.4). Su reutilización es gratuita con dos condiciones: citar la fuente y no modificar las estadísticas ni sus metadatos; los datos de terceros que publique el BCE necesitan permiso de quien los elabora7Nota 7 · BCE: Policy regarding the reuse of ESCB statistics..
El Banco de España tiene un servicio web JSON que da acceso a toda la información de su sección de estadísticas y
de la aplicación HERMES, con dos tipos de petición: el último dato de una o varias series (favoritas) y un listado de
la serie en un rango (listaSeries). Para descargas masivas ofrece también todos los CSV de series temporales en un ZIP.
La serie D_1NBAF472 es el Euribor a un año8Nota 8 · Banco de España: servicio web de Estadísticas (API) y descargas completas, consultadas el 21/10/2026.:
url = ("https://app.bde.es/bierest/resources/srdatosapp/favoritas"
"?idioma=es&series=D_1NBAF472")bde_txt = """[{"serie": "D_1NBAF472",
"descripcionCorta": "Euribor a un año", "codFrecuencia": "M",
"decimales": 3, "simbolo": "%", "tendencia": "+",
"fechaValor": "2026-09-01T08:15:00Z", "valor": 3.247}]"""
bde = pd.DataFrame(json.loads(bde_txt))
bde["fechaValor"] = pd.to_datetime(bde["fechaValor"])
bde[["serie", "descripcionCorta", "fechaValor", "valor", "simbolo"]] serie descripcionCorta fechaValor valor simbolo
0 D_1NBAF472 Euribor a un año 2026-09-01 08:15:00+00:00 3.247 %Es la respuesta que devolvía el servicio el 21/10/2026: el dato mensual de septiembre de 2026, un 3,247 %. La marca
fechaValor va en UTC (Z) y la frecuencia es mensual (M): conviene guardarlas, porque una serie mensual unida a
precios diarios necesita una regla de alineación explícita (lección 1.4.2).
CNMV: información regulatoria
La CNMV no tiene una API pública documentada como las anteriores, pero publica información útil en su web, en consultas y archivos descargables9Nota 9 · CNMV: posiciones cortas, otra información relevante, registros oficiales y estadísticas.:
- Posiciones cortas: las notificaciones de posiciones cortas netas sobre acciones españolas, con un fichero descargable de las publicadas e históricas. Útil para estudiar presión bajista o riesgo de short squeeze.
- Otra información relevante: las comunicaciones de los emisores (antes «hechos relevantes», hasta febrero de 2020), notificaciones de directivos y participaciones significativas.
- Registros oficiales de emisores y cotizadas, y estadísticas de emisores, fondos y empresas de servicios de inversión.
Como no hay API, el acceso es por descarga de archivos o lectura de páginas, y cualquier automatización tiene que respetar las condiciones de uso de la web. Es información para research y para contexto, no un feed.
Un formato común para fuentes distintas
Cada fuente devuelve un formato distinto. Antes de guardar, conviene llevar todo a una forma común, con la fuente y la serie como columnas, para que el resto del código no dependa de quién dio cada dato:
largo = pd.concat([
pd.DataFrame({"fuente": "FRED", "serie": "DGS10",
"fecha": obs["date"], "valor": obs["value"]}),
pd.DataFrame({"fuente": "BCE", "serie": "EXR.D.USD.EUR",
"fecha": eurusd.index, "valor": eurusd.values}),
pd.DataFrame({"fuente": "BdE", "serie": bde["serie"],
"fecha": bde["fechaValor"].dt.tz_localize(None)
.dt.normalize(), "valor": bde["valor"]}),
], ignore_index=True)
print(largo.dtypes.to_dict())
largo.groupby(["fuente", "serie"])["fecha"].agg(["min", "max", "size"]){'fuente': <StringDtype(na_value=nan)>, 'serie': <StringDtype(na_value=nan)>, 'fecha': dtype('<M8[us]'), 'valor': dtype('float64')}
min max size
fuente serie
BCE EXR.D.USD.EUR 2026-10-16 2026-10-20 3
BdE D_1NBAF472 2026-09-01 2026-09-01 1
FRED DGS10 2025-01-02 2025-01-06 3El formato largo (una fila por fuente, serie y fecha) es fácil de guardar en Parquet, de filtrar y de pivotar cuando
hace falta (lección 1.4.7). Lo que se pierde al normalizar (las unidades, la frecuencia, el estado de cada observación)
se guarda aparte, en una tabla de metadatos por serie: sin ella, dentro de un año nadie sabrá si 3.247 eran puntos
porcentuales o tanto por uno.
Ejercicios
- Añade a
versionesuna cuarta publicación del dato de marzo, 199,0, vigente desde el 2025-09-05. Ajusta elrealtime_endde la anterior y comprueba qué valor se conocía el 2025-07-01 y el 2025-10-01.
Solución
v2 = versiones.copy()
v2.loc[2, "realtime_end"] = "2025-09-04"
v2.loc[3] = ["2025-03-01", 199.0, "2025-09-05", "9999-12-31"]
for f in ["2025-07-01", "2025-10-01"]:
print(f, conocido_el(v2, f)["value"].tolist())2025-07-01 [205.0]
2025-10-01 [199.0]El 1 de julio seguía vigente 205; desde el 5 de septiembre, 199. Cada revisión cierra el periodo de la anterior: si se
olvida ajustar realtime_end, dos versiones parecen vigentes a la vez y conocido_el devuelve dos valores.
- Con
simular, ¿cuántas peticiones por segundo como máximo saldrían si cada respuesta de EDGAR tardase 150 ms y no hubiera limitador? ¿Hace falta entonces el limitador?
Solución
lentas = simular(100, latencia_ms=150)
print("máximo en un segundo:", peor_segundo(lentas))máximo en un segundo: 7Con 150 ms por respuesta, un bucle secuencial no pasa de 7 peticiones por segundo: por debajo del límite. Pero el limitador sigue haciendo falta: la latencia cambia (una caché, otra red) y, en cuanto el código se paraleliza con hilos, la cuenta se multiplica. El límite se impone en el código, no se confía a la lentitud de la red.
- Vas a estudiar si el diferencial entre el Euribor a un año y el bono a 10 años de EE. UU. anticipa algo en el EURUSD. Di de qué fuentes sacarías cada serie, en qué frecuencia vienen y qué tendrías que resolver para unirlas.
Solución
Euribor a un año: Banco de España (o el BCE), mensual. Rentabilidad del bono a 10 años de EE. UU.: FRED, diaria. EURUSD: el tipo de referencia diario del BCE (EXR), un dato de cierre a media tarde de Fráncfort. Para unirlas hay que decidir la frecuencia común (mensual, si el Euribor es mensual), qué valor diario representa cada mes (el último conocido al cierre de mes, no el promedio que solo se sabe después), las horas de cada dato (el tipo del BCE se fija antes del cierre de Nueva York) y, si se usan datos que se revisan, la versión que se conocía en cada fecha (vintages de FRED). Además, citar las fuentes y comprobar las condiciones de uso de cada serie (lección 1.3.7).
Footnotes
-
README de yfinance y documentación, consultados el 21/10/2026; versión 1.7.0 en PyPI. ↩
-
FRED API: series/observations, API key y errores («Up to 120 requests per minute»), consultados el 21/10/2026. ↩
-
FRED: Legal Notices, Information and Disclaimers y FRED API Terms of Use. ↩
-
SEC: Accessing EDGAR Data, consultada el 21/10/2026. ↩
-
Banco de España: servicio web de Estadísticas (API) y descargas completas, consultadas el 21/10/2026. ↩
-
CNMV: posiciones cortas, otra información relevante, registros oficiales y estadísticas. ↩
Respuesta plegada en cada pregunta
Autoevaluación
¿Qué dice el propio README de yfinance sobre su relación con Yahoo?
- AQue es la API oficial de Yahoo Finance
- BQue no está afiliada, respaldada ni revisada por Yahoo, que es para investigación y educación y que hay que consultar los términos de Yahoo sobre el uso de los datos
- CQue los datos son de dominio público
- DQue tiene un acuerdo comercial con Yahoo
Ver respuesta
Respuesta B. Que no está afiliada, respaldada ni revisada por Yahoo, que es para investigación y educación y que hay que consultar los términos de Yahoo sobre el uso de los datos
yfinance es una herramienta de código abierto que usa las APIs públicas de Yahoo. El README remite a los términos de uso de Yahoo y recuerda que su API es para uso personal.
¿Qué exige la SEC para descargar de EDGAR de forma automatizada?
- AUna clave de API de pago
- BDeclarar un User-Agent con nombre y correo de contacto y no pasar de 10 peticiones por segundo
- CRegistrarse como profesional
- DNada
Ver respuesta
Respuesta B. Declarar un User-Agent con nombre y correo de contacto y no pasar de 10 peticiones por segundo
La página «Accessing EDGAR Data» fija un máximo actual de 10 peticiones por segundo y pide declarar el User-Agent. Las APIs de data.sec.gov no necesitan clave.
En la API de FRED, el campo
valuede cada observación llega como"4.33". ¿Qué hay que hacer?- ANada, ya es un número
- BConvertirlo con
pd.to_numeric(..., errors="coerce"), porque es texto y puede haber valores no numéricos - CMultiplicarlo por 100
- DLeerlo como fecha
Ver respuesta
Respuesta B. Convertirlo con
pd.to_numeric(..., errors="coerce"), porque es texto y puede haber valores no numéricosEl JSON de FRED trae los valores entre comillas. Si no se convierten, las operaciones aritméticas fallan o concatenan texto.
¿Qué permite el parámetro
realtime_start/realtime_endde FRED?- APedir datos en tiempo real
- BPedir los datos tal como se conocían en una fecha pasada, con las revisiones posteriores excluidas (ALFRED)
- CCambiar la zona horaria
- DLimitar el número de observaciones
Ver respuesta
Respuesta B. Pedir los datos tal como se conocían en una fecha pasada, con las revisiones posteriores excluidas (ALFRED)
Las series macro se revisan. Con el periodo de tiempo real (o
vintage_dates) se obtienen las versiones que existían en cada fecha, que es lo que necesita un backtest sin anticipación (lección 1.5.2).¿Qué condición pone el BCE para reutilizar sus estadísticas públicas?
- APagar una licencia
- BCitar la fuente y no modificar las estadísticas (ni sus metadatos); los datos de terceros necesitan permiso de su autor
- CUso solo no comercial
- DNinguna
Ver respuesta
Respuesta B. Citar la fuente y no modificar las estadísticas (ni sus metadatos); los datos de terceros necesitan permiso de su autor
La política de reutilización de estadísticas del SEBC permite la reutilización gratuita citando la fuente («Source: ECB statistics.») y sin modificar los datos.
Ideas clave
- «Gratis» incluye dos cosas distintas: servicios oficiales con API documentada (FRED, EDGAR, BCE, Banco de España, CNMV) y herramientas no oficiales que leen una web (yfinance).
- yfinance no está afiliada a Yahoo, ajusta precios por defecto (
auto_adjust=True) y puede devolver errores de límite (YFRateLimitError): sirve para aprender, no como base de un sistema. - Cada fuente oficial tiene sus reglas: FRED pide clave y admite hasta 120 peticiones por minuto; EDGAR, User-Agent declarado y 10 por segundo; el BCE, citar la fuente.
- Las respuestas hay que tiparlas: valores como texto en FRED, listas en columnas en EDGAR, decenas de columnas de metadatos en el CSV del BCE.
- Las fuentes cambian: la descarga directa de CSV de Stooq, muy usada durante años, devolvía el 21/10/2026 una verificación de navegador en lugar del archivo.