This commit is contained in:
aaron 2026-04-28 13:15:11 +08:00
parent a0407f69a2
commit abe709305a
4 changed files with 29 additions and 2 deletions

View File

@ -24,6 +24,7 @@ logger = logging.getLogger(__name__)
ZT_POOL_URL = "https://push2ex.eastmoney.com/getTopicZTPool"
DT_POOL_URL = "https://push2ex.eastmoney.com/getTopicDTPool"
MIN_RELIABLE_SAMPLE_COUNT = 4500
async def get_market_breadth() -> MarketBreadth:
@ -34,7 +35,7 @@ async def get_market_breadth() -> MarketBreadth:
return cached
quotes = await get_a_share_realtime_ranking(page_size=6000)
if quotes and len(quotes) >= 3000:
if quotes and len(quotes) >= MIN_RELIABLE_SAMPLE_COUNT:
up_count = sum(1 for q in quotes if q.get("pct_chg", 0) > 0)
down_count = sum(1 for q in quotes if q.get("pct_chg", 0) < 0)
flat_count = sum(1 for q in quotes if q.get("pct_chg", 0) == 0)
@ -55,7 +56,11 @@ async def get_market_breadth() -> MarketBreadth:
cache.set(cache_key, breadth, ttl=60)
return breadth
logger.warning("市场广度实时样本不足quotes=%s", len(quotes))
logger.warning(
"市场广度实时样本不足quotes=%s,小于可靠阈值 %s,回退到基线口径",
len(quotes),
MIN_RELIABLE_SAMPLE_COUNT,
)
breadth = MarketBreadth(
trade_date=today_trade_date(),
total_count=len(quotes),

View File

@ -31,6 +31,8 @@ class StrategyProfile(BaseModel):
market_stance: str = ""
decision_note: str = ""
notes: list[str] = []
feedback_applied: bool = False
feedback_notes: list[str] = []
generated_by: str = "rules"
@ -165,6 +167,7 @@ async def _apply_strategy_feedback(profile: StrategyProfile) -> StrategyProfile:
return profile
updated = profile.model_copy(deep=True)
updated.feedback_applied = True
if controls.get("force_defensive"):
updated.allow_trading = False
@ -180,6 +183,7 @@ async def _apply_strategy_feedback(profile: StrategyProfile) -> StrategyProfile:
notes = controls.get("notes") or []
if notes:
updated.feedback_notes = notes[:3]
updated.notes.extend(notes[:2])
updated.decision_note = notes[0]

View File

@ -268,6 +268,7 @@ export default function DashboardPage() {
<AdminPanel
isAdmin={user?.role === "admin"}
opsStatus={opsStatus}
strategyProfile={data?.strategy_profile ?? null}
refreshing={refreshing}
opsRunning={opsRunning}
onRefresh={handleRefresh}
@ -472,6 +473,7 @@ function MarketSnapshot({
function AdminPanel({
isAdmin,
opsStatus,
strategyProfile,
refreshing,
opsRunning,
onRefresh,
@ -479,6 +481,7 @@ function AdminPanel({
}: {
isAdmin?: boolean;
opsStatus: OpsStatusResponse | null;
strategyProfile: LatestResult["strategy_profile"];
refreshing: boolean;
opsRunning: string | null;
onRefresh: () => void;
@ -505,6 +508,19 @@ function AdminPanel({
<FreshnessPill label="推荐" value={opsStatus.data_freshness.last_recommendation_created_at || "暂无"} />
</div>
{strategyProfile?.feedback_applied ? (
<div className="mt-3 rounded-2xl border border-cyan-500/15 bg-cyan-500/[0.05] p-3">
<div className="text-[10px] uppercase tracking-wider text-cyan-400 font-semibold"></div>
<div className="mt-2 space-y-1.5">
{(strategyProfile.feedback_notes?.length ? strategyProfile.feedback_notes : strategyProfile.notes || []).slice(0, 3).map((note, index) => (
<div key={`${note}-${index}`} className="text-xs leading-5 text-cyan-400/85">
{note}
</div>
))}
</div>
</div>
) : null}
<div className="mt-3 flex flex-wrap gap-2">
<button
onClick={onRefresh}

View File

@ -236,6 +236,8 @@ export interface LatestResult {
market_stance?: string;
decision_note?: string;
notes?: string[];
feedback_applied?: boolean;
feedback_notes?: string[];
generated_by?: string;
} | null;
}