<?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%2Fe278d800-4f1b-4a9a-8659-027387e2544f.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>Why a Scheduler Cannot Trust Its Own Database</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 15 Oct 2026 18:40:43 +0000</pubDate>
      <link>https://dev.to/tradevodata/why-a-scheduler-cannot-trust-its-own-database-4mh9</link>
      <guid>https://dev.to/tradevodata/why-a-scheduler-cannot-trust-its-own-database-4mh9</guid>
      <description>&lt;p&gt;If your scheduler submits transactions, its database is not a record of current chain state. It is a local projection assembled from RPC reads, transaction submissions, receipts, logs, and assumptions about what happened between those events.&lt;/p&gt;

&lt;p&gt;That projection is useful. Scheduling would be impractical if every decision required rebuilding state from chain history. But the projection is always slightly wrong. The important engineering question is not whether it becomes stale; it is how the system behaves when it does.&lt;/p&gt;

&lt;p&gt;There are two failure directions: stale-permissive state and stale-restrictive state. They have different costs and need different controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The database is authoritative about intent, not chain facts
&lt;/h2&gt;

&lt;p&gt;A scheduler can authoritatively say, “I created this job,” “I attempted this transaction,” or “I assigned this idempotency key.” Those are facts owned by the application.&lt;/p&gt;

&lt;p&gt;It cannot authoritatively say, “This wallet still has enough balance,” “This nonce is unused,” “This contract remains unpaused,” or “That transaction is final.” Those are chain facts. The database can cache observations of them, but an observation has a block context and an age.&lt;/p&gt;

&lt;p&gt;Even an RPC read is not an timeless answer. A read against a recent block can be displaced by a reorganization. Different RPC nodes can briefly disagree. A transaction visible in the pending pool may disappear, be replaced, or execute with different surrounding state than the scheduler expected.&lt;/p&gt;

&lt;p&gt;Treating cached chain data as ordinary application state hides all of this uncertainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stale-permissive: the database says yes
&lt;/h2&gt;

&lt;p&gt;Stale-permissive state means the local model allows an action that current chain state rejects or makes unsafe.&lt;/p&gt;

&lt;p&gt;Common examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A cached balance still includes funds already spent elsewhere.&lt;/li&gt;
&lt;li&gt;A nonce has been consumed by another transaction.&lt;/li&gt;
&lt;li&gt;An authorization was removed after the last sync.&lt;/li&gt;
&lt;li&gt;A contract parameter changed, invalidating the scheduler’s assumptions.&lt;/li&gt;
&lt;li&gt;A previous transaction succeeded, but the local worker timed out and scheduled the same intent again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The obvious cost is a reverted transaction and wasted gas. The less obvious cost is incorrect execution. A transaction can succeed while doing something different from what the scheduler intended because price, liquidity, contract configuration, or account state changed before inclusion.&lt;/p&gt;

&lt;p&gt;Preflight simulation helps, but it is not a lock on future state. Other transactions can land between simulation and execution. The right interpretation is “this action was valid against this observed state,” not “this action will remain valid.”&lt;/p&gt;

&lt;p&gt;Safety-critical predicates therefore need checks as close to submission as practical. The scheduler should also encode constraints in the transaction itself when the contract interface supports them. A database check can reduce bad attempts; transaction-level constraints can stop execution when state moves beyond accepted bounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stale-restrictive: the database says no
&lt;/h2&gt;

&lt;p&gt;Stale-restrictive state is the mirror case. The database blocks an action that chain state would allow.&lt;/p&gt;

&lt;p&gt;This often happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A worker marks an intent as pending and crashes before clearing its lease.&lt;/li&gt;
&lt;li&gt;A transaction is replaced, but the database keeps watching the old hash.&lt;/li&gt;
&lt;li&gt;A receipt or log is missed because an RPC request failed.&lt;/li&gt;
&lt;li&gt;A transaction succeeds while the local record remains in a submitted state.&lt;/li&gt;
&lt;li&gt;A temporary chain condition clears, but the scheduler never revisits the job.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This direction usually preserves funds but harms liveness. Jobs remain stuck, valid execution windows pass, and operators have to repair records manually. Over time, conservative patches can turn transient uncertainty into permanent blockage.&lt;/p&gt;

&lt;p&gt;A common mistake is to treat “unknown” as “failed” or “still pending.” Unknown should be its own state. It tells the reconciliation process to gather evidence rather than blindly submit again or wait forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple failure sequence
&lt;/h2&gt;

&lt;p&gt;Consider a scheduler that records an intended transaction and then broadcasts it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intent: created
transaction: submitted
local state: pending
worker: crashes
chain: transaction succeeds
receipt update: never written
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On restart, resubmitting may duplicate the action. Refusing to resubmit may leave the job stuck indefinitely.&lt;/p&gt;

&lt;p&gt;The scheduler needs a third path: inspect the account nonce, transaction receipt, relevant logs, and resulting contract state. It can then classify the intent using chain evidence. That process is reconciliation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconciliation is a control loop
&lt;/h2&gt;

&lt;p&gt;Reconciliation should run continuously, not as an incident script. A practical design separates command handling from observation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persist an intent before submission, with an idempotency key tied to the logical action.&lt;/li&gt;
&lt;li&gt;Record every submission attempt and transaction hash.&lt;/li&gt;
&lt;li&gt;Advance state through explicit transitions rather than overwriting a generic status field.&lt;/li&gt;
&lt;li&gt;Scan receipts and logs from a durable checkpoint.&lt;/li&gt;
&lt;li&gt;Re-read critical contract state for records that remain uncertain.&lt;/li&gt;
&lt;li&gt;Expire worker leases independently from transaction status.&lt;/li&gt;
&lt;li&gt;Revisit recently confirmed records when the chain can still reorganize.&lt;/li&gt;
&lt;li&gt;Rewind derived state when a previously observed block leaves the canonical chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Logs are efficient for broad discovery, while direct reads are useful for checking current state. Neither is sufficient alone. A log scanner can miss data because of checkpoint bugs or provider failures. A direct read can show the current value without explaining which local intents produced it.&lt;/p&gt;

&lt;p&gt;Reconciliation also needs bounded concurrency and rate controls. An outage can create a large backlog, and an eager recovery worker can overwhelm the same RPC provider it depends on. The recovery path should be designed as normal production traffic, not exceptional traffic with unlimited resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide where freshness is worth its cost
&lt;/h2&gt;

&lt;p&gt;Reading chain state before every action reduces some stale-permissive failures, but adds latency and consumes RPC budget. It can also create a false sense of certainty because the state can change after the read.&lt;/p&gt;

&lt;p&gt;Skipping fresh reads improves throughput, but shifts more responsibility to transaction constraints, idempotency, and later reconciliation. For low-risk observations, cached state may be acceptable. For actions involving balances, permissions, nonces, or irreversible contract calls, fresher evidence is usually worth more.&lt;/p&gt;

&lt;p&gt;There is no universal freshness policy. The honest trade-off is latency and RPC usage versus correctness risk. Make that decision per predicate rather than applying one cache duration to the whole scheduler.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://app.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=why-a-scheduler-cannot-trust-its-own-database" rel="noopener noreferrer"&gt;Tradevo&lt;/a&gt;, software that compiles plain-English crypto strategies into deterministic rules, tests and paper-trades them, supports review and marketplace publishing, and executes subscribed rules on Base from wallets users control.&lt;/p&gt;

&lt;p&gt;A reliable scheduler does not eliminate stale state. It makes staleness visible, limits what stale assumptions can authorize, and continuously compares its local model with chain evidence. Reconciliation is not cleanup after the system fails. It is part of the system’s normal operation.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>architecture</category>
      <category>backend</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>How to Find Financial Restatements in SEC Filings</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Wed, 14 Oct 2026 18:44:48 +0000</pubDate>
      <link>https://dev.to/tradevodata/how-to-find-financial-restatements-in-sec-filings-1349</link>
      <guid>https://dev.to/tradevodata/how-to-find-financial-restatements-in-sec-filings-1349</guid>
      <description>&lt;p&gt;Financial restatements can be useful research signals. They show that a previously reported figure changed after becoming public—sometimes because of an error, sometimes because of an amendment, and sometimes because the company changed how a fact was tagged or presented.&lt;/p&gt;

&lt;p&gt;For researchers, the central question is not merely: &lt;strong&gt;What does the latest filing say?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is: &lt;strong&gt;What was first reported, when did it become public, and what changed afterward?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Answering that question requires point-in-time data. A current-only financial database may replace an original value with a revision, obscuring how the historical record changed.&lt;/p&gt;

&lt;p&gt;This guide explains how to find financial restatements in SEC filings, compare original and revised values, and avoid false positives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as a financial restatement?
&lt;/h2&gt;

&lt;p&gt;In a broad research workflow, a restatement flag can mean a meaningful change to a previously reported financial fact for the same company, fiscal period, and accounting concept.&lt;/p&gt;

&lt;p&gt;That is broader than searching only for filings explicitly labeled as restatements. A changed value may appear in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An amended filing such as a 10-K/A or 10-Q/A&lt;/li&gt;
&lt;li&gt;A later filing that presents comparative prior-period figures&lt;/li&gt;
&lt;li&gt;An XBRL fact using the same concept and period but a revised value&lt;/li&gt;
&lt;li&gt;A filing that corrects or updates an earlier disclosure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not every change means fraud, distress, or even an accounting error. Units, decimals, contexts, fiscal periods, taxonomy choices, and presentation changes can all create apparent differences. Treat a restatement flag as a research lead, not a verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reliable method: preserve original and revised values
&lt;/h2&gt;

&lt;p&gt;To detect revisions, retain filing-time observations rather than storing only the latest value.&lt;/p&gt;

&lt;p&gt;For each fact, capture relevant fields such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Company identifier and ticker&lt;/li&gt;
&lt;li&gt;SEC filing accession or source filing&lt;/li&gt;
&lt;li&gt;Filing or acceptance date&lt;/li&gt;
&lt;li&gt;Form type&lt;/li&gt;
&lt;li&gt;Fiscal period and fiscal year&lt;/li&gt;
&lt;li&gt;XBRL concept or tag&lt;/li&gt;
&lt;li&gt;Units&lt;/li&gt;
&lt;li&gt;Context dimensions&lt;/li&gt;
&lt;li&gt;Reported value&lt;/li&gt;
&lt;li&gt;Whether the filing is an amendment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then group comparable observations by company, period, concept, units, and compatible context.&lt;/p&gt;

&lt;p&gt;Within each group, sort observations by the date they became public. The earliest valid observation becomes the &lt;strong&gt;original value&lt;/strong&gt;. The most recent comparable observation becomes the &lt;strong&gt;latest value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A simple relative-change test is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;relative_change = abs(latest_value - original_value) / abs(original_value)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tradevo Data labels a row as restated when the same-tag value changes by more than 0.5%, including amendments. This is its documented classification rule, not a determination that a change is legally or economically material.&lt;/p&gt;

&lt;p&gt;The edge case is an original value of zero. Relative change is undefined there, so a detection system needs a separately documented rule rather than an arbitrary percentage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the same-tag requirement matters
&lt;/h2&gt;

&lt;p&gt;Comparing values across different XBRL concepts can produce misleading alerts.&lt;/p&gt;

&lt;p&gt;For example, a company may move from a standard taxonomy concept to a custom extension or disclose a related but not identical measure. Even when labels look similar, the accounting meaning may differ.&lt;/p&gt;

&lt;p&gt;A conservative detector can begin by comparing values reported under the same tag. This may not capture every economically equivalent revision, but it reduces semantic mismatches and makes each flag easier to audit.&lt;/p&gt;

&lt;p&gt;A more advanced pipeline can map extensions to standard concepts, but that requires validation and should preserve the original tags for inspection.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical SEC EDGAR workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Identify the original filing
&lt;/h3&gt;

