a
    ÅP¨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ej 	ej 
e¡¡Zej ed¡Zej ed¡Zh d£Zddd	„Zd
Zefdd„Zdd„ Zedkr¦e eƒ ¡ dS )aõ  Accurate per-wallet P&L for Polymarket makers, from the FULL activity ledger.

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

WHY NOT /trades. Reconstructing a position from /trades alone is WRONG for
exactly the wallets worth studying. Polymarket lets you SPLIT $1 of collateral
into a Yes+No pair and MERGE it back, and neither is a trade -- so a maker who
mints inventory instead of crossing a spread has trades that look like
unexplained one-sided selling. pm_maker_study.py did this and produced $1.7M on
384 fills, which is not a real number.

/activity carries every cash-affecting event:
    TRADE  SPLIT  MERGE  REDEEM  REWARD  MAKER_REBATE  YIELD
so net cash flow over a closed history IS realised P&L, with no position
arithmetic required at all.

SIGNS. BUY -usdc, SELL +usdc, SPLIT -usdc (pay collateral, receive tokens),
MERGE +usdc (return tokens, receive collateral), REDEEM +usdc (settlement).
Still-open positions are valued from /positions.currentValue.

SUBSIDY IS NOT THE STORY. Kalshi pays makers nothing, so income Polymarket pays
for providing liquidity would not transfer to us -- but measured over six
high-activity wallets it is 0.000%-0.351% of trade volume. Reported separately
so it can be subtracted, but it does not change any ranking.

CROSS-CHECK. /positions carries Polymarket's OWN cashPnl per open position. Two
independent derivations that agree are worth far more than one that cannot be
checked; `pnl_check_delta` is the disagreement and a large value means do not
trust the row.
é    Nzhttps://data-api.polymarket.comzpm_maker_study.csvzpm_maker_pnl.csv>   ZMAKER_REBATEZREWARDZYIELDé   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timeoutg333333ó?é   éÈ   i�  )ÚrangeÚrequestsÚgetÚ	ExceptionÚtimeÚsleepZstatus_codeZjson)Zurlr   ZtriesÚiÚr© r   úresearch/pm_maker_pnl.pyÚ_get.   s    

r   i  c                 C   s6  d}d}d}t  ¡ }d}d}||kr,d}�qHtt› d�| d|dœƒ}|du rTd}�qH|s\�qH|D ]¼}	|	 d	¡}
zt|	 d
¡p~dƒ}W n ttfyž   Y q`Y n0 ||
  d7  < |
tv rÂ||7 }q`|
dkrø||7 }||	 d¡pàd 	¡ dkrî|n| 7 }q`|
dk�r||8 }q`|
dv r`||7 }q`|t
|ƒ7 }t
|ƒdk �r<�qHt d¡ qd}d}tt› d�| ddœƒ�pjg }|D ]T}z4|t| d¡�pˆdƒ7 }|t| d¡�p dƒ7 }W n ttf�yÀ   Y n0 �qp| t|| dƒt|dƒt|| | dƒt|ƒt|dƒt|dƒt| ¡ ƒ| dd¡| dd¡| dd¡t|ƒdœS )aj  Cash-flow P&L. `capped` means the history is INCOMPLETE -- do not use it.

    The offset cap is not a nuisance, it is a correctness boundary. With only
    the most recent N events, a SELL or REDEEM whose matching purchase falls
    outside the window has no cost basis and reads as pure profit, so a
    truncated wallet's P&L is biased UPWARD without limit. That is what
    produced an $8.8M row on the first pass.

    So a wallet is only scored when its ENTIRE history fits under the cap.
    That biases the sample toward smaller operators -- the tradeoff is an exact
    number instead of a fictional one.
    g        r   FTz	/activityiô  )ÚuserÚlimitÚoffsetNÚtypeZusdcSizer   ZTRADEZsideÚ ZSELLZSPLIT)ÚMERGEÚREDEEMgš™™™™™¹?z
