+
    (jJF                        R t ^ RIt^ RIt^ RIt^ RIt^ RIHtHtHt ^ RIH	t	 ^ RI
Ht ^ RIt^ RIt^ RIt^ RIHt ]! 4        ]P&                  ! RR4      tRtR	tR
t]! ]P&                  ! RR4      4      tRR.tRR^2RR/RR^RR//t^	t^t^t^Ft^Kt Rt!^t"Rt#Rt$^t%]	! R4      t&]! ^	^4      ]! ^^ 4      3]! ^^ 4      ]! ^^ 4      3.t']PP                  ! ]PR                  RRR7       ]PT                  ! ]+4      t,R R lt-R R lt.R6R R llt/R  R! lt0R" R# lt1R7R$ R% llt2]"3R& R' llt3R( R) lt4R* R+ lt5R, R- lt6R. R/ lt7R0 R1 lt8R2 R3 lt9R4 t:]+R58X  d
   ]:! 4        R# R# )8u  
Futures Paper Trading Bot
Strategy: 9/21 EMA crossover with RSI filter on 15-minute bars.
Instruments: ES=F (E-mini S&P 500), MES=F (Micro E-mini S&P 500)
All state tracked locally in JSON files.

Protections:
  - Trading-hours filter: only trade 09:30–16:00 and 18:00–23:00 ET
  - ATR volatility filter: skip entries when ATR is in the top 10% of recent values
  - 1% stop loss per position
  - Max one open futures position at a time
N)datetimetimezonetime)ZoneInfo)Optional)load_dotenvDISCORD_WEBHOOK_URL zfutures_trade_log.jsonzfutures_portfolio.jsong     @FUTURES_POSITION_SIZE_USD5000zES=FzMES=F
multipliermargin_per_contracti:  i  i  g?g{Gz?zAmerica/New_Yorkz(%(asctime)s  %(levelname)-8s %(message)sz%Y-%m-%d %H:%M:%S)levelformatdatefmtc                $    V ^8  d   QhR\         /#    returndict)r   s   "./home/david-summers/trading-bot/futures_bot.py__annotate__r   M   s     	 	 	    c                     \         P                  P                  \        4      '       d:    \	        \        4      ;_uu_ 4       p \
        P                  ! V 4      uuR R R 4       # R\        R/ /p\        V4       V#   + '       g   i     L'; i  \
        P                  \        3 d    \        P                  R\        4        Lai ; i)Nu%   Could not read %s — starting fresh.cash	positions)ospathexistsPORTFOLIO_FILEopenjsonloadJSONDecodeErrorIOErrorlogwarningSTARTING_CASHsave_portfolio)finitials     r   load_portfolior,   M   s    	ww~~n%%	Qn%%yy| &% }k26G7N &%$$g. 	QKK?P	Qs/   B A:
B :B
	B 
B 5CCc                $    V ^8  d   QhR\         /# )r   	portfolior   )r   s   "r   r   r   Y   s     * *d *r   c                     \        \        R 4      ;_uu_ 4       p\        P                  ! W^R7       RRR4       R#   + '       g   i     R# ; i)windentN)r!   r    r"   dump)r.   r*   s   & r   r)   r)   Y   s.    	nc	"	"a		)q) 
#	"	"	"s	   <A	c                    V ^8  d   QhR\         R\         R\        R\        R\        R\         R\        \        ,          /# )r   tickeractionprice	contracts
cash_after	reasoningpnlstrfloatintr   )r   s   "r   r   r   `   sD     ! !c !3 !u ! !!,/!6>uo!r   c                    \         V ,          pR \        P                  ! \        P                  4      P                  4       RT RTR\        V^4      RTRVR,          R\        W'R,          ,          V,          ^4      R\        VR,          V,          ^4      R	\        V^4      R
Ve   \        V^4      MRRV/p. p	\        P                  P                  \        4      '       d8    \        \        4      ;_uu_ 4       p
\        P                  ! V
4      p	RRR4       V	P!                  V4       \        \        R4      ;_uu_ 4       p
\        P"                  ! W^R7       RRR4       \$        P'                  RWW#W'R,          ,          V,          V4       R#   + '       g   i     L; i  \        P                  \        3 d    . p	 Li ; i  + '       g   i     Lu; i)	timestampr5   r6   r7   r8   r   notional_valuemargin_usedr   r9   r;   Nr:   r0   r1   zDLOGGED  %-4s  %-6s  @ %.2f  contracts=%d  notional=$%.0f  cash=$%.2f)FUTURES_SPECSr   nowr   utc	isoformatroundr   r   r   TRADE_LOG_FILEr!   r"   r#   r$   r%   appendr3   r&   info)r5   r6   r7   r8   r9   r:   r;   specentryrecordsr*   s   &&&&&&&    r   	log_traderO   `   sj    DHLL6@@BFFE%OID.E%|*<"<y"H!LE$'<"=	"I1ME*a0S_E#qM$IE G	ww~~n%%	n%%))A, &
 NN5	nc	"	"a		'Q' 
