+
    (jf                     &   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]P&                  ! RR	4      t]P&                  ! R
R4      t]! ]P&                  ! RR4      4      t]! ]P&                  ! RR4      4      t. RLOt^	t^t^t^Ft^KtRt ]P&                  ! RR4      t!]"! ]P&                  ! RR4      4      t#0 RMmt$Rt%]! ]P&                  ! RR4      4      t&]"! ]P&                  ! RR4      4      t']! ]P&                  ! RR4      4      t(^t)0 RNmt*]	! R4      t+]PX                  ! ]PZ                  RRR7       ]P\                  ! ]/4      t0R  R! lt1R" R# lt2R$ R% lt3R& R' lt4R( R) lt5ROR* R+ llt6RPR, R- llt7])3R. R/ llt8R0 R1 lt9R2 R3 lt:R4 R5 lt;R6 R7 lt<R8 R9 lt=R: R; lt>R< R= lt?R> R? lt@R@ RA ltARB RC ltBRD RE ltCRF RG ltDRH RI ltERJ tF]/RK8X  d
   ]F! 4        R# R# )Qa  
Local Paper Trading Bot
Strategy: 9/21 EMA crossover with RSI filter on 15-minute bars.
Uses yfinance for market data. All state tracked locally in JSON files.

Protections:
  - SPY 50-day MA filter: no new longs when broad market is in downtrend
  - QQQ 50-day MA filter: no new tech-stock longs when sector is in downtrend
  - 7% individual stop loss per position
  - Earnings blackout: skip entries within 3 days of a known earnings date
  - Max 5 simultaneous open positions
  - Max 20% of portfolio value in any single stock
N)datetimetimezone	timedelta)ZoneInfo)Optional)load_dotenvDISCORD_WEBHOOK_URL TRADE_LOG_FILEztrade_log.jsonPORTFOLIO_FILEzportfolio.jsonPOSITION_SIZE_USD1000STARTING_CASH10000i  SCREENER_LOG_FILEzscreener_log.jsonSCREENER_TOP_N10z9https://en.wikipedia.org/wiki/List_of_S%26P_500_companiesSTOP_LOSS_PCTz0.07MAX_POSITIONS5MAX_POSITION_PCTz0.20z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   "trading_bot.py__annotate__r!   N   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existsr   openjsonloadJSONDecodeErrorIOErrorlogwarningr   save_portfolio)finitials     r    load_portfolior3   N   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!   Z   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)r5   r1   s   & r    r0   r0   Z   s.    	nc	"	"a		)q) 
#	"	"	"s	   <A	c                `    V ^8  d   QhR\         R\         R\        R\        R\        R\         /# )r   tickeractionpriceqty
cash_after	reasoning)strfloatint)r   s   "r    r!   r!   a   s8      c 3 u 3 ,/r"   c                    R \         P                  ! \        P                  4      P	                  4       RV RVR\        V^4      RVR\        W#,          ^4      R\        V^4      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                   ! Wx^R
7       RRR4       \"        P%                  RWW#V4       R#   + '       g   i     Lt; i  \        P                  \        3 d    . p Li ; i  + '       g   i     L`; i)	timestampr<   r=   r>   r?   total_valuer@   rA   Nr7   r8   z>LOGGED  %-4s  %-6s  @ $%-10.2f  qty=%-4d  cash_remaining=$%.2f)r   nowr   utc	isoformatroundr&   r'   r(   r
   r)   r*   r+   r,   r-   appendr:   r.   info)	r<   r=   r>   r?   r@   rA   entryrecordsr1   s	   &&&&&&   r    	log_traderP   a   s    	x||HLL1;;=vvuUAsuU[!,uZ+y	E G	ww~~n%%	n%%))A, &
 NN5	nc	"	"a		'Q' 
