Lección 6.7.4 · 60 min

Proveedores y due diligence de datos alternativos

Un proveedor de datos vende una muestra, una ficha y una historia de varios años. La muestra se prueba en un día; la historia decide si el backtest dice la verdad. Esta lección recorre lo que dicen hacer Neudata, Nasdaq Data Link y Eagle Alpha y ejecuta una due diligence completa sobre los datos de tarjetas del curso: la historia reconstruida infla el R² de 0,28 a 0,39, el panel cambia de composición y la legalidad se comprueba con el texto de MAR y del RGPD.

Fig. — R² de la señal de tarjetas frente a la sorpresa de ventas: con la historia que entrega el proveedor al contratar (2023-07-03), con el dato tal como llegó y después de la entrega
Lecciones del nivel 6
  1. 6.1 · NLP financiero clásico y moderno

    1. 6.1.1 Tokenización, embeddings, diccionarios financieros y FinBERT
    2. 6.1.2 Fuentes de texto: EDGAR, CNMV, transcripciones y redes sociales
    3. 6.1.3 Event studies con señales de texto, alineación temporal y topic modeling
  2. 6.2 · LLMs para investigación y trading

    1. 6.2.1 Cómo funciona un LLM: del siguiente token a la API
    2. 6.2.2 Prompting estructurado: JSON que se puede validar
    3. 6.2.3 Guidance, KPIs y tono: qué extrae bien un LLM
    4. 6.2.4 Fine-tuning ligero para tareas financieras: LoRA y QLoRA
    5. 6.2.5 Sesgo de anticipación en LLMs: el modelo que ya conoce el futuro
  3. 6.3 · RAG financiero

    1. 6.3.1 Embeddings y bases vectoriales: FAISS, Chroma, pgvector y Qdrant
    2. 6.3.2 Chunking de documentos financieros: tablas, notas al pie y XBRL
    3. 6.3.3 RAG sobre informes, folletos y KID, y cómo evaluarlo
  4. 6.4 · Agentes de IA para trading

    1. 6.4.1 Agentes con herramientas: tool calling, ReAct y planificación
    2. 6.4.2 Marcos de agentes y MCP
    3. 6.4.3 Arquitecturas multiagente tipo TradingAgents
    4. 6.4.4 Evidencia y límites de los agentes
    5. 6.4.5 Guardarraíles para agentes: el agente propone, el motor decide
    6. 6.4.6 Agentes de investigación: hipótesis, alpha mining y revisión
  5. 6.5 · IA como copiloto del quant

    1. 6.5.1 Asistentes de código con un flujo de tests primero
    2. 6.5.2 Revisión automática de backtests y migraciones de código
    3. 6.5.3 Riesgos del código generado por IA
  6. 6.6 · Big data para trading

    1. 6.6.1 Cuándo los datos son «big»: ticks, libros, opciones y universos
    2. 6.6.2 Parquet, Arrow, Polars en streaming y DuckDB fuera de memoria
    3. 6.6.3 Dask, Spark y Ray: de una máquina a un clúster
    4. 6.6.4 Bases de series temporales: TimescaleDB, QuestDB, ClickHouse, ArcticDB y kdb+
    5. 6.6.5 Almacenamiento en la nube, lakehouse (Delta, Iceberg) y GPU (RAPIDS, cuDF)
  7. 6.7 · Datos alternativos

    1. 6.7.1 Panorama y tamaño del mercado de datos alternativos
    2. 6.7.2 Tipos de datos alternativos: qué mide cada uno
    3. 6.7.3 Web scraping responsable
    4. 6.7.4 Proveedores y due diligence de datos alternativos
    5. 6.7.5 Del dato alternativo a la señal
    6. 6.7.6 Satélite, visión por computador y cadena de suministro

Quién vende y quién responde

La lección 6.7.1 midió el mercado y la 6.7.2 recorrió los tipos de dato. Esta empieza donde empieza una compra real: un gestor ha visto una ficha en un catálogo, el proveedor le ofrece una muestra gratuita durante unas semanas y la pregunta es si pagar. Entre el gestor y el proveedor suele haber intermediarios. Lo que dicen hacer los tres que cita el temario, leído en sus propias webs el 2026-10-23:

EmpresaQué dice hacer (su web, 2026-10-23)Qué no hace por el comprador
NeudataCatálogo de «7,000+ datasets» de «4,200+» proveedores, informes de datasets escritos por analistas, información de precios de «over 2,000 data products», búsqueda de datos por encargo y avisos de datos de baja calidadProbar el dataset con la estrategia del comprador
Eagle AlphaPlataforma y asesoría: «over 2,500 alternative data products» en su portada y «over 2,600 datasets» con una taxonomía de 16 categorías y 56 subcategorías en sus preguntas frecuentes; Surveyor, una herramienta para gestionar los cuestionarios de due diligence (DDQ) de los proveedoresDecidir si el uso es legal para el comprador
Nasdaq Data LinkPlataforma en la nube con «more than 350 trusted data sets» por API, con clientes de R, Python y Excel; el cliente de Python tiene get_table, export_table y get_point_in_time para pedir una tabla «as of» una fechaGarantizar que un dataset de terceros no tenga sesgos

