numerai-tools 0.4.0.dev2__tar.gz → 0.4.2.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/PKG-INFO +1 -1
  2. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools/scoring.py +18 -13
  3. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools/signals.py +28 -1
  4. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools.egg-info/PKG-INFO +1 -1
  5. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools.egg-info/requires.txt +0 -1
  6. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/setup.py +1 -2
  7. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/tests/test_scoring.py +2 -2
  8. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/tests/test_signals.py +8 -1
  9. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/LICENSE +0 -0
  10. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/README.md +0 -0
  11. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools/__init__.py +0 -0
  12. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools/py.typed +0 -0
  13. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools/submissions.py +0 -0
  14. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools.egg-info/SOURCES.txt +0 -0
  15. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools.egg-info/dependency_links.txt +0 -0
  16. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/numerai_tools.egg-info/top_level.txt +0 -0
  17. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/setup.cfg +0 -0
  18. {numerai_tools-0.4.0.dev2 → numerai_tools-0.4.2.dev1}/tests/test_submissions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: numerai_tools
3
- Version: 0.4.0.dev2
3
+ Version: 0.4.2.dev1
4
4
  Summary: A collection of open-source tools to help interact with Numerai, model data, and automate submissions.
5
5
  Home-page: https://github.com/numerai/numerai-tools
6
6
  Maintainer: Numerai
@@ -1,6 +1,5 @@
1
1
  from typing import List, Tuple, Union, Optional, TypeVar
2
2
 
3
- import torch
4
3
  import numpy as np
5
4
  import pandas as pd # type: ignore
6
5
  from scipy import stats # type: ignore
@@ -582,10 +581,11 @@ def meta_portfolio_contribution(
582
581
  targets: pd.Series,
583
582
  ) -> pd.Series:
