<a id="skfolio-alpha-ewsharpeoptimalalpha"></a>

# skfolio.alpha.EWSharpeOptimalAlpha

<a id="skfolio.alpha.EWSharpeOptimalAlpha"></a>

### *class* skfolio.alpha.EWSharpeOptimalAlpha(\*, descriptors, half_life=20, ridge_scale=1e-06, horizon=1, signal_lag=1, neutralize_against=None, outlier_transformer=None, scoring_transformer=None, transform_by_group=None, forecast_unit=IDIO_RETURN, forecast_scale=1.0, normalize_weights=True, n_jobs=1)

Exponentially weighted least-squares Sharpe-optimal alpha estimator.

This estimator aggregates multiple cross-sectional signals from descriptors into a
single alpha forecast by estimating their joint contribution to forward
idiosyncratic returns. Coefficients are estimated with exponentially weighted
least squares.

The estimator supports two forecast units. With the default
`forecast_unit=ForecastUnit.IDIO_RETURN`, descriptors are fitted directly to forward
idiosyncratic returns. When descriptor scores linearly forecast idiosyncratic
returns and residual noise is proportional to idiosyncratic variance, the learned
signal blend is Sharpe-optimal in idiosyncratic return space for an unconstrained
long-short strategy.

With `forecast_unit=ForecastUnit.IDIO_SHARPE`, descriptors are fitted to forward
idiosyncratic return divided by idiosyncratic volatility, with unit regression
weights. Dividing the target by $\sigma_i$ transforms the inverse-variance
GLS objective in return units into OLS in idiosyncratic-Sharpe units.

Signals are first transformed into cross-sectional scores (e.g., z-scores, ranks),
then optionally neutralized against factors and re-transformed into cross-sectional
scores and finally combined linearly:

$$
\alpha_i = \sum_{k=1}^{K} \beta_k \, S_{k,i}
$$

where $S_{k,i}$ denotes the cross-sectional score of signal $k$ for
asset $i$ and $\beta_k$ is the estimated signal coefficient.

By default, coefficients map descriptor scores directly into expected return units.
With `forecast_unit=ForecastUnit.IDIO_SHARPE`, coefficients map descriptor scores into
idiosyncratic-Sharpe units and the final forecast is multiplied by current
idiosyncratic volatility so `alpha_` remains in expected return units.

This generalizes IC-based signal weighting by:

- accounting for cross-signal correlations (multivariate estimation)
- incorporating asset-specific risk, either through inverse idiosyncratic variance
  weights or through volatility-scaled targets
- producing an alpha forecast in expected return units, which is required whenever
  the optimizer is trading off alpha against real costs and constraints (e.g.
  transaction costs, market impact, borrow costs, turnover constraints).

For an individual signal with constant idiosyncratic variance, the estimator reduces
to a scaled IC-like weighting.

The estimator uses the following regression target:

$$
y_t =
\begin{cases}
\epsilon_t, & \text{if } \texttt{forecast\_unit=ForecastUnit.IDIO\_RETURN} \\
\epsilon_t / \sigma_t, &
\text{if } \texttt{forecast\_unit=ForecastUnit.IDIO\_SHARPE}
\end{cases}
$$

and regression weights:

$$
W_t =
\begin{cases}
\operatorname{diag}(1 / \sigma_{t,i}^2), &
\text{if } \texttt{forecast\_unit=ForecastUnit.IDIO\_RETURN} \\
I, & \text{if } \texttt{forecast\_unit=ForecastUnit.IDIO\_SHARPE}
\end{cases}
$$

where:

- $\epsilon_{t,i}$ is the forward mean idiosyncratic return over the chosen
  horizon
- $S_{t,i} \in \mathbb{R}^K$ is the vector of cross-sectional scores
- $\sigma_{t,i}^2$ is the forecast idiosyncratic variance

If `normalize_weights=True`, the positive diagonal entries of $W_t$ are
divided by their cross-sectional average before computing the normal-equation
statistics.

With `forecast_unit=ForecastUnit.IDIO_SHARPE`, the return model is instead:

$$
\epsilon_{t,i} = \sigma_{t,i} S_{t,i}^\top \beta + \eta_{t,i},
\quad \operatorname{Var}(\eta_{t,i}) \propto \sigma_{t,i}^2
$$

The corresponding inverse-variance GLS objective is:

$$
\beta_t = \arg\min_\beta \sum_i
    \frac{(\epsilon_{t,i} - \sigma_{t,i} S_{t,i}^\top \beta)^2}
    {\sigma_{t,i}^2}
$$

which is equivalent to ordinary least squares on the volatility-scaled target
$\epsilon_{t,i}/\sigma_{t,i}$:

$$
\beta_t = \arg\min_\beta \sum_i
  \left(\frac{\epsilon_{t,i}}{\sigma_{t,i}}
  - S_{t,i}^\top \beta\right)^2
$$

The final forecast is converted back to expected return units:

$$
\alpha_i = \sigma_i S_i^\top \beta
$$

This is useful when signals are assumed to forecast idiosyncratic Sharpe rather than
raw idiosyncratic return. For the same scaled signal forecast, higher-volatility
assets receive larger return alpha because the forecast is converted back from
idiosyncratic-Sharpe units to return units.

To reduce estimation noise and turnover, the estimator maintains exponentially
weighted least-squares statistics:

$$
A_t^{EW} = \lambda A_{t-1}^{EW} + (1 - \lambda) S_t^\top W_t S_t
$$

$$
b_t^{EW} = \lambda b_{t-1}^{EW} + (1 - \lambda) S_t^\top W_t y_t,
\quad \lambda = 2^{-1/\text{half-life}}
$$

Coefficients are obtained by ridge-stabilized normal equations:

$$
\beta_t = (A_t^{EW} + \rho_t I)^{-1} b_t^{EW}
$$

With `forecast_unit=ForecastUnit.IDIO_RETURN`, the final alpha forecast is:

$$
\alpha_i = S_i^\top \beta
$$

With `forecast_unit=ForecastUnit.IDIO_SHARPE`, the forecast is:

$$
\alpha_i = \sigma_i S_i^\top \beta
$$

No intercept is included to avoid absorbing cross-sectional means, making the
resulting alpha suitable for long-short strategies.

