feat(signal_v2-phase4): add emit/skip logging to signal_generator

logger was declared but unused. Operational visibility was zero —
trader debugging 'why no signal?' had to step through code mentally.

- INFO on emit: '[signal emit] 005930 buy conf=0.823 rank=3' / sell with reason
- DEBUG on each skip path: same-cycle sell, hard gate, low confidence,
  dedup 24h (buy and sell)

Per final reviewer recommendation. 56 tests still pass.
This commit is contained in:
2026-05-17 13:35:29 +09:00
parent cc6310d72f
commit 2aa9f48ea3

View File

@@ -32,16 +32,22 @@ def _evaluate_buy_signals(state, dedup, settings) -> None:
for ticker, name, rank in candidates: for ticker, name, rank in candidates:
existing = state.signals.get(ticker) existing = state.signals.get(ticker)
if existing is not None and existing.get("action") == "sell": if existing is not None and existing.get("action") == "sell":
logger.debug("buy %s skipped: same-cycle sell precedence", ticker)
continue continue
if not _check_buy_hard_gate(state, ticker, settings): if not _check_buy_hard_gate(state, ticker, settings):
logger.debug("buy %s skipped: hard gate failed", ticker)
continue continue
confidence = _compute_buy_confidence(state, ticker, rank) confidence = _compute_buy_confidence(state, ticker, rank)
if confidence <= settings.confidence_threshold: if confidence <= settings.confidence_threshold:
logger.debug("buy %s skipped: confidence %.3f <= %.3f",
ticker, confidence, settings.confidence_threshold)
continue continue
if dedup.is_recent(ticker, "buy", within_hours=24): if dedup.is_recent(ticker, "buy", within_hours=24):
logger.debug("buy %s skipped: dedup 24h", ticker)
continue continue
state.signals[ticker] = _build_buy_signal(state, ticker, name, rank, confidence) state.signals[ticker] = _build_buy_signal(state, ticker, name, rank, confidence)
dedup.record(ticker, "buy", confidence=confidence) dedup.record(ticker, "buy", confidence=confidence)
logger.info("signal emit %s buy conf=%.3f rank=%s", ticker, confidence, rank)
def _buy_candidates(state) -> list[tuple[str, str, int | None]]: def _buy_candidates(state) -> list[tuple[str, str, int | None]]:
@@ -122,9 +128,13 @@ def _evaluate_sell_signals(state, dedup, settings) -> None:
if sell is None: if sell is None:
continue continue
if dedup.is_recent(ticker, "sell", within_hours=24): if dedup.is_recent(ticker, "sell", within_hours=24):
logger.debug("sell %s skipped: dedup 24h", ticker)
continue continue
state.signals[ticker] = sell state.signals[ticker] = sell
dedup.record(ticker, "sell", confidence=sell["confidence_webai"]) dedup.record(ticker, "sell", confidence=sell["confidence_webai"])
logger.info("signal emit %s sell conf=%.3f reason=%s",
ticker, sell["confidence_webai"],
sell.get("context", {}).get("sell_reason"))
def _try_stop_loss(state, holding: dict, settings) -> dict | None: def _try_stop_loss(state, holding: dict, settings) -> dict | None: