a
    nÞ‰jŠA  ã                   @   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dlm	Z	 ddl
mZ ej dd¡ ddlZddlZdZdZdZd	Zd	Zd
ZdZe	dƒZg d¢Zdai Zeddœdd„Zedœdd„Zdd„ Z dd„ Z!dddœZ"dZ#e#fe$dœdd„Z%dd „ Z&d!d"„ Z'e$dœd#d$„Z(e)dœd%d&„Z*e+e$d'œd(d)„Z,efe+e$e$e)e-d*œd+d,„Z.d7e+e)d.œd/d0„Z/e+e$e-d1œd2d3„Z0d4d5„ Z1e2d6k�r~e1ƒ  dS )8as  Poly-US order layer for the live three-leg maker. Mirror of kalshi_maker.py.

SEPARATE MODULE ON PURPOSE. mm_maker_live.py is holding real Kalshi positions and
completing at 76.7% right now; a second venue must not be able to break it. Same
reasoning mm_quoter_polyus.py already applies to the paper side.

WHAT A "MAKER ASK" IS HERE. On Kalshi the maker posts `side: 'ask'` -- sell YES.
Poly-US has no naked sell-YES; the equivalent is BUYING NO, which is the same
economics (buy NO at q == sell YES at 1-q) and opens a position without holding
anything first. So `place_maker_ask(slug, qty, yes_price)` sends
ORDER_INTENT_BUY_SHORT at (1 - yes_price).

  *** THE SIDE MECHANICS ARE NOT YET VERIFIED ON A REAL FILL. ***
  Everything else here was proven live on 2026-08-22 at $0.00 cost: post-only
  refuses crosses, GTD self-expiry fires at 90s, cancel works and verifies, and
  three legs of one book rest simultaneously. The BUY_SHORT price convention is
  read from docs.polymarket.us and has NOT been confirmed against a fill. Run
  verify_side_convention() before arming. Docs and this venue have disagreed
  before -- cancel_order once called an endpoint that did not exist and 404'd
  silently for the entire period the account was auto-trading.

TWO THINGS DIFFER FROM KALSHI AND BOTH BITE:

  buyingPower DOES NOT DECREMENT for resting orders. Measured: $3.00 of orders
    resting, buyingPower unchanged at $101.1668. So the venue's own number is
    useless as a collateral check and resting_notional() computes it here.

  A POST-ONLY KILL LOOKS LIKE SUCCESS. A crossing post-only order returns
    HTTP 200 with {"id": ..., "executions": []} and is then silently discarded.
    "Received" and "resting" are indistinguishable in the response, so nothing
    may treat a 200 as a resting order -- verify against open_orders().

LIVE IS OFF BY DEFAULT. set_live(True) must be called explicitly.
é    N)ÚPath)ÚOptionalzE:\soccer\mainéZ   ç      @g®Gáz®ï?g{®Gáz„?g      .@é   z'E:\soccer\research\pus_maker_orders.csv©ÚtsÚactionÚslugÚ	yes_priceÚno_priceÚcountÚ
expires_atZliveÚorder_idÚstatusÚdetailF)ÚflagÚreturnc                 C   s   t | ƒad S ©N)ÚboolÚ_LIVE)r   © r   úmain\poly_us_maker.pyÚset_liveH   s    r   )r   c                   C   s   t S r   )r   r   r   r   r   Úis_liveM   s    r   c	                 C   s¶   zžt jjddd� t  ¡  }	t jdddd��`}
tj|
td�}|	rH| ¡  | 	t
j t
jj¡ ¡ | |||||t|prd||dœ¡ W d   ƒ n1 s’0    Y  W n ty°   Y n0 d S )	NT)ÚparentsÚexist_okÚaÚ zutf-8)ÚnewlineÚencoding)Z
fieldnamesr   )Ú	ORDER_LOGÚparentÚmkdirÚexistsÚopenÚcsvZ
DictWriterÚ
LOG_FIELDSZwriteheaderZwriterowÚdtÚdatetimeÚnowÚtimezoneÚutcÚ	isoformatr   ÚOSError)r	   r
   r   r   r   r   r   r   r   ÚnewZfhÚwr   r   r   Ú_logQ   s     
û*r1   c                 C   s�   | rt | tƒstd| ›�ƒ‚||kr,|tk r@tdt› d|›�ƒ‚|tkr\td|› dt› �ƒ‚t|  krptksŒn td|› dt› dt› �ƒ‚d S )	Nz	bad slug zcount must be >= z, got zcount z exceeds MAX_CONTRACTS=z
yes_price z	 outside z..)Ú
isinstanceÚstrÚ
ValueErrorÚ	MIN_COUNTÚMAX_CONTRACTSÚ	MIN_PRICEÚ	MAX_PRICE)r
   r   r   r   r   r   Ú	_validated   s    r9   ç        )r   Úrowsg      @)Úmax_agec                 C   sÂ   t   ¡ }| dkr4td dur4|td  | kr4td S d}tdƒD ]x\}}|rVt  |¡ z*t ¡ pbg }|td< t   ¡ td< |W   S  ty¶ } z|}dt|ƒvr¢‚ W Y d}~q@d}~0 0 q@|‚dS )a=  Resting orders, from the EXCHANGE, cached for a few seconds.

    RATE LIMITED WITHOUT THIS. Poly-US sits behind Cloudflare and returns
    429 / error 1015 under load. One place_maker_ask call reads this list
    TWICE (order count, then notional), the driver reads it again to see what
    is already working, and again to confirm each post -- so a nine-leg cycle
    made ~40 requests every 25s and the endpoint started refusing. The caps
    then fail closed, so the maker went silent and posted nothing: safe, but
    indistinguishable from a dead market.

    A 3-second cache collapses that to roughly one request per cycle. Pass
    max_age=0 where freshness genuinely matters -- confirming a specific order
    actually rested is the one case that does.

    Raises on failure; callers fail closed on the exception.
    r   r;   Nr   )r   g       @r   Z429)ÚtimeÚ_ORDERS_CACHEÚ	enumerateÚsleepÚputZopen_ordersÚ	Exceptionr3   )r<   r*   ZlastZattemptZpauser;   Úer   r   r   Ú_orderss   s&    ÿ

rD   c                   C   s   dt d< dt d< dS )z3Drop the cache -- call after placing or cancelling.Nr;   r:   r   )r>   r   r   r   r   Úinvalidate_orders_cacheœ   s    rE   c              	   C   sŽ   |   d¡}z(t|tƒr$t|  d¡ƒn
t|p,dƒ}W n ttfyJ   Y dS 0 zt|   d¡p\dƒ}W n ttfyz   Y dS 0 t|d| ƒ| S )av  Cash a resting order can consume. CONSERVATIVE BY CONSTRUCTION.

    The book quotes every order YES-side, so the same price means different
    commitments depending on the intent behind it:
        BUY_LONG  (bid YES at P)                  commits  P * qty
        BUY_SHORT (offer YES at P = buy NO at 1-P) commits (1-P) * qty
    The listing does not reliably say which, and this account can carry both
    (the xlow trader places BUY_LONG). Taking the larger of the two can only
    over-state exposure, which makes the cap bind early rather than late --
    the right direction for a venue that has never been run live.
    ÚpriceÚvaluer   r:   Úquantityç      ð?)Úgetr2   ÚdictÚfloatÚ	TypeErrorr4   Úmax)ÚoÚpxÚvÚqr   r   r   Ú_order_notional¢   s    
(rS   c                   C   s6   zt dd„ tƒ D ƒƒW S  ty0   tdƒ Y S 0 dS )aN  Dollars committed to resting orders.

    Computed here rather than read from the venue: buyingPower does NOT move
    when orders rest (verified 2026-08-22, $3.00 resting and buyingPower
    unchanged), so it cannot serve as the cap this is protecting.

    Fails CLOSED -- if the book cannot be read, report the cap as reached.
    c                 s   s   | ]}t |ƒV  qd S r   )rS   ©Ú.0rO   r   r   r   Ú	<genexpr>Ä   ó    z#resting_notional.<locals>.<genexpr>ÚinfN)ÚsumrD   rB   rL   r   r   r   r   Úresting_notionalº   s    	rZ   c                   C   s(   zt tƒ ƒW S  ty"   t Y S 0 d S r   )ÚlenrD   rB   ÚMAX_CONCURRENT_ORDERSr   r   r   r   Úopen_order_countÉ   s    r]   )r
   r   c                 C   sf   zFt  ¡ pi }| d¡pi }| | ¡}|du r4W dS t| d¡pBdƒW S  ty`   tdƒ Y S 0 dS )z‹Net contracts held in this market. NaN when positions are unreadable,
    which callers must treat as 'refuse to post' rather than as zero.Z	positionsNr:   ZnetPositionDecimalr   Únan)ÚpaZget_positionsrJ   rL   rB   )r
   ÚpÚposrQ   r   r   r   Úcurrent_positionÐ   s    
