Saltar a contenido

Factor combination — score aditivo vs rank-based

Decisión técnica: cómo combinar los 3 factors (momentum ensemble + quality + value) en un single composite score. Resuelve PP-05. Recomendación: z-score aditivo con winsorizing.

El problema

Cada factor vive en su propio dominio:

  • momentum_12_1 = 0.25 (25% past 12m return) — ~ -0.5 a +2.0.
  • ROE = 0.18 (18% ROE) — ~ -0.1 a +0.4.
  • P/B = 3.5 (price/book) — ~ 0.5 a 20.

Sumar directo pesa desproporcionadamente al de mayor magnitud. Necesitamos normalizar.

Opción A: Z-score aditivo (recomendado)

Para cada factor, para cada stock en el universe:

z_factor = (raw_value - mean_universe) / stdev_universe

Luego:

composite = w1 × z_momentum + w2 × z_quality + w3 × z_value

Ventajas

  • Cada factor contribuye proporcionalmente a su spread relativo.
  • Interpretable: z=2 significa "2 stdev mejor que el mean".
  • Recombina factors con distinta distribución fácilmente.

Desventajas

  • Sensible a outliers: un stock con P/E de 500 infla el stdev → comprime el resto.
  • Fix: winsorizing (truncar al p1/p99) antes de z-score.

Opción B: Rank-based

Para cada factor, rankear el universe 1 a N:

rank_factor = percentile_rank(raw_value)  # entre 0 y 1
composite = w1 × rank_momentum + w2 × rank_quality + w3 × rank_value

Ventajas

  • Robusto a outliers (por construcción).
  • Todos los factors en [0, 1] — escala uniforme.
  • Más usado por AQR y large quant shops.

Desventajas

  • Pierde información: un stock con momentum z=3 (muy excepcional) tiene misma contribución que z=0.5 si ambos están en top 10%.
  • Menos granularidad en los extremos.

Opción C: Rank con no-linearity (enhanced)

composite = w1 × f(rank_momentum) + w2 × f(rank_quality) + w3 × f(rank_value)

Donde f(rank) = 2*rank - 1 (linear, mean 0) o f(rank) = Φ^-1(rank) (inverse normal, recupera algo de granularidad).

Hybrid de A y B.

Evidence empírica

  • AQR usa rank-based en la mayoría de papers ("Value and Momentum Everywhere") — prioriza robustez.
  • Academic papers de factor research (Fama-French etc.) usan z-score típicamente, porque evaluan factor premiums directamente.
  • Investopedia / blogs retail: inconsistente, mix de ambos.

Recomendación

Z-score aditivo con winsorizing p1/p99.

Razones

  1. Granularidad: distingue "excepcional" de "solo bueno".
  2. Evidence mixta entre opciones → elegir la más directa + interpretable.
  3. Debug-friendly: un score de 2.5 es inmediatamente "~2.5 stdev por encima del mean" — fácil de razonar si la strategy tiene sentido.

Formula exacta para la strategy

def composite_score(stock, universe):
    # Winsorize cada input al p1/p99 del universe
    mom_ensemble = winsorize(0.5 * (ret_12m - ret_1m) + 0.5 * (ret_6m - ret_1m), 0.01, 0.99)
    roe = winsorize(stock.roe, 0.01, 0.99)
    op_margin = winsorize(stock.op_margin, 0.01, 0.99)
    debt_eq = winsorize(stock.debt_equity, 0.01, 0.99)
    pe_inv = winsorize(1 / stock.pe, 0.01, 0.99)
    pb_inv = winsorize(1 / stock.pb, 0.01, 0.99)

    # Z-score sobre universe (winsorized)
    z_mom = zscore(mom_ensemble, universe)
    z_quality = zscore(roe) + zscore(op_margin) - zscore(debt_eq)  # quality composite
    z_value = zscore(pe_inv) + zscore(pb_inv)  # value composite

    return 0.5 * z_mom + 0.3 * z_quality + 0.2 * z_value

Edge cases

  • Missing data: si un stock no tiene P/E (ej. loss-making), z_value = 0 (neutral). No excluir del universe si los otros factors están.
  • Extreme outliers post-winsorizing: log-transform para factors fat-tailed (P/E especialmente).
  • Sector bias: quality y value pueden correlacionar con sector (ej. tech low debt-equity típicamente). Opcional: z-score sector-neutral.

Gaps

  • Sector-neutral z-scoring — reduce factor exposure concentrada en sectores.
  • Weights óptimos: los 0.5/0.3/0.2 son heurísticos. Optimización requiere backtesting + cross-validation (riesgo overfitting).
  • Time-varying weights: en diferentes regimes, distintos factors dominan.

Relaciones