Explored after seeing a CrowdCent participant use ovvo-nns. Their competition performance alone doesn't establish whether the method is useful.
In this context, Nonlinear Nonparametric Statistics (NNS) is a specific framework built around partial moments, associated with Fred Viole and the book by Viole and David Nawrocki, Nonlinear Nonparametric Statistics: Using Partial Moments.
ovvo-nns is a native Python port of the R NNS package, imported as nns. It includes descriptive statistics, dependence measures, regression, classification, forecasting, and stochastic dominance tools. It is more than a feature-engineering library.
For observations \(x_i\), threshold \(t\), and degree \(p>0\):
Divide by all observations, not just those on the relevant side. These measure frequency and magnitude together, rather than the conditional average loss or gain.
The threshold is sometimes called a target, but this is not the prediction label! For returns, zero is a useful threshold; the sample mean is useful for decomposing variance. Other thresholds could represent a benchmark or minimum acceptable return.
import numpy as np
def partial_moments(x, degree=2, target=0.0):
"""Return (LPM, UPM), normalized by the total observation count."""
x = np.asarray(x, dtype=float)
if x.ndim != 1 or x.size == 0 or not np.isfinite(x).all():
raise ValueError("x must be a nonempty, finite, 1-D array")
if not np.isfinite(degree) or degree < 0:
raise ValueError("degree must be finite and nonnegative")
if not np.isfinite(target):
raise ValueError("target must be finite")
# Explicit degree-zero convention: assign ties to the lower side.
# Avoid raising clipped zeros to power zero, which would count both sides.
if degree == 0:
return np.mean(x <= target), np.mean(x > target)
lower = np.maximum(target - x, 0)
upper = np.maximum(x - target, 0)
return np.mean(lower ** degree), np.mean(upper ** degree)
x = np.array([-2.0, -1.0, 0.0, 3.0])
mu = x.mean()
lower, upper = partial_moments(x, degree=2, target=mu)
variance = lower + upper # 3.5 == np.var(x)
sample_variance = variance * len(x) / (len(x) - 1)
assert np.isclose(variance, np.var(x))
assert np.isclose(sample_variance, np.var(x, ddof=1))
Here the downside contribution is 1.25 and the upside contribution is 2.25. Variance hides that split.
For any threshold \(t\):
The sum is variance only when centered at the mean.
Fractional degrees work too. For example, a deviation four times larger receives \(4^{0.5}=2\) times the weight at degree 0.5, versus 16 times at degree 2.
For every \(p>0\):
These are absolute moments about the threshold, including at fractional degrees. Degrees below one reduce the relative emphasis on large deviations, but are not immune to outliers: their contribution still grows without bound.
Taking a partial moment to the power \(1/p\) restores the units of the original data. This rooted measure scales linearly with positive changes of scale for every \(p>0\); degree 0.5 is not uniquely more scale-sensitive.
For Machine learning or Algorithmic trading, compute over trailing windows per asset:
These add representations, not new information. Explicit aggregation may help a neural network learn with less data, particularly when the target signal is weak. But there is no established reason to expect larger gains for an NN than a GBM: ReLU represents the positive-part function exactly, and either model's benefit depends on its inputs and training.
Neighboring degrees may be highly redundant. Heavy-tailed features may benefit from scaling, clipping, or per-date cross-sectional ranks. Choose preprocessing using training data and the actual prediction setup. No degree is known in advance to be best.
nns_dep, nns_cor: compare two variables. Comparing a feature with a label is supervised feature selection and must happen inside training folds.nns_reg, nns_m_reg, nns_stack, nns_boost: supervised prediction tools using features and labels.nns_causation is not evidence that observational association establishes causality.Partial-moment features can still leak information if their windows include unavailable observations or if thresholds/preprocessing are estimated from future data. Use only information available at prediction time; shift rolling windows when the timing requires it.
Avoid interpreting the package's breadth as evidence of predictive advantage. We reviewed its PyPI documentation, not its implementation or independent benchmarks; precise algorithmic and parity claims need source verification.