584
583
  """Calculates the "meta portfolio" score:
585
- - rank, normalize, and power the signal
586
- - convert signal into neutralized weights
584
+ - rank, normalize, and power each signal
585
+ - convert each signal into neutralized weights
586
+ - generate the stake-weighted portfolio
587
+ - calculate the gradient of the portfolio w.r.t. the stakes
587
588
  - multiplying the weights by the targets
588
-
589
589
  Arguments:
590
590
  predictions: pd.DataFrame - the predictions to evaluate
591
591
  stakes: pd.Series - the stakes to use as weights
@@ -599,22 +599,27 @@ def meta_portfolio_contribution(
599
599
  predictions, neutralizers, sample_weights, targets = filter_sort_index_many(
600
600
  [predictions, neutralizers, sample_weights, targets]
601
601
  )
602
-
603
602
  stake_weights = weight_normalize(stakes.fillna(0))
604
603
  assert np.isclose(stake_weights.sum(), 1), "Stakes must sum to 1"
605
-
606
604
  weights = tie_kept_rank__gaussianize__pow_1_5(predictions).apply(
607
605
  lambda s_prime: generate_neutralized_weights(
608
606
  s_prime, neutralizers, sample_weights
609
607
  )
610
608
  )
611
-
612
- w = torch.tensor(weights[stakes.index].values)
613
- s = torch.tensor(stake_weights.values, requires_grad=True)
614
- t = torch.tensor(targets.values)
609
+ w = weights[stakes.index].values
610
+ s = stake_weights.values
611
+ t = targets.values
615
612
  swp = w @ s
616
613
  swp = swp - swp.mean()
617
- swp = swp / swp.abs().sum()
618
- alpha = swp @ t
619
- mpc = torch.autograd.grad(alpha, s)[0].numpy()
614
+ swp_abs_sum = np.sum(np.abs(swp))
615
+ swp_sign = np.sign(swp)
616
+ alpha_unnormalized_swp_grad = (
617
+ 1
618
+ / np.power(swp_abs_sum, 2)
619
+ * (swp_abs_sum * t - swp_sign * np.dot(swp, t)).reshape(-1, 1)
620
+ )
621
+ zero_mean_jac_vec_prod = (
622
+ alpha_unnormalized_swp_grad - alpha_unnormalized_swp_grad.mean()
623
+ )
624
+ mpc = (w.T @ zero_mean_jac_vec_prod).squeeze()
620
625
  return pd.Series(mpc, index=stakes.index)
@@ -19,7 +19,8 @@ def churn(
19
19
 
20
20
  For 2 given series with overlapping indices, churn is 1 - Spearman Correlation.
21
21
  If top_bottom is provided, the churn is calculated as the average of the % of
22
- tickers that stay in the top and bottom predictions.
22
+ tickers that stay in the top and bottom predictions. This is only relevant when
23
+ the series are rank signals and not portfolio weights.
23
24
 
24
25
  Arguments:
25
26
  s1: pd.Series - the first series to compare
@@ -43,3 +44,29 @@ def churn(
43
44
  assert s1.std() > 0, "s1 must have non-zero standard deviation"
44
45
  assert s2.std() > 0, "s2 must have non-zero standard deviation"
45
46
  return 1 - spearman_correlation(s1, s2)
47
+
48
+
49
+ def turnover(
50
+ s1: pd.Series,
51
+ s2: pd.Series,
52
+ ):
53
+ """Calculate the turnover between two series. Turnover is the total change in weights between
54
+ the two series divided by 2.
55
+
56
+ For 2 given series with overlapping indices, join the series on index, fill nans with zeroes
57
+ and calculate turnover as the absolute total difference between the two series divided by 2.
58
+ This is only relevant when the series are portfolio weights and not rank signals.
59
+
60
+ Arguments:
61
+ s1: pd.Series - the first series to compare
62
+ s2: pd.Series - the second series to compare
63
+ top_bottom: Optional[int] - the number of top and bottom predictions to use
64
+ when calculating the correlation. Results in
65
+ 2*top_bottom predictions.
66
+
67
+ Returns:
68
+ float - the turnover between the two series
69
+ """
70
+ s1, s2 = filter_sort_index(s1, s2)
71
+ turnover = (s1 - s2).abs().sum() / 2
72
+ return turnover
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: numerai-tools
3
- Version: 0.4.0.dev2
3
+ Version: 0.4.2.dev1
4
4
  Summary: A collection of open-source tools to help interact with Numerai, model data, and automate submissions.
5
5
  Home-page: https://github.com/numerai/numerai-tools
6
6
  Maintainer: Numerai
@@ -2,4 +2,3 @@ pandas<3.0.0,>=2.2.2
2
2
  numpy<3.0.0,>=2.0.0
3
3
  scipy<2.0.0,>=1.13.0
4
4
  scikit-learn<2.0.0,>=1.5.0
5
- torch
@@ -1,7 +1,7 @@
1
1
  from setuptools import setup
2
2
  from setuptools import find_packages
3
3
 
4
- VERSION = "0.4.0.dev2"
4
+ VERSION = "0.4.2.dev1"
5
5
 
6
6
 
7
7
  def load(path):
@@ -43,6 +43,5 @@ if __name__ == "__main__":
43
43
  "numpy>=2.0.0,<3.0.0",
44
44
  "scipy>=1.13.0,<2.0.0",
45
45
  "scikit-learn>=1.5.0,<2.0.0",
46
- "torch",
47
46
  ],
48
47
  )
@@ -318,8 +318,8 @@ class TestScoring(unittest.TestCase):
318
318
  [5, 1],
319
319
  ]
320
320
  )
321
- v = pd.Series([3, 2, 1, 2, 3]).T
322
- t = pd.Series([1, 2, 3, 2, 1]).T
321
+ v = pd.Series([1, 0.5, 1, 0.5, 1]).T
322
+ t = pd.Series([1, 0, 1, 0, 1]).T
323
323
  score = alpha(s, N, v, t)
324
324
  np.testing.assert_allclose(score, 0.0, atol=1e-14, rtol=1e-14)
325
325
 
@@ -3,7 +3,7 @@ import unittest
3
3
  import numpy as np
4
4
  import pandas as pd # type: ignore
5
5
 
6
- from numerai_tools.signals import churn
6
+ from numerai_tools.signals import churn, turnover
7
7
 
8
8
 
9
9
  class TestSignals(unittest.TestCase):
@@ -39,6 +39,13 @@ class TestSignals(unittest.TestCase):
39
39
  tmp = churn(self.up, self.constant, top_bottom=2)
40
40
  assert np.isclose(tmp, 0), tmp
41
41
 
42
+ def test_turnover(self):
43
+ assert np.isclose(turnover(self.up, self.up), 0)
44
+ assert np.isclose(turnover(self.up, self.up_down), 3)
45
+ assert np.isclose(turnover(self.up, self.oscillate), 4.5)
46
+ assert np.isclose(turnover(self.up, self.down), 6)
47
+ assert np.isclose(turnover(self.up, self.constant), 3.5)
48
+
42
49
 
43
50
  if __name__ == "__main__":
44
51
  unittest.main()