a
    C7ej44  ã                   @   s4  d Z ddlZddlZddlZddlZddlmZ dZeej	ƒdkrPe
ej	d ƒndZdZdZd	Zd
ZddgfddgfddgfdddgfddgfdœZg d¢ZddddœZdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zd d!„ Zd"d#„ Zd$d%„ Zd&d'„ Zd(d)„ Zd*d+„ Zd,d-„ Zd.d/„ Zd0d1„ Z d2d3„ Z!e"d4k�r0e ƒ  dS )5ax	  
Which book is actually SHARPEST? (read-only, trades nothing)

THE QUESTION: pin_calibration_by_league.py proved Pinnacle is *calibrated* --
implied 25.0% draws really do land 25.5% of the time. That is NOT the same as
proving Pinnacle is *better than the exchanges*. Kalshi and Poly could be equally
calibrated, in which case CLV-vs-Pinnacle measures noise, not information, and
xlow's premise ("when the exchange is cheaper than Pinnacle, the exchange is
wrong") is unfounded.

Calibration is marginal (do 20% shots win 20% of the time). Sharpness is
conditional (does the book separate the 15% games from the 25% games). A book
quoting the league base rate every time is perfectly calibrated and useless. So
we score with PROPER SCORING RULES, which reward both at once.

METHOD -- two controls that the first cut of this script got wrong, both of which
flipped the headline:

  1. SAME-SNAPSHOT PAIRING. Every pairwise comparison uses the last pre-KO row
     where BOTH books have a complete quote. Scoring each book at its own last
     quote silently hands an information edge to whichever book happens to quote
     nearest kickoff -- and the exchanges quote later than Pinnacle far more
     often than the reverse.
  2. OUTCOME-INDEPENDENT DISAGREEMENT. Disagreement is total-variation distance
     over all three outcomes, computed before the result is known. Selecting
     fixtures where the books differed *on the outcome that happened* is
     conditioning on the answer and manufactures a gap out of nothing.

  - Every book devigged identically (proportional normalisation to sum 1) so no
    book is advantaged by the devig choice. Pinnacle/B365 quote decimal odds
    (raw = 1/odds); Kalshi/Poly/Poly-US quote probabilities (raw = mid).
  - Log loss (primary; strictly proper, punishes confident errors) and Brier
    (secondary; bounded, less tail-sensitive), over all 3 outcomes.
  - Paired t-stat on the per-fixture log-loss difference. Fixture difficulty
    varies enormously and cancels in the difference, so paired is far more
    powerful than comparing the two means.

READING THE RESULT
  Lower log loss = sharper. The paired t is the honest test; a raw log-loss gap
  without it is meaningless at these samples. If Pinnacle does NOT beat the
  exchanges, xlow's premise is unsupported -- a far bigger finding than any
  bet-level ROI.

Usage:  python research\book_accuracy_compare.py [min_n]
é    N)Údefaultdict)z*E:\soccer\poly_backfill\*_polybackfill.csvzE:\soccer\live_data\*.csvé   é   g¸…ëQ¸ž?)gš™™™™™©?g¸…ëQ¸¾?z
2026-07-05g�íµ ÷Æ°>Úodds)Zpin_homeZpin_drawZpin_away)Z	b365_homeZ	b365_drawZ	b365_awayZprob)Zkalshi_home_midZkalshi_draw_midZkalshi_away_mid)Zpoly_home_midZpoly_draw_midZpoly_away_mid)Zpoly_home_priceZpoly_draw_priceZpoly_away_price)Zpolyus_home_midZpolyus_draw_midZpolyus_away_mid)ÚpinnacleZb365ZkalshiZpolyZpolyusé   ©ÚHÚDÚAc              	   C   s(   z
t | ƒW S  ttfy"   Y d S 0 d S ©N)ÚfloatÚ	TypeErrorÚ
ValueError)Úx© r   ú+E:\soccer\research\book_accuracy_compare.pyÚ_fN   s    
r   c                 C   s4   | D ]*}|  d¡pd ¡  ¡ }|dv r|  S qd S )NÚftrÚ r   )ÚgetÚstripÚupper)ÚrowsÚrÚvr   r   r   Ú_resultU   s
    