# HHN\""Y.
 &%$$g. 	G	 
#	"s<   #F% :FF% GF"	F% "F% %GGG	c                    V ^8  d   QhR\         R\         R\        R\        R\        R\        \        ,          R\         /# )r   r5   r6   r7   r8   r9   r;   r:   r<   )r   s   "r   r   r      sD     $< $<3 $< $<E $<c $<$$<+3E?$<GJ$<r   c                    \         '       g   R # \        V ,          pW'R,          ,          V,          pVR,          V,          p	VR8H  p
V
'       d   RMRpV
'       d   RMRpRR	R
VR RR/RRR
\        V4      RR/RRR
RVR 2RR/RRR
RV	R 2RR/RRR
RVR 2RR/.pVe   VP                  RRR
RVR 2RR/4       VP                  RRR
VRR/4       RRV RV RV  2RVRVR\        P
                  ! \        P                  4      P                  4       /./p \        P                  ! \         V^
R7      pVP                  4        R #   \         d"   p\        P                  R T4        R p?R # R p?ii ; i)!Nr   r   BUYu   🟢u   🔴iq. i<L namePricevaluez,.2finlineT	ContractszNotional Value$z,.0fzMargin UsedzCash RemainingzP&Lz+,.2fSignalFembedstitle z: colorfieldsrA   )r"   timeoutzDiscord notification failed: %s)DISCORD_WEBHOOKrD   r=   rJ   r   rE   r   rF   rG   requestspostraise_for_status	Exceptionr&   r'   )r5   r6   r7   r8   r9   r;   r:   rL   notionalmarginis_buyiconr]   r^   payloadrespexcs   &&&&&&&          r   notify_discordrl      s   ?V$DL))I5H)*Y6FFvVD!xxE 
7uTl8TR	7C	NHdS	!7a,?8TR	7at},=8TR	!7a
4/@,A8TRF vug3u+$OP
MM68Wi5IJ 	D66("VH5hll3==?	
 G<}}_7BG <5s;;<s   -E E2E--E2c                d    V ^8  d   QhR\         P                  R\        R\         P                  /# r   seriesperiodr   pdSeriesr?   )r   s   "r   r   r      s)     8 8RYY 8 8		 8r   c                 D    V P                  VR R7      P                  4       # )F)spanadjust)ewmmean)ro   rp   s   &&r   calc_emary      s    ::6%:05577r   c                d    V ^8  d   QhR\         P                  R\        R\         P                  /# rn   rq   )r   s   "r   r   r      s)     " "RYY " "RYY "r   c                 J   V P                  4       pVP                  ^ R7      pV) P                  ^ R7      pVP                  V^,
          RR7      P                  4       pVP                  V^,
          RR7      P                  4       pWV,          p^d^d^V,           ,          ,
          # )    )lowerFcomrv   )diffcliprw   rx   )ro   rp   deltagainlossavg_gainavg_lossrss   &&      r   calc_rsir      s    {{}Ezzz"D}}1}%DxxFQJux5::<HxxFQJux5::<H"B#R.!!r   c                d    V ^8  d   QhR\         P                  R\        R\         P                  /# )r   dfrp   r   )rr   	DataFramer?   rs   )r   s   "r   r   r      s)     7 7 7s 7BII 7r   c                r   V R,          P                  ^4      p\        P                  ! V R,          V R,          ,
          V R,          V,
          P                  4       V R,          V,
          P                  4       .^R7      P	                  ^R7      pVP                  V^,
          RR7      P                  4       # )z0Wilder's Average True Range using EWM smoothing.closehighlow)axisFr~   )shiftrr   concatabsmaxrw   rx   )r   rp   
