feat(selection): implement gold brick resonance strategy with evaluation and logging enhancements

This commit is contained in:
yuxuanhui
2026-09-05 10:22:08 +08:00
parent 9163590070
commit 79476252b1
20 changed files with 1060 additions and 97 deletions
@@ -14,7 +14,14 @@ from uuid import uuid4
import psycopg
from psycopg.types.json import Jsonb
from ..domain.models import SelectionSignal, ZhixingB1Category
from ..domain.gold_brick import GOLD_BRICK_SIGNAL_ORDER
from ..domain.models import (
GoldBrickCategory,
SelectionSignal,
SelectionSignalCategory,
SelectionStrategyName,
ZhixingB1Category,
)
from ..domain.pattern_scoring import (
ZHIXING_B1_PATTERN_CASES,
PatternScore,
@@ -35,19 +42,26 @@ from ..domain.runs import (
from ..domain.zhixing_b1 import ZHIXING_B1_SIGNAL_ORDER
from .postgres_pool import SelectionConnectionPool, SelectionPostgresPool
_SIGNAL_PRIORITY = {category: index for index, category in enumerate(ZHIXING_B1_SIGNAL_ORDER)}
_SELECTION_SIGNAL_ORDER: tuple[SelectionSignalCategory, ...] = (
*ZHIXING_B1_SIGNAL_ORDER,
*GOLD_BRICK_SIGNAL_ORDER,
)
_SIGNAL_PRIORITY = {
category: index for index, category in enumerate(_SELECTION_SIGNAL_ORDER)
}
_CATEGORY_PREFIXES = {
"pullback": "zhixing_b1_pullback_",
"oversold": "zhixing_b1_oversold_",
"original": "zhixing_b1_original_b1",
"resonance": "gold_brick_resonance",
}
_SIGNAL_ORDER_SQL = (
"CASE category "
+ " ".join(
f"WHEN '{category.value}' THEN {index}"
for index, category in enumerate(ZHIXING_B1_SIGNAL_ORDER)
for index, category in enumerate(_SELECTION_SIGNAL_ORDER)
)
+ f" ELSE {len(ZHIXING_B1_SIGNAL_ORDER)} END"
+ f" ELSE {len(_SELECTION_SIGNAL_ORDER)} END"
)
_PATTERN_CASES_BY_ID = {definition.id: definition for definition in ZHIXING_B1_PATTERN_CASES}
_STOCK_ORDER_SQL = {
@@ -120,7 +134,7 @@ class PostgresSelectionRunRepository(SelectionRunStore):
def prepare_run(
self,
strategy: Literal["zhixing_b1"],
strategy: SelectionStrategyName,
target_trade_date: date,
source: SelectionExecutionSource,
*,
@@ -332,7 +346,7 @@ class PostgresSelectionRunRepository(SelectionRunStore):
def get_latest_run(
self,
strategy: Literal["zhixing_b1"],
strategy: SelectionStrategyName,
target_trade_date: date | None = None,
*,
query: SelectionResultQuery | None = None,
@@ -463,6 +477,7 @@ class PostgresSelectionRunRepository(SelectionRunStore):
"no_signal",
"insufficient_history",
"missing_target_bar",
"missing_turnover_rate",
"data_error",
],
str(value[2]),
@@ -476,7 +491,7 @@ class PostgresSelectionRunRepository(SelectionRunStore):
)
return SelectionRun(
id=str(row[0]),
strategy=cast(Literal["zhixing_b1"], str(row[1])),
strategy=cast(SelectionStrategyName, str(row[1])),
target_trade_date=_as_date(row[2]),
market_sync_batch_id=str(row[3]) if row[3] is not None else None,
status=cast(SelectionRunStatus, str(row[4])),
@@ -537,13 +552,22 @@ def _signal_from_row(row: tuple[object, ...]) -> SelectionSignal:
ts_code=str(row[0]),
name=str(row[1] or ""),
target_trade_date=_as_date(row[2]),
strategy=cast(Literal["zhixing_b1"], str(row[3])),
category=ZhixingB1Category(str(row[4])),
strategy=cast(SelectionStrategyName, str(row[3])),
category=_signal_category(str(row[4])),
close=float(str(row[5])),
details=_details(row[6]),
)
def _signal_category(value: str) -> SelectionSignalCategory:
"""Map a persisted category for either supported selection strategy."""
try:
return ZhixingB1Category(value)
except ValueError:
return GoldBrickCategory(value)
def _stock_filter(query: SelectionResultQuery, run_id: str) -> tuple[str, list[object]]:
"""Build the signal predicate used to select distinct matching stocks.