a
    *è»j”3 ã                   @   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	 ej
 dd¡ ddlZddlZddlZdZdZdadZd	Zd
Zedeje ƒZdZdd„ ZdZdZdZdZ dZ!dZ"dZ#dd„ Z$dmdd„Z%e	dƒZ&da'e	dƒZ(e	dƒZ)g d¢Z*e	dƒZ+g d ¢Z,e	d!ƒZ-d"Z.d#d$„ Z/d%d&„ Z0d'd(„ Z1d)d*„ Z2da3da4da5da6d+Z7d,Z8d-Z9da:da;d.Z<d/d0„ Z=d1d2„ Z>e?ƒ Z@d3d4„ ZAe ejBeA¡ d5d6„ ZCd7d8„ ZDdnd:d;„ZEd<d=„ ZFd>d?„ ZGd@dA„ ZHdBdC„ ZIdDdE„ ZJdFdG„ ZKdHdI„ ZLdJdK„ ZMdLdM„ ZNe	dNƒZOg dO¢ZPdaQdPdQ„ ZRdRdS„ ZSd.ZTdTZUdUdddVœiZVdWdX„ eV W¡ D ƒZXtZYe	dYƒZZe	dZƒZ[i a\da]d[d\„ Z^dod^d_„Z_d`daiZ`d.Zaebƒ fdbdc„Zcebƒ ebƒ fddde„Zddfdg„ Zedhdi„ Zfdjdk„ Zgehdlk�r²e iegƒ ¡ dS )pa<	  LIVE market maker: rest one contract per leg at the touch, and record fills.

WHAT THIS IS FOR. Every fill number in this project is an inference. The
optimistic model ("a print landed at our price") says 89% of legs fill at the
touch; the queue-aware model ("enough traded to clear the size ahead of us")
says 37%; accounting for the two thirds of the queue that cancels rather than
trades puts it near 53%. Those bounds cannot be narrowed from the log, because
queue position is not observable from outside the book. Only our own resting
orders can measure it. That measurement -- not profit -- is the deliverable.

SEPARATE PROCESS FROM mm_quoter.py ON PURPOSE. That one is the paper quoter and
it feeds mm_settle, the dashboard, the forward test and the fill research. A bug
in live order handling must not be able to take the research pipeline with it.
This module imports mm_quoter for book SELECTION only and never mutates it.

THE STRATEGY, and why it is this and not the paper one:
  * ONE rung, at the touch. The tape says a premium of +1c drops book completion
    from 71% to 6% and +2c to zero. The paper quoter's 0-5c decay ladder is
    walking down a curve whose only live rung is the bottom.
  * All three legs at the SAME price rung. The paper quoter's md5 per-leg offset
    puts legs of one book on different rungs, which is the mechanism that
    strands a book at 1-of-3.
  * NO repair. Crossing to complete costs ~1.5c of a ~3c book; flattening a
    stranded leg costs ~2.9c, and the measured alternative -- simply riding it
    to settlement -- lost less (-0.165 vs -0.291 $/book on Poly-US). At size 1
    the exposure is at most $1 a leg, so repair cannot pay for itself here.

SAFETY. Orders self-expire (kalshi_maker.MAKER_TTL_S) so a crash or a power cut
cannot leave anything resting for more than 90s -- the failure that actually
happened on 2026-08-10, when this machine was off for two and a half days
unnoticed. On top of that: cancel-all at startup and at shutdown, a kill file
checked every cycle, a hard book cap, and every cap in kalshi_maker.

Usage:
    python main\mm_maker_live.py                 dry run, places nothing
    python main\mm_maker_live.py --once          one cycle, dry
    python main\mm_maker_live.py --live          ARMED
    touch E:\soccer\STOP_MAKER                   stops it and cancels everything
é    N)ÚPathzE:\soccer\mainé   g      R@é   é
   g     â­@éŒ   é   i@8  c              	   C   sH   zt |  d¡ptjƒ}W n  ttfy8   t tjƒ}Y n0 td|t ƒS )aü  Age at which THIS leg's order must be re-posted, from its own TTL.

    A held book's order is issued with a much longer TTL than the default, so a
    single global threshold would cancel it at 58 minutes and hand back the
    queue position the long TTL exists to protect. Derive the threshold from
    the ttl actually used, keeping the same two-cycle margin:
        refresh_at <= ttl - 2*cycle   (or the order can lapse unquoted)
    Falls back to the module default when a leg predates this field.
    Úttl_sg      >@)ÚfloatÚgetÚkmÚMAKER_TTL_SÚ	TypeErrorÚ
ValueErrorÚmaxÚ_CYCLE_MARGIN_S)ÚstateZttl© r   úmm_maker_live.pyÚrefresh_at_forÑ   s
    
r   gìQ¸…ë‘?g{®Gázt?g{®Gáz”?gÍÌÌÌÌÌÜ?g¸…ëQ¸ž?g{®Gáz„?c                 C   s   t |  d|   S )Nç      ð?)ÚMAKER_FEE_RATE)Úpr   r   r   Ú	maker_fee  s    r   ÚevenFc                    s  t ˆdu rtnˆƒ‰|du r tnt |ƒ‰dg i   ‰}‰ˆ ¡ D ]à\}}ˆ |¡pTi }	t |	 d¡pddƒ}
|	 d¡}|du rª|	 d¡rªt |	 d¡p–|d ƒd }|
d	krªˆ}
|durþ|
d	krþ|	 d
¡}|durêt |ƒd	krêˆt |ƒˆ 7 ‰nˆt |ƒ|
 ˆ 7 ‰ˆ|
 }|dkr@| |¡ |ˆ|< q@tdd„ ˆ ¡ D ƒƒ}d| t }|ˆ }t‡‡‡fdd„|D ƒƒ}|d	k�rx|| nd‰|�r¤ˆdk�r¤t	‡‡fdd„|D ƒƒnd}d‰	|dk�rjˆdk�rjt
|ƒdk�rjdd„ |D ƒ‰ ‡ fdd„|D ƒ}ˆ �rj|�rjt‡‡‡fdd„|D ƒƒ}t‡‡fdd„ˆ D ƒƒ}|d	k�rj|| | }t	|ˆˆ d	  ƒ‰	t	dˆ	ˆˆ d	   ƒ}|�svd}n<ˆd	k�rž|||�rŽtnd k �ržd}n|tk�r®d}nd}ˆ�rÌt	ˆ‡fdd„d�nd‰‡‡‡‡‡‡‡‡	fdd„}|ˆ|||ˆ|ˆ||d œ
S )!a  Decide what a three-leg book still has to fetch, and whether to bother.

    Pure: `asks` is {ticker: yes_ask}, `st` the per-leg state. No I/O, no
    side effects -- this is the arithmetic that decides whether real money
    completes a book, so it is kept separately testable from the loop that
    acts on it.

    Everything is PER CONTRACT, because Kalshi fills fractionally (a live fill
    came back count_fp 0.50). A leg half-filled at 0.47 banked 0.235, not 0.47,
    and is still half open; counting it as closed at 0.47 would overstate the
    budget and underprice the rest of the book -- exactly how a book locks a
    loss.

    Returns a dict with `decision` in {'quote', 'gate', 'abandon'}:
      gate    -- nothing filled yet and the touches do not clear target, so
                 walking away is still free. Take it.
      abandon -- something HAS filled and finishing now needs more over the
                 touch than the market will pay. Hold the partial.
      quote   -- post the open legs at `floor_for(ticker)`.
    Nç        Úfill_qtyÚfill_pxÚfill_loggedÚprice_centséd   ç      Y@r   Ú	fill_cashç•Ö&è.>c                 s   s   | ]}t |ƒV  qd S ©N©r   ©Ú.0Úar   r   r   Ú	<genexpr>S  ó    zbook_budget.<locals>.<genexpr>r   c                 3   s"   | ]}ˆ | ˆ|  ˆ V  qd S r#   r   ©r&   Út©ÚasksÚopen_qtyÚsizer   r   r(   V  r)   c                 3   s   | ]}ˆ | ˆd   V  qdS )r   Nr   r*   )r-   Úscaler   r   r(   X  r)   Ztieé   c                 S   s$   g | ]}|  d d¡d dkr|‘qS )ú-r1   éÿÿÿÿÚTIE)Úrsplitr*   r   r   r   Ú
<listcomp>`  r)   zbook_budget.<locals>.<listcomp>c                    s   g | ]}|ˆ vr|‘qS r   r   r*   )Ú_tiesr   r   r6   a  r)   c                 3   s"   | ]}ˆ | ˆ|  ˆ V  qd S r#   r   r*   r,   r   r   r(   c  r)   c                 3   s   | ]}ˆ | ˆ V  qd S r#   r   r*   )r.   r/   r   r   r(   d  r)   ÚquoteÚgateÚabandonc                    s   ˆ |  S r#   r   )Úk©r-   r   r   Ú<lambda>™  r)   zbook_budget.<locals>.<lambda>©Úkeyc                    sÎ   ˆ |  }| ˆu r8ˆ |  t kr8| ˆv r8ˆdkr8ˆ |  t }| ˆv r~ˆdkr~ˆd urr|  dd¡d dkrhˆnˆ |  }nˆ |  ˆ }ˆ | ¡pŠi  d¡}|d urÊˆdkr²t|t|ƒƒ}nt|tt|ƒ|ˆ ƒƒ}|S )Nr   r   r2   r1   r3   r4   Úfloor_px)ÚFAVOURITE_MIN_PXÚFAVOURITE_PREMIUMr5   r
   r   r	   Úmin)r+   ÚfZlatched)r-   ÚcapÚ	collectedÚdearestr.   r0   ÚstÚtie_pxr   r   Ú	floor_for›  s"    ÿÿ"0zbook_budget.<locals>.floor_for)
ÚdecisionrF   ÚfeesÚtargetÚneedr0   Ú	open_legsr.   Úworst_over_touchrJ   )r	   ÚSIZEÚ	LATCH_CAPÚitemsr
   ÚappendÚsumÚvaluesÚTARGET_MARGINr   ÚlenÚ	EXIT_BANDÚMAX_ABOVE_TOUCH)r-   rH   r/   Ú	latch_capÚallocÚ	incumbentrO   r+   r'   ÚsÚqtyÚfpÚ_cashZremrL   rM   rN   Zopen_ask_sumZworstZ_teamZ	_team_sumZ_tie_qtyZ_pxrK   rJ   r   )
r7   r-   rE   rF   rG   r.   r0   r/   rH   rI   r   Úbook_budget  st    
	


ÿÿÿ"
ÿ
Fýrb   zE:\soccer\STOP_MAKERz%E:\soccer\research\mm_live_state.jsonz(E:\soccer\research\mm_live_realfills.csv©	ÚtsÚtickerÚeventÚsider   ÚcountZqueue_ahead_at_postÚ	posted_atZseconds_to_fillz&E:\soccer\research\mm_flatten_snap.csv©rd   rf   Úleaguere   rg   Ú	hrs_to_koZlegs_filled_nowZsold_pxr   rF   Z
filled_bidZ
filled_askZfilled_spreadZsurvivor_tickersZsurvivor_bidsZsurvivor_asksZsurvivor_ask_sumz&E:\soccer\research\.mm_maker_live.lockéx   c                 C   s^   zDddl }|jj}| ddt| ƒ¡}|r8| |¡ W dS | ¡ dkW S  tyX   Y dS 0 dS )añ  Is this pid a running process? Windows, no psutil.

    ACCESS DENIED MEANS ALIVE. OpenProcess returns NULL both for a pid that
    is gone and for one owned by another user, and this treated both as dead.
    The maker normally runs as SYSTEM (the guard starts it), so a restart from
    an interactive shell saw the live holder as dead, took the lock, and ran a
    SECOND maker against the account -- the exact 2026-08-16 failure this lock
    exists to prevent. Observed again 2026-08-29 doing precisely that.

    GetLastError separates the two cases: ERROR_ACCESS_DENIED (5) means the
    process is there and is not ours; ERROR_INVALID_PARAMETER (87) means there
    is no such pid. Anything else is unknown, and unknown fails safe to alive.
    r   Ni   FTéW   )ÚctypesZwindllZkernel32ZOpenProcessÚintZCloseHandleZGetLastErrorÚ	Exception)Úpidro   r;   Úhr   r   r   Ú
_pid_alive  s    
rt   c               
   C   sÂ   ddl } t ¡ }zŽt tjdd�¡}|t| d¡p4dƒ }| d¡}|rPt|ƒnd}|t	k rœ||  
¡ krœ|rœtd| d¡› d	|d
›dt	| d
›d�ƒ W dS W n tttfy¶   Y n0 tƒ  dS )aj  Refuse to start if another maker is alive. Returns True if we own it.

    On 2026-08-16 a relaunch happened before the previous process had exited,
    so two makers ran against the same account. Each posted its own orders,
    every leg went to two resting orders, and the duplicate guard could not win:
    it cancels BOTH orders on a leg, and the other process re-posted immediately.
    Resting orders climbed 54 -> 81. Every other safety in this file assumes a
    single writer -- the duplicate guard, the resting-over-cap abort, the post
    budget, the ratchet floors in state -- so that assumption has to be enforced
    rather than hoped for.

    Heartbeat rather than a plain PID file: a crashed process leaves a stale
    lock, and a lock that needs manual clearing after a crash is one that gets
    deleted reflexively. The heartbeat expires on its own.
    r   Núutf-8©ÚencodingÚ	heartbeatrr   Fz5refusing to start: another maker holds the lock (pid z, heartbeat ú.0fzs ago). Stop it first, or wait zs if it died.T)ÚosÚtimeÚjsonÚloadsÚ	LOCK_FILEÚ	read_textr	   r
   rt   ÚLOCK_STALE_SÚgetpidÚprintÚOSErrorr   r   Ú	lock_beat)rz   ÚnowÚprevÚageZholderÚaliver   r   r   Úlock_acquire'  s(    
ÿÿþ

r‰   c                  C   sF   dd l } z&tjt |  ¡ t ¡ dœ¡dd� W n ty@   Y n0 d S )Nr   )rr   rx   ru   rv   )rz   r~   Ú
write_textr|   Údumpsr�   r{   rƒ   )rz   r   r   r   r„   N  s    ÿþ
r„   c               
   C   sV   dd l } z0t tjdd�¡}| d¡|  ¡ kr6t ¡  W n tt	t
fyP   Y n0 d S )Nr   ru   rv   rr   )rz   r|   r}   r~   r   r
   r�   Úunlinkrƒ   r   r   )rz   r†   r   r   r   Úlock_releaseX  s    r�   g¸…ëQ(V@g      4@é   g      @c                  C   s†   z2t jdddid�pi } t|  dd¡p(dƒd }W n tyF   Y dS 0 zt ¡ }W n tyh   Y dS 0 |tdƒkrzdS || t S )	aq  Free cash in the MAKER's subaccount, minus what resting orders will
    consume, minus a small buffer. None if unreadable.

    Replaces the fixed MAX_RESTING_COLLATERAL (removed 2026-09-01, user: "the
    book cap should be enough to cover it"). MAX_BOOKS bounds normal operation;
    this exists only so a book is never posted half-in because the money ran
    out mid-post -- the SFEMIL failure, 1 leg of 3.

    Reads the exchange rather than a constant, so it cannot go stale as
    positions settle and cash comes back. Subaccount 0 explicitly: xlow's $50
    lives in subaccount 1 and is not the maker's to spend.
    z/portfolio/balanceZ
subaccountr   )ÚparamsÚbalancer    NÚinf)ÚktÚ_getr	   r
   rq   r   Zresting_collateralÚCASH_BUFFER_USD)ÚbÚcashÚrestr   r   r   Úcash_headroom�  s    r˜   c                  C   sX   z>t  ¡ pi } t|  dd¡ƒd }t|  dd¡ƒd }|| W S  tyR   Y dS 0 dS )a©  Kalshi's own account value: cash + portfolio_value. None if unreadable.

    This used to mark each short at (1 - yes_ask) and add that to cash. That
    number is defensible as a liquidation estimate, but it is NOT what Kalshi
    says the account is worth, and the gap is large: on 2026-08-16 it read
    $114.14 while the exchange and the app both said $101.26. Reporting a
    twelve-dollar gain the venue does not recognise is worse than useless on a
    stop loss, which is the one place this number has to be right.

    /portfolio/balance returns portfolio_value in cents alongside the cash
    balance, and it agrees with the app to the penny. Use the venue's number.
    r�   r   r    Zportfolio_valueN)r’   Zget_balancer	   r
   rq   )r•   r–   Zpvr   r   r   Úaccount_equity¨  s    
r™   c                 C   s   da tdƒ d S )NTz+
[signal] stopping -- cancelling everything)Ú_stopr‚   )Z_sigZ_frmr   r   r   Ú_sigintÄ  s    r›   c                   C   s   t j t jj¡S r#   )ÚdtÚdatetimer…   ÚtimezoneZutcr   r   r   r   Ú_nowÍ  s    rŸ   c                 C   s   |   d¡}|rt|ƒS ttƒS )aé  The target size for THIS leg -- SIZE at the moment it was first posted,
    not necessarily the SIZE running now.

    Added 2026-09-22 when SIZE went 1 -> 10. Fill state (fill_qty, fill_cash)
    persists across restarts keyed by ticker, and "done" means fill_qty >=
    target. Without this, a leg sold to completion under SIZE=1 -- fill_qty
    1.0, already closed -- would be read as "10% filled" the moment SIZE=10
    goes live, and the maker would try to sell 9 MORE contracts of a leg it
    already finished: real orders, deepening a real short position well past
    what that book's collateral and budget were priced for.

    `opened_size` is stamped once, on the FIRST touch of a leg's state (see the
    posting block), and never overwritten after -- so it survives every
    refresh and every TTL renewal for that leg's whole life. Falls back to the
    live SIZE for a leg that has never been posted, which is correct: a book
    opened AFTER a SIZE change should use the new one.
    Úopened_size)r
   r	   rQ   )r^   Úvr   r   r   Úleg_sizeÑ  s    
r¢   r   c           	      C   sð   d| v }|   di ¡}| d¡}|du r>|s>t|ƒttƒkr>|}|durÜt|ƒttƒkrÜt| ƒ}d}|  ¡ D ]L\}}|dksjt|tƒs†qj| d¡du rjt||ƒ|v rjt|ƒ|d< |d7 }qj|rÜt	d|d›d	td›d
|› d�ƒ t|d< t
| ƒ dS )a³  On a SIZE change, freeze every PRE-EXISTING leg to the size it predates.

    Compares the SIZE this state file was last saved under (st['_meta']) to
    the live SIZE. If they differ, every ticker already in `st` that has a
    footprint (a fill or a post -- i.e. everything except a bare placeholder)
    and no `opened_size` yet is stamped with the OLD size, so leg_size() keeps
    reading it correctly right through the transition. Brand-new legs posted
    from here on stamp themselves with the new SIZE at first touch, same as
    always. Runs once at startup; a no-op when SIZE has not changed.

    COLD START. The very first time this runs, `_meta` does not exist -- this
    tracking mechanism has never written it, not "SIZE has never changed". A
    naive `last is None -> skip` here would silently leave every pre-existing
    leg (1,667 of them, verified 2026-09-22) unstamped, which is exactly the
    bug this function exists to prevent: they would fall through to leg_size()'s
    live-SIZE default and get topped up to the NEW size. So an entirely absent
    `_meta` is treated as "everything here predates this code" and backfilled
    against `bootstrap_size` -- the constant's actual value immediately before
    this mechanism was added (1, confirmed from the source at the time). This
    branch fires at most once, ever, on whichever machine boots this code
    first; every run after that reads a real `last_size` from `_meta`.
    Ú_metaZ	last_sizeNr   r    r1   z  [SIZE-CHANGE] Úgz -> z: froze z) leg(s) of books that already hold a fill)Ú
setdefaultr
   r	   rQ   Ú_filled_eventsrS   Ú
isinstanceÚdictÚ	_event_ofr‚   Úrelease_unfilled_books)	rH   Zbootstrap_sizeZhad_metaÚmetaZlastÚ
filled_evsÚnÚtkrr^   r   r   r   Úbackfill_opened_sizeç  s(    
ÿ
 r¯   c                 C   s   |  d¡p|  dd¡d S )Nrf   r2   r1   r   )r
   r5   )r®   r^   r   r   r   r©     s    r©   c                 C   s   dd„ |   ¡ D ƒS )Nc                 S   sB   h | ]:\}}|d krt |tƒrt| d¡p,dƒdkrt||ƒ’qS )r£   r   r   r"   )r§   r¨   r	   r
   r©   )r&   r+   r^   r   r   r   Ú	<setcomp>  s   þz!_filled_events.<locals>.<setcomp>)rS   ©rH   r   r   r   r¦     s    r¦   c                 C   s”   t | ƒ}d}|  ¡ D ]`\}}|dkst|tƒs0q| d¡durt|d ƒttƒkrt||ƒ|vr| dd¡ |d7 }q|r�t	d|› dtd›�ƒ dS )	a  Un-freeze every book with NO leg filled, so it adopts the live SIZE.

    The freeze exists to stop a leg that has already SOLD being topped up to a
    bigger size than its book was committed at. A book where nothing has
    filled has no commitment: its resting orders can be cancelled and
    re-posted at any size at zero cost. Freezing it anyway -- which the first
    version of the 2026-09-22 backfill did, stamping everything with a post --
    left SEARSL quoting 1 contract a leg after the switch to size 10, which is
    the opposite of what the switch was for.

    Per BOOK, not per leg: the three legs are priced against one payout, so
    they must share a size. One filled leg keeps the whole book at its
    original size. Runs every startup, and is idempotent.
    r   r£   r    Nr1   z  [SIZE-RELEASE] z, leg(s) on books with no fill moved to size r¤   )
