The Problem
Factor research and backtesting in Python traditionally run on CPU. The zipline.pipeline engine takes seconds to evaluate a single factor on a few thousand assets. This repo attacks that bottleneck with GPU-accelerated factor computation, claiming 30–70x speedups over zipline in its own benchmarks.
What This Does
spectre is a GPU-accelerated factors library and backtester built on PyTorch. The spectre/factors/ package provides a factor engine (engine.py), factor primitives (basic.py, statistical.py, technical.py), and filter/label classes. The spectre/data/ package handles data ingestion from CSV, Arrow, Yahoo, Quandl, and IEX sources. The spectre/trading/ package covers the backtest side: portfolio, position, blotter, events, and stop models.
The README positions it as compatible with alphalens and pyfolio, and the codebase is pure Python over PyTorch tensors, so it integrates with deep learning models directly.
How It Is Wired
Execution starts at run in spectre/factors/engine.py:410, which reaches 174 functions — the largest reach of any entry point in the repo. The engine calls into compute, which fans out to agg (21 call sites), nanmean (17), nansum (10), and unmasked_sum (9). The FactorEngine class is called from 22 places, and CsvDirLoader from 25.
The dependency hub is spectre/factors/factor.py: 140 functions, 44 classes, imported by 25 files. It defines the operator overrides (__add__, __mul__, etc.) that make factor expressions composable. Its blast radius is the widest in the repo — change it and most of the factor system moves.
The cost of that centrality is visible in the module graph: factor.py, engine.py, basic.py, and trading/__init__.py all sit inside circular import cycles (16 modules total). Anyone modifying these files must track mutually-reachable imports, which makes refactoring slow and error-prone.
File-by-file map:
spectre/factors/factor.py— factor expression base, operator overloading, the hubspectre/parallel/algorithmic.py— tensor operations, split/revert/padding, 58 functionsspectre/factors/filter.py— filter and selection primitivesspectre/trading/blotter.py— trade recording and metrics, 64 functionsspectre/factors/engine.py— the run orchestration, data loading, cachingspectre/data/arrow.py— the only file that both reads/writes files and makes an outbound network call (ingest)
How To Use It
Setup requires Python 3.7+, PyTorch 1.3+, and Pandas 1.0+. The README documents:
pip install --no-deps git+git://github.com/Heerozh/spectre.git
conda install pytorch torchvision torchaudio cudatoolkit=11.0 -c pytorch
conda install pyarrow pandas tqdm plotly requests bs4 lxml
The quick-start path: ingest data with YahooDownloader.ingest(), load it via ArrowLoader, build a FactorEngine, add factors, call engine.run(start, end). The full example is in examples/dual_ema_on_apple.py and examples/smart_beta.py. Configuration lives in spectre/config.py.
Real-World Use
A typical workflow: download 5 years of S&P 500 data, load it into an ArrowLoader, attach a momentum factor (EMA shift/sum per the README's Barra example), run the engine over a date range, and feed the resulting factor values into alphalens for analysis. The same FactorEngine can move to CUDA with engine.to_cuda() for large universes.
Code Health & Issues
Static analysis (not opinion) found 53 issues: 33 high, 20 medium.
- High — deep nesting (31 occurrences):
spectre/factors/factor.py,spectre/data/dataloader.py,spectre/factors/engine.pyreach indentation depth 11. Control flow is hard to follow; fix with guard clauses and extracted inner blocks. - High — import cycles (16 modules):
factor.py,engine.py,basic.pyparticipate in circular imports. Breaking these requires extracting shared types or deferring imports. - High — oversized files (3):
factor.pyat 909 lines, plusblotter.pyandtest_factor.py. Changes ripple widely. - Medium — file opened without context manager (2):
spectre/data/quandl.py,setup.py— potential handle leaks. - Medium — duplicated code: 11 repeated 6-line blocks across 6 files including both examples and
spectre/data/csv.py.
SDLC gaps: no CI/CD pipeline exists, so nothing runs the build or tests on merge. Dependencies are pinned in requirements.txt without a lockfile, so builds are not reproducible. Tests exist (59 files) but are not gated by automation.
The Bottom Line
The factor engine is genuinely fast and the API is clean for expression-style factor construction. The trade-off is code quality: circular imports and deep nesting make factor.py and engine.py expensive to modify safely. Use it if you need GPU factor throughput and can tolerate a codebase that needs careful refactoring before substantial changes.