a
    ºgej  ã                   @   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 dd¡ ddl	Z
dZdZeejƒdkrjeejd ƒndZg d	¢Zd
d„ Zdd„ Zddd„Zdd„ Zdd„ Zedkr®eƒ  dS )u‚	  
Market-making feasibility on Kalshi soccer â€” measured, not modelled. (read-only)

WHY THIS FIRST. project_xlow_spread_verdict established that the money in these
markets is the spread, and that MAKERS PAY NO FEE (confirmed on a real fill:
7.8 contracts @ 0.36, fee_cost 0.0000) while takers pay 0.07*P*(1-P) ~= 4.4% of
stake. So making is the structurally correct side. The open question is whether
the spread you collect survives ADVERSE SELECTION â€” you get filled precisely when
someone wanted to trade, which is disproportionately when they are right.

That question is answerable from history with zero capital at risk, which is why
this runs before anything is built. [[project_no_subask_limits]] already measured
a version of it (resting BELOW ask on a directional bet: 37% fill rate, P&L
+$213 -> +$19). This is the cleaner test: pure passive quoting, both sides.

THE MEASUREMENT. Kalshi's /markets/trades exposes `taker_book_side` on every
trade, so we know which side was PASSIVE. **The field describes the TAKER'S OWN
order side** -- verified against our own fills, which are all buys and all carry
`book_side='bid'`, `is_taker=true`:
  - taker_book_side == 'bid'  -> the TAKER BOUGHT (lifted a resting ask)
                                 -> a MAKER SOLD at that price
  - taker_book_side == 'ask'  -> the TAKER SOLD (hit a resting bid)
                                 -> a MAKER BOUGHT at that price
The first version of this script had that inverted and produced a confident
-5% "market making loses" result. Sanity check that saves you: takers here are
net BUYERS about 4:1, which is exactly what a sports market looks like (retail
backs teams, it rarely lays them). If your mapping shows takers as net sellers,
it is backwards.
Every settled market has a known outcome (1.00 or 0.00), so each passive fill has
an exact realised P&L. No simulation of queue position is needed for the headline
number: this is what makers actually got.

READ IT AS: if passive BUYS settle below what makers paid, and passive SELLS
settle above what makers received, adverse selection is winning and market making
here loses despite the zero fee. Both sides positive = the spread survives.

CAVEAT THIS CANNOT ANSWER: queue priority. These are the fills makers DID get;
whether *you* would have been at the front of the queue is a latency question no
historical data answers.

Usage:  python research\mm_feasibility.py [n_markets]
é    N)ÚdefaultdictzE:\soccer\mainz&E:\soccer\research\kalshi_backfill.csvz E:\soccer\research\mm_trades.csvé   é<   ©ÚtickerÚdivÚ
result_yesZtsÚpriceÚcountÚtaker_book_sidec                 C   s~   g }zTt tddd��2}t |¡D ]}|d }| |¡ q W d  ƒ n1 sL0    Y  W n tyn   g  Y S 0 ||  d… S )zLSettled soccer markets whose YES outcome we already know, from the backfill.Ú úutf-8©ÚnewlineÚencodingÚeventN)ÚopenÚBACKFILLÚcsvÚ
DictReaderÚappendÚFileNotFoundError)ÚnÚoutÚfhÚrZev© r   ú$E:\soccer\research\mm_feasibility.pyÚsettled_targets:   s    .
r   c                 C   s<   z t  d| ddœ¡}| dg ¡W S  ty6   g  Y S 0 d S )Nz/marketsé
   )Úevent_tickerÚlimitZmarkets)ÚktÚ_getÚgetÚ	Exception)r    r   r   r   r   Úlegs_for_eventJ   s
    r&   éÐ  c                 C   sŒ   g d  }}t |ƒ|k rˆ| ddœ}|r,||d< zt d|¡}W n tyR   Y qˆY n0 | dg ¡}||7 }| d¡}|rˆ|s|qˆt d¡ q
|S )NéÈ   )r   r!   Úcursorz/markets/tradesZtradesg{®Gáz´?)Úlenr"   r#   r%   r$   ÚtimeÚsleep)r   Zcapr   r)   ÚpÚdZtrr   r   r   Ú
trades_forR   s     



r/   c                     sL  dd l } ttƒ}|s tdƒ d S tƒ ‰ d}| j t¡r¸ttddd��J}t	 
|¡D ].}ˆ  d |d  d¡d d… ¡¡ |d	7 }qPW d   ƒ n1 s”0    Y  td
tˆ ƒ› d|› d�ƒ ‡ fdd„|D ƒ}tdt|ƒ› dt|ƒ› d�ƒ ttdddd�}t	j|td�}ˆ �s| ¡  d}t|d	ƒD �]\}}	t|	d ƒD ]¸}
|
 d¡}|dv�rT�q8t|
d ƒD ]‚}zZ| t|
d |	d |dk�r†d	nd| d¡t|d ƒt|d ƒ| d¡d�¡ |d	7 }W n  tttf�yÞ   Y �q`Y n0 �q`t d¡ �q8|d dk�r"| ¡  td |› d!t|ƒ› d"|› d#�ƒ �q"| ¡  td$|› d%t› �ƒ d S )&Nr   z9no backfill rows -- run research/kalshi_backfill.py firstr   r   r   ú-r   é   r   z
resuming: z
 events / z trades already on diskc                    s   g | ]}|d  ˆ vr|‘qS )r   r   )Ú.0Út©Zdoner   r   Ú
<listcomp>v   ó    zmain.<locals>.<listcomp>zpulling trades for z new events (z requested) ...Úa)Z
fieldnamesr   Úresult)ÚyesZnor   r9   Zcreated_timeZyes_price_dollarsZcount_fpr   r   gš™™™™™©?é   z  ú/z	 events, z new tradeszwrote z new trades -> ) Úosr   Ú	N_MARKETSÚprintÚsetÚpathÚexistsÚOUTr   r   r   ÚaddÚjoinÚsplitr*   Z
DictWriterÚFIELDSZwriteheaderÚ	enumerater&   r$   r/   ZwriterowÚdictÚfloatÚKeyErrorÚ	TypeErrorÚ
ValueErrorr+   r,   ÚflushÚclose)r<   ÚtargetsÚexistingr   r   ZtodoÚwÚtotalÚir3   ÚmÚresÚxr   r4   r   Úmaine   sX    "(



ú$rW   c                 C   s¶  d } }}d } }}| D ]z}|d |d |d   }}	}
|d dkrj||	7 }|||	 7 }||
|	 7 }q|d dkr||	7 }|||	 7 }||
|	 7 }qt dƒ |rî|| }t d	|d
›d|| d›d|| d›d|d›d|| d d›d�ƒ |�r>|| }t d|d
›d|| d›d|| d›d|d›d|| d d›d�ƒ |�r²|�r²|| ||  }|| }t d|d›d|d›d|| d d›d�ƒ t ƒ  t dƒ t dƒ t dƒ t dƒ d S )!z0Realised P&L of the PASSIVE side of every trade.g        r	   r
   r   r   ZbidZaskz8PASSIVE (MAKER) SIDE, realised to settlement, zero fees:z  maker BOUGHT : z12,.0fz contracts  avg px z.4fz
  settled z  P&L $z+,.0fz  (éd   z+.2fz
% of cost)z  maker SOLD   : z% of received)z  COMBINED     : P&L $z on $z,.0fz notional = z+.3fú%z<  Positive combined = the spread survives adverse selection.zG  Negative = takers are informed and making loses despite the zero fee.zE  NOTE: assumes you get every passive fill. Queue priority is the onez=  thing history cannot tell you -- treat this as the CEILING.N)r>   )ZrowsZbuy_nZbuy_costZbuy_valZsell_nZ	sell_recvZsell_valr   r-   ÚcrU   ZpnlZtotZnotionalr   r   r   Úanalyse˜   sT    ÿÿþ
ÿÿþ
ÿ
r[   Ú__main__)r'   )Ú__doc__Úsysr   r+   ZjsonÚcollectionsr   r@   ÚinsertZkalshi_traderr"   r   rB   r*   ÚargvÚintr=   rF   r   r&   r/   rW   r[   Ú__name__r   r   r   r   Ú<module>   s$   * 
3&