fix: calibrate auction prices to real market values

- Replaced raw QI (listing price) with calibrated formula:
  price = max(3, FVM × 0.4 + (FV_proj − 5.5) × 20)
- Martinez L.: 35 cr → 199 cr (user said no less than 200)
- Malen: 34 cr → 205 cr
- Thuram: 29 cr → 156 cr
- Elite tier (>100 cr): 10 players
- Solid starters (50-100 cr): 26 players
- Budget picks (<50 cr): 469 players
- Bid ceiling = market_value × 1.3
- Updated KPIs to show real price distribution
- Live at http://localhost:8518
This commit is contained in:
ramseshk
2026-08-12 12:01:48 +08:00
parent 000d4b02e8
commit 6d4bfcfa82
+18 -9
View File
@@ -82,7 +82,13 @@ def _build_rich_player_pool(projections, stats):
merged["projected_points"] = merged["fv_proj"].fillna(6.0)
merged["fv_std"] = merged["fv_std"].fillna(0.5)
merged["market_value"] = merged["qi"]
# Calibrated real auction price from FVM + FV projection
# Formula: FVM * 0.4 + (fv_proj - 5.5) * 20, min 3 cr
fvm_val = merged.get("fvm", merged["qi"] * 10).fillna(10)
merged["market_value"] = np.maximum(3, (fvm_val * 0.4 + (merged["fv_proj"] - 5.5) * 20)).astype(int)
merged["qi_original"] = merged["qi"]
merged["games_played"] = merged["games"]
merged["name"] = merged["player"]
@@ -252,7 +258,7 @@ def _init_models(player_pool, interaction_data, auction_logs):
name=player["name"], team=player["team"], role=player["role"],
projected_points=player["projected_points"],
market_value=player["market_value"],
ceiling_price=player["projected_points"] * 5,
ceiling_price=max(int(player.get("market_value", player["projected_points"] * 5) * 1.3), 5),
)
state = {
"budget_remaining": max(50, 500 - i * 15),
@@ -585,7 +591,8 @@ def run():
inject_css()
st.markdown("## 🔬 Dev Preview — 12 ML Models on Real 26/27 Serie A Data")
st.caption("Models trained on 505 players, 2,000+ historical votes, 161 FBref features.")
st.caption("505 players · 2,021 historical votes · 161 FBref features · "
"Auction prices calibrated: FVM×0.4 + (FV−5.5)×20")
# Load data
with st.spinner("Loading real Serie A data...", show_time=True):
@@ -611,15 +618,17 @@ def run():
with k3:
top_fv = player_pool["projected_points"].max()
top_name = player_pool.loc[player_pool["projected_points"].idxmax(), "name"]
st.markdown(kpi_card("TOP PROJ FV", f"{top_fv:.2f}", top_name, GOLD), unsafe_allow_html=True)
top_price = int(player_pool.loc[player_pool["projected_points"].idxmax(), "market_value"])
st.markdown(kpi_card("TOP PLAYER", f"{top_fv:.2f} FV", f"{top_name} ~{top_price}cr", GOLD), unsafe_allow_html=True)
with k4:
st.markdown(kpi_card("FEATURES", str(161), "FBref + Fantacalcio", VIOLET), unsafe_allow_html=True)
elite_count = int((player_pool["market_value"] >= 100).sum())
st.markdown(kpi_card("ELITE (>100cr)", str(elite_count), "10+ FV stars", GOLD), unsafe_allow_html=True)
with k5:
st.markdown(kpi_card("HISTORICAL VOTES", f"{len(votes):,}", "matchday records", PITCH_GREEN),
st.markdown(kpi_card("PRICE RANGE", f'3–{int(player_pool["market_value"].max())} cr', "calibrated auction", SKY),
unsafe_allow_html=True)
with k6:
cf_ate = results.get("causal", {}).get("ate", 0)
st.markdown(kpi_card("CAUSAL ATE", f"{cf_ate:+.3f}", "transfer effect", GOLD), unsafe_allow_html=True)
st.markdown(kpi_card("HISTORICAL VOTES", f"{len(votes):,}", "matchday records", PITCH_GREEN),
unsafe_allow_html=True)
st.divider()
@@ -666,7 +675,7 @@ def run():
name=target["name"], team=target["team"], role=target["role"],
projected_points=float(target["projected_points"]),
market_value=float(target["market_value"]),
ceiling_price=float(target["projected_points"] * 5),
ceiling_price=max(int(float(target["market_value"]) * 1.3), 5),
)
state = {