skills/momentum-monitor/SKILL.md
Per-stock momentum & flow monitor — volume dynamics, MA structure & crosses, short interest, spike detection. Use when user asks about 爆大量, 動能, volume spike, moving average cross, short interest / squeeze potential, or a quick bullish/bearish flow read on a single ticker.
npx skillsauth add kavi-lin/stock momentum-monitorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Single-ticker technical flow read:
Designed as a focused companion to technical-analyst skill — technical-analyst handles
RSI/MACD/chart patterns; this skill handles the tape/flow side (volume + shorts + MA crosses).
# Standalone (any terminal)
python3 skills/momentum-monitor/scripts/momentum.py TSLA
python3 skills/momentum-monitor/scripts/momentum.py TSLA --json-only
python3 skills/momentum-monitor/scripts/momentum.py TSLA --no-cache
python3 skills/momentum-monitor/scripts/momentum.py TSLA --max-age 300
Within Claude Code, trigger via:
/momentum-monitor TSLAscripts/screen.py runs momentum.py's analyze() in parallel across a
universe, then filters, ranks, and emits CSV + Markdown. Same cache as
single-ticker mode, so repeat runs within 15 min are near-instant.
# Built-in S&P 500 universe, bullish filter
python3 skills/momentum-monitor/scripts/screen.py --universe sp500 --min-score 70
# Custom ticker list
python3 skills/momentum-monitor/scripts/screen.py --tickers AAPL,MSFT,NVDA,AMD
# Watchlist file (one ticker per line; # comments allowed)
python3 skills/momentum-monitor/scripts/screen.py --tickers-file my_watchlist.txt
# Multi-filter: Stage 2 uptrend + fresh golden cross, exclude blow-off risk
python3 skills/momentum-monitor/scripts/screen.py \
--universe sp500 \
--stage "Stage 2 uptrend" \
--signal fresh_golden_cross_20_50 \
--exclude-warning parabolic_blowoff_risk \
--top 25
| Flag | Effect |
|---|---|
| --min-score N / --max-score N | Composite score bounds |
| --stage "Stage 2 uptrend" | Exact stage match |
| --label BULLISH | Composite label match |
| --signal X (repeatable, AND) | Required signal in .signals[] |
| --exclude-signal X / --exclude-warning X | Negative filters |
| --min-rsi N / --max-rsi N | RSI-14 bounds |
| Flag | Effect | Replaces |
|---|---|---|
| --min-nhp N | 52w high proximity %; e.g. -5 = within 5% of 52w high | parabolic_blowoff_risk exclusion (which over-filters winners) |
| --min-rs N | Min RS rating (0-99) vs SPY 3M; e.g. 75 = top 25% momentum | --stage "Stage 2 uptrend" exact-string match |
| --min-rs-3m-pct N | Min absolute RS 3M %; e.g. 15 = +15pp over SPY in 3M | (additive) |
| --require-vcp | Require VCP compression (4w/12w range ratio < 0.55) | fresh_golden_cross_50_200 (lagging) |
| --require-volume-dryup-spike | Require dry-up (5D < 0.75×20D) AND spike (today >1.5×20D) | volume_expansion (too loose) |
| --min-eps-yoy N | Min latest-Q EPS YoY %; reads earnings-analyst cache | (NEW — adds fundamental confirmation) |
| --require-eps-accelerating | Require growth_acceleration == accelerating (CAN SLIM C law) | (NEW) |
| --min-dtc N | Min days-to-cover; 5+ = squeeze candidate | better than --signal high_short_interest |
| --top-sectors N | Only keep tickers in top-N sectors by sector_intel.json composite_score | (NEW — strength-pre-filter) |
signals[])| Signal | Trigger |
|---|---|
| at_52w_new_high | Price within 0.5% of 52w high |
| near_52w_high | Price within 5% of 52w high |
| rs_leader_3m | RS 3M ≥ +15pp vs SPY |
| vcp_compressed | 4w/12w range ratio < 0.55 |
| vol_dryup_spike | 5D vol < 0.75×20D + today > 1.5×20D |
| eps_accelerating | earnings-analyst cache shows accelerating |
| dtc_squeeze_candidate | DTC ≥ 5 + price > MA50 |
# Find true momentum leaders (RS top 25% in top-4 sectors, near 52w high, accelerating EPS)
python3 skills/momentum-monitor/scripts/screen.py \
--universe sp500 \
--top-sectors 4 \
--min-rs 75 \
--min-nhp -5 \
--require-eps-accelerating
# VCP setup hunter (compression + dry-up spike = breakout setup)
python3 skills/momentum-monitor/scripts/screen.py \
--universe sp500 \
--require-vcp \
--require-volume-dryup-spike
# Squeeze candidates with momentum confirmation
python3 skills/momentum-monitor/scripts/screen.py \
--universe sp500 \
--min-dtc 5 \
--signal stage2_uptrend_intact
| Flag | Default | Effect |
|---|---|---|
| --workers N | 15 | Parallel yfinance fetches |
| --no-cache | off | Force fresh fetch for every ticker |
| --max-age SEC | 900 | Cache TTL |
| --top N | 30 | MD table row limit (CSV always has all) |
| --output-dir PATH | cache/ | Where to write screen_YYYYMMDD_HHMM.csv |
| --json | off | Also print full JSON summary to stdout |
Ship with scripts/universes/sp500.txt (503 tickers). Add your own file
there and pass its name: --universe my_list.
/momentum-screen (see .claude/commands/momentum-screen.md)scripts/journal.py accumulates screener results over time and fills in
5/20/60-day forward returns so you can measure signal quality empirically.
# Record today's screen (or pass --journal to screen.py so it auto-appends)
python3 skills/momentum-monitor/scripts/journal.py snapshot cache/screen_YYYYMMDD_HHMM.csv
# Run DAILY after market close to fill matured returns
python3 skills/momentum-monitor/scripts/journal.py update
# Aggregate + print summary
python3 skills/momentum-monitor/scripts/journal.py stats
# Wipe journal + stats (asks for YES confirmation; use --yes to skip)
python3 skills/momentum-monitor/scripts/journal.py clear
skills/momentum-monitor/journal/journal.jsonl — append/update-only, one JSON per snap × tickerskills/momentum-monitor/journal/stats.json — rolling aggregates (by_signal, by_score_bin, by_stage){
"snap_id": "screen_20260418_1430",
"snap_date": "2026-04-18",
"ticker": "NVDA",
"entry_price": 201.68,
"score": 72.5,
"label": "BULLISH",
"stage": "Stage 2 uptrend",
"ratio_20d": 1.35,
"above_ma200_pct": 11.0,
"signals": ["fresh_golden_cross_20_50", "volume_expansion"],
"warnings": [],
"returns": {
"5d": {"value": null, "filled_date": null},
"20d": {"value": 4.2, "filled_date": "2026-05-15"},
"60d": {"value": null, "filled_date": null}
},
"mae_20d": -1.8,
"mfe_20d": 6.4,
"updated_at": "2026-05-15T16:05:10"
}
Per signal: n, win_rate, mean, median, p25, p75, min, max
(default horizon = 20d). Also mae_mfe_by_signal with mean/median MAE and MFE.
screen.py --journal once pre-open (records tickers seen)journal.py update then journal.py statsby_signal — signals with n ≥ 30 AND win_rate ≥ 0.55
are candidates to promote into edge-candidate-agent tickets/momentum-journal <snapshot|update|stats>Per-ticker file cache at skills/momentum-monitor/cache/momentum_<TICKER>.json,
default TTL 900 s (15 min). yfinance data doesn't need sub-minute granularity
for this read; 15 min is enough. --no-cache forces a fresh fetch.
{
"ticker": "TSLA",
"generated_at": "ISO-8601",
"price": 400.62,
"cache_hit": false,
"cache_age_sec": 0,
"volume": {
"today": 65000000,
"avg_20d": 48000000,
"avg_50d": 52000000,
"ratio_20d": 1.35,
"spike_label": "NORMAL | MILD_SPIKE (>=2x) | HEAVY_SPIKE (>=3x)",
"spike_days_last_10": 2,
"volume_trend": "expanding | stable | contracting"
},
"ma_structure": {
"ma_20": 395.50,
"ma_50": 380.12,
"ma_200": 310.40,
"stage": "Stage 1 basing | Stage 2 uptrend | Stage 3 top | Stage 4 downtrend",
"above_ma20_pct": 1.3,
"above_ma200_pct": 29.1,
"recent_crosses": [
{"type": "golden_cross_20_50", "date": "YYYY-MM-DD", "days_ago": 14}
]
},
"short_interest": {
"shares_short": 18500000,
"short_pct_float": 1.2,
"short_ratio_days_to_cover": 2.3,
"last_updated": "YYYY-MM-DD",
"interpretation": "low | moderate | high | squeeze_candidate"
},
"rsi": {
"rsi_14": 68.4,
"zone": "oversold | neutral | bullish | overbought"
},
"momentum_composite": {
"score": 72,
"label": "BEARISH | WEAK | NEUTRAL | BULLISH | STRONGLY_BULLISH",
"components": {
"volume_flow": 60,
"ma_stage": 95,
"short_squeeze_potential": 20,
"trend_acceleration": 80
}
},
"signals": ["stage2_uptrend_intact", "volume_expansion", "low_short_interest"],
"warnings": []
}
Equal-weight (25% each) of 4 components, each normalized 0-100:
volume_flow (25%) — expansion vs contraction
ratio_20d ≥ 1.5 AND today > prev day → 80-100ratio_20d ≥ 1.0 → 50-80ratio_20d < 0.7 → 20-40ma_stage (25%) — Weinstein stage
short_squeeze_potential (25%) — high short + positive momentum
short_pct_float > 20% AND above_ma20_pct > 5% → 80+short_pct_float 10-20% → 50-70short_pct_float < 3% → 10-30 (no fuel)trend_acceleration (25%) — how far above long-term MA + recent cross
above_ma200_pct 20-50% healthy uptrend → 70-80above_ma200_pct > 100% parabolic exhaustion → 20-40volume_today / avg_20d ≥ 3.0 → HEAVY_SPIKE (spike_label)volume_today / avg_20d ≥ 2.0 → MILD_SPIKEspike_days_last_10: count of days in last 10 sessions where volume ≥ 2× avg_20d.
Scans last 30 sessions for these events:
golden_cross_20_50 — 20MA crosses above 50MAdeath_cross_20_50 — 20MA crosses below 50MAgolden_cross_50_200 — 50MA crosses above 200MA (major bull)death_cross_50_200 — 50MA crosses below 200MA (major bear).signals[])stage2_uptrend_intact — MA stack 20>50>200 validvolume_expansion — ratio_20d ≥ 1.3 + today upvolume_dry_up — ratio_20d < 0.7low_short_interest / high_short_interest / squeeze_candidateparabolic_blowoff_risk — above_ma200_pct > 50%fresh_golden_cross — golden_cross_20_50 within 10 daysfresh_death_cross — death_cross_20_50 within 10 daysoverbought_rsi — RSI-14 > 70 (warning, near-term exhaustion)oversold_rsi — RSI-14 < 30 AND Stage 2 uptrend (signal, momentum pullback buy; oversold in a downtrend is weakness, not opportunity — not flagged)Classic Wilder smoothing (first value is 14-period SMA, subsequent use exponential
with α = 1/14). Pure tape indicator, not included in composite score because
it overlaps with trend_acceleration — kept as independent signal/warning + CSV
column so you can filter or analyze separately.
CLI:
python3 screen.py --universe sp500 --max-rsi 70 # exclude overbought
python3 screen.py --universe sp500 --min-rsi 30 --max-rsi 60 # non-extreme zone
last_updated may be 1-2 weeks oldshort_pct_float from yfinance info; occasionally null for micro-caps → interpretation = "unknown"development
# earnings-analyst — 個股財報深度分析 > **Trigger**: `財報 [TICKER]` > **Version**: V1.0 > **Data Source**: FMP HTTP REST(`$FMP_API_KEY`) ## 目的 針對單一個股產出**深度財報分析報告**(逐季趨勢、品質指標、估值、分析師共識),涵蓋 sector V1.4 與 `分析 [TICKER]` 既有 protocol **沒有**的「財報層級」深潛內容。 ## 與既有 skill 的差異 | Skill | 重點 | 觸發 | |---|---|---| | `us-stock-analysis` | 估值/技術/情緒 snapshot(yfinance + FMP partial) | Phase 2 fundamentals lane | | `earnings-valuation-forecaster` | 12M 目標價 3×3 敏感度 | ad-hoc / earnings 前 14 天 | | `earnings-trade-analyzer` |
testing
Daily Top N hot themes × Top M short-term movers per theme. Combines theme-detector heat scoring (medium-term) with short-term-target predictions (1d/5d/15d) into a "Tactical Opportunity Radar" recommendation log. Tags concentration WARNING when ≥2 picks share theme. Records FRED + market regime snapshot at recommendation time for future backtest cross-tabs. Standalone — not auto-wired into investment_protocol. Use for daily watchlist refresh / Dashboard推薦面板feed / batch screening across hot themes.
testing
Short-term (1d / 5d / 15d) directional projection for a US stock — "Tactical Opportunity Radar". Outputs target range + confidence breakdown + benchmark-relative alpha + trading meta (stop / position size hint / exit trigger). Each horizon uses independent weights from config/weights.yaml. Refuses to project when source data is stale (returns insufficient_data with reasons). Hard-clamped to prevent cold-start absurd predictions. Use when caller wants short-term directional bias on a specific ticker, NOT for long-term valuation (use earnings-valuation-forecaster for 12-month). Standalone — not auto-wired into investment_protocol.
tools
Shared Finnhub API client used by other skills. Provides rate-limited (60/min), cached, retry-aware access to 17 Finnhub endpoints covering quotes, OHLCV, fundamentals, earnings calendar, earnings surprises, insider transactions, recommendation history, price targets, upgrades/downgrades, dividends, splits, IPOs, and SEC filings. Also exports adapters that normalize Finnhub raw responses into FMP-compatible shapes so that downstream code can swap providers without changing call sites. Use when another skill needs Finnhub data or when building a unified provider layer.