a
    Ë¢djA  ã                   @   sž   d Z ddlZddlZddlZddlZddlmZ ej dd¡ ddl	Z
dZeejƒdkrfeejd ƒndZdd	„ Zd
d„ Zddd„Zdd„ Zedkršeƒ  dS )aÚ  
Per-league Pinnacle calibration test (read-only, trades nothing).

THE QUESTION IT ANSWERS: "is a league's bad record a structural problem with
that market, or just variance?" xlow measures CLV against Pinnacle fair. If
Pinnacle is mis-calibrated in a league, CLV against it is meaningless there --
no amount of sample size on our BETS would reveal that, because the bets
inherit the broken anchor.

WHY IT'S POWERFUL: it uses EVERY fixture with Pinnacle odds + a result, not
just the handful we bet. That's ~40x more data per league (BSA: 32 fixtures vs
12 bets), so calibration becomes testable long before bet-level ROI does.

FIRST RUN (2026-07-25, n=1212 fixtures across 21+ leagues):
  - POOLED: Pinnacle implied 25.0% draws, actual 25.4%. Calibrated to within
    0.4pp. The anchor the whole strategy rests on is sound -- this had never
    actually been verified before.
  - BSA (the 0-for-12 league that prompted the test): implied 47.6%H/26.7%D,
    actual 56.2%H/15.6%D. Biggest deviation in the table, and adverse to us
    since BSA bets skew AWAY. BUT home z=+0.98, draw z=-1.42 -- noise.
  - Every league shows large deviations at n=30-50 with RANDOMLY SIGNED
    direction (E0 +9.6pp draws, P1 +8.9, N1 +8.5 vs E2 -6.3, T1 -6.1,
    SC0 -4.7). Random signs at small n is the signature of noise; a real
    structural bias would be consistent across comparable leagues.
  - To detect BSA's observed 8.6pp home-calibration error at 95% you need
    ~130 BSA fixtures (~4x current, roughly a full season).

HOW TO USE LATER: re-run when a league has accumulated materially more
fixtures (or when any league's record looks structurally broken). A league
whose deviation reaches |z| > 2 with n >= 100+ fixtures is a genuine
calibration problem -> that league's CLV signal is not trustworthy and it
should be dropped from the strategy (a real reason, unlike the hindsight
league-culling that project_league_filter_study already killed).

Usage:  python research\pin_calibration_by_league.py [min_n]
é    N)ÚdefaultdictzE:\soccer\research)z*E:\soccer\poly_backfill\*_polybackfill.csvzE:\soccer\live_data\*.csvé   é   c                  C   s´  g t ƒ  } }tD �]œ}t |¡D �]Š}ttƒ}zpt|ddd��N}t |¡D ]2}|| d¡| d¡| d¡| d¡f  	|¡ qJW d  ƒ n1 s’0    Y  W n t
y´   Y q Y n0 | ¡ D ]ì\}}||v s¾|d	 sØq¾t |¡}	|	d
vrìq¾dd„ |D ƒ}
|
�sq¾|
d }t | d¡¡t | d¡¡t | d¡¡  }}}d|||fv s¾|dkr¾|dkr¾|dk�shq¾| |¡ t |||f¡}|  	t|d |	|d	 |d |d d�¡ q¾q q| S )zEvery unique fixture with a result and valid Pinnacle 1X2 at the LAST
    pre-KO snapshot (closest to kickoff = sharpest line).Ú zutf-8)ÚnewlineÚencodingZ	game_dateZleagueÚhomeZawayNr   )ÚHÚDÚAc                 S   s&   g | ]}|  d ¡pd ¡ dkr|‘qS )Zstatusr   ZPRE)ÚgetÚupper)Ú.0Úr© r   ú/E:\soccer\research\pin_calibration_by_league.pyÚ
<listcomp>G   ó    z!load_fixtures.<locals>.<listcomp>éÿÿÿÿZpin_homeZpin_drawZpin_awayr   é   )ÚlgÚftrÚpfhÚpfdÚpfa)ÚsetÚSOURCESÚglobr   ÚlistÚopenÚcsvZ
DictReaderr   ÚappendÚ	ExceptionÚitemsÚKZ_resultZ_fÚaddZ	fair_multÚdict)ÚoutÚseenZpatÚpathZfxZfhr   ÚkeyZrowsr   ZpreZlastZphZpd_ZpaZpfr   r   r   Úload_fixtures3   sB    
ÿÿ(

ÿ
(
.r+   c                 C   s2   t  | | d|  ¡}|dkr.|||   | S dS )Nr   r   g        ©ÚmathZsqrt)ÚnZ	implied_pZactual_countZsdr   r   r   Úz_forU   s    r/   çX9´ÈvÞ?ç\�Âõ(\ÿ?c                 C   s&   t  |d|  ¡}|| | d  d S )zGFixtures required to detect a calibration error of `effect_pp` (in pp).r   g      Y@r   r,   )Z	effect_ppZbase_pZconfZsd1r   r   r   Ún_neededZ   s    r2   c                  C   s(  t ƒ } ttƒ}| D ]}||d   |¡ qtdt› �ƒ tdt| ƒ› d�ƒ tdƒ g }t| ¡ dd„ d�D �]�\}}t|ƒ}|tk rˆqlt	d	d
„ |D ƒƒ| }t	dd
„ |D ƒƒ| }t	dd
„ |D ƒƒ| }	t	dd
„ |D ƒƒ}
t	dd
„ |D ƒƒ}t	dd
„ |D ƒƒ}t
|||
ƒt
|||ƒt
||	|ƒ  }}}tt|ƒt|ƒt|ƒƒ}d}|dk�rh|dk�rhd}| |¡ n|dk�rvd}t|d›d|d›d|d d›d|
| d d›d|d›d|d d›d|| d d›d|d›d|	d d›d|| d d›d|d›d|› �ƒ qlt| ƒ}t	dd
„ | D ƒƒ| }t	dd
„ | D ƒƒ}t	dd
„ | D ƒƒ| }t	dd
„ | D ƒƒ}
tƒ  td |› d!|d d"›d#|| d d"›d$t
|||ƒd%›d&|d d"›d#|
| d d"›d$t
|||
ƒd%›d'�ƒ td(ƒ tƒ  td)ƒ d*D ]"}td|d+›d,t|ƒd-›�ƒ �qà|�rtd.|› d/�ƒ ntd0ƒ d S )1Nr   z=Pinnacle calibration by league (last pre-KO snapshot), min_n=ztotal fixtures loaded: Ú
zIlg      n   impH  actH    zH   impD  actD    zD   impA  actA    zA   flagc                 S   s   t | d ƒ S )Nr   )Úlen)Zkvr   r   r   Ú<lambda>j   r   zmain.<locals>.<lambda>)r*   c                 s   s   | ]}|d  V  qdS ©r   Nr   ©r   Úxr   r   r   Ú	<genexpr>n   r   zmain.<locals>.<genexpr>c                 s   s   | ]}|d  V  qdS ©r   Nr   r7   r   r   r   r9   o   r   c                 s   s   | ]}|d  V  qdS )r   Nr   r7   r   r   r   r9   p   r   c                 s   s   | ]}|d  dkV  qdS ©r   r	   Nr   r7   r   r   r   r9   q   r   c                 s   s   | ]}|d  dkV  qdS ©r   r
   Nr   r7   r   r   r   r9   r   r   c                 s   s   | ]}|d  dkV  qdS )r   r   Nr   r7   r   r   r   r9   s   r   r   r   éd   zSTRUCTURAL?zwatch (n low)Z6sú Z4dz5.1fz+5.2fz  z   c                 s   s   | ]}|d  V  qdS r:   r   r7   r   r   r   r9   �   r   c                 s   s   | ]}|d  dkV  qdS r<   r   r7   r   r   r   r9   ‚   r   c                 s   s   | ]}|d  V  qdS r6   r   r7   r   r   r   r9   ƒ   r   c                 s   s   | ]}|d  dkV  qdS r;   r   r7   r   r   r   r9   „   r   z	POOLED n=z:  draws implied z.1fz	% actual z% (z=z+.2fz)  |  homes implied ú)zƒ  -> pooled calibration is the load-bearing check: if this is tight, the fair-value anchor is sound and per-league wobble is noise.zBfixtures needed to detect a calibration error of given size (95%):)é   g333333!@é
   é   z4.1fz
pp -> n = z5.0fz
FLAGGED (|z|>2 with n>=100): zV -- these leagues' CLV signal may not be trustworthy; investigate before trading them.z2
No league flagged as structurally mis-calibrated.)r+   r   r   r!   ÚprintÚMIN_Nr4   Úsortedr#   Úsumr/   ÚmaxÚabsr2   )ZallfZbyÚfZflaggedr   Úsr.   ZiHZiDZiAZcHZcDZcAZzHZzDZzAZworstÚflagZppr   r   r   Úmain`   s€    (
6ÿÿÿþþþþ
&ÿÿþþ
 rL   Ú__main__)r0   r1   )Ú__doc__Úsysr    r   r-   Úcollectionsr   r)   ÚinsertZkalshi_only_executionr$   r   r4   ÚargvÚintrD   r+   r/   r2   rL   Ú__name__r   r   r   r   Ú<module>   s   $ "
6