
Stock Analysis
- 11.1k installs
- 39 repo stars
- Updated July 27, 2026
- gracefullight/stock-checker
Should I buy/hold/sell this stock or crypto? What is my portfolio P&L and sector exposure?
About
Stock-analysis evaluates US equities and top-20 cryptocurrencies across 8 stock dimensions (earnings, fundamentals, sentiment, technicals, sector, momentum) and 3 crypto dimensions (market cap, correlation, momentum). Developers use it to build portfolio dashboards, monitor holdings with cost-basis P&L, detect pre/post-earnings risk, scan geopolitical/sector headwinds, and generate daily/weekly/monthly period returns. Core workflows: analyze single/multiple tickers via CLI, create/manage mixed asset portfolios (stocks + crypto), retrieve structured JSON for downstream analysis, and receive BUY/HOLD/SELL signals with 5-7 risk caveats (VIX, overbought, news alerts).
- Portfolio management: create, add, remove, update assets; tracks cost basis and current P&L
- 8 stock + 3 crypto analysis dimensions: earnings surprise, fundamentals, sentiment, technicals, sector, momentum, market
- Pre/post-earnings timing detection: pre-earnings < 14 days becomes HOLD; post-spike > 15% in 5 days flags gains priced i
- Risk flags: high VIX, overbought RSI, sector weakness, geopolitical news (Taiwan, China, Russia, Middle East, banking),
- CLI + JSON output: Yahoo Finance data with 1-hour caching, async parallel fetch (3-5s per stock), 2 breaking news alerts
Stock Analysis by the numbers
- 11,106 all-time installs (skills.sh)
- +116 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #7 of 1,136 Finance & Trading skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
stock-analysis capabilities & compatibility
Free (Yahoo Finance, Google News RSS, CNN Fear & Greed).
- Capabilities
- single/multi ticker analysis with 8 stock or 3 c · portfolio crud: create, add, remove, update, lis · period returns: daily, weekly, monthly, quarterl · risk detection: pre/post earnings timing, overbo · sentiment indicators: fear & greed index, short · json and text output formats · async parallel fetch with 1 hour caching
- Use cases
- trading
- Platforms
- macOS · Windows · Linux
- Runs
- Runs locally
- Pricing
- Free
What stock-analysis says it does
Analyze US stocks and cryptocurrencies using Yahoo Finance data. Includes portfolio management, crypto support, and periodic analysis.
npx skills add https://github.com/gracefullight/stock-checker --skill stock-analysisAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11.1k |
|---|---|
| repo stars | ★ 39 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | gracefullight/stock-checker ↗ |
What it does
Analyze US stocks and cryptocurrencies with portfolio tracking, earnings reactions, and periodic performance reports.
Who is it for?
Portfolio tracking dashboards, earnings reaction monitoring, crypto market cap analysis, risk-off detection, sector rotation alerts.
Skip if: High-frequency trading, options strategies, international stocks, crypto on-chain analysis, fundamental research deep dives.
When should I use this skill?
User asks for stock/crypto analysis, portfolio update, earnings timeline, sentiment signals, or P&L reports.
What you get
Automated stock/crypto scoring with 8 dimensions, portfolio dashboards with cost basis, and risk alerts (earnings timing, VIX, geopolitics, technicals).
- BUY/HOLD/SELL signal with confidence score
- 8-dimension stock metrics or 3-dimension crypto metrics
- Portfolio P&L and concentration warnings
By the numbers
- 8 analysis dimensions for stocks (earnings 30%, fundamentals 20%, sentiment 20%, technicals/momentum combined 25%)
- 3 analysis dimensions for crypto (market cap, BTC correlation, momentum/RSI)
- 20 supported cryptocurrencies: BTC, ETH, BNB, SOL, XRP, ADA, DOGE, AVAX, DOT, MATIC, LINK, ATOM, UNI, LTC, BCH, XLM, ALG
Files
Stock Analysis (v5.0)
Analyze US stocks and cryptocurrencies using Yahoo Finance data. Includes portfolio management, crypto support, and periodic analysis.
Quick Start
IMPORTANT: Pass ONLY the stock ticker symbol(s) as arguments. Do NOT add extra text, headers, or formatting in the command.
Analyze a single ticker:
uv run {baseDir}/scripts/analyze_stock.py AAPL
uv run {baseDir}/scripts/analyze_stock.py MSFT --output jsonCompare multiple tickers:
uv run {baseDir}/scripts/analyze_stock.py AAPL MSFT GOOGLCryptocurrency Analysis (v5.0)
Analyze top 20 cryptocurrencies by market cap:
uv run {baseDir}/scripts/analyze_stock.py BTC-USD
uv run {baseDir}/scripts/analyze_stock.py ETH-USD SOL-USDSupported Cryptos: BTC-USD, ETH-USD, BNB-USD, SOL-USD, XRP-USD, ADA-USD, DOGE-USD, AVAX-USD, DOT-USD, MATIC-USD, LINK-USD, ATOM-USD, UNI-USD, LTC-USD, BCH-USD, XLM-USD, ALGO-USD, VET-USD, FIL-USD, NEAR-USD
Crypto Analysis Dimensions:
- Market cap (large/mid/small classification)
- Category (Smart Contract L1, DeFi, Payment, etc.)
- BTC correlation (30-day)
- Momentum (RSI, price range)
- Market context (VIX, general market regime)
Portfolio Management (v5.0)
Create and manage portfolios with mixed assets (stocks + crypto):
# Create portfolio
uv run {baseDir}/scripts/portfolio.py create "My Portfolio"
# Add assets
uv run {baseDir}/scripts/portfolio.py add AAPL --quantity 100 --cost 150.00
uv run {baseDir}/scripts/portfolio.py add BTC-USD --quantity 0.5 --cost 40000 --portfolio "My Portfolio"
# View holdings with current P&L
uv run {baseDir}/scripts/portfolio.py show
# Update/remove assets
uv run {baseDir}/scripts/portfolio.py update AAPL --quantity 150
uv run {baseDir}/scripts/portfolio.py remove BTC-USD
# List/delete portfolios
uv run {baseDir}/scripts/portfolio.py list
uv run {baseDir}/scripts/portfolio.py delete "My Portfolio"Portfolio Storage: ~/.clawdbot/skills/stock-analysis/portfolios.json
Portfolio Analysis (v5.0)
Analyze all assets in a portfolio with optional period returns:
# Analyze portfolio
uv run {baseDir}/scripts/analyze_stock.py --portfolio "My Portfolio"
# With period returns (daily/weekly/monthly/quarterly/yearly)
uv run {baseDir}/scripts/analyze_stock.py --portfolio "My Portfolio" --period weekly
uv run {baseDir}/scripts/analyze_stock.py -p "My Portfolio" --period monthlyPortfolio Summary includes:
- Total cost, current value, P&L
- Period return (if specified)
- Concentration warnings (>30% in single asset)
- Recommendation summary (BUY/HOLD/SELL counts)
Examples:
- ✅ CORRECT:
uv run {baseDir}/scripts/analyze_stock.py BAC - ✅ CORRECT:
uv run {baseDir}/scripts/analyze_stock.py BTC-USD - ❌ WRONG:
uv run {baseDir}/scripts/analyze_stock.py === BANK OF AMERICA (BAC) - Q4 2025 EARNINGS === - ❌ WRONG:
uv run {baseDir}/scripts/analyze_stock.py "Bank of America"
Use the ticker symbol only (e.g., BAC, not "Bank of America"). For crypto, use the -USD suffix (e.g., BTC-USD).
Analysis Components
The script evaluates eight key dimensions:
1. Earnings Surprise (30% weight): Actual vs expected EPS, revenue beats/misses 2. Fundamentals (20% weight): P/E ratio, profit margins, revenue growth, debt levels 3. Analyst Sentiment (20% weight): Consensus ratings, price target vs current price 4. Historical Patterns (10% weight): Past earnings reactions, volatility 5. Market Context (10% weight): VIX, SPY/QQQ trends, market regime 6. Sector Performance (15% weight): Stock vs sector comparison, sector trends 7. Momentum (15% weight): RSI, 52-week range, volume, relative strength 8. Sentiment Analysis (10% weight): Fear/Greed Index, short interest, VIX term structure, insider trading, put/call ratio
Sentiment Sub-Indicators:
- Fear & Greed Index (CNN): Contrarian signal (extreme fear = buy opportunity, extreme greed = caution)
- Short Interest: High shorts + squeeze potential = bullish; justified shorts = bearish
- VIX Term Structure: Contango = complacency/bullish; backwardation = stress/bearish
- Insider Activity: Net buying/selling from SEC Form 4 filings (90-day window)
- Put/Call Ratio: High ratio = excessive fear/bullish; low ratio = complacency/bearish
Weights auto-normalize if some components unavailable.
Special Timing Checks:
- Pre-earnings warning (< 14 days): Recommends HOLD instead of BUY
- Post-earnings spike detection (> 15% in 5 days): Flags "gains priced in"
- Overbought conditions (RSI > 70 + near 52w high): Reduces confidence
Timing Warnings & Risk Flags
The script detects high-risk scenarios:
Earnings Timing
- Pre-Earnings Period: If earnings < 14 days away, BUY signals become HOLD
- Post-Earnings Spike: If stock up > 15% in 5 days after earnings, warns "gains may be priced in"
Technical Risk
- Overbought Conditions: RSI > 70 + near 52-week high = high-risk entry
Market Risk
- High VIX: Market fear (VIX > 30) reduces confidence in BUY signals
- Risk-Off Mode (v4.0.0): When safe-havens (GLD, TLT, UUP) rise together, reduces BUY confidence by 30%
- Detects flight to safety across gold, treasuries, and USD
- Triggers when GLD ≥ +2%, TLT ≥ +1%, UUP ≥ +1% (5-day change)
Sector Risk
- Sector Weakness: Stock may look good but sector is rotating out
Geopolitical Risk (v4.0.0)
The script now scans breaking news (last 24h) for crisis keywords and automatically flags affected stocks:
- Taiwan Conflict: Semiconductors (NVDA, AMD, TSM, INTC, etc.) → 30% confidence penalty
- China Tensions: Tech/Consumer (AAPL, QCOM, NKE, SBUX, etc.) → 30% confidence penalty
- Russia-Ukraine: Energy/Materials (XOM, CVX, MOS, CF, etc.) → 30% confidence penalty
- Middle East: Oil/Defense (XOM, LMT, RTX, etc.) → 30% confidence penalty
- Banking Crisis: Financials (JPM, BAC, WFC, C, etc.) → 30% confidence penalty
If a ticker is not in the affected list but its sector is exposed, applies a 15% confidence penalty.
Example Alert:
⚠️ SECTOR RISK: Tech supply chain and consumer market exposure (detected: china, tariff)Breaking News Alerts (v4.0.0)
- Scans Google News RSS for crisis keywords (war, recession, sanctions, disasters, etc.)
- Displays up to 2 breaking news alerts in caveats (last 24 hours)
- Uses 1-hour cache to avoid excessive API calls
Output Format
Default (text): Concise buy/hold/sell signal with 3-5 bullet points and caveats
JSON: Structured data with scores, metrics, and raw data for further analysis
Limitations
- Data freshness: Yahoo Finance may lag 15-20 minutes
- Sentiment data staleness:
- Short interest data lags ~2 weeks (FINRA reporting schedule)
- Insider trades may lag filing by 2-3 days
- VIX term structure only updates during futures trading hours
- Breaking news limitations (v4.0.0):
- Google News RSS may lag by 15-60 minutes
- Keyword matching may have false positives/negatives
- Does not analyze sentiment, only detects keywords
- 1-hour cache means alerts may be slightly stale
- Missing data: Not all stocks have analyst coverage, options chains, or complete fundamentals
- Execution time: 3-5s per stock with async parallel fetching and caching (shared indicators cached for 1h)
- Disclaimer: All outputs include prominent "not financial advice" warning
- US markets only: Non-US tickers may have incomplete data
Error Handling
The script gracefully handles:
- Invalid tickers → Clear error message
- Missing analyst data → Signal based on available metrics only
- API failures → Retry with exponential backoff, fail after 3 attempts
{
"owner": "udiedrichsen",
"slug": "stock-analysis",
"displayName": "Stock Analysis",
"latest": {
"version": "5.0.0",
"publishedAt": 1768556016509,
"commit": "https://github.com/clawdbot/skills/commit/30a37ea7915845847948a175274179bc1ac11bfc"
},
"history": [
{
"version": "4.0.0",
"publishedAt": 1768461072536,
"commit": "https://github.com/clawdbot/skills/commit/7f2a77fdf43d1b08e0028309534447c6b68f3416"
},
{
"version": "3.0.0",
"publishedAt": 1768457437779,
"commit": "https://github.com/clawdbot/skills/commit/533c7f6773d22498c4bf93a2b18cddab6289a3cc"
},
{
"version": "2.0.0",
"publishedAt": 1768408281094,
"commit": "https://github.com/clawdbot/skills/commit/e381887e572635fc39bfe331ead82c6f330cfa62"
},
{
"version": "1.0.1",
"publishedAt": 1768404118281,
"commit": "https://github.com/clawdbot/skills/commit/c699d563470a6c2bf1e0cc912e6c988cae4a7f91"
},
{
"version": "1.0.0",
"publishedAt": 1768383975214,
"commit": "https://github.com/clawdbot/skills/commit/c69a401b8760e319a5c4f8b066688b7eca65d730"
}
]
}
StockPulse - Commercial Product Roadmap
Vision
Transform the stock-analysis skill into StockPulse, a commercial mobile app for retail investors with AI-powered stock and crypto analysis, portfolio tracking, and personalized alerts.
Technical Decisions
- Mobile: Flutter (iOS + Android cross-platform)
- Backend: Python FastAPI on AWS (ECS/Lambda)
- Database: PostgreSQL (RDS) + Redis (ElastiCache)
- Auth: AWS Cognito or Firebase Auth
- Monetization: Freemium + Subscription ($9.99/mo or $79.99/yr)
---
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ MOBILE APP (Flutter) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Dashboard │ │Portfolio │ │ Analysis │ │ Alerts │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ HTTPS/REST
▼
┌─────────────────────────────────────────────────────────────────┐
│ API GATEWAY (AWS) │
│ Rate Limiting, Auth, Caching │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ BACKEND (FastAPI on ECS) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Auth Service │ │ Analysis API │ │ Portfolio API│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Alerts Svc │ │ Subscription │ │ User Service │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PostgreSQL │ │ Redis │ │ S3 │
│ (RDS) │ │ (ElastiCache)│ │ (Reports) │
└──────────────┘ └──────────────┘ └──────────────┘
BACKGROUND WORKERS (Lambda/ECS)
┌─────────────────────────────────────────────────────────────────┐
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │Price Updater │ │Alert Checker │ │Daily Reports │ │
│ │ (5 min) │ │ (1 min) │ │ (Daily) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘---
Feature Tiers
Free Tier
- 1 portfolio (max 10 assets)
- Basic stock/crypto analysis
- Daily market summary
- Limited to 5 analyses/day
- Ads displayed
Premium ($9.99/mo)
- Unlimited portfolios & assets
- Full 8-dimension analysis
- Real-time price alerts
- Push notifications
- Period reports (daily/weekly/monthly)
- No ads
- Priority support
Pro ($19.99/mo) - Future
- API access
- Custom watchlists
- Advanced screeners
- Export to CSV/PDF
- Portfolio optimization suggestions
---
Development Phases
Phase 1: Backend API
Goal: Convert Python scripts to production REST API
Tasks:
1. Project Setup
- FastAPI project structure
- Docker containerization
- CI/CD pipeline (GitHub Actions)
- AWS infrastructure (Terraform)
2. Core API Endpoints
POST /auth/register
POST /auth/login
POST /auth/refresh
GET /analysis/{ticker}
POST /analysis/batch
GET /portfolios
POST /portfolios
PUT /portfolios/{id}
DELETE /portfolios/{id}
GET /portfolios/{id}/assets
POST /portfolios/{id}/assets
PUT /portfolios/{id}/assets/{ticker}
DELETE /portfolios/{id}/assets/{ticker}
GET /portfolios/{id}/performance?period=weekly
GET /portfolios/{id}/summary
GET /alerts
POST /alerts
DELETE /alerts/{id}
GET /user/subscription
POST /user/subscription/upgrade3. Database Schema
users (id, email, password_hash, created_at, subscription_tier)
portfolios (id, user_id, name, created_at, updated_at)
assets (id, portfolio_id, ticker, asset_type, quantity, cost_basis)
alerts (id, user_id, ticker, condition, threshold, enabled)
analysis_cache (ticker, data, expires_at)
subscriptions (id, user_id, stripe_id, status, expires_at)4. Refactor Existing Code
- Extract
analyze_stock.pyinto modules: analysis/earnings.pyanalysis/fundamentals.pyanalysis/sentiment.pyanalysis/crypto.pyanalysis/market_context.py- Add async support throughout
- Implement proper caching (Redis)
- Rate limiting per user tier
Files to Create:
backend/
├── app/
│ ├── main.py # FastAPI app
│ ├── config.py # Settings
│ ├── models/ # SQLAlchemy models
│ ├── schemas/ # Pydantic schemas
│ ├── routers/ # API routes
│ │ ├── auth.py
│ │ ├── analysis.py
│ │ ├── portfolios.py
│ │ └── alerts.py
│ ├── services/ # Business logic
│ │ ├── analysis/ # Refactored from analyze_stock.py
│ │ ├── portfolio.py
│ │ └── alerts.py
│ └── workers/ # Background tasks
├── tests/
├── Dockerfile
├── docker-compose.yml
└── requirements.txt---
Phase 2: Flutter Mobile App
Goal: Build polished cross-platform mobile app
Screens:
1. Onboarding - Welcome, feature highlights, sign up/login 2. Dashboard - Market overview, portfolio summary, alerts 3. Analysis - Search ticker, view full analysis, save to portfolio 4. Portfolio - List portfolios, asset breakdown, P&L chart 5. Alerts - Manage price alerts, notification settings 6. Settings - Account, subscription, preferences
Key Flutter Packages:
dependencies:
flutter_bloc: ^8.0.0 # State management
dio: ^5.0.0 # HTTP client
go_router: ^12.0.0 # Navigation
fl_chart: ^0.65.0 # Charts
firebase_messaging: ^14.0.0 # Push notifications
in_app_purchase: ^3.0.0 # Subscriptions
shared_preferences: ^2.0.0
flutter_secure_storage: ^9.0.0App Structure:
lib/
├── main.dart
├── app/
│ ├── routes.dart
│ └── theme.dart
├── features/
│ ├── auth/
│ │ ├── bloc/
│ │ ├── screens/
│ │ └── widgets/
│ ├── dashboard/
│ ├── analysis/
│ ├── portfolio/
│ ├── alerts/
│ └── settings/
├── core/
│ ├── api/
│ ├── models/
│ └── utils/
└── shared/
└── widgets/---
Phase 3: Infrastructure & DevOps
Goal: Production-ready cloud infrastructure
AWS Services:
- ECS Fargate - Backend containers
- RDS PostgreSQL - Database
- ElastiCache Redis - Caching
- S3 - Static assets, reports
- CloudFront - CDN
- Cognito - Authentication
- SES - Email notifications
- SNS - Push notifications
- CloudWatch - Monitoring
- WAF - Security
Terraform Modules:
infrastructure/
├── main.tf
├── variables.tf
├── modules/
│ ├── vpc/
│ ├── ecs/
│ ├── rds/
│ ├── elasticache/
│ └── cognito/
└── environments/
├── dev/
├── staging/
└── prod/Estimated Monthly Costs (Production):
| Service | Est. Cost |
|---|---|
| ECS Fargate (2 tasks) | $50-100 |
| RDS (db.t3.small) | $30-50 |
| ElastiCache (cache.t3.micro) | $15-25 |
| S3 + CloudFront | $10-20 |
| Other (Cognito, SES, etc.) | $20-30 |
| Total | $125-225/mo |
---
Phase 4: Payments & Subscriptions
Goal: Integrate Stripe for subscriptions
Implementation:
1. Stripe subscription products (Free, Premium, Pro) 2. In-app purchase for iOS/Android 3. Webhook handlers for subscription events 4. Grace period handling 5. Receipt validation
Stripe Integration:
# Backend webhook handler
@router.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
event = stripe.Webhook.construct_event(...)
if event.type == "customer.subscription.updated":
update_user_tier(event.data.object)
elif event.type == "customer.subscription.deleted":
downgrade_to_free(event.data.object)---
Phase 5: Push Notifications & Alerts
Goal: Real-time price alerts and notifications
Alert Types:
- Price above/below threshold
- Percentage change (daily)
- Earnings announcement
- Breaking news (geopolitical)
- Portfolio performance
Implementation:
- Firebase Cloud Messaging (FCM)
- Background worker checks alerts every minute
- Rate limit: max 10 alerts/day per free user
---
Phase 6: Analytics & Monitoring
Goal: Track usage, errors, business metrics
Tools:
- Mixpanel/Amplitude - Product analytics
- Sentry - Error tracking
- CloudWatch - Infrastructure metrics
- Custom dashboard - Business KPIs
Key Metrics:
- DAU/MAU
- Conversion rate (free → premium)
- Churn rate
- API response times
- Analysis accuracy feedback
---
Security Considerations
1. Authentication
- JWT tokens with refresh rotation
- OAuth2 (Google, Apple Sign-In)
- 2FA optional for premium users
2. Data Protection
- Encrypt PII at rest (RDS encryption)
- TLS 1.3 for all API traffic
- No plaintext passwords
3. API Security
- Rate limiting per tier
- Input validation (Pydantic)
- SQL injection prevention (SQLAlchemy ORM)
- CORS configuration
4. Compliance
- Privacy policy
- Terms of service
- GDPR data export/deletion
- Financial disclaimer (not investment advice)
---
Risks & Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| Yahoo Finance rate limits | High | Implement caching, use paid API fallback |
| App store rejection | Medium | Follow guidelines, proper disclaimers |
| Data accuracy issues | High | Clear disclaimers, data validation |
| Security breach | Critical | Security audit, penetration testing |
| Low conversion rate | Medium | A/B testing, feature gating |
---
Success Metrics (Year 1)
| Metric | Target |
|---|---|
| App downloads | 10,000+ |
| DAU | 1,000+ |
| Premium subscribers | 500+ |
| Monthly revenue | $5,000+ |
| App store rating | 4.5+ stars |
| Churn rate | <5%/month |
---
Next Steps (Immediate)
1. Validate idea - User interviews, landing page 2. Design - Figma mockups for key screens 3. Backend MVP - Core API endpoints 4. Flutter prototype - Basic app with analysis feature 5. Beta testing - TestFlight/Google Play beta
---
Repository Structure (Final)
stockpulse/
├── backend/ # FastAPI backend
│ ├── app/
│ ├── tests/
│ ├── Dockerfile
│ └── requirements.txt
├── mobile/ # Flutter app
│ ├── lib/
│ ├── test/
│ ├── ios/
│ ├── android/
│ └── pubspec.yaml
├── infrastructure/ # Terraform
│ ├── modules/
│ └── environments/
├── docs/ # Documentation
│ ├── api/
│ └── architecture/
└── scripts/ # Utility scripts---
Timeline Summary (Planning Only)
| Phase | Duration | Dependencies |
|---|---|---|
| 1. Backend API | 4-6 weeks | - |
| 2. Flutter App | 6-8 weeks | Phase 1 |
| 3. Infrastructure | 2-3 weeks | Phase 1 |
| 4. Payments | 2 weeks | Phase 2, 3 |
| 5. Notifications | 2 weeks | Phase 2, 3 |
| 6. Analytics | 1 week | Phase 2 |
| Total | 17-22 weeks |
This is a planning document. No fixed timeline - execute phases as resources allow.
---
Disclaimer: This tool is for informational purposes only and does NOT constitute financial advice.
Stock Analysis v5.0
Analyze stocks and cryptocurrencies with portfolio management, periodic reports, and 8-dimension analysis.
Features
| Feature | Description |
|---|---|
| Stock Analysis | 8 dimensions: earnings, fundamentals, analysts, historical, market, sector, momentum, sentiment |
| Crypto Analysis | Top 20 cryptos: market cap, category, BTC correlation, momentum |
| Portfolio Management | Create portfolios, add/remove assets, track P&L |
| Periodic Reports | Daily, weekly, monthly, quarterly, yearly returns |
| Risk Detection | Geopolitical risk, breaking news alerts, concentration warnings |
Quick Start
Analyze Stocks
uv run scripts/analyze_stock.py AAPL
uv run scripts/analyze_stock.py AAPL MSFT GOOGLAnalyze Crypto
uv run scripts/analyze_stock.py BTC-USD
uv run scripts/analyze_stock.py ETH-USD SOL-USDPortfolio Management
# Create and manage portfolios
uv run scripts/portfolio.py create "My Portfolio"
uv run scripts/portfolio.py add AAPL --quantity 100 --cost 150
uv run scripts/portfolio.py add BTC-USD --quantity 0.5 --cost 40000
uv run scripts/portfolio.py show
# Analyze portfolio
uv run scripts/analyze_stock.py --portfolio "My Portfolio"
uv run scripts/analyze_stock.py --portfolio "My Portfolio" --period weeklySupported Cryptocurrencies (Top 20)
BTC, ETH, BNB, SOL, XRP, ADA, DOGE, AVAX, DOT, MATIC, LINK, ATOM, UNI, LTC, BCH, XLM, ALGO, VET, FIL, NEAR
Analysis Dimensions
Stocks (8 dimensions)
1. Earnings Surprise - EPS beats/misses 2. Fundamentals - P/E, margins, growth, debt 3. Analyst Sentiment - Ratings, price targets 4. Historical Patterns - Past earnings reactions 5. Market Context - VIX, SPY/QQQ trends 6. Sector Performance - Relative strength 7. Momentum - RSI, 52-week range 8. Sentiment - Fear/Greed, short interest, insider trading
Crypto (3 dimensions)
1. Crypto Fundamentals - Market cap, category, BTC correlation 2. Momentum - RSI, price range 3. Market Context - General market regime
Risk Features
- Breaking News Alerts - Scans for crisis keywords (war, recession, sanctions)
- Geopolitical Risk - Taiwan, China, Russia, Middle East, banking crisis detection
- Safe-Haven Tracking - GLD, TLT, UUP for risk-off detection
- Concentration Warnings - Alerts when >30% in single asset
Portfolio Storage
Portfolios stored at: ~/.clawdbot/skills/stock-analysis/portfolios.json
Version History
- v5.0.0 - Portfolio management, crypto support, periodic analysis
- v4.1.0 - Insider trading via edgartools, market context caching
- v4.0.0 - Geopolitical risk, breaking news, safe-haven detection
- v3.0.0 - Sentiment analysis (5 indicators)
- v2.0.0 - Market context, sector, momentum
- v1.0.0 - Initial release
Data Sources
- Yahoo Finance - Price, fundamentals, earnings
- CNN Fear & Greed - Market sentiment
- SEC EDGAR - Insider trading (Form 4)
- Google News RSS - Breaking news
---
Disclaimer: This tool is for informational purposes only and does NOT constitute financial advice.
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = ["yfinance>=0.2.40"]
# ///
"""
Portfolio management for stock-analysis skill.
Usage:
uv run portfolio.py create "Portfolio Name"
uv run portfolio.py list
uv run portfolio.py show [--portfolio NAME]
uv run portfolio.py delete "Portfolio Name"
uv run portfolio.py rename "Old Name" "New Name"
uv run portfolio.py add TICKER --quantity 100 --cost 150.00 [--portfolio NAME]
uv run portfolio.py update TICKER --quantity 150 [--portfolio NAME]
uv run portfolio.py remove TICKER [--portfolio NAME]
"""
import argparse
import json
import os
import sys
from dataclasses import dataclass, asdict
from datetime import datetime
from pathlib import Path
from typing import Literal
import yfinance as yf
# Top 20 supported cryptocurrencies
SUPPORTED_CRYPTOS = {
"BTC-USD", "ETH-USD", "BNB-USD", "SOL-USD", "XRP-USD",
"ADA-USD", "DOGE-USD", "AVAX-USD", "DOT-USD", "MATIC-USD",
"LINK-USD", "ATOM-USD", "UNI-USD", "LTC-USD", "BCH-USD",
"XLM-USD", "ALGO-USD", "VET-USD", "FIL-USD", "NEAR-USD",
}
def get_storage_path() -> Path:
"""Get the portfolio storage path."""
# Use ~/.clawdbot/skills/stock-analysis/portfolios.json
state_dir = os.environ.get("CLAWDBOT_STATE_DIR", os.path.expanduser("~/.clawdbot"))
portfolio_dir = Path(state_dir) / "skills" / "stock-analysis"
portfolio_dir.mkdir(parents=True, exist_ok=True)
return portfolio_dir / "portfolios.json"
def detect_asset_type(ticker: str) -> Literal["stock", "crypto"]:
"""Detect asset type from ticker format."""
ticker_upper = ticker.upper()
if ticker_upper.endswith("-USD"):
base = ticker_upper[:-4]
if base.isalpha() and f"{base}-USD" in SUPPORTED_CRYPTOS:
return "crypto"
# Allow any *-USD ticker as crypto (flexible)
if base.isalpha():
return "crypto"
return "stock"
@dataclass
class Asset:
ticker: str
type: Literal["stock", "crypto"]
quantity: float
cost_basis: float
added_at: str
@dataclass
class Portfolio:
name: str
created_at: str
updated_at: str
assets: list[Asset]
class PortfolioStore:
"""Manages portfolio storage with atomic writes."""
def __init__(self, path: Path | None = None):
self.path = path or get_storage_path()
self._data: dict | None = None
def _load(self) -> dict:
"""Load portfolios from disk."""
if self._data is not None:
return self._data
if not self.path.exists():
self._data = {"version": 1, "portfolios": {}}
return self._data
try:
with open(self.path, "r", encoding="utf-8") as f:
self._data = json.load(f)
return self._data
except (json.JSONDecodeError, IOError):
self._data = {"version": 1, "portfolios": {}}
return self._data
def _save(self) -> None:
"""Save portfolios to disk with atomic write."""
if self._data is None:
return
# Ensure directory exists
self.path.parent.mkdir(parents=True, exist_ok=True)
# Atomic write: write to temp file, then rename
tmp_path = self.path.with_suffix(".tmp")
try:
with open(tmp_path, "w", encoding="utf-8") as f:
json.dump(self._data, f, indent=2)
tmp_path.replace(self.path)
except Exception:
if tmp_path.exists():
tmp_path.unlink()
raise
def _get_portfolio_key(self, name: str) -> str:
"""Convert portfolio name to storage key."""
return name.lower().replace(" ", "-")
def list_portfolios(self) -> list[str]:
"""List all portfolio names."""
data = self._load()
return [p["name"] for p in data["portfolios"].values()]
def get_portfolio(self, name: str) -> Portfolio | None:
"""Get a portfolio by name."""
data = self._load()
key = self._get_portfolio_key(name)
if key not in data["portfolios"]:
# Try case-insensitive match
for k, v in data["portfolios"].items():
if v["name"].lower() == name.lower():
key = k
break
else:
return None
p = data["portfolios"][key]
assets = [
Asset(
ticker=a["ticker"],
type=a["type"],
quantity=a["quantity"],
cost_basis=a["cost_basis"],
added_at=a["added_at"],
)
for a in p.get("assets", [])
]
return Portfolio(
name=p["name"],
created_at=p["created_at"],
updated_at=p["updated_at"],
assets=assets,
)
def create_portfolio(self, name: str) -> Portfolio:
"""Create a new portfolio."""
data = self._load()
key = self._get_portfolio_key(name)
if key in data["portfolios"]:
raise ValueError(f"Portfolio '{name}' already exists")
now = datetime.now().isoformat()
portfolio = {
"name": name,
"created_at": now,
"updated_at": now,
"assets": [],
}
data["portfolios"][key] = portfolio
self._save()
return Portfolio(name=name, created_at=now, updated_at=now, assets=[])
def delete_portfolio(self, name: str) -> bool:
"""Delete a portfolio."""
data = self._load()
key = self._get_portfolio_key(name)
# Try case-insensitive match
if key not in data["portfolios"]:
for k, v in data["portfolios"].items():
if v["name"].lower() == name.lower():
key = k
break
else:
return False
del data["portfolios"][key]
self._save()
return True
def rename_portfolio(self, old_name: str, new_name: str) -> bool:
"""Rename a portfolio."""
data = self._load()
old_key = self._get_portfolio_key(old_name)
new_key = self._get_portfolio_key(new_name)
# Find old portfolio
if old_key not in data["portfolios"]:
for k, v in data["portfolios"].items():
if v["name"].lower() == old_name.lower():
old_key = k
break
else:
return False
if new_key in data["portfolios"] and new_key != old_key:
raise ValueError(f"Portfolio '{new_name}' already exists")
portfolio = data["portfolios"].pop(old_key)
portfolio["name"] = new_name
portfolio["updated_at"] = datetime.now().isoformat()
data["portfolios"][new_key] = portfolio
self._save()
return True
def add_asset(
self,
portfolio_name: str,
ticker: str,
quantity: float,
cost_basis: float,
) -> Asset:
"""Add an asset to a portfolio."""
data = self._load()
key = self._get_portfolio_key(portfolio_name)
# Find portfolio
if key not in data["portfolios"]:
for k, v in data["portfolios"].items():
if v["name"].lower() == portfolio_name.lower():
key = k
break
else:
raise ValueError(f"Portfolio '{portfolio_name}' not found")
portfolio = data["portfolios"][key]
ticker = ticker.upper()
# Check if asset already exists
for asset in portfolio["assets"]:
if asset["ticker"] == ticker:
raise ValueError(f"Asset '{ticker}' already in portfolio. Use 'update' to modify.")
# Validate ticker
asset_type = detect_asset_type(ticker)
try:
stock = yf.Ticker(ticker)
info = stock.info
if "regularMarketPrice" not in info:
raise ValueError(f"Invalid ticker: {ticker}")
except Exception as e:
raise ValueError(f"Could not validate ticker '{ticker}': {e}")
now = datetime.now().isoformat()
asset = {
"ticker": ticker,
"type": asset_type,
"quantity": quantity,
"cost_basis": cost_basis,
"added_at": now,
}
portfolio["assets"].append(asset)
portfolio["updated_at"] = now
self._save()
return Asset(**asset)
def update_asset(
self,
portfolio_name: str,
ticker: str,
quantity: float | None = None,
cost_basis: float | None = None,
) -> Asset | None:
"""Update an asset in a portfolio."""
data = self._load()
key = self._get_portfolio_key(portfolio_name)
# Find portfolio
if key not in data["portfolios"]:
for k, v in data["portfolios"].items():
if v["name"].lower() == portfolio_name.lower():
key = k
break
else:
return None
portfolio = data["portfolios"][key]
ticker = ticker.upper()
for asset in portfolio["assets"]:
if asset["ticker"] == ticker:
if quantity is not None:
asset["quantity"] = quantity
if cost_basis is not None:
asset["cost_basis"] = cost_basis
portfolio["updated_at"] = datetime.now().isoformat()
self._save()
return Asset(**asset)
return None
def remove_asset(self, portfolio_name: str, ticker: str) -> bool:
"""Remove an asset from a portfolio."""
data = self._load()
key = self._get_portfolio_key(portfolio_name)
# Find portfolio
if key not in data["portfolios"]:
for k, v in data["portfolios"].items():
if v["name"].lower() == portfolio_name.lower():
key = k
break
else:
return False
portfolio = data["portfolios"][key]
ticker = ticker.upper()
original_len = len(portfolio["assets"])
portfolio["assets"] = [a for a in portfolio["assets"] if a["ticker"] != ticker]
if len(portfolio["assets"]) < original_len:
portfolio["updated_at"] = datetime.now().isoformat()
self._save()
return True
return False
def get_default_portfolio_name(self) -> str | None:
"""Get the default (first) portfolio name, or None if empty."""
portfolios = self.list_portfolios()
return portfolios[0] if portfolios else None
def format_currency(value: float) -> str:
"""Format a value as currency."""
if abs(value) >= 1_000_000:
return f"${value/1_000_000:.2f}M"
elif abs(value) >= 1_000:
return f"${value/1_000:.2f}K"
else:
return f"${value:.2f}"
def show_portfolio(portfolio: Portfolio, verbose: bool = False) -> None:
"""Display portfolio details with current prices."""
print(f"\n{'='*60}")
print(f"PORTFOLIO: {portfolio.name}")
print(f"Created: {portfolio.created_at[:10]} | Updated: {portfolio.updated_at[:10]}")
print(f"{'='*60}\n")
if not portfolio.assets:
print(" No assets in portfolio. Use 'add' to add assets.\n")
return
total_cost = 0.0
total_value = 0.0
print(f"{'Ticker':<12} {'Type':<8} {'Qty':>10} {'Cost':>12} {'Current':>12} {'Value':>14} {'P&L':>12}")
print("-" * 82)
for asset in portfolio.assets:
try:
stock = yf.Ticker(asset.ticker)
current_price = stock.info.get("regularMarketPrice", 0) or 0
except Exception:
current_price = 0
cost_total = asset.quantity * asset.cost_basis
current_value = asset.quantity * current_price
pnl = current_value - cost_total
pnl_pct = (pnl / cost_total * 100) if cost_total > 0 else 0
total_cost += cost_total
total_value += current_value
pnl_str = f"{'+' if pnl >= 0 else ''}{format_currency(pnl)} ({pnl_pct:+.1f}%)"
print(f"{asset.ticker:<12} {asset.type:<8} {asset.quantity:>10.4f} "
f"{format_currency(asset.cost_basis):>12} {format_currency(current_price):>12} "
f"{format_currency(current_value):>14} {pnl_str:>12}")
print("-" * 82)
total_pnl = total_value - total_cost
total_pnl_pct = (total_pnl / total_cost * 100) if total_cost > 0 else 0
print(f"{'TOTAL':<12} {'':<8} {'':<10} {format_currency(total_cost):>12} {'':<12} "
f"{format_currency(total_value):>14} {'+' if total_pnl >= 0 else ''}{format_currency(total_pnl)} ({total_pnl_pct:+.1f}%)")
print()
def main():
parser = argparse.ArgumentParser(description="Portfolio management for stock-analysis")
subparsers = parser.add_subparsers(dest="command", help="Commands")
# create
create_parser = subparsers.add_parser("create", help="Create a new portfolio")
create_parser.add_argument("name", help="Portfolio name")
# list
subparsers.add_parser("list", help="List all portfolios")
# show
show_parser = subparsers.add_parser("show", help="Show portfolio details")
show_parser.add_argument("--portfolio", "-p", help="Portfolio name (default: first portfolio)")
# delete
delete_parser = subparsers.add_parser("delete", help="Delete a portfolio")
delete_parser.add_argument("name", help="Portfolio name")
# rename
rename_parser = subparsers.add_parser("rename", help="Rename a portfolio")
rename_parser.add_argument("old_name", help="Current portfolio name")
rename_parser.add_argument("new_name", help="New portfolio name")
# add
add_parser = subparsers.add_parser("add", help="Add an asset to portfolio")
add_parser.add_argument("ticker", help="Stock/crypto ticker (e.g., AAPL, BTC-USD)")
add_parser.add_argument("--quantity", "-q", type=float, required=True, help="Quantity")
add_parser.add_argument("--cost", "-c", type=float, required=True, help="Cost basis per unit")
add_parser.add_argument("--portfolio", "-p", help="Portfolio name (default: first portfolio)")
# update
update_parser = subparsers.add_parser("update", help="Update an asset in portfolio")
update_parser.add_argument("ticker", help="Stock/crypto ticker")
update_parser.add_argument("--quantity", "-q", type=float, help="New quantity")
update_parser.add_argument("--cost", "-c", type=float, help="New cost basis per unit")
update_parser.add_argument("--portfolio", "-p", help="Portfolio name (default: first portfolio)")
# remove
remove_parser = subparsers.add_parser("remove", help="Remove an asset from portfolio")
remove_parser.add_argument("ticker", help="Stock/crypto ticker")
remove_parser.add_argument("--portfolio", "-p", help="Portfolio name (default: first portfolio)")
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
store = PortfolioStore()
try:
if args.command == "create":
portfolio = store.create_portfolio(args.name)
print(f"Created portfolio: {portfolio.name}")
elif args.command == "list":
portfolios = store.list_portfolios()
if not portfolios:
print("No portfolios found. Use 'create' to create one.")
else:
print("\nPortfolios:")
for name in portfolios:
p = store.get_portfolio(name)
asset_count = len(p.assets) if p else 0
print(f" - {name} ({asset_count} assets)")
print()
elif args.command == "show":
portfolio_name = args.portfolio or store.get_default_portfolio_name()
if not portfolio_name:
print("No portfolios found. Use 'create' to create one.")
sys.exit(1)
portfolio = store.get_portfolio(portfolio_name)
if not portfolio:
print(f"Portfolio '{portfolio_name}' not found.")
sys.exit(1)
show_portfolio(portfolio)
elif args.command == "delete":
if store.delete_portfolio(args.name):
print(f"Deleted portfolio: {args.name}")
else:
print(f"Portfolio '{args.name}' not found.")
sys.exit(1)
elif args.command == "rename":
if store.rename_portfolio(args.old_name, args.new_name):
print(f"Renamed portfolio: {args.old_name} -> {args.new_name}")
else:
print(f"Portfolio '{args.old_name}' not found.")
sys.exit(1)
elif args.command == "add":
portfolio_name = args.portfolio or store.get_default_portfolio_name()
if not portfolio_name:
print("No portfolios found. Use 'create' to create one first.")
sys.exit(1)
asset = store.add_asset(portfolio_name, args.ticker, args.quantity, args.cost)
print(f"Added {asset.ticker} ({asset.type}) to {portfolio_name}: "
f"{asset.quantity} units @ {format_currency(asset.cost_basis)}")
elif args.command == "update":
portfolio_name = args.portfolio or store.get_default_portfolio_name()
if not portfolio_name:
print("No portfolios found.")
sys.exit(1)
if args.quantity is None and args.cost is None:
print("Must specify --quantity and/or --cost to update.")
sys.exit(1)
asset = store.update_asset(portfolio_name, args.ticker, args.quantity, args.cost)
if asset:
print(f"Updated {asset.ticker} in {portfolio_name}: "
f"{asset.quantity} units @ {format_currency(asset.cost_basis)}")
else:
print(f"Asset '{args.ticker}' not found in portfolio '{portfolio_name}'.")
sys.exit(1)
elif args.command == "remove":
portfolio_name = args.portfolio or store.get_default_portfolio_name()
if not portfolio_name:
print("No portfolios found.")
sys.exit(1)
if store.remove_asset(portfolio_name, args.ticker):
print(f"Removed {args.ticker.upper()} from {portfolio_name}")
else:
print(f"Asset '{args.ticker}' not found in portfolio '{portfolio_name}'.")
sys.exit(1)
except ValueError as e:
print(f"Error: {e}")
sys.exit(1)
except Exception as e:
print(f"Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Stock Analysis - Future Enhancements
Roadmap Overview
v4.0.0 (Current) - Geopolitical Risk & News Sentiment
✅ 8 analysis dimensions with Fear/Greed, short interest, VIX structure, put/call ratio ✅ Safe-haven indicators (GLD, TLT, UUP) with risk-off detection ✅ Breaking news alerts via Google News RSS ✅ Geopolitical risk mapping (Taiwan, China, Russia, Middle East, Banking) ✅ Sector-specific crisis flagging with confidence penalties ✅ 1h caching for shared indicators (Fear/Greed, VIX structure, breaking news) ✅ Async parallel sentiment fetching (5 indicators with 10s timeouts)
v5.0.0 (Current) - Portfolio & Crypto
✅ Portfolio management (create, add, remove, show assets) ✅ Cryptocurrency support (Top 20 by market cap) ✅ Portfolio analysis with --portfolio flag ✅ Periodic returns (--period daily/weekly/monthly/quarterly/yearly) ✅ Concentration warnings (>30% single asset) ✅ Crypto fundamentals (market cap, category, BTC correlation)
v4.1.0 - Performance & Completeness
✅ Full insider trading parsing via edgartools (Task #1) ✅ Market context caching with 1h TTL (Task #3b) 🔧 SEC EDGAR rate limit monitoring (Task #4 - low priority)
Future (v6.0+)
💡 Research phase: Social sentiment, fund flows, on-chain metrics
---
Sentiment Analysis Improvements
1. Implement Full Insider Trading Parsing
Status: ✅ DONE Priority: Medium Effort: 2-3 hours
Current State:
- ✅
get_insider_activity()fetches Form 4 filings via edgartools - ✅ SEC identity configured (
stock-analysis@clawd.bot) - ✅ Aggregates buys/sells over 90-day window
- ✅ Scoring logic: strong buying (+0.8), moderate (+0.4), neutral (0), moderate selling (-0.4), strong (-0.8)
Tasks:
- [ ] Research edgartools API for Form 4 parsing
- [ ] Implement transaction aggregation (90-day window)
- [ ] Calculate net shares bought/sold
- [ ] Calculate net value in millions USD
- [ ] Apply scoring logic:
- Strong buying (>100K shares or >$1M): +0.8
- Moderate buying (>10K shares or >$0.1M): +0.4
- Neutral: 0
- Moderate selling: -0.4
- Strong selling: -0.8
- [ ] Add error handling for missing/incomplete filings
- [ ] Test with multiple tickers (BAC, TSLA, AAPL)
- [ ] Verify SEC rate limit compliance (10 req/s)
Expected Impact:
- Insider activity detection for 4th sentiment indicator
- Increase from 3/5 to 4/5 indicators typically available
---
2. Add Parallel Async Fetching
Status: ✅ DONE (sentiment indicators) Priority: High Effort: 4-6 hours
Current State:
- ✅ Sentiment indicators fetched in parallel via
asyncio.gather() - ✅ 10s timeout per indicator
- Main data fetches (yfinance) still sequential (acceptable)
Tasks:
- [ ] Convert sentiment helper functions to async
- [ ]
async def get_fear_greed_index() - [ ]
async def get_short_interest(data) - [ ]
async def get_vix_term_structure() - [ ]
async def get_insider_activity(ticker) - [ ]
async def get_put_call_ratio(data) - [ ] Update
analyze_sentiment()to useasyncio.gather() - [ ] Handle yfinance thread safety (may need locks)
- [ ] Add timeout per indicator (10s max)
- [ ] Test with multiple stocks in sequence
- [ ] Measure actual runtime improvement
- [ ] Update SKILL.md with new runtime (target: 3-4s)
Expected Impact:
- Reduce runtime from 6-10s to 3-4s per stock
- Better user experience for multi-stock analysis
---
3. Add Caching for Shared Indicators
Status: ✅ DONE (sentiment + breaking news) Priority: Medium Effort: 2-3 hours
Current State:
- ✅ Fear & Greed Index cached (1h TTL)
- ✅ VIX term structure cached (1h TTL)
- ✅ Breaking news cached (1h TTL)
- ✅ Market context (VIX/SPY/QQQ/GLD/TLT/UUP) cached (1h TTL)
Tasks:
- [ ] Design cache structure (simple dict or functools.lru_cache)
- [ ] Implement TTL (time-to-live):
- Fear & Greed: 1 hour
- VIX structure: 1 hour
- Short interest: No cache (per-stock)
- Insider activity: No cache (per-stock)
- Put/Call ratio: No cache (per-stock)
- [ ] Add cache invalidation logic
- [ ] Add verbose logging for cache hits/misses
- [ ] Test multi-stock analysis (e.g.,
BAC TSLA AAPL) - [ ] Measure performance improvement
- [ ] Document caching behavior in SKILL.md
Expected Impact:
- Multi-stock analysis faster (e.g., 3 stocks: 18-30s → 10-15s)
- Reduced API calls to Fear/Greed and VIX data sources
- Same-session analysis efficiency
---
4. Monitor SEC EDGAR Rate Limits
Status: Not Started Priority: Low (until insider trading implemented) Effort: 1-2 hours
Current State:
- SEC EDGAR API has 10 requests/second rate limit
- No rate limit tracking or logging
- edgartools may handle rate limiting internally
Tasks:
- [ ] Research edgartools rate limit handling
- [ ] Add request counter/tracker if needed
- [ ] Implement exponential backoff on 429 errors
- [ ] Add logging for rate limit hits
- [ ] Test with high-volume scenarios (10+ stocks in quick succession)
- [ ] Document rate limit behavior
- [ ] Add error message if rate limited: "SEC API rate limited, try again in 1 minute"
Expected Impact:
- Robust handling of SEC API limits in production
- Clear user feedback if limits hit
- Prevent API blocking/banning
---
Stock Analysis 4.0: Geopolitical Risk & News Sentiment
What's Currently Missing
The current implementation captures:
- ✅ VIX (general market fear)
- ✅ SPY/QQQ trends (market direction)
- ✅ Sector performance
What we don't have yet:
- ❌ Geopolitical risk indicators
- ❌ News sentiment analysis
- ❌ Sector-specific crisis flags
---
7. Geopolitical Risk Index
Status: ✅ DONE (keyword-based) Priority: High Effort: 8-12 hours
Proposed Approach: Option A: Use GPRD (Geopolitical Risk Daily Index) from policyuncertainty.com Option B: Scan news APIs (NewsAPI, GDELT) for geopolitical keywords
Tasks:
- [ ] Research free geopolitical risk data sources
- [ ] Check policyuncertainty.com API availability
- [ ] Evaluate NewsAPI free tier limits
- [ ] Consider GDELT Project (free, comprehensive)
- [ ] Design risk scoring system (0-100 scale)
- [ ] Implement data fetching with caching (4-hour TTL)
- [ ] Map risk levels to sentiment scores:
- Low risk (0-30): +0.2 (bullish)
- Moderate risk (30-60): 0 (neutral)
- High risk (60-80): -0.3 (caution)
- Extreme risk (80-100): -0.5 (bearish)
- [ ] Add to sentiment analysis as 6th indicator
- [ ] Test with historical crisis periods
- [ ] Update SKILL.md with geopolitical indicator
Expected Impact:
- Early warning for market-wide risk events
- Better context for earnings-season volatility
- Complement to VIX (VIX is reactive, geopolitical is predictive)
Example Output:
⚠️ GEOPOLITICAL RISK: HIGH (72/100)
Context: Elevated Taiwan tensions detected
Market Impact: Risk-off sentiment likely---
8. Sector-Specific Crisis Mapping
Status: ✅ DONE Priority: High Effort: 6-8 hours
Current Gap:
- No mapping between geopolitical events and affected sectors
- No automatic flagging of at-risk holdings
Proposed Risk Mapping:
| Geopolitical Event | Affected Sectors | Example Tickers |
|---|---|---|
| Taiwan conflict | Semiconductors | NVDA, AMD, TSM, INTC |
| Russia-Ukraine | Energy, Agriculture | XLE, MOS, CF, NTR |
| Middle East escalation | Oil, Defense | XOM, CVX, LMT, RTX |
| China tensions | Tech supply chain, Retail | AAPL, QCOM, NKE, SBUX |
| Banking crisis | Financials | JPM, BAC, WFC, C |
Tasks:
- [ ] Build event → sector → ticker mapping database
- [ ] Implement keyword detection in news feeds:
- "Taiwan" + "military" → Semiconductors ⚠️
- "Russia" + "sanctions" → Energy ⚠️
- "Iran" + "attack" → Oil, Defense ⚠️
- "China" + "tariffs" → Tech, Consumer ⚠️
- [ ] Add sector exposure check to analysis
- [ ] Generate automatic warnings in output
- [ ] Apply confidence penalty for high-risk sectors
- [ ] Test with historical crisis events
- [ ] Document in SKILL.md
Expected Impact:
- Automatic detection of sector-specific risks
- Clear warnings for exposed holdings
- Reduced false positives (only flag relevant sectors)
Example Output:
⚠️ SECTOR RISK ALERT: Semiconductors
Event: Taiwan military exercises (elevated tensions)
Impact: NVDA HIGH RISK - supply chain exposure
Recommendation: HOLD → downgraded from BUY---
9. Breaking News Check
Status: ✅ DONE Priority: Medium Effort: 4-6 hours
Current Gap:
- No real-time news scanning before analysis
- User might get stale recommendation during breaking events
Proposed Solution:
- Scan Google News or Reuters RSS before analysis
- Flag high-impact keywords within last 24 hours
Tasks:
- [ ] Choose news source (Google News RSS, Reuters API, or NewsAPI)
- [ ] Implement news fetching with 24-hour lookback
- [ ] Define crisis keywords:
- War/Conflict: "war", "invasion", "military strike", "attack"
- Economic: "recession", "crisis", "collapse", "default"
- Regulatory: "sanctions", "embargo", "ban", "investigation"
- Natural disaster: "earthquake", "hurricane", "pandemic"
- [ ] Add ticker-specific news check (company name + keywords)
- [ ] Generate automatic caveat in output
- [ ] Cache news check results (1 hour TTL)
- [ ] Add
--skip-newsflag for offline mode - [ ] Test with historical crisis dates
- [ ] Document in SKILL.md
Expected Impact:
- Real-time awareness of breaking events
- Automatic caveats during high volatility
- User protection from stale recommendations
Example Output:
⚠️ BREAKING NEWS ALERT (last 6 hours):
"Fed announces emergency rate hike"
Impact: Market-wide volatility expected
Caveat: Analysis may be outdated - rerun in 24h---
10. Safe-Haven Indicators
Status: ✅ DONE Priority: Medium Effort: 3-4 hours
Current Gap:
- No detection of "risk-off" market regime
- VIX alone is insufficient (measures implied volatility, not capital flows)
Proposed Indicators:
- Gold (GLD) - Flight to safety
- US Treasuries (TLT) - Bond market fear
- USD Index (UUP) - Dollar strength during crisis
Risk-Off Detection Logic:
IF GLD +2% AND TLT +1% AND UUP +1% (all rising together)
THEN Market Regime = RISK-OFFTasks:
- [ ] Fetch GLD, TLT, UUP price data (5-day change)
- [ ] Implement risk-off detection algorithm
- [ ] Add to market context analysis
- [ ] Apply broad risk penalty:
- Risk-off detected → Reduce all BUY confidence by 30%
- Add caveat: "Market in risk-off mode - defensive positioning recommended"
- [ ] Test with historical crisis periods (2008, 2020, 2022)
- [ ] Add verbose output for safe-haven movements
- [ ] Document in SKILL.md
Expected Impact:
- Detect market-wide flight to safety
- Automatic risk reduction during panics
- Complement geopolitical risk scoring
Example Output:
🛡️ SAFE-HAVEN ALERT: Risk-off mode detected
- Gold (GLD): +3.2% (5d)
- Treasuries (TLT): +2.1% (5d)
- USD Index: +1.8% (5d)
Recommendation: Reduce equity exposure, favor defensives---
General Improvements
11. Add Social Sentiment (Future Phase)
Status: Deferred Priority: Low Effort: 8-12 hours
Notes:
- Requires free API (Twitter/Reddit alternatives?)
- Most sentiment APIs are paid (StockTwits, etc.)
- Research needed for viable free sources
12. Add Fund Flows (Future Phase)
Status: Deferred Priority: Low Effort: 6-8 hours
Notes:
- Requires ETF flow data
- May need paid data source
- Research free alternatives
---
Implementation Priorities
v4.1.0 Complete
- ✅ Task #1 - Insider trading parsing via edgartools
- ✅ Task #3b - Market context caching (1h TTL)
- 🔧 Task #4 - SEC EDGAR rate limits (low priority, only if hitting limits)
Completed in v4.0.0
- ✅ Task #2 - Async parallel fetching (sentiment)
- ✅ Task #3 - Caching for shared indicators (sentiment + news)
- ✅ Task #7 - Geopolitical risk (keyword-based)
- ✅ Task #8 - Sector-specific crisis mapping
- ✅ Task #9 - Breaking news check
- ✅ Task #10 - Safe-haven indicators
---
Version History
- v5.0.0 (2026-01-16): Portfolio management, cryptocurrency support (Top 20), periodic analysis
- v4.1.0 (2026-01-16): Full insider trading parsing via edgartools, market context caching
- v4.0.0 (2026-01-15): Geopolitical risk, breaking news, safe-haven detection, sector crisis mapping
- v3.0.0 (2026-01-15): Sentiment analysis added with 5 indicators (3-4 typically working)
- v2.0.0: Market context, sector performance, earnings timing, momentum
- v1.0.0: Initial release with earnings, fundamentals, analysts, historical
Related skills
Forks & variants (1)
Stock Analysis has 1 known copy in the catalog totaling 143 installs. They canonicalize to this original listing.
- sundial-org - 143 installs
FAQ
What data sources does stock-analysis use?
Yahoo Finance (stock/crypto prices, fundamentals, analyst ratings), CNN Fear & Greed Index, Google News RSS (breaking news), FINRA (short interest), SEC Form 4 (insider trades).
How often is data refreshed?
Yahoo Finance lags 15-20 minutes; short interest ~2 weeks; insider trades 2-3 days; breaking news 15-60 minutes. Shared indicators cached 1 hour.
Can I analyze non-US stocks or crypto outside top 20?
US markets only. Crypto limited to top 20 by market cap (BTC-USD, ETH-USD, SOL-USD, etc.). Non-US tickers have incomplete data.
Is Stock Analysis safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.