&lt;p&gt;Start with the relevant 10-K or 10-Q in SEC EDGAR. Record the filing date, accession, fiscal period, units, context, and reported value.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Search for amendments
&lt;/h3&gt;

&lt;p&gt;Check for 10-K/A or 10-Q/A filings. Amendments are an obvious place to look, but do not stop there. A later annual or quarterly filing may present a changed comparative prior-period value.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Match the accounting fact
&lt;/h3&gt;

&lt;p&gt;Compare the same XBRL concept for the same fiscal period, units, and compatible context. Watch for consolidated versus segment facts and continuing operations versus total-company measures.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Compare chronologically
&lt;/h3&gt;

&lt;p&gt;Sort matched observations by when they became public. Preserve the first-reported value even if a later filing changes it.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Apply a documented rule
&lt;/h3&gt;

&lt;p&gt;Use a documented classification rule, such as Tradevo Data's greater-than-0.5% same-tag rule. Keep the underlying observations so researchers can investigate or override the classification.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Review the filing narrative
&lt;/h3&gt;

&lt;p&gt;Read amendment explanations, footnotes, auditor language, accounting-policy disclosures, and internal-control sections. A numeric change identifies where to look; the filing may explain why it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why current-only databases can hide the signal
&lt;/h2&gt;

&lt;p&gt;Suppose a company first reports an accounting value and later revises it. If a database keeps only the revised figure, a historical backtest may behave as though that figure was known on the original date.&lt;/p&gt;

&lt;p&gt;That creates lookahead bias.&lt;/p&gt;

&lt;p&gt;A point-in-time record can instead separate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;first_filed&lt;/code&gt;: the date the value became public&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;original_value&lt;/code&gt;: the first-reported, point-in-time-safe value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;latest_value&lt;/code&gt;: the current revision&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;restated&lt;/code&gt;: whether the same-tag change exceeds 0.5%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;qa_status&lt;/code&gt;: a quality-control field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an &lt;code&gt;as_of&lt;/code&gt; query, Tradevo Data includes rows where &lt;code&gt;first_filed &amp;lt;= as_of&lt;/code&gt;, with the selected date included. For a deeper treatment, see &lt;a href="https://tradevodata.com/blog/lookahead-bias-fundamental-backtests?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/blog/lookahead-bias-fundamental-backtests?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt; and &lt;a href="https://tradevodata.com/blog/point-in-time-fundamentals-data?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/blog/point-in-time-fundamentals-data?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Tradevo Data to surface revisions
&lt;/h2&gt;

&lt;p&gt;Tradevo Data provides point-in-time US equity fundamentals sourced from public-domain SEC EDGAR data. Its annual dataset contains 632,466 point-in-time rows across 5,168 US companies, covers 16 annual concepts, and labels 37,804 restatements. It also contains 1,089,635 quarterly rows across 5,382 companies and seven quarterly concepts.&lt;/p&gt;

&lt;p&gt;The API exposes one fundamentals endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/v1/fundamentals?ticker&amp;amp;as_of[&amp;amp;concept][&amp;amp;period=annual|quarterly]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Annual is the default period. Server-side &lt;code&gt;as_of&lt;/code&gt; filtering uses &lt;code&gt;first_filed &amp;lt;= as_of&lt;/code&gt;, including the selected date.&lt;/p&gt;

&lt;p&gt;Each row includes &lt;code&gt;first_filed&lt;/code&gt;, &lt;code&gt;original_value&lt;/code&gt;, &lt;code&gt;latest_value&lt;/code&gt;, a &lt;code&gt;restated&lt;/code&gt; flag, and &lt;code&gt;qa_status&lt;/code&gt;. Researchers can use these fields to screen for changed facts before reviewing the underlying SEC filings.&lt;/p&gt;

&lt;p&gt;Tradevo Data covers up to 12 fiscal years. It does not cover non-US or delisted companies and does not provide TTM figures. Quarterly Q4 is reported where tagged or derived and labeled where supported; Q4 EPS and share values are not derived. Parquet format is not included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Options compared fairly
&lt;/h2&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;Strengths&lt;/th&gt;
&lt;th&gt;Limitations to evaluate&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SEC EDGAR&lt;/td&gt;
&lt;td&gt;Primary public source with complete filing context and no vendor normalization&lt;/td&gt;
&lt;td&gt;Building structured point-in-time data requires ingestion, XBRL normalization, chronology, deduplication, and QA&lt;/td&gt;
&lt;td&gt;Researchers who need maximum control and can maintain infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tradevo Data&lt;/td&gt;
&lt;td&gt;Original and latest values, filing chronology, restatement flags, server-side &lt;code&gt;as_of&lt;/code&gt;, and bulk access on Pro&lt;/td&gt;
&lt;td&gt;US scope; no non-US, delisted, TTM, or Parquet coverage&lt;/td&gt;
&lt;td&gt;Budget-conscious researchers who want structured point-in-time fundamentals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharadar&lt;/td&gt;
&lt;td&gt;Credible established alternative&lt;/td&gt;
&lt;td&gt;Check its current coverage, methodology, and point-in-time semantics against your requirements&lt;/td&gt;
&lt;td&gt;Teams whose needs match its ecosystem; see their pricing page: &lt;a href="https://data.nasdaq.com/databases/SF1" rel="noopener noreferrer"&gt;https://data.nasdaq.com/databases/SF1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiingo&lt;/td&gt;
&lt;td&gt;Credible API-oriented market-data provider&lt;/td&gt;
&lt;td&gt;Verify current restatement fields, historical semantics, and coverage for your use case&lt;/td&gt;
&lt;td&gt;Developers seeking a broader API stack; see their pricing page: &lt;a href="https://www.tiingo.com/pricing/overview" rel="noopener noreferrer"&gt;https://www.tiingo.com/pricing/overview&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QuantConnect&lt;/td&gt;
&lt;td&gt;Integrated research and execution environment with supported datasets&lt;/td&gt;
&lt;td&gt;Evaluate whether its platform model fits a need for standalone filing data&lt;/td&gt;
&lt;td&gt;Researchers building inside its environment; see their pricing page: &lt;a href="https://www.quantconnect.com/pricing/" rel="noopener noreferrer"&gt;https://www.quantconnect.com/pricing/&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Competitor capabilities and pricing can change. Review each provider's current documentation, methodology, and pricing page rather than comparing labels alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  When another option wins
&lt;/h2&gt;

&lt;p&gt;Use SEC EDGAR directly when you need complete filing narratives, exhibits, custom tags, or provenance beyond normalized numeric rows.&lt;/p&gt;

&lt;p&gt;A broader vendor may win when you require international issuers, delisted-company coverage, additional asset classes, or a larger integrated data catalog. Sharadar or Tiingo may be a better fit when their broader products, coverage, or existing integration matches your workflow. QuantConnect may win when research, data, backtesting, and execution need to live in one environment.&lt;/p&gt;

&lt;p&gt;Tradevo Data is positioned as an honest budget tier of research-grade point-in-time fundamentals—not as the only credible or affordable option.&lt;/p&gt;

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

&lt;p&gt;Build your own pipeline when restatement detection is core intellectual property, when you need custom materiality rules, or when vendor normalization would conceal details essential to your research.&lt;/p&gt;

&lt;p&gt;Be prepared to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SEC submissions and accession histories&lt;/li&gt;
&lt;li&gt;Filing dates and acceptance timestamps&lt;/li&gt;
&lt;li&gt;XBRL contexts, units, dimensions, and custom extensions&lt;/li&gt;
&lt;li&gt;Fiscal-calendar irregularities&lt;/li&gt;
&lt;li&gt;Duplicate and conflicting facts&lt;/li&gt;
&lt;li&gt;Amendments and later comparative disclosures&lt;/li&gt;
&lt;li&gt;Taxonomy migrations&lt;/li&gt;
&lt;li&gt;Reproducible quality checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The advantage is control. The trade-off is continuing maintenance and validation, not just the initial parser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the method before paying
&lt;/h2&gt;

&lt;p&gt;The public proof pack contains 225 rows for five companies, covering their latest three fiscal years, with full methodology and no signup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;https://github.com/christianpichichero-max/pit-fundamentals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inspect the chronology, source logic, original values, and revisions before deciding whether the normalization fits your research.&lt;/p&gt;

&lt;p&gt;If it does, the card-backed 7-day free trial at &lt;a href="https://tradevodata.com/?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt; covers 10 companies, a rolling 3-year history, and 100 requests per day without bulk. It renews at $29 per month unless canceled. Pro provides complete available history, 5,000 requests per day, and bulk annual or quarterly downloads through &lt;code&gt;/v1/download&lt;/code&gt; and &lt;code&gt;/v1/snapshot?as_of&lt;/code&gt;. Documentation is at &lt;a href="https://tradevodata.com/docs?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/docs?utm_content=blog-how-to-find-restatements-in-sec-filings&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Restatement flags are best used as triage: identify changed facts, inspect the filing sequence, read the disclosures, and decide whether the revision is relevant to your research.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>finance</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Fundamentals Data for Finance LLM AI Agents: A Practical Grounding Layer</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Sat, 10 Oct 2026 18:44:17 +0000</pubDate>
      <link>https://dev.to/tradevodata/fundamentals-data-for-finance-llm-ai-agents-a-practical-grounding-layer-534j</link>
      <guid>https://dev.to/tradevodata/fundamentals-data-for-finance-llm-ai-agents-a-practical-grounding-layer-534j</guid>
      <description>&lt;p&gt;Finance AI agents have a basic reliability problem: language models can produce plausible answers, including plausible numbers. Plausible is not enough when a user asks what a company reported, when the information became public, or whether the figure was later restated.&lt;/p&gt;

&lt;p&gt;One practical approach is a grounding layer built from dated financial facts.&lt;/p&gt;

&lt;p&gt;For developers building retrieval-augmented generation systems, filing copilots, screening agents, research assistants, or automated company briefs, point-in-time fundamentals can provide a structured bridge between SEC filings and model-generated explanations. The model can reason over the data, but it should not invent the data.&lt;/p&gt;

&lt;p&gt;Tradevo Data provides point-in-time US equity fundamentals sourced from SEC EDGAR. Its annual dataset covers 5,168 US companies, 632,466 point-in-time rows, 16 annual concepts, and up to 12 fiscal years. Its quarterly dataset contains 1,089,635 rows across 5,382 companies and seven quarterly concepts. Coverage varies by filer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why ordinary fundamentals can mislead an AI agent
&lt;/h2&gt;

&lt;p&gt;A conventional fundamentals database may emphasize the latest known value. That can be useful for current analysis, but it may be inappropriate for a historical question.&lt;/p&gt;

&lt;p&gt;Suppose an agent is asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What revenue and net income were publicly available for a company on June 15 of a past year?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If retrieval returns a figure restated months later, the answer contains information that was unavailable on June 15. The number may now be technically correct while still being historically invalid for that date.&lt;/p&gt;

&lt;p&gt;That distinction matters for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Historical research and event reconstruction&lt;/li&gt;
&lt;li&gt;Backtests using fundamental signals&lt;/li&gt;
&lt;li&gt;Filing-aware RAG systems&lt;/li&gt;
&lt;li&gt;Agents answering “as of” questions&lt;/li&gt;
&lt;li&gt;Audit trails for generated financial summaries&lt;/li&gt;
&lt;li&gt;Comparing first-reported figures with subsequent revisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is lookahead bias at the data layer. A longer explanation is available at &lt;a href="https://tradevodata.com/blog/lookahead-bias-fundamental-backtests?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/blog/lookahead-bias-fundamental-backtests?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fields an agent needs for defensible answers
&lt;/h2&gt;

&lt;p&gt;A useful finance grounding record should do more than return a value. It should establish provenance.&lt;/p&gt;

