DataOHLCV::addOHLCV()   A
last analyzed

Complexity

Conditions 6
Paths 9

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 30
ccs 15
cts 15
cp 1
rs 9.1111
c 0
b 0
f 0
cc 6
nc 9
nop 8
crap 6

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * Date: 31.10.18
4
 * Time: 15:20
5
 */
6
declare(strict_types=1);
7
8
namespace AlecRabbit;
9
10
use function AlecRabbit\Helpers\bounds;
11
use AlecRabbit\Structures\Trade;
12
use BCMathExtended\BC;
13
14
class DataOHLCV
15
{
16
    protected const DEFAULT_PERIOD_MULTIPLIER = 3;
17
    protected const MAX_PERIOD_MULTIPLIER = 100;
18
    protected const DEFAULT_SIZE = 500;
19
    protected const MAX_SIZE = 1440;
20
    protected const MIN_SIZE = 10;
21
    protected const RESOLUTIONS = RESOLUTIONS;
22
    /** @var array */
23
    protected $current = [];
24
    /** @var array */
25
    protected $timestamps = [];
26
    /** @var array */
27
    protected $opens = [];
28
    /** @var array */
29
    protected $highs = [];
30
    /** @var array */
31
    protected $lows = [];
32
    /** @var array */
33
    protected $closes = [];
34
    /** @var array */
35
    protected $volumes = [];
36
    /** @var array */
37
    protected $proxies = [];
38
    /** @var int */
39
    private $size;
40
    /** @var int */
41
    private $coefficient;
42
    /** @var string */
43
    private $pair;
44
45
    /**
46
     * DataOHLCV constructor.
47
     * @param string $pair
48
     * @param int|null $size
49
     * @param int|null $coefficient
50
     */
51 97
    public function __construct(
52
        string $pair,
53
        ?int $size = null,
54
        ?int $coefficient = null
55
    ) {
56 97
        $this->size =
57 97
            (int)bounds(
58 97
                $size ?? static::DEFAULT_SIZE,
59 97
                static::MIN_SIZE,
60 97
                static::MAX_SIZE
61
            );
62 97
        $this->pair = $pair;
63 97
        $this->coefficient = $coefficient ?? 1;
64 97
    }
65
66
    /**
67
     * @return int
68
     */
69 9
    public function getSize(): int
70
    {
71 9
        return $this->size;
72
    }
73
74 1
    public function hasPeriods(int $resolution, int $periods, int $multiplier = null): bool
75
    {
76
        $multiplier =
77 1
            (int)bounds($multiplier ?? self::DEFAULT_PERIOD_MULTIPLIER, 1, self::MAX_PERIOD_MULTIPLIER);
78
79
        return
80 1
            isset($this->timestamps[$resolution])
81 1
            && (\count($this->timestamps[$resolution]) >= ($periods * $multiplier));
82
    }
83
84 13
    public function addTrade(Trade $trade): void
85
    {
86 13
        $this->addOHLCV(
87 13
            $trade->timestamp,
88 13
            $trade->price,
89 13
            $trade->price,
90 13
            $trade->price,
91 13
            $trade->price,
92 13
            $trade->amount,
93 13
            $trade->side
94
        );
95 13
    }
96
97 13
    public function addOHLCV(
98
        int $timestamp,
99
        float $open,
100
        float $high,
101
        float $low,
102
        float $close,
103
        float $volume,
104
        int $side,
105
        int $resolution = RESOLUTION_01MIN
106
    ): void {
107 13
        $ts = base_timestamp($timestamp, $resolution);
108 13
        if (isset($this->current[$resolution])) {
109 13
            if ($ts > $this->current[$resolution]['timestamp']) {
110 13
                $this->updateLast($resolution);
111 13
                $this->setCurrent($resolution, $ts, $open, $high, $low, $close, $volume);
112 13
            } elseif ($ts === $this->current[$resolution]['timestamp']) {
113 13
                $this->updateCurrent($resolution, $high, $low, $close, $volume);
114 1
            } elseif ($ts < $this->current[$resolution]['timestamp']) {
115 1
                throw new \RuntimeException(
116
                    'Incoming data are in unsorted order. Current timestamp is greater then incoming data\'s.' .
117 1
                    ' (' . $ts . ' < ' . $this->current[$resolution]['timestamp'] . ')'
118
                );
119
            }
120 13
            $this->trim($resolution);
121
        } else {
122 13
            $this->setCurrent($resolution, $ts, $open, $high, $low, $close, $volume);
123
        }
124
125 13
        if ($nextResolution = $this->nextResolution($resolution)) {
126 13
            $this->addOHLCV($timestamp, $open, $high, $low, $close, $volume, $side, $nextResolution);
127
        }
128 13
    }
129
130 13
    private function updateLast(int $resolution): void
131
    {
132 13
        $this->timestamps[$resolution][] = $this->current[$resolution]['timestamp'];
133 13
        $this->opens[$resolution][] = $this->current[$resolution]['opens'];
134 13
        $this->highs[$resolution][] = $this->current[$resolution]['high'];
135 13
        $this->lows[$resolution][] = $this->current[$resolution]['low'];
136 13
        $this->closes[$resolution][] = $this->current[$resolution]['close'];
137 13
        $this->volumes[$resolution][] = $this->current[$resolution]['volume'];
138 13
    }
139
140 13
    private function setCurrent(
141
        int $resolution,
142
        int $timestamp,
143
        float $open,
144
        float $high,
145
        float $low,
146
        float $close,
147
        float $volume
148
    ): void {
149 13
        $this->current[$resolution]['timestamp'] = $timestamp;
150 13
        $this->current[$resolution]['opens'] = $open;
151 13
        $this->current[$resolution]['high'] = $high;
152 13
        $this->current[$resolution]['low'] = $low;
153 13
        $this->current[$resolution]['close'] = $close;
154 13
        $this->current[$resolution]['volume'] = $volume;
155 13
    }
156
157 13
    private function updateCurrent(
158
        int $resolution,
159
        float $high,
160
        float $low,
161
        float $close,
162
        float $volume
163
    ): void {
164 13
        if ($high > $this->current[$resolution]['high']) {
165 11
            $this->current[$resolution]['high'] = $high;
166
        }
167 13
        if ($low < $this->current[$resolution]['low']) {
168 12
            $this->current[$resolution]['low'] = $low;
169
        }
170
171 13
        $this->current[$resolution]['close'] = $close;
172 13
        $this->current[$resolution]['volume'] =
173 13
            (float)BC::add((string)$this->current[$resolution]['volume'], (string)$volume, NORMAL_SCALE);
174 13
    }
175
176 13
    private function trim(int $resolution): void
177
    {
178 13
        if (isset($this->timestamps[$resolution]) && (\count($this->timestamps[$resolution]) > $this->size)) {
179 1
            unset_first($this->timestamps[$resolution]);
180 1
            unset_first($this->opens[$resolution]);
181 1
            unset_first($this->highs[$resolution]);
182 1
            unset_first($this->lows[$resolution]);
183 1
            unset_first($this->closes[$resolution]);
184 1
            unset_first($this->volumes[$resolution]);
185
        }
186 13
    }
187
188 24
    private function nextResolution(int $resolution): ?int
189
    {
190 24
        $key = array_search($resolution, static::RESOLUTIONS, true);
191 24
        if ($key !== false && array_key_exists(++$key, static::RESOLUTIONS)) {
192 23
            return static::RESOLUTIONS[$key];
193
        }
194 14
        return null;
195
    }
196
197
    /**
198
     * @param int $resolution
199
     * @return array
200
     */
201 11
    public function getTimestamps(int $resolution): array
202
    {
203
        return
204 11
            $this->timestamps[$resolution] ?? [];
205
    }
206
207
    /**
208
     * @param int $resolution
209
     * @param bool $useCoefficient
210
     * @return array
211
     */
212 1
    public function getOpens(int $resolution, bool $useCoefficient = false): array
213
    {
214
        return
215 1
            $this->mulArr(
216 1
                $this->opens[$resolution] ?? [],
217 1
                $useCoefficient
218
            );
219
    }
220
221 7
    private function mulArr(array $values, bool $useCoefficient): array
222
    {
223 7
        if ($useCoefficient && $this->coefficient !== 1) {
224 1
            $values = array_map(
225
                function ($v) {
226
                    return
227 1
                        $v * $this->coefficient;
228 1
                },
229 1
                $values
230
            );
231
        }
232
233 7
        return $values;
234
    }
235
236
    /**
237
     * @param int $resolution
238
     * @param bool $useCoefficient
239
     * @return array
240
     */
241 1
    public function getHighs(int $resolution, bool $useCoefficient = false): array
242
    {
243
        return
244 1
            $this->mulArr(
245 1
                $this->highs[$resolution] ?? [],
246 1
                $useCoefficient
247
            );
248
    }
249
250
    /**
251
     * @param int $resolution
252
     * @param bool $useCoefficient
253
     * @return null|float
254
     */
255 1
    public function getLastHigh(int $resolution, bool $useCoefficient = false): ?float
256
    {
257
        return
258 1
            $this->lastElement($this->highs[$resolution] ?? [], $useCoefficient);
259
    }
260
261
    /**
262
     * @param array $element
263
     * @param bool $useCoefficient
264
     * @return null|float
265
     */
266 3
    private function lastElement(array $element, bool $useCoefficient = false): ?float
267
    {
268 3
        if (false !== $lastElement = end($element)) {
269
            return
270 3
                $this->mul(
271 3
                    $lastElement,
272 3
                    $useCoefficient
273
                );
274
        }
275 3
        return null;
276
    }
277
278 8
    private function mul(float $value, bool $useCoefficient): float
279
    {
280 8
        if ($useCoefficient && $this->coefficient !== 1) {
281 2
            $value *= $this->coefficient;
282
        }
283 8
        return $value;
284
    }
285
286
    /**
287
     * @param int $resolution
288
     * @param bool $useCoefficient
289
     * @return array
290
     */
291 1
    public function getLows(int $resolution, bool $useCoefficient = false): array
292
    {
293
        return
294 1
            $this->mulArr(
295 1
                $this->lows[$resolution] ?? [],
296 1
                $useCoefficient
297
            );
298
    }
299
300
    /**
301
     * @param int $resolution
302
     * @param bool $useCoefficient
303
     * @return null|float
304
     */
305 1
    public function getLastLow(int $resolution, bool $useCoefficient = false): ?float
306
    {
307
        return
308 1
            $this->lastElement($this->lows[$resolution] ?? [], $useCoefficient);
309
    }
310
311
    /**
312
     * @param int $resolution
313
     * @param bool $useCoefficient
314
     * @return array
315
     */
316 1
    public function getCloses(int $resolution, bool $useCoefficient = false): array
317
    {
318
        return
319 1
            $this->mulArr(
320 1
                $this->closes[$resolution] ?? [],
321 1
                $useCoefficient
322
            );
323
    }
324
325
    /**
326
     * @param int $resolution
327
     * @param bool $useCoefficient
328
     * @return null|float
329
     */
330 1
    public function getLastClose(int $resolution, bool $useCoefficient = false): ?float
331
    {
332
        return
333 1
            $this->lastElement($this->closes[$resolution] ?? [], $useCoefficient);
334
    }
335
336
    /**
337
     * @param int $resolution
338
     * @return array
339
     */
340 1
    public function getVolumes(int $resolution): array
341
    {
342
        return
343 1
            $this->volumes[$resolution] ?? [];
344
    }
345
346
    /**
347
     * @codeCoverageIgnore
348
     */
349
    public function dump(): void
350
    {
351
        if (\defined('APP_DEBUG') && APP_DEBUG) {
352
            $result = [];
353
            $pair = $this->getPair();
354
            foreach (static::RESOLUTIONS as $resolution) {
355
                $count = \count($this->timestamps[$resolution] ?? []);
356
                $result[] =
357
                    sprintf(
358
                        '%s [%s] %s %s %s %s %s %s %s',
359
                        $this->current[$resolution]['timestamp'],
360
                        RESOLUTION_ALIASES[$resolution],
361
                        $count,
362
                        $pair,
363
                        $this->current[$resolution]['opens'],
364
                        $this->current[$resolution]['high'],
365
                        $this->current[$resolution]['low'],
366
                        $this->current[$resolution]['close'],
367
                        $this->current[$resolution]['volume']
368
                    );
369
            }
370
            dump($result);
371
        }
372
    }
373
374
    /**
375
     * @return string
376
     */
377 57
    public function getPair(): string
378
    {
379 57
        return $this->pair;
380
    }
381
}
382