Quant-style S&P 500 stock selection and portfolio allocation engine with risk profiles (conservative, moderate, aggressive), beta-aware scoring, and a companion backtest tool.
- Builds a ranked S&P 500 universe from fundamentals.
- Scores stocks using valuation, growth, profitability, and beta-aware style logic.
- Produces style-specific top picks and a dollar/share allocation plan.
- Caches ranked universe data and refreshes weekly (configurable).
- Computes portfolio beta (selected style + style summary).
- Backtests style portfolios vs benchmark (SPY by default) with risk metrics.
stock_picker.py: Main ranking + allocation engine.backtest_styles.py: Backtest script for style portfolios.sp500_ranked.csv: Cached ranked universe.allocation_plan.csv: Latest allocation output.style_backtest_metrics.csv: Backtest performance metrics.style_equity_curves.csv: Equity curves for styles + benchmark.style_holdings_snapshot.csv: Holdings/weights snapshot used in backtest.
- Python 3.10+
- Internet access for market/fundamental data
git clone https://github.com/manishklach/stock-picker.git
cd stock-pickerpython -m pip install --upgrade pip
python -m pip install yfinance pandas numpy lxml html5libpython -u stock_picker.py --refresh-ranking-now --budget 100000 --top-n 10 --allocation-style moderatepython -u stock_picker.py --budget 100000 --top-n 10 --allocation-style aggressive --verbosepython -u backtest_styles.py --start-date 2022-01-01 --rebalance monthly --benchmark SPY --top-n 10python -u stock_picker.py [options]--budget: Portfolio budget (default100000).--top-n: Number of stocks to pick (default10).--allocation-style:conservative | moderate | aggressive.--verbose: Detailed fetch/allocation logs.
--rank-refresh-days: Reusesp500_ranked.csvif newer than this many days (default7).--refresh-ranking-now/--force-refresh: Force fresh ranking now.--universe-csv: Ranked universe file path (defaultsp500_ranked.csv).--tickers-csv: Optional local symbol list (CSV withSymbolcolumn).
--workers: Parallel workers for fundamentals fetch (default8).--overall-timeout: Max seconds for fundamentals phase (default240).--progress-every: Progress print interval (default25).--max-tickers: Optional cap for quicker runs.
--allocation-csv: Allocation output CSV path (defaultallocation_plan.csv).--beta-coverage-threshold: Warn when selected picks with beta are below threshold fraction (default0.70).
-
conservative -
Selection: quality + valuation with lower-beta tilt.
-
Sizing: smoother weights and tighter concentration cap.
-
moderate -
Selection: balanced core score with light low-beta preference.
-
Sizing: balanced concentration.
-
aggressive -
Selection: growth-heavy with high-beta tilt.
-
Sizing: more concentrated on top-ranked names.
The script prints and/or saves:
- Top picks table (
Style_Score,Total_Score,Beta, core metrics) - Allocation plan (
Ticker,Weight,Target_Dollars,Shares,Invested_Dollars,Beta) - Style beta summary table (all three styles)
- Selected-style realized portfolio beta
- Beta coverage warning (if below threshold)
python -u backtest_styles.py [options]--start-date: Backtest start (YYYY-MM-DD, default2022-01-01).--end-date: Optional end date.--top-n: Number of names per style (default10).--rebalance:none | weekly | monthly | quarterly(defaultmonthly).--benchmark: Benchmark ticker (defaultSPY).--risk-free-rate: Annual risk-free rate for Sharpe/alpha (default0.04).--ranked-csv: Input ranked universe CSV (defaultsp500_ranked.csv).
style_backtest_metrics.csvstyle_equity_curves.csvstyle_holdings_snapshot.csv
Total_Return,CAGR,Volatility,SharpeMax_Drawdown,CalmarBeta_vs_Benchmark,Alpha_CAPMHit_Rate,Avg_Turnover_per_Rebalance,Observations
python -u stock_picker.py --budget 100000 --top-n 10 --allocation-style moderatepython -u stock_picker.py --refresh-ranking-now --allocation-style aggressive --budget 100000python -u stock_picker.py --allocation-style conservative --beta-coverage-threshold 0.85python -u backtest_styles.py --start-date 2021-01-01 --rebalance quarterly --benchmark SPY --top-n 15- Ranked universe uses currently fetched fundamentals and is cached.
- Backtest uses style baskets derived from ranked universe and price-based portfolio simulation.
- Backtest does not reconstruct historical fundamentals point-in-time by rebalance date.
- Missing beta values are excluded from weighted-beta calculations.
- Market data source behavior depends on Yahoo Finance API availability.
-
If fetch hangs/slow:
-
Reduce
--workersand/or--max-tickers. -
Use
--overall-timeoutto cap fundamentals fetch time. -
If beta coverage is low:
-
Run with
--refresh-ranking-now. -
Increase
--top-nfor broader basket. -
If ranked CSV schema is old:
-
Rebuild with
--refresh-ranking-now.
Add your preferred license (MIT/Apache-2.0/etc.) in a LICENSE file.
This project is for research/education. It is not investment advice.
python -m pip install -r requirements.txt; python -u stock_picker.py --refresh-ranking-now --budget 100000 --top-n 10 --allocation-style moderateThis installs dependencies and generates a fresh ranked universe plus allocation in one command.
You now have three shortcut options:
tasks.ps1(best on Windows PowerShell)justfile(if you usejust)Makefile(if you usemake)
# install deps
.\tasks.ps1 -Task install
# refresh ranked universe and build moderate allocation
.\tasks.ps1 -Task refresh
# run style portfolios
.\tasks.ps1 -Task run-conservative
.\tasks.ps1 -Task run-moderate
.\tasks.ps1 -Task run-aggressive
# run aggressive with extra flags
.\tasks.ps1 -Task run-aggressive -Extra '--verbose --beta-coverage-threshold 0.8'
# backtest
.\tasks.ps1 -Task backtest
.\tasks.ps1 -Task backtest -Extra '--start-date 2021-01-01 --rebalance quarterly --top-n 15'just install
just refresh
just run-conservative
just run-moderate
just run-aggressive
just backtestmake install
make refresh
make run-conservative
make run-moderate
make run-aggressive
make backtestBelow are real example runs from this project (PowerShell, Python venv).
Refreshing ranked universe from live data (--refresh-ranking-now set)...
Started fundamentals fetch for 503 tickers with 8 workers...
Fetched metrics for 25/503 tickers
...
Fetched metrics for 503/503 tickers
Saved refreshed ranked universe -> sp500_ranked.csv
Top picks:
Ticker Style_Score Total_Score Beta Forward_PE PEG Earnings_Growth Revenue_Growth
PSX 7.500726 6.194144 0.871 13.058196 0.5211 2427.284 0.013
KEY 1.935328 1.502370 1.086 9.350459 1.8911 NaN 1.315
...
Style beta summary (Top-N picks by style):
Style Portfolio_Beta Beta_Coverage TopN
conservative 0.877956 1.0 10
moderate 1.041412 1.0 10
aggressive 1.230383 1.0 10
Allocation style: aggressive
Selected-style realized portfolio beta: 1.228
Beta coverage (selected picks): 100.0%
Budget: $100,000.00
Invested: $99,531.12
Cash left: $468.88
Saved allocation plan -> allocation_plan.csv
Using cached ranked universe (sp500_ranked.csv, age=0.0 days, refresh in ~7.0 days)
...
Style beta summary (Top-N picks by style):
Style Portfolio_Beta Beta_Coverage TopN
conservative 0.877956 1.0 10
moderate 1.041412 1.0 10
aggressive 1.230383 1.0 10
Allocation style: moderate
Selected-style realized portfolio beta: 1.040
Beta coverage (selected picks): 100.0%
Budget: $100,000.00
Invested: $99,381.30
Cash left: $618.70
Saved allocation plan -> allocation_plan.csv
Using cached ranked universe (sp500_ranked.csv, age=0.0 days, refresh in ~7.0 days)
...
Style beta summary (Top-N picks by style):
Style Portfolio_Beta Beta_Coverage TopN
conservative 0.877956 1.0 10
moderate 1.041412 1.0 10
aggressive 1.230383 1.0 10
Allocation style: conservative
Selected-style realized portfolio beta: 0.879
Beta coverage (selected picks): 100.0%
Budget: $100,000.00
Invested: $99,144.41
Cash left: $855.59
Saved allocation plan -> allocation_plan.csv