r   c                    s€   |dkr.t dd„ | D ƒƒrdS dd„ | D ƒ}nt dd„ | D ƒƒrDdS t| ƒ}t|ƒ‰ dˆ   k rhd	k snn dS ‡ fd
d„|D ƒS )zFProportional devig -> probabilities summing to 1, or None if unusable.r   c                 s   s   | ]}|d u p|dkV  qd S )Nç      ð?r   ©Ú.0r   r   r   r   Ú	<genexpr>`   ó    zdevig.<locals>.<genexpr>Nc                 S   s   g | ]}d | ‘qS )r   r   r   r   r   r   Ú
<listcomp>b   r!   zdevig.<locals>.<listcomp>c                 s   s0   | ](}|d u p&d|  k o dk n   V  qd S )Nç        r   r   r   r   r   r   r    d   r!   gš™™™™™é?gš™™™™™ù?c                    s   g | ]}|ˆ  ‘qS r   r   r   ©Úsr   r   r"   j   r!   )ÚanyÚlistÚsum)ÚrawÚkindÚimpr   r$   r   Údevig]   s    r,   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|ƒ}|tvrêq¾d
d„ |D ƒ}	|	�s q¾g }
|	D ]j‰ i }t ¡ D ]F\}\}}|D ]2}t‡ fdd„|D ƒ|ƒ}|�r(|||<  �q�q(�q|�r|
 	|¡ �q|
�s|q¾| |¡ |  	t|d	 |d ||
d�¡ q¾q q| S )zJOne record per fixture: {ftr, snaps:[{book: probs}, ...]} in pre-KO order.r   zutf-8)ÚnewlineÚencodingZ	game_dateZleagueÚhomeZawayNr   c                 S   s&   g | ]}|  d ¡pd ¡ dkr|‘qS )Zstatusr   ZPRE)r   r   )r   r   r   r   r   r"   €   r!   zload.<locals>.<listcomp>c                    s   g | ]}t ˆ  |¡ƒ‘qS r   )r   r   )r   Úc©r   r   r   r"   ˆ   r!   r   )ÚgdZlgr   Úsnaps)ÚsetÚSOURCESÚglobr   r'   ÚopenÚcsvZ
DictReaderr   ÚappendÚ	ExceptionÚitemsr   ÚIDXÚBOOKSr,   ÚaddÚdict)ÚoutÚseenZpatÚpathÚfxZfhÚkeyr   r   Zprer3   ÚdÚbookr*   ZcolsetsZcolsÚpr   r1   r   Úloadm   sN    
ÿÿ(

$rH   c                 C   s*   t | d ƒD ]}||v r||   S qd S )Nr3   ©Úreversed)rC   rF   rE   r   r   r   Ú	last_solo•   s    rK   c                 C   s:   t | d ƒD ](}||v r||v r|| || f  S qdS )zILast pre-KO snapshot where BOTH books quote -- the fair comparison point.r3   NrI   )rC   ÚaÚbrE   r   r   r   Ú	last_bothœ   s    rN   c                 C   s   t  t| t|  tƒ¡ S r   )ÚmathÚlogÚmaxr<   ÚEPS©rG   r   r   r   r   Úlogloss¤   s    rT   c                    s   t ‡ ‡fdd„tdƒD ƒƒS )Nc                 3   s.   | ]&}ˆ| |t ˆ  krd nd d V  qdS )r   r#   r   N)r<   ©r   Úi©r   rG   r   r   r    ©   r!   zbrier.<locals>.<genexpr>é   ©r(   ÚrangerS   r   rW   r   Úbrier¨   s    r[   c                    s    dt ‡ ‡fdd„tdƒD ƒƒ S )z=Total-variation distance -- outcome-independent disagreement.g      à?c                 3   s"   | ]}t ˆ | ˆ|  ƒV  qd S r   )ÚabsrU   ©rG   Úqr   r   r    ®   r!   ztvd.<locals>.<genexpr>rX   rY   r]   r   r]   r   Útvd¬   s    r_   c                 C   s   | rt | ƒt| ƒ S tdƒS )NÚnan)r(   Úlenr   )Zxsr   r   r   Úmean±   s    rb   c                    sd   t | ƒ}|dk rtdƒS t| ƒ‰ t‡ fdd„| D ƒƒ|d  }t || ¡}|dkr\ˆ | S tdƒS )NrX   r`   c                 3   s   | ]}|ˆ  d  V  qdS ©r   Nr   )r   rE   ©Úmr   r   r    º   r!   zpaired_t.<locals>.<genexpr>r   r   )ra   r   rb   r(   rO   Úsqrt)ÚdiffsÚnÚvarZser   rd   r   Úpaired_tµ   s    rj   c                 C   s8   t  | ¡rdS t| ƒdk rdS | dkr*dS | ¡ › d�S )Nzn/ag\�Âõ(\ÿ?zno detectable differencer   zPINNACLE SHARPERz SHARPER)rO   Zisnanr\   r   )ÚtrF   r   r   r   Úverdict_for¿   s
    
rl   c                 C   s¢  t d|› �ƒ t d|› �ƒ t ddd›ddd›ddd	›dd
d	›ddd›ddd›ddd›d�ƒ tD �]8}|dkrrqbg g  }}| D ]R}t|d|ƒ}|s–q€|\}	}
||	|
ƒsªq€| t|	|d ƒ¡ | t|
|d ƒ¡ q€t|ƒ}|tk �rt d|d›d|d›dt› d�ƒ qbdd„ t||ƒD ƒ}t|ƒ}t	dd„ t||ƒD ƒƒ}t d|d›d|d›dt
|ƒd›dt
|ƒd›dt
|ƒd›d|d›d|| d d›d t||ƒ› �ƒ qbd S )!NÚ
ú  rF   Ú9sú rh   ú>5sZpin_LLú>8sZbook_LLZdiffú>9srk   z>7sZ
pin_closerú>10sz	  verdictr   r   Ú5dú  (below min_n=ú)c                 S   s   g | ]\}}|| ‘qS r   r   ©r   r   Úyr   r   r   r"   Ý   r!   z report_pairs.<locals>.<listcomp>c                 s   s   | ]\}}||k rd V  qdS ©r   Nr   rx   r   r   r   r    ß   r!   zreport_pairs.<locals>.<genexpr>ú8.4fz+9.4fz+7.2féd   z9.0fz%  )ÚprintÚORDERrN   r9   rT   ra   ÚMIN_NÚziprj   r(   rb   rl   )ÚfixturesZ	subset_fnÚtitleZnoterM   ZplZblrC   ÚpairÚppÚbprh   rg   rk   Zwinsr   r   r   Úreport_pairsÇ   sD    ,ÿÿ




 8ÿÿÿr†   c                     sz  t ƒ } tdƒ tdt| ƒ› d�ƒ tdƒ tdd›ddd	›dd
d›ddd›�ƒ tD ]”‰ ‡ fdd„| D ƒ}t|ƒtk rštˆ d›dt|ƒd›dt› d�ƒ qTtˆ d›dt|ƒd›dt‡ fdd„|D ƒƒd›dt‡ fdd„|D ƒƒd›�ƒ qTtdd›ddd›dt d¡d›ddd›d�ƒ t| dd„ dd ƒ t| d!d„ d"t	d#›d$�d%ƒ t
| ƒ td&ƒ td'ƒ td(ƒ td)ƒ td*ƒ d S )+NzEBook accuracy comparison  (proportional devig; SAME-SNAPSHOT pairing)z/fixtures with a result and >=1 complete quote: rm   zˆSTANDALONE (each book on every fixture it quotes -- samples DIFFER and quote times differ, so NOT comparable across rows; coverage only)rF   Z10srp   rh   rq   rT   rs   r[   rr   c                    s   g | ]}t |ˆ ƒr|‘qS r   )rK   ©r   Úf©rM   r   r   r"   í   r!   zmain.<locals>.<listcomp>ru   rv   rw   c                    s    g | ]}t t|ˆ ƒ|d  ƒ‘qS ©r   )rT   rK   r‡   r‰   r   r   r"   ò   r!   z9.4fc                    s    g | ]}t t|ˆ ƒ|d  ƒ‘qS rŠ   )r[   rK   r‡   r‰   r   r   r"   ó   r!   r{   z1/3 eachr   Z5srX   gUUUUUUå?z   uninformative baselinec                 S   s   dS )NTr   ©r„   r…   r   r   r   Ú<lambda>ø   r!   zmain.<locals>.<lambda>z)PAIRED vs PINNACLE -- ALL shared fixtureszodiff = book_LL - pinnacle_LL;  POSITIVE => Pinnacle sharper. pin_closer = % of fixtures Pinnacle scored better.c                 S   s   t | |ƒtkS r   )r_   ÚDISAGREE_MINr‹   r   r   r   rŒ   þ   r!   z0PAIRED vs PINNACLE -- DISAGREEMENT ONLY (TVD >= ú.0%z, computed WITHOUT the result)zPxlow only fires when the books disagree, so this is the decision-relevant slice.zW
SCALE (do not eyeball this -- an early draft of this script guessed it wrong by ~30x):zG  a 1pp systematic error on a ~22% outcome costs only ~0.0003 log loss.z[  So the paired test above can only resolve errors of roughly 3pp; a book that is genuinelyzY  1pp sharper than another is INVISIBLE at these sample sizes. "No detectable difference"z/  means "no LARGE difference", not "identical".)rH   r}   ra   r~   r   rb   rO   rP   r†   r�   Úband_premise)r�   r%   r   r‰   r   Úmainä   s@    &"ÿþ
.ýür�   c                 C   s¸  t dƒ t dtd d›dtd d›d�ƒ t dt› d	�ƒ t d
ƒ 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›�ƒ tD �]}|dkržqŽddd„ fdd d„ ffD �]è\}}g }| D ]¼}||d! pØd"ƒsÞqÈt|d|ƒ}|sðqÈ|\}}td#ƒD ]‚}	||	 dk�r�q ||	 ||	  ||	  }
td |
  k�rNtd k �r n n.| ||	 ||	 t|d$  |	k�rxdndf¡ �q qÈt|ƒ}|d%k �r¼t d|d›d|d›d|d&›d'�ƒ q¶d(d)„ |D ƒ}d*d)„ |D ƒ}t	d+d,„ |D ƒƒ}t	d-d,„ |D ƒƒ}|dk�r|t	|ƒ t
 |¡ ntd.ƒ}t	d/d,„ |D ƒƒ| }t d|d›d|d›d|d&›dt|ƒd0 d1›d2t|ƒd0 d3›d2|| d0 d4›d2|d5›d|d0 d6›d7�ƒ q¶qŽt d8ƒ t d9ƒ d:S );a?  THE test of xlow's premise, on all fixtures rather than just our bets.

    xlow bets an outcome when an exchange prices it 5-12% BELOW Pinnacle fair
    (relative). The premise is that Pinnacle is right, so the outcome should
    occur near PINNACLE's rate, not the exchange's. Selection is PRICES ONLY --
    never the result -- and uses every qualifying fixture-outcome, which is
    several times the bet-level sample.

    Caveat: a fixture can contribute more than one outcome, so rows are not
    fully independent; in practice multi-qualifying fixtures are rare.
    z;
