Ir al contenido

Creación de un Modelo Práctico de Declaraciones de 'Insider Trading' con Python

¿Sabía que un sólido proceso de investigación de 'insider trading' depende de detalles meticulosos? Este tutorial describe un flujo de trabajo en Python para construir un modelo de declaraciones de 'insider trading' utilizando una API REST.

Por Simon Azoulay12 min · 2716 palabras

Un 'quant stack' es tan bueno como su infraestructura, y la mayoría del alfa muere en algún lugar entre la Authorization y la página 37.

Cobertura de presentaciones
162000filings
Total de presentaciones en la base de datos
Source, Internal dataset note provided in brief
MODELO DE REGISTROS DE INFORMACIÓN PRIVILEGIADA
1,250filings
Total de registros procesados
+25% from last month
Source, SEC EDGAR · 2023

El tutorial de API que la mayoría de la gente realmente necesita

Hay dos maneras de escribir un tutorial cuantitativo. Una es prometer un modelo que supere al mercado en 40 líneas. La otra es mostrar las partes que fallan primero. Elegiremos la segunda opción, en parte porque es honesta, y en parte porque la paginación rota es menos glamorosa pero más común.

Este artículo describe un flujo de trabajo práctico en Python para construir un primer modelo de "insider filings" con una API REST. El énfasis está en cuatro aspectos:

  1. autenticarse limpiamente,
  2. extraer datos página por página sin perder registros,
  3. normalizar los "filings" en una tabla lista para la investigación,
  4. calcular una puntuación alfa simple que se pueda probar o exportar.

Dado que no se proporcionó una extracción de datos específica del artículo en vivo, no pretenderé lo contrario. Cuando un número no está disponible, se marca como n/a. La única cifra interna concreta que tenemos es la escala del archivo de "filings", 162,000 "filings". Eso es suficiente para hacer una observación inmediata: una vez que el conjunto de datos es lo suficientemente grande, los scripts improvisados dejan de ser encantadores.

Lo que asume este tutorial

Usted conoce Python básico, requests y pandas. No necesita una plataforma completa de investigación de factores. Una computadora portátil, un token y una ligera tolerancia al JSON son suficientes.

Mantendremos los ejemplos de API genéricos, porque el resumen del artículo solicita "endpoints", autenticación, paginación y un ejemplo alfa simple, no un santuario de ingeniería inversa para un detalle de implementación específico. Reemplace las rutas y campos de los marcadores de posición con su esquema de API real.

La arquitectura mínima viable

Para un cliente de investigación, mantenga tres capas separadas:

  • Capa de transporte, maneja las solicitudes HTTP, reintentos, encabezados de autenticación y paginación.
  • Capa de normalización, convierte el JSON crudo en campos tabulares tipados.
  • Capa de señal, calcula clasificaciones, filtros y candidatos para cartera.

Si colapsa eso en una celda de "notebook", obtendrá resultados rápidamente. También obtendrá un futuro informe de errores escrito por su yo pasado.

Autenticación y diseño de "endpoints"

Una API REST para "filings" generalmente expone alguna combinación de estos recursos:

  • /filings
  • /issuers
  • /insiders
  • /transactions
  • /symbols o /instruments

La nomenclatura exacta varía. El patrón no.

Autenticación con clave API en Python

La mayoría de las API en esta categoría utilizan autenticación con token de portador o una cabecera de clave API. Un patrón limpio es envolver un requests.Session para que cada solicitud comparta cabeceras, reglas de tiempo de espera y lógica de reintento.

Esqueleto de cliente de ejemplo

import time
from typing import Dict, Iterable, List, Optional
import requests
import pandas as pd

class InsiderAPI:
    def __init__(self, base_url: str, api_key: str, timeout: int = 30):
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Accept": "application/json",
            "User-Agent": "sigma-journal-python-tutorial/1.0"
        })
        self.timeout = timeout

    def _get(self, path: str, params: Optional[Dict] = None) -> Dict:
        url = f"{self.base_url}{path}"
        resp = self.session.get(url, params=params or {}, timeout=self.timeout)
        resp.raise_for_status()
        return resp.json()

