trading/docs/plans/2026-02-23-strategies-fundamentals-design.md
Viktor Barzin 9eebd3fe61
docs: add design for extended strategies + fundamental data
6 new strategies (Value, MACD Crossover, Bollinger Breakout, VWAP,
Liquidity, MA Stack) plus fundamental data pipeline with 3 providers
(Alpha Vantage, FMP, Yahoo Finance) and DB-backed caching.
2026-02-23 21:27:08 +00:00

299 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/)