quantlab.factor.kunquant_cs.CrossSectionalZScore#

class quantlab.factor.kunquant_cs.CrossSectionalZScore(v)[source]#

Bases: GenericCrossSectionalOp

Cross-sectional z-score, (x - mean_t) / std_t over symbols per bar.

At every timestamp the mean and the sample standard deviation (ddof=1, matching pandas .std() and KunQuant’s WindowedStddev) are taken over all symbols, ignoring NaN. NaN inputs stay NaN. A bar with fewer than two valid values, or with zero standard deviation, is NaN for every symbol. No fill is applied.

KunQuant’s composite ops can only be built from time-series operators, so this op is a GenericCrossSectionalOp whose loop body is hand-written C++ (see generate_body). That choice brings three constraints. First, the C++ body must not read any parameter of the op: KunQuant reuses generated C++ functions by class name and data layout alone, so a parameterized variant needs a class of its own. Second, batch runs must start at bar 0, because in KunQuant 0.1.11 a non-zero start index gives wrong results for every GenericCrossSectionalOp. Third, the number of symbols must be a multiple of the host’s SIMD block width (the number of values the CPU’s vector instructions process at once), in both the time-major TS layout used for batch runs and the STREAM layout used for bar-by-bar runs.

Alpha101Stock and Alpha158Stock apply this op to every output; the spot-kline classes apply WindowedZScore instead. Choosing between the two is a strategy decision.

Parameters:
vOpBase

The input series, usually a factor expression.

Examples

>>> Output(CrossSectionalZScore(alpha(all_data)), "alpha001_cs")
generate_body()[source]#

Return the C++ loop that z-scores input_0 into output_0.

KunQuant calls this when it emits the C++ for the graph. The loop runs once per timestamp over num_stocks symbols: one pass for the mean, one for the variance, and one to write the scores.

Examples

>>> body = CrossSectionalZScore(Input("close")).generate_body()
>>> body.strip().splitlines()[0]
'T sum = 0;'
generate_head()[source]#

Return the C++ preamble for the generated function, which is empty.

Examples

>>> CrossSectionalZScore(Input("close")).generate_head()
''