prev_closetrs   &&  r   calc_atrr      s    G""1%J	
6
RY	Fj	 %%'	Ej	 %%' 	
 ss{	 
 66fqj6/4466r   c                $    V ^8  d   QhR\         /# r   )bool)r   s   "r   r   r      s     A A4 Ar   c                    a  \         P                  ! \        4      P                  4       o \        ;QJ d#    V 3R l\
         4       F  '       g   K   R# 	  R# ! V 3R l\
         4       4      # )zCTrue if the current ET time falls within an allowed trading window.c              3   T   <"   T F  w  rVSu;8*  ;'       d    V8  Mu x  K  	  R # 5iN ).0startendts   &  r   	<genexpr>$in_trading_window.<locals>.<genexpr>   s#     @-JEuC-s   %(TF)r   rE   ETr   anyTRADE_WINDOWS)r   s   @r   in_trading_windowr      sC    RA3@-@33@3@3@-@@@r   c                Z    V ^8  d   QhR\         R\        \        P                  ,          /# )r   symbolr   )r=   r   rr   r   )r   s   "r   r   r      s#      s x5 r   c                    \         P                  ! V 4      pVP                  RRRR7      pVP                  '       d   \        P                  RV 4       R# V. RO,          P                  RR
RRR	R/R7      P                  4       p\        V4      \        ^,           8  d/   \        P                  RV \        V4      \        ^,           4       R# \        VR,          \        4      VR&   \        VR,          \        4      VR&   \        VR,          \        4      VR&   \        V\        4      VR&   V#   \          d"   p\        P#                  RY4        Rp?R# Rp?ii ; i)z>Return 15-min bars with ema9, ema21, rsi14, and atr14 columns.5d15mT)rp   intervalauto_adjustz %s: no data returned by yfinanceNHighLowCloser   r   r   )columnsz%s: only %d bars (need %d+)ema9ema21rsi14atr14u   %s: failed to fetch bars — %s)r   r   r   )yfTickerhistoryemptyr&   r'   renamecopylenEMA_SLOWry   EMA_FASTr   
RSI_PERIODr   
ATR_PERIODrd   error)r   r5   r   rk   s   &   r   
fetch_barsr      s   6"^^4%T^J888KK:FC()00VUE7GD 1 

$& 	 r7X\!KK5vs2wSTUr'{H56
r'{H57r'{J77r:.7	 		3VAs*   ;D1 D1 A6D1 A"D1 1E<EEc                H    V ^8  d   QhR\         R\        R\         R\        /# r   r5   r7   r:   r.   r=   r>   r   )r   s   "r   r   r      s.     a ac a% aC aD ar   c           
         \         V ,          pVR ,          p\        \        \        VR,          4      V,          4      pV^8  d/   \        P                  RW\        \        VR,          4      4       R# We,          pVR;;,          V,          uu&   RVR\        V^4      R\        V^4      R\        P                  ! \        P                  4      P                  4       /VR,          V &   \        V4       \        P                  R	WWVR,          4       \        V R
WVR,          V4       \        V R
WVR,          RV4       R# )r   r   uW   %s: need $%.0f margin/contract but only $%.2f available in position budget — skippingNr8   	avg_pricemargin_held
entry_timer   z=BUY  %-6s  contracts=%d @ %.2f  margin=$%.0f  cash_left=$%.2frR   )rD   r?   minPOSITION_SIZE_USDr&   r'   rH   r   rE   r   rF   rG   r)   rK   rO   rl   )r5   r7   r:   r.   rL   
margin_percontracts_possiblemargin_totals   &&&&    r   	place_buyr      s   &v.D34JS!2If4EFSTAe$5y7H I	
 	%2Lf%(eE1ou\1-hll8<<0::<	&Ik6" 9HHGE69J feU	&8I9U65%Yv=NPTV_`r   c                H    V ^8  d   QhR\         R\        R\         R\        /# r   r   )r   s   "r   r   r     s.     X Xs X5 XS XT Xr   c           	         VR ,          P                  V 4      pV'       g   R# \        V ,          pVR,          pVR,          pVR,          pW,
          VR,          ,          V,          p	W,           p
VR;;,          V
,          uu&   VR ,          V  \        V4       \        P	                  RWWVR,          4       \        V RWVR,          W)4       \        V RWVR,          W4       R# )	r   Nr8   r   r   r   r   z6SELL %-6s  contracts=%d @ %.2f  pnl=$%+.2f  cash=$%.2fSELL)getrD   r)   r&   rK   rO   rl   )r5   r7   r:   r.   posrL   r8   r   rf   r;   returneds   &&&&       r   
place_sellr     s    
K
 
