a
    Ñå‘j°9  ã                   @   sî   d Z ddlZddlZddlZddlZddlmZ ej ej 	e
¡¡Zej de¡ ej dej edd¡¡ ej ed¡Zej ed¡ZdZdd
d„Zdd„ Zdd„ Zd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�  Is market making on Poly-US worth it? Settle the LIVE maker's books.

WHAT THIS ANSWERS. project_mm_completion_arithmetic reduces a maker to one
number: the completion rate, against a break-even set by what a complete book
pays versus what a partial one costs. That was measured on KALSHI. Poly-US has
different economics -- it CREDITS the maker 0.0125*p(1-p) where Kalshi CHARGES
0.0175*p(1-p) -- so the break-even is its own number and has never been
measured on live fills.

The paper-era scorer (main/mm_settle_polyus.py, regime `pus-v1`) inferred fills
from the tape. This does not: it reads the LIVE maker's own state, where a leg
is marked filled only from a real position, so every book here is real money.

METHOD. For each event the maker sold at least one leg of:
    P&L = sum(prices of legs SOLD) - (1.00 if the winning outcome was one we
          sold, else 0.00) + maker rebate
A COMPLETE book always sells the winner, so it is deterministically
sum(px) - 1.00. A PARTIAL book is a coin flip on whether the outcome it is
short actually lands -- which is the whole risk of the strategy.

Read-only. Run:  python research/pus_maker_pnl.py
é    N)Údefaultdictú..Úmainzpus_live_state.jsonzpus_live_legs.csvgš™™™™™‰?ç        c              	   C   s*   z
t | ƒW S  ttfy$   | Y S 0 d S ©N)ÚfloatÚ	TypeErrorÚ
ValueError)ÚxÚd© r   úresearch/pus_maker_pnl.pyÚ_f'   s    
r   c               
   C   s0  z.t j dtj tdd¡¡ ddl} ddl}W n@ tyn } z(t	dt
|ƒj› d|› �ƒ W Y d}~dS d}~0 0 ttƒdd  }}}�z\|dk �ràdd	i}|r¦||d
< |j| jd |  dd¡|dd�}|jdkrît|j› d|jdd… › �ƒ‚| ¡ }| d¡�pg D ]®}	|	 d¡dk�r�q|	 d¡�p,i }
|
 d¡}|
 d¡�pFi |
 d¡�pTi  }}t| d¡ƒ}|�r|�sx�qt| d¡�pˆi  d¡ƒ}| dd¡\}}||f|| |< �q| d¡|d  }}|�rà| d¡rˆ�qàqˆW nD t�y& } z*t	dt
|ƒj› d|› d �ƒ W Y d}~dS d}~0 0 t|ƒS )!aŠ  event -> {leg: (net, realized)} from Poly-US's OWN settlement record.

    REPLACES the state-derived version (2026-08-28). pus_live_state.json is
    written by detect_fills, which reads positions and prints
    'positions unreadable, not marking fills' on every 503 -- and this venue
    503s for hours at a time. Fills that landed while it was blind were simply
    never recorded, so the state undercounts BOOKS badly: 16 settled books in
    state against 62 the venue has resolved.

    The same class of bug was found on Kalshi the same day, where a lossy local
    join reported 45 complete books against the exchange's 115. Completion is
    the number this strategy is judged on; it cannot come from a local record
    that stops writing when a venue hiccups.

    ACTIVITY_TYPE_POSITION_RESOLUTION carries beforePosition.netPosition (what
    we held into settlement) and afterPosition.realized (what the venue paid or
    took). Using the venue's own realized figure removes the need to reconstruct
    P&L from prices and outcomes at all -- no fixture lookup, no side mapping,
    no result join.

    Returns None if activities cannot be read.
    r   r   r   Nz#  [warn] poly-US auth unavailable: z: é(   Úlimitéd   Úcursorz/v1/portfolio/activitiesZGETé   )ZheadersÚparamsZtimeoutéÈ   éx   Z
activitiesÚtypeZ!ACTIVITY_TYPE_POSITION_RESOLUTIONZpositionResolutionZ
marketSlugZbeforePositionZafterPositionZnetPositionÚrealizedÚvalueú-é   Z
nextCursorÚeofz   [warn] activities unreadable (ú))ÚsysÚpathÚinsertÚosÚjoinÚHEREZpoly_us_authÚrequestsÚ	ExceptionÚprintr   Ú__name__r   ÚdictÚgetZAPI_BASEZ_auth_headersZstatus_codeÚRuntimeErrorÚtextÚjsonr   Úrsplit)Zpar$   ÚeÚoutr   Zpagesr   Úrr   ÚaZprÚslugZbpZapZnetÚrealÚevZlegr   r   r   Úsettled_books.   sN    

þ

"
r5   c                  C   sv   i } t tddd��P}t |¡D ]4}| d¡p.d ¡  ¡ }| d¡r|r|| |d < qW d  ƒ n1 sh0    Y  | S )z3slug -> 'home' | 'draw' | 'away', from the leg log.Ú úutf-8©ÚnewlineÚencodingZsider2   N)ÚopenÚLEGSÚcsvÚ
DictReaderr)   ÚstripÚlower)r/   Úfhr0   Úsr   r   r   Úside_mapm   s    ,rC   c                  C   s”   i } t tddd��n}t |¡D ]R}| d¡p.d}|s6q| dd¡d }|| v rPqt| d¡d	ƒ}|d	ur|| |< qW d	  ƒ n1 s†0    Y  | S )
a“  event slug -> gross_c the FIRST time the book was quoted.

    The gate (GROSS_GATE_C, 2.0c) decides which books open, and the 2026-08-24
    read found 16 of 18 risked books opened at exactly the gate minimum and
    completed 50% -- against a 74.2% break-even. A book opened on 2c of
    overround has almost nothing to spread across the remaining legs once one
    fills, so the ratchet holds out near the touch and strands.

    FIRST row, not min or mean: the question is what the book was worth when
    the decision to open it was made. Later rows carry the ratchet's own
    repricing, which is a consequence of that decision, not an input to it.
    r6   r7   r8   r2   r   r   r   Zgross_cN)r;   r<   r=   r>   r)   r-   r   )r/   rA   r0   r2   r4   Úgr   r   r   Úopening_grossx   s    (rE   c                  C   sp   ddl } i }| jdd�D ]R}|d r0|d d ni }| d¡| d¡ }}|r|r|d || ¡ | ¡ f< q|S )	z6(home, away) -> 'H'/'D'/'A' for every settled fixture.r   NT)Zrequire_resultZpreÚhomeÚawayÚftr)Zforward_test_unique_bookZload_fixturesr)   r?   )ÚFr/   ZfxZr0Úhr1   r   r   r   Úresults_map”   s    rK   c               
   C   s°   t ƒ } znttddd��L}t |¡D ]0}| d¡p2d}d|v r"|  | dd¡d ¡ q"W d  ƒ n1 sh0    Y  W n6 tyª } zt	d	|› d
�ƒ W Y d}~dS d}~0 0 | S )a/  Event prefixes the MAKER actually quoted, from its own leg log.

    WHY THIS FILTER IS NOT OPTIONAL. /v1/portfolio/activities is the ACCOUNT's
    history, not the maker's: it carries manual bets, xlow-era trades and World
    Cup futures going back to June, while the live maker only started
    2026-08-22. Without this, `tec-f-wc-2026-07-19-groupd-winner` lands in a
    market-making completion rate as a 1-leg 'partial book' worth -$1.00.

    The leg log is the record of what this maker offered, so an event absent
    from it was never its book.
    r6   r7   r8   r2   r   r   r   Nz  [warn] leg log unreadable (z); cannot filter to maker books)
Úsetr;   r<   r=   r>   r)   Úaddr-   ÚOSErrorr&   )r/   rA   r0   r2   r.   r   r   r   Úmaker_events    s    :rO   c                     sf   t ƒ } | d urXtƒ ‰ ˆ rPt| ƒ}‡ fdd„|  ¡ D ƒ} td|› dt| ƒ› d�ƒ t| ƒS tdƒ tƒ S )Nc                    s   i | ]\}}|ˆ v r||“qS r   r   )Ú.0r.   Úv©Zquotedr   r   Ú
<dictcomp>¿   ó    zmain.<locals>.<dictcomp>zvenue settled books: z  ->  z9 the maker actually quoted (rest are manual/xlow/futures)z>  falling back to the local state file -- expect undercounting)r5   rO   ÚlenÚitemsr&   Úreport_exchangeÚmain_from_state)Zbooks_exZbeforer   rR   r   r   ¹   s    c                 C   s|  g }|   ¡ D ]B\}}tdd„ | ¡ D ƒƒ}| |t|ƒ|d t|ƒdkdœ¡ q|s`tdƒ dS dd	„ |D ƒ}d
d	„ |D ƒ}tdd„ |D ƒƒ}tdt|ƒ› dt|ƒ› dt|ƒ› d�ƒ tƒ  tdd›dd›dd›�ƒ t|dd„ d�dd… D ]4}td|d dd… d›|d d›|d  d!›�ƒ qîtd"ƒ t|d#d„ d�d$d… D ]6}td|d dd… d›|d d›|d  d!›�ƒ �qDtƒ  d%d&„ }	|	d'|ƒ |	d(|ƒ |	d)|ƒ tƒ  td*d+t|ƒ t|ƒ d,›d-�ƒ |�rJ|�rJtd.d„ |D ƒƒt|ƒ }
td/d„ |D ƒƒt|ƒ }|
|k�rJtd0| |
|  d+ d,›d1|
d2›d3|d2›d4�ƒ tƒ  td5|d d2›d6t|ƒ› d7�ƒ td8ƒ dS )9z>Completion and P&L straight off the venue's settlement record.c                 s   s   | ]\}}|d ur|V  qd S r   r   )rP   Ú_r0   r   r   r   Ú	<genexpr>Ë   rT   z"report_exchange.<locals>.<genexpr>ç      Y@é   )r4   ÚnÚpnl_cÚcompletezno settled books yetr   c                 S   s   g | ]}|d  r|‘qS ©r_   r   ©rP   r0   r   r   r   Ú
<listcomp>Ñ   rT   z#report_exchange.<locals>.<listcomp>c                 S   s   g | ]}|d  s|‘qS r`   r   ra   r   r   r   rb   Ò   rT   c                 s   s   | ]}|d  V  qdS ©r^   Nr   ra   r   r   r   rZ   Ó   rT   zsettled books (venue record): ú   (complete ú
, partial r   Úbookz<46Úlegsú>5úP&L cú>10c                 S   s   | d S ©Nr^   r   ©r
   r   r   r   Ú<lambda>Ù   rT   z!report_exchange.<locals>.<lambda>©ÚkeyNé   ú  r4   iÔÿÿÿú<44r]   r^   z>10.2fz  ...c                 S   s   | d S rk   r   rl   r   r   r   rm   Ü   rT   éûÿÿÿc              
   S   sp   |st | d›d�ƒ d S tdd„ |D ƒƒt|ƒ }t | d›dt|ƒd›d|d›d	td
d„ |D ƒƒd›d�ƒ d S )Nú<26ú (none)c                 s   s   | ]}|d  V  qdS rc   r   ©rP   r
   r   r   r   rZ   ä   rT   z.report_exchange.<locals>._s.<locals>.<genexpr>ú n=ú>3ú   mean z>+8.2fúc/book   total c                 s   s   | ]}|d  V  qdS rc   r   rv   r   r   r   rZ   æ   rT   z>+9.1fÚc©r&   ÚsumrU   ©ZlabelZxsÚmr   r   r   Ú_sà   s    ÿzreport_exchange.<locals>._súCOMPLETE booksúPARTIAL booksú	ALL booksúcompletion rate: r   ú.1fú%c                 s   s   | ]}|d  V  qdS rc   r   rv   r   r   r   rZ   î   rT   c                 s   s   | ]}|d  V  qdS rc   r   rv   r   r   r   rZ   ï   rT   úbreak-even completion rate: ú%   (complete ú+.2fúc vs partial úc)zREALISED (venue-reported): $ú over ú booksz\P&L is afterPosition.realized summed per book -- the venue own number, not a reconstruction.)rV   r}   ÚvaluesÚappendrU   r&   Úsorted)ÚbooksÚrowsr4   rg   r   ÚcompÚpartÚtotr0   r€   ÚcgÚpcr   r   r   rW   Ç   sX    
ÿ
ÿÿ
24


"
ÿÿ
 rW   c            (         sÌ  t  ttdd�¡} tƒ ‰ tƒ }ttƒ}i }|  ¡ D ]j\}}t	|tƒsFq2| 
dd¡d }| d¡rl|d ||< t| d¡ƒ}|dkr2|t| d¡ƒf|| |< q2g g  }}	| ¡ D �](\}}
| |d	¡}d
|vrä|	 ||t|
ƒf¡ q°dd„ | d
d¡D ƒ\}}| ||f¡}|d u �r,|	 ||t|
ƒf¡ q°ddddœ| }tdd„ |
 ¡ D ƒƒ}tdd„ |
 ¡ D ƒƒ}‡ fdd„|
D ƒ}||v �rˆdnd}tdd„ |
 ¡ D ƒƒ}|| | d }| ||t|
ƒ||||t|
ƒdkdœ¡ q°|�sîtdƒ dS dd„ |D ƒ}dd„ |D ƒ}tdd„ |D ƒƒ}td t|ƒ› d!t|ƒ› d"t|ƒ› d#�ƒ td$t|	ƒ› �ƒ tƒ  td%d&›d'd(›d)d*›d+d,›d-d.›�ƒ t|d/d0„ d1�D ]J}td2|d3 d d4… d5›|d6 d(›|d) d7›|d+ d8›|d9 d:›�ƒ �q’tƒ  d;d<„ }|d=|ƒ |d>|ƒ |d?|ƒ tƒ  |�r*t|ƒt|ƒ d@ nd}tdA|dB›dC�ƒ |�r¾|�r¾tdDd„ |D ƒƒt|ƒ }tdEd„ |D ƒƒt|ƒ }||k�r¾| ||  d@ }tdF|dB›dG|dH›dI|dH›dJ�ƒ tƒ  tdK|d dL dM›dN|d |d dL  dH›dOt|ƒ› dP�ƒ tƒ }g dQ¢} dRdS„ | D ƒ}!d}"|D ]¨}| |dT ¡}#|#d u �rL|"d7 }"�q$| D ]x\}}$}%|$|#  k�rr|%k �rPn nP|!| }&|&d6  d7  < |&dU  |dV �r¤dnd7  < |&dW  |d9 7  <  �q$�qP�q$tƒ  tdXƒ td2dYdZ›d6d[›dVd\›d]d^›d_d`›�ƒ | D ]˜\}}'}'|!| }&|&d6 �sPtd2|dZ›dd[›dad\›dad^›dad`›�ƒ �qtd2|dZ›|&d6 d[›|&dU d\›d@|&dU  |&d6  db›dC|&dW |&d6  dc›�ƒ �q|"�r¸tdd|"› de�ƒ tdfƒ tdgƒ dS )hNr7   )r:   r   r   r   ZeventZfill_qtyZfill_pxr6   z vs c                 S   s   g | ]}|  ¡ ‘qS r   )r?   rv   r   r   r   rb     rT   z#main_from_state.<locals>.<listcomp>rF   ZdrawrG   )ÚHÚDÚAc                 s   s   | ]\}}|V  qd S r   r   ©rP   rY   Zpxr   r   r   rZ     rT   z"main_from_state.<locals>.<genexpr>c                 s   s   | ]\}}|V  qd S r   r   )rP   ÚqrY   r   r   r   rZ     rT   c                    s   h | ]}ˆ   |¡’qS r   )r)   )rP   rB   ©Zsidesr   r   Ú	<setcomp>  rT   z"main_from_state.<locals>.<setcomp>g      ð?r   c                 s   s"   | ]\}}t | d |  V  qdS )r   N)ÚMAKER_REBATEr›   r   r   r   rZ     rT   r[   r\   )r4   Únamer]   ÚqtyÚ	collectedÚpaidr^   r_   zno settled live books yetc                 S   s   g | ]}|d  r|‘qS r`   r   ra   r   r   r   rb   %  rT   c                 S   s   g | ]}|d  s|‘qS r`   r   ra   r   r   r   rb   &  rT   c                 s   s   | ]}|d  V  qdS rc   r   ra   r   r   r   rZ   '  rT   zsettled live books: rd   re   r   zunsettled / unmatched: rf   rr   rg   rh   r¢   z>11r£   z>7ri   z>9c                 S   s   | d S rk   r   rl   r   r   r   rm   .  rT   z!main_from_state.<locals>.<lambda>rn   rq   r    é*   z<42r]   z>11.2fz>7.2fr^   z>9.2fc              
   S   sp   |st | d›d�ƒ d S tdd„ |D ƒƒt|ƒ }t | d›dt|ƒd›d|d›d	td