&lt;p&gt;Each Tradevo Data row includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;first_filed&lt;/code&gt;: the date the value became public&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;original_value&lt;/code&gt;: the first-reported, point-in-time-safe value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;latest_value&lt;/code&gt;: the current revision&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;restated&lt;/code&gt;: a flag for a greater-than-0.5% change under the same XBRL tag, including amendments&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;qa_status&lt;/code&gt;: a quality-assurance status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dataset labels 37,804 restatements.&lt;/p&gt;

&lt;p&gt;For an AI agent, those fields support a stronger response pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve facts available by the requested date.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;original_value&lt;/code&gt; for an as-reported historical answer.&lt;/li&gt;
&lt;li&gt;Compare it with &lt;code&gt;latest_value&lt;/code&gt; if revisions matter.&lt;/li&gt;
&lt;li&gt;Include &lt;code&gt;first_filed&lt;/code&gt; and the requested concept in the response.&lt;/li&gt;
&lt;li&gt;State uncertainty or coverage gaps instead of filling them with generated numbers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An XBRL tag is not a complete semantic guarantee: filers can use extensions, presentation varies, and accounting context still matters. But a dated record containing the original value, revised value, restatement flag, and QA status is more auditable than an unsupported number in model output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example query for a finance AI agent
&lt;/h2&gt;

&lt;p&gt;Tradevo Data exposes one JSON fundamentals endpoint with server-side point-in-time filtering:&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 https://tradevodata.com/v1/fundamentals?ticker=AAPL&amp;amp;as_of=2020-06-15&amp;amp;concept=Revenue&amp;amp;period=annual&amp;amp;utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server applies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;first_filed &amp;lt;= as_of
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The date is inclusive. Annual data is the default, while &lt;code&gt;period=quarterly&lt;/code&gt; requests quarterly rows.&lt;/p&gt;

&lt;p&gt;A basic agent tool contract might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_fundamentals"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AAPL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"as_of"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-06-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"concept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Revenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"period"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"annual"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model should then compose an answer from returned records rather than relying on memorized parameters. A system instruction can require it to include the fiscal period, &lt;code&gt;first_filed&lt;/code&gt;, &lt;code&gt;original_value&lt;/code&gt;, and concept in every numerical citation.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use only retrieved values. If no qualifying row exists by the requested
as-of date, say that the dataset returned no supported value. Do not infer
or interpolate a financial figure.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That final rule is important. Retrieval does not prevent hallucination unless the agent is explicitly required to abstain when evidence is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical RAG architecture
&lt;/h2&gt;

&lt;p&gt;A filing agent can separate structured facts from unstructured context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use point-in-time fundamentals for supported numerical claims.&lt;/li&gt;
&lt;li&gt;Use filing text retrieval for management commentary, accounting policies, and risk disclosures.&lt;/li&gt;
&lt;li&gt;Store source metadata alongside every chunk or fact.&lt;/li&gt;
&lt;li&gt;Require citations in the final answer.&lt;/li&gt;
&lt;li&gt;Validate numerical statements against retrieved structured records before returning them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structured fundamentals are not a replacement for the filing. They can serve as a compact numerical layer alongside it.&lt;/p&gt;

&lt;p&gt;For bulk workflows, the $29-per-month Pro plan includes &lt;code&gt;/v1/download&lt;/code&gt; and &lt;code&gt;/v1/snapshot?as_of&lt;/code&gt;, each supporting &lt;code&gt;period=annual|quarterly&lt;/code&gt;. These endpoints can support a local retrieval store or reproducible historical snapshot. Parquet format is not included.&lt;/p&gt;

&lt;p&gt;More background on the data model is available at &lt;a href="https://tradevodata.com/blog/point-in-time-fundamentals-data?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/blog/point-in-time-fundamentals-data?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tradevo Data covers—and what it does not
&lt;/h2&gt;

&lt;p&gt;The annual dataset contains these 16 concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue&lt;/li&gt;
&lt;li&gt;NetIncome&lt;/li&gt;
&lt;li&gt;Assets&lt;/li&gt;
&lt;li&gt;StockholdersEquity&lt;/li&gt;
&lt;li&gt;OperatingCashFlow&lt;/li&gt;
&lt;li&gt;EPSDiluted&lt;/li&gt;
&lt;li&gt;DilutedShares&lt;/li&gt;
&lt;li&gt;GrossProfit&lt;/li&gt;
&lt;li&gt;OperatingIncome&lt;/li&gt;
&lt;li&gt;PretaxIncome&lt;/li&gt;
&lt;li&gt;IncomeTaxExpense&lt;/li&gt;
&lt;li&gt;CapitalExpenditures&lt;/li&gt;
&lt;li&gt;CashAndCashEquivalents&lt;/li&gt;
&lt;li&gt;CurrentAssets&lt;/li&gt;
&lt;li&gt;CurrentLiabilities&lt;/li&gt;
&lt;li&gt;NetPPE&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quarterly coverage includes seven concepts. Q4 is reported where tagged or derived and labelled where supported. Derived Q4 EPS and share figures are not provided.&lt;/p&gt;

&lt;p&gt;The service does not provide TTM calculations, non-US coverage, delisted-company coverage, or Parquet output. It is not presented as a full filing-text corpus, market-data feed, estimates database, or accounting ontology.&lt;/p&gt;

&lt;p&gt;These limits matter. An agent should not present absence as zero, assume every filer reports every concept, or silently substitute one accounting concept for another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fair comparison for finance-agent developers
&lt;/h2&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;Best fit&lt;/th&gt;
&lt;th&gt;Potential strengths&lt;/th&gt;
&lt;th&gt;Important considerations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tradevo Data&lt;/td&gt;
&lt;td&gt;Developers wanting a budget-tier API and bulk access for core US fundamentals&lt;/td&gt;
&lt;td&gt;Server-side &lt;code&gt;as_of&lt;/code&gt;, original and latest values, filing dates, restatement labels, and QA status&lt;/td&gt;
&lt;td&gt;Limited concept set; no delisted, TTM, non-US, or Parquet coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw SEC EDGAR/XBRL&lt;/td&gt;
&lt;td&gt;Teams needing maximum control and direct primary-source processing&lt;/td&gt;
&lt;td&gt;Public-domain source material and direct control over interpretation&lt;/td&gt;
&lt;td&gt;Requires ingestion, taxonomy mapping, duplicate handling, amendment processing, QA, and point-in-time logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharadar&lt;/td&gt;
&lt;td&gt;Researchers evaluating a broader established data product&lt;/td&gt;
&lt;td&gt;A credible alternative with its own coverage and data model&lt;/td&gt;
&lt;td&gt;Confirm current scope and licensing; see their pricing page: &lt;a href="https://data.nasdaq.com/databases/SF1" rel="noopener noreferrer"&gt;https://data.nasdaq.com/databases/SF1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiingo&lt;/td&gt;
&lt;td&gt;Developers evaluating fundamentals alongside other financial APIs&lt;/td&gt;
&lt;td&gt;A credible provider with a broader product context&lt;/td&gt;
&lt;td&gt;Verify point-in-time semantics and coverage; see their pricing page: &lt;a href="https://www.tiingo.com/pricing/overview" rel="noopener noreferrer"&gt;https://www.tiingo.com/pricing/overview&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QuantConnect&lt;/td&gt;
&lt;td&gt;Teams building inside an integrated research and execution platform&lt;/td&gt;
&lt;td&gt;Data access within a broader algorithmic environment&lt;/td&gt;
&lt;td&gt;Platform fit may matter more than a standalone fundamentals API; see their pricing page: &lt;a href="https://www.quantconnect.com/pricing/" rel="noopener noreferrer"&gt;https://www.quantconnect.com/pricing/&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No provider should be chosen from a feature label alone. Test amendment handling, filing-date semantics, survivorship assumptions, missing values, concept normalization, redistribution rights, and historical coverage against your actual agent workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  When another option wins
&lt;/h2&gt;

&lt;p&gt;Another provider can be the better choice when you need a broader security universe, delisted companies, non-US issuers, more accounting concepts, analyst estimates, TTM figures, market data, or a managed research platform. A specialist vendor may also win when your institution needs enterprise support, contractual service levels, or licensing terms beyond a developer-oriented product.&lt;/p&gt;

&lt;p&gt;If your workflow already runs inside QuantConnect, its integrated environment may reduce engineering work. If you need a broader commercial dataset, Sharadar or Tiingo may fit better after you verify current coverage and point-in-time behavior.&lt;/p&gt;

&lt;p&gt;Tradevo Data is positioned as an honest budget tier of research-grade point-in-time data, not as the only affordable option and not as a universal financial-data layer.&lt;/p&gt;

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

&lt;p&gt;Build directly from SEC EDGAR when provenance control is more important than implementation speed, your required concepts fall outside the supported set, or your team has specialized accounting and data-engineering expertise.&lt;/p&gt;

&lt;p&gt;Be prepared to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filing and amendment chronology&lt;/li&gt;
&lt;li&gt;XBRL standard tags and company extensions&lt;/li&gt;
&lt;li&gt;Units, periods, contexts, and duplicate facts&lt;/li&gt;
&lt;li&gt;Fiscal-year differences&lt;/li&gt;
&lt;li&gt;Restatements and comparable-tag logic&lt;/li&gt;
&lt;li&gt;Quarterly versus year-to-date facts&lt;/li&gt;
&lt;li&gt;Q4 derivation rules&lt;/li&gt;
&lt;li&gt;Missing and malformed filings&lt;/li&gt;
&lt;li&gt;Reprocessing when extraction rules change&lt;/li&gt;
&lt;li&gt;Reproducible historical snapshots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;EDGAR is public domain, so self-building can be rational. The practical burden is maintaining consistent interpretation and QA over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect the evidence before connecting an agent
&lt;/h2&gt;

&lt;p&gt;A public proof pack is available at &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;https://github.com/christianpichichero-max/pit-fundamentals&lt;/a&gt;. It contains five companies, their latest three fiscal years, 225 rows, and the full methodology with no signup.&lt;/p&gt;

&lt;p&gt;On reliable-filing rows in that five-company proof pack only, measured lookahead averaged 35.2 days and reached a maximum of 48 days. Those measurements describe the proof pack, not the full dataset.&lt;/p&gt;

&lt;p&gt;For API evaluation, &lt;a href="https://tradevodata.com/?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt; offers a card-backed seven-day free trial covering 10 companies, a rolling three-year history, and 100 requests per day without bulk. Unless canceled, it renews at $29 per month. Pro provides complete available history, 5,000 requests per day, and bulk endpoints. Documentation is at &lt;a href="https://tradevodata.com/docs?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/docs?utm_content=blog-grounding-a-finance-llm-in-as-reported-fundamentals&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start with the public sample, test the dates and concepts against source filings, and make abstention part of the agent design. The goal is not to make a model sound certain. It is to make supported financial numbers traceable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>finance</category>
      <category>datascience</category>
    </item>
    <item>
      <title>A Transaction Timeout Is Not a Transaction Failure</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 08 Oct 2026 20:09:56 +0000</pubDate>
      <link>https://dev.to/tradevodata/a-transaction-timeout-is-not-a-transaction-failure-mkn</link>
      <guid>https://dev.to/tradevodata/a-transaction-timeout-is-not-a-transaction-failure-mkn</guid>
      <description>&lt;p&gt;You broadcast a transaction, wait for the RPC response, and get a timeout. What happened?&lt;/p&gt;

&lt;p&gt;The honest answer is that you do not know. The node may never have received the request. It may have accepted the transaction into its mempool but lost the response. Another node may already have propagated it. The transaction may even be mined while your executor records a network error.&lt;/p&gt;

&lt;p&gt;Treating that timeout as a failure and sending a fresh transaction can execute the same business action twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate the request from the transaction
&lt;/h2&gt;

&lt;p&gt;An RPC request and a blockchain transaction have different lifecycles. A request can fail at the HTTP layer while the transaction continues through the network.&lt;/p&gt;

