Computes 20+ technical indicators, and combines sentiment signals into a single decision score. It outputs a clear buy / hold / sell recommendation with interactive charts and locally stored run history.
Framework-agnostic core, three consumption surfaces: a LangGraph subgraph, a set of LangChain @tool functions, and a single-page web UI.
yfinance for stocks, ccxt (binance/bybit/…) for crypto, polygon/alpha-vantage/alpaca for real-time. Provider strings accept a head:extra form. Zero-key providers just work.
Core (RSI, MACD, Bollinger, ATR, stochastic, OBV, SMAs/EMAs) → Extended (ADX/DMI, Ichimoku, VWAP, CCI, Williams %R, MFI, Aroon, PSAR) → Full (the entire pandas-ta catalog).
Each source normalises to [-1, 1] with a confidence. A confidence-weighted mean yields the composite; per-source rationales surface in the UI.
15 components collapse to three category scores (momentum · trend · volatility) and then to a single composite in [−1, +1], thresholded into a discrete label.
500-bar history by default; line + filled-area close price with a secondary volume axis. Runs entirely in-browser via Chart.js.
Past runs live as re-runnable chips. Click one and the form + request replay. Nothing is persisted server-side.
A LangGraph subgraph fans out from a single fetch, then fans back in. The whole pipeline is stateless, cached at the module level, and pluggable at three seams: providers, indicators, sentiment sources.
Each indicator is normalised to a score in [−1, +1] — +1 is maximally bullish, −1 maximally bearish. Scores are averaged within three categories, then combined with category weights into a single composite. A ±0.20 dead-zone separates actionable signals from noise.
When a category has no available components, its weight is redistributed proportionally to the others — missing indicators never silently drag the composite toward zero.
| Category | Indicator | Formula | +1 / −1 anchors |
|---|---|---|---|
| M | RSI (14) | (50 − RSI) / 20 | 30 → +1 · 70 → −1 |
| M | Stochastic %K | (50 − %K) / 30 | 20 → +1 · 80 → −1 |
| M | Williams %R (14) | (−50 − WR) / 30 | −80 → +1 · −20 → −1 |
| M | CCI (20) | −CCI / 150 | −150 → +1 · +150 → −1 |
| M | MFI (14) | (50 − MFI) / 30 | 20 → +1 · 80 → −1 |
| V | Bollinger %B | (0.5 − %B) × 2 | 0 → +1 · 1 → −1 |
| T | Price vs SMA(50) | (close − SMA₅₀) / SMA₅₀ / 0.05 | ±5% distance saturates |
| T | Price vs SMA(200) | (close − SMA₂₀₀) / SMA₂₀₀ / 0.10 | ±10% distance saturates |
| T | EMA(12) vs EMA(26) | (EMA₁₂ − EMA₂₆) / (close × 0.02) | spread = 2% close |
| T | MACD histogram | hist / (close × 0.005) | |hist| = 0.5% close |
| T | Aroon oscillator | aroon / 50 | ±50 on [−100, +100] |
| T | Price vs PSAR | (close − PSAR) / close / 0.02 | ±2% distance saturates |
| T | Price vs VWAP | (close − VWAP) / VWAP / 0.03 | ±3% distance saturates |
| T | Ichimoku | cloud (±0.6) + T/K (±0.4) | cloud position + tenkan-kijun |
| T | DI+/DI− (ADX-gated) | ((DI+ − DI−)/25) × min(ADX/25, 1) | 25pt spread, weak-trend scale-down |
Momentum oscillators are read contrarianly (oversold → BUY), trend indicators are read directionally, Bollinger %B is contrarian (price at lower band → BUY). Every result is clamped to [−1, +1].
Framework-agnostic core means the same pipeline ships as a local Python library, a Docker image, and a Helm release on Kubernetes.
pip install -e ".[web]" ./web/run.sh # → http://127.0.0.1:8000
cp .env.example .env docker compose up --build # → http://127.0.0.1:8000
kubectl create ns indicators kubectl -n indicators create secret generic \ indictor-api-keys --from-env-file=.env ./redeploy.sh # build → push → helm upgrade
Type an asset like BTC/USDT or AAPL, hit Analyze, and watch the full stack collapse into one number in under 2 seconds.