Las cifras son de cada empresa sobre sí misma, sin auditar, y cambian entre páginas de la misma web (Eagle Alpha da 2.500 en una y 2.600 en otra). Sirven para saber qué ofrece cada uno, no para compararlos. Ninguno de los tres puede responder la pregunta que importa: si este dataset mejora esta estrategia con los datos tal como habrían llegado. Eso es la due diligence del comprador, y tiene un orden:

Ficha y catálogoNDA y muestraPruebas técnicasPruebas de señalRevisión legalContratoDescartar
Fuente Mermaid
flowchart TB
  A[Ficha y catálogo] --> B[NDA y muestra]
  B --> C[Pruebas técnicas]
  C --> D[Pruebas de señal]
  D --> E[Revisión legal]
  E --> F[Contrato]
  C --> X[Descartar]
  D --> X
  E --> X

Las pruebas técnicas comprueban que el dato es lo que dice ser (cobertura, historia, entregas, sesgos); las de señal, que aporta algo (lección 6.7.5); la revisión legal, que se puede usar. Cualquiera de las tres puede terminar la compra. El orden importa: un dataset con historia reconstruida da pruebas de señal brillantes y engañosas, así que la historia se examina antes.

La ficha del dataset

Lo primero es reconstruir la ficha a partir de los ficheros, no copiarla del folleto: cuántos comercios, qué fechas, cuándo llega cada dato. El proveedor entrega dos cosas. alt6_tarjetas_pit.parquet es el dato tal como se entregó cada día (point-in-time, PIT): el gasto del día t llega el día t + 3 y es preliminar. Y alt6_tarjetas_historico.parquet es la historia que el proveedor entrega al firmar el contrato, el 2023-07-03.

IN [01]python
import numpy as np
import pandas as pd

pit = pd.read_parquet("alt6_tarjetas_pit.parquet")
hist = pd.read_parquet("alt6_tarjetas_historico.parquet")
mapa = pd.read_csv("alt6_comercios_verdad.csv")
for nombre, df in [("PIT", pit), ("histórico", hist)]:
    print(f"{nombre:10s} {len(df):7,d} filas · "
          f"{df['descriptor'].nunique()} comercios · "
          f"{df['fecha'].min():%Y-%m-%d} a "
          f"{df['fecha'].max():%Y-%m-%d}")
retraso = (pit["fecha_entrega"] - pit["fecha"]).dt.days
print("días entre la fecha y la entrega:", retraso.unique())
print(mapa["relacion"].value_counts().to_string())
OUT [01]Reconstruir la ficha del dataset
PIT        359,410 filas · 127 comercios · 2018-01-01 a 2025-09-30
histórico  254,889 filas · 127 comercios · 2018-01-01 a 2023-06-30
días entre la fecha y la entrega: [3]
relacion
matriz          69
marca           22
filial          18
sin_relacion    18

127 comercios: 109 son de alguna de las 25 empresas (la matriz, una marca o una filial) y 18 no tienen relación aunque se llamen parecido. Aquí se usa el mapeo correcto (alt6_comercios_verdad.csv) como si lo diera el proveedor; construirlo desde cero es la primera parte de la lección 6.7.5. La historia acaba el 2023-06-30, tres días antes de la entrega.

La pregunta práctica de la ficha es si el dato llega a tiempo. Un dataset de ventas sirve si el trimestre está completo antes de que la empresa publique sus resultados:

IN [02]python
emp = pd.read_csv("acciones6_empresas.csv")
ventas = pd.read_csv("acciones6_ventas.csv",
                     parse_dates=["fin_trimestre", "fecha_publicacion"])
ventas = ventas.merge(emp[["ticker", "id_empresa"]], on="ticker")
cubiertas = mapa["id_empresa"].dropna().unique()
v = ventas[ventas["id_empresa"].isin(cubiertas)]
# el último día del trimestre se entrega 3 días después
completo = v["fin_trimestre"] + pd.Timedelta(days=3)
margen = (v["fecha_publicacion"] - completo).dt.days
print(f"{len(v)} resultados · margen (días): mínimo {margen.min()}, "
      f"mediana {margen.median():.0f}, máximo {margen.max()}")
OUT [02]¿Llega el trimestre antes que los resultados?
800 resultados · margen (días): mínimo 17, mediana 36, máximo 62

El trimestre completo llega entre 17 y 62 días antes de los resultados: hay tiempo para calcular y operar. Con un proveedor real, esta cifra se comprueba con las fechas de entrega de la muestra, no con la promesa comercial: los retrasos que se publicitan suelen ser los del mejor día.

Point-in-time frente a historia reconstruida

Un proveedor que lleva años vendiendo mejora su producto: incorpora bancos, corrige el mapeo de comercios, ajusta los pesos. Cuando un cliente nuevo pide historia, lo habitual es entregarle la serie reconstruida con la metodología actual (backfill), no la que vieron los clientes de entonces. Con datos de precios eso es una molestia; con datos que se calibran con las cifras que pretenden anticipar, es anticipación pura.

La historia del curso hace las tres cosas típicas: completa los días con las operaciones que llegaron tarde, repondera el panel antiguo al tamaño y la composición actuales y calibra cada trimestre con las ventas ya publicadas. La forma de verlo es poner las dos versiones una al lado de la otra. Para que la comparación sea limpia hace falta una función que devuelva lo que se sabía en una fecha:

