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:
@@ -82,7 +82,13 @@ def _build_rich_player_pool(projections, stats):
|
|||||||
|
|
||||||
merged["projected_points"] = merged["fv_proj"].fillna(6.0)
|
merged["projected_points"] = merged["fv_proj"].fillna(6.0)
|
||||||
merged["fv_std"] = merged["fv_std"].fillna(0.5)
|
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["games_played"] = merged["games"]
|
||||||
merged["name"] = merged["player"]
|
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"],
|
name=player["name"], team=player["team"], role=player["role"],
|
||||||
projected_points=player["projected_points"],
|
projected_points=player["projected_points"],
|
||||||
market_value=player["market_value"],
|
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 = {
|
state = {
|
||||||
"budget_remaining": max(50, 500 - i * 15),
|
"budget_remaining": max(50, 500 - i * 15),
|
||||||
@@ -585,7 +591,8 @@ def run():
|
|||||||
inject_css()
|
inject_css()
|
||||||
|
|
||||||
st.markdown("## 🔬 Dev Preview — 12 ML Models on Real 26/27 Serie A Data")
|
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
|
# Load data
|
||||||
with st.spinner("Loading real Serie A data...", show_time=True):
|
with st.spinner("Loading real Serie A data...", show_time=True):
|
||||||
@@ -611,15 +618,17 @@ def run():
|
|||||||
with k3:
|
with k3:
|
||||||
top_fv = player_pool["projected_points"].max()
|
top_fv = player_pool["projected_points"].max()
|
||||||
top_name = player_pool.loc[player_pool["projected_points"].idxmax(), "name"]
|
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:
|
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:
|
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)
|
unsafe_allow_html=True)
|
||||||
with k6:
|
with k6:
|
||||||
cf_ate = results.get("causal", {}).get("ate", 0)
|
st.markdown(kpi_card("HISTORICAL VOTES", f"{len(votes):,}", "matchday records", PITCH_GREEN),
|
||||||
st.markdown(kpi_card("CAUSAL ATE", f"{cf_ate:+.3f}", "transfer effect", GOLD), unsafe_allow_html=True)
|
unsafe_allow_html=True)
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
|
|
||||||
@@ -666,7 +675,7 @@ def run():
|
|||||||
name=target["name"], team=target["team"], role=target["role"],
|
name=target["name"], team=target["team"], role=target["role"],
|
||||||
projected_points=float(target["projected_points"]),
|
projected_points=float(target["projected_points"]),
|
||||||
market_value=float(target["market_value"]),
|
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 = {
|
state = {
|
||||||
|
|||||||
Reference in New Issue
Block a user