Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Filter and screen stocks by financial metrics like P/E ratio, market cap, dividend yield, and growth rates. Analyze and compare stocks from CSV data.
.claude/skills/nicepkg-stock-screener/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-16 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 124% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 128% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 101% | 0% |
Filter stocks by financial metrics and perform comparative analysis.
pythonfrom stock_screener import StockScreener screener = StockScreener() # Load stock data screener.load_csv("stocks.csv") # Apply filters results = screener.filter( pe_ratio=(0, 20), market_cap_min=1e9, dividend_yield_min=2.0 ) print(results)
bash# Basic screening python stock_screener.py --input stocks.csv --pe-max 20 --div-min 2.0 # Multiple filters python stock_screener.py --input stocks.csv --pe 5 25 --pb-max 3 --cap-min 1B # Sector filter python stock_screener.py --input stocks.csv --sector Technology --pe-max 30 # Rank by metric python stock_screener.py --input stocks.csv --rank-by dividend_yield --top 20 # Compare specific stocks python stock_screener.py --input stocks.csv --compare AAPL MSFT GOOGL # Export results python stock_screener.py --input stocks.csv --pe-max 15 --output screened.csv
csvsymbol,name,sector,price,pe_ratio,pb_ratio,market_cap,dividend_yield,eps,revenue_growth,profit_margin AAPL,Apple Inc,Technology,175.50,28.5,45.2,2.8e12,0.5,6.16,8.5,25.3 MSFT,Microsoft,Technology,380.00,35.2,12.8,2.8e12,0.8,10.79,12.3,36.7 JNJ,Johnson & Johnson,Healthcare,155.00,15.2,5.8,3.8e11,2.9,10.20,5.2,22.1
pythonclass StockScreener: def __init__(self) # Data Loading def load_csv(self, filepath: str) -> 'StockScreener' def load_dataframe(self, df: pd.DataFrame) -> 'StockScreener' # Filtering def filter(self, **criteria) -> pd.DataFrame def filter_by_sector(self, sectors: List[str]) -> 'StockScreener' def filter_by_metric(self, metric: str, min_val: float = None, max_val: float = None) -> 'StockScreener' # Screening Presets def value_screen(self) -> pd.DataFrame def growth_screen(self) -> pd.DataFrame def dividend_screen(self) -> pd.DataFrame def quality_screen(self) -> pd.DataFrame def custom_screen(self, criteria: Dict) -> pd.DataFrame # Analysis def compare(self, symbols: List[str]) -> pd.DataFrame def rank_by(self, metric: str, ascending: bool = True) -> pd.DataFrame def sector_summary(self) -> pd.DataFrame def metric_distribution(self, metric: str) -> Dict # Scoring def score_stocks(self, weights: Dict[str, float] = None) -> pd.DataFrame def percentile_rank(self, metrics: List[str]) -> pd.DataFrame # Export def to_csv(self, filepath: str) -> str def to_json(self, filepath: str) -> str def summary_report(self) -> str
pythonscreener.filter( pe_ratio=(5, 20), # P/E between 5 and 20 pb_ratio_max=3.0, # P/B ratio under 3 ps_ratio_max=5.0, # Price/Sales under 5 peg_ratio_max=1.5 # PEG ratio under 1.5 )
pythonscreener.filter( market_cap_min=1e9, # Min $1B market cap market_cap_max=10e9, # Max $10B (mid-cap) revenue_min=500e6 # Min $500M revenue )
pythonscreener.filter( dividend_yield_min=2.0, # Min 2% dividend dividend_yield_max=8.0, # Max 8% (avoid yield traps) payout_ratio_max=75 # Sustainable payout )
pythonscreener.filter( revenue_growth_min=10, # Min 10% revenue growth earnings_growth_min=15, # Min 15% earnings growth eps_growth_min=10 # Min 10% EPS growth )
pythonscreener.filter( profit_margin_min=15, # Min 15% profit margin roe_min=15, # Min 15% return on equity debt_to_equity_max=1.0, # Max 1.0 D/E ratio current_ratio_min=1.5 # Min 1.5 current ratio )
pythonresults = screener.value_screen() # Finds undervalued stocks: # - P/E < 15 # - P/B < 2 # - Dividend yield > 2% # - Profit margin > 10%
pythonresults = screener.growth_screen() # Finds growth stocks: # - Revenue growth > 15% # - Earnings growth > 20% # - PEG ratio < 2
pythonresults = screener.dividend_screen() # Finds dividend stocks: # - Dividend yield 2-8% # - Payout ratio < 75% # - 5+ years dividend history
pythonresults = screener.quality_screen() # Finds high-quality stocks: # - ROE > 15% # - Profit margin > 15% # - D/E < 0.5 # - Current ratio > 2
pythoncomparison = screener.compare(["AAPL", "MSFT", "GOOGL"]) # Returns: # AAPL MSFT GOOGL # price 175.50 380.00 140.00 # pe_ratio 28.50 35.20 25.30 # market_cap 2.8T 2.8T 1.7T # dividend_yield 0.50 0.80 0.00 # profit_margin 25.30 36.70 22.50 # ...
python# Top 20 by dividend yield top_dividend = screener.rank_by("dividend_yield", ascending=False).head(20)
python# Score stocks with custom weights scores = screener.score_stocks({ "pe_ratio": -0.2, # Lower is better "dividend_yield": 0.3, # Higher is better "profit_margin": 0.3, # Higher is better "revenue_growth": 0.2 # Higher is better }) # Returns stocks ranked by composite score
python# See where each stock ranks on multiple metrics ranked = screener.percentile_rank(["pe_ratio", "dividend_yield", "profit_margin"]) # Returns percentile (0-100) for each metric
pythonsector_stats = screener.sector_summary() # Returns: # sector | count | avg_pe | avg_div | avg_margin # Technology | 45 | 28.5 | 1.2 | 22.3 # Healthcare | 32 | 18.2 | 2.1 | 18.7 # Financials | 28 | 12.5 | 3.2 | 25.1
pythonscreener = StockScreener() screener.load_csv("sp500.csv") # Apply filters results = screener.filter( pe_ratio=(5, 15), dividend_yield_min=3.0, payout_ratio_max=70, profit_margin_min=10 ) # Rank by dividend yield top = results.sort_values("dividend_yield", ascending=False).head(10) print(top[["symbol", "name", "pe_ratio", "dividend_yield", "payout_ratio"]])
pythonresults = screener.filter( revenue_growth_min=15, earnings_growth_min=15, peg_ratio_max=1.5, pe_ratio_max=25 )
python# Filter to technology sector tech = screener.filter_by_sector(["Technology"]).filter( market_cap_min=10e9, profit_margin_min=15 ) # Compare top tech stocks comparison = screener.compare(tech["symbol"].head(5).tolist())
pythonscreener.filter(pe_ratio_max=20).to_csv("value_stocks.csv")
pythonscreener.filter(dividend_yield_min=3).to_json("dividend_stocks.json")
pythonreport = screener.summary_report() # Returns formatted text summary of screening results
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-16 | fail→pass | 12,967 | 6,419 | -50% | 1 | 1 | 0% | 2,471 | 3,830 | +55% | 0 | 0 | — |
case-01 | fail→pass | 10,668 | 6,765 | -37% | 1 | 1 | 0% | 1,997 | 3,691 | +85% | 0 | 0 | — |
case-02 | pass→pass | 12,941 | 4,873 | -62% | 1 | 1 | 0% | 2,511 | 3,582 | +43% | 0 | 0 | — |
case-03 | pass→pass | 17,319 | 5,562 | -68% | 1 | 1 | 0% | 3,844 | 3,616 | -6% | 0 | 0 | — |
case-04 | fail→pass | 7,774 | 2,311 | -70% | 1 | 1 | 0% | 1,325 | 2,969 | +124% | 0 | 0 | — |
case-05 | fail→pass | 8,061 | 2,448 | -70% | 1 | 1 | 0% | 1,301 | 2,965 | +128% | 0 | 0 | — |
case-06 | fail→pass | 8,785 | 2,102 | -76% | 1 | 1 | 0% | 1,453 | 2,917 | +101% | 0 | 0 | — |
case-07 | fail→pass | 10,301 | 1,582 | -85% | 1 | 1 | 0% | 1,697 | 2,821 | +66% | 0 | 0 | — |
case-08 | fail→pass | 20,261 | 2,859 | -86% | 1 | 1 | 0% | 1,823 | 3,192 | +75% | 0 | 0 | — |
case-09 | fail→pass | 9,780 | 2,980 | -70% | 1 | 1 | 0% | 1,804 | 3,170 | +76% | 0 | 0 | — |
case-10 | fail→pass | 12,142 | 4,436 | -63% | 1 | 1 | 0% | 2,288 | 3,475 | +52% | 0 | 0 | — |
case-11 | fail→pass | 11,109 | 3,482 | -69% | 1 | 1 | 0% | 2,001 | 3,234 | +62% | 0 | 0 | — |
case-12 | fail→pass | 10,978 | 3,750 | -66% | 1 | 1 | 0% | 1,641 | 3,171 | +93% | 0 | 0 | — |
case-13 | fail→pass | 10,298 | 2,517 | -76% | 1 | 1 | 0% | 1,511 | 2,979 | +97% | 0 | 0 | — |
case-14 | fail→pass | 6,934 | 1,762 | -75% | 1 | 1 | 0% | 1,110 | 2,815 | +154% | 0 | 0 | — |
case-15 | fail→pass | 9,832 | 3,452 | -65% | 1 | 1 | 0% | 1,579 | 3,198 | +103% | 0 | 0 | — |
case-17 | fail→pass | 14,434 | 7,400 | -49% | 1 | 1 | 0% | 2,725 | 4,108 | +51% | 0 | 0 | — |
case-18 | fail→pass | 11,696 | 5,075 | -57% | 1 | 1 | 0% | 2,107 | 3,546 | +68% | 0 | 0 | — |
case-19 | pass→pass | 10,098 | 3,271 | -68% | 1 | 1 | 0% | 1,562 | 3,124 | +100% | 0 | 0 | — |
case-20 | pass→pass | 9,589 | 4,010 | -58% | 1 | 1 | 0% | 1,596 | 3,221 | +102% | 0 | 0 | — |
case-21 | pass→pass | 10,416 | 9,567 | -8% | 1 | 1 | 0% | 2,225 | 4,549 | +104% | 0 | 0 | — |
case-22 | pass→pass | 10,994 | 12,003 | +9% | 1 | 1 | 0% | 1,934 | 4,753 | +146% | 0 | 0 | — |
case-23 | pass→pass | 9,323 | 8,867 | -5% | 1 | 1 | 0% | 1,761 | 4,306 | +145% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +70 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.