The estimator supports latest-alpha fitting with [`fit`](#skfolio.alpha.EWSharpeOptimalAlpha.fit) and [`partial_fit`](#skfolio.alpha.EWSharpeOptimalAlpha.partial_fit),
and historical alpha forecasts with [`fit_transform`](#skfolio.alpha.EWSharpeOptimalAlpha.fit_transform) and
[`partial_fit_transform`](#skfolio.alpha.EWSharpeOptimalAlpha.partial_fit_transform). Historical rows are computed as-of each observation:
for horizon $h$ and signal lag $\ell$, alpha at observation $t$
uses coefficient updates from signal observations up to $t - \ell - h + 1$.

* **Parameters:**
  **descriptors** *list of (name, estimator) tuples*
  : List of descriptors that compute signals from characteristics. Each tuple
    contains a string name and a descriptor estimator. Multiple descriptors are
    aggregated into a single alpha using multivariate regression. The descriptors
    are evaluated in parallel if `n_jobs > 1`.

  **half_life** *float, default=20*
  : Half-life of the exponential weights in number of observations.
    * Larger half-life: More stable alpha estimates, slower adaptation
    * Smaller half-life: More responsive estimates, faster adaptation

  **horizon** *int, default=1*
  : Number of forward periods to average for the target idiosyncratic return.
    Must be >= 1. The target for observation $t$ is
    `mean(idio_returns[t+signal_lag : t+signal_lag+horizon])`.
    * `horizon=1`: Predicts one-period idiosyncratic return starting after `signal_lag`
    * `horizon>1`: Predicts the mean of `horizon` idiosyncratic returns starting
      after `signal_lag`.

  **signal_lag** *int, default=1*
  : Number of periods between the signal observation and the first return in the
    target window. Must be >= 1. Under skfolio’s as-of time-indexing convention,
    `signal_lag=0` would use information observed at the end of $t$ to predict
    return at $t$, which is look-ahead. Values larger than 1 can model
    conservative data availability or execution delays.

  **neutralize_against** *list of str, optional*
  : Factor names or families to neutralize scores against. If provided, scores are
    orthogonalized with respect to the specified factor exposures before regression.

  **outlier_transformer** *BaseCSTransformer or “passthrough”, optional*
  : Cross-sectional transformer for descriptor outlier handling. If None, defaults
    to `CSWinsorizer()`. Use “passthrough” to skip.

  **scoring_transformer** *BaseCSTransformer or “passthrough”, optional*
  : Cross-sectional transformer for descriptor scoring applied after outlier
    handling. If None, defaults to `CSStandardScaler()`. Use “passthrough” to skip.

  **transform_by_group** *str, optional*
  : Name of a categorical characteristic in the AssetPanel to use for group-wise
    transformations. If provided, outlier and scoring transformations are applied
    within each group separately.

  **forecast_unit** *ForecastUnit, default=ForecastUnit.IDIO_RETURN*
  : Unit of the intermediate forecast learned from descriptor scores. With
    `ForecastUnit.IDIO_RETURN`, the target is the forward mean idiosyncratic return
    and WLS weights are inverse idiosyncratic variance. With
    `ForecastUnit.IDIO_SHARPE`, the target is divided by forecast idiosyncratic
    volatility and fitted with unit weights. The resulting idiosyncratic-Sharpe
    forecast is converted back to return units by multiplying by current
    idiosyncratic volatility.

  **forecast_scale** *float, default=1.0*
  : Multiplicative scale applied to the final alpha forecast after the learned
    coefficients have been converted to expected return units. This controls alpha
    strength without changing the EWLS coefficient estimates.

  **normalize_weights** *bool, default=True*
  : If `True`, regression weights are normalized within each observation to have an
    average of one across valid assets. This removes changes in aggregate weight
    caused by the scale of idiosyncratic variances, while preserving the greater
    statistical weight of observations with more valid assets. In practice, this
    prevents calm, low-volatility regimes from mechanically dominating the EWLS
    statistics just because inverse-variance weights are larger in those regimes.
    Set `normalize_weights=False` for the unnormalized GLS estimator (which is
    BLUE under the usual assumptions).

  **ridge_scale** *float, default=1e-6*
  : Relative ridge penalty applied to the exponentially weighted normal matrix.

  **n_jobs** *int, default=1*
  : Number of parallel jobs for descriptor computation. Use `-1` for all available
    cores.
* **Attributes:**
  **alpha_** *ndarray of shape (n_assets,) or None*
  : Estimated alpha (expected idiosyncratic return) for each asset. This is the
    aggregated prediction from all signals. Returns `None` during warmup phase
    (fewer than `signal_lag + horizon` observations).

  **coef_** *ndarray of shape (n_descriptors,)*
  : Estimated descriptor coefficients.

  **descriptors_** *list of BaseDescriptor*
  : Fitted descriptor estimators.

  **named_descriptors_** *dict of {str: BaseDescriptor}*
  : Dictionary mapping descriptor names to fitted estimators.

  **outlier_transformer_** *BaseCSTransformer or str*
  : The fitted outlier transformer.

  **scoring_transformer_** *BaseCSTransformer or str*
  : The fitted scoring transformer.

  **n_assets_** *int*
  : Number of assets seen during fitting.

  **asset_names_** *ndarray of shape (n_assets,)*
  : Asset names seen during fitting.

### Methods

| [`fit`](#skfolio.alpha.EWSharpeOptimalAlpha.fit)(X[, y])                   | Fit the alpha model.                                                         |
|--------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [`fit_transform`](#skfolio.alpha.EWSharpeOptimalAlpha.fit_transform)(X[, y])         | Fit the alpha model and return historical alpha forecasts.                   |
| [`get_metadata_routing`](#skfolio.alpha.EWSharpeOptimalAlpha.get_metadata_routing)()        | Return metadata routing for descriptor estimators.                           |
| [`get_params`](#skfolio.alpha.EWSharpeOptimalAlpha.get_params)([deep])            | Get the parameters of an estimator from the ensemble.                        |
| [`partial_fit`](#skfolio.alpha.EWSharpeOptimalAlpha.partial_fit)(X[, y])           | Incrementally fit the alpha model with new observations.                     |
| [`partial_fit_transform`](#skfolio.alpha.EWSharpeOptimalAlpha.partial_fit_transform)(X[, y]) | Incrementally fit the alpha model and return new historical alpha forecasts. |
| [`set_params`](#skfolio.alpha.EWSharpeOptimalAlpha.set_params)(\*\*params)        | Set the parameters of a factor from the ensemble.                            |

### Notes

The Information Ratio (IR) of a strategy is approximately [[1]](#r21b9913a35b8-1):

$$
\text{IR} \approx \text{IC} \times \sqrt{\text{Breadth}}
$$

This estimator generalizes single-signal IC weighting by estimating multivariate,
risk-weighted signal payoffs. The exponential weighting and ridge stabilization
reduce turnover and estimation noise in the coefficients.

### References

* <a id='r21b9913a35b8-1'>**[1]**</a> “Active Portfolio Management: A Quantitative Approach for Producing Superior Returns and Controlling Risk”, McGraw-Hill, Grinold & Kahn (1999).

### Examples

```pycon
>>> import numpy as np
>>> from skfolio.datasets import make_synthetic_characteristics
>>> from skfolio.alpha import EWSharpeOptimalAlpha, ForecastUnit
>>> from skfolio.descriptor import EWMomentum, BookToPrice, Reversal, Passthrough
>>>
>>> X = make_synthetic_characteristics(
...     n_assets=100, n_observations=504, n_industries=5, random_state=0
... )
>>> rng = np.random.default_rng(0)
>>>
>>> # Alpha models regress forward idiosyncratic returns. In production these
>>> # come from a fitted CharacteristicsFactorModel.
>>> idio_returns = rng.standard_normal((X.n_observations, X.n_assets))
>>> idio_returns[~X.active_mask] = np.nan
>>> X["idio_returns"] = idio_returns
>>>
>>> # Required when forecast_unit=ForecastUnit.IDIO_SHARPE to scale targets and alphas.
>>> idio_variances = rng.uniform(0.01, 0.05, (X.n_observations, X.n_assets))
>>> idio_variances[~X.active_mask] = np.nan
>>> X["idio_variances"] = idio_variances
>>>
>>> # Required when neutralize_against is set. In production these are factor
>>> # exposures from the characteristics factor model.
>>> exposures = rng.standard_normal((X.n_observations, X.n_assets, 3))
>>> exposures[~X.active_mask] = np.nan
>>> X.add_3d_field(
...     "exposures",
...     exposures,
...     third_axis_name="factors",
...     third_axis_labels=["market", "beta", "size"],
... )
AssetPanel(n_observations=504, n_assets=100, n_fields=25)
>>>
>>> alpha_model = EWSharpeOptimalAlpha(
...     descriptors=[
...         ("momentum", EWMomentum()),
...         ("book_to_price", BookToPrice()),
...         ("reversal", Reversal()),
...         ("eps_ntm", Passthrough("eps_ntm")),
...     ],
...     horizon=5,      # one-week forward idiosyncratic return
...     half_life=21,    # one-month EWLS half-life
...     neutralize_against=["market", "beta", "size"],
...     forecast_unit=ForecastUnit.IDIO_SHARPE,
... )
>>>
>>> # Latest alpha forecast for the current rebalance.
>>> alpha_model.fit(X[:-5])
EWSharpeOptimalAlpha(...)
>>> # Preview five forecasts; NaN means no forecast is available.
>>> print(alpha_model.alpha_[:5])
[ 0.00649... nan        -0.0216... 0.00358... nan]
>>>
>>> # Update with the next five observations
>>> alpha_model.partial_fit(X[-5:])
EWSharpeOptimalAlpha(...)
>>> print(alpha_model.alpha_[:5])
[ 0.00837... nan        -0.0341... 0.0222...  nan]
>>>
>>> # Historical as-of alpha forecasts with fit_transform
>>> alphas = alpha_model.fit_transform(X)
>>> alphas.shape
(504, 100)
```

<a id="skfolio.alpha.EWSharpeOptimalAlpha.fit"></a>

#### fit(X, y=None, \*\*fit_params)

Fit the alpha model.

Resets all internal state, processes the provided panel and stores the latest
alpha forecast in `alpha_`.

* **Parameters:**
  **X** *AssetPanel*
  : Input panel containing “idio_returns”, “idio_variances”, descriptor
    fields and optionally “exposures” for score neutralization.

  **y** *None*
  : Ignored. Present for compatibility with scikit-learn’s API.

  **\*\*fit_params** *dict*
  : Additional fit parameters passed to descriptors through metadata routing.
* **Returns:**
  **self** *EWSharpeOptimalAlpha*
  : Fitted estimator.

<a id="skfolio.alpha.EWSharpeOptimalAlpha.fit_transform"></a>

#### fit_transform(X, y=None, \*\*fit_params)

Fit the alpha model and return historical alpha forecasts.

The returned alpha at observation $t$ only uses coefficient updates whose
forward-return target is observable by $t$. Warmup rows are `NaN`.

* **Parameters:**
  **X** *AssetPanel*
  : Input panel containing “idio_returns”, “idio_variances”, descriptor
    fields and optionally “exposures” for score neutralization.

  **y** *None*
  : Ignored. Present for compatibility with scikit-learn’s API.

  **\*\*fit_params** *dict*
  : Additional fit parameters passed to descriptors through metadata routing.
* **Returns:**
  **alphas** *ndarray of shape (n_observations, n_assets)*
  : Historical alpha forecasts for the input panel.

<a id="skfolio.alpha.EWSharpeOptimalAlpha.get_metadata_routing"></a>

#### get_metadata_routing()

Return metadata routing for descriptor estimators.

<a id="skfolio.alpha.EWSharpeOptimalAlpha.get_params"></a>

#### get_params(deep=True)

Get the parameters of an estimator from the ensemble.

Returns the parameters given in the constructor as well as the
estimators contained within the `estimators` parameter.

* **Parameters:**
  **deep** *bool, default=True*
  : Setting it to True gets the various estimators and the parameters
    of the estimators as well.
* **Returns:**
  **params** *dict*
  : Parameter and estimator names mapped to their values or parameter
    names mapped to their values.

<a id="skfolio.alpha.EWSharpeOptimalAlpha.named_descriptors"></a>

#### *property* named_descriptors

Dictionary to access any fitted factors by name.

* **Returns:**
  `Bunch`

<a id="skfolio.alpha.EWSharpeOptimalAlpha.partial_fit"></a>

#### partial_fit(X, y=None, \*\*fit_params)

Incrementally fit the alpha model with new observations.

This method supports streaming/online updates. It maintains internal buffers to
compute forward returns across partial_fit calls.

* **Parameters:**
  **X** *AssetPanel*
  : Input panel containing “idio_returns”, “idio_variances”, descriptor
    fields and optionally “exposures” for score neutralization.

  **y** *None*
  : Ignored. Present for compatibility with scikit-learn’s API.

  **\*\*fit_params** *dict*
  : Additional fit parameters passed to descriptors through metadata routing.
* **Returns:**
  **self** *EWSharpeOptimalAlpha*
  : Fitted estimator.

<a id="skfolio.alpha.EWSharpeOptimalAlpha.partial_fit_transform"></a>

#### partial_fit_transform(X, y=None, \*\*fit_params)

Incrementally fit the alpha model and return new historical alpha forecasts.

Only rows corresponding to the newly supplied observations are returned.

* **Parameters:**
  **X** *AssetPanel*
  : Input panel containing “idio_returns”, “idio_variances”, descriptor
    fields and optionally “exposures” for score neutralization.

  **y** *None*
  : Ignored. Present for compatibility with scikit-learn’s API.

  **\*\*fit_params** *dict*
  : Additional fit parameters passed to descriptors through metadata routing.
* **Returns:**
  **alphas** *ndarray of shape (n_observations, n_assets)*
  : Historical alpha forecasts for the new observations.

<a id="skfolio.alpha.EWSharpeOptimalAlpha.set_params"></a>

#### set_params(\*\*params)

Set the parameters of a factor from the ensemble.

Valid parameter keys can be listed with `get_params()`. Note that you
can directly set the parameters of the estimators contained in
`estimators`.

* **Parameters:**
  **\*\*params** *keyword arguments*
  : Specific parameters using e.g.
    `set_params(parameter_name=new_value)`. In addition, to setting the
    parameters of the estimator, the individual estimator of the
    estimators can also be set, or can be removed by setting them to
    ‘drop’.
* **Returns:**
  **self** *object*
  : Estimator instance.