# HHHJ &%$$g. 	G	 
#	"s<   E  *D-E  2E#-D=	8E  =E   E E #E3	c                    V ^8  d   QhR\         R\         R\        R\        R\        R\        \        ,          R\         /# )r   r<   r=   r>   r?   r@   pnlrA   )rB   rC   rD   r   )r   s   "r    r!   r!      sD     !< !<3 !< !<E !< !<$!<+3E?!<GJ!<r"   c                    \         '       g   R # VR8H  pV'       d   RMRpV'       d   RMRp	RRRR	VR
 2RR/RRR\        V4      RR/RRRR	W#,          R
 2RR/RRRR	VR
 2RR/.p
Ve   R	VR 2pV
P                  RRRV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)NBUYu   🟢u   🔴iq. i<L namePricevalue$z,.2finlineTShareszTrade ValuezCash Remainingz+,.2fzP&LSignalFembedstitle z: colorfieldsrF   )r*   timeoutzDiscord notification failed: %s)DISCORD_WEBHOOKrB   rL   r   rH   r   rI   rJ   requestspostraise_for_status	Exceptionr.   r/   )r<   r=   r>   r?   r@   rR   rA   is_buyiconr_   r`   pnl_strpayloadrespexcs   &&&&&&&        r    notify_discordrm      sn   ?u_FV6DXXE 
7ad|,<8TR	7CHHdS	7aD/A,BHdS	!7a
4/@,AHdS	F c%[/vugw$GH
MM68Wi5IJ 	D66("VH5hll3==?	
 G<}}_7BG <5s;;<s   #-D D>D99D>c                d    V ^8  d   QhR\         P                  R\        R\         P                  /# r   seriesperiodr   pdSeriesrD   )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)rp   rq   s   &&r    calc_emarz      s    ::6%:05577r"   c                d    V ^8  d   QhR\         P                  R\        R\         P                  /# ro   rr   )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)comrw   )diffcliprx   ry   )rp   rq   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                <    V ^8  d   QhR\         R\        R\        /# )r   symbolrq   r   rB   rD   bool)r   s   "r    r!   r!      s!      C  d r"   c                4    \         P                  ! V 4      P                  RRRR7      p\        V4      V8  d#   \        P                  RV \        V4      4       R# VR,          P                  V4      P                  4       P                  R
,          p\        VR,          P                  R
,          V8  4      p\        P                  RWR,          P                  R
,          WV4       V#   \         d"   p\        P                  RY4        R	p?R# R	p?ii ; i)z
True if symbol's latest daily close is above its `period`-day SMA.
Returns True on any API error so a yfinance hiccup doesn't block all trading.
60d1dTrq   intervalauto_adjustu=   fetch_ma_filter(%s): only %d days of data — skipping filterClosez4fetch_ma_filter(%s): close=%.2f  ma%d=%.2f  above=%su4   fetch_ma_filter(%s) failed: %s — permitting tradesN)yfTickerhistorylenr.   r/   rollingry   ilocr   debugrf   )r   rq   dfmaaboverl   s   &&    r    fetch_ma_filterr      s    
YYv&&edPT&Ur7VKKWY_adegahi7##F+00277;R[%%b)B./		HW+**2.E	C JFXs   AC+ BC+ +D6DDc                <    V ^8  d   QhR\         R\        R\        /# )r   r   windowr   r   )r   s   "r    r!   r!      s!      # s d r"   c           	         \         P                  ! V 4      P                  pV'       g   R# VP                  R. 4      p\	        V\
        \        34      '       g   V.p\        P                  ! \        P                  4      P                  4       pV F  p \        \        P                  ! V4      P                  4       V,
          P                  4      pWa8:  d=   \         P#                  RW\        P                  ! V4      P                  4       4        R# K  	  R#   \$         d     K  i ; i  \$         d"   p\         P'                  RY4        Rp?R# Rp?ii ; i)z
True if symbol has a known earnings date within `window` calendar days of
today (before or after). Returns False on any API error.
FzEarnings Dateu5   %s: earnings within %d day(s) (%s) — blocking entryTznear_earnings(%s): %sN)r   r   calendarget
isinstancelisttupler   rH   r   rI   dateabsrs   	Timestampdaysr.   rM   rf   r   )r   r   caldatestodaydr   rl   s   &&      r    near_earningsr      s	   
8ii((,%$//GEX\\*//1AR\\!_113e;AAB?HHT#BLLO,@,@,BD #  	   8		)6778sH   'D- A(D- A=DD- D- D*&D- )D**D- -E8EEc                0    V ^8  d   QhR\         R\        /# r   r5   r   )r   rC   )r   s   "r    r!   r!      s      4 E r"   c                    V R,          pV R,          P                  4        F   pWR,          VR,          ,          ,          pK"  	  V# )z=Cash + all open positions valued at their average entry cost.r$   r%   r?   avg_cost)values)r5   rW   poss   &  r    est_portfolio_valuer      s>    fE%,,.Uc*o-- /Lr"   c                Z    V ^8  d   QhR\         R\        \        P                  ,          /# )r   r   r   )rB   r   rs   	DataFrame)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.,          P                  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#   \         d"   p\        P                  RY4        Rp?R# Rp?ii ; i)zFReturn a DataFrame of 15-min bars with ema9, ema21, and rsi14 columns.5d15mTr   z %s: no data returned by yfinanceNr   close)columnsz%s: only %d bars (need %d+)ema9ema21rsi14u   %s: failed to fetch bars — %s)r   r   r   emptyr.   r/   renamecopyr   EMA_SLOWrz   EMA_FASTr   
RSI_PERIODrf   error)r   r<   r   rl   s   &   r    
fetch_barsr      s    6"^^4%T^J888KK:FC	]!!7G*<!=BBDr7X\!KK5vs2wSTUr'{H56
r'{H57r'{J77	 		3VAs*   ;D D A1D 	AD E$E  Ec                $    V ^8  d   QhR\         /# r   )r   )r   s   "r    r!   r!     s     0 0 0r"   c                      \         P                  ! \        4      p V P                  4       ^8  d   R# V P	                  ^	^^ ^ R7      pV P	                  ^^ ^ ^ R7      pYu;8*  ;'       d    V8  # u # )   Fhourminutesecondmicrosecond)r   rH   ETweekdayreplace)now_etmarket_openmarket_closes      r    is_market_openr     se    \\"F~~1>>q"QA>NK>>r!QA>NL//<////r"   c                $    V ^8  d   QhR\         /# r   rC   )r   s   "r    r!   r!     s     0 0e 0r"   c                    \         P                  ! \        4      p V P                  ^	^^ ^ R7      pW8:  d   V\	        ^R7      ,          pVP                  4       ^8  d   V\	        ^R7      ,          pK*  W,
          P                  4       # )z-Seconds until 9:30 AM ET on the next weekday.r   r   r   rH   r   r   r   r   total_secondsr   	candidates     r    secs_until_next_openr     sm    R FAbJIYA&&	




"YA&&	--//r"   c                $    V ^8  d   QhR\         /# r   r   )r   s   "r    r!   r!   "  s     0 0U 0r"   c                    \         P                  ! \        4      p V P                  ^	^ ^ ^ R7      pW8:  d   V\	        ^R7      ,          pVP                  4       ^8  d   V\	        ^R7      ,          pK*  W,
          P                  4       # )zJSeconds until 9:00 AM ET on the next weekday (pre-market screener window).r   r   r   r   s     r    secs_until_screenerr   "  sm    R FAaqIIYA&&	




"YA&&	--//r"   c                H    V ^8  d   QhR\         R\        R\         R\        /# r   r<   r>   rA   r5   rB   rC   r   )r   s   "r    r!   r!   /  s.     R Rc R% RC RD Rr"   c           	      .   \        \        VR ,          4      p\        WA,          4      pV^8  d&   \        P	                  RWVR ,          \        4       R# WQ,          pVR ;;,          V,          uu&   RV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$   uN   %s: price $%.2f exceeds available funds (cash=$%.2f, limit=$%.2f) — skippingNr?   r   
entry_timer%   z6BUY  %-6s  qty=%d @ $%.2f  cost=$%.2f  cash_left=$%.2frT   )minr   rD   r.   r/   rK   r   rH   r   rI   rJ   r0   rM   rP   rm   )r<   r>   rA   r5   	availabler?   costs   &&&&   r    	place_buyr   /  s    %y'89I
i
 C
Qw\9V,.?	
 	;DfceE1ohll8<<0::<&Ik6"
 9HH@U)F"3 feU6):IF65%i.?yQr"   c                H    V ^8  d   QhR\         R\        R\         R\        /# r   r   )r   s   "r    r!   r!   K  s.     R Rs R5 RS RT Rr"   c           
         VR ,          P                  V 4      pV'       g   R# VR,          pVR,          pWQ,          pWuV,          ,
          pVR;;,          V,          uu&   VR ,          V  \        V4       \        P                  RWWWR,          4       \	        V RWVR,          V4       \        V RWVR,          W4       R# )r%   Nr?   r   r$   zASELL %-6s  qty=%d @ $%.2f  proceeds=$%.2f  pnl=$%+.2f  cash=$%.2fSELL)r   r0   r.   rM   rP   rm   )	r<   r>   rA   r5   r   r?   r   proceedsrR   s	   &&&&     r    
place_sellr   K  s    
K
 
$
$V
,C5zC:H{H.(Cf!+v&9HHKUcV+< ffe)F*;YG665y/@#Qr"   c                H    V ^8  d   QhR\         R\        R\        R\        /# )r   r   r5   spy_okqqq_ok)rB   r   r   )r   s   "r    r!   r!   c  s.     ` `S `T `4 ` `r"   c                 d   \        V 4      pVe   \        V4      ^8  d   R # VR,          P                  R,          pVR,          P                  R,          pVR,          P                  R,          VR,          P                  R,          rVR,          P                  R,          VR,          P                  R,          rW8*  ;'       d    Wy8  pW8  ;'       d    Wy8  pWR,          9   pV'       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VV4       R # \        P                  RWR,          VR,          W_WyV4	       R # V'       d   V\        8  g   \        P                  RWWV4       R # V'       g   \        P                  RV 4       R # V \        9   d!   V'       g   \        P                  RV 4       R # \        VR,          4      \        8  d/   \        P                  RV \        VR,          4      \        4       R # \        V 4      '       d   R # \        \        VR,          4      p\!        V4      pV^ 8  dF   VV,          \"        8  d4   \        P                  RV VV,          ^d,          \"        ^d,          4       R # \%        WRVR RV	R RVR R\         2V4       R # ) Nr   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 zV%-6s  holding  qty=%d  avg=$%.2f  now=$%.2f  stop=$%.2f  ema9=%.2f ema21=%.2f rsi=%.1fr?   z9%-6s  no signal  ema9=%.2f ema21=%.2f rsi=%.1f bullish=%su:   %-6s  SKIP  SPY below 50-day MA — broad market downtrendu9   %-6s  SKIP  QQQ below 50-day MA — tech sector downtrendz(%-6s  SKIP  %d/%d positions already openr$   zB%-6s  SKIP  new position would be %.0f%% of portfolio (cap %.0f%%)z) crossed above 21 EMA (z < r   )r   r   r   rK   r   r   RSI_SELL_TRIGGERr.   r   RSI_BUY_MAXrM   TECH_STOCKSr   r   r   r   r   r   r   )r   r5   r   r   r   r>   rsi_ve9_nowe9_preve21_nowe21_prevbullish_crossbearish_crossin_positionr   
stop_pricereasonposition_cost	total_vals   &&&&               r    evaluater	  c  s   	F	B	zSWq[wKR EwKR E6
+bjoob.AW7((,bk.>.>r.BX(@@v/?M(@@v/?Mk22K{+F33z?a-.?@!D
 uSkZ4D E!#%c*/#j/#9NaQ	  ##E#;&?@P?QRF&%=gc] KSk# 
 vfi8 	 II0E
C
OU	 	 ek1		GGM	
 	
 MvV VLfU 9[!"m3;Y{34m	E V )9V+<=M'	2I1}25EEPMI-35E5K	
 	
&5gc] CSk[M	+	r"   c                $    V ^8  d   QhR\         /# r   r   )r   s   "r    r!   r!     s     	 	T 	r"   c                 N    \         P                  ! \        4      p V ^ ,          R,          P                  P	                  RRRR7      P                  4       p\        P                  R\        V4      4       V#   \         d#   p\        P                  RT4       . u Rp?# Rp?ii ; i)	z8Return current S&P 500 component tickers from Wikipedia.Symbol.-F)regexz)Fetched %d S&P 500 tickers from Wikipediaz#Could not fetch S&P 500 tickers: %sN)rs   	read_htmlSP500_WIKI_URLrB   r   tolistr.   rM   r   rf   r   )tablestickersrl   s      r    fetch_sp500_tickersr    s    n-)H%))11#s%1HOOQ<c'lK 		7=	s   A4A7 7B$BB$B$c                D    V ^8  d   QhR\         P                  R\        /# )r   r   r   )rs   r   rC   )r   s   "r    r!   r!     s     +7 +7bll +7u +7r"   c                   V e   \        V 4      ^#8  d   R
# V R,          pV R,          p\        V\        4      p\        V\        4      p\	        V\
        4      pRp\        ^^4       F  p\        V 4      V,
          ^,
          pV^8  d    MVP                  V,          VP                  V,          8  g   KP  VP                  V^,
          ,          VP                  V^,
          ,          8:  g   K  \        RRV^,
          ,
          4      p M	  \        VP                  R,          4      p	RT	u;8:  d   \        8  d   M MV	R,
          R,          R,          p
MRp
\        V4      ^8  d   VP                  RR MVP                  RR pVP                  4       pV^ 8  d=   \        \        VP                  R,          4      V,          R	,          R4      R,          pMRp\        Wj,           V,           ^4      # )z
Returns a 0-30 score based on:
  - EMA 9/21 crossover recency on daily bars (0-10, higher = more recent)
  - RSI proximity to 70 from below (0-10)
  - Volume vs 30-day average (0-10, capped at 3x)
Returns -1 if there is insufficient data.
N      ?r   Volume        g      $@g      >@g      D@g      @g      r   i)r   rz   r   r   r   r   ranger   maxrC   r   ry   r   rK   )r   r   volumer   r   rsi	ema_scorebars_agoirsi_val	rsi_score
vol_windowvol_avg	vol_scores   &             r    _score_tickerr(    s    
zSWr\[E\FUH%DUH%EUJ'C I!RLGh"q599Q<%**Q-'DIIa!e,<

1q5@Q,QCA!67I ! CHHRL!Gw$$t^t+d2		 ),Fr(9S$v{{3B?OJ"G{fkk"o.83>DtK		&2A66r"   c                <    V ^8  d   QhR\         R\        R\         /# )r   	watchlistscorestop_n)r   r   )r   s   "r    r!   r!     s&     B BT B4 B Br"   c                 l   R \         P                  ! \        4      P                  R4      R\         P                  ! \        P
                  4      P                  4       RT RV Uu. uF  pRVRVP                  VR4      /NK  	  upR\        \        4      R	\        V4      /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*                  ! WV^R7       R
R
R
4       \,        P/                  R\        4       R
# u upi   + '       g   i     L{; i  \         P$                  \&        3 d    . p Li ; i  + '       g   i     Lg; i)r   z%Y-%m-%drF   selected_watchlist
top_scoredr<   scorer  pinnedtotal_scoredNr7   r8   zScreener results written to %s)r   rH   r   strftimer   rI   rJ   r   sortedPINNED_TICKERSr   r&   r'   r(   r   r)   r*   r+   r,   r-   rL   r:   r.   rM   )r*  r+  r,  trN   rO   r1   s   &&&    r    _log_screener_resultsr7    s7   hll2.77
Chll8<<8BBDiSXYSXa!WfjjC6HISXYf^4c&kE G	ww~~'((	'((A))A, )
 NN5		%	%		'Q' 
&HH-/@A! Z )($$g. 	G	 
&	%sB   #E(F  #E-:F  +F#-E=	8F  =F   F F #F3	c                0    V ^8  d   QhR\         R\         /# )r   fallbackr   r  )r   s   "r    r!   r!     s     4 44 4D 4r"   c           
     j   \         P                  R4       \        4       pV'       g   \         P                  R4       V # \         P                  R\	        V4      4        \
        P                  ! VRRRRRRR	7      p/ pT F4  p Y%,          P                  RR7      p\        T4      pT^ 8  d   YtT&   K4  K6  	  \        YDP                  RR7      pTR\         p	\        \         4      p
T	 F!  pT\         9  g   K  T
P#                  T4       K#  	  \%        YT	4       \         P                  R\	        T4      \	        T
4      RP'                  T
4      4       T
#   \         d#   p\         P                  R
T4       T u Rp?# Rp?ii ; i  \         d#   p\         P                  RYS4        Rp?EK-  Rp?ii ; i)z
Download 60d daily bars for all S&P 500 tickers, score each one, and return
a watchlist of the top SCREENER_TOP_N stocks plus PINNED_TICKERS.
Falls back to `fallback` on any unrecoverable error.
u+   ═══ Daily screener starting ═══u;   Screener: no tickers fetched — keeping existing watchlistu7   Screener: downloading 60d daily bars for %d tickers …r   r   Tr<   F)rq   r   r   group_bythreadsprogressuC   Screener: batch download failed (%s) — keeping existing watchlistNall)howzScreener: skipping %s (%s))keyreverseu:   Screener done: %d candidates scored → watchlist (%d): %s, )r.   rM   r  r/   r   r   downloadrf   r   dropnar(  r   r4  __getitem__r   r   r5  rL   r7  join)r9  sp500rawrl   r+  r<   r   ssorted_tickersr,  finalr6  s   &           r    run_screenerrL    s    HH:;!EQRHHFE
Skk
 F	A###.Br"A1u!"v 	  F(:(:DIN?N+E ~&EN"LLO  %/HHDFSZ5!1 L9  		WY\]  	AII2F@@	As6   E .FF E=7F=FF2F--F2c                0    V ^8  d   QhR\         R\        /# r   )r   rB   )r   s   "r    r!   r!   S  s       # r"   c                 t    V R ,          pV'       g   R# RP                  R VP                  4        4       4      # )r%   zno open positionsrB  c              3   X   "   T F   w  rV R VR,           RVR,          R 2x  K"  	  R# 5i)   ×r?   z@$r   r   N ).0symr   s   &  r    	<genexpr>$portfolio_summary.<locals>.<genexpr>W  s5      )HC %r#e*RJ45)s   (*)rF  items)r5   r%   s   & r    portfolio_summaryrW  S  s7    +&I"99 !)  r"   c                  
   \        4       p \        \        4      pR p\        P	                  R4       \        P	                  RV R,          \        V 4      4       \        P	                  R\        V4      RP                  V4      4       \        P	                  R\        \        \        \        \        4       \        P	                  R\        ^d,          \        \        ^d,          \         4         \"        P$                  ! \&        4      pVP)                  4       ^8  dN   VP*                  ^	8  d=   W#P-                  4       8w  d)   \/        \        \        4      4      pVP-                  4       p\1        4       '       g   \3        4       pW#P-                  4       8w  d+   VP)                  4       ^8  d   \5        V\7        4       4      pMTp\5        \9        V^
,           ^<4      R4      p\        P	                  R	V^<,          V^<,          4       \:        P<                  ! V4       EK2  \?        R
4      p\?        R4      p\        P	                  RV'       d   RMRV'       d   RMR4       \        P	                  R\        V4      4       \        P	                  RV R,          \        V 4      4       V F&  p	 \A        WWx4       \:        P<                  ! R4       K(  	  \        P	                  R\F        ^<,          4       \:        P<                  ! \F        4       EK#    \B         d    \        PE                  RT	4        L{i ; i  \H         dA    \        P	                  R4       \        P	                  RT R,          \        T 4      4        R # i ; i)Nu3   ═══ Local Paper Trading Bot started ═══zCash: $%.2f  |  Positions: %sr$   zWatchlist (%d): %srB  zBStrategy: EMA %d/%d | RSI buy<%.0f sell>%.0f | position_size=$%.0fzVProtections: stop=%.0f%% | max_positions=%d | max_pos_pct=%.0f%% | earnings_window=%ddi  z8Market closed. Next open in %.0f min. Sleeping %.0f min.SPYQQQu:   Market filters — SPY 50-day MA: %s  |  QQQ 50-day MA: %sABOVEzBELOW (blocking new longs)zBELOW (blocking new tech longs)u'   ═══ Scanning %d tickers ═══zUnhandled error evaluating %sr  z#Scan complete. Next scan in %d min.zBot stopped by user.u'   Final — Cash: $%.2f  |  Positions: %s)%r3   r   	WATCHLISTr.   rM   rW  r   rF  r   r   r   r   r   r   r   r   EARNINGS_WINDOW_DAYSr   rH   r   r   r   r   rL  r   r   r   r   r  timesleepr   r	  rf   	exceptionLOOP_SECONDSKeyboardInterrupt)
r5   current_watchlistlast_screener_dater   	secs_open	wake_secs
sleep_secsr   r   r   s
             r    mainrh  ]  s   ')IiHHBCHH,i.?ARS\A]^HH!3'8#9499EV;WXHHL(K)9;L HH`],<s,BDX
