Spaces:
Running
Stock Predictor β Codex Handoff
Project in one line
Taiwan stock ML predictor (FastAPI + React/Vite). Runs locally on a Mac mini, also deployed on HuggingFace Space. Mac computes predictions; HF Space reads them from a shared HF Dataset queue.
Repo layout
stock-predictor/
βββ main.py # FastAPI app entry, lifespan warmup
βββ routers/
β βββ stock.py # /api/stock/* endpoints
β βββ line_webhook.py # LINE Bot webhook
βββ services/
β βββ predictor_service.py # get_prediction(), caches, quick estimate
β βββ hf_queue_service.py # HF Dataset queue read/write
β βββ backtest_service.py # walk-forward backtest
β βββ news_service.py # Gemini news overlay
βββ models/
β βββ predictor.py # StockPredictor, FEATURE_COLUMNS, _build_features
βββ data/
β βββ fetcher.py # yfinance + TWSE history
β βββ institutional_flow.py # δΈε€§ζ³δΊΊ
β βββ margin_flow.py # θθ³θεΈ
β βββ monthly_revenue.py # ζηζΆ
β βββ ptt_sentiment.py # PTT [ζ¨η] sentiment (currently returns zeros)
β βββ ...
βββ indicators/
β βββ technical.py # add_all_indicators(), add_cross_asset_tw()
βββ scripts/
β βββ hf_queue_worker.py # Mac launchd job: poll queue, push predictions
β βββ backtest_3way.py # baseline backtest runner
β βββ backtest_c*.py # per-experiment backtest scripts (C1βC12)
β βββ improvement_harness.py # runs backtest on 5 stocks, checks thresholds
βββ frontend/
β βββ src/
β βββ components/ # React components (StockPage, RecommendPage, etc.)
βββ static/ # built frontend (served by FastAPI)
βββ docs/
βββ improvements_log.md # history of model experiments
Deployment
Mac local (port 7861)
- Managed by launchd:
com.dennis.stock-predictor - Restart:
launchctl stop com.dennis.stock-predictor && launchctl start com.dennis.stock-predictor - Logs:
logs/server.log
HF Space
- URL:
https://dennischan0909-dockerspace.hf.space/ - Rebuilds automatically on push to
mainbranch - HF Dataset queue:
DennisChan0909/stock-predictor-queue(stores predictions as JSON) - Current runbook:
docs/huggingface_integration.md - HF is the lightweight serving layer. Mac remains the durable ML/precompute
worker; HF returns
quick_ruleswhile waiting for Mac queue results.
Mac worker (launchd, every 5 min)
scripts/hf_queue_worker.pyβ computes predictions for WATCHLIST (20 stocks) + any pending queue items, pushes to HF Dataset- Commands:
./venv/bin/python scripts/hf_queue_worker.py statusβ show queue./venv/bin/python scripts/hf_queue_worker.py push 2330 0050β force-push specific stocks
Frontend build + deploy
cd frontend && npm run build
cp frontend/dist/index.html static/index.html
cp frontend/dist/assets/* static/assets/
git add -u && git commit -m "..." && git push origin main
HF Space prediction flow (as of 2026-05-12)
/api/stock/<code>/predictcalledget_prediction()checks in-memory pred cache (30 min TTL on HF)- Cache miss: checks HF Dataset queue synchronously (
get_cached_result)- Queue hit β return Mac ML result immediately, cache it for 30 min
- Queue miss β call
enqueue()+ fall through to quick estimate
- Quick estimate: rule-based signal from technical indicators (<100ms), NOT cached on HF Space
- Background ML training is skipped on HF Space (Mac worker handles it)
- Startup warmup (
_warmup_from_queueinmain.py) pre-loads all "done" queue results before accepting requests
Model
Stack
RF + XGB + LGBM ensemble (CatBoost compiled but optional). 29 features after SHAP pruning.
Labels
Triple barrier (C3): upper/lower barrier = close Γ (1 Β± vol_20d Γ 1.0), vertical = 5 trading days. Label = first barrier hit.
Current baseline (5-stock average, 24-month walk-forward)
dir_accuracy β 42%up_precision β 53%FEATURE_COLUMNS: 29 features inmodels/predictor.py:153
Meta-label filter (C4)
meta_filtered field in prediction output. Secondary RF trained on (features + buy_prob β correct?). Threshold 0.50.
Experiments history (docs/improvements_log.md)
| ID | What | Result |
|---|---|---|
| C1 | fracdiff price features | FAILED |
| C3 | Triple barrier labels | PASSED β dir_acc +9.8pp |
| C4 | Meta-label filter | PASSED β up_prec +4.1pp |
| C5 | SHAP pruning (removed 11 low-signal features) | PASSED (+1pp), now in FEATURE_COLUMNS |
| C6 | HMM regime detection | FAILED (<1.5pp) |
| C2 | PTT sentiment (ptt_sentiment_1d, _5d_ma) | FAILED β data pipeline returns 0 rows for all stocks |
| C7 | Calendar features + 36m lookback | FAILED β up_prec dropped -5.8pp |
| C8 | Optuna RF tuning | FAILED β params already near-optimal |
| C9 | Multi-stock training universe | FAILED |
| C10 | Asymmetric triple barrier grid | FAILED |
| C11 | Full ensemble walk-forward | FAILED |
| C12 | 1-day label horizon | FAILED catastrophically |
All C-experiments are self-contained in scripts/backtest_c*.py. New experiments should follow the same pattern: run scripts/improvement_harness.py to validate before touching models/predictor.py.
Known issues / pending work
Hermes / agent-team integration audit (2026-05-21)
Hermes added standalone agent-team utilities: Antigravity notes, W&B logging, data quality validation, and Telegram notification wrappers. These are intended to stay outside the core predictor path unless separately promoted.
Hermes hierarchy entry point: docs/hermes/README.md. Read
docs/hermes/rules.md before changing Hermes utilities; it records that generic
Hermes helper cleanup must not modify predictor/recommendation/precompute
logic.
Codex audit fixes applied:
scripts/telegram_bot_wrapper.py: fixed.env.telegramparsing, and made CLI arguments mode-specific so--update,--validation, and experiment modes can run without a dummy--message.scripts/test_agent_integration.py: added missingargparseimport and avoided importing Python's built-inantigravityeaster-egg module. The check now uses the CLI/config path instead..gitignore: ignore local.env.telegramand.env.wandbfiles; keep.env.telegram.exampleas the shareable template.
Verification:
./venv/bin/python -m py_compile scripts/telegram_bot_wrapper.py scripts/send_experiment_notification.py scripts/data_quality_validator.py scripts/test_agent_integration.py scripts/wandb_experiment_tracker.py
./venv/bin/python scripts/telegram_bot_wrapper.py --message "Hermes side-effect smoke" --success
./venv/bin/python scripts/telegram_bot_wrapper.py --update --step 1 --total 2
./venv/bin/python scripts/data_quality_validator.py --check features
./venv/bin/python scripts/test_agent_integration.py --quiet
Residual risk: hermes_tools is not importable in the normal venv, so Telegram
wrapper uses fallback console mode unless Hermes injects that module at runtime.
Do not wire these utilities into production precompute or recommendation
publishing until a real Telegram send and no-lookahead validation gate pass.
PTT sentiment (data/ptt_sentiment.py)
add_ptt_sentiment() exists but returns 0 rows for all stocks. PTT scraper (scripts/survey_ptt.py) exists. The merge pipeline needs debugging β likely a date alignment or cache miss issue. Features ptt_sentiment_1d, ptt_sentiment_5d_ma are NOT in FEATURE_COLUMNS yet.
Improve harness threshold
Current pass gate: mean(dir_accuracy) > 31.5% β this was set before C3/C4/C5 raised the baseline to ~42%. Should be updated to > 43.5% and up_precision > 54% for any new experiment to be meaningful.
Market Scan (RecommendPage)
Custom stock input added (2026-05-12). Backend /api/stock/recommend accepts ?watchlist=2330,0050 query param. State and handlers are wired.
Key env vars
HF_TOKEN (or .hf_token file) β HF Hub write access
SPACE_ID / HF_SPACE_ID / SPACE_HOST β set automatically on HF, detect HF vs Mac
WATCHLIST_STOCKS β override default 20-stock watchlist in worker
LIGHTWEIGHT_MODE=1 β use 12-month training window (HF)
LINE_CHANNEL_SECRET / ACCESS_TOKEN β LINE Bot
How to run a new backtest experiment
# 1. Write scripts/backtest_cN.py following backtest_c3.py as template
# 2. Run improvement_harness.py which tests on 5 stocks:
./venv/bin/python scripts/improvement_harness.py --script scripts/backtest_cN.py
# Pass gate: mean dir_accuracy > 43.5% AND up_precision > 54%
# If PASSED: update FEATURE_COLUMNS in models/predictor.py, add row to docs/improvements_log.md
# If FAILED: do NOT touch models/predictor.py, log result only
Quick test commands
# Predict
curl http://localhost:7861/api/stock/2330/predict | python3 -m json.tool | head -20
# Backtest
curl "http://localhost:7861/api/stock/2330/backtest?months=6" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['stats'])"
# Queue status
./venv/bin/python scripts/hf_queue_worker.py status
# Force-push to queue
./venv/bin/python scripts/hf_queue_worker.py push 2330 0050
# Run frontend dev server
cd frontend && npm run dev