fix(selection): render gold brick chart as TDX brick blocks with strong-red flag
Match the ZXB1 sub-chart formula: each brick spans between yesterday's and today's brick value via a stacked range bar — rising bricks are hollow red (solid on strong_red), falling bricks solid green. The chart API now also returns brick_strong_red per point.
This commit is contained in:
@@ -37,6 +37,7 @@ class SelectionChartPoint:
|
||||
trend_white: float | None
|
||||
trend_yellow: float | None
|
||||
brick_chart: float | None = None
|
||||
brick_strong_red: bool | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -95,7 +96,7 @@ class GetSelectionChart:
|
||||
)
|
||||
kdj = compute_kdj(frame)
|
||||
white, yellow = compute_zhixing_lines(frame["close"])
|
||||
brick_chart = self._compute_brick_chart(ts_code, bars) if include_brick_chart else None
|
||||
brick = self._compute_brick_indicators(ts_code, bars) if include_brick_chart else None
|
||||
start = max(0, len(bars) - SELECTION_CHART_LIMIT)
|
||||
points = tuple(
|
||||
SelectionChartPoint(
|
||||
@@ -111,7 +112,10 @@ class GetSelectionChart:
|
||||
trend_white=_finite_or_none(white.iloc[index]),
|
||||
trend_yellow=_finite_or_none(yellow.iloc[index]),
|
||||
brick_chart=(
|
||||
None if brick_chart is None else _finite_or_none(brick_chart.iloc[index])
|
||||
None if brick is None else _finite_or_none(brick["brick_chart"].iloc[index])
|
||||
),
|
||||
brick_strong_red=(
|
||||
None if brick is None else _strong_red_or_none(brick["strong_red"].iloc[index])
|
||||
),
|
||||
)
|
||||
for index, bar in enumerate(bars)
|
||||
@@ -125,11 +129,11 @@ class GetSelectionChart:
|
||||
points=points,
|
||||
)
|
||||
|
||||
def _compute_brick_chart(
|
||||
def _compute_brick_indicators(
|
||||
self,
|
||||
ts_code: str,
|
||||
bars: tuple[SelectionBar, ...],
|
||||
) -> pd.Series:
|
||||
) -> pd.DataFrame:
|
||||
"""Run the gold-brick formula on full history for stable warmup values."""
|
||||
|
||||
frame = pd.DataFrame(
|
||||
@@ -142,7 +146,16 @@ class GetSelectionChart:
|
||||
"volume": [bar.volume for bar in bars],
|
||||
}
|
||||
)
|
||||
return prepare_gold_brick_indicators(frame, ts_code)["brick_chart"]
|
||||
prepared = prepare_gold_brick_indicators(frame, ts_code)
|
||||
return prepared[["brick_chart", "strong_red"]]
|
||||
|
||||
|
||||
def _strong_red_or_none(value: object) -> bool | None:
|
||||
"""Convert one Pandas scalar to a JSON-safe brick flag or ``None``."""
|
||||
|
||||
if value is None or pd.isna(value):
|
||||
return None
|
||||
return bool(value)
|
||||
|
||||
|
||||
def _finite_or_none(value: object) -> float | None:
|
||||
|
||||
@@ -167,6 +167,7 @@ class SelectionChartPointResponse(BaseModel):
|
||||
trend_white: float | None
|
||||
trend_yellow: float | None
|
||||
brick_chart: float | None = None
|
||||
brick_strong_red: bool | None = None
|
||||
|
||||
|
||||
class SelectionChartResponse(BaseModel):
|
||||
@@ -510,6 +511,7 @@ def _chart_response(chart: SelectionChart) -> SelectionChartResponse:
|
||||
trend_white=point.trend_white,
|
||||
trend_yellow=point.trend_yellow,
|
||||
brick_chart=point.brick_chart,
|
||||
brick_strong_red=point.brick_strong_red,
|
||||
)
|
||||
for point in chart.points
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user