HedgeFriend
← All posts
Research6 min readHedgeFriendData as of

Congress trades. We time-stamped the disclosures.

Median disclosure lag for House stock trades is 15 days, the 90th percentile is 35, and 7.9% arrive after the 45-day deadline. Key your backtest on the disclosure date.

A congressional stock trade has two dates: the day it happened and the day the public could first see it. Most congress-trading backtests key on the first. That is a look-ahead bug, and it is not a small one. The gap between the two dates is a distribution of its own, with a fat tail, and it moves from year to year.

We measured it on every trade in our `congressional_trades` table as of 2026-10-22: 20,262 rows with transaction dates from 2015-05-08 to 2026-10-16, across 1,505 distinct symbols. Of the 20,241 rows that have a usable disclosure date, the median trade is disclosed 15 days after it happens, the 90th percentile is 35 days, and 7.9% arrive after the STOCK Act's 45-day deadline.

The universe is the House

Every one of those rows has `chamber = 'house'`. The data is built from House periodic transaction reports, and the table holds no Senate trades. Nothing in this post says anything about Senate disclosure behaviour, and you should not extend it there.

Two other exclusions. Eleven rows have no disclosure date at all, and ten have a disclosure date earlier than the transaction date (by 1 to 73 days). Neither group can be given a latency, so both are left out of every percentile below and reported here instead. That leaves the 20,241 rows used throughout.

The distribution

Latency here is `disclosure_date - transaction_date` in calendar days. The bucketed distribution:

  • 35.7% — 0 to 7 days (7,230 trades).
  • 13.1% — 8 to 14 days.
  • 35.3% — 15 to 30 days (7,139 trades).
  • 8.1% — 31 to 45 days.
  • 2.7% — 46 to 90 days.
  • 1.2% — 91 to 180 days.
  • 4.0% — more than 180 days (801 trades).

The median is 15 days and the 90th percentile is 35. The 99th percentile is 346 days, and the longest gap in the table is 3660 days, roughly ten years. The mean is a poor summary here for the same reason it always is: a tail of trades filed months or years late.

In total 1,591 trades were disclosed more than 45 days after the transaction, 7.9% of the table. The body of the distribution is close to what the statute describes: a member has 30 days from notice of a trade and 45 days from the trade itself. The 35.3% of trades in the 15 to 30 day bucket reflect people filing near the first of those limits. The interesting part is what is outside it.

Has it improved?

Not in any way you could draw a line through. By transaction year:

  • 2019: median 12 days, 3.6% over 45 days (478 trades).
  • 2020: median 13, 2.8% over 45 (1,589 trades).
  • 2021: median 9, 5.0% (1,482 trades).
  • 2022: median 13, 1.2% (2,210 trades).
  • 2023: median 23, 7.1% (3,472 trades).
  • 2024: median 20, 21.8% (2,071 trades). The 90th percentile is 309 days.
  • 2025: median 14, 11.0% (6,599 trades), 90th percentile 50 days.

Years before 2019 have too few trades to read (the 2015 value is two rows). The 2024 tail is heavy, and the next section shows how concentrated late filing is among members.

The 2026 row says 0.0% over 45 days, and that is censoring, not good behaviour. A trade that has not yet been disclosed is not in the table, so the latest months can only contain the fast filers. Drop the most recent 45 days, and in practice the whole current year, before drawing any trend.

Who is late

Late filing is not spread evenly across the House. Of 201 members with at least one usable trade, 59 have at least one disclosure past 45 days. Ten members account for 1,349 of the 1,591 late trades, which is 84.8%. The ten members with the most late trades have 3,681 trades between them, 36.6% of which were late. The other 191 members have 16,560 trades, 1.5% of which were late.

The per-member counts for those ten are 510, 211, 137, 123, 86, 74, 68, 66, 38 and 36. We are deliberately not naming them here; the member field is in the feed if you want to look. The point for a modeller is that lateness is a persistent trait of a member, not noise. A rule such as "exclude or flag members whose past disclosures have mostly been late" removes most of the tail at a small cost in trade count.

Latency by trade type and size

Purchases are disclosed more slowly than sales:

  • Sales: 10,675 trades, median 14 days, 90th percentile 34, 4.6% over 45 days.
  • Purchases: 9,473 trades, median 16 days, 90th percentile 57, 11.5% over 45 days.
  • Exchanges: 93 trades, median 12 days.

The purchase tail is the one that matters for a long-biased signal: the trades you most want to follow are the ones most likely to be late.

Size shows an odd pattern. Trades with `amount_low` under $15,000 make up 16,109 of the 20,241 rows and have a median of 17 days and 9.3% over 45. At $250,000 and up (234 trades) the median is 1 day and only 0.9% are late. Large trades are filed almost immediately; small ones sit. We have not tried to explain why. Whatever the cause, it means a size filter changes the latency profile of your sample, so a backtest restricted to large trades will understate how stale the typical small trade is.

What this does to a backtest

Suppose you buy on `transaction_date` whenever a member buys. For every trade, the information you used was not public until `disclosure_date`. Over 2025 and 2026 to date (8,823 trades) that look-ahead averaged 20.7 days. For 67.6% of trades it was at least a week, and for 49.9% it was at least 15 days.

Any return you attribute to those 15 days was available only to the member and whoever else knew. The rule is simple: the tradable date is the disclosure date, and the earliest realistic entry is the next session after it. Index everything on `disclosure_date`, and treat `transaction_date` as a feature (how stale is this information?), never as a clock.

One more reason for caution: disclosures arrive in batches. The 20,241 usable trades land on only 2,888 distinct member and disclosure-date pairs, so a lot of trades become visible at once, on the filing day, rather than as a trickle.

Querying it

Every row carries both dates, so the latency is one subtraction on your side. List recent trades, filtered to a ticker and sorted newest transaction first:

curl
bash
curl -H "Authorization: Bearer $HEDGEFRIEND_KEY" \
  "https://api.hedgefriend.dev/v1/alt/congress/trades?chamber=house&transaction_type=purchase&from=2025-01-01&limit=200"

# One symbol, same row shape.
curl -H "Authorization: Bearer $HEDGEFRIEND_KEY" \
  "https://api.hedgefriend.dev/v1/alt/congress/trades/TICKER"

# Per-member rollup of buys and sells over a lookback window.
curl -H "Authorization: Bearer $HEDGEFRIEND_KEY" \
  "https://api.hedgefriend.dev/v1/alt/congress/trades/TICKER/summary?days=180"

Computing latency and the point-in-time entry date from the paginated feed:

congress_latency.py
python
import os, requests
from datetime import date

HEADERS = {"Authorization": f"Bearer {os.environ['HEDGEFRIEND_KEY']}"}
URL = "https://api.hedgefriend.dev/v1/alt/congress/trades"

rows, cursor = [], None
while True:
    r = requests.get(
        URL,
        params={"from": "2025-01-01", "limit": 200, "cursor": cursor},
        headers=HEADERS,
        timeout=30,
    )
    r.raise_for_status()
    page = r.json()
    rows += page["data"]
    cursor = page["meta"]["next"]
    if not cursor:
        break

for x in rows:
    # Null or negative gaps cannot be dated; skip them rather than guess.
    if not x["disclosure_date"]:
        continue
    lag = (date.fromisoformat(x["disclosure_date"])
           - date.fromisoformat(x["transaction_date"])).days
    if lag < 0:
        continue
    x["latency_days"] = lag
    x["tradable_from"] = x["disclosure_date"]  # not transaction_date

The summary endpoint aggregates by `transaction_date`. If you use it for a signal, remember the window it reports includes trades that were not yet public on the dates they are counted against.

Caveats

  • House only. No Senate data, and no claim about it.
  • Calendar days, not business days, and the disclosure date is the filing date as recorded, not the time of day the document was posted.
  • Censoring: recent months are biased toward fast filers (see 2026 above). Trades not yet disclosed are invisible by construction.
  • Latency uses the disclosure date recorded on each row. If a row reflects an amended or later-filed report, the gap is to that filing, not to an earlier one.
  • We do not serve prices, so this post says nothing about returns, only about how many days the information was not yet public.
  • The amount-band and purchase-versus-sale gaps are descriptive. We have not controlled for member, and the concentration section suggests a handful of members drive a large part of both.

The reproducing SQL is in the repository under `research/blog/congress-trade-disclosure-latency/queries.sql`, alongside the raw results.

Try it on your own data

The free tier covers 500 requests a day — enough to reproduce anything in this post.

Get a free key