quantlab.factor.kunquant.FactorKunQuant#
- class quantlab.factor.kunquant.FactorKunQuant(config)[source]#
Bases:
FactorFactor backend that compiles a KunQuant op graph to native code.
A subclass describes its factor as a KunQuant graph in
_get_factor_func:Inputnodes named afterconfig.data_columns, operator nodes, and oneOutputper factor name. The same graph is compiled on demand in two layouts,TSforcompute()(a whole date range in one call) andSTREAMforcal_stream()(one bar at a time), so a factor validated in a backtest runs unchanged on live data.config.modesays which of the two the object is used in.Compilation needs a working C++ compiler and dominates run time on small panels; a graph is compiled once and then reused from the process or the disk cache (
quantlab.factor.kunquant_compile). Pinningconfig.factor_namesto the columns you need keeps the compiled graph small. In batch mode the number of symbols must be a multiple of the SIMD block width KunQuant uses on the host, that is, the number of values the CPU processes in one vector instruction.- Parameters:
- configFactorConfig
The factor config, including
mode("batch"or"stream"),data_columnsandnjobs.
Examples
A factor measuring how far the close is above its 5-bar average:
class MaDeviation(FactorKunQuant): def _get_factor_names(self): return ("ma_dev_5",) def _get_factor_func(self): builder = Builder() with builder: close = Input("close") dev = op.Div(close, op.WindowedAvg(close, 5)) Output(op.SubConst(dev, 1.0), "ma_dev_5") return Function(builder.ops)
- PEAK_COPIES = 1#
A compiled graph holds its float32 outputs and little else: Alpha101 and Alpha158 on us3000 (3103 bars x 7625 symbols) peaked at 0.43-0.50 of their inputs and outputs at 8 bytes (2026-10-09), so 1 leaves about a factor of two. See
Factor.cell_bytes.
- SYMBOL_BLOCK_DARWIN = 8#
Symbol count a batch run is padded to a multiple of on macOS. KunQuant’s compiled loops process symbols in fixed-size SIMD blocks and cannot handle a remainder: four values per block on Apple silicon, eight with AVX2 on an Intel Mac; eight covers both.
- cal_stream(data, timestamp, symbols)[source]#
Advance the streaming graph by one bar and return that bar’s outputs.
The stream is initialized on first use.
- Parameters:
- datadict[str, np.ndarray]
Column name to a 1-D array of length
num_symbolsfor every name inconfig.data_columns.- timestampint
The bar’s timestamp, used as the single time coordinate.
- symbolslist[str]
Symbol coordinate values, in the order the arrays are laid out.
- Returns:
- xr.Dataset
The
(1, num_symbols)panel of this bar.
Examples
>>> for step in range(3): # replay three bars ... bar = {"adjClose": adj_close[step]} # float32, per symbol ... row = factor.cal_stream(bar, step, symbols) >>> row.sizes Frozen({'timestamp': 1, 'symbol': 16}) >>> list(row.data_vars) ['rank_close', 'ma_close', 'ma_rank']
- compute(start, end)[source]#
Compute
starttoendin batch mode; seeFactor.compute.- Raises:
- ValueError
If
config.modeis not"batch", orstartis afterend.
Examples
>>> dict(factor.compute("2024-02-01", "2024-02-10").sizes) {'timestamp': 10, 'symbol': 16}
- config_cls#
The config class
from_configrebuilds this factor with.alias of
FactorConfig
- copy()[source]#
Return a copy without the compiled library or stream state.
See
Factor.copy. The compiled batch library, the stream context and its buffer handles belong to this object’s own graph and are not carried over; the copy compiles again on its firstcompute().Examples
>>> factor.copy()._lib is None True
- init_stream()[source]#
Compile the graph in the streaming layout and bind its buffers.
Creates a
StreamContextsized tonum_symbolsand caches a buffer handle for every input column and every factor name, socal_streamdoes not look handles up by name on the hot path. Every name inconfig.data_columnsmust be consumed by a reachableOutput: KunQuant prunes unused inputs and the handle lookup for a pruned one fails.- Returns:
- Self
self, for chaining.
Examples
>>> factor.init_stream() is factor # config.mode == "stream" True >>> sorted(factor._buffer_name_to_id) # one input, three outputs ['adjClose', 'ma_close', 'ma_rank', 'rank_close']
- keep_compiled()[source]#
Hold the batch library for every
computeinside the block.Outside the block each call builds its graph again and takes the library from
compile_graphs, which compiles only the first time (in this process or, through the disk cache, any other); inside it the first call’s library is kept until the block exits, so later calls do not even build the graph. SeeFactor.keep_compiled.Examples
>>> with factor.keep_compiled(): # builds the graph once, not twice ... january = factor.compute("2024-01-01", "2024-01-31") ... february = factor.compute("2024-02-01", "2024-02-29") >>> factor._lib is None True
- property num_symbols#
Number of symbols the streaming graph runs over.
This is the symbol list pinned on the dataset config, since a stream loads no panel.
- Raises:
- ValueError
If
config.modeis not"stream".
Examples
>>> factor.num_symbols # stream config pinning 16 symbols 16
- property symbols#
Symbols the streaming graph runs over, in axis order.
This is the symbol list pinned on the dataset config. In batch mode the symbols are those of the requested panel instead.
- Raises:
- ValueError
If
config.modeis not"stream".
Examples
>>> factor.symbols[:3] ['AAPL', 'MSFT', 'NVDA']