Metadata-Version: 2.5
Name: portfolio-risk-engine
Version: 0.3.0
Summary: Standalone portfolio risk analytics engine
Requires-Python: >=3.11
Requires-Dist: cvxpy[ecos-bb]<2,>=1.8
Requires-Dist: fmp-mcp<0.6,>=0.5.4
Requires-Dist: numpy<3,>=2.4
Requires-Dist: pandas<4,>=3.0
Requires-Dist: portfolio-math==0.1.0
Requires-Dist: pyarrow<26,>=23.0.1
Requires-Dist: pydantic<3,>=2.12
Requires-Dist: python-dateutil<3,>=2.9
Requires-Dist: pyyaml<7,>=6.0
Requires-Dist: requests<3,>=2.32
Requires-Dist: scipy<2,>=1.17
Requires-Dist: statsmodels<1,>=0.14
Requires-Dist: value-semantics-core<3,>=2.1
Description-Content-Type: text/markdown

# portfolio-risk-engine

Reusable portfolio analytics with explicit price, FX, identity, and configuration inputs.
The wheel contains only `portfolio_risk_engine`; it never imports the Risk checkout.
`portfolio-math` is a separate declared library dependency.

## Install

```bash
pip install portfolio-risk-engine
```

For a source build, build `portfolio_math/` and this directory with `python -m build`,
then install both wheels (the other declared dependencies resolve from PyPI).

## Public API

```python
from portfolio_risk_engine import (
    build_portfolio_view,
    normalize_weights,
    calculate_portfolio_performance_metrics,
    PriceProvider,
    FXProvider,
    set_price_provider,
    get_price_provider,
    set_fx_provider,
    get_fx_provider,
)
```

The engine covers portfolio risk, performance, optimization, efficient frontiers,
Monte Carlo, stress/scenario analysis, factors, income, and allocation drift.
Applications provide the data needed by the analytics they invoke; a missing provider
raises an explicit configuration error rather than importing a default application.

## Quick start: deterministic portfolio risk

This complete example runs without credentials, network data, or the Risk checkout.
The fixture supplies month-end prices; replace it with your own `PriceProvider` for
market data. The five symbols and prices are illustrative inputs, not live quotes.

```python
import numpy as np
import pandas as pd
from portfolio_risk_engine import build_portfolio_view, set_price_provider

weights = {"AAPL": 0.30, "MSFT": 0.25, "GOOGL": 0.20, "IEF": 0.15, "GLD": 0.10}
index = pd.date_range("2020-01-31", "2024-12-31", freq="ME")
prices = pd.DataFrame({
    ticker: 100 * np.cumprod(1 + 0.005 + 0.02 * np.sin(np.arange(len(index)) + offset))
    for offset, ticker in enumerate(weights)
}, index=index)

class FramePrices:
    def fetch_monthly_close(self, ticker, start_date=None, end_date=None, **kwargs):
        return prices[ticker].loc[start_date:end_date]

    def fetch_monthly_total_return_price(self, ticker, start_date=None, end_date=None, **kwargs):
        return prices[ticker].loc[start_date:end_date]

set_price_provider(FramePrices())
result = build_portfolio_view(
    weights=weights,
    start_date="2020-01-01",
    end_date="2024-12-31",
    currency_map={ticker: "USD" for ticker in weights},
    instrument_types={"AAPL": "equity", "MSFT": "equity", "GOOGL": "equity", "IEF": "etf", "GLD": "etf"},
)
print("observations:", result["return_observation_count"])
print("annual volatility:", round(result["volatility_annual"], 12))
print("risk contributions:", result["risk_contributions"].round(12).to_dict())
```

## Provider and policy integration

`portfolio_risk_engine.providers` owns the price/FX/currency ports and the optional
instrument-chain/metadata registry input. Configure providers with `set_price_provider`,
`set_fx_provider`, `set_currency_resolver`, and `set_provider_registry`. Derivative
history and latest-futures pricing are supplied by the configured price provider;
option chains retain their observed/theoretical policy and explicit contract identity.

`portfolio_risk_engine.config.configure` binds application policy: `CASH_MAP`,
`CONFIG_PATH_RESOLVER`, `CONTRACT_SPEC_LOADER`, `POSITION_IDENTITY_RESOLVER`,
`LISTED_INSTRUMENT_TYPE_RESOLVER`, and optional `RESULT_FACTORIES`, alongside the
analytics settings. Library position identities are canonical projected mappings;
legacy/persisted-row decoding belongs to the application. Configure before importing
calculation modules that consume scalar settings.

The single alias precedence algorithm is `portfolio_risk_engine._ticker.resolve_ticker_alias`:
explicit alias, caller resolver/map, reviewed alias data, then the normalized symbol.
Pass reviewed aliases as `reviewed_aliases`; the engine does not load application YAML.
Explicit `futures`, `derivative`, or `option` kind excludes reviewed equity aliases and
preserves an unaliased contract symbol. Explicit per-position/map aliases still apply.
No kind is inferred from ticker spelling.

## Risk application boundary

Risk's `settings.py` explicitly calls `providers.engine.configure_engine` to install
its configuration and adapters. `providers/engine_fmp.py` is application code, not part
of this wheel. `core/` and `services/` orchestrate application workflows; the engine
owns reusable calculations, coverage, canonical metadata, cash mechanics, quality
contracts, price requests, and risk-limit evaluation. Applications may supply result
factories to build their own rich response types; standalone calls return library
payload objects with `to_api_response()`.

## License

Proprietary. All rights reserved. See the `LICENSE` file at the repository root.
