Passed
Push — master ( 85bfe7...8b81ae )
by Alec
03:15
created

VolumeCounter::getVolumeArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 12
ccs 9
cts 9
cp 1
crap 2
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * User: alec
4
 * Date: 19.11.18
5
 * Time: 17:27
6
 */
7
declare(strict_types=1);
8
9
namespace AlecRabbit\Counters;
10
11
use AlecRabbit\Money\CalculatorFactory as Factory;
12
use AlecRabbit\Money\Contracts\CalculatorInterface;
13
use AlecRabbit\Structures\Trade;
14
15
class VolumeCounter extends EventsCounter
16
{
17
    protected const DOMAINS = [
18
        STR_TOTAL,
19
        STR_SELL,
20
        STR_BUY,
21
        STR_VP_TOTAL,
22
        STR_VP_SELL,
23
        STR_VP_BUY,
24
        STR_P_SUM_TOTAL,
25
        STR_P_SUM_SELL,
26
        STR_P_SUM_BUY,
27
    ];
28
29
    /** @var CalculatorInterface */
30
    protected $calculator;
31
32
    /**
33
     * @inheritDoc
34
     */
35 4
    public function __construct(?array $periods = null)
36
    {
37 4
        parent::__construct($periods);
38 4
        $this->calculator = Factory::getCalculator();
39 4
    }
40
41 3
    public function addTrade(Trade $trade): void
42
    {
43 3
        $baseTimes = $this->getBaseTimes($trade->timestamp);
44 3
        if ($this->precisionMode) {
45 1
            $volumePrice = $this->calculator->multiply((string)$trade->amount, $trade->price);
46
        } else {
47 2
            $volumePrice = $trade->amount * $trade->price;
48
        }
49 3
        foreach ($baseTimes as $period => $timestamp) {
50 3
            $this->processPart(STR_TOTAL, $period, $timestamp, $volumePrice, $trade);
51 3
            $this->processPart(
52 3
                $trade->side === T_SELL ? STR_SELL : STR_BUY,
53 3
                $period,
54 3
                $timestamp,
55 3
                $volumePrice,
56 3
                $trade
57
            );
58 3
            $this->trim($period);
59
        }
60 3
    }
61
62
    /**
63
     * @param string $subdomain
64
     * @param int $period
65
     * @param int $timestamp
66
     * @param float|string $volumePrice
67
     * @param Trade $trade
68
     */
69 3
    protected function processPart(
70
        string $subdomain,
71
        int $period,
72
        int $timestamp,
73
        $volumePrice,
74
        Trade $trade
75
    ): void {
76 3
        $this->data[STR_VOLUMES][STR_VP . $subdomain][$period][$timestamp] =
77 3
            $this->data[STR_VOLUMES][STR_VP . $subdomain][$period][$timestamp] ?? 0;
78
79 3
        $this->data[STR_VOLUMES][STR_P_SUM . $subdomain][$period][$timestamp] =
80 3
            $this->data[STR_VOLUMES][STR_P_SUM . $subdomain][$period][$timestamp] ?? 0;
81
82 3
        $this->data[STR_VOLUMES][$subdomain][$period][$timestamp] =
83 3
            $this->data[STR_VOLUMES][$subdomain][$period][$timestamp] ?? 0;
84
85 3
        $this->data[STR_EVENTS][$subdomain][$period][$timestamp] =
86 3
            $this->data[STR_EVENTS][$subdomain][$period][$timestamp] ?? 0;
87
88 3
        $this->data[STR_EVENTS][$subdomain][$period][$timestamp]++;
89
90 3
        if ($this->precisionMode) {
91 1
            $this->data[STR_VOLUMES][STR_VP . $subdomain][$period][$timestamp] =
92
                (float)
93 1
                $this->calculator->add(
94 1
                    $this->data[STR_VOLUMES][STR_VP . $subdomain][$period][$timestamp],
95 1
                    $volumePrice
96
                );
97 1
            $this->data[STR_VOLUMES][STR_P_SUM . $subdomain][$period][$timestamp] =
98
                (float)
99 1
                $this->calculator->add(
100 1
                    $this->data[STR_VOLUMES][STR_P_SUM . $subdomain][$period][$timestamp],
101 1
                    $trade->price
102
                );
103 1
            $this->data[STR_VOLUMES][$subdomain][$period][$timestamp] =
104
                (float)
105 1
                $this->calculator->add(
106 1
                    $this->data[STR_VOLUMES][$subdomain][$period][$timestamp],
107 1
                    $trade->amount
108
                );
109
        } else {
110 2
            $this->data[STR_VOLUMES][STR_VP . $subdomain][$period][$timestamp] += $volumePrice;
111 2
            $this->data[STR_VOLUMES][STR_P_SUM . $subdomain][$period][$timestamp] += $trade->price;
112 2
            $this->data[STR_VOLUMES][$subdomain][$period][$timestamp] += $trade->amount;
113
        }
114 3
    }
115
116
    /**
117
     * @param int $period
118
     */
119 3
    protected function trim(int $period): void
120
    {
121 3
        $threshold = $this->getThreshold($period);
122 3
        foreach (static::DOMAINS as $domain) {
123 3
            if (null !== ($timestamp = array_key_first($this->data[STR_EVENTS][$domain][$period] ?? []))
124 3
                && ($timestamp <= $threshold)) {
125 2
                unset($this->data[STR_EVENTS][$domain][$period][$timestamp]);
126
            }
127 3
            if (null !== ($timestamp = array_key_first($this->data[STR_VOLUMES][$domain][$period] ?? []))
128 3
                && ($timestamp <= $threshold)) {
129 3
                unset($this->data[STR_VOLUMES][$domain][$period][$timestamp]);
130
            }
131
        }
132 3
    }
133
134
    /**
135
     * @param bool|null $reset
136
     * @return array
137
     */
138 4
    public function getVolumeArray(?bool $reset = null): array
139
    {
140 4
        $volumes = $this->calcVolumes();
141 4
        $events = $this->getEventsArray();
142 4
        $averages = $this->calcAverages($volumes, $events);
143 4
        if ($reset) {
144 1
            $this->reset();
145
        }
146
        return [
147 4
            STR_VOLUMES => $volumes,
148 4
            STR_AVERAGES => $averages,
149 4
            STR_EVENTS => $events
150
        ];
151
    }
152
153
    /**
154
     * @inheritDoc
155
     */
156 4
    public function getEventsArray(?bool $reset = null): array
157
    {
158 4
        $events = [];
159 4
        foreach (static::DOMAINS as $domain) {
160 4
            foreach ($this->periods as $period => $groupBy) {
161 4
                if (0 < ($sum = array_sum($this->data[STR_EVENTS][$domain][$period] ?? []))) {
162 4
                    $events[$domain][$period] = $sum;
163
                }
164
            }
165
        }
166 4
        if ($reset) {
167 1
            $this->reset();
168
        }
169 4
        return $events;
170
    }
171
172
    /**
173
     * @param array $volumes
174
     * @param array $events
175
     * @return array
176
     */
177 4
    private function calcAverages(array $volumes, array $events): array
178
    {
179 4
        $averages = [];
180 4
        foreach ($this->periods as $period => $groupBy) {
181 4
            if (isset($volumes[STR_VP_TOTAL][$period], $volumes[STR_TOTAL][$period])) {
182 3
                $averages[STR_VWAP_TOTAL][$period] = $volumes[STR_VP_TOTAL][$period] / $volumes[STR_TOTAL][$period];
183
            }
184 4
            if (isset($volumes[STR_VP_SELL][$period], $volumes[STR_SELL][$period])) {
185 3
                $averages[STR_VWAP_SELL][$period] = $volumes[STR_VP_SELL][$period] / $volumes[STR_SELL][$period];
186
            }
187 4
            if (isset($volumes[STR_VP_BUY][$period], $volumes[STR_BUY][$period])) {
188 3
                $averages[STR_VWAP_BUY][$period] = $volumes[STR_VP_BUY][$period] / $volumes[STR_BUY][$period];
189
            }
190 4
            if (isset($volumes[STR_P_SUM_TOTAL][$period], $events[STR_TOTAL][$period])) {
191 3
                $averages[STR_AVG_PRICE_TOTAL][$period] =
192 3
                    $volumes[STR_P_SUM_TOTAL][$period] / $events[STR_TOTAL][$period];
193
            }
194 4
            if (isset($volumes[STR_P_SUM_SELL][$period], $events[STR_SELL][$period])) {
195 3
                $averages[STR_AVG_PRICE_SELL][$period] =
196 3
                    $volumes[STR_P_SUM_SELL][$period] / $events[STR_SELL][$period];
197
            }
198 4
            if (isset($volumes[STR_P_SUM_BUY][$period], $events[STR_BUY][$period])) {
199 3
                $averages[STR_AVG_PRICE_BUY][$period] =
200 4
                    $volumes[STR_P_SUM_BUY][$period] / $events[STR_BUY][$period];
201
            }
202
        }
203 4
        return $averages;
204
    }
205
206
    /**
207
     * @return array
208
     */
209 4
    private function calcVolumes(): array
210
    {
211 4
        $volumes = [];
212 4
        foreach (static::DOMAINS as $domain) {
213 4
            foreach ($this->periods as $period => $groupBy) {
214 4
                if (0 < ($sum = array_sum($this->data[STR_VOLUMES][$domain][$period] ?? []))) {
215 4
                    $volumes[$domain][$period] = $sum;
216
                }
217
            }
218
        }
219 4
        return $volumes;
220
    }
221
}
222