$
$V
,Cv&D[!I[!I]#F !T,%77)CC|Hf!+v&9HH@5y'8 ffe	&0A9R665Yv5FWr   c                0    V ^8  d   QhR\         R\        /# )r   r   r.   )r=   r   )r   s   "r   r   r   +  s     Z ZS ZT Zr   c                    \        4       '       gA   \        P                  R V \        P                  ! \
        4      P                  R4      4       R# \        V 4      pVe   \        V4      ^8  d   R# VR,          P                  R!,          pVR,          P                  R!,          p\        VR,          P                  R!,          4      pVR,          P                  R!,          VR,          P                  R",          rvVR,          P                  R!,          VR,          P                  R",          rWy8*  ;'       d    Wh8  p
Wy8  ;'       d    Wh8  pWR,          9   pV'       Ed   VR,          V ,          p\        VR	,          ^\        ,
          ,          ^4      pW>8:  d3   \        WR
VR RVR R\        ^d,          R RVR	,          R R2	V4       R# RpV\        8  d   RVR R\         2pMV'       d   RVR RVR RVR 2pV'       d   \        WW4       R# \        V ,          pW=R	,          ,
          VR,          ,          VR,          ,          p\        P                  RWR,          VR	,          W>VWVW4       R# V
'       d   V\         8  g   \        P                  RWWV
4       R# \        VR,          4      \"        8  d/   \        P%                  RV \        VR,          4      \"        4       R# \        VR,          P'                  4       P)                  \*        4      4      pVV8  d&   \        P%                  RW\*        ^d,          V4       R# \-        WRVR RVR RVR R\          RVR R 2V4       R# )#u1   %-6s  outside trading window (ET %s) — skipping%H:%MNr   r   r   r   r   r   r   zStop loss: .2fu
    ≤ stop z (z.0fz% below entry )zRSI z.1fz exceeded sell threshold z9 EMA (z) crossed below 21 EMA (z); RSI r   r8   zv%-6s  holding  contracts=%d  avg=%.2f  now=%.2f  stop=%.2f  unrealized=$%+.2f  atr=%.2f  ema9=%.2f ema21=%.2f rsi=%.1fz9%-6s  no signal  ema9=%.2f ema21=%.2f rsi=%.1f bullish=%sz;%-6s  SKIP  already holding %d futures position(s) (max %d)uH   %-6s  SKIP  ATR %.2f > %.0fth-pct threshold %.2f — elevated volatilityz) crossed above 21 EMA (z < z; ATR z within normal range)r   r&   debugr   rE   r   strftimer   r   ilocr>   rH   STOP_LOSS_PCTr   RSI_SELL_TRIGGERrD   RSI_BUY_MAXMAX_OPEN_CONTRACTSrK   dropnaquantileATR_HIGH_PERCENTILEr   )r   r.   r   r7   rsi_vatr_ve9_nowe9_preve21_nowe21_prevbullish_crossbearish_crossin_positionr   
