a
    ¶Ugj÷  ã                   @   s€   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dZ	g d¢Z
dd„ Zd	d
„ Zdd„ Zdd„ Zdd„ Zedkr|e eƒ ¡ dS )aÓ  Do Kalshi prices drift TOWARD Pinnacle fair?

The maker strategy caps our ask at the market's ask, so we only ever collect the
market's own price. That is fine if the market is already right, and costly if the
market is systematically about to move toward pin_fair -- in that case we are
selling a leg that is about to get cheaper (good) or, worse, selling the leg on the
wrong side of a move we could have anticipated.

METHOD
  For each leg, walk its quote history on a NON-OVERLAPPING grid of horizon H.
    gap(t)  = pin_fair(t) - k_mid(t)          (cents; + means Kalshi too cheap)
    dmid(t) = k_mid(t+H)  - k_mid(t)          (cents; how the market actually moved)
  If Kalshi drifts toward Pinnacle, dmid should share gap's sign and be some
  fraction of it.

WHY NON-OVERLAPPING: consecutive 20-second snapshots of the same leg are almost
the same observation. Overlapping windows would inflate n by ~200x and make any
t-stat meaningless. We also cluster standard errors by EVENT, because the three
legs of one match move together mechanically (they sum to ~1).

CAVEAT this cannot escape: ~1.4 days of data, 48 events. This is a directional
read, not a verdict. The historical xlow work (n=395, +0.30c) is the real estimate.
é    N)Údefaultdictzresearch/mm_paper_quotes.csvç      à?)é   é   é<   éx   c                 C   sP   | sd S zt j |  dd¡¡}W n ty4   Y d S 0 |jr@|S |jt jjd�S )NÚZz+00:00)Útzinfo)ÚdtÚdatetimeZfromisoformatÚreplaceÚ
ValueErrorr	   ÚtimezoneZutc)ÚsÚd© r   ú&E:\soccer\research\mm_drift_to_fair.pyÚ_pt#   s    r   c               	   C   s  t tƒ} ttdd��Æ}t |¡D ]ª}|d r |d r |d s>q t|d ƒ}t|d ƒ}|d u s |d u s ||krpq t|d ƒt|d ƒ }}| |d |d	 f  |t|d ƒd
 || d d
 |d dœ¡ q W d   ƒ n1 sà0    Y  |  	¡ D ]}|j
dd„ d� qò| S )Nzutf-8)ÚencodingZpin_fairZk_bidZk_askZtsZkickoffÚeventZsideéd   é   )ÚtÚfairÚmidr   c                 S   s   | d S )Nr   r   )Úxr   r   r   Ú<lambda>?   ó    zload.<locals>.<lambda>)Úkey)r   ÚlistÚopenÚPATHÚcsvZ
DictReaderr   ÚfloatÚappendÚvaluesÚsort)ÚlegsZfhÚrr   ZkoZbidZaskÚvr   r   r   Úload-   s&    ü(r*   c                 C   sÐ   t j|d�}g }|  ¡ D ]²}d}|t|ƒk r|| d }d}t|d t|ƒƒD ] }|| d | |krN|} qpqN|du rzq|| d || d  }	|| d || d  }
| |	|
|| d d	œ¡ |}q q|S )
z"Non-overlapping (gap, dmid) pairs.)Zminutesr   r   Né   r   r   r   )ÚgapÚdmidr   )r
   Z	timedeltar%   ÚlenÚranger$   )r'   Zhorizon_minÚHÚoutZrowsÚiZt0ÚjÚkr,   r-   r   r   r   ÚsamplesC   s$    r5   c                 C   sr   dd„ |   ¡ D ƒ}t|ƒdk r0tdƒtdƒdfS t |¡}t |¡}|t|ƒd  }||rb|| ntdƒt|ƒfS )z=t-stat on the mean, treating each cluster as one observation.c                 S   s   g | ]}|rt  |¡‘qS r   )Ú
statisticsÚmean)Ú.0r)   r   r   r   Ú
<listcomp>[   r   zclustered_t.<locals>.<listcomp>é   Únanr   r   )r%   r.   r#   r6   r7   Zstdev)Zvals_by_clusterZmeansÚmZsdZser   r   r   Úclustered_tY   s    