rb   )r
   r   r   Úttl_sr   c                 C   sv  t | ||ƒ td| dƒ}tj tjj¡tj|d�  ¡ }t	ƒ }|t
krxtd| ||||dd|› d�ƒ	 t|› dt
› �ƒ‚t| ƒ}||kr¨td| ||||ddd	ƒ	 td
ƒ‚t|ƒ| td k�rtd| ||||ddd|› d|› dt› �ƒ	 td|› d|› dt› d| › �ƒ‚tƒ ||  }|tk�rXtd| ||||ddd|d›�ƒ	 td|d›dtd›�ƒ‚t�s˜td| ||||ddt d||d|dœ¡ƒ	 d| |||dœS ztj| ||dd|d�}	W nP t�y }
 z6td| ||||ddt|
ƒj› d|
› �ƒ	 ‚ W Y d}
~
n
d}
~
0 0 tƒ  |	�pi  d ¡�p*|	�p$i  d!¡}|	�p4i  d"¡�p@g }|�rP| t|< td#| |||||dd$t|ƒ› �ƒ	 |	S )%aT  Rest a maker order equivalent to selling YES at `yes_price`.

    Implemented as BUY NO at (1 - yes_price): Poly-US has no naked sell-YES, and
    buying NO opens the same economic position without holding anything first.

    Post-only, so it can never cross and can never be the taker -- the entire
    case for this venue is earning the 0.0125*p(1-p) maker rebate instead of
    paying the 0.06*p(1-p) taker fee, and one crossed order inverts that.

    Returns the API response, or {'dry_run': True} when not armed. Every cap is
    checked against the EXCHANGE before any network write.
    rI   é   )ZsecondsZrejectNZcapz orders already restingz resting orders >= cap zpositions unreadablez)cannot read positions -- refusing to postç•Ö&è.>zheld z + z	 exceeds z	position z plus z would exceed MAX_CONTRACTS=z on z
notional $z.2fzresting notional $z exceeds cap $Údry_runÚokZORDER_INTENT_BUY_SHORTT)ÚintentrF   rH   Ú	post_onlyrc   )rf   r
   r   r   r   )rh   ri   Zttl_secsÚerrorú: ÚidÚorderIdZ
executionsZplacedzexecutions=)r9   Úroundr(   r)   r*   r+   r,   Z	timedeltar-   r]   r\   r1   ÚRuntimeErrorrb   Úabsr6   rZ   ÚMAX_RESTING_NOTIONALr   ÚjsonÚdumpsrA   Zplace_limit_orderrB   ÚtypeÚ__name__rE   rJ   Ú_PLACEDr[   )r
   r   r   rc   r   Zexp_isoZn_openZheldZnotionalÚrrC   ÚoidZexecsr   r   r   Úplace_maker_askÞ   s~    
ÿ
ÿÿÿÿÿ
ÿÿþÿÿ
þ
ÿ"ÿry   r   )Úreasonr   c                 C   sx  t tƒ}z:tƒ D ].}| d¡p&| d¡}|r| d¡p8d||< qW nX tyš } z@tdddddddddt|ƒ› d	t|ƒj› d
|› �ƒ	 W Y d}~n
d}~0 0 d}t	| 
¡ ƒD ]Æ\}}tsÚtd|dddd|d| ƒ	 |d7 }q¬z<t ||¡ td|dddd|d| ƒ	 t |d¡ |d7 }W q¬ t�yp } z@t |d¡ td|dddd|dt|ƒj› d
|› �ƒ	 W Y d}~q¬d}~0 0 q¬|S )a  Cancel everything we placed AND everything the venue lists resting.

    Union, for the same reason as Kalshi: our own ids miss anything a previous
    process left behind, and the listing misses anything accepted but not yet
    published. Cancelling a dead order is harmless.
    rl   rm   Ú
