stable/freqtrade/analyze.py

164 lines
5.2 KiB
Python
Raw Normal View History

2017-11-18 07:34:32 +00:00
"""
Functions to analyze ticker data with indicators and produce buy and sell signals
"""
2017-10-06 10:22:04 +00:00
import logging
2017-05-24 19:52:41 +00:00
from datetime import timedelta
2017-11-20 21:26:32 +00:00
from enum import Enum
from typing import List, Dict
2017-10-06 10:22:04 +00:00
2017-08-27 14:12:28 +00:00
import arrow
import talib.abstract as ta
from pandas import DataFrame, to_datetime
2017-11-09 21:29:23 +00:00
from freqtrade.exchange import get_ticker_history
2017-11-18 07:34:57 +00:00
from freqtrade.vendor.qtpylib.indicators import awesome_oscillator, crossed_above
2017-05-24 19:52:41 +00:00
logger = logging.getLogger(__name__)
2017-11-25 02:28:52 +00:00
2017-11-14 17:06:03 +00:00
class SignalType(Enum):
2017-11-18 07:34:32 +00:00
""" Enum to distinguish between buy and sell signals """
2017-11-14 17:06:03 +00:00
BUY = "buy"
SELL = "sell"
2017-05-24 19:52:41 +00:00
def parse_ticker_dataframe(ticker: list) -> DataFrame:
"""
Analyses the trend for the given ticker history
:param ticker: See exchange.get_ticker_history
:return: DataFrame
"""
2017-11-07 19:13:36 +00:00
columns = {'C': 'close', 'V': 'volume', 'O': 'open', 'H': 'high', 'L': 'low', 'T': 'date'}
frame = DataFrame(ticker) \
.drop('BV', 1) \
2017-11-07 19:13:36 +00:00
.rename(columns=columns)
frame['date'] = to_datetime(frame['date'], utc=True, infer_datetime_format=True)
frame.sort_values('date', inplace=True)
return frame
2017-10-06 10:22:04 +00:00
def populate_indicators(dataframe: DataFrame) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
"""
dataframe['sar'] = ta.SAR(dataframe)
2017-09-12 08:47:23 +00:00
dataframe['adx'] = ta.ADX(dataframe)
2017-09-29 06:37:45 +00:00
stoch = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch['fastd']
dataframe['fastk'] = stoch['fastk']
dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
2017-09-29 06:37:45 +00:00
dataframe['mfi'] = ta.MFI(dataframe)
dataframe['rsi'] = ta.RSI(dataframe)
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
dataframe['ao'] = awesome_oscillator(dataframe)
2017-10-28 13:43:34 +00:00
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
2017-11-12 07:13:54 +00:00
hilbert = ta.HT_SINE(dataframe)
dataframe['htsine'] = hilbert['sine']
dataframe['htleadsine'] = hilbert['leadsine']
dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
2017-05-24 19:52:41 +00:00
return dataframe
def populate_buy_trend(dataframe: DataFrame) -> DataFrame:
2017-05-24 19:52:41 +00:00
"""
2017-11-14 18:28:31 +00:00
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(
(dataframe['rsi'] < 35) &
(dataframe['fastd'] < 35) &
(dataframe['adx'] > 30) &
(dataframe['plus_di'] > 0.5)
) |
(
(dataframe['adx'] > 65) &
(dataframe['plus_di'] > 0.5)
),
2017-09-12 08:47:23 +00:00
'buy'] = 1
2017-05-24 19:52:41 +00:00
return dataframe
2017-11-14 18:28:31 +00:00
def populate_sell_trend(dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(
(
(crossed_above(dataframe['rsi'], 70)) |
(crossed_above(dataframe['fastd'], 70))
) &
(dataframe['adx'] > 10) &
(dataframe['minus_di'] > 0)
) |
(
(dataframe['adx'] > 70) &
(dataframe['minus_di'] > 0.5)
),
2017-11-14 18:28:31 +00:00
'sell'] = 1
return dataframe
2017-05-24 19:52:41 +00:00
def analyze_ticker(ticker_history: List[Dict]) -> DataFrame:
"""
Parses the given ticker history and returns a populated DataFrame
add several TA indicators and buy signal to it
:return DataFrame with ticker data and indicator data
"""
dataframe = parse_ticker_dataframe(ticker_history)
dataframe = populate_indicators(dataframe)
dataframe = populate_buy_trend(dataframe)
2017-11-14 18:28:31 +00:00
dataframe = populate_sell_trend(dataframe)
return dataframe
2017-10-06 10:22:04 +00:00
2017-11-14 17:06:03 +00:00
def get_signal(pair: str, signal: SignalType) -> bool:
2017-05-24 19:52:41 +00:00
"""
2017-11-14 17:06:03 +00:00
Calculates current signal based several technical analysis indicators
2017-05-24 19:52:41 +00:00
:param pair: pair in format BTC_ANT or BTC-ANT
:return: True if pair is good for buying, False otherwise
2017-05-24 19:52:41 +00:00
"""
ticker_hist = get_ticker_history(pair)
if not ticker_hist:
logger.warning('Empty ticker history for pair %s', pair)
return False
try:
dataframe = analyze_ticker(ticker_hist)
except ValueError as ex:
logger.warning('Unable to analyze ticker for pair %s: %s', pair, str(ex))
return False
except Exception:
logger.exception('Unexpected error when analyzing ticker for pair %s.', pair)
return False
2017-09-27 22:43:32 +00:00
if dataframe.empty:
return False
2017-05-24 19:52:41 +00:00
latest = dataframe.iloc[-1]
# Check if dataframe is out of date
signal_date = arrow.get(latest['date'])
if signal_date < arrow.now() - timedelta(minutes=10):
return False
2017-11-14 17:06:03 +00:00
result = latest[signal.value] == 1
logger.debug('%s_trigger: %s (pair=%s, signal=%s)', signal.value, latest['date'], pair, result)
return result