Passed
Push — master ( 1bd18b...966309 )
by Alec
03:04
created

DataOHLCV::getPair()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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
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
23
    /** @var array */
24
    protected $current;
25
    protected $timestamps = [];
26
    protected $opens = [];
27
    protected $highs = [];
28
    protected $lows = [];
29
    protected $closes = [];
30
    protected $volumes = [];
31
    protected $proxies = [];
32
33
    /** @var int */
34
    private $size;
35
    /** @var int */
36
    private $coefficient;
37
    /** @var string */
38
    private $pair;
39
40
    /**
41
     * DataOHLCV constructor.
42
     * @param string $pair
43
     * @param integer $size
44
     * @param int $coefficient
45
     */
46 98
    public function __construct(string $pair, ?int $size = null, int $coefficient = 1)
47
    {
48 98
        $this->size =
49 98
            (int)bounds(
50 98
                $size ?? static::DEFAULT_SIZE,
51 98
                static::MIN_SIZE,
52 98
                static::MAX_SIZE
53
            );
54 98
        $this->pair = $pair;
55 98
        $this->coefficient = $coefficient;
56 98
    }
57
58
    /**
59
     * @return int
60
     */
61 9
    public function getSize(): int
62
    {
63 9
        return $this->size;
64
    }
65
66 1
    public function hasPeriods(int $resolution, int $periods, int $multiplier = null): bool
67
    {
68
        $multiplier =
69 1
            (int)bounds($multiplier ?? self::DEFAULT_PERIOD_MULTIPLIER, 1, self::MAX_PERIOD_MULTIPLIER);
70
71 1
        return isset($this->timestamps[$resolution]) && (\count($this->timestamps[$resolution]) >= ($periods * $multiplier));
72
    }
73
74 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

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