Class Factors

java.lang.Object
com.quantfinlib.alpha.Factors

public final class Factors extends Object
The standard alpha factor library — nine signal generators covering the classic technical, factor-investing and defensive families. Each returns an AlphaFactor producing raw cross-sectional scores where higher = more attractive long (see the interface contract).

Sign conventions are chosen so every factor is usable long/short as-is:

  • Trend (MA crossover, MACD, momentum) score positively when the trend is up;
  • Mean reversion (RSI, Bollinger, mean reversion) score positively when the price is depressed — the contrarian orientation, since these signals bet on the snap-back;
  • Defensive/fundamental (value, quality, low volatility) score positively for cheap, profitable, calm names — the Fama-French/AQR orientation of each anomaly.

All computations read only bars <= index (the no-look-ahead contract) and cost O(window) per symbol per call. They are stateless, so they can be evaluated at arbitrary dates, at the price of recomputing windows the streaming indicators would carry. EMAs are truncated at 4× their period and seeded with the first price of that window; the dropped tail weight is (1−2/(p+1))^{4p} < 0.04%.

EMA definitions differ from indicators.Indicators. Indicators.ema seeds with the SMA of the first period bars of the whole series and runs from bar 0; the factors here seed with the first price of a 4×slow window ending at the scored bar. The two converge as the seed's weight decays, but macd(int, int, int) and Indicators.macd are not the same number on short histories, in the same way rsi(int) (Cutler) is not Wilder's RSI.

Cross-sectional preparation. Raw technical scores are dominated by market and sector moves in trending regimes, and their rank IC then measures beta timing. winsorized(AlphaFactor, double), standardized(AlphaFactor) and sectorNeutralized(AlphaFactor, Map) are per-date decorators; prepared(AlphaFactor, double, Map) applies them in the order that is correct for fat-tailed raw scores: winsorise the raw score, then z-score, then demean within sector.

Fundamentals are read through AlphaContext.fundamentalsAsOf(int, int): point-in-time with a FundamentalsHistory; with only a static snapshot they can be scored at the last bar and throw for every earlier bar.

  • Method Details

    • movingAverageCrossover

      public static AlphaFactor movingAverageCrossover(int fast, int slow)
      Moving-average crossover: (SMA_fast − SMA_slow) / SMA_slow. Positive when the fast average rides above the slow — the % spread makes scores comparable across price levels.
    • macd

      public static AlphaFactor macd(int fast, int slow, int signal)
      MACD histogram normalized by price: (macdLine − signalLine) / close. Positive while bullish momentum is accelerating; the price normalization keeps a $10 and a $1000 stock on one scale.
    • momentum

      public static AlphaFactor momentum(int lookback, int skip)
      Cross-sectional momentum: close[i−skip] / close[i−lookback] − 1. The academic 12-1 form when called as momentum(252, 21) — skipping the last month sidesteps short-term reversal (Jegadeesh-Titman 1993).
    • rsi

      public static AlphaFactor rsi(int period)
      Contrarian RSI: (50 − RSI) / 50, in [−1, +1]. Oversold names (RSI 30 → +0.4) score positively.

      Definition note: this is Cutler's RSI (arithmetic average of gains/losses over the window), chosen because it is stateless and exactly recomputable at any bar. It is not the same number as indicators.Indicators#rsi, which uses Wilder smoothing — after a trend the two can disagree near the 30/70 thresholds. The factor name says so to keep reports unambiguous.

    • bollinger

      public static AlphaFactor bollinger(int period, double stdDevs)
      Bollinger mean reversion: −(close − SMA) / (k·σ) — the negative band position, +1 at the lower band, −1 at the upper.
    • meanReversion

      public static AlphaFactor meanReversion(int lookback)
      Plain mean reversion: −(close / SMA − 1) — how far the price sits below its own average, as a fraction.
    • value

      public static AlphaFactor value()
      Value composite: the average of earnings yield and book yield (1/PB). Yields rather than ratios, so "cheap" is high and negative-earnings names contribute a negative yield rather than a meaningless negative P/E rank. The earnings yield is eps / close(t) when eps is known, so the price side is current; otherwise 1/PE as recorded. NaN without fundamentals.
    • quality

      public static AlphaFactor quality()
      Quality composite: profitability minus leverage — ROE − 0.1 × debt/equity. The 0.1 haircut puts one turn of leverage on the same scale as 10 points of ROE, the usual quality-minus-junk shape (profitable and conservatively financed).
    • lowVolatility

      public static AlphaFactor lowVolatility(int lookback)
      Low-volatility anomaly: −σ(returns) over the lookback, calm names scoring high. Ranking only needs the negative sign, not annualisation. Synthetic bars are excluded; NaN with fewer than two real bars in the window.
    • winsorized

      public static AlphaFactor winsorized(AlphaFactor factor, double p)
      Clips each date's scores to the cross-sectional p and 1 − p quantiles (linear interpolation between order statistics), NaN entries untouched.
      Parameters:
      p - tail fraction in [0, 0.5), for example 0.01
    • standardized

      public static AlphaFactor standardized(AlphaFactor factor)
      Cross-sectional z-score per date: (s − mean)/sd over the scored names (population sd); all-zero when the scores have no dispersion.
    • sectorNeutralized

      public static AlphaFactor sectorNeutralized(AlphaFactor factor, Map<String,String> sectorBySymbol)
      Demeans scores within each sector per date, so the factor ranks names against their sector peers. Symbols missing from the map form their own singleton sector and score 0.
    • prepared

      public static AlphaFactor prepared(AlphaFactor factor, double p, Map<String,String> sectorBySymbol)
      The standard preparation chain: winsorized(AlphaFactor, double) at p, then standardized(AlphaFactor), then sectorNeutralized(AlphaFactor, Map) (skipped when sectorBySymbol is null).