diff --git a/dashboard/pages/06_dev_preview.py b/dashboard/pages/06_dev_preview.py index 37343df..bf9a600 100644 --- a/dashboard/pages/06_dev_preview.py +++ b/dashboard/pages/06_dev_preview.py @@ -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 = {