Created
June 4, 2017 20:12
-
-
Save ak9250/65f9ac6fe8ccd3200044da0b299b2d5c to your computer and use it in GitHub Desktop.
indicators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Created by PhpStorm. | |
* User: joeldg | |
* Date: 4/13/17 | |
* Time: 6:26 PM | |
*/ | |
namespace App\Util; | |
use App\Util\Util; | |
use Symfony\Component\Process\Process; | |
use Symfony\Component\Process\Exception\ProcessFailedException; | |
/** | |
* Class Indicators | |
* @package App\Util | |
* | |
* signal functions should return 1 for buy -1 for sell and 0 for no change | |
* other functions can return single floats for predictions. | |
* | |
* all signal functions can be called alone, with jsut a pair or a just a pair and data | |
* time periods can be tweaked in backtesting and regression testing. | |
* | |
* TODO: port over 'Ichimoku Kinko Hyo' (Ichimoku Cloud) for basic signals | |
* http://www.babypips.com/school/elementary/common-chart-indicators/summary-common-chart-indicators.html | |
* http://stockcharts.com/school/doku.php?id=chart_school:trading_strategies:ichimoku_cloud | |
* http://jsfiddle.net/oscglezm/phq7yo9y/ | |
*/ | |
class Indicators | |
{ | |
/** | |
* @var array | |
* array with the available types of moving averages | |
*/ | |
public $mas = array('sma','ema','wma','dema','tema','trima','kama','mama','t3'); | |
/** | |
* @var array | |
* array with the available types of buy/sell signals | |
* 'aroonosc','cmo','cci','mfi' = a good group with volume | |
*/ | |
public $available_signals = array('adx','aroonosc','cmo','sar','cci','mfi','obv','stoch','rsi','macd','bollingerBands','atr'); | |
/** | |
* @param $ma | |
* | |
* @return mixed | |
* built-in types of moving averages. | |
* http://php.net/manual/en/trader.constants.php | |
*/ | |
public function ma_type($ma) | |
{ | |
if (!in_array($ma, $this->mas)) { | |
return 0; // simple | |
} | |
$types = array( | |
'sma' => TRADER_MA_TYPE_SMA, // simple moving average | |
'ema' => TRADER_MA_TYPE_EMA, // exponential moving average | |
'wma' => TRADER_MA_TYPE_WMA, // weighted moving average | |
'dema' => TRADER_MA_TYPE_DEMA, // Double Exponential Moving Average | |
'tema' => TRADER_MA_TYPE_TEMA, // Triple Exponential Moving Average | |
'trima' => TRADER_MA_TYPE_TRIMA, // Triangular Moving Average | |
'kama' => TRADER_MA_TYPE_KAMA, // Kaufman's Adaptive Moving Average | |
'mama' => TRADER_MA_TYPE_MAMA, // The Mother of Adaptive Moving Average | |
't3' => TRADER_MA_TYPE_T3 // The Triple Exponential Moving Average | |
); | |
return $types[$ma]; | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* Average True Range | |
* http://www.investopedia.com/articles/trading/08/atr.asp | |
* The idea is to use ATR to identify breakouts, if the price goes higher than | |
* the previous close + ATR, a price breakout has occurred. | |
* | |
* The position is closed when the price goes 1 ATR below the previous close. | |
* | |
* This algorithm uses ATR as a momentum strategy, but the same signal can be used for | |
* a reversion strategy, since ATR doesn't indicate the price direction (like adx below) | |
*/ | |
public function atr($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
if ($period > count($data['close'])) { | |
$period = round(count($data['close'])/2); | |
} | |
$prev_close = $data['close'][count($data['close']) - 2]; // prior close | |
$current = $data['close'][count($data['close']) - 1]; // we assume this is current | |
$atr = trader_atr ($data['high'], $data['low'], $data['close'], $period); | |
$atr = $atr[count($atr)-1]; // pick off the last | |
# An upside breakout occurs when the price goes 1 ATR above the previous close | |
$upside_signal = ($current - ($prev_close + $atr)); | |
# A downside breakout occurs when the previous close is 1 ATR above the price | |
$downside_signal = ($prev_close - ($current + $atr)); | |
if ($upside_signal > 0) { | |
return 1; // buy | |
} elseif ($downside_signal > 0){ | |
return -1; // sell | |
} | |
return 0; | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* This algorithm uses the talib Bollinger Bands function to determine entry entry | |
* points for long and sell/short positions. | |
* | |
* When the price breaks out of the upper Bollinger band, a sell or short position | |
* is opened. A long position is opened when the price dips below the lower band. | |
* | |
* | |
* Used to measure the market’s volatility. | |
* They act like mini support and resistance levels. | |
* Bollinger Bounce | |
* | |
* A strategy that relies on the notion that price tends to always return to the middle of the Bollinger bands. | |
* You buy when the price hits the lower Bollinger band. | |
* You sell when the price hits the upper Bollinger band. | |
* Best used in ranging markets. | |
* Bollinger Squeeze | |
* | |
* A strategy that is used to catch breakouts early. | |
* When the Bollinger bands “squeeze”, it means that the market is very quiet, and a breakout is eminent. | |
* Once a breakout occurs, we enter a trade on whatever side the price makes its breakout. | |
*/ | |
public function bollingerBands($pair='ETH-USD', $data=null, $period=10) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
$prev_close = $data['close'][count($data['close']) - 2]; // prior close | |
$current = $data['close'][count($data['close']) - 1]; // we assume this is current | |
# array $real [, integer $timePeriod [, float $nbDevUp [, float $nbDevDn [, integer $mAType ]]]] | |
$bbands = trader_bbands($data['close'], $period, 2, 2, 0); | |
$upper = $bbands[0]; | |
#$middle = $bbands[1]; // we'll find a use for you, one day | |
$lower = $bbands[2]; | |
# If price is below the recent lower band | |
if ($current <= $lower[count($lower)-1]) { | |
return 1; // buy long | |
# If price is above the recent upper band | |
} elseif ($current >= $upper[count($upper)-1]) { | |
return -1; // sell (or short) | |
} else { | |
return 0; // notta | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* Moving Average Crossover Divergence (MACD) indicator as a buy/sell signal. | |
* When the MACD signal less than 0, the price is trending down and it's time to sell. | |
* When the MACD signal greater than 0, the price is trending up it's time to buy. | |
* | |
* Used to catch trends early and can also help us spot trend reversals. | |
* It consists of 2 moving averages (1 fast, 1 slow) and vertical lines called a histogram, | |
* which measures the distance between the 2 moving averages. | |
* Contrary to what many people think, the moving average lines are NOT moving averages of the price. | |
* They are moving averages of other moving averages. | |
* MACD’s downfall is its lag because it uses so many moving averages. | |
* One way to use MACD is to wait for the fast line to “cross over” or “cross under” the slow line and | |
* enter the trade accordingly because it signals a new trend. | |
*/ | |
public function macd($pair='ETH-USD', $data=null, $period=10) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
# Create the MACD signal and pass in the three parameters: fast period, slow period, and the signal. | |
# we will want to tweak these periods later for now these are fine. | |
# data, fast period, slow period, signal period (2-100000) | |
# array $real [, integer $fastPeriod [, integer $slowPeriod [, integer $signalPeriod ]]] | |
$macd = trader_macd($data['close'], 12, 26, 9); | |
$macd_raw = $macd[0]; | |
$signal = $macd[1]; | |
$hist = $macd[2]; | |
$macd = $macd_raw[count($macd_raw)-1] - $signal[count($signal)-1]; | |
# Close position for the pair when the MACD signal is negative | |
if ($macd < 0) { | |
return -1; | |
# Enter the position for the pair when the MACD signal is positive | |
} elseif ($macd > 0) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $fastPeriod | |
* @param int $fastMAType | |
* @param int $slowPeriod | |
* @param int $slowMAType | |
* @param int $signalPeriod | |
* @param int $signalMAType | |
* | |
* @return int | |
* | |
* MACD indicator with controllable types and tweakable periods. | |
* | |
* TODO This will be for various backtesting and tests | |
* all periods are ranges of 2 to 100,000 | |
*/ | |
public function macdext($pair='ETH-USD', $data=null, $fastPeriod=12, $fastMAType=0, $slowPeriod=26, $slowMAType=0, $signalPeriod=9, $signalMAType=0) | |
{ | |
$fastMAType = $this->ma_type($fastMAType); | |
$slowMAType = $this->ma_type($slowMAType); | |
$signalMAType = $this->ma_type($signalMAType); | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
# Create the MACD signal and pass in the three parameters: fast period, slow period, and the signal. | |
# we will want to tweak these periods later for now these are fine. | |
$macd = trader_macdext($data['close'], $fastPeriod, $fastMAType, $slowPeriod, $slowMAType, $signalPeriod, $signalMAType); | |
$macd_raw = $macd[0]; | |
$signal = $macd[1]; | |
$hist = $macd[2]; | |
$macd = $macd_raw[count($macd_raw)-1] - $signal[count($signal)-1]; | |
# Close position for the pair when the MACD signal is negative | |
if ($macd < 0) { | |
return -1; | |
# Enter the position for the pair when the MACD signal is positive | |
} elseif ($macd > 0) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* Relative Strength Index indicator as a buy/sell signal. | |
* When the RSI is over 70, a pair can be seen as overbought and it's time to sell. | |
* When the RSI is below 30, a pair can be seen as oversold and it's time to buy. | |
* | |
* Similar to the stochastic in that it indicates overbought and oversold conditions. | |
* When RSI is above 70, it means that the market is overbought and we should look to sell. | |
* When RSI is below 30, it means that the market is oversold and we should look to buy. | |
* RSI can also be used to confirm trend formations. If you think a trend is forming, wait for | |
* RSI to go above or below 50 (depending on if you’re looking at an uptrend or downtrend) before you enter a trade. | |
*/ | |
public function rsi($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$LOW_RSI = 30; | |
$HIGH_RSI = 70; | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
$prev_close = $data['close'][count($data['close']) - 2]; // prior close | |
$current = $data['close'][count($data['close']) - 1]; // we assume this is current | |
$rsi = trader_rsi ($data['close'], $period); | |
$rsi = $rsi[count($rsi)-1]; // pick off the last | |
# RSI is above 70 and we own, sell | |
if ($rsi > $HIGH_RSI) { | |
return -1; | |
# RSI is below 30, buy | |
} elseif ($rsi < $LOW_RSI) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param $matype1 | |
* @param $matype2 | |
* | |
* @return int | |
* | |
* STOCH function to determine entry and exit points. | |
* When the stochastic oscillator dips below 10, the pair is determined to be oversold | |
* and a long position is opened. The position is exited when the indicator rises above 90 | |
* because the pair is thought to be overbought. | |
* | |
* Used to indicate overbought and oversold conditions. | |
* When the moving average lines are above 80, it means that the market is overbought and we should look to sell. | |
* When the moving average lines are below 20, it means that the market is oversold and we should look to buy. | |
*/ | |
public function stoch($pair='ETH-USD', $data=null, $matype1=TRADER_MA_TYPE_SMA, $matype2=TRADER_MA_TYPE_SMA) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
#$prev_close = $data['close'][count($data['close']) - 2]; // prior close | |
#$current = $data['close'][count($data['close']) - 1]; // we assume this is current | |
#high,low,close, fastk_period, slowk_period, slowk_matype, slowd_period, slowd_matype | |
$stoch = trader_stoch($data['high'], $data['low'], $data['close'], 5, 3, $matype1, 3, $matype2); | |
$slowk = $stoch[0]; | |
$slowd = $stoch[1]; | |
$slowk = $slowk[count($slowk) - 1]; | |
$slowd = $slowd[count($slowd) - 1]; | |
# If either the slowk or slowd are less than 10, the pair is | |
# 'oversold,' a long position is opened | |
if ($slowk < 10 || $slowd < 10) { | |
return 1; | |
# If either the slowk or slowd are larger than 90, the pair is | |
# 'overbought' and the position is closed. | |
}elseif ($slowk > 90 || $slowd > 90) { | |
return -1; | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* Money flow index | |
*/ | |
public function mfi($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
$mfi = trader_mfi($data['high'], $data['low'], $data['close'], $data['volume'], $period); | |
$mfi = $mfi[count($mfi) - 1]; | |
if ($mfi > 80) { | |
return -1; // overbought | |
} elseif ($mfi < 10) { | |
return 1; // underbought | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* On Balance Volume | |
* http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:on_balance_volume_obv | |
* signal assumption that volume precedes price on confirmation, divergence and breakouts | |
* | |
* use with mfi to confirm | |
*/ | |
public function obv($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair, $period, true, 12); // getting day 'noon' data for last two weeks | |
} | |
$_obv = trader_obv($data['close'], $data['volume']); | |
$earlier_obv = $_obv[count($_obv) - 3]; | |
$prior_obv = $_obv[count($_obv) - 2]; | |
$current_obv = $_obv[count($_obv) - 1]; | |
/** | |
* This forecasts a trend in the last three periods | |
* TODO: this needs to be tested more, we might need to look closer for crypto currencies | |
*/ | |
if (($current_obv > $prior_obv) && ($prior_obv > $earlier_obv)) { | |
return 1; | |
} elseif (($current_obv < $prior_obv) && ($prior_obv < $earlier_obv)) { | |
return -1; | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* @param int $acceleration | |
* @param int $maximum | |
* | |
* @return int | |
* | |
* Parabolic Stop And Reversal (SAR) | |
* | |
* http://www.babypips.com/school/elementary/common-chart-indicators/parabolic-sar.html | |
* | |
* This indicator is made to spot trend reversals, hence the name Parabolic Stop And Reversal (SAR). | |
* This is the easiest indicator to interpret because it only gives bullish and bearish signals. | |
* When the dots are above the candles, it is a sell signal. | |
* When the dots are below the candles, it is a buy signal. | |
* These are best used in trending markets that consist of long rallies and downturns. | |
*/ | |
public function sar($pair='ETH-USD', $data=null, $period=14, $acceleration=0.2, $maximum=20) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair, $period, true, 12); // getting day 'noon' data for last two weeks | |
} | |
// SEE TODO: let sar handle the acceleration itself for now | |
// SEE TODO: http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:parabolic_sar | |
# array $high , array $low [, float $acceleration [, float $maximum ]] | |
$_sar = trader_sar($data['high'], $data['low']); #, $acceleration, $maximum); | |
$earlier_sar = $_sar[count($_sar) - 3]; | |
$prior_sar = $_sar[count($_sar) - 2]; | |
$current_sar = $_sar[count($_sar) - 1]; | |
$last_high = $data['high'][count($data['high'])-1]; | |
$last_low = $data['low'][count($data['low'])-1]; | |
/** | |
* if the last three SAR points are above the candle (high) then it is a sell signal | |
* if the last three SAE points are below the candle (low) then is a buy signal | |
*/ | |
if (($current_sar > $last_high) && ($prior_sar > $last_high) && ($earlier_sar > $last_high)) { | |
return -1; //sell | |
} elseif (($current_sar < $last_low) && ($prior_sar < $last_low) && ($earlier_sar < $last_low)) { | |
return 1; // buy | |
} else { | |
return 0; // hold | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* Commodity Channel Index | |
*/ | |
public function cci($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
# array $high , array $low , array $close [, integer $timePeriod ] | |
$cci = trader_cci($data['high'], $data['low'], $data['close'], $period); | |
$cci = $cci[count($cci) - 1]; | |
if ($cci > 100) { | |
return -1; // overbought | |
} elseif ($cci < -100) { | |
return 1; // underbought | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* Chande Momentum Oscillator | |
*/ | |
public function cmo($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
$cmo = trader_cmo($data['close'], $period); | |
$cmo = $cmo[count($cmo) - 1]; | |
if ($cmo > 50) { | |
return -1; // overbought | |
} elseif ($cmo < -50) { | |
return 1; // underbought | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* Aroon Oscillator | |
*/ | |
public function aroonosc($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
$aroonosc = trader_aroonosc($data['high'], $data['low'], $period); | |
$aroonosc = $aroonosc[count($aroonosc) - 1]; | |
if ($aroonosc < -50) { | |
return -1; // overbought | |
} elseif ($aroonosc > 50) { | |
return 1; // underbought | |
} else { | |
return 0; | |
} | |
} | |
/** | |
* @param string $pair | |
* @param null $data | |
* @param int $period | |
* | |
* @return int | |
* | |
* Average Directional Movement Index | |
* | |
* TODO, this one needs more research for the returns | |
* http://www.investopedia.com/terms/a/adx.asp | |
* | |
* The ADX calculates the potential strength of a trend. | |
* It fluctuates from 0 to 100, with readings below 20 indicating a weak trend and readings above 50 signaling a strong trend. | |
* ADX can be used as confirmation whether the pair could possibly continue in its current trend or not. | |
* ADX can also be used to determine when one should close a trade early. For instance, when ADX starts to slide below 50, | |
* it indicates that the current trend is possibly losing steam. | |
*/ | |
public function adx($pair='ETH-USD', $data=null, $period=14) | |
{ | |
$util = new Util(); | |
if (empty($data)) { | |
$data = $util->getRecentData($pair); | |
} | |
$adx = trader_adx($data['high'], $data['low'], $data['close'], $period); | |
$adx = $adx[count($adx) - 1]; | |
if ($adx > 50) { | |
return -1; // overbought | |
} elseif ($adx < 20) { | |
return 1; // underbought | |
} else { | |
return 0; | |
} | |
} | |
public function allSignals($pair='ETH-USD', $data=null) | |
{ | |
$flags['adx'] = @$this->adx($pair, $data); | |
$flags['aroonosc'] = @$this->aroonosc($pair, $data); | |
$flags['cmo'] = @$this->cmo($pair, $data); | |
$flags['sar'] = @$this->sar($pair, $data); | |
$flags['cci'] = @$this->cci($pair, $data); | |
$flags['mfi'] = @$this->mfi($pair, $data); | |
$flags['obv'] = @$this->obv($pair, $data); | |
$flags['stoch'] = @$this->stoch($pair, $data); | |
$flags['rsi'] = @$this->rsi($pair, $data); | |
$flags['macd'] = @$this->macd($pair, $data); | |
$flags['bollingerBands'] = @$this->bollingerBands($pair, $data); | |
$flags['atr'] = @$this->atr($pair, $data); | |
return $flags; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment