a
    Éßij„  ã                   @   sn   d Z ddlZddlZddlmZ ddlZej dd¡ ddlZ	dhZ
dd„ Zdd	„ Zd
d„ Zedkrjeƒ  dS )a¦  Can a multi-book consensus stand in for Pinnacle as the MAKER's fair value?

WHY THIS MATTERS. mm_quoter skips any fixture without a Pinnacle line. On
2026-07-29 that cost 4 of 10 in-window fixtures -- all four had healthy 3-leg
Kalshi books and were dropped purely because API-Football's relay carries no
Pinnacle for them. Coverage is 92-100% per league, so these are a genuine tail,
not a timing lag (Internacional and Mirassol, same league and day, do have it).

THE ARGUMENT FOR A FALLBACK. The betting strategy needs Pinnacle specifically --
its entire edge is defined as a deviation FROM Pinnacle, so substituting a worse
book would change what is being measured. The market maker does not: it needs a
LEVEL to quote around, and its ask is capped at the market's own ask anyway. A
consensus of 8-11 other bookmakers is plausibly good enough for that.

THE TEST. On fixtures where Pinnacle IS present, build the consensus WITHOUT it
and measure how far the consensus fair lands from Pinnacle's fair, in cents of
implied probability. That is the error we would be introducing on the fixtures
where we currently quote nothing at all.

Judge it against the quantities that matter, not against zero:
  - the 1c tick: an error below ~0.5c cannot move our quoted price at all
  - the typical margin we add: 2-6c
  - FAIR_TOLERANCE_C = 2.0, the point at which mm_quoter refuses to sell

Usage:  python research\consensus_fair_test.py [n_fixtures]
é    N)ÚdefaultdictzE:\soccer\mainÚpinnaclec                    s(   dd„ | D ƒ}t |ƒ‰ ‡ fdd„|D ƒS )Nc                 S   s   g | ]}d | ‘qS )ç      ð?© ©Ú.0Úxr   r   ú)E:\soccer\research\consensus_fair_test.pyÚ
<listcomp>(   ó    zdevig.<locals>.<listcomp>c                    s   g | ]}|ˆ  ‘qS r   r   r   ©Úsr   r	   r
   *   r   )Úsum)ÚoÚrr   r   r	   Údevig'   s    r   c                    sØ   z¼t jtj› d�tj| ddœdd�}i }| ¡  dg ¡D ]~}| dg ¡D ]l}| dg ¡D ]Z}| d	¡dkrlqXd