stop_pricereasonrL   
unrealizedatr_thresholds   &&                 r   evaluater   +  sG   		E(,,r*33G<	>	F	B	zSWq[[b!E[b!E2g;##B'(E6
+bjoob.AW7((,bk.>.>r.BX(@@v/?M(@@v/?Mk22K{{+F33{+q=/@A1E
 eC[
:c2B C!#%c*.[9I#8NaQ	  ##E#;&?@P?QRF&%=gc] KSk# 
 vf8 	 'v.Dk"22d<6HH3{K[[JIIMK(#k*:EE7	 	 ek1		GGM	
 	
 9[!"&88NY{346H	J "W+,,.778KLMM}V.4m	
 	
&5gc] CSk[Mc{:N	P	r   c                0    V ^8  d   QhR\         R\        /# )r   r.   r   )r   r=   )r   s   "r   r   r     s       # r   c           	          V R ,          pV'       g   R# . pVP                  4        F-  w  r4VP                  V RVR,           RVR,          R 24       K/  	  RP                  V4      # )r   zno open positions   ×r8   @r   r   , )itemsrJ   join)r.   r   partssymr   s   &    r   portfolio_summaryr     sd    +&I"EOO%uBs;/0#k2B31GHI &99Ur   c            
         \        4       p \        P                  R 4       \        P                  RV R,          \        V 4      4       \        P                  RRP	                  \
        4      4       \        P                  R\        \        \        \        \        4       \        P                  4        F*  w  r\        P                  RWR,          VR,          4       K,  	  \        P                  R	\        ^d,          \        \        ^d,          R
P	                  R \          4       4      4         \        P                  RRP	                  \
        4      4       \        P                  RV R,          \        V 4      4       \
         F%  p \#        W04       \(        P*                  ! R4       K'  	  \        P                  R\,        ^<,          4       \(        P*                  ! \,        4       K    \$         d    \        P'                  RT4        Lzi ; i  \.         dA    \        P                  R4       \        P                  RT R,          \        T 4      4        R# i ; i)u5   ═══ Futures Paper Trading Bot started ═══zCash: $%.2f  |  Positions: %sr   zInstruments: %sr   zBStrategy: EMA %d/%d | RSI buy<%.0f sell>%.0f | position_size=$%.0fz.  %s  multiplier=%d  margin_per_contract=$%.0fr   r   zQProtections: stop=%.0f%% | max_contracts=%d | atr_filter=top_%.0fpct | windows=%sz & c              3   r   "   T F-  w  rVP                  R 4       RVP                  R 4       R2x  K/  	  R# 5i)r   u   –z ETN)r   )r   ses   &  r   r   main.<locals>.<genexpr>  s5     aS`41ajj)*#ajj.A-B#FS`s   57u)   ═══ Scanning futures (%s) ═══zUnhandled error evaluating %sg       @z#Scan complete. Next scan in %d min.zBot stopped by user.u'   Final — Cash: $%.2f  |  Positions: %sN)r,   r&   rK   r   r   	WATCHLISTr   r   r   r   r   rD   r   r   r   r   r   r   rd   	exceptionr   sleepLOOP_SECONDSKeyboardInterrupt)r.   r   rL   r   s       r   mainr    s    IHHDEHH,i.?ARS\A]^HH		) 45HHL(K)9;L #((*	<l#T*?%@	
 +
 HH[/1Ds1J

aS`aa
HH@$))IBVWHH4i6GIZ[dIef#KV/ 

3 $ HH:LB<NOJJ|$ ! KMM"A6JK  
'(5f0;	

s9   .AH 	G+AH +!HH HH AII__main__r   )   );__doc__r   r"   r   loggingr   r   dtimezoneinfor   typingr   pandasrr   yfinancer   ra   dotenvr   getenvr`   rI   r    r(   r>   r   r  rD   r   r   r   r   r   r	  r   r   r   r   r   r   basicConfigINFO	getLogger__name__r&   r,   r)   rO   rl   ry   r   r   r   r   r   r   r   r   r  r   r   r   <module>r     s   
    6 6       yy!6;//RYY'BFKL W	
 lB!6?lA!6?
 
  
   ! 1b\E"aL!
2q\E"aL!
   
,,5
 !
	*!L$<R8" .8 7A>a<X8Z~)
X zF r   