<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Pichichero</title>
    <description>The latest articles on DEV Community by Christian Pichichero (@tradevodata).</description>
    <link>https://dev.to/tradevodata</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4017923%2F2d918596-6eda-4a1a-92be-53c55ae5ce79.png</url>
      <title>DEV Community: Christian Pichichero</title>
      <link>https://dev.to/tradevodata</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tradevodata"/>
    <language>en</language>
    <item>
      <title>Survivorship Bias vs Lookahead Bias: The Two Silent Backtest Killers</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Mon, 06 Jul 2026 14:46:57 +0000</pubDate>
      <link>https://dev.to/tradevodata/survivorship-bias-vs-lookahead-bias-the-two-silent-backtest-killers-pmm</link>
      <guid>https://dev.to/tradevodata/survivorship-bias-vs-lookahead-bias-the-two-silent-backtest-killers-pmm</guid>
      <description>&lt;p&gt;Most blown-up backtests aren't wrecked by a bad strategy idea. They're wrecked by two data problems that quietly inflate returns: &lt;strong&gt;survivorship bias&lt;/strong&gt; and &lt;strong&gt;lookahead bias&lt;/strong&gt;. They're often mentioned in the same breath, which is a mistake — they come from different places, they fail differently, and one of them is much easier to catch than the other.&lt;/p&gt;

