quantlab.factor.kunquant_cs.CrossSectionalZScore#
- class quantlab.factor.kunquant_cs.CrossSectionalZScore(v)[source]#
Bases:
GenericCrossSectionalOpCross-sectional z-score,
(x - mean_t) / std_tover symbols per bar.At every timestamp the mean and the sample standard deviation (
ddof=1, matching pandas.std()and KunQuant’sWindowedStddev) 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
GenericCrossSectionalOpwhose loop body is hand-written C++ (seegenerate_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 everyGenericCrossSectionalOp. 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-majorTSlayout used for batch runs and theSTREAMlayout used for bar-by-bar runs.Alpha101StockandAlpha158Stockapply this op to every output; the spot-kline classes applyWindowedZScoreinstead. 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_0intooutput_0.KunQuant calls this when it emits the C++ for the graph. The loop runs once per timestamp over
num_stockssymbols: 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;'