&lt;p&gt;This distinction produces three outcomes, not two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Succeeded:&lt;/strong&gt; chain state shows the intended transaction was included.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failed:&lt;/strong&gt; chain state shows a terminal failure, such as a reverted transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uncertain:&lt;/strong&gt; the executor cannot yet establish either outcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;uncertain&lt;/code&gt; is not a softer spelling of &lt;code&gt;failed&lt;/code&gt;. It is a durable state that needs reconciliation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persist the intent before broadcasting
&lt;/h2&gt;

&lt;p&gt;The first durable record should describe the business action, not the RPC attempt. Write it before signing or sending anything.&lt;/p&gt;

&lt;p&gt;A minimal record might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intent_id:      rebalance:account-42:2026-09-03T10
account:        0xabc...
chain_id:       8453
operation:      swap token A for token B
status:         created
nonce:          null
signed_tx:      null
tx_hashes:      []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give &lt;code&gt;intent_id&lt;/code&gt; a uniqueness constraint based on the business operation. If two workers receive the same job, they should load the same intent rather than create independent transactions.&lt;/p&gt;

&lt;p&gt;Next, reserve a nonce, construct the transaction, sign it, and persist the signed bytes and hash before broadcast. This ordering lets a restarted worker send the exact same payload.&lt;/p&gt;

&lt;p&gt;A database entry marked &lt;code&gt;broadcasting&lt;/code&gt; does not prove that any node saw the transaction. Likewise, a missing &lt;code&gt;sent&lt;/code&gt; log does not prove that no node saw it. Your database records what the executor attempted; the chain records what took effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use nonces deliberately
&lt;/h2&gt;

&lt;p&gt;For an EVM externally owned account, the nonce is the main duplicate-execution boundary. Two transactions from the same account with the same nonce cannot both become canonical.&lt;/p&gt;

&lt;p&gt;That does not mean an executor can derive any nonce from an intent ID. Account nonces are sequential. A practical nonce manager must serialize reservations per account and persist them so concurrent workers do not choose the same nonce for unrelated intents.&lt;/p&gt;

&lt;p&gt;After a timeout, rebroadcasting the exact signed bytes is the simplest safe retry. The transaction hash stays the same, and nodes that already know it can treat the broadcast as redundant.&lt;/p&gt;

&lt;p&gt;Fee replacement is more complicated. A replacement uses the same nonce but changes fee fields, producing a new hash. Persist every candidate hash under the same intent and monitor all of them. Never respond to a timeout by allocating a fresh nonce to the same action.&lt;/p&gt;

&lt;p&gt;If the original transaction has already been mined, a replacement may be rejected because the nonce is too low. That error is a reason to inspect chain state, not to create another transaction.&lt;/p&gt;

&lt;p&gt;Smart-contract accounts can provide a stronger application-level boundary by storing an operation ID and rejecting duplicate execution. That handles cases an account nonce cannot express, but it adds contract complexity and execution cost. Sequential EOA nonces are simpler, though they can become a throughput bottleneck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconcile with chain state
&lt;/h2&gt;

&lt;p&gt;A reconciliation worker should examine evidence in roughly this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check receipts for every transaction hash attached to the intent.&lt;/li&gt;
&lt;li&gt;Check whether the sending account's nonce has been consumed.&lt;/li&gt;
&lt;li&gt;Inspect the destination contract's state or events for the intended operation.&lt;/li&gt;
&lt;li&gt;Wait for the confirmation depth required by the application before marking the intent final.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A single RPC node saying “not found” is weak evidence. Mempools differ, nodes can lag, and old pending transactions may disappear from local views. Querying another provider can improve visibility, but it still does not turn absence into proof.&lt;/p&gt;

&lt;p&gt;If the account nonce has advanced, determine which transaction consumed it. Your intended transaction may have been included, replaced, or displaced by another process using the same account.&lt;/p&gt;

&lt;p&gt;Reorganizations also matter. &lt;code&gt;included&lt;/code&gt; and &lt;code&gt;finalized&lt;/code&gt; should be separate states. An included transaction can temporarily disappear and need further reconciliation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ugly middle state
&lt;/h2&gt;

&lt;p&gt;Suppose the broadcast timed out, no receipt is visible, the transaction is absent from the RPC node, and the account nonce has not moved. The transaction might still be propagating, or every node might have dropped it.&lt;/p&gt;

&lt;p&gt;There is no honest terminal answer yet.&lt;/p&gt;

&lt;p&gt;Keep the intent &lt;code&gt;uncertain&lt;/code&gt;, rebroadcast the same signed payload, and schedule another reconciliation pass. Time alone should not silently convert uncertainty into failure. If the application eventually stops retrying, preserve the unresolved status for operator inspection.&lt;/p&gt;

&lt;p&gt;A “cancel” transaction is also only a competing transaction with the same nonce. It can win the race, or the original can be mined first. Model the outcome from chain evidence rather than from the fact that a cancellation request was sent.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://app.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=idempotency-when-a-transaction-may-already-be-mined" rel="noopener noreferrer"&gt;Tradevo&lt;/a&gt;, which compiles plain-English crypto strategies into deterministic rules, runs historical tests and paper trading, lets creators submit strategies for human review and publish approved strategies to a marketplace, and lets subscribers run published rules from wallets they control.&lt;/p&gt;

&lt;h2&gt;
  
  
  A useful executor invariant
&lt;/h2&gt;

&lt;p&gt;The core invariant is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One business intent may have several broadcast attempts and transaction hashes, but it must not silently become several independent nonce-bearing actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That requires coordination across the database, signer, nonce manager, broadcaster, and reconciliation worker. Idempotency is not a retry flag attached to an RPC call. It is a state machine built around the possibility that the network acted while your process learned nothing.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>architecture</category>
      <category>ethereum</category>
      <category>backend</category>
    </item>
    <item>
      <title>Point-in-Time Data for Academic Finance Research: A Reproducibility Guide</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Wed, 07 Oct 2026 20:02:49 +0000</pubDate>
      <link>https://dev.to/tradevodata/point-in-time-data-for-academic-finance-research-a-reproducibility-guide-1mkj</link>
      <guid>https://dev.to/tradevodata/point-in-time-data-for-academic-finance-research-a-reproducibility-guide-1mkj</guid>
      <description>&lt;p&gt;In empirical finance, a backtest should use only information available on the simulated decision date. If it uses financial statements based on fiscal period-end dates, current database values, or later restatements, the model may incorporate facts that had not yet been published.&lt;/p&gt;

&lt;p&gt;That is lookahead bias. It can affect estimated predictability, reported coefficients, and reproducibility.&lt;/p&gt;

&lt;p&gt;Point-in-time, or PIT, fundamentals record when each value became public and allow researchers to reconstruct the information set available as of a historical date. For accounting-based asset-pricing signals, PIT treatment can be an important part of research design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why ordinary fundamentals may not be enough
&lt;/h2&gt;

&lt;p&gt;Suppose a company has a fiscal year ending December 31. Its annual revenue does not necessarily become public on December 31. The filing may arrive weeks later, and an amendment or restatement may subsequently change the value.&lt;/p&gt;

&lt;p&gt;A conventional panel might contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the fiscal period end;&lt;/li&gt;
&lt;li&gt;the latest known value;&lt;/li&gt;
&lt;li&gt;a standardized concept;&lt;/li&gt;
&lt;li&gt;the company identifier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That panel may be useful for descriptive tasks, but it does not necessarily answer the historical question: &lt;strong&gt;What value could a researcher have observed on the portfolio formation date?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A PIT row needs an availability date or timestamp. Tradevo Data exposes &lt;code&gt;first_filed&lt;/code&gt;, defined as the date the value became public. Its API applies &lt;code&gt;first_filed &amp;lt;= as_of&lt;/code&gt;, including same-day filings. Each row also contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;original_value&lt;/code&gt;: the first-reported, PIT-safe value;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;latest_value&lt;/code&gt;: the current revision;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;restated&lt;/code&gt;: a flag for changes above 0.5% on the same tag, including amendments;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;qa_status&lt;/code&gt;: a data-quality field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The distinction between original and latest values matters. Replacing the original observation with a later revision can introduce future information even if the fiscal period itself is correctly lagged.&lt;/p&gt;

&lt;p&gt;For a deeper treatment, see &lt;a href="https://tradevodata.com/blog/lookahead-bias-fundamental-backtests?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/blog/lookahead-bias-fundamental-backtests?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  PIT design belongs in the methods section
&lt;/h2&gt;

&lt;p&gt;A reproducible paper should state more than “fundamentals were lagged.” A fixed lag is a convention, not proof that the information was public.&lt;/p&gt;

&lt;p&gt;A stronger methodology documents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Availability rule:&lt;/strong&gt; Which filing date or timestamp determines eligibility?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boundary rule:&lt;/strong&gt; Are filings published on the formation date included?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revision policy:&lt;/strong&gt; Does the study use first-reported or latest values?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fiscal-period policy:&lt;/strong&gt; How are annual observations aligned across firms?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concept mapping:&lt;/strong&gt; Which source tags map to each standardized variable?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missingness policy:&lt;/strong&gt; Are missing values dropped, imputed, or interpreted economically?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Universe policy:&lt;/strong&gt; Does the source include delisted companies and non-US issuers?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot procedure:&lt;/strong&gt; Can another researcher reconstruct the same extract later?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tradevo Data's server-side endpoint is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/v1/fundamentals?ticker&amp;amp;as_of[&amp;amp;concept][&amp;amp;period=annual|quarterly]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Annual is the default. Bulk access is available through &lt;code&gt;/v1/download&lt;/code&gt; and &lt;code&gt;/v1/snapshot?as_of&lt;/code&gt;, each accepting &lt;code&gt;period=annual|quarterly&lt;/code&gt;. The snapshot route makes the historical cutoff explicit. Parquet format is not included.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tradevo Data covers—and what it does not
&lt;/h2&gt;

&lt;p&gt;Tradevo Data provides point-in-time US equity fundamentals derived from public-domain SEC EDGAR filings. Its annual dataset contains 632,466 point-in-time rows across 5,168 US companies, with 16 annual concepts and up to 12 fiscal years. Its quarterly dataset contains 1,089,635 rows across 5,382 companies and seven quarterly concepts. Across the data, 37,804 restatements are labeled.&lt;/p&gt;

&lt;p&gt;The annual concepts are Revenue, NetIncome, Assets, StockholdersEquity, OperatingCashFlow, EPSDiluted, DilutedShares, GrossProfit, OperatingIncome, PretaxIncome, IncomeTaxExpense, CapitalExpenditures, CashAndCashEquivalents, CurrentAssets, CurrentLiabilities, and NetPPE.&lt;/p&gt;

&lt;p&gt;Quarterly data is also available with seven concepts. Q4 is reported where tagged or derived and labeled where supported. Derived Q4 EPS and shares are excluded.&lt;/p&gt;

&lt;p&gt;The limitations are material:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;US companies only;&lt;/li&gt;
&lt;li&gt;no non-US coverage;&lt;/li&gt;
&lt;li&gt;no delisted-company coverage;&lt;/li&gt;
&lt;li&gt;no TTM fields;&lt;/li&gt;
&lt;li&gt;a focused concept set rather than the breadth of a large institutional platform;&lt;/li&gt;
&lt;li&gt;no included Parquet format.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These constraints should appear in a paper's data section. In particular, the absence of delisted companies may create survivorship concerns depending on sample construction and the research question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tradevo Data versus WRDS and Compustat
&lt;/h2&gt;

&lt;p&gt;WRDS is an institutional research platform through which universities and other organizations may access datasets such as Compustat, subject to their subscriptions and licenses. It may be the stronger choice when a study requires broader historical coverage, standardized identifiers across databases, delisted securities, international accounting data, or established institutional workflows.&lt;/p&gt;