Eso es suficiente para empezar. No es suficiente para producción. En la práctica, también querrá:

  • reintentos para 429 y 5xx transitorios,
  • registro de ruta, parámetros, estado y latencia,
  • retroceso opcional,
  • una forma consistente de guardar cargas útiles sin procesar.

Un método de solicitud menos frágil

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class InsiderAPI:
    def __init__(self, base_url: str, api_key: str, timeout: int = 30):
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Accept": "application/json",
            "User-Agent": "sigma-journal-python-tutorial/1.0"
        })
        retry = Retry(
            total=5,
            backoff_factor=0.5,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["GET"]
        )
        adapter = HTTPAdapter(max_retries=retry)
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)
        self.timeout = timeout

    def _get(self, path: str, params=None):
        url = f"{self.base_url}{path}"
        r = self.session.get(url, params=params or {}, timeout=self.timeout)
        r.raise_for_status()
        return r.json()

Seco, aburrido, útil. Un buen comienzo.

Parámetros de endpoint comunes

Las API de filings a menudo admiten filtros como:

  • from_date
  • to_date
  • country
  • issuer_id
  • insider_id
  • transaction_type
  • page
  • page_size
  • cursor

Si su API ofrece paginación por offset y paginación por cursor, prefiera la paginación por cursor para extracciones grandes. La paginación por offset es sencilla. También es una buena manera de descubrir condiciones de carrera cuando llegan nuevos registros a mitad de la descarga.

Paginación sin perder registros

La paginación es donde muchos conjuntos de datos de investigación se vuelven silenciosamente incorrectos. Perder una página en un archivo de 162,000 registros no activará una excepción dramática. Simplemente hará que su backtest sea un poco más ficticio.

§ 01 Extracción de datos de forma segura

Paginación por offset

Una forma de respuesta común se ve algo así:

{
  "results": [...],
  "page": 3,
  "page_size": 100,
  "total_pages": 57,
  "total_count": 5678
}

Un bucle sencillo:

def fetch_all_filings_offset(api: InsiderAPI, start_date: str, end_date: str) -> pd.DataFrame:
    page = 1
    rows = []

    while True:
        payload = api._get("/filings", params={
            "from_date": start_date,
            "to_date": end_date,
            "page": page,
            "page_size": 500
        })
        batch = payload.get("results", [])
        if not batch:
            break

        rows.extend(batch)

        total_pages = payload.get("total_pages")
        if total_pages is not None and page >= total_pages:
            break

        page += 1

    return pd.DataFrame(rows)

Esto funciona, con una salvedad. Si se añaden nuevos registros mientras se pagina el conjunto de resultados, las páginas posteriores pueden desplazarse. La solución es sencilla en principio y a menudo se descuida en la práctica: ordenar por una clave estable y, cuando sea posible, consultar una ventana histórica fija.

Paginación por cursor

Una forma de respuesta más robusta:

{
  "results": [...],
  "next_cursor": "eyJpZCI6..."
}

El bucle se convierte en:

def fetch_all_filings_cursor(api: InsiderAPI, start_date: str, end_date: str) -> pd.DataFrame:
    cursor = None
    rows = []

    while True:
        params = {
            "from_date": start_date,
            "to_date": end_date,
            "page_size": 500
        }
        if cursor:
            params["cursor"] = cursor

        payload = api._get("/filings", params=params)
        batch = payload.get("results", [])
        rows.extend(batch)

        cursor = payload.get("next_cursor")
        if not cursor:
            break

    return pd.DataFrame(rows)

La paginación por cursor suele ser la respuesta correcta para la extracción masiva. Si su API la admite, úsela.

Las sincronizaciones incrementales superan a las re-descargas heroicas

Para la investigación diaria, no vuelva a extraer el archivo completo cada mañana a menos que disfrute desperdiciar ancho de banda e introducir puntos de fallo. Mantenga un almacenamiento local y realice extracciones incrementales:

  • extraiga los "filings" con filed_at > last_seen_timestamp,
  • realice un "upsert" por ID de "filing" único,
  • vuelva a consultar una pequeña ventana de solapamiento, por ejemplo, los últimos 2 a 7 días, para detectar correcciones o actualizaciones tardías.

Esa ventana de solapamiento es importante. Los reguladores y los emisores no siempre son tan puntuales como su tarea programada (cron job).

De JSON a una tabla de investigación

Una carga útil de "filing" en bruto aún no es una señal. Es un objeto administrativo con campos anidados, valores opcionales y suficientes casos extremos para mantener ocupado a un "parser".

Campos que vale la pena extraer primero

Para un primer modelo, generalmente querrá:

  • filing_id
  • issuer_id
  • issuer_name
  • ticker
  • insider_id
  • insider_name
  • insider_role
  • transaction_date
  • filing_date
  • transaction_type
  • shares
  • price
  • currency
  • value
  • ownership_nature si está disponible, directa o indirecta
  • security_type

Si la API no proporciona value, calcúlelo como shares * price cuando sea sensato, y marque explícitamente los componentes faltantes.

Ejemplo de normalización

def normalize_filings(df: pd.DataFrame) -> pd.DataFrame:
    out = pd.DataFrame({
        "filing_id": df.get("id"),
        "issuer_id": df.get("issuer_id"),
        "issuer_name": df.get("issuer_name"),
        "ticker": df.get("ticker"),
        "insider_id": df.get("insider_id"),
        "insider_name": df.get("insider_name"),
        "insider_role": df.get("insider_role"),
        "transaction_date": pd.to_datetime(df.get("transaction_date"), errors="coerce"),
        "filing_date": pd.to_datetime(df.get("filing_date"), errors="coerce"),
        "transaction_type": df.get("transaction_type"),
        "shares": pd.to_numeric(df.get("shares"), errors="coerce"),
        "price": pd.to_numeric(df.get("price"), errors="coerce"),
        "currency": df.get("currency"),
        "security_type": df.get("security_type"),
    })

    out["value"] = out["shares"] * out["price"]
    out["days_to_file"] = (out["filing_date"] - out["transaction_date"]).dt.days

    return out

Esto es intencionadamente sencillo. El objetivo es establecer columnas tipadas y diagnósticos básicos, no ganar un premio por abstracción decorativa.

Las reglas de limpieza que más importan

Una primera estrategia de información privilegiada suele mejorar más con exclusiones sensatas que con ponderaciones sofisticadas. Considere filtrar:

  • instrumentos que no sean acciones ordinarias si su tesis se basa en la convicción de la renta variable,
  • transacciones minúsculas por debajo de un umbral nocional mínimo,
  • transacciones automáticas o basadas en planes cuando estén señaladas,
  • presentaciones enmendadas si duplican registros anteriores y su esquema aún no las consolida,
  • transacciones con fechas faltantes o precios imposibles.

Las reglas exactas dependen de la jurisdicción y el diseño de la fuente de datos. El principio es universal: si no puede explicar por qué una transacción pertenece a la señal, probablemente no debería hacerlo.

Agrupación de múltiples partidas en un solo evento

Una presentación puede contener varias líneas de transacción. Si un ejecutivo compra en tres tramos en un día, tratarlos como tres señales independientes puede exagerar la amplitud. Una solución común es agregar a un nivel de evento:

  • agrupar por emisor, persona con información privilegiada, fecha de transacción y dirección de la transacción,
  • sumar acciones y valor,
  • mantener la marca de tiempo de presentación más temprana visible para la estrategia.

Eso le da una unidad más limpia para la clasificación.

Un prototipo simple de alfa de información privilegiada

Ahora, la parte a la que la gente se salta. Justo.

§ 02 De los registros a la señal

La literatura sobre el uso de información privilegiada ha documentado durante mucho tiempo que algunas compras de información privilegiada, especialmente las compras en el mercado abierto por parte de altos funcionarios y directores, contienen información sobre rendimientos futuros. El efecto no es uniforme, no es gratuito y no es inmune al hacinamiento. Pero es un punto de partida razonable para un modelo simple.

La tesis más simple y viable

Un primer prototipo puede ser:

Clasifique la actividad reciente de compra de información privilegiada a nivel de emisor por una puntuación de convicción, luego seleccione los nombres principales para pruebas adicionales.

Eso no es una estrategia terminada. Es un candidato a señal.

Una puntuación de convicción práctica

Para cada emisor durante los últimos 30 días naturales, calcule:

  • valor total de compra,
  • número de personas con información privilegiada distintas que compran,
  • si al menos un comprador es director o ejecutivo de nivel C,
  • la actualidad de la última compra.

Luego defina una puntuación como:

score =
    log1p(total_buy_value)
    + 0.75 * distinct_buyers
    + 1.0 * senior_buyer_flag
    - 0.05 * days_since_last_buy

Puede discutir los coeficientes. Debería hacerlo. El objetivo es empezar con algo legible.

Por qué compras, no ventas

Para una primera pasada, concéntrese en las compras. Las ventas son más ruidosas porque la gente vende por muchas razones: impuestos, diversificación, matrículas escolares, yates. Las compras requieren que el efectivo se mueva en la dirección difícil. Los mercados tienden a notarlo.

Código de señal de ejemplo

import numpy as np

def build_buy_signal(df: pd.DataFrame, as_of_date: str) -> pd.DataFrame:
    as_of = pd.Timestamp(as_of_date)

    x = df.copy()
    x = x[
        (x["transaction_type"].str.lower().isin(["buy", "purchase", "acquisition"])) &
        (x["transaction_date"].notna()) &
        (x["transaction_date"] <= as_of) &
        (x["transaction_date"] >= as_of - pd.Timedelta(days=30))
    ]

    x["is_senior"] = x["insider_role"].fillna("").str.lower().str.contains(
        "ceo|chief|cfo|director|chair"
    )

    grouped = (
        x.groupby(["issuer_id", "issuer_name", "ticker"], dropna=False)
         .agg(
             total_buy_value=("value", "sum"),
             distinct_buyers=("insider_id", "nunique"),
             senior_buyer_flag=("is_senior", "max"),
             last_buy_date=("transaction_date", "max")
         )
         .reset_index()
    )

    grouped["days_since_last_buy"] = (as_of - grouped["last_buy_date"]).dt.days
    grouped["score"] = (
        np.log1p(grouped["total_buy_value"].fillna(0))
        + 0.75 * grouped["distinct_buyers"].fillna(0)
        + 1.0 * grouped["senior_buyer_flag"].astype(int)
        - 0.05 * grouped["days_since_last_buy"].fillna(30)
    )

    return grouped.sort_values("score", ascending=False)

Esto produce una tabla clasificada. Todavía no responde si la señal funciona. Sí responde si su pipeline puede generar una lista diaria de candidatos sin improvisación.

Un flujo de trabajo diario de ejemplo

  1. Extraer nuevas presentaciones de la API.
  2. Normalizar y añadir al almacenamiento local.
  3. Reconstruir el último conjunto de eventos de 30 días.
  4. Calcular puntuaciones a nivel de emisor.
  5. Exportar los nombres mejor clasificados a CSV, base de datos o motor de backtest posterior.

Eso es suficiente para crear un ciclo de investigación repetible.

Los errores evitables en el modelado de datos de insiders

Los datos de insiders tienen un talento especial para parecer más limpios de lo que son. Una presentación es un documento formal, por lo tanto, uno se siente tranquilo. Esa tranquilidad debe ser racionada.

Sesgo de anticipación a través de la fecha incorrecta

Este es el error clásico. Si clasifica valores utilizando transaction_date pero solo pudo haber observado la presentación en filing_date o la marca de tiempo de publicación, su backtest está haciendo trampa. Gran parte de las tonterías publicadas comienzan de esta manera.

La fecha correcta para la disponibilidad de la señal es la marca de tiempo más temprana en la que su sistema pudo haber visto la presentación. Dependiendo de su fuente, puede ser:

  • hora de publicación del regulador,
  • hora de ingesta del proveedor,
  • created_at o published_at de su propia API.

Si todo lo que tiene es una fecha de presentación sin hora intradiaria, asuma que la señal se vuelve negociable no antes de la siguiente sesión, a menos que su convención de mercado y el momento de la alimentación admitan algo más preciso.

Presentaciones duplicadas y modificadas

Una presentación puede ser corregida, reenviada o representada en múltiples filas. Si su API expone indicadores de enmienda o versiones, úselos. Si no, elimine duplicados en una clave conservadora como emisor, insider, fecha de transacción, acciones, precio y dirección, mientras mantiene los registros brutos archivados.

La clasificación de roles es complicada

Un campo de rol de "insider" que dice Officer en un caso y Chief Financial Officer en otro no es una taxonomía, es una sugerencia. Construya una capa de mapeo. Manténgala versionada. Espere revisarla.

Comparabilidad de moneda y valor

Si su universo abarca múltiples mercados, los valores nocionales en moneda local no son directamente comparables. Convierta a una moneda base utilizando una fuente de tipo de cambio consistente si su puntuación utiliza el valor. Si aún no tiene un mapeo de tipo de cambio fiable, clasifique primero dentro del mercado. Es mejor un modelo con un alcance modesto que uno global sostenido por una aritmética ilusoria.

Un ejemplo compacto de Python de principio a fin

§ 03 Un script de investigación mínimo

A continuación, se presenta un script compacto que une las piezas. Asume un endpoint /filings y un token de portador.

import pandas as pd
import numpy as np
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class InsiderAPI:
    def __init__(self, base_url: str, api_key: str, timeout: int = 30):
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Accept": "application/json",
            "User-Agent": "sigma-journal-python-tutorial/1.0"
        })
        retry = Retry(
            total=5,
            backoff_factor=0.5,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["GET"]
        )
        adapter = HTTPAdapter(max_retries=retry)
        self.session.mount("https://", adapter)
        self.timeout = timeout

    def get(self, path: str, params=None):
        r = self.session.get(f"{self.base_url}{path}", params=params or {}, timeout=self.timeout)
        r.raise_for_status()
        return r.json()

    def fetch_filings(self, start_date: str, end_date: str) -> pd.DataFrame:
        cursor = None
        rows = []

        while True:
            params = {
                "from_date": start_date,
                "to_date": end_date,
                "page_size": 500
            }
            if cursor:
                params["cursor"] = cursor

            payload = self.get("/filings", params=params)
            batch = payload.get("results", [])
            rows.extend(batch)

            cursor = payload.get("next_cursor")
            if not cursor:
                break

        return pd.DataFrame(rows)

def normalize_filings(df: pd.DataFrame) -> pd.DataFrame:
    out = pd.DataFrame({
        "filing_id": df.get("id"),
        "issuer_id": df.get("issuer_id"),
        "issuer_name": df.get("issuer_name"),
        "ticker": df.get("ticker"),
        "insider_id": df.get("insider_id"),
        "insider_name": df.get("insider_name"),
        "insider_role": df.get("insider_role"),
        "transaction_date": pd.to_datetime(df.get("transaction_date"), errors="coerce"),
        "filing_date": pd.to_datetime(df.get("filing_date"), errors="coerce"),
        "transaction_type": df.get("transaction_type"),
        "shares": pd.to_numeric(df.get("shares"), errors="coerce"),
        "price": pd.to_numeric(df.get("price"), errors="coerce"),
        "currency": df.get("currency"),
        "security_type": df.get("security_type"),
    })
    out["value"] = out["shares"] * out["price"]
    return out

def build_signal(df: pd.DataFrame, as_of_date: str) -> pd.DataFrame:
    as_of = pd.Timestamp(as_of_date)

    x = df.copy()
    x["transaction_type"] = x["transaction_type"].fillna("").str.lower()
    x["insider_role"] = x["insider_role"].fillna("")

    x = x[
        x["transaction_type"].isin(["buy", "purchase", "acquisition"]) &
        x["transaction_date"].between(as_of - pd.Timedelta(days=30), as_of)
    ]

    x["is_senior"] = x["insider_role"].str.lower().str.contains("ceo|chief|cfo|director|chair")
    x = x.dropna(subset=["issuer_id", "transaction_date"])

    signal = (
        x.groupby(["issuer_id", "issuer_name", "ticker"], dropna=False)
         .agg(
             total_buy_value=("value", "sum"),
             distinct_buyers=("insider_id", "nunique"),
             senior_buyer_flag=("is_senior", "max"),
             last_buy_date=("transaction_date", "max")
         )
         .reset_index()
    )

    signal["days_since_last_buy"] = (as_of - signal["last_buy_date"]).dt.days
    signal["score"] = (
        np.log1p(signal["total_buy_value"].fillna(0))
        + 0.75 * signal["distinct_buyers"].fillna(0)
        + 1.0 * signal["senior_buyer_flag"].astype(int)
        - 0.05 * signal["days_since_last_buy"].fillna(30)
    )

    return signal.sort_values("score", ascending=False)

if __name__ == "__main__":
    api = InsiderAPI(base_url="https://api.example.com", api_key="YOUR_API_KEY")
    raw = api.fetch_filings(start_date="2026-01-01", end_date="2026-03-31")
    filings = normalize_filings(raw)
    signal = build_signal(filings, as_of_date="2026-03-31")
    signal.to_csv("daily_insider_signal.csv", index=False)
    print(signal.head(20))

Esto es suficiente para producir un archivo clasificado. También es suficiente para revelar dónde su esquema difiere de sus suposiciones, que es el verdadero valor educativo de cualquier primer script.

Convirtiendo el script en un activo de investigación

Las próximas actualizaciones son predecibles:

  • guardar las respuestas crudas de la API en disco o almacenamiento de objetos,
  • persistir tablas normalizadas en DuckDB, Postgres o Parquet,
  • añadir pruebas unitarias para el análisis de fechas y el mapeo de roles,
  • parametrizar la ventana de retrospectiva y el tamaño mínimo de transacción,
  • añadir una capa de backtest con benchmark.

En ese punto, habrá pasado de un tutorial a una herramienta.

Qué probar antes de confiar en la señal

Una puntuación simple está bien. Una puntuación no probada es decoración.

§ 04 Validación y próximos pasos de investigación

Controles de coherencia

Antes de cualquier backtest, ejecute estas comprobaciones:

Desviación de cobertura

¿Los recuentos de presentaciones son estables a lo largo del tiempo, o su extracción omitió una semana?

Tasas de nulos

¿Qué proporción de registros tiene ticker, price, shares o transaction_date faltantes?

Tasas de duplicados

¿Cuántos ID de presentación o claves de evento duplicados existen?

Retraso en la presentación

¿Cuál es la distribución de filing_date - transaction_date?

Si alguno de estos produce sorpresas, posponga sus ambiciones alfa y arregle los datos.

Un primer diseño de backtest

Incluso un estudio de eventos simple es informativo:

  • definir la formación de la señal en la fecha de disponibilidad de la presentación,
  • clasificar a los emisores cada día o semana,
  • formar carteras del decil superior o N superior,
  • mantener durante 20, 60 o 120 días de negociación,
  • comparar con un índice de referencia de igual ponderación o un índice de mercado.

Dado que el resumen del artículo no incluía datos de rendimiento ni una extracción de backtest en vivo, no fabricaré ratios de Sharpe para su entretenimiento. La cifra correcta aquí es n/a.

Refinamientos útiles después del primer prototipo

Una vez que la señal de compra básica está en funcionamiento, las siguientes extensiones sensatas son:

Ponderación de roles

Diferenciar entre CEO, CFO, presidente y director no ejecutivo.

Compra en clúster

Recompensar a múltiples insiders que compran el mismo emisor dentro de un corto período de tiempo.

Tamaño relativo

Escalar el valor de la transacción por el proxy de compensación del insider, la capitalización de mercado o el volumen diario promedio, si está disponible.

Reglas específicas del mercado

Las diferentes jurisdicciones codifican los roles de los insiders, los plazos y las categorías de transacciones de manera diferente. La armonización es un trabajo, no una nota al pie.