d„ |d D ƒ‰ t‡ fdd„dD ƒƒrXˆ d ˆ d ˆ d g||d < qXqHq8|W S  tyÒ   i  Y S 0 dS )z>{book_name: [h,d,a] decimal odds} for the match-winner market.z/oddsé   )ÚfixtureÚbeté   )ZheadersÚparamsZtimeoutZresponseZ
bookmakersZbetsÚidc                 S   s"   i | ]}|d    ¡ t|d ƒ“qS )ÚvalueZodd)ÚlowerÚfloatr   r   r   r	   Ú
<dictcomp>8   r   zbooks_for.<locals>.<dictcomp>Úvaluesc                 3   s   | ]}|ˆ v V  qd S )Nr   )r   Úk©Úvr   r	   Ú	<genexpr>9   r   zbooks_for.<locals>.<genexpr>)ÚhomeÚdrawÚawayr!   r"   r#   ÚnameN)ÚrequestsÚgetÚLMZAF_BASEZ
AF_HEADERSZjsonÚallÚ	Exception)Úfidr   ÚoutÚeÚbkr   r   r   r	   Ú	books_for-   s     
ÿ$r.   c                     s  t tjƒdkrttjd ƒnd} t ¡ \}}g d }}|D ]ò}t |ƒ| krP �q.| d¡}|s`q:t|ƒ}tdd„ | 	¡ D ƒd ƒ}dd„ | 	¡ D ƒ}	|r¢t |	ƒd	k r¬|d7 }q:t
|ƒ}
d
d„ |	 ¡ D ƒ‰ ‡ fdd„td	ƒD ƒ}t|ƒ‰‡fdd„|D ƒ}| | d¡|d t |	ƒdd„ t|
|ƒD ƒ|
|dœ¡ q:|�s@tdƒ d S tt |ƒ› d|› d�ƒ tddd›ddd›ddd›ddd›ddd›ddd›�ƒ |d d … D ]l}|d! }tdt|d" ƒd›d|d d d … d›d|d# d$›d|d d%›d&|d d%›d&|d' d%›d(�ƒ �qœd)d„ |D ƒ}| ¡  td*t |ƒ› d+�ƒ td,t |¡d-›d.|tt |ƒd/ ƒ d-›d0|tt |ƒd1 ƒ d-›d2t|ƒd-›d(�	ƒ td3td4d„ |D ƒƒt |ƒ d5 d6›d7�ƒ td8td9d„ |D ƒƒt |ƒ d5 d6›d7�ƒ td:td;d„ |D ƒƒt |ƒ d5 d6›d7�ƒ td<ƒ d S )=Nr   é(   r   r*   c                 s   s"   | ]\}}d |  ¡ v r|V  qdS )r   N)r   ©r   r   r   r   r   r	   r    K   r   zmain.<locals>.<genexpr>c                 S   s.   i | ]&\}}|  ¡ tvrd |  ¡ vr||“qS )r   )r   ÚEXCLUDEr0   r   r   r	   r   L   s   ÿzmain.<locals>.<dictcomp>é   c                 S   s   g | ]}t |ƒ‘qS r   )r   )r   r   r   r   r	   r
   U   r   zmain.<locals>.<listcomp>c                    s$   g | ]‰ t  ‡ fd d„ˆD ƒ¡‘qS )c                    s   g | ]}|ˆ  ‘qS r   r   )r   Úd©Úir   r	   r
   V   r   z#main.<locals>.<listcomp>.<listcomp>)Ú
statisticsÚmedian)r   )Údeviggedr4   r	   r
   V   r   c                    s   g | ]}|ˆ  ‘qS r   r   )r   Úcr   r   r	   r
   X   r   Zlcr!   c                 S   s    g | ]\}}t || ƒd  ‘qS )éd   )Úabs)r   ÚaÚbr   r   r	   r
   Z   r   )Údivr!   Ún_booksÚdiff_cÚpfÚconsz2no fixtures with both Pinnacle and >=3 other booksz+ fixtures with Pinnacle + >=3 other books (z
 skipped)
z  ZlgZ5sú r   Z18sZbooksz>5sz>7sr"   r#   é   r@   r>   r?   Z5dz6.2fzc é   r9   c                 S   s   g | ]}|d  D ]}|‘qqS )r@   r   )r   r   r   r   r   r	   r
   g   r   z!
  |consensus - Pinnacle| across z legs:z    median z.2fzc   p75 g      è?zc   p90 gÍÌÌÌÌÌì?zc   max z1    within 0.5c (cannot move our 1c-tick quote): c                 s   s   | ]}|d k rdV  qdS )g      à?r   Nr   r   r   r   r	   r    n   r   r:   z.0fú%z    within 1.0c: c                 s   s   | ]}|d k rdV  qdS )r   r   Nr   r   r   r   r	   r    o   r   z1    beyond 2.0c (would breach FAIR_TOLERANCE_C): c                 s   s   | ]}|d krdV  qdS )g       @r   Nr   r   r   r   r	   r    q   r   z<
  Reference: our staged margin is 2-6c, and the tick is 1c.)ÚlenÚsysÚargvÚintr'   Zload_watchlist_filer&   r.   ÚnextÚitemsr   r   Úranger   ÚappendÚzipÚprintÚstrÚsortr6   r7   Úmax)ZwantÚ_ZmsZrowsZskippedÚmr*   r-   ZpinZothersrA   rB   r   r3   Zalldr   )r8   r   r	   Úmain@   sp     

þ
84ÿÿÿÿþþ
ÿ
,ÿ
rV   Ú__main__)Ú__doc__r6   rH   Úcollectionsr   r%   ÚpathÚinsertZlive_monitorr'   r1   r   r.   rV   Ú__name__r   r   r   r	   Ú<module>   s   5