From 484103b957974e2091c619626b1359074902b258 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Jul 2018 18:22:23 +0100 Subject: [PATCH 1/5] extract get_history_data from get_signal --- freqtrade/freqtradebot.py | 11 ++++++++--- freqtrade/strategy/interface.py | 3 +-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3527fba81..08eda2843 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -327,13 +327,18 @@ class FreqtradeBot(object): if not whitelist: raise DependencyException('No currency pairs in whitelist') + th = {} + for _pair in whitelist: + th[_pair] = self.exchange.get_ticker_history(_pair, interval) # Pick pair based on buy signals + bought_at_least_one = False for _pair in whitelist: - (buy, sell) = self.strategy.get_signal(self.exchange, _pair, interval) + (buy, sell) = self.strategy.get_signal(_pair, interval, th[_pair]) + if buy and not sell: - return self.execute_buy(_pair, stake_amount) - return False + bought_at_least_one |= self.execute_buy(_pair, stake_amount) + return bought_at_least_one def execute_buy(self, pair: str, stake_amount: float) -> bool: """ diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 9120d3e04..23b0563a2 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -107,14 +107,13 @@ class IStrategy(ABC): dataframe = self.populate_sell_trend(dataframe) return dataframe - def get_signal(self, exchange: Exchange, pair: str, interval: str) -> Tuple[bool, bool]: + def get_signal(self, pair: str, interval: str, ticker_hist: List[Dict]) -> Tuple[bool, bool]: """ Calculates current signal based several technical analysis indicators :param pair: pair in format ANT/BTC :param interval: Interval to use (in min) :return: (Buy, Sell) A bool-tuple indicating buy/sell signal """ - ticker_hist = exchange.get_ticker_history(pair, interval) if not ticker_hist: logger.warning('Empty ticker history for pair %s', pair) return False, False From 3324cdfcbe78a110e28ae0024b72c8c1cf46a537 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Jul 2018 18:58:49 +0100 Subject: [PATCH 2/5] add mock for get_history in patch_get_signal --- freqtrade/freqtradebot.py | 4 ++-- freqtrade/tests/test_freqtradebot.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 08eda2843..824607a2d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -337,8 +337,8 @@ class FreqtradeBot(object): (buy, sell) = self.strategy.get_signal(_pair, interval, th[_pair]) if buy and not sell: - bought_at_least_one |= self.execute_buy(_pair, stake_amount) - return bought_at_least_one + return self.execute_buy(_pair, stake_amount) + return False def execute_buy(self, pair: str, stake_amount: float) -> bool: """ diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 72f11abf9..fb6687a2c 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -47,6 +47,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None: :return: None """ freqtrade.strategy.get_signal = lambda e, s, t: value + freqtrade.exchange.get_ticker_history = lambda p, i: None def patch_RPCManager(mocker) -> MagicMock: From f2a9be36847e9eb98300da6d1992383cb35902cb Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Jul 2018 19:06:25 +0100 Subject: [PATCH 3/5] Adjust tests and remove legacy variable --- freqtrade/freqtradebot.py | 1 - freqtrade/tests/strategy/test_interface.py | 30 +++++++--------------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 824607a2d..3317a7b68 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -332,7 +332,6 @@ class FreqtradeBot(object): th[_pair] = self.exchange.get_ticker_history(_pair, interval) # Pick pair based on buy signals - bought_at_least_one = False for _pair in whitelist: (buy, sell) = self.strategy.get_signal(_pair, interval, th[_pair]) diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py index a016b7f96..de11a9d2c 100644 --- a/freqtrade/tests/strategy/test_interface.py +++ b/freqtrade/tests/strategy/test_interface.py @@ -20,74 +20,62 @@ _STRATEGY = DefaultStrategy(config={}) def test_returns_latest_buy_signal(mocker, default_conf): - mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock()) - exchange = get_patched_exchange(mocker, default_conf) mocker.patch.object( _STRATEGY, 'analyze_ticker', return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}]) ) - assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (True, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', MagicMock()) == (True, False) mocker.patch.object( _STRATEGY, 'analyze_ticker', return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}]) ) - assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (False, True) + assert _STRATEGY.get_signal('ETH/BTC', '5m', MagicMock()) == (False, True) def test_returns_latest_sell_signal(mocker, default_conf): - mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock()) - exchange = get_patched_exchange(mocker, default_conf) mocker.patch.object( _STRATEGY, 'analyze_ticker', return_value=DataFrame([{'sell': 1, 'buy': 0, 'date': arrow.utcnow()}]) ) - assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (False, True) + assert _STRATEGY.get_signal('ETH/BTC', '5m', MagicMock()) == (False, True) mocker.patch.object( _STRATEGY, 'analyze_ticker', return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}]) ) - assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (True, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', MagicMock()) == (True, False) def test_get_signal_empty(default_conf, mocker, caplog): - caplog.set_level(logging.INFO) - mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=None) - exchange = get_patched_exchange(mocker, default_conf) - assert (False, False) == _STRATEGY.get_signal(exchange, 'foo', default_conf['ticker_interval']) + assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], + None) assert log_has('Empty ticker history for pair foo', caplog.record_tuples) def test_get_signal_exception_valueerror(default_conf, mocker, caplog): caplog.set_level(logging.INFO) - mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1) - exchange = get_patched_exchange(mocker, default_conf) mocker.patch.object( _STRATEGY, 'analyze_ticker', side_effect=ValueError('xyz') ) - assert (False, False) == _STRATEGY.get_signal(exchange, 'foo', default_conf['ticker_interval']) + assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], 1) assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples) def test_get_signal_empty_dataframe(default_conf, mocker, caplog): caplog.set_level(logging.INFO) - mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1) - exchange = get_patched_exchange(mocker, default_conf) mocker.patch.object( _STRATEGY, 'analyze_ticker', return_value=DataFrame([]) ) - assert (False, False) == _STRATEGY.get_signal(exchange, 'xyz', default_conf['ticker_interval']) + assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], 1) assert log_has('Empty dataframe for pair xyz', caplog.record_tuples) def test_get_signal_old_dataframe(default_conf, mocker, caplog): caplog.set_level(logging.INFO) - mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1) - exchange = get_patched_exchange(mocker, default_conf) # default_conf defines a 5m interval. we check interval * 2 + 5m # this is necessary as the last candle is removed (partial candles) by default oldtime = arrow.utcnow().shift(minutes=-16) @@ -96,7 +84,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog): _STRATEGY, 'analyze_ticker', return_value=DataFrame(ticks) ) - assert (False, False) == _STRATEGY.get_signal(exchange, 'xyz', default_conf['ticker_interval']) + assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'], 1) assert log_has( 'Outdated history for pair xyz. Last tick is 16 minutes old', caplog.record_tuples From df3e76a65df49448315786f23af73d637a76889d Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Jul 2018 19:11:29 +0100 Subject: [PATCH 4/5] Remove legacy code, fix missed call --- freqtrade/freqtradebot.py | 11 ++++++----- freqtrade/strategy/interface.py | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3317a7b68..d7c5657c3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -327,13 +327,13 @@ class FreqtradeBot(object): if not whitelist: raise DependencyException('No currency pairs in whitelist') - th = {} + thistory = {} for _pair in whitelist: - th[_pair] = self.exchange.get_ticker_history(_pair, interval) + thistory[_pair] = self.exchange.get_ticker_history(_pair, interval) # Pick pair based on buy signals for _pair in whitelist: - (buy, sell) = self.strategy.get_signal(_pair, interval, th[_pair]) + (buy, sell) = self.strategy.get_signal(_pair, interval, thistory[_pair]) if buy and not sell: return self.execute_buy(_pair, stake_amount) @@ -499,8 +499,9 @@ class FreqtradeBot(object): (buy, sell) = (False, False) experimental = self.config.get('experimental', {}) if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'): - (buy, sell) = self.strategy.get_signal(self.exchange, - trade.pair, self.strategy.ticker_interval) + ticker = self.exchange.get_ticker_history(trade.pair, self.strategy.ticker_interval) + (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.ticker_interval, + ticker) should_sell = self.strategy.should_sell(trade, current_rate, datetime.utcnow(), buy, sell) if should_sell.sell_flag: diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 23b0563a2..a5145aabb 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,6 @@ from pandas import DataFrame from freqtrade import constants from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe -from freqtrade.exchange import Exchange from freqtrade.persistence import Trade logger = logging.getLogger(__name__) From 48cd468b6ca10438d367644ae970de35bc4ce16b Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 27 Jul 2018 07:40:27 +0100 Subject: [PATCH 5/5] Don't do all network calls at once without async --- freqtrade/freqtradebot.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d7c5657c3..46fbb3a38 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -327,13 +327,11 @@ class FreqtradeBot(object): if not whitelist: raise DependencyException('No currency pairs in whitelist') - thistory = {} - for _pair in whitelist: - thistory[_pair] = self.exchange.get_ticker_history(_pair, interval) # Pick pair based on buy signals for _pair in whitelist: - (buy, sell) = self.strategy.get_signal(_pair, interval, thistory[_pair]) + thistory = self.exchange.get_ticker_history(_pair, interval) + (buy, sell) = self.strategy.get_signal(_pair, interval, thistory) if buy and not sell: return self.execute_buy(_pair, stake_amount)