Optimize signal read paths and enforce score invariants
This commit is contained in:
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user