Skip to content

Instantly share code, notes, and snippets.

@ak9250
Created June 4, 2017 20:12
Show Gist options
  • Save ak9250/65f9ac6fe8ccd3200044da0b299b2d5c to your computer and use it in GitHub Desktop.
Save ak9250/65f9ac6fe8ccd3200044da0b299b2d5c to your computer and use it in GitHub Desktop.
indicators
/**
* 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