IN [03]python
def a_fecha(df, fecha):
    """Filas entregadas hasta `fecha` (incluida)."""
    return df[df["fecha_entrega"] <= pd.Timestamp(fecha)]

def por_trimestre(df):
    d = df.merge(mapa.dropna(subset=["id_empresa"]),
                 on="descriptor")
    d["fin_trimestre"] = (d["fecha"].dt.to_period("Q")
                          .dt.end_time.dt.normalize())
    return d.groupby(["id_empresa", "fin_trimestre"])["gasto_usd"].sum()

ejemplo = a_fecha(pit, "2021-04-02")
print("último día conocido el 2021-04-02:",
      ejemplo["fecha"].max().date())
gp, gh = por_trimestre(pit), por_trimestre(hist)
r = (gh / gp).dropna().rename("ratio").reset_index()
r["tramo"] = np.select(
    [r["fin_trimestre"] < "2021-03-31",
     r["fin_trimestre"] == "2021-03-31",
     r["fin_trimestre"] <= "2023-03-31"],
    ["1 antes del banco nuevo", "2 trimestre mixto",
     "3 resultados publicados"], "4 sin publicar (2023T2)")
r.groupby("tramo")["ratio"].agg(["count", "mean", "std"]).round(3)
OUT [03]Lo que se sabía en una fecha (as of)
último día conocido el 2021-04-02: 2021-03-30
                         count   mean    std
tramo
1 antes del banco nuevo    300  1.431  0.029
2 trimestre mixto           25  1.277  0.046
3 resultados publicados    200  1.053  0.066
4 sin publicar (2023T2)     25  1.067  0.001

El cociente entre la historia y el dato entregado cuenta lo que hizo el proveedor:

  • Antes del banco nuevo la historia es un 43 % mayor: la reponderación al panel actual (que tiene un 35 % más de tarjetas) y la parte de las operaciones tardías.
  • 2023T2 es el único trimestre de la historia cuyas ventas no se conocían el día de la entrega. Su cociente es 1,067 en las 25 empresas, con una desviación de 0,001: solo se le añadieron las operaciones que llegan tarde (un 6 % del gasto preliminar, 1 / 0,94 ≈ 1,064).
  • Con los resultados publicados, el cociente varía de una empresa y un trimestre a otro (desviación 0,066): la historia se ha movido hacia unas ventas que en esa fecha nadie conocía.

Esta prueba no necesita conocer la verdad del generador: basta comparar la historia con el dato en vivo de los trimestres que se solapan, o con la entrega de un trimestre que todavía no tenía resultados. Si el proveedor no puede dar el archivo de lo que entregó cada día, la comparación se hace con los primeros meses de prueba y con preguntas directas: cuándo cambió la metodología y si la historia se recalcula con ella.

Algunas plataformas ofrecen consultas «as of» de forma explícita. El cliente de Python de Nasdaq Data Link (versión 1.0.4, licencia MIT) tiene get_point_in_time con tres modos (asofdate, from, between), además de get_table para tablas paginadas; según su documentación, sin clave de API algunas tablas devuelven solo una muestra. El código, sin ejecutar (necesita red y clave; la tabla es la del ejemplo de la documentación):

IN [04]python
import nasdaqdatalink

# la clave, en la variable de entorno NASDAQ_DATA_LINK_API_KEY
tabla = nasdaqdatalink.get_table(
    "ZACKS/FC", paginate=True, ticker=["AAPL", "MSFT"],
    per_end_date={"gte": "2015-01-01"},
    qopts={"columns": ["ticker", "per_end_date"]})
# la tabla tal como estaba en una fecha
vista = nasdaqdatalink.get_point_in_time(
    "DATABASE/CODE", interval="asofdate", date="2020-01-01")

La consecuencia en la señal es lo que importa. La señal de la lección 6.7.5 compara el crecimiento interanual del gasto por tarjeta con el que implica el consenso; aquí basta con su R² frente a la sorpresa de ventas, medido con cada versión del dato:

IN [05]python
panel = pd.read_csv("alt6_tarjetas_panel.csv", parse_dates=["fecha"])
panel["fin_trimestre"] = (panel["fecha"].dt.to_period("Q")
                          .dt.end_time.dt.normalize())
tarjetas = panel.groupby("fin_trimestre")["tarjetas_activas"].mean()
T = v.set_index(["id_empresa", "fin_trimestre"]).sort_index()

def interanual(s):
    return np.log(s).groupby(level=0).diff(4)

T["g_pit"] = interanual(
    gp / tarjetas.reindex(gp.index.get_level_values(1)).to_numpy())
T["g_hist"] = interanual(gh)  # ya va al panel actual
T["g_cons"] = (np.log(T["consenso_ventas"])
               - np.log(T["ventas"].groupby(level=0).shift(4)))
T["sorpresa"] = np.log(T["ventas"] / T["consenso_ventas"])
f = T.index.get_level_values(1)

def r2(col, desde, hasta):
    d = T[(f >= desde) & (f <= hasta)].dropna(subset=[col])
    x = d[col] - d["g_cons"]
    return len(d), x.corr(d["sorpresa"]) ** 2

for nombre, col, a, b in [
        ("histórico, 2019T1-2023T1", "g_hist", "2019", "2023-03-31"),
        ("PIT, 2019T1-2023T1", "g_pit", "2019", "2023-03-31"),
        ("PIT tras la entrega, 2023T3-", "g_pit", "2023-09-30",
         "2025-12-31")]:
    n, valor = r2(col, a, b)
    print(f"{nombre:30s} n = {n}  R² = {valor:.3f}")
verdad = pd.read_csv("alt6_verdad_trimestral.csv",
                     parse_dates=["fin_trimestre"])
vv = verdad[verdad["ticker"].isin(v["ticker"])
            & (verdad["fin_trimestre"] >= "2019")]
print(f"techo (parte medible verdadera)  R² = "
      f"{vv['pred'].corr(vv['sorpresa_ventas']) ** 2:.3f}")
OUT [05]La inflación in-sample de la historia reconstruida
histórico, 2019T1-2023T1       n = 425  R² = 0.391
PIT, 2019T1-2023T1             n = 425  R² = 0.276
PIT tras la entrega, 2023T3-   n = 225  R² = 0.277
techo (parte medible verdadera)  R² = 0.567

Es la figura de la cabecera. Con la historia entregada, la señal explica el 39,1 % de la varianza de las sorpresas entre 2019 y 2023; con el dato tal como llegó cada día, el 27,6 % en los mismos 425 empresa-trimestres. El gestor que evaluase el dataset con la historia esperaría un 39 %, y después de contratar vería un 27,7 %: exactamente lo que el dato en vivo daba antes. La diferencia era la calibración con las ventas. El techo (0,567) es la parte de la sorpresa que miden los datos de verdad, el máximo que podría alcanzar un método perfecto.

El patrón es general y por eso es una prueba de due diligence: una caída del rendimiento justo en la fecha de entrega de la historia delata un backfill, igual que la caída del acierto de un LLM en su fecha de corte delata su memoria (lección 6.2.5).

Sesgos del panel

Un panel de tarjetas es una muestra de conveniencia: los titulares de los bancos o apps que ceden datos al proveedor. La lección 6.7.2 mostró el salto del panel el 2021-03-01 en el gasto diario. En la due diligence se mira su efecto en la magnitud que se va a usar, el crecimiento interanual de cada trimestre:

IN [06]python
T["bruto"] = gp
T["por_tarjeta"] = gp / tarjetas.reindex(
    gp.index.get_level_values(1)).to_numpy()
g = pd.DataFrame({c: interanual(T[c])
                  for c in ["ventas", "bruto", "por_tarjeta"]}).dropna()
mediana = 100 * g.groupby(level=1).median()
mediana.loc["2020-12-31":"2022-06-30"].round(1)
OUT [06]El salto del panel en el crecimiento interanual
               ventas  bruto  por_tarjeta
fin_trimestre
2020-12-31        4.6   13.5          3.6
2021-03-31        5.4   22.5          1.0
2021-06-30        7.5   47.6          7.5
2021-09-30        6.7   48.5          8.5
2021-12-31        7.7   46.3          6.3
2022-03-31        5.5   34.0          5.3
2022-06-30        4.6   15.4          5.4
Fig. — Crecimiento interanual mediano de 25 empresas: gasto bruto del panel de tarjetas, gasto por tarjeta activa y ventas (2019-2025)

Durante cuatro trimestres el gasto bruto crece un 46-48 % interanual mientras las ventas crecen un 7-8 %: un modelo entrenado con el gasto bruto vería un auge de consumo. Fuera del salto, el bruto también crece unos 10 puntos más que las ventas cada año, porque el panel gana un 10 % de tarjetas anual. Normalizado por tarjetas activas, sigue a las ventas.

El tamaño se corrige con una división. La composición es más difícil. Con el banco nuevo, y año tras año, el panel tiene más titulares jóvenes (del 30 % en 2018 al 47 % en 2025), y las marcas que prefieren ganan peso en el panel sin vender más. La prueba es medir, empresa a empresa, cuánto se desvía el gasto por tarjeta de sus ventas a lo largo de la muestra. El proveedor no publica la clientela de cada marca, pero el generador sí (alt6_tarjetas_verdad.csv), y permite ver si la desviación tiene un patrón:

IN [07]python
clientela = pd.read_csv("alt6_tarjetas_verdad.csv",
                        index_col="id_empresa")["clientela_joven"]
deriva = 100 * (g["por_tarjeta"] - g["ventas"]).groupby(level=0).mean()
deriva = pd.DataFrame({"deriva_pp": deriva,
                       "clientela_joven": clientela}).dropna()
print(deriva.sort_values("clientela_joven").iloc[[0, 1, -2, -1]]
      .round(2).to_string())
print("correlación:", round(deriva.corr().iloc[0, 1], 3))
b = np.polyfit(deriva["clientela_joven"], deriva["deriva_pp"], 1)[0]
print(f"pendiente: {b:.2f} pp de crecimiento anual por unidad")
OUT [07]La deriva de cada empresa y su clientela
            deriva_pp  clientela_joven
id_empresa
E011            -1.80             0.12
E117            -1.62             0.12
E120             2.04             0.95
E105             2.39             0.99
correlación: 0.982
pendiente: 4.70 pp de crecimiento anual por unidad
Fig. — Deriva del gasto por tarjeta frente a las ventas de cada empresa (media del crecimiento interanual, 2019-2025) según la fracción joven de su clientela

