a
    ·™hjö  ã                   @   sf   d Z ddlZddlmZ ej dd¡ ddlZejZdd„ Z	dd„ Z
d	d
„ Zdd„ Zedkrbeƒ  dS )a¤  Is xlow's edge real, or is it just measuring a stale Pinnacle line?

No new data feed needed -- this answers the question with what we already have.

THE HYPOTHESIS. API-Football's "Pinnacle" repricess about once every 5.8h while
Kalshi moves every ~2.3h (measured 2026-07-28, 147 fixtures). A line that has not
moved for hours drifts away from a live exchange through pure time-lag, and that
drift is indistinguishable, in the CLV formula, from genuine mispricing. If that
is what xlow has been trading, then:

  (a) signals should fire disproportionately on OLD lines, and
  (b) bets entered on old lines should perform WORSE than bets on fresh ones.

(b) is the decisive one, because it uses realised results rather than theory.

LINE AGE = time since that fixture's (pin_home, pin_draw, pin_away) triple last
changed value, as of the snapshot the bet fired on. Age is measured only from the
first snapshot we have for the fixture, so early bets have censored ages -- those
are reported separately rather than silently counted as "fresh".

If old-line bets are the losers, the fix is free and immediate: gate xlow on line
age. That is worth knowing BEFORE paying for a real-time feed.
é    N)ÚdefaultdictzE:\soccer\researchc           
      C   sº   g dddf\}}}}| d D ]˜}t  | d¡¡}|du rH| |df¡ q|du rT|}| d¡| d¡| d¡f}||kr‚|| }}|r–||  ¡ d nd}||k}	| |||	ff¡ q|S )z@[(snapshot_row, age_hours_or_None)] for a fixture's pre-KO rows.NZpreZ	timestampÚpin_homeÚpin_drawÚpin_awayi  )ÚFZ_ptsÚgetÚappendZtotal_seconds)
ÚfxÚoutZlast_valZlast_tZfirst_tÚrÚtÚvÚageÚcensored© r   ú&E:\soccer\research\xlow_vs_line_age.pyÚ
age_series!   s    
r   c              	   C   sV  t | ƒD �]F\}}t | d¡¡t | d¡¡t | d¡¡  }}}d|||fv s|dkr|dkr|dksnqt |||f¡}g }tj ¡ D �]x\}}	t |d|¡\}
}t |d|¡\}}|
du sŒ|du sŒd|
  k rÞdk rŒn qŒd|  k rödk súqŒ qŒ|
|k�r|
||  }}}n|||
  }}}|du sŒd|  k �rFdk �sJqŒ qŒ||	 | ||	  }||	 | ||	  }||k rŒtd |  k�rštd k �sžqŒ qŒtd |  k�r¾td k �rÄqŒ nqŒ| d	 d
k�rÜ|dk�pð| d	 dk�oð|dk}| 	t
|||d�¡ qŒ|rt|dd„ d�}|�r*|d nd|d< |�r@|d nd|d< |  S qdS )zGRe-run fire_xlow, returning the winning bet plus the line age at entry.r   r   r   Né   ZkalshiZpolyr   ZftrÚHÚhomeÚAZaway)ÚclvÚpxÚwonc                 S   s   | d S )Nr   r   )Údr   r   r   Ú<lambda>P   ó    zfire_with_age.<locals>.<lambda>)Úkeyr   Tr   )r   r   Z_fr   Z	fair_multZSIDXÚitemsZ
book_quoteÚBANDr   ÚdictÚmax)r	   r   ÚmetaZphZpd_ZpaZpfZcandsZsideZsiZkmZkaZpmZpa_Zmidr   Úotherr   Zoclvr   Úbr   r   r   Úfire_with_age4   s<    4&>
"*$.
r%   c                 C   st   t  |¡}|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 S )Nz  Z22sz n=0z n=ÚnZ3dz  WR Úwrz5.1fz%  impl Ziwrz%  ROI Zroiz+7.1fz%  z Úzz+5.2f)r   ÚstatÚprint)ÚnameÚbetsÚsr   r   r   ÚblockW   s    
0ÿÿr.   c                     sè  t  ¡ } dd„ | D ƒ}dd„ dd„ |D ƒD ƒ}dd„ |D ƒ}tt|ƒ› dt|ƒ› dt|ƒ› d	�ƒ d
d„ |D ƒ}dd„ |D ƒ}tt|ƒ› dt|ƒ› d�ƒ |�rºtdd„ |D ƒƒ}tdƒ td|d d›d|t|ƒd  d›d|d d›�ƒ tdtdd„ |D ƒƒt|ƒ d d›d�ƒ tdƒ g d¢}|D ]0\‰‰ tˆ› dˆ › d�‡ ‡fd d„|D ƒƒ �q.tƒ  |t|ƒd  ‰td!ˆd›d"�‡fd#d„|D ƒƒ td$ˆd›d"�‡fd%d„|D ƒƒ tƒ  td&|ƒ td'|ƒ td(ƒ td)ƒ d S )*Nc                 S   s   g | ]}|d  t jkr|‘qS )Úgd)r   ZFREEZE_DATE©Ú.0r	   r   r   r   Ú
<listcomp>b   r   zmain.<locals>.<listcomp>c                 S   s   g | ]}|r|‘qS r   r   ©r1   r$   r   r   r   r2   c   r   c                 s   s   | ]}t |ƒV  qd S )N)r%   r0   r   r   r   Ú	<genexpr>c   r   zmain.<locals>.<genexpr>c                 S   s   g | ]}|  d ¡dur|‘qS ©r   N)r   r3   r   r   r   r2   d   r   z OOS fixtures -> z xlow bets, z with a measurable line age
c                 S   s   g | ]}|d  s|‘qS ©r   r   r3   r   r   r   r2   h   r   c                 S   s   g | ]}|d  r|‘qS r6   r   r3   r   r   r   r2   i   r   z! bets on a line we WATCHED move; z8 censored (line had not moved since our first snapshot)
c                 s   s   | ]}|d  V  qdS r5   r   r3   r   r   r   r4   n   r   z6LINE AGE AT ENTRY (hours since Pinnacle last repriced)z  min r   z.1fz	  median é   z  max éÿÿÿÿz#  share entered on a line >3h old: c                 s   s   | ]}|d krdV  qdS )é   r   Nr   )r1   Úar   r   r   r4   s   r   éd   z.0fz%
z,PERFORMANCE BY LINE AGE  (the decisive test)))r   r   )r   r9   )r9   é   )r<   iç  ú-zh oldc                    s,   g | ]$}ˆ|d    kr ˆ k rn q|‘qS ©r   r   r3   )ÚhiÚlor   r   r2   x   r   zfresher than Úhc                    s   g | ]}|d  ˆ k r|‘qS r>   r   r3   ©Úmedr   r   r2   {   r   zstaler than c                    s   g | ]}|d  ˆ kr|‘qS r>   r   r3   rB   r   r   r2   |   r   r   zALL (control)zD
If the stale buckets carry the losses, xlow has been partly tradingz>lag, and a line-age gate is a free fix that needs no new feed.)r   Zload_fixturesr*   ÚlenÚsortedÚsumr.   )ZpreppedZoosr,   ZdatedZknownZcensZagesZcutsr   )r?   r@   rC   r   Úmain`   s@    ÿ
$ÿÿ
*""

rG   Ú__main__)Ú__doc__ÚsysÚcollectionsr   ÚpathÚinsertZforward_test_unique_bookr   r   r   r%   r.   rG   Ú__name__r   r   r   r   Ú<module>   s   #	$