diff --git a/src/context/aggregator.py b/src/context/aggregator.py index fb8a6d8..b93ce81 100644 --- a/src/context/aggregator.py +++ b/src/context/aggregator.py @@ -230,21 +230,44 @@ class ContextAggregator: ) def run_all_aggregations(self) -> None: - """Run all aggregations from L7 to L1 (bottom-up).""" + """Run all aggregations from L7 to L1 (bottom-up). + + All timeframes are derived from the latest trade timestamp so that + past data re-aggregation produces consistent results across layers. + """ + cursor = self.conn.execute("SELECT MAX(timestamp) FROM trades") + row = cursor.fetchone() + if not row or row[0] is None: + return + + ts_raw = row[0] + if ts_raw.endswith("Z"): + ts_raw = ts_raw.replace("Z", "+00:00") + latest_ts = datetime.fromisoformat(ts_raw) + trade_date = latest_ts.date() + date_str = trade_date.isoformat() + + iso_year, iso_week, _ = trade_date.isocalendar() + week_str = f"{iso_year}-W{iso_week:02d}" + month_str = f"{trade_date.year}-{trade_date.month:02d}" + quarter = (trade_date.month - 1) // 3 + 1 + quarter_str = f"{trade_date.year}-Q{quarter}" + year_str = str(trade_date.year) + # L7 (trades) → L6 (daily) - self.aggregate_daily_from_trades() + self.aggregate_daily_from_trades(date_str) # L6 (daily) → L5 (weekly) - self.aggregate_weekly_from_daily() + self.aggregate_weekly_from_daily(week_str) # L5 (weekly) → L4 (monthly) - self.aggregate_monthly_from_weekly() + self.aggregate_monthly_from_weekly(month_str) # L4 (monthly) → L3 (quarterly) - self.aggregate_quarterly_from_monthly() + self.aggregate_quarterly_from_monthly(quarter_str) # L3 (quarterly) → L2 (annual) - self.aggregate_annual_from_quarterly() + self.aggregate_annual_from_quarterly(year_str) # L2 (annual) → L1 (legacy) self.aggregate_legacy_from_annual() diff --git a/tests/test_context.py b/tests/test_context.py index c56415d..145ef8c 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -300,9 +300,17 @@ class TestContextAggregator: # Verify data exists in each layer store = aggregator.store assert store.get_context(ContextLayer.L6_DAILY, date, "total_pnl") == 1000.0 - current_week = datetime.now(UTC).strftime("%Y-W%V") - assert store.get_context(ContextLayer.L5_WEEKLY, current_week, "weekly_pnl") is not None - # Further layers depend on time alignment, just verify no crashes + from datetime import date as date_cls + trade_date = date_cls.fromisoformat(date) + iso_year, iso_week, _ = trade_date.isocalendar() + trade_week = f"{iso_year}-W{iso_week:02d}" + assert store.get_context(ContextLayer.L5_WEEKLY, trade_week, "weekly_pnl") is not None + trade_month = f"{trade_date.year}-{trade_date.month:02d}" + trade_quarter = f"{trade_date.year}-Q{(trade_date.month - 1) // 3 + 1}" + trade_year = str(trade_date.year) + assert store.get_context(ContextLayer.L4_MONTHLY, trade_month, "monthly_pnl") == 1000.0 + assert store.get_context(ContextLayer.L3_QUARTERLY, trade_quarter, "quarterly_pnl") == 1000.0 + assert store.get_context(ContextLayer.L2_ANNUAL, trade_year, "annual_pnl") == 1000.0 class TestLayerMetadata: