fix: fetch trend history per-window and run aggregation 24/7
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled

Two fixes for missing intraday data:

1. Frontend: lifted selectedWindow state to page level so useTrendHistory
   passes window param to the API. Previously fetched all windows with
   limit=500 which exhausted the limit before reaching recent intraday
   data. Now fetches only the selected window's data.

2. Scheduler: removed market-hours-only restriction from periodic
   aggregation. Runs every 15 minutes 24/7 so intraday data is always
   populated for backtesting regardless of market state.
This commit is contained in:
Celes Renata
2026-04-29 21:11:46 +00:00
parent 963a5c462c
commit cb3eb230d6
2 changed files with 9 additions and 20 deletions
+5 -5
View File
@@ -44,7 +44,8 @@ export function CompanyDetailPage() {
const { data: signals } = useCompetitiveSignals(company?.ticker);
const { data: decisions } = useCorporateDecisions(company?.ticker);
const { data: trends } = useTrends({ ticker: company?.ticker, limit: 200 });
const { data: trendHistory } = useTrendHistory({ ticker: company?.ticker, limit: 500 });
const [selectedWindow, setSelectedWindow] = useState('7d');
const { data: trendHistory } = useTrendHistory({ ticker: company?.ticker, window: selectedWindow, limit: 500 });
const { data: marketPrices } = useMarketPrices(company?.ticker, 200);
const { data: positions } = usePositions(company?.ticker);
const [tab, setTab] = useState<'trends' | 'sources' | 'aliases' | 'macro' | 'competitors' | 'patterns' | 'signals' | 'decisions'>('trends');
@@ -87,7 +88,7 @@ export function CompanyDetailPage() {
{tab === 'trends' && (
<div className="space-y-4">
<PositionCard positions={positions ?? []} ticker={company.ticker} />
<TrendHistoryChart trends={trendHistory ?? []} latestTrends={trends ?? []} ticker={company.ticker} marketPrices={marketPrices ?? []} />
<TrendHistoryChart trends={trendHistory ?? []} latestTrends={trends ?? []} ticker={company.ticker} marketPrices={marketPrices ?? []} selectedWindow={selectedWindow} onWindowChange={setSelectedWindow} />
</div>
)}
@@ -662,8 +663,7 @@ function PositionCard({ positions, ticker }: { positions: import('../api/hooks')
);
}
function TrendHistoryChart({ trends, latestTrends, ticker, marketPrices }: { trends: TrendSummary[]; latestTrends: TrendSummary[]; ticker: string; marketPrices: MarketPrice[] }) {
const [selectedWindow, setSelectedWindow] = useState('7d');
function TrendHistoryChart({ trends, latestTrends, ticker, marketPrices, selectedWindow, onWindowChange }: { trends: TrendSummary[]; latestTrends: TrendSummary[]; ticker: string; marketPrices: MarketPrice[]; selectedWindow: string; onWindowChange: (w: string) => void }) {
// Determine the time range for the selected window to filter data
const windowHours: Record<string, number> = {
@@ -742,7 +742,7 @@ function TrendHistoryChart({ trends, latestTrends, ticker, marketPrices }: { tre
{(availableWindows.length > 0 ? availableWindows : WINDOW_ORDER).map((w) => (
<button
key={w}
onClick={() => setSelectedWindow(w)}
onClick={() => onWindowChange(w)}
className={`rounded-md px-3 py-1 text-xs font-medium transition-colors ${
selectedWindow === w
? 'bg-brand-600 text-white'
+4 -15
View File
@@ -501,22 +501,11 @@ async def schedule_cycle(pool: asyncpg.Pool, rds: aioredis.Redis) -> int:
async def enqueue_periodic_aggregation(pool: asyncpg.Pool, rds: aioredis.Redis) -> int:
"""Enqueue aggregation jobs for all active tickers.
Runs periodically during market hours to ensure trend data stays fresh
even when no new documents are being ingested. This gives the intraday
and 1d windows continuous updates based on existing signals and market
price changes.
Runs periodically to ensure trend data stays fresh even when no new
documents are being ingested. During market hours this runs every ~15
minutes; outside market hours it runs every ~60 minutes (for backtesting
data continuity).
"""
# Only run during US market hours (Mon-Fri, 6:30 AM - 1:30 PM PT / 13:30-20:30 UTC)
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
weekday = now.weekday() # 0=Mon, 6=Sun
hour_utc = now.hour + now.minute / 60.0
if weekday >= 5: # Weekend
return 0
if hour_utc < 13.5 or hour_utc > 20.5: # Outside market hours (with 30min buffer)
return 0
# Fetch all active tickers
rows = await pool.fetch(
"SELECT ticker FROM companies WHERE active = TRUE ORDER BY ticker"