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ddlmZ ej 	dd¡ ej 	dd¡ ddl
ZddlZddlZddlmZ dZdZd	Zd
d„ Zddd„Zdd„ Zdd„ Zdd„ Zedkr¼eƒ  dS )a   BACKTEST the ratchet against real Kalshi trade prints, queue-aware.

The ratchet was justified by replaying ONE day (34 books, 3 completions). That
is an anecdote. This scores it properly over every book since depth logging
began (2026-07-28), using the fill model from queue_aware_fill.py: a resting ask
at price P fills only once cumulative volume traded at >= P exceeds the depth
that was already queued at P when we posted.

Three policies, same books, same trades:

  CHASE   -- what the live loop did: re-quote at the touch every cycle, so the
             effective offer is the LOWEST ask seen while waiting.
  RATCHET -- never quote below the first offer; follow a rising touch up.
  GATE+R  -- ratchet, and only open books whose entry touches clear target.

The ratchet trades fill probability for margin, so the only question that
matters is whether the books it still completes are worth more than the books it
loses. That is exactly what this measures, and it is not answerable from the
paper log, which records every fill landing at our quoted price.

Bounds are reported both ways -- queue-aware (lower) and optimistic (upper) --
because the truth is between them and a single number here would be a lie.

Usage:  python research\mm_ratchet_backtest.py [--refresh]
é    N)ÚdefaultdictzE:\soccer\mainzE:\soccer\research)Útrades_sizedz&E:\soccer\research\mm_paper_quotes.csvz%E:\soccer\research\.trades_cache.jsonz
2026-07-28c                  C   sþ   t tƒ} ttddd��h}t |¡D ]L}| d¡}|r"|d dd… tk rJq"| d¡r"| d	¡s`q"| |  |¡ q"W d  ƒ n1 s„0    Y  t t	ƒ}|  
¡ D ]L\}}|jd
d„ d� |d  d¡pÆd ¡ sÎqž| dd¡\}}||| |< qždd„ | 
¡ D ƒS )z?{event: {leg: [rows sorted by ts]}} for legs with depth logged.úutf-8Úreplace)ÚencodingÚerrorsZ
leg_tickerÚtsNé
   Úk_askÚkickoffc                 S   s   | d S )Nr   © )Úxr   r   ú)E:\soccer\research\mm_ratchet_backtest.pyÚ<lambda>6   ó    zload_books.<locals>.<lambda>©Úkeyr   Úask_sizeÚ ú-é   c                 S   s"   i | ]\}}t |ƒd kr||“qS )é   )Úlen)Ú.0ÚeÚbr   r   r   Ú
<dictcomp>;   r   zload_books.<locals>.<dictcomp>)r   ÚlistÚopenÚQUOTESÚcsvZ
DictReaderÚgetÚDEPTH_STARTÚappendÚdictÚitemsÚsortÚstripÚrsplit)ÚlegsZfhÚrÚtÚbooksÚrsÚevÚlegr   r   r   Ú
load_books)   s"    
.r0   ç        c              	   C   s*   z
t | ƒW S  ttfy$   | Y S 0 d S )N)ÚfloatÚ	TypeErrorÚ
ValueError)r   Údr   r   r   Ú_f>   s    
r6   c                 C   sL   | › d|  ¡ › �}||v r,dd„ || D ƒS t| |ƒ}dd„ |D ƒ||< |S )Nú|c                 S   s"   g | ]\}}}t  |¡||f‘qS r   )ÚSÚ_pt©r   r+   ÚpÚcr   r   r   Ú
<listcomp>H   r   zget_trades.<locals>.<listcomp>c                 S   s    g | ]\}}}|  ¡ ||f‘qS r   )Ú	isoformatr:   r   r   r   r=   J   r   )r>   r   )ÚtickerZsinceÚcacher   Útrr   r   r   Ú
get_tradesE   s    
rB   c           	         sh   t ‡ ‡fdd„| D ƒƒ}d}| D ]>\}}}|ˆ kr6 q`|ˆd kr ||7 }||kr d|f  S q d|fS )zA(queue_aware, optimistic) -- would a resting ask at `price` fill?c                 3   s(   | ] \}}}|ˆ k r|ˆd  kV  qdS )ç•Ö&è.>Nr   )r   r+   r;   Ú_©ÚkoÚpricer   r   Ú	<genexpr>P   r   zfills.<locals>.<genexpr>r1   rC   TF)Úany)	ZtradesrG   ÚqueuerF   ZoptZcumr+   r;   r<   r   rE   r   ÚfillsN   s    rK   c                     s\  dt jv } i }tj t¡rL| sLzt ttdd�¡}W n t	yJ   i }Y n0 t
ƒ }tt|ƒ› dt› d�ƒ dd„ dD ƒ}d	}tt| ¡ ƒd
ƒD �]Z\}\}}|d d	krÄtd|› dt|ƒ› �dd� i d }}	| ¡ D ]x\}
}|d	 }t |d ¡}|�sd}	 �qPt|d ƒtdd„ |D ƒƒt| d¡ƒt |d ¡||› d|
› �dœ||
< qÖ|	rŽtdd„ | ¡ D ƒƒ�rnqŽdd„ | ¡ D ƒ}t |i ¡d }i ‰ | ¡ D ]d\}
}t|d |d |ƒ}t||d |d |d  ƒt||d! |d |d  ƒ|d |d! d"œˆ |
< �qœd#D ]v}|d$k�rd$nd%‰t‡ ‡fd&d„ˆ D ƒƒ}t‡ ‡fd'd„ˆ D ƒƒ}t‡ ‡fd(d„ˆ D ƒƒ}||  |||d)œ¡ �q|d*k�r’|d
7 }qŽt‡ fd+d„ˆ D ƒƒ}t‡ fd,d„ˆ D ƒƒ}t‡ fd-d„ˆ D ƒƒ}|d*  |||d)œ¡ qŽt |ttd.dd�¡ d/d0„ }d1D �]\}}td2|› d3�ƒ td4d5›d6d7d8›d6d9d:›d6d;d<›d6d=d:›d6d>d:›d6d?d@›d6dAd:›�ƒ dBD ]®\}}||| |ƒ}|�sºt|d5›d6t|| ƒd8›d6dCd:›dD�ƒ �qrt|d5›d6|dE d8›d6|dF d:›d6|d; dG›dH|dI dJ›d6|dK dL›dM|dN dO›dH|dA dP›dQ�ƒ �qr�qtdR|› dSt|ƒ› dT�ƒ tdUƒ tdVƒ tdWƒ d S )XNz	--refreshr   )r   z three-leg books since Ú
c                 S   s   i | ]
}|g “qS r   r   )r   Úkr   r   r   r   h   r   zmain.<locals>.<dictcomp>)ÚchaseÚratchetÚgater   r   é   z  ...ú/T)Úflushr   Fr
   c                 s   s&   | ]}t |d  ƒrt |d  ƒV  qdS )r
   N)r6   ©r   r*   r   r   r   rH   u   r   zmain.<locals>.<genexpr>r   r   r   )Úask0ÚlowrJ   ÚstartrF   r?   c                 s   s   | ]}|d  dkV  qdS )rU   r   Nr   )r   Úvr   r   r   rH   y   r   c                 S   s   i | ]\}}||d  “qS )rU   r   )r   ÚlrX   r   r   r   r   |   r   Údecisionr?   rW   rV   rJ   rF   rU   )rN   rO   Zpx_chaseÚ
px_ratchet)rN   rO   rN   rO   c                 3   s   | ]}ˆ | ˆ d  V  qdS )r   Nr   ©r   rY   ©Úper_legÚsrcr   r   rH   Š   r   c                 3   s   | ]}ˆ | ˆ d  V  qdS )r   Nr   r\   r]   r   r   rH   ‹   r   c                 3   s    | ]}ˆ | d ˆ› � V  qdS )Zpx_Nr   r\   r]   r   r   rH   Œ   r   )ÚqÚoÚcollrP   c                 3   s   | ]}ˆ | d  d V  qdS )rO   r   Nr   r\   ©r^   r   r   rH   ‘   r   c                 3   s   | ]}ˆ | d  d V  qdS )rO   r   Nr   r\   rc   r   r   rH   ’   r   c                 3   s   | ]}ˆ | d  V  qdS )r[   Nr   r\   rc   r   r   rH   “   r   Úwc              
      sÀ   ‡ fdd„| D ƒ}|sd S g }|D ]4‰t ‡fdd„tdƒD ƒƒ}| ˆd | d ¡ q"t| ƒt|ƒdt|ƒ t| ƒ t d	d„ |D ƒ¡t |¡d
 t |ƒd
 dt dd„ |D ƒƒ t|ƒ dœS )Nc                    s   g | ]}|ˆ  r|‘qS r   r   rT   r   r   r   r=   ™   r   z'main.<locals>.score.<locals>.<listcomp>c                 3   s    | ]}t  ˆ d  d ¡V  qdS )rb   g      @N)ÚMLZ	maker_fee)r   rD   )r*   r   r   rH   ž   r   z&main.<locals>.score.<locals>.<genexpr>r   rb   g      ð?g      Y@c                 s   s   | ]}|d  V  qdS )rb   Nr   rT   r   r   r   rH   ¢   r   éd   c                 s   s   | ]}|d krdV  qdS )r   r   Nr   )r   r   r   r   r   rH   ¥   r   )ÚnÚdoneÚraterb   ÚmeanÚtotalÚwinrate)ÚsumÚranger#   r   Ú
statisticsZmedianrj   )Zrowsr   rh   ZnetZfeer   )r   r*   r   Úscore˜   s    
ûzmain.<locals>.score))r`   zQUEUE-AWARE (lower bound))ra   zOPTIMISTIC (upper bound)z
=== z ===ZpolicyZ10ú r,   z>6Zcompletez>9ri   z>7zmed collznet/bookZ
profitablez>11rk   ))rN   rN   )rO   rO   )rP   z
gate+ratchÚ0u    â€” no completionsrg   rh   z>6.1fz% rb   z>9.4frj   z>8.2fzc rl   z>10.0fz>8.1fr<   z
gate refused z of z! books before any leg was offeredz@
The ratchet is only worth it if its net/book gain outweighs thezAcompletion it gives up -- compare the TOTAL column, which alreadyzcarries both effects.) ÚsysÚargvÚosÚpathÚexistsÚCACHEÚjsonÚloadr   Ú	Exceptionr0   Úprintr   r"   Ú	enumerateÚsortedr%   r8   r9   r6   Úminr!   rI   Úvaluesre   Zbook_budgetrB   rK   Úallrm   r#   Údump)Zrefreshr@   r,   ZresultsZgatedÚir.   r)   ÚentryZko_okr/   r-   Zr0rF   Zasks0rZ   rX   rA   Zpolr`   ra   rb   rp   r   ZlblÚnameÚsr   r]   r   Úmain\   s¢    

 

üý

"ÿÿÿÿ(.ÿÿÿþr‡   Ú__main__)r1   )Ú__doc__r    ry   ru   rs   ro   Úcollectionsr   rv   ÚinsertZ	mm_settler8   Zkalshi_traderZktZmm_maker_livere   Zqueue_aware_fillr   r   rx   r"   r0   r6   rB   rK   r‡   Ú__name__r   r   r   r   Ú<module>   s$   (
	_