La deriva tiene una correlación de 0,98 con la clientela joven: el gasto por tarjeta de una empresa con clientela casi toda joven crece unos 2 puntos al año más que sus ventas, y el de una con clientela mayor, casi 2 puntos menos. En una señal de sorpresa de ventas, ese sesgo entra como un error sistemático por empresa. Con datos reales la clientela no se conoce, pero la deriva sí se mide (gasto frente a ventas publicadas, empresa a empresa) y se pregunta al proveedor cómo cambia la composición del panel y si la repondera (por edad, renta, región). La reponderación es legítima si se aplica igual en vivo y en la historia; lo que no lo es es aplicarla solo hacia atrás.

Supervivencia y entregas

El sesgo de supervivencia de los universos de acciones (lección 1.4.4) tiene su versión en los datasets: un proveedor que solo entrega historia de los comercios que siguen activos hoy borra las tiendas que cerraron, y el crecimiento pasado parece mejor. La prueba es contar los comercios con datos en cada periodo, en las dos versiones:

IN [08]python
def vivos(df):
    m = df.assign(mes=df["fecha"].dt.to_period("M"))
    return m.groupby("mes")["descriptor"].nunique()

print("PIT: comercios por mes, mínimo y máximo:",
      vivos(pit).min(), vivos(pit).max())
print("histórico:", vivos(hist).min(), vivos(hist).max())
primero = pit.groupby("descriptor")["fecha"].agg(["min", "max"])
print("altas después de 2018-01-01:",
      int((primero["min"] > "2018-01-01").sum()),
      "· bajas antes de 2025-09-30:",
      int((primero["max"] < "2025-09-30").sum()))
print("comercios del PIT que faltan en la historia:",
      len(set(pit["descriptor"]) - set(hist["descriptor"])))
OUT [08]¿Desaparecen comercios?
PIT: comercios por mes, mínimo y máximo: 127 127
histórico: 127 127
altas después de 2018-01-01: 0 · bajas antes de 2025-09-30: 0
comercios del PIT que faltan en la historia: 0

En estos datos no desaparece ningún comercio: los 127 están todos los meses en las dos versiones. La prueba pasa, y eso también es información: el panel sintético no tiene altas ni bajas de comercios, así que el sesgo de supervivencia no se puede medir aquí. Con un proveedor real, lo normal es lo contrario: tiendas que cierran, comercios que cambian de pasarela de pago (y de descriptor) y marcas que se venden. Las preguntas son si la historia incluye los comercios dados de baja y si el mapeo a empresas es el de cada fecha o el de hoy (una marca vendida en 2020 no debe sumar a su comprador en 2018).

El retraso se mide igual. Ya se vio que cada día llega tres días después; el dato llega además incompleto: el cociente de 1,067 del trimestre 2023T2 dice que al gasto preliminar le falta un 6 % que llega más tarde. Si el proveedor revisa sus entregas, hay que guardar cada versión con su fecha (como los fundamentales point-in-time del nivel 5), y no sobrescribir la anterior.

Legalidad: información privilegiada y datos personales

La revisión legal no la hace el analista cuantitativo, pero él tiene que saber qué preguntar y qué hacen los datos. Dos normas de la UE cubren lo esencial. Lo que sigue resume su texto, leído en la versión publicada en el BOE del Diario Oficial de la UE el 2026-10-231Nota 1 · El texto se leyó en las versiones del DOUE que publica el BOE: MAR y RGPD, consultadas el 2026-10-23. EUR-Lex publica el mismo texto (enlaces en los recursos) y las versiones consolidadas con las modificaciones posteriores.; no es asesoramiento jurídico.

Información privilegiada (MAR, Reglamento (UE) 596/2014). El artículo 7.1.a) la define como «la información de carácter concreto que no se haya hecho pública, que se refiera directa o indirectamente a uno o varios emisores o a uno o varios instrumentos financieros» y que, de hacerse pública, podría influir de manera apreciable en sus precios. El artículo 8 define las operaciones con ella y el 14 las prohíbe, junto con recomendarlas y comunicarla ilícitamente. Para los datos alternativos, el texto clave es el considerando 28: «Los análisis y los cálculos elaborados a partir de datos públicos no deben considerarse en sí mismos como información privilegiada», con excepciones (por ejemplo, si el mercado espera sistemáticamente su publicación y contribuye a la formación de precios), y pide a los agentes valorar «en qué medida la información no es pública». El riesgo no está en agregar datos de miles de tarjetas, sino en su origen: un dataset que contenga cifras internas del emisor, cedidas por un empleado o por un proveedor obligado a confidencialidad, puede ser información privilegiada aunque llegue en un fichero comercial.

Datos personales (RGPD, Reglamento (UE) 2016/679). El artículo 4.1 define datos personales como «toda información sobre una persona física identificada o identificable»; el 4.5, la seudonimización (datos que no pueden atribuirse a una persona «sin utilizar información adicional», guardada por separado). El considerando 26 aclara que los datos seudonimizados siguen siendo datos personales y que el reglamento no se aplica a la información anónima, la que no permite identificar a nadie con los medios que razonablemente puedan usarse. Un panel de transacciones «anonimizado» con un identificador aleatorio por titular es, en general, seudonimizado.

La diferencia no es teórica. de Montjoye y coautores (2015) estudiaron tres meses de transacciones de 1,1 millones de personas y encontraron que cuatro puntos espaciotemporales bastan para reidentificar al 90 %. La celda siguiente lo reproduce en pequeño con titulares simulados: 20.000 personas que compran sobre todo en sus seis comercios habituales, de 400, durante 90 días.

