Emrysv9's picture
Update app.py
d9fadd2 verified
import gradio as gr
import matplotlib.pyplot as plt
BURGUNDY = "#5A1414"
GOLD = "#FFB612"
# Hidden model performance constants (Boosted NN)
TP_COUNT = 588
FP_COUNT = 1310
# -----------------------------
# Core economics (TP/FP-aware)
# -----------------------------
def compute_profit(
tier1_contract, # full contract value for TRUE Tier 1 (e.g., 24.3M)
tier2_contract, # full contract value for TRUE Tier 2 (e.g., 2.7M)
premium_rate, # e.g., 0.007 (0.7%)
injury_rate, # e.g., 0.003 (0.3%)
payout_pct_contract, # e.g., 0.6 or 0.8 (portion paid out under PTD)
combine_players, # e.g., 300-330
insured_pct_players # % of combine players insured (predicted Tier 1)
):
# Players insured per year (predicted Tier 1)
players_insured = combine_players * insured_pct_players
# Precision among insured (TP share) from model constants
denom = TP_COUNT + FP_COUNT
precision = (TP_COUNT / denom) if denom > 0 else 0.0
# Profit per insured player IF actually Tier 1
profit_t1 = (premium_rate * tier1_contract) - (injury_rate * payout_pct_contract * tier1_contract)
# Profit per insured player IF actually Tier 2 (false positive)
profit_t2 = (premium_rate * tier2_contract) - (injury_rate * payout_pct_contract * tier1_contract)
# Expected profit per insured player, accounting for TP/FP mix
profit_per_player = precision * profit_t1 + (1 - precision) * profit_t2
# Expected annual profit
annual_profit = players_insured * profit_per_player
# Breakdown (useful outputs)
insured_t1 = players_insured * precision
insured_t2 = players_insured * (1 - precision)
return annual_profit, profit_per_player, players_insured, insured_t1, insured_t2, precision
# -----------------------------
# Sensitivity chart
# -----------------------------
def sensitivity_plot(
tier1_contract,
tier2_contract,
premium_rate,
injury_rate,
payout_pct_contract,
combine_players,
insured_pct_players
):
def annual(t1, t2, pr, ir, pay):
return compute_profit(
t1, t2, pr, ir, pay,
combine_players, insured_pct_players
)[0]
# Salary sensitivity: scale BOTH tiers together ±20%
salary_vals = [
annual(tier1_contract * 0.8, tier2_contract, premium_rate, injury_rate, payout_pct_contract),
annual(tier1_contract, tier2_contract, premium_rate, injury_rate, payout_pct_contract),
annual(tier1_contract * 1.2, tier2_contract, premium_rate, injury_rate, payout_pct_contract),
]
# Premium sensitivity ±0.2% (0.002)
premium_vals = [
annual(tier1_contract, tier2_contract, max(premium_rate - 0.002, 0.0), injury_rate, payout_pct_contract),
annual(tier1_contract, tier2_contract, premium_rate, injury_rate, payout_pct_contract),
annual(tier1_contract, tier2_contract, premium_rate + 0.002, injury_rate, payout_pct_contract),
]
# Injury sensitivity ±0.1% (0.001)
injury_vals = [
annual(tier1_contract, tier2_contract, premium_rate, max(injury_rate - 0.001, 0.0), payout_pct_contract),
annual(tier1_contract, tier2_contract, premium_rate, injury_rate, payout_pct_contract),
annual(tier1_contract, tier2_contract, premium_rate, injury_rate + 0.001, payout_pct_contract),
]
labels = [
"Salary -20%", "Salary Base", "Salary +20%",
"Premium -0.2%", "Premium Base", "Premium +0.2%",
"Injury -0.1%", "Injury Base", "Injury +0.1%",
]
values = salary_vals + premium_vals + injury_vals
fig, ax = plt.subplots(figsize=(10, 4.3))
ax.bar(labels, values, color=GOLD, edgecolor=BURGUNDY, linewidth=0.8)
ax.axhline(0, color=BURGUNDY, linewidth=2)
ax.set_title("Sensitivity of Expected Annual Profit to Key Assumptions",
color=BURGUNDY, fontsize=14, fontweight="bold")
ax.set_ylabel("Expected Annual Profit ($)")
ax.tick_params(axis="x", rotation=35)
fig.tight_layout()
return fig
# -----------------------------
# Run button handler
# -----------------------------
def run_simulator(
tier1_contract,
tier2_contract,
premium_rate,
injury_rate,
combine_players,
insured_pct_players,
payout_pct_contract,
):
annual_profit, profit_per_player, insured_players, insured_t1, insured_t2, precision = compute_profit(
tier1_contract,
tier2_contract,
premium_rate,
injury_rate,
payout_pct_contract,
combine_players,
insured_pct_players
)
fig = sensitivity_plot(
tier1_contract,
tier2_contract,
premium_rate,
injury_rate,
payout_pct_contract,
combine_players,
insured_pct_players
)
return (
round(annual_profit,0),
round(profit_per_player,0),
round(insured_players, 0),
round(insured_t1, 0),
round(insured_t2, 0),
round(precision, 3),
fig,
)
# -----------------------------
# Styling (Burgundy + Gold)
# -----------------------------
custom_css = f"""
:root {{
--primary-500: {BURGUNDY} !important;
--primary-600: {BURGUNDY} !important;
--primary-700: #4A0F0F !important;
--accent-color: {BURGUNDY} !important;
}}
label span {{
color: {BURGUNDY} !important;
font-weight: 700;
}}
.gr-markdown h2, .gr-markdown h3 {{
color: {BURGUNDY} !important;
}}
.gr-slider input[type="range"] {{
accent-color: {BURGUNDY} !important;
}}
input[type="range"]::-webkit-slider-thumb {{
background: {GOLD} !important;
border: 2px solid {BURGUNDY} !important;
}}
input[type="range"]::-moz-range-thumb {{
background: {GOLD} !important;
border: 2px solid {BURGUNDY} !important;
}}
input[type="range"]:focus::-webkit-slider-thumb {{
box-shadow: 0 0 0 4px rgba(255, 182, 18, 0.45);
}}
.gr-button {{
background-color: {BURGUNDY} !important;
color: {GOLD} !important;
font-weight: 800 !important;
border-radius: 10px !important;
}}
.gr-button:hover {{
background-color: #4A0F0F !important;
}}
.gr-label span {{
background-color: {BURGUNDY} !important;
color: {GOLD} !important;
border-radius: 6px;
padding: 2px 6px;
}}
"""
# -----------------------------
# UI
# -----------------------------
with gr.Blocks() as demo:
gr.Markdown(f"""
<h2 style="color:{BURGUNDY}; margin-bottom:0.2rem;">
NFL Combine Insurance Revenue Simulator (TP/FP-adjusted)
</h2>
<p style="margin-top:0.2rem;">
Uses hidden boosted-NN precision derived from TP={TP_COUNT} and FP={FP_COUNT} (precision ≈ {TP_COUNT/(TP_COUNT+FP_COUNT):.3f}).
Adjust assumptions to stress-test expected annual profit.
</p>
""")
with gr.Row():
tier1_contract = gr.Slider(
18_000_000, 30_000_000, value=24_300_000, step=100_000,
label="Tier 1 Avg Full Contract ($)"
)
tier2_contract = gr.Slider(
750_000, 6_000_000, value=2_700_000, step=50_000,
label="Tier 2 Avg Full Contract ($)"
)
with gr.Row():
premium_rate = gr.Slider(
0.003, 0.010, value=0.007, step=0.001,
label="Premium Rate"
)
injury_rate = gr.Slider(
0.001, 0.010, value=0.003, step=0.001,
label="Injury / Payout Probability"
)
with gr.Row():
combine_players = gr.Slider(
300, 330, value=330, step=1,
label="Total Combine Participants Per Year"
)
insured_pct_players = gr.Slider(
0.20, 0.60, value=0.40, step=0.01,
label="Percent of Players Insured"
)
payout_pct_contract = gr.Slider(
0.40, 0.90, value=0.60, step=0.05,
label="Percent of Contract Paid Out on PTD"
)
gr.Markdown("### Key Outputs")
with gr.Row():
annual_out = gr.Number(label="Expected Annual Profit ($)")
profit_out = gr.Number(label="Profit per Insured Player ($)")
insured_out = gr.Number(label="Players Insured Per Year")
with gr.Row():
insured_t1_out = gr.Number(label="Expected Tier 1 Insured ")
insured_t2_out = gr.Number(label="Expected Tier 2 Insured ")
precision_out = gr.Number(label="Precision Among Insured ")
sensitivity = gr.Plot(label="Sensitivity Analysis")
run_btn = gr.Button("Run Simulation", variant="primary")
run_btn.click(
fn=run_simulator,
inputs=[
tier1_contract,
tier2_contract,
premium_rate,
injury_rate,
combine_players,
insured_pct_players,
payout_pct_contract,
],
outputs=[
annual_out,
profit_out,
insured_out,
insured_t1_out,
insured_t2_out,
precision_out,
sensitivity
],
)
if __name__ == "__main__":
demo.launch(
theme=gr.themes.Soft(),
css=custom_css,
share=True
)