|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Laratrade\Indicators; |
|
4
|
|
|
|
|
5
|
|
|
use Illuminate\Support\Collection; |
|
6
|
|
|
use Laratrade\Indicators\Contracts\Indicator; |
|
7
|
|
|
use Laratrade\Indicators\Exceptions\NotEnoughDataPointsException; |
|
8
|
|
|
|
|
9
|
|
|
/** |
|
10
|
|
|
* Average True Range |
|
11
|
|
|
* |
|
12
|
|
|
* @see http://www.investopedia.com/articles/trading/08/atr.asp |
|
13
|
|
|
* |
|
14
|
|
|
* The idea is to use ATR to identify breakouts, if the price goes higher than |
|
15
|
|
|
* the previous close + ATR, a price breakout has occurred. |
|
16
|
|
|
* |
|
17
|
|
|
* The position is closed when the price goes 1 ATR below the previous close. |
|
18
|
|
|
* |
|
19
|
|
|
* This algorithm uses ATR as a momentum strategy, but the same signal can be used for |
|
20
|
|
|
* a reversion strategy, since ATR doesn't indicate the price direction (like adx below) |
|
21
|
|
|
*/ |
|
22
|
|
|
class AverageTrueRangeIndicator implements Indicator |
|
23
|
|
|
{ |
|
24
|
|
|
|
|
25
|
|
|
public function __invoke(Collection $ohlcv, int $period = 14): int |
|
26
|
|
|
{ |
|
27
|
|
|
|
|
28
|
|
|
if ($period > count($ohlcv->get('close'))) { |
|
29
|
|
|
$period = round(count($ohlcv->get('close')) / 2); |
|
30
|
|
|
} |
|
31
|
|
|
|
|
32
|
|
|
$data2 = $ohlcv; |
|
33
|
|
|
$current = array_pop($data2->get('close')); //[count($data['close']) - 1]; // we assume this is current |
|
|
|
|
|
|
34
|
|
|
$prev_close = array_pop($data2->get('close')); //[count($data['close']) - 2]; // prior close |
|
|
|
|
|
|
35
|
|
|
|
|
36
|
|
|
$atr = trader_atr( |
|
37
|
|
|
$ohlcv->get('high'), |
|
38
|
|
|
$ohlcv->get('low'), |
|
39
|
|
|
$ohlcv->get('close'), |
|
40
|
|
|
$period |
|
41
|
|
|
); |
|
42
|
|
|
|
|
43
|
|
|
if (false === $atr) { |
|
44
|
|
|
throw new NotEnoughDataPointsException('Not enough data points'); |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
|
|
48
|
|
|
$atr = array_pop($atr); // pick off the last |
|
49
|
|
|
|
|
50
|
|
|
// An upside breakout occurs when the price goes 1 ATR above the previous close |
|
51
|
|
|
$upside_signal = ($current - ($prev_close + $atr)); |
|
52
|
|
|
|
|
53
|
|
|
// A downside breakout occurs when the previous close is 1 ATR above the price |
|
54
|
|
|
$downside_signal = ($prev_close - ($current + $atr)); |
|
55
|
|
|
|
|
56
|
|
|
if ($upside_signal > 0) { |
|
57
|
|
|
return static::BUY; |
|
58
|
|
|
} elseif ($downside_signal > 0) { |
|
59
|
|
|
return static::SELL; |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
return static::HOLD; |
|
63
|
|
|
} |
|
64
|
|
|
} |
|
65
|
|
|
|