d„ |D ƒƒd›d�ƒ d S )Nrt   ru   c                 s   s   | ]}|d  V  qdS rc   r   rv   r   r   r   rZ   7  rT   z.main_from_state.<locals>._s.<locals>.<genexpr>rw   rx   ry   z>+7.2frz   c                 s   s   | ]}|d  V  qdS rc   r   rv   r   r   r   rZ   9  rT   z>+8.1fr{   r|   r~   r   r   r   r€   3  s    ÿzmain_from_state.<locals>._sr�   r‚   rƒ   r   r„   r…   r†   c                 s   s   | ]}|d  V  qdS rc   r   rv   r   r   r   rZ   B  rT   c                 s   s   | ]}|d  V  qdS rc   r   rv   r   r   r   rZ   C  rT   r‡   rˆ   r‰   rŠ   r‹   zREALISED, at r¡   z.0fz contract(s)/leg: $rŒ   r�   ))z2-3cg       @ç      @)z3-5cr¥   ç      @)z5-8cr¦   ç       @)z8c+r§   g    eÍÍAc                 S   s   i | ]}|d  d d ddœ“qS )r   r   )r]   r“   Úpnlr   )rP   Úbr   r   r   rS   U  rT   z#main_from_state.<locals>.<dictcomp>r4   r“   r_   r¨   z4COMPLETION AND P&L BY OPENING GROSS (settled books):Zgrossz<7z>4rj   Úratez>8zmean c/bookz>13u   â€”z>7.1fz>+13.2fz  (z' book(s) had no opening gross recorded)zD  NOTE: a single-book bucket is not evidence. The gate only moves ifzG        a POPULATED wider bucket clears break-even while 2-3c does not.)r,   Úloadr;   ÚSTATErC   rK   r   r(   rV   Ú
isinstancer-   r)   r   r�   rU   Úsplitr}   rŽ   Úmaxr&   r�   rE   )(ÚstÚresr‘   Únamesr2   rQ   r4   rœ   r’   Z	unmatchedrg   r    rF   rG   rH   Zwantr¢   r¡   Z
sold_sidesr£   Zrebater^   r“   r”   r•   r0   r€   rª   r–   r—   ZbeZogZbucketsZaggZmissingrD   ÚloÚhir1   rY   r   r�   r   rX   ú   sä    




þ
ÿÿ
&*ÿÿ



ÿÿ
ÿÿ
	
(
(ÿÿrX   Ú__main__)r   )Ú__doc__r=   r,   r!   r   Úcollectionsr   r   ÚdirnameÚabspathÚ__file__r#   r    r"   r¬   r<   rŸ   r   r5   rC   rE   rK   rO   r   rW   rX   r'   Úexitr   r   r   r   Ú<module>   s,   
?3z