From 8d96a6fbecd7debfe5ab9644dad1097f95f90c73 Mon Sep 17 00:00:00 2001 From: Jens Scherbl Date: Fri, 9 Aug 2024 16:19:08 +0200 Subject: [PATCH] Adds minor refactorings --- src/Candle/Candle.php | 12 +++--- src/Candle/CandleFactory.php | 48 ++++++++++----------- src/Chart/Chart.php | 74 ++++++++++++++++---------------- src/Chart/ChartFactory.php | 24 +++++------ src/Indicator/ADX/ADX.php | 10 ++--- src/Indicator/ADX/ADXFactory.php | 6 +-- src/Indicator/DI/DI.php | 19 ++++---- src/Indicator/DI/DIFactory.php | 10 ++--- src/Indicator/EMA/EMA.php | 16 +++---- src/Indicator/EMA/EMAFactory.php | 2 +- src/Indicator/SMA/SMA.php | 19 ++++---- src/Indicator/SMA/SMAFactory.php | 2 +- src/Indicator/WSMA/WSMA.php | 22 +++++----- 13 files changed, 131 insertions(+), 133 deletions(-) diff --git a/src/Candle/Candle.php b/src/Candle/Candle.php index e2942e9..6859c20 100644 --- a/src/Candle/Candle.php +++ b/src/Candle/Candle.php @@ -23,32 +23,32 @@ public function __construct( ) { if ($open->isNegative()) { throw new DomainException( - 'Invalid open price. Open price must not be negative.' + message: 'Invalid open price. Open price must not be negative.' ); } if ($high->isNegative()) { throw new DomainException( - 'Invalid high price. High price must not be negative.' + message: 'Invalid high price. High price must not be negative.' ); } if ($low->isNegative()) { throw new DomainException( - 'Invalid low price. Low price must not be negative.' + message: 'Invalid low price. Low price must not be negative.' ); } if ($high->isLessThan($low)) { throw new DomainException( - 'Invalid high- and low price. High price must not be lower than low price.' + message: 'Invalid high- and low price. High price must not be lower than low price.' ); } if ($close->isNegative()) { throw new DomainException( - 'Invalid close price. Close price must not be negative.' + message: 'Invalid close price. Close price must not be negative.' ); } if ($volume->isNegative()) { throw new DomainException( - 'Invalid volume. Volume must not be negative.' + message: 'Invalid volume. Volume must not be negative.' ); } } diff --git a/src/Candle/CandleFactory.php b/src/Candle/CandleFactory.php index b5f7015..f72c75c 100644 --- a/src/Candle/CandleFactory.php +++ b/src/Candle/CandleFactory.php @@ -14,15 +14,15 @@ public static function create( string $volume, Candle|null $previous, ): Candle { - $open = new Number($open); - $high = new Number($high); - $low = new Number($low); - $close = new Number($close); - $volume = new Number($volume); - $highLow = $high->minus($low); + $open = new Number(value: $open); + $high = new Number(value: $high); + $low = new Number(value: $low); + $close = new Number(value: $close); + $volume = new Number(value: $volume); + $highLow = $high->minus(value: $low); $TR = $highLow; - $DMp = new Number(0); - $DMm = new Number(0); + $DMp = new Number(value: 0); + $DMm = new Number(value: 0); if ($previous !== null) { @@ -30,15 +30,15 @@ public static function create( * Calculates true range (TR). */ - $highClose = $high->minus($previous->close); + $highClose = $high->minus(value: $previous->close); $absHighClose = $highClose->abs(); - $lowClose = $low->minus($previous->close); + $lowClose = $low->minus(value: $previous->close); $absLowClose = $lowClose->abs(); - if ($absHighClose->isGreaterThan($TR)) { + if ($absHighClose->isGreaterThan(value: $TR)) { $TR = $absHighClose; } - if ($absLowClose->isGreaterThan($TR)) { + if ($absLowClose->isGreaterThan(value: $TR)) { $TR = $absLowClose; } @@ -46,25 +46,25 @@ public static function create( * Calculates directional movements (+DM & -DM). */ - $upMove = $high->minus($previous->high); - $downMove = $previous->low->minus($low); + $upMove = $high->minus(value: $previous->high); + $downMove = $previous->low->minus(value: $low); - if ($upMove->isPositive() && $upMove->isGreaterThan($downMove)) { + if ($upMove->isPositive() && $upMove->isGreaterThan(value: $downMove)) { $DMp = $upMove; } - if ($downMove->isPositive() && $downMove->isGreaterThan($upMove)) { + if ($downMove->isPositive() && $downMove->isGreaterThan(value: $upMove)) { $DMm = $downMove; } } return new Candle( - $open, - $high, - $low, - $close, - $volume, - $TR, - $DMp, - $DMm, + open: $open, + high: $high, + low: $low, + close: $close, + volume: $volume, + TR: $TR, + DMp: $DMp, + DMm: $DMm, ); } } diff --git a/src/Chart/Chart.php b/src/Chart/Chart.php index e8740c8..e35b58e 100644 --- a/src/Chart/Chart.php +++ b/src/Chart/Chart.php @@ -12,7 +12,7 @@ final readonly class Chart implements ChartInterface { - private const SCALE = 4; + private const int SCALE = 4; /** * @param array $candles @@ -27,66 +27,66 @@ public function __construct( public function getSMA(int $period): array { - $SMA = $this->SMAFactory::create($period); + $SMA = $this->SMAFactory::create(period: $period); $result = []; foreach ($this->candles as $date => $candle) { $close = $candle->close; - $SMAResult = $SMA->calculate($close); - $result[$date] = $SMAResult?->round(self::SCALE); + $SMAResult = $SMA->calculate(value: $close); + $result[$date] = $SMAResult?->round(scale: self::SCALE); } return $result; } public function getEMA(int $period): array { - $EMA = $this->EMAFactory::create($period); + $EMA = $this->EMAFactory::create(period: $period); $result = []; foreach ($this->candles as $date => $candle) { $close = $candle->close; - $EMAResult = $EMA->calculate($close); - $result[$date] = $EMAResult?->round(self::SCALE); + $EMAResult = $EMA->calculate(value: $close); + $result[$date] = $EMAResult?->round(scale: self::SCALE); } return $result; } public function getDI(int $period): array { - $DI = $this->DIFactory::create($period); + $DI = $this->DIFactory::create(period: $period); $result = []; foreach ($this->candles as $date => $candle) { $DMp = $candle->DMp; $DMm = $candle->DMm; $TR = $candle->TR; - $DIResult = $DI->calculate($DMp, $DMm, $TR); + $DIResult = $DI->calculate(DMp: $DMp, DMm: $DMm, TR: $TR); $DIpResult = $DIResult->DIp; - $DIpRounded = $DIpResult?->round(self::SCALE); + $DIpRounded = $DIpResult?->round(scale: self::SCALE); $DImResult = $DIResult->DIm; - $DImRounded = $DImResult?->round(self::SCALE); - $result[$date] = new DI($DIpRounded, $DImRounded); + $DImRounded = $DImResult?->round(scale: self::SCALE); + $result[$date] = new DI(DIp: $DIpRounded, DIm: $DImRounded); } return $result; } public function getADX(int $period): array { - $DI = $this->DIFactory::create($period); - $ADX = $this->ADXFactory::create($period); + $DI = $this->DIFactory::create(period: $period); + $ADX = $this->ADXFactory::create(period: $period); $result = []; foreach ($this->candles as $date => $candle) { $DMp = $candle->DMp; $DMm = $candle->DMm; $TR = $candle->TR; - $DIResult = $DI->calculate($DMp, $DMm, $TR); + $DIResult = $DI->calculate(DMp: $DMp, DMm: $DMm, TR: $TR); $DIpResult = $DIResult->DIp; $DImResult = $DIResult->DIm; if ($DIpResult !== null && $DImResult !== null) { - $ADXResult = $ADX->calculate($DIpResult, $DImResult); - $result[$date] = $ADXResult?->round(self::SCALE); + $ADXResult = $ADX->calculate(DIp: $DIpResult, DIm: $DImResult); + $result[$date] = $ADXResult?->round(scale: self::SCALE); } else { $result[$date] = null; } @@ -96,41 +96,41 @@ public function getADX(int $period): array public function getTrend(int $SMAPeriod, int $EMAPeriod): array { - $SMA = $this->SMAFactory::create($SMAPeriod); - $EMA = $this->EMAFactory::create($EMAPeriod); - $DI = $this->DIFactory::create($EMAPeriod); - $ADX = $this->ADXFactory::create($EMAPeriod); + $SMA = $this->SMAFactory::create(period: $SMAPeriod); + $EMA = $this->EMAFactory::create(period: $EMAPeriod); + $DI = $this->DIFactory::create(period: $EMAPeriod); + $ADX = $this->ADXFactory::create(period: $EMAPeriod); $result = []; foreach ($this->candles as $date => $candle) { $close = $candle->close; - $closeRounded = $close->round(self::SCALE); - $SMAResult = $SMA->calculate($close); - $SMARounded = $SMAResult?->round(self::SCALE); - $EMAResult = $EMA->calculate($close); - $EMARounded = $EMAResult?->round(self::SCALE); + $closeRounded = $close->round(scale: self::SCALE); + $SMAResult = $SMA->calculate(value: $close); + $SMARounded = $SMAResult?->round(scale: self::SCALE); + $EMAResult = $EMA->calculate(value: $close); + $EMARounded = $EMAResult?->round(scale: self::SCALE); $DMp = $candle->DMp; $DMm = $candle->DMm; $TR = $candle->TR; - $DIResult = $DI->calculate($DMp, $DMm, $TR); + $DIResult = $DI->calculate(DMp: $DMp, DMm: $DMm, TR: $TR); $DIpResult = $DIResult->DIp; - $DIpRounded = $DIpResult?->round(self::SCALE); + $DIpRounded = $DIpResult?->round(scale: self::SCALE); $DImResult = $DIResult->DIm; - $DImRounded = $DImResult?->round(self::SCALE); + $DImRounded = $DImResult?->round(scale: self::SCALE); $ADXRounded = null; if ($DIpResult !== null && $DImResult !== null) { - $ADXResult = $ADX->calculate($DIpResult, $DImResult); - $ADXRounded = $ADXResult?->round(self::SCALE); + $ADXResult = $ADX->calculate(DIp: $DIpResult, DIm: $DImResult); + $ADXRounded = $ADXResult?->round(scale: self::SCALE); } $result[$date] = new Trend( - $closeRounded, - $SMARounded, - $EMARounded, - $DIpRounded, - $DImRounded, - $ADXRounded, + close: $closeRounded, + SMA: $SMARounded, + EMA: $EMARounded, + DIp: $DIpRounded, + DIm: $DImRounded, + ADX: $ADXRounded, ); } return $result; diff --git a/src/Chart/ChartFactory.php b/src/Chart/ChartFactory.php index 5a2334f..c8dedc0 100644 --- a/src/Chart/ChartFactory.php +++ b/src/Chart/ChartFactory.php @@ -18,12 +18,12 @@ public static function bootstrap(array $data): Chart { return (new ChartFactory( - new CandleFactory, - new SMAFactory, - new EMAFactory, - new DIFactory, - new ADXFactory, - ))->create($data); + candleFactory: new CandleFactory(), + SMAFactory: new SMAFactory(), + EMAFactory: new EMAFactory(), + DIFactory: new DIFactory(), + ADXFactory: new ADXFactory(), + ))->create(data: $data); } public function __construct( @@ -46,15 +46,15 @@ public function create(array $data): Chart 'close' => $close, 'volume' => $volume, ]) { - $candles[$date] = $this->candleFactory::create($open, $high, $low, $close, $volume, $previous); + $candles[$date] = $this->candleFactory::create(open: $open, high: $high, low: $low, close: $close, volume: $volume, previous: $previous); $previous = $candles[$date]; } return new Chart( - $candles, - $this->SMAFactory, - $this->EMAFactory, - $this->DIFactory, - $this->ADXFactory, + candles: $candles, + SMAFactory: $this->SMAFactory, + EMAFactory: $this->EMAFactory, + DIFactory: $this->DIFactory, + ADXFactory: $this->ADXFactory, ); } } diff --git a/src/Indicator/ADX/ADX.php b/src/Indicator/ADX/ADX.php index b0a6554..c40b7c7 100644 --- a/src/Indicator/ADX/ADX.php +++ b/src/Indicator/ADX/ADX.php @@ -17,19 +17,19 @@ public function calculate(Number $DIp, Number $DIm): Number|null * Calculates the directional movement index (DI). */ - $numerator = $DIp->minus($DIm)->abs(); - $denominator = $DIp->plus($DIm); + $numerator = $DIp->minus(value: $DIm)->abs(); + $denominator = $DIp->plus(value: $DIm); if ($denominator->isZero()) { - $DX = new Number(0); + $DX = new Number(value: 0); } else { - $DX = $numerator->dividedBy($denominator)->multipliedBy(100); + $DX = $numerator->dividedBy(value: $denominator)->multipliedBy(value: 100); } /* * Calculates the average directional movement index (ADX). */ - return $this->WSMA->calculate($DX); + return $this->WSMA->calculate(value: $DX); } } diff --git a/src/Indicator/ADX/ADXFactory.php b/src/Indicator/ADX/ADXFactory.php index 37cc3a9..9ed204f 100644 --- a/src/Indicator/ADX/ADXFactory.php +++ b/src/Indicator/ADX/ADXFactory.php @@ -8,8 +8,8 @@ { public static function create(int $period): ADXInterface { - return new ADX( - new WSMA($period), - ); + $WSMA = new WSMA(period: $period); + + return new ADX(WSMA: $WSMA); } } diff --git a/src/Indicator/DI/DI.php b/src/Indicator/DI/DI.php index 7f12121..93e5bd8 100644 --- a/src/Indicator/DI/DI.php +++ b/src/Indicator/DI/DI.php @@ -20,14 +20,14 @@ public function calculate(Number $DMp, Number $DMm, Number $TR): DIResult * of directional movements (+DM & -DM). */ - $DMpSMA = $this->DMpSMA->calculate($DMp); - $DMmSMA = $this->DMmSMA->calculate($DMm); + $DMpSMA = $this->DMpSMA->calculate(value: $DMp); + $DMmSMA = $this->DMmSMA->calculate(value: $DMm); /* * Calculates the average true range (ATR). */ - $ATR = $this->ATR->calculate($TR); + $ATR = $this->ATR->calculate(value: $TR); $DIp = null; $DIm = null; @@ -38,16 +38,13 @@ public function calculate(Number $DMp, Number $DMm, Number $TR): DIResult */ if ($ATR->isZero()) { - $DIp = new Number(0); - $DIm = new Number(0); + $DIp = new Number(value: 0); + $DIm = new Number(value: 0); } else { - $DIp = $DMpSMA->dividedBy($ATR)->multipliedBy(100); - $DIm = $DMmSMA->dividedBy($ATR)->multipliedBy(100); + $DIp = $DMpSMA->dividedBy(value: $ATR)->multipliedBy(value: 100); + $DIm = $DMmSMA->dividedBy(value: $ATR)->multipliedBy(value: 100); } } - return new DIResult( - $DIp, - $DIm, - ); + return new DIResult(DIp: $DIp, DIm: $DIm); } } diff --git a/src/Indicator/DI/DIFactory.php b/src/Indicator/DI/DIFactory.php index 742e59f..39addab 100644 --- a/src/Indicator/DI/DIFactory.php +++ b/src/Indicator/DI/DIFactory.php @@ -8,10 +8,10 @@ { public static function create(int $period): DIInterface { - return new DI( - new WSMA($period), - new WSMA($period), - new WSMA($period), - ); + $DMpSMA = new WSMA(period: $period); + $DMmSMA = new WSMA(period: $period); + $ATR = new WSMA(period: $period); + + return new DI(DMpSMA: $DMpSMA, DMmSMA: $DMmSMA, ATR: $ATR); } } diff --git a/src/Indicator/EMA/EMA.php b/src/Indicator/EMA/EMA.php index 9cbaa83..38d7934 100644 --- a/src/Indicator/EMA/EMA.php +++ b/src/Indicator/EMA/EMA.php @@ -7,21 +7,21 @@ final class EMA implements EMAInterface { - private int $period; - private Number $weightingFactor; - private int $dataCount; + private readonly int $period; + private readonly Number $weightingFactor; + private int $dataCount; private Number|null $result; public function __construct(int $period) { if ($period < 2) { throw new DomainException( - 'Invalid period. Period must be higher than `1` for EMA calculation.' + message: 'Invalid period. Period must be higher than `1` for EMA calculation.' ); } $this->period = $period; - $this->weightingFactor = (new Number(2))->dividedBy($period + 1); + $this->weightingFactor = (new Number(value: 2))->dividedBy(value: $period + 1); $this->dataCount = 0; $this->result = null; } @@ -31,9 +31,9 @@ public function calculate(Number $value): Number|null if ($this->result === null) { $this->result = $value; } else { - $value = $value->minus($this->result); - $weighted = $value->multipliedBy($this->weightingFactor); - $this->result = $this->result->plus($weighted); + $value = $value->minus(value: $this->result); + $weighted = $value->multipliedBy(value: $this->weightingFactor); + $this->result = $this->result->plus(value: $weighted); } if (++$this->dataCount < $this->period) { return null; diff --git a/src/Indicator/EMA/EMAFactory.php b/src/Indicator/EMA/EMAFactory.php index 18c480f..64f730d 100644 --- a/src/Indicator/EMA/EMAFactory.php +++ b/src/Indicator/EMA/EMAFactory.php @@ -6,6 +6,6 @@ { public static function create(int $period): EMAInterface { - return new EMA($period); + return new EMA(period: $period); } } diff --git a/src/Indicator/SMA/SMA.php b/src/Indicator/SMA/SMA.php index 8815690..4e693a8 100644 --- a/src/Indicator/SMA/SMA.php +++ b/src/Indicator/SMA/SMA.php @@ -4,45 +4,46 @@ use DomainException; use Kensho\Chart\Number; +use function array_shift; final class SMA implements SMAInterface { - private int $period; + private readonly int $period; /** * @var array */ - private array $buffer; - private int $bufferSize; + private array $buffer; + private int $bufferSize; private Number $sum; public function __construct(int $period) { if ($period < 2) { throw new DomainException( - 'Invalid period. Period must be higher than `1` for SMA calculation.' + message: 'Invalid period. Period must be higher than `1` for SMA calculation.' ); } $this->period = $period; $this->buffer = []; $this->bufferSize = 0; - $this->sum = new Number(0); + $this->sum = new Number(value: 0); } public function calculate(Number $value): Number|null { $this->buffer[] = $value; - $this->sum = $this->sum->plus($value); + $this->sum = $this->sum->plus(value: $value); $this->bufferSize++; if ($this->bufferSize < $this->period) { return null; } if ($this->bufferSize > $this->period) { - $oldest = array_shift($this->buffer); - $this->sum = $this->sum->minus($oldest); + $oldest = array_shift(array: $this->buffer); + $this->sum = $this->sum->minus(value: $oldest); $this->bufferSize--; } - return $this->sum->dividedBy($this->period); + return $this->sum->dividedBy(value: $this->period); } } diff --git a/src/Indicator/SMA/SMAFactory.php b/src/Indicator/SMA/SMAFactory.php index eaf9f86..a111e60 100644 --- a/src/Indicator/SMA/SMAFactory.php +++ b/src/Indicator/SMA/SMAFactory.php @@ -6,6 +6,6 @@ { public static function create(int $period): SMAInterface { - return new SMA($period); + return new SMA(period: $period); } } diff --git a/src/Indicator/WSMA/WSMA.php b/src/Indicator/WSMA/WSMA.php index 2a64325..9a53a9c 100644 --- a/src/Indicator/WSMA/WSMA.php +++ b/src/Indicator/WSMA/WSMA.php @@ -7,40 +7,40 @@ final class WSMA implements WSMAInterface { - private int $period; - private int $weightingFactor; - private int $initialDataCount; - private Number $initialSum; + private readonly int $period; + private readonly int $weightingFactor; + private int $initialDataCount; + private Number $initialSum; private Number|null $result; public function __construct(int $period) { if ($period < 2) { throw new DomainException( - 'Invalid period. Period must be higher than `1` for WSMA calculation.' + message: 'Invalid period. Period must be higher than `1` for WSMA calculation.' ); } $this->period = $period; $this->weightingFactor = $period - 1; $this->initialDataCount = 0; - $this->initialSum = new Number(0); + $this->initialSum = new Number(value: 0); $this->result = null; } public function calculate(Number $value): Number|null { if ($this->result === null) { - $this->initialSum = $this->initialSum->plus($value); + $this->initialSum = $this->initialSum->plus(value: $value); $this->initialDataCount++; if ($this->initialDataCount === $this->period) { - $this->result = $this->initialSum->dividedBy($this->period); + $this->result = $this->initialSum->dividedBy(value: $this->period); } } else { - $weighted = $this->result->multipliedBy($this->weightingFactor); - $sum = $weighted->plus($value); - $this->result = $sum->dividedBy($this->period); + $weighted = $this->result->multipliedBy(value: $this->weightingFactor); + $sum = $weighted->plus(value: $value); + $this->result = $sum->dividedBy(value: $this->period); } return $this->result; }