+
    i                        R t ^ RIHt ^ RIt^ RIt^ RIHt ^ RIHtH	t	 ^ RI
Ht  ! R R]4      t] ! R R	4      4       t] ! R
 R4      4       tR# )z
Rate limiter for proactive request throttling.

Composable, optional, and easy to remove. Inject into KalshiClient
to prevent 429s rather than just retrying after them.
)annotationsN)deque)	dataclassfield)Protocolc                  6    ] tR t^tRtRR R lltR R ltRtR# )	RateLimiterProtocolzEProtocol for rate limiters. Implement this to create custom limiters.c                    V ^8  d   QhRRRR/#    weightintreturnfloat )formats   "\/home/wkmabra/.openclaw/workspace/venv/lib/python3.14/site-packages/pykalshi/rate_limiter.py__annotate__ RateLimiterProtocol.__annotate__   s     	 	c 	% 	    c                    R# )zAcquire permission to make a request.

Args:
    weight: Cost of this request (default 1).

Returns:
    Time waited in seconds (0 if no wait).
Nr   selfr   s   &&r   acquireRateLimiterProtocol.acquire   s     	r   c               $    V ^8  d   QhRRRRRR/# r   	remaining
int | Nonereset_atr   Noner   )r   s   "r   r   r      s"      Z : RV r   c                    R# )zUpdate internal state from response headers.

Args:
    remaining: Requests remaining in current window.
    reset_at: Unix timestamp when window resets.
Nr   r   r   r   s   &&&r   update_from_headers'RateLimiterProtocol.update_from_headers   s     	r   r   N   __name__
__module____qualname____firstlineno____doc__r   r#   __static_attributes__r   r   r   r   r      s    O	 r   r   c                  :   ] tR t^)t$ RtRtR]R&   RtR]R&   RtR]R	&   ]	! ]
R
R7      tR]R&   ]	! ]P                  R
R7      tR]R&   ]	! RR
R7      tR]R&   ]	! RR
R7      tR]R&   ]	! RR
R7      tR]R&   R R ltRR R lltR R lt]R R l4       tR R ltRtR# ) RateLimitera  Token bucket rate limiter with sliding window.

Thread-safe. Proactively throttles requests to stay under limits.

Usage:
    limiter = RateLimiter(requests_per_second=10)
    client = KalshiClient(..., rate_limiter=limiter)

    # Or manual usage:
    limiter.acquire()  # Blocks if needed
    response = make_request()
    limiter.update_from_headers(
        remaining=int(response.headers.get('X-RateLimit-Remaining')),
        reset_at=int(response.headers.get('X-RateLimit-Reset')),
    )

Attributes:
    requests_per_second: Target request rate.
    burst: Maximum burst size (default: 2x requests_per_second).
    min_spacing_ms: Minimum ms between requests (anti-burst).
g      $@r   requests_per_secondNr   burst        min_spacing_msF)default_factoryreprr   _timestampszthreading.Lock_lock)defaultr5   _last_request_server_remaining_server_reset_atc                   V ^8  d   QhRR/# r   r   r    r   )r   s   "r   r   RateLimiter.__annotate__M   s        t  r   c                	    V P                   f,   \        ^\        V P                  ^,          4      4      V n         RV n        R # )Ng      ?)r1   maxr   r0   _window_sizer   s   &r   __post_init__RateLimiter.__post_init__M   s2    ::QD$<$<q$@ ABDJr   c                    V ^8  d   QhRRRR/# r
   r   )r   s   "r   r   r>   R   s     0 0c 0% 0r   c                   V P                   ;_uu_ 4        \        P                  ! 4       pRpV P                  ^ 8  dw   W P                  ,
          R,          pW@P                  8  dN   V P                  V,
          R,          p\        P
                  ! V4       W5,          p\        P                  ! 4       pW P                  ,
          pV P                  '       d5   V P                  ^ ,          V8  d   V P                  P                  4        KF  \        V P                  4      V P                  8  d   V P                  ^ ,          pWpP                  ,           V,
          pV^ 8  d4   \        P
                  ! V4       W5,          p\        P                  ! 4       pW P                  ,
          pV P                  '       g   K  V P                  ^ ,          V8  g   K  V P                  P                  4        KJ  V P                  e   V P                  ^8:  dv   V P                  eh   V P                  \        P                  ! 4       ,
          pV^ 8  d;   \        P
                  ! V4       W8,          p\        P                  ! 4       pRV n        \        V4       F  p	V P                  P                  V4       K   	  W n        VuuRRR4       #   + '       g   i     R# ; i)z=Block until request is allowed. Returns wait time in seconds.r2   i  N)r7   time	monotonicr3   r9   sleeprA   r6   popleftlenr1   r:   r;   rangeappend)