r¦   rS   r§   r¨   r
   r	   rQ   r©   Úpopr‚   )rH   r¬   r­   r®   r^   r   r   r   rª   !  s     ÿþ

ÿrª   c                   C   s,   zt  t ¡ ¡W S  ty&   i  Y S 0 d S r#   )r|   r}   ÚSTATE_Fr   rq   r   r   r   r   Ú
load_state?  s    r´   c                 C   s0   zt  tj| dd�¡ W n ty*   Y n0 d S )Nr1   )Úindent)r³   rŠ   r|   r‹   rq   r±   r   r   r   Ú
save_stateF  s    r¶   c              	   C   s�   | sdS zt  d| › �¡}W n ty0   Y dS 0 z@t| d¡tƒrN| d¡n|}t| dd¡ƒ ¡ dv rjt|ƒW S  t	t
fyŠ   Y dS 0 dS )aï  How much a no-longer-resting order filled before it left the book.

    AN ORDER THAT VANISHED EITHER EXPIRED OR EXECUTED, and only the exchange
    knows which. The maker reads positions once per ~56s cycle, so a fill is
    invisible until the next read -- and the refresh in between sees an empty
    order list, concludes the order expired, and re-posts the FULL size on a
    leg that is already sold.

    Observed on GETDEP-GET, 2026-09-13: executed 16:41:04, a second sell order
    posted immediately after and cancelled at 16:42:00 once positions caught
    up. It rested ~56s; had it filled, the leg would have gone 2/1. Neither
    existing guard can see this -- [DUP] needs two orders resting at once and
    only one ever is, and the cancel SUCCEEDS so [CANCEL-FAIL] is silent.

    Fails OPEN (0.0) on any error: that restores the old behaviour of
    re-posting, which is the risk we already carry, whereas a false positive
    would silently stop quoting a leg we do not actually hold.
    r   z/portfolio/orders/ÚorderÚstatusÚ )ZcanceledZ	cancelledZexpiredN)r’   r“   rq   r§   r
   r¨   ÚstrÚlowerÚ_cancel_filled_qtyÚAttributeErrorr   )Úorder_idÚrespÚor   r   r   Ú_vanished_order_filledM  s    
rÁ   c              
   C   sÜ   z¾t | tƒsW dS t |  d¡tƒr,|  d¡n| }dD ]*}| |¡}|dur4tdt|ƒƒ  W S q4| d| d| d¡¡¡}| d| d	| d
¡¡¡}|dur¼|dur¼tdt|ƒt|ƒ ƒW S W n tttfyÖ   Y n0 dS )a;  How much of a just-cancelled order had already filled. 0.0 if unknown.

    Kalshi's DELETE returns the cancelled order, but the field carrying the
    filled amount is not guaranteed to keep one name, and this runs in the
    order path of a live maker -- so it reads every plausible spelling and
    NEVER raises. Unknown must read as 0.0: over-counting here would suppress
    a legitimate re-post and silently stop quoting the leg, which is a worse
    failure than the duplicate it prevents.

    Returns a float. See the [LATE-FILL] branch for why this exists.
    r   r·   )Zfill_count_fpZfilled_count_fpZfilled_countZ
fill_countZfilled_quantityÚfilledZcount_filledNZinitial_count_fpZinitial_countrh   Úremaining_count_fpÚremaining_countZ	remaining)r§   r¨   r
   r   r	   r   r   r½   )r¿   rÀ   r;   r¡   ZorigÚleftr   r   r   r¼   q  s"    

ÿr¼   c                 C   sb   t  ¡  }t jdddd��4}tj|td�}|r6| ¡  | | ¡ W d   ƒ n1 sT0    Y  d S )Nr'   r¹   ru   ©Únewlinerw   )Ú
fieldnames)ÚFILL_LOGÚexistsÚopenÚcsvÚ
DictWriterÚFILL_FIELDSÚwriteheaderÚwriterow©ÚrowÚnewÚfhÚwr   r   r   Úlog_fill—  s    
rÖ   c                 C   s|   zdt  ¡  }t jdddd��6}tj|tdd�}|r:| ¡  | | ¡ W d  ƒ n1 sX0    Y  W n tyv   Y n0 dS )a  Book state at the instant of a fill. See FLATTEN_LOG.

    Wrapped in its own try: this is a research capture, and a malformed row or
    a locked file must never take down a live trading cycle. Everything else in
    this function is already best-effort for the same reason.
    r'   r¹   ru   rÆ   Úignore©rÈ   ZextrasactionN)	ÚFLATTEN_LOGrÊ   rË   rÌ   rÍ   ÚFLATTEN_FIELDSrÏ   rÐ   rq   rÑ   r   r   r   Úlog_flatten_snap   s    
