+
    VÊôj×(  ã                  óX  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIHtHt ^ RI	H	t	H
t
 ^ RIHtHt R t] ! R R4      4       t ! R	 R
4      t ! R R]4      t ! R R]4      t]R8X  dÅ   ]! ^d^^ R7      t]P-                  RRRR7       ]P/                  RRR7      t]! R]R]P4                  R4       ]! R]P7                  R4      4       ]P-                  RRRR7       ]! R]P7                  R4      4       ]! R]P7                  R4      4       ]! R]P9                  ]! R4      4      4       R# R# ) u“  
Venue-agnostic execution layer for the Renko line bot.

The strategy decides LONG / SHORT / CLOSE at the line. This file decides *how* that
becomes an order, and how the bot recovers if it was switched off mid-trade.

  PaperBroker   â€” works today. Fills at the line (or line +/- slip), charges fees,
                  keeps a CSV, reconciles against its own state file.
  LighterBroker â€” the real venue. Deliberately refuses to trade until it is given a
                  testnet/mainnet client and an API key, because sending invented
                  calls to a live exchange is how accounts die.

LIGHTER FACTS (verified 2026-11-11, docs.lighter.xyz + apidocs.lighter.xyz):
  * official Python SDK:  pip install lighter-sdk     (elliottech/lighter-python)
  * REST + WebSocket + transaction signing are all in the SDK
  * API keys are created with ChangePubKey, signed by the L1 wallet, and can
    trade / cancel / change leverage WITHOUT withdrawal rights â€” use that
  * testnet is a separate environment with separate accounts, balances, API keys,
    nonces and chain id. Get the exact URLs from
    https://docs.lighter.xyz/developers -> "Environments & endpoints"
  * Standard accounts pay 0% maker / 0% taker on regular orders, 300ms taker latency
  * funding is charged hourly on notional (it is not a trading fee and cannot be avoided)

Usage:
    from broker import PaperBroker
    b = PaperBroker(capital=100, leverage=20)
    b.open("LONG", price=1150.0, line=1150.0)
    b.close(price=1151.15, reason="line touch")
    b.reconcile(expected="LONG")     # what to call on every boot

Before real money: run this against Lighter TESTNET, then mainnet with $50.
)ÚannotationsN)Ú	dataclassÚasdict)ÚdatetimeÚtimezone)ÚDecimalÚ
ROUND_DOWNc                 óh   € \         P                  ! \        P                  4      P	                  R 4      # )z%Y-%m-%d %H:%M:%S)r   Únowr   ÚutcÚstrftime© ó    Ú	broker.pyr
   r
   +   s!   € Ü�<Š<œŸ™Ó%×.Ñ.Ð/BÓCÐCr   c                  óv   € ] tR t^/t$ R]R&   R]R&   R]R&   R]R&   R]R&   R]R&   R	tR
]R&   RtR]R&   R tRtR	# )ÚPositionÚstrÚsideÚDÚqtyÚentryÚnotionalÚfee_paidÚopenedNzD | NoneÚstopFÚboolÚcarriedc           	     	ó°   € \        V 4      pVP                  4        UUu/ uF)  w  r#T\        V\        4      '       d   \	        V4      MTbK+  	  upp# u uppi ©N)r   ÚitemsÚ
isinstancer   r   )ÚselfÚdÚkÚvs   &   r   ÚjsonableÚPosition.jsonable:   sB   € Ü�4‹LˆØCDÇ7Á7Ä9ÔMÁ9¹4¸1�œj¨¬A×.Ò.”C˜”F°AÒ5Á9ÒMÐMùÓMs   Ÿ/Ar   )	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__annotations__r   r   r%   Ú__static_attributes__r   r   r   r   r   /   s:   ‡ à
ƒIØ	
ƒFØƒHØƒKØƒKØƒKØ€Dˆ(ÓØ€GˆTÓöNr   r   c                  ó@   € ] tR t^?tRtR tR	R ltR tR
R ltR t	Rt
R# )ÚBrokerzDInterface. Every venue implements these five calls and nothing else.c                	ó   € \         hr   ©ÚNotImplementedError©r!   r   ÚpriceÚlines   &&&&r   ÚopenÚBroker.openB   ó   € Ü!Ð!r   c                	ó   € \         hr   r0   ©r!   r3   Úreasons   &&&r   ÚcloseÚBroker.closeE   r7   r   c                	ó   € \         hr   r0   ©r!   s   &r   ÚpositionÚBroker.positionH   r7   r   Nc                	ó   € \         hr   r0   ©r!   Úexpected_sides   &&r   Ú	reconcileÚBroker.reconcileK   r7   r   c                	ó   € \         hr   r0   ©r!   Úamounts   &&r   Úmove_to_savingsÚBroker.move_to_savingsN   r7   r   r   ©ú
line touchr   )r'   r(   r)   r*   Ú__doc__r5   r;   r?   rD   rI   r,   r   r   r   r.   r.   ?   s   † ÙNò"ô"ò"ô"ö"r   r.   c            	      óÂ   € ] tR t^RtRt]! R4      ^]! R4      ]! R4      ]! R4      ]! R4      RRR	3	R
 ltR tR tR t	R t
]! R4      3R ltRR ltR tRR ltR tRtR# )ÚPaperBrokerzFPaper fills. 'line' fills at the line exactly; 'market' adds one tick.Ú100Ú0ú0.001Ú5z0.01r4   zbroker_state.jsonzbroker_fills.csvc
                	ó@  € \        \        V4      4      V n        \        R 4      V n        \        \        V4      4      \        \        V4      4      VuV n        V n        V n        WVVuV n        V n        V n	        W‰uV n
        V n        RV n        V P                  4        R# )rQ   N)r   r   ÚcapitalÚsavingsÚleverageÚfeeÚqty_stepÚmin_notionalÚtickÚfillÚ
state_fileÚlog_fileÚposÚ_load)
r!   rU   rW   rX   rY   rZ   r[   r\   r]   r^   s
   &&&&&&&&&&r   Ú__init__ÚPaperBroker.__init__U   sy   € ô œ˜W›“ˆŒÜ˜“vˆŒÜ12´3°x³=Ó1AÄ1ÄSÈÃXÃ;ÐPXÐ.ˆŒ�t”x ¤Ø2>ÀdÐ/ˆÔ˜4œ9 d¤iØ)3Ð&ˆŒ˜œØˆŒØ�
‰
Žr   c                	óŒ  € \         P                  P                  V P                  4      '       Ed   \        P
                  ! \        V P                  4      4      p\        VR ,          4      \        VR,          4      uV n        V n	        VP                  R4      pV'       d¡   \        VR,          \        VR,          4      \        VR,          4      \        VR,          4      \        VR,          4      VR,          VP                  R	4      '       d   \        VR	,          4      MR
VP                  RR4      4      V n        R
# R
# R
# )rU   rV   r_   r   r   r   r   r   r   r   Nr   F)ÚosÚpathÚexistsr]   ÚjsonÚloadr5   r   rU   rV   Úgetr   r_   )r!   ÚsÚps   &  r   r`   ÚPaperBroker._loada   sÔ   € Ü�7‰7�>‰>˜$Ÿ/™/×*Ó*Ü—	’	œ$˜tŸ™Ó/Ó0ˆAÜ)*¨1¨Y­<«¼!¸A¸i½L»/Ð&ˆDŒL˜$œ,Ø—‘�e“ˆAßÜ# A f¥I¬q°°5µ«{¼A¸aÀ½j»MÌ1ÈQÈzÍ]ÓK[Ü$% a¨
¥mÓ$4°a¸µkØ45·E±E¸&·M²M¤A a¨¥i¤LÀtØ$%§E¡E¨)°UÓ$;ó=�–ñ ñ	 +r   c           	     	ó  € \         P                  ! R \        V P                  4      R\        V P                  4      RV P
                  '       d   V P
                  P                  4       MR/\        V P                  R4      ^R7       R# )rU   rV   r_   NÚw)Úindent)	rg   Údumpr   rU   rV   r_   r%   r5   r]   r>   s   &r   Ú_saveÚPaperBroker._savel   sZ   € Ü�	Š	�9œc $§,¡,Ó/°¼CÀÇÁÓ<MØ°··°˜$Ÿ(™(×+Ñ+Ô-¸tðEä�t—‘¨Ó,°Q÷	8r   c                	ó`  € \         P                  P                  V P                  4      '       * p\	        V P                  R RR7      ;_uu_ 4       p\
        P                  ! V4      pV'       d   VP                  . RO4       VP                  V4       RRR4       R#   + '       g   i     R# ; i)ÚaÚ )ÚnewlineN)
ÚtimeÚactionr   r   r3   rX   ÚpnlrU   rV   r:   )rd   re   rf   r^   r5   ÚcsvÚwriterÚwriterow)r!   ÚrowÚnewÚfrn   s   &&   r   Ú_logÚPaperBroker._logq   sk   € Ü—'‘'—.‘. §¡Ó/Ô/ˆÜ�$—-‘- ¨b×1Õ1°QÜ—
’
˜1“ˆAßØ—
‘
ò <ô =à�J‰J�sŒO÷ 2×1×1Ò1ús   ÁABÂB-	c                	óØ   € V P                   \        R 4      ,          V P                  ,          V,          pW P                  ,          P	                  \
        R7      V P                  ,          # )z0.95)Úrounding)rU   r   rW   rY   Úto_integral_valuer   )r!   r3   Úraws   && r   Ú_sizeÚPaperBroker._sizez   sF   € Ø�l‰lœQ˜v›YÕ&¨¯©Õ6¸Õ>ˆØ—m‘mÕ#×6Ñ6Ä
Ð6ÓKÈdÏmÉmÕ[Ð[r   Ú1c                	ó†  € V P                   '       d   \        R 4      h\        \        V4      4      pV P                  R8X  d   TM%Y!R8X  d   V P
                  MV P
                  ) ,           pV P                  V4      pWe,          pWpP                  8  d   \        RVR RV P                   24      hWpP                  ,          pV ;P                  V,          un
        \        \        V4      4      \        R4      ,          V,          p	VR8X  d   \        \        V4      4      V	,
          M\        \        V4      4      V	,           p
\        WWWV\        4       V
4      V n         V P                  \        4       RV\        V4      VR VR R	V P                  R V P                  R R
.
4       V P                  4        V P                   # )zalready in a positionr4   ÚLONGz	notional ú.2fz < venue minimum rR   ÚOPENú.4fru   rL   )r_   ÚRuntimeErrorr   r   r\   r[   r†   rZ   Ú
ValueErrorrX   rU   r   r
   r€   rV   rq   )r!   r   r3   r4   Ú
stop_boxesÚpxr   r   rX   Úoffr   s   &&&&&      r   r5   ÚPaperBroker.open   s]  € Ø�8�8ˆ8ÜÐ6Ó7Ð7Ü”#�e“*“ˆØ—i‘i 6Ô)‰U¨uÈVÄ^¸¿	º	ÐZ^×ZcÑZcÐYcÕ/dˆØ�j‰j˜‹nˆØ•8ˆØ×'Ñ'Ô'Ü˜y¨°#¨Ð6GÈ×HYÑHYÐGZÐ[Ó\Ð\ØŸ™Õ!ˆØ�Š˜Õ�Ü”�D“	‹lœQ˜w›ZÕ'¨*Õ4ˆØ%)¨V¤^Œq”�T“‹|˜cÖ!¼¼3¸t»9»ÈÕ9KˆÜ˜D r°S¼#»%ÀÓFˆŒØ�	‰	”3“5˜& $¬¨C«°R¸°HÀ#ÀcÀÈRØ—l‘l 3Ð'¨T¯\©\¸#Ð,>ÀðOô 	Pà�
‰
ŒØ�x‰xˆr   c                	óF  € V P                   '       g   \        R 4      hV P                   \        \        V4      4      rV P                  R8X  d   TM/YP
                  R8X  d   V P                  MV P                  ) ,
          pVP
                  R8X  d   \        R4      M
\        R4      pWTVP                  ,
          ,          VP                  ,          pWCP                  ,          V P                  ,          pWcP                  ,
          V,
          pV ;P                  Wg,
          ,          un        RV n         V P                  \        4       RVP
                  \        VP                  4      VR VR VR V P                  R V P                  R V.