r=   c                     sâ  t ƒ } tt| ƒ› dttdd„ | D ƒƒƒ› d�ƒ tD �]}t| |ƒ}dd„ |D ƒ}|sptd|d›d	t› d
�ƒ q4ttƒ}|D ].}||d   	|d |d dkr¢dnd ¡ q|t
|ƒ\}}}dd„ |D ƒ}	|	rêtdd„ |	D ƒƒt|	ƒ d ntdƒ}
|t dd„ |D ƒ¡ d }td|d›dt|ƒd›d|d›d|d›d|d›d|
d›d |d!›d"�ƒ q4td#ƒ t| d$ƒ}g d%¢}td&d'd(›d)d*d+›d)d,d-›d)d.d/›d)d0d(›�
ƒ |D �]$\‰‰ ‡ ‡fd1d„|D ƒ}t|ƒd2k �rþtd&ˆ› d3ˆ › d
�d(›d)t|ƒd›d4�ƒ �q¤ttƒ}|D ]2}||d   	|d |d dk�r2dnd ¡ �q
t
|ƒ\}}}d5d„ |D ƒ}|�r~td6d„ |D ƒƒt|ƒ d ntdƒ}
td&ˆ› d3ˆ › d
�d(›d)t|ƒd›d)|d7›d8|d9›d)|
d:›d;�ƒ �q¤td<d=d>›d?�ƒ d S )@Nz legs, c                 s   s   | ]}|d  V  qdS )r   Nr   )r8   r4   r   r   r   Ú	<genexpr>f   r   zmain.<locals>.<genexpr>z events
c                 S   s    g | ]}t |d  ƒtkr|‘qS ©r,   )ÚabsÚ	MIN_GAP_C©r8   r   r   r   r   r9   j   r   zmain.<locals>.<listcomp>zH=z>4zmin  no samples with |gap| >= Úcr   r-   r,   r   r+   éÿÿÿÿc                 S   s    g | ]}t |d  ƒdkr|‘qS ©r-   g•Ö&è.>©r@   rB   r   r   r   r9   u   r   c                 s   s*   | ]"}|d  dk|d dkkrdV  qdS ©r-   r   r,   r+   Nr   rB   r   r   r   r>   v   s   ÿr   r;   c                 S   s   g | ]}t |d  ƒ‘qS r?   rF   rB   r   r   r   r9   x   r   zmin  n=Z5dz obs / Z2dz events   drift toward fair z+.3fzc  (t=z+.2fz, clustered)  | moved-right z4.1fz%  | closes z+.0fz% of gapz
BY GAP SIZE (H=60min):r   ))r   ç      ð?)rH   ç       @)rI   ç      @)rJ   g     ÀX@z  z|gap|z>12sú Únz>5szdrift toward fairz>19sr   z>7szmoved-rightc                    s0   g | ](}ˆt |d  ƒ  kr$ˆ k rn q|‘qS r?   rF   rB   ©ÚhiÚlor   r   r9   …   r   é
   ú-z   (too few)c                 S   s    g | ]}t |d  ƒdkr|‘qS rE   rF   rB   r   r   r   r9   �   r   c                 s   s*   | ]"}|d  dk|d dkkrdV  qdS rG   r   rB   r   r   r   r>   Ž   r   z+18.3fzc z+7.2fz11.1fú%zE
For scale: crossing a 1c spread costs 0.5c; the taker fee at 25c is g     õ?z.2fzc.)r*   Úprintr.   ÚsetÚHORIZONS_MINr5   rA   r   r   r$   r=   Úsumr#   r6   r7   )r'   r0   r   ZwideZby_evr   Zmean_towardr   ZnclustZmovedZhitZfracZbucketsÚbr<   ZncZmvr   rM   r   Úmaind   sd    *

,ÿÿÿÿ ÿÿþþ
0*0,FÿrX   Ú__main__)Ú__doc__r"   r   r
   r6   ÚsysÚcollectionsr   r!   rA   rU   r   r*   r5   r=   rX   Ú__name__Úexitr   r   r   r   Ú<module>   s   
2