{"record":{"id":"040c78c18555f70f","repo":"HKUDS/Vibe-Trading","slug":"no-base-self-quote-currency-rate-on-or-before","errorCode":null,"errorMessage":"no {base}/{self.quote_currency} rate on or before {day}{bound}","messagePattern":"no (.+?)/(.+?) rate on or before (.+?)(.+?)","errorType":"exception","errorClass":"MissingExchangeRateError","httpStatus":null,"severity":"error","filePath":"agent/src/entities/cashflow.py","lineNumber":677,"sourceCode":"                \"never applied silently\"\n            )\n\n        candidates = [\n            entry\n            for (entry_base, entry_date), entry in self.rates.items()\n            if entry_base == base and entry_date <= day\n        ]\n        if max_staleness_days is not None:\n            candidates = [\n                entry\n                for entry in candidates\n                if (day - entry.date).days <= max_staleness_days\n            ]\n        if not candidates:\n            bound = (\n                f\" within {max_staleness_days} days\" if max_staleness_days is not None else \"\"\n            )\n            raise MissingExchangeRateError(\n                f\"no {base}/{self.quote_currency} rate on or before {day}{bound}\"\n            )\n        best = max(candidates, key=lambda entry: entry.date)\n        return best, True\n\n\ndef translate_cashflows(\n    flows: CashFlowSeries | Iterable[CashFlow],\n    rate_table: FxRateTable,\n    *,\n    allow_stale_rates: bool = False,\n    max_staleness_days: int | None = None,\n) -> CashFlowSeries:\n    \"\"\"Convert flows in one or more currencies into ``rate_table``'s currency.\n\n    Each flow is converted using the FX rate quoted on **that flow's own\n    settlement date** -- never a single series-level or period-end rate.\n    Applying one end-of-period rate to every flow is the most common","sourceCodeStart":659,"sourceCodeEnd":695,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/entities/cashflow.py#L659-L695","documentation":"Even with allow_stale=True, get_rate needs at least one quote for the base currency on or before the requested day, and within max_staleness_days if provided. No candidate exists -> MissingExchangeRateError, with the staleness bound included in the message when relevant.","triggerScenarios":"get_rate('EUR', date(2024,1,31), allow_stale=True) when the table's earliest EUR quote is 2024-02-05; or a candidate exists but is 10 days old with max_staleness_days=7.","commonSituations":"Rates feed starting later than the flow history (backfilling old periods); a thinly quoted currency where gaps exceed your staleness tolerance; max_staleness_days set too tight for weekly-quoted currencies.","solutions":["Source earlier rates covering the requested period and rebuild the table","Increase max_staleness_days to a tolerance appropriate for the currency's quote frequency","Remove or relax the bound (pass None) if some staleness is acceptable for your use case"],"exampleFix":"# before\nrate, stale = table.get_rate('EUR', d, allow_stale=True, max_staleness_days=3)\n# after\nrate, stale = table.get_rate('EUR', d, allow_stale=True, max_staleness_days=10)","handlingStrategy":"fallback","validationCode":"earliest = min(e.date for k, e in table.rates.items() if k[0] == base)\nif day < earliest:\n    raise ValueError('need earlier FX history')","typeGuard":null,"tryCatchPattern":"from agent.src.entities.cashflow import MissingExchangeRateError\ntry:\n    rate, stale = table.get_rate(base, day, allow_stale=True, max_staleness_days=N)\nexcept MissingExchangeRateError:\n    rate = fetch_rate_from_api(base, day)  # backfill then rebuild table","preventionTips":["Backfill FX history to before your earliest flow","Size max_staleness_days to the currency's quote frequency","Alert when requested dates approach the table's earliest quote"],"tags":["python","fx-rate","missing-data","staleness"],"backgroundTag":"missing-fx-rate","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}