4       V P                  4        V# )	zno positionr4   rŠ   rˆ   z-1NÚCLOSEr‹   r�   )r_   rŽ   r   r   r\   r   r[   r   r   rX   r   rU   r€   r
   rV   rq   )	r!   r3   r:   rk   r‘   Ú	directionÚgrossÚfee_cÚnets	   &&&      r   r;   ÚPaperBroker.close’   s*  € Ø�x�xˆxÜ˜}Ó-Ð-Ø—8‘8œQœs 5›z›]ˆ5Ø—i‘i 6Ô)‰U¨uÇVÁVÈvÔEU¸¿	º	Ð\`×\eÑ\eÐ[eÕ/fˆØŸf™f¨Ô.”A�c”F´A°d³Gˆ	Ø !§'¡'�\Õ*¨Q¯U©UÕ2ˆØ—U‘U•
˜TŸX™XÕ%ˆØ—j‘jÕ  5Õ(ˆØ�Š˜�Õ%�ØˆŒØ�	‰	”3“5˜' 1§6¡6¬3¨q¯u©u«:¸"¸S¸ÀuÈSÀkØ˜#�Y D§L¡L°Ð#5¸4¿<¹<ÈÐ:LÈvðWô 	Xà�
‰
ŒØˆ
r   c                	ó   € V P                   # r   )r_   r>   s   &r   r?   ÚPaperBroker.position¢   s   € Ø�x‰xˆr   Nc                óª  € V P                   '       d   V P                   P                  MRTr2W#8X  dV   T P                  \        4       RT;'       g    RRRRRV P                  R V P
                  R R.
4       RT;'       g    R R2# Vf   V'       d   R	V R
2# V'       d,   Vf(   RV P                   n        V P                  4        RV R2# RV RV R2# )z&Call on every boot. Never assume flat.NÚ	RECONCILEÚflatru   r�   zin syncz	in sync (Ú)zMISMATCH: bot wanted z, venue is flat - treat as flatTzMISMATCH: venue holds z- unknown to the bot - adopted, marked carriedzMISMATCH: venue z vs bot z - stop and fix before trading)r_   r   r€   r
   rU   rV   r   rq   )r!   rC   ÚhaveÚwants   &&  r   rD   ÚPaperBroker.reconcile¥   sÑ   € à'+§x§x x�d—h‘h—m’m°T¸MˆdØŒ<Ø�I‰I”s“u˜k¨4¯>¨>°6¸2¸rÀ2ÀrØŸ,™, sÐ+°·±¸cÐ0BÀYðPô Qà˜tŸ~˜~ vÐ.¨aÐ0Ð0ØŠ<ŸDØ*¨4¨&Ð0OÐPÐPß�D’LØ#ˆD�H‰HÔØ�J‰JŒLØ+¨D¨6Ð1^Ð_Ð_Ø! $  x°¨vÐ5SÐTÐTr   c                	ó~  € \        \        \        V4      4      \        V P                  \        R 4      4      4      pV ;P                  V,          un        V ;P
                  V,          un        V P                  \        4       RRRRRRV P                  R V P
                  R RVR 2.
4       V P                  4        V# )rQ   ÚSAVINGSru   r�   zmoved )	Úminr   r   ÚmaxrU   rV   r€   r
   rq   rG   s   &&r   rI   ÚPaperBroker.move_to_savings´   s”   € Ü”Q”s˜6“{“^¤S¨¯©´q¸³vÓ%>Ó?ˆØ�Š˜Õ�Ø�Š˜Õ�Ø�	‰	”3“5˜) R¨¨R°°R¸D¿L¹LÈÐ;MØ—l‘l 3Ð'¨F°6¸#°,Ð*?ðAô 	Bà�
‰
ŒØˆr   )rU   rX   r\   rW   r^   rZ   r_   rY   rV   r]   r[   rK   r   )r'   r(   r)   r*   rM   r   ra   r`   rq   r€   r†   r5   r;   r?   rD   rI   r,   r   r   r   rO   rO   R   sl   † ÙPá  ›x°"¹!¸C»&É1ÈWË:Ù ›f©1¨V«9¸6Ø/Ð:Lô	ò	=ò8ò
ò\ñ
 23°3³ô ô&ò ôUör   rO   c                  óf   € ] tR t^¾tRtRR^]! R4      R3R ltR tR tRR	 lt	R
 t
RR ltR tRtR# )ÚLighterBrokera³  
The real venue. Intentionally inert until wired to a SIGNED client.

