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.
11 KiB
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
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
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
fundamentalstable (Alembic migration required). - On startup: load from DB. If
fetched_atis 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 | 0–1 |
Fundamentals data passed via a new optional fundamentals field on MarketSnapshot:
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 | 0–1 |
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 | 0–1 |
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 | 0–1 |
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] | 0–1 |
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 | 0–1 |
4. Updated Ensemble
4.1 Default Weights (equal, 9 strategies)
_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
- Import and instantiate all 6 new strategies in
run(). - Add fundamentals fetching as a background task (daily refresh).
- Inject
FundamentalsSnapshotintoMarketSnapshot.fundamentalsbefore calling ensemble. - Update
_DEFAULT_WEIGHTSto include all 9 strategies.
5.2 MarketDataManager Changes
- Add EMA computation method (exponential moving average).
- Compute MACD, Bollinger, VWAP, ATR, EMA-9, EMA-21, SMA-200 in
get_snapshot(). - Track previous MACD state for crossover detection (or let strategy handle it).
5.3 Schema Changes
- Add new optional fields to
MarketSnapshot(all technical indicators + fundamentals). - Add
FundamentalsSnapshotPydantic model.
5.4 Database Changes
- New
fundamentalstable (Alembic migration). - Seed 6 new strategies via
seed_strategies.py.
5.5 Configuration Changes
TRADING_ALPHA_VANTAGE_API_KEY=M0I3TWB6VKU0UF51
TRADING_FMP_API_KEY=34zqbQFeRxYvPtzp3Y5QLKPVPztkZyfK
TRADING_FUNDAMENTALS_CACHE_TTL_HOURS=24
TRADING_HISTORICAL_BARS=250
5.6 Dependencies
# 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__.pyshared/fundamentals/base.py—FundamentalsProviderABC +FundamentalsSnapshotshared/fundamentals/alpha_vantage.py— Alpha Vantage providershared/fundamentals/fmp.py— Financial Modeling Prep providershared/fundamentals/yahoo.py— Yahoo Finance provider (yfinance)shared/fundamentals/rotating.py—RotatingProviderwith failovershared/strategies/value.py— Value strategyshared/strategies/macd_crossover.py— MACD Crossover strategyshared/strategies/bollinger_breakout.py— Bollinger Breakout strategyshared/strategies/vwap.py— VWAP strategyshared/strategies/liquidity.py— Liquidity strategyshared/strategies/ma_stack.py— MA Stack strategyalembic/versions/xxx_add_fundamentals_table.py— Migrationtests/test_fundamentals.py— Unit tests for providerstests/test_new_strategies.py— Unit tests for 6 new strategiestests/test_indicators.py— Unit tests for technical indicator computations
Modified Files
shared/schemas/trading.py— Add indicator fields toMarketSnapshotservices/signal_generator/market_data.py— Add EMA, MACD, Bollinger, VWAP, ATR, SMA-200 computationsservices/signal_generator/main.py— Import new strategies, add fundamentals fetching, update weightsshared/strategies/__init__.py— Export new strategiesscripts/seed_strategies.py— Seed 6 new strategies.env— Add API keys and configpyproject.toml— Add yfinance dependencyshared/models/trading.py— AddFundamentalsDB model (or new file in models/)