
    ǥj                     \    d Z ddlZdZdZdZdZd Zd Zd	 Zddd
eefdZ	d Z
ddZddZdS )a]  Model: strength-based Poisson for football 1X2 predictions.

lambda_home = HOME_BASE * att(home) * def(away)
lambda_away = AWAY_BASE * att(away) * def(home)
att = clamp(ppg / league_avg_ppg), def = 2 - att
Constants fit 2024-26 Bundesliga calibration; same family as Dixon-Coles (minus the
low-score correlation correction, which we can add later).
    N333333?gffffff?)g?r   	   c                 b    t          j        |            | |z  z  t          j        |          z  S )N)mathexp	factorial)lmbdaks     //root/.hermes/scripts/sports-predictor/model.pypoisson_pmfr      s,    8UFeQh&):):::    c                 ,   t          | d          }t          |d          }dx}}t          t                    D ]Q}t          t                    D ]:}t          ||          t          ||          z  }||k    r||z  }/||k    r||z  };R||d|z
  |z
  fS )zBReturns (p_home, p_draw, p_away) over an independent Poisson grid.皙?              ?)maxrange	MAX_GOALSr   )	lambda_homelambda_awaylhlaphpdhaps	            r   match_probsr      s    	[$		B	[$		BMB9  y!! 	 	AB""[Q%7%77A1uuaaa	 r38b=  r   c                 h    t           \  }}|dk    r!t          |t          || |z                      ndS )Nr   r   )	ATT_CLAMPr   min)ppgavg_ppglohis       r   att_coefr&   $   s6    FB.5kk3r3r3=))***sBr   g      $@c                 &   fd} || |          }  |||          }t          |           }	t          |          }
||	z  d|
z
  z  }||
z  d|	z
  z  }t          ||          \  }}}t          d|fd|fd|ffd           \  }}|||||||dS )	u!  Returns dict: lambda_home, lambda_away, ph, pd, pa, pick, pick_prob.

    Shrinkage: with home_n/away_n games played, ppg is regressed toward the
    league average (prior strength shrink=6 games) — tames early-season noise
    where 3 games can make PSG look like a mid-table side.
    c                 :    ||dk     r| S | |z  z  z   |z   z  S N    )r"   nr#   shrinks     r   
shrink_ppgzpredict.<locals>.shrink_ppg1   s2    9AJa'F**q6z::r   g       @1X2c                     | d         S r)   r+   )xs    r   <lambda>zpredict.<locals>.<lambda>=   s
    ad r   )key)r   r   r   r   papick	pick_prob)r&   r   r   )home_ppgaway_ppgr#   home_naway_nr-   	home_base	away_baser.   att_hatt_ar   r   r   r   r6   r7   probs     `  `            r   predictrB   )   s    ; ; ; ; ; ;
 z(F++Hz(F++HXw''EXw''E	U	cEk	*B	U	cEk	*BR$$JBBsBi#rS"I6NNKKKJD$Bb"t- - -r   c           	          i }dD ]d}|                      |          }||         }|r|dk    r(||z  dz
  }|dk    r||dz
  z  nd}|t          dt          d|                    d||<   e|S )zE(outcome, decimal_odds, model_prob) -> EV per 1u, Kelly stake cap 5%.)r/   r0   r1   r   r   r   )evkelly)getr   r!   )decimal_oddsmodel_probsoutoutcomeor   rD   rE   s           r   ev_1x2rL   B   s    
C" G GW%%  	AHHUS[$%GGC   3sCe4D4D+E+EFFGJr      c                    t          | d          t          |d          }}dx}}d}t          |          D ]Z}t          |          D ]H}	t          ||          t          ||	          z  }
||	k    r||
z  }n||	k    r||
z  }||	z   dk    r||
z  }I[dt          j        |           z
  dt          j        |           z
  z  }||d|z
  |z
  |d|z
  |dS )a(  Convert Poisson expectations into binary prediction-market prices.

    Returns {home_win, away_win, draw, over_2_5, under_2_5, btts} as
    probabilities (0-1). 'Win' markets resolve NO on a draw in regulation.
    Used for yes/no markets (World.xyz, Polymarket) where price == probability.
    r   r      r   )home_windrawaway_winover_2_5	under_2_5btts)r   r   r   r   r   )r   r   maxgr   r   r   r   over25r   r   r   p_bttss               r   binary_marketsrY   P   s    d##Sd%;%;BMBF4[[  t 	 	AB""[Q%7%77A1uuaaa1uzz!	 DHbSMM!cDHbSMM&9:FBC"HrMS6\6K K Kr   {Gz?r   c                     |dddS | |z
  |z
  }d| z
  d|z
  z
  |z
  }||k    rd|| |z
  |z
  dS ||k    rd|| |z   |z   d	S d||d
S )a  Decision rule for a yes/no market given model prob and quoted price.

    World's dealer spread is baked into the quoted price (~2-3%), so the edge
    must clear model_p - price > edge_min + spread. Returns dict with side,
    edge, and action ('BUY YES' / 'BUY NO' / 'PASS').
    NPASSzno price)actionreasonr   zBUY YES)r]   edgeprice_at_mostzBUY NO)r]   r_   price_at_least)r]   edge_yesedge_nor+   )model_pmarket_pricespreadedge_minrb   rc   s         r   world_verdictrh   h   s      J777%.HW}|!34v=G8#XRZHZ]cHcddd("G")H"4v"=? ? 	?(wGGGr   )rM   )rZ   r   )__doc__r   	HOME_BASE	AWAY_BASEr    r   r   r   r&   rB   rL   rY   rh   r+   r   r   <module>rl      s     				; ; ;! ! !C C C
 15T9	- - - -2  K K K K0H H H H H Hr   