Wiring it up (in this order, no shortcuts):
  1. pip install lighter-sdk
  2. read https://docs.lighter.xyz/developers -> Environments & endpoints
  3. TESTNET first: create an account, create an API key with ChangePubKey signed by
     the L1 wallet (trade/cancel only - no withdrawal rights)
  4. implement open/close as a MARKET order on the ZEC market, plus a STOP order at
     Position.stop as the disaster exit
  5. call reconcile() on boot before any strategy decision

Do not fill these in from memory. The SDK's own examples in
elliottech/lighter-python are the reference; a wrong payload here is a wrong fill.
NÚZECrQ   Tc                	ó€   € WuV n         V n        V\        \        V4      4      VuV n        V n        V n        R V n        R # r   )ÚclientÚmarketr   r   rW   rX   ÚtestnetÚ_pos)r!   r­   r®   rW   rX   r¯   s   &&&&&&r   ra   ÚLighterBroker.__init__Ï   s5   € Ø#)Ð ˆŒ�T”[Ø08¼!¼CÀ»H»+ÀwÐ-ˆŒ�t”x ¤ØˆŽ	r   c                	ó8   € V P                   f   \        R4      hR # )NzlLighterBroker has no signed client. Wire it against TESTNET first - see this docstring and OPS.md section 6.)r­   rŽ   r>   s   &r   Ú_guardÚLighterBroker._guardÔ   s#   € Ø�;‰;ÒÜð;ó<ð <ñ r   c                	ó8   € V P                  4        \        R 4      h)z:market order + stop order: implement from the SDK examples©r³   r1   r2   s   &&&&r   r5   ÚLighterBroker.openÚ   s   € Ø�‰ŒÜ!Ð"^Ó_Ð_r   c                	ó8   € V P                  4        \        R 4      h)z-market close: implement from the SDK examplesr¶   r9   s   &&&r   r;   ÚLighterBroker.closeÞ   s   € Ø�‰ŒÜ!Ð"QÓRÐRr   c                	ó   € V P                   # r   )r°   r>   s   &r   r?   ÚLighterBroker.positionâ   s   € Ø�y‰yÐr   c                	ó8   € V P                  4        \        R 4      h)z9read positions from the venue, compare with expected_sider¶   rB   s   &&r   rD   ÚLighterBroker.reconcileå   s   € Ø�‰ŒÜ!Ð"]Ó^Ð^r   c                	ó   € \        R 4      h)z…withdrawals need a withdrawal-capable path; decide the custody trade-off first (OPS.md section 6) rather than reusing the trading keyr0   rG   s   &&r   rI   ÚLighterBroker.move_to_savingsé   s   € Ü!ðEóFð 	Fr   )r°   r­   rX   rW   r®   r¯   rK   r   )r'   r(   r)   r*   rM   r   ra   r³   r5   r;   r?   rD   rI   r,   r   r   r   rª   rª   ¾   s@   † ñð  #¨5¸2Á1ÀSÃ6ÐSWô ò
<ò`ôSòô_öFr   rª   Ú__main__)rU   rW   rX   rŠ   g     ø‘@)r3   r4   gš™™™™ü‘@zheld trade closed at the line)r3   r:   z
paper PnL:z	| capitalz
| stop waszreconcile (flat as expected):ÚSHORTzreconcile (bot thought flat):zreconcile (both short):zsavings move:z0.10)rM   Ú
__future__r   rz   rg   rd   Údataclassesr   r   r   r   Údecimalr   r   r   r
   r   r.   rO   rª   r'   Úbr5   r;   ry   ÚprintrU   rD   rI   r   r   r   Ú<module>rÇ      s)  ðñõ@ #ã 
Û Û 	ß )ß 'ß ,òDð ÷Nð Nó ðN÷"ñ "ô&i�&ô iôX.F�Fô .Fðb ˆzÔÙ˜C¨"°!Ô4€AØ‡F�Fˆ6˜ w€FÔ/Ø
�'‰'˜Ð(Gˆ'Ó
H€CÙ	ˆ,˜˜[¨!¯)©)°\ÔDÙ	Ð
)¨1¯;©;°tÓ+<Ô=Ø‡F�Fˆ7˜'¨€FÔ0Ù	Ð
)¨1¯;©;°tÓ+<Ô=Ù	Ð
# Q§[¡[°Ó%9Ô:Ù	ˆ/˜1×,Ñ,©Q¨v«YÓ7Ö8ñ r   