Architecture Overview
This page provides a high-level overview of the alpha-oracle system architecture, core design patterns, and key interfaces.
Architectural Style: Modular Monolith
The system uses a modular monolith architecture — a single deployable application with well-defined internal module boundaries.
Why Not Microservices?
For a trading system under active development by a small team, a modular monolith provides:
- Simplicity: Single deployment, no distributed system complexity
- Performance: No network latency between modules, shared in-memory state
- Transactions: ACID guarantees across modules (single database)
- Debugging: Unified logs, no distributed tracing overhead
- Evolution: Easy to extract microservices later if needed
Module Communication
Modules communicate via:
- Direct function calls — For synchronous operations (e.g., risk checks, order generation)
- Redis pub/sub — For asynchronous events (e.g.,
signal:generated,order:filled,risk:alert) - Shared database — TimescaleDB for time-series data, Redis for cache/state
Example Redis pub/sub channels:
signals— Strategy signals generatedorders— Order lifecycle events (submitted, filled, rejected)risk— Risk alerts and circuit breaker activationskill_switch— Emergency halt events
Core Abstractions
The system defines 5 abstract base classes (ABCs) in src/core/interfaces.py to enforce module boundaries and enable pluggability.
1. DataSourceInterface
Interface for market data providers.
from abc import ABC, abstractmethod
from datetime import datetime
from src.core.models import OHLCV, FundamentalData
class DataSourceInterface(ABC):
@abstractmethod
async def get_historical_bars(
self, symbol: str, start: datetime, end: datetime, timeframe: str = "1Day"
) -> list[OHLCV]:
"""Fetch historical OHLCV bars."""
...
@abstractmethod
async def get_latest_bar(self, symbol: str) -> OHLCV | None:
"""Fetch most recent bar."""
...
@abstractmethod
async def get_fundamentals(self, symbol: str) -> FundamentalData | None:
"""Fetch fundamental data (PE ratio, debt/equity, etc.)."""
...
@abstractmethod
async def health_check(self) -> bool:
"""Verify data source is accessible."""
...
Implementations:
IBKRDataAdapter— Interactive Brokers market dataAlphaVantageAdapter— Alpha Vantage REST APIEDGARAdapter— SEC EDGAR filingsFINRAAdapter— FINRA short interest data
2. BaseStrategy
Base class for trading strategies.
from abc import ABC, abstractmethod
from src.core.models import OHLCV, Signal
class BaseStrategy(ABC):
@property
@abstractmethod
def name(self) -> str:
"""Strategy identifier."""
...
@property
@abstractmethod
def min_hold_days(self) -> int:
"""Minimum holding period (must be >= 2 for swing trading)."""
...
@abstractmethod
def generate_signals(self, data: dict[str, list[OHLCV]]) -> list[Signal]:
"""Generate trading signals from market data."""
...
@abstractmethod
def get_parameters(self) -> dict[str, Any]:
"""Return strategy parameters for logging/tuning."""
...
@abstractmethod
def get_required_data(self) -> list[str]:
"""Return data types needed: ['ohlcv', 'fundamentals', etc.]"""
...
Implementations:
MomentumStrategy— Dual momentum (relative + absolute)MeanReversionStrategy— Bollinger Band reversalsBreakoutStrategy— ATR-based breakoutsMLSignalStrategy— XGBoost predictions
3. BrokerAdapter
Interface for broker integrations.
from abc import ABC, abstractmethod
from src.core.models import Order, OrderStatus, Position, PortfolioSnapshot
class BrokerAdapter(ABC):
@abstractmethod
async def submit_order(self, order: Order) -> Order:
"""Submit order to broker, return updated order with broker_order_id."""
...
@abstractmethod
async def cancel_order(self, broker_order_id: str) -> bool:
"""Cancel pending order."""
...
@abstractmethod
async def get_order_status(self, broker_order_id: str) -> OrderStatus:
"""Query order status."""
...
@abstractmethod
async def get_positions(self) -> list[Position]:
"""Fetch current positions."""
...
@abstractmethod
async def get_portfolio(self) -> PortfolioSnapshot:
"""Fetch portfolio snapshot (equity, cash, positions)."""
...
@abstractmethod
async def health_check(self) -> bool:
"""Verify broker connection."""
...
Implementations:
IBKRBrokerAdapter— Interactive Brokers via ib-asyncSimulatedBroker— In-memory broker for testing (realistic fills, slippage)PaperStubBroker— Demo stub with mock data
4. RiskManager
Interface for risk management checks.
from abc import ABC, abstractmethod
from src.core.models import Order, PortfolioSnapshot, RiskCheckResult
class RiskManager(ABC):
@abstractmethod
async def check_pre_trade(
self, order: Order, portfolio: PortfolioSnapshot
) -> RiskCheckResult:
"""Run pre-trade risk checks (PDT, position limits, portfolio limits)."""
...
@abstractmethod
async def check_portfolio(
self, portfolio: PortfolioSnapshot
) -> RiskCheckResult:
"""Run portfolio-level risk checks (drawdown, daily loss)."""
...
@abstractmethod
async def is_kill_switch_active(self) -> bool:
"""Check if emergency kill switch is active."""
...
@abstractmethod
async def activate_kill_switch(self, reason: str) -> None:
"""Activate kill switch and halt trading."""
...
Implementations:
PreTradeRiskManager— Pre-trade checks (PDT, position limits)PortfolioMonitor— Real-time portfolio monitoringPDTGuard— Pattern Day Trader rule enforcementCircuitBreakers— VIX, stale data, reconciliation checks
5. BacktestEngine
Interface for backtesting frameworks.
from abc import ABC, abstractmethod
from datetime import datetime
from src.core.models import BacktestResult
class BacktestEngine(ABC):
@abstractmethod
def run(
self,
strategy: BaseStrategy,
data: dict[str, list[OHLCV]],
initial_capital: float,
start: datetime,
end: datetime,
) -> BacktestResult:
"""Run single backtest."""
...
@abstractmethod
def walk_forward(
self,
strategy: BaseStrategy,
data: dict[str, list[OHLCV]],
initial_capital: float,
train_months: int,
test_months: int,
step_months: int,
) -> list[BacktestResult]:
"""Run walk-forward analysis."""
...
Implementations:
BacktraderEngine— Backtrader framework integrationVectorBTEngine— VectorBT for vectorized backtests (optional)
Configuration System
Configuration uses Pydantic Settings with a two-tier hierarchy:
- YAML files (
config/settings.yaml,config/risk_limits.yaml) - Environment variables (with
SA_prefix and__nesting)
Loading Order
from src.core.config import Settings
settings = Settings.from_yaml() # Load YAML, then apply env overrides
Example YAML (config/settings.yaml):
app:
environment: development
log_level: INFO
broker:
provider: ibkr
paper_trading: true
ibkr:
host: 127.0.0.1
port: 4002
database:
url: postgresql+asyncpg://trader:dev_password@localhost:5432/stock_analysis
redis:
url: redis://localhost:6379/0
Environment Variable Overrides:
# Override nested config with __ delimiter
SA_BROKER__PROVIDER=simulated
SA_BROKER__IBKR__PORT=4001
SA_DATABASE__URL=postgresql+asyncpg://user:pass@prod-db:5432/trading
Key Settings Modules
BrokerSettings— Broker provider, IBKR connectionDataSettings— Alpha Vantage rate limits, EDGAR user agent, feed configStrategySettings— Min Sharpe ratio, walk-forward paramsExecutionSettings— Order type, Kelly sizingRiskSettings— Autonomy mode, position/portfolio limits, PDT guard, circuit breakersMLSettings— XGBoost params, confidence threshold, retraining scheduleSchedulerSettings— Cron schedules for data jobs and model retraining
Data Models
All Pydantic models live in src/core/models.py.
Key Models
- OHLCV — Open/High/Low/Close/Volume bar
- FundamentalData — PE ratio, debt/equity, ROE, etc.
- Signal — Strategy signal (LONG/SHORT/FLAT) with strength and metadata
- Order — Order request (symbol, side, type, quantity, limit price)
- Position — Open position (symbol, quantity, entry price, P&L)
- PortfolioSnapshot — Account equity, cash, positions, drawdown
- BacktestResult — Sharpe, Sortino, max drawdown, equity curve, trades
Key Enums
SignalDirection— LONG, SHORT, FLATOrderSide— BUY, SELLOrderType— MARKET, LIMIT, STOP, STOP_LIMIT, BRACKETOrderStatus— PENDING, SUBMITTED, PARTIALLY_FILLED, FILLED, CANCELLED, REJECTEDRiskAction— APPROVE, REJECT, REQUIRE_HUMAN_APPROVAL, REDUCE_SIZEAutonomyMode— PAPER_ONLY, MANUAL_APPROVAL, BOUNDED_AUTONOMOUS, FULL_AUTONOMOUS
Lazy Imports
Modules with heavy dependencies (e.g., backtrader, pandas_ta) use lazy imports in __init__.py to avoid import-time overhead.
Example (src/strategy/backtest/__init__.py):
# Don't import at module level
# from .backtrader_engine import BacktraderEngine # ❌ Slow
# Instead, import on-demand
def get_backtrader_engine():
from .backtrader_engine import BacktraderEngine # ✅ Fast
return BacktraderEngine()
This pattern is used in:
src/strategy/backtest/— Backtrader, VectorBTsrc/strategy/builtin/— pandas_ta indicatorssrc/scheduling/jobs.py— Adapter imports inside job functions
Dependency Direction
Modules follow a strict dependency hierarchy (no circular deps):
api → execution → strategy → signals → data → core
↓ ↓ ↓ ↓ ↓
risk → monitoring → scheduling
- core — Foundation (models, interfaces, config, DB, Redis)
- data — Adapters, feeds, storage, universe
- signals — Feature store, ML pipeline
- strategy — Strategy engine, backtesting
- execution — Order routing, broker adapters
- risk — Pre-trade checks, PDT guard, circuit breakers
- scheduling — Cron jobs, model registry
- monitoring — Metrics, alerts
- api — FastAPI routes, WebSocket
Next Steps
- Data Flows — Understand signal generation, execution, and risk flows
- Module Map — Detailed directory structure and entry points