,rÛ   z#E:\soccer\research\mm_live_legs.csv©rd   re   rf   rk   rg   r   Zask_at_postZbid_at_postZmid_at_postZpin_fairÚvolumeZ
volume_24hZbook_volumeZ	touch_askr@   Zabove_touch_cÚqueue_aheadÚbid_sizeÚopen_interestrl   rF   rM   r0   ÚfreshÚlivec                     s  t  ¡ sdS t jdddd��<} t | ¡}|jtkrBW d  ƒ dS t|ƒ}W d  ƒ n1 s^0    Y  t  d¡}t  	|¡ t jdddd��J} tj
| tdd	�}| ¡  |D ]‰ | ‡ fd
d„tD ƒ¡ qªW d  ƒ n1 sÜ0    Y  tdttƒ› dt|ƒ› d|j› d�ƒ dS )aÊ  Rewrite the leg log under the current header when columns are added.

    csv.DictWriter appends in LEG_FIELDS order regardless of what header is
    already on disk. Add a column and every new row silently lands under the
    wrong old heading -- the file still parses, so nothing complains, and the
    corruption is only visible when the numbers stop making sense. Old rows
    keep blanks in the new columns, which is honest: we did not record them.
    NÚrr¹   ru   rÆ   z.csv.bakrÕ   r×   rØ   c                    s   i | ]}|ˆ   |d ¡“qS )r¹   ©r
   ©r&   r;   ©rã   r   r   Ú
<dictcomp>à  r)   z$_migrate_leg_log.<locals>.<dictcomp>z  leg log migrated to z
 columns (z rows kept; backup ú))ÚLEG_LOGrÊ   rË   rÌ   Z
DictReaderrÈ   Ú
LEG_FIELDSÚlistZwith_suffixÚreplacerÍ   rÏ   rÐ   r‚   rX   Úname)rÔ   ZrdZrowsZbakrÕ   r   ræ   r   Ú_migrate_leg_logÊ  s&    	

&

8ÿÿrî   c                 C   sr   t stƒ  da t ¡  }tjdddd��6}tj|tdd�}|rF| ¡  | 	| ¡ W d   ƒ n1 sd0    Y  d S )NTr'   r¹   ru   rÆ   r×   rØ   )
Ú_LEG_MIGRATEDrî   ré   rÊ   rË   rÌ   rÍ   rê   rÏ   rÐ   rÑ   r   r   r   Úlog_legå  s    
rð   g      @Úlatch3)r[   r\   c                 C   s   i | ]\}}||d  “qS )r[   r   ©r&   r;   r¡   r   r   r   rç   O  r)   rç   zE:/soccer/research/.mm_arm.jsonz!E:/soccer/research/mm_arm_log.csvc               	   C   sD   zt  tjdd�¡} W n ttfy0   i  Y S 0 dd„ |  ¡ D ƒS )aX  Which arm owns each book. Kept OUT of the maker's state file.

    state is keyed by ticker and every loop over it assumes that shape, so a
    per-EVENT mapping does not belong there. A book must also keep its arm
    across restarts, or a restart would re-attribute part-filled books and the
    comparison would quietly measure nothing.
    ru   rv   c                 S   s   i | ]\}}|t v r||“qS r   )ÚARM_CAPrò   r   r   r   rç   f  r)   zload_arms.<locals>.<dictcomp>)r|   r}   ÚARM_FILEr   rƒ   r   rS   ©r'   r   r   r   Ú	load_armsY  s
    
rö   r   c                 C   sÊ   zt jt | ¡dd� W n ty*   Y n0 |s4dS z~t ¡  }tjdddd��P}t 	|¡}|rn| 
g d¢¡ |D ]\}}| 
tƒ  ¡ ||g¡ qrW d  ƒ n1 s¦0    Y  W n tyÄ   Y n0 dS )aÇ  Persist the mapping, and append-only log each book's FIRST assignment.

    ARM_FILE is pruned to live books every cycle, so it cannot answer 'which arm
    owned this book' once the book settles -- which is the only question the test
    exists to answer. The log is a separate small file on purpose: the obvious
    alternative, a column on mm_live_legs.csv, triggers a migration that reads
    all 374k rows of an 81MB file into memory at startup.
    ru   rv   Nr'   r¹   rÆ   )rd   rf   Úarm)rô   rŠ   r|   r‹   rƒ   ÚARM_LOGrÊ   rË   rÌ   ÚwriterrÐ   rŸ   Ú	isoformat)ÚarmsZ
new_eventsrá   rÔ   rÕ   Úevr÷   r   r   r   Ú	save_armsi  s     	

:rý   ZKXUEFANLGAMEZUNLc                 C   s*  g }t  ¡ D �]\}}g d }}zdtdƒD ]V}|ddddœ}	|rH||	d< t d|	¡pVi }
||
 d	¡pfg 7 }|
 d¡pvd}|s* q‚q*W nP tyÔ } z8td
|› dt|ƒj	› d|› �ƒ W Y d }~qW Y d }~n
d }~0 0 |D �]F}| d¡}| d¡pög }|rÚ||v sÚt
|ƒdk�rqÚtdd„ |D ƒd ƒ}|�s0qÚz$tj | dd¡¡tjtd� }W n t�yl   Y qÚY n0 ||   ¡ d }d|  k �o’tkn  }t |  k�o®dkn  }|�sÂ|�sÂqÚ|�rÔ||v�rÔqÚ|�rð|tk �rð||v�rðqÚt ||¡�s qÚ| ||| d¡�p|||d f¡ qÚq|S )Nr¹   r   rË   éÈ   Útrue)Zseries_tickerr¸   ÚlimitZwith_nested_marketsZcursorz/eventsÚeventsú	  [skip] z discovery: ú: Zevent_tickerZmarketsr   c                 s   s"   | ]}|  d ¡r|  d ¡V  qdS )Zoccurrence_datetimeNrä   ©r&   ÚLr   r   r   r(   ·  s   
ÿz$_maker_only_books.<locals>.<genexpr>ÚZz+00:00)Zhoursç      ¬@r   Útitle)ÚMAKER_ONLY_SERIESrS   Úranger’   r“   r
   rq   r‚   ÚtypeÚ__name__rX   Únextrœ   r�   Zfromisoformatrì   Z	timedeltaÚ_KO_FROM_OCCURRENCE_Hr   Útotal_secondsÚWINDOW_HÚ	IN_PLAY_HÚ
MIN_OPEN_HÚQÚkalshi_playablerT   )r…   Úheld_eventsÚalreadyÚoutZseriesZlabelr  ZcurÚ_r   rã   Úerü   ÚlegsZoccÚkoÚhrsÚprerâ   r   r   r   Ú_maker_only_books�  s^    
ÿ
 "