&lt;p&gt;Tradevo Data is a narrower, budget-tier PIT source for researchers who need US filing fundamentals without requiring university-gated access.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;Tradevo Data&lt;/th&gt;
&lt;th&gt;WRDS/Compustat&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Access model&lt;/td&gt;
&lt;td&gt;Public API and bulk files; card-backed trial&lt;/td&gt;
&lt;td&gt;Commonly accessed through subscribing institutions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source orientation&lt;/td&gt;
&lt;td&gt;Public-domain SEC EDGAR filings&lt;/td&gt;
&lt;td&gt;Commercial research databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PIT mechanism&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;first_filed&lt;/code&gt;, original and latest values, and server-side &lt;code&gt;as_of&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Capabilities depend on the subscribed dataset and fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coverage&lt;/td&gt;
&lt;td&gt;US companies; focused annual and quarterly concepts&lt;/td&gt;
&lt;td&gt;Potentially broader options, depending on institutional subscriptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delisted companies&lt;/td&gt;
&lt;td&gt;Not covered&lt;/td&gt;
&lt;td&gt;May be the better route when delisted coverage is required; verify the specific product&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;International research&lt;/td&gt;
&lt;td&gt;Not covered&lt;/td&gt;
&lt;td&gt;May be better suited when subscribed products cover the required markets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reproducibility aids&lt;/td&gt;
&lt;td&gt;Public methodology, no-signup sample, and dated snapshots&lt;/td&gt;
&lt;td&gt;Institutional workflows subject to licensing and local access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price&lt;/td&gt;
&lt;td&gt;$29/month after the trial unless canceled&lt;/td&gt;
&lt;td&gt;See their pricing or institutional-access page: &lt;a href="https://wrds-www.wharton.upenn.edu/" rel="noopener noreferrer"&gt;https://wrds-www.wharton.upenn.edu/&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is not an argument that one source universally replaces the other. The appropriate source depends on the identification strategy, required universe, licensing constraints, and replication plan. Sharadar, Tiingo, and QuantConnect are also credible alternatives; compare their current documentation, coverage, PIT methodology, and pricing pages directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public proof before signup
&lt;/h2&gt;

&lt;p&gt;Tradevo Data publishes a no-signup proof pack at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;https://github.com/christianpichichero-max/pit-fundamentals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It contains 225 rows for five companies over their latest three fiscal years, plus the full methodology. Researchers can inspect the schema, test availability logic, and evaluate whether the concept mapping is suitable before entering payment details.&lt;/p&gt;

&lt;p&gt;On reliable-filing rows in that five-company proof pack only, measured lookahead averaged 35.2 days and reached a maximum of 48 days. These figures are not evidence about the full dataset. They demonstrate within the sample how filing availability can differ from fiscal period-end dating.&lt;/p&gt;

&lt;p&gt;More background on the data model is available at &lt;a href="https://tradevodata.com/blog/point-in-time-fundamentals-data?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/blog/point-in-time-fundamentals-data?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical research workflow
&lt;/h2&gt;

&lt;p&gt;A defensible workflow can be simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pre-register or document the formation-date rule.&lt;/li&gt;
&lt;li&gt;Query or snapshot the data with the required &lt;code&gt;as_of&lt;/code&gt; date.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;original_value&lt;/code&gt; for the historical information set unless the research question explicitly concerns revised accounts.&lt;/li&gt;
&lt;li&gt;Retain &lt;code&gt;first_filed&lt;/code&gt;, &lt;code&gt;restated&lt;/code&gt;, and &lt;code&gt;qa_status&lt;/code&gt; in intermediate files.&lt;/li&gt;
&lt;li&gt;Archive query parameters, code, and extraction dates.&lt;/li&gt;
&lt;li&gt;Report coverage exclusions, especially delisted and non-US companies.&lt;/li&gt;
&lt;li&gt;Run sensitivity checks using alternative availability rules where justified.&lt;/li&gt;
&lt;li&gt;Make a small, license-compliant replication extract available when possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The card-backed seven-day free trial covers 10 companies, a rolling three-year history, and 100 requests per day, without bulk access. It renews at $29/month unless canceled. Pro provides the complete available history, 5,000 requests per day, and bulk access through &lt;code&gt;/v1/download&lt;/code&gt; and &lt;code&gt;/v1/snapshot?as_of&lt;/code&gt;. Documentation is at &lt;a href="https://tradevodata.com/docs?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/docs?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;, and the trial is available at &lt;a href="https://tradevodata.com/?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When WRDS or another institutional source wins
&lt;/h2&gt;

&lt;p&gt;Use WRDS, Compustat, or another appropriate institutional dataset when the paper requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;delisted-company coverage to address survivorship bias;&lt;/li&gt;
&lt;li&gt;non-US or cross-country samples;&lt;/li&gt;
&lt;li&gt;history beyond Tradevo Data's available horizon;&lt;/li&gt;
&lt;li&gt;accounting fields outside Tradevo Data's focused concept set;&lt;/li&gt;
&lt;li&gt;linked returns, analyst estimates, ownership, executive, or security-master data;&lt;/li&gt;
&lt;li&gt;identifiers and conventions already standard in a target literature;&lt;/li&gt;
&lt;li&gt;a licensed source mandated by coauthors, reviewers, or the institution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Researchers should inspect the exact subscribed product, documentation, PIT fields, and licensing terms rather than assume every dataset exposed through a platform has identical historical properties. For current access and cost information, see each provider's pricing page.&lt;/p&gt;

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

&lt;p&gt;Building directly from SEC EDGAR can be the better choice when the extraction pipeline is itself a research contribution, when unusual XBRL tags are central to the hypothesis, or when complete control over transformation rules matters more than implementation time.&lt;/p&gt;

&lt;p&gt;A credible build may need to handle filing chronology, amendments, duplicate facts, taxonomy changes, units, dimensions, fiscal calendars, concept mapping, accession-level provenance, and historical snapshots. It may also require tests for revisions and malformed filings.&lt;/p&gt;

&lt;p&gt;The advantage is transparency and customization. The disadvantage is the required data-engineering work. One approach is to validate a managed dataset against raw filings for a stratified sample and publish the audit code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The standard is reconstructability
&lt;/h2&gt;

&lt;p&gt;Using a familiar database does not by itself make academic finance reproducible. The core question is whether another researcher can reconstruct the information set available on each simulated date and understand the transformations applied.&lt;/p&gt;

&lt;p&gt;Tradevo Data offers a focused option for annual and quarterly US filing fundamentals, with explicit availability dates, original and revised values, restatement labels, snapshots, and a public methodology. It is not a substitute for broad institutional databases in every research design. It is an inspectable budget-tier option when its coverage matches the paper.&lt;/p&gt;

&lt;p&gt;Start by auditing the free sample at &lt;a href="https://github.com/christianpichichero-max/pit-fundamentals" rel="noopener noreferrer"&gt;https://github.com/christianpichichero-max/pit-fundamentals&lt;/a&gt;. If the schema and methodology fit the study, the card-backed seven-day free trial is available at &lt;a href="https://tradevodata.com/?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate" rel="noopener noreferrer"&gt;https://tradevodata.com/?utm_content=blog-point-in-time-data-for-academic-finance-research&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&lt;/a&gt; and renews at $29/month unless canceled.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not investment advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>python</category>
      <category>finance</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Sponsoring Gas Without Holding User Funds: Paymasters and the Trust Model</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:50:45 +0000</pubDate>
      <link>https://dev.to/tradevodata/sponsoring-gas-without-holding-user-funds-paymasters-and-the-trust-model-5a0l</link>
      <guid>https://dev.to/tradevodata/sponsoring-gas-without-holding-user-funds-paymasters-and-the-trust-model-5a0l</guid>
      <description>&lt;p&gt;The awkward edge case in a consumer wallet flow is the first transaction: the user has the asset your application needs, but none of the chain’s native token. Asking them to visit an exchange or bridge before they can use the app is technically honest and often terrible UX.&lt;/p&gt;

&lt;p&gt;Account abstraction offers another option. A paymaster can cover the network fee while the user’s assets remain in their own smart account. That removes one prerequisite, but it also introduces a sponsor with policy control, infrastructure costs, and several ways to be abused.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic ERC-4337 flow
&lt;/h2&gt;

&lt;p&gt;In ERC-4337, a wallet submits a &lt;code&gt;UserOperation&lt;/code&gt; rather than sending a conventional transaction directly. Conceptually, the flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wallet builds and signs UserOperation
        |
        v
sponsor checks operation against its policy
        |
        v
sponsor adds signed paymaster data
        |
        v
bundler simulates and submits the operation
        |
        v
EntryPoint validates wallet and paymaster
        |
        v
call executes; fee is charged to paymaster deposit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The paymaster keeps native tokens deposited with the EntryPoint contract. Its validation logic decides whether it accepts responsibility for a particular operation. The EntryPoint then charges the actual execution cost to that deposit, subject to the operation’s gas fields and protocol rules.&lt;/p&gt;

&lt;p&gt;Most consumer applications use a verifying paymaster. An off-chain service inspects the operation and signs sponsorship data if the request satisfies its policy. The on-chain paymaster verifies that signature during validation.&lt;/p&gt;

&lt;p&gt;This split matters. The on-chain component checks that the sponsor approved the exact operation, while the off-chain service handles changing concerns such as rate limits, account status, application plans, and allowed contract methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the sponsor can do
&lt;/h2&gt;

&lt;p&gt;A sponsor can decide which operations it is willing to fund. Its policy might inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The destination contract and function selector.&lt;/li&gt;
&lt;li&gt;The chain and EntryPoint version.&lt;/li&gt;
&lt;li&gt;Maximum gas and fee fields.&lt;/li&gt;
&lt;li&gt;Calldata size.&lt;/li&gt;
&lt;li&gt;Wallet, user, device, or subscription quotas.&lt;/li&gt;
&lt;li&gt;Whether the operation was produced through an expected application flow.&lt;/li&gt;
&lt;li&gt;Estimated cost under current network conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sponsor can also stop issuing approvals, lower limits, or make sponsorship conditional on its backend being available. That is a real dependency. A wallet may remain usable, but the sponsored path can disappear.&lt;/p&gt;

&lt;p&gt;The sponsor also learns about an operation before inclusion. Even though the eventual transaction is public, advance visibility and request metadata can matter. Applications should avoid pretending that sponsorship is a privacy mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the sponsor cannot do
&lt;/h2&gt;

&lt;p&gt;Paying gas is not the same as authorizing the wallet’s call.&lt;/p&gt;

&lt;p&gt;The smart account still validates the user’s signature or whatever authorization scheme the account implements. A paymaster cannot modify the signed destination, calldata, or value without invalidating that authorization. It also cannot spend wallet assets merely because it paid the network fee.&lt;/p&gt;

&lt;p&gt;This distinction becomes blurred when one company operates several components. An application might run the sponsor, bundler access, and a delegated wallet signer. Those roles should be analyzed separately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The paymaster decides whether to pay.&lt;/li&gt;
&lt;li&gt;The bundler decides whether to submit a valid operation.&lt;/li&gt;
&lt;li&gt;The wallet’s authorization logic decides which calls may execute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A restricted paymaster policy does not make a broad wallet key safe. Likewise, a narrow wallet authorization does not prevent somebody from wasting an overly permissive sponsorship budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open sponsorship is a griefing surface
&lt;/h2&gt;

&lt;p&gt;A policy that effectively says “pay for any operation from our app” is easy to consume at scale. An attacker does not need to steal the sponsor’s deposit. They only need to create operations that qualify and cost money.&lt;/p&gt;

&lt;p&gt;Simple per-wallet limits are weak if wallets are cheap to create. IP limits can block legitimate shared networks and are easy to distribute around. Requiring an application login raises the cost of abuse but moves the problem into account creation and session security.&lt;/p&gt;

&lt;p&gt;Failed calls also deserve attention. An operation can pass validation and then revert during execution, consuming gas anyway. Simulation catches many failures, but state can change between simulation and inclusion. Deliberately expensive valid calls may still satisfy a policy that checks only destinations and selectors.&lt;/p&gt;

&lt;p&gt;A practical policy therefore combines several controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow specific contracts and methods rather than arbitrary calls.&lt;/li&gt;
&lt;li&gt;Cap gas fields and calldata size.&lt;/li&gt;
&lt;li&gt;Check call arguments when their meaning is stable and unambiguous.&lt;/li&gt;
&lt;li&gt;Set budgets across wallets, users, and time windows.&lt;/li&gt;
&lt;li&gt;Reject stale sponsorship signatures.&lt;/li&gt;
&lt;li&gt;Monitor cost per successful operation and cost per failed operation.&lt;/li&gt;
&lt;li&gt;Add a global circuit breaker for unexpected spending.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these controls is free. More inspection creates more backend coupling and more ways to reject legitimate traffic after a contract upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sponsorship is an operating decision
&lt;/h2&gt;

&lt;p&gt;The contract is only part of the system. Someone must keep the paymaster deposit funded, monitor native-token exposure, handle fee spikes, and decide what happens when a daily budget is exhausted.&lt;/p&gt;

&lt;p&gt;You also need an outage mode. Can users pay their own gas if the sponsor is unavailable? Does the interface explain that option before asking them to sign? Can another bundler submit the same operation, or have you coupled sponsorship to one provider’s API?&lt;/p&gt;

&lt;p&gt;Cost attribution matters too. “Gasless” usually means somebody else receives the bill. A product team needs to know which user action created each charge, whether retries duplicate work, and how much abuse the support team is willing to tolerate. Sponsorship may be cheap during normal traffic but difficult to bound during a fee spike or automated attack.&lt;/p&gt;

&lt;p&gt;Alternatives have valid trade-offs. Letting users pay is simpler and makes costs explicit. A faucet is straightforward but attracts automated abuse. Reimbursing users after execution still requires them to hold native tokens first. Application-specific relayers can work well when contracts already support meta-transactions, though they introduce their own trust and compatibility constraints.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://app.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=sponsoring-gas-without-holding-user-funds" rel="noopener noreferrer"&gt;Tradevo&lt;/a&gt;, which lets people build, test, paper-trade, publish, subscribe to, and automatically run crypto strategies from wallets they control; its free tier sponsors gas.&lt;/p&gt;

&lt;p&gt;A paymaster improves onboarding by separating transaction authorization from fee payment. It does not remove trust; it moves part of the trust model into sponsorship policy and operations. Treat that policy like a security boundary, and treat its budget like production infrastructure rather than a promotional checkbox.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>architecture</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Uncertain State Between Sending and Mining</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 24 Sep 2026 18:41:15 +0000</pubDate>
      <link>https://dev.to/tradevodata/the-uncertain-state-between-sending-and-mining-3iij</link>
      <guid>https://dev.to/tradevodata/the-uncertain-state-between-sending-and-mining-3iij</guid>
      <description>&lt;p&gt;An automated executor can make a valid decision and still have no safe action to take.&lt;/p&gt;

&lt;p&gt;The awkward interval is between deciding to submit a transaction and learning whether the chain included it. During that interval, permissions can disappear, processes can crash, RPC requests can time out, and retries can create a second execution.&lt;/p&gt;

&lt;p&gt;Treating all of this as a generic &lt;code&gt;pending&lt;/code&gt; status hides the information needed for safe recovery. A useful executor distinguishes at least three states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECIDED_UNSENT -&amp;gt; SENT_UNMINED -&amp;gt; CONFIRMED | REVERTED
       |               |
       v               v
   ABANDONED        UNCERTAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;UNCERTAIN&lt;/code&gt; is not another spelling of pending. It means the executor lacks enough evidence to act automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decided but unsent
&lt;/h2&gt;

&lt;p&gt;In this state, the executor has chosen an action and recorded that decision, but nothing has left the machine.&lt;/p&gt;

&lt;p&gt;This is the easy case operationally because abandoning the action has no chain effect. If the relevant permission disappears before submission, the executor can mark the decision abandoned and stop.&lt;/p&gt;

&lt;p&gt;Before making any external call, persist enough information to explain and reproduce the decision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An immutable intent ID&lt;/li&gt;
&lt;li&gt;The account and chain&lt;/li&gt;
&lt;li&gt;The exact proposed action&lt;/li&gt;
&lt;li&gt;The policy or configuration version used&lt;/li&gt;
&lt;li&gt;The decision timestamp&lt;/li&gt;
&lt;li&gt;The observed input data or a reference to it&lt;/li&gt;
&lt;li&gt;The permission state observed during evaluation&lt;/li&gt;
&lt;li&gt;A terminal reason if the intent is abandoned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not overwrite an abandoned intent with a newer decision. Create another intent. An append-only history makes it possible to distinguish “we chose not to send” from “we lost track of the send.”&lt;/p&gt;

&lt;p&gt;The executor may re-check whether submission is still allowed, but that check does not create an atomic boundary across a database, signer, RPC provider, and chain. If the permission check succeeds and then the process crashes, recovery still needs to know whether a request left the machine.&lt;/p&gt;

&lt;p&gt;That requires a write-ahead submission record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persist before the network call
&lt;/h2&gt;

&lt;p&gt;Once a transaction has been constructed and signed, its raw bytes and transaction hash should be persisted before broadcasting when the signing setup permits that. The hash is derived locally, so the executor does not need an RPC response to identify the transaction.&lt;/p&gt;

&lt;p&gt;A submission record might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"intent_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"intent-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attempt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submission_prepared"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chain_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8453&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nonce"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signed_payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an external signing or relay service does not expose the signed payload, persist an idempotency key and provider request ID before the call. This is weaker because recovery now depends on the provider’s idempotency and lookup semantics. That limitation should be explicit in the design rather than hidden behind a retry loop.&lt;/p&gt;

&lt;p&gt;The key rule is simple: the durable record must advance before the next irreversible external interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sent but unmined
&lt;/h2&gt;

&lt;p&gt;A transaction is sent but unmined when the executor has evidence of submission but no inclusion receipt.&lt;/p&gt;

&lt;p&gt;The executor may observe. It may query multiple RPC providers, inspect the account nonce, rebroadcast the identical signed payload, or ask a relay service about the original request ID. It must not casually construct a fresh transaction.&lt;/p&gt;

&lt;p&gt;Rebroadcasting identical signed bytes is generally different from creating another transaction. The identical payload has the same hash and nonce. A newly built payload may have a new nonce, different fee fields, or altered call data, making a second execution possible.&lt;/p&gt;

&lt;p&gt;This distinction becomes especially important with application-level relayers. Retrying an HTTP request without an idempotency key may cause the relayer to create another transaction even if the first request merely timed out on the client side.&lt;/p&gt;

&lt;p&gt;A timeout says that the caller did not receive an answer. It does not say that the callee did nothing.&lt;/p&gt;

&lt;p&gt;While the transaction remains observable by hash, the executor can keep the state as &lt;code&gt;SENT_UNMINED&lt;/code&gt;. It should record each observation without rewriting the original submission facts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uncertain must be a first-class status
&lt;/h2&gt;

&lt;p&gt;Some failures cannot be settled automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The process crashed during a remote signing or relay call.&lt;/li&gt;
&lt;li&gt;The provider accepted a request but supplied no durable identifier.&lt;/li&gt;
&lt;li&gt;The account nonce advanced, but the executor cannot identify which transaction used it.&lt;/li&gt;
&lt;li&gt;Different providers disagree about whether a transaction exists.&lt;/li&gt;
&lt;li&gt;A replacement transaction may have been created outside the executor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Marking these cases failed is unsafe because a retry may duplicate the action. Marking them successful is dishonest because inclusion has not been established.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UNCERTAIN&lt;/code&gt; should therefore be terminal for automatic execution. The system may continue collecting evidence, but it must not silently move the record back into the normal retry path. A user or operator can review the evidence and make a new, explicit decision.&lt;/p&gt;

&lt;p&gt;The executor also should not invent a compensating transaction. An opposite action is not an undo operation. Prices may have changed, fees have already been spent, and the relevant permission may no longer exist. Compensation is a separate intent requiring separate authorization.&lt;/p&gt;

&lt;p&gt;This is less convenient than automatic recovery. It is also more accurate. Distributed systems sometimes lose the evidence needed to distinguish “did not happen” from “happened, but the acknowledgement was lost.” A status model should preserve that fact instead of guessing.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://app.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=the-uncertain-state-between-sending-and-mining" rel="noopener noreferrer"&gt;Tradevo&lt;/a&gt;, software where users describe, test, paper-trade, publish, subscribe to, and automatically run crypto strategies from wallets they control.&lt;/p&gt;

&lt;p&gt;The honest executor is not the one that always keeps moving. It is the one that knows when movement would be an unsupported assumption.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>architecture</category>
      <category>ethereum</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What Regime Splits Actually Tell You (and Where the Math Runs Out)</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 17 Sep 2026 18:34:45 +0000</pubDate>
      <link>https://dev.to/tradevodata/what-regime-splits-actually-tell-you-and-where-the-math-runs-out-1p41</link>
      <guid>https://dev.to/tradevodata/what-regime-splits-actually-tell-you-and-where-the-math-runs-out-1p41</guid>
      <description>&lt;p&gt;Regime splitting is one of the first "advanced" moves people make once they've built a backtest they trust. You have a couple hundred closed trades, an equity curve that looks fine in aggregate, and a nagging question: does this actually work in all markets, or did it just get lucky during a two-year trend? So you split the history — trending vs choppy, high volatility vs low, before some macro event vs after — and look at the four (or eight) resulting curves.&lt;/p&gt;

&lt;p&gt;This is a genuinely good instinct. It's also one of the easiest ways to produce false confidence, for a reason that has nothing to do with your strategy and everything to do with arithmetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sample size problem, concretely
&lt;/h2&gt;

&lt;p&gt;Say you have 200 closed trades. You define a regime filter with two states — trending or choppy — using something like ADX or a moving-average slope threshold. If the market cooperated and split roughly evenly, you now have two buckets of 100. Split again by volatility (high/low), and you have four buckets of ~50. Add a third cut — say, before/after a rate-hike cycle — and you're down to sub-25 per bucket, sometimes much less, because market regimes don't obligingly divide your history into equal quarters. A trending, high-vol regime might have supplied 60 trades and a choppy, low-vol one might have supplied 8.&lt;/p&gt;

&lt;p&gt;Fifty trades is not enough to say much about a strategy's edge, and eight is not enough to say anything. Here's the shape of the problem using a rough binomial confidence interval on win rate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n = 200, observed win rate = 55%  -&amp;gt; 95% CI ≈ [48%, 62%]
n = 50,  observed win rate = 55%  -&amp;gt; 95% CI ≈ [41%, 69%]
n = 15,  observed win rate = 55%  -&amp;gt; 95% CI ≈ [30%, 78%]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At n=200 you can at least argue the edge is probably real if the interval stays above 50%. At n=50 the interval already swallows a coin flip. At n=15 — a completely normal size for a "regime bucket" carved out of a modest history — the interval tells you almost nothing. You can't distinguish a strategy with a genuine 55% edge in that regime from a fair coin that happened to run hot for a couple weeks.&lt;/p&gt;

&lt;p&gt;This is the trap: the split itself feels like rigor. You went looking for a weakness, you found a bucket where performance looked worse, and you either panic and discard a strategy based on 12 trades, or rationalize the bad bucket away ("that regime is rare, doesn't matter") using the same small sample that should make you distrust the conclusion in either direction. Both moves are overconfident. The honest answer for a 12-trade bucket is usually "I don't know," which is an unsatisfying thing to put in a report, so people tend not to write it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where regime splits still earn their keep
&lt;/h2&gt;

&lt;p&gt;None of this means regime analysis is worthless. It's good for a narrower job than people use it for: sanity-checking that performance isn't concentrated in a single, unrepeatable environment — discovering, say, that 80% of your profit came from March 2020. That's a real, useful finding even from a small bucket, because you're not estimating a rate, you're checking for concentration. "Most of the P&amp;amp;L came from one regime" is a safer claim than "the strategy has a 61% win rate in choppy markets," because the first is close to arithmetic on the data you already have, and the second is an inference about data you don't.&lt;/p&gt;

&lt;p&gt;It also helps a lot to define the regime boundaries before looking at performance, not after. If you tune the ADX threshold until the choppy bucket looks bad (or good), you've turned a diagnostic into a curve-fit, and the regime split now carries the same overfitting risk as the strategy it was supposed to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the alternatives do differently
&lt;/h2&gt;

&lt;p&gt;Walk-forward testing sidesteps part of this by re-optimizing on a rolling window and testing out-of-sample on the next chunk, which at least keeps you honest about lookahead. It doesn't fix small-sample problems inside any single fold, though, and it's sensitive to window-length choices that are themselves a quiet form of overfitting. Purged k-fold cross-validation, borrowed from the ML side of quant research, handles leakage between adjacent samples better than a naive split — at the cost of being more work to implement correctly, and it still assumes your trades are close to independent, which they often aren't. Monte Carlo resampling of trade order or returns answers a different question again: whether your equity curve's shape depended on the sequence trades happened to occur in, rather than whether a regime subset has a real edge. It's worth running alongside a regime split, not instead of one.&lt;/p&gt;

&lt;p&gt;None of these make the sample-size problem disappear. They're different lenses on the same underlying scarcity: you only have as many independent trades as you have, and slicing them doesn't create more evidence — it redistributes the evidence you already had into smaller, noisier piles.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://verify.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=regime-splits-and-what-they-cannot-tell-you" rel="noopener noreferrer"&gt;Tradevo Verify&lt;/a&gt;, which runs this kind of stress-testing — including path-dependency and regime checks — against a closed-trade export and reports how much weight the evidence can actually carry, rather than tuning a bucket until it looks convincing.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical rule of thumb
&lt;/h2&gt;

&lt;p&gt;If a regime bucket has fewer than roughly 30 trades, treat any rate computed from it — win rate, average R, Sharpe — as a data point, not a conclusion, and report the count next to the number every time. And if you catch yourself narrowing a regime definition after seeing what the split produced, stop. You've left testing and entered storytelling, and the two look identical in a report until the next 50 trades arrive and disagree with you.&lt;/p&gt;

</description>
      <category>statistics</category>
      <category>trading</category>
      <category>datascience</category>
      <category>python</category>
    </item>
    <item>
      <title>Return Concentration: How to Tell If Your Backtest Has Ten Trades or One</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 10 Sep 2026 17:53:42 +0000</pubDate>
      <link>https://dev.to/tradevodata/return-concentration-how-to-tell-if-your-backtest-has-ten-trades-or-one-48a8</link>
      <guid>https://dev.to/tradevodata/return-concentration-how-to-tell-if-your-backtest-has-ten-trades-or-one-48a8</guid>
      <description>&lt;p&gt;You've got a trade log. Two hundred closed trades, a decent win rate, a Sharpe ratio that looks respectable. Before you trust any of it, ask one question: how many of those trades actually matter?&lt;/p&gt;

&lt;p&gt;This is the return concentration problem, and it's one of the easier things to check in a backtest and one of the most commonly skipped. The idea is simple. Take your closed trades, sort them by profit, and ask what happens to the equity curve if you remove the best few. If removing five trades out of two hundred turns a profitable strategy into a flat or losing one, your two-hundred-trade backtest is actually a five-trade backtest wearing a costume.&lt;/p&gt;

&lt;h3&gt;
  
  
  The test itself
&lt;/h3&gt;

&lt;p&gt;You don't need anything sophisticated to run this. Sort trades descending by PnL, then walk down the list removing the top 1, top 3, top 5, top 10, and recompute total return each time.&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="n"&gt;trades_sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pnl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pnl&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
 &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trades_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
 &lt;span class="n"&gt;new_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pnl&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;remaining&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;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_total&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;new_total&lt;/code&gt; goes negative or near-zero after dropping the top 5, you're looking at a strategy where almost all the reported edge lives in a handful of trades. That's not automatically damning — more on that below — but it changes what question you should be asking next.&lt;/p&gt;

&lt;p&gt;A useful companion number: what fraction of total profit comes from the top 10% of winning trades? For a strategy trading a mean-reverting, high-frequency pattern, healthy concentration might mean the top 10% of trades account for 20-30% of profit — the rest of the distribution is doing real work. For a trend-following system that holds through long drawdowns waiting for a few large moves, it's normal for the top 10% of trades to account for 60-80% of profit. Both can be fine. The number alone doesn't tell you which case you're in.&lt;/p&gt;

&lt;h3&gt;
  
  
  The trap: assuming concentration means broken
&lt;/h3&gt;

&lt;p&gt;Here's where people get it wrong in both directions. The naive read is "concentrated returns are fragile, spread returns are robust." That's not true. Trend-following, breakout, and long-volatility strategies are &lt;em&gt;supposed&lt;/em&gt; to look concentrated — they're structurally a long series of small losses funding a few large wins. If you ran the drop-top-N test on a well-known trend system's historical trade list, you'd likely find the same lottery-ticket-looking distribution, because that's the mechanism, not a flaw in it.&lt;/p&gt;

&lt;p&gt;The actual question isn't "is it concentrated," it's "is the concentration structural or accidental." Structural concentration comes from a repeatable trigger: your strategy has a rule that lets winners run and cuts losers early, and the big trades share a common cause — a volatility regime, a trend-continuation pattern, a specific setup that recurs. Accidental concentration is a few trades that happened to catch a one-off event (a short squeeze, a flash crash reversal, an earnings gap you didn't intend to hold through) that has no mechanism tying it to the rest of your trades. Same table of numbers, very different implications.&lt;/p&gt;

&lt;p&gt;A few ways to tell them apart without much tooling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cluster the top trades by date and instrument.&lt;/strong&gt; If your five biggest winners all fired in the same three-week window, or all on the same underlying, that's a concentration-in-time problem — you got one regime right, not an edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the exit reason.&lt;/strong&gt; If the big winners exited on the same rule as the rest of your trades (say, a trailing stop or a signal reversal), that's structural. If they exited because you happened to close the account before a reversal, or the backtest's fill logic let you ride a gap that wouldn't be tradeable live, that's a data artifact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Look at whether the same setup that produced the big winners also produced most of your losers.&lt;/strong&gt; A strategy where the big-win trades and the many-small-loss trades share the identical entry logic is doing something coherent. If the big winners are a different distribution of setups than everything else, you may be looking at noise that got included in the same backtest by coincidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is definitive from a spreadsheet alone. A trade log doesn't tell you about slippage on the entries that mattered, whether the position size on the big winners was realistic to fill, or whether the same setup will recur going forward — that requires separate checks on execution and forward behavior, not just the PnL column.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://verify.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=return-concentration-in-a-trade-list" rel="noopener noreferrer"&gt;Tradevo Verify&lt;/a&gt;, which runs this kind of concentration check (along with execution, path-dependency, and regime tests) against a closed-trade CSV and produces a versioned record of the result — it doesn't tell you whether the strategy is good, and a concentrated result often comes back flagged as fragile evidence rather than a pass.&lt;/p&gt;

&lt;p&gt;Either way, before you trust a backtest's headline number, spend the ten minutes sorting the trade list and dropping the top few. It won't tell you if your edge is real. It will tell you how many trades you're actually betting the conclusion on.&lt;/p&gt;

</description>
      <category>statistics</category>
      <category>datascience</category>
      <category>python</category>
      <category>trading</category>
    </item>
    <item>
      <title>Allowlists Are Not Spending Caps: Two Different Security Properties, Often Confused</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 03 Sep 2026 18:05:38 +0000</pubDate>
      <link>https://dev.to/tradevodata/allowlists-are-not-spending-caps-two-different-security-properties-often-confused-lk0</link>
      <guid>https://dev.to/tradevodata/allowlists-are-not-spending-caps-two-different-security-properties-often-confused-lk0</guid>
      <description>&lt;p&gt;If you've ever configured a delegated signer, a session key, or a bot's wallet permissions, you've probably reached for two knobs: an allowlist of assets it's allowed to touch, and a cap on how much value it can move. It's tempting to treat these as two settings on the same dial — both feel like "limits." They're not. They prevent different failure modes, and conflating them is how permission systems end up weaker than their configuration screen suggests.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a spending cap actually prevents
&lt;/h3&gt;

&lt;p&gt;A spending cap bounds &lt;em&gt;magnitude&lt;/em&gt;. If a delegated key can move at most 500 USDC per day, then no matter what it does with that authority — swap, stake, send to an arbitrary address — the maximum damage from a compromised or misbehaving key is 500 USDC. It says nothing about &lt;em&gt;what&lt;/em&gt; the money touches. A cap enforced correctly stops a runaway loop or a fully compromised signer from draining a wallet in one shot. It does not stop that signer from moving the capped amount into something worthless.&lt;/p&gt;

&lt;h3&gt;
  
  
  What an allowlist actually prevents
&lt;/h3&gt;

&lt;p&gt;An allowlist bounds &lt;em&gt;scope&lt;/em&gt;. If a signer can only interact with three named token contracts, then even with no spending cap at all, it categorically cannot send funds to an arbitrary address or interact with an unknown contract. This is a different property: it's not about how much moves, it's about which counterparties are even reachable.&lt;/p&gt;

&lt;p&gt;The two compose well in theory — cap the blast radius, and separately narrow the attack surface — but each has failure modes the other doesn't cover, and "we have both" is not the same claim as "we're safe."&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure mode 1: the allowlisted token isn't the code you allowlisted
&lt;/h3&gt;

&lt;p&gt;An allowlist entry is usually just an address. But an address is not fixed behavior. If the token contract sits behind a proxy — which a large fraction of tokens do, for legitimate upgrade reasons — the logic executed when your signer calls &lt;code&gt;transfer&lt;/code&gt; or &lt;code&gt;approve&lt;/code&gt; can change after you added the address to your list. You audited version 1. The proxy now points at version 2. Your allowlist still matches, because the allowlist only ever checked the address, and the address never moved. This isn't a hypothetical: it's the entire reason "audit the contract, then allowlist the address" is a weaker guarantee than people assume — the audit has a timestamp, the allowlist doesn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure mode 2: the router that accepts arbitrary calldata
&lt;/h3&gt;

&lt;p&gt;A lot of "swap-only" permission schemes are enforced by pointing the signer at a router contract instead of individual token contracts, on the theory that a router only does swaps. But look at what the router's function signature actually accepts. Plenty of router and aggregator interfaces expose something shaped like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function execute(address target, bytes calldata data) external;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's not a swap function. That's a generic call forwarder that happens to usually be used for swaps. If your policy allowlists the router's address and stops there, you've allowlisted "anything the router is willing to forward," which — depending on the router's own internal checks — can include calls into arbitrary target contracts with arbitrary calldata. The allowlist has to be checked against the &lt;em&gt;target and selector inside the calldata&lt;/em&gt;, not just the outermost contract address, or it isn't restricting anything meaningful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure mode 3: "it can only swap" is a claim about a function signature you haven't read
&lt;/h3&gt;

&lt;p&gt;This is the general version of failure mode 2. "Swap-only" is not a security property until you know exactly what parameters the swap function takes. A swap function that hard-codes the output token and the pool is a narrow, auditable action. A swap function that takes a &lt;code&gt;path: address[]&lt;/code&gt; or a &lt;code&gt;router: address&lt;/code&gt; parameter chosen by the caller is, functionally, "call arbitrary code with a legitimate-looking wrapper around it." The English sentence "it can only swap" is doing no work — the actual boundary is whatever the function's parameter types and the policy engine's validation of those parameters allow. Read the signature, not the label.&lt;/p&gt;

&lt;h3&gt;
  
  
  A practical checklist
&lt;/h3&gt;

&lt;p&gt;When someone tells you an authorization is scoped, four questions separate a real boundary from a documentation claim:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allowlisted at what granularity — contract address, function selector, or full calldata shape?&lt;/li&gt;
&lt;li&gt;Capped on what — per-transaction, cumulative, or notional value at execution time (which can be gamed by slippage)?&lt;/li&gt;
&lt;li&gt;Enforced &lt;em&gt;where&lt;/em&gt; — checked by a policy engine before signing, checked by the contract itself on-chain, or just described in a UI with no enforcement at all?&lt;/li&gt;
&lt;li&gt;What happens when a check fails — does the transaction revert, or does the check silently pass because the policy engine and the contract disagree about what "the same asset" means?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this makes allowlists useless. It makes them one layer, with a specific and narrow claim, that has to be paired with selector-level restriction and cap enforcement at the layer that actually executes the call — and it means the honest description of any "restricted signer" is a list of exactly what's checked and where, not the word "scoped" on its own.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://app.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=allowlists-as-a-security-boundary" rel="noopener noreferrer"&gt;Tradevo&lt;/a&gt;, which runs delegated strategy execution through a scoped session signer with a token allowlist, per-subscription allocation cap, and slippage limits — enforced by the signing provider's policy and by our own executor, not by the chain, which is exactly why I spent this piece on where enforcement actually lives instead of what the label says.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>security</category>
      <category>ethereum</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Fill Model Is Where Backtests Quietly Cheat</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Thu, 03 Sep 2026 12:54:19 +0000</pubDate>
      <link>https://dev.to/tradevodata/the-fill-model-is-where-backtests-quietly-cheat-4mhe</link>
      <guid>https://dev.to/tradevodata/the-fill-model-is-where-backtests-quietly-cheat-4mhe</guid>
      <description>&lt;p&gt;Every backtest has to answer a boring question: when the strategy says "buy," what price does it actually get? Most backtesting frameworks answer this question badly by default, and the badness is almost always in the strategy's favor.&lt;/p&gt;

&lt;p&gt;Here are the four assumptions that do the most damage, roughly in order of how often they show up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mid-price fills
&lt;/h3&gt;

&lt;p&gt;If your backtest fills orders at the midpoint of the bid-ask spread, you are assuming you trade for free. You don't. A market order pays at least half the spread to cross it; a marketable limit order pays something close to that too, once you're honest about how often it actually gets hit versus sitting unfilled while the market moves away. Mid-price fills are the single most common way a backtest manufactures edge that doesn't exist, because the effect compounds with trade frequency — a strategy that trades often looks great on mid-price fills and mediocre-to-negative once it pays the spread on every round trip.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero slippage
&lt;/h3&gt;

&lt;p&gt;Slippage is the gap between the price your signal fired at and the price your order actually executed at, and it's not just a queuing artifact — it's partly information. If your strategy is buying because something changed, other participants are reacting to the same thing, and the price you wanted is often gone by the time your order reaches the book. A backtest with zero slippage is quietly assuming the market waits for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unlimited size at the touch
&lt;/h3&gt;

&lt;p&gt;Backtests routinely assume you can execute your full position size at the best bid or ask, no matter how large the order is relative to the visible size there. In practice, a large order walks the book, and the average fill price is worse than the touch price by an amount that depends on how thin the book is. This one is invisible until you try to size up, which is exactly when a strategy that looked fine in testing starts bleeding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commissions omitted or averaged
&lt;/h3&gt;

&lt;p&gt;Commissions and fees are usually small per trade and therefore easy to skip or fold into a rough average. But a strategy with thin per-trade edge and high turnover can have its entire expectancy eaten by costs that were treated as a rounding error.&lt;/p&gt;

&lt;h3&gt;
  
  
  A worked example
&lt;/h3&gt;

&lt;p&gt;Take a mean-reversion strategy trading a $30 stock with a 2-cent spread: average win 18¢, average loss 14¢, win rate 55%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expectancy (per share, before costs)
= 0.55 × 18¢ − 0.45 × 14¢
= 9.9¢ − 6.3¢
= 3.6¢
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That 3.6¢ looks like a real edge. But if the backtest filled at the mid, it never paid the spread it would pay in live trading. A more honest fill — buying near the ask, selling near the bid — costs roughly the full spread on the round trip, here about 2¢. Subtract that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3.6¢ − 2¢ = 1.6¢
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The edge didn't disappear, but it lost more than half its value to an assumption that never showed up as a line item anywhere in the report. Add a per-share commission and a little realistic slippage on top, and it's easy to see how a strategy with a "good" backtest turns out to be trading the spread, not an actual signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you can actually check from a trade list
&lt;/h3&gt;

&lt;p&gt;If all you have is a CSV of closed trades — entry, exit, size, timestamps — there's a limit to how much of this you can diagnose. You can check sensitivity: rerun expectancy with a range of assumed slippage and spread costs and see how much of the edge survives. You can check whether wins are concentrated in trades with unusually favorable prices relative to the surrounding bars, which is a proxy for lookahead or mid-price fills. You can check whether performance depends on a handful of trades — if removing the best 5% of trades erases the edge, that's worth knowing regardless of the fill model.&lt;/p&gt;

&lt;p&gt;What you generally can't check from a trade list alone is anything that requires order book state: actual queue position, actual available size at the touch at the moment of the signal, actual latency between signal and order arrival. Those require tick-level or order-book data and a simulator that models the exchange mechanics, not just entry and exit prices. If someone tells you they can fully validate execution realism from a CSV of closed trades, they're skipping something — the honest version of this check is partial, and it should say so.&lt;/p&gt;

&lt;h3&gt;
  
  
  The practical test
&lt;/h3&gt;

&lt;p&gt;The cheap version of all this: take your reported average win and average loss, subtract a full spread crossing on both entry and exit, and see if the strategy still has positive expectancy. If it doesn't survive that adjustment, the edge was largely the spread it never paid, and no amount of additional testing further downstream is going to rescue it. If it does survive, you've at least confirmed the edge isn't purely a fill-model artifact — which is a different question from whether it will hold up in other ways, but it's the first one worth asking.&lt;/p&gt;

&lt;p&gt;Disclosure: I build &lt;a href="https://verify.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=execution-assumptions-that-flatter-a-backtest" rel="noopener noreferrer"&gt;Tradevo Verify&lt;/a&gt;, which takes a closed-trade export and runs it through this kind of stress-testing, among other checks, and produces a versioned evidence record rather than a verdict on whether the strategy is good.&lt;/p&gt;

</description>
      <category>python</category>
      <category>trading</category>
      <category>datascience</category>
      <category>statistics</category>
    </item>
    <item>
      <title>What 'Revocable' Actually Means at the Contract Level</title>
      <dc:creator>Christian Pichichero</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:05:03 +0000</pubDate>
      <link>https://dev.to/tradevodata/what-revocable-actually-means-at-the-contract-level-5201</link>
      <guid>https://dev.to/tradevodata/what-revocable-actually-means-at-the-contract-level-5201</guid>
      <description>&lt;p&gt;If you've ever called &lt;code&gt;approve()&lt;/code&gt; on an ERC-20 token and then moved on with your life, you've already brushed up against the thing this post is about: an approval is not a setting inside some app, it's a row in a smart contract's storage, and every system built on top of it is only as honest as its last read of that row.&lt;/p&gt;

&lt;p&gt;Most token approvals work the same way. A user signs a transaction granting a spender contract permission to move up to some amount of a token from their wallet. The ERC-20 standard stores this as &lt;code&gt;allowance[owner][spender]&lt;/code&gt;. Any contract that wants to move the user's tokens checks that number before doing so, and the check happens inside the same transaction that tries to move funds — so the contract-level enforcement is real. The token contract itself will not let a transfer through if the allowance is insufficient.&lt;/p&gt;

&lt;p&gt;Revocation is just another write to that same slot, usually setting it to zero. It's a normal transaction. It has to be signed, broadcast, and mined like any other. That's the part people gloss over: revoking is not a UI toggle, it's a transaction with all the same properties as the transaction that created the approval in the first place — it sits in a mempool, it can be delayed by network congestion, and it isn't final until it's in a confirmed block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the gap shows up
&lt;/h2&gt;

&lt;p&gt;Say you're building a service that executes on a user's behalf using a stored approval — a trading bot, a subscription payment puller, anything with an off-chain component that decides &lt;em&gt;when&lt;/em&gt; to spend and an on-chain component that actually moves the funds. The natural design is to keep a local record: "user X has approved up to Y, active." That record is convenient. It's also just an opinion your own server holds about the world, and it can be wrong in both directions.&lt;/p&gt;

&lt;p&gt;It can be wrong stale-permissive: the user revokes, your service hasn't seen it yet, and if the on-chain check before spending is missing or weak, you build and sign a transaction anyway. Depending on how you structured the check, this either fails harmlessly at the token contract (wasted gas, a failed tx, an alert) or, if you did something sloppier — like checking your database instead of the chain — you send a transaction that the chain itself will still reject, because the allowance really is zero now. The token contract is the backstop here, which is good, but you don't want your system's normal path to depend on that backstop catching your own mistake.&lt;/p&gt;

&lt;p&gt;It can also be wrong stale-restrictive: your database says revoked, but the revoke transaction is still sitting unconfirmed, and the user expects it to be in effect immediately because that's what the button said. This one is more of a UX problem than a safety problem, but it's the same root cause — a record and the chain disagreeing about what's true right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the check has to happen at the moment of execution
&lt;/h2&gt;

&lt;p&gt;The fix sounds almost too obvious to write down: before doing anything that spends a user's tokens, read the allowance from the chain, right then, not from whatever your database cached the last time you looked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;attemptExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requiredAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;onChainAllowance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readAllowance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// the ground truth, right now&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onChainAllowance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;requiredAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;skipAndLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allowance insufficient at execution time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;sendExecutionTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requiredAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This has real costs. Every execution now needs an RPC round-trip before it can act, which adds latency and adds load on whatever node provider you're using. If you're executing for many users on a schedule, that's a lot of extra reads for what is, most of the time, a value that hasn't changed since the last check. There's also a subtler question buried in "read from the chain": read at what block? RPC providers don't all agree on the very latest block during a reorg, and a read that lands on a block that later gets replaced is its own small version of the same trust problem, one layer down. Treating the freshest confirmed state as authoritative, and re-checking rather than caching, is the mechanism — it doesn't make the read instantaneous or immune to provider disagreement, it just means you're asking the right question at the right time instead of trusting an answer that might be minutes or days old.&lt;/p&gt;

&lt;h2&gt;
  
  
  What breaks mid-cycle
&lt;/h2&gt;

&lt;p&gt;The uncomfortable case is a multi-step action — say a swap that routes through two pools, or a strategy that does three on-chain calls in sequence to complete one logical action. If a user revokes between step one and step two, step one already happened. You can't undo it. The system has to be built so a partial completion is a safe, loggable state rather than an unrecoverable one — which mostly means designing each step so it's fine to stop after it, not chaining steps that only make sense together and hoping revocation never lands in the middle. That's a design constraint, not something the revoke mechanism itself solves for you.&lt;/p&gt;

&lt;p&gt;None of this makes revocation less real. The user genuinely can remove the authorization, and once that transaction confirms, the contract genuinely will not let the spender move their tokens anymore — that part is enforced by the token contract itself, not by anyone's goodwill. What it means is narrower and more mechanical than "revocable" sounds: it's a state change on a specific chain, subject to that chain's confirmation times, and any executor sitting on top of it is only trustworthy if it treats its own records as a guess and the chain as the check.&lt;/p&gt;

&lt;p&gt;Disclosure: I build Tradevo, which runs automated strategies on Base from wallets whose keys the user holds. More at &lt;a href="https://app.tradevo.co/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=syndicate&amp;amp;utm_content=what-revoking-actually-does" rel="noopener noreferrer"&gt;app.tradevo.co&lt;/a&gt;. The trade-offs above are ones I have had to think through rather than ones I am claiming to have solved.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
