trading/docs/plans/2026-02-23-strategies-fundamentals-design.md

300 lines
11 KiB
Markdown
Raw Normal View History

# Design: Extended Strategies + Fundamental Data
**Date:** 2026-02-23
**Status:** Approved
## Summary
Add 6 new trading strategies and a fundamental data pipeline to the existing 3-strategy ensemble. Introduces fundamental analysis data (EPS, P/E, PEG, etc.) from three providers with rotation and DB-backed caching, plus additional technical indicators computed from existing OHLCV bars.
**Result:** 9-strategy weighted ensemble (3 existing + 6 new).
---
## 1. Fundamental Data Provider System
### 1.1 Architecture
New module `shared/fundamentals/` with a provider abstraction:
```
FundamentalsProvider (ABC)
├── AlphaVantageProvider (25 req/day free tier)
├── FMPProvider (250 req/day free tier)
├── YahooFinanceProvider (no API key, yfinance library)
└── RotatingProvider (wraps all three, rotates on rate limit)
```
### 1.2 Provider Interface
```python
class FundamentalsProvider(ABC):
@abstractmethod
async def fetch(self, ticker: str) -> FundamentalsSnapshot | None:
"""Fetch fundamental data for a single ticker."""
class RotatingProvider(FundamentalsProvider):
"""Tries providers in order, rotating to the next on rate limit or error."""
def __init__(self, providers: list[FundamentalsProvider]): ...
async def fetch(self, ticker: str) -> FundamentalsSnapshot | None: ...
```
### 1.3 Data Model
```python
class FundamentalsSnapshot(BaseModel):
ticker: str
eps_ttm: float | None = None # Earnings per share (trailing 12 months)
pe_ratio: float | None = None # Price-to-earnings ratio
peg_ratio: float | None = None # Price/earnings-to-growth ratio
revenue_growth_yoy: float | None = None # Year-over-year revenue growth (decimal)
profit_margin: float | None = None # Net profit margin (decimal)
debt_to_equity: float | None = None # Total debt / shareholder equity
market_cap: float | None = None # Total market capitalization
fetched_at: datetime # When this data was fetched
```
### 1.4 DB-Backed Caching
- New `fundamentals` table (Alembic migration required).
- On startup: load from DB. If `fetched_at` is within 24h, skip API calls.
- Daily refresh: background task re-fetches and updates DB.
- Prevents wasting free-tier API calls during local dev / restarts.
### 1.5 Configuration
New `.env` variables:
```
TRADING_ALPHA_VANTAGE_API_KEY=<key>
TRADING_FMP_API_KEY=<key>
TRADING_FUNDAMENTALS_CACHE_TTL_HOURS=24
```
No API key needed for Yahoo Finance (yfinance library).
---
## 2. New Technical Indicators
Computed in `MarketDataManager.get_snapshot()` from the existing OHLCV rolling window. No new data source needed.
### 2.1 Indicators
| Indicator | Computation | Added to MarketSnapshot |
|-----------|-------------|------------------------|
| MACD | EMA-12 minus EMA-26 | `macd: float \| None` |
| MACD Signal | EMA-9 of MACD line | `macd_signal: float \| None` |
| MACD Histogram | MACD minus signal | `macd_histogram: float \| None` |
| Bollinger Upper | SMA-20 + 2σ | `bollinger_upper: float \| None` |
| Bollinger Mid | SMA-20 | `bollinger_mid: float \| None` |
| Bollinger Lower | SMA-20 2σ | `bollinger_lower: float \| None` |
| VWAP | Cumulative(price × volume) / cumulative(volume) | `vwap: float \| None` |
| ATR (14) | Average true range, 14-period | `atr: float \| None` |
| EMA-9 | 9-period exponential moving average | `ema_9: float \| None` |
| EMA-21 | 21-period exponential moving average | `ema_21: float \| None` |
| SMA-200 | 200-period simple moving average | `sma_200: float \| None` |
### 2.2 Historical Bars Increase
`TRADING_HISTORICAL_BARS` bumped from 100 → 250 to support SMA-200 computation.
---
## 3. New Strategies (6 total)
All strategies implement `BaseStrategy.evaluate(ticker, market, sentiment) -> TradeSignal | None`.
### 3.1 Value Strategy (fundamental)
**File:** `shared/strategies/value.py`
**Name:** `"value"`
Uses fundamental data (EPS, P/E, PEG, profit margin, debt-to-equity).
| Condition | Signal |
|-----------|--------|
| PEG < 1.0 AND P/E < 25 AND EPS growth > 0 | LONG |
| PEG > 3.0 OR (P/E > 50 AND negative EPS growth) | SHORT |
| Strength: composite of how far metrics deviate from thresholds | 01 |
Fundamentals data passed via a new optional `fundamentals` field on `MarketSnapshot`:
```python
fundamentals: FundamentalsSnapshot | None = None
```
### 3.2 MACD Crossover Strategy (technical)
**File:** `shared/strategies/macd_crossover.py`
**Name:** `"macd_crossover"`
| Condition | Signal |
|-----------|--------|
| MACD crosses above signal line AND histogram > 0 | LONG |
| MACD crosses below signal line AND histogram < 0 | SHORT |
| Strength: abs(histogram) normalized by ATR | 01 |
Requires tracking previous MACD/signal values to detect crossovers. Stores last state per ticker in instance dict.
### 3.3 Bollinger Bands Breakout Strategy (technical)
**File:** `shared/strategies/bollinger_breakout.py`
**Name:** `"bollinger_breakout"`
| Condition | Signal |
|-----------|--------|
| Price above upper band + volume > 1.5× average | LONG (momentum breakout) |
| Price below lower band | LONG (mean reversion bounce) |
| Price was above upper band, now back inside | SHORT (failed breakout) |
| Strength: distance from band / band width | 01 |
### 3.4 VWAP Strategy (technical, intraday)
**File:** `shared/strategies/vwap.py`
**Name:** `"vwap"`
| Condition | Signal |
|-----------|--------|
| Price crosses above VWAP with increasing volume | LONG |
| Price crosses below VWAP with increasing volume | SHORT |
| Strength: distance from VWAP as % of price, weighted by volume ratio | 01 |
### 3.5 Liquidity Strategy (volume-based)
**File:** `shared/strategies/liquidity.py`
**Name:** `"liquidity"`
| Indicator | Computation |
|-----------|-------------|
| Relative Volume | Current volume / 20-bar average volume |
| Volume Trend | Slope of volume over recent bars |
| Volume-Price Divergence | Price moving without volume confirmation |
| Condition | Signal |
|-----------|--------|
| Relative volume > 2.0 AND price rising AND volume trend increasing | LONG |
| Relative volume > 2.0 AND price falling AND volume trend increasing | SHORT |
| Relative volume < 0.5 (thin liquidity) | Neutral skip |
| Price rising on declining volume (divergence) | SHORT (weak rally) |
| Strength: relative volume magnitude, clamped to [0, 1] | 01 |
### 3.6 MA Stack Strategy (trend alignment)
**File:** `shared/strategies/ma_stack.py`
**Name:** `"ma_stack"`
Reads the full 5-MA stack: EMA-9, EMA-21, SMA-50, SMA-200 (plus price).
| Condition | Signal |
|-----------|--------|
| Full bull alignment: price > EMA-9 > EMA-21 > SMA-50 > SMA-200 | LONG, high strength |
| Partial bull: price > EMA-9 > EMA-21, below SMA-200 | LONG, lower strength |
| Full bear alignment: price < EMA-9 < EMA-21 < SMA-50 < SMA-200 | SHORT, high strength |
| Golden cross: SMA-50 crosses above SMA-200 | LONG boost |
| Death cross: SMA-50 crosses below SMA-200 | SHORT boost |
| MAs tangled / no clear order | Neutral — no signal |
| Strength: alignment count / 5, adjusted by fan spread | 01 |
---
## 4. Updated Ensemble
### 4.1 Default Weights (equal, 9 strategies)
```python
_DEFAULT_WEIGHTS = {
"momentum": 0.111,
"mean_reversion": 0.111,
"news_driven": 0.111,
"value": 0.111,
"macd_crossover": 0.111,
"bollinger_breakout": 0.111,
"vwap": 0.111,
"liquidity": 0.112,
"ma_stack": 0.111,
}
```
### 4.2 Learning Engine
The existing learning engine (multi-armed bandit) will automatically adjust weights for the new strategies based on trade outcomes. No changes needed to the learning engine itself — it already handles arbitrary strategy names.
### 4.3 Strategy Seeding
`scripts/seed_strategies.py` updated to seed all 9 strategies into the `strategies` DB table.
---
## 5. Integration Points
### 5.1 Signal Generator Changes
1. Import and instantiate all 6 new strategies in `run()`.
2. Add fundamentals fetching as a background task (daily refresh).
3. Inject `FundamentalsSnapshot` into `MarketSnapshot.fundamentals` before calling ensemble.
4. Update `_DEFAULT_WEIGHTS` to include all 9 strategies.
### 5.2 MarketDataManager Changes
1. Add EMA computation method (exponential moving average).
2. Compute MACD, Bollinger, VWAP, ATR, EMA-9, EMA-21, SMA-200 in `get_snapshot()`.
3. Track previous MACD state for crossover detection (or let strategy handle it).
### 5.3 Schema Changes
1. Add new optional fields to `MarketSnapshot` (all technical indicators + fundamentals).
2. Add `FundamentalsSnapshot` Pydantic model.
### 5.4 Database Changes
1. New `fundamentals` table (Alembic migration).
2. Seed 6 new strategies via `seed_strategies.py`.
### 5.5 Configuration Changes
```env
TRADING_ALPHA_VANTAGE_API_KEY=M0I3TWB6VKU0UF51
TRADING_FMP_API_KEY=34zqbQFeRxYvPtzp3Y5QLKPVPztkZyfK
TRADING_FUNDAMENTALS_CACHE_TTL_HOURS=24
TRADING_HISTORICAL_BARS=250
```
### 5.6 Dependencies
```toml
# pyproject.toml — new deps
yfinance # Yahoo Finance fundamentals (no API key)
# httpx/aiohttp already available for Alpha Vantage + FMP REST calls
```
---
## 6. File Changes Summary
### New Files
- `shared/fundamentals/__init__.py`
- `shared/fundamentals/base.py``FundamentalsProvider` ABC + `FundamentalsSnapshot`
- `shared/fundamentals/alpha_vantage.py` — Alpha Vantage provider
- `shared/fundamentals/fmp.py` — Financial Modeling Prep provider
- `shared/fundamentals/yahoo.py` — Yahoo Finance provider (yfinance)
- `shared/fundamentals/rotating.py``RotatingProvider` with failover
- `shared/strategies/value.py` — Value strategy
- `shared/strategies/macd_crossover.py` — MACD Crossover strategy
- `shared/strategies/bollinger_breakout.py` — Bollinger Breakout strategy
- `shared/strategies/vwap.py` — VWAP strategy
- `shared/strategies/liquidity.py` — Liquidity strategy
- `shared/strategies/ma_stack.py` — MA Stack strategy
- `alembic/versions/xxx_add_fundamentals_table.py` — Migration
- `tests/test_fundamentals.py` — Unit tests for providers
- `tests/test_new_strategies.py` — Unit tests for 6 new strategies
- `tests/test_indicators.py` — Unit tests for technical indicator computations
### Modified Files
- `shared/schemas/trading.py` — Add indicator fields to `MarketSnapshot`
- `services/signal_generator/market_data.py` — Add EMA, MACD, Bollinger, VWAP, ATR, SMA-200 computations
- `services/signal_generator/main.py` — Import new strategies, add fundamentals fetching, update weights
- `shared/strategies/__init__.py` — Export new strategies
- `scripts/seed_strategies.py` — Seed 6 new strategies
- `.env` — Add API keys and config
- `pyproject.toml` — Add yfinance dependency
- `shared/models/trading.py` — Add `Fundamentals` DB model (or new file in models/)