Skip to content

Commit

Permalink
remove previous_values and added period param
Browse files Browse the repository at this point in the history
  • Loading branch information
romulodl committed May 28, 2020
1 parent 1636dd1 commit 97bd9ef
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 111 deletions.
11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,15 @@ or add `romulodl/ema` to your `composer.json`. Please check the latest version i

```php
$ema = new Romulodl\Ema();
$ema->calculate(array $values, array $previous_values = []);
$ema->calculate(array $values, int $period = 9);
```

For example:
```php
$ema = new Romulodl\Ema();
$ema->calculate([10, 12, 14, 20, 14, 10, 11]);
$ema->calculate([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
```

#### What is `$previous_values` for?

The EMA calculation is based on the previous round of calculation. The `n` round depends on `n - 1` result.
Then what is `n - 1` for the first round? If `$previous_values` is not available, it uses a simple moving average
for it. With `$previous_values` set, it will start the calculation of the EMA before and the result will be more
accurate (at least closest to what TradingView shows.)

## Why did you do this?

The PECL Trading extension is crap and not everyone wants to install it.
Expand Down
39 changes: 12 additions & 27 deletions src/Ema.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,31 @@ class Ema
* The previous values options exists to try to smooth the current calculation
* if it is empty, it will calculate the SMA for the first round.
*/
public function calculate(array $values, array $previous_values = []) : float
public function calculate(array $values, int $period = 9) : float
{
if (empty($values)) {
if (empty($values) || count($values) < $period) {
throw new \Exception('[' . __METHOD__ . '] $values parameters is empty');
}

$mult = 2 / (count($values) + 1);

return $this->calculate_ema($values, $mult, $previous_values);
}

private function calculate_ema(
array $values,
float $mult,
array $previous_values = [],
$prev = false
): float
{
$mult = 2 / ($period + 1);
$prev_close = [];
$prev_ema = false;
foreach ($values as $value) {
if ( !is_numeric($value)) {
throw new \Exception('[' . __METHOD__ . '] invalid value: '. $value);
}

if (!$prev) {
$prev = !empty($previous_values) ?
$this->calculate_ema($previous_values, $mult) :
$this->sma($values);

if (count($prev_close) < $period) {
$prev_close[] = $value;
if (count($prev_close) === $period) {
$prev_ema = array_sum($prev_close) / $period;
}
continue;
}

$prev = ($value - $prev) * $mult + $prev;
$prev_ema = ($value - $prev_ema) * $mult + $prev_ema;
}

return $prev;
return $prev_ema;
}

private function sma(array $values) : float
{
return array_sum($values) / count($values);
}

}
88 changes: 13 additions & 75 deletions tests/TestEma.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,88 +8,27 @@ final class EmaTest extends TestCase
{
public function testCalculateEmaWithEmptyPreviousValues(): void
{
$values = [
9148.27,
9995,
9807.49,
9550.67,
8719.53,
8561.09,
8808.71,
9305.91,
9786.80,
9310.73
];
$val = require(__DIR__ . '/values.php');
$values = [];
foreach ($val as $v) {
$values[] = $v[2];
}
$values = array_slice($values, -9);

$ema = new Ema();
$this->assertSame(9288.86, round($ema->calculate($values), 2));
}

public function testCalculateEmaWithPreviousValues(): void
{
$previous_values = [
7694.17,
7774,
7735.75,
8784.20,
8624.76,
8830.52,
8975.18,
8900,
8873.98,
9022.78
];

$values = [
9148.27,
9995,
9807.49,
9550.67,
8719.53,
8561.09,
8808.71,
9305.91,
9786.80,
9310.73
];

$ema = new Ema();
$this->assertSame(9197.01, round($ema->calculate($values, $previous_values), 2));
$this->assertSame(9147.33, round($ema->calculate($values), 2));
}

public function testCalculateEmaWithMorePreviousValues(): void
{
$previous_values = [
7483.35,
7504.11,
7534.01,
7694.17,
7774,
7735.75,
8784.20,
8624.76,
8830.52,
8975.18,
8900,
8873.98,
9022.78
];

$values = [
9148.27,
9995,
9807.49,
9550.67,
8719.53,
8561.09,
8808.71,
9305.91,
9786.80,
9310.73
];
$val = require(__DIR__ . '/values.php');
$values = [];
foreach ($val as $v) {
$values[] = $v[2];
}

$ema = new Ema();
$this->assertSame(9182.18, round($ema->calculate($values, $previous_values), 2));
$this->assertSame(9101.36, round($ema->calculate($values), 2));
}

public function testCalculateEmaWithEmptyArray(): void
Expand All @@ -112,7 +51,6 @@ public function testCalculateEmaWithInvalidArray(): void
8808.71,
9305.91,
9786.80,
9310.73
];

$this->expectException(Exception::class);
Expand Down
Loading

0 comments on commit 97bd9ef

Please sign in to comment.