stable/freqtrade/analyze.py

122 lines
4.3 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
2018-01-10 07:51:36 +00:00
from typing import Dict, List
2017-10-06 10:22:04 +00:00
2017-08-27 14:12:28 +00:00
import arrow
from pandas import DataFrame, to_datetime
2018-01-10 07:51:36 +00:00
from freqtrade.exchange import get_ticker_history
2018-01-15 08:35:11 +00:00
from freqtrade.strategy.strategy import Strategy
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) \
.rename(columns=columns)
if 'BV' in frame:
frame.drop('BV', 1, inplace=True)
2017-11-07 19:13:36 +00:00
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, pair: str) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
Performance Note: For the best performance be frugal on the number of indicators
you are using. Let uncomment only the indicator you are using in your strategies
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
"""
2018-01-15 08:35:11 +00:00
strategy = Strategy()
return strategy.populate_indicators(dataframe=dataframe, pair=pair)
2017-05-24 19:52:41 +00:00
def populate_buy_trend(dataframe: DataFrame, pair: str) -> 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
"""
2018-01-15 08:35:11 +00:00
strategy = Strategy()
return strategy.populate_buy_trend(dataframe=dataframe, pair=pair)
2017-05-24 19:52:41 +00:00
def populate_sell_trend(dataframe: DataFrame, pair: str) -> DataFrame:
2017-11-14 18:28:31 +00:00
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
2018-01-15 08:35:11 +00:00
strategy = Strategy()
return strategy.populate_sell_trend(dataframe=dataframe, pair=pair)
2017-11-14 18:28:31 +00:00
2017-05-24 19:52:41 +00:00
def analyze_ticker(ticker_history: List[Dict], pair: str) -> 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, pair)
dataframe = populate_buy_trend(dataframe, pair)
dataframe = populate_sell_trend(dataframe, pair)
return dataframe
2017-10-06 10:22:04 +00:00
# FIX: Maybe return False, if an error has occured,
# Otherwise we might mask an error as an non-signal-scenario
def get_signal(pair: str, interval: int) -> (bool, 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: (Buy, Sell) A bool-tuple indicating buy/sell signal
2017-05-24 19:52:41 +00:00
"""
ticker_hist = get_ticker_history(pair, interval)
if not ticker_hist:
logger.warning('Empty ticker history for pair %s', pair)
return (False, False) # return False ?
try:
dataframe = analyze_ticker(ticker_hist, pair)
except ValueError as ex:
logger.warning('Unable to analyze ticker for pair %s: %s', pair, str(ex))
return (False, False) # return False ?
except Exception as ex:
logger.exception('Unexpected error when analyzing ticker for pair %s: %s', pair, str(ex))
return (False, False) # return False ?
2017-09-27 22:43:32 +00:00
if dataframe.empty:
2018-01-10 06:40:40 +00:00
logger.warning('Empty dataframe for pair %s', pair)
return (False, False) # return False ?
2017-09-27 22:43:32 +00:00
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=(interval + 5)):
2018-01-10 06:40:40 +00:00
logger.warning('Too old dataframe for pair %s', pair)
return (False, False) # return False ?
2018-01-16 20:18:43 +00:00
(buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1
logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell))
return (buy, sell)