feat(observability): fine-grained metrics for summary, trades, and cycle log
CI/CD / build-and-push (push) Successful in 1m51s

api/summary — new fields:
  open_trades_count, closed_trades_count, cash_available (bankroll−deployed),
  legacy_incomplete_count, reentry_guard_blocks_24h
  parallel fetch via asyncio.gather for sub-ms overhead

api/trades?status=open — trade enrichment:
  days_open (float, rounded to 1 decimal)
  signal_components {fg, mom, news, mfld} parsed from reasoning via regex
  Old trades without feat_str in reasoning return signal_components: null

bayesian.py — reasoning now embeds feat_str:
  "fg=+0.0600 mom=+0.0000 news=+0.0000 mfld=-0.7483 |"
  Manifold counters: _manifold_fetched / _manifold_on_trade per cycle
  get_cycle_stats() exposes manifold_matches_accepted / manifold_matches_rejected

bot/main.py — CYCLE SUMMARY 4 new fields:
  reentry_guard_blocked, legacy_incomplete_seen,
  family_conflicts_prevented, manifold_matches_accepted/rejected
  legacy_incomplete_count queried from DB once per cycle

db.py — get_legacy_incomplete_count(): open trades with NULL edge_net

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chemavx
2026-04-21 09:48:31 +00:00
parent e2fb697c0c
commit 46f8f4b79a
4 changed files with 83 additions and 6 deletions
+13
View File
@@ -201,6 +201,8 @@ class BayesianStrategy:
self._skip_prior_extreme: int = 0
self._skip_edge_net_nonpositive: int = 0 # edge_net <= 0
self._skip_edge_net_below_regime: int = 0 # 0 < edge_net < regime_min
self._manifold_fetched: int = 0 # markets where Manifold prob was retrieved
self._manifold_on_trade: int = 0 # subset of above that ended in a trade signal
# (edge_gross, edge_net, regime_min) for every market that reached the
# edge computation stage (passed prior-extreme, family, unsupported filters)
self._evaluated_edges: list[tuple[float, float, float]] = []
@@ -212,6 +214,8 @@ class BayesianStrategy:
self._skip_prior_extreme = 0
self._skip_edge_net_nonpositive = 0
self._skip_edge_net_below_regime = 0
self._manifold_fetched = 0
self._manifold_on_trade = 0
self._evaluated_edges = []
def get_cycle_stats(self) -> dict:
@@ -230,6 +234,8 @@ class BayesianStrategy:
"evaluated_count": len(edges),
"gross_gt_002": sum(1 for g in all_gross if g > 0.02),
"gross_gt_004": sum(1 for g in all_gross if g > 0.04),
"manifold_matches_accepted": self._manifold_on_trade,
"manifold_matches_rejected": self._manifold_fetched - self._manifold_on_trade,
}
async def evaluate(
@@ -401,9 +407,12 @@ class BayesianStrategy:
# Applies a log-odds adjustment proportional to divergence from prior.
# No query budget — 30 min cache means network cost is paid once per cycle.
manifold_log_adj = 0.0
manifold_used = False
if (is_politics or is_tech) and self._manifold is not None:
manifold_prob = await self._manifold.get_probability(market.question)
if manifold_prob is not None:
manifold_used = True
self._manifold_fetched += 1
m_clamped = max(0.05, min(0.95, manifold_prob))
m_log = math.log(m_clamped / (1 - m_clamped))
p_log = math.log(prior / (1 - prior))
@@ -487,6 +496,8 @@ class BayesianStrategy:
f"regime_min={regime_min:.2f} | days={days} | "
f"family={family} | "
f"Direction={direction} | "
f"fg={_fg_contribution:+.4f} mom={_momentum_contribution:+.4f} "
f"news={news_log_adj:+.4f} mfld={manifold_log_adj:+.4f} | "
f"Signals: {', '.join(sources[1:])}"
)
@@ -501,6 +512,8 @@ class BayesianStrategy:
)
self._signal_count += 1
if manifold_used:
self._manifold_on_trade += 1
return TradingSignal(
market_id=market.id,
question=market.question,