From 179b10e6e72f7ad2ad8a8e20f20d6c2123ba55b4 Mon Sep 17 00:00:00 2001 From: Gert Wohlgemuth Date: Thu, 14 Jun 2018 20:27:41 -0700 Subject: [PATCH 1/5] working on refacturing of the strategy class --- freqtrade/analyze.py | 27 ++++++++------- freqtrade/strategy/interface.py | 57 +++++++++++++++++++++++++++++-- freqtrade/strategy/resolver.py | 3 +- freqtrade/tests/test_dataframe.py | 2 +- 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/freqtrade/analyze.py b/freqtrade/analyze.py index f18ae291c..dcf66b0a2 100644 --- a/freqtrade/analyze.py +++ b/freqtrade/analyze.py @@ -14,7 +14,6 @@ from freqtrade.exchange import get_ticker_history from freqtrade.persistence import Trade from freqtrade.strategy.resolver import StrategyResolver, IStrategy - logger = logging.getLogger(__name__) @@ -31,6 +30,7 @@ class Analyze(object): Analyze class contains everything the bot need to determine if the situation is good for buying or selling. """ + def __init__(self, config: dict) -> None: """ Init Analyze @@ -62,10 +62,10 @@ class Analyze(object): 'close': 'last', 'volume': 'max', }) - frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle + frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle return frame - def populate_indicators(self, dataframe: DataFrame) -> DataFrame: + def populate_indicators(self, dataframe: DataFrame, pair: str = None) -> DataFrame: """ Adds several different TA indicators to the given DataFrame @@ -73,23 +73,23 @@ class Analyze(object): 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. """ - return self.strategy.populate_indicators(dataframe=dataframe) + return self.strategy.advise_indicators(dataframe=dataframe, pair=pair) - def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: + def populate_buy_trend(self, dataframe: DataFrame, pair: str = None) -> DataFrame: """ Based on TA indicators, populates the buy signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with buy column """ - return self.strategy.populate_buy_trend(dataframe=dataframe) + return self.strategy.advise_buy(dataframe=dataframe, pair=pair) - def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame: + def populate_sell_trend(self, dataframe: DataFrame, pair: str = None) -> DataFrame: """ Based on TA indicators, populates the sell signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with buy column """ - return self.strategy.populate_sell_trend(dataframe=dataframe) + return self.strategy.advise_sell(dataframe=dataframe, pair=pair) def get_ticker_interval(self) -> str: """ @@ -98,16 +98,17 @@ class Analyze(object): """ return self.strategy.ticker_interval - def analyze_ticker(self, ticker_history: List[Dict]) -> DataFrame: + def analyze_ticker(self, 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 = self.parse_ticker_dataframe(ticker_history) - dataframe = self.populate_indicators(dataframe) - dataframe = self.populate_buy_trend(dataframe) - dataframe = self.populate_sell_trend(dataframe) + dataframe = self.populate_indicators(dataframe, pair) + dataframe = self.populate_buy_trend(dataframe, pair) + dataframe = self.populate_sell_trend(dataframe, pair) return dataframe def get_signal(self, pair: str, interval: str) -> Tuple[bool, bool]: @@ -123,7 +124,7 @@ class Analyze(object): return False, False try: - dataframe = self.analyze_ticker(ticker_hist) + dataframe = self.analyze_ticker(ticker_hist, pair) except ValueError as error: logger.warning( 'Unable to analyze ticker for pair %s: %s', diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 4ae358c6f..6ffd7b981 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -6,6 +6,7 @@ from typing import Dict from abc import ABC, abstractmethod from pandas import DataFrame +import warnings class IStrategy(ABC): @@ -19,30 +20,80 @@ class IStrategy(ABC): ticker_interval -> str: value of the ticker interval to use for the strategy """ + # associated minimal roi minimal_roi: Dict + + # associated stoploss stoploss: float + + # associated ticker interval ticker_interval: str - @abstractmethod + # configuration used, just in case the strategy want's to use it for something + config: dict = {} + def populate_indicators(self, dataframe: DataFrame) -> DataFrame: """ Populate indicators that will be used in the Buy and Sell strategy :param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe() :return: a Dataframe with all mandatory indicators for the strategies """ + warnings.warn("deprecated - please replace this method with advise_indicators!", DeprecationWarning) + return dataframe - @abstractmethod def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: """ Based on TA indicators, populates the buy signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with buy column """ + warnings.warn("deprecated - please replace this method with advise_buy!", DeprecationWarning) + dataframe.loc[ + ( + ), + 'buy'] = 0 + return dataframe - @abstractmethod def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame: """ Based on TA indicators, populates the sell signal for the given dataframe :param dataframe: DataFrame :return: DataFrame with sell column """ + warnings.warn("deprecated - please replace this method with advise_sell!", DeprecationWarning) + dataframe.loc[ + ( + ), + 'sell'] = 0 + return dataframe + + def advise_indicators(self, dataframe: DataFrame, pair: str) -> DataFrame: + """ + + This wraps around the internal method + + Populate indicators that will be used in the Buy and Sell strategy + :param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe() + :param pair: The currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + return self.populate_indicators(dataframe) + + def advise_buy(self, dataframe: DataFrame, pair: str) -> DataFrame: + """ + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :param pair: The currently traded pair + :return: DataFrame with buy column + """ + + return self.populate_buy_trend(dataframe) + + def advise_sell(self, dataframe: DataFrame, pair: str) -> DataFrame: + """ + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :param pair: The currently traded pair + :return: DataFrame with sell column + """ + return self.populate_sell_trend(dataframe) diff --git a/freqtrade/strategy/resolver.py b/freqtrade/strategy/resolver.py index 3fd39bca3..abdfc2223 100644 --- a/freqtrade/strategy/resolver.py +++ b/freqtrade/strategy/resolver.py @@ -13,7 +13,6 @@ from typing import Optional, Dict, Type from freqtrade import constants from freqtrade.strategy.interface import IStrategy - logger = logging.getLogger(__name__) @@ -36,6 +35,8 @@ class StrategyResolver(object): self.strategy: IStrategy = self._load_strategy(strategy_name, extra_dir=config.get('strategy_path')) + self.strategy.config = config + # Set attributes # Check if we need to override configuration if 'minimal_roi' in config: diff --git a/freqtrade/tests/test_dataframe.py b/freqtrade/tests/test_dataframe.py index fd461a503..43003cb83 100644 --- a/freqtrade/tests/test_dataframe.py +++ b/freqtrade/tests/test_dataframe.py @@ -16,7 +16,7 @@ def load_dataframe_pair(pairs): dataframe = ld[pairs[0]] analyze = Analyze({'strategy': 'DefaultStrategy'}) - dataframe = analyze.analyze_ticker(dataframe) + dataframe = analyze.analyze_ticker(dataframe, pairs[0]) return dataframe From 77e46ebc561575191b57c6214f771f46b9cb177b Mon Sep 17 00:00:00 2001 From: Gert Wohlgemuth Date: Fri, 15 Jun 2018 09:59:34 -0700 Subject: [PATCH 2/5] revised code --- freqtrade/strategy/interface.py | 21 ++++++--------------- freqtrade/strategy/resolver.py | 1 - 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6ffd7b981..e9e9e6d7b 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -2,11 +2,11 @@ IStrategy interface This module defines the interface to apply for strategies """ -from typing import Dict -from abc import ABC, abstractmethod - -from pandas import DataFrame import warnings +from typing import Dict + +from abc import ABC +from pandas import DataFrame class IStrategy(ABC): @@ -29,9 +29,6 @@ class IStrategy(ABC): # associated ticker interval ticker_interval: str - # configuration used, just in case the strategy want's to use it for something - config: dict = {} - def populate_indicators(self, dataframe: DataFrame) -> DataFrame: """ Populate indicators that will be used in the Buy and Sell strategy @@ -48,10 +45,7 @@ class IStrategy(ABC): :return: DataFrame with buy column """ warnings.warn("deprecated - please replace this method with advise_buy!", DeprecationWarning) - dataframe.loc[ - ( - ), - 'buy'] = 0 + dataframe.loc[(), 'buy'] = 0 return dataframe def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame: @@ -61,10 +55,7 @@ class IStrategy(ABC): :return: DataFrame with sell column """ warnings.warn("deprecated - please replace this method with advise_sell!", DeprecationWarning) - dataframe.loc[ - ( - ), - 'sell'] = 0 + dataframe.loc[(), 'sell'] = 0 return dataframe def advise_indicators(self, dataframe: DataFrame, pair: str) -> DataFrame: diff --git a/freqtrade/strategy/resolver.py b/freqtrade/strategy/resolver.py index abdfc2223..6244dc4e7 100644 --- a/freqtrade/strategy/resolver.py +++ b/freqtrade/strategy/resolver.py @@ -35,7 +35,6 @@ class StrategyResolver(object): self.strategy: IStrategy = self._load_strategy(strategy_name, extra_dir=config.get('strategy_path')) - self.strategy.config = config # Set attributes # Check if we need to override configuration From b034c444e5071db67e26c841bca1b51bd1168b9f Mon Sep 17 00:00:00 2001 From: Gert Wohlgemuth Date: Sat, 16 Jun 2018 10:55:15 -0700 Subject: [PATCH 3/5] fixed some default settings --- freqtrade/freqtradebot.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index bf29029af..ecd5cf952 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -156,7 +156,7 @@ class FreqtradeBot(object): state_changed |= self.process_maybe_execute_sell(trade) # Then looking for buy opportunities - if (self.config['disable_buy']): + if (self.config.get('disable_buy', False)): logger.info('Buy disabled...') else: if len(trades) < self.config['max_open_trades']: @@ -250,7 +250,7 @@ class FreqtradeBot(object): balance = self.config['bid_strategy']['ask_last_balance'] ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask']) - if self.config['bid_strategy']['use_book_order']: + if self.config['bid_strategy'].get('use_book_order', False): logger.info('Getting price from Order Book') orderBook = exchange.get_order_book(pair) orderBook_rate = orderBook['bids'][self.config['bid_strategy']['book_order_top']][0] @@ -444,14 +444,14 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \ if self.config.get('experimental', {}).get('use_sell_signal'): (buy, sell) = self.analyze.get_signal(trade.pair, self.analyze.get_ticker_interval()) - if self.config['ask_strategy']['use_book_order']: + if 'ask_strategy' in self.config and self.config['ask_strategy'].get('use_book_order', False): logger.info('Using order book for selling...') orderBook = exchange.get_order_book(trade.pair) # logger.debug('Order book %s',orderBook) orderBook_min = self.config['ask_strategy']['book_order_min'] orderBook_max = self.config['ask_strategy']['book_order_max'] - for i in range(orderBook_min, orderBook_max+1): - orderBook_rate = orderBook['asks'][i-1][0] + for i in range(orderBook_min, orderBook_max + 1): + orderBook_rate = orderBook['asks'][i - 1][0] # if orderbook has higher rate (high profit), # use orderbook, otherwise just use sell rate if (sell_rate < orderBook_rate): @@ -502,7 +502,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \ ordertime = arrow.get(order['datetime']).datetime # Check if trade is still actually open - if (int(order['filled']) == 0) and (order['status'] == 'open'): + if order['status'] == 'open': if order['side'] == 'buy' and ordertime < buy_timeoutthreashold: self.handle_timedout_limit_buy(trade, order) elif order['side'] == 'sell' and ordertime < sell_timeoutthreashold: From 65cdd4046f756f0e2c8ecb2e5ed44fdd1e9f7232 Mon Sep 17 00:00:00 2001 From: peterkorodi Date: Sun, 17 Jun 2018 11:00:34 +0200 Subject: [PATCH 4/5] Update stoploss.md --- docs/stoploss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/stoploss.md b/docs/stoploss.md index ff93fd1d8..110cfa0d1 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -40,7 +40,7 @@ due to demand, it is possible to have a default stop loss, when you are in the r the system will utilize a new stop loss, which can be a different value. For example your default stop loss is 5%, but once you are in the black, it will be changed to be only a 1% stop loss -this can be configured in the main confiuration file, the following way: +this can be configured in the main configuration file, the following way: ``` "trailing_stop": { From 0568ce20d32837a287f412f03d018566597176c0 Mon Sep 17 00:00:00 2001 From: Gert Wohlgemuth Date: Mon, 18 Jun 2018 23:03:45 -0700 Subject: [PATCH 5/5] reduced logging --- freqtrade/strategy/resolver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/strategy/resolver.py b/freqtrade/strategy/resolver.py index 9c354a3c5..845d20874 100644 --- a/freqtrade/strategy/resolver.py +++ b/freqtrade/strategy/resolver.py @@ -126,7 +126,7 @@ class StrategyResolver(object): strategy_name = os.path.splitext(name)[0] - print("stored downloaded stat at: {}".format(temp)) + # print("stored downloaded stat at: {}".format(temp)) # register temp path with the bot abs_paths.insert(0, temp.absolute())