Last active
May 2, 2026 08:21
-
-
Save jweinst1/b103abc706fb5090a0e51de4f1ebb03e to your computer and use it in GitHub Desktop.
get russell 2000 tickers in python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import csv | |
| import io | |
| def get_russell_2000_tickers() -> list[str]: | |
| """ | |
| Fetches current Russell 2000 constituents from iShares IWM ETF holdings CSV. | |
| Pure Python + built-in csv module. No pandas. Handles the messy header + huge disclaimer. | |
| Returns clean tickers ready for yfinance (BRK.B → BRK-B). | |
| """ | |
| url = "https://www.ishares.com/us/products/239710/ishares-russell-2000-etf/1467271812596.ajax?fileType=csv&fileName=IWM" | |
| headers = { | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", | |
| "Accept": "text/csv" | |
| } | |
| resp = requests.get(url, headers=headers, timeout=20) | |
| resp.raise_for_status() | |
| text = resp.text | |
| lines = [line.strip() for line in text.splitlines() if line.strip()] | |
| # Find the start of the real data (header row that contains "Ticker") | |
| start_idx = 0 | |
| for i, line in enumerate(lines): | |
| if line.startswith('"Ticker"') or ('Ticker' in line and 'Name' in line): | |
| start_idx = i | |
| break | |
| else: | |
| # Fallback: first line with many commas | |
| for i, line in enumerate(lines): | |
| if line.count(',') > 8: | |
| start_idx = i | |
| break | |
| # Parse only the data section with csv module | |
| data_section = '\n'.join(lines[start_idx:]) | |
| csv_file = io.StringIO(data_section) | |
| reader = csv.reader(csv_file) | |
| tickers = [] | |
| header = next(reader, None) # skip header row | |
| if header: | |
| # Find Ticker column index (handles slight column name variations) | |
| ticker_col = next((i for i, col in enumerate(header) if col and ('Ticker' in col or 'Symbol' in col)), 0) | |
| for row in reader: | |
| if not row or len(row) <= ticker_col: | |
| continue | |
| ticker = row[ticker_col].strip() | |
| # Stop when we hit the legal disclaimer text | |
| if (ticker.startswith('The content contained herein') or | |
| ticker.startswith('©') or | |
| ticker.startswith('Holdings subject to change') or | |
| 'BlackRock' in ticker): | |
| break | |
| # Skip junk rows | |
| if (not ticker or | |
| ticker == '-' or | |
| ticker.startswith('RTYM') or # futures | |
| len(ticker) < 2): | |
| continue | |
| # Clean for yfinance | |
| ticker = ticker.replace('.', '-') | |
| tickers.append(ticker) | |
| clean_tickers = sorted(set(tickers)) | |
| print(f"✅ Successfully parsed {len(clean_tickers)} Russell 2000 tickers") | |
| return clean_tickers | |
| print(get_russell_2000_tickers()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment