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"""
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.
""") 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 )