Completed
Pull Request — master (#884)
by
unknown
01:22
created

zipline.pipeline.factors.Returns   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 14
Duplicated Lines 0 %
Metric Value
dl 0
loc 14
rs 10
wmc 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A compute() 0 2 1
1
"""
2
Technical Analysis Factors
3
--------------------------
4
"""
5
from bottleneck import (
6
    nanargmax,
7
    nanmax,
8
    nanmean,
9
    nansum,
10
)
11
from numpy import (
12
    abs,
13
    clip,
14
    diff,
15
    fmax,
16
    inf,
17
    isnan,
18
    NINF,
19
)
20
from numexpr import evaluate
21
22
from zipline.pipeline.data import USEquityPricing
23
from zipline.pipeline.term import SingleInputMixin
24
from zipline.utils.control_flow import ignore_nanwarnings
25
from .factor import CustomFactor
26
27
28
class Returns(CustomFactor):
29
    """
30
    Calculates the % change in close price over the given window_length
31
    (i.e. the returns for a given asset).
32
33
    **Default Inputs**: [USEquityPricing.close]
34
35
    **Default Window Length**: 30
36
    """
37
    window_length = 30
38
    inputs = [USEquityPricing.close]
39
40
    def compute(self, today, assets, out, close):
41
        out[:] = (close[-1] - close[0]) / close[0]
42
43
44
class RSI(CustomFactor, SingleInputMixin):
45
    """
46
    Relative Strength Index
47
48
    **Default Inputs**: [USEquityPricing.close]
49
50
    **Default Window Length**: 15
51
    """
52
    window_length = 15
53
    inputs = (USEquityPricing.close,)
54
55
    def compute(self, today, assets, out, closes):
56
        diffs = diff(closes, axis=0)
57
        ups = nanmean(clip(diffs, 0, inf), axis=0)
58
        downs = abs(nanmean(clip(diffs, -inf, 0), axis=0))
59
        return evaluate(
60
            "100 - (100 / (1 + (ups / downs)))",
61
            local_dict={'ups': ups, 'downs': downs},
62
            global_dict={},
63
            out=out,
64
        )
65
66
67
class SimpleMovingAverage(CustomFactor, SingleInputMixin):
68
    """
69
    Average Value of an arbitrary column
70
71
    **Default Inputs**: None
72
73
    **Default Window Length**: None
74
    """
75
    # numpy's nan functions throw warnings when passed an array containing only
76
    # nans, but they still returns the desired value (nan), so we ignore the
77
    # warning.
78
    ctx = ignore_nanwarnings()
79
80
    def compute(self, today, assets, out, data):
81
        out[:] = nanmean(data, axis=0)
82
83
84
class WeightedAverageValue(CustomFactor):
85
    """
86
    Helper for VWAP-like computations.
87
88
    **Default Inputs:** None
89
90
    **Default Window Length:** None
91
    """
92
    def compute(self, today, assets, out, base, weight):
93
        out[:] = nansum(base * weight, axis=0) / nansum(weight, axis=0)
94
95
96
class VWAP(WeightedAverageValue):
97
    """
98
    Volume Weighted Average Price
99
100
    **Default Inputs:** [USEquityPricing.close, USEquityPricing.volume]
101
102
    **Default Window Length:** None
103
    """
104
    inputs = (USEquityPricing.close, USEquityPricing.volume)
105
106
107
class MaxDrawdown(CustomFactor, SingleInputMixin):
108
    """
109
    Max Drawdown
110
111
    **Default Inputs:** None
112
113
    **Default Window Length:** None
114
    """
115
    ctx = ignore_nanwarnings()
116
117
    def compute(self, today, assets, out, data):
118
        drawdowns = fmax.accumulate(data, axis=0) - data
119
        drawdowns[isnan(drawdowns)] = NINF
120
        drawdown_ends = nanargmax(drawdowns, axis=0)
121
122
        # TODO: Accelerate this loop in Cython or Numba.
123
        for i, end in enumerate(drawdown_ends):
124
            peak = nanmax(data[:end + 1, i])
125
            out[i] = (peak - data[end, i]) / data[end, i]
126