/positions)r   r   ZcurrentValueZcashPnlé   r   r   )ÚwalletÚ
pnl_spreadÚpnl_subsidyZ	pnl_totalZtrade_volumeZ
open_valueZpm_cashpnl_openÚrowsZsplitsZmergesZredeemsÚcapped)ÚcollectionsÚCounterr   ÚDATAr	   ÚfloatÚ	TypeErrorÚ
ValueErrorÚSUBSIDYÚupperÚlenr   r   ÚroundÚsumÚvaluesÚint)ÚwZmax_rowsZcashZsubsidyZvolÚtypesZoffr   ÚdÚaÚtÚuZopen_valZown_pnlÚposÚpr   r   r   Ú
wallet_pnl?   sp    


&





õr4   c               
      sv  t tjƒdkrttjd ƒnd‰ tt ttdd�¡ƒ} ‡ fdd„| D ƒ}t	t |ƒ› dˆ › d�ƒ g }t
|dƒD ]®\}}t|d	 ƒ}|d
 |d
< |d |d< |d |d< |d |d< |d rÞt	d|t |ƒ|d	 d d… f ƒ qn| |¡ t	d|t |ƒ|d	 d d… |d |d |d f ƒ qn|jdd„ d� ttdddd��<}tj|t|d  ¡ ƒd�}| ¡  | |¡ W d   ƒ n1 �s‚0    Y  t	dt |ƒ› dt› �ƒ d d„ |D ƒ}t	d!t |ƒt |ƒd"t |ƒ tt |ƒdƒ f ƒ |�r8td#d$„ |D ƒƒt |ƒ }	td%d$„ |D ƒƒt |ƒ }
t	d&|	 ƒ t	d'd"|
  ƒ d(d„ |D ƒ}|�rrtd)d$„ |D ƒƒt |ƒ }	t	d*|	 ƒ dS )+Nr   r   zutf-8)Úencodingc                    s0   g | ](}t |d  ƒˆ krt|d ƒdkr|‘qS )ÚfillsÚmaker_shareg333333ã?)r+   r"   )Ú.0r   ©Z	min_fillsr   r   Ú
<listcomp>Œ   s    ÿzmain.<locals>.<listcomp>z maker-dominant wallets with >=z fills
r   r7   Úavg_legs_per_eventÚall3_event_sharer6   Zfills_sampledr   z5  %2d/%d %s  SKIPPED - history exceeds the offset capé   z:  %2d/%d %s spread=%12.0f subsidy=%8.0f rows=%-6d COMPLETEr   r   r   c                 S   s
   | d  S )Nr   r   )r.   r   r   r   Ú<lambda>ž   ó    zmain.<locals>.<lambda>)Úkeyr,   r   )Únewliner5   r   )Z
fieldnamesz
wrote z -> c                 S   s   g | ]}|d  dkr|‘qS ©r   r   r   ©r8   r.   r   r   r   r:   £   r?   z.
profitable on SPREAD alone: %d of %d (%.0f%%)éd   c                 s   s   | ]}t |d  ƒV  qdS ©r;   N©r"   rC   r   r   r   Ú	<genexpr>§   r?   zmain.<locals>.<genexpr>c                 s   s   | ]}t |d  ƒV  qdS )r<   NrF   rC   r   r   r   rG   ¨   r?   z@  their mean legs/match      : %.2f  (3.0 = full book like ours)z%  their share of matches all3: %.0f%%c                 S   s   g | ]}|d  dkr|‘qS rB   r   rC   r   r   r   r:   «   r?   c                 s   s   | ]}t |d  ƒV  qdS rE   rF   rC   r   r   r   rG   ­   r?   z#  LOSERS mean legs/match     : %.2f)r'   ÚsysÚargvr+   ÚlistÚcsvZ
DictReaderÚopenÚIN_CSVÚprintÚ	enumerater4   ÚappendÚsortÚOUTZ
DictWriterÚkeysZwriteheaderZ	writerowsÚmaxr)   )ÚsrcZwsÚoutr   r   r.   ZfhZw_Zprofr/   Zs3Zlosr   r9   r   Úmain‰   sR     ÿ
"ÿÿ2&ÿrW   Ú__main__)r   )Ú__doc__r   rK   ÚosrH   r   r   r!   ÚpathÚdirnameÚabspathÚ__file__ZHEREÚjoinrM   rR   r%   r   ZACTIVITY_CAPr4   rW   Ú__name__Úexitr   r   r   r   Ú<module>   s"   
J)