Why Multi-Agent?
Single-model approaches to stock analysis suffer from a fundamental problem: they try to compress too many distinct reasoning tasks into one function. A single LLM call that must simultaneously analyze market data, assess risk, verify news, and write a report inevitably produces shallow, hallucinated outputs.
The solution is multi-agent decomposition — split the task into specialized sub-tasks, each handled by an agent with a focused context. StockAudit AI implements this with three CrewAI agents, an XGBoost risk model, and a TF-IDF RAG pipeline.
System Architecture
Ticker Input
|
v
+------------------+ +--------------------+
| Market Data |--->| Feature Engineering |
| (yfinance) | | 30+ indicators |
+------------------+ +--------------------+
|
+--------------+--------------+
| | |
v v v
+-----------+ +----------+ +-----------+
| Risk | | Alpha | | RAG |
| XGBoost | | Signals | | TF-IDF |
| + SHAP | | Whale | | News |
+-----------+ | Momentum | +-----------+
| Retail |
+----------+
| | |
v v v
+-----------------------------------------+
| CrewAI Agents |
| Market Analyst -> Risk & Compliance |
| -> Report Summary |
+-----------------------------------------+
|
v
SSE Stream -> Frontend
Layer 1: Market Data + Feature Engineering
The market_data.py module fetches from yfinance:
- Info: price, market cap, sector, P/E, beta
- History: 6 months of OHLCV at daily resolution
- Financials: balance sheet, cash flow, income statement
From these, 30+ technical features are computed:
| Category | Features |
|---|---|
| Momentum | RSI, MACD, MACD signal, MACD histogram |
| Trend | SMA 20/50, EMA 12/26, price vs MA ratios |
| Volatility | ATR, Bollinger Band width, rolling std dev |
| Volume | Volume ratio (current vs avg), volume trend |
| Macro | SPY return, sector ETF return, VIX, Treasury yield |
Layer 2: XGBoost Risk Model
The risk model is an XGBoost Regressor trained on synthetic data engineered to reflect realistic financial risk patterns.
Why XGBoost?
- Handles non-linear feature interactions naturally (e.g., high RSI + low volume has different risk implications than high RSI + high volume)
- Built-in regularization prevents overfitting on noisy financial data
- Native feature importance and SHAP compatibility for interpretability
The model outputs two scores:
- Risk Score (0–100) — higher = riskier. Driven by volatility, momentum divergence, and volume anomalies.
- Confidence Score (0–100) — how certain the model is in its risk assessment. Lower when features fall outside training distribution.
SHAP Explainability
Every prediction is accompanied by a SHAP waterfall showing which features contributed most to the risk score:
import shap
def explain(features: np.ndarray) -> list[dict]:
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(features)
return [
{'feature': name, 'contribution': float(val)}
for name, val in zip(FEATURE_NAMES, shap_values[0])
]
This gives users actionable insight: not just "risk score is 72" but "risk score is 72, driven primarily by elevated RSI (+14), volume ratio anomaly (+9), and momentum divergence (+6)."
Layer 3: Alpha Signals
Three domain-specific anomaly detectors run in parallel:
Whale Detection
Looks for block trades exceeding 2 standard deviations from the 20-day rolling mean trade size. Filters for institutional-scale activity.
def detect_whale(trades: pd.Series) -> WhaleSignal:
rolling_mean = trades.rolling(20).mean()
rolling_std = trades.rolling(20).std()
z_scores = (trades - rolling_mean) / (rolling_std + 1e-8)
anomalies = trades[z_scores > 2.0]
return WhaleSignal(
detected=len(anomalies) > 0,
magnitude=anomalies.max() / rolling_mean.iloc[-1] if len(anomalies) > 0 else 0
)
Momentum Exhaustion
Detects divergence between price trend and momentum (MACD histogram rate-of-change). Price may still be rising, but if momentum is decelerating, a reversal becomes statistically more likely.
Retail Concentration
Measures the fraction of odd-lot and small-order volume relative to total volume. When retail participation exceeds the 90th percentile of the trailing 60-day distribution, short-term noise is elevated.
Layer 4: RAG Over News
A TF-IDF vectorizer indexes recent financial news articles for the target ticker. At report time, the top-3 most relevant articles are retrieved via cosine similarity and injected into the report generation prompt.
Why TF-IDF instead of embeddings?
- Lower latency (no GPU required for encoding)
- Deterministic and reproducible
- Sufficient for short financial news snippets
- Zero operational cost
vectorizer = TfidfVectorizer(max_features=5000, stop_words='english')
tfidf_matrix = vectorizer.fit_transform(all_articles)
def retrieve(query: str, k: int = 3) -> list[Article]:
query_vec = vectorizer.transform([query])
scores = cosine_similarity(query_vec, tfidf_matrix).flatten()
top_indices = scores.argsort()[-k:][::-1]
return [articles[i] for i in top_indices]
Layer 5: CrewAI Agent Orchestration
Three agents collaborate in a sequential workflow:
1. Market Analyst
Role: Interpret market data, technical indicators, and alpha signals. Input: All raw data + computed features + signal detections. Output: Structured analysis noting key patterns and anomalies.
2. Risk & Compliance
Role: Assess risk posture using the XGBoost model output and SHAP explanations. Input: Market analyst's output + risk score + confidence score + SHAP contributions. Output: Risk assessment narrative, flagging specific concerns.
3. Report Summary
Role: Compile the final audit report, including RAG-retrieved news context. Input: Risk assessment + retrieved articles. Output: Comprehensive markdown report streamed via SSE.
Progressive Streaming
The frontend consumes analysis results via Server-Sent Events (SSE) — each phase emits events as they complete:
event: market_data
event: alpha_signals
event: risk_analysis
event: agent_progress
event: report_chunk
event: analysis_complete
This gives the user a live, progressive experience rather than a static loading spinner.
Performance
| Phase | Average Latency |
|---|---|
| Data fetch + features | 1.8s |
| Alpha signals | 0.4s |
| Risk model + SHAP | 0.7s |
| Agent pipeline (3 agents) | 3.2s |
| RAG retrieval | 0.3s |
| Total | ~6.4s |
The system is designed for interactive exploration — a full audit in under 10 seconds.
What's Next
- Live market data via WebSocket feeds instead of yfinance polling
- Fine-tuned LLM agents specialized for financial analysis (BloombergGPT-style)
- Portfolio-level analysis — running the pipeline across all holdings simultaneously
The full source is on GitHub with a live interactive demo at live_stock.