r   r   nowwaitedelapsed
sleep_timecutoffoldestsleep_until_s
   &&        r   r   RateLimiter.acquireR   s   ZZZ.."CF ""Q&!3!33t;000"&"5"5"?4!GJJJz*(F..*C ,,,F"""t'7'7':V'C  ((* d&&'4::5))!,#&7&77#=
>JJz*(F..*C000&&&4+;+;A+>+G$$,,. %%1d6L6LPQ6Q((4"&"7"7$))+"EK"Q

;/-"nn.15. 6]  '', #!$] ZZZs    CKCK<KC$KK	c               $    V ^8  d   QhRRRRRR/# r   r   )r   s   "r   r   r>      s$     1 1#1/91	1r   c                    V P                   ;_uu_ 4        Ve   Wn        Ve   W n        RRR4       R#   + '       g   i     R# ; i)zFUpdate state from X-RateLimit-Remaining and X-RateLimit-Reset headers.N)r7   r:   r;   r"   s   &&&r   r#   RateLimiter.update_from_headers   s1     ZZZ$)2&#(0%	 ZZZs	   4A	c                   V ^8  d   QhRR/# )r   r   r   r   )r   s   "r   r   r>      s     ) )e )r   c                l   V P                   ;_uu_ 4        \        P                  ! 4       pWP                  ,
          pV P                  '       d5   V P                  ^ ,          V8  d   V P                  P                  4        KF  \        V P                  4      uuRRR4       #   + '       g   i     R# ; i)z/Current request rate (requests in last second).N)r7   rG   rH   rA   r6   rJ   rK   )r   rN   rR   s   &  r   current_rateRateLimiter.current_rate   sq     ZZZ.."C,,,F"""t'7'7':V'C  ((*t''( ZZZs   9B"AB""B3	c                   V ^8  d   QhRR/# r=   r   )r   s   "r   r   r>      s     ) )t )r   c                    V P                   ;_uu_ 4        V P                  P                  4        RV n        RV n        RV n        RRR4       R#   + '       g   i     R# ; i)z$Clear all state. Useful for testing.r2   N)r7   r6   clearr9   r:   r;   rB   s   &r   resetRateLimiter.reset   sA    ZZZ""$!$D%)D"$(D!	 ZZZs   0AA 	)r9   r:   r;   rA   r1   r%   )r(   r)   r*   r+   r,   r0   __annotations__r1   r3   r   r   r6   	threadingLockr7   r9   r:   r;   rC   r   r#   propertyr\   ra   r-   r   r   r   r/   r/   )   s    , "&%E:NEu5AKA!)..uME>M 59M59 %*$U$CzC#(E#BjB 
0d1 ) )) )r   r/   c                  6    ] tR t^tRtRR R lltR R ltRtR# )	NoOpRateLimiterz7Rate limiter that does nothing. For testing or opt-out.c                    V ^8  d   QhRRRR/# r
   r   )r   s   "r   r   NoOpRateLimiter.__annotate__   s      c % r   c                	    R # )r2   r   r   s   &&r   r   NoOpRateLimiter.acquire   s    r   c               $    V ^8  d   QhRRRRRR/# r   r   )r   s   "r   r   rj      s$      #/9	r   c                	    R # )Nr   r"   s   &&&r   r#   #NoOpRateLimiter.update_from_headers   s     	r   r   Nr%   r'   r   r   r   rh   rh      s    A r   rh   )r,   
__future__r   rd   rG   collectionsr   dataclassesr   r   typingr   r   r/   rh   r   r   r   <module>rt      s`    #    ( ( 0 t) t) t)n 	 	 	r   