XLOW PREMISE TEST -- outcome-blind selection, all fixturesz  exchange prices outcome i r   rŽ   ú-r   z6 BELOW Pinnacle fair. Whose number does reality match?z  SPLIT BY FREEZE_DATE z4: the band was CHOSEN on the in-sample rows, so onlyzg  the OOS rows are evidence. Reporting pooled numbers here was a real mistake once -- do not repeat it.rn   rF   ro   rp   ÚsliceZ6srh   rq   Zpin_saysrs   Z	book_saysrt   Zactualrr   Z	z_vs_bookzROI@midr   ÚINc                 S   s   | t kS r   ©ÚFREEZE_DATE©Úgr   r   r   rŒ   '  r!   zband_premise.<locals>.<lambda>ZOOSc                 S   s   | t kS r   r”   r–   r   r   r   rŒ   (  r!   r2   r   rX   r   é
   ru   z  (too few)c                 S   s   g | ]}|d  ‘qS )r   r   ©r   r%   r   r   r   r"   ;  r!   z band_premise.<locals>.<listcomp>c                 S   s   g | ]}|d  ‘qS )r   r   r™   r   r   r   r"   <  r!   c                 s   s   | ]}|d  V  qdS rc   r   r™   r   r   r   r    =  r!   zband_premise.<locals>.<genexpr>c                 s   s   | ]}|d |  V  qdS rz   r   )r   rG   r   r   r   r    >  r!   r`   c                 s   s(   | ] \}}}|rd | d  ndV  qdS )r   g      ð¿Nr   )r   Ú_ZpxÚwr   r   r   r    @  r!   r|   z8.2fz% z9.2fz7.2fz+10.2fz+7.1fú%zY  Positive z_vs_book (actual ABOVE the exchange price) = exchange too cheap = xlow works.zX  ROI is mid-to-mid and ignores the spread; real fills are at the ask, so live is worse.N)r}   ÚBANDr•   r~   rN   rZ   r9   r<   ra   r(   rO   rf   r   rb   )r�   rF   ZlabelZkeepZselrC   rƒ   r„   r…   rV   Zrelrh   Zpin_pZbk_pÚhitsri   ÚzZroir   r   r   r�     sh    ",ÿÿÿ


ÿ$4
"(,ÿÿÿþr�   Ú__main__)#Ú__doc__Úsysr8   r6   rO   Úcollectionsr   r5   ra   ÚargvÚintr   r�   r�   r•   rR   r=   r~   r<   r   r   r,   rH   rK   rN   rT   r[   r_   rb   rj   rl   r†   r�   r�   Ú__name__r   r   r   r   Ú<module>   sL   - 	ÿú(
+;
