
Canslim Screener
- 1.1k installs
- 2.5k repo stars
- Updated July 26, 2026
- tradermonty/claude-trading-skills
CANSLIM Screener is an agent skill that systematically screens stocks using William O'Neil's seven-factor CANSLIM growth methodology for developers and engineers making data-informed investment or trading decisions.
About
CANSLIM Screener is a Claude trading skill that applies William O'Neil's CANSLIM growth stock selection system from Investor's Business Daily. The methodology identifies seven common characteristics—Current earnings, Annual earnings, New products, Supply/demand, Leader status, Institutional sponsorship, and Market direction—that winning stocks exhibited in IBD historical studies. Developers reach for CANSLIM Screener before investment or trading decisions when they want structured screening instead of ad hoc ticker picks. The skill documents IBD research spanning stocks from 1953 onward and frames multi-bagger trait analysis as a repeatable checklist.
- Applies the full 7-component CANSLIM framework developed by William O'Neil
- Screens for accelerating quarterly earnings, annual growth, new products, supply-demand, leader status, institutional sp
- Delivers historical performance context showing 100-300% average gains for stocks meeting all criteria
- Runs as an autonomous agent that pulls and analyzes real-time financial data
- Outputs ranked watchlist with supporting evidence for each CANSLIM trait
Canslim Screener by the numbers
- 1,102 all-time installs (skills.sh)
- +49 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #128 of 1,136 Finance & Trading skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/tradermonty/claude-trading-skills --skill canslim-screenerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.1k |
|---|---|
| repo stars | ★ 2.5k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 26, 2026 |
| Repository | tradermonty/claude-trading-skills ↗ |
How do you screen stocks with CANSLIM criteria?
Systematically screen stocks using the CANSLIM methodology before making investment or trading decisions.
Who is it for?
Developers who actively trade or invest and want systematic CANSLIM screening grounded in William O'Neil's seven-factor framework.
Skip if: Developers building trading bots or needing execution infrastructure should skip CANSLIM Screener because it focuses on screening methodology, not order routing.
When should I use this skill?
A developer asks to screen stocks, evaluate growth leaders, or apply CANSLIM before a trading or investment decision.
What you get
Ranked stock watchlist scored against seven CANSLIM factors with trait-level notes.
By the numbers
- Screens stocks against 7 CANSLIM characteristics from William O'Neil's methodology
- References IBD analysis of winning stocks from 1953 to the present
Files
CANSLIM Stock Screener - Phase 3 (Full CANSLIM)
Overview
This skill screens US stocks using William O'Neil's proven CANSLIM methodology, a systematic approach for identifying growth stocks with strong fundamentals and price momentum. CANSLIM analyzes 7 key components: Current Earnings, Annual Growth, Newness/New Highs, Supply/Demand, Leadership/RS Rank, Institutional Sponsorship, and Market Direction.
Phase 3 implements all 7 of 7 components (C, A, N, S, L, I, M), representing 100% of the full methodology.
Two-Stage Approach: 1. Stage 1 (FMP API + Finviz): Analyze stock universe with all 7 CANSLIM components 2. Stage 2 (Reporting): Rank by composite score and generate actionable reports
Key Features:
- Composite scoring (0-100 scale) with weighted components
- Finviz fallback for institutional ownership data (automatic when FMP data incomplete)
- Progressive filtering to optimize API usage
- JSON + Markdown output formats
- Interpretation bands: Exceptional+ (90+), Exceptional (80-89), Strong (70-79), Above Average (60-69)
- Bear market protection (M component gating)
Phase 3.1 Component Weights (Original O'Neil weights):
- C (Current Earnings): 15%
- A (Annual Growth): 20%
- N (Newness): 15%
- S (Supply/Demand): 15%
- L (Leadership/RS Rank): 20% — multi-period weighted RS (3m/6m/12m vs configurable benchmark)
- I (Institutional): 10%
- M (Market Direction): 5%
Weighted RS Formula:
Weighted RS = 0.40 × rel_3m + 0.30 × rel_6m + 0.30 × rel_12mAvailable periods are re-normalized when some are missing. Default benchmark is ^GSPC; override with --rs-benchmark SPY/QQQ/IWM/....
Fallback hierarchy when multi-period data is incomplete: 1. No benchmark → weighted absolute stock performance + 20% penalty. 2. All multi-period windows missing but >=50 bars of price history → fall back to the legacy 365-day full-window absolute return as the scoring input (20% penalty if no benchmark). 3. <50 bars of price history → score=0 with error set.
Future Phases:
- Phase 4: FINVIZ Elite integration → 10x faster execution
---
When to Use This Skill
Explicit Triggers:
- "Find CANSLIM stocks"
- "Screen for growth stocks using O'Neil's method"
- "Which stocks have strong earnings and momentum?"
- "Identify stocks near 52-week highs with accelerating earnings"
- "Run a CANSLIM screener on [sector/universe]"
Implicit Triggers:
- User wants to identify multi-bagger candidates
- User is looking for growth stocks with proven fundamentals
- User wants systematic stock selection based on historical winners
- User needs a ranked list of stocks meeting O'Neil's criteria
When NOT to Use:
- Value investing focus (use value-dividend-screener instead)
- Income/dividend focus (use dividend-growth-pullback-screener instead)
- Bear market conditions (M component will flag - consider raising cash)
---
Prerequisites
API Requirements:
- FMP API key (free tier: 250 calls/day, sufficient for 35 stocks; Starter tier $29.99/mo for 40+ stocks)
- Sign up: https://site.financialmodelingprep.com/developer/docs
- Set via environment variable:
export FMP_API_KEY=your_key_here
Python Dependencies:
- Python 3.9+
requests(FMP API calls)beautifulsoup4(Finviz web scraping)lxml(HTML parsing)
Installation:
pip install requests beautifulsoup4 lxml---
Output
Output Directory: reports/ (default) or custom via --output-dir
Generated Files:
canslim_screener_YYYY-MM-DD_HHMMSS.json- Structured data for programmatic usecanslim_screener_YYYY-MM-DD_HHMMSS.md- Human-readable report
Report Contents:
- Market Condition Summary (trend, M score, warnings)
- Top N CANSLIM Candidates (ranked by composite score)
- Component Breakdown for each stock (C, A, N, S, L, I, M scores with details)
- Rating interpretation (Exceptional+/Exceptional/Strong/Above Average)
- Quality warnings and data source notes
- Summary statistics (rating distribution)
Rating Bands:
- Exceptional+ (90-100): All components near-perfect, aggressive buy
- Exceptional (80-89): Outstanding fundamentals + momentum, strong buy
- Strong (70-79): Solid across components, standard buy
- Above Average (60-69): Meets thresholds with minor weaknesses, buy on pullback
---
Workflow
Step 1: Verify API Access and Requirements
Check if user has FMP API key configured:
# Check environment variable
echo $FMP_API_KEY
# If not set, prompt user to provide itRequirements:
- FMP API key (free tier: 250 calls/day, sufficient for 40 stocks)
- Python 3.9+ with required libraries:
requests(FMP API calls)beautifulsoup4(Finviz web scraping)lxml(HTML parsing)
Installation:
pip install requests beautifulsoup4 lxmlIf API key is missing, guide user to: 1. Sign up at https://site.financialmodelingprep.com/developer/docs 2. Get free API key (250 calls/day) 3. Set environment variable: export FMP_API_KEY=your_key_here
Step 2: Determine Stock Universe
Option A: Default Universe (Recommended) Use top 40 S&P 500 stocks by market cap (predefined in script):
python3 skills/canslim-screener/scripts/screen_canslim.pyOption B: Custom Universe User provides specific symbols or sector:
python3 skills/canslim-screener/scripts/screen_canslim.py \
--universe AAPL MSFT GOOGL AMZN NVDA META TSLAOption C: Sector-Specific User can provide sector-focused list (Technology, Healthcare, etc.)
API Budget Considerations (Phase 3):
- 40 stocks × 7 FMP calls/stock = 280 API calls
- FMP: 7 calls/stock (profile, quote, income×2, historical_90d, historical_365d, institutional)
- Finviz: ~1.8 calls/stock (institutional ownership fallback, 2s rate limit, not counted in FMP budget)
- Market data (^GSPC quote, ^VIX quote, ^GSPC 52-week history): 3 FMP calls
- Total: ~283 FMP calls per screening run (exceeds 250 free tier)
- Recommendation: Use
--max-candidates 35for free tier (35 × 7 + 3 = 248 calls), or upgrade to FMP Starter tier ($29.99/mo, 750 calls/day) for full 40-stock screening
Step 3: Execute CANSLIM Screening Script
Run the main screening script with appropriate parameters:
cd skills/canslim-screener/scripts
# Basic run (40 stocks, top 20 in report)
python3 screen_canslim.py --api-key $FMP_API_KEY
# Custom parameters
python3 screen_canslim.py \
--api-key $FMP_API_KEY \
--max-candidates 40 \
--top 20 \
--output-dir ../../../
# Custom RS benchmark (Phase 3.1)
python3 screen_canslim.py --rs-benchmark SPY
# Disable L component (saves per-stock 365-day fetch; L fixed at neutral 50)
python3 screen_canslim.py --disable-rsScript Workflow (Phase 3 - Full CANSLIM): 1. Market Direction (M): Analyze S&P 500 trend vs 50-day EMA (using real historical data for accurate EMA)
- If bear market detected (M=0), warn user to raise cash
2. S&P 500 Historical Data: Fetch 52-week data for M component EMA and L component RS calculation 3. Stock Analysis: For each stock, calculate:
- C Component: Quarterly EPS/revenue growth (YoY)
- A Component: 3-year EPS CAGR and stability
- N Component: Distance from 52-week high, breakout detection
- S Component: Volume-based accumulation/distribution (up-day vs down-day volume)
- L Component: 52-week Relative Strength vs S&P 500
- I Component: Institutional holder count + ownership % (with Finviz fallback)
4. Composite Scoring: Weighted average with all 7 component breakdown 5. Ranking: Sort by composite score (highest first) 6. Reporting: Generate JSON + Markdown outputs
Expected Execution Time (Phase 3):
- 40 stocks: ~2 minutes (additional 52-week history fetch per stock for L component)
- Finviz fallback adds ~2 seconds per stock (rate limiting)
- L component requires 365-day historical data for each stock
Finviz Fallback Behavior:
- Triggers automatically when FMP
sharesOutstandingunavailable - Scrapes institutional ownership % from Finviz.com (free, no API key)
- Increases I component accuracy from 35/100 (partial data) to 60-100/100 (full data)
- User sees:
✅ Using Finviz institutional ownership for NVDA: 68.3%
Step 4: Read and Parse Screening Results
The script generates two output files:
canslim_screener_YYYY-MM-DD_HHMMSS.json- Structured datacanslim_screener_YYYY-MM-DD_HHMMSS.md- Human-readable report
Read the Markdown report to identify top candidates:
# Find the latest report
ls -lt canslim_screener_*.md | head -1
# Read the report
cat canslim_screener_YYYY-MM-DD_HHMMSS.mdReport Structure (Phase 3 - Full CANSLIM):
- Market Condition Summary (trend, M score, warnings)
- Top N CANSLIM Candidates (ranked, N = --top parameter)
- For each stock:
- Composite Score and Rating (Exceptional+/Exceptional/Strong/etc.)
- Component Breakdown (C, A, N, S, L, I, M scores with details)
- Interpretation (rating description, guidance, weakest component)
- Warnings (quality issues, market conditions, data source notes)
- Summary Statistics (rating distribution)
- Methodology note (Phase 3: 7 components, 100% coverage)
Component Details in Report:
- S Component: "Up/Down Volume Ratio: 1.06 ✓ Accumulation"
- L Component (Phase 3.1): "3m/6m/12m: +12.4%/+18.7%/+44.1% (rel +5.2%/+8.3%/+22.0%) | RS: 88 (Strong)"
- I Component: "6199 holders, 68.3% ownership ⭐ Superinvestor"
A new Summary Table appears above the candidate list in Phase 3.1 reports, showing rank, symbol, composite score, rating, RS rating, and RS percentile for quick scanning.
Step 5: Analyze Top Candidates and Provide Recommendations
Review the top-ranked stocks and cross-reference with knowledge bases:
Reference Documents to Consult: 1. references/interpretation_guide.md - Understand rating bands and portfolio sizing 2. references/canslim_methodology.md - Deep dive into component meanings (now includes S and I) 3. references/scoring_system.md - Understand scoring formulas (Phase 3 weights)
Analysis Framework:
For Exceptional+ stocks (90-100 points):
- All components near-perfect (C≥85, A≥85, N≥85, S≥80, L≥85, I≥80, M≥80)
- Guidance: Immediate buy, aggressive position sizing (15-20% of portfolio)
- Example: "NVDA scores 97.2 - explosive quarterly earnings (100), strong 3-year growth (95), at new highs (98), volume accumulation (85), RS leader (92), strong institutional support (90), uptrend market (100)"
For Exceptional stocks (80-89 points):
- Outstanding fundamentals + strong momentum
- Guidance: Strong buy, standard sizing (10-15% of portfolio)
For Strong stocks (70-79 points):
- Solid across all components, minor weaknesses
- Guidance: Buy, standard sizing (8-12% of portfolio)
- Phase 3 Example: "Stock scores 77.5 - strong earnings (85), solid growth (80), near high (70), accumulation (60), RS leader (75), good institutions (60), uptrend (90)"
For Above Average stocks (60-69 points):
- Meets thresholds, one component weak
- Guidance: Buy on pullback, conservative sizing (5-8% of portfolio)
Bear Market Override:
- If M component = 0 (bear market detected), do NOT buy regardless of other scores
- Guidance: Raise 80-100% cash, wait for market recovery
- CANSLIM does not work in bear markets (3 out of 4 stocks follow market trend)
Step 6: Generate User-Facing Report
Create a concise, actionable summary for the user:
Report Format:
# CANSLIM Stock Screening Results (Phase 3 - Full CANSLIM)
**Date:** YYYY-MM-DD
**Market Condition:** [Trend] - M Score: [X]/100
**Stocks Analyzed:** [N]
**Components:** C, A, N, S, L, I, M (7 of 7, 100% coverage)
## Market Summary
[2-3 sentences on current market environment based on M component]
[If bear market: WARNING - Consider raising cash allocation]
## Top 5 CANSLIM Candidates
### 1. [SYMBOL] - [Company Name] ⭐⭐⭐
**Score:** [X.X]/100 ([Rating])
**Price:** $[XXX.XX] | **Sector:** [Sector]
**Component Breakdown:**
- C (Earnings): [X]/100 - [EPS growth]% QoQ, [Revenue growth]% revenue
- A (Growth): [X]/100 - [CAGR]% 3yr EPS CAGR
- N (Newness): [X]/100 - [Distance]% from 52wk high
- S (Supply/Demand): [X]/100 - Up/Down Volume Ratio: [X.XX]
- L (Leadership): [X]/100 - 52wk: [+X.X]% ([+X.X]% vs S&P) RS: [XX]
- I (Institutional): [X]/100 - [N] holders, [X.X]% ownership [⭐ Superinvestor if present]
- M (Market): [X]/100 - [Trend]
**Interpretation:** [Rating description and guidance]
**Weakest Component:** [X] ([score])
**Data Source Note:** [If Finviz used: "Institutional data from Finviz"]
[Repeat for top 5 stocks]
## Investment Recommendations
**Immediate Buy List (90+ score):**
- [List stocks with exceptional+ ratings]
- Position sizing: 15-20% each
**Strong Buy List (80-89 score):**
- [List stocks with exceptional ratings]
- Position sizing: 10-15% each
**Watchlist (70-79 score):**
- [List stocks with strong ratings]
- Buy on pullback
## Risk Factors
- [Identify any quality warnings from components]
- [Market condition warnings]
- [Sector concentration risks if applicable]
- [Data source reliability notes if Finviz heavily used]
## Next Steps
1. Conduct detailed fundamental analysis on top 3 candidates
2. Check earnings calendars for upcoming reports
3. Review technical charts for entry timing
4. [If bear market: Wait for market recovery before deploying capital]
---
**Note:** This is Phase 3 (Full CANSLIM: C, A, N, S, L, I, M - 100% coverage).---
Resources
Scripts Directory (scripts/)
Main Scripts:
screen_canslim.py- Main orchestrator script- Entry point for screening workflow
- Handles argument parsing, API coordination, ranking, reporting
- Usage:
python3 screen_canslim.py --api-key KEY [options]
fmp_client.py- FMP API client wrapper- Rate limiting (0.3s between calls)
- 429 error handling with 60s retry
- Session-based caching
- Methods:
get_income_statement(),get_quote(),get_historical_prices(),get_institutional_holders()
finviz_stock_client.py- Finviz web scraping client ← NEW- BeautifulSoup-based HTML parsing
- Fetches institutional ownership % from Finviz.com
- Rate limiting (2.0s between calls)
- No API key required (free web scraping)
- Methods:
get_institutional_ownership(),get_stock_data()
Calculators (`scripts/calculators/`):
earnings_calculator.py- C component (Current Earnings)- Quarterly EPS/revenue growth (YoY)
- Scoring: 50%+ = 100pts, 30-49% = 80pts, 18-29% = 60pts
growth_calculator.py- A component (Annual Growth)- 3-year EPS CAGR calculation
- Stability check (no negative growth years)
- Scoring: 40%+ = 90pts, 30-39% = 70pts, 25-29% = 50pts
new_highs_calculator.py- N component (Newness)- Distance from 52-week high
- Volume-confirmed breakout detection
- Scoring: 5% of high + breakout = 100pts, 10% + breakout = 80pts
supply_demand_calculator.py- S component (Supply/Demand) ← NEW- Volume-based accumulation/distribution analysis
- Up-day volume vs down-day volume ratio (60-day lookback)
- Scoring: ratio ≥2.0 = 100pts, 1.5-2.0 = 80pts, 1.0-1.5 = 60pts
leadership_calculator.py- L component (Leadership/Relative Strength)- 52-week stock performance vs S&P 500 benchmark
- RS Rank estimation (1-99 scale, O'Neil style)
- Scoring: RS 90+ outperforming market = 100pts, RS 80-89 = 80pts
institutional_calculator.py- I component (Institutional)- Institutional holder count (from FMP)
- Ownership % (from FMP or Finviz fallback)
- Superinvestor detection (Berkshire Hathaway, Baupost, etc.)
- Scoring: 50-100 holders + 30-60% ownership = 100pts
market_calculator.py- M component (Market Direction)- S&P 500 vs 50-day EMA
- VIX-adjusted scoring
- Scoring: Strong uptrend = 100pts, Uptrend = 80pts, Bear market = 0pts
Supporting Modules:
scorer.py- Composite score calculation- Phase 3 weighted average: C×15% + A×20% + N×15% + S×15% + L×20% + I×10% + M×5%
- Rating interpretation (Exceptional+/Exceptional/Strong/etc.)
- Minimum threshold validation (all 7 components must meet baseline)
report_generator.py- Output generation- JSON export (programmatic use)
- Markdown export (human-readable)
- Phase 3 component breakdown tables (all 7 components)
- Summary statistics calculation
References Directory (references/)
Knowledge Bases:
references/canslim_methodology.md(27KB) - Complete CANSLIM explanation- All 7 components with O'Neil's original thresholds
- S component (Volume accumulation/distribution) detailed explanation
- L component (Leadership/Relative Strength) detailed explanation
- I component (Institutional sponsorship) detailed explanation
- Historical examples (AAPL 2009, NFLX 2013, TSLA 2019, NVDA 2023)
references/scoring_system.md(21KB) - Technical scoring specification (Phase 3)- Phase 3 component weights and formulas (all 7 components)
- Interpretation bands (90-100, 80-89, etc.)
- Minimum thresholds for all 7 components
- Composite score calculation examples
references/fmp_api_endpoints.md(18KB) - API integration guide (Phase 3)- Required endpoints for all 7 components
- L component: 52-week historical prices endpoint
- Institutional holder endpoint documentation
- Finviz fallback strategy explanation
- Rate limiting strategy
- Cost analysis (Phase 3: ~283 FMP calls for 40 stocks, exceeds 250 free tier)
references/interpretation_guide.md(18KB) - User guidance- Portfolio construction rules
- Position sizing by rating
- Entry/exit strategies
- Bear market protection rules
How to Use References:
- Read
references/canslim_methodology.mdfirst to understand O'Neil's system (now includes S and I) - Consult
references/interpretation_guide.mdwhen analyzing results - Reference
references/scoring_system.mdif scores seem unexpected - Check
references/fmp_api_endpoints.mdfor API troubleshooting or Finviz fallback issues
---
Troubleshooting
Issue 1: FMP API Rate Limit Exceeded
Symptoms:
ERROR: 429 Too Many Requests - Rate limit exceeded
Retrying in 60 seconds...Causes:
- Running multiple screenings within short time window
- Exceeding 250 calls/day (free tier limit)
- Other applications using same API key
Solutions: 1. Wait and Retry: Script auto-retries after 60s 2. Reduce Universe: Use --max-candidates 30 to lower API usage 3. Check Daily Usage: Free tier resets at midnight UTC 4. Upgrade Plan: FMP Starter ($29.99/month) provides 750 calls/day
Issue 2: Missing Required Libraries
Symptoms:
ERROR: required libraries not found. Install with: pip install beautifulsoup4 requests lxmlSolutions:
# Install all required libraries
pip install requests beautifulsoup4 lxml
# Or install individually
pip install beautifulsoup4
pip install requests
pip install lxmlIssue 3: Finviz Fallback Slow Execution
Symptoms:
Execution time: 2 minutes 30 seconds for 40 stocks (slower than expected)Causes:
- Finviz rate limiting (2.0s per request)
- All stocks triggering fallback due to FMP data gaps
Solutions: 1. Accept Delay: 1-2 minutes for 40 stocks is normal with Finviz fallback 2. Monitor Fallback Usage: Check logs for "Using Finviz institutional ownership" messages 3. Reduce Rate Limit (advanced): Edit finviz_stock_client.py, change rate_limit_seconds=2.0 to 1.5 (risk: IP ban)
Note: Finviz fallback adds ~2 seconds per stock but significantly improves I component accuracy (35 → 60-100 points).
Issue 4: Finviz Web Scraping Failure
Symptoms:
WARNING: Finviz request failed with status 403 for NVDA
⚠️ Using Finviz institutional ownership data - FMP shares outstanding unavailable. Finviz fallback also unavailable. Score reduced by 50%.Causes:
- Finviz blocking scraping requests (User-Agent detection)
- Rate limit exceeded (too many requests)
- Network issues or Finviz downtime
Solutions: 1. Wait and Retry: Rate limit resets after a few minutes 2. Check Internet Connection: Verify network access to finviz.com 3. Fallback Accepted: Script continues with FMP holder count only (I score capped at 70/100) 4. Manual Verification: Check Finviz website manually for blocked IP
Graceful Degradation:
- Script never fails due to Finviz issues
- Falls back to FMP holder count only
- User sees quality warning in report
Issue 5: No Stocks Meet Minimum Thresholds
Symptoms:
✓ Successfully analyzed 40 stocks
Top 5 Stocks:
1. AAPL - 58.3 (Average)
2. MSFT - 55.1 (Average)
...Causes:
- Bear market conditions (M component low)
- Selected universe lacks growth stocks
- Market rotation away from growth
Solutions: 1. Check M Component: If M=0 (bear market), raise cash per CANSLIM rules 2. Expand Universe: Try different sectors or market cap ranges 3. Lower Expectations: Average scores (55-65) may still be actionable in weak markets 4. Wait for Better Setup: CANSLIM works best in bull markets
Issue 6: Data Quality Warnings
Symptoms:
⚠️ Revenue declining despite EPS growth (possible buyback distortion)
⚠️ Using Finviz institutional ownership data (68.3%) - FMP shares outstanding unavailable.Interpretation:
- These are not errors - they are quality flags from calculators
- Revenue warning: EPS growth may be from share buybacks, not organic growth
- Finviz warning: Data source switched from FMP to Finviz (still accurate)
Actions: 1. Review component details in full report 2. Cross-check with fundamental analysis 3. Adjust position sizing based on risk level 4. Finviz data is reliable - no action needed for data source warnings
---
Important Notes
Phase 3 Implementation Status
This is Phase 3 implementing all 7 of 7 CANSLIM components:
- ✅ C (Current Earnings) - Implemented
- ✅ A (Annual Growth) - Implemented
- ✅ N (Newness) - Implemented
- ✅ S (Supply/Demand) - Implemented
- ✅ L (Leadership/RS Rank) - Implemented
- ✅ I (Institutional) - Implemented
- ✅ M (Market Direction) - Implemented
Implications:
- Composite scores represent 100% of full CANSLIM methodology
- Uses original O'Neil component weights (C 15%, A 20%, N 15%, S 15%, L 20%, I 10%, M 5%)
- L component (20% weight) is the largest individual factor alongside A, emphasizing relative strength leadership
- M component uses real 50-day EMA from historical data (not fallback estimate)
Finviz Integration Benefits
Automatic Fallback System:
- When FMP API doesn't provide
sharesOutstanding, Finviz automatically activates - Scrapes institutional ownership % from Finviz.com (free, no API key)
- Improves I component accuracy from 35/100 (partial) to 60-100/100 (full)
Data Source Priority: 1. FMP API (primary): Institutional holder count + shares outstanding calculation 2. Finviz (fallback): Direct institutional ownership % from web page 3. Partial Data (last resort): Holder count only, 50% penalty applied
Tested Reliability:
- 39/39 stocks successfully retrieved ownership % via Finviz (100% success rate)
- Average execution time: 2.54 seconds per stock
- No errors or IP blocks during testing
Future Enhancements
Phase 4 (Planned):
- FINVIZ Elite integration for pre-screening
- Execution time: 2 minutes → 10-15 seconds
- FMP API usage reduction: 90%
- Larger universe possible (100+ stocks)
Data Source Attribution
- FMP API: Income statements, quotes, historical prices, key metrics, institutional holders
- Finviz: Institutional ownership % (fallback), market data
- Methodology: William O'Neil's "How to Make Money in Stocks" (4th edition)
- Scoring System: Adapted from IBD MarketSmith proprietary system
Disclaimer
This screener is for educational and informational purposes only.
- Not investment advice
- Past performance does not guarantee future results
- CANSLIM methodology works best in bull markets (M component confirms)
- Conduct your own research and consult a financial advisor before making investment decisions
- O'Neil's historical winners include AAPL (2009: +1,200%), NFLX (2013: +800%), but many stocks fail to perform
---
Version: Phase 3.1 (multi-period RS) Last Updated: 2026-05-03 API Requirements: FMP API (free tier: up to 35 stocks; Starter tier recommended for 40 stocks) + BeautifulSoup/requests/lxml for Finviz Execution Time: ~2 minutes for 40 stocks Output Formats: JSON + Markdown (now includes Summary Table and schema_version: "3.1") Components Implemented: C, A, N, S, L, I, M (7 of 7, 100% coverage) Phase 3.1 additions: multi-period RS (3m/6m/12m), --rs-benchmark, --disable-rs, new RS fields (rs_rating, rs_rank_percentile, rs_3m_return, rs_6m_return, rs_12m_return, rs_benchmark, rs_benchmark_relative_return, rs_component_score, benchmark_52w_performance).
CANSLIM Methodology - William O'Neil's Growth Stock Selection System
Overview
CANSLIM is a proven growth stock selection methodology developed by William O'Neil, founder of Investor's Business Daily (IBD). Based on comprehensive analysis of the greatest winning stocks from 1953 to the present, CANSLIM identifies 7 common characteristics that multi-bagger stocks exhibit before their major price advances.
Historical Performance: IBD's studies show that stocks exhibiting all 7 CANSLIM traits delivered average gains of 100-300% over 1-3 year periods, with many producing returns exceeding 1,000%.
Investment Philosophy: CANSLIM focuses on identifying emerging growth leaders at the beginning of their major price advances - not after they've already run. The methodology emphasizes buying quality growth stocks during brief consolidations or pullbacks in confirmed market uptrends.
---
The 7 CANSLIM Components
C - Current Quarterly Earnings (15% Weight)
O'Neil's Rule: "Look for companies whose current quarterly earnings per share are up at least 18-20% compared to the same quarter the prior year."
Why It Matters
Earnings are the primary fundamental driver of stock prices. Companies with accelerating quarterly earnings signal business momentum, competitive advantages, and management execution. O'Neil's research found that 3 out of 4 winning stocks showed EPS growth of at least 70% in the quarter before their major price advance began.
Quantitative Criteria
- Minimum: 18% year-over-year (YoY) quarterly EPS growth
- Preferred: 25%+ YoY growth
- Exceptional: 50%+ YoY growth with accelerating trend
Scoring Formula (0-100 Points)
100 points: EPS growth >= 50% AND revenue growth >= 25% (explosive acceleration)
80 points: EPS growth 30-49% AND revenue growth >= 15% (strong growth)
60 points: EPS growth 18-29% AND revenue growth >= 10% (meets minimum)
40 points: EPS growth 10-17% (below threshold)
0 points: EPS growth < 10% or negativeRevenue Growth Verification
Critical Check: EPS growth must be supported by revenue growth. If EPS grows significantly faster than revenue, investigate whether growth is driven by cost-cutting, share buybacks, or accounting adjustments rather than genuine business expansion.
Red Flag: EPS growth > 30% while revenue growth < 10% suggests unsustainable earnings quality.
Historical Examples
- AAPL (2009 Q3): EPS +45% YoY (iPhone 3GS launch), stock gained 200% over next 2 years
- NFLX (2013 Q1): EPS +278% YoY (streaming acceleration), stock up 400% in 18 months
- TSLA (2020 Q3): EPS turned positive (first sustained profitability), stock up 700% in 12 months
- NVDA (2023 Q2): EPS +429% YoY (AI chip demand), stock up 240% YTD
---
A - Annual Earnings Growth (20% Weight)
O'Neil's Rule: "Annual earnings per share should be up 25% or more in each of the last three years."
Why It Matters
While current quarterly earnings (C) signal near-term momentum, annual earnings growth (A) validates sustained competitive advantages and business model strength. One-quarter wonders rarely sustain price gains. Multi-year consistency separates authentic growth companies from cyclical or temporary winners.
Quantitative Criteria
- Minimum: 25% annual EPS CAGR over 3 years
- Preferred: 30-40% annual CAGR
- Exceptional: 40%+ annual CAGR with no down years
3-Year CAGR Calculation
# Compound Annual Growth Rate formula
EPS_CAGR = ((EPS_current / EPS_3_years_ago) ^ (1/3)) - 1
# Example: MSFT 2017-2020
# EPS: $3.25 (2017) → $5.76 (2020)
# CAGR = (5.76 / 3.25) ^ (1/3) - 1 = 21.0%Scoring Formula (0-100 Points)
90 points: EPS CAGR >= 40% AND stable (no down years) AND revenue CAGR >= 20%
70 points: EPS CAGR 30-39% AND stable
50 points: EPS CAGR 25-29% (meets CANSLIM minimum)
30 points: EPS CAGR 15-24% (below threshold)
0 points: EPS CAGR < 15% or erratic (down years present)
Bonus: +10 points if all 3 years show year-over-year growth (stability bonus)
Penalty: -20% of score if revenue CAGR < 50% of EPS CAGR (buyback-driven growth)Growth Stability Assessment
Stable Growth (preferred): EPS increases every year over the 3-year period
- Example: $1.00 → $1.30 → $1.69 → $2.20 (consistent 30% growth)
Erratic Growth (penalty): One or more years show declining EPS
- Example: $1.00 → $1.50 → $1.20 → $1.80 (volatile, uncertain)
O'Neil's Insight: "It's better to see three years of 25% growth than one year of 80% followed by two years of 10%."
Revenue Growth Validation
Healthy Growth: EPS CAGR and Revenue CAGR both strong (within 10 percentage points)
- Example: EPS 30% CAGR + Revenue 25% CAGR = Sustainable
Warning Sign: EPS CAGR >> Revenue CAGR (gap > 15 percentage points)
- Likely driven by margin expansion, buybacks, or cost cuts (less sustainable)
- Example: EPS 35% CAGR + Revenue 10% CAGR = Investigate quality
Historical Examples
- V (2015-2018): EPS CAGR 29%, Revenue CAGR 18% → 180% stock gain
- MA (2014-2017): EPS CAGR 33%, Revenue CAGR 14% → 200% stock gain
- MSFT (2017-2020): EPS CAGR 21%, Revenue CAGR 13% → 280% stock gain
- NVDA (2020-2023): EPS CAGR 76%, Revenue CAGR 52% → 450% stock gain
---
N - New Products, Management, or Highs (15% Weight)
O'Neil's Rule: "Stocks making new price highs have no overhead supply (resistance), signaling strong demand. New products, services, or management can catalyze major moves."
Why It Matters
Price action near 52-week highs indicates institutional accumulation and investor demand. New products create earnings catalysts. New management brings strategic changes. The "N" component combines technical momentum (new highs) with fundamental catalysts (newness).
Key Insight: 95% of CANSLIM winning stocks made new all-time highs before their major advance. Stocks far from highs face resistance and lack sponsorship.
Quantitative Criteria
Price Position:
- Ideal: Within 5% of 52-week high
- Acceptable: Within 15% of 52-week high
- Caution: 15-25% below 52-week high
- Avoid: >25% below 52-week high (lacks momentum)
Breakout Pattern (bonus):
- New 52-week high achieved on volume 40-50%+ above average
- Signals institutional buying (large orders driving price)
Scoring Formula (0-100 Points)
# Distance from 52-week high
distance_pct = ((current_price / week_52_high) - 1) * 100
# Base score from price position
100 points: distance <= 5% from high AND breakout detected AND new product/catalyst
80 points: distance <= 10% from high AND breakout detected
60 points: distance <= 15% from high OR breakout detected
40 points: distance <= 25% from high
20 points: distance > 25% from high (insufficient momentum)
# Bonus for new product/catalyst signals (from news)
+10-20 points: Keywords detected - "FDA approval", "new product", "patent granted", "breakthrough"New Product/Catalyst Detection (Supplementary)
High-Impact Catalysts:
- FDA drug approvals (pharmaceuticals)
- New platform/service launches (technology)
- Patent grants (defensive moat)
- Strategic acquisitions (expansion)
- New management from successful companies
Data Source: Keyword search in recent news headlines (FMP news API)
- "FDA approval" → +20 points
- "new product", "product launch" → +10 points
- "acquisition", "expansion" → +10 points
Note: Price action (new highs) is the primary signal (80% of N score). New product detection is supplementary (20% of N score).
Historical Examples
New Highs + New Products:
- AAPL (2007): iPhone launch + new highs → 600% gain in 5 years
- TSLA (2020): Model 3 production scale + new highs → 700% gain in 12 months
- NVDA (2023): AI chip (H100) demand + new highs → 240% YTD gain
New Highs After Consolidation:
- MSFT (2018): Azure cloud growth + breakout from 3-year consolidation → 350% gain in 3 years
- META (2023): AI efficiency gains + breakout from 2022 bear market → 200% gain in 12 months
Caution - Stocks Far from Highs:
- Stocks 30-50% below highs rarely lead; they often become "dead money" for years
- Exception: Deep pullbacks in bear markets can reset; wait for new highs in next bull market
---
M - Market Direction (5% Weight)
O'Neil's Rule: "You can be right about a stock but wrong about the market, and still lose money. Three out of four stocks follow the market's trend."
Why It Matters
CANSLIM doesn't work in bear markets. Even the best growth stocks decline 20-50% in sustained downtrends. O'Neil's research shows:
- Bull markets: 75% of stocks move with market direction
- Bear markets: Profitable stock picking nearly impossible
- Market timing: Staying in cash during corrections preserves capital for next bull phase
Critical Insight: "The most important decision is not which stock to buy, but whether to be invested at all."
Quantitative Criteria
Primary Signal: S&P 500 vs. 50-day Exponential Moving Average (EMA)
- Uptrend: S&P 500 closes above 50-day EMA for 3+ consecutive days
- Choppy/Neutral: S&P 500 oscillating around 50-day EMA (±2%)
- Downtrend: S&P 500 closes below 50-day EMA for 3+ consecutive days
Secondary Signal: VIX (Fear Gauge)
- Low Fear: VIX < 15 (complacent, supportive environment)
- Normal: VIX 15-20 (healthy market)
- Elevated: VIX 20-30 (caution, potential volatility)
- Panic: VIX > 30 (sell signal, go to cash)
Scoring Formula (0-100 Points)
# Calculate distance from 50-day EMA
distance_from_ema = (sp500_price / sp500_ema_50) - 1
# Market trend scoring
100 points: sp500 > EMA by 2%+ AND VIX < 15 AND follow-through day detected
80 points: sp500 > EMA by 1-2% AND VIX < 20
60 points: sp500 > EMA by 0-1% (early uptrend)
40 points: sp500 within ±2% of EMA (choppy, neutral)
20 points: sp500 < EMA by 1-3% (early downtrend)
0 points: sp500 < EMA by 3%+ OR VIX > 30 (bear market - DO NOT BUY)Follow-Through Day (FTD) - O'Neil's Bull Market Confirmation
Definition: A day when a major index (S&P 500, Nasdaq) rallies 1.25%+ on volume higher than the previous day, occurring on Day 4-10 of an attempted rally after a correction.
Significance:
- Signals institutional buying has resumed
- Confirms market bottoming process complete
- Green light to begin buying leading growth stocks
Without FTD: Rally attempts often fail; premature buying leads to losses
Market Direction Interpretation
100 Points (Strong Uptrend):
- S&P 500 well above 50-day EMA
- VIX low (complacent)
- Follow-through day confirmed
- Action: Aggressively buy leading growth stocks (CANSLIM candidates)
60 Points (Early Uptrend):
- S&P 500 just crossed above 50-day EMA
- Market still establishing trend
- Action: Start small positions (25-50% allocation)
40 Points (Choppy/Neutral):
- S&P 500 oscillating around 50-day EMA
- Direction unclear
- Action: Reduce exposure to 25-50%, wait for confirmation
0 Points (Downtrend/Bear Market):
- S&P 500 below 50-day EMA and declining
- VIX elevated or spiking
- Action: Sell all stocks, raise 80-100% cash, wait for FTD
Historical Examples
Bull Market Phases (M Score 80-100):
- 2009-2011: Post-financial crisis recovery (AAPL, PCLN major winners)
- 2013-2015: QE-driven rally (NFLX, FB explosive gains)
- 2016-2018: Tax reform bull run (NVDA, AMD, FANG stocks)
- 2020-2021: Pandemic recovery (TSLA, NVDA, growth stocks)
- 2023-2024: AI boom (NVDA, META, MAG7)
Bear Market Phases (M Score 0-20):
- 2008 (Financial Crisis): Even best stocks fell 40-60%
- 2011 (Debt Ceiling Crisis): 20% correction
- 2015-2016 (China Devaluation): Growth stocks down 20-30%
- 2018 (Fed Tightening): Nasdaq down 23%
- 2022 (Inflation/Fed): Nasdaq down 33%, growth stocks down 50-80%
Key Takeaway: In bear markets (M score < 20), CANSLIM candidates still decline despite strong fundamentals. Market direction trumps stock selection.
---
S - Supply and Demand (15% Weight)
O'Neil's Rule: "Volume is the gas in the tank of a stock. Without gas, the car doesn't go anywhere. Look for stocks where volume expands on up days and contracts on down days."
Why It Matters
Volume patterns reveal institutional accumulation (buying) or distribution (selling). Individual investors trade in small lots (100-1,000 shares), but institutions move millions of shares. When institutions accumulate, volume surges on up days. When they distribute, volume spikes on down days. Volume precedes price - smart money moves before the crowd notices.
Key Principle: UP-DAY VOLUME > DOWN-DAY VOLUME = Accumulation (bullish)
Quantitative Criteria
Accumulation/Distribution Analysis (60-day period):
- Classify each day as up-day (close > previous close) or down-day (close < previous close)
- Calculate average volume for up-days vs. down-days
- Accumulation Ratio = Avg Up-Day Volume / Avg Down-Day Volume
Thresholds:
- Strong Accumulation: Ratio ≥ 2.0 (institutions aggressively buying)
- Accumulation: Ratio 1.5-2.0 (bullish volume pattern)
- Neutral: Ratio 1.0-1.5 (slightly positive)
- Distribution: Ratio 0.5-0.7 (institutions selling)
- Strong Distribution: Ratio < 0.5 (heavy selling pressure)
Scoring Formula (0-100 Points)
# Calculate accumulation/distribution ratio
up_days_volume = [volume for day in last_60_days if close > prev_close]
down_days_volume = [volume for day in last_60_days if close < prev_close]
avg_up_volume = sum(up_days_volume) / len(up_days_volume)
avg_down_volume = sum(down_days_volume) / len(down_days_volume)
ratio = avg_up_volume / avg_down_volume
# Scoring
100 points: ratio >= 2.0 (Strong Accumulation)
80 points: ratio 1.5-2.0 (Accumulation)
60 points: ratio 1.0-1.5 (Neutral/Weak Accumulation)
40 points: ratio 0.7-1.0 (Neutral/Weak Distribution)
20 points: ratio 0.5-0.7 (Distribution)
0 points: ratio < 0.5 (Strong Distribution)Historical Examples
- NVDA (2023): Up/down ratio 2.3 before 500% run (massive institutional accumulation)
- META (2023): Up/down ratio 1.8 during recovery from 2022 lows
- TSLA (2019-2020): Ratio 2.1 during Model 3 production ramp
- AAPL (2019): Ratio 1.7 before iPhone 11 cycle launched
Red Flag: Ratio < 0.7 signals distribution - institutions are selling into strength. Avoid or exit positions.
---
I - Institutional Sponsorship (10% Weight)
O'Neil's Rule: "You need some of the big boys on your side. Look for stocks with increasing institutional sponsorship, but not too much. The sweet spot is 50-100 institutional holders with 30-60% ownership."
Why It Matters
Institutional investors (mutual funds, pension funds, hedge funds) have research teams, long time horizons, and large capital pools. Their sponsorship provides: 1. Liquidity: Enables smooth trading without excessive slippage 2. Price Support: Large holders defend positions during pullbacks 3. Discovery: Attracts additional institutional and retail capital 4. Validation: Smart money confirms the investment thesis
Key Insight: Too little institutional ownership (< 20%) means the stock is neglected. Too much (> 80%) means institutions have already bought - no buying power left to drive future gains.
Quantitative Criteria
Holder Count:
- Sweet Spot: 50-100 institutional holders
- Good: 30-50 holders (building interest)
- Acceptable: 100-150 holders (getting crowded)
- Avoid: < 30 holders (underowned) or > 150 holders (overcrowded)
Ownership Percentage:
- Ideal Range: 30-60% institutional ownership
- Acceptable: 20-30% or 60-80%
- Caution: < 20% (neglected) or > 80% (saturated)
Quality Signal: Superinvestor Presence (bonus)
- Holdings by legendary investors: Berkshire Hathaway, Baupost Group, Pershing Square, Greenlight Capital, Third Point, Appaloosa Management
Scoring Formula (0-100 Points)
# Calculate institutional ownership %
total_shares_held = sum(holder.shares for holder in institutional_holders)
ownership_pct = (total_shares_held / shares_outstanding) * 100
# Scoring logic
if 50 <= num_holders <= 100 and 30 <= ownership_pct <= 60:
score = 100 # O'Neil's sweet spot
elif superinvestor_present and 30 <= num_holders <= 150:
score = 90 # Superinvestor quality signal
elif (30 <= num_holders < 50 and 20 <= ownership_pct <= 40) or \
(100 < num_holders <= 150 and 40 <= ownership_pct <= 70):
score = 80 # Good ranges
elif (20 <= num_holders < 30 and 20 <= ownership_pct <= 50) or \
(50 <= num_holders <= 150 and 20 <= ownership_pct <= 70):
score = 60 # Acceptable
elif ownership_pct < 20 or ownership_pct > 80:
score = 40 # Suboptimal ownership
elif ownership_pct < 10 or ownership_pct > 90:
score = 20 # Extreme ownership (avoid)
else:
score = 50 # Default
# Superinvestor bonus
if superinvestor_present and score < 100:
score = min(score + 10, 100)Interpretation
100 Points (Ideal):
- 50-100 holders, 30-60% ownership
- Action: Perfect institutional backing - proceed with confidence
90 Points (Quality Signal):
- Superinvestor holding + good holder count
- Action: Quality investors provide margin of safety
60 Points (Acceptable):
- Decent institutional interest, but not optimal
- Action: Proceed cautiously, monitor for changes
40 Points (Suboptimal):
- Either underowned (< 20%) or overcrowded (> 80%)
- Action: Investigate why institutions are avoiding or have saturated
20 Points (Avoid):
- Extreme ownership levels (< 10% or > 90%)
- Action: Pass - either neglected or no buying power left
Historical Examples
- NVDA (2023 Q2): 74 holders, 44% ownership → 240% gain YTD
- META (2023 Q1): 68 holders, 51% ownership → 194% recovery
- TSLA (2020): Increased from 35 to 89 holders during 700% run
- AAPL (2019): Berkshire Hathaway holding (superinvestor) → confidence signal
Warning: Stocks with > 150 holders and > 80% ownership often underperform - institutions have already bought, creating selling pressure during any weakness.
---
Phase 2 Implementation Notes
This Phase 2 implementation includes C, A, N, S, I, M components, covering 80% of the full CANSLIM methodology weight. Phase 2 adds two critical components to Phase 1:
1. S (Supply/Demand): Volume-based accumulation/distribution analysis - reveals institutional buying/selling 2. I (Institutional Sponsorship): Holder count and ownership percentage - validates "smart money" backing
Key Improvements Over Phase 1:
- More accurate filtering: Volume patterns (S) eliminate stocks with distribution
- Quality validation: Institutional backing (I) confirms investment thesis
- Better ranking: Stocks with both strong fundamentals AND institutional support rise to top
- API efficient: Phase 2 requires ~203 calls for 40 stocks (81% of free tier)
Component Weights (Phase 2 - Renormalized)
| Component | Original Weight | Phase 2 Weight | Rationale |
|---|---|---|---|
| C - Current Earnings | 15% | 19% | Core fundamental - quarterly momentum |
| A - Annual Growth | 20% | 25% | Validates sustainability |
| N - Newness | 15% | 19% | Momentum confirmation |
| S - Supply/Demand | 15% | 19% | Institutional accumulation signal |
| I - Institutional | 10% | 13% | Smart money validation |
| M - Market Direction | 5% | 6% | Gating filter |
| Subtotal (Phase 2) | 80% | 100% | Renormalized to 100% (excluding L) |
Future Phases:
- Phase 3: Add L (Leadership/RS Rank) → 100% complete CANSLIM
- Phase 4: FINVIZ Elite integration for 10x speed improvement
Interpretation for Phase 2 Scores
Phase 2 implements 6 of 7 components (80% weight), providing substantially more accurate screening than Phase 1:
- 90-100 points (Phase 2): Exceptional+ (Rare multi-bagger setup with institutional backing)
- 80-89 points (Phase 2): Exceptional (Outstanding fundamentals + accumulation)
- 70-79 points (Phase 2): Strong (High-quality CANSLIM stock)
- 60-69 points (Phase 2): Above Average (Watchlist - monitor for improvement)
- <60 points (Phase 2): Below CANSLIM standards
Minimum Thresholds (Phase 2): All 6 components must meet baseline criteria:
- C ≥ 60 (18%+ quarterly EPS growth)
- A ≥ 50 (25%+ annual CAGR)
- N ≥ 40 (within 15% of 52-week high)
- S ≥ 40 (accumulation pattern, ratio ≥ 1.0)
- I ≥ 40 (30+ holders OR 20%+ ownership)
- M ≥ 40 (market in uptrend)
Key Difference from Phase 1: Phase 2 scores reflect institutional validation (S, I). A stock with great fundamentals (C, A) but distribution pattern (S < 40) will score poorly, protecting against false positives.
---
Investment Philosophy and Best Practices
When CANSLIM Works Best
1. Confirmed Bull Markets: S&P 500 above 50-day and 200-day moving averages 2. Growth-Favoring Environment: Low interest rates, economic expansion, innovation cycles 3. Sector Rotation into Growth: Technology, healthcare, consumer discretionary leading 4. Positive Market Breadth: Majority of stocks in uptrends (>50% above 200-day MA)
When to Avoid CANSLIM
1. Bear Markets: Major indices below 200-day MA, VIX > 30 2. Value-Favoring Markets: High inflation, rising rates favor defensive stocks 3. Sector Rotation into Defensives: Utilities, consumer staples, REITs leading 4. Negative Breadth: Majority of stocks in downtrends, breadth deteriorating
Position Management Rules
Entry:
- Buy stocks scoring 80+ (Phase 1) or 140+ (Full CANSLIM)
- Enter on pullback to 10-week moving average (best risk/reward)
- Position size: 10-20% of portfolio per stock, 5-10 total positions
Stops:
- Initial stop loss: 7-8% below entry (O'Neil's strict rule)
- Move to breakeven once stock up 15%
- Trail stop 10-15% below peak as stock advances
Profit Taking:
- Sell 20-25% of position when up 20-25% (lock in partial gains)
- Sell another 20-25% when up 50%
- Let winners run if still in Stage 2 uptrend (hold final 50%)
Sell Signals: 1. Stop loss hit (7-8% loss) - no exceptions 2. Market enters correction (M score drops to 0-20) 3. Fundamental deterioration (C or A component score drops below 40) 4. Climax top (parabolic move on extreme volume, then reversal) 5. Distribution (heavy volume on down days, institutional selling)
---
Common Mistakes and How to Avoid Them
Mistake 1: Ignoring Market Direction (M Component)
Error: Buying CANSLIM stocks in bear markets because "they have great fundamentals"
Reality: 75% of stocks follow market direction. Even perfect CANSLIM candidates decline 20-50% in sustained downtrends.
Solution:
- Check M component FIRST before analyzing individual stocks
- If M score < 40, raise cash to 80-100% and wait
- "Being right about a stock but wrong about the market still loses money"
Mistake 2: Chasing Stocks Far from Highs
Error: Buying stocks 30-50% below 52-week highs because "they're on sale"
Reality: Stocks trading far from highs lack institutional sponsorship and face overhead resistance. They rarely lead the next advance.
Solution:
- Focus on N component: stocks within 15% of 52-week high
- Wait for proper base breakout (7-8 weeks minimum consolidation)
- "Leaders of the last bull market rarely lead the next one"
Mistake 3: Overweighting Quarterly Earnings, Ignoring Annual Growth
Error: Buying stocks with explosive Q1 EPS growth (C) but erratic 3-year history (A)
Reality: One-quarter wonders often revert. Sustainable winners show consistent multi-year growth.
Solution:
- Require BOTH C >= 60 AND A >= 50 (Phase 1 scoring)
- View A component as validation of C component
- "Consistency beats volatility in long-term wealth building"
Mistake 4: Not Cutting Losses Quickly
Error: Holding losing positions hoping they "come back"
Reality: O'Neil's studies show that cutting losses at 7-8% prevents small losses from becoming large disasters.
Solution:
- Set stop loss at entry (7-8% below buy price)
- No exceptions: stop hit = sell immediately
- "Your first loss is your smallest loss - take it"
---
Conclusion
CANSLIM is a time-tested, quantitative methodology for identifying emerging growth leaders before major price advances. The system combines fundamental analysis (C, A) with technical confirmation (N, M) to filter for stocks with institutional sponsorship and momentum.
Phase 1 MVP (C, A, N, M) provides immediate value by screening for stocks with:
- Accelerating quarterly earnings (C)
- Consistent multi-year growth (A)
- Price momentum near new highs (N)
- Confirmation of market uptrend (M)
Future Phases will add supply/demand analysis (S), institutional sponsorship tracking (I), and relative strength leadership ranking (L) to create a complete implementation of O'Neil's system.
Remember: CANSLIM is a growth stock methodology best applied during confirmed bull markets. In bear markets, the best action is to raise cash and wait for the next follow-through day signaling institutional buying has resumed.
---
References and Further Reading
- "How to Make Money in Stocks" by William J. O'Neil (4th Edition, 2009)
- "The Successful Investor" by William J. O'Neil (2004)
- IBD (Investor's Business Daily) - Daily CANSLIM stock screens and market analysis
- MarketSmith - IBD's institutional-grade stock analysis platform with official RS Ranks
- Historical Studies: IBD's analysis of 600+ winning stocks (1953-2008) validating CANSLIM
FMP API Endpoints - CANSLIM Screener Phase 3 (Full CANSLIM)
Overview
This document specifies the Financial Modeling Prep (FMP) API endpoints required for the CANSLIM screener Phase 3 implementation (all 7 components: C, A, N, S, L, I, M).
Base URL: https://financialmodelingprep.com/api/v3
Authentication: All requests require apikey parameter
Rate Limiting:
- Free tier: 250 requests/day
- Recommended delay: 0.3 seconds between requests (200 requests/minute max)
---
C Component - Current Quarterly Earnings
Endpoint: Income Statement (Quarterly)
URL: /income-statement/{symbol}?period=quarter&limit=8
Method: GET
Parameters:
symbol: Stock ticker (e.g., "AAPL")period: "quarter" (quarterly data)limit: 8 (fetch last 8 quarters = 2 years)apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/income-statement/AAPL?period=quarter&limit=8&apikey=YOUR_KEY"Response Fields Used:
[
{
"date": "2023-09-30", # Quarter end date
"symbol": "AAPL",
"reportedCurrency": "USD",
"fillingDate": "2023-11-02",
"eps": 1.46, # Diluted EPS ← KEY
"epsdiلuted": 1.46, # Alternative field
"revenue": 89498000000, # Total revenue ← KEY
"grossProfit": 41104000000,
"operatingIncome": 26982000000,
"netIncome": 22956000000
},
# ... 7 more quarters
]Usage:
- Compare
epsfrom most recent quarter to quarter 4 positions back (YoY comparison) - Compare
revenuesame way - Calculate YoY growth percentage
API Calls: 1 per stock
---
A Component - Annual EPS Growth
Endpoint: Income Statement (Annual)
URL: /income-statement/{symbol}?period=annual&limit=5
Method: GET
Parameters:
symbol: Stock tickerperiod: "annual" (annual data)limit: 5 (fetch last 5 years for 4-year CAGR calculation)apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/income-statement/AAPL?period=annual&limit=5&apikey=YOUR_KEY"Response Fields Used:
[
{
"date": "2023-09-30", # Fiscal year end
"symbol": "AAPL",
"eps": 6.13, # Annual diluted EPS ← KEY
"revenue": 383285000000, # Annual revenue ← KEY
"netIncome": 96995000000
},
# ... 4 more years
]Usage:
- Use 4 most recent years to calculate 3-year CAGR
- CAGR = ((EPS_current / EPS_3years_ago) ^ (1/3)) - 1
- Check for stability (no down years)
- Validate with revenue CAGR
API Calls: 1 per stock
---
N Component - Newness / New Highs
Endpoint 1: Historical Prices (Daily)
URL: /historical-price-full/{symbol}?timeseries=365
Method: GET
Parameters:
symbol: Stock tickertimeseries: 365 (fetch last 365 days)apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/historical-price-full/AAPL?timeseries=365&apikey=YOUR_KEY"Response Fields Used:
{
"symbol": "AAPL",
"historical": [
{
"date": "2024-01-10",
"open": 185.16,
"high": 186.40, # Daily high ← KEY
"low": 184.00, # Daily low ← KEY
"close": 185.92, # Close price ← KEY
"volume": 50123456 # Daily volume ← KEY
},
# ... 364 more days
]
}Usage:
- Calculate 52-week high:
max(historical[0:252].high) - Calculate 52-week low:
min(historical[0:252].low) - Current price:
historical[0].close - Distance from high:
(current / 52wk_high - 1) * 100 - Detect breakout: Check if recent high >= 52wk_high with elevated volume
API Calls: 1 per stock
Endpoint 2: Quote (Real-Time Price)
URL: /quote/{symbol}
Method: GET
Parameters:
symbol: Stock ticker (can be comma-separated for batch)apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/quote/AAPL?apikey=YOUR_KEY"Response Fields Used:
[
{
"symbol": "AAPL",
"name": "Apple Inc.",
"price": 185.92, # Current price ← KEY
"changesPercentage": 1.23,
"change": 2.25,
"dayLow": 184.00,
"dayHigh": 186.40,
"yearHigh": 198.23, # 52-week high ← KEY
"yearLow": 164.08, # 52-week low ← KEY
"marketCap": 2913000000000,
"volume": 50123456,
"avgVolume": 48000000, # Average volume ← KEY
"exchange": "NASDAQ",
"sector": "Technology"
}
]Usage:
- Alternative to historical prices for 52-week high/low
- Faster (1 call vs historical prices) but less granular
- Use for quick screening; historical prices for detailed analysis
API Calls: 1 per stock (or batch multiple)
Endpoint 3: Stock News (Optional - New Product Detection)
URL: /stock_news?tickers={symbol}&limit=50
Method: GET
Parameters:
tickers: Stock ticker (comma-separated for multiple)limit: 50 (recent news articles)apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/stock_news?tickers=AAPL&limit=50&apikey=YOUR_KEY"Response Fields Used:
[
{
"symbol": "AAPL",
"publishedDate": "2024-01-10T14:30:00.000Z",
"title": "Apple Launches Revolutionary AI Chip", # ← KEY (keyword search)
"image": "https://...",
"site": "Reuters",
"text": "Apple Inc announced today...",
"url": "https://..."
},
# ... 49 more articles
]Usage:
- Search
titlefield for keywords: - High impact: "FDA approval", "patent granted", "breakthrough"
- Moderate: "new product", "product launch", "acquisition"
- Bonus points for N component if catalyst detected
- Optional: Can skip to reduce API calls (N component primarily uses price action)
API Calls: 1 per stock (optional, can be skipped to save quota)
---
M Component - Market Direction
Endpoint 1: Quote (Major Indices)
URL: /quote/^GSPC,^IXIC,^DJI
Method: GET
Parameters:
- Symbol:
^GSPC(S&P 500),^IXIC(Nasdaq),^DJI(Dow Jones) - Can batch multiple indices in single call
apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/quote/%5EGSPC,%5EIXIC,%5EDJI?apikey=YOUR_KEY"Response Fields Used:
[
{
"symbol": "^GSPC",
"name": "S&P 500",
"price": 4783.45, # Current level ← KEY
"changesPercentage": 0.85,
"change": 40.25,
"dayLow": 4750.20,
"dayHigh": 4790.10,
"yearHigh": 4818.62,
"yearLow": 4103.78,
"marketCap": null,
"volume": null,
"avgVolume": null
},
# IXIC, DJI...
]Usage:
- Get current S&P 500 price for trend analysis
- Compare to 50-day EMA (from separate call or calculated locally)
API Calls: 1 (batch call for all indices)
Endpoint 2: Historical Prices (S&P 500 for EMA Calculation)
URL: /historical-price-full/^GSPC?timeseries=60
Method: GET
Parameters:
- Symbol:
^GSPC(S&P 500) timeseries: 60 (fetch 60 days for 50-day EMA calculation)apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/historical-price-full/%5EGSPC?timeseries=60&apikey=YOUR_KEY"Response Fields Used:
{
"symbol": "^GSPC",
"historical": [
{
"date": "2024-01-10",
"close": 4783.45 # ← KEY (for EMA calculation)
},
# ... 59 more days
]
}Usage:
- Calculate 50-day EMA from closing prices
- EMA formula:
EMA_today = (Price_today * k) + (EMA_yesterday * (1 - k))wherek = 2/(50+1) - Alternative: Use simple moving average (SMA) for simplicity
API Calls: 1 (reused for all stocks)
Endpoint 3: VIX (Fear Gauge)
URL: /quote/^VIX
Method: GET
Parameters:
- Symbol:
^VIX(CBOE Volatility Index) apikey: Your FMP API key
Example Request:
curl "https://financialmodelingprep.com/api/v3/quote/%5EVIX?apikey=YOUR_KEY"Response Fields Used:
[
{
"symbol": "^VIX",
"name": "CBOE Volatility Index",
"price": 13.24, # Current VIX level ← KEY
"changesPercentage": -2.15,
"change": -0.29
}
]Usage:
- VIX < 15: Low fear (bullish environment)
- VIX 15-20: Normal (healthy market)
- VIX 20-30: Elevated (caution)
- VIX > 30: Panic (bear market signal)
API Calls: 1 (reused for all stocks)
---
L Component - Leadership / Relative Strength (Phase 3)
Endpoint: Historical Prices (52-Week)
URL: /v3/historical-price-full/{symbol}?timeseries=365
Purpose: Calculate 52-week stock performance vs S&P 500 benchmark for RS Rank estimation
Request:
curl "https://financialmodelingprep.com/api/v3/historical-price-full/NVDA?timeseries=365&apikey=YOUR_KEY"Response Structure:
{
"symbol": "NVDA",
"historical": [
{
"date": "2025-01-10",
"close": 148.50,
"open": 146.20,
"high": 149.80,
"low": 145.90,
"volume": 250000000
}
// ... 364 more days
]
}Usage:
# Stock 52-week performance
current_price = historical[0]['close']
price_52w_ago = historical[-1]['close'] # ~252 trading days
stock_perf = ((current_price / price_52w_ago) - 1) * 100
# Compare vs S&P 500 (^GSPC) for RS calculation
relative_perf = stock_perf - sp500_perfS&P 500 Benchmark Data: S&P 500 52-week historical prices are fetched once using ^GSPC (same endpoint, shared for both M component EMA and L component RS calculation). Both quote and historical use ^GSPC to ensure price scale consistency.
API Calls: 1 per stock (52-week data) + 1 shared S&P 500 call
---
S Component - Supply and Demand
Endpoint: Historical Prices (Already Fetched for N Component)
URL: /v3/historical-price-full/{symbol}?timeseries=90
Purpose: Volume-based accumulation/distribution analysis
Data Reuse: S component uses the same historical_prices data already fetched for N component (52-week high calculation). No additional API calls required.
Algorithm:
# Classify last 60 days into up-days and down-days
for day in last_60_days:
if close > previous_close:
up_days.append(volume)
elif close < previous_close:
down_days.append(volume)
# Calculate accumulation/distribution ratio
avg_up_volume = sum(up_days) / len(up_days)
avg_down_volume = sum(down_days) / len(down_days)
ratio = avg_up_volume / avg_down_volumeScoring:
- Ratio ≥ 2.0: 100 points (Strong Accumulation)
- Ratio 1.5-2.0: 80 points (Accumulation)
- Ratio 1.0-1.5: 60 points (Neutral/Weak Accumulation)
- Ratio 0.7-1.0: 40 points (Neutral/Weak Distribution)
- Ratio 0.5-0.7: 20 points (Distribution)
- Ratio < 0.5: 0 points (Strong Distribution)
API Calls: 0 (data already fetched)
---
I Component - Institutional Sponsorship (Phase 2)
Endpoint: Institutional Holders
URL: /v3/institutional-holder/{symbol}
Purpose: Analyze institutional holder count and ownership percentage
Authentication: Requires FMP API key (available on free tier)
Request:
curl "https://financialmodelingprep.com/api/v3/institutional-holder/AAPL?apikey=YOUR_KEY"Response Structure:
[
{
"holder": "Vanguard Group Inc",
"shares": 1295611697,
"dateReported": "2024-09-30",
"change": 12500000,
"changePercent": 0.0097
},
{
"holder": "Blackrock Inc.",
"shares": 1042156037,
"dateReported": "2024-09-30",
"change": -5234567,
"changePercent": -0.0050
},
{
"holder": "Berkshire Hathaway Inc",
"shares": 915560382,
"dateReported": "2024-09-30",
"change": 0,
"changePercent": 0.0000
}
// ... hundreds more holders ...
]Key Fields:
holder: Institution name (string)shares: Number of shares held (int)dateReported: 13F filing date (string, YYYY-MM-DD)change: Change in shares from previous quarter (int)changePercent: Percentage change (float)
Typical Response Size: 100-7,000 holders per stock (AAPL has ~7,111 holders)
Free Tier Availability: ✅ Available (tested with AAPL on 2026-01-12)
Usage:
# Calculate total institutional ownership
total_shares_held = sum(holder['shares'] for holder in institutional_holders)
ownership_pct = (total_shares_held / shares_outstanding) * 100
# Count unique holders
num_holders = len(institutional_holders)
# Detect superinvestors
SUPERINVESTORS = [
"BERKSHIRE HATHAWAY",
"BAUPOST GROUP",
"PERSHING SQUARE",
# ...
]
superinvestor_present = any(
superinvestor in holder['holder'].upper()
for holder in institutional_holders
for superinvestor in SUPERINVESTORS
)Scoring (O'Neil's Criteria):
- 50-100 holders + 30-60% ownership: 100 points (Sweet spot)
- Superinvestor present + good holder count: 90 points
- 30-50 holders + 20-40% ownership: 80 points
- Acceptable ranges: 60 points
- Suboptimal (< 20% or > 80% ownership): 40 points
- Extreme (< 10% or > 90% ownership): 20 points
API Calls: 1 per stock
Data Freshness: Updated quarterly (13F filings due 45 days after quarter-end)
Quality Notes:
- Large-cap stocks (AAPL, MSFT): 5,000-10,000 holders
- Mid-cap stocks: 500-2,000 holders
- Small-cap stocks: 50-500 holders
- Micro-cap stocks: < 50 holders (may lack institutional interest)
---
API Call Summary (Per Stock)
Phase 3 (Full CANSLIM: C, A, N, S, L, I, M)
Per-Stock Calls: 1. Profile (for company info): 1 call 2. Quote (for current price): 1 call 3. Income Statement (Quarterly): 1 call 4. Income Statement (Annual): 1 call 5. Historical Prices (90 days): 1 call (reused for N and S) 6. Historical Prices (365 days): 1 call (for L component RS calculation) 7. Institutional Holders: 1 call
Per-Stock Total: 7 calls (profile, quote, income×2, historical_90d, historical_365d, institutional)
Market Data Calls (One-Time per Session): 1. S&P 500 Quote (^GSPC): 1 call (shared) 2. S&P 500 Historical (^GSPC, 365 days): 1 call (shared, used for both M component EMA and L component RS benchmark) 3. VIX Quote (^VIX): 1 call (shared)
Market Data Total: 3 calls
Important: Both the quote and historical data use ^GSPC (S&P 500 index) to ensure price scale consistency for M component EMA calculation. Using SPY (ETF, ~500) for historical while ^GSPC (~5000) for quote would cause a ~10× scale mismatch.
Total for 40 Stocks:
- Per-stock: 40 stocks × 7 calls = 280 calls
- Market data: 3 calls
- Grand Total: ~283 calls (exceeds 250 free tier limit) ⚠️
Key Efficiency:
- S component: 0 extra calls (reuses historical_prices from N component's 90-day fetch)
- L component: +1 call per stock (365-day historical prices, separate cache key from 90-day)
- S&P 500 historical data: shared between M (EMA) and L (RS benchmark)
Free Tier Workaround: Use --max-candidates 35 (35 × 7 + 3 = 248 calls, within 250 limit). For full 40-stock screening, upgrade to FMP Starter tier ($29.99/mo, 750 calls/day).
Note on `mktCap` field: FMP profile API returns mktCap (not marketCap). The screener handles both field names for compatibility.
---
Rate Limiting Strategy
Implementation
import time
def rate_limited_get(url, params):
"""Enforce 0.3s delay between requests (200 requests/minute max)"""
response = requests.get(url, params=params)
time.sleep(0.3) # 300ms delay
return responseHandling 429 Errors (Rate Limit Exceeded)
def handle_rate_limit(response):
"""Retry once with 60-second wait if rate limit hit"""
if response.status_code == 429:
print("WARNING: Rate limit exceeded. Waiting 60 seconds...")
time.sleep(60)
return True # Signal to retry
return False # No retry neededFree Tier Management
Daily Quota: 250 requests/day
Strategies to Stay Within Limits: 1. Batch calls where possible: Use comma-separated symbols in quote endpoint 2. Cache market data: Fetch S&P 500/VIX once, reuse for all stocks 3. Skip optional calls: News endpoint can be omitted to save 40 calls 4. Limit universe: Analyze top 35 stocks for free tier, or 40 with Starter tier 5. Progressive filtering: Apply cheap filters first (market cap, sector) before expensive API calls
---
Error Handling
Common Errors
401 Unauthorized:
- Cause: Invalid or missing API key
- Solution: Verify
apikeyparameter, check environment variable
404 Not Found:
- Cause: Invalid symbol or endpoint
- Solution: Verify ticker symbol exists, check endpoint URL
429 Too Many Requests:
- Cause: Exceeded daily/minute rate limit
- Solution: Wait 60 seconds (minute limit) or 24 hours (daily limit)
500 Internal Server Error:
- Cause: FMP server issue
- Solution: Retry after 5 seconds, skip stock if persistent
Empty Response `[]`:
- Cause: Symbol exists but no data available (e.g., recent IPO, delisted stock)
- Solution: Skip stock, log warning
Retry Logic
MAX_RETRIES = 1
retry_count = 0
while retry_count <= MAX_RETRIES:
response = make_request()
if response.status_code == 200:
return response.json()
elif response.status_code == 429:
time.sleep(60)
retry_count += 1
else:
print(f"ERROR: {response.status_code}")
return None
print("ERROR: Max retries exceeded")
return None---
Data Quality Considerations
Freshness
- Quarterly/Annual Data: Updated within 1-2 days of earnings release
- Price Data: Real-time (15-minute delay on free tier)
- News Data: Updated continuously
Completeness
- Large-cap stocks: Complete historical data (10+ years)
- Mid-cap stocks: Mostly complete (5+ years typical)
- Small-cap/Recent IPOs: May have gaps (<2 years data)
Validation
Always check for:
nullor0values in critical fields (EPS, revenue)- Negative EPS when calculating growth rates (use absolute value in denominator)
- Missing quarters (delisted stocks, special situations)
---
Example API Call Sequence
For analyzing NVDA:
# 1. Quarterly income statement (C component)
curl "https://financialmodelingprep.com/api/v3/income-statement/NVDA?period=quarter&limit=8&apikey=YOUR_KEY"
# 2. Annual income statement (A component)
curl "https://financialmodelingprep.com/api/v3/income-statement/NVDA?period=annual&limit=5&apikey=YOUR_KEY"
# 3. Historical prices (N component)
curl "https://financialmodelingprep.com/api/v3/quote/NVDA?apikey=YOUR_KEY"
# 4. S&P 500 for market direction (M component - once per session)
curl "https://financialmodelingprep.com/api/v3/quote/%5EGSPC&apikey=YOUR_KEY"
curl "https://financialmodelingprep.com/api/v3/historical-price-full/%5EGSPC?timeseries=60&apikey=YOUR_KEY"
curl "https://financialmodelingprep.com/api/v3/quote/%5EVIX&apikey=YOUR_KEY"Total: 7 calls per stock (profile, quote, income×2, historical_90d, historical_365d, institutional) + 3 market calls (reused for all stocks)
---
Cost Analysis
Free Tier (250 requests/day)
- 40 stocks × 7 calls = 280 calls
- Market data: 3 calls
- Total: ~283 calls (exceeds 250 quota) ⚠️
- Workaround: Use
--max-candidates 35(35 × 7 + 3 = 248 calls)
Paid Tiers
- Starter ($29.99/month): 750 requests/day → ~106 stocks/run ((750 - 3) / 7)
- Professional ($79.99/month): 2000 requests/day → ~285 stocks/run ((2000 - 3) / 7)
Recommendation for Phase 3: Free tier supports up to 35 stocks (--max-candidates 35). For the default 40-stock universe, upgrade to Starter tier ($29.99/mo).
---
This API reference provides complete documentation for implementing Phase 3 (Full CANSLIM) screening. Free tier users should use --max-candidates 35 to stay within the 250 calls/day limit.
CANSLIM Score Interpretation Guide - Phase 3 (Full CANSLIM)
Overview
This guide helps users interpret CANSLIM screening results and translate composite scores into actionable investment decisions. Phase 3 implements all 7 components (C, A, N, S, L, I, M) with original O'Neil weights, presented on a 0-100 scale.
---
Composite Score Interpretation
Score Bands and Meanings
| Score Range | Rating | Meaning | Action | Position Size |
|---|---|---|---|---|
| 90-100 | Exceptional+ | Rare multi-bagger setup - all 4 components near-perfect | Immediate buy | 15-20% of portfolio |
| 80-89 | Exceptional | Outstanding fundamentals + strong momentum | Strong buy | 10-15% of portfolio |
| 70-79 | Strong | Solid across all components, minor weaknesses | Buy | 8-12% of portfolio |
| 60-69 | Above Average | Meets thresholds, one component weak | Buy on pullback | 5-8% of portfolio |
| 50-59 | Average | Marginal CANSLIM candidate | Watchlist only | Consider 3-5% if conviction high |
| 40-49 | Below Average | Fails one or more key thresholds | Monitor, do not buy | 0% |
| <40 | Weak | Does not meet CANSLIM criteria | Avoid | 0% |
---
Component-Level Analysis
How to Read Individual Component Scores
Each component contributes to the composite score with different weights:
- C (Current Earnings): 15% weight - Quarterly EPS acceleration
- A (Annual Growth): 20% weight - Multi-year consistency
- N (Newness): 15% weight - Momentum and price action
- S (Supply/Demand): 15% weight - Volume accumulation/distribution
- L (Leadership/RS Rank): 20% weight - Relative strength vs market (highest weight, tied with A!)
- I (Institutional): 10% weight - Smart money confirmation
- M (Market Direction): 5% weight - Macro environment filter
Minimum Thresholds (all must be met for "buy" rating):
- C >= 60 (18%+ quarterly EPS growth)
- A >= 50 (25%+ annual EPS CAGR)
- N >= 40 (within 25% of 52-week high)
- S >= 40 (accumulation pattern, ratio >= 1.0)
- L >= 50 (RS Rank 60+, outperforming market)
- I >= 40 (30+ holders or 20%+ ownership)
- M >= 40 (market not in downtrend)
Component Score Interpretation
| Score | C (Earnings) | A (Growth) | N (Newness) | M (Market) |
|---|---|---|---|---|
| 100 | EPS +50%+, explosive | CAGR 40%+, stable | At new high + catalyst | Strong bull, VIX <15 |
| 80 | EPS +30-49%, strong | CAGR 30-39% | Within 10% of high | Bull market, VIX <20 |
| 60 | EPS +18-29%, meets min | CAGR 25-29% | Within 15% of high | Early uptrend |
| 40 | EPS +10-17%, below | CAGR 15-24% | Within 25% of high | Choppy/neutral |
| 20 | EPS <10%, weak | CAGR <15%, erratic | >25% from high | Downtrend forming |
| 0 | Negative growth | No growth/volatile | Far from highs | Bear market - DO NOT BUY |
---
Action Recommendations by Score
90-100 Points (Exceptional+)
Characteristics:
- All 7 components aligned (C, A, N, S, L, I, M all 80+)
- Explosive earnings growth (C: 90-100)
- Multi-year growth validation (A: 90-100)
- At or near new highs with catalyst (N: 90-100)
- Strong volume accumulation (S: 80+)
- Relative strength leader (L: 90+)
- Strong institutional backing (I: 80+)
- Strong bull market environment (M: 90-100)
Historical Examples:
- NVDA (2023 Q2): Score ~97 (AI chip explosion)
- AAPL (2009 Q3): Score ~95 (iPhone 3GS launch)
- TSLA (2020 Q3): Score ~92 (Model 3 profitability)
Investment Action:
- Immediate buy - Do not wait for pullback (may never come)
- Position size: 15-20% of portfolio (high conviction)
- Entry timing: Buy now or on minor 2-3% dip
- Stop loss: 7-8% below entry (O'Neil's rule)
- Profit target: Let winners run; these can become 200-500% gainers
Risk Management:
- Even exceptional setups can fail - honor stop loss
- If market deteriorates (M drops to 0-20), sell regardless of stock quality
- Take partial profits (20-25% of position) at 20-25% gain
---
80-89 Points (Exceptional)
Characteristics:
- Most components strong, one may be slightly lower
- Strong earnings and growth (C + A both 70+)
- Good momentum (N 70+)
- Favorable market (M 60+)
Typical Weakest Component:
- Often A component (recovering from prior downturn, 3-year CAGR improving but not yet 40%+)
- Sometimes N component (8-12% from high, not quite breakout)
Investment Action:
- Strong buy
- Position size: 10-15% of portfolio (standard sizing)
- Entry timing: Ideal entry on pullback to 10-week moving average; can buy now if urgent
- Stop loss: 7-8% below entry
- Profit target: Sell 20-25% at 20-25% gain, hold remainder for 50-100%+ move
Example:
- Stock scoring 85: C=90, A=78, N=88, M=80
- Weakest = A (78): Growth is strong but not yet exceptional
- Action: Buy, expect 50-150% upside over 12-18 months if fundamentals hold
---
70-79 Points (Strong)
Characteristics:
- All components meet CANSLIM minimums
- One component may be outstanding, others solid
- No major red flags
Typical Profile:
- C: 70-80 (strong earnings, not explosive)
- A: 60-75 (meets 25% CAGR, could be more consistent)
- N: 65-80 (10-15% from high, good but not perfect momentum)
- M: 60-80 (bull market confirmed, not extended)
Investment Action:
- Buy (standard recommendation)
- Position size: 8-12% of portfolio
- Entry timing: Wait for pullback to 10-week MA or minor consolidation
- Stop loss: 7-8% below entry
- Profit target: 30-80% upside typical over 6-12 months
Risk Consideration:
- Monitor weakest component for deterioration
- If weakest drops below 50, consider reducing position
- These are quality candidates but not "home run" setups
---
60-69 Points (Above Average)
Characteristics:
- Meets CANSLIM minimums but lacks conviction
- Typically one component weak (40-50 range)
- Remaining components solid (60-70 range)
Common Weak Points:
- N weak (40-50): Stock 20-25% from 52-week high → Lacks momentum
- A weak (50-60): 3-year CAGR only 25-28% → Marginal growth rate
- C weak (50-60): Quarterly EPS growth 18-22% → Just meets minimum
Investment Action:
- Buy on pullback or Watchlist
- Position size: 5-8% of portfolio (conservative)
- Entry timing: Wait for confirmation (improvement in weak component, or pullback to support)
- Stop loss: 7-8% below entry
- Profit target: 20-40% upside typical
Monitoring Strategy:
- Set alerts for earnings reports (watch if C/A component improves)
- Check weekly if price approaches 52-week high (N component improving)
- If composite score rises to 70+, increase position
---
50-59 Points (Average)
Characteristics:
- Barely meets CANSLIM criteria
- Multiple components in 50-60 range (marginal)
- May have one strong component (70-80) but others drag down composite
Investment Action:
- Watchlist only - Do not buy yet
- Position size: 0% (not ready)
- Monitoring: Track for improvement in weak components
- Potential catalyst: Upcoming earnings report could boost C score
When to Reconsider:
- If next quarter shows earnings acceleration (C rises to 70+)
- If stock breaks to new 52-week high (N rises to 70+)
- If market strengthens significantly (M rises to 80+)
- Goal: Wait for composite to reach 65-70+ before buying
Common Scenario:
- Stock recovering from pullback
- Fundamentals improving but not yet exceptional
- Price consolidating, building base for next move
- Patient approach: Wait 1-2 quarters for confirmation
---
40-49 Points (Below Average)
Characteristics:
- Fails one or more key CANSLIM thresholds
- Typically C or A component below 50 (earnings/growth insufficient)
- Or N component very weak (stock far from highs, no momentum)
Investment Action:
- Do not buy
- Position size: 0%
- Action: Remove from watchlist unless fundamental catalyst expected
Why Avoid:
- Stocks scoring <50 rarely lead market advances
- Even if they rally, gains are typically modest (10-20%)
- Better opportunities exist in 70+ scorers
- "You don't get paid to be right about mediocre stocks"
Exception:
- If M (Market) component is weak (0-20) but C, A, N are all 70+
- This indicates bear market is suppressing good stocks
- Add to watchlist, wait for M to recover (next bull market)
---
<40 Points (Weak)
Characteristics:
- Multiple components fail CANSLIM thresholds
- Earnings decelerating (C < 40)
- Or inconsistent growth history (A < 40)
- Or price action weak (N < 40)
Investment Action:
- Avoid entirely
- These stocks are not CANSLIM candidates
- Focus efforts on higher-scoring opportunities
---
Weakest Component Analysis
Every stock report identifies the weakest component (lowest individual score). This guides risk assessment:
If Weakest = C (Current Earnings)
Risk: Earnings deceleration or miss Monitor: Upcoming quarterly earnings reports Warning signs:
- Revenue growth slowing
- Margin compression
- Guidance lowered
Action:
- If C drops to <40 post-earnings → Sell immediately
- Set tighter stop loss (5-6% vs. standard 7-8%)
---
If Weakest = A (Annual Growth)
Risk: Inconsistent multi-year history, or recovering from prior downturn Monitor: Multi-quarter trends (is growth accelerating consistently?) Warning signs:
- Next quarter shows deceleration (C component weakens)
- Revenue growth lags EPS growth significantly
Action:
- Acceptable if A >= 50 and trending up
- If A drops below 50 or C also weakens → Sell
- This is often a "prove it" situation - stock must deliver for 2-3 more quarters
---
If Weakest = N (Newness)
Risk: Lacks price momentum, overhead resistance Monitor: Weekly chart for breakout to new highs Warning signs:
- Stock fails to participate in sector rallies
- Continues drifting further from 52-week high
- Volume declining (waning interest)
Action:
- Wait for N to improve (breakout confirmation) before buying
- If N drops to <30 → Remove from watchlist
- Stocks far from highs rarely lead; don't fight the tape
---
If Weakest = M (Market Direction)
Risk: Macro headwinds, bear market Monitor: S&P 500 vs. 50-day EMA, VIX level Warning signs:
- S&P breaks below 50-day and 200-day MAs
- VIX spikes above 25-30
- Breadth deteriorates (more declining than advancing stocks)
Action:
- If M = 0-20 → Raise cash immediately to 80-100%
- Do not buy any stocks, even with perfect C, A, N
- Wait for follow-through day (FTD) signaling market bottom before re-entering
O'Neil's Wisdom: "The best offense in a bear market is cash. You can't lose money sitting on the sidelines."
---
Market Direction Override
Critical Rule: If M component score = 0-20 (bear market), do not buy any stocks regardless of composite score.
Why This Matters
Historical data shows:
- Bull markets: 75% of stocks move with market direction
- Bear markets: Even best growth stocks decline 20-50%
- Poor timing: Buying great stocks in bear markets = losses
Example Scenario
Stock XYZ:
- C Score: 100 (exceptional earnings)
- A Score: 95 (exceptional growth)
- N Score: 98 (new highs)
- M Score: 0 (bear market)
- Composite: 85.3 (Exceptional by fundamentals)
What happens: 1. Initial purchase at $100 2. Bear market persists (M remains 0-20) 3. Stock declines to $70-80 despite strong fundamentals (-20-30%) 4. Stop loss triggered at $92-93 (-7-8% loss)
Lesson: Even 85-point stocks fail in bear markets. Wait for M > 40 (market recovery) before buying.
---
Portfolio Construction Guidelines
Position Sizing by Score
| Composite Score | Max Position Size | Rationale |
|---|---|---|
| 90-100 | 15-20% | High conviction, rare setups |
| 80-89 | 10-15% | Standard sizing for strong stocks |
| 70-79 | 8-12% | Solid candidates, slight caution |
| 60-69 | 5-8% | Smaller positions, higher uncertainty |
| <60 | 0% | Do not buy |
Diversification Rules
Total Portfolio:
- 5-10 positions maximum (concentrated portfolio, O'Neil style)
- 10-20% cash reserve (for new opportunities)
- Rebalance when any position exceeds 25% due to gains
Sector Limits:
- Maximum 2 stocks per sector (avoid concentration risk)
- Exception: If sector is clearly leading (e.g., Technology in AI boom), can have 3
Score Distribution:
- 60-70% of portfolio: Stocks scoring 80+ (high conviction)
- 30-40% of portfolio: Stocks scoring 70-79 (solid candidates)
- 0% of portfolio: Stocks scoring <70 (only if very high conviction and specific catalyst)
---
Entry and Exit Rules
Entry Timing
Best Entry Points (O'Neil's research): 1. Breakout from base (7-8 week consolidation minimum)
- Stock makes new high on volume 40-50%+ above average
- Buy within 5% of breakout price ("buy point")
2. Pullback to 10-week moving average
- Stock in uptrend pulls back 8-12%
- Finds support at 10-week MA
- Buy when bounces off MA with volume
3. Early-stage base (Stages 1-2 of Weinstein cycle)
- Stock just emerging from consolidation
- Beginning Stage 2 uptrend
- Low-risk entry before major advance
Stop Loss Discipline
Initial Stop: 7-8% below entry price (non-negotiable)
Why 7-8%:
- O'Neil's 50-year research: Cutting losses at 7-8% prevents small losses from becoming disasters
- Average successful trade: +20-25% gain
- Risk/reward: Lose 7%, make 20% = 3:1 favorable
Stop Loss Adjustments:
- Once stock up 15%: Move stop to breakeven (protect capital)
- Once stock up 25%: Trail stop 10-15% below peak
- In strong uptrend: Use 10-week MA as trailing stop
Profit Taking
Partial Profit Strategy: 1. First 20-25% gain: Sell 20-25% of position (lock in profit) 2. 50% gain: Sell another 20-25% 3. Remainder: Hold for potential multi-bagger (100-500%)
Full Sell Signals: 1. Stop loss hit: 7-8% loss → Sell immediately 2. Climax top: Parabolic move on extreme volume, then reversal → Sell 50-100% 3. Distribution: Heavy selling volume (stock down on high volume 4-5 days in week) → Sell 4. Fundamental deterioration: C or A component drops below 40 → Sell 5. Market enters correction: M score drops to 0-20 → Sell all stocks
---
Common Interpretation Mistakes
Mistake 1: Ignoring M Component
Error: "This stock has a 95 composite score, I must buy it!"
Reality: If M = 0, composite score is misleading. Stock will likely decline in bear market.
Correct Approach:
- Always check M component FIRST
- If M < 40, question whether to buy
- If M = 0, do NOT buy regardless of C, A, N scores
---
Mistake 2: Chasing Low-Scoring Stocks
Error: "This 55-point stock is cheap, it could bounce!"
Reality: Stocks scoring <60 rarely lead market advances. Even if they rally, gains are modest.
Correct Approach:
- Focus on 70+ scorers
- "It's not about being right, it's about making money"
- Better to buy fewer high-quality stocks than many mediocre ones
---
Mistake 3: Overweighting N Component
Error: "This stock is 40% below its high, so it's a bargain!"
Reality: Stocks far from highs lack institutional sponsorship and face overhead resistance.
Correct Approach:
- N >= 60 (within 15% of high) is minimum
- N >= 80 (within 10% of high) is ideal
- "Leaders make new highs; laggards don't"
---
Mistake 4: Ignoring Weakest Component
Error: "Composite score is 75, that's strong enough to buy"
Reality: If weakest component is C = 40 (earnings decelerating), stock is at risk of sharp decline on next earnings miss.
Correct Approach:
- Identify weakest component
- Understand the specific risk it represents
- Monitor that component closely
- Set appropriate stop loss based on risk
---
Mistake 5: Not Cutting Losses
Error: "I'll hold this losing position; it will come back eventually"
Reality: Small losses turn into large disasters. O'Neil's data shows 90% of losses come from not selling losers early.
Correct Approach:
- Set stop loss at entry (7-8% below)
- Execute stop loss without hesitation
- "Your first loss is your smallest loss"
- Free up capital for better opportunities
---
Summary Checklist
Before buying any stock, verify:
- [ ] Composite score >= 70 (Strong or higher)
- [ ] M component >= 40 (Market not in downtrend)
- [ ] C component >= 60 (Meets earnings minimum)
- [ ] A component >= 50 (Meets growth minimum)
- [ ] N component >= 40 (Not too far from highs)
- [ ] S component >= 40 (Accumulation pattern present)
- [ ] L component >= 50 (Outperforming market, RS leader)
- [ ] I component >= 40 (Institutional backing present)
- [ ] Weakest component identified and understood
- [ ] Stop loss set at 7-8% below entry
- [ ] Position size appropriate (based on score tier)
- [ ] Sector exposure acceptable (max 2-3 stocks per sector)
- [ ] Cash reserve maintained (10-20% of portfolio)
If all boxes checked → Execute purchase
If any box unchecked → Do not buy (or wait for condition to be met)
---
This interpretation guide provides a systematic framework for translating CANSLIM scores into disciplined investment decisions. By following these guidelines, you implement O'Neil's research-backed methodology for identifying growth stock leaders while managing risk effectively.
CANSLIM Scoring System - Phase 3 (Full CANSLIM)
Overview
This document specifies the composite scoring system for the CANSLIM screener. Phase 3 implements all 7 of 7 components (C, A, N, S, L, I, M), representing 100% of the full CANSLIM methodology weight using O'Neil's original component weights.
---
Component Weights
Phase 3 Weights (Full CANSLIM - 7 Components)
| Component | Weight | Rationale |
|---|---|---|
| C - Current Earnings | 15% | Most predictive single factor (O'Neil's #1) |
| A - Annual Growth | 20% | Validates sustainability of earnings growth |
| N - Newness | 15% | Momentum confirmation critical |
| S - Supply/Demand | 15% | Volume accumulation/distribution analysis |
| L - Leadership/RS Rank | 20% | Largest weight (tied with A) - identifies sector leaders |
| I - Institutional Sponsorship | 10% | Smart money confirmation |
| M - Market Direction | 5% | Gating filter - affects all stocks |
| Total | 100% | Original O'Neil weights |
Legacy Phase Weights (Reference Only)
Phase 1 MVP (4 components - C, A, N, M): Renormalized to C 27%, A 36%, N 27%, M 10% Phase 2 (6 components - C, A, N, S, I, M): Renormalized to C 19%, A 25%, N 19%, S 19%, I 13%, M 6%
---
Component Scoring Formulas (0-100 Scale)
Each component is scored on a 0-100 point scale based on O'Neil's quantitative thresholds.
C - Current Quarterly Earnings (0-100 Points)
Input Data Required:
- Latest quarterly EPS (most recent quarter)
- Year-ago quarterly EPS (same quarter, prior year)
- Latest quarterly revenue
- Year-ago quarterly revenue
Calculation:
eps_growth_pct = ((latest_qtr_eps - year_ago_qtr_eps) / abs(year_ago_qtr_eps)) * 100
revenue_growth_pct = ((latest_qtr_revenue - year_ago_qtr_revenue) / year_ago_qtr_revenue) * 100Scoring Logic:
if eps_growth_pct >= 50 and revenue_growth_pct >= 25:
c_score = 100 # Explosive growth
elif eps_growth_pct >= 30 and revenue_growth_pct >= 15:
c_score = 80 # Strong growth
elif eps_growth_pct >= 18 and revenue_growth_pct >= 10:
c_score = 60 # Meets CANSLIM minimum
elif eps_growth_pct >= 10:
c_score = 40 # Below threshold
else:
c_score = 0 # Weak or negative growthInterpretation:
- 100 points: Exceptional - Top-tier earnings acceleration
- 80 points: Strong - Well above CANSLIM threshold
- 60 points: Acceptable - Meets minimum 18% threshold
- 40 points: Weak - Below CANSLIM standards
- 0 points: Fails - Insufficient growth
Quality Checks:
- If revenue growth < 50% of EPS growth → Investigate earnings quality (potential buyback-driven)
- If revenue is negative while EPS is positive → Red flag (cost-cutting, not growth)
---
A - Annual EPS Growth (0-100 Points)
Input Data Required:
- Annual EPS for current year and previous 3 years (4 years total)
- Annual revenue for same 4 years (validation)
Calculation:
# 3-year CAGR (Compound Annual Growth Rate)
eps_cagr_3yr = (((current_year_eps / eps_3_years_ago) ** (1/3)) - 1) * 100
revenue_cagr_3yr = (((current_year_revenue / revenue_3_years_ago) ** (1/3)) - 1) * 100
# Growth stability check
eps_values = [year1_eps, year2_eps, year3_eps, year4_eps] # chronological order
stable = all(eps_values[i] >= eps_values[i-1] for i in range(1, 4)) # No down yearsScoring Logic:
# Base score from EPS CAGR
if eps_cagr_3yr >= 40:
base_score = 90
elif eps_cagr_3yr >= 30:
base_score = 70
elif eps_cagr_3yr >= 25:
base_score = 50 # Meets CANSLIM minimum
elif eps_cagr_3yr >= 15:
base_score = 30
else:
base_score = 0
# Revenue growth validation penalty
if revenue_cagr_3yr < (eps_cagr_3yr * 0.5):
base_score = int(base_score * 0.8) # 20% penalty for weak revenue growth
# Stability bonus
if stable: # No down years
base_score += 10
a_score = min(base_score, 100) # Cap at 100Interpretation:
- 90-100 points: Exceptional - Sustainable high growth with stability
- 70-89 points: Strong - Well above 25% threshold
- 50-69 points: Acceptable - Meets CANSLIM minimum
- 30-49 points: Weak - Below threshold
- 0-29 points: Fails - Insufficient or erratic growth
Quality Checks:
- Stability bonus (+10) rewards consistency (no down years)
- Revenue validation prevents buyback-driven EPS growth from scoring high
---
N - Newness / New Highs (0-100 Points)
Input Data Required:
- Current stock price
- 52-week high price
- 52-week low price
- Recent daily volume data (30 days)
- Average volume (30-day average)
- Recent news headlines (optional, for new product detection)
Calculation:
# Distance from 52-week high
distance_from_high_pct = ((current_price / week_52_high) - 1) * 100
# Breakout detection (new high on volume)
breakout_detected = (
current_price >= week_52_high * 0.995 and # Within 0.5% of high
recent_volume > avg_volume * 1.4 # Volume 40%+ above average
)
# New product signal detection (keyword search in news)
new_product_signals = search_news_keywords([
"FDA approval", "patent granted", "breakthrough", "game-changer",
"new product", "product launch", "expansion", "acquisition"
])Scoring Logic:
# Base score from price position
if distance_from_high_pct >= -5 and breakout_detected and new_product_signals:
base_score = 100 # Perfect setup
elif distance_from_high_pct >= -10 and breakout_detected:
base_score = 80 # Strong momentum
elif distance_from_high_pct >= -15 or breakout_detected:
base_score = 60 # Acceptable
elif distance_from_high_pct >= -25:
base_score = 40 # Weak momentum
else:
base_score = 20 # Too far from highs
# Bonus for new product/catalyst signals (optional data)
if new_product_signals:
if "FDA approval" in signals or "breakthrough" in signals:
base_score += 20 # High-impact catalyst
elif "new product" in signals or "acquisition" in signals:
base_score += 10 # Moderate catalyst
n_score = min(base_score, 100) # Cap at 100Interpretation:
- 90-100 points: Exceptional - At new highs with catalysts
- 70-89 points: Strong - Near highs with volume confirmation
- 50-69 points: Acceptable - Within 15% of highs
- 30-49 points: Weak - Lacks momentum
- 0-29 points: Fails - Too far from highs, no sponsorship
Note: Price position is primary signal (80% of score). New product detection is supplementary (20% bonus).
---
M - Market Direction (0-100 Points)
Input Data Required:
- S&P 500 current price
- S&P 500 50-day Exponential Moving Average (EMA)
- VIX current level
- Follow-through day detection (optional advanced feature)
Calculation:
# Distance from 50-day EMA
distance_from_ema_pct = ((sp500_price / sp500_ema_50) - 1) * 100
# Trend determination
if distance_from_ema_pct >= 2.0:
trend = "strong_uptrend"
elif distance_from_ema_pct >= 0:
trend = "uptrend"
elif distance_from_ema_pct >= -2.0:
trend = "choppy"
elif distance_from_ema_pct >= -5.0:
trend = "downtrend"
else:
trend = "bear_market"Scoring Logic:
# Base score from trend
if trend == "strong_uptrend" and vix < 15:
base_score = 100 # Ideal conditions
elif trend == "strong_uptrend" or (trend == "uptrend" and vix < 20):
base_score = 80 # Favorable
elif trend == "uptrend":
base_score = 60 # Acceptable
elif trend == "choppy":
base_score = 40 # Neutral/caution
elif trend == "downtrend":
base_score = 20 # Weak market
else: # bear_market or vix > 30
base_score = 0 # Avoid stocks entirely
# VIX adjustment (fear gauge)
if vix < 15:
base_score += 10 # Low fear, bullish
elif vix > 30:
base_score = 0 # Panic, override trend
# Follow-through day bonus (optional advanced feature)
if follow_through_day_detected:
base_score += 10 # Confirmed institutional buying
m_score = min(max(base_score, 0), 100) # Cap between 0-100Interpretation:
- 90-100 points: Strong bull market - Aggressive buying recommended
- 70-89 points: Bull market - Standard position sizing
- 50-69 points: Early uptrend - Small initial positions
- 30-49 points: Choppy/neutral - Reduce exposure, be selective
- 10-29 points: Downtrend - Defensive posture, minimal positions
- 0 points: Bear market - Raise 80-100% cash, do not buy
Critical Rule: If M score = 0, do not buy any stocks regardless of other component scores. Market direction trumps stock selection.
---
L - Leadership / Relative Strength (0-100 Points)
Input Data Required:
- Stock 52-week historical prices
- S&P 500 52-week historical prices (benchmark)
Calculation:
# 52-week stock performance
stock_perf = ((current_price / price_52w_ago) - 1) * 100
# 52-week S&P 500 performance
sp500_perf = ((sp500_current / sp500_52w_ago) - 1) * 100
# Relative performance
relative_perf = stock_perf - sp500_perf
# RS Rank estimate (1-99 scale)
rs_rank = calculate_rs_rank(relative_perf)Scoring Logic:
if rs_rank >= 90:
base_score = 100 # Top decile leader
elif rs_rank >= 80:
base_score = 80 # Strong leader
elif rs_rank >= 70:
base_score = 60 # Above average
elif rs_rank >= 60:
base_score = 40 # Average
else:
base_score = 20 # LaggardInterpretation:
- 90-100 points: Top RS leader - stock significantly outperforming market
- 70-89 points: Strong relative strength - outperforming market
- 50-69 points: Average relative strength
- 30-49 points: Below average - underperforming market
- 0-29 points: Laggard - significantly underperforming, avoid per CANSLIM
O'Neil's Rule: "Buy stocks with an RS rating of 80 or higher. Avoid laggards below 70."
---
Composite Score Calculation
Formula (Phase 3 - Full CANSLIM)
composite_score = (
c_score * 0.15 + # Current Earnings: 15% weight
a_score * 0.20 + # Annual Growth: 20% weight
n_score * 0.15 + # Newness: 15% weight
s_score * 0.15 + # Supply/Demand: 15% weight
l_score * 0.20 + # Leadership/RS Rank: 20% weight
i_score * 0.10 + # Institutional: 10% weight
m_score * 0.05 # Market Direction: 5% weight
)
# Result: 0-100 composite scoreInterpretation Bands (Phase 3)
| Score Range | Rating | Percentile | Meaning | Action |
|---|---|---|---|---|
| 90-100 | Exceptional+ | Top 1-2% | Rare multi-bagger setup with full institutional backing | Immediate buy, aggressive sizing (15-20% position) |
| 80-89 | Exceptional | Top 5-10% | Outstanding fundamentals + accumulation | Strong buy, standard sizing (10-15% position) |
| 70-79 | Strong | Top 15-20% | High-quality CANSLIM stock | Buy on pullback, standard sizing (10-15%) |
| 60-69 | Above Average | Top 30% | Solid candidate, minor weaknesses | Watchlist, smaller sizing (5-10%) on pullback |
| 50-59 | Average | Top 50% | Meets minimums, lacks conviction | Watchlist, wait for improvement |
| 40-49 | Below Average | Bottom 50% | One or more components weak | Monitor only, do not buy |
| <40 | Weak | Bottom 30% | Fails CANSLIM criteria | Avoid |
Weakest Component Identification
For each stock, identify the component with the lowest individual score to guide further analysis:
components = {
'C': c_score,
'A': a_score,
'N': n_score,
'S': s_score,
'L': l_score,
'I': i_score,
'M': m_score
}
weakest_component = min(components, key=components.get)
weakest_score = components[weakest_component]Use Case: Helps user understand risks
- Weakest = C → Earnings deceleration risk
- Weakest = A → Lack of sustained growth history
- Weakest = N → Lacks momentum, far from highs
- Weakest = S → Distribution pattern, institutions selling
- Weakest = L → Lagging market, not a sector leader
- Weakest = I → Underowned or overcrowded, investigate further
- Weakest = M → Poor market timing, consider waiting
Formula (Phase 2 - 6 Components)
composite_score = (
c_score * 0.19 + # Current Earnings: 19% weight
a_score * 0.25 + # Annual Growth: 25% weight
n_score * 0.19 + # Newness: 19% weight
s_score * 0.19 + # Supply/Demand: 19% weight (NEW)
i_score * 0.13 + # Institutional: 13% weight (NEW)
m_score * 0.06 # Market Direction: 6% weight
)
# Result: 0-100 composite score (Phase 2)Interpretation Bands (Phase 2)
| Score Range | Rating | Percentile | Meaning | Action |
|---|---|---|---|---|
| 90-100 | Exceptional+ | Top 1-2% | Rare multi-bagger setup with full institutional backing | Immediate buy, aggressive sizing (15-20% position) |
| 80-89 | Exceptional | Top 5-10% | Outstanding fundamentals + accumulation | Strong buy, standard sizing (10-15% position) |
| 70-79 | Strong | Top 15-20% | High-quality CANSLIM stock | Buy on pullback, standard sizing (10-15%) |
| 60-69 | Above Average | Top 30% | Solid candidate, minor weaknesses | Watchlist, smaller sizing (5-10%) on pullback |
| <60 | Below Standard | Bottom 70% | Fails one or more thresholds | Monitor only, do not buy |
Key Improvement: Phase 2 scores include institutional validation (S, I components), making them more predictive than Phase 1.
Minimum Thresholds (Phase 2)
All 6 components must meet baseline criteria to qualify as a CANSLIM candidate:
thresholds = {
"C": 60, # 18%+ quarterly EPS growth
"A": 50, # 25%+ annual CAGR
"N": 40, # Within 15% of 52-week high
"S": 40, # Accumulation pattern (ratio ≥ 1.0)
"I": 40, # 30+ holders OR 20%+ ownership
"M": 40 # Market in uptrend
}
# Stock passes if ALL components >= thresholds
passes_threshold = all(score >= thresholds[comp] for comp, score in scores.items())Failure Interpretation:
- Fails C threshold → Earnings deceleration, avoid
- Fails A threshold → Lacks sustained growth, not a growth stock
- Fails N threshold → Too far from highs, wait for strength
- Fails S threshold → Distribution pattern, institutions selling
- Fails I threshold → Neglected by institutions, lacks backing
- Fails M threshold → Bear market, wait for market recovery
Weakest Component Identification (Phase 2)
components = {
'C': c_score,
'A': a_score,
'N': n_score,
'S': s_score, # NEW
'I': i_score, # NEW
'M': m_score
}
weakest_component = min(components, key=components.get)
weakest_score = components[weakest_component]Additional Interpretations:
- Weakest = S → Distribution pattern, institutions selling - caution
- Weakest = I → Underowned or overcrowded, investigate further
---
Example Calculations
Example 1: NVDA (2023 Q2) - Exceptional Setup
Component Scores:
- C Score: 100 points (EPS +429% YoY, Revenue +101% YoY)
- A Score: 95 points (3yr CAGR 89%, stable, revenue strong)
- N Score: 98 points (New all-time high, AI catalyst, breakout volume)
- M Score: 100 points (S&P 500 in strong uptrend, VIX <15)
Composite Calculation:
composite = (100 * 0.27) + (95 * 0.36) + (98 * 0.27) + (100 * 0.10)
= 27.0 + 34.2 + 26.46 + 10.0
= 97.66 pointsRating: Exceptional (97.66/100) Interpretation: Textbook CANSLIM setup - all components aligned, rare multi-bagger candidate Weakest Component: A (95) - even this is exceptional Action: Strong buy, aggressive position sizing (15-20% of portfolio)
---
Example 2: META (2023 Q3) - Strong Setup
Component Scores:
- C Score: 85 points (EPS +164% YoY, Revenue +23% YoY)
- A Score: 78 points (3yr CAGR 28%, recovery from 2022 trough, stable recent)
- N Score: 88 points (5% from 52-week high, breakout pattern)
- M Score: 80 points (S&P 500 above EMA, VIX 18)
Composite Calculation:
composite = (85 * 0.27) + (78 * 0.36) + (88 * 0.27) + (80 * 0.10)
= 22.95 + 28.08 + 23.76 + 8.0
= 82.79 pointsRating: Exceptional (82.79/100) Interpretation: Strong CANSLIM candidate, slight weakness in historical growth Weakest Component: A (78) - recovering from prior downturn Action: Buy, standard position sizing (10-15% of portfolio)
---
Example 3: Hypothetical "Average" Stock
Component Scores:
- C Score: 60 points (EPS +20% YoY - meets minimum)
- A Score: 55 points (3yr CAGR 26%, one down year)
- N Score: 65 points (12% from high, no catalyst)
- M Score: 60 points (S&P 500 just above EMA, early uptrend)
Composite Calculation:
composite = (60 * 0.27) + (55 * 0.36) + (65 * 0.27) + (60 * 0.10)
= 16.2 + 19.8 + 17.55 + 6.0
= 59.55 pointsRating: Average (59.55/100) Interpretation: Meets minimum thresholds but lacks conviction Weakest Component: A (55) - inconsistent growth history Action: Watchlist only, wait for A or N component to strengthen
---
Example 4: Bear Market Scenario (M Score = 0)
Component Scores:
- C Score: 100 points (Excellent earnings)
- A Score: 90 points (Excellent growth)
- N Score: 95 points (New highs)
- M Score: 0 points (S&P 500 in bear market, VIX > 30)
Composite Calculation:
composite = (100 * 0.27) + (90 * 0.36) + (95 * 0.27) + (0 * 0.10)
= 27.0 + 32.4 + 25.65 + 0
= 85.05 pointsRating: Exceptional fundamentals (85.05) BUT bear market Interpretation: DO NOT BUY despite high score - market direction overrides stock quality Weakest Component: M (0) - bear market environment Action: Raise cash, wait for M score > 40 (market recovery signal)
Critical Lesson: This example illustrates O'Neil's principle: "You can be right about a stock but wrong about the market, and still lose money."
---
Scoring Evolution History
Phase 3 (current) uses the original O'Neil weights across all 7 components. Previous phases used renormalized weights to compensate for missing components:
- Phase 1 MVP (4 components): C 27%, A 36%, N 27%, M 10%
- Phase 2 (6 components): C 19%, A 25%, N 19%, S 19%, I 13%, M 6%
- Phase 3 (7 components): C 15%, A 20%, N 15%, S 15%, L 20%, I 10%, M 5% (original O'Neil weights)
---
Usage Notes
For Screener Implementation
1. Calculate all 7 component scores (C, A, N, S, L, I, M) for each stock 2. Apply composite formula with Phase 3 weights 3. Identify weakest component for each stock 4. Rank stocks by composite score (highest to lowest) 5. Apply market filter FIRST: If M score < 40, warn user to reduce exposure
For User Reports
Include in output:
- Composite score (0-100)
- Rating (Exceptional / Strong / Above Average / Average / Below Average / Weak)
- Individual component scores (C, A, N, S, L, I, M)
- Weakest component identification
- Interpretation guidance
- Recommended action (buy / watchlist / avoid)
Format Example:
NVDA - NVIDIA Corporation
Composite Score: 95.2 / 100 (Exceptional+)
Component Breakdown:
C (Current Earnings): 100 / 100 - Explosive growth (EPS +429% YoY)
A (Annual Growth): 95 / 100 - Exceptional 3yr CAGR (89%)
N (Newness): 98 / 100 - At new highs with AI catalyst
S (Supply/Demand): 85 / 100 - Strong accumulation pattern
L (Leadership): 92 / 100 - RS Rank 95, sector leader
I (Institutional): 90 / 100 - 6199 holders, 68% ownership
M (Market Direction): 100 / 100 - Strong bull market
Weakest Component: S (85) - Strong accumulation
Recommendation: Strong buy - Rare multi-bagger setup---
Validation and Testing
Test Cases
Validate scoring system with known CANSLIM winners:
Expected Results (Phase 1 MVP):
- NVDA (2023 Q2): 95-100 points (Exceptional)
- META (2023 Q3): 80-90 points (Exceptional/Strong)
- AAPL (2009 Q3): 85-95 points (Exceptional)
- TSLA (2020 Q3): 80-90 points (Exceptional)
Expected Results for Non-CANSLIM Stocks:
- Declining earnings stocks: C < 40 → Composite < 50
- Stocks far from highs: N < 40 → Composite < 60
- In bear markets: M = 0 → Warning generated regardless of other scores
Scoring System Integrity Checks
1. Range Validation: All component scores must be 0-100 2. Weight Validation: Sum of weights = 100% (0.27 + 0.36 + 0.27 + 0.10 = 1.00) 3. Monotonicity: Higher inputs → Higher scores (linear or step-function increases) 4. Boundary Conditions: Test edge cases (zero EPS, negative growth, etc.) 5. Historical Validation: Backtest on known winners 2019-2024
---
This scoring system provides a quantitative, objective framework for implementing O'Neil's complete CANSLIM methodology. Phase 3 implements all 7 components with original O'Neil weights, providing comprehensive growth stock screening.
#!/usr/bin/env python3
"""
C Component - Current Quarterly Earnings Calculator
Calculates CANSLIM 'C' component score based on quarterly EPS and revenue growth.
O'Neil's Rule: "Look for companies whose current quarterly earnings per share
are up at least 18-20% compared to the same quarter the prior year."
Scoring:
- 100 points: EPS +50%+ YoY AND Revenue +25%+ YoY (explosive growth)
- 80 points: EPS +30-49% AND Revenue +15%+ (strong growth)
- 60 points: EPS +18-29% AND Revenue +10%+ (meets CANSLIM minimum)
- 40 points: EPS +10-17% (below threshold)
- 0 points: EPS <10% or negative
"""
def calculate_quarterly_growth(income_statements: list[dict]) -> dict:
"""
Calculate quarterly EPS and revenue growth (year-over-year)
Args:
income_statements: List of quarterly income statements from FMP API
(most recent quarter first, needs at least 5 quarters)
Returns:
Dict with:
- score: 0-100 points
- latest_qtr_eps_growth: YoY EPS growth percentage
- latest_qtr_revenue_growth: YoY revenue growth percentage
- latest_eps: Most recent quarterly EPS
- year_ago_eps: EPS from same quarter last year
- latest_revenue: Most recent quarterly revenue
- year_ago_revenue: Revenue from same quarter last year
- interpretation: Human-readable interpretation
- error: Error message if calculation failed
Example:
>>> income_stmts = client.get_income_statement("NVDA", period="quarter", limit=8)
>>> result = calculate_quarterly_growth(income_stmts)
>>> print(f"C Score: {result['score']}, EPS Growth: {result['latest_qtr_eps_growth']:.1f}%")
"""
# Validate input
if not income_statements or len(income_statements) < 5:
return {
"score": 0,
"error": "Insufficient quarterly data (need at least 5 quarters for YoY comparison)",
"latest_qtr_eps_growth": None,
"latest_qtr_revenue_growth": None,
"latest_eps": None,
"year_ago_eps": None,
"latest_revenue": None,
"year_ago_revenue": None,
"interpretation": "Data unavailable",
}
# Extract most recent quarter (index 0) and year-ago quarter (index 4)
latest = income_statements[0]
year_ago = income_statements[4]
# Extract EPS (try multiple field names for compatibility)
latest_eps = latest.get("eps") or latest.get("epsdiluted") or latest.get("netIncomePerShare")
year_ago_eps = (
year_ago.get("eps") or year_ago.get("epsdiluted") or year_ago.get("netIncomePerShare")
)
# Extract revenue
latest_revenue = latest.get("revenue")
year_ago_revenue = year_ago.get("revenue")
# Validate extracted data
if latest_eps is None or year_ago_eps is None:
return {
"score": 0,
"error": "EPS data missing or invalid",
"latest_qtr_eps_growth": None,
"latest_qtr_revenue_growth": None,
"latest_eps": latest_eps,
"year_ago_eps": year_ago_eps,
"latest_revenue": latest_revenue,
"year_ago_revenue": year_ago_revenue,
"interpretation": "EPS data unavailable",
}
if latest_revenue is None or year_ago_revenue is None or year_ago_revenue == 0:
return {
"score": 0,
"error": "Revenue data missing or invalid",
"latest_qtr_eps_growth": None,
"latest_qtr_revenue_growth": None,
"latest_eps": latest_eps,
"year_ago_eps": year_ago_eps,
"latest_revenue": latest_revenue,
"year_ago_revenue": year_ago_revenue,
"interpretation": "Revenue data unavailable",
}
# Calculate year-over-year growth
# Use abs() for denominator to handle negative EPS (turnaround situations)
if year_ago_eps == 0:
# Handle zero/negative EPS edge case
if latest_eps > 0 and year_ago_eps <= 0:
# Turnaround situation (negative to positive)
eps_growth = 999.9 # Cap at very high growth
else:
eps_growth = 0
else:
eps_growth = ((latest_eps - year_ago_eps) / abs(year_ago_eps)) * 100
revenue_growth = ((latest_revenue - year_ago_revenue) / year_ago_revenue) * 100
# Calculate score
score = score_current_earnings(eps_growth, revenue_growth)
# Generate interpretation
interpretation = interpret_earnings_score(score, eps_growth, revenue_growth)
# Quality check: flag if revenue growth significantly lags EPS growth
quality_warning = None
if revenue_growth < (eps_growth * 0.5) and eps_growth > 20:
quality_warning = (
"Revenue growth significantly lags EPS growth - "
"investigate earnings quality (potential buyback-driven)"
)
return {
"score": score,
"latest_qtr_eps_growth": round(eps_growth, 1),
"latest_qtr_revenue_growth": round(revenue_growth, 1),
"latest_eps": latest_eps,
"year_ago_eps": year_ago_eps,
"latest_revenue": latest_revenue,
"year_ago_revenue": year_ago_revenue,
"latest_qtr_date": latest.get("date"),
"year_ago_qtr_date": year_ago.get("date"),
"interpretation": interpretation,
"quality_warning": quality_warning,
"error": None,
}
def score_current_earnings(eps_growth: float, revenue_growth: float) -> int:
"""
Score C component based on quarterly EPS and revenue growth
Args:
eps_growth: Year-over-year EPS growth percentage
revenue_growth: Year-over-year revenue growth percentage
Returns:
Score (0-100)
Scoring Logic (from scoring_system.md):
- 100: EPS >=50% AND Revenue >=25% (explosive)
- 80: EPS 30-49% AND Revenue >=15% (strong)
- 60: EPS 18-29% AND Revenue >=10% (meets CANSLIM minimum)
- 40: EPS 10-17% (below threshold)
- 0: EPS <10% (weak/negative)
"""
# Exceptional growth
if eps_growth >= 50 and revenue_growth >= 25:
return 100
# Strong growth
if eps_growth >= 30 and revenue_growth >= 15:
return 80
# Meets CANSLIM minimum (18%+ EPS growth)
if eps_growth >= 18 and revenue_growth >= 10:
return 60
# Below threshold but positive
if eps_growth >= 10:
return 40
# Weak or negative growth
return 0
def interpret_earnings_score(score: int, eps_growth: float, revenue_growth: float) -> str:
"""
Generate human-readable interpretation of C component score
Args:
score: Component score (0-100)
eps_growth: YoY EPS growth percentage
revenue_growth: YoY revenue growth percentage
Returns:
Interpretation string
"""
if score >= 90:
return (
f"Exceptional - Explosive earnings acceleration "
f"(EPS +{eps_growth:.1f}%, Revenue +{revenue_growth:.1f}%)"
)
elif score >= 70:
return (
f"Strong - Well above CANSLIM threshold "
f"(EPS +{eps_growth:.1f}%, Revenue +{revenue_growth:.1f}%)"
)
elif score >= 50:
return (
f"Acceptable - Meets CANSLIM minimum 18% threshold "
f"(EPS +{eps_growth:.1f}%, Revenue +{revenue_growth:.1f}%)"
)
elif score >= 30:
return (
f"Below threshold - Insufficient growth "
f"(EPS +{eps_growth:.1f}%, Revenue +{revenue_growth:.1f}%)"
)
else:
return (
f"Weak - Does not meet CANSLIM criteria "
f"(EPS {eps_growth:+.1f}%, Revenue {revenue_growth:+.1f}%)"
)
def detect_earnings_acceleration(income_statements: list[dict]) -> dict:
"""
Detect if earnings are accelerating or decelerating (trend analysis)
Args:
income_statements: List of quarterly income statements (recent first)
Returns:
Dict with:
- trend: "accelerating", "stable", or "decelerating"
- recent_growth: Most recent quarter YoY growth
- prior_growth: Prior quarter YoY growth
- interpretation: Trend description
Note:
Earnings acceleration (recent > prior) is bullish signal per O'Neil.
Deceleration is early warning of potential weakness.
"""
if len(income_statements) < 6:
return {
"trend": "unknown",
"recent_growth": None,
"prior_growth": None,
"interpretation": "Insufficient data for trend analysis",
}
# Most recent quarter vs year-ago
recent_eps = income_statements[0].get("eps", 0)
recent_year_ago_eps = income_statements[4].get("eps", 0.01)
recent_growth = (
((recent_eps - recent_year_ago_eps) / abs(recent_year_ago_eps)) * 100
if recent_year_ago_eps
else 0
)
# Prior quarter vs its year-ago
prior_eps = income_statements[1].get("eps", 0)
prior_year_ago_eps = income_statements[5].get("eps", 0.01)
prior_growth = (
((prior_eps - prior_year_ago_eps) / abs(prior_year_ago_eps)) * 100
if prior_year_ago_eps
else 0
)
# Determine trend
if recent_growth > prior_growth + 5: # 5% threshold for significance
trend = "accelerating"
interpretation = (
f"Earnings accelerating ({recent_growth:.1f}% vs {prior_growth:.1f}% prior quarter)"
)
elif recent_growth < prior_growth - 5:
trend = "decelerating"
interpretation = f"Earnings decelerating ({recent_growth:.1f}% vs {prior_growth:.1f}% prior quarter) - Warning sign"
else:
trend = "stable"
interpretation = (
f"Earnings stable ({recent_growth:.1f}% vs {prior_growth:.1f}% prior quarter)"
)
return {
"trend": trend,
"recent_growth": round(recent_growth, 1),
"prior_growth": round(prior_growth, 1),
"interpretation": interpretation,
}
# Example usage and testing
if __name__ == "__main__":
print("Testing Earnings Calculator (C Component)...\n")
# Test case 1: Exceptional growth (should score 100)
test_data_exceptional = [
{"date": "2023-06-30", "eps": 2.70, "revenue": 13507000000}, # Q2 2023
{"date": "2023-03-31", "eps": 1.09, "revenue": 7192000000},
{"date": "2022-12-31", "eps": 0.88, "revenue": 6051000000},
{"date": "2022-09-30", "eps": 0.58, "revenue": 5931000000},
{"date": "2022-06-30", "eps": 0.51, "revenue": 6704000000}, # Q2 2022 (year-ago)
]
result1 = calculate_quarterly_growth(test_data_exceptional)
print("Test 1: Exceptional Growth (NVDA-like)")
print(f" Score: {result1['score']}/100")
print(f" EPS Growth: {result1['latest_qtr_eps_growth']}%")
print(f" Revenue Growth: {result1['latest_qtr_revenue_growth']}%")
print(f" Interpretation: {result1['interpretation']}\n")
# Test case 2: Meets minimum (should score 60)
test_data_minimum = [
{"date": "2023-06-30", "eps": 1.20, "revenue": 10500000000},
{"date": "2023-03-31", "eps": 1.15, "revenue": 10200000000},
{"date": "2022-12-31", "eps": 1.10, "revenue": 10000000000},
{"date": "2022-09-30", "eps": 1.05, "revenue": 9800000000},
{"date": "2022-06-30", "eps": 1.00, "revenue": 9500000000}, # +20% EPS, +10.5% revenue
]
result2 = calculate_quarterly_growth(test_data_minimum)
print("Test 2: Meets CANSLIM Minimum")
print(f" Score: {result2['score']}/100")
print(f" EPS Growth: {result2['latest_qtr_eps_growth']}%")
print(f" Revenue Growth: {result2['latest_qtr_revenue_growth']}%")
print(f" Interpretation: {result2['interpretation']}\n")
# Test case 3: Below threshold (should score 40)
test_data_weak = [
{"date": "2023-06-30", "eps": 1.12, "revenue": 10200000000},
{"date": "2023-03-31", "eps": 1.08, "revenue": 10100000000},
{"date": "2022-12-31", "eps": 1.05, "revenue": 10000000000},
{"date": "2022-09-30", "eps": 1.02, "revenue": 9900000000},
{"date": "2022-06-30", "eps": 1.00, "revenue": 9800000000}, # +12% EPS, +4% revenue
]
result3 = calculate_quarterly_growth(test_data_weak)
print("Test 3: Below Threshold")
print(f" Score: {result3['score']}/100")
print(f" EPS Growth: {result3['latest_qtr_eps_growth']}%")
print(f" Revenue Growth: {result3['latest_qtr_revenue_growth']}%")
print(f" Interpretation: {result3['interpretation']}\n")
# Test case 4: Turnaround (negative to positive EPS)
test_data_turnaround = [
{"date": "2023-06-30", "eps": 0.50, "revenue": 10000000000},
{"date": "2023-03-31", "eps": 0.20, "revenue": 9500000000},
{"date": "2022-12-31", "eps": -0.10, "revenue": 9000000000},
{"date": "2022-09-30", "eps": -0.30, "revenue": 8500000000},
{"date": "2022-06-30", "eps": -0.40, "revenue": 8000000000}, # Turnaround situation
]
result4 = calculate_quarterly_growth(test_data_turnaround)
print("Test 4: Turnaround (Negative to Positive)")
print(f" Score: {result4['score']}/100")
print(f" EPS Growth: {result4['latest_qtr_eps_growth']}%")
print(f" Revenue Growth: {result4['latest_qtr_revenue_growth']}%")
print(f" Interpretation: {result4['interpretation']}\n")
# Test acceleration detection
print("Test 5: Acceleration Detection")
accel = detect_earnings_acceleration(test_data_exceptional)
print(f" Trend: {accel['trend']}")
print(f" Recent growth: {accel['recent_growth']}%")
print(f" Prior growth: {accel['prior_growth']}%")
print(f" Interpretation: {accel['interpretation']}")
print("\n✓ All tests completed")
#!/usr/bin/env python3
"""
N Component - Newness / New Highs Calculator
Calculates CANSLIM 'N' component score based on price position relative to
52-week high and momentum indicators.
O'Neil's Rule: "Stocks making new price highs have no overhead supply.
New products, services, or management catalyze major moves."
Scoring:
- 100: Within 5% of 52-week high + breakout + new product catalyst
- 80: Within 10% of 52-week high + breakout
- 60: Within 15% of 52-week high OR breakout
- 40: Within 25% of 52-week high
- 20: >25% from 52-week high (lacks momentum)
"""
from typing import Optional
def calculate_newness(quote: dict, historical_prices: Optional[dict] = None) -> dict:
"""
Calculate N component score based on price position and momentum
Args:
quote: Stock quote data from FMP API (contains yearHigh, price, volume)
historical_prices: Optional historical price data for detailed analysis
Returns:
Dict with:
- score: 0-100 points
- distance_from_high_pct: Distance from 52-week high (%)
- current_price: Current stock price
- week_52_high: 52-week high price
- week_52_low: 52-week low price
- breakout_detected: Boolean
- interpretation: Human-readable interpretation
"""
# Validate input
if not quote:
return {
"score": 0,
"error": "Quote data missing",
"distance_from_high_pct": None,
"interpretation": "Data unavailable",
}
# Extract price data
current_price = quote.get("price")
week_52_high = quote.get("yearHigh")
week_52_low = quote.get("yearLow")
current_volume = quote.get("volume")
avg_volume = quote.get("avgVolume")
if not current_price or not week_52_high:
return {
"score": 0,
"error": "Price or 52-week high data missing",
"distance_from_high_pct": None,
"interpretation": "Data unavailable",
}
# Calculate distance from 52-week high
distance_from_high_pct = ((current_price / week_52_high) - 1) * 100
# Detect breakout (new high on elevated volume)
breakout_detected = False
if current_volume and avg_volume:
# Within 0.5% of high AND volume 40%+ above average
if current_price >= week_52_high * 0.995 and current_volume > avg_volume * 1.4:
breakout_detected = True
# Calculate score
score = score_newness(distance_from_high_pct, breakout_detected)
# Generate interpretation
interpretation = interpret_newness_score(score, distance_from_high_pct, breakout_detected)
return {
"score": score,
"distance_from_high_pct": round(distance_from_high_pct, 1),
"current_price": current_price,
"week_52_high": week_52_high,
"week_52_low": week_52_low,
"breakout_detected": breakout_detected,
"current_volume": current_volume,
"avg_volume": avg_volume,
"volume_ratio": round(current_volume / avg_volume, 2) if avg_volume else None,
"interpretation": interpretation,
"error": None,
}
def score_newness(distance_from_high_pct: float, breakout_detected: bool) -> int:
"""
Score N component based on price position and breakout
Args:
distance_from_high_pct: Distance from 52-week high (negative = below high)
breakout_detected: Boolean indicating volume-confirmed breakout
Returns:
Score (0-100)
Scoring Logic:
- Within 5% of high + breakout: 100
- Within 10% of high + breakout: 80
- Within 15% of high OR breakout: 60
- Within 25% of high: 40
- >25% from high: 20
"""
if distance_from_high_pct >= -5 and breakout_detected:
return 100 # Perfect setup - at new highs with volume
elif distance_from_high_pct >= -10 and breakout_detected:
return 80 # Strong momentum
elif distance_from_high_pct >= -15 or breakout_detected:
return 60 # Acceptable
elif distance_from_high_pct >= -25:
return 40 # Weak momentum
else:
return 20 # Too far from highs, lacks sponsorship
def interpret_newness_score(score: int, distance: float, breakout: bool) -> str:
"""
Generate human-readable interpretation
Args:
score: Component score
distance: Distance from 52-week high (%)
breakout: Breakout detected flag
Returns:
Interpretation string
"""
breakout_text = " with volume breakout" if breakout else ""
if score >= 90:
return f"Exceptional - At new highs{breakout_text} ({distance:+.1f}% from high)"
elif score >= 70:
return f"Strong - Near 52-week high{breakout_text} ({distance:+.1f}% from high)"
elif score >= 50:
return f"Acceptable - Within 15% of high ({distance:+.1f}% from high)"
elif score >= 30:
return f"Weak - Lacks momentum ({distance:+.1f}% from high)"
else:
return f"Poor - Too far from highs, overhead resistance ({distance:+.1f}% from high)"
# Example usage
if __name__ == "__main__":
print("Testing New Highs Calculator (N Component)...\n")
# Test 1: At new high with breakout (score 100)
test_quote_1 = {
"symbol": "NVDA",
"price": 495.50,
"yearHigh": 496.00,
"yearLow": 280.00,
"volume": 60000000,
"avgVolume": 40000000,
}
result1 = calculate_newness(test_quote_1)
print(f"Test 1: New High + Breakout - Score: {result1['score']}")
print(f" {result1['interpretation']}\n")
# Test 2: Within 10% of high, no breakout (score 60)
test_quote_2 = {
"symbol": "META",
"price": 450.00,
"yearHigh": 490.00,
"yearLow": 320.00,
"volume": 15000000,
"avgVolume": 16000000,
}
result2 = calculate_newness(test_quote_2)
print(f"Test 2: Near High, No Breakout - Score: {result2['score']}")
print(f" {result2['interpretation']}\n")
# Test 3: Far from high (score 20)
test_quote_3 = {
"symbol": "XYZ",
"price": 50.00,
"yearHigh": 80.00,
"yearLow": 45.00,
"volume": 1000000,
"avgVolume": 1200000,
}
result3 = calculate_newness(test_quote_3)
print(f"Test 3: Far from High - Score: {result3['score']}")
print(f" {result3['interpretation']}\n")
print("✓ All tests completed")
"""Shared fixtures for CANSLIM Screener tests"""
import os
import sys
# Add scripts directory to path so modules can be imported
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
# Add calculators subdirectory
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "calculators"))
# Add tests directory to path so helpers can be imported
sys.path.insert(0, os.path.dirname(__file__))
Related skills
FAQ
What are the seven CANSLIM factors?
CANSLIM Screener evaluates seven factors from William O'Neil's methodology: Current quarterly earnings, Annual earnings growth, New products or management, Supply and demand, Leader or laggard status, Institutional sponsorship, and Market direction.
When should CANSLIM Screener run?
CANSLIM Screener runs before investment or trading decisions when a developer wants systematic growth stock screening using IBD's CANSLIM framework instead of unstructured ticker research.
Is Canslim Screener safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.