> ## Documentation Index
> Fetch the complete documentation index at: https://mathematicalcompany.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Portable Alpha

> Factor decomposition, beta-neutral adjustments, and portable alpha strategies. Separate alpha from market exposure using OLS regression and EWMA tracking.

<Warning>
  **Ultra Feature.** Requires an Ultra subscription. [Get started at api.mathematicalcompany.com](https://api.mathematicalcompany.com)
</Warning>

# Portable Alpha

A strategy can appear profitable when it is just riding market beta. Horizon's portable alpha module decomposes returns into alpha (skill) and beta (market exposure), tracks factor betas incrementally, and calculates the hedge ratios needed to neutralize unwanted exposures. All regression math is in Rust.

## Overview

<CardGroup cols={2}>
  <Card title="Beta Decomposition" icon="chart-mixed">
    `hz.beta_decompose()` separates alpha from market exposure via OLS.
  </Card>

  <Card title="Factor Exposures" icon="layer-group">
    `hz.compute_factor_exposures()` computes betas across multiple factors.
  </Card>

  <Card title="Neutrality Adjustment" icon="scale-balanced">
    `hz.neutrality_adjustment()` calculates hedge ratios to reach target betas.
  </Card>

  <Card title="Incremental Beta" icon="gauge-high">
    `hz.incremental_beta_update()` tracks beta in O(1) per cycle via EWMA.
  </Card>
</CardGroup>

***

## Core Functions

### hz.beta\_decompose

Decompose portfolio returns into alpha + beta \* factor returns via OLS.

```python theme={null}
import horizon as hz

result = hz.beta_decompose(
    portfolio_returns=[0.01, -0.02, 0.015, 0.005, -0.01],
    factor_returns=[0.008, -0.015, 0.012, 0.003, -0.008],
)
print(f"Alpha: {result.alpha:.6f}")
print(f"Beta: {result.beta:.4f}")
print(f"R-squared: {result.r_squared:.4f}")
print(f"Tracking error: {result.tracking_error:.4f}")
print(f"Information ratio: {result.information_ratio:.4f}")
```

| Parameter           | Type          | Description                    |
| ------------------- | ------------- | ------------------------------ |
| `portfolio_returns` | `list[float]` | Portfolio return series        |
| `factor_returns`    | `list[float]` | Factor/benchmark return series |

Returns an `AlphaDecomposition` with fields: `alpha`, `beta`, `r_squared`, `tracking_error`, `information_ratio`.

### hz.compute\_factor\_exposures

Multi-factor regression: compute betas across multiple factors.

```python theme={null}
exposures = hz.compute_factor_exposures(
    portfolio_returns=port_ret,
    factor_names=["market", "size", "momentum"],
    factor_returns_matrix=[mkt_ret, smb_ret, mom_ret],
)
for e in exposures:
    print(f"{e.factor_name}: beta={e.beta:.4f}, R2={e.r_squared:.4f}")
```

### hz.neutrality\_adjustment

Calculate hedge ratios needed to reach target betas.

```python theme={null}
adjustments = hz.neutrality_adjustment(
    current_betas=[0.8, -0.3],
    factor_names=["market", "size"],
    target_betas=[0.0, 0.0],
    tolerances=[0.05, 0.05],
)
for a in adjustments:
    print(f"{a.factor_name}: hedge={a.hedge_ratio:+.4f}, ok={a.is_within_tolerance}")
```

### hz.incremental\_beta\_update

O(1) EWMA-based beta tracking. No need to store full history.

```python theme={null}
cov, var, beta = hz.incremental_beta_update(
    prev_cov=0.0, prev_var=0.0,
    portfolio_return=0.01, factor_return=0.008,
    decay=0.94,
)
print(f"Live beta: {beta:.4f}")
```

| Parameter          | Type    | Description                   |
| ------------------ | ------- | ----------------------------- |
| `prev_cov`         | `float` | Previous EWMA covariance      |
| `prev_var`         | `float` | Previous EWMA variance        |
| `portfolio_return` | `float` | Latest portfolio return       |
| `factor_return`    | `float` | Latest factor return          |
| `decay`            | `float` | EWMA decay factor (e.g. 0.94) |

***

## Pipeline Functions

### hz.beta\_decompose\_pipeline

Decompose returns into alpha + beta each cycle.

```python theme={null}
decomp = hz.beta_decompose_pipeline(
    portfolio_feed="my_portfolio",
    factor_feed="spy",
    window=50,
)
```

| Parameter        | Type  | Default  | Description                    |
| ---------------- | ----- | -------- | ------------------------------ |
| `portfolio_feed` | `str` | required | Feed name for portfolio        |
| `factor_feed`    | `str` | required | Feed name for factor/benchmark |
| `window`         | `int` | `50`     | Rolling window for OLS         |

### hz.market\_neutral

Track live beta and calculate hedge ratios for neutrality.

```python theme={null}
neutral = hz.market_neutral(
    portfolio_feed="my_portfolio",
    factor_feed="spy",
    target_beta=0.0,
    tolerance=0.05,
    decay=0.94,
)
```

| Parameter        | Type    | Default  | Description                      |
| ---------------- | ------- | -------- | -------------------------------- |
| `portfolio_feed` | `str`   | required | Portfolio feed                   |
| `factor_feed`    | `str`   | required | Factor feed                      |
| `target_beta`    | `float` | `0.0`    | Target beta exposure             |
| `tolerance`      | `float` | `0.05`   | Acceptable deviation from target |
| `decay`          | `float` | `0.94`   | EWMA decay factor                |

Returns each cycle:

```python theme={null}
{
    "beta": 0.45,
    "hedge_ratio": -0.45,
    "neutral": False,
    "target_beta": 0.0,
}
```

### hz.portable\_alpha

Full multi-factor portable alpha with exposure tracking and adjustment signals.

```python theme={null}
pa = hz.portable_alpha(
    portfolio_feed="my_portfolio",
    factor_feeds={"market": "spy_feed", "rates": "tlt_feed"},
    target_betas={"market": 0.0, "rates": 0.0},
    tolerance=0.05,
)
```

### hz.factor\_model

Track factor betas incrementally (O(1) per cycle).

```python theme={null}
fm = hz.factor_model(
    factor_feeds={"market": "spy", "rates": "tlt"},
    decay=0.94,
)
```

***

## Examples

### Market-Neutral Strategy

```python theme={null}
import horizon as hz

def alpha_strategy(ctx):
    if not ctx.params.get("neutral", False):
        return []  # not neutral yet, wait
    return hz.quotes(fair=ctx.feed.price, spread=0.03, size=10)

hz.run(
    name="market_neutral",
    exchange=[hz.Polymarket(), hz.Alpaca(paper=True)],
    feeds={
        "market": hz.PolymarketBook("will-btc-hit-100k"),
        "spy": hz.AlpacaFeed(symbols=["SPY"]),
    },
    pipeline=[
        hz.market_neutral(
            portfolio_feed="market",
            factor_feed="spy",
            target_beta=0.0,
            tolerance=0.05,
        ),
        alpha_strategy,
    ],
)
```

***

## Mathematical Background

<AccordionGroup>
  <Accordion title="OLS Beta Decomposition">
    For portfolio returns R\_p and factor returns R\_f:

    **R\_p = alpha + beta \* R\_f + epsilon**

    Beta = Cov(R\_p, R\_f) / Var(R\_f). Alpha is the intercept (excess return after removing factor exposure). R-squared measures the fraction of variance explained by the factor.
  </Accordion>

  <Accordion title="EWMA Incremental Beta">
    Instead of storing full history, use exponentially weighted moving averages:

    `cov_t = decay * cov_{t-1} + (1 - decay) * R_p * R_f`

    `var_t = decay * var_{t-1} + (1 - decay) * R_f^2`

    `beta_t = cov_t / var_t`

    With decay = 0.94 (approx 15-day half-life), recent observations matter more. This runs in O(1) per update.
  </Accordion>

  <Accordion title="Neutrality Adjustment">
    To reach target beta from current beta:

    **hedge\_ratio = -(current\_beta - target\_beta)**

    A positive hedge\_ratio means add long factor exposure; negative means short it.
  </Accordion>
</AccordionGroup>
