a
    ÝÜ�j  ã                   @   sš   d Z ddlZddlZddlZddlZddlZej dd¡ ej dd¡ ddlZ	dZ
dZdZdZd	d
„ Zdd„ Zdd„ Zdd„ Zdd„ Zedkr–eƒ  dS )ab  Does Kalshi lag Pinnacle, and is the gap big enough to take?

The maker is answered: 3.7% completion whatever we change, 2-4c a completion,
20-42c a stranded favourite, and Kalshi's rebate programme excludes Sports. So
this asks the other question the same data can answer -- not resting orders and
waiting, but crossing the spread when Kalshi is wrong.

The mechanism is specific. Pinnacle is the sharpest book in football; its line
IS fair value for practical purposes. Kalshi is a retail exchange that has to
find out. If Kalshi's price still reflects Pinnacle's OLD number some minutes
after Pinnacle moved, that gap is takeable.

What has to be true for it to pay:
  * the gap must exceed the TAKER fee, ~7% x p(1-p), so ~1.75c at even money
  * it must be biggest right after Pinnacle moves and decay after -- otherwise
    it is not lag, it is just Kalshi disagreeing, and disagreement is not edge
  * it must survive on liquid legs, not only on ones nobody trades

Measured on live_data: pin_home/draw/away are Pinnacle decimal odds, the
kalshi_*_mid columns are Kalshi. Pre-kickoff only, so no in-play confusion.
é    NzE:\soccer\mainzE:\soccer\researchzE:\soccer\live_data)ÚhomeZdrawÚawayé   gìQ¸…ë±?c                 C   s   t |  d|   S )Nç      ð?)Ú
TAKER_RATE)Úp© r   ú$E:\soccer\research\pin_kalshi_lag.pyÚ	taker_fee"   s    r
   c              	   C   sH   z*t | ƒ}||  k r|k r&n n|nd W S  ttfyB   Y d S 0 d S ©N)ÚfloatÚ	TypeErrorÚ
ValueError)ÚxÚloÚhiÚvr   r   r	   Ú_f&   s
    "r   c                    s(   dd„ | D ƒ}t |ƒ‰ ‡ fdd„|D ƒS )zBPinnacle decimal odds -> de-vigged probabilities (multiplicative).c                 S   s   g | ]}d | ‘qS )r   r   )Ú.0Úor   r   r	   Ú
<listcomp>0   ó    znovig.<locals>.<listcomp>c                    s   g | ]}|ˆ  ‘qS r   r   )r   Úi©Úsr   r	   r   2   r   )Úsum)ZoddsÚinvr   r   r	   Únovig.   s    r   c                     sv  t dd„ t t¡D ƒƒt d… } t t¡}| D �]>}ttj	 
t|¡ddd���}t |¡D ]ò‰t ˆ d¡¡}ˆ d¡}|du s\|d	v rŒq\zt|ƒd
kržW q\W n ty¶   Y q\Y n0 ‡fdd„tD ƒ}‡fdd„tD ƒ‰tdd„ |D ƒƒs\tdd„ ˆD ƒƒ�rq\t|ƒ‰ ˆ d¡ˆ d¡ˆ d¡f}||  |‡ ‡fdd„ttƒD ƒf¡ q\W d  ƒ q01 �sf0    Y  q0|S )zC{fixture: [(ts, {side: (pin_fair, kalshi_mid)})]} pre-kickoff only.c                 s   s   | ]}|  d ¡r|V  qdS )z.csvN)Úendswith)r   Úfr   r   r	   Ú	<genexpr>7   r   zload.<locals>.<genexpr>Nzutf-8Úreplace)ÚencodingÚerrorsZ	timestampÚelapsed)NÚ r   c                    s$   g | ]}t ˆ  d |› �¡ddƒ‘qS )Zpin_g)\�Âõ(ð?éÈ   ©r   Úget©r   r   ©Úrr   r	   r   F   r   zload.<locals>.<listcomp>c                    s&   g | ]}t ˆ  d |› d�¡ddƒ‘qS )Zkalshi_Z_midr   é   r'   r)   r*   r   r	   r   G   r   c                 s   s   | ]}|d u V  qd S r   r   ©r   r   r   r   r	   r    H   r   Z	game_dater   r   c                    s"   i | ]\}}|ˆ | ˆ| f“qS r   r   )r   r   r   )ÚfairÚkmr   r	   Ú
<dictcomp>L   s   ÿzload.<locals>.<dictcomp>)ÚsortedÚosÚlistdirÚLIVE_DIRÚDAYSÚcollectionsÚdefaultdictÚlistÚopenÚpathÚjoinÚcsvZ
DictReaderÚSZ_ptr(   r   r   ÚSIDESÚanyr   ÚappendÚ	enumerate)ÚfilesÚoutÚfnZfhÚtsZelZpoÚkeyr   )r.   r/   r+   r	   Úload5   s6    "

ÿ

&ÿ.rG   c                     sÔ  t ƒ } tt| ƒ› d�ƒ g }d}|  ¡ D ]È\}}| ¡  dd„ tD ƒ}dd„ tD ƒ}|D ]–\}}tD ]ˆ}	||	 \}
}||	 d ur¤t|
||	  ƒdkr¤|||	< |d7 }|
||	< ||	 d u rºqd|||	   ¡ d }|d	krØqd| ||
 ||f¡ qdqXq(t|d
›dt|ƒd
›d�ƒ t|ƒdk �r(tdƒ d S t	dd„ |D ƒƒ}t|ƒ}tdƒ dD ]4\}}td|d›d|t
|| ƒ d d›d�ƒ �qNtdƒd ‰ t‡ fdd„|D ƒƒ}tdˆ d›d|d
›d|d
›dd | | d!›d"�	ƒ td#ƒ td$ƒ td%ƒ tdd&d'›dd(d)›dd*d+›dd,d-›�ƒ d.D ]ª\‰‰‡‡fd/d0„|D ƒ}t|ƒdk �rJ�qt	d1d„ |D ƒƒ}td2d„ |D ƒƒ}tdˆ› d3ˆ› d4�d'›dt|ƒd5›d|t|ƒd6  d d7›d8d | t|ƒ d9›d:�	ƒ �qtd;ƒ d S )<Nz3 fixtures with both Pinnacle and Kalshi pre-kickoffr   c                 S   s   i | ]
}|d “qS r   r   r)   r   r   r	   r0   Y   r   zmain.<locals>.<dictcomp>c                 S   s   i | ]
}|d “qS r   r   r)   r   r   r	   r0   Z   r   g{®Gázt?r,   g      N@éx   ú,z Pinnacle moves (>=0.5pp), z observations within 2h of one
iô  ztoo fewc                 s   s   | ]\}}}t |ƒV  qd S r   ©Úabs)r   ÚgÚ_r   r   r	   r    o   r   zmain.<locals>.<genexpr>z+|Kalshi - Pinnacle fair|, all observations:))Zmedianç      à?)Zp75g      è?)Zp90gÍÌÌÌÌÌì?)Zp99g®Gáz®ï?z   z>7ú éd   z>6.2fÚcrN   c                 3   s   | ]}|d  ˆ krdV  qdS )rP   r,   Nr   r-   )Úfee_at_evenr   r	   r    u   r   z   above the ~z.2fzc taker fee at even money: ú/z (g      Y@z.1fz%)zD
=== the decisive test: does the gap DECAY after Pinnacle moves? ===z,   if it does, it is lag and it is takeable.zA   if it is flat, Kalshi simply disagrees, and that is not edge.
zminutes since movez>20Zobsz>8zmedian |gap|z>13z
% over feez>11))r   é   )rT   é   )rU   é
   )rV   é   )rW   é-   )rX   rH   c                    s6   g | ].\}}}ˆ|  kr"ˆ k rn qt |ƒ|f‘qS r   rJ   )r   rL   ÚmÚk)r   r   r   r	   r      r   zmain.<locals>.<listcomp>c                 s   s   | ]\}}|V  qd S r   r   )r   r   rM   r   r   r	   r    ‚   r   c                 s   s"   | ]\}}|t |ƒkrd V  qdS )r,   N)r
   )r   r   rZ   r   r   r	   r    ƒ   r   ú-rY   z>8,rT   z>12.2fzc z>10.1fú%zB
   (% over fee uses the fee at THAT leg's own price, not at 0.50))rG   ÚprintÚlenÚitemsÚsortr>   rK   Ztotal_secondsr@   r1   Úintr
   r   )ÚdataZgapsZmovesrF   ZrowsZ	last_moveÚprevrE   Údr   ZpinZkalZminsZabsgÚnZlblr   ZoverZselÚaZrichr   )rR   r   r   r	   ÚmainQ   sr     .ÿÿÿ
ÿ$ÿÿrg   Ú__main__)Ú__doc__r<   r6   r2   Z
statisticsÚsysr:   ÚinsertZ	mm_settler=   r4   r>   r5   r   r
   r   r   rG   rg   Ú__name__r   r   r   r	   Ú<module>   s   (9