a
    ÜÎXj<  ã                   @   s¬   d Z ddlZddlZddlZddlmZ ddlmZ dZg d¢Zdd„ Z	d	d
„ Z
dd„ Zdd„ Zdd„ Zdd„ ZdddœZdd„ Zdd„ Zdd„ Zdd„ Zedkr¨eƒ  dS )ai
  
Does the RAW SIZE of the Kalshi-vs-Poly CLV gap predict profit, with NO band
gate at all (i.e. bet the cheaper book regardless of whether either leg
clears [5,12) vs Pinnacle)?  NO.

Distinct from two prior negative results this complements:
  - kalshi_poly_pure_deviation.py: fair = mean(kalshi,poly), no Pinnacle at all -> dead.
  - forward_test_unique_book.py fire_xlow(): the LIVE rule, which REQUIRES the
    cheap leg's clv-vs-Pinnacle to land in [5,12) AND the other leg to sit
    OUTSIDE that band.
This script asks the question in between: keep Pinnacle as the reference for
each leg's CLV (so it's not "pure" deviation), but drop the band gate
entirely and ask whether the raw GAP SIZE between clv_kalshi and clv_poly
(both vs pin fair) is itself predictive, continuously, regardless of where
either value falls.

METHOD: first pre-KO snapshot per fixture; for each side, cheap leg = lower-
priced book, gap = clv(cheap leg) - clv(other leg) [always >= 0]; pick the
side with the single largest gap; settle at the cheap book's ask (Kalshi
ask / Poly est-ask). Bet UNCONDITIONALLY -- no band requirement anywhere.

RESULT on poly_backfill (643 fixtures with a resolvable K/P gap, 2026-07-16):
  Disjoint gap buckets are NOT monotonic (the tell something's wrong):
    [0,2)%   n=305  ROI -4.0%   z-1.05
    [2,5)%   n=255  ROI +4.2%   z-0.01
    [5,8)%   n= 50  ROI -24.1%  z-0.86   <- dips hard, breaks monotonicity
    [8,12)%  n= 22  ROI -8.6%   z+0.86
    [12,20)% n= 11  ROI +77.5%  z+1.37   <- only n=11, ~7% implied (longshot)

  Cumulative thresholds all collapse to a handful of ultra-longshot bets as T
  rises (n: 643->83->33->8 as T: 0%->5%->8%->15%); the ONE "significant" cell
  (gap>=15%, n=8, ROI+144%, z+2.06, p=.02) is 8 bets at ~7% implied odds --
  one win at ~14:1 explains the whole result. Classic small-cell longshot mirage,
  same shape as the round-robin/sub-band traps already catalogued.

  OOS 60/40 split KILLS every threshold tried:
    gap>=0%: TRAIN -8.2% -> TEST +9.6% (sign flip, both near zero -- just noise)
    gap>=5%: TRAIN +5.6% -> TEST -23.9% (sign flip)
    gap>=8%: TRAIN -12.9% -> TEST +64.9% (sign flip, both small n)
  No threshold holds sign across the split.

VERDICT: raw cross-book CLV-gap magnitude carries NO exploitable edge without
the band gate. This is not a contradiction of the frozen strict-xlow rule --
it's confirmation that the band conditions (cheap leg IN [5,12), other leg
OUT of [5,12)) are doing real selection work, not just being an arbitrary
cutoff. Don't drop or loosen the band based on gap size alone.

Read-only, backtest only. Mirrors kalshi_poly_pure_deviation.py conventions.
é    N)Údatetime)ÚdefaultdictzE:\soccer\poly_backfill)z%Y-%m-%d %H:%M:%Sz%m/%d/%Y %H:%Mz%m/%d/%Y %H:%M:%Sz%Y-%m-%d %H:%Mc              
   C   s8   t D ].}zt | |¡W   S  ttfy0   Y q0 qd S ©N)Ú_FMTSr   ÚstrptimeÚ
ValueErrorÚ	TypeError)ÚsÚf© r   ú*E:\soccer\research\kalshi_poly_gap_size.pyÚ_pts9   s    r   c              	   C   s(   z
t | ƒW S  ttfy"   Y d S 0 d S r   )Úfloatr   r   )Úxr   r   r   Ú_f>   s    r   c                 C   s8   t | ƒD ]*}| d¡pd ¡  ¡ }|dv r|  S qd S )NÚftrÚ )ÚHÚDÚA)ÚreversedÚgetÚupperÚstrip)ÚrowsÚrÚvr   r   r   Ú_resultA   s    r   c                    s(   dd„ | D ƒ}t |ƒ‰ ‡ fdd„|D ƒS )Nc                 S   s   g | ]}d | ‘qS )ç      ð?r   )Ú.0r   r   r   r   Ú
<listcomp>G   ó    zfair_mult.<locals>.<listcomp>c                    s   g | ]}|ˆ  ‘qS r   r   )r   Zri©r	   r   r   r    G   r!   )Úsum)Úor   r   r"   r   Ú	fair_multF   s    r%   c                 C   s(   | dk rdS | dk rdS | dk r$dS dS )Nçš™™™™™©?gú~j¼t“h?g      Ð?g{®Gázt?gffffffÖ?gš™™™™™™?ç{®Gáz”?r   )Úmidr   r   r   Úhalf_spreadH   s    r)   c            
   	   C   s  t tƒ} t t› d�¡D ]h}t|ddd��F}t |¡D ]*}| | d¡| d¡| d¡f  |¡ q6W d   ƒ q1 sv0    Y  qg }|  	¡ D ]^\}}|d }|s¤qŽt
d	d
„ |D ƒdd„ d�}t|ƒ}	|rŽ|	rŽ| t||	dd
„ |D ƒd�¡ qŽ|jdd„ d� |S )Nz\*_polybackfill.csvr   zutf-8)ÚnewlineÚencodingZ	game_dateÚhomeÚawayr   c                 S   s>   g | ]6}|  d ¡pd ¡ dkrt|d ƒrt|d ƒ|f‘qS )Zstatusr   ZPREZ	timestamp)r   r   r   )r   r   r   r   r   r    X   s   "ÿzload.<locals>.<listcomp>c                 S   s   | d S )Nr   r   )Útr   r   r   Ú<lambda>Z   r!   zload.<locals>.<lambda>©Úkeyc                 S   s   g | ]\}}|‘qS r   r   )r   Ú_r   r   r   r   r    ]   r!   )Úgdr   Úprec                 S   s   | d S ©Nr3   r   )r
   r   r   r   r/   ^   r!   )r   ÚlistÚglobÚDIRÚopenÚcsvZ
DictReaderr   ÚappendÚitemsÚsortedr   ÚdictÚsort)
ZfixturesÚpathZfhr   Úoutr1   r   r3   r4   r   r   r   r   ÚloadN   s"    Hþ rB   é   )r,   r-   c                 C   s  | d D �]ô}t | d¡ƒt | d¡ƒt | d¡ƒ  }}}d|||fv s|dkr|dkr|dksdqt|||fƒ}g }t ¡ D �]d\}}t | d|› d�¡ƒ}	t | d|› d	�¡ƒ}
t | d
|› d�¡ƒ}|	du s~|du s~d|	  k rîdk r~n q~d|  k �r
dk �sq~ q~|	|k�rP|	|
�r@d|
  k �r8dk �r@n n|
n|	|  }}}n||t|ƒ |	  }}}|| | ||  }|| | ||  }|| }| d dk�r°|dk�pÄ| d dk�oÄ|dk}| t|||||| d d�¡ q~|rt|dd„ d�  S qdS )zžFirst pre-KO snapshot: pick the side/book with the LARGEST clv-vs-Pinnacle
    gap between Kalshi and Poly. No band requirement anywhere. Bet unconditionally.r4   Zpin_homeZpin_drawZpin_awayNé   Zkalshi_Z_midZ_askZpoly_Z_pricer   r   r   r,   r   r-   r3   )ÚclvÚgapÚsideÚpxÚwonr3   c                 S   s   | d S )NrF   r   )Údr   r   r   r/   u   r!   zpick_no_band.<locals>.<lambda>r0   )	r   r   r%   ÚSIDXr<   r)   r;   r>   Úmax)Zfxr   ZphZpd_ZpaZpfZcandsrG   ZsiZkmZkaZpmr(   rH   ÚotherrE   ZoclvrF   rI   r   r   r   Úpick_no_bandc   s"    .(,DB(. rN   c                 C   s¸   t | ƒ}|sd S tdd„ | D ƒƒ}tdd„ | D ƒƒ}tdd„ | D ƒƒ}tdd„ | D ƒƒ}|dkrr|| t |¡ nd}t|||| d || d || d |d	t |t d
¡ ¡ d�S )Nc                 s   s   | ]}|d  V  qdS )rI   Nr   ©r   Úbr   r   r   Ú	<genexpr>{   r!   zstat.<locals>.<genexpr>c                 s   s.   | ]&}|d  r"d|d  |d  ndV  qdS )rI   rD   rH   g      ð¿Nr   rO   r   r   r   rQ   |   r!   c                 s   s   | ]}|d  V  qdS )rH   Nr   rO   r   r   r   rQ   }   r!   c                 s   s"   | ]}|d  d|d    V  qdS )rH   rD   Nr   rO   r   r   r   rQ   }   r!   r   ç        éd   g      à?rC   )ÚnÚwÚwrÚiwrÚroiÚzÚp)Úlenr#   ÚmathZsqrtr>   Zerfc)ÚbetsrT   rU   ZnetZmuÚvarrY   r   r   r   Ústatx   s    $&ÿr_   c                 C   s’   |st d| d›d�ƒ d S 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 S )Nz  Z26sz (none)z n=rT   Z3drU   zW-zL  WR=rV   z4.1fz% (impl rW   z%)  ROI=rX   z+7.1fz%  z=rY   z+.2fz  p=rZ   z.4f)Úprint)Útagr	   r   r   r   Úline‚   s    @ÿÿÿÿrb   c                     s`  t ƒ } dd„ dd„ | D ƒD ƒ}|jdd„ d� tdt|ƒ› d	�ƒ td
ƒ dD ]D\‰‰‡‡fdd„|D ƒ}tdˆd d›dˆd d›d�t|ƒƒ qNtdƒ dD ]2‰ ‡ fdd„|D ƒ}tdˆ d d›d�t|ƒƒ q tdƒ dD ]z‰ t‡ fdd„|D ƒdd„ d�}tt|ƒd ƒ}tdˆ d d›d�t|d |… ƒƒ tdˆ d d›d�t||d … ƒƒ qàd S )Nc                 S   s   g | ]}|r|‘qS r   r   rO   r   r   r   r    ‰   r!   zmain.<locals>.<listcomp>c                 s   s   | ]}t |ƒV  qd S r   )rN   )r   r
   r   r   r   rQ   ‰   r!   zmain.<locals>.<genexpr>c                 S   s   | d S r5   r   ©rP   r   r   r   r/   Š   r!   zmain.<locals>.<lambda>r0   z4Total fixtures with a K/P gap on at least one side: Ú
z$=== Disjoint buckets by GAP SIZE ===))r   r'   )r'   r&   )r&   ç{®Gáz´?)re   ç¸…ëQ¸¾?)rf   çš™™™™™É?)rg   r   c                    s,   g | ]$}ˆ|d    kr ˆ k rn q|‘qS ©rF   r   rO   )ÚhiÚlor   r   r    �   r!   zgap [rS   z.0fú,z)%z<
=== Cumulative gap thresholds (bet if gap >= T, no cap) ===)	rR   r'   g¸…ëQ¸ž?r&   re   gš™™™™™¹?rf   g333333Ã?rg   c                    s   g | ]}|d  ˆ kr|‘qS rh   r   rO   ©ÚTr   r   r    ”   r!   zgap >= ú%z
=== OOS 60/40 check ===)rR   r&   re   c                    s   g | ]}|d  ˆ kr|‘qS rh   r   rO   rl   r   r   r    ™   r!   c                 S   s   | d S r5   r   rc   r   r   r   r/   ™   r!   g333333ã?zT=z% TRAINz% TEST)rB   r?   r`   r[   rb   r_   r=   Úint)ÚFr]   ÚsubZcutr   )rm   ri   rj   r   Úmain‡   s$    *$rr   Ú__main__)Ú__doc__r:   r7   r\   r   Úcollectionsr   r8   r   r   r   r   r%   r)   rB   rK   rN   r_   rb   rr   Ú__name__r   r   r   r   Ú<module>   s$   1

