Completed
Push — master ( ebb4fb...323695 )
by
unknown
01:25
created

_validate()   A

Complexity

Conditions 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 4
rs 10
1
"""
2
Mixins classes for use with Filters and Factors.
3
"""
4
from numpy import full_like
5
from zipline.errors import WindowLengthNotPositive
6
7
from .term import NotSpecified
8
9
10
class PositiveWindowLengthMixin(object):
11
    """
12
    Validation mixin enforcing that a Term gets a positive WindowLength
13
    """
14
    def _validate(self):
15
        if not self.windowed:
16
            raise WindowLengthNotPositive(window_length=self.window_length)
17
        return super(PositiveWindowLengthMixin, self)._validate()
18
19
20
class SingleInputMixin(object):
21
    """
22
    Validation mixin enforcing that a Term gets a length-1 inputs list.
23
    """
24
    def _validate(self):
25
        num_inputs = len(self.inputs)
26
        if num_inputs != 1:
27
            raise ValueError(
28
                "{typename} expects only one input, "
29
                "but received {num_inputs} instead.".format(
30
                    typename=type(self).__name__,
31
                    num_inputs=num_inputs
32
                )
33
            )
34
        return super(SingleInputMixin, self)._validate()
35
36
37
class CustomTermMixin(object):
38
    """
39
    Mixin for user-defined rolling-window Terms.
40
41
    Implements `_compute` in terms of a user-defined `compute` function, which
42
    is mapped over the input windows.
43
44
    Used by CustomFactor, CustomFilter, CustomClassifier, etc.
45
    """
46
    def __new__(cls,
47
                inputs=NotSpecified,
48
                window_length=NotSpecified,
49
                dtype=NotSpecified,
50
                **kwargs):
51
52
        unexpected_keys = set(kwargs) - set(cls.params)
53
        if unexpected_keys:
54
            raise TypeError(
55
                "{termname} received unexpected keyword "
56
                "arguments {unexpected}".format(
57
                    termname=cls.__name__,
58
                    unexpected={k: kwargs[k] for k in unexpected_keys},
59
                )
60
            )
61
62
        return super(CustomTermMixin, cls).__new__(
63
            cls,
64
            inputs=inputs,
65
            window_length=window_length,
66
            dtype=dtype,
67
            **kwargs
68
        )
69
70
    def compute(self, today, assets, out, *arrays):
71
        """
72
        Override this method with a function that writes a value into `out`.
73
        """
74
        raise NotImplementedError()
75
76
    def _compute(self, windows, dates, assets, mask):
77
        """
78
        Call the user's `compute` function on each window with a pre-built
79
        output array.
80
        """
81
        # TODO: Make mask available to user's `compute`.
82
        compute = self.compute
83
        missing_value = self.missing_value
84
        params = self.params
85
        out = full_like(mask, missing_value, dtype=self.dtype)
86
        with self.ctx:
87
            # TODO: Consider pre-filtering columns that are all-nan at each
88
            # time-step?
89
            for idx, date in enumerate(dates):
90
                compute(
91
                    date,
92
                    assets,
93
                    out[idx],
94
                    *(next(w) for w in windows),
95
                    **params
96
                )
97
        out[~mask] = missing_value
98
        return out
99
100
    def short_repr(self):
101
        return type(self).__name__ + '(%d)' % self.window_length
102