ÿ
ÿ
&r  c                    sè  t j ¡ \}}g }|D �]–}t  |¡\}}|du r4q||   ¡ d }	d|	  k oVtkn  }
t |	  kopdkn  }|
s€|s€q| d¡pŒd ¡ }zzt  	||| d¡| d¡¡}|s¸W q|rÈ|ˆvrÈW q|
rà|	t
k rà|ˆvràW qt  |¡}t|ƒdk�st  ||¡�s
W qW n^ t�yj } zDtd	|› d
| d¡› dt|ƒj› d|› �ƒ W Y d}~qW Y d}~n
d}~0 0 | ||| d¡› d| d¡› �|	| d¡�pœd ¡ | d¡f¡ q| t| ˆdd„ |D ƒd�¡ dd„ ‰ dd„ }d$‡fdd„	‰‡‡fdd„‰t|‡ ‡fdd„d�}tƒ ‰g tƒ g   ‰‰}|D ]f}|d ˆv �r,|d ˆv�r,ˆ |d ¡ ˆ |¡ |d ˆv�r,| |d df¡ dˆ|d < �q,‡‡fdd„tD ƒ‰d}|D ]æ}|d }|ˆv �rÊ�q°||d ƒ}|du�rö|tk �rö|d7 }�q°ˆ |¡�ptt‡fdd„d�}|tv�s°ˆ| tk�r2�q°|ˆv�rJ| ||f¡ |ˆ|< ˆ|  d7  < ˆ |¡ ˆ |¡ t‡fd d!„tD ƒƒ�r° �q˜�q°t‡fd"d„ˆ ¡ D ƒ|ƒ t ˆƒa!|a"t#t$t%‡fd#d!„ˆD ƒƒƒ}ˆd|… S )%aÉ  [(event, [legs], match, hrs)] for playable books inside the window.

    Reuses mm_quoter's resolution so the live maker and the paper maker are
    always looking at the same universe -- a divergence there would make the
    measured fill rate uncomparable to everything already logged.

    IN-PLAY, BUT ONLY TO FINISH WHAT WE STARTED. The window used to be
    `0 < hrs`, so the maker went silent at kickoff and every resting order
    expired 90s later. Any book still short of three legs at kickoff was
    stranded permanently -- five of today's six open legs are sitting in
    markets that are still active and tradeable, abandoned by a cutoff rather
    than by the market.

    Opening a NEW book in play is a bad idea and stays forbidden: the three
    legs sum to one, so a goal that lifts one leg pushes the other two away,
    and the fill arrives exactly when the book can no longer be completed.
    FINISHING an existing partial is the opposite -- a completed book pays the
    same whoever scores, so a fill that completes it cannot lose. `held_events`
    is what separates the two, and the entry gate refuses anything with nothing
    banked, so an unheld book cannot be opened here even by accident.
    Nr  r   Zlcr¹   ÚhomeÚawayr   r  ú r  z vs Úfidc                 S   s   h | ]}|d  ’qS ©r   r   r*   r   r   r   r°     r)   z"books_in_window.<locals>.<setcomp>)r  c              
   S   sN   d}| D ]@}z$|t | d¡p(| d¡p(dƒ7 }W q ttfyF   Y q0 q|S )Nr   Ú	volume_fprÝ   r   )r	   r
   r   r   )r  Útotr  r   r   r   Ú	_book_vol"  s    $z"books_in_window.<locals>._book_volc              
   S   sp   d\}}| D ]V}|  d¡}|du r,|  d¡}|dv r6qz|t|ƒ7 }d}W q ttfy`   Y q0 q|rl|S dS )a¸  Summed open interest across the three legs. See MIN_BOOK_OI.

        Mirrors _book_vol's field fallback: the *_fp names are what the fill
        pipeline writes and the bare names are what 31,613 older rows carry.
        Returns None when no leg reports it at all, which is NOT the same as
        zero -- a book we cannot measure must not be silently treated as thin
        (that would gate out every book on any feed hiccup).
        )r   FÚopen_interest_fpNrà   )Nr¹   T)r
   r	   r   r   )r  r%  Zseen_anyr  r¡   r   r   r   Ú_book_oi+  s    	

z!books_in_window.<locals>._book_oic              
      sº   |ˆ v rt nd}g }| D ]j}| d¡}|d u r4 dS zt|ƒ}W n ttfyZ   Y  dS 0 d|  k rpdk sxn  dS | |¡ qt|ƒdkr”dS t|ƒdtdd„ |D ƒƒ t | kS )Nr   Úyes_ask_dollarsFr   r   c                 s   s   | ]}t |ƒV  qd S r#   r$   r%   r   r   r   r(   ‘  r)   z5books_in_window.<locals>._entry_ok.<locals>.<genexpr>)	rY   r
   r	   r   r   rT   rX   rU   rW   )r  rü   Zbandr-   r  r'   )Úresting_eventsr   r   Ú	_entry_ok{  s(    

ÿÿz"books_in_window.<locals>._entry_okc                    s.   | d ˆv rdndˆ | d | d ƒr(dndfS )Nr   r1   r   ©r+   )r+  r  r   r   r=   £  s   ÿz!books_in_window.<locals>.<lambda>c                    s   ˆ| ƒˆ | d ƒ | d f S )Nr1   r   r   r,  )r&  Úprefixr   r   r=   ¨  r)   r>   rñ   c                    s&   i | ]‰ ˆ t ‡ ‡fd d„ˆD ƒƒ“qS )c                 3   s$   | ]}ˆ  |d  ¡ˆ krdV  qdS ©r   r1   Nrä   r*   )r'   rû   r   r   r(   ¼  r)   z-books_in_window.<locals>.<dictcomp>.<genexpr>)rU   )r&   )rû   Úpickedrõ   r   rç   ¼  r)   z#books_in_window.<locals>.<dictcomp>r1   c                    s   ˆ |  S r#   r   rõ   ©Úcountsr   r   r=   Ó  r)   c                 3   s   | ]}ˆ | t kV  qd S r#   )Ú	ARM_BOOKSr%   r0  r   r   r(   Û  r)   z"books_in_window.<locals>.<genexpr>c                    s   i | ]\}}|ˆ v r||“qS r   r   )r&   r  r'   ©Úseenr   r   rç   Þ  r)   c                 3   s   | ]}|d  ˆ v rdV  qdS r.  r   r*   )r  r   r   r(   â  r)   )N)&r  ÚLMZload_watchlist_fileZ_ko_ofr  r  r  r
   ÚupperZresolve_eventr  Zlegs_forrX   r  rq   r‚   r  r  rT   Úextendr  Úsortedrö   ÚsetÚaddró   ÚMIN_BOOK_OIrC   r2  Úallrý   rS   r¨   Ú_ARM_OFÚ_THIN_GATEDr   Ú	MAX_BOOKSrU   )r…   r  r*  r  Zmatchesr  Úmr  Úgdr  r  râ   Zdivrü   r  r  r(  ZrankedZnewlyr+   Z_gated_thinZ_oir÷   rE   r   )	r&  r+  rû   r1  r  r/  r-  r*  r4  r   Úbooks_in_windowÏ  sŒ    

,""ÿ
ÿ!	P(
 

rB  c           h         s
  t ƒ }tƒ  td7 a|r~tt dkr~tƒ }|d ur~|at| }|tkr~td|d›dtd›d|d›dtd›d�	ƒ t	 
d¡ d	S t ¡ räztjd
d� ¡ dkaW n ty´   d	aY n0 tdt› d�trÊdnd ƒ t	j
dtd� d	S z¬i ‰t ¡ pôi  dg ¡�pg D ]}ˆ | d¡g ¡ |¡ �qi }t ¡ �p4i  dg ¡�pBg D ](}| d¡}|�rD| |g ¡ |¡ �qDdd„ t ¡ �p€i  dg ¡D ƒ}	W nB t�yÒ }
 z(tdt|
ƒj› d|
› �ƒ W Y d }
~
dS d }
~
0 0 dd„ |	 ¡ D ƒ‰i }|	 ¡ D ]j\}}tˆ	 |¡�p
i ƒd }|| k�rò| d | d¡d d … ¡d!¡d |d | d¡d d … ¡< �qòd"d„ | ¡ D ƒ‰ˆ�r~ˆˆ ‰d#d„ ˆ ¡ D ƒ}‡fd$d%„t|ˆ|ƒD ƒ}t�rÂ‡fd&d%„|D ƒ}t ˆƒ}d'd„ |D ƒ}d!}|�rît!ˆ ¡ ƒng D ]Œ\}}|�s�qòd | d¡d d … ¡}||v �sò||v �r4�qò|D ]6}zt	 "|d( ¡ |d7 }W n t�yj   Y n0 �q8ˆ	 #|d ¡ �qò|�r–td)|› d*�ƒ d! } } } }}d }|D �]:\}}}} }!}"i }#zr|"�rÜt$j% &|"¡nd+\}$‰ˆ�r8t'‡fd,d-„d.D ƒƒ�r8t$ (ˆd/ ˆd0 ˆd1 g¡‰ t)ˆ d! ˆ d ˆ d  d2�}#W n t�yN   Y n0 i ‰|D ]f}%zt*|% d3¡�pnd!ƒ}&W n t+t,f�y�   d4}&Y n0 d!|&  k �rªdk �rXn n|&ˆ|% d¡< �qXt-ˆƒd5k�rÒ�q²t. t/ |¡¡�pæi }'t0‡	fd6d-„ˆD ƒt*t1ƒd7�}(t2ˆˆ	|(|' d8t3¡|' d9d:¡||v d;�})|)d< |)d= |)d>   }*}+},d4}-|D ]<}%z|-t*|% d?¡�pjd!ƒ7 }-W n t+t,f�yŠ   Y n0 �qRd@d%„ |+D ƒ}.|.�r*|*d!k�r*t4�r*t5t-|.ƒ t4k�r*|+D ]R}/ˆ |/¡�pÜg D ].}zt	 "|d( ¡ W n t�y   Y n0 �qÞˆ	 #|/d ¡ �qÊ|d7 }�q²|)dA dBk�rœ|+D ]R}/ˆ |/¡�pNg D ].}zt	 "|d( ¡ W n t�yz   Y n0 �qPˆ	 #|/d ¡ �q<|d7 }�q²|)dA dCk�rø|d7 }tdD|d dE… › dF|*d›dG|)dH d›dIt-|+ƒ› dJ|)dK dL dM›dN�ƒ t6‡fdOd-„|D ƒƒ}0|*d!k�r˜|0�s˜t7‡fdPd-„|D ƒƒ}1|d u �rRt8ƒ }|d u �rRdQ}|1|k�r�tdR|d dE… › dS|1d›dT|d›dU�ƒ |d7 }�q²||18 }|D �]L}%|% d¡‰
ˆ ˆ
¡}2|2d u �rÄ�qœ|)dV ˆ
ƒ}3t9t:|3dL |3|2k�rèdWnd! ƒƒ}4d|4  k�r
dXk�sn �qœˆ	 ˆ
¡�pi }5|5 dY¡�p.dZ‰|	 ˆ
d!¡dk�r¶t*|5 d[¡�pRd!ƒd!k�r¶|5 d\¡�s¶g �s¶td]ˆ
› d^|5 d[¡› d_|	 ˆ
¡› d`�ƒ daD ]}6|5 #|6d ¡ �qš|5ˆ	ˆ
< t |5 db¡�pÆg ƒ‰‡‡fdcd%„| ˆ
g ¡D ƒ}7t |5 d\¡�pög ƒ‰‡fddd%„|7D ƒ}8|8�
r|8D ]¬}9z,t*|9 de¡�	p,d!ƒ}t*|9 df¡�	p@d!ƒ}:W n t+t,f�	yd   Y �	qY n0 |d!k�	rt�	qt*|5 d[¡�	p„d!ƒ| |5d[< t*|5 dg¡�	p d!ƒ|:|  |5dg< ˆ ;|9 dh¡¡ �	qt<did-„ ˆD ƒƒ|5d\< |5 d[¡�	rú|5dg |5d[  |5dj< |5ˆ	ˆ
< �nP|5 d[¡�sV|	 ˆ
d!¡dkk�rVdld%„ | ˆ
g ¡D ƒ};d4 }<}=|;D ]p}9z,t*|9 de¡�
p\d!ƒ}t*|9 df¡�
ppd!ƒ}:W n t+t,f�
y”   Y �
qFY n0 |d!k�
rF|<|7 }<|=|:| 7 }=�
qF|<d!k�
rø|<|5d[< |=|< |5dj< t<dmd-„ dnd-„ |;D ƒD ƒƒ|5d\< nV‡fdod%„| ˆ
g ¡D ƒ}>tdpˆ
› dq|	 ˆ
d!¡› dr�|>�rBdst-|>ƒ› dt�ndZ du ƒ |5ˆ	ˆ
< t*|5 d[¡�pfd!ƒ}?|	 ˆ
d4¡}@‡fdvd%„| ˆ
g ¡D ƒ}A|A�rœ|?‰n|@d!k �r¬|@ nd4‰ˆdk�rpt=ˆ|? ƒdk�rp|?t*|5 dg¡�pÞd4ƒ }B}Cˆ}?|?|5d[< |Cd!k�rZ|?dk�rZ|?|Bd k�rNt*|5 dw¡�p$d!ƒdx }D|Dd!k�rN|C|D|?|B  7 }C|C|5dg< |C|? |5dj< |5 #dyd ¡ |5ˆ	ˆ
< n¾ˆdk�r�|5 #dyd ¡ |5ˆ	ˆ
< nž|?dk�r.ˆ
|	v �r.t9|5 dy¡�p´d!ƒd }E|E|5dy< |5ˆ	ˆ
< |Ed k�r.tdzˆ
› d{|?d|›d}�ƒ d4|5d[< d4|5dg< d |5dj< d4|5d~< d	|5d< |5 #dyd ¡ |5ˆ	ˆ
< d4}?|?dk�r¤t*|5 d~¡�pHd!ƒ|?d k �rnt>| ?¡ ˆ
|ˆ
 @dd¡d€ |5 dwdZ¡|?|5 d�dZ¡ˆ|5 d‚¡�rªt:tA A¡ |5d‚  dƒndZdƒœ	ƒ |?|5d~< |5ˆ	ˆ
< |d7 }td„ˆ
› d…|?d|›d†t|5ƒd|›d‡|5 djd!¡dL dˆ›d‰|5 d�¡› dŠ�ƒ d‹dŒ„ ‰ ˆ |% d�¡ƒˆ |% d3¡ƒ }F}G‡
fdŽd%„|D ƒ}H‡ fd�d%„|HD ƒ}I‡ fd�d%„|HD ƒ}Jt7‡	fd‘d-„|D ƒƒ}Kd’d“„ }LtB| ?¡ ||!ˆ
ˆ
 @dd¡d€ | d u�r¸| d›ndZ|K|5 dj¡d u�rØ|5dj d”›ndZ|?|*d”›|Fd u �rðdZn|Fd”›|Gd u �rdZn|Gd”›d |F|Gfv �rdZn
|G|F d”›d• d–d-„ |HD ƒ¡|L|Iƒ|L|Jƒt6d—d-„ |JD ƒƒ�r\dZn
t7|Jƒd”›d˜œƒ |?t|5ƒd k�r”d|5d< |5ˆ	ˆ
< �qœd	|5d< |5ˆ	ˆ
< ˆ ˆ
¡�p²g }M|5 d‚¡�rÔtA A¡ |5 d‚d!¡ nd™}Nt-|Mƒdk�rDtdšˆ
› d›t-|Mƒ› dœ�ƒ |MD ].}zt	 "|d( ¡ W n t�y.   Y n0 �qˆ	 #ˆ
d ¡ �qœ|M�sd|5 d(¡�rd|Nd�k �rd�qœ|M�sÌ|5 d(¡�rÌtC|5 d(¡ƒ}O|Odk�rÌt0t*|5 d[¡�p d!ƒ|Oƒ|5d[< |5ˆ	ˆ
< tdžˆ
› dŸ|Od|›d �ƒ t7d¡d-„ |MD ƒƒ}Pt|5ƒt*|5 d[¡�pôd!ƒ }Q|M �p.|5 dw¡|4k�p.|NtD|5ƒk�p.t=|P|Q ƒd¢k}R|R�s:�qœd }Sd4}T|MD ]^}zt	 "|d( ¡}UW n8 t�y” }
 zd£tE|
ƒv�r€|
}SW Y d }
~
nd }
~
0 0 |TtF|Uƒ7 }T�qF|Sd u�rÊtd¤ˆ
› d|S› d¥�ƒ �qœ|Tdk�rt*|5 d[¡�päd!ƒ|T |5d[< |5ˆ	ˆ
< td¦ˆ
› d§|Td|›d¨�ƒ t:t|5ƒt*|5 d[¡�p(d!ƒ d ƒ}V|Vt	jGk �rB�qœˆ
tHv}W|W�rdt4�rdt5t4k�rd�qœt	jI}X||v �s~|*dk�r²| d u�r”| tJ d© nd4}Yt9t0t	jItKtL|Ydª ƒƒƒ}Xz.t	jMˆ
|V|4|Xd«t9tA A¡ d¬ ƒd­ › �d®�}ZW nT t�y4 }
 z:td¯ˆ
› dt|
ƒj› d|
› �ƒ W Y d }
~
�qœW Y d }
~
n
d }
~
0 0 |Z�p>i  d°¡�pJi  d(¡�pb|Z�p\i  d(¡}[ˆ	 ˆ
¡�pri }5d±d%„ |5 db¡�pˆg D ƒ}\|[�r¨|[|\v�r¨|\ |[¡ |\d²d … |5db< |5 N|4tA A¡ |X| ?¡ |[||% d³¡�pê|% d´¡�pêdZd	dµœ¡ t0t*|5 d¶¡�pd!ƒ|4dx ƒ|5d¶< |5 d·t1¡ |5ˆ	ˆ
< |W�rBtH ;ˆ
¡ t5d7 a5|% d�¡}]|]d u�r^t*|]ƒnd }]|]d u�rx|]|2 d¸ nd }^t$ Oˆ
|d d ¡}_|# |_¡}`tP| ?¡ ˆ
||!ˆ
 @dd¡d€ |4|2d”›|]d u�rÌ|]d”›ndZ|^d u�rà|^d”›ndZ|`d u�rô|`d”›ndZ|% d?¡�p|% d¹¡�pdZ|% dº¡�pdZ|-�r,|-dˆ›ndZ|2d”›|5 d¶¡|4dx |2 dL d›|5 d�¡|% d»¡�pp|% d¼¡�ppdZ|% d½¡�pŠ|% d¾¡�pŠdZ| d u�rœ| d›ndZ|)d< d”›|)d¿ d”›|)d> d”›t9|Wƒt9|ƒdÀœƒ |M�rà|d7 }n|d7 }�qœ�q²tQˆ	ƒ |�rdÁndÂ}adÃ}bt7dÄd-„ ˆ R¡ D ƒƒ}ct9tSd5 |b ƒ}d|c|dk}et4�rNdÅt5› d†t4› �ndZ}ftd u�rl|fdÆtd›�7 }ftT�r|dÇtT› �ndZ}gttUjV W¡ dÈ›dÉ|a› dÊt-|ƒ› dË|› dÌ|› dÍ|› dÎ|› dÏ|› |g› dÐ|c› |f› �ƒ |e�rtdÑ|c› dÒ|d› dstS› dÓ|b› dÔ�	ƒ t	 
dÕ¡ d	S dS )ÖNr1   z[STOP LOSS] equity $z.2fz vs $z funded -- down $z	, limit $z. Cancelling and stopping.z	stop lossFru   rv   Zrestartz[KILL] z present -- z4restart: keeping held-book legs, cancelling the restzcancelling and stoppingz	kill file©Z	keep_heldÚordersre   Zfillsc                 S   s&   i | ]}|  d ¡t|  d¡pdƒ“qS )re   Zposition_fpr   )r
   r	   )r&   r   r   r   r   rç     s   ÿzcycle.<locals>.<dictcomp>Zmarket_positionsz0  [warn] portfolio read failed, skipping cycle: r  Tc                 S   s0   h | ](\}}|d krd  | d¡dd… ¡’qS )ç•Ö&è.¾r2   Né   ©ÚjoinÚsplit)r&   r+   Úqr   r   r   r°   '  s   ÿzcycle.<locals>.<setcomp>r"   r2   rF  r   c                 S   s   h | ]\}}|d kr|’qS )r   r   )r&   rü   r­   r   r   r   r°   M  r)   c                 S   s0   h | ](\}}|r|rd   | d ¡dd… ¡’qS )r2   NrF  rG  )r&   r+   Zos_r   r   r   r°   R  s   ÿc                    s   g | ]}|d  ˆ vr|‘qS r#  r   ©r&   r•   )Údoner   r   r6   T  s   ÿzcycle.<locals>.<listcomp>c                    s   g | ]}|d  ˆ v r|‘qS r#  r   rK  )Úheldr   r   r6   ]  r)   c                 S   s   h | ]}|d  ’qS r#  r   rK  r   r   r   r°   z  r)   r¾   z  [ROTATE] cancelled z% offer(s) on books no longer selected)NNc                 3   s   | ]}ˆ   |¡V  qd S r#   rä   rå   )Ú_pinr   r   r(   —  r)   zcycle.<locals>.<genexpr>)rs   Údr'   rs   rO  r'   )r  Zdrawr   r)  r   r   c                 3   s    | ]}t ˆ  |¡pi ƒV  qd S r#   )r¢   r
   r*   r±   r   r   r(   ¯  r)   )Údefaultr[   r\   r   )r/   r[   r\   r]   rF   rO   r0   r$  c                 S   s   g | ]}|t vr|‘qS r   )Ú_OFFEREDr*   r   r   r   r6   Î  r)   rK   r9   r:   z	  [HOLD] é&   z: collected z, needs rN   z from z	 leg(s), rP   r   z.1fz!c over touch -- holding the offerc                 3   s   | ]}ˆ   |  d ¡¡V  qdS )re   Nrä   r  )Úrestingr   r   r(   #  r)   c                 3   s8   | ]0}ˆ   |  d ¡¡durdˆ |  d ¡  t V  qdS )re   Nr   )r
   rQ   r  r<   r   r   r(   %  s   ÿg      ð¿z  [fit] z: book needs $z, only $z; free -- skipping whole book rather than posting part of itrJ   g<NÑ‘\þß?éc   ri   r¹   r   Zfill_idsz	  [heal] z: booked fill z but exchange shows LONG z: -- not ours (xlow trades these markets), clearing phantom)r   r!   r   Ú
logged_qtyr   r@   Zour_idsc                    st   g | ]l}t | d d¡ƒ ¡ dkr| d¡st | dd¡ƒ ¡ dkrt | dd¡ƒˆkrˆrˆ rl| d¡ˆ v r|‘qS )Úactionr¹   ÚsellÚis_takerrg   ÚyesZcreated_timer¾   ©rº   r
   r»   ©r&   rD   )Ú_our_idsÚ
posted_isor   r   r6   ‹  s   
úc                    s   g | ]}|  d ¡ˆ vr|‘qS )Úfill_idrä   r[  r3  r   r   r6   —  r)   Zcount_fpZyes_price_dollarsr!   r^  c                 s   s   | ]}|r|V  qd S r#   r   ©r&   Úxr   r   r   r(   ¤  r)   r   rE  c                 S   sJ   g | ]B}t | d d¡ƒ ¡ dkr| d¡st | dd¡ƒ ¡ dkr|‘qS )rV  r¹   rW  rX  rg   rY  rZ  r[  r   r   r   r6   ¸  s   
ýc                 s   s   | ]}|r|V  qd S r#   r   r_  r   r   r   r(   É  s   ÿc                 s   s   | ]}|  d ¡V  qdS )r^  Nrä   r[  r   r   r   r(   Ê  r)   c                    s"   g | ]}ˆ r|  d ¡ˆ vr|‘qS )r¾   rä   r[  ©r\  r   r   r6   Ý  s   ÿz  [!] z: short z  with no attributable maker fillz (zB fill(s) on this ticker are not ours -- xlow trades these markets)z -- NOT booking a fillc                    s:   g | ]2}ˆ r|  d ¡ˆ vrt|  dd¡ƒ ¡ dkr|‘qS )r¾   rV  r¹   Zbuy)r
   rº   r»   r[  ra  r   r   r6     s   þr   r    Z	flat_seenz
  [RESET] z state says r¤   z= sold, exchange flat -- closed outside the maker; re-offeringrU  r   r3   rÞ   Ú	posted_tsrc   z	  [FILL] r!  ú/z @ ry   zc after z aheadc              	   S   s(   z
t | ƒW S  ttfy"   Y d S 0 d S r#   )r	   r   r   )r¡   r   r   r   Ú_f†  s    
zcycle.<locals>._fÚyes_bid_dollarsc                    s   g | ]}|  d ¡ˆ kr|‘qS ©re   rä   ©r&   ÚX)r®   r   r   r6   Œ  r)   c                    s   g | ]}ˆ |  d ¡ƒ‘qS )re  rä   rg  ©rd  r   r   r6   �  r)   c                    s   g | ]}ˆ |  d ¡ƒ‘qS )r)  rä   rg  ri  r   r   r6   Ž  r)   c                 3   s8   | ]0}t ˆ  | d ¡¡pi  d¡p$dƒdkrdV  qdS )re   r   r   r"   r1   N©r	   r
   rg  r±   r   r   r(   �  s   ÿÿÿc                 S   s   d  dd„ | D ƒ¡S )Nú|c                 s   s"   | ]}|d u rdn|d›V  qd S )Nr¹   ú.4fr   r_  r   r   r   r(   ’  r)   z*cycle.<locals>.<lambda>.<locals>.<genexpr>)rH  )Zxsr   r   r   r=   ’  r)   zcycle.<locals>.<lambda>rl  rk  c                 s   s   | ]}|  d ¡pdV  qdS )re   r¹   Nrä   rg  r   r   r   r(      r)   c                 s   s   | ]}|d u V  qd S r#   r   r_  r   r   r   r(   £  r)   rj   g    eÍÍAz  [DUP] z has z resting -- cancelling allé   z  [VANISHED-FILLED] z order executed z4 -- not re-posting (position read had not caught up)c                 s   s*   | ]"}t | d ¡p| d¡pdƒV  qdS )rÃ   rÄ   r   Nrj  ©r&   rÀ   r   r   r   r(   ß  s   ÿÿÿg      à?Z404z  [CANCEL-FAIL] z -- not re-posting this cyclez  [LATE-FILL] z cancel returned z( filled -- counting it before re-postingr  i,  Úmkiè  i Êš;)r   Zclient_order_idr  r·   c                 S   s   g | ]}|r|‘qS r   r   r_  r   r   r   r6   U	  r)   iØÿÿÿZyes_ask_size_fpZask_size)r   rb  r   ri   r¾   rf   rÞ   r   r@   r    g       @rÝ   Zvolume_24h_fpZyes_bid_size_fprß   r'  rà   rM   rÜ   ZLIVEZdryg      ø?c                 s   s   | ]}t |ƒV  qd S r#   )rX   )r&   r¡   r   r   r   r(   ¬	  r)   z  legs z
  equity $z  thin ú%H:%M:%Sz [z] z books  posted z  refreshed z  fills z  gated z  abandoned z
  resting z
  [ABORT] z resting > cap z books x3 xz') -- cancelling everything and stoppingzresting over cap)XrŸ   r„   Ú_equity_tickÚ_EQUITY_EVERYr™   Ú_equity_lastÚ
