From b21bfb7684003c2188a277623a1fab5315e2eca0 Mon Sep 17 00:00:00 2001 From: mameikagou Date: Thu, 20 Aug 2026 15:39:39 +0800 Subject: [PATCH] fix(data): support unary expression negation --- qlib/data/base.py | 5 +++++ tests/ops/test_elem_operator.py | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/qlib/data/base.py b/qlib/data/base.py index 496ae38ee23..77109f1d87a 100644 --- a/qlib/data/base.py +++ b/qlib/data/base.py @@ -29,6 +29,11 @@ def __str__(self): def __repr__(self): return str(self) + def __neg__(self): + from .ops import Sub # pylint: disable=C0415 + + return Sub(0, self) + def __gt__(self, other): from .ops import Gt # pylint: disable=C0415 diff --git a/tests/ops/test_elem_operator.py b/tests/ops/test_elem_operator.py index 8349157ff44..399bffc0df9 100644 --- a/tests/ops/test_elem_operator.py +++ b/tests/ops/test_elem_operator.py @@ -39,6 +39,19 @@ def test_Sign(self): golden = change.to_numpy() self.assertIsNone(np.testing.assert_allclose(result, golden)) + def test_unary_negation(self): + cases = [ + ("-$close", "0 - $close"), + ("-Std($close, 3)", "0 - Std($close, 3)"), + ] + for field, equivalent in cases: + with self.subTest(field=field): + result = ExpressionD.expression(self.instrument, field, self.start_time, self.end_time, self.freq) + expected = ExpressionD.expression( + self.instrument, equivalent, self.start_time, self.end_time, self.freq + ) + np.testing.assert_allclose(result.to_numpy(), expected.to_numpy(), equal_nan=True) + class TestOperatorDataSetting(TestOperatorData): def test_setting(self):