Passed
Push — master ( 385e45...34c628 )
by Alec
02:58
created

DataOHLCV::getLastClose()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
/**
3
 * User: alec
4
 * Date: 31.10.18
5
 * Time: 15:20
6
 */
7
declare(strict_types=1);
8
9
namespace AlecRabbit;
10
11
use BCMathExtended\BC;
12
13
class DataOHLCV
14
{
15
    protected const DEFAULT_PERIOD_MULTIPLIER = 3;
16
    protected const MAX_PERIOD_MULTIPLIER = 100;
17
    protected const DEFAULT_SIZE = 500;
18
    protected const MAX_SIZE = 1440;
19
    protected const MIN_SIZE = 10;
20
    protected const RESOLUTIONS = RESOLUTIONS;
21
22
    /** @var array */
23
    protected $current;
24
    protected $timestamps = [];
25
    protected $opens = [];
26
    protected $highs = [];
27
    protected $lows = [];
28
    protected $closes = [];
29
    protected $volumes = [];
30
    protected $proxies = [];
31
32
    /** @var int */
33
    private $size;
34
    /** @var int */
35
    private $coefficient;
36
    /** @var string */
37
    private $pair;
38
39
    /**
40
     * DataOHLCV constructor.
41
     * @param string $pair
42
     * @param integer $size
43
     * @param int $coefficient
44
     */
45 98
    public function __construct(string $pair, ?int $size = null, int $coefficient = 1)
46
    {
47 98
        $this->size =
48 98
            (int)bounds(
49 98
                $size ?? static::DEFAULT_SIZE,
50 98
                static::MIN_SIZE,
51 98
                static::MAX_SIZE
52
            );
53 98
        $this->pair = $pair;
54 98
        $this->coefficient = $coefficient;
55 98
    }
56
57
    /**
58
     * @return int
59
     */
60 9
    public function getSize(): int
61
    {
62 9
        return $this->size;
63
    }
64
65 1
    public function hasPeriods(int $resolution, int $periods, int $multiplier = null): bool
66
    {
67
        $multiplier =
68 1
            (int)bounds($multiplier ?? self::DEFAULT_PERIOD_MULTIPLIER, 1, self::MAX_PERIOD_MULTIPLIER);
69
70
        return
71 1
            isset($this->timestamps[$resolution])
72 1
            && (\count($this->timestamps[$resolution]) >= ($periods * $multiplier));
73
    }
74
75 14
    public function addTrade(int $timestamp, string $side, float $price, float $amount): void
0 ignored issues
show
Unused Code introduced by
The parameter $side is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

75
    public function addTrade(int $timestamp, /** @scrutinizer ignore-unused */ string $side, float $price, float $amount): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
76
    {
77 14
        $this->addOHLCV($timestamp, $price, $price, $price, $price, $amount);
78 14
    }
79
80 14
    public function addOHLCV(
81
        int $timestamp,
82
        float $open,
83
        float $high,
84
        float $low,
85
        float $close,
86
        float $volume,
87
        int $resolution = RESOLUTION_01MIN
88
    ): void {
89 14
        $ts = base_timestamp($timestamp, $resolution);
90 14
        if (isset($this->current[$resolution])) {
91 14
            if ($ts > $this->current[$resolution]['timestamp']) {
92 14
                $this->updateLast($resolution);
93
94 14
                $this->setCurrent($resolution, $ts, $open, $high, $low, $close, $volume);
95 14
            } elseif ($ts === $this->current[$resolution]['timestamp']) {
96 14
                if ($high > $this->current[$resolution]['high']) {
97 12
                    $this->current[$resolution]['high'] = $high;
98
                }
99 14
                if ($low < $this->current[$resolution]['low']) {
100 13
                    $this->current[$resolution]['low'] = $low;
101
                }
102
103 14
                $this->current[$resolution]['close'] = $close;
104 14
                $this->current[$resolution]['volume'] =
105 14
                    (float)BC::add((string)$this->current[$resolution]['volume'], (string)$volume, NORMAL_SCALE);
106 1
            } elseif ($ts < $this->current[$resolution]['timestamp']) {
107 1
                throw new \RuntimeException(
108
                    'Incoming data are in unsorted order. Current timestamp is greater then incoming data\'s.' .
109 14
                    ' (' . $ts . ' < ' . $this->current[$resolution]['timestamp'] . ')'
110
                );
111
            }
112
        } else {
113 14
            $this->setCurrent($resolution, $ts, $open, $high, $low, $close, $volume);
114
        }
115
116 14
        $this->trim($resolution);
117 14
        if ($nextResolution = $this->nextResolution($resolution)) {
118 14
            $this->addOHLCV($timestamp, $open, $high, $low, $close, $volume, $nextResolution);
119
        }
120 14
    }
121
122 14
    private function trim(int $resolution): void
123
    {
124 14
        if (isset($this->timestamps[$resolution]) && (\count($this->timestamps[$resolution]) > $this->size)) {
125 1
            unset_first($this->timestamps[$resolution]);
126 1
            unset_first($this->opens[$resolution]);
127 1
            unset_first($this->highs[$resolution]);
128 1
            unset_first($this->lows[$resolution]);
129 1
            unset_first($this->closes[$resolution]);
130 1
            unset_first($this->volumes[$resolution]);
131
        }
132 14
    }
133
134 25
    private function nextResolution($resolution): ?int
135
    {
136 25
        $key = array_search($resolution, static::RESOLUTIONS, true);
137 25
        if ($key !== false && array_key_exists(++$key, static::RESOLUTIONS)) {
138 24
            return static::RESOLUTIONS[$key];
139
        }
140 15
        return null;
141
    }
142
143
    /**
144
     * @param int $resolution
145
     * @return array
146
     */
147 11
    public function getTimestamps(int $resolution): array
148
    {
149
        return
150 11
            $this->timestamps[$resolution] ?? [];
151
    }
152
153
    /**
154
     * @param int $resolution
155
     * @param bool $useCoefficient
156
     * @return array
157
     */
158 1
    public function getOpens(int $resolution, bool $useCoefficient = false): array
159
    {
160
        return
161 1
            $this->mulArr(
162 1
                $this->opens[$resolution] ?? [],
163 1
                $useCoefficient
164
            );
165
    }
166
167 4
    private function mulArr(array $values, bool $useCoefficient): array
168
    {
169 4
        if ($useCoefficient && $this->coefficient !== 1) {
170 1
            $values = array_map(
171
                function ($v) {
172
                    return
173 1
                        $v * $this->coefficient;
174 1
                },
175 1
                $values
176
            );
177
        }
178
179 4
        return $values;
180
    }
181
182
    /**
183
     * @param int $resolution
184
     * @param bool $useCoefficient
185
     * @return array
186
     */
187 1
    public function getHighs(int $resolution, bool $useCoefficient = false): array
188
    {
189
        return
190 1
            $this->mulArr(
191 1
                $this->highs[$resolution] ?? [],
192 1
                $useCoefficient
193
            );
194
    }
195
196
    /**
197
     * @param int $resolution
198
     * @param bool $useCoefficient
199
     * @return null|float
200
     */
201 1
    public function getLastHigh(int $resolution, bool $useCoefficient = false): ?float
202
    {
203
        return
204 1
            $this->lastElement($this->highs[$resolution] ?? [], $useCoefficient);
205
    }
206
207
    /**
208
     * @param array $element
209
     * @param bool $useCoefficient
210
     * @return null|float
211
     */
212 1
    private function lastElement(array $element, bool $useCoefficient = false): ?float
213
    {
214 1
        if (false !== $lastElement = end($element)) {
215
            return
216 1
                $this->mul(
217 1
                    $lastElement,
218 1
                    $useCoefficient
219
                );
220
        }
221 1
        return null;
222
    }
223
224 6
    private function mul(float $value, bool $useCoefficient): float
225
    {
226 6
        if ($useCoefficient && $this->coefficient !== 1) {
227 2
            $value *= $this->coefficient;
228
        }
229 6
        return $value;
230
    }
231
232
    /**
233
     * @param int $resolution
234
     * @param bool $useCoefficient
235
     * @return array
236
     */
237 1
    public function getLows(int $resolution, bool $useCoefficient = false): array
238
    {
239
        return
240 1
            $this->mulArr(
241 1
                $this->lows[$resolution] ?? [],
242 1
                $useCoefficient
243
            );
244
    }
245
246
    /**
247
     * @param int $resolution
248
     * @param bool $useCoefficient
249
     * @return null|float
250
     */
251 1
    public function getLastLow(int $resolution, bool $useCoefficient = false): ?float
252
    {
253
        return
254 1
            $this->lastElement($this->lows[$resolution] ?? [], $useCoefficient);
255
    }
256
257
    /**
258
     * @param int $resolution
259
     * @param bool $useCoefficient
260
     * @return array
261
     */
262 1
    public function getCloses(int $resolution, bool $useCoefficient = false): array
263
    {
264
        return
265 1
            $this->mulArr(
266 1
                $this->closes[$resolution] ?? [],
267 1
                $useCoefficient
268
            );
269
    }
270
271
    /**
272
     * @param int $resolution
273
     * @param bool $useCoefficient
274
     * @return null|float
275
     */
276 1
    public function getLastClose(int $resolution, bool $useCoefficient = false): ?float
277
    {
278
        return
279 1
            $this->lastElement($this->closes[$resolution] ?? [], $useCoefficient);
280
    }
281
282
    /**
283
     * @param int $resolution
284
     * @return array
285
     */
286 1
    public function getVolumes(int $resolution): array
287
    {
288
        return
289 1
            $this->volumes[$resolution] ?? [];
290
    }
291
292
    /**
293
     * @codeCoverageIgnore
294
     */
295
    public function dump(): void
296
    {
297
        if (\defined('APP_DEBUG') && APP_DEBUG) {
298
            $result = [];
299
            $pair = $this->getPair();
300
            foreach (static::RESOLUTIONS as $resolution) {
301
                $count = \count($this->timestamps[$resolution] ?? []);
302
                $result[] =
303
                    sprintf(
304
                        '%s [%s] %s %s %s %s %s %s %s',
305
                        $this->current[$resolution]['timestamp'],
306
                        RESOLUTION_ALIASES[$resolution],
307
                        $count,
308
                        $pair,
309
                        $this->current[$resolution]['opens'],
310
                        $this->current[$resolution]['high'],
311
                        $this->current[$resolution]['low'],
312
                        $this->current[$resolution]['close'],
313
                        $this->current[$resolution]['volume']
314
                    );
315
            }
316
            dump($result);
317
        }
318
    }
319
320
    /**
321
     * @return string
322
     */
323 57
    public function getPair(): string
324
    {
325 57
        return $this->pair;
326
    }
327
328 14
    private function setCurrent(
329
        int $resolution,
330
        int $timestamp,
331
        float $open,
332
        float $high,
333
        float $low,
334
        float $close,
335
        float $volume
336
    ): void {
337 14
        $this->current[$resolution]['timestamp'] = $timestamp;
338 14
        $this->current[$resolution]['opens'] = $open;
339 14
        $this->current[$resolution]['high'] = $high;
340 14
        $this->current[$resolution]['low'] = $low;
341 14
        $this->current[$resolution]['close'] = $close;
342 14
        $this->current[$resolution]['volume'] = $volume;
343 14
    }
344
345 14
    private function updateLast(int $resolution): void
346
    {
347 14
        $this->timestamps[$resolution][] = $this->current[$resolution]['timestamp'];
348 14
        $this->opens[$resolution][] = $this->current[$resolution]['opens'];
349 14
        $this->highs[$resolution][] = $this->current[$resolution]['high'];
350 14
        $this->lows[$resolution][] = $this->current[$resolution]['low'];
351 14
        $this->closes[$resolution][] = $this->current[$resolution]['close'];
352 14
        $this->volumes[$resolution][] = $this->current[$resolution]['volume'];
353 14
    }
354
}
355