a
    sJ¨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ZdZdZej	 
ej	 ej	 e¡¡d¡ZdZdZdd	d
„Zddd„Zdd„ Zddd„Zdd„ Zedkr¤e eƒ ¡ dS )ah  Who are the PROFITABLE market makers in Polymarket soccer, and what do they do?

(read-only; touches no account and places nothing)

WHY. Our own maker is stranded 13.8% of the time against an 11.1% break-even,
and the open question is whether assembling a 3-leg book is even the right
construct. Polymarket settles on-chain, so unlike Kalshi -- where trades are
anonymous and only AGGREGATE maker P&L is recoverable (research/mm_feasibility.py)
-- individual makers can be named, ranked by realised P&L, and studied.

Polymarket models a soccer match as THREE binary markets ("Will X win?", "...end
in a draw?", "Will Y win?"), which is exactly our three legs, so the comparison
is structural rather than analogical.

HOW A MAKER IS IDENTIFIED. data-api /trades returns the TAKER side only by
default; with takerOnly=false it returns BOTH sides of every fill. The set
difference on (transactionHash, proxyWallet) is therefore the maker side.
Verified 2026-09-14: 901 of 942 hashes resolve to clean 2-row pairs and 53% of
rows classify as maker, which is the ~half you would expect.

P&L. Per wallet per market: cash flow (BUY -size*px, SELL +size*px) plus the
settlement value of the net position (outcomePrices is ["1","0"]-style and
aligns with outcomeIndex). A negative net position is the complement leg and the
same arithmetic holds.

READ THE OUTPUT SCEPTICALLY:
  * SURVIVORSHIP. These are wallets that are currently large. Anyone who ran the
    same strategy into the ground is absent, which is exactly the inference
    error that produced the South America mirage.
  * NO QUEUE DATA. Fills are visible; resting orders are not. Nothing here can
    speak to queue position or refresh cadence.
  * DIFFERENT ECONOMICS. Polymarket's fee and tick structure is not Kalshi's,
    and our verdict rests on the 1c tick being the binding constraint. Findings
    transfer as HYPOTHESES.
  * SIZE. The top wallet moved 2.2M on one match. What works while warehousing
    that inventory need not work at $1/leg.
  * The offset cap on /trades is ~10k rows per market, so the very largest
    markets are TRUNCATED (reported per market as `truncated`).
é    Nz https://gamma-api.polymarket.comzhttps://data-api.polymarket.comzpm_maker_study.csviô  i'  é   c              	   C   sˆ   t |ƒD ]z}ztj| |dd�}W n( tyH   t d|d  ¡ Y qY n0 |jdkr`| ¡   S |jdkrp d S t d|d  ¡ qd S )Né   )ÚparamsZtimeoutg      ø?é   éÈ   é�  )ÚrangeÚrequestsÚgetÚ	ExceptionÚtimeÚsleepZstatus_codeÚjson)Zurlr   ZtriesÚiÚr© r   úresearch/pm_maker_study.pyÚ_get7   s    

r   Tc              
   C   sâ   g }dD ]²}t t› d�d|dt|ƒ ¡ dddœƒ}|s: q¼|D ]f}| d¡pNg }t|ƒd	kr>d
| d¡pjd ¡ v r>tdd„ |D ƒƒr>tdd„ |D ƒƒ}| ||f¡ q>t|ƒ| d	 kr q¼q|j	dd„ d� dd„ |d| … D ƒS )z<Highest-volume soccer matches that have the 3-leg structure.)r   éd   r   i,  r   z/eventsr   ZsoccerÚvolumeÚfalse)ÚlimitÚoffsetZtag_slugÚclosedÚorderZ	ascendingÚmarketsé   z vsÚtitleÚ c                 s   s   | ]}|  d ¡V  qdS )ÚoutcomePricesN)r
   ©Ú.0Úmr   r   r   Ú	<genexpr>R   ó    zmatch_events.<locals>.<genexpr>c                 s   s    | ]}t | d ¡pdƒV  qdS )r   r   N)Úfloatr
   r    r   r   r   r#   S   r$   c                 S   s
   | d  S )Nr   r   )Zkvr   r   r   Ú<lambda>W   r$   zmatch_events.<locals>.<lambda>©Úkeyc                 S   s   g | ]\}}|‘qS r   r   )r!   Ú_Úer   r   r   Ú
