Market Watch: multiplayer GARP paper trading simulator
- Game engine with multiplayer support (create games, join, leaderboard) - GARP stock screener (S&P 500 + 400 MidCap, 900+ tickers) - Automated trading logic for AI player (Case) - Web portal at marketwatch.local:8889 with dark theme - Systemd timer for Mon-Fri market hours - Telegram alerts on trades and daily summary - Stock analysis deep dive data (BAC, CFG, FITB, INCY) - Expanded scan results (22 GARP candidates) - Craigslist account setup + credentials
This commit is contained in:
902
data/broad_tickers.txt
Normal file
902
data/broad_tickers.txt
Normal file
@ -0,0 +1,902 @@
|
||||
SLB
|
||||
ADBE
|
||||
WMG
|
||||
DT
|
||||
WBD
|
||||
ELS
|
||||
TKO
|
||||
VZ
|
||||
MTZ
|
||||
SYNA
|
||||
AZO
|
||||
FITB
|
||||
OPCH
|
||||
AVTR
|
||||
UNH
|
||||
TFC
|
||||
MANH
|
||||
LDOS
|
||||
MOS
|
||||
MTB
|
||||
SON
|
||||
CFG
|
||||
BLDR
|
||||
LOW
|
||||
TTMI
|
||||
USB
|
||||
SPGI
|
||||
AA
|
||||
CDP
|
||||
WLK
|
||||
SRE
|
||||
GDDY
|
||||
ALL
|
||||
CARR
|
||||
DECK
|
||||
SW
|
||||
ULTA
|
||||
FND
|
||||
IDA
|
||||
COST
|
||||
CSL
|
||||
DG
|
||||
CMS
|
||||
IPGP
|
||||
HOG
|
||||
PBF
|
||||
INVH
|
||||
TSCO
|
||||
MET
|
||||
FNF
|
||||
VNT
|
||||
PII
|
||||
KEYS
|
||||
PWR
|
||||
CHWY
|
||||
NDAQ
|
||||
PVH
|
||||
PRI
|
||||
MSCI
|
||||
BXP
|
||||
MSI
|
||||
ARE
|
||||
CINF
|
||||
IR
|
||||
HWC
|
||||
APA
|
||||
KRG
|
||||
AMD
|
||||
WING
|
||||
LEN
|
||||
FLR
|
||||
KKR
|
||||
USFD
|
||||
GS
|
||||
TAP
|
||||
KNF
|
||||
WWD
|
||||
EBAY
|
||||
ALLE
|
||||
YETI
|
||||
CAT
|
||||
KMPR
|
||||
DD
|
||||
CAH
|
||||
FCX
|
||||
CTSH
|
||||
RTX
|
||||
TYL
|
||||
MCO
|
||||
ABNB
|
||||
MO
|
||||
OLLI
|
||||
FICO
|
||||
MNST
|
||||
SSD
|
||||
LVS
|
||||
BMRN
|
||||
CFR
|
||||
PKG
|
||||
WYNN
|
||||
NFLX
|
||||
RSG
|
||||
WRB
|
||||
RYAN
|
||||
BHF
|
||||
JPM
|
||||
GPC
|
||||
TXRH
|
||||
AVB
|
||||
IBM
|
||||
PAYX
|
||||
HRB
|
||||
KBH
|
||||
OTIS
|
||||
FTNT
|
||||
OLN
|
||||
JCI
|
||||
DLB
|
||||
COF
|
||||
HBAN
|
||||
BSY
|
||||
COTY
|
||||
WTS
|
||||
LRCX
|
||||
AEP
|
||||
PPL
|
||||
VRTX
|
||||
WMS
|
||||
GTLS
|
||||
V
|
||||
CMC
|
||||
WH
|
||||
NRG
|
||||
CPB
|
||||
PB
|
||||
CPRI
|
||||
AXTA
|
||||
RPM
|
||||
IRT
|
||||
SYF
|
||||
HII
|
||||
PLNT
|
||||
HPE
|
||||
WFRD
|
||||
TXNM
|
||||
AMP
|
||||
ATO
|
||||
WSO
|
||||
AEE
|
||||
CL
|
||||
MMS
|
||||
HLI
|
||||
AMZN
|
||||
AMCR
|
||||
NXST
|
||||
SBRA
|
||||
CBRE
|
||||
KVUE
|
||||
HAE
|
||||
LEA
|
||||
ZBH
|
||||
PK
|
||||
SIGI
|
||||
SHC
|
||||
SLM
|
||||
OGE
|
||||
ULS
|
||||
J
|
||||
XPO
|
||||
BEN
|
||||
BRKR
|
||||
VC
|
||||
AKAM
|
||||
FHI
|
||||
MGM
|
||||
KMI
|
||||
WAB
|
||||
CBSH
|
||||
FIX
|
||||
RH
|
||||
IDXX
|
||||
RF
|
||||
GWW
|
||||
SR
|
||||
SLAB
|
||||
SWX
|
||||
INCY
|
||||
LW
|
||||
GE
|
||||
SYY
|
||||
EXPE
|
||||
MAR
|
||||
SEIC
|
||||
IT
|
||||
COIN
|
||||
RRC
|
||||
KLAC
|
||||
HOMB
|
||||
ADP
|
||||
MRNA
|
||||
BYD
|
||||
CRL
|
||||
GLW
|
||||
ROL
|
||||
VICI
|
||||
AAON
|
||||
ARMK
|
||||
CRBG
|
||||
STWD
|
||||
TOL
|
||||
MUSA
|
||||
PPG
|
||||
DY
|
||||
CG
|
||||
CBT
|
||||
OC
|
||||
VAL
|
||||
BBY
|
||||
DAR
|
||||
XOM
|
||||
VTRS
|
||||
JAZZ
|
||||
XYZ
|
||||
MEDP
|
||||
FIS
|
||||
KIM
|
||||
CHRD
|
||||
MPC
|
||||
WFC
|
||||
BDX
|
||||
IEX
|
||||
AES
|
||||
HOLX
|
||||
RL
|
||||
ACM
|
||||
CART
|
||||
BWA
|
||||
DTE
|
||||
PGR
|
||||
ITT
|
||||
ROP
|
||||
CHDN
|
||||
NBIX
|
||||
WST
|
||||
GMED
|
||||
DOW
|
||||
STLD
|
||||
SFM
|
||||
CDNS
|
||||
WTRG
|
||||
XRAY
|
||||
UAL
|
||||
WAT
|
||||
DASH
|
||||
JBL
|
||||
FFIV
|
||||
ETR
|
||||
TTC
|
||||
CPRT
|
||||
D
|
||||
THC
|
||||
IBOC
|
||||
ALGN
|
||||
ILMN
|
||||
H
|
||||
DXCM
|
||||
FISV
|
||||
MLI
|
||||
SWK
|
||||
COO
|
||||
IBKR
|
||||
KNX
|
||||
FLEX
|
||||
STX
|
||||
GEHC
|
||||
HQY
|
||||
HL
|
||||
PSN
|
||||
NTAP
|
||||
ESNT
|
||||
LPX
|
||||
CCI
|
||||
VMC
|
||||
FDX
|
||||
ERIE
|
||||
L
|
||||
TMUS
|
||||
CTRA
|
||||
COLB
|
||||
BRBR
|
||||
UFPI
|
||||
CACI
|
||||
CVS
|
||||
WPC
|
||||
RVTY
|
||||
GD
|
||||
FAF
|
||||
TNL
|
||||
ASB
|
||||
REGN
|
||||
MIDD
|
||||
COHR
|
||||
CNM
|
||||
AME
|
||||
HAL
|
||||
PTC
|
||||
AFL
|
||||
MTD
|
||||
LSCC
|
||||
MRSH
|
||||
TSN
|
||||
GILD
|
||||
HLNE
|
||||
LAD
|
||||
ADSK
|
||||
NLY
|
||||
R
|
||||
THG
|
||||
TLN
|
||||
FE
|
||||
PNFP
|
||||
UPS
|
||||
REXR
|
||||
SNDK
|
||||
MSM
|
||||
OLED
|
||||
TRU
|
||||
UNM
|
||||
IP
|
||||
HR
|
||||
COLM
|
||||
EQIX
|
||||
CSX
|
||||
ANF
|
||||
FRT
|
||||
BDC
|
||||
PSTG
|
||||
BJ
|
||||
COP
|
||||
JHG
|
||||
FCFS
|
||||
MU
|
||||
BWXT
|
||||
HIG
|
||||
INTC
|
||||
NWS
|
||||
NJR
|
||||
JKHY
|
||||
KEY
|
||||
SARO
|
||||
GM
|
||||
SCHW
|
||||
ES
|
||||
VRSN
|
||||
ON
|
||||
APG
|
||||
FTV
|
||||
HIMS
|
||||
BRO
|
||||
GOOG
|
||||
PLD
|
||||
SJM
|
||||
TRGP
|
||||
FANG
|
||||
JLL
|
||||
MTG
|
||||
STZ
|
||||
ANET
|
||||
MDLZ
|
||||
WSM
|
||||
DTM
|
||||
SWKS
|
||||
RBA
|
||||
APP
|
||||
BLD
|
||||
WBS
|
||||
QCOM
|
||||
AMT
|
||||
RGEN
|
||||
AYI
|
||||
YUM
|
||||
AMH
|
||||
OMC
|
||||
G
|
||||
SATS
|
||||
ALB
|
||||
APO
|
||||
FAST
|
||||
PSKY
|
||||
PCAR
|
||||
TTEK
|
||||
ALK
|
||||
CELH
|
||||
HD
|
||||
CRWD
|
||||
PSA
|
||||
PNC
|
||||
RRX
|
||||
LYV
|
||||
LULU
|
||||
ENSG
|
||||
AOS
|
||||
MCD
|
||||
MCHP
|
||||
ZTS
|
||||
EEFT
|
||||
GWRE
|
||||
TEX
|
||||
NEU
|
||||
CNH
|
||||
GOOGL
|
||||
ODFL
|
||||
CIEN
|
||||
TTWO
|
||||
KBR
|
||||
UTHR
|
||||
INTU
|
||||
IQV
|
||||
O
|
||||
EVR
|
||||
POR
|
||||
VNO
|
||||
FCN
|
||||
STAG
|
||||
SOLV
|
||||
GLPI
|
||||
COR
|
||||
LIVN
|
||||
MKC
|
||||
MA
|
||||
FOX
|
||||
CNXC
|
||||
Q
|
||||
MCK
|
||||
FOUR
|
||||
BC
|
||||
AON
|
||||
CMI
|
||||
CBOE
|
||||
SMG
|
||||
GPK
|
||||
BLKB
|
||||
NVDA
|
||||
PANW
|
||||
HPQ
|
||||
ATR
|
||||
DVA
|
||||
ACN
|
||||
HOOD
|
||||
NSC
|
||||
PNR
|
||||
POOL
|
||||
TT
|
||||
AIT
|
||||
CLH
|
||||
EXR
|
||||
GIS
|
||||
TRV
|
||||
CNO
|
||||
MSFT
|
||||
DLR
|
||||
ESAB
|
||||
GEN
|
||||
NVT
|
||||
WAL
|
||||
UGI
|
||||
PEP
|
||||
NWSA
|
||||
CRH
|
||||
LHX
|
||||
GAP
|
||||
PH
|
||||
LOPE
|
||||
RCL
|
||||
LNT
|
||||
WEC
|
||||
EPAM
|
||||
TSLA
|
||||
PEN
|
||||
CRS
|
||||
RYN
|
||||
SNX
|
||||
KTOS
|
||||
ADI
|
||||
GNRC
|
||||
OXY
|
||||
RJF
|
||||
ONTO
|
||||
NOC
|
||||
CXT
|
||||
TREX
|
||||
CHD
|
||||
JBHT
|
||||
RBC
|
||||
AXP
|
||||
TPL
|
||||
KNSL
|
||||
ROIV
|
||||
VST
|
||||
META
|
||||
REG
|
||||
NOV
|
||||
CVNA
|
||||
DRI
|
||||
BAX
|
||||
ASGN
|
||||
GGG
|
||||
EQT
|
||||
CHTR
|
||||
PPC
|
||||
CF
|
||||
RS
|
||||
DLTR
|
||||
STRL
|
||||
PNW
|
||||
CEG
|
||||
SBAC
|
||||
PAYC
|
||||
BBWI
|
||||
LECO
|
||||
APD
|
||||
BSX
|
||||
AMAT
|
||||
OKE
|
||||
ABT
|
||||
CAR
|
||||
COKE
|
||||
CDW
|
||||
NSA
|
||||
NDSN
|
||||
VFC
|
||||
JNJ
|
||||
NOVT
|
||||
SYK
|
||||
PATH
|
||||
LAMR
|
||||
EQH
|
||||
AMGN
|
||||
BURL
|
||||
WTFC
|
||||
HSY
|
||||
AN
|
||||
NXPI
|
||||
KO
|
||||
UBER
|
||||
CPT
|
||||
ORLY
|
||||
HLT
|
||||
WMB
|
||||
EIX
|
||||
INGR
|
||||
PFE
|
||||
CGNX
|
||||
CMCSA
|
||||
GEV
|
||||
PR
|
||||
APTV
|
||||
FLS
|
||||
PCG
|
||||
PYPL
|
||||
ELAN
|
||||
GRMN
|
||||
EGP
|
||||
DOC
|
||||
CVX
|
||||
DIS
|
||||
FR
|
||||
LNTH
|
||||
CSGP
|
||||
DUOL
|
||||
CME
|
||||
FN
|
||||
TXT
|
||||
NOW
|
||||
WHR
|
||||
CHH
|
||||
AHR
|
||||
EXLS
|
||||
PLTR
|
||||
BKR
|
||||
ASH
|
||||
HSIC
|
||||
BLK
|
||||
T
|
||||
NXT
|
||||
MOH
|
||||
XEL
|
||||
MPWR
|
||||
EFX
|
||||
AVAV
|
||||
EPR
|
||||
VVV
|
||||
DOCU
|
||||
KHC
|
||||
MAA
|
||||
EXE
|
||||
AR
|
||||
DBX
|
||||
ALLY
|
||||
MMM
|
||||
BALL
|
||||
FNB
|
||||
SAIA
|
||||
UMBF
|
||||
CAVA
|
||||
AFG
|
||||
TDY
|
||||
IFF
|
||||
CCK
|
||||
POST
|
||||
ATI
|
||||
ORI
|
||||
PINS
|
||||
SGI
|
||||
TECH
|
||||
ARW
|
||||
AAPL
|
||||
ACGL
|
||||
EL
|
||||
HST
|
||||
UBSI
|
||||
SBUX
|
||||
EA
|
||||
RLI
|
||||
C
|
||||
KDP
|
||||
PHM
|
||||
BRK.B
|
||||
CROX
|
||||
NUE
|
||||
KEX
|
||||
FLG
|
||||
MTN
|
||||
PM
|
||||
HGV
|
||||
SCI
|
||||
DKS
|
||||
LII
|
||||
MLM
|
||||
ARES
|
||||
CRM
|
||||
AJG
|
||||
ISRG
|
||||
SMCI
|
||||
EXP
|
||||
NEE
|
||||
LLY
|
||||
LSTR
|
||||
TKR
|
||||
DDOG
|
||||
DCI
|
||||
DAL
|
||||
TXN
|
||||
LMT
|
||||
ST
|
||||
DELL
|
||||
TJX
|
||||
CYTK
|
||||
GNTX
|
||||
MTCH
|
||||
BROS
|
||||
VLO
|
||||
VRSK
|
||||
SNA
|
||||
TEL
|
||||
MDT
|
||||
ACI
|
||||
MUR
|
||||
WEX
|
||||
AM
|
||||
UNP
|
||||
GTM
|
||||
MASI
|
||||
PEG
|
||||
MTDR
|
||||
CPAY
|
||||
SSB
|
||||
ENS
|
||||
OVV
|
||||
ZION
|
||||
EHC
|
||||
UDR
|
||||
BAH
|
||||
AMKR
|
||||
MORN
|
||||
STE
|
||||
VMI
|
||||
MTSI
|
||||
F
|
||||
FBIN
|
||||
EVRG
|
||||
CTAS
|
||||
AVY
|
||||
AVGO
|
||||
DOCS
|
||||
EOG
|
||||
NFG
|
||||
EG
|
||||
CNX
|
||||
VNOM
|
||||
SF
|
||||
PCTY
|
||||
BAC
|
||||
HON
|
||||
FHN
|
||||
RNR
|
||||
PEGA
|
||||
TWLO
|
||||
WDC
|
||||
OGS
|
||||
ED
|
||||
AIG
|
||||
MRK
|
||||
HUBB
|
||||
LITE
|
||||
TPR
|
||||
HRL
|
||||
EME
|
||||
AAL
|
||||
MAT
|
||||
CNC
|
||||
DOV
|
||||
TDG
|
||||
DINO
|
||||
EWBC
|
||||
BKH
|
||||
GEF
|
||||
RGLD
|
||||
ROST
|
||||
ZBRA
|
||||
BG
|
||||
WCC
|
||||
KD
|
||||
EW
|
||||
RMBS
|
||||
MS
|
||||
SHW
|
||||
NI
|
||||
KMB
|
||||
STT
|
||||
OSK
|
||||
XYL
|
||||
HXL
|
||||
ITW
|
||||
OZK
|
||||
CB
|
||||
PODD
|
||||
PG
|
||||
NVST
|
||||
CMG
|
||||
IRM
|
||||
CNP
|
||||
TER
|
||||
ENTG
|
||||
FLO
|
||||
GL
|
||||
TTD
|
||||
BR
|
||||
BA
|
||||
ROK
|
||||
CUZ
|
||||
GT
|
||||
BILL
|
||||
AXON
|
||||
MZTI
|
||||
GPN
|
||||
VLTO
|
||||
VTR
|
||||
CRUS
|
||||
FIVE
|
||||
KR
|
||||
MSA
|
||||
LYB
|
||||
BX
|
||||
ALGM
|
||||
A
|
||||
DHI
|
||||
LUV
|
||||
TMHC
|
||||
CLX
|
||||
ETN
|
||||
CLF
|
||||
BIIB
|
||||
EXEL
|
||||
SAM
|
||||
AVT
|
||||
VOYA
|
||||
ECL
|
||||
UHS
|
||||
RMD
|
||||
EXPO
|
||||
LFUS
|
||||
DVN
|
||||
BK
|
||||
M
|
||||
TMO
|
||||
SAIC
|
||||
OKTA
|
||||
LH
|
||||
NKE
|
||||
GATX
|
||||
KRC
|
||||
RGA
|
||||
TROW
|
||||
CR
|
||||
BMY
|
||||
ESS
|
||||
GXO
|
||||
ICE
|
||||
WY
|
||||
JEF
|
||||
MP
|
||||
WELL
|
||||
FTI
|
||||
IVZ
|
||||
EMR
|
||||
LIN
|
||||
HUM
|
||||
BKNG
|
||||
ELF
|
||||
FOXA
|
||||
HAS
|
||||
CI
|
||||
TRMB
|
||||
AEIS
|
||||
GHC
|
||||
CTVA
|
||||
EQR
|
||||
AGCO
|
||||
GME
|
||||
WM
|
||||
NCLH
|
||||
GBCI
|
||||
HWM
|
||||
AMG
|
||||
DE
|
||||
BCO
|
||||
ONB
|
||||
CASY
|
||||
ABBV
|
||||
HALO
|
||||
DHR
|
||||
WTW
|
||||
ORCL
|
||||
MAS
|
||||
PFG
|
||||
WMT
|
||||
SNPS
|
||||
ALV
|
||||
NYT
|
||||
FDS
|
||||
MKSI
|
||||
CCL
|
||||
PRU
|
||||
URI
|
||||
TCBI
|
||||
BRX
|
||||
BF.B
|
||||
EXC
|
||||
WDAY
|
||||
FFIN
|
||||
AWK
|
||||
CUBE
|
||||
NTNX
|
||||
CAG
|
||||
NWE
|
||||
ADC
|
||||
APPF
|
||||
DUK
|
||||
SLGN
|
||||
THO
|
||||
CW
|
||||
SPG
|
||||
NEM
|
||||
CSCO
|
||||
PAG
|
||||
APH
|
||||
FSLR
|
||||
VLY
|
||||
DPZ
|
||||
EXPD
|
||||
PFGC
|
||||
PSX
|
||||
AVNT
|
||||
CHE
|
||||
OHI
|
||||
HCA
|
||||
CHRW
|
||||
TGT
|
||||
SPXC
|
||||
ADM
|
||||
NVR
|
||||
BIO
|
||||
QLYS
|
||||
ELV
|
||||
SO
|
||||
DGX
|
||||
ORA
|
||||
AIZ
|
||||
CVLT
|
||||
NNN
|
||||
NTRS
|
||||
310
data/garp-expanded-scan.json
Normal file
310
data/garp-expanded-scan.json
Normal file
@ -0,0 +1,310 @@
|
||||
[
|
||||
{
|
||||
"ticker": "ALLY",
|
||||
"name": "Ally Financial Inc.",
|
||||
"market_cap_B": 13.1,
|
||||
"trailing_pe": 17.85,
|
||||
"forward_pe": 6.7,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 12.0,
|
||||
"earnings_growth_pct": 265.4,
|
||||
"roe_pct": 5.8,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Credit Services"
|
||||
},
|
||||
{
|
||||
"ticker": "WAL",
|
||||
"name": "Western Alliance Bancorporation",
|
||||
"market_cap_B": 10.4,
|
||||
"trailing_pe": 10.81,
|
||||
"forward_pe": 7.93,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 16.6,
|
||||
"earnings_growth_pct": 32.9,
|
||||
"roe_pct": 13.5,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "CART",
|
||||
"name": "Maplebear Inc.",
|
||||
"market_cap_B": 9.1,
|
||||
"trailing_pe": 19.03,
|
||||
"forward_pe": 8.84,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 10.2,
|
||||
"earnings_growth_pct": 21.1,
|
||||
"roe_pct": 15.3,
|
||||
"debt_to_equity": 1.0,
|
||||
"sector": "Consumer Cyclical",
|
||||
"industry": "Internet Retail"
|
||||
},
|
||||
{
|
||||
"ticker": "ONB",
|
||||
"name": "Old National Bancorp",
|
||||
"market_cap_B": 10.1,
|
||||
"trailing_pe": 14.46,
|
||||
"forward_pe": 9.02,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 41.4,
|
||||
"earnings_growth_pct": 17.2,
|
||||
"roe_pct": 9.0,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "VLY",
|
||||
"name": "Valley National Bancorp",
|
||||
"market_cap_B": 7.6,
|
||||
"trailing_pe": 13.57,
|
||||
"forward_pe": 9.19,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 38.3,
|
||||
"earnings_growth_pct": 66.3,
|
||||
"roe_pct": 7.8,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "FSLR",
|
||||
"name": "First Solar, Inc.",
|
||||
"market_cap_B": 23.5,
|
||||
"trailing_pe": 16.77,
|
||||
"forward_pe": 9.4,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 79.7,
|
||||
"earnings_growth_pct": 45.7,
|
||||
"roe_pct": 16.9,
|
||||
"debt_to_equity": 9.9,
|
||||
"sector": "Technology",
|
||||
"industry": "Solar"
|
||||
},
|
||||
{
|
||||
"ticker": "FNB",
|
||||
"name": "F.N.B. Corporation",
|
||||
"market_cap_B": 6.8,
|
||||
"trailing_pe": 12.12,
|
||||
"forward_pe": 9.66,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 26.4,
|
||||
"earnings_growth_pct": 56.5,
|
||||
"roe_pct": 8.7,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "WBS",
|
||||
"name": "Webster Financial Corporation",
|
||||
"market_cap_B": 11.8,
|
||||
"trailing_pe": 12.39,
|
||||
"forward_pe": 9.77,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 18.2,
|
||||
"earnings_growth_pct": 53.4,
|
||||
"roe_pct": 10.8,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "ZION",
|
||||
"name": "Zions Bancorporation, National Association",
|
||||
"market_cap_B": 9.6,
|
||||
"trailing_pe": 10.86,
|
||||
"forward_pe": 9.99,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 13.6,
|
||||
"earnings_growth_pct": 31.4,
|
||||
"roe_pct": 13.5,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "JHG",
|
||||
"name": "Janus Henderson Group plc",
|
||||
"market_cap_B": 7.4,
|
||||
"trailing_pe": 9.22,
|
||||
"forward_pe": 10.12,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 61.3,
|
||||
"earnings_growth_pct": 243.6,
|
||||
"roe_pct": 16.2,
|
||||
"debt_to_equity": 6.5,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Asset Management"
|
||||
},
|
||||
{
|
||||
"ticker": "SSB",
|
||||
"name": "SouthState Bank Corporation",
|
||||
"market_cap_B": 10.8,
|
||||
"trailing_pe": 13.7,
|
||||
"forward_pe": 10.19,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 53.2,
|
||||
"earnings_growth_pct": 30.9,
|
||||
"roe_pct": 10.7,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "PINS",
|
||||
"name": "Pinterest, Inc.",
|
||||
"market_cap_B": 13.3,
|
||||
"trailing_pe": 6.88,
|
||||
"forward_pe": 10.37,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 16.8,
|
||||
"earnings_growth_pct": 225.0,
|
||||
"roe_pct": 51.5,
|
||||
"debt_to_equity": 4.3,
|
||||
"sector": "Communication Services",
|
||||
"industry": "Internet Content & Information"
|
||||
},
|
||||
{
|
||||
"ticker": "RRC",
|
||||
"name": "Range Resources Corporation",
|
||||
"market_cap_B": 8.7,
|
||||
"trailing_pe": 15.37,
|
||||
"forward_pe": 10.45,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 16.1,
|
||||
"earnings_growth_pct": 189.8,
|
||||
"roe_pct": 14.2,
|
||||
"debt_to_equity": 32.7,
|
||||
"sector": "Energy",
|
||||
"industry": "Oil & Gas E&P"
|
||||
},
|
||||
{
|
||||
"ticker": "EWBC",
|
||||
"name": "East West Bancorp, Inc.",
|
||||
"market_cap_B": 16.9,
|
||||
"trailing_pe": 12.87,
|
||||
"forward_pe": 11.18,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 21.6,
|
||||
"earnings_growth_pct": 21.3,
|
||||
"roe_pct": 15.9,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "FHN",
|
||||
"name": "First Horizon Corporation",
|
||||
"market_cap_B": 12.9,
|
||||
"trailing_pe": 14.03,
|
||||
"forward_pe": 11.19,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 23.7,
|
||||
"earnings_growth_pct": 74.9,
|
||||
"roe_pct": 10.9,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "ORI",
|
||||
"name": "Old Republic International Corporation",
|
||||
"market_cap_B": 10.3,
|
||||
"trailing_pe": 11.23,
|
||||
"forward_pe": 11.99,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 19.3,
|
||||
"earnings_growth_pct": 97.3,
|
||||
"roe_pct": 16.3,
|
||||
"debt_to_equity": 26.8,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Insurance - Property & Casualty"
|
||||
},
|
||||
{
|
||||
"ticker": "WTFC",
|
||||
"name": "Wintrust Financial Corporation",
|
||||
"market_cap_B": 10.8,
|
||||
"trailing_pe": 14.14,
|
||||
"forward_pe": 12.03,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 10.5,
|
||||
"earnings_growth_pct": 19.4,
|
||||
"roe_pct": 12.1,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "UBSI",
|
||||
"name": "United Bankshares, Inc.",
|
||||
"market_cap_B": 6.3,
|
||||
"trailing_pe": 13.92,
|
||||
"forward_pe": 12.08,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 22.1,
|
||||
"earnings_growth_pct": 32.1,
|
||||
"roe_pct": 8.9,
|
||||
"debt_to_equity": null,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Banks - Regional"
|
||||
},
|
||||
{
|
||||
"ticker": "PGR",
|
||||
"name": "The Progressive Corporation",
|
||||
"market_cap_B": 118.6,
|
||||
"trailing_pe": 10.51,
|
||||
"forward_pe": 12.49,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 12.2,
|
||||
"earnings_growth_pct": 25.2,
|
||||
"roe_pct": 40.4,
|
||||
"debt_to_equity": 22.7,
|
||||
"sector": "Financial Services",
|
||||
"industry": "Insurance - Property & Casualty"
|
||||
},
|
||||
{
|
||||
"ticker": "EXEL",
|
||||
"name": "Exelixis, Inc.",
|
||||
"market_cap_B": 11.8,
|
||||
"trailing_pe": 18.45,
|
||||
"forward_pe": 12.79,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 10.8,
|
||||
"earnings_growth_pct": 72.5,
|
||||
"roe_pct": 30.6,
|
||||
"debt_to_equity": 8.2,
|
||||
"sector": "Healthcare",
|
||||
"industry": "Biotechnology"
|
||||
},
|
||||
{
|
||||
"ticker": "NEM",
|
||||
"name": "Newmont Corporation",
|
||||
"market_cap_B": 126.7,
|
||||
"trailing_pe": 17.93,
|
||||
"forward_pe": 12.89,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 20.0,
|
||||
"earnings_growth_pct": 108.1,
|
||||
"roe_pct": 22.9,
|
||||
"debt_to_equity": 16.9,
|
||||
"sector": "Basic Materials",
|
||||
"industry": "Gold"
|
||||
},
|
||||
{
|
||||
"ticker": "CTRA",
|
||||
"name": "Coterra Energy Inc.",
|
||||
"market_cap_B": 23.3,
|
||||
"trailing_pe": 14.19,
|
||||
"forward_pe": 13.95,
|
||||
"peg": null,
|
||||
"revenue_growth_pct": 34.9,
|
||||
"earnings_growth_pct": 23.5,
|
||||
"roe_pct": 11.9,
|
||||
"debt_to_equity": 28.0,
|
||||
"sector": "Energy",
|
||||
"industry": "Oil & Gas E&P"
|
||||
}
|
||||
]
|
||||
74
data/garp_scan.py
Normal file
74
data/garp_scan.py
Normal file
@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""GARP scan on broad ticker list."""
|
||||
import yfinance as yf
|
||||
import json, time
|
||||
|
||||
EXCLUDE = {"BAC", "CFG", "FITB", "INCY"}
|
||||
|
||||
with open("broad_tickers.txt") as f:
|
||||
tickers = [l.strip().replace(".", "-") for l in f if l.strip()]
|
||||
|
||||
print(f"Screening {len(tickers)} tickers...")
|
||||
passed = []
|
||||
|
||||
for i, tick in enumerate(tickers):
|
||||
if tick in EXCLUDE:
|
||||
continue
|
||||
if i % 100 == 0:
|
||||
print(f" Progress: {i}/{len(tickers)} ({len(passed)} passed so far)")
|
||||
try:
|
||||
t = yf.Ticker(tick)
|
||||
info = t.info or {}
|
||||
|
||||
mc = info.get("marketCap", 0) or 0
|
||||
if mc < 5e9: continue
|
||||
|
||||
tpe = info.get("trailingPE")
|
||||
if not tpe or tpe >= 25 or tpe <= 0: continue
|
||||
|
||||
fpe = info.get("forwardPE")
|
||||
if not fpe or fpe >= 15 or fpe <= 0: continue
|
||||
|
||||
rg = info.get("revenueGrowth")
|
||||
if rg is None or rg < 0.10: continue
|
||||
|
||||
eg = info.get("earningsGrowth")
|
||||
if eg is None or eg < 0.15: continue
|
||||
|
||||
roe = info.get("returnOnEquity")
|
||||
if roe is None or roe < 0.05: continue
|
||||
|
||||
peg = info.get("pegRatio")
|
||||
if peg is not None and peg > 1.2: continue
|
||||
|
||||
dte = info.get("debtToEquity")
|
||||
if dte is not None and dte > 35: continue
|
||||
|
||||
entry = {
|
||||
"ticker": tick,
|
||||
"name": info.get("longName", tick),
|
||||
"market_cap_B": round(mc / 1e9, 1),
|
||||
"trailing_pe": round(tpe, 2),
|
||||
"forward_pe": round(fpe, 2),
|
||||
"peg": round(peg, 2) if peg else None,
|
||||
"revenue_growth_pct": round(rg * 100, 1),
|
||||
"earnings_growth_pct": round(eg * 100, 1),
|
||||
"roe_pct": round(roe * 100, 1),
|
||||
"debt_to_equity": round(dte, 1) if dte else None,
|
||||
"sector": info.get("sector"),
|
||||
"industry": info.get("industry"),
|
||||
}
|
||||
passed.append(entry)
|
||||
print(f" ✅ {tick}: PE={tpe:.1f} FPE={fpe:.1f} RG={rg*100:.0f}% EG={eg*100:.0f}% ROE={roe*100:.0f}%")
|
||||
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
passed.sort(key=lambda x: x.get("forward_pe", 99))
|
||||
|
||||
with open("garp-expanded-scan.json", "w") as f:
|
||||
json.dump(passed, f, indent=2)
|
||||
|
||||
print(f"\nDone! {len(passed)} stocks passed GARP screen")
|
||||
for s in passed:
|
||||
print(f" {s['ticker']:6s} ${s['market_cap_B']:6.1f}B PE:{s['trailing_pe']:5.1f} FPE:{s['forward_pe']:5.1f} RG:{s['revenue_growth_pct']:5.1f}% EG:{s['earnings_growth_pct']:5.1f}% ROE:{s['roe_pct']:5.1f}%")
|
||||
85
data/garp_scan2.py
Normal file
85
data/garp_scan2.py
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""GARP scan - batch download approach for speed."""
|
||||
import yfinance as yf
|
||||
import json, sys
|
||||
|
||||
sys.stdout.reconfigure(line_buffering=True)
|
||||
|
||||
EXCLUDE = {"BAC", "CFG", "FITB", "INCY"}
|
||||
|
||||
with open("broad_tickers.txt") as f:
|
||||
tickers = [l.strip().replace(".", "-") for l in f if l.strip()]
|
||||
|
||||
print(f"Screening {len(tickers)} tickers in batches...")
|
||||
|
||||
passed = []
|
||||
batch_size = 20
|
||||
|
||||
for batch_start in range(0, len(tickers), batch_size):
|
||||
batch = tickers[batch_start:batch_start + batch_size]
|
||||
batch = [t for t in batch if t not in EXCLUDE]
|
||||
if not batch:
|
||||
continue
|
||||
|
||||
print(f"Batch {batch_start}-{batch_start+len(batch)} / {len(tickers)}...")
|
||||
|
||||
try:
|
||||
data = yf.Tickers(" ".join(batch))
|
||||
for tick in batch:
|
||||
try:
|
||||
info = data.tickers[tick].info or {}
|
||||
|
||||
mc = info.get("marketCap", 0) or 0
|
||||
if mc < 5e9: continue
|
||||
|
||||
tpe = info.get("trailingPE")
|
||||
if not tpe or tpe >= 25 or tpe <= 0: continue
|
||||
|
||||
fpe = info.get("forwardPE")
|
||||
if not fpe or fpe >= 15 or fpe <= 0: continue
|
||||
|
||||
rg = info.get("revenueGrowth")
|
||||
if rg is None or rg < 0.10: continue
|
||||
|
||||
eg = info.get("earningsGrowth")
|
||||
if eg is None or eg < 0.15: continue
|
||||
|
||||
roe = info.get("returnOnEquity")
|
||||
if roe is None or roe < 0.05: continue
|
||||
|
||||
peg = info.get("pegRatio")
|
||||
if peg is not None and peg > 1.2: continue
|
||||
|
||||
dte = info.get("debtToEquity")
|
||||
if dte is not None and dte > 35: continue
|
||||
|
||||
entry = {
|
||||
"ticker": tick,
|
||||
"name": info.get("longName", tick),
|
||||
"market_cap_B": round(mc / 1e9, 1),
|
||||
"trailing_pe": round(tpe, 2),
|
||||
"forward_pe": round(fpe, 2),
|
||||
"peg": round(peg, 2) if peg else None,
|
||||
"revenue_growth_pct": round(rg * 100, 1),
|
||||
"earnings_growth_pct": round(eg * 100, 1),
|
||||
"roe_pct": round(roe * 100, 1),
|
||||
"debt_to_equity": round(dte, 1) if dte else None,
|
||||
"sector": info.get("sector"),
|
||||
"industry": info.get("industry"),
|
||||
}
|
||||
passed.append(entry)
|
||||
print(f" ✅ {tick}: PE={tpe:.1f} FPE={fpe:.1f} RG={rg*100:.0f}% EG={eg*100:.0f}% ROE={roe*100:.0f}%")
|
||||
except:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f" Batch error: {e}")
|
||||
continue
|
||||
|
||||
passed.sort(key=lambda x: x.get("forward_pe", 99))
|
||||
|
||||
with open("garp-expanded-scan.json", "w") as f:
|
||||
json.dump(passed, f, indent=2)
|
||||
|
||||
print(f"\nDone! {len(passed)} stocks passed GARP screen")
|
||||
for s in passed:
|
||||
print(f" {s['ticker']:6s} ${s['market_cap_B']:6.1f}B PE:{s['trailing_pe']:5.1f} FPE:{s['forward_pe']:5.1f} RG:{s['revenue_growth_pct']:5.1f}% EG:{s['earnings_growth_pct']:5.1f}% ROE:{s['roe_pct']:5.1f}%")
|
||||
762
data/stock-analysis-deep-dive.json
Normal file
762
data/stock-analysis-deep-dive.json
Normal file
@ -0,0 +1,762 @@
|
||||
{
|
||||
"BAC": {
|
||||
"ticker": "BAC",
|
||||
"name": "Bank of America Corporation",
|
||||
"price_action": {
|
||||
"current": 56.53,
|
||||
"1yr_ago": 46.33,
|
||||
"1yr_return_pct": 22.02,
|
||||
"52wk_high": 57.25,
|
||||
"52wk_low": 33.82,
|
||||
"pct_from_52wk_high": -1.26,
|
||||
"50d_ma": 54.25,
|
||||
"200d_ma": 49.18,
|
||||
"above_50d_ma": true,
|
||||
"above_200d_ma": true
|
||||
},
|
||||
"insider_transactions": [
|
||||
{
|
||||
"date": "2026-01-15 00:00:00",
|
||||
"insider": "MOYNIHAN BRIAN T",
|
||||
"transaction": "",
|
||||
"shares": "17891",
|
||||
"value": "nan"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-15 00:00:00",
|
||||
"insider": "MOYNIHAN BRIAN T",
|
||||
"transaction": "",
|
||||
"shares": "17892",
|
||||
"value": "nan"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-11 00:00:00",
|
||||
"insider": "MOYNIHAN BRIAN T",
|
||||
"transaction": "",
|
||||
"shares": "130000",
|
||||
"value": "0.0"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-09 00:00:00",
|
||||
"insider": "GREENER GEOFFREY S",
|
||||
"transaction": "",
|
||||
"shares": "9265",
|
||||
"value": "0.0"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-03 00:00:00",
|
||||
"insider": "SCRIVENER THOMAS M",
|
||||
"transaction": "",
|
||||
"shares": "3000",
|
||||
"value": "0.0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-28 00:00:00",
|
||||
"insider": "OKPARA JOHNBULL",
|
||||
"transaction": "",
|
||||
"shares": "26784",
|
||||
"value": "nan"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-14 00:00:00",
|
||||
"insider": "MOYNIHAN BRIAN T",
|
||||
"transaction": "",
|
||||
"shares": "17891",
|
||||
"value": "nan"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-14 00:00:00",
|
||||
"insider": "SCHIMPF ERIC A",
|
||||
"transaction": "",
|
||||
"shares": "1234",
|
||||
"value": "nan"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-14 00:00:00",
|
||||
"insider": "HANS LINDSAY D",
|
||||
"transaction": "",
|
||||
"shares": "975",
|
||||
"value": "nan"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-14 00:00:00",
|
||||
"insider": "GOPALKRISHNAN HARI",
|
||||
"transaction": "",
|
||||
"shares": "2703",
|
||||
"value": "nan"
|
||||
}
|
||||
],
|
||||
"top_institutional_holders": [
|
||||
{
|
||||
"holder": "Vanguard Group Inc",
|
||||
"shares": 651076825,
|
||||
"pct_held": "0.0892"
|
||||
},
|
||||
{
|
||||
"holder": "Berkshire Hathaway, Inc",
|
||||
"shares": 568070012,
|
||||
"pct_held": "0.077800006"
|
||||
},
|
||||
{
|
||||
"holder": "Blackrock Inc.",
|
||||
"shares": 535326028,
|
||||
"pct_held": "0.0733"
|
||||
},
|
||||
{
|
||||
"holder": "JPMORGAN CHASE & CO",
|
||||
"shares": 363341282,
|
||||
"pct_held": "0.0498"
|
||||
},
|
||||
{
|
||||
"holder": "State Street Corporation",
|
||||
"shares": 301312466,
|
||||
"pct_held": "0.041300002"
|
||||
}
|
||||
],
|
||||
"institutional_pct": 0.71739,
|
||||
"earnings": [
|
||||
{
|
||||
"date": "2026-04-15 09:00:00-04:00",
|
||||
"eps_estimate": 0.99,
|
||||
"eps_actual": null,
|
||||
"surprise_pct": null
|
||||
},
|
||||
{
|
||||
"date": "2026-01-14 06:00:00-05:00",
|
||||
"eps_estimate": 0.96,
|
||||
"eps_actual": 0.98,
|
||||
"surprise_pct": 2.23
|
||||
},
|
||||
{
|
||||
"date": "2025-10-15 06:00:00-04:00",
|
||||
"eps_estimate": 0.95,
|
||||
"eps_actual": 1.06,
|
||||
"surprise_pct": 11.43
|
||||
},
|
||||
{
|
||||
"date": "2025-07-16 06:00:00-04:00",
|
||||
"eps_estimate": 0.86,
|
||||
"eps_actual": 0.89,
|
||||
"surprise_pct": 3.61
|
||||
},
|
||||
{
|
||||
"date": "2025-04-15 06:00:00-04:00",
|
||||
"eps_estimate": 0.82,
|
||||
"eps_actual": 0.9,
|
||||
"surprise_pct": 10.29
|
||||
},
|
||||
{
|
||||
"date": "2025-01-16 06:00:00-05:00",
|
||||
"eps_estimate": 0.77,
|
||||
"eps_actual": 0.82,
|
||||
"surprise_pct": 6.84
|
||||
},
|
||||
{
|
||||
"date": "2024-10-15 06:00:00-04:00",
|
||||
"eps_estimate": 0.76,
|
||||
"eps_actual": 0.81,
|
||||
"surprise_pct": 6.34
|
||||
},
|
||||
{
|
||||
"date": "2024-07-16 06:00:00-04:00",
|
||||
"eps_estimate": 0.8,
|
||||
"eps_actual": 0.83,
|
||||
"surprise_pct": 3.58
|
||||
}
|
||||
],
|
||||
"analyst": {
|
||||
"target_mean": 62.20833,
|
||||
"target_low": 56.0,
|
||||
"target_high": 71.0,
|
||||
"recommendation": "buy",
|
||||
"num_analysts": 24,
|
||||
"upside_pct": 10.04
|
||||
},
|
||||
"fundamentals": {
|
||||
"trailing_pe": 14.83727,
|
||||
"forward_pe": 11.407365,
|
||||
"peg_ratio": null,
|
||||
"market_cap": 412810084352,
|
||||
"revenue_growth": 0.132,
|
||||
"earnings_growth": 0.209,
|
||||
"roe": 0.1019,
|
||||
"debt_to_equity": null,
|
||||
"dividend_yield": 1.95,
|
||||
"beta": 1.273
|
||||
},
|
||||
"technical": {
|
||||
"rsi_14": 71.14,
|
||||
"trend": "bullish"
|
||||
}
|
||||
},
|
||||
"CFG": {
|
||||
"ticker": "CFG",
|
||||
"name": "Citizens Financial Group, Inc.",
|
||||
"price_action": {
|
||||
"current": 68.12,
|
||||
"1yr_ago": 46.24,
|
||||
"1yr_return_pct": 47.32,
|
||||
"52wk_high": 68.12,
|
||||
"52wk_low": 33.06,
|
||||
"pct_from_52wk_high": 0.0,
|
||||
"50d_ma": 59.58,
|
||||
"200d_ma": 49.69,
|
||||
"above_50d_ma": true,
|
||||
"above_200d_ma": true
|
||||
},
|
||||
"insider_transactions": [
|
||||
{
|
||||
"date": "2025-12-11 00:00:00",
|
||||
"insider": "VAN SAUN BRUCE W",
|
||||
"transaction": "",
|
||||
"shares": "8000",
|
||||
"value": "458558"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-03 00:00:00",
|
||||
"insider": "LAMONICA SUSAN",
|
||||
"transaction": "",
|
||||
"shares": "20128",
|
||||
"value": "1120727"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "KELLY EDWARD J. III",
|
||||
"transaction": "",
|
||||
"shares": "316",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "HANKOWSKY WILLIAM P",
|
||||
"transaction": "",
|
||||
"shares": "347",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "ZURAITIS MARITA",
|
||||
"transaction": "",
|
||||
"shares": "347",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "CUMMING CHRISTINE M",
|
||||
"transaction": "",
|
||||
"shares": "347",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "ATKINSON TRACY A",
|
||||
"transaction": "",
|
||||
"shares": "85",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "CUMMINGS KEVIN",
|
||||
"transaction": "",
|
||||
"shares": "162",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "SWIFT CHRISTOPHER J",
|
||||
"transaction": "",
|
||||
"shares": "200",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-11-12 00:00:00",
|
||||
"insider": "SIEKERKA MICHELE N",
|
||||
"transaction": "",
|
||||
"shares": "162",
|
||||
"value": "0"
|
||||
}
|
||||
],
|
||||
"top_institutional_holders": [
|
||||
{
|
||||
"holder": "Vanguard Group Inc",
|
||||
"shares": 51303226,
|
||||
"pct_held": "0.1195"
|
||||
},
|
||||
{
|
||||
"holder": "Blackrock Inc.",
|
||||
"shares": 43851199,
|
||||
"pct_held": "0.1021"
|
||||
},
|
||||
{
|
||||
"holder": "Capital World Investors",
|
||||
"shares": 37289711,
|
||||
"pct_held": "0.0868"
|
||||
},
|
||||
{
|
||||
"holder": "Invesco Ltd.",
|
||||
"shares": 24064513,
|
||||
"pct_held": "0.055999998"
|
||||
},
|
||||
{
|
||||
"holder": "State Street Corporation",
|
||||
"shares": 22969833,
|
||||
"pct_held": "0.0535"
|
||||
}
|
||||
],
|
||||
"institutional_pct": 0.99170995,
|
||||
"earnings": [
|
||||
{
|
||||
"date": "2026-04-16 09:00:00-04:00",
|
||||
"eps_estimate": 1.09,
|
||||
"eps_actual": null,
|
||||
"surprise_pct": null
|
||||
},
|
||||
{
|
||||
"date": "2026-01-21 06:00:00-05:00",
|
||||
"eps_estimate": 1.11,
|
||||
"eps_actual": 1.13,
|
||||
"surprise_pct": 2.24
|
||||
},
|
||||
{
|
||||
"date": "2025-10-15 06:00:00-04:00",
|
||||
"eps_estimate": 1.03,
|
||||
"eps_actual": 1.05,
|
||||
"surprise_pct": 2.27
|
||||
},
|
||||
{
|
||||
"date": "2025-07-17 06:00:00-04:00",
|
||||
"eps_estimate": 0.88,
|
||||
"eps_actual": 0.92,
|
||||
"surprise_pct": 4.13
|
||||
},
|
||||
{
|
||||
"date": "2025-04-16 06:00:00-04:00",
|
||||
"eps_estimate": 0.75,
|
||||
"eps_actual": 0.77,
|
||||
"surprise_pct": 2.68
|
||||
},
|
||||
{
|
||||
"date": "2025-01-17 06:00:00-05:00",
|
||||
"eps_estimate": 0.83,
|
||||
"eps_actual": 0.85,
|
||||
"surprise_pct": 2.3
|
||||
},
|
||||
{
|
||||
"date": "2024-10-16 06:00:00-04:00",
|
||||
"eps_estimate": 0.79,
|
||||
"eps_actual": 0.79,
|
||||
"surprise_pct": -0.24
|
||||
},
|
||||
{
|
||||
"date": "2024-07-17 03:00:00-04:00",
|
||||
"eps_estimate": 0.79,
|
||||
"eps_actual": 0.82,
|
||||
"surprise_pct": 3.5
|
||||
}
|
||||
],
|
||||
"analyst": {
|
||||
"target_mean": 72.275,
|
||||
"target_low": 62.5,
|
||||
"target_high": 80.0,
|
||||
"recommendation": "buy",
|
||||
"num_analysts": 20,
|
||||
"upside_pct": 6.1
|
||||
},
|
||||
"fundamentals": {
|
||||
"trailing_pe": 17.647669,
|
||||
"forward_pe": 10.848602,
|
||||
"peg_ratio": null,
|
||||
"market_cap": 29256599552,
|
||||
"revenue_growth": 0.107,
|
||||
"earnings_growth": 0.359,
|
||||
"roe": 0.07241,
|
||||
"debt_to_equity": null,
|
||||
"dividend_yield": 2.7,
|
||||
"beta": 1.071
|
||||
},
|
||||
"technical": {
|
||||
"rsi_14": 75.46,
|
||||
"trend": "bullish"
|
||||
}
|
||||
},
|
||||
"FITB": {
|
||||
"ticker": "FITB",
|
||||
"name": "Fifth Third Bancorp",
|
||||
"price_action": {
|
||||
"current": 55.08,
|
||||
"1yr_ago": 42.49,
|
||||
"1yr_return_pct": 29.63,
|
||||
"52wk_high": 55.08,
|
||||
"52wk_low": 32.46,
|
||||
"pct_from_52wk_high": 0.0,
|
||||
"50d_ma": 48.27,
|
||||
"200d_ma": 42.82,
|
||||
"above_50d_ma": true,
|
||||
"above_200d_ma": true
|
||||
},
|
||||
"insider_transactions": [
|
||||
{
|
||||
"date": "2026-02-02 00:00:00",
|
||||
"insider": "SMITH BARBARA",
|
||||
"transaction": "",
|
||||
"shares": "40498",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2026-02-02 00:00:00",
|
||||
"insider": "SEFZIK PETER L",
|
||||
"transaction": "",
|
||||
"shares": "209382",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2026-02-02 00:00:00",
|
||||
"insider": "KERR DEREK J",
|
||||
"transaction": "",
|
||||
"shares": "14189",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2026-02-02 00:00:00",
|
||||
"insider": "VAN DE VEN MICHAEL G",
|
||||
"transaction": "",
|
||||
"shares": "47972",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2026-01-07 00:00:00",
|
||||
"insider": "ALMODOVAR PRISCILLA",
|
||||
"transaction": "",
|
||||
"shares": "848",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-15 00:00:00",
|
||||
"insider": "PRESTON BRYAN D",
|
||||
"transaction": "",
|
||||
"shares": "1100",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-11 00:00:00",
|
||||
"insider": "SCHRAMM JUDE",
|
||||
"transaction": "",
|
||||
"shares": "2250",
|
||||
"value": "109125"
|
||||
},
|
||||
{
|
||||
"date": "2025-10-20 00:00:00",
|
||||
"insider": "BAYH B EVAN III",
|
||||
"transaction": "",
|
||||
"shares": "3000",
|
||||
"value": "123650"
|
||||
},
|
||||
{
|
||||
"date": "2025-10-09 00:00:00",
|
||||
"insider": "GONZALEZ CHRISTIAN",
|
||||
"transaction": "",
|
||||
"shares": "5709",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-08-28 00:00:00",
|
||||
"insider": "SHAFFER ROBERT P",
|
||||
"transaction": "",
|
||||
"shares": "14035",
|
||||
"value": "372208"
|
||||
}
|
||||
],
|
||||
"top_institutional_holders": [
|
||||
{
|
||||
"holder": "Vanguard Group Inc",
|
||||
"shares": 107237083,
|
||||
"pct_held": "0.16219999"
|
||||
},
|
||||
{
|
||||
"holder": "Blackrock Inc.",
|
||||
"shares": 88421716,
|
||||
"pct_held": "0.1338"
|
||||
},
|
||||
{
|
||||
"holder": "JPMORGAN CHASE & CO",
|
||||
"shares": 65878286,
|
||||
"pct_held": "0.099700004"
|
||||
},
|
||||
{
|
||||
"holder": "State Street Corporation",
|
||||
"shares": 39653081,
|
||||
"pct_held": "0.06"
|
||||
},
|
||||
{
|
||||
"holder": "Charles Schwab Investment Management, Inc.",
|
||||
"shares": 32138466,
|
||||
"pct_held": "0.048600003"
|
||||
}
|
||||
],
|
||||
"institutional_pct": 0.66008,
|
||||
"earnings": [
|
||||
{
|
||||
"date": "2026-04-17 09:00:00-04:00",
|
||||
"eps_estimate": 0.43,
|
||||
"eps_actual": null,
|
||||
"surprise_pct": null
|
||||
},
|
||||
{
|
||||
"date": "2026-01-20 06:00:00-05:00",
|
||||
"eps_estimate": 0.99,
|
||||
"eps_actual": 1.04,
|
||||
"surprise_pct": 4.93
|
||||
},
|
||||
{
|
||||
"date": "2025-10-17 06:00:00-04:00",
|
||||
"eps_estimate": 0.86,
|
||||
"eps_actual": 0.91,
|
||||
"surprise_pct": 5.47
|
||||
},
|
||||
{
|
||||
"date": "2025-07-17 06:00:00-04:00",
|
||||
"eps_estimate": 0.87,
|
||||
"eps_actual": 0.9,
|
||||
"surprise_pct": 3.85
|
||||
},
|
||||
{
|
||||
"date": "2025-04-17 06:00:00-04:00",
|
||||
"eps_estimate": 0.7,
|
||||
"eps_actual": 0.71,
|
||||
"surprise_pct": 1.42
|
||||
},
|
||||
{
|
||||
"date": "2025-01-21 06:00:00-05:00",
|
||||
"eps_estimate": 0.87,
|
||||
"eps_actual": 0.9,
|
||||
"surprise_pct": 3.01
|
||||
},
|
||||
{
|
||||
"date": "2024-10-18 06:00:00-04:00",
|
||||
"eps_estimate": 0.82,
|
||||
"eps_actual": 0.85,
|
||||
"surprise_pct": 3.17
|
||||
},
|
||||
{
|
||||
"date": "2024-07-19 06:00:00-04:00",
|
||||
"eps_estimate": 0.84,
|
||||
"eps_actual": 0.86,
|
||||
"surprise_pct": 1.79
|
||||
}
|
||||
],
|
||||
"analyst": {
|
||||
"target_mean": 57.15789,
|
||||
"target_low": 49.0,
|
||||
"target_high": 61.0,
|
||||
"recommendation": "buy",
|
||||
"num_analysts": 19,
|
||||
"upside_pct": 3.77
|
||||
},
|
||||
"fundamentals": {
|
||||
"trailing_pe": 15.6034,
|
||||
"forward_pe": 11.235359,
|
||||
"peg_ratio": null,
|
||||
"market_cap": 49574670336,
|
||||
"revenue_growth": 0.115,
|
||||
"earnings_growth": 0.208,
|
||||
"roe": 0.121929996,
|
||||
"debt_to_equity": null,
|
||||
"dividend_yield": 2.9,
|
||||
"beta": 0.977
|
||||
},
|
||||
"technical": {
|
||||
"rsi_14": 71.83,
|
||||
"trend": "bullish"
|
||||
}
|
||||
},
|
||||
"INCY": {
|
||||
"ticker": "INCY",
|
||||
"name": "Incyte Corporation",
|
||||
"price_action": {
|
||||
"current": 108.39,
|
||||
"1yr_ago": 74.13,
|
||||
"1yr_return_pct": 46.22,
|
||||
"52wk_high": 110.57,
|
||||
"52wk_low": 55.17,
|
||||
"pct_from_52wk_high": -1.97,
|
||||
"50d_ma": 101.9,
|
||||
"200d_ma": 84.49,
|
||||
"above_50d_ma": true,
|
||||
"above_200d_ma": true
|
||||
},
|
||||
"insider_transactions": [
|
||||
{
|
||||
"date": "2026-01-16 00:00:00",
|
||||
"insider": "MORRISSEY MICHAEL JAMES",
|
||||
"transaction": "",
|
||||
"shares": "7426",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2026-01-16 00:00:00",
|
||||
"insider": "HEESON LEE",
|
||||
"transaction": "",
|
||||
"shares": "8911",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2026-01-07 00:00:00",
|
||||
"insider": "ISSA MOHAMED KHAIRIE",
|
||||
"transaction": "",
|
||||
"shares": "10856",
|
||||
"value": "1184064"
|
||||
},
|
||||
{
|
||||
"date": "2026-01-05 00:00:00",
|
||||
"insider": "STEIN STEVEN H.",
|
||||
"transaction": "",
|
||||
"shares": "15634",
|
||||
"value": "1589978"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-31 00:00:00",
|
||||
"insider": "BAKER BROS ADVISORS L.P.",
|
||||
"transaction": "",
|
||||
"shares": "656",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-31 00:00:00",
|
||||
"insider": "HARRIGAN EDMUND P. M.D.",
|
||||
"transaction": "",
|
||||
"shares": "245",
|
||||
"value": "24199"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-31 00:00:00",
|
||||
"insider": "CLANCY PAUL J.",
|
||||
"transaction": "",
|
||||
"shares": "241",
|
||||
"value": "23804"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-19 00:00:00",
|
||||
"insider": "TRAY THOMAS R",
|
||||
"transaction": "",
|
||||
"shares": "3374",
|
||||
"value": "336350"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-19 00:00:00",
|
||||
"insider": "TRAY THOMAS R",
|
||||
"transaction": "",
|
||||
"shares": "2774",
|
||||
"value": "265638"
|
||||
},
|
||||
{
|
||||
"date": "2025-12-17 00:00:00",
|
||||
"insider": "MORRISSEY MICHAEL JAMES",
|
||||
"transaction": "",
|
||||
"shares": "58331",
|
||||
"value": "5675002"
|
||||
}
|
||||
],
|
||||
"top_institutional_holders": [
|
||||
{
|
||||
"holder": "Baker Bros. Advisors, LP",
|
||||
"shares": 30743663,
|
||||
"pct_held": "0.1566"
|
||||
},
|
||||
{
|
||||
"holder": "Vanguard Group Inc",
|
||||
"shares": 19911434,
|
||||
"pct_held": "0.1014"
|
||||
},
|
||||
{
|
||||
"holder": "Blackrock Inc.",
|
||||
"shares": 17894297,
|
||||
"pct_held": "0.0911"
|
||||
},
|
||||
{
|
||||
"holder": "Dodge & Cox Inc.",
|
||||
"shares": 13932416,
|
||||
"pct_held": "0.071"
|
||||
},
|
||||
{
|
||||
"holder": "State Street Corporation",
|
||||
"shares": 9676796,
|
||||
"pct_held": "0.0493"
|
||||
}
|
||||
],
|
||||
"institutional_pct": 1.05931,
|
||||
"earnings": [
|
||||
{
|
||||
"date": "2026-02-10 08:00:00-05:00",
|
||||
"eps_estimate": 1.95,
|
||||
"eps_actual": null,
|
||||
"surprise_pct": null
|
||||
},
|
||||
{
|
||||
"date": "2025-10-28 07:00:00-04:00",
|
||||
"eps_estimate": 1.64,
|
||||
"eps_actual": 2.26,
|
||||
"surprise_pct": 38.04
|
||||
},
|
||||
{
|
||||
"date": "2025-07-29 07:00:00-04:00",
|
||||
"eps_estimate": 1.47,
|
||||
"eps_actual": 1.57,
|
||||
"surprise_pct": 6.59
|
||||
},
|
||||
{
|
||||
"date": "2025-04-29 07:00:00-04:00",
|
||||
"eps_estimate": 1.03,
|
||||
"eps_actual": 1.16,
|
||||
"surprise_pct": 12.32
|
||||
},
|
||||
{
|
||||
"date": "2025-02-10 07:00:00-05:00",
|
||||
"eps_estimate": 1.55,
|
||||
"eps_actual": 1.43,
|
||||
"surprise_pct": -8.01
|
||||
},
|
||||
{
|
||||
"date": "2024-10-29 07:00:00-04:00",
|
||||
"eps_estimate": 1.09,
|
||||
"eps_actual": 1.07,
|
||||
"surprise_pct": -2.03
|
||||
},
|
||||
{
|
||||
"date": "2024-07-30 07:00:00-04:00",
|
||||
"eps_estimate": 1.11,
|
||||
"eps_actual": -1.82,
|
||||
"surprise_pct": -264.53
|
||||
},
|
||||
{
|
||||
"date": "2024-04-30 07:00:00-04:00",
|
||||
"eps_estimate": 0.83,
|
||||
"eps_actual": 0.64,
|
||||
"surprise_pct": -23.09
|
||||
}
|
||||
],
|
||||
"analyst": {
|
||||
"target_mean": 104.22727,
|
||||
"target_low": 70.0,
|
||||
"target_high": 135.0,
|
||||
"recommendation": "buy",
|
||||
"num_analysts": 22,
|
||||
"upside_pct": -3.84
|
||||
},
|
||||
"fundamentals": {
|
||||
"trailing_pe": 18.371185,
|
||||
"forward_pe": 13.606971,
|
||||
"peg_ratio": null,
|
||||
"market_cap": 21279418368,
|
||||
"revenue_growth": 0.2,
|
||||
"earnings_growth": 2.907,
|
||||
"roe": 0.30389,
|
||||
"debt_to_equity": 0.887,
|
||||
"dividend_yield": null,
|
||||
"beta": 0.847
|
||||
},
|
||||
"technical": {
|
||||
"rsi_14": 54.22,
|
||||
"trend": "bullish"
|
||||
}
|
||||
}
|
||||
}
|
||||
71
data/stock-screener-results.json
Normal file
71
data/stock-screener-results.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"date": "2026-02-08",
|
||||
"filters": "GARP",
|
||||
"results": [
|
||||
{
|
||||
"ticker": "BAC",
|
||||
"name": "Bank of America Corporation",
|
||||
"mcap_b": 412.8,
|
||||
"rev_growth": 13.2,
|
||||
"trail_pe": 14.8,
|
||||
"fwd_pe": 11.4,
|
||||
"peg": "N/A",
|
||||
"eps_growth": 20.9,
|
||||
"roe": 10.2,
|
||||
"quick": "N/A",
|
||||
"de": "N/A"
|
||||
},
|
||||
{
|
||||
"ticker": "CFG",
|
||||
"name": "Citizens Financial Group, Inc.",
|
||||
"mcap_b": 29.3,
|
||||
"rev_growth": 10.7,
|
||||
"trail_pe": 17.6,
|
||||
"fwd_pe": 10.8,
|
||||
"peg": "N/A",
|
||||
"eps_growth": 35.9,
|
||||
"roe": 7.2,
|
||||
"quick": "N/A",
|
||||
"de": "N/A"
|
||||
},
|
||||
{
|
||||
"ticker": "FITB",
|
||||
"name": "Fifth Third Bancorp",
|
||||
"mcap_b": 49.6,
|
||||
"rev_growth": 11.5,
|
||||
"trail_pe": 15.6,
|
||||
"fwd_pe": 11.2,
|
||||
"peg": "N/A",
|
||||
"eps_growth": 20.8,
|
||||
"roe": 12.2,
|
||||
"quick": "N/A",
|
||||
"de": "N/A"
|
||||
},
|
||||
{
|
||||
"ticker": "INCY",
|
||||
"name": "Incyte Corporation",
|
||||
"mcap_b": 21.3,
|
||||
"rev_growth": 20.0,
|
||||
"trail_pe": 18.4,
|
||||
"fwd_pe": 13.6,
|
||||
"peg": "N/A",
|
||||
"eps_growth": 290.7,
|
||||
"roe": 30.4,
|
||||
"quick": 2.86,
|
||||
"de": 0.9
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"scanned": 503,
|
||||
"revenue": 316,
|
||||
"pe": 121,
|
||||
"fwd_pe": 32,
|
||||
"peg": 0,
|
||||
"eps": 14,
|
||||
"roe": 1,
|
||||
"quick": 11,
|
||||
"de": 4,
|
||||
"mcap": 0,
|
||||
"err": 0
|
||||
}
|
||||
}
|
||||
295
data/stock_deep_dive.py
Normal file
295
data/stock_deep_dive.py
Normal file
@ -0,0 +1,295 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deep dive analysis on BAC, CFG, FITB, INCY + expanded GARP scan."""
|
||||
|
||||
import yfinance as yf
|
||||
import json
|
||||
import datetime
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
TARGETS = ["BAC", "CFG", "FITB", "INCY"]
|
||||
|
||||
def safe_get(d, *keys, default=None):
|
||||
for k in keys:
|
||||
if isinstance(d, dict):
|
||||
d = d.get(k, default)
|
||||
else:
|
||||
return default
|
||||
return d
|
||||
|
||||
def analyze_stock(ticker_str):
|
||||
print(f"\n{'='*60}\nAnalyzing {ticker_str}...")
|
||||
t = yf.Ticker(ticker_str)
|
||||
info = t.info or {}
|
||||
result = {"ticker": ticker_str, "name": info.get("longName", ticker_str)}
|
||||
|
||||
# 1. Price action (1yr)
|
||||
hist = t.history(period="1y")
|
||||
if not hist.empty:
|
||||
prices = hist["Close"]
|
||||
result["price_action"] = {
|
||||
"current": round(float(prices.iloc[-1]), 2),
|
||||
"1yr_ago": round(float(prices.iloc[0]), 2),
|
||||
"1yr_return_pct": round(float((prices.iloc[-1] / prices.iloc[0] - 1) * 100), 2),
|
||||
"52wk_high": round(float(prices.max()), 2),
|
||||
"52wk_low": round(float(prices.min()), 2),
|
||||
"pct_from_52wk_high": round(float((prices.iloc[-1] / prices.max() - 1) * 100), 2),
|
||||
"50d_ma": round(float(prices.tail(50).mean()), 2),
|
||||
"200d_ma": round(float(prices.tail(200).mean()), 2) if len(prices) >= 200 else None,
|
||||
}
|
||||
cur = float(prices.iloc[-1])
|
||||
ma50 = result["price_action"]["50d_ma"]
|
||||
ma200 = result["price_action"]["200d_ma"]
|
||||
result["price_action"]["above_50d_ma"] = cur > ma50
|
||||
result["price_action"]["above_200d_ma"] = cur > ma200 if ma200 else None
|
||||
|
||||
# 2. Insider activity
|
||||
try:
|
||||
insiders = t.insider_transactions
|
||||
if insiders is not None and not insiders.empty:
|
||||
recent = insiders.head(10)
|
||||
txns = []
|
||||
for _, row in recent.iterrows():
|
||||
txns.append({
|
||||
"date": str(row.get("Start Date", "")),
|
||||
"insider": str(row.get("Insider", "")),
|
||||
"transaction": str(row.get("Transaction", "")),
|
||||
"shares": str(row.get("Shares", "")),
|
||||
"value": str(row.get("Value", "")),
|
||||
})
|
||||
result["insider_transactions"] = txns
|
||||
else:
|
||||
result["insider_transactions"] = []
|
||||
except:
|
||||
result["insider_transactions"] = []
|
||||
|
||||
# 3. Institutional ownership
|
||||
try:
|
||||
inst = t.institutional_holders
|
||||
if inst is not None and not inst.empty:
|
||||
top5 = []
|
||||
for _, row in inst.head(5).iterrows():
|
||||
top5.append({
|
||||
"holder": str(row.get("Holder", "")),
|
||||
"shares": int(row.get("Shares", 0)) if row.get("Shares") else 0,
|
||||
"pct_held": str(row.get("pctHeld", "")),
|
||||
})
|
||||
result["top_institutional_holders"] = top5
|
||||
result["institutional_pct"] = info.get("heldPercentInstitutions")
|
||||
except:
|
||||
result["top_institutional_holders"] = []
|
||||
|
||||
# 4. Earnings surprises
|
||||
try:
|
||||
earn = t.earnings_dates
|
||||
if earn is not None and not earn.empty:
|
||||
surprises = []
|
||||
for idx, row in earn.head(8).iterrows():
|
||||
s = {
|
||||
"date": str(idx),
|
||||
"eps_estimate": row.get("EPS Estimate"),
|
||||
"eps_actual": row.get("Reported EPS"),
|
||||
"surprise_pct": row.get("Surprise(%)"),
|
||||
}
|
||||
# clean NaN
|
||||
s = {k: (None if isinstance(v, float) and np.isnan(v) else v) for k, v in s.items()}
|
||||
surprises.append(s)
|
||||
result["earnings"] = surprises
|
||||
else:
|
||||
result["earnings"] = []
|
||||
except:
|
||||
result["earnings"] = []
|
||||
|
||||
# 5. Analyst consensus
|
||||
result["analyst"] = {
|
||||
"target_mean": info.get("targetMeanPrice"),
|
||||
"target_low": info.get("targetLowPrice"),
|
||||
"target_high": info.get("targetHighPrice"),
|
||||
"recommendation": info.get("recommendationKey"),
|
||||
"num_analysts": info.get("numberOfAnalystOpinions"),
|
||||
}
|
||||
if result["price_action"].get("current") and info.get("targetMeanPrice"):
|
||||
upside = (info["targetMeanPrice"] / result["price_action"]["current"] - 1) * 100
|
||||
result["analyst"]["upside_pct"] = round(upside, 2)
|
||||
|
||||
# 6. Key fundamentals snapshot
|
||||
result["fundamentals"] = {
|
||||
"trailing_pe": info.get("trailingPE"),
|
||||
"forward_pe": info.get("forwardPE"),
|
||||
"peg_ratio": info.get("pegRatio"),
|
||||
"market_cap": info.get("marketCap"),
|
||||
"revenue_growth": info.get("revenueGrowth"),
|
||||
"earnings_growth": info.get("earningsGrowth"),
|
||||
"roe": info.get("returnOnEquity"),
|
||||
"debt_to_equity": info.get("debtToEquity"),
|
||||
"dividend_yield": info.get("dividendYield"),
|
||||
"beta": info.get("beta"),
|
||||
}
|
||||
|
||||
# 7. Technical - RSI approximation
|
||||
if not hist.empty and len(hist) >= 14:
|
||||
delta = hist["Close"].diff()
|
||||
gain = delta.where(delta > 0, 0).rolling(14).mean()
|
||||
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
|
||||
rs = gain / loss
|
||||
rsi = 100 - (100 / (1 + rs))
|
||||
result["technical"] = {
|
||||
"rsi_14": round(float(rsi.iloc[-1]), 2) if not np.isnan(rsi.iloc[-1]) else None,
|
||||
"trend": "bullish" if cur > ma50 and (ma200 is None or cur > ma200) else "bearish" if cur < ma50 and (ma200 and cur < ma200) else "mixed",
|
||||
}
|
||||
|
||||
print(f" Done: {result['name']} @ ${result['price_action']['current']}")
|
||||
return result
|
||||
|
||||
# ============================================================
|
||||
# EXPANDED GARP SCAN
|
||||
# ============================================================
|
||||
def get_broad_ticker_list():
|
||||
"""Get a broad list of US tickers to screen."""
|
||||
import urllib.request
|
||||
# Use Wikipedia's S&P 500 + S&P 400 midcap for broader coverage
|
||||
# Plus some known Russell 1000 members not in S&P 500
|
||||
|
||||
# Start with S&P 500
|
||||
sp500 = []
|
||||
try:
|
||||
import pandas as pd
|
||||
tables = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_500_companies")
|
||||
sp500 = tables[0]["Symbol"].str.replace(".", "-", regex=False).tolist()
|
||||
except:
|
||||
pass
|
||||
|
||||
# S&P 400 midcap
|
||||
sp400 = []
|
||||
try:
|
||||
import pandas as pd
|
||||
tables = pd.read_html("https://en.wikipedia.org/wiki/List_of_S%26P_400_companies")
|
||||
sp400 = tables[0]["Symbol"].str.replace(".", "-", regex=False).tolist()
|
||||
except:
|
||||
pass
|
||||
|
||||
all_tickers = list(set(sp500 + sp400))
|
||||
print(f"Total tickers to screen: {len(all_tickers)}")
|
||||
return all_tickers
|
||||
|
||||
def garp_screen(tickers, exclude=None):
|
||||
"""Apply GARP filters to ticker list."""
|
||||
exclude = set(exclude or [])
|
||||
passed = []
|
||||
|
||||
for i, tick in enumerate(tickers):
|
||||
if tick in exclude:
|
||||
continue
|
||||
if i % 50 == 0:
|
||||
print(f" Screening {i}/{len(tickers)}...")
|
||||
try:
|
||||
t = yf.Ticker(tick)
|
||||
info = t.info or {}
|
||||
|
||||
mc = info.get("marketCap", 0) or 0
|
||||
if mc < 5e9:
|
||||
continue
|
||||
|
||||
tpe = info.get("trailingPE")
|
||||
if tpe is None or tpe >= 25 or tpe <= 0:
|
||||
continue
|
||||
|
||||
fpe = info.get("forwardPE")
|
||||
if fpe is None or fpe >= 15 or fpe <= 0:
|
||||
continue
|
||||
|
||||
rg = info.get("revenueGrowth")
|
||||
if rg is None or rg < 0.10:
|
||||
continue
|
||||
|
||||
eg = info.get("earningsGrowth")
|
||||
if eg is None or eg < 0.15:
|
||||
continue
|
||||
|
||||
roe = info.get("returnOnEquity")
|
||||
if roe is None or roe < 0.05:
|
||||
continue
|
||||
|
||||
# Optional filters
|
||||
peg = info.get("pegRatio")
|
||||
if peg is not None and peg > 1.2:
|
||||
continue
|
||||
|
||||
dte = info.get("debtToEquity")
|
||||
if dte is not None and dte > 35:
|
||||
continue
|
||||
|
||||
passed.append({
|
||||
"ticker": tick,
|
||||
"name": info.get("longName", tick),
|
||||
"market_cap": mc,
|
||||
"trailing_pe": round(tpe, 2),
|
||||
"forward_pe": round(fpe, 2),
|
||||
"peg": round(peg, 2) if peg else None,
|
||||
"revenue_growth": round(rg * 100, 1),
|
||||
"earnings_growth": round(eg * 100, 1),
|
||||
"roe": round(roe * 100, 1),
|
||||
"debt_to_equity": round(dte, 1) if dte else None,
|
||||
})
|
||||
print(f" ✅ PASS: {tick} (PE:{tpe:.1f} FPE:{fpe:.1f} RG:{rg*100:.0f}% EG:{eg*100:.0f}%)")
|
||||
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
return passed
|
||||
|
||||
# ============================================================
|
||||
# MAIN
|
||||
# ============================================================
|
||||
if __name__ == "__main__":
|
||||
output_dir = Path("/home/wdjones/.openclaw/workspace/data")
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Deep dive on 4 stocks
|
||||
print("=" * 60)
|
||||
print("DEEP DIVE ANALYSIS")
|
||||
print("=" * 60)
|
||||
|
||||
analyses = {}
|
||||
for tick in TARGETS:
|
||||
analyses[tick] = analyze_stock(tick)
|
||||
|
||||
# Save deep dive
|
||||
with open(output_dir / "stock-analysis-deep-dive.json", "w") as f:
|
||||
json.dump(analyses, f, indent=2, default=str)
|
||||
print(f"\nSaved deep dive to stock-analysis-deep-dive.json")
|
||||
|
||||
# Expanded GARP scan
|
||||
print("\n" + "=" * 60)
|
||||
print("EXPANDED GARP SCAN (S&P 500 + S&P 400 MidCap)")
|
||||
print("=" * 60)
|
||||
|
||||
tickers = get_broad_ticker_list()
|
||||
new_passes = garp_screen(tickers, exclude=set(TARGETS))
|
||||
|
||||
with open(output_dir / "garp-expanded-scan.json", "w") as f:
|
||||
json.dump(new_passes, f, indent=2, default=str)
|
||||
print(f"\nExpanded scan found {len(new_passes)} additional stocks")
|
||||
print("Saved to garp-expanded-scan.json")
|
||||
|
||||
# Print summary
|
||||
print("\n" + "=" * 60)
|
||||
print("SUMMARY")
|
||||
print("=" * 60)
|
||||
for tick, data in analyses.items():
|
||||
pa = data.get("price_action", {})
|
||||
an = data.get("analyst", {})
|
||||
fu = data.get("fundamentals", {})
|
||||
te = data.get("technical", {})
|
||||
print(f"\n{tick} - {data['name']}")
|
||||
print(f" Price: ${pa.get('current')} | 1yr Return: {pa.get('1yr_return_pct')}%")
|
||||
print(f" From 52wk High: {pa.get('pct_from_52wk_high')}%")
|
||||
print(f" PE: {fu.get('trailing_pe')} | Fwd PE: {fu.get('forward_pe')} | PEG: {fu.get('peg_ratio')}")
|
||||
print(f" Target: ${an.get('target_mean')} ({an.get('upside_pct')}% upside) | Rec: {an.get('recommendation')}")
|
||||
print(f" RSI: {te.get('rsi_14')} | Trend: {te.get('trend')}")
|
||||
print(f" Insiders: {len(data.get('insider_transactions', []))} recent txns")
|
||||
|
||||
if new_passes:
|
||||
print(f"\nNew GARP Candidates ({len(new_passes)}):")
|
||||
for s in sorted(new_passes, key=lambda x: x.get("forward_pe", 99)):
|
||||
print(f" {s['ticker']:6s} PE:{s['trailing_pe']:5.1f} FPE:{s['forward_pe']:5.1f} RG:{s['revenue_growth']:5.1f}% EG:{s['earnings_growth']:5.1f}% ROE:{s['roe']:5.1f}%")
|
||||
Reference in New Issue
Block a user