Filtros de liquidez

Una señal brillante en nombres que no se pueden negociar es un pasatiempo.

MarketRegulatorRuleDeadlineNotes
FRAMFMAR Art 19T+3Las personas que ejercen responsabilidades de gestión y las personas estrechamente relacionadas deben notificar las transacciones con prontitud, a más tardar en un plazo de tres días hábiles.
EUESMA y autoridades nacionales competentesMAR Art 19T+3El marco principal está armonizado bajo MAR, pero la difusión y el formato difieren según el lugar y la autoridad.
USSECExchange Act Sección 16, Formulario 4T+2La mayoría de las transacciones de información privilegiada reportables por parte de funcionarios, directores y propietarios del 10 por ciento deben presentarse en un plazo de dos días hábiles.
Los plazos de presentación de informes determinan la disponibilidad de la señal. La norma legal no es lo mismo que la marca de tiempo de su feed, pero es donde comienza el reloj.

Por qué esto es importante específicamente para los usuarios de API

Si está consumiendo una API REST en lugar de extraer datos directamente de los sitios web de los reguladores, la calidad de su investigación depende de dos capas de puntualidad:

  1. el régimen de presentación regulatoria,
  2. el pipeline de ingesta del proveedor o plataforma.

Esto significa que su modelo debe registrar tanto la fecha del evento regulatorio como la marca de tiempo de disponibilidad observada por la API, siempre que sea posible. Si su endpoint expone created_at, updated_at o published_at, consérvelos todos. El almacenamiento es barato. La falsa precisión en los backtests es costosa.

El beneficio práctico

Un buen tutorial de API debe dejarle con algo operativo, no meramente inspirado. Al final de este flujo de trabajo, su entregable objetivo es simple: un script o notebook que pueda autenticarse, obtener presentaciones de forma incremental, normalizarlas en una tabla estable, calcular una puntuación de convicción de compra transparente y exportar un archivo de señal diario.

Ese no es el modelo final. Es el primero honesto.

El siguiente paso concreto es conectar esta señal a un pequeño backtest de estudio de eventos utilizando las marcas de tiempo de disponibilidad de la presentación en lugar de las fechas de transacción, y luego probar si la compra en grupo por parte de insiders senior agrega poder predictivo incremental después de los filtros básicos de liquidez. Si lo hace, tienes un programa de investigación. Si no, al menos tu paginación seguirá siendo correcta, lo cual es más de lo que se puede decir de muchos esfuerzos más grandes.

Fuentes y lecturas adicionales

  1. SEC, Formularios 3, 4 y 5regulator
  2. SEC, Transacciones de iniciados y Formularios 3, 4 y 5regulator
  3. ESMA, Reglamento sobre el abuso de mercadoregulator
  4. AMF, Transacciones de directivos y recursos del reglamento sobre el abuso de mercadoregulator
  5. Comisión Europea, Reglamento sobre el abuso de mercado (UE) n.º 596/2014regulator2014-04-16
  6. Seyhun, H. Nejat, Inteligencia de inversión a partir del uso de información privilegiadabook1998-01-01
  7. Jeng, Leslie A., Metrick, Andrew y Zeckhauser, Richard, Estimación de los rendimientos del uso de información privilegiada: una perspectiva de evaluación del rendimientopaper2003-04-01

Nota editorial: este artículo se elaboró sin investigación web en vivo (Grok no disponible en esta pasada de generación). Las fuentes perennes se citan arriba; las afirmaciones numéricas se extraen de nuestra propia instantánea de la base de datos a partir del 17-05-2026.

Última revisión · 18-05-2026 · Por Simon Azoulay · Fuentes, SEC EDGAR, AMF BDIF y otros 28 reguladores.

Series

Working with insider data

From raw filings to a workflow: API, MCP, Excel, alerts, and how to read backtest claims honestly.

Pasar a la acción

SeñalesRecomendaciones de hoyFiltroScreener de señales de insidersUniversoTodas las empresas cubiertas

Lecturas relacionadas