diff --git a/qlib/data/ops.py b/qlib/data/ops.py index d9a2ffbb3e3..f68fd894ece 100644 --- a/qlib/data/ops.py +++ b/qlib/data/ops.py @@ -1277,6 +1277,7 @@ def _load_internal(self, instrument, start_index, end_index, *args): _series = self.feature.load(instrument, start_index, end_index, *args) if self.N == 0: series = pd.Series(expanding_rsquare(_series.values), index=_series.index) + series.loc[np.isclose(_series.expanding(min_periods=1).std(), 0, atol=2e-05)] = np.nan else: series = pd.Series(rolling_rsquare(_series.values, self.N), index=_series.index) series.loc[np.isclose(_series.rolling(self.N, min_periods=1).std(), 0, atol=2e-05)] = np.nan diff --git a/tests/ops/test_rolling_operator.py b/tests/ops/test_rolling_operator.py new file mode 100644 index 00000000000..f950895f2c6 --- /dev/null +++ b/tests/ops/test_rolling_operator.py @@ -0,0 +1,28 @@ +import numpy as np +import pandas as pd + +from qlib.data.ops import Rsquare + + +class FeatureStub: + def __init__(self, values): + self.series = pd.Series(values, dtype=float) + + def load(self, instrument, start_index, end_index, *args): + return self.series + + +def test_expanding_rsquare_masks_near_constant_windows(): + values = [100, 100, 100, 100.000001, 100, 100] + + result = Rsquare(FeatureStub(values), 0)._load_internal(None, None, None) + + assert result.isna().all() + + +def test_expanding_rsquare_keeps_varying_windows(): + values = [1, 2, 4, 8, 16] + + result = Rsquare(FeatureStub(values), 0)._load_internal(None, None, None) + + assert np.isfinite(result.iloc[1:]).all()