Interpretable quantitative factor mining with genetic programming, NumPy/Pandas, and a Torch GPU panel backend.
QuantGplearn
Evolve interpretable quantitative factors. Evaluate them on CPU or GPU. Keep the formula.
Quick start · Features · Operators · Architecture · Documentation
QuantGplearn is a genetic-programming framework for quantitative factor research. Instead of fitting an opaque set of weights, it searches for human-readable expressions built from market features, rolling operators, and cross-sectional transformations:
csrank(tszscore(close, 168))
div(tsdelta(vwap, 24), tsstd(close, 72))
csdemean(tsoneolsresid(close, volume, 336))
The same symbolic program representation can run through the original NumPy/Pandas engine or through a Torch tensor backend designed for dense panel data. The result is a practical bridge between explainable symbolic research and GPU-accelerated factor evaluation.
QuantGplearn discovers candidate signals; it is not a promise of investment
performance. Validate every factor with leakage-aware, out-of-sample
research and a realistic execution model.
中文简介
QuantGplearn 是一个面向量化因子研究的遗传规划框架。它不输出难以解释的黑盒权重, 而是进化出由行情特征、时序算子和截面算子组成的可读公式。项目同时保留原有 NumPy/Pandas CPU 路径,并提供适用于 [时间, 标的, 特征] 面板数据的 Torch/GPU 执行后端,支持 IC、RankIC、ICIR 和多空组合 Sharpe 代理目标,以及因子相关性过滤。
Why QuantGplearn?
| Capability | What it gives you | |---|---| | Formula-native research | Every candidate is an inspectable expression tree, not a hidden parameter vector. | | Two execution backends | Keep the mature NumPy/Pandas workflow or evaluate dense panel expressions with Torch. | | Time-series + cross-section semantics | Mix rolling logic within each security with ranking and normalization across securities. | | Finance-aware objectives | Search with IC, RankIC, ICIR, RankICIR, or a long-short Sharpe proxy. | | Diversity controls | Hall-of-fame ranking and factor-correlation filtering reduce duplicate discoveries. | | Research ergonomics | Scikit-learn-style estimators, warm starts, low-memory mode, score caching, and expression export. |
What It Does
Symbolic learning
SymbolicRegressorevolves formulas for continuous prediction.SymbolicClassifierevolves binary classification formulas.SymbolicTransformerproduces multiple symbolic features on the CPU path.GpuSymbolicTransformermines multiple panel factors with Torch execution.
Genetic search
grow,full, andhalf and halfpopulation initialization.- Tournament selection.
- Subtree crossover.
- Subtree, hoist, point, and point-replacement mutation.
- Reproduction of surviving programs.
- A maximum-length evolution guard and parsimony pressure against unnecessarily complex formulas.
- Warm-started evolution and generation-level diagnostics.
Panel factor evaluation
- Converts a
pandas.DataFrameinto a dense[T, N, F]tensor. - Applies time-series operators along time for each security.
- Applies cross-section operators across securities at each timestamp.
- Carries a validity mask for missing features and targets.
- Cleans non-finite outputs and optionally normalizes each cross-section.
- Caches expression scores and, optionally, factor tensors.
Factor selection
- Ranks a hall of fame by the chosen research objective.
- Applies best-effort correlation filtering with
tolerable_corr; if too few
- Exports factor values as tensors, NumPy arrays, or panel DataFrames.
- Exports rank, score, expression, tree length, and tree depth.
- Provides
TensorFactorCalculatorandGPAlphaPoolfor IC-based factor pools.
Quick Start
1. Install
git clone https://github.com/WYFHHH/QuantGplearn.git
cd QuantGplearn
python -m pip install -e .
QuantGplearn targets Python 3.11+ on Linux. For GPU execution, install the PyTorch build that matches your CUDA environment before installing the project.
2. Build a panel
The GPU API accepts a DataFrame with a [datetime, symbol] MultiIndex, or equivalent ordinary columns:
import numpy as np
import pandas as pd
rng = np.random.default_rng(7) times = pd.date_range("2024-01-01", periods=256, freq="h") symbols = [f"asset_{i}" for i in range(12)] index = pd.MultiIndex.from_product( [times, symbols], names=["datetime", "symbol"] )
returns = rng.normal(0.0, 0.01, size=(len(times), len(symbols))) close = 100.0 * np.exp(np.cumsum(returns, axis=0)) volume = rng.lognormal(10.0, 0.7, size=close.shape) target = np.roll(returns, -1, axis=0) target[-1] = np.nan
panel = pd.DataFrame( { "close": close.reshape(-1), "volume": volume.reshape(-1), "target": target.reshape(-1), }, index=index, )
3. Evolve factors
from QuantGplearn.gpu_transformer import GpuSymbolicTransformer
model = GpuSymbolicTransformer( population_size=256, generations=10, halloffame=50, n_components=8, tournament_size=32, function_set=[ "add", "sub", "mul", "div", "tsdelta", "tsmean", "tsstd", "tszscore", "csrank", "csdemean", ], feature_names=["close", "volume"], objective="icir", max_length=20, tolerable_corr=0.7, device="cuda:0", random_state=2025, verbose=1, )
model.fitpanel(panel, targetcol="target")
expressions = model.getfactorexpressions() factors = model.transform_panel(output="dataframe")
print(expressions) print(factors.tail())
If CUDA is unavailable, a CUDA device request falls back to CPU tensor execution with a warning. This makes the same example useful as a functional smoke test on a non-GPU machine.
4. Use the CPU estimators
The original estimators remain available for conventional tabular symbolic learning:
from QuantGplearn.genetic import SymbolicTransformer
cpu_model = SymbolicTransformer( population_size=1000, generations=20, halloffame=100, n_components=10, function_set=["add", "sub", "mul", "div", "sqrt", "log"], metric="spearman", parsimony_coefficient=0.001, random_state=2025, )
cpumodel.fit(Xtrain, y_train) symbolicfeatures = cpumodel.transform(X_test)
Architecture
flowchart LR
A["Panel DataFrame<br/>datetime × symbol"] --> B["TensorPanelData<br/>[T, N, F] + mask"]
B --> C["GP Population<br/>expression trees"]
C --> D["Program.executetensor"]
D --> E["Torch operators<br/>raw · time-series · cross-section"]
E --> F["ProgramEvaluator<br/>clean · normalize · cache"]
F --> G["TensorFitness<br/>IC · RankIC · ICIR · Sharpe"]
G --> H["Selection<br/>parsimony · hall of fame · diversity"]
H --> C
H --> I["Factors + readable expressions"]
Execution semantics
For an internal tensor with shape [T, N, F]:
| Dimension | Meaning | Example operation | |---|---|---| | T | ordered timestamps | ts_mean(close, 24) rolls along time | | N | securities at one timestamp | cs_rank(factor) ranks across securities | | F | input feature channels | close, volume, vwap, custom features |
A program returns one [T, N] factor tensor. Warm-up periods, missing observations, and invalid targets are represented through NaNs and a boolean mask rather than silently becoming valid samples.
Search Objectives
| Objective | Alias | Interpretation | |---|---|---| | ic | pearson | Mean cross-sectional Pearson correlation with the target. | | rank_ic | spearman | Mean cross-sectional rank correlation. | | icir | - | Mean IC divided by IC volatility. | | rank_icir | - | Mean RankIC divided by RankIC volatility. | | longshortsharpe | sharpe | Sharpe proxy for a top-minus-bottom factor portfolio. |
The GPU evaluator can normalize each timestamp's cross-section before scoring. Expression errors and non-finite scores are converted to neutral scores so one invalid tree does not stop an evolutionary run.
Operator Library
The recommended GPU panel preset contains 49 operators. Torch backends are registered when QuantGplearn.torch_functions is imported.
Protected mathematical primitives
add sub mul div sqrt log abs neg inv max min sig
Division, logarithm, square root, inverse, tangent, and ratio-style indicators use protected or clipped implementations to keep expression trees numerically closed.
Time-series operators
| Family | Operators | |---|---| | Lag and change | tsshift, tsdelta, ts_mom | | Rolling statistics | tsmin, tsmax, tssum, tsmean, tsstd, tszscore | | Rolling position | tsargmax, tsargmin, tsrank, tsfreq | | Relationships | tscorr, tsoneolsk, tsoneolsresid, tshedge | | Distribution shape | tsskew, tskurt | | Price action | tscdlbodym, tsbarbs, tsxsratio, tsbband | | Technical indicators | tsadx, tsaroon, tsbopr, tscmo, tsema, tsmacd, tsrsi, tsstochf, ts_atr |
Cross-section operators
csrank cszscore csdemean csscale cs_winsorize
Ready-made sets
from QuantGplearn import functions
from QuantGplearn.torchfunctions import GPUSAFEPANELFUNCTIONS
functions.all_function # raw + time-series operators functions.section_function # cross-section-only operators functions.panel_function # raw + time-series + cross-section GPUSAFEPANEL_FUNCTIONS # recommended 49-operator Torch preset
Trigonometric Torch backends for sin, cos, and tan are also registered and can be selected explicitly, although they are not part of the recommended default preset.
Evolution Controls
| Parameter | Purpose | |---|---| | population_size | Programs evaluated in each generation. | | generations | Maximum evolutionary generations. | | tournament_size | Selection pressure during parent choice. | | initdepth / initmethod | Shape and diversity of the initial trees. | | p_crossover | Probability of exchanging subtrees between parents. | | psubtreemutation | Probability of replacing a subtree. | | phoistmutation | Probability of simplifying a tree by hoisting a subtree. | | ppointmutation | Probability of replacing compatible functions or terminals. | | max_length | Evolution guard: overlength parents are reproduced instead of being mutated again. | | parsimony_coefficient | Penalty for complexity; "auto" is supported. | | ppointreplace | Replacement probability inside point mutation. | | warm_start | Continue evolution from an existing run. | | low_memory | Discard older populations to reduce memory usage. | | cachescores / cachefactors | Reuse repeated expression evaluations. | | tolerable_corr | Maximum accepted mutual factor correlation during selection. |
The probabilities for crossover and the three mutation methods must leave room for reproduction; their cumulative sum cannot exceed one.
Result Inspection
getfactorexpressions() returns a compact research table:
rank score expression length depth
transform_panel() supports:
model.transform_panel(output="tensor") # list of [T, N] tensors
model.transform_panel(output="numpy") # flattened [samples, factors]
model.transform_panel(output="dataframe") # MultiIndex panel DataFrame
The estimator also records generation diagnostics in rundetails, including average expression length, average fitness, best fitness, and generation time.
Alpha Pool Utilities
For workflows that build a factor library incrementally:
from QuantGplearn.alpha_pool import GPAlphaPool, TensorFactorCalculator
calculator = TensorFactorCalculator( model.tensordata, normalize=True, cache_factors=True, )
pool = GPAlphaPool( capacity=20, calculator=calculator, iclowerbound=0.01, mutualicupper_bound=0.7, )
pool.update(list(model)) records = pool.to_records()
The pool scores single-factor IC, rejects factors that are too correlated with existing members, and keeps the strongest expressions up to its capacity.
Extending the Framework
Custom CPU function
Use make_function to add a numerically closed vector operator:
import numpy as np
from QuantGplearn.functions import make_function
def signed_square(x): x = np.asarray(x, dtype=float) return np.sign(x) x*2
signedsquarefn = make_function( function=signed_square, name="signed_square", arity=1, )
Add a Torch implementation
A custom _Function can participate in GPU evolution after attaching a Torch callable with matching semantics:
import torch
def torchsignedsquare(x): return torch.sign(x) x*2
signedsquarefn.torchfunction = torchsigned_square
model = GpuSymbolicTransformer( functionset=["add", "sub", signedsquare_fn], featurenames=featurenames, )
Custom CPU fitness functions can be created with QuantGplearn.fitness.make_fitness. The GPU path uses TensorFitness objectives from QuantGplearn.tensor_fitness.
CPU and GPU Paths
| | NumPy/Pandas path | Torch path | |---|---|---| | Main API | SymbolicRegressor, SymbolicClassifier, SymbolicTransformer | GpuSymbolicTransformer | | Typical data | arrays or panel DataFrames | panel DataFrames / dense tensors | | Parallel model | joblib/pathos CPU workers | one Torch device per estimator | | Time-series operators | grouped by security | tensor time dimension | | Cross-section operators | grouped by timestamp | tensor security dimension | | Best use | compatibility, custom CPU research, smaller workloads | dense panel evaluation and GPU-oriented factor mining |
Current Limitations
- The GPU estimator evaluates a population serially on one Torch device; it is
maxsamplesandnjobsare retained in the GPU estimator signature for
- Dense
[T, N, F]panels can be memory intensive when the source panel is
- Rolling operators based on
torch.unfoldmay create large intermediate
- GPU rank tie handling and a few NaN edge cases can differ slightly from
- The GPU path currently focuses on numeric factors and does not support the
longshortsharpeis a fast training proxy, not a replacement for a
These constraints are documented deliberately so research results are easier to interpret and future contributions have clear targets.
Project Layout
QuantGplearn/
├── genetic.py # CPU symbolic estimators and genetic evolution
├── _program.py # expression-tree representation and execution
├── functions.py # NumPy/Pandas primitives and operator metadata
├── gpu_transformer.py # GPU panel factor-mining estimator
├── tensor_data.py # DataFrame <-> [T, N, F] tensor conversion
├── torch_functions.py # Torch implementations of factor operators
├── tensor_fitness.py # IC, RankIC, ICIR, and Sharpe objectives
├── evaluator.py # cleaning, normalization, evaluation, and caches
└── alpha_pool.py # IC and mutual-correlation factor pool
The public repository contains the reusable framework, its documentation, and framework-level tests. Proprietary datasets, mined strategies, and private research workflows are intentionally outside the repository.
Testing
python -m pip install pytest
pytest -q
The GPU transformer smoke test runs on CPU tensors when CUDA is unavailable.
Documentation
Roadmap
- Chunked and batched expression evaluation.
- Multi-GPU evaluator workers.
- Lower-memory rolling kernels for long panels.
- More exact CPU/GPU parity for rank ties and missing-value edges.
- Weight optimization for multi-factor alpha pools.
- Expanded tests and reproducible performance benchmarks.
Acknowledgements
QuantGplearn is inspired by and adapted from:
License
QuantGplearn is released under the MIT License.