3B\\"%F  1$q(*kkm;%1$y/%B!%+[[]"!##02	%66>>;Ka;O #I/B/D EI )I Y^R!8$?
NNJO 

:& %U+F$U+FHHL!'C!'H HH>DU@VWHH4i6GIZ[dIef+KV? 

3 , HH:LB<NOJJ|$ ! KMM"A6JK  B'(:6"$5i$@	BBs@   -E*L7 AL7 ,L8AL7 !L41L7 3L44L7 7ANN__main__)AAPLMSFTNVDAAMZNGOOGLMETATSLAAMDrY  rZ  NBISSOFIBE>   rt  rr  rs  >	   rq  rj  rm  ro  rk  rr  rl  rp  rn  )   )2   )G__doc__r&   r*   r^  loggingr   r   r   zoneinfor   typingr   pandasrs   yfinancer   rc   dotenvr   getenvrb   r
   r   rC   r   r   r\  r   r   r   r   r   ra  r   rD   r   r5  r  r   r   r   r]  r   r   basicConfigINFO	getLogger__name__r.   r3   r0   rP   rm   rz   r   r   r   r   r   r   r   r   r   r   r	  r  r(  r7  rL  rW  rh  rQ  r"   r    <module>r     s   
    2 2       II3R8II.0@AII.0@A"))$7@A "))OW=>	 
  II13FG 		"2D9:*O RYY?@299_c:;RYY'96BC   W !   
,,5
 !
	*B!<L8"( .B 6:000R8R0`J	+7\B04rDBN zF r"   