TradingAgents/tradingagents/dataflows/fmp_api.py

234 lines
8.5 KiB
Python

"""
Yahoo Finance API - Short Interest Data using yfinance
Identifies potential short squeeze candidates with high short interest
"""
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Annotated
from tradingagents.dataflows.market_data_utils import format_markdown_table, format_market_cap
from tradingagents.dataflows.y_finance import get_ticker_info
from tradingagents.utils.logger import get_logger
logger = get_logger(__name__)
def get_short_interest(
min_short_interest_pct: Annotated[float, "Minimum short interest % of float"] = 10.0,
min_days_to_cover: Annotated[float, "Minimum days to cover ratio"] = 2.0,
top_n: Annotated[int, "Number of top results to return"] = 20,
) -> str:
"""
Get stocks with high short interest using yfinance (FREE data source).
Checks a watchlist of stocks for high short interest data from Yahoo Finance.
High short interest + positive catalyst = short squeeze potential.
Note: This scans a predefined universe of stocks. For comprehensive scanning,
consider using a stock screener API with short interest filters.
Args:
min_short_interest_pct: Minimum short interest as % of float
min_days_to_cover: Minimum days to cover ratio
top_n: Number of top results to return
Returns:
Formatted markdown report of high short interest stocks
"""
try:
# Curated watchlist of stocks known for volatility/short interest
# In a production system, this would come from a screener API
watchlist = [
# Meme stocks & high short interest candidates
"GME",
"AMC",
"BBBY",
"BYND",
"CLOV",
"WISH",
"PLTR",
"SPCE",
# EV & Tech
"RIVN",
"LCID",
"NIO",
"TSLA",
"NKLA",
"PLUG",
"FCEL",
# Biotech (often heavily shorted)
"SAVA",
"NVAX",
"MRNA",
"BNTX",
"VXRT",
"SESN",
"OCGN",
# Retail & Consumer
"PTON",
"W",
"CVNA",
"DASH",
"UBER",
"LYFT",
# Finance & REITs
"SOFI",
"HOOD",
"COIN",
"SQ",
"AFRM",
# Small caps with squeeze potential
"APRN",
"ATER",
"BBIG",
"CEI",
"PROG",
"SNDL",
# Others
"TDOC",
"ZM",
"PTON",
"NFLX",
"SNAP",
"PINS",
]
logger.info(f"Checking short interest for {len(watchlist)} tickers...")
high_si_candidates = []
# Use threading to speed up API calls
def fetch_short_data(ticker):
try:
info = get_ticker_info(ticker)
# Get short interest data
short_pct = info.get("shortPercentOfFloat", info.get("sharesPercentSharesOut", 0))
if short_pct and isinstance(short_pct, (int, float)):
short_pct = short_pct * 100 # Convert to percentage
else:
return None
# Only include if meets criteria
if short_pct >= min_short_interest_pct:
# Get other data
price = info.get("currentPrice", info.get("regularMarketPrice", 0))
market_cap = info.get("marketCap", 0)
volume = info.get("volume", info.get("regularMarketVolume", 0))
# Categorize squeeze potential
if short_pct >= 30:
signal = "extreme_squeeze_risk"
elif short_pct >= 20:
signal = "high_squeeze_potential"
elif short_pct >= 15:
signal = "moderate_squeeze_potential"
else:
signal = "low_squeeze_potential"
return {
"ticker": ticker,
"price": price,
"market_cap": market_cap,
"volume": volume,
"short_interest_pct": short_pct,
"signal": signal,
}
except Exception:
return None
# Fetch data in parallel (faster)
with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(fetch_short_data, ticker): ticker for ticker in watchlist}
for future in as_completed(futures):
result = future.result()
if result:
high_si_candidates.append(result)
if not high_si_candidates:
return f"# High Short Interest Stocks\n\n**No stocks found** matching criteria: SI% >{min_short_interest_pct}%\n\n**Note**: Checked {len(watchlist)} tickers from watchlist."
# Sort by short interest percentage (highest first)
sorted_candidates = sorted(
high_si_candidates, key=lambda x: x["short_interest_pct"], reverse=True
)[:top_n]
# Format output
report = "# High Short Interest Stocks (Yahoo Finance Data)\n\n"
report += f"**Criteria**: Short Interest >{min_short_interest_pct}%\n"
report += "**Data Source**: Yahoo Finance via yfinance\n"
report += f"**Checked**: {len(watchlist)} tickers from watchlist\n\n"
report += f"**Found**: {len(sorted_candidates)} stocks with high short interest\n\n"
report += f"**Found**: {len(sorted_candidates)} stocks with high short interest\n\n"
report += "## Potential Short Squeeze Candidates\n\n"
headers = ["Ticker", "Price", "Market Cap", "Volume", "Short %", "Signal"]
rows = []
for candidate in sorted_candidates:
rows.append(
[
candidate["ticker"],
f"${candidate['price']:.2f}",
format_market_cap(candidate["market_cap"]),
f"{candidate['volume']:,}",
f"{candidate['short_interest_pct']:.1f}%",
candidate["signal"],
]
)
report += format_markdown_table(headers, rows)
report += "\n\n## Signal Definitions\n\n"
report += "- **extreme_squeeze_risk**: Short interest >30% - Very high squeeze potential\n"
report += "- **high_squeeze_potential**: Short interest 20-30% - High squeeze risk\n"
report += (
"- **moderate_squeeze_potential**: Short interest 15-20% - Moderate squeeze risk\n"
)
report += "- **low_squeeze_potential**: Short interest 10-15% - Lower squeeze risk\n\n"
report += "**Note**: High short interest alone doesn't guarantee a squeeze. Look for positive catalysts.\n"
report += "**Limitation**: This checks a curated watchlist. For comprehensive scanning, use a stock screener with short interest filters.\n"
return report
except Exception as e:
return f"Unexpected error in short interest detection: {str(e)}"
def get_fmp_short_interest(
min_short_interest_pct: float = 10.0,
min_days_to_cover: float = 2.0,
top_n: int = 20,
) -> str:
"""Alias for get_short_interest to match registry naming convention"""
return get_short_interest(min_short_interest_pct, min_days_to_cover, top_n)
def get_finra_short_interest(
min_short_interest_pct: float = 10.0,
min_days_to_cover: float = 2.0,
top_n: int = 20,
) -> str:
"""
Alternative: Get short interest from Finra public data.
Note: Finra data is updated bi-monthly and requires parsing from their website.
"""
# This would require web scraping or using Finra's data API
# For now, return a message directing to manual sources
return """# Finra Short Interest Data
**Note**: Finra short interest data is publicly available but requires specialized parsing.
## Access Finra Data:
1. Visit: https://www.finra.org/finra-data/browse-catalog/short-sale-volume-data
2. Download latest settlement date files
3. Parse for high short interest stocks
## Alternative Free Sources:
- **Market Beat**: https://www.marketbeat.com/short-interest/
- **Finviz Screener**: Filter by "Short Float >20%"
- **Yahoo Finance**: Individual stock pages show short % of float
For automated access, consider FMP Premium API or implementing Finra data parser.
"""