Italianhype commited on
Commit
92ab5f4
·
1 Parent(s): 7d84d55

Fix daily market data freshness across weekends

Browse files
MARKET_DESK_AGENTS_ORCHESTRATOR_REPORT.md CHANGED
@@ -56,7 +56,7 @@ Verdicts are:
56
  - `REJECTED_BENCHMARK_UNDERPERFORMANCE`
57
  - `DATA_BLOCKED`
58
 
59
- Approval requires the configured minimum sample size, positive expectancy, sufficient edge score, acceptable risk/reward, no high overfitting risk and no measured benchmark underperformance. Stored fractional rates are normalized consistently (`0.58` is reported as `58%`). Sharpe and Sortino remain `null` when no persisted source supplies them.
60
 
61
  ## Diversification Rules
62
 
 
56
  - `REJECTED_BENCHMARK_UNDERPERFORMANCE`
57
  - `DATA_BLOCKED`
58
 
59
+ Approval requires the configured minimum sample size, positive expectancy, sufficient edge score, acceptable risk/reward, no high overfitting risk and no measured benchmark underperformance. Stored fractional rates are normalized consistently (`0.58` is reported as `58%`). Sharpe and Sortino remain `null` when no persisted source supplies them. Daily OHLCV freshness uses the end of the stored market date, preventing a Friday close from being rejected early on Monday solely because `PriceHistory.date` has no intraday timestamp.
60
 
61
  ## Diversification Rules
62
 
backend/app/services/market_desks.py CHANGED
@@ -1,7 +1,7 @@
1
  from __future__ import annotations
2
 
3
  from dataclasses import dataclass
4
- from datetime import date, datetime, timedelta
5
  from typing import Callable, Protocol
6
 
7
  from sqlalchemy import func, select
@@ -173,7 +173,9 @@ class BaseMarketDeskAgent:
173
  def _is_stale(self, latest_date: date) -> bool:
174
  if self.stale_after_hours <= 0:
175
  return False
176
- latest = datetime.combine(latest_date, datetime.min.time())
 
 
177
  return latest < datetime.utcnow() - timedelta(hours=self.stale_after_hours)
178
 
179
  def _availability_payload(self, status: str, configured_count: int, eligible_count: int) -> dict:
 
1
  from __future__ import annotations
2
 
3
  from dataclasses import dataclass
4
+ from datetime import date, datetime, time, timedelta
5
  from typing import Callable, Protocol
6
 
7
  from sqlalchemy import func, select
 
173
  def _is_stale(self, latest_date: date) -> bool:
174
  if self.stale_after_hours <= 0:
175
  return False
176
+ # PriceHistory stores a market date, not an intraday observation time.
177
+ # Keep a Friday daily bar fresh through the weekend freshness budget.
178
+ latest = datetime.combine(latest_date, time.max)
179
  return latest < datetime.utcnow() - timedelta(hours=self.stale_after_hours)
180
 
181
  def _availability_payload(self, status: str, configured_count: int, eligible_count: int) -> dict:
backend/tests/test_market_desk_orchestrator.py CHANGED
@@ -1,4 +1,4 @@
1
- from datetime import date
2
 
3
  from sqlalchemy import create_engine
4
  from sqlalchemy.orm import Session
@@ -138,6 +138,19 @@ def test_unavailable_agent_remains_skipped_when_reused_after_discovery():
138
  assert result["opportunities_found"] == 0
139
 
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  def test_quant_edge_rejects_candidate_when_sample_is_insufficient():
142
  with setup_db() as db:
143
  assessment = BlumQuantEdgeAgent(min_score=60.0, min_sample_size=20).assess(db, candidate())
 
1
+ from datetime import date, timedelta
2
 
3
  from sqlalchemy import create_engine
4
  from sqlalchemy.orm import Session
 
138
  assert result["opportunities_found"] == 0
139
 
140
 
141
+ def test_daily_bar_freshness_uses_end_of_market_date():
142
+ with setup_db() as db:
143
+ asset = add_asset(db, "NVDA")
144
+ stored = db.query(PriceHistory).filter(PriceHistory.asset_id == asset.id).one()
145
+ stored.date = date.today() - timedelta(days=4)
146
+ db.commit()
147
+
148
+ availability = NasdaqAgent(stale_after_hours=96.0).availability(db)
149
+
150
+ assert availability["status"] == "AVAILABLE"
151
+ assert availability["eligible_asset_count"] == 1
152
+
153
+
154
  def test_quant_edge_rejects_candidate_when_sample_is_insufficient():
155
  with setup_db() as db:
156
  assessment = BlumQuantEdgeAgent(min_score=60.0, min_sample_size=20).assess(db, candidate())