diff --git a/README.md b/README.md index c268545..ff5a947 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Calculates [indicators][1] for [technical chart analysis][2] in [PHP][3]. - [PHP 8.3][4] - Minimal dependencies. -- Uses [brick/math][5] for arbitrary precision numbers. +- Uses [BCMath][5] for arbitrary precision calculations. - Avoids redundant calculations and keeps the overall complexity low. - Unit- and integration tested against [other libraries][6] and [real-world data][7]. @@ -169,15 +169,15 @@ $result = $chart->getTrend($SMAPeriod, $EMAPeriod); #### Why are numeric values represented as strings? -> Note about floating-point values: instantiating from a float might be unsafe, as floating-point values are imprecise by design, and could result in a loss of information. Always prefer instantiating from a string, which supports an unlimited number of digits. +> Floating point numbers have limited precision. [...] So never trust floating number results to the last digit, and do not compare floating point numbers directly for equality. If higher precision is necessary, the arbitrary precision math functions are available. > -> — [brick/math][5] +> — [php.net][15] [1]: https://en.wikipedia.org/wiki/Technical_indicator [2]: https://en.wikipedia.org/wiki/Technical_analysis [3]: https://www.php.net [4]: https://www.php.net/releases/8.3/en.php - [5]: https://github.com/brick/math + [5]: https://www.php.net/manual/en/book.bc.php [6]: https://github.com/bennycode/trading-signals [7]: https://www.alphavantage.co [8]: #prepare-chart @@ -187,3 +187,4 @@ $result = $chart->getTrend($SMAPeriod, $EMAPeriod); [12]: #ema-exponential-moving-average [13]: #di---di-positive---negative-directional-indicator [14]: #adx-average-directional-index +[15]: https://www.php.net/manual/en/language.types.float.php \ No newline at end of file diff --git a/composer.json b/composer.json index d3a2902..a9bbf7a 100644 --- a/composer.json +++ b/composer.json @@ -47,8 +47,7 @@ "require": { "php": "^8.3", - "ext-bcmath": "*", - "brick/math": "^0.12" + "ext-bcmath": "*" }, "require-dev": { diff --git a/src/Candle/Candle.php b/src/Candle/Candle.php index 66e80bf..e2942e9 100644 --- a/src/Candle/Candle.php +++ b/src/Candle/Candle.php @@ -2,9 +2,8 @@ namespace Kensho\Chart\Candle; -use Brick\Math\BigDecimal; -use Brick\Math\BigInteger; use DomainException; +use Kensho\Chart\Number; /** * Open price, high price, low price, close price, volume, @@ -13,14 +12,14 @@ final readonly class Candle { public function __construct( - public BigDecimal $open, - public BigDecimal $high, - public BigDecimal $low, - public BigDecimal $close, - public BigInteger $volume, - public BigDecimal $TR, - public BigDecimal $DMp, - public BigDecimal $DMm, + public Number $open, + public Number $high, + public Number $low, + public Number $close, + public Number $volume, + public Number $TR, + public Number $DMp, + public Number $DMm, ) { if ($open->isNegative()) { throw new DomainException( diff --git a/src/Candle/CandleFactory.php b/src/Candle/CandleFactory.php index bc23abe..b5f7015 100644 --- a/src/Candle/CandleFactory.php +++ b/src/Candle/CandleFactory.php @@ -2,15 +2,10 @@ namespace Kensho\Chart\Candle; -use Brick\Math\BigDecimal; -use Brick\Math\BigInteger; -use Brick\Math\Exception\MathException; +use Kensho\Chart\Number; final readonly class CandleFactory implements CandleFactoryInterface { - /** - * @throws MathException - */ public static function create( string $open, string $high, @@ -19,15 +14,15 @@ public static function create( string $volume, Candle|null $previous, ): Candle { - $open = BigDecimal::of($open); - $high = BigDecimal::of($high); - $low = BigDecimal::of($low); - $close = BigDecimal::of($close); - $volume = BigInteger::of($volume); + $open = new Number($open); + $high = new Number($high); + $low = new Number($low); + $close = new Number($close); + $volume = new Number($volume); $highLow = $high->minus($low); $TR = $highLow; - $DMp = BigDecimal::zero(); - $DMm = BigDecimal::zero(); + $DMp = new Number(0); + $DMm = new Number(0); if ($previous !== null) { @@ -35,11 +30,17 @@ public static function create( * Calculates true range (TR). */ - $highClose = $high->minus($previous->close); - $absHighClose = $highClose->abs(); - $lowClose = $low->minus($previous->close); - $absLowClose = $lowClose->abs(); - $TR = BigDecimal::max($highLow, $absHighClose, $absLowClose); + $highClose = $high->minus($previous->close); + $absHighClose = $highClose->abs(); + $lowClose = $low->minus($previous->close); + $absLowClose = $lowClose->abs(); + + if ($absHighClose->isGreaterThan($TR)) { + $TR = $absHighClose; + } + if ($absLowClose->isGreaterThan($TR)) { + $TR = $absLowClose; + } /* * Calculates directional movements (+DM & -DM). diff --git a/src/Chart/Chart.php b/src/Chart/Chart.php index d14fd3c..e8740c8 100644 --- a/src/Chart/Chart.php +++ b/src/Chart/Chart.php @@ -2,9 +2,6 @@ namespace Kensho\Chart\Chart; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\RoundingNecessaryException; -use Brick\Math\RoundingMode; use Kensho\Chart\Candle\Candle; use Kensho\Chart\DI; use Kensho\Chart\Indicator\ADX\ADXFactoryInterface; @@ -15,8 +12,7 @@ final readonly class Chart implements ChartInterface { - private const ROUNDING_MODE = RoundingMode::HALF_UP; - private const SCALE = 4; + private const SCALE = 4; /** * @param array $candles @@ -29,9 +25,6 @@ public function __construct( private ADXFactoryInterface $ADXFactory, ) {} - /** - * @throws RoundingNecessaryException - */ public function getSMA(int $period): array { $SMA = $this->SMAFactory::create($period); @@ -40,14 +33,11 @@ public function getSMA(int $period): array foreach ($this->candles as $date => $candle) { $close = $candle->close; $SMAResult = $SMA->calculate($close); - $result[$date] = $this->round($SMAResult); + $result[$date] = $SMAResult?->round(self::SCALE); } return $result; } - /** - * @throws RoundingNecessaryException - */ public function getEMA(int $period): array { $EMA = $this->EMAFactory::create($period); @@ -56,14 +46,11 @@ public function getEMA(int $period): array foreach ($this->candles as $date => $candle) { $close = $candle->close; $EMAResult = $EMA->calculate($close); - $result[$date] = $this->round($EMAResult); + $result[$date] = $EMAResult?->round(self::SCALE); } return $result; } - /** - * @throws RoundingNecessaryException - */ public function getDI(int $period): array { $DI = $this->DIFactory::create($period); @@ -75,17 +62,14 @@ public function getDI(int $period): array $TR = $candle->TR; $DIResult = $DI->calculate($DMp, $DMm, $TR); $DIpResult = $DIResult->DIp; - $DIpRounded = $this->round($DIpResult); + $DIpRounded = $DIpResult?->round(self::SCALE); $DImResult = $DIResult->DIm; - $DImRounded = $this->round($DImResult); + $DImRounded = $DImResult?->round(self::SCALE); $result[$date] = new DI($DIpRounded, $DImRounded); } return $result; } - /** - * @throws RoundingNecessaryException - */ public function getADX(int $period): array { $DI = $this->DIFactory::create($period); @@ -102,7 +86,7 @@ public function getADX(int $period): array if ($DIpResult !== null && $DImResult !== null) { $ADXResult = $ADX->calculate($DIpResult, $DImResult); - $result[$date] = $this->round($ADXResult); + $result[$date] = $ADXResult?->round(self::SCALE); } else { $result[$date] = null; } @@ -110,9 +94,6 @@ public function getADX(int $period): array return $result; } - /** - * @throws RoundingNecessaryException - */ public function getTrend(int $SMAPeriod, int $EMAPeriod): array { $SMA = $this->SMAFactory::create($SMAPeriod); @@ -123,24 +104,24 @@ public function getTrend(int $SMAPeriod, int $EMAPeriod): array foreach ($this->candles as $date => $candle) { $close = $candle->close; - $closeRounded = $this->round($close); + $closeRounded = $close->round(self::SCALE); $SMAResult = $SMA->calculate($close); - $SMARounded = $this->round($SMAResult); + $SMARounded = $SMAResult?->round(self::SCALE); $EMAResult = $EMA->calculate($close); - $EMARounded = $this->round($EMAResult); + $EMARounded = $EMAResult?->round(self::SCALE); $DMp = $candle->DMp; $DMm = $candle->DMm; $TR = $candle->TR; $DIResult = $DI->calculate($DMp, $DMm, $TR); $DIpResult = $DIResult->DIp; - $DIpRounded = $this->round($DIpResult); + $DIpRounded = $DIpResult?->round(self::SCALE); $DImResult = $DIResult->DIm; - $DImRounded = $this->round($DImResult); + $DImRounded = $DImResult?->round(self::SCALE); $ADXRounded = null; if ($DIpResult !== null && $DImResult !== null) { $ADXResult = $ADX->calculate($DIpResult, $DImResult); - $ADXRounded = $this->round($ADXResult); + $ADXRounded = $ADXResult?->round(self::SCALE); } $result[$date] = new Trend( @@ -154,12 +135,4 @@ public function getTrend(int $SMAPeriod, int $EMAPeriod): array } return $result; } - - /** - * @throws RoundingNecessaryException - */ - private function round(BigDecimal|null $value): string|null - { - return $value?->toScale(self::SCALE, self::ROUNDING_MODE)->__toString(); - } } diff --git a/src/Indicator/ADX/ADX.php b/src/Indicator/ADX/ADX.php index 3d6bef5..b0a6554 100644 --- a/src/Indicator/ADX/ADX.php +++ b/src/Indicator/ADX/ADX.php @@ -2,23 +2,16 @@ namespace Kensho\Chart\Indicator\ADX; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; -use Kensho\Chart\Indicator\PrecisionTrait; use Kensho\Chart\Indicator\WSMA\WSMAInterface; +use Kensho\Chart\Number; final readonly class ADX implements ADXInterface { - use PrecisionTrait; - public function __construct( private WSMAInterface $WSMA, ) {} - /** - * @throws MathException - */ - public function calculate(BigDecimal $DIp, BigDecimal $DIm): BigDecimal|null + public function calculate(Number $DIp, Number $DIm): Number|null { /* * Calculates the directional movement index (DI). @@ -28,9 +21,9 @@ public function calculate(BigDecimal $DIp, BigDecimal $DIm): BigDecimal|null $denominator = $DIp->plus($DIm); if ($denominator->isZero()) { - $DX = BigDecimal::zero(); + $DX = new Number(0); } else { - $DX = $numerator->dividedBy($denominator, self::SCALE, self::ROUNDING_MODE)->multipliedBy(100); + $DX = $numerator->dividedBy($denominator)->multipliedBy(100); } /* diff --git a/src/Indicator/ADX/ADXInterface.php b/src/Indicator/ADX/ADXInterface.php index 7f75993..117c4e6 100644 --- a/src/Indicator/ADX/ADXInterface.php +++ b/src/Indicator/ADX/ADXInterface.php @@ -2,12 +2,12 @@ namespace Kensho\Chart\Indicator\ADX; -use Brick\Math\BigDecimal; +use Kensho\Chart\Number; /** * Calculates the average directional movement index (ADX). */ interface ADXInterface { - public function calculate(BigDecimal $DIp, BigDecimal $DIm): BigDecimal|null; + public function calculate(Number $DIp, Number $DIm): Number|null; } diff --git a/src/Indicator/DI/DI.php b/src/Indicator/DI/DI.php index c6d3c58..7f12121 100644 --- a/src/Indicator/DI/DI.php +++ b/src/Indicator/DI/DI.php @@ -2,25 +2,18 @@ namespace Kensho\Chart\Indicator\DI; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; -use Kensho\Chart\Indicator\PrecisionTrait; use Kensho\Chart\Indicator\WSMA\WSMAInterface; +use Kensho\Chart\Number; final readonly class DI implements DIInterface { - use PrecisionTrait; - public function __construct( private WSMAInterface $DMpSMA, private WSMAInterface $DMmSMA, private WSMAInterface $ATR, ) {} - /** - * @throws MathException - */ - public function calculate(BigDecimal $DMp, BigDecimal $DMm, BigDecimal $TR): DIResult + public function calculate(Number $DMp, Number $DMm, Number $TR): DIResult { /* * Calculates the smoothed moving averages @@ -45,11 +38,11 @@ public function calculate(BigDecimal $DMp, BigDecimal $DMm, BigDecimal $TR): DIR */ if ($ATR->isZero()) { - $DIp = BigDecimal::zero(); - $DIm = BigDecimal::zero(); + $DIp = new Number(0); + $DIm = new Number(0); } else { - $DIp = $DMpSMA->dividedBy($ATR, self::SCALE, self::ROUNDING_MODE)->multipliedBy(100); - $DIm = $DMmSMA->dividedBy($ATR, self::SCALE, self::ROUNDING_MODE)->multipliedBy(100); + $DIp = $DMpSMA->dividedBy($ATR)->multipliedBy(100); + $DIm = $DMmSMA->dividedBy($ATR)->multipliedBy(100); } } return new DIResult( diff --git a/src/Indicator/DI/DIInterface.php b/src/Indicator/DI/DIInterface.php index 2f1a15b..a680b0b 100644 --- a/src/Indicator/DI/DIInterface.php +++ b/src/Indicator/DI/DIInterface.php @@ -2,12 +2,12 @@ namespace Kensho\Chart\Indicator\DI; -use Brick\Math\BigDecimal; +use Kensho\Chart\Number; /** * Calculates the directional indicators (+DI & -DI). */ interface DIInterface { - public function calculate(BigDecimal $DMp, BigDecimal $DMm, BigDecimal $TR): DIResult; + public function calculate(Number $DMp, Number $DMm, Number $TR): DIResult; } diff --git a/src/Indicator/DI/DIResult.php b/src/Indicator/DI/DIResult.php index 2c45f49..96c55ae 100644 --- a/src/Indicator/DI/DIResult.php +++ b/src/Indicator/DI/DIResult.php @@ -2,12 +2,12 @@ namespace Kensho\Chart\Indicator\DI; -use Brick\Math\BigDecimal; +use Kensho\Chart\Number; final readonly class DIResult { public function __construct( - public BigDecimal|null $DIp, - public BigDecimal|null $DIm, + public Number|null $DIp, + public Number|null $DIm, ) {} } diff --git a/src/Indicator/EMA/EMA.php b/src/Indicator/EMA/EMA.php index 08628be..9cbaa83 100644 --- a/src/Indicator/EMA/EMA.php +++ b/src/Indicator/EMA/EMA.php @@ -2,23 +2,16 @@ namespace Kensho\Chart\Indicator\EMA; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; use DomainException; -use Kensho\Chart\Indicator\PrecisionTrait; +use Kensho\Chart\Number; final class EMA implements EMAInterface { - use PrecisionTrait; + private int $period; + private Number $weightingFactor; + private int $dataCount; + private Number|null $result; - private int $period; - private BigDecimal $weightingFactor; - private int $dataCount; - private BigDecimal|null $result; - - /** - * @throws MathException - */ public function __construct(int $period) { if ($period < 2) { @@ -28,15 +21,12 @@ public function __construct(int $period) } $this->period = $period; - $this->weightingFactor = BigDecimal::of(2)->dividedBy($period + 1, self::SCALE, self::ROUNDING_MODE); + $this->weightingFactor = (new Number(2))->dividedBy($period + 1); $this->dataCount = 0; $this->result = null; } - /** - * @throws MathException - */ - public function calculate(BigDecimal $value): BigDecimal|null + public function calculate(Number $value): Number|null { if ($this->result === null) { $this->result = $value; diff --git a/src/Indicator/EMA/EMAFactory.php b/src/Indicator/EMA/EMAFactory.php index 0477226..18c480f 100644 --- a/src/Indicator/EMA/EMAFactory.php +++ b/src/Indicator/EMA/EMAFactory.php @@ -2,13 +2,8 @@ namespace Kensho\Chart\Indicator\EMA; -use Brick\Math\Exception\MathException; - final readonly class EMAFactory implements EMAFactoryInterface { - /** - * @throws MathException - */ public static function create(int $period): EMAInterface { return new EMA($period); diff --git a/src/Indicator/EMA/EMAInterface.php b/src/Indicator/EMA/EMAInterface.php index ecb41d6..3765f76 100644 --- a/src/Indicator/EMA/EMAInterface.php +++ b/src/Indicator/EMA/EMAInterface.php @@ -2,12 +2,12 @@ namespace Kensho\Chart\Indicator\EMA; -use Brick\Math\BigDecimal; +use Kensho\Chart\Number; /** * Calculates the exponential moving average (EMA). */ interface EMAInterface { - public function calculate(BigDecimal $value): BigDecimal|null; + public function calculate(Number $value): Number|null; } diff --git a/src/Indicator/PrecisionTrait.php b/src/Indicator/PrecisionTrait.php deleted file mode 100644 index eecd68e..0000000 --- a/src/Indicator/PrecisionTrait.php +++ /dev/null @@ -1,11 +0,0 @@ - + * @var array */ - private array $buffer; - private int $bufferSize; - private BigDecimal $sum; + private array $buffer; + private int $bufferSize; + private Number $sum; public function __construct(int $period) { @@ -30,13 +26,10 @@ public function __construct(int $period) $this->period = $period; $this->buffer = []; $this->bufferSize = 0; - $this->sum = BigDecimal::zero(); + $this->sum = new Number(0); } - /** - * @throws MathException - */ - public function calculate(BigDecimal $value): BigDecimal|null + public function calculate(Number $value): Number|null { $this->buffer[] = $value; $this->sum = $this->sum->plus($value); @@ -50,6 +43,6 @@ public function calculate(BigDecimal $value): BigDecimal|null $this->sum = $this->sum->minus($oldest); $this->bufferSize--; } - return $this->sum->dividedBy($this->period, self::SCALE, self::ROUNDING_MODE); + return $this->sum->dividedBy($this->period); } } diff --git a/src/Indicator/SMA/SMAInterface.php b/src/Indicator/SMA/SMAInterface.php index 5af6717..937cb13 100644 --- a/src/Indicator/SMA/SMAInterface.php +++ b/src/Indicator/SMA/SMAInterface.php @@ -2,12 +2,12 @@ namespace Kensho\Chart\Indicator\SMA; -use Brick\Math\BigDecimal; +use Kensho\Chart\Number; /** * Calculates the simple moving average (SMA). */ interface SMAInterface { - public function calculate(BigDecimal $value): BigDecimal|null; + public function calculate(Number $value): Number|null; } diff --git a/src/Indicator/WSMA/WSMA.php b/src/Indicator/WSMA/WSMA.php index 2abf3d8..2a64325 100644 --- a/src/Indicator/WSMA/WSMA.php +++ b/src/Indicator/WSMA/WSMA.php @@ -2,20 +2,16 @@ namespace Kensho\Chart\Indicator\WSMA; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; use DomainException; -use Kensho\Chart\Indicator\PrecisionTrait; +use Kensho\Chart\Number; final class WSMA implements WSMAInterface { - use PrecisionTrait; - - private int $period; - private int $weightingFactor; - private int $initialDataCount; - private BigDecimal $initialSum; - private BigDecimal|null $result; + private int $period; + private int $weightingFactor; + private int $initialDataCount; + private Number $initialSum; + private Number|null $result; public function __construct(int $period) { @@ -28,26 +24,23 @@ public function __construct(int $period) $this->period = $period; $this->weightingFactor = $period - 1; $this->initialDataCount = 0; - $this->initialSum = BigDecimal::zero(); + $this->initialSum = new Number(0); $this->result = null; } - /** - * @throws MathException - */ - public function calculate(BigDecimal $value): BigDecimal|null + public function calculate(Number $value): Number|null { if ($this->result === null) { $this->initialSum = $this->initialSum->plus($value); $this->initialDataCount++; if ($this->initialDataCount === $this->period) { - $this->result = $this->initialSum->dividedBy($this->period, self::SCALE, self::ROUNDING_MODE); + $this->result = $this->initialSum->dividedBy($this->period); } } else { $weighted = $this->result->multipliedBy($this->weightingFactor); $sum = $weighted->plus($value); - $this->result = $sum->dividedBy($this->period, self::SCALE, self::ROUNDING_MODE); + $this->result = $sum->dividedBy($this->period); } return $this->result; } diff --git a/src/Indicator/WSMA/WSMAInterface.php b/src/Indicator/WSMA/WSMAInterface.php index 20729a3..1da406b 100644 --- a/src/Indicator/WSMA/WSMAInterface.php +++ b/src/Indicator/WSMA/WSMAInterface.php @@ -2,12 +2,12 @@ namespace Kensho\Chart\Indicator\WSMA; -use Brick\Math\BigDecimal; +use Kensho\Chart\Number; /** * Calculates Wilder’s smoothed moving average. */ interface WSMAInterface { - public function calculate(BigDecimal $value): BigDecimal|null; + public function calculate(Number $value): Number|null; } diff --git a/tests/integration/Chart/ChartTest.php b/tests/integration/Chart/ChartTest.php index df8004a..ee02802 100644 --- a/tests/integration/Chart/ChartTest.php +++ b/tests/integration/Chart/ChartTest.php @@ -2,7 +2,6 @@ namespace Kensho\Chart\Tests\Integration\Chart; -use Brick\Math\Exception\MathException; use Kensho\Chart\Chart\ChartFactory; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -12,8 +11,6 @@ final class ChartTest extends TestCase /** * @param array> $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideDataSMA')] public function testGetSMA(int $period, array $values, array $expected): void @@ -27,8 +24,6 @@ public function testGetSMA(int $period, array $values, array $expected): void /** * @param array> $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideDataEMA')] public function testGetEMA(int $period, array $values, array $expected): void @@ -42,8 +37,6 @@ public function testGetEMA(int $period, array $values, array $expected): void /** * @param array> $values * @param array> $expected - * - * @throws MathException */ #[DataProvider('provideDataDI')] public function testGetDI(int $period, array $values, array $expected): void @@ -65,8 +58,6 @@ public function testGetDI(int $period, array $values, array $expected): void /** * @param array> $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideDataADX')] public function testGetADX(int $period, array $values, array $expected): void @@ -80,8 +71,6 @@ public function testGetADX(int $period, array $values, array $expected): void /** * @param array> $values * @param array> $expected - * - * @throws MathException */ #[DataProvider('provideDataTrend')] public function testGetTrend(int $SMAperiod, int $EMAperiod, array $values, array $expected): void diff --git a/tests/integration/Indicator/ADXTest.php b/tests/integration/Indicator/ADXTest.php index 0981dd1..998b300 100644 --- a/tests/integration/Indicator/ADXTest.php +++ b/tests/integration/Indicator/ADXTest.php @@ -2,10 +2,8 @@ namespace Kensho\Chart\Tests\Integration\Indicator; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; -use Brick\Math\RoundingMode; use Kensho\Chart\Indicator\ADX\ADXFactory; +use Kensho\Chart\Number; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -14,8 +12,6 @@ final class ADXTest extends TestCase /** * @param array> $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideData')] public function testCalculate(int $period, array $values, array $expected): void @@ -29,12 +25,9 @@ public function testCalculate(int $period, array $values, array $expected): void ]) { if ($DIp !== null && $DIm !== null) { $actual[$date] = $instance->calculate( - BigDecimal::of($DIp), - BigDecimal::of($DIm), - )?->toScale( - 4, - RoundingMode::HALF_UP, - )->__toString(); + new Number($DIp), + new Number($DIm) + )?->round(4); } else { $actual[$date] = null; } diff --git a/tests/integration/Indicator/DITest.php b/tests/integration/Indicator/DITest.php index ae247aa..7f585e0 100644 --- a/tests/integration/Indicator/DITest.php +++ b/tests/integration/Indicator/DITest.php @@ -2,10 +2,8 @@ namespace Kensho\Chart\Tests\Integration\Indicator; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; -use Brick\Math\RoundingMode; use Kensho\Chart\Indicator\DI\DIFactory; +use Kensho\Chart\Number; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -14,8 +12,6 @@ final class DITest extends TestCase /** * @param array> $values * @param array> $expected - * - * @throws MathException */ #[DataProvider('provideData')] public function testCalculate(int $period, array $values, array $expected): void @@ -28,12 +24,12 @@ public function testCalculate(int $period, array $values, array $expected): void 'DMm' => $DMm, 'TR' => $TR, ]) { - $DMp = BigDecimal::of($DMp); - $DMm = BigDecimal::of($DMm); - $TR = BigDecimal::of($TR); + $DMp = new Number($DMp); + $DMm = new Number($DMm); + $TR = new Number($TR); $result = $instance->calculate($DMp, $DMm, $TR); - $DIp = $result->DIp?->toScale(4, RoundingMode::HALF_UP)->__toString(); - $DIm = $result->DIm?->toScale(4, RoundingMode::HALF_UP)->__toString(); + $DIp = $result->DIp?->round(4); + $DIm = $result->DIm?->round(4); $actual[$date] = [ 'DIp' => $DIp, 'DIm' => $DIm, diff --git a/tests/unit/Candle/CandleFactoryTest.php b/tests/unit/Candle/CandleFactoryTest.php index a4de9b9..0b4d83f 100644 --- a/tests/unit/Candle/CandleFactoryTest.php +++ b/tests/unit/Candle/CandleFactoryTest.php @@ -13,8 +13,6 @@ final class CandleFactoryTest extends TestCase /** * @param array $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideData')] public function testCalculate(array $values, array $expected): void @@ -31,9 +29,9 @@ public function testCalculate(array $values, array $expected): void ]) { $result = CandleFactory::create($open, $high, $low, $close, $volume, $previous); $previous = $result; - $TR = $result->TR->toScale(4, RoundingMode::HALF_UP)->__toString(); - $DMp = $result->DMp->toScale(4, RoundingMode::HALF_UP)->__toString(); - $DMm = $result->DMm->toScale(4, RoundingMode::HALF_UP)->__toString(); + $TR = $result->TR->round(4); + $DMp = $result->DMp->round(4); + $DMm = $result->DMm->round(4); $actual[$date] = [ 'TR' => $TR, 'DMp' => $DMp, diff --git a/tests/unit/Candle/CandleTest.php b/tests/unit/Candle/CandleTest.php index f2f24a2..7a152ee 100644 --- a/tests/unit/Candle/CandleTest.php +++ b/tests/unit/Candle/CandleTest.php @@ -2,19 +2,14 @@ namespace Kensho\Chart\Tests\Unit\Candle; -use Brick\Math\BigDecimal; -use Brick\Math\BigInteger; -use Brick\Math\Exception\MathException; use DomainException; use Kensho\Chart\Candle\Candle; +use Kensho\Chart\Number; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class CandleTest extends TestCase { - /** - * @throws MathException - */ #[DataProvider('provideInvalidValues')] public function testInvalidValues( string $open, @@ -26,22 +21,19 @@ public function testInvalidValues( $this->expectException(DomainException::class); $actual = new Candle( - BigDecimal::of($open), - BigDecimal::of($high), - BigDecimal::of($low), - BigDecimal::of($close), - BigInteger::of($volume), - BigDecimal::zero(), - BigDecimal::zero(), - BigDecimal::zero(), + new Number($open), + new Number($high), + new Number($low), + new Number($close), + new Number($volume), + new Number(0), + new Number(0), + new Number(0), ); $this->assertInstanceOf(Candle::class, $actual); } - /** - * @throws MathException - */ #[DataProvider('provideValidValues')] public function testValidValues( string $open, @@ -51,14 +43,14 @@ public function testValidValues( string $volume, ): void { $actual = new Candle( - BigDecimal::of($open), - BigDecimal::of($high), - BigDecimal::of($low), - BigDecimal::of($close), - BigInteger::of($volume), - BigDecimal::zero(), - BigDecimal::zero(), - BigDecimal::zero(), + new Number($open), + new Number($high), + new Number($low), + new Number($close), + new Number($volume), + new Number(0), + new Number(0), + new Number(0), ); $this->assertInstanceOf(Candle::class, $actual); diff --git a/tests/unit/Indicator/EMATest.php b/tests/unit/Indicator/EMATest.php index 684fa57..a65a8f2 100644 --- a/tests/unit/Indicator/EMATest.php +++ b/tests/unit/Indicator/EMATest.php @@ -2,20 +2,15 @@ namespace Kensho\Chart\Tests\Unit\Indicator; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; -use Brick\Math\RoundingMode; use DomainException; use Kensho\Chart\Indicator\EMA\EMA; use Kensho\Chart\Indicator\EMA\EMAInterface; +use Kensho\Chart\Number; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; final class EMATest extends TestCase { - /** - * @throws MathException - */ #[DataProvider('provideInvalidPeriod')] public function testInvalidPeriod(int $period): void { @@ -29,8 +24,6 @@ public function testInvalidPeriod(int $period): void /** * @param array $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideData')] public function testCalculate(int $period, array $values, array $expected): void @@ -39,12 +32,7 @@ public function testCalculate(int $period, array $values, array $expected): void $actual = []; foreach ($values as $date => $value) { - $actual[$date] = $instance->calculate( - BigDecimal::of($value), - )?->toScale( - 4, - RoundingMode::HALF_UP, - )->__toString(); + $actual[$date] = $instance->calculate(new Number($value))?->round(4); } $this->assertSame($expected, $actual); } diff --git a/tests/unit/Indicator/SMATest.php b/tests/unit/Indicator/SMATest.php index a759180..cf1c35c 100644 --- a/tests/unit/Indicator/SMATest.php +++ b/tests/unit/Indicator/SMATest.php @@ -2,12 +2,10 @@ namespace Kensho\Chart\Tests\Unit\Indicator; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; -use Brick\Math\RoundingMode; use DomainException; use Kensho\Chart\Indicator\SMA\SMA; use Kensho\Chart\Indicator\SMA\SMAInterface; +use Kensho\Chart\Number; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -26,8 +24,6 @@ public function testInvalidPeriod(int $period): void /** * @param array $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideData')] public function testCalculate(int $period, array $values, array $expected): void @@ -36,12 +32,7 @@ public function testCalculate(int $period, array $values, array $expected): void $actual = []; foreach ($values as $date => $value) { - $actual[$date] = $instance->calculate( - BigDecimal::of($value), - )?->toScale( - 4, - RoundingMode::HALF_UP, - )->__toString(); + $actual[$date] = $instance->calculate(new Number($value))?->round(4); } $this->assertSame($expected, $actual); } diff --git a/tests/unit/Indicator/WSMATest.php b/tests/unit/Indicator/WSMATest.php index db9898e..294173d 100644 --- a/tests/unit/Indicator/WSMATest.php +++ b/tests/unit/Indicator/WSMATest.php @@ -2,12 +2,10 @@ namespace Kensho\Chart\Tests\Unit\Indicator; -use Brick\Math\BigDecimal; -use Brick\Math\Exception\MathException; -use Brick\Math\RoundingMode; use DomainException; use Kensho\Chart\Indicator\WSMA\WSMA; use Kensho\Chart\Indicator\WSMA\WSMAInterface; +use Kensho\Chart\Number; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -26,8 +24,6 @@ public function testInvalidPeriod(int $period): void /** * @param array $values * @param array $expected - * - * @throws MathException */ #[DataProvider('provideData')] public function testCalculate(int $period, array $values, array $expected): void @@ -36,12 +32,7 @@ public function testCalculate(int $period, array $values, array $expected): void $actual = []; foreach ($values as $date => $value) { - $actual[$date] = $instance->calculate( - BigDecimal::of($value), - )?->toScale( - 4, - RoundingMode::HALF_UP, - )->__toString(); + $actual[$date] = $instance->calculate(new Number($value))?->round(4); } $this->assertSame($expected, $actual); }