IN [09]python
rng = np.random.default_rng(0)
n_pers, n_com, dias = 20_000, 400, 90
habituales = rng.integers(0, n_com, size=(n_pers, 6))
quien_compra = np.repeat(np.arange(n_pers),
                         rng.poisson(25, n_pers) + 1)
comercio = habituales[quien_compra,
                      rng.integers(0, 6, len(quien_compra))]
otro = rng.random(len(comercio)) < 0.2  # compras fuera de lo habitual
comercio[otro] = rng.integers(0, n_com, otro.sum())
dia = rng.integers(0, dias, len(comercio))
tx = pd.DataFrame({"persona": quien_compra, "comercio": comercio,
                   "dia": dia}).drop_duplicates()
tx["punto"] = tx["comercio"] * dias + tx["dia"]
personas_en = tx.groupby("punto")["persona"].apply(set).to_dict()
puntos_de = tx.groupby("persona")["punto"].apply(np.array)
muestra = rng.choice(n_pers, 2000, replace=False)
for k in (1, 2, 3, 4):
    unicos = 0
    for p in muestra:
        elegidos = rng.choice(puntos_de[p], k, replace=False)
        candidatos = set.intersection(
            *(personas_en[x] for x in elegidos))
        unicos += len(candidatos) == 1
    print(f"{k} compra(s) conocida(s): {unicos / 20:.1f} % únicos")
celdas = tx.groupby(["comercio", "dia"])["persona"].nunique()
print(f"celdas comercio-día con menos de 10 personas: "
      f"{(celdas < 10).mean():.1%}")
OUT [09]Cuántas compras identifican a una persona
1 compra(s) conocida(s): 0.0 % únicos
2 compra(s) conocida(s): 96.0 % únicos
3 compra(s) conocida(s): 100.0 % únicos
4 compra(s) conocida(s): 100.0 % únicos
celdas comercio-día con menos de 10 personas: 9.9%

Con una compra conocida (el comercio y el día, por ejemplo por un ticket o una foto en redes) nadie queda identificado: cada comercio recibe de media unas 14 personas al día. Con dos, el 96 % de los titulares de la muestra queda singularizado; con tres, todos. Un identificador aleatorio no protege. Lo que reduce el riesgo es agregar: el proveedor del curso entrega gasto y número de operaciones por comercio y día, no compras de personas. Pero la agregación tiene su propio límite: en la simulación, el 9,9 % de las celdas comercio-día tiene menos de 10 personas, y una celda con dos o tres compradores casi vuelve a ser un dato individual. Por eso los proveedores serios suprimen o agrupan las celdas pequeñas, y el umbral que usan es una pregunta de la due diligence. Para el comprador, la diferencia entre recibir transacciones con identificador de titular y recibir agregados con umbral es la diferencia entre tratar datos personales y, en general, no tratarlos.

Las preguntas de la revisión legal salen de esas dos normas:

sínonosísínoOrigen del datoDatos del emisor nopúblicosRiesgo MAR: pararDatos de personasRevisión de contratoAgregado y anónimoBase jurídica RGPDÁrea legal decide
Fuente Mermaid
flowchart TB
  A[Origen del dato] --> B{Datos del emisor no públicos}
  B -->|sí| X[Riesgo MAR: parar]
  B -->|no| C{Datos de personas}
  C -->|no| OK[Revisión de contrato]
  C -->|sí| D{Agregado y anónimo}
  D -->|sí| OK
  D -->|no| E[Base jurídica RGPD]
  E --> F[Área legal decide]

En la práctica eso son preguntas por escrito al proveedor (el DDQ): de dónde salen los datos y con qué contrato, si los titulares consintieron y para qué, si hay datos de empleados o de proveedores del emisor, cómo se agregan, qué umbral mínimo de personas por celda se usa y qué pasa si un titular ejerce su derecho de supresión. Las respuestas se archivan con la versión del dataset: si cambian, la revisión se repite.

El informe de due diligence

Todo lo anterior cabe en una tabla que se pueda repetir cada vez que el proveedor entrega una versión nueva. Las pruebas técnicas salen del código; las legales, de las respuestas del proveedor:

IN [10]python
n_in, r2_hist = r2("g_hist", "2019", "2023-03-31")
_, r2_pit = r2("g_pit", "2019", "2023-03-31")
informe = pd.DataFrame([
    ("historia PIT disponible", "sí (archivo diario)", "ok"),
    ("inflación del R² con la historia",
     f"{r2_hist:.3f} frente a {r2_pit:.3f}", "descartar historia"),
    ("cambio de tamaño del panel", "+35 % el 2021-03-01",
     "normalizar"),
    ("deriva por composición", "corr. 0,98 con clientela",
     "corregir o modelar"),
    ("comercios que desaparecen", "0 de 127", "ok"),
    ("retraso de entrega", "3 días; 6 % llega tarde", "ok"),
    ("margen hasta resultados", f"{margen.min()} días o más", "ok"),
    ("origen y consentimiento", "pregunta al proveedor",
     "pendiente"),
    ("agregación mínima por celda", "pregunta al proveedor",
     "pendiente"),
], columns=["prueba", "resultado", "decisión"])
print(informe.to_string(index=False))
OUT [10]Informe de due diligence del dataset de tarjetas
                          prueba                resultado           decisión
         historia PIT disponible      sí (archivo diario)                 ok
