Clarify ops market opportunity side probability
This commit is contained in:
@@ -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",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 md:grid-cols-5">
|
||||
<Stat label="机会数" value={summary.opportunity_count ?? rows.length} sub="当前筛选结果" />
|
||||
<Stat label="正边际" value={summary.positive_edge_count ?? 0} sub="模型概率高于买入价" />
|
||||
<Stat label="正边际" value={summary.positive_edge_count ?? 0} sub="方向概率高于买入价" />
|
||||
<Stat label="最低价格" value={cents(summary.min_price)} sub="低价合约下限" />
|
||||
<Stat label="最大 Edge" value={percent(summary.max_edge)} sub="模型概率 - 市场价" />
|
||||
<Stat label="最大 Edge" value={percent(summary.max_edge)} sub="方向概率 - 市场价" />
|
||||
<Stat label="报价状态" value={quoteStatus} sub={`${summary.matched_event_count ?? 0}/${summary.scanned_city_count ?? 0} 城市匹配`} />
|
||||
</div>
|
||||
|
||||
@@ -243,7 +258,7 @@ export function MarketOpportunitiesPageClient() {
|
||||
<th className="px-3 py-2">选项</th>
|
||||
<th className="px-3 py-2">方向</th>
|
||||
<th className="px-3 py-2 text-right">买入价</th>
|
||||
<th className="px-3 py-2 text-right">模型概率</th>
|
||||
<th className="px-3 py-2 text-right">方向概率</th>
|
||||
<th className="px-3 py-2 text-right">Edge</th>
|
||||
<th className="px-3 py-2 text-right">当前最高</th>
|
||||
<th className="px-3 py-2 text-right">DEB</th>
|
||||
@@ -277,7 +292,14 @@ export function MarketOpportunitiesPageClient() {
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-right font-black text-slate-950">{cents(row.ask_price)}</td>
|
||||
<td className="px-3 py-2 text-right font-bold text-blue-700">{percent(row.model_probability)}</td>
|
||||
<td className="px-3 py-2 text-right">
|
||||
<div className="font-bold text-blue-700">{percent(sideProbability(row))}</div>
|
||||
{String(row.side).toLowerCase() === "no" ? (
|
||||
<div className="mt-0.5 text-[11px] font-semibold text-slate-400">
|
||||
YES {percent(yesProbability(row))}
|
||||
</div>
|
||||
) : null}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-right font-black text-emerald-700">{percent(row.edge)}</td>
|
||||
<td className="px-3 py-2 text-right font-semibold text-slate-700">{tempLabel(row.current_max_so_far)}</td>
|
||||
<td className="px-3 py-2 text-right font-semibold text-orange-700">{tempLabel(row.deb_prediction)}</td>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")),
|
||||
|
||||
Reference in New Issue
Block a user