mirror of
https://github.com/wassname/catalyst.git
synced 2026-08-10 12:00:15 +08:00
DEV: Kill compute_from_{arrays,windows}.
All terms just implement `_compute` now. (We reserve `compute` for the public API of `CustomFactor`.) Also removed `TestingTermMixin` and its subclasses in favor of just using `CustomFactor.`
This commit is contained in:
@@ -40,7 +40,7 @@ from zipline.data.ffc.loaders.us_equity_pricing import (
|
||||
)
|
||||
from zipline.finance.trading import TradingEnvironment
|
||||
from zipline.modelling.engine import SimpleFFCEngine
|
||||
from zipline.modelling.factor import TestingFactor
|
||||
from zipline.modelling.factor import CustomFactor
|
||||
from zipline.modelling.factor.technical import (
|
||||
MaxDrawdown,
|
||||
SimpleMovingAverage,
|
||||
@@ -54,12 +54,12 @@ from zipline.utils.test_utils import (
|
||||
)
|
||||
|
||||
|
||||
class RollingSumDifference(TestingFactor):
|
||||
class RollingSumDifference(CustomFactor):
|
||||
window_length = 3
|
||||
inputs = [USEquityPricing.open, USEquityPricing.close]
|
||||
|
||||
def from_windows(self, open, close):
|
||||
return (open - close).sum(axis=0)
|
||||
def compute(self, today, assets, out, open, close):
|
||||
out[:] = (open - close).sum(axis=0)
|
||||
|
||||
|
||||
def assert_product(case, index, *levels):
|
||||
|
||||
@@ -7,13 +7,13 @@ from numpy import (
|
||||
)
|
||||
|
||||
from zipline.errors import UnknownRankMethod
|
||||
from zipline.modelling.factor import TestingFactor
|
||||
from zipline.modelling.factor import Factor
|
||||
from zipline.utils.test_utils import check_arrays
|
||||
|
||||
from .base import BaseFFCTestCase
|
||||
|
||||
|
||||
class F(TestingFactor):
|
||||
class F(Factor):
|
||||
inputs = ()
|
||||
window_length = 0
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from numpy import (
|
||||
)
|
||||
|
||||
from zipline.errors import BadPercentileBounds
|
||||
from zipline.modelling.factor import TestingFactor
|
||||
from zipline.modelling.factor import Factor
|
||||
from zipline.utils.test_utils import check_arrays
|
||||
|
||||
from .base import BaseFFCTestCase
|
||||
@@ -51,12 +51,12 @@ def rowwise_rank(array):
|
||||
return argsort(argsort(array))
|
||||
|
||||
|
||||
class SomeFactor(TestingFactor):
|
||||
class SomeFactor(Factor):
|
||||
inputs = ()
|
||||
window_length = 0
|
||||
|
||||
|
||||
class SomeOtherFactor(TestingFactor):
|
||||
class SomeOtherFactor(Factor):
|
||||
inputs = ()
|
||||
window_length = 0
|
||||
|
||||
|
||||
@@ -28,21 +28,21 @@ from zipline.modelling.expression import (
|
||||
NumericalExpression,
|
||||
NUMEXPR_MATH_FUNCS,
|
||||
)
|
||||
from zipline.modelling.factor import TestingFactor
|
||||
from zipline.modelling.factor import Factor
|
||||
from zipline.utils.test_utils import check_arrays
|
||||
|
||||
|
||||
class F(TestingFactor):
|
||||
class F(Factor):
|
||||
inputs = ()
|
||||
window_length = 0
|
||||
|
||||
|
||||
class G(TestingFactor):
|
||||
class G(Factor):
|
||||
inputs = ()
|
||||
window_length = 0
|
||||
|
||||
|
||||
class H(TestingFactor):
|
||||
class H(Factor):
|
||||
inputs = ()
|
||||
window_length = 0
|
||||
|
||||
@@ -63,7 +63,7 @@ class NumericalExpressionTestCase(TestCase):
|
||||
self.mask = DataFrame(True, index=self.dates, columns=self.assets)
|
||||
|
||||
def check_output(self, expr, expected):
|
||||
result = expr.compute_from_arrays(
|
||||
result = expr._compute(
|
||||
[self.fake_raw_data[input_] for input_ in expr.inputs],
|
||||
self.mask,
|
||||
)
|
||||
|
||||
@@ -317,11 +317,7 @@ class SimpleFFCEngine(object):
|
||||
for loaded_term, adj_array in zip_longest(to_load, loaded):
|
||||
workspace[loaded_term] = adj_array
|
||||
else:
|
||||
if term.windowed:
|
||||
compute = term.compute_from_windows
|
||||
else:
|
||||
compute = term.compute_from_arrays
|
||||
workspace[term] = compute(
|
||||
workspace[term] = term._compute(
|
||||
self._inputs_for_term(term, workspace, extra_rows),
|
||||
base_mask_for_term,
|
||||
)
|
||||
|
||||
@@ -228,7 +228,7 @@ class NumericalExpression(Term):
|
||||
)
|
||||
return super(NumericalExpression, self)._validate()
|
||||
|
||||
def compute_from_arrays(self, arrays, mask):
|
||||
def _compute(self, arrays, mask):
|
||||
"""
|
||||
Compute our stored expression string with numexpr.
|
||||
"""
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
from .factor import (
|
||||
Factor,
|
||||
TestingFactor,
|
||||
CustomFactor,
|
||||
)
|
||||
from .latest import Latest
|
||||
from .technical import (
|
||||
MaxDrawdown,
|
||||
RSI,
|
||||
SimpleMovingAverage,
|
||||
VWAP,
|
||||
WeightedAverageValue,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'Factor',
|
||||
'TestingFactor',
|
||||
'CustomFactor',
|
||||
'Factor',
|
||||
'Latest',
|
||||
'MaxDrawdown',
|
||||
'RSI',
|
||||
'SimpleMovingAverage',
|
||||
'VWAP',
|
||||
'WeightedAverageValue',
|
||||
]
|
||||
|
||||
@@ -19,7 +19,6 @@ from zipline.modelling.term import (
|
||||
RequiredWindowLengthMixin,
|
||||
SingleInputMixin,
|
||||
Term,
|
||||
TestingTermMixin,
|
||||
)
|
||||
from zipline.modelling.expression import (
|
||||
BadBinaryOperator,
|
||||
@@ -393,7 +392,7 @@ class Rank(SingleInputMixin, Factor):
|
||||
)
|
||||
return super(Rank, self)._validate()
|
||||
|
||||
def compute_from_arrays(self, arrays, mask):
|
||||
def _compute(self, arrays, mask):
|
||||
"""
|
||||
For each row in the input, compute a like-shaped array of per-row
|
||||
ranks.
|
||||
@@ -441,11 +440,3 @@ class CustomFactor(RequiredWindowLengthMixin, CustomTermMixin, Factor):
|
||||
if self.dtype != float64:
|
||||
raise UnsupportedDataType(self.dtype)
|
||||
return super(CustomFactor, self)._validate()
|
||||
|
||||
|
||||
class TestingFactor(TestingTermMixin, Factor):
|
||||
"""
|
||||
Base class for testing engines that asserts all inputs are correctly
|
||||
shaped.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -16,7 +16,6 @@ from zipline.errors import (
|
||||
from zipline.modelling.term import (
|
||||
SingleInputMixin,
|
||||
Term,
|
||||
TestingTermMixin,
|
||||
)
|
||||
from zipline.modelling.expression import (
|
||||
BadBinaryOperator,
|
||||
@@ -124,11 +123,11 @@ class NumExprFilter(NumericalExpression, Filter):
|
||||
A Filter computed from a numexpr expression.
|
||||
"""
|
||||
|
||||
def compute_from_arrays(self, arrays, mask):
|
||||
def _compute(self, arrays, mask):
|
||||
"""
|
||||
Compute our result with numexpr, then apply `mask`.
|
||||
"""
|
||||
return super(NumExprFilter, self).compute_from_arrays(
|
||||
return super(NumExprFilter, self)._compute(
|
||||
arrays,
|
||||
mask,
|
||||
) & mask.values
|
||||
@@ -181,7 +180,7 @@ class PercentileFilter(SingleInputMixin, Filter):
|
||||
)
|
||||
return super(PercentileFilter, self)._validate()
|
||||
|
||||
def compute_from_arrays(self, arrays, mask):
|
||||
def _compute(self, arrays, mask):
|
||||
"""
|
||||
For each row in the input, compute a mask of all values falling between
|
||||
the given percentiles.
|
||||
@@ -261,21 +260,13 @@ class SequencedFilter(Filter):
|
||||
then,
|
||||
)
|
||||
|
||||
def compute_from_arrays(self, arrays, mask):
|
||||
def _compute(self, arrays, mask):
|
||||
"""
|
||||
Call our second filter on its inputs, masking out any inputs rejected
|
||||
by our first filter.
|
||||
"""
|
||||
first_result, then_inputs = arrays[0], arrays[1:]
|
||||
return self._then.compute_from_arrays(
|
||||
return self._then._compute(
|
||||
then_inputs,
|
||||
mask & first_result,
|
||||
)
|
||||
|
||||
|
||||
class TestingFilter(TestingTermMixin, Filter):
|
||||
"""
|
||||
Base class for testing engines that asserts all inputs are correctly
|
||||
shaped.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -172,17 +172,12 @@ class Term(object):
|
||||
"""
|
||||
return max(0, self.window_length - 1)
|
||||
|
||||
def compute_from_windows(self, windows, mask):
|
||||
def _compute(self, inputs, mask):
|
||||
"""
|
||||
Subclasses should implement this for computations requiring moving
|
||||
windows of continually-adjusting data.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
Subclasses should implement this to perform actual computation.
|
||||
|
||||
def compute_from_arrays(self, arrays, mask):
|
||||
"""
|
||||
Subclasses should implement this for computations that can be expressed
|
||||
directly as array computations.
|
||||
This is `_compute` rather than just `compute` because `compute` is
|
||||
reserved for user-supplied functions in CustomFactor.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -235,7 +230,7 @@ class CustomTermMixin(object):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def compute_from_windows(self, windows, mask):
|
||||
def _compute(self, windows, mask):
|
||||
"""
|
||||
Call the user's `compute` function on each window with a pre-built
|
||||
output array.
|
||||
@@ -256,37 +251,3 @@ class CustomTermMixin(object):
|
||||
)
|
||||
out[~mask.values] = nan
|
||||
return out
|
||||
|
||||
|
||||
class TestingTermMixin(object):
|
||||
"""
|
||||
Mixin for Term subclasses testing engines that asserts all inputs are
|
||||
correctly shaped.
|
||||
|
||||
Used by TestingTerm, TestingFilter, TestingClassifier, etc.
|
||||
"""
|
||||
def compute_from_windows(self, windows, mask):
|
||||
assert self.window_length > 0
|
||||
dates, assets = mask.index, mask.columns
|
||||
outbuf = empty(mask.shape, dtype=self.dtype)
|
||||
for idx, _ in enumerate(dates):
|
||||
result = self.from_windows(*(next(w) for w in windows))
|
||||
assert result.shape == (len(assets),)
|
||||
outbuf[idx] = result
|
||||
|
||||
for window in windows:
|
||||
try:
|
||||
next(window)
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("window %s was not exhausted" % window)
|
||||
return outbuf
|
||||
|
||||
def compute_from_arrays(self, arrays, mask):
|
||||
assert self.window_length == 0
|
||||
outbuf = empty(mask.shape, dtype=self.dtype)
|
||||
for array in arrays:
|
||||
assert array.shape == outbuf.shape
|
||||
outbuf[:] = self.from_arrays(*arrays)
|
||||
return outbuf
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
"""
|
||||
An immutable, lazily loaded value descriptor.
|
||||
"""
|
||||
|
||||
|
||||
from weakref import WeakKeyDictionary
|
||||
|
||||
|
||||
class lazyval(object):
|
||||
"""
|
||||
Decorator that marks that an attribute should not be computed until
|
||||
needed, and that the value should be memoized.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
>>> from zipline.utils.lazyval import lazyval
|
||||
>>> class C(object):
|
||||
... def __init__(self):
|
||||
... self.count = 0
|
||||
... @lazyval
|
||||
... def val(self):
|
||||
... self.count += 1
|
||||
... return "val"
|
||||
...
|
||||
>>> c = C()
|
||||
>>> c.count
|
||||
0
|
||||
>>> c.val, c.count
|
||||
('val', 1)
|
||||
>>> c.val, c.count
|
||||
('val', 1)
|
||||
"""
|
||||
def __init__(self, get):
|
||||
self._get = get
|
||||
self._cache = WeakKeyDictionary()
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if instance is None:
|
||||
return self
|
||||
|
||||
try:
|
||||
return self._cache[instance]
|
||||
except KeyError:
|
||||
self._cache[instance] = val = self._get(instance)
|
||||
return val
|
||||
Reference in New Issue
Block a user