&lt;p&gt;This article defines both, explains why lookahead bias is the sneakier of the two, walks through how ordinary fundamentals data causes each one, and lays out what actually fixes them (spoiler: it's not one thing).&lt;/p&gt;

&lt;h2&gt;
  
  
  Survivorship bias, in one sentence
&lt;/h2&gt;

&lt;p&gt;Your backtest only includes companies that still exist today, so every failure, delisting, and bankruptcy is invisible — and your historical universe silently gets better with hindsight than it actually was at the time.&lt;/p&gt;

&lt;p&gt;It's the classic example: you backtest a value strategy on "all S&amp;amp;P 500 companies" using today's constituent list, applied to 2005 data. Every company that got delisted, acquired, or dropped from the index between 2005 and today never shows up in your test. Your strategy looks great because it only ever traded winners-in-hindsight.&lt;/p&gt;

&lt;p&gt;Survivorship bias is loud once you know to look for it. If your universe is built from a current list of tickers and applied to past dates, you have it. The fix is mechanical: use a point-in-time universe (constituents as they were, on each date, including the companies that later failed).&lt;/p&gt;

&lt;h2&gt;
  
  
  Lookahead bias, in one sentence
&lt;/h2&gt;

&lt;p&gt;Your backtest uses a data value on a date before that value was actually knowable, so the strategy is trading on information from the future.&lt;/p&gt;

&lt;p&gt;This is the sneaky one. It rarely announces itself. It hides inside something completely mundane: a merge, a join, a &lt;code&gt;groupby().last()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why lookahead bias is sneakier
&lt;/h2&gt;

&lt;p&gt;Survivorship bias is a universe problem — you either have delisted names in your data or you don't, and that's checkable with a row count. Lookahead bias is a &lt;strong&gt;timing&lt;/strong&gt; problem buried inside data that otherwise looks perfectly normal.&lt;/p&gt;

&lt;p&gt;Here's the mechanism most people never notice: financial databases typically store one row per fiscal period, keyed by &lt;em&gt;fiscal period end date&lt;/em&gt; (e.g., "FY2022, ended 2022-12-31") — not by the date the number was actually disclosed. A 10-K for fiscal year 2022 isn't filed on December 31, 2022. It's filed weeks or months later, in early-to-mid 2023. If your backtest joins fundamentals to prices using the fiscal period end date as the effective date, you're handing your strategy Q4 2022 revenue on December 31, 2022 — months before any investor could have known it.&lt;/p&gt;

&lt;p&gt;The merge runs without errors. The output looks like a normal dataframe. Nothing crashes. Numbers just quietly get used before they were public, because the strategy is trading on data from the future relative to the date you think it's trading on. That's why lookahead bias survives code review, survives peer review, and often survives into live trading before it's caught — because catching it requires knowing to ask "on what date was this number actually public?", not just "is this number correct?"&lt;/p&gt;

&lt;p&gt;Restatements make it worse. Companies revise prior-period numbers (that's the &lt;code&gt;restated&lt;/code&gt; flag you'll see in decent PIT datasets, and amendments — 10-K/As — are a normal part of that). If your data only stores the latest, current value, you're not just early, you're not even testing the number the market originally saw.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ordinary fundamentals data causes both, side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Survivorship bias&lt;/th&gt;
&lt;th&gt;Lookahead bias&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Root cause&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Universe built from current tickers, not historical ones&lt;/td&gt;
&lt;td&gt;Data keyed by fiscal period end, not filing/disclosure date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What's missing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Delisted, acquired, bankrupt companies&lt;/td&gt;
&lt;td&gt;The gap between period end and public filing date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Where it hides&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The universe list itself&lt;/td&gt;
&lt;td&gt;A join/merge on &lt;code&gt;date&lt;/code&gt;, invisible in the code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;How obvious is it&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fairly obvious once you check for delistings&lt;/td&gt;
&lt;td&gt;Not obvious — no error, no crash, silently optimistic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical inflation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Overstates returns by excluding losers&lt;/td&gt;
&lt;td&gt;Overstates returns by using future information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Point-in-time universe / constituent history&lt;/td&gt;
&lt;td&gt;Point-in-time fundamentals, filtered by first-known date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Also affected by restatements?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not directly&lt;/td&gt;
&lt;td&gt;Yes — original vs. latest values matter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The actual fix: PIT data + a stable universe
&lt;/h2&gt;

&lt;p&gt;There isn't one fix for both, because they're two different bugs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;For survivorship bias&lt;/strong&gt;: build or buy a universe that includes delisted/failed companies as of each historical date, not a snapshot of today's list applied backward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For lookahead bias&lt;/strong&gt;: use fundamentals filtered by the date a value first became public (&lt;code&gt;first_filed &amp;lt;= as_of&lt;/code&gt;), not the fiscal period end date. And decide deliberately whether you're testing against the &lt;code&gt;original_value&lt;/code&gt; (what the market actually saw first) or the &lt;code&gt;latest_value&lt;/code&gt; (post-restatement), because those are two different questions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do both, and the numbers your backtest trades on are the numbers an investor could actually have had, on the date the strategy would have acted. We go deeper on the mechanics of the merge bug specifically in &lt;a href="https://tradevodata.com/blog/lookahead-bias-fundamental-backtests" rel="noopener noreferrer"&gt;Lookahead Bias in Fundamental Backtests&lt;/a&gt;, and on what point-in-time data actually looks like row-by-row in &lt;a href="https://tradevodata.com/blog/point-in-time-fundamentals-data" rel="noopener noreferrer"&gt;Point-in-Time Fundamentals, Explained&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On our own 40-company free sample, the measured gap between fiscal period end and first public filing averaged 43.4 days and reached up to 61 days on the reliable-filing rows — scoped to that sample only, not a claim about the broader dataset. That gap is exactly the window lookahead bias exploits.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the other option is the better choice
&lt;/h2&gt;

&lt;p&gt;Being honest about scope matters more than winning the comparison. A few cases where you should look elsewhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You need quarterly fundamentals.&lt;/strong&gt; Tradevo Data is annual-only (10-K/10-K/A) right now; quarterly is on the roadmap, not shipped. If your strategy needs 10-Q cadence today, that's a real gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need international coverage.&lt;/strong&gt; This is US-only, sourced from SEC EDGAR. Non-US equities need a different provider entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need bulk Parquet files or very high request volume.&lt;/strong&gt; The API is JSON, rate-limited to 5,000 requests/day. If you're doing large-scale offline research across the full universe repeatedly, a bulk-file provider fits better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need decades of history or more concepts.&lt;/strong&gt; This dataset covers up to 12 fiscal years and 7 core concepts. Established research-grade vendors like Sharadar, Tiingo, or QuantConnect may offer more concepts, longer history, or broader asset-class coverage depending on the plan — see their pricing pages directly, since we won't quote competitor prices here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You'd rather build it yourself.&lt;/strong&gt; SEC EDGAR is public domain. If you have the engineering time, you can pull filings, extract first-filed dates, and track restatements yourself. It's genuinely doable — it's also exactly the plumbing work this dataset already did, which is the tradeoff to weigh against $49/month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If none of those apply and you mainly need clean, point-in-time US equity fundamentals without the merge-bug risk, the free sample is the lowest-friction way to see the actual row structure: &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;github.com/christianpichichero-max/pit-fundamentals&lt;/a&gt; — 40 companies, 3,212 rows, full methodology, no signup. When you're ready for the full 5,214-company dataset, the API is at &lt;a href="https://tradevodata.com/?ref=blog" rel="noopener noreferrer"&gt;tradevodata.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Not investment advice; verify competitor pricing yourself.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>finance</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Point-in-Time Fundamentals Data: What It Is, Why It Matters, and How to Choose</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Mon, 06 Jul 2026 14:46:27 +0000</pubDate>
      <link>https://dev.to/tradevodata/point-in-time-fundamentals-data-what-it-is-why-it-matters-and-how-to-choose-a70</link>
      <guid>https://dev.to/tradevodata/point-in-time-fundamentals-data-what-it-is-why-it-matters-and-how-to-choose-a70</guid>
      <description>&lt;p&gt;If you backtest strategies on company fundamentals — earnings, revenue, book value, cash flow — the single most expensive mistake you can make is testing on numbers that &lt;em&gt;weren't actually knowable&lt;/em&gt; on the date your model traded. That mistake has a name: lookahead bias. And the fix has a name too: &lt;strong&gt;point-in-time (PIT) fundamentals data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This guide explains what PIT data actually is, why it matters, what separates a real PIT dataset from a marketing label, and an honest survey of your options — from free DIY tooling to five-figure institutional feeds. We build one of those options, and we'll say so plainly at the end, but the goal here is to help you choose the right tool, not to talk you into ours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "point-in-time" actually means
&lt;/h2&gt;

&lt;p&gt;A normal fundamentals table gives you one number per company per period — say, Apple's FY2019 revenue. The problem is &lt;em&gt;which&lt;/em&gt; version of that number you're looking at. Companies file a 10-K, then sometimes amend it (10-K/A), then restate prior years inside &lt;em&gt;later&lt;/em&gt; filings. A vanilla database silently overwrites the original with the newest revision. So when your 2020 backtest reads "2019 revenue," it may be reading a value that wasn't published until 2022.&lt;/p&gt;

&lt;p&gt;A point-in-time dataset instead answers a different question: &lt;strong&gt;"What was the value of this metric, as it was known on date X?"&lt;/strong&gt; To do that honestly, each data point has to carry two things a plain table throws away:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A filing date&lt;/strong&gt; — the date the value first became public (the 10-K's filing timestamp, not the fiscal-period-end date).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The original, first-reported value&lt;/strong&gt; — preserved separately from any later revision.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With those, a query engine can filter to &lt;em&gt;only the rows whose filing date is on or before your as-of date&lt;/em&gt; and hand back the number the market actually had. That's the whole game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters: lookahead bias and restatements
&lt;/h2&gt;

&lt;p&gt;Two distinct failure modes make non-PIT fundamentals dangerous for backtesting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reporting lag.&lt;/strong&gt; A fiscal year ends December 31, but the 10-K might not be filed until late February or March. If your backtest assumes the annual numbers were available on January 1, you've handed your model roughly two months of the future. Multiply that across every rebalance and the phantom edge can be large.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restatements.&lt;/strong&gt; When a company restates, the revised figure often "improves" the historical picture in ways the market didn't know at the time. A model trained on restated data learns to trade on information that didn't exist yet. Restatements are common enough that ignoring them meaningfully distorts value and quality factors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We go deep on the mechanics, worked examples, and how to audit your own pipeline in &lt;a href="https://dev.to/blog/lookahead-bias-fundamental-backtests"&gt;our companion article on lookahead bias in fundamental backtests&lt;/a&gt;. If you only read one follow-up, read that one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to look for in a PIT dataset
&lt;/h2&gt;

&lt;p&gt;Not every dataset that says "point-in-time" earns the label. Here's a practical checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A real filing-date stamp per row&lt;/strong&gt; — the date the value became public, not just the period-end date. Without this, "as-of" queries are impossible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Original value preserved separately from the latest value&lt;/strong&gt; — so you can see both what was first reported and what it became, and a &lt;strong&gt;restated flag&lt;/strong&gt; so you can filter or study revisions instead of being silently fed them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A server-side as-of query&lt;/strong&gt; — ideally you pass an &lt;code&gt;as_of&lt;/code&gt; date and the API does the filing-date filtering for you, rather than you reconstructing it and hoping you got the boundary conditions right.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Survivorship handling&lt;/strong&gt; — for cross-sectional backtests you also need delisted and dead companies present in the universe, or your results are biased toward winners. (Note: fundamentals coverage and a survivorship-free &lt;em&gt;price/universe&lt;/em&gt; file are separate concerns — check both.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent QA&lt;/strong&gt; — you want to know when a value is flagged or uncertain, not have it quietly dropped.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  An honest survey of your options
&lt;/h2&gt;

&lt;p&gt;There's no single right answer here — the right tool depends on your budget, coverage needs, and how much plumbing you want to own. Pricing below is directional and drifts constantly, so treat every number as "verify on their page," linked.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Rough cost (as of July 2026)&lt;/th&gt;
&lt;th&gt;PIT capability&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DIY from SEC EDGAR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (your time + infra)&lt;/td&gt;
&lt;td&gt;Full, if you build it&lt;/td&gt;
&lt;td&gt;Teams who want total control and have engineering time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Budget PIT APIs&lt;/strong&gt; (incl. us)&lt;/td&gt;
&lt;td&gt;~$0–$50/mo tier&lt;/td&gt;
&lt;td&gt;Varies — check for filing dates + original values&lt;/td&gt;
&lt;td&gt;Individuals and small shops proving an idea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://sharadar.com/" rel="noopener noreferrer"&gt;Sharadar&lt;/a&gt; SF1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subscription — see &lt;a href="https://data.nasdaq.com/databases/SF1" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Point-in-time (datekey-based)&lt;/td&gt;
&lt;td&gt;Retail quants wanting broad coverage + ratios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.tiingo.com/about/pricing" rel="noopener noreferrer"&gt;Tiingo&lt;/a&gt; fundamentals&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add-on — &lt;a href="https://www.tiingo.com/about/pricing" rel="noopener noreferrer"&gt;contact/pricing&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;As-reported dimension available; confirm filing-date detail&lt;/td&gt;
&lt;td&gt;Users already on Tiingo for prices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.spglobal.com/market-intelligence/en/solutions/products/fundamental-data" rel="noopener noreferrer"&gt;Compustat&lt;/a&gt; / &lt;a href="https://www.lseg.com/en/data-analytics/financial-data/company-data/fundamentals-data/standardized-fundamentals/sp-compustat-database" rel="noopener noreferrer"&gt;LSEG&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Institutional (commonly $10k–$50k+/yr)&lt;/td&gt;
&lt;td&gt;Deep PIT snapshots (Compustat PIT from 1987)&lt;/td&gt;
&lt;td&gt;Funds, academics via WRDS, anyone needing decades of history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;DIY from EDGAR&lt;/strong&gt; is genuinely free — all US filings are public domain. You can parse XBRL, stamp filing dates yourself, and own the whole stack. The catch is that "parse XBRL correctly, dedupe amendments, and handle restatements" is weeks-to-months of unglamorous work, and quiet bugs there reintroduce the exact lookahead you were trying to remove.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharadar and Tiingo&lt;/strong&gt; are credible, established budget-to-mid options. Sharadar's fundamentals are point-in-time via its datekey structure and cover a broad US universe with many pre-computed ratios; Tiingo offers an as-reported dimension and pairs naturally with its price data. Confirm the specific PIT semantics you need against their current docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compustat and LSEG&lt;/strong&gt; are the institutional standard for a reason: decades of carefully curated PIT snapshots, global coverage, and support. They also cost accordingly and are usually accessed through an institution or WRDS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where we fit
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://tradevodata.com/?ref=blog" rel="noopener noreferrer"&gt;Tradevo Data&lt;/a&gt; is a deliberately narrow, honest &lt;strong&gt;budget tier&lt;/strong&gt; of research-grade PIT — one middle option among the credible ones above, not "the only cheap PIT." Concretely, today it is: point-in-time US &lt;strong&gt;annual&lt;/strong&gt; equity fundamentals sourced from SEC EDGAR (public domain), covering 5,214 companies and 313,562 PIT rows across 7 core concepts (Revenue, NetIncome, Assets, StockholdersEquity, OperatingCashFlow, EPSDiluted, DilutedShares), up to 12 fiscal years.&lt;/p&gt;

&lt;p&gt;Every row carries &lt;code&gt;first_filed&lt;/code&gt; (when it became public), &lt;code&gt;original_value&lt;/code&gt; (first-reported, PIT-safe), &lt;code&gt;latest_value&lt;/code&gt; (current revision), a &lt;code&gt;restated&lt;/code&gt; flag, and a per-row &lt;code&gt;qa_status&lt;/code&gt; — flagged, never silently hidden. There are 18,539 labeled restatements in the set. Delivery is one JSON endpoint — &lt;code&gt;/v1/fundamentals?ticker&amp;amp;as_of[&amp;amp;concept]&lt;/code&gt; — with server-side as-of filtering (&lt;code&gt;first_filed &amp;lt;= as_of&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Being equally plain about the limits: it's &lt;strong&gt;annual only&lt;/strong&gt; right now (no quarterly yet — on the roadmap), &lt;strong&gt;US only&lt;/strong&gt;, and there's &lt;strong&gt;no Parquet bulk download yet&lt;/strong&gt;. If you need quarterly data, decades of history, or global coverage today, one of the options above is a better fit, and you should use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When each option is the better choice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DIY EDGAR&lt;/strong&gt; — you have engineering time, want zero per-month cost, and value owning the pipeline over speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharadar&lt;/strong&gt; — you want broad US coverage plus ready-made ratios and a survivorship-free universe file, at a retail-quant price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiingo&lt;/strong&gt; — you're already using Tiingo for prices and want fundamentals in the same stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compustat / LSEG&lt;/strong&gt; — you need decades of PIT history, global coverage, or institutional support, and have the budget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tradevo Data&lt;/strong&gt; — you want US annual PIT with explicit original-vs-restated fields and a dead-simple as-of API, cheaply, to prove out an idea before committing to a bigger feed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it without a signup
&lt;/h2&gt;

&lt;p&gt;If you just want to see what honest PIT rows look like, our &lt;strong&gt;free sample is on GitHub&lt;/strong&gt; — 40 large caps, 3,212 rows, full methodology, no email required: &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;github.com/christianpichichero-max/pit-fundamentals&lt;/a&gt;. Inspect the schema, run an as-of query against your own backtest, and decide for yourself.&lt;/p&gt;

&lt;p&gt;If it fits and you want the full universe, the API is &lt;strong&gt;$49/mo&lt;/strong&gt;, 5,000 requests/day, key issued instantly, cancel anytime — &lt;a href="https://tradevodata.com/?ref=blog" rel="noopener noreferrer"&gt;details and docs at tradevodata.com&lt;/a&gt;. And if it doesn't fit, one of the alternatives above genuinely will.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is educational content, not investment advice. Pricing and product details for third-party vendors change — verify every number on the vendor's own pricing page before you buy.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>finance</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Your Backtest Saw the Future: Lookahead Bias in Fundamental Data, Measured Across 313,562 Rows</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Mon, 06 Jul 2026 14:38:33 +0000</pubDate>
      <link>https://dev.to/tradevodata/your-backtest-saw-the-future-lookahead-bias-in-fundamental-data-measured-across-313562-rows-4cba</link>
      <guid>https://dev.to/tradevodata/your-backtest-saw-the-future-lookahead-bias-in-fundamental-data-measured-across-313562-rows-4cba</guid>
      <description>&lt;p&gt;If your backtest joins fundamentals on the fiscal-period-end date, it is trading on numbers that did not exist yet. That's the whole bug, in one sentence. A company's fiscal year ends, and then — weeks later — a 10-K gets filed and the numbers become public. Join on the wrong date and every fundamental data point in your backtest arrives early.&lt;/p&gt;

&lt;p&gt;We measured how early. Across the 313,562 point-in-time rows in our dataset (5,214 US companies, built from raw SEC EDGAR filings), on rows with a reliable filing date, fundamentals became public an average of &lt;strong&gt;43 days after&lt;/strong&gt; the fiscal period ended. The maximum in our sample was 61 days.&lt;/p&gt;

&lt;p&gt;Forty-three days of free clairvoyance on the average row, compounding across every rebalance. That is why fundamental strategies so often look great in testing and evaporate live.&lt;/p&gt;

&lt;p&gt;This post covers what lookahead bias is, the measurement, how to check your own pipeline for it, and what the honest fixes are — including the free ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is lookahead bias?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lookahead bias is when a backtest uses information that was not yet publicly available at the simulated decision time.&lt;/strong&gt; The simulation "knows" something the market didn't, so the strategy's historical results are inflated in a way that cannot be replicated live.&lt;/p&gt;

&lt;p&gt;It's the quieter sibling of survivorship bias, and it's more dangerous, because it doesn't require a broken universe or a bad vendor — it hides inside a perfectly innocent-looking &lt;code&gt;merge()&lt;/code&gt;. The data is real. The values are correct. Only the &lt;em&gt;timestamps&lt;/em&gt; are lying.&lt;/p&gt;

&lt;p&gt;Price data mostly can't have this problem: a trade prints and is public within milliseconds. Fundamental data is the worst offender, for a structural reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why fundamentals are the worst offender
&lt;/h2&gt;

&lt;p&gt;Every fundamentals row has two dates, and most datasets only make one of them easy to use:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Period end&lt;/strong&gt; — the date the fiscal year closed. This is the date the numbers &lt;em&gt;describe&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First filed&lt;/strong&gt; — the date the 10-K hit EDGAR. This is the date the numbers &lt;em&gt;became knowable&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The gap between them is dead air during which the company knows its results and you don't. SEC deadlines let a 10-K be filed up to 60 days after fiscal year-end for large accelerated filers, 75 for accelerated filers, and 90 for everyone else — and late filers can extend beyond that.&lt;/p&gt;

&lt;p&gt;Naive datasets index everything by period end, because that's the natural key for the &lt;em&gt;accounting&lt;/em&gt;. But your backtest doesn't care what period the number describes; it cares when a trader could have acted on it. Those are different questions with different dates, an average of 43 days apart in our data.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete example: Apple's FY2024
&lt;/h2&gt;

&lt;p&gt;These rows come straight from our dataset (you can verify them in the &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;free sample&lt;/a&gt; — no signup):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FY2024 period end&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2024-09-28&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FY2024 10-K filed&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2024-11-01&lt;/strong&gt; (34 days later)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revenue as first reported&lt;/td&gt;
&lt;td&gt;$391.0B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diluted EPS as first reported&lt;/td&gt;
&lt;td&gt;$6.08&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now ask the only question a backtest should ever ask: &lt;em&gt;what was the newest annual revenue you could honestly know about Apple on a given date?&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;As-of date&lt;/th&gt;
&lt;th&gt;Newest AAPL annual revenue honestly knowable&lt;/th&gt;
&lt;th&gt;Filed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2024-10-15&lt;/td&gt;
&lt;td&gt;FY2023 — $383.3B&lt;/td&gt;
&lt;td&gt;2023-11-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024-11-15&lt;/td&gt;
&lt;td&gt;FY2024 — $391.0B&lt;/td&gt;
&lt;td&gt;2024-11-01&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same company, one month apart, a different "latest known" number. A naive period-end join hands your October 2024 backtest the FY2024 figures — a month before the market saw them.&lt;/p&gt;

&lt;p&gt;And note: Apple is the &lt;em&gt;best case&lt;/em&gt; on timing. Across 12 fiscal years in our data, Apple filed its 10-K 30–37 days after year end, faster than the 43-day cross-sectional average — the average company gives your backtest a bigger peek than this. Even Apple, though, isn't clean on what comes next: run the sample and you'll find five AAPL rows flagged &lt;code&gt;restated&lt;/code&gt;. FY2018–19 diluted EPS and share counts were retroactively rewritten by exactly 4× when the 2020 stock split was applied to prior years — the diluted EPS the market first saw as $11.91 is carried in current data as $2.98 — and FY2017 operating cash flow was later revised by about 1%. Which is the second problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second leak: restatements
&lt;/h2&gt;

&lt;p&gt;Fixing the filing date is necessary but not sufficient, because filed numbers don't stay put. Companies restate and revise: 10-K/A amendments, reclassifications, corrections, retroactive split adjustments. In our dataset we found &lt;strong&gt;18,539 rows where a later filing revised a previously reported value by more than 0.5%&lt;/strong&gt; on the same XBRL tag.&lt;/p&gt;

&lt;p&gt;Here's why that leaks even in a dataset that &lt;em&gt;has&lt;/em&gt; a filing-date field. Most budget fundamentals APIs store &lt;strong&gt;one mutable value per period&lt;/strong&gt;. When a restatement lands, the historical value is silently overwritten. Your backtest then reads the &lt;em&gt;corrected&lt;/em&gt; number stamped with the &lt;em&gt;original&lt;/em&gt; filing date — information that genuinely did not exist on that date. The lookahead is smaller and rarer than the 43-day version, but it's concentrated exactly where it hurts: in the companies whose numbers turned out to be wrong.&lt;/p&gt;

&lt;p&gt;A genuinely point-in-time dataset has to keep both values: what the market first saw, and what we now believe is true.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to detect lookahead bias in your own backtest
&lt;/h2&gt;

&lt;p&gt;You don't have to take any of this on faith. Two checks, both cheap.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Audit your join dates against real filing dates
&lt;/h3&gt;

&lt;p&gt;Our free sample (40 large caps, 3,212 rows, CSV) carries a &lt;code&gt;first_filed&lt;/code&gt; column derived from the actual EDGAR filings. Merge your vendor's data against it and count the leaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="c1"&gt;# free sample: github.com/christianpichichero-max/pit-fundamentals
&lt;/span&gt;&lt;span class="n"&gt;pit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/pit_fundamentals_history.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;parse_dates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;period_end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first_filed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;pit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filed_reliable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first_filed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;period_end&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c1"&gt;# mean lag ≈ 43 days — on this sample and on the full 313,562-row dataset
&lt;/span&gt;
&lt;span class="c1"&gt;# your data: ticker, fiscal_year, and the date your backtest
# currently *sees* the row (whatever you join on today)
&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fiscal_year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first_filed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;&lt;span class="nf"&gt;drop_duplicates&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fiscal_year&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;leaked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visible_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first_filed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;leaked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; of rows were visible to your backtest &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;before they were public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that percentage isn't ~0%, your historical results include information that hadn't been published yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The lag-shift test
&lt;/h3&gt;

&lt;p&gt;Re-run your backtest with every fundamental data point delayed to &lt;code&gt;period_end + 90 days&lt;/code&gt; (a conservative bound on public availability). If your Sharpe or CAGR degrades materially, the difference was never alpha — it was the peek. A strategy that only works when it sees filings early is a strategy that doesn't work.&lt;/p&gt;

&lt;p&gt;Softer symptoms worth suspicion: fundamental signals that seem to "time" earnings surprisingly well, value strategies whose turnover clusters near fiscal year-ends, and — the classic — a live track record that decays immediately relative to the backtest.&lt;/p&gt;

&lt;h2&gt;
  
  
  How point-in-time data fixes it
&lt;/h2&gt;

&lt;p&gt;A point-in-time (PIT) dataset makes &lt;em&gt;knowability&lt;/em&gt; the primary index. Every value in ours carries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;first_filed&lt;/code&gt;&lt;/strong&gt; — the date the number actually became public (the PIT stamp)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;original_value&lt;/code&gt;&lt;/strong&gt; — the number as first reported: what the market saw. Use this for backtests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;latest_value&lt;/code&gt;&lt;/strong&gt; — the current post-revision number. Use this for present-day screening.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;restated&lt;/code&gt;&lt;/strong&gt; — flagged when a later filing moved the value &amp;gt;0.5% on the same tag (including 10-K/A amendments)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;qa_status&lt;/code&gt;&lt;/strong&gt; — per-row validation result. Rows we can't fully trust are flagged, never hidden.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The query semantics then enforce honesty server-side. Our API is one endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /v1/fundamentals?ticker=AAPL&amp;amp;as_of=2024-10-15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns only rows where &lt;code&gt;first_filed &amp;lt;= as_of&lt;/code&gt; — so the 2024-10-15 query above returns FY2023 and earlier, never FY2024, no matter how you write your backtest loop. (One honest edge to know: the comparison is inclusive, so a filing dated exactly on your as-of date is returned. If you want to be strict about same-day tradability, subtract a day.)&lt;/p&gt;

&lt;p&gt;One more thing, because trust in a dataset should be earned, not asserted: before launch we had independent agents adversarially re-derive values from the raw EDGAR filings and compare them against ours. They found one real bug in our revision tracking. We fixed it before shipping. The full methodology — tag resolution, duration filters, restatement thresholds, QA rules — is &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;published in the open&lt;/a&gt; alongside the sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your options, honestly compared
&lt;/h2&gt;

&lt;p&gt;We sell one of these options, so calibrate accordingly. But the comparison below is real, and for some readers the right answer is not us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. DIY from SEC EDGAR — free.&lt;/strong&gt; EDGAR's &lt;code&gt;companyfacts&lt;/code&gt; API is public domain, free, and needs no key. This is what we build from, and you can too. The catch is engineering, not access: companies switch XBRL tags over time (Apple's revenue has lived under three different tags since 2014), stub periods and quarters can leak into annual figures, and reconstructing &lt;em&gt;first-reported&lt;/em&gt; values through restatements is genuinely fiddly — that's where our own audit found a bug. If you have the time, our published methodology is a usable blueprint. Budget a few weekends, not a few hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Budget fundamentals APIs (~$15–100/mo).&lt;/strong&gt; Many include a filing-date field, which solves the 43-day problem &lt;em&gt;if you remember to join on it&lt;/em&gt;. The structural gap is the mutable single value per period described above: restatements silently overwrite history, so the "point-in-time" join can still leak, and you can't reconstruct what the market originally saw.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Sharadar and Tiingo — the credible budget incumbents.&lt;/strong&gt; We will not pretend to be the only affordable PIT-aware option, because we aren't. Sharadar (via Nasdaq Data Link, roughly $550–830/yr for personal use) offers as-reported dimensions, quarterly data, ~20,000 companies including delisted ones, and history to 1998 — &lt;strong&gt;if you need quarterly or delisted coverage today, buy Sharadar; it is better than us at those things.&lt;/strong&gt; Tiingo's &lt;code&gt;asReported&lt;/code&gt; fields are also genuinely PIT-relevant. The tradeoffs: you assemble the as-of join yourself from dimension tables, pricing sits behind a login, and Sharadar's non-professional license excludes money-management use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Research-grade institutional PIT ($10k–50k/yr).&lt;/strong&gt; Compustat Snapshot, LSEG, Calcbench, Intrinio (~$9.6k/yr). These are the genuine article — deep history, full revision archives, survivorship-bias-safe universes — and if someone is paying you to manage money, they are probably the right call. They are also priced for institutions and generally won't sell to an individual with a Stripe card.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Tradevo Data — the middle.&lt;/strong&gt; $49/mo, one documented JSON endpoint with server-side &lt;code&gt;as_of&lt;/code&gt;, &lt;code&gt;original_value&lt;/code&gt; vs &lt;code&gt;latest_value&lt;/code&gt; on every row, restatement flags, per-row QA, 5,000 requests/day, key issued instantly after checkout. We think of it as the honest budget tier of a product that has historically cost $10k–50k/yr — not "better data than everyone," just lookahead-safety as the default query semantic, at a price an individual can justify.&lt;/p&gt;

&lt;h2&gt;
  
  
  What our data doesn't do (yet)
&lt;/h2&gt;

&lt;p&gt;Stating this up front so you don't find out after paying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Annual only&lt;/strong&gt; — 10-K and 10-K/A. Quarterly (10-Q) is on the roadmap, not shipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;US companies only&lt;/strong&gt; (5,214 of them).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;7 concepts&lt;/strong&gt;: Revenue, NetIncome, Assets, StockholdersEquity, OperatingCashFlow, EPSDiluted, DilutedShares.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Up to 12 fiscal years&lt;/strong&gt; of history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No bulk Parquet download yet&lt;/strong&gt; — it's a query API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;first_filed &amp;lt;= as_of&lt;/code&gt; is same-day-inclusive, as noted above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of those is a dealbreaker, the comparison section above has options that fit better, and we'd rather you use them than be disappointed here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the work yourself
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;free sample is on GitHub — 40 large caps, 3,212 point-in-time rows, full methodology, no signup&lt;/strong&gt;: &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;github.com/christianpichichero-max/pit-fundamentals&lt;/a&gt;. Run the audit snippet above against your current vendor. If your pipeline comes back clean, excellent — you lost ten minutes and gained a verified negative.&lt;/p&gt;

&lt;p&gt;If it doesn't, and you want the fix as an API instead of a weekend project: the full dataset is &lt;strong&gt;$49/mo&lt;/strong&gt; at &lt;a href="https://tradevodata.com" rel="noopener noreferrer"&gt;tradevodata.com&lt;/a&gt;, key issued instantly, docs at &lt;a href="https://tradevodata.com/docs" rel="noopener noreferrer"&gt;tradevodata.com/docs&lt;/a&gt;, cancel anytime.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tradevo Data is a product of Tradevo Technologies Inc. Source data is U.S. SEC EDGAR (public domain). We sell a dataset, not signals — nothing here is investment advice, and no data product can make a bad strategy good. It can only stop a backtest from lying to you.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>finance</category>
      <category>datascience</category>
    </item>
    <item>
      <title>How to Build a Point-in-Time Fundamentals Database from SEC EDGAR (and When Not To)</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Mon, 06 Jul 2026 14:38:27 +0000</pubDate>
      <link>https://dev.to/tradevodata/how-to-build-a-point-in-time-fundamentals-database-from-sec-edgar-and-when-not-to-2gn6</link>
      <guid>https://dev.to/tradevodata/how-to-build-a-point-in-time-fundamentals-database-from-sec-edgar-and-when-not-to-2gn6</guid>
      <description>&lt;p&gt;If you backtest with today's fundamentals, you are cheating yourself. The Revenue figure you pull for Q4 2019 was probably restated in 2021, and the restated number was not knowable on the day you would have traded. That gap is &lt;a href="https://dev.to/blog/lookahead-bias-fundamental-backtests"&gt;lookahead bias&lt;/a&gt;, and it is why "point-in-time" (PIT) fundamentals exist: a dataset that answers &lt;em&gt;what did filers report, and when did the market first see it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The good news: the raw material is free and public. The SEC's EDGAR system publishes structured XBRL financials for every US filer, and with enough patience you can assemble a real PIT database yourself. The honest news: a few steps are fiddly enough that most DIY builds ship a subtle bug. This guide teaches the whole pipeline — our full &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;methodology is public on GitHub&lt;/a&gt; — then weighs the cost fairly so you can decide whether to build or buy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build: EDGAR companyfacts, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Pull companyfacts.&lt;/strong&gt; EDGAR exposes a per-company JSON blob at &lt;code&gt;data.sec.gov/api/xbrl/companyfacts/CIK{10-digit}.json&lt;/code&gt;. It contains every XBRL fact the company has ever tagged, grouped by taxonomy (&lt;code&gt;us-gaap&lt;/code&gt;, &lt;code&gt;dei&lt;/code&gt;) and concept, each with an array of reported values. This is your source of truth. Respect the SEC's fair-access rules (declare a User-Agent, throttle to ~10 requests/sec).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Filter to annual 10-K facts.&lt;/strong&gt; Each fact carries a &lt;code&gt;form&lt;/code&gt; (&lt;code&gt;10-K&lt;/code&gt;, &lt;code&gt;10-K/A&lt;/code&gt;, &lt;code&gt;10-Q&lt;/code&gt;, &lt;code&gt;8-K&lt;/code&gt;…) and, for flow concepts like Revenue, a &lt;code&gt;start&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt; window. You want annual figures, so keep &lt;code&gt;10-K&lt;/code&gt; and &lt;code&gt;10-K/A&lt;/code&gt; (amendments) and filter duration concepts to roughly 365-day windows. Skip this and you will silently mix quarterly and annual Revenue into the same series — a classic corruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Resolve XBRL synonym tags across years.&lt;/strong&gt; This is where builds quietly break. Companies change the tag they use for the &lt;em&gt;same&lt;/em&gt; economic concept. Revenue might be &lt;code&gt;Revenues&lt;/code&gt; one year, &lt;code&gt;RevenueFromContractWithCustomerExcludingAssessedTax&lt;/code&gt; the next, and &lt;code&gt;SalesRevenueNet&lt;/code&gt; in an older filing. If you map only one tag, you get holes exactly where a company evolved its reporting. You need an ordered synonym list per concept and a deterministic resolution rule so the &lt;em&gt;same&lt;/em&gt; logical field is populated every year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Stamp &lt;code&gt;first_filed&lt;/code&gt;.&lt;/strong&gt; For each value, record the date it &lt;em&gt;first&lt;/em&gt; appeared in a filing (the accession's filing date), not the fiscal period end. This &lt;code&gt;first_filed&lt;/code&gt; stamp is the entire point of PIT: a backtest asks "what was known as of date X?" and you answer by returning only rows where &lt;code&gt;first_filed &amp;lt;= X&lt;/code&gt;. Same-day filings should be inclusive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Detect same-tag restatements.&lt;/strong&gt; When a later 10-K or 10-K/A reports a &lt;em&gt;different&lt;/em&gt; value for a period you already have, that is a restatement. Keep both: the &lt;code&gt;original_value&lt;/code&gt; (first-reported, PIT-safe) and the &lt;code&gt;latest_value&lt;/code&gt; (current best estimate), plus a &lt;code&gt;restated&lt;/code&gt; flag. Use a tolerance — we flag when the same tag's value moves more than 0.5% — so rounding noise does not masquerade as a restatement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. QA every row.&lt;/strong&gt; Sanity-check signs, magnitudes, and duplicate periods; carry a &lt;code&gt;qa_status&lt;/code&gt; so consumers can filter. Fundamentals data is dirty; a QA column is not optional.&lt;/p&gt;

&lt;h3&gt;
  
  
  The part everyone underestimates
&lt;/h3&gt;

&lt;p&gt;Restatement reconstruction is deceptively hard, and we say that from experience: our own audit found a bug in exactly this step. It is easy to write logic that &lt;em&gt;looks&lt;/em&gt; right on Apple and Microsoft — clean filers with stable tags — and quietly mislabels the messier 80% of the market. Tag switches (step 3) and restatement detection (step 5) are where a DIY dataset silently drifts from ground truth, and you often won't notice until a backtest looks suspiciously good. Budget real test coverage across small caps, amenders, and tag-switchers, not just the mega-caps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build vs. buy: an honest comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Build from EDGAR&lt;/th&gt;
&lt;th&gt;Tradevo Data ($49/mo)&lt;/th&gt;
&lt;th&gt;Research-grade vendors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;$0 + your time&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;Higher / quote-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Days to weeks&lt;/td&gt;
&lt;td&gt;Minutes (key after checkout)&lt;/td&gt;
&lt;td&gt;Varies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PIT correctness&lt;/td&gt;
&lt;td&gt;Yours to get right&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;first_filed&lt;/code&gt;, original vs. latest, restatement flags&lt;/td&gt;
&lt;td&gt;Yes (their core)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coverage&lt;/td&gt;
&lt;td&gt;Whatever you build&lt;/td&gt;
&lt;td&gt;5,214 US companies, annual only&lt;/td&gt;
&lt;td&gt;Often broader (quarterly, global)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;History&lt;/td&gt;
&lt;td&gt;As far as you pull&lt;/td&gt;
&lt;td&gt;Up to 12 fiscal years&lt;/td&gt;
&lt;td&gt;Often 20-25+ years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restatements&lt;/td&gt;
&lt;td&gt;You reconstruct&lt;/td&gt;
&lt;td&gt;18,539 labeled&lt;/td&gt;
&lt;td&gt;Handled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulk / Parquet&lt;/td&gt;
&lt;td&gt;Build it&lt;/td&gt;
&lt;td&gt;JSON API only (no bulk yet)&lt;/td&gt;
&lt;td&gt;Often available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Methodology&lt;/td&gt;
&lt;td&gt;Yours&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;Public&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Usually opaque&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Credible paid PIT options exist and we won't pretend otherwise. &lt;strong&gt;Sharadar Core US Fundamentals&lt;/strong&gt; (via Nasdaq Data Link) is a well-regarded point-in-time dataset with deep history; its personal pricing is not publicly listed as of July 2026, so &lt;a href="https://data.nasdaq.com/databases/SF1" rel="noopener noreferrer"&gt;check their page&lt;/a&gt;. &lt;strong&gt;Tiingo&lt;/strong&gt; offers a Power plan at $30/mo individual / $50/mo commercial as of July 2026, with fundamentals sold as a separate API add-on (pricing on request) and an as-reported option of its own — &lt;a href="https://www.tiingo.com/about/pricing" rel="noopener noreferrer"&gt;see their pricing&lt;/a&gt;. Both are legitimate; verify current terms before you buy, because vendor pricing moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to build it yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You want to learn the plumbing.&lt;/strong&gt; Building the pipeline once teaches you what PIT actually means, and you'll trust your data more for having wrangled it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need concepts or coverage nobody sells.&lt;/strong&gt; Custom tags, obscure line items, non-standard filers — EDGAR has it all; you just have to extract it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your time is genuinely free&lt;/strong&gt; relative to $49/mo, and you enjoy this kind of work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need full control and auditability&lt;/strong&gt; down to the accession level.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When buying is the better choice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You'd rather test a strategy than debug XBRL synonym tables.&lt;/strong&gt; The engineering is real, and every week on the pipeline is a week not spent on research.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want restatement handling you didn't have to reconstruct&lt;/strong&gt; — the exact step where our own audit caught a bug, and where DIY builds most often ship wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$49/mo is cheaper than your afternoon.&lt;/strong&gt; For most working quants, it is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need broader coverage or quarterly data.&lt;/strong&gt; Then a deeper vendor like Sharadar or Tiingo may fit better than either a weekend build &lt;em&gt;or&lt;/em&gt; our annual-only tier — we're the honest budget tier, not the only option.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start free, either way
&lt;/h2&gt;

&lt;p&gt;Whichever path you pick, start by looking at real PIT data. Our &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;free GitHub sample&lt;/a&gt; has 40 large caps, 3,212 rows, and the complete methodology — tag resolution, &lt;code&gt;first_filed&lt;/code&gt; stamping, same-tag restatement detection, QA — with no signup. Read it, copy the approach, and build your own. If you'd rather skip the fiddly parts, &lt;a href="https://tradevodata.com/?ref=blog" rel="noopener noreferrer"&gt;Tradevo Data&lt;/a&gt; serves the same discipline as one JSON endpoint (&lt;code&gt;/v1/fundamentals?ticker&amp;amp;as_of&lt;/code&gt;) with server-side &lt;code&gt;as_of&lt;/code&gt; filtering, 5,000 requests/day, and an instant key after checkout — cancel anytime.&lt;/p&gt;

&lt;p&gt;Build it or buy it. Just don't backtest on restated numbers and call it edge.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Not investment advice. Tradevo Data provides point-in-time US equity fundamentals sourced from public SEC EDGAR filings; it does not make performance claims. Competitor pricing and features are as of July 2026 — always verify current terms on each vendor's own page before purchasing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>finance</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
