Architecture¶
The Portfolio Risk Engine follows a hexagonal (ports & adapters) architecture with three layers. Domain logic is fully isolated from infrastructure concerns.
Layer Overview¶
graph TB
subgraph CLI["CLI Layer"]
cli[portfolio-sim<br/>Interactive menu]
end
subgraph APP["Application Layer"]
direction LR
uc1[FetchMarketData]
uc2[ComputeLogReturns]
uc3[EstimateMarketParameters]
uc4[RunMonteCarlo]
uc5[ComputePortfolioRisk]
end
subgraph DOMAIN["Domain Layer"]
direction TB
subgraph VO["Value Objects"]
ticker[Ticker]
currency[Currency]
weight[Weight]
daterange[DateRange]
end
subgraph MODELS["Models"]
asset[Asset]
position[Position]
portfolio[Portfolio]
hp[HistoricalPrices]
hr[HistoricalReturns]
mp[MarketParameters]
gbm[MultivariateGBM]
sr[SimulationResult]
rm[RiskMetrics]
end
subgraph PORTS["Ports"]
mdp[MarketDataProvider<br/>Protocol]
mce[MonteCarloEngine<br/>Protocol]
end
subgraph SERVICES["Services"]
chol[cholesky]
end
end
subgraph INFRA["Infrastructure Layer"]
yahoo[YahooFinance<br/>MarketDataProvider]
cpu[CpuMonteCarlo<br/>Engine]
gpu[GpuMonteCarlo<br/>Engine]
gpuaccel[GpuAccelerated<br/>Pipeline]
end
cli --> APP
APP --> DOMAIN
yahoo -.->|implements| mdp
cpu -.->|implements| mce
gpu -.->|implements| mce
style DOMAIN fill:#e8eaf6,stroke:#3f51b5
style APP fill:#e3f2fd,stroke:#2196f3
style INFRA fill:#fce4ec,stroke:#e91e63
style CLI fill:#e8f5e9,stroke:#4caf50
Dependency Rule¶
Dependencies point inward — infrastructure and application depend on domain, never the reverse.
graph LR
INFRA[Infrastructure] -->|depends on| DOMAIN[Domain]
APP[Application] -->|depends on| DOMAIN
CLI -->|depends on| APP
CLI -->|depends on| INFRA
DOMAIN -.-x|never depends on| INFRA
DOMAIN -.-x|never depends on| APP
style DOMAIN fill:#e8eaf6,stroke:#3f51b5,stroke-width:3px
- Domain defines
Protocolinterfaces (ports) with zero external dependencies - Infrastructure provides concrete implementations (adapters)
- Application orchestrates domain operations via use cases
Data Pipeline¶
The simulation flow chains five use cases in sequence. Each use case transforms one domain model into the next:
flowchart LR
A["📊 FetchMarketData"] --> B["📈 ComputeLogReturns"]
B --> C["📐 EstimateMarket<br/>Parameters"]
C --> D["🎲 RunMonteCarlo"]
D --> E["⚠️ ComputePortfolio<br/>Risk"]
A1[/"HistoricalPrices"/] --> B
B1[/"HistoricalReturns"/] --> C
C1[/"MarketParameters"/] --> D
D1[/"SimulationResult"/] --> E
E1[/"PortfolioRiskMetrics"/]
A --> A1
B --> B1
C --> C1
D --> D1
E --> E1
style A fill:#bbdefb
style B fill:#bbdefb
style C fill:#bbdefb
style D fill:#bbdefb
style E fill:#bbdefb
style A1 fill:#fff9c4
style B1 fill:#fff9c4
style C1 fill:#fff9c4
style D1 fill:#fff9c4
style E1 fill:#c8e6c9
Step Details¶
| Step | Use Case | Input | Output | Key Operation |
|---|---|---|---|---|
| 1 | FetchMarketData |
Tickers + DateRange | HistoricalPrices |
Yahoo Finance API call |
| 2 | ComputeLogReturns |
HistoricalPrices |
HistoricalReturns |
\(r_t = \ln(S_t / S_{t-1})\) |
| 3 | EstimateMarketParameters |
HistoricalReturns |
MarketParameters |
Annualized drift + covariance |
| 4 | RunMonteCarlo |
MarketParameters |
MonteCarloSimulationResult |
Cholesky → GBM → terminal prices |
| 5 | ComputePortfolioRisk |
Simulation + Portfolio | PortfolioRiskMetrics |
Weighted returns → VaR/ES |
Domain Model¶
classDiagram
class Ticker {
+str value
__post_init__() validates
}
class Currency {
+str code
}
class Weight {
+float value
0.0 ≤ value ≤ 1.0
}
class DateRange {
+date start
+date end
start < end
}
class Asset {
+Ticker ticker
+Currency currency
+str? name
}
class Position {
+Asset asset
+Weight weight
}
class Portfolio {
+tuple~Position~ positions
+tickers() list~Ticker~
+weights() list~float~
Σ weights = 1.0
}
class HistoricalPrices {
+tuple~Ticker~ tickers
+tuple~date~ dates
+dict prices_by_ticker
}
class HistoricalReturns {
+tuple~Ticker~ tickers
+tuple~date~ dates
+dict returns_by_ticker
}
class MarketParameters {
+tuple~Ticker~ tickers
+tuple~float~ drift_vector
+tuple~tuple~float~~ covariance_matrix
+int annualization_factor
}
class MultivariateGBM {
+MarketParameters market_parameters
+tuple~tuple~float~~ cholesky_factor
}
class MonteCarloSimulationResult {
+tuple~Ticker~ tickers
+tuple~float~ initial_prices
+dict terminal_prices
+int num_simulations
+int time_horizon_days
}
class PortfolioRiskMetrics {
+float mean_return
+float volatility
+float var_95
+float var_99
+float es_95
+float es_99
}
Asset --> Ticker
Asset --> Currency
Position --> Asset
Position --> Weight
Portfolio --> Position
HistoricalPrices --> Ticker
HistoricalReturns --> Ticker
MarketParameters --> Ticker
MultivariateGBM --> MarketParameters
MonteCarloSimulationResult --> Ticker
Compute Backends¶
The engine supports two execution backends through the MonteCarloEngine protocol:
graph TB
subgraph PROTOCOL["Port (Protocol)"]
MCE[MonteCarloEngine<br/>simulate]
end
subgraph CPU["CPU Backend"]
CPUE[CpuMonteCarloEngine<br/>NumPy + np.random]
end
subgraph GPU["GPU Backends"]
GPUE[GpuMonteCarloEngine<br/>CuPy arrays]
GPUA[GpuAcceleratedPipeline<br/>Fused simulate + risk]
end
CPUE -.->|implements| MCE
GPUE -.->|implements| MCE
GPUA -.->|bypasses port| MCE
style PROTOCOL fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
style CPU fill:#e8f5e9,stroke:#4caf50
style GPU fill:#fff3e0,stroke:#ff9800
| Backend | Library | Data Path | Best For |
|---|---|---|---|
CpuMonteCarloEngine |
NumPy | CPU → tuples → CPU | Development, small-scale |
GpuMonteCarloEngine |
CuPy | GPU → tuples → CPU | Medium scale, compatible with domain pipeline |
GpuAcceleratedPipeline |
CuPy | GPU → GPU → 6 scalars | Production, large-scale (zero tuple allocation) |
The GpuAcceleratedPipeline fuses simulation and risk computation into a single GPU pass — only 6 scalar floats (the risk metrics) are transferred back to CPU. This avoids the overhead of materializing millions of terminal prices as Python tuples.
Project Structure¶
src/portfolio_risk_engine/
├── domain/
│ ├── value_objects/ # Ticker, Currency, Weight, DateRange
│ ├── models/ # Asset, Position, Portfolio, ...
│ ├── ports/ # MarketDataProvider, MonteCarloEngine (Protocol)
│ └── services/ # cholesky()
├── application/
│ └── use_cases/ # FetchMarketData, ComputeLogReturns, ...
├── infrastructure/
│ ├── market_data/ # YahooFinanceMarketDataProvider
│ └── simulation/ # CpuMonteCarloEngine, Gpu*
├── cli.py # Interactive CLI (PortfolioSimulatorCLI)
└── __main__.py # python -m portfolio_risk_engine