diff --git a/frontend/components/ops/__tests__/opsMarketOpportunities.test.ts b/frontend/components/ops/__tests__/opsMarketOpportunities.test.ts index 49a76c49..becba70c 100644 --- a/frontend/components/ops/__tests__/opsMarketOpportunities.test.ts +++ b/frontend/components/ops/__tests__/opsMarketOpportunities.test.ts @@ -47,7 +47,7 @@ export function runTests() { "选项", "方向", "买入价", - "模型概率", + "方向概率", "Edge", "市场链接", ]) { @@ -63,4 +63,10 @@ export function runTests() { client.includes('showAllLowPrice ? "-1" : minEdge'), "market opportunities filters must label price versus edge and not edge-filter the show-all-low-price view", ); + assert( + client.includes("side_probability") && + client.includes("yes_probability") && + client.includes("YES"), + "NO opportunities must show side probability while keeping the underlying YES probability visible", + ); } diff --git a/frontend/components/ops/market-opportunities/MarketOpportunitiesPageClient.tsx b/frontend/components/ops/market-opportunities/MarketOpportunitiesPageClient.tsx index 04456182..a6ebb10c 100644 --- a/frontend/components/ops/market-opportunities/MarketOpportunitiesPageClient.tsx +++ b/frontend/components/ops/market-opportunities/MarketOpportunitiesPageClient.tsx @@ -15,6 +15,8 @@ type MarketOpportunityRow = { side?: string; ask_price?: number; model_probability?: number; + yes_probability?: number; + side_probability?: number; edge?: number; liquidity?: number | null; volume?: number | null; @@ -74,6 +76,19 @@ function sideTone(side?: string) { : "border-emerald-200 bg-emerald-50 text-emerald-700"; } +function yesProbability(row: MarketOpportunityRow) { + return typeof row.yes_probability === "number" ? row.yes_probability : row.model_probability; +} + +function sideProbability(row: MarketOpportunityRow) { + if (typeof row.side_probability === "number") return row.side_probability; + const yes = yesProbability(row); + if (String(row.side).toLowerCase() === "no" && typeof yes === "number") { + return 1 - yes; + } + return yes; +} + function Stat({ label, value, @@ -152,9 +167,9 @@ export function MarketOpportunitiesPageClient() {
- + - +
@@ -243,7 +258,7 @@ export function MarketOpportunitiesPageClient() { 选项 方向 买入价 - 模型概率 + 方向概率 Edge 当前最高 DEB @@ -277,7 +292,14 @@ export function MarketOpportunitiesPageClient() { {cents(row.ask_price)} - {percent(row.model_probability)} + +
{percent(sideProbability(row))}
+ {String(row.side).toLowerCase() === "no" ? ( +
+ YES {percent(yesProbability(row))} +
+ ) : null} + {percent(row.edge)} {tempLabel(row.current_max_so_far)} {tempLabel(row.deb_prediction)} diff --git a/tests/test_ops_market_opportunities.py b/tests/test_ops_market_opportunities.py index c4cf7ace..4cb1480b 100644 --- a/tests/test_ops_market_opportunities.py +++ b/tests/test_ops_market_opportunities.py @@ -101,9 +101,13 @@ def test_build_market_opportunities_scans_yes_and_no_low_price_edges(): no = next(row for row in rows if row["bucket_label"] == "31°C" and row["side"] == "no") assert yes["model_probability"] == 0.46 + assert yes["yes_probability"] == 0.46 + assert yes["side_probability"] == 0.46 assert yes["ask_price"] == 0.18 assert round(yes["edge"], 2) == 0.28 assert no["model_probability"] == 0.10 + assert no["yes_probability"] == 0.10 + assert no["side_probability"] == 0.90 assert no["ask_price"] == 0.18 assert round(no["edge"], 2) == 0.72 assert all(row["ask_price"] < 0.20 for row in rows) diff --git a/web/services/ops/market_opportunities.py b/web/services/ops/market_opportunities.py index d1c04b1b..fd964527 100644 --- a/web/services/ops/market_opportunities.py +++ b/web/services/ops/market_opportunities.py @@ -336,6 +336,8 @@ def build_market_opportunity_rows( "side": option_side, "ask_price": _round_price(ask_number), "model_probability": model_probability, + "yes_probability": model_probability, + "side_probability": target_probability, "edge": edge, "liquidity": _finite_number(market.get("liquidity")), "volume": _finite_number(market.get("volume")),