refactor(selection): 统一信号与股票处理逻辑,重构相关数据结构与接口,更新前端展示以支持股票信息
This commit is contained in:
@@ -12,6 +12,7 @@ from zhixing_server.bootstrap.config import Settings, get_settings
|
||||
from zhixing_server.modules.selection.application.run import (
|
||||
RunZhixingB1,
|
||||
)
|
||||
from zhixing_server.modules.selection.domain.models import SelectionSignal
|
||||
from zhixing_server.modules.selection.domain.runs import (
|
||||
SelectionRerunRequired,
|
||||
SelectionResultQuery,
|
||||
@@ -93,8 +94,25 @@ def _empty_signals() -> list[SelectionSignalResponse]:
|
||||
return []
|
||||
|
||||
|
||||
class SelectionStockResponse(BaseModel):
|
||||
"""One selected stock with all independently persisted signals."""
|
||||
|
||||
ts_code: str
|
||||
name: str
|
||||
target_trade_date: date
|
||||
strategy: StrategyValue
|
||||
close: float
|
||||
signals: list[SelectionSignalResponse] = Field(default_factory=_empty_signals)
|
||||
|
||||
|
||||
def _empty_stocks() -> list[SelectionStockResponse]:
|
||||
"""Create a typed default stock-result list."""
|
||||
|
||||
return []
|
||||
|
||||
|
||||
class SelectionResultsResponse(BaseModel):
|
||||
"""Batch summary and one filtered page of signals consumed by the Web feature."""
|
||||
"""Batch summary and one filtered page of selected stocks."""
|
||||
|
||||
strategy: StrategyValue
|
||||
target_trade_date: date | None
|
||||
@@ -114,9 +132,9 @@ class SelectionResultsResponse(BaseModel):
|
||||
finished_at: datetime | None = None
|
||||
page: int = Field(default=1, ge=1)
|
||||
page_size: int = Field(default=10, ge=1, le=100)
|
||||
signals_total: int = Field(default=0, ge=0)
|
||||
stocks_total: int = Field(default=0, ge=0)
|
||||
failures: list[SelectionFailureResponse] = Field(default_factory=_empty_failures)
|
||||
signals: list[SelectionSignalResponse] = Field(default_factory=_empty_signals)
|
||||
stocks: list[SelectionStockResponse] = Field(default_factory=_empty_stocks)
|
||||
|
||||
|
||||
def get_selection_service(
|
||||
@@ -247,7 +265,7 @@ def get_selection_results(
|
||||
coverage=0,
|
||||
page=query.page,
|
||||
page_size=query.page_size,
|
||||
signals_total=0,
|
||||
stocks_total=0,
|
||||
)
|
||||
return _run_response(run, query=query)
|
||||
|
||||
@@ -255,6 +273,10 @@ def get_selection_results(
|
||||
def _run_response(run: SelectionRun, *, query: SelectionResultQuery) -> SelectionResultsResponse:
|
||||
"""Translate a domain run without exposing storage-specific fields."""
|
||||
|
||||
signals_by_stock: dict[str, list[SelectionSignalResponse]] = {}
|
||||
for signal in run.signals:
|
||||
signals_by_stock.setdefault(signal.ts_code, []).append(_signal_response(signal))
|
||||
|
||||
return SelectionResultsResponse(
|
||||
strategy=run.strategy,
|
||||
target_trade_date=run.target_trade_date,
|
||||
@@ -274,7 +296,9 @@ def _run_response(run: SelectionRun, *, query: SelectionResultQuery) -> Selectio
|
||||
finished_at=run.finished_at,
|
||||
page=query.page,
|
||||
page_size=query.page_size,
|
||||
signals_total=run.signals_total if run.signals_total is not None else run.signal_count,
|
||||
stocks_total=(
|
||||
run.stocks_total if run.stocks_total is not None else run.selected_stock_count
|
||||
),
|
||||
failures=[
|
||||
SelectionFailureResponse(
|
||||
ts_code=item.ts_code,
|
||||
@@ -285,21 +309,34 @@ def _run_response(run: SelectionRun, *, query: SelectionResultQuery) -> Selectio
|
||||
for item in run.items
|
||||
if item.status in {"insufficient_history", "missing_target_bar", "data_error"}
|
||||
],
|
||||
signals=[
|
||||
SelectionSignalResponse(
|
||||
ts_code=signal.ts_code,
|
||||
name=signal.name,
|
||||
target_trade_date=signal.target_trade_date,
|
||||
strategy=signal.strategy,
|
||||
category=signal.category.value,
|
||||
close=signal.close,
|
||||
details=dict(signal.details),
|
||||
stocks=[
|
||||
SelectionStockResponse(
|
||||
ts_code=signals[0].ts_code,
|
||||
name=signals[0].name,
|
||||
target_trade_date=signals[0].target_trade_date,
|
||||
strategy=signals[0].strategy,
|
||||
close=signals[0].close,
|
||||
signals=signals,
|
||||
)
|
||||
for signal in run.signals
|
||||
for signals in signals_by_stock.values()
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def _signal_response(signal: SelectionSignal) -> SelectionSignalResponse:
|
||||
"""Map a domain signal while preserving its category-specific details."""
|
||||
|
||||
return SelectionSignalResponse(
|
||||
ts_code=signal.ts_code,
|
||||
name=signal.name,
|
||||
target_trade_date=signal.target_trade_date,
|
||||
strategy=signal.strategy,
|
||||
category=signal.category.value,
|
||||
close=signal.close,
|
||||
details=dict(signal.details),
|
||||
)
|
||||
|
||||
|
||||
def _result_query(
|
||||
page: int,
|
||||
page_size: int,
|
||||
@@ -330,6 +367,7 @@ __all__ = [
|
||||
"SelectionResultsResponse",
|
||||
"SelectionRunAcceptedResponse",
|
||||
"SelectionRunRequest",
|
||||
"SelectionStockResponse",
|
||||
"get_selection_service",
|
||||
"get_selection_postgres_pool",
|
||||
"selection_router",
|
||||
|
||||
Reference in New Issue
Block a user