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

11 KiB
Raw Permalink Blame 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

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 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:

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)

_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

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__.py
  • shared/fundamentals/base.pyFundamentalsProvider 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.pyRotatingProvider 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/)