1#!/usr/bin/env python
2# -*- coding: utf-8; py-indent-offset:4 -*-
3###############################################################################
4#
5# Copyright (C) 2015, 2016, 2017 Daniel Rodriguez
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19#
20###############################################################################
21from __future__ import (absolute_import, division, print_function,
22                        unicode_literals)
23
24from . import Indicator, And
25
26
27class NonZeroDifference(Indicator):
28    '''
29    Keeps track of the difference between two data inputs skipping, memorizing
30    the last non zero value if the current difference is zero
31
32    Formula:
33      - diff = data - data1
34      - nzd = diff if diff else diff(-1)
35    '''
36    _mindatas = 2  # requires two (2) data sources
37    alias = ('NZD',)
38    lines = ('nzd',)
39
40    def nextstart(self):
41        self.l.nzd[0] = self.data0[0] - self.data1[0]  # seed value
42
43    def next(self):
44        d = self.data0[0] - self.data1[0]
45        self.l.nzd[0] = d if d else self.l.nzd[-1]
46
47    def oncestart(self, start, end):
48        self.line.array[start] = (
49            self.data0.array[start] - self.data1.array[start])
50
51    def once(self, start, end):
52        d0array = self.data0.array
53        d1array = self.data1.array
54        larray = self.line.array
55
56        prev = larray[start - 1]
57        for i in range(start, end):
58            d = d0array[i] - d1array[i]
59            larray[i] = prev = d if d else prev
60
61
62class _CrossBase(Indicator):
63    _mindatas = 2
64
65    lines = ('cross',)
66
67    plotinfo = dict(plotymargin=0.05, plotyhlines=[0.0, 1.0])
68
69    def __init__(self):
70        nzd = NonZeroDifference(self.data0, self.data1)
71
72        if self._crossup:
73            before = nzd(-1) < 0.0  # data0 was below or at 0
74            after = self.data0 > self.data1
75        else:
76            before = nzd(-1) > 0.0  # data0 was above or at 0
77            after = self.data0 < self.data1
78
79        self.lines.cross = And(before, after)
80
81
82class CrossUp(_CrossBase):
83    '''
84    This indicator gives a signal if the 1st provided data crosses over the 2nd
85    indicator upwards
86
87    It does need to look into the current time index (0) and the previous time
88    index (-1) of both the 1st and 2nd data
89
90    Formula:
91      - diff = data - data1
92      - upcross =  last_non_zero_diff < 0 and data0(0) > data1(0)
93    '''
94    _crossup = True
95
96
97class CrossDown(_CrossBase):
98    '''
99    This indicator gives a signal if the 1st provided data crosses over the 2nd
100    indicator upwards
101
102    It does need to look into the current time index (0) and the previous time
103    index (-1) of both the 1st and 2nd data
104
105    Formula:
106      - diff = data - data1
107      - downcross = last_non_zero_diff > 0 and data0(0) < data1(0)
108    '''
109    _crossup = False
110
111
112class CrossOver(Indicator):
113    '''
114    This indicator gives a signal if the provided datas (2) cross up or down.
115
116      - 1.0 if the 1st data crosses the 2nd data upwards
117      - -1.0 if the 1st data crosses the 2nd data downwards
118
119    It does need to look into the current time index (0) and the previous time
120    index (-1) of both the 1t and 2nd data
121
122    Formula:
123      - diff = data - data1
124      - upcross =  last_non_zero_diff < 0 and data0(0) > data1(0)
125      - downcross = last_non_zero_diff > 0 and data0(0) < data1(0)
126      - crossover = upcross - downcross
127    '''
128    _mindatas = 2
129
130    lines = ('crossover',)
131
132    plotinfo = dict(plotymargin=0.05, plotyhlines=[-1.0, 1.0])
133
134    def __init__(self):
135        upcross = CrossUp(self.data, self.data1)
136        downcross = CrossDown(self.data, self.data1)
137
138        self.lines.crossover = upcross - downcross
139