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
24import math
25
26from ..observer import Observer
27
28
29class BuySell(Observer):
30    '''
31    This observer keeps track of the individual buy/sell orders (individual
32    executions) and will plot them on the chart along the data around the
33    execution price level
34
35    Params:
36      - ``barplot`` (default: ``False``) Plot buy signals below the minimum and
37        sell signals above the maximum.
38
39        If ``False`` it will plot on the average price of executions during a
40        bar
41
42      - ``bardist`` (default: ``0.015`` 1.5%) Distance to max/min when
43        ``barplot`` is ``True``
44    '''
45    lines = ('buy', 'sell',)
46
47    plotinfo = dict(plot=True, subplot=False, plotlinelabels=True)
48    plotlines = dict(
49        buy=dict(marker='^', markersize=8.0, color='lime',
50                 fillstyle='full', ls=''),
51        sell=dict(marker='v', markersize=8.0, color='red',
52                  fillstyle='full', ls='')
53    )
54
55    params = (
56        ('barplot', False),  # plot above/below max/min for clarity in bar plot
57        ('bardist', 0.015),  # distance to max/min in absolute perc
58    )
59
60    def next(self):
61        buy = list()
62        sell = list()
63
64        for order in self._owner._orderspending:
65            if order.data is not self.data or not order.executed.size:
66                continue
67
68            if order.isbuy():
69                buy.append(order.executed.price)
70            else:
71                sell.append(order.executed.price)
72
73        # Take into account replay ... something could already be in there
74        # Write down the average buy/sell price
75
76        # BUY
77        curbuy = self.lines.buy[0]
78        if curbuy != curbuy:  # NaN
79            curbuy = 0.0
80            self.curbuylen = curbuylen = 0
81        else:
82            curbuylen = self.curbuylen
83
84        buyops = (curbuy + math.fsum(buy))
85        buylen = curbuylen + len(buy)
86
87        value = buyops / float(buylen or 'NaN')
88        if not self.p.barplot:
89            self.lines.buy[0] = value
90        elif value == value:  # Not NaN
91            pbuy = self.data.low[0] * (1 - self.p.bardist)
92            self.lines.buy[0] = pbuy
93
94        # Update buylen values
95        curbuy = buyops
96        self.curbuylen = buylen
97
98        # SELL
99        cursell = self.lines.sell[0]
100        if cursell != cursell:  # NaN
101            cursell = 0.0
102            self.curselllen = curselllen = 0
103        else:
104            curselllen = self.curselllen
105
106        sellops = (cursell + math.fsum(sell))
107        selllen = curselllen + len(sell)
108
109        value = sellops / float(selllen or 'NaN')
110        if not self.p.barplot:
111            self.lines.sell[0] = value
112        elif value == value:  # Not NaN
113            psell = self.data.high[0] * (1 + self.p.bardist)
114            self.lines.sell[0] = psell
115
116        # Update selllen values
117        cursell = sellops
118        self.curselllen = selllen
119