<listcomp>X   r$   z match_events.<locals>.<listcomp>N)
r   ÚGAMMAÚstrÚlowerr
   ÚlenÚallÚsumÚappendÚsort)Ún_eventsr   ÚoutÚoffÚevsr*   ZmsZvolr   r   r   Úmatch_eventsE   s(    
þÿ"r8   c                 C   sz   g d }}|t k rn| t|dœ}|s*d|d< tt› d�|ƒ}|s@qn||7 }|t|ƒ7 }t|ƒtk rbqnt d¡ q
||t kfS )Nr   )Úmarketr   r   r   Ú	takerOnlyú/tradesg¸…ëQ¸¾?)Ú
OFFSET_CAPÚPAGEr   ÚDATAr/   r   r   )ÚcidZ
taker_onlyÚrowsr6   ÚpÚdr   r   r   Útrades[   s    
rC   é   r   c                    sZ  |rt |ƒnt | ƒ}tt|ƒ› d|r(dnd› d�ƒ t dd„ ¡}d}|D �]}|| kr^ �qV| d¡pjd d	d
¡ ¡ }|rÄd}|d D ]4}	tt	› d�|	 d¡dt
d ddœƒ}
|
rˆd} q¾qˆ|rÄqJ|d7 }td|dd… d›�ddd� d}|d D �]H}	|	 d¡}zdd„ t |	 d¡¡D ƒ‰ W n t�y<   Y qöY n0 t|dƒ\}}t|dƒ\}}dd„ |D ƒ}|t|ƒ7 }t t¡}t dd„ ¡}|D �]b}|d }z@t| d ¡�p°dƒ}t| d!¡�pÄdƒ}t| d"¡�pØdƒ}W n ttf�yü   Y �q�Y n0 |d# |f|v}|�r*|| d$  d7  < n|| d%  d7  < || d   |7  < || d&  | d'¡¡ || d( | d'¡  |¡ | d)¡�p”d ¡ d*k�rÊ||  || 8  < || |  |7  < n(||  || 7  < || |  |8  < �q�| ¡ D ]@\}}t‡ fd+d,„||  ¡ D ƒƒ}|| d-  || 7  < �qþqötd.|d/›d0�ƒ qJ|S )1a  Aggregate maker behaviour over matches with COMPLETE trade history.

    THE TRUNCATION PROBLEM. /trades refuses offset > ~10500, and before/after
    filters are ignored, so the biggest markets return only their most RECENT
    10k rows per leg -- a slice pressed right up against settlement, where
    trades happen at 0.999/0.001 and are near-riskless cleanup rather than
    market making. Measuring "legs quoted per match" on that window biases
    hard toward 1, because a maker who worked all three legs during the match
    but only one at the end reads as a one-leg maker.

    So: walk DOWN the volume ranking and keep only matches whose every leg
    came back under the cap. Smaller markets, but the history is whole and the
    number means what it says.
    z+ candidate matches with the 3-leg structurez" (selecting complete-history ones)r   Ú
c                   S   s   ddddt ƒ t t ¡dœS )Nç        r   )ÚpnlÚmaker_fillsÚtaker_fillsÚsizeÚeventsÚlegs)ÚsetÚcollectionsÚdefaultdictr   r   r   r   r&      s    þzstudy.<locals>.<lambda>r   r   ÚasciiÚreplaceFr   r;   ZconditionIdr   r   r   )r9   r   r   r:   Tz  Né4   z<52)ÚendÚflushc                 S   s   g | ]}t |ƒ‘qS r   )r%   )r!   Úxr   r   r   r+   ›   r$   zstudy.<locals>.<listcomp>r   c                 S   s   h | ]}|d  |d f’qS )ÚtransactionHashÚproxyWalletr   )r!   Útr   r   r   Ú	<setcomp>    r$   zstudy.<locals>.<setcomp>c                   S   s
   t  t¡S )N)rN   rO   r%   r   r   r   r   r&   ¤   r$   rW   rJ   ZpriceZoutcomeIndexrV   rH   rI   rK   ZslugrL   ZsideZBUYc                 3   s.   | ]&\}}||t ˆ ƒk r ˆ | nd  V  qdS )rF   N©r/   )r!   r   Úq©Zpricesr   r   r#   ¹   s   ÿzstudy.<locals>.<genexpr>rG   ú z>6z rows)r8   Úprintr/   rN   rO   r
   ÚencodeÚdecoder   r>   r<   r   Úloadsr   rC   r%   ÚintÚ	TypeErrorÚ
ValueErrorÚaddÚupperÚitemsr1   )r4   Zcomplete_onlyZpoolr7   ÚWZusedr*   r   Z	trunc_anyr"   rB   Zn_rowsr?   ZallrZtruncZtkr)   ZtkeyZcashZposnrX   ÚwZszZpxZoiZis_makerÚcZsettler   r\   r   Ústudyl   sz    
ÿ

þ



(*,
ÿrk   c                  C   s”  t tjƒdkrttjd ƒnd} t| ƒ}g }| ¡ D ]¾\}}|d |d  }|dk rVq4|d | }dd„ |d  ¡ D ƒ}| |t|d	 d
ƒ|t|dƒt|d ƒt |d ƒ|rÂtt	|ƒt |ƒ d
ƒnd|rètt	dd„ |D ƒƒt |ƒ d
ƒnddœ¡ q4|j
dd„ d� dd l}ttdddd��J}	|j|	|�r:t|d  ¡ ƒng d¢d�}
|
 ¡  |
 |¡ W d   ƒ n1 �sn0    Y  tdt |ƒ› dt› d�ƒ dd„ |D ƒ}tdƒ tdd  ƒ |d d!… D ]6}td"|d# |d	 |d$ |d% |d |d& f ƒ �qÀ|�r�d'd„ |D ƒ}td(t |ƒt |ƒd)t |ƒ t |ƒ f ƒ |�r�td*ƒ t	d+d„ |D ƒƒt |ƒ }t	d,d„ |D ƒƒt |ƒ }td-| ƒ td.| ƒ dS )/Nr   rD   rH   rI   é   c                 S   s   g | ]}t |ƒ‘qS r   rZ   )r!   Úvr   r   r   r+   É   r$   zmain.<locals>.<listcomp>rL   rG   é   r   rJ   rK   r   c                 s   s   | ]}|d krdV  qdS )r   r   Nr   )r!   ÚLr   r   r   r#   Ï   r$   zmain.<locals>.<genexpr>)ÚwalletrG   ÚfillsÚmaker_sharerJ   rK   Úavg_legs_per_eventÚall3_event_sharec                 S   s
   | d  S )NrG   r   )r   r   r   r   r&   Ñ   r$   zmain.<locals>.<lambda>r'   ri   r   zutf-8)ÚnewlineÚencoding)Z
fieldnamesz
wrote z wallets -> rE   c                 S   s   g | ]}|d  dkr|‘qS )rr   g333333ã?r   ©r!   r   r   r   r   r+   Ù   r$   z*TOP MAKER-DOMINANT WALLETS BY REALISED P&Lz  %-44s %10s %7s %6s %6s %s)rp   rG   rq   zmk%Zevtszlegs/evté   z!  %-44s %10.0f %7d %6.2f %6d %.2frp   rq   rr   rs   c                 S   s   g | ]}|d  dkr|‘qS )rG   r   r   rw   r   r   r   r+   á   r$   z7
  maker-dominant wallets: %d   profitable: %d (%.0f%%)r   z8  THE QUESTION: legs quoted per match, profitable makersc                 s   s   | ]}|d  V  qdS )rs   Nr   rw   r   r   r   r#   æ   r$   c                 s   s   | ]}|d  V  qdS )rt   Nr   rw   r   r   r   r#   ç   r$   z<    mean legs/match      : %.2f  (3.0 = full book like ours)z    share of matches all3: %.2f)r/   ÚsysÚargvrb   rk   rg   Úvaluesr2   Úroundr1   r3   ÚcsvÚopenÚOUTZ
DictWriterÚlistÚkeysZwriteheaderZ	writerowsr^   )Únrh   r@   ri   ÚsZtotZmsharerL   r}   ZfhZw_Zmkr   ZprofÚaZs3r   r   r   ÚmainÀ   sZ     
&û
ÿ2ÿÿ
 ÿr…   Ú__main__)r   )T)rD   Tr   )Ú__doc__rN   r   Úosry   r   r	   r,   r>   ÚpathÚjoinÚdirnameÚabspathÚ__file__r   r=   r<   r   r8   rC   rk   r…   Ú__name__Úexitr   r   r   r   Ú<module>   s$   '


T-