Optimize signal read paths and enforce score invariants

This commit is contained in:
2026-07-11 16:04:13 +02:00
parent fdc49d0e28
commit 9450831ef3
13 changed files with 426 additions and 170 deletions
+102 -69
View File
@@ -8,6 +8,7 @@ best trade setup, active S/R levels, and latest price + day-over-day move.
from __future__ import annotations
import logging
from collections import defaultdict
from datetime import datetime, timezone
from sqlalchemy import func, select
@@ -102,87 +103,122 @@ async def remove_entry(
await db.commit()
async def _enrich_entry(
async def _enrich_entries(
db: AsyncSession,
entry: WatchlistEntry,
symbol: str,
) -> dict:
"""Build enriched watchlist entry dict with scores, R:R, SR levels, price."""
ticker_id = entry.ticker_id
rows: list[tuple[WatchlistEntry, str]],
) -> list[dict]:
"""Build watchlist rows from a fixed set of bulk lookups."""
if not rows:
return []
# Composite score
comp_result = await db.execute(
select(CompositeScore).where(CompositeScore.ticker_id == ticker_id)
ticker_ids = [entry.ticker_id for entry, _ in rows]
comps_result = await db.execute(
select(CompositeScore).where(CompositeScore.ticker_id.in_(ticker_ids))
)
comp = comp_result.scalar_one_or_none()
comps = {score.ticker_id: score for score in comps_result.scalars()}
# Dimension scores
dim_result = await db.execute(
select(DimensionScore).where(DimensionScore.ticker_id == ticker_id)
dims_result = await db.execute(
select(DimensionScore).where(DimensionScore.ticker_id.in_(ticker_ids))
)
dims = [
{"dimension": ds.dimension, "score": ds.score}
for ds in dim_result.scalars().all()
]
dims_by_ticker: dict[int, list[dict]] = defaultdict(list)
for score in dims_result.scalars():
dims_by_ticker[score.ticker_id].append(
{"dimension": score.dimension, "score": score.score}
)
# Best trade setup (highest R:R) for this ticker
ranked_setups = (
select(
TradeSetup.id,
func.row_number()
.over(
partition_by=TradeSetup.ticker_id,
order_by=TradeSetup.rr_ratio.desc(),
)
.label("rank"),
)
.where(TradeSetup.ticker_id.in_(ticker_ids))
.subquery()
)
setup_result = await db.execute(
select(TradeSetup)
.where(TradeSetup.ticker_id == ticker_id)
.order_by(TradeSetup.rr_ratio.desc())
.limit(1)
.join(ranked_setups, TradeSetup.id == ranked_setups.c.id)
.where(ranked_setups.c.rank == 1)
)
setup = setup_result.scalar_one_or_none()
best_setups = {setup.ticker_id: setup for setup in setup_result.scalars()}
# Active SR levels
sr_result = await db.execute(
levels_result = await db.execute(
select(SRLevel)
.where(SRLevel.ticker_id == ticker_id)
.order_by(SRLevel.strength.desc())
.where(SRLevel.ticker_id.in_(ticker_ids))
.order_by(SRLevel.ticker_id, SRLevel.strength.desc())
)
sr_levels = [
{
"price_level": lv.price_level,
"type": lv.type,
"strength": lv.strength,
}
for lv in sr_result.scalars().all()
]
levels_by_ticker: dict[int, list[dict]] = defaultdict(list)
for level in levels_result.scalars():
levels_by_ticker[level.ticker_id].append(
{
"price_level": level.price_level,
"type": level.type,
"strength": level.strength,
}
)
# Latest two daily closes → current price + day-over-day move
price_result = await db.execute(
select(OHLCVRecord.close, OHLCVRecord.date)
.where(OHLCVRecord.ticker_id == ticker_id)
.order_by(OHLCVRecord.date.desc())
.limit(2)
ranked_prices = (
select(
OHLCVRecord.ticker_id,
OHLCVRecord.close,
OHLCVRecord.date,
func.row_number()
.over(
partition_by=OHLCVRecord.ticker_id,
order_by=OHLCVRecord.date.desc(),
)
.label("rank"),
)
.where(OHLCVRecord.ticker_id.in_(ticker_ids))
.subquery()
)
bars = price_result.all()
last_close = bars[0].close if bars else None
prev_close = bars[1].close if len(bars) > 1 else None
change_pct = (
(last_close - prev_close) / prev_close * 100
if last_close is not None and prev_close
else None
prices_result = await db.execute(
select(
ranked_prices.c.ticker_id,
ranked_prices.c.close,
ranked_prices.c.date,
)
.where(ranked_prices.c.rank <= 2)
.order_by(ranked_prices.c.ticker_id, ranked_prices.c.rank)
)
price_date = bars[0].date if bars else None
prices_by_ticker: dict[int, list[tuple[float, datetime]]] = defaultdict(list)
for ticker_id, close, price_date in prices_result.all():
prices_by_ticker[ticker_id].append((close, price_date))
return {
"symbol": symbol,
"entry_type": entry.entry_type,
"composite_score": comp.score if comp else None,
"dimensions": dims,
"rr_ratio": setup.rr_ratio if setup else None,
"rr_direction": setup.direction if setup else None,
# Residual 12-1 activation percentile gates qualification; strategy_rank
# is the promoted top-pick ordering score.
"momentum_percentile": setup.momentum_percentile if setup else None,
"strategy_rank": setup.strategy_rank if setup else None,
"sr_levels": sr_levels,
"last_close": last_close,
"change_pct": change_pct,
"price_date": price_date,
"added_at": entry.added_at,
}
entries: list[dict] = []
for entry, symbol in rows:
ticker_id = entry.ticker_id
comp = comps.get(ticker_id)
setup = best_setups.get(ticker_id)
bars = prices_by_ticker[ticker_id]
last_close = bars[0][0] if bars else None
prev_close = bars[1][0] if len(bars) > 1 else None
entries.append(
{
"symbol": symbol,
"entry_type": entry.entry_type,
"composite_score": comp.score if comp else None,
"dimensions": dims_by_ticker[ticker_id],
"rr_ratio": setup.rr_ratio if setup else None,
"rr_direction": setup.direction if setup else None,
"momentum_percentile": setup.momentum_percentile if setup else None,
"strategy_rank": setup.strategy_rank if setup else None,
"sr_levels": levels_by_ticker[ticker_id],
"last_close": last_close,
"change_pct": (
(last_close - prev_close) / prev_close * 100
if last_close is not None and prev_close
else None
),
"price_date": bars[0][1] if bars else None,
"added_at": entry.added_at,
}
)
return entries
async def get_watchlist(
@@ -203,10 +239,7 @@ async def get_watchlist(
result = await db.execute(stmt)
rows = result.all()
entries: list[dict] = []
for entry, symbol in rows:
enriched = await _enrich_entry(db, entry, symbol)
entries.append(enriched)
entries = await _enrich_entries(db, rows)
# Sort
if sort_by == "composite":