marketSlugr   Ú
cancel_allNrj   zlisting failed, using z known ids: rk   r   Zdry_run_cancelrg   é   Z	cancelledZcancel_fail)rK   rv   rD   rJ   rB   r1   r[   rt   ru   ÚlistÚitemsr   rA   Zcancel_orderÚpop)rz   ÚtargetsrO   rx   rC   Únr
   r   r   r   r|   .  s>    
ÿÿÿÿr|   )r
   r   r   c                    sV  t }tdƒ ˆ |td| dƒdœ}�z
z¦tˆ td |ƒ}||d< t d¡ ‡ fdd	„tƒ D ƒ}g }|D ]N}| d
¡}t	|t
ƒrˆ| d¡n|}	| |	| d¡| d¡p¬| d¡dœ¡ qf||d< t|ƒ|d< W n> t�y
 }
 z$t|
ƒj› d|
› �|d< W Y d}
~
n
d}
~
0 0 W ztdƒ W t|ƒ n
t|ƒ 0 n"ztdƒ W t|ƒ n
t|ƒ 0 0 |S )a  Prove BUY_SHORT rests where we think it does. RUN BEFORE ARMING.

    Places one deep, unfillable post-only order and reports what actually
    appears on the book, so the price convention is confirmed against the venue
    rather than against the docs. Cancels itself.
    TrI   rd   )r
   Zsent_yes_priceZsent_no_priceéd   Zresponseé   c                    s   g | ]}|  d ¡ˆ kr|‘qS )r{   )rJ   rT   ©r
   r   r   Ú
<listcomp>`  rW   z*verify_side_convention.<locals>.<listcomp>rF   rG   rH   rl   rm   )Zprice_on_bookZqtyrl   ZrestingZrestedrk   rj   NÚverify_side_convention)r   r   rn   ry   r5   r=   r@   rD   rJ   r2   rK   Úappendr   rB   rt   ru   r|   )r
   r   ZwasÚoutrw   Zminer;   rO   rP   rQ   rC   r   r…   r   r‡   Q  s8    ÿ

ÿ
0
ý
r‡   c               
   C   s  t dt› d�ƒ tdu s J dƒ‚dD ]6} zt| Ž  W n ttfyJ   Y q$0 td| ›�ƒ‚q$tddd	ƒ}| d
¡du s~J dƒ‚|d d	ksœJ d|d › �ƒ‚tt	ddiddœƒd ƒdk s¾J ‚tt	ddiddœƒd ƒdk sàJ ‚t dƒ t dt
› dt› dt› dt› d�	ƒ dS )z%Guards only -- no network, no orders.zLIVE = z  (must be False here)Fzmodule must not start armed))Nr}   ç      à?)Úsr   rŠ   )r‹   iç  rŠ   )r‹   r}   r:   )r‹   r}   g      ø?z_validate accepted z	fake-slugrI   g333333Ó?rf   Tzunarmed place must be a dry runr   zexpected yes_price 0.30, got rG   z0.79r}   )rF   rH   gHáz®Gé?re   z0.21zGcaps, YES-side pricing and conservative notional OK; dry run is offlinezMAX_CONTRACTS=z  MAX_RESTING_NOTIONAL=$z  MAX_CONCURRENT_ORDERS=z  TTL=r‹   N)Úprintr   r9   r4   rM   ÚAssertionErrorry   rJ   rp   rS   r6   rq   r\   ÚMAKER_TTL_S)Zbadrw   r   r   r   Ú	_selftests  s*    ""
ÿþþr�   Ú__main__)r   )3Ú__doc__r&   r)   r(   rr   ZmathÚsysr=   Úpathlibr   Útypingr   ÚpathÚinsertZpoly_us_traderrA   Zpoly_us_authr_   rŽ   r6   r8   r7   r5   rq   r\   r!   r'   r   rv   r   r   r   r1   r9   r>   ZORDERS_CACHE_SrL   rD   rE   rS   rZ   Úintr]   r3   rb   rK   ry   r|   r‡   r�   ru   r   r   r   r   Ú<module>   sV   "
)ÿÿP#"
