Skip to content

alpha-101: add bias config for long-biased / long-only mandates #25

Description

@gangtao

Background

The alpha-101 demo currently produces a strictly mean-zero Alpha #1 signal: (rank − 1) / (N − 1) − 0.5, summing to 0 across the N ranked stocks per bucket. That's the right default for evaluating the pure alpha — it isolates relative-value skill from market drift.

In practice, real books often want a non-zero mean (long-tilted):

  • Long-only mandates — can't short, so the portfolio is structurally biased
  • 130/30 or other tilts — the bias is a deliberate, sized component
  • Stacked sleeves — a mean-zero alpha plus an explicit beta exposure

These should be expressible by the demo without abandoning the mean-zero foundation.

Proposal

Add a bias config knob to apps/alpha-101/manifest.yaml:

- key: bias
  type: string  # accept a float as string, parsed downstream
  required: false
  default: "0.0"
  description: >
    Constant added to alpha_1 *after* mean-zero centering. 0 = pure
    mean-zero (default). +0.5 ≈ 100% long-only tilt (every position
    is long; the rank still determines relative sizing). Negative
    values produce a net-short tilt. The tilt is reported separately
    from the alpha so P&L attribution can decompose alpha-skill
    from beta-exposure.

Plumb it into v_alpha_1:

cast(idx - 1, 'float64') / null_if(n - 1, 0) - 0.5 + {{ .Config.bias }}  AS alpha_1

Reporting / attribution

To keep the bias visible (not just absorbed into a single PnL number), v_backtest should also emit the decomposition:

SELECT
  ...,
  (alpha_prev - bias_value) * returns  AS pnl_alpha,   -- pure mean-zero contribution
  bias_value * returns                  AS pnl_bias,    -- tilt contribution
  alpha_prev * returns                  AS pnl_total
FROM ...

Then the backtest dashboard's "Backtest Summary" gets two extra columns (alpha-only Sharpe vs total Sharpe), and the "PnL over time" line chart can render both series so users see how much of the cum-PnL is "skill" vs "beta drift".

Acceptance criteria

  • bias config in manifest (string, default "0.0", validated as a float)
  • v_alpha_1 adds the bias constant after mean-zero centering
  • v_backtest decomposes pnl into pnl_alpha and pnl_bias
  • Backtest dashboard panels show both decomposed Sharpes / cum-PnLs
  • README documents the trade-off (pure alpha vs tilted) and references the factorization principle: alpha (mean-zero) + beta sizing (macro view) + gross sizing (capital at risk), tracked separately
  • End-to-end install verified with at least bias=0.0 (default), bias=0.5 (long-only tilt), and bias=-0.5 (short tilt)

Out of scope

  • A continuous (non-rank) version of the alpha (would discard rank's outlier-robustness — different design discussion).
  • Risk-based bias sizing (Kelly, target-vol, etc.) — that belongs at a portfolio-construction layer above the per-alpha view, not here.

References

Discussion thread that motivated this: PR for #24, conversation about mean-zero conventions in research vs production. Summary:

Research / signal evaluation → always mean-zero (otherwise Sharpe measures market drift, not skill).
Production / portfolio construction → factorized: alpha (mean-zero) + beta sizing (separate scalar) + gross sizing (separate scalar). Mixing bias into the alpha makes P&L attribution opaque.

This issue's bias config preserves that factorization by keeping the pure mean-zero alpha intact and exposing the tilt as a separately-reported component.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions