feat(selection): expose run sector aggregates and sector filter on results API

- sector_radar: add batch sector-count aggregation and sector member lookup
  over the strict last-good membership snapshot (postgres + in-memory fakes)
- selection: add SelectionSectorReader port, list_sector_counts use case,
  and sector_stock_codes filtering via run identity resolution; queries stay
  inside the selection context per ADR 0001
- http: add GET /api/v1/selection/sectors and forward sector param on
  /results and /runs/{run_id}
- fix stale positional args in pattern-scoring run tests; cover new behavior
  with read-service, application, and HTTP contract tests
This commit is contained in:
yuxuanhui
2026-09-05 19:48:30 +08:00
parent 41a9b4eb9a
commit 7e0f13d678
13 changed files with 1135 additions and 36 deletions
@@ -136,7 +136,13 @@ class FakeStore:
kwargs["error_message"] = error_message
self.finished = (run_id, status, kwargs)
def get_run(self, run_id: str, *, query: SelectionResultQuery | None = None):
def get_run(
self,
run_id: str,
*,
query: SelectionResultQuery | None = None,
sector_stock_codes: Sequence[str] | None = None,
):
return None
def get_latest_run(
@@ -145,9 +151,16 @@ class FakeStore:
target_trade_date: date | None = None,
*,
query: SelectionResultQuery | None = None,
sector_stock_codes: Sequence[str] | None = None,
):
return None
def get_run_identity(self, run_id: str):
return None
def get_latest_run_identity(self, strategy: str, target_trade_date: date | None = None):
return None
class BatchStore(FakeStore):
def __init__(self) -> None:
@@ -427,8 +440,8 @@ def test_execute_loads_cases_once_and_scores_only_selected_stocks() -> None:
reader,
store,
evaluator,
loader,
scorer,
pattern_case_loader=loader,
pattern_scorer=scorer,
pattern_scoring_enabled=True,
batch_size=1,
)
@@ -463,8 +476,8 @@ def test_execute_isolates_pattern_scoring_failure_from_selection_status() -> Non
BatchReader(source),
store,
evaluator,
loader,
scorer,
pattern_case_loader=loader,
pattern_scorer=scorer,
pattern_scoring_enabled=True,
)
@@ -497,8 +510,8 @@ def test_execute_skips_pattern_dependencies_when_feature_flag_is_disabled() -> N
BatchReader(source),
store,
evaluator,
loader,
scorer,
pattern_case_loader=loader,
pattern_scorer=scorer,
pattern_scoring_enabled=False,
)
@@ -535,8 +548,8 @@ def test_execute_marks_scores_failed_when_case_library_is_unavailable() -> None:
BatchReader(source),
store,
evaluator,
loader,
scorer,
pattern_case_loader=loader,
pattern_scorer=scorer,
pattern_scoring_enabled=True,
)