Public-Data Mini Reproduction

July 11, 2026 ยท View on GitHub

This note records a small public-data factor IC run using yfinance. It is meant to verify the public API path and document a reproducible workflow without proprietary data. It is not a trading strategy, investment advice, or evidence of live profitability.

Setup

  • Commit: c16501c
  • Data source: yfinance
  • Universe: AAPL, MSFT, NVDA, GOOGL, AMZN, META, JPM, XOM, UNH, LLY
  • Date range requested: 2021-01-01 to 2025-01-01
  • Date range returned: 2021-01-04 to 2024-12-31
  • Observations: 1005 dates x 10 stocks
  • Device: CPU
  • Random seed: not used for the yfinance path; the synthetic fallback in the notebook uses seed=7

Factor subset:

  • best_001
  • best_002
  • original_001
  • stock_001
  • add_015
  • old_042

Reproduction Snippet

This follows the same pattern as notebooks/public_factor_ic.ipynb.

from mlquant.data import make_panel
from mlquant.features import compute_legacy_set

tickers = [
    "AAPL", "MSFT", "NVDA", "GOOGL", "AMZN",
    "META", "JPM", "XOM", "UNH", "LLY",
]

panel = make_panel(
    source="yfinance",
    tickers=tickers,
    start="2021-01-01",
    end="2025-01-01",
    device="cpu",
)

factor_names = (
    "best_001",
    "best_002",
    "original_001",
    "stock_001",
    "add_015",
    "old_042",
)

factors, factor_mask, names = compute_legacy_set(panel, names=factor_names)

One-Day Forward Rank IC Summary

The table reports daily cross-sectional Spearman rank IC between each factor and one-day forward returns. With only 10 liquid US equities this is intentionally a small smoke test, not a statistically strong research claim.

FactorMean ICMedian ICIC StdPositive RateObservations
old_0420.01500.03030.36250.5224985
add_0150.01390.00610.39530.4985985
stock_0010.00780.01820.34970.5015985
best_002-0.0007-0.00610.35720.4796983
original_001-0.00130.00610.41130.4935985
best_001-0.0050-0.00660.36870.4677975

Best mean IC in this small run: old_042.

Interpretation

  • The workflow successfully downloads public OHLCV data, builds a Panel, computes a factor subset, and calculates one-day forward rank IC.
  • The universe is deliberately tiny, so cross-sectional IC is noisy.
  • yfinance adjusts and backfills data differently from institutional market data vendors; treat this as an API/reproducibility check rather than a paper replication.
  • No transaction costs, survivorship-bias controls, portfolio construction, or slippage model are included in this mini note.

Expected Output Check

Use the table above as a setup check. A successful run should produce:

  • a Panel with 10 stocks and roughly 1000 trading dates for the requested range
  • one-day rank IC rows for all six factors
  • old_042 near the top of the small-universe mean IC ranking in the maintainer run

Small numerical differences are expected when yfinance revises data or when pandas, PyTorch, or BLAS versions change. Treat large differences as a prompt to check the downloaded ticker data, missing values, and dependency versions.

Second Case: ETF Cross-Asset Universe

This second public-data case expands the cross-section from 10 individual US stocks to 20 liquid ETFs spanning broad equity benchmarks, US sectors, fixed income, gold, and international equity markets. The purpose remains an API and reproducibility check; the results are not a trading recommendation or evidence of investable performance.

Setup

  • Commit used as the repository base: 52c894e
  • Data source: yfinance 0.2.40
  • Broad market ETFs: SPY, QQQ, IWM, DIA
  • Sector ETFs: XLK, XLF, XLE, XLV, XLY, XLP, XLI, XLB, XLU
  • Cross-asset ETFs: AGG, TLT, GLD, HYG
  • International ETFs: EFA, EEM, FXI
  • Date range requested: 2021-01-01 to 2025-01-01 (end exclusive)
  • Date range returned: 2021-01-04 to 2024-12-31
  • Observations: 1005 dates x 20 ETFs
  • Per-ticker coverage: all 20 ETFs returned 1005 valid OHLCV rows
  • Failed or partial tickers: none
  • Device: CPU
  • Random seed: not used

The factor subset is unchanged from the first case: best_001, best_002, original_001, stock_001, add_015, and old_042.

Reproduction Snippet

The complete reproduction, including a bulk download followed by one-ticker retries for any missing columns, is checked in as scripts/etf_factor_ic.py. From the repository root, run:

PYTHONIOENCODING=utf-8 python scripts/etf_factor_ic.py \
  | tee scripts/etf_factor_ic_output.txt

The factor and IC path used by that script is:

from scripts.etf_factor_ic import (
    ETF_UNIVERSE,
    FACTOR_NAMES,
    configure_yfinance_user_agent,
    download_with_fallback,
    one_day_forward_returns,
    rank_ic_by_date,
    summarize_ic,
)
from mlquant.features import compute_legacy_set

configure_yfinance_user_agent()
result = download_with_fallback(ETF_UNIVERSE)
panel = result.panel
panel.assert_consistent()

factors, factor_mask, names = compute_legacy_set(panel, names=FACTOR_NAMES)
fwd_returns, fwd_mask = one_day_forward_returns(panel)
ic = rank_ic_by_date(
    factors,
    fwd_returns,
    factor_mask & fwd_mask,
    panel.dates,
    names,
)
summary = summarize_ic(ic)
print(summary)

The compatibility helper only replaces yfinance's obsolete Chrome 39 user agent when that exact default is present. The loader call itself remains make_panel(source="yfinance", ...), and newer yfinance versions without that legacy default are left unchanged.

One-Day Forward Rank IC Summary

As in the first case, the table reports daily cross-sectional Spearman rank IC between each factor and one-day forward returns. The calculations match notebooks/public_factor_ic.ipynb, including its warm-up masks and summary expressions.

FactorMean ICMedian ICIC StdPositive RateObservations
original_0010.01310.00900.37820.4965985
add_015-0.0031-0.00600.35950.4846985
stock_001-0.00440.00300.26180.4915985
old_042-0.0057-0.00450.31080.4806985
best_002-0.0061-0.01080.31740.4746985
best_001-0.0136-0.00300.31500.4856982

Best mean IC in this ETF run: original_001.

Interpretation

  • Expanding from 10 stocks to 20 ETFs changes the factor ordering: old_042 led the first mini case, while original_001 has the highest mean IC here. All mean values remain close to zero, so the ranking should be treated as a smoke-test result rather than stable evidence of predictive power.
  • ETFs compress many company-specific effects into portfolio-level returns. The nine sector funds still provide cross-sectional differentiation through sector rotation, but they also share substantial broad-market exposure; that combination can change how stock-oriented legacy factors rank the universe.
  • The bond, gold, and international funds add return drivers that differ from US equity sectors. This broadens the API check, but the aggregate summary does not by itself establish that any factor is stable within each asset subgroup.
  • The two repeated maintainer runs produced byte-identical standard output. That checks deterministic computation against the same downloaded data, not future stability of the upstream public data.

Caveats

  • yfinance is a convenient public-data source, not an institutional data contract. Historical adjustments, missing values, endpoint behavior, and rate limits can change after this run.
  • ETF liquidity, trading hours, premiums or discounts, and underlying-market closures differ across the universe. This mini reproduction does not model those differences.
  • ETF survivorship bias is generally less visible than in a stock universe because these selected funds all survived the requested period. The selection is retrospective and does not constitute a point-in-time membership rule.
  • No transaction costs, slippage, portfolio construction, multiple-testing controls, or subgroup significance tests are included.
  • This project and reproduction are for research and engineering experimentation only. They are not financial advice, investment advice, or a trading recommendation.

Expected Output Check

Use the committed scripts/etf_factor_ic_output.txt as the detailed reference. A successful rerun should satisfy these checks:

CheckMaintainer Run
Requested ETFs20
Successfully loaded ETFs20
Failed or partial ETFs0
Returned panel1005 dates x 20 ETFs
Returned date range2021-01-04 to 2024-12-31
Factor summary rows6
IC observations per factor982 to 985
Highest mean ICoriginal_001 at 0.0131

Small numerical differences are expected if yfinance revises adjusted history or if pandas, PyTorch, SciPy, or yfinance behavior changes. Missing tickers should be reported rather than silently replaced, and large IC differences should prompt a review of the coverage table, dependency versions, and factor masks.

Next Useful Reproductions

  • Repeat the workflow on ETFs or sector-balanced universes.
  • Add a larger public universe with clear membership rules.
  • Compare US equities with A-share symbols supported by public providers.
  • Run the larger walk-forward validation harness in public_data_validation.md, which includes portfolio construction, transaction costs, slippage, turnover, drawdown, and baseline comparisons.