tradevodata
Point-in-time US equity fundamentals from SEC EDGAR. Every value carries the date it actually became public, so a backtest can only see what was knowable at the time.
See the bug before installing anything
The no-signup Colab experiment joins the same fundamentals two ways. The ordinary period-end join uses a value that was not public yet in 47 of 413 ticker-months (11%). The point-in-time join removes those future values. Run the 3-minute proof in Colab.
pip install tradevodata
The free sample needs no key
import tradevodata as tv
rows = tv.sample() # 40 large caps, ~3,280 rows, CC0, no signup
Returns a pandas DataFrame if pandas is installed, otherwise a plain list of dicts — so
the line above works on a bare install. Force either shape with to_pandas=True/False.
Same columns and the same semantics as the paid dataset, so you can write and verify your join logic before paying for anything.
as_of is required, on purpose
import tradevodata as tv
client = tv.Client(api_key="tvd_...") # or set TRADEVODATA_API_KEY
client.fundamentals("AAPL", as_of="2024-06-30") # annual (default)
client.fundamentals("AAPL", as_of="2024-06-30", period="quarterly")
There is no way to ask this library "what is Apple's revenue?" — only "what was Apple's
revenue knowable on this date?". On 2024-06-30 the answer is FY2023, because the FY2024 10-K
had not been filed yet. That distinction is the entire product, and a default value for
as_of is how lookahead bias gets written by accident.
Whole-universe cross-section
One call per rebalance date, instead of looping tickers:
snap = client.snapshot(
as_of="2024-06-30", concept="Revenue", period="quarterly", to_pandas=True
)
Bulk
client.download("tradevodata_annual.csv.gz")
client.download("tradevodata_quarterly.csv.gz", period="quarterly")
Doing the point-in-time join yourself
If you're working from sample() or a bulk download, the as-of join is yours to do —
as_of_filter does it correctly:
rows = tv.sample()
knowable = tv.as_of_filter(rows, as_of="2020-03-31")
Keeps only rows whose first_filed <= as_of, then the newest fiscal period per
ticker/concept. That is the same logic the API applies server-side.
What each row tells you
| column | meaning |
|---|---|
first_filed |
the date the value became public — the point-in-time stamp |
original_value |
what was first reported. Use this for backtests |
latest_value |
the current revision. May post-date your as_of — not PIT-safe |
restated |
a later filing revised this by more than 0.5% |
lag_days |
days from period end to first publication |
qa_status |
clean, or FLAG: + reasons. We flag; we never silently drop |
fiscal_period |
FY for annual rows; Q1–Q4 for quarterly rows |
period_start |
start of a quarterly duration; null for instant concepts |
ytd_value |
source year-to-date value when a discrete quarter is derived |
derivation |
reported, ytd_diff, or fy_minus_9m |
The client raises a UserWarning when rows come back flagged or when your as_of runs past
our data cutoff. Silence it with Client(warn_on_flags=False) if you're handling qa_status
yourself.
Honest limits
- Annual data comes from 10-K/10-K/A filings. Quarterly data is requested explicitly with
period="quarterly"and includes accepted 10-Q/10-Q/A facts plus supported Q4 derivations. - Quarterly depth varies by filer and tag availability; do not assume every company has 48 quarters.
- US only, and no delisted companies — so mind survivorship bias if you build universes from this alone. We fix lookahead bias; that is a different problem.
- 16 annual concepts and 7 quarterly concepts, with up to 12 fiscal years of history where SEC XBRL coverage supports it.
- Filing lag averages 66 days across the universe (median 60, max 120). The 40-company sample averages 43 — large caps file fastest, so the sample is better than the whole.
If you need delisted coverage or a survivorship-bias-free historical universe, this dataset does not provide it; use a provider that explicitly includes inactive securities.
Zero dependencies
Standard library only. pandas is opt-in:
pip install "tradevodata[pandas]"
See the bug for yourself
The Colab notebook runs the experiment on the free sample — no key, no signup, no install. It joins fundamentals both ways at every month-end and counts the disagreements. On the 40-company sample: 47 of 413 ticker-months (11%) use a revenue number that was not yet public.
Audit another provider
If you already have a fundamentals CSV, the local audit checks whether it exposes a real availability date and whether revised values appear where the free sample recorded a different first-reported value:
python examples/audit_provider_csv.py provider.csv \
--ticker-col ticker --concept-col concept --period-end-col period_end \
--value-col value --available-col filed
Nothing is uploaded. The script runs locally and explains every check it can and cannot verify.
Build a point-in-time factor snapshot
pip install "tradevodata[pandas]"
python examples/point_in_time_factor.py --as-of 2024-06-30
This produces margins and return-on-capital measures using only annual values that had been filed by the requested date.
Links
- Product — https://tradevodata.com/?utm_source=github&utm_medium=repo&utm_campaign=python-client
- Docs — https://tradevodata.com/docs?utm_source=github&utm_medium=repo&utm_campaign=python-client
- Free CC0 sample — https://github.com/christianpichichero-max/pit-fundamentals
- Methodology — how each number is derived from raw filings, so you can check any row
MIT licensed. Data sourced from SEC EDGAR (public domain). This is a dataset, not investment advice.
Metadata
Release files for tradevodata 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tradevodata-0.2.0.tar.gz | 23.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tradevodata-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.6 kB
Release files / tradevodata-0.2.0.tar.gz
| Download URL | tradevodata-0.2.0.tar.gz |
|---|---|
| Size | 23.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
51770dc6e6bce9b302b142541cb1062254b5ae42c9eb25c81a76525c49c911af
|
|
BLAKE2b-256 checksum How to use checksums |
788a1850352f7499d0ef6fd824908728142d50fdb5dec653b3382a9cb56d4592
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / tradevodata-0.2.0-py3-none-any.whl
| Download URL | tradevodata-0.2.0-py3-none-any.whl |
|---|---|
| Size | 11.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
53007ce72700625b0829db246485bbf06616a5199e6bacdad5b5e17834a17812
|
|
BLAKE2b-256 checksum How to use checksums |
ac80106b2de123dc6a29f6575d6982773fa92b4c81d5b54be943fe08a4887647
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency log