fix: revalidate the vault price history cache on live universe loads - #1583
Open
miohtama wants to merge 1 commit into
Open
fix: revalidate the vault price history cache on live universe loads#1583miohtama wants to merge 1 commit into
miohtama wants to merge 1 commit into
Conversation
A live executor failed to start with DataTooOld while holding a cached vault-price-history.parquet whose newest rows were three days old, at a moment when the remote file contained rows from two hours earlier. The transport trusts a cached parquet for 24 hours with no remote check. Inside that window no HEAD request is made at all, so a parquet published after the last check is not picked up and nothing reports that the local copy has fallen behind. The window is also measured from the last validation rather than the last download, because a HEAD check that finds the remote unchanged bumps the cached file's mtime. Pass revalidate=execution_context.mode.is_live_trading() from load_partial_data() so live universe loads always validate against the remote. An unchanged remote still short-circuits the download, so this costs one HEAD request rather than a ~200 MB re-download. Backtests keep the plain cached path. Requires tradingstrategy-ai/trading-strategy#243. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A live
hyper-aiexecutor failed to start withDataTooOld:All 327 Hyperliquid vaults reported a newest source timestamp of
2026-07-24 14:1x,three days before the 12:10 crash. The data itself was fine — checking the source
directly,
cleaned-vault-prices-1h.parquethadlast-modified: 2026-07-27 09:53:55andcarried rows up to
09:32that morning, including for the exact vaults in the log(HLP
0xdfc2…f303→09:30:06, Citadel0xda51…c167→09:30:07). The executor wasreading a stale local cache.
fetch_vault_price_history()trusts a cached parquet for 24 hours with no remote check:check is not picked up, and nothing reports that the local copy has fallen behind.
Reproduced in isolation: cache mtime 23h old, remote newer with a different ETag and
size → no HEAD, no download, stale file served.
HEAD check that finds the remote unchanged calls
os.utime(path, None).Scope note: a 24h window bounds staleness at roughly 24h plus pipeline lag, so this alone
does not account for a three-day gap. This is hardening of a real defect, not a confirmed
root cause. Closing out the remaining gap needs the executor's own
Vault price history cache hit/expiredandVault history freshness summarylines fromthe incident, which were not in the captured output. A second candidate is still open:
_is_matching_http_cache_metadata()falls back toremote_last_modified <= local_mtimeeven when sidecar metadata disagrees, which is unsound once mtime has been bumped —
currently masked by the
Content-Lengthguard, and left for a follow-up.Lessons learnt
bookkeeping. It stops reading as "age of the data" and starts meaning "time since we
last looked", which is not what a bare
timedelta(hours=24)conveys.The blind window emitted no log line; the staleness surfaced three layers downstream as
a
DataTooOldcrash behind a 300-line table dump, and a SentryMessage too longlogging error on the way.
cached path; a live executor must not guess. Same function, opposite requirements.
request and one parquet read, and ruled out the entire data-pipeline branch of the
investigation immediately. Worth doing before reading any cache code.
Summary
load_partial_data()passesrevalidate=execution_context.mode.is_live_trading()toclient.transport.fetch_vault_price_history(), and to theclient.fetch_vault_price_history()fallback branch, so live universe loads always validate the cache against the remote.
Backtests keep the plain cached path.
deps/trading-strategysubmodule to pick up therevalidateflag.test_load_partial_data_revalidates_vault_history_cache_only_when_live,captures the flag the transport receives and asserts
Trueforreal_tradingandFalseforbacktesting.test_hyper_ai_stale_data_indicators.py,including dropping a stale
cache.py:625line reference.test_vault_live_cutoff.pyaccept the new keyword.Verified against the live server: with a 25-minute-old cache — deep inside the window
that produced the incident — the HEAD now runs and correctly short-circuits the 197 MB
download because the remote is unchanged.
Merge order: tradingstrategy-ai/trading-strategy#243 must land first; the submodule
pointer here should then be repointed at the merged commit rather than the branch commit.
🤖 Generated with Claude Code