FUNDED_USDÚSTOP_LOSS_USDr‚   r   Ú
cancel_allÚ	KILL_FILErÊ   r   ÚstripÚ_RESTART_MODErq   r’   Úget_open_ordersr
   r¥   rT   Z	get_fillsZget_positionsr  r  rS   r¢   rH  rI  rB  ÚFINISH_ONLYr9  rë   Zcancel_order_v2r²   r  r5  Zfetch_prematch_oddsr<  Zdevigr¨   r	   r   r   rX   ÚARM_SPECr=  r   rQ   rb   rR   Ú_POST_BUDGETÚ_POSTS_MADEÚanyrU   r˜   rp   Úroundr:  r8  ÚabsrÖ   rú   r5   r{   rÛ   rÁ   r   rº   r¼   Z	MIN_COUNTrQ  r   r  rC   ÚHELD_TTL_CAP_SZplace_maker_askÚupdateZleg_siderð   r¶   rV   r?  r>  rœ   r�   r…   )hrH   râ   r…   ÚeqZdownrÀ   Zfills_by_tkrZflr+   Z	positionsr  Z
_legs_heldrJ  Z_thrZ_resting_evsZbooksZ_held_eventsZ_activeZ_rotatedZ_tkrZ_ordersZ_evZ_oZpostedZ	refreshedrÂ   Z	abandonedZgatedZ_cycle_headrü   r  Úmatchr  rk   r"  ZfairZ_b365r  r'   Z_specZ
_book_sizer•   rF   rO   r0   Z_bookvolrá   Zt3Z_already_upZ_fitZaskZfloorZprice_cr^   Z_kZoursrÓ   rD   Zpxro  r_   r–   ZforeignZgotZ_posZ_foreign_hereZ_prev_qra   Z_estZ	seen_flatZ_fbZ_faZ_svZ_svbZ_svaZ_nfillZ_jZminer‡   Z_doneZ_rest_qZ	_want_nowrN   Zcancel_failedZcancelled_filledZ_respZwantZ	fresh_legZ_ttlZ
_stop_in_srã   ZoidZ_oidsZ_bidZ_midZ_sdZ_pfÚtagZABORT_SLACKÚtotalZ	_abort_atZoverZbudgetZthinr   )rd  r\  rN  r-   rL  rM  r]  rS  r4  rH   r®   r   Úcycleæ  sÌ   ÿÿ


ÿþ
 
ÿ
ÿÿ "ÿ

ÿ

ý


ÿÿþ
+ÿ


ÿ




"ÿþþÿ

 

ÿ

ÿÿûú	







ÿø	ÿþ

ÿ

ï$

ÿÿ
þý

"ÿÿ
 $ÿ
ÿö"


ÿ


ç

 ÿÿÿþþþþþÿÿ

rˆ  c                 C   sZ   | t jv rVt j | ¡}|d tt jƒk rVztdtt j|d  ƒƒW S  tyT   Y n0 |S )z°--flag N off the command line. Used for --books so the FIRST armed run
    can be one book (three orders) rather than sixty, without editing and then
    un-editing a constant.r1   )ÚsysÚargvÚindexrX   r   rp   r   )ÚflagrP  Úir   r   r   Ú_arg_intÂ	  s    
rŽ  c            	         sD  dt jv } dt jv }tdtƒatddƒ}|r<t ¡ |d  nd }tddƒadt jv at ¡ rpt	d	t› d
�ƒ dS t
ƒ szdS t | ¡ t	d| › dt› dt› dtj› dt› d�trºdt› d�nd |rÖdtj |¡d›�nd ƒ | sêt	dƒ t	dtjddd�› �ƒ tƒ }t|ƒ t dd„ | ¡ D ƒ¡ ttƒat�rpt	dttƒ› d�t�rhd tdtt ƒ› d!t› d"�nd ƒ �zNt�s@|�r®t ¡ |k�r®t	tj ¡ d›d#|› d$�ƒ �q@zt|| ƒ�sÂW �q@W n@ t�y } z&t	d%t |ƒj!› d&|› �ƒ W Y d }~n
d }~0 0 |�r�q@t}|�r2tdt"t|t ¡  ƒƒ}t #|¡ �qtW t	d'tjd(t$d�› �ƒ t d)¡ t%ƒ  t&|ƒ t'd*ƒD �]@}t #d*¡ z t( )¡ �p˜i  *d+g ¡�p¦g }W nP t�yú } z6t	d,t |ƒj!› d&|› �ƒ W Y d }~ �q@W Y d }~n
d }~0 0 t$�rht +¡ �pt,ƒ ‰ ‡ fd-d.„|D ƒ‰‡fd/d.„|D ƒ}ˆ�rh|dk�rht	d0tˆƒ› d1d2d.„ ˆD ƒ› �ƒ |�s|t	d3ƒ  �q@|d4k�rzt	d5t|ƒ› d6d7d.„ |D ƒ› �ƒ t	d8tj› d9�ƒ �qz�n~t	d'tjd(t$d�› �ƒ t d)¡ t%ƒ  t&|ƒ t'd*ƒD �]@}t #d*¡ z t( )¡ �pi  *d+g ¡�p&g }W nP t�yz } z6t	d,t |ƒj!› d&|› �ƒ W Y d }~ �q>W Y d }~n
d }~0 0 t$�rèt +¡ �p�t,ƒ ‰ ‡ fd-d.„|D ƒ‰‡fd/d.„|D ƒ}ˆ�rè|dk�rèt	d0tˆƒ› d1d2d.„ ˆD ƒ› �ƒ |�süt	d3ƒ  �q>|d4k�rút	d5t|ƒ› d6d7d.„ |D ƒ› �ƒ t	d8tj› d9�ƒ �qú0 dS ):Nz--livez--oncez--booksz	--minutesr   é<   z--max-postsz--finish-onlyzrefusing to start: z existsr1   zmaker starting  LIVE=z  size=z	  books<=z  ttl=zs  poll=r^   z  max z	 new legsr¹   z  backstop rp  z6DRY RUN -- no orders will be sent. Pass --live to arm.zstartup cancel-all: ZstartupTrC  c                 s   s.   | ]&\}}t |tƒr| d ¡dur|V  qdS )r@   N)r§   r¨   r
   )r&   r+   r¡   r   r   r   r(   
  s   
ÿzmain.<locals>.<genexpr>z	resuming z legs already offeredz -- z of z
 remainingz deadline reached (z min) -- stoppingz  [cycle error] r  zshutdown cancel-all: ZshutdownFr   rD  z#  could not verify resting orders: c                    s0   g | ](}t | d d¡ƒ dd¡d ˆ v r|‘qS )re   r¹   r2   r1   r   )rº   r
   r5   rn  )Ú_hr   r   r6   E
  s    ÿzmain.<locals>.<listcomp>c                    s   g | ]}|ˆ vr|‘qS r   r   rn  )Ú_keptr   r   r6   G
  r)   zkept z+ held-book leg(s) resting for the restart: c                 S   s   g | ]}|  d ¡‘qS rf  rä   rn  r   r   r   r6   J
  r)   z&resting orders remaining: 0 (verified)rF  z!! z) ORDERS STILL RESTING after cancel + 9s: c                 S   s   g | ]}|  d ¡‘qS rf  rä   rn  r   r   r   r6   P
  r)   z$!! they expire by themselves within zs of posting)-r‰  rŠ  rŽ  r?  r{   r}  r{  rw  rÊ   r‚   r‰   r   Zset_liverQ   r   Ú	POLL_SECSrœ   r�   Zfromtimestamprv  r´   r¯   rQ  rƒ  rS   rX   r~  r   rš   r…   rˆ  rq   r  r  rC   Úsleepry  r�   r¶   r
  r’   rz  r
   r  r9  )	râ   ÚonceZminutesZdeadlinerH   r  ZnapZattemptrÅ   r   )r�  r‘  r   ÚmainÐ	  sô    






ÿÿþÿüÿ
ÿÿþÿ

0ÿ

 &ÿ
ÿÿáÿ

 &ÿ
ÿÿr•  Ú__main__)NNr   F)r   )r   )jÚ__doc__rÌ   r�   rœ   r|   Úsignalr‰  r{   Úpathlibr   ÚpathÚinsertZkalshi_traderr’   Zkalshi_makerr   Z	mm_quoterr  r’  r  r?  rQ   r;  r   r   r   ZREFRESH_AT_Sr‚  r   r   rW   rB   rA   rZ   rY   rR   r   rb   rw  ry  r³   rÉ   rÎ   rÙ   rÚ   r~   r€   rt   r‰   r„   r�   rš   r}  r~  r{  rt  ru  rr  rq  rs  r”   r˜   r™   r9  rQ  r›   ÚSIGINTrŸ   r¢   r¯   r©   r¦   rª   r´   r¶   rÁ   r¼   rÖ   rÛ   ré   rê   rï   rî   rð   r  r  r|  rS   ró   r2  rô   rø   r=  r>  rö   rý   r	  r  Ú	frozensetr  rB  rˆ  rŽ  r•  r  Úexitr   r   r   r   Ú<module>   sØ   '	M-  ÿ
 K'


0$&	Y
ÿ
02         c 