inflación del R² con la historia     0.391 frente a 0.276 descartar historia
      cambio de tamaño del panel      +35 % el 2021-03-01         normalizar
          deriva por composición corr. 0,98 con clientela corregir o modelar
       comercios que desaparecen                 0 de 127                 ok
              retraso de entrega  3 días; 6 % llega tarde                 ok
         margen hasta resultados            17 días o más                 ok
         origen y consentimiento    pregunta al proveedor          pendiente
     agregación mínima por celda    pregunta al proveedor          pendiente

La decisión de este caso: el dataset es usable con el archivo point-in-time, normalizado por tarjetas, con la deriva de composición tratada y descartando la historia calibrada para cualquier backtest. Con eso, la lección 6.7.5 construye la señal y mide cuánto alfa queda.

Ejercicios

  1. Repite la prueba de la inflación con un corte distinto: mide el R² del histórico y del PIT solo en 2019-2020 y solo en 2021T2-2023T1. ¿En qué tramo infla más la historia?
Solución
IN [11]python
for a, b in [("2019", "2020-12-31"), ("2021-06-30", "2023-03-31")]:
    _, rh = r2("g_hist", a, b)
    n, rp = r2("g_pit", a, b)
    print(f"{a} a {b}: n = {n}  histórico {rh:.3f}  PIT {rp:.3f}")
OUT [11]La inflación por tramos
2019 a 2020-12-31: n = 200  histórico 0.437  PIT 0.330
2021-06-30 a 2023-03-31: n = 200  histórico 0.345  PIT 0.232

La historia infla los dos tramos casi lo mismo, unos 11 puntos de R² (0,437 frente a 0,330 en 2019-2020; 0,345 frente a 0,232 en 2021T2-2023T1). El nivel de cada tramo cambia con la muestra (200 empresa-trimestres dan un R² ruidoso), pero la diferencia entre versiones es estable: es la huella de la calibración, que se aplicó a todos los trimestres con resultados publicados.

  1. Cambia la simulación de reidentificación para que el proveedor entregue la semana en lugar del día de cada compra. ¿Cuántas compras hacen falta ahora para singularizar a la mayoría?
Solución
IN [12]python
tx["punto_sem"] = tx["comercio"] * 13 + tx["dia"] // 7
personas_sem = tx.groupby("punto_sem")["persona"].apply(set).to_dict()
puntos_sem = tx.groupby("persona")["punto_sem"].apply(
    lambda s: np.unique(s.to_numpy()))
for k in (2, 3, 4):
    unicos = 0
    for p in muestra[:500]:
        pts = puntos_sem[p]
        elegidos = rng.choice(pts, min(k, len(pts)), replace=False)
        cand = set.intersection(*(personas_sem[x] for x in elegidos))
        unicos += len(cand) == 1
    print(f"{k} compras: {unicos / 5:.1f} % únicos")
OUT [12]Reidentificar con la semana en vez del día
2 compras: 65.4 % únicos
3 compras: 98.0 % únicos
4 compras: 99.6 % únicos

Con la semana, dos compras singularizan al 65 % (frente al 96 % con el día), tres al 98 % y cuatro a casi todos. Reducir la resolución sube el umbral sin eliminar el riesgo, que es lo que encontraron de Montjoye y coautores con datos reales: incluso con información gruesa en todas las dimensiones el anonimato es escaso.

  1. Un proveedor te ofrece un dataset de «intenciones de compra» obtenido de encuestas a directivos de compras de grandes cadenas, entregado dos semanas antes de que las cadenas publiquen sus ventas. ¿Qué preguntas harías antes de probar la señal?
Solución

Primero las de MAR: si los encuestados trabajan en el emisor o en sus proveedores y comparten cifras internas no publicadas (ventas del trimestre, pedidos), el dataset puede contener información privilegiada (artículo 7) y operar con él estaría prohibido (artículo 14); el considerando 28 protege los análisis hechos con datos públicos, no los hechos con datos internos de la empresa. Después, las de contrato: si los encuestados tienen obligaciones de confidencialidad con su empleador y cómo las gestiona el proveedor. Las de RGPD son menores si las respuestas se agregan, pero hay que saber si se guardan los nombres de los encuestados. Y las técnicas: desde cuándo existe la encuesta tal como se entrega hoy (point-in-time) y si la historia se ha «limpiado» con las ventas ya publicadas.

Footnotes

  1. El texto se leyó en las versiones del DOUE que publica el BOE: MAR y RGPD, consultadas el 2026-10-23. EUR-Lex publica el mismo texto (enlaces en los recursos) y las versiones consolidadas con las modificaciones posteriores. ↩

Respuesta plegada en cada pregunta

Autoevaluación

  1. Un proveedor de tarjetas entrega en julio de 2023 una historia desde 2018 con la que su señal explica el 39 % de la varianza de las sorpresas de ventas. Con los datos tal como se entregaron cada día, el mismo cálculo da un 28 %. ¿Qué cifra debe usarse para decidir la compra?

    • AEl 39 %, porque la historia está más limpia y completa
    • BLa media de las dos
    • CEl 28 %: es lo que se habría podido medir en cada fecha; la historia está recalibrada con ventas que entonces no se conocían
    • DNinguna: el R² no sirve para evaluar datos
    Ver respuesta

    Respuesta C. El 28 %: es lo que se habría podido medir en cada fecha; la historia está recalibrada con ventas que entonces no se conocían

    La historia reconstruida incluye ajustes hechos con información posterior (reponderación al panel actual y calibración con las ventas publicadas). Después de la entrega, el dato en vivo vuelve a dar un 28 %: la diferencia era anticipación, no calidad.

  2. El gasto bruto del panel crece un 47,6 % interanual en el segundo trimestre de 2021 y las ventas un 7,5 %. ¿Qué es lo primero que hay que comprobar?

    • ASi el número de tarjetas del panel ha cambiado (en los datos, un banco nuevo añade un 35 % de tarjetas el 2021-03-01)
    • BSi el consenso de analistas se ha quedado corto
    • CSi hay un error de redondeo en el proveedor
    • DSi el trimestre tuvo más días hábiles
    Ver respuesta

    Respuesta A. Si el número de tarjetas del panel ha cambiado (en los datos, un banco nuevo añade un 35 % de tarjetas el 2021-03-01)

    Un panel crece y cambia de bancos o de apps; sin normalizar por tarjetas activas, ese cambio parece demanda. Por tarjeta, el crecimiento es del 7,5 %, el mismo que el de las ventas.

  3. Normalizado por tarjetas, el gasto de las empresas con clientela joven crece más que sus ventas año tras año, y el de las de clientela mayor, menos. ¿Qué sesgo es?

    • ASupervivencia de comercios
    • BAnticipación por la fecha de entrega
    • CError de mapeo de entidades
    • DComposición del panel: la fracción de titulares jóvenes sube con el tiempo, así que las marcas que ellos prefieren ganan peso sin vender más
    Ver respuesta

    Respuesta D. Composición del panel: la fracción de titulares jóvenes sube con el tiempo, así que las marcas que ellos prefieren ganan peso sin vender más

    En los datos, la desviación media del gasto por tarjeta frente a las ventas tiene una correlación de 0,98 con la fracción de clientela joven de cada empresa. Un panel no es la población: si su composición cambia, la medida cambia.

  4. Según el considerando 28 de MAR, ¿cuándo pueden los análisis elaborados a partir de datos públicos constituir información privilegiada?

    • ANunca: un análisis propio nunca es información privilegiada
    • BPor ejemplo, si el mercado espera sistemáticamente su publicación y esta contribuye a la formación de precios; el considerando pide valorar en qué medida la información no es pública
    • CSiempre que den beneficios
    • DSolo si los elabora un empleado del emisor
    Ver respuesta

    Respuesta B. Por ejemplo, si el mercado espera sistemáticamente su publicación y esta contribuye a la formación de precios; el considerando pide valorar en qué medida la información no es pública

    El considerando 28 dice que esos análisis no deben considerarse en sí mismos información privilegiada, con excepciones. La pregunta de la due diligence es de dónde salen los datos: un dataset que contenga información no pública del emisor (por ejemplo, filtrada por un empleado) es otro caso.

  5. Un proveedor dice que sus datos de tarjetas están «anonimizados» porque sustituye el nombre del titular por un identificador aleatorio. ¿Qué dice el RGPD?

    • AQue son datos anónimos y el RGPD no se aplica
    • BQue basta con cifrar el identificador
    • CQue son datos seudonimizados (se pueden atribuir a una persona con información adicional) y siguen siendo datos personales; solo la información que no permite identificar a nadie queda fuera
    • DQue el RGPD solo se aplica a los datos de salud
    Ver respuesta

    Respuesta C. Que son datos seudonimizados (se pueden atribuir a una persona con información adicional) y siguen siendo datos personales; solo la información que no permite identificar a nadie queda fuera

    Artículo 4, apartado 5, y considerando 26 del RGPD. Con 2 compras (comercio y día) de una persona se identifica al 96 % de los titulares de la simulación de la lección: un identificador aleatorio no impide singularizar.

Ideas clave

  • Neudata, Eagle Alpha y Nasdaq Data Link cubren partes distintas de la compra (catálogo y evaluaciones, asesoría y cuestionarios de cumplimiento, distribución por API); ninguno sustituye las pruebas del comprador sobre sus propios datos.
  • La historia que el proveedor entrega al contratar no es el dato que existía en cada fecha: la de tarjetas del curso está reponderada y calibrada con ventas ya publicadas y lleva el R² de la señal de 0,28 a 0,39. Tras la entrega, el dato en vivo vuelve a 0,28.
  • Un panel cambia de tamaño (+35 % de tarjetas el 2021-03-01) y de composición (más titulares jóvenes): se normaliza por tarjetas activas y se mide la deriva de cada empresa frente a sus ventas.
  • Supervivencia y retrasos se comprueban en los datos: en el curso, ningún comercio desaparece y cada día llega tres días después, con un 6 % de operaciones todavía por llegar; el trimestre completo está 17 días o más antes de los resultados.
  • Legalidad: MAR prohíbe operar con información privilegiada (artículos 7, 8 y 14) y el RGPD se aplica a los datos seudonimizados. Las preguntas al proveedor son de origen, consentimiento y agregación; las respuestas, para el área legal y de cumplimiento.

Recursos

Datos