From eac98dbbd69954573bea3671aebc91938639f2f6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 27 Jan 2021 07:29:40 +0100 Subject: [PATCH 001/519] Version bump to 2021.1 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index e96e7f530..74c8c412c 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = 'develop' +__version__ = '2021.1' if __version__ == 'develop': From aea8f05d10946ca488a2f1ec2564e92d0922feff Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 24 Feb 2021 06:39:59 +0100 Subject: [PATCH 002/519] Version bump 2021.2 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 74c8c412c..2205d284d 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.1' +__version__ = '2021.2' if __version__ == 'develop': From 834f00f5803431270f736d3a098eb9c5623cfbe8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 24 Feb 2021 06:46:07 +0100 Subject: [PATCH 003/519] Refresh slack link --- CONTRIBUTING.md | 2 +- README.md | 4 ++-- docs/developer.md | 2 +- docs/faq.md | 2 +- docs/index.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index afa41ed33..c29d6e632 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Few pointers for contributions: - New features need to contain unit tests, must conform to PEP8 (max-line-length = 100) and should be documented with the introduction PR. - PR's can be declared as `[WIP]` - which signify Work in Progress Pull Requests (which are not finished). -If you are unsure, discuss the feature on our [discord server](https://discord.gg/MA9v74M), on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-l9d9iqgl-9cVBIeBkCBa8j6upSmd_NA) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR. +If you are unsure, discuss the feature on our [discord server](https://discord.gg/MA9v74M), on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR. ## Getting started diff --git a/README.md b/README.md index 7ef0d4ce7..c3a665c47 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ For any questions not covered by the documentation or for further information ab Please check out our [discord server](https://discord.gg/MA9v74M). -You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-l9d9iqgl-9cVBIeBkCBa8j6upSmd_NA). +You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw). ### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue) @@ -178,7 +178,7 @@ to understand the requirements before sending your pull-requests. Coding is not a necessity to contribute - maybe start with improving our documentation? Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase. -**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [discord](https://discord.gg/MA9v74M) or [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-k9o2v5ut-jX8Mc4CwNM8CDc2Dyg96YA). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. +**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [discord](https://discord.gg/MA9v74M) or [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. **Important:** Always create your PR against the `develop` branch, not `stable`. diff --git a/docs/developer.md b/docs/developer.md index c09e528bf..4b8c64530 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -2,7 +2,7 @@ This page is intended for developers of Freqtrade, people who want to contribute to the Freqtrade codebase or documentation, or people who want to understand the source code of the application they're running. -All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel on [discord](https://discord.gg/MA9v74M) or [slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-l9d9iqgl-9cVBIeBkCBa8j6upSmd_NA) where you can ask questions. +All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel on [discord](https://discord.gg/MA9v74M) or [slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) where you can ask questions. ## Documentation diff --git a/docs/faq.md b/docs/faq.md index 87b0893bd..93b806dca 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -142,7 +142,7 @@ freqtrade hyperopt --hyperopt SampleHyperopt --hyperopt-loss SharpeHyperOptLossD ### Why does it take a long time to run hyperopt? -* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/zt-l9d9iqgl-9cVBIeBkCBa8j6upSmd_NA) - or the Freqtrade [discord community](https://discord.gg/X89cVG). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you. +* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) - or the Freqtrade [discord community](https://discord.gg/X89cVG). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you. * If you wonder why it can take from 20 minutes to days to do 1000 epochs here are some answers: diff --git a/docs/index.md b/docs/index.md index db5088707..9d1a1532e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -81,7 +81,7 @@ For any questions not covered by the documentation or for further information ab Please check out our [discord server](https://discord.gg/MA9v74M). -You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-l9d9iqgl-9cVBIeBkCBa8j6upSmd_NA). +You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw). ## Ready to try? From 7fb34f7e25e1a9d08b3200fb8a0c7b57df773a52 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 27 Mar 2021 11:34:11 +0100 Subject: [PATCH 004/519] Version bump 2021.3 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 2205d284d..5e2a1f88e 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.2' +__version__ = '2021.3' if __version__ == 'develop': From 2b78ee254cab129aed9448e201e496961b3cb788 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Apr 2021 21:06:32 +0200 Subject: [PATCH 005/519] Version bump to 2021.4 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 5e2a1f88e..68bcad396 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.3' +__version__ = '2021.4' if __version__ == 'develop': From 1ffc53b3b5c770198bd664c534ae7a1f251a08d6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 May 2021 19:21:26 +0200 Subject: [PATCH 006/519] Fix docs typo for CategoryParameter closes #4852 --- docs/hyperopt.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index b3fdc699b..5f1f9bffa 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -251,9 +251,9 @@ We continue to define hyperoptable parameters: class MyAwesomeStrategy(IStrategy): buy_adx = IntParameter(20, 40, default=30) buy_rsi = IntParameter(20, 40, default=30) - buy_adx_enabled = CategoricalParameter([True, False]), - buy_rsi_enabled = CategoricalParameter([True, False]), - buy_trigger = CategoricalParameter(['bb_lower', 'macd_cross_signal']), + buy_adx_enabled = CategoricalParameter([True, False]) + buy_rsi_enabled = CategoricalParameter([True, False]) + buy_trigger = CategoricalParameter(['bb_lower', 'macd_cross_signal']) ``` Above definition says: I have five parameters I want to randomly combine to find the best combination. From 3d11df68e32e9415146f6920746dc4722a527116 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 3 May 2021 08:33:06 +0200 Subject: [PATCH 007/519] Be explicit with space assignment in documentation --- docs/hyperopt.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 5f1f9bffa..d8f4a8071 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -249,11 +249,11 @@ We continue to define hyperoptable parameters: ```python class MyAwesomeStrategy(IStrategy): - buy_adx = IntParameter(20, 40, default=30) - buy_rsi = IntParameter(20, 40, default=30) - buy_adx_enabled = CategoricalParameter([True, False]) - buy_rsi_enabled = CategoricalParameter([True, False]) - buy_trigger = CategoricalParameter(['bb_lower', 'macd_cross_signal']) + buy_adx = IntParameter(20, 40, default=30, space="buy") + buy_rsi = IntParameter(20, 40, default=30, space="buy") + buy_adx_enabled = CategoricalParameter([True, False], space="buy") + buy_rsi_enabled = CategoricalParameter([True, False], space="buy") + buy_trigger = CategoricalParameter(['bb_lower', 'macd_cross_signal'], space="buy") ``` Above definition says: I have five parameters I want to randomly combine to find the best combination. @@ -262,6 +262,10 @@ Then we have three category variables. First two are either `True` or `False`. We use these to either enable or disable the ADX and RSI guards. The last one we call `trigger` and use it to decide which buy trigger we want to use. +!!! Note "Parameter space assignment" + Parameters must either be assigned to a variable named `buy_*` or `sell_*` - or contain `space='buy'` | `space='sell'` to be assigned to a space correctly. + If no parameter is available for a space, you'll receive the error that no space was found when running hyperopt. + So let's write the buy strategy using these values: ```python From 0c9b913cad25e0f75cde562415014116e28c3153 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 27 May 2021 11:10:10 +0200 Subject: [PATCH 008/519] Version bump 2021.5 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 68bcad396..ed0c70417 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.4' +__version__ = '2021.5' if __version__ == 'develop': From 639c83575bd0094141f15d4f58814a1a59c565b1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 27 May 2021 13:08:28 +0200 Subject: [PATCH 009/519] Fix csv-export error with new hyperopt format --- freqtrade/optimize/hyperopt_tools.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 49e70913f..38cb0854e 100644 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -376,10 +376,11 @@ class HyperoptTools(): trials['Avg profit'] = trials['Avg profit'].apply( lambda x: f'{x * perc_multi:,.2f}%' if not isna(x) else "" ) - trials['Avg duration'] = trials['Avg duration'].apply( - lambda x: f'{x:,.1f} m' if isinstance( - x, float) else f"{x.total_seconds() // 60:,.1f} m" if not isna(x) else "" - ) + if perc_multi == 1: + trials['Avg duration'] = trials['Avg duration'].apply( + lambda x: f'{x:,.1f} m' if isinstance( + x, float) else f"{x.total_seconds() // 60:,.1f} m" if not isna(x) else "" + ) trials['Objective'] = trials['Objective'].apply( lambda x: f'{x:,.5f}' if x != 100000 else "" ) From 1cb057bda7f49a45ac7de38254374ed045ef27ef Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Jun 2021 14:01:23 +0200 Subject: [PATCH 010/519] Version bump 2021.6 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index ed0c70417..d76b655d7 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.5' +__version__ = '2021.6' if __version__ == 'develop': From 055229a44a8288b4f32ccd2604d08c1d1e2dd045 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Sat, 3 Jul 2021 11:39:14 +0200 Subject: [PATCH 011/519] first iteration of volume pairlist with range lookback --- freqtrade/plugins/pairlist/VolumePairList.py | 71 +++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 8eff137b0..af26201a9 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -11,6 +11,9 @@ from cachetools.ttl import TTLCache from freqtrade.exceptions import OperationalException from freqtrade.plugins.pairlist.IPairList import IPairList +import arrow +from copy import deepcopy +from freqtrade.exchange import timeframe_to_minutes logger = logging.getLogger(__name__) @@ -36,6 +39,25 @@ class VolumePairList(IPairList): self._min_value = self._pairlistconfig.get('min_value', 0) self._refresh_period = self._pairlistconfig.get('refresh_period', 1800) self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) + self._lookback_days = self._pairlistconfig.get('lookback_days', 0) + self._lookback_timeframe = self._pairlistconfig.get('lookback_timeframe', '1d') + self._lookback_period = self._pairlistconfig.get('lookback_period', 0) + + # overwrite lookback timeframe and days when lookback_days is set + if self._lookback_days > 0: + self._lookback_timeframe = '1d' + self._lookback_period = self._lookback_days + + self._tf_in_min = timeframe_to_minutes(self._lookback_timeframe) + self._tf_in_secs = self._tf_in_min * 60 + + self._use_range = (self._tf_in_min > 0) & (self._lookback_period > 0) + + if self._use_range & (self._refresh_period < self._tf_in_secs): + raise OperationalException( + f'Refresh period of {self._refresh_period} seconds is smaller than one timeframe of {self._lookback_timeframe}. ' + f'Please adjust refresh_period to at least {self._tf_in_secs} and restart the bot.' + ) if not self._exchange.exchange_has('fetchTickers'): raise OperationalException( @@ -47,6 +69,14 @@ class VolumePairList(IPairList): raise OperationalException( f'key {self._sort_key} not in {SORT_VALUES}') + + if self._lookback_period < 0: + raise OperationalException("VolumeFilter requires lookback_period to be >= 0") + if self._lookback_period > exchange.ohlcv_candle_limit(self._lookback_timeframe): + raise OperationalException("VolumeFilter requires lookback_period to not " + "exceed exchange max request size " + f"({exchange.ohlcv_candle_limit(self._lookback_timeframe)})") + @property def needstickers(self) -> bool: """ @@ -78,7 +108,6 @@ class VolumePairList(IPairList): # Item found - no refresh necessary return pairlist else: - # Use fresh pairlist # Check if pair quote currency equals to the stake currency. filtered_tickers = [ @@ -100,9 +129,44 @@ class VolumePairList(IPairList): :param tickers: Tickers (from exchange.get_tickers()). May be cached. :return: new whitelist """ - # Use the incoming pairlist. + + # Use the incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] + if self._use_range == True: + since_ms = int(arrow.utcnow() + .floor('minute') + .shift(minutes=-(self._lookback_period * self._tf_in_min) - self._tf_in_min) + .float_timestamp) * 1000 + + self.log_once(f"Using volume range of {self._lookback_period} {self._lookback_timeframe} candles from {since_ms}", logger.info) + needed_pairs = [(p, self._lookback_timeframe) for p in [s['symbol'] for s in filtered_tickers] if p not in self._pair_cache] + # Get all candles + candles = {} + if needed_pairs: + candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, + cache=False) + + for i,p in enumerate(filtered_tickers): + # for p in deepcopy([s['symbol'] for s in filtered_tickers]): + pair_candles = candles[(p['symbol'], self._lookback_timeframe)] if (p['symbol'], self._lookback_timeframe) in candles else None + #print(p['symbol'], " 24h quote volume = ",filtered_tickers[i]['quoteVolume']) + + #if p['symbol'] == 'BCC/USDT': + #print(pair_candles) + #quit() + + print(p['symbol'], " 24h quote volume = ",filtered_tickers[i]['quoteVolume']) + if not pair_candles.empty: + pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] + pair_candles['close']) / 3 + pair_candles['quoteVolume'] = pair_candles['volume'] * pair_candles['typical_price'] + # print(p['symbol'], " range quote volume = ", pair_candles['quoteVolume'].sum()) + filtered_tickers[i]['quoteVolume'] = pair_candles['quoteVolume'].sum() + else: + filtered_tickers[i]['quoteVolume'] = 0 + + print(p['symbol'], " range quote volume = ",filtered_tickers[i]['quoteVolume']) + if self._min_value > 0: filtered_tickers = [ v for v in filtered_tickers if v[self._sort_key] > self._min_value] @@ -112,9 +176,12 @@ class VolumePairList(IPairList): # Validate whitelist to only have active market pairs pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers]) pairs = self.verify_blacklist(pairs, logger.info) + # Limit pairlist to the requested number of pairs + pairs = pairs[:self._number_pairs] self.log_once(f"Searching {self._number_pairs} pairs: {pairs}", logger.info) + quit() return pairs From 62da4b452ccbab16044adc03f9c74d2821710d13 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Sat, 3 Jul 2021 11:47:17 +0200 Subject: [PATCH 012/519] code cleanup and comments --- freqtrade/plugins/pairlist/VolumePairList.py | 36 +++++++------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index af26201a9..ae6be54ba 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -4,16 +4,14 @@ Volume PairList provider Provides dynamic pair list based on trade volumes """ import logging +import arrow from typing import Any, Dict, List from cachetools.ttl import TTLCache from freqtrade.exceptions import OperationalException -from freqtrade.plugins.pairlist.IPairList import IPairList - -import arrow -from copy import deepcopy from freqtrade.exchange import timeframe_to_minutes +from freqtrade.plugins.pairlist.IPairList import IPairList logger = logging.getLogger(__name__) @@ -48,15 +46,16 @@ class VolumePairList(IPairList): self._lookback_timeframe = '1d' self._lookback_period = self._lookback_days + # get timeframe in minutes and seconds self._tf_in_min = timeframe_to_minutes(self._lookback_timeframe) - self._tf_in_secs = self._tf_in_min * 60 + self._tf_in_sec = self._tf_in_min * 60 self._use_range = (self._tf_in_min > 0) & (self._lookback_period > 0) if self._use_range & (self._refresh_period < self._tf_in_secs): raise OperationalException( f'Refresh period of {self._refresh_period} seconds is smaller than one timeframe of {self._lookback_timeframe}. ' - f'Please adjust refresh_period to at least {self._tf_in_secs} and restart the bot.' + f'Please adjust refresh_period to at least {self._tf_in_sec} and restart the bot.' ) if not self._exchange.exchange_has('fetchTickers'): @@ -129,10 +128,10 @@ class VolumePairList(IPairList): :param tickers: Tickers (from exchange.get_tickers()). May be cached. :return: new whitelist """ - # Use the incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] + # get lookback period in ms, for exchange ohlcv fetch if self._use_range == True: since_ms = int(arrow.utcnow() .floor('minute') @@ -141,31 +140,25 @@ class VolumePairList(IPairList): self.log_once(f"Using volume range of {self._lookback_period} {self._lookback_timeframe} candles from {since_ms}", logger.info) needed_pairs = [(p, self._lookback_timeframe) for p in [s['symbol'] for s in filtered_tickers] if p not in self._pair_cache] + # Get all candles candles = {} if needed_pairs: - candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, - cache=False) + candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, cache=False) for i,p in enumerate(filtered_tickers): - # for p in deepcopy([s['symbol'] for s in filtered_tickers]): pair_candles = candles[(p['symbol'], self._lookback_timeframe)] if (p['symbol'], self._lookback_timeframe) in candles else None - #print(p['symbol'], " 24h quote volume = ",filtered_tickers[i]['quoteVolume']) - - #if p['symbol'] == 'BCC/USDT': - #print(pair_candles) - #quit() - - print(p['symbol'], " 24h quote volume = ",filtered_tickers[i]['quoteVolume']) + # print(p['symbol'], " 24h quote volume = ",filtered_tickers[i]['quoteVolume']) + # in case of candle data calculate typical price and quoteVolume for candle if not pair_candles.empty: pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] + pair_candles['close']) / 3 pair_candles['quoteVolume'] = pair_candles['volume'] * pair_candles['typical_price'] - # print(p['symbol'], " range quote volume = ", pair_candles['quoteVolume'].sum()) + # replace quoteVolume with range sum filtered_tickers[i]['quoteVolume'] = pair_candles['quoteVolume'].sum() else: filtered_tickers[i]['quoteVolume'] = 0 - print(p['symbol'], " range quote volume = ",filtered_tickers[i]['quoteVolume']) + # print(p['symbol'], " range quote volume = ",filtered_tickers[i]['quoteVolume']) if self._min_value > 0: filtered_tickers = [ @@ -175,13 +168,10 @@ class VolumePairList(IPairList): # Validate whitelist to only have active market pairs pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers]) - pairs = self.verify_blacklist(pairs, logger.info) - + pairs = self.verify_blacklist(pairs, logger.info) # Limit pairlist to the requested number of pairs - pairs = pairs[:self._number_pairs] self.log_once(f"Searching {self._number_pairs} pairs: {pairs}", logger.info) - quit() return pairs From 53f963dd736188c2cc5efe27a52ccbb40c2af12c Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Sat, 3 Jul 2021 11:49:05 +0200 Subject: [PATCH 013/519] fixed `self._tf_in_secs` to `self._tf_in_sec` --- freqtrade/plugins/pairlist/VolumePairList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index ae6be54ba..4e9102243 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -52,7 +52,7 @@ class VolumePairList(IPairList): self._use_range = (self._tf_in_min > 0) & (self._lookback_period > 0) - if self._use_range & (self._refresh_period < self._tf_in_secs): + if self._use_range & (self._refresh_period < self._tf_in_sec): raise OperationalException( f'Refresh period of {self._refresh_period} seconds is smaller than one timeframe of {self._lookback_timeframe}. ' f'Please adjust refresh_period to at least {self._tf_in_sec} and restart the bot.' From 3d9f3eeb07bc67e08d7ce9b3ba1506a422dae774 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sat, 3 Jul 2021 23:58:04 +0700 Subject: [PATCH 014/519] feat(agefilter): add max_days_listed --- config_full.json.example | 2 +- docs/includes/pairlists.md | 4 +-- freqtrade/plugins/pairlist/AgeFilter.py | 17 +++++++++--- tests/plugins/test_pairlist.py | 37 +++++++++++++++++++++---- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/config_full.json.example b/config_full.json.example index d404391a4..6df4a8253 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -67,7 +67,7 @@ "sort_key": "quoteVolume", "refresh_period": 1800 }, - {"method": "AgeFilter", "min_days_listed": 10}, + {"method": "AgeFilter", "min_days_listed": 10, "max_days_listed": 7300}, {"method": "PrecisionFilter"}, {"method": "PriceFilter", "low_price_ratio": 0.01, "min_price": 0.00000010}, {"method": "SpreadFilter", "max_spread_ratio": 0.005}, diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index f19c5a181..b07bde62f 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -87,7 +87,7 @@ When pairs are first listed on an exchange they can suffer huge price drops and in the first few days while the pair goes through its price-discovery period. Bots can often be caught out buying before the pair has finished dropping in price. -This filter allows freqtrade to ignore pairs until they have been listed for at least `min_days_listed` days. +This filter allows freqtrade to ignore pairs until they have been listed for at least `min_days_listed` days and listed before `max_days_listed`. #### PerformanceFilter @@ -212,7 +212,7 @@ The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets, "number_assets": 20, "sort_key": "quoteVolume" }, - {"method": "AgeFilter", "min_days_listed": 10}, + {"method": "AgeFilter", "min_days_listed": 10, "max_days_listed": 7300}, {"method": "PrecisionFilter"}, {"method": "PriceFilter", "low_price_ratio": 0.01}, {"method": "SpreadFilter", "max_spread_ratio": 0.005}, diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 8f623b062..56643cca8 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -27,6 +27,7 @@ class AgeFilter(IPairList): super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) self._min_days_listed = pairlistconfig.get('min_days_listed', 10) + self._max_days_listed = pairlistconfig.get('max_days_listed', None) if self._min_days_listed < 1: raise OperationalException("AgeFilter requires min_days_listed to be >= 1") @@ -34,6 +35,8 @@ class AgeFilter(IPairList): raise OperationalException("AgeFilter requires min_days_listed to not exceed " "exchange max request size " f"({exchange.ohlcv_candle_limit('1d')})") + if self._max_days_listed and self._max_days_listed <= self._min_days_listed: + raise OperationalException("AgeFilter max_days_listed <= min_days_listed not permitted") @property def needstickers(self) -> bool: @@ -49,7 +52,9 @@ class AgeFilter(IPairList): Short whitelist method description - used for startup-messages """ return (f"{self.name} - Filtering pairs with age less than " - f"{self._min_days_listed} {plural(self._min_days_listed, 'day')}.") + f"{self._min_days_listed} {plural(self._min_days_listed, 'day')}" + " or more than " + f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}") def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ @@ -61,9 +66,10 @@ class AgeFilter(IPairList): if not needed_pairs: return pairlist + since_days = -(self._max_days_listed if self._max_days_listed else self._min_days_listed) - 1 since_ms = int(arrow.utcnow() .floor('day') - .shift(days=-self._min_days_listed - 1) + .shift(days=since_days) .float_timestamp) * 1000 candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, cache=False) if self._enabled: @@ -86,7 +92,8 @@ class AgeFilter(IPairList): return True if daily_candles is not None: - if len(daily_candles) >= self._min_days_listed: + if len(daily_candles) >= self._min_days_listed and \ + len(daily_candles) <= self._max_days_listed: # We have fetched at least the minimum required number of daily candles # Add to cache, store the time we last checked this symbol self._symbolsChecked[pair] = int(arrow.utcnow().float_timestamp) * 1000 @@ -94,6 +101,8 @@ class AgeFilter(IPairList): else: self.log_once(f"Removed {pair} from whitelist, because age " f"{len(daily_candles)} is less than {self._min_days_listed} " - f"{plural(self._min_days_listed, 'day')}", logger.info) + f"{plural(self._min_days_listed, 'day')} or more than " + f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}", + logger.info) return False return False diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index ae8f6e958..03d8fc563 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -79,7 +79,8 @@ def whitelist_conf_agefilter(default_conf): }, { "method": "AgeFilter", - "min_days_listed": 2 + "min_days_listed": 2, + "max_days_listed": 100 } ] return default_conf @@ -302,7 +303,7 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): # No pair for ETH, all handlers ([{"method": "StaticPairList"}, {"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, - {"method": "AgeFilter", "min_days_listed": 2}, + {"method": "AgeFilter", "min_days_listed": 2, "max_days_listed": None}, {"method": "PrecisionFilter"}, {"method": "PriceFilter", "low_price_ratio": 0.03}, {"method": "SpreadFilter", "max_spread_ratio": 0.005}, @@ -310,11 +311,15 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): "ETH", []), # AgeFilter and VolumePairList (require 2 days only, all should pass age test) ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, - {"method": "AgeFilter", "min_days_listed": 2}], + {"method": "AgeFilter", "min_days_listed": 2, "max_days_listed": 100}], "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC', 'HOT/BTC']), # AgeFilter and VolumePairList (require 10 days, all should fail age test) ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, - {"method": "AgeFilter", "min_days_listed": 10}], + {"method": "AgeFilter", "min_days_listed": 10, "max_days_listed": None}], + "BTC", []), + # AgeFilter and VolumePairList (all pair listed > 2, all should fail age test) + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, + {"method": "AgeFilter", "min_days_listed": 1, "max_days_listed": 2}], "BTC", []), # Precisionfilter and quote volume ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, @@ -480,9 +485,13 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t for pairlist in pairlists: if pairlist['method'] == 'AgeFilter' and pairlist['min_days_listed'] and \ - len(ohlcv_history) <= pairlist['min_days_listed']: + len(ohlcv_history) < pairlist['min_days_listed']: assert log_has_re(r'^Removed .* from whitelist, because age .* is less than ' - r'.* day.*', caplog) + r'.* day.* or more than .* day', caplog) + if pairlist['method'] == 'AgeFilter' and pairlist['max_days_listed'] and \ + len(ohlcv_history) > pairlist['max_days_listed']: + assert log_has_re(r'^Removed .* from whitelist, because age .* is less than ' + r'.* day.* or more than .* day', caplog) if pairlist['method'] == 'PrecisionFilter' and whitelist_result: assert log_has_re(r'^Removed .* from whitelist, because stop price .* ' r'would be <= stop limit.*', caplog) @@ -650,6 +659,22 @@ def test_agefilter_min_days_listed_too_small(mocker, default_conf, markets, tick get_patched_freqtradebot(mocker, default_conf) +def test_agefilter_max_days_lower_than_min_days(mocker, default_conf, markets, tickers): + default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, + {'method': 'AgeFilter', 'min_days_listed': 3, + "max_days_listed": 2}] + + mocker.patch.multiple('freqtrade.exchange.Exchange', + markets=PropertyMock(return_value=markets), + exchange_has=MagicMock(return_value=True), + get_tickers=tickers + ) + + with pytest.raises(OperationalException, + match=r'AgeFilter max_days_listed <= min_days_listed not permitted'): + get_patched_freqtradebot(mocker, default_conf) + + def test_agefilter_min_days_listed_too_large(mocker, default_conf, markets, tickers): default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, {'method': 'AgeFilter', 'min_days_listed': 99999}] From b72bbebccbe5bec7ad920122b1cd7bdfa1541246 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 01:46:51 +0700 Subject: [PATCH 015/519] fix flake8 --- freqtrade/plugins/pairlist/AgeFilter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 56643cca8..e792e0343 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -66,7 +66,9 @@ class AgeFilter(IPairList): if not needed_pairs: return pairlist - since_days = -(self._max_days_listed if self._max_days_listed else self._min_days_listed) - 1 + since_days = -( + self._max_days_listed if self._max_days_listedelse else self._min_days_listed + ) - 1 since_ms = int(arrow.utcnow() .floor('day') .shift(days=since_days) From f6511c3e3f75597cdc725e4ba9c4cb078291e680 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 02:20:53 +0700 Subject: [PATCH 016/519] fix typo and add blocker --- freqtrade/plugins/pairlist/AgeFilter.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index e792e0343..63a9ecfeb 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -37,6 +37,10 @@ class AgeFilter(IPairList): f"({exchange.ohlcv_candle_limit('1d')})") if self._max_days_listed and self._max_days_listed <= self._min_days_listed: raise OperationalException("AgeFilter max_days_listed <= min_days_listed not permitted") + if self._max_days_listed > exchange.ohlcv_candle_limit('1d'): + raise OperationalException("AgeFilter requires max_days_listed to not exceed " + "exchange max request size " + f"({exchange.ohlcv_candle_limit('1d')})") @property def needstickers(self) -> bool: @@ -67,7 +71,7 @@ class AgeFilter(IPairList): return pairlist since_days = -( - self._max_days_listed if self._max_days_listedelse else self._min_days_listed + self._max_days_listed if self._max_days_listed else self._min_days_listed ) - 1 since_ms = int(arrow.utcnow() .floor('day') From b722e1235019571288216f3408165ac06f8de028 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 02:44:48 +0700 Subject: [PATCH 017/519] compact low balance currencies --- freqtrade/rpc/telegram.py | 15 ++++++++++++--- tests/conftest.py | 2 +- tests/rpc/test_rpc_telegram.py | 11 ++++++++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index d857e46a9..7ed564297 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -598,6 +598,9 @@ class Telegram(RPCHandler): "Starting capital: " f"`{self._config['dry_run_wallet']}` {self._config['stake_currency']}.\n" ) + total_dust_balance = 0 + total_dust_currencies = 0 + curr_output = '' for curr in result['currencies']: if curr['est_stake'] > balance_dust_level: curr_output = ( @@ -607,9 +610,9 @@ class Telegram(RPCHandler): f"\t`Pending: {curr['used']:.8f}`\n" f"\t`Est. {curr['stake']}: " f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n") - else: - curr_output = (f"*{curr['currency']}:* not showing <{balance_dust_level} " - f"{curr['stake']} amount \n") + elif curr['est_stake'] <= balance_dust_level: + total_dust_balance += curr['est_stake'] + total_dust_currencies += 1 # Handle overflowing message length if len(output + curr_output) >= MAX_TELEGRAM_MESSAGE_LENGTH: @@ -618,6 +621,12 @@ class Telegram(RPCHandler): else: output += curr_output + if total_dust_balance > 0: + output += ( + f"*{total_dust_currencies} Other Currencies:*\n" + f"\t`Est. {result['stake']}: " + f"{round_coin_value(total_dust_balance, result['stake'], False)}`\n") + output += ("\n*Estimated Value*:\n" f"\t`{result['stake']}: {result['total']: .8f}`\n" f"\t`{result['symbol']}: " diff --git a/tests/conftest.py b/tests/conftest.py index a843d9397..4a81175ed 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1761,7 +1761,7 @@ def rpc_balance(): 'total': 0.1, 'free': 0.01, 'used': 0.0 - }, + }, 'EUR': { 'total': 10.0, 'free': 10.0, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 782ae69c6..241805736 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -515,16 +515,21 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick patch_get_signal(freqtradebot, (True, False)) telegram._balance(update=update, context=MagicMock()) + print('msg_mock.call_args_list') + print(msg_mock.call_args_list) result = msg_mock.call_args_list[0][0][0] assert msg_mock.call_count == 1 assert '*BTC:*' in result assert '*ETH:*' not in result - assert '*USDT:*' in result - assert '*EUR:*' in result + assert '*USDT:*' not in result + assert '*EUR:*' not in result + assert '*LTC:*' in result + assert '*XRP:*' not in result assert 'Balance:' in result assert 'Est. BTC:' in result assert 'BTC: 12.00000000' in result - assert '*XRP:* not showing <0.0001 BTC amount' in result + assert "*3 Other Currencies:*" in result + assert 'BTC: 0.00000309' in result def test_balance_handle_empty_response(default_conf, update, mocker) -> None: From dbdd7f38a88b5857dd276456951c168796b5b286 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 02:56:05 +0700 Subject: [PATCH 018/519] add plural --- freqtrade/rpc/telegram.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 7ed564297..389ef84a4 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -11,7 +11,6 @@ from html import escape from itertools import chain from math import isnan from typing import Any, Callable, Dict, List, Optional, Union - import arrow from tabulate import tabulate from telegram import (CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, @@ -24,7 +23,7 @@ from freqtrade.__init__ import __version__ from freqtrade.constants import DUST_PER_COIN from freqtrade.enums import RPCMessageType from freqtrade.exceptions import OperationalException -from freqtrade.misc import chunks, round_coin_value +from freqtrade.misc import chunks, round_coin_value, plural from freqtrade.rpc import RPC, RPCException, RPCHandler @@ -623,7 +622,8 @@ class Telegram(RPCHandler): if total_dust_balance > 0: output += ( - f"*{total_dust_currencies} Other Currencies:*\n" + f"*{total_dust_currencies} Other " + f"{plural(total_dust_currencies, 'Currency', 'Currencies')}:*\n" f"\t`Est. {result['stake']}: " f"{round_coin_value(total_dust_balance, result['stake'], False)}`\n") From 7efa228d73e538165ffacce578a3d649725aab01 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 03:08:29 +0700 Subject: [PATCH 019/519] add dust balance --- freqtrade/rpc/telegram.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 389ef84a4..0bacf0504 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -623,7 +623,8 @@ class Telegram(RPCHandler): if total_dust_balance > 0: output += ( f"*{total_dust_currencies} Other " - f"{plural(total_dust_currencies, 'Currency', 'Currencies')}:*\n" + f"{plural(total_dust_currencies, 'Currency', 'Currencies')} " + f"(< {balance_dust_level} {result['stake']}):*\n" f"\t`Est. {result['stake']}: " f"{round_coin_value(total_dust_balance, result['stake'], False)}`\n") From 348dbeff3f99e848666e7b5242ea02e205c73a64 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Sun, 4 Jul 2021 11:16:33 +0200 Subject: [PATCH 020/519] added meaningful logging of used lookback range --- freqtrade/plugins/pairlist/VolumePairList.py | 23 +++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 4e9102243..5820bc667 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -12,6 +12,7 @@ from cachetools.ttl import TTLCache from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes from freqtrade.plugins.pairlist.IPairList import IPairList +from freqtrade.misc import format_ms_time logger = logging.getLogger(__name__) @@ -41,6 +42,12 @@ class VolumePairList(IPairList): self._lookback_timeframe = self._pairlistconfig.get('lookback_timeframe', '1d') self._lookback_period = self._pairlistconfig.get('lookback_period', 0) + if (self._lookback_days > 0) & (self._lookback_period > 0): + raise OperationalException( + f'Ambigous configuration: lookback_days and lookback_period both set in pairlist config. ' + f'Please set lookback_days only or lookback_period and lookback_timeframe and restart the bot.' + ) + # overwrite lookback timeframe and days when lookback_days is set if self._lookback_days > 0: self._lookback_timeframe = '1d' @@ -49,7 +56,8 @@ class VolumePairList(IPairList): # get timeframe in minutes and seconds self._tf_in_min = timeframe_to_minutes(self._lookback_timeframe) self._tf_in_sec = self._tf_in_min * 60 - + + # wether to use range lookback or not self._use_range = (self._tf_in_min > 0) & (self._lookback_period > 0) if self._use_range & (self._refresh_period < self._tf_in_sec): @@ -138,7 +146,13 @@ class VolumePairList(IPairList): .shift(minutes=-(self._lookback_period * self._tf_in_min) - self._tf_in_min) .float_timestamp) * 1000 - self.log_once(f"Using volume range of {self._lookback_period} {self._lookback_timeframe} candles from {since_ms}", logger.info) + to_ms = int(arrow.utcnow() + .floor('minute') + .shift(minutes=-self._tf_in_min) + .float_timestamp) * 1000 + + # todo: utc date output for starting date + self.log_once(f"Using volume range of {self._lookback_period} candles, timeframe: {self._lookback_timeframe}, starting from {format_ms_time(since_ms)} till {format_ms_time(to_ms)}", logger.info) needed_pairs = [(p, self._lookback_timeframe) for p in [s['symbol'] for s in filtered_tickers] if p not in self._pair_cache] # Get all candles @@ -148,18 +162,15 @@ class VolumePairList(IPairList): for i,p in enumerate(filtered_tickers): pair_candles = candles[(p['symbol'], self._lookback_timeframe)] if (p['symbol'], self._lookback_timeframe) in candles else None - # print(p['symbol'], " 24h quote volume = ",filtered_tickers[i]['quoteVolume']) # in case of candle data calculate typical price and quoteVolume for candle if not pair_candles.empty: pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] + pair_candles['close']) / 3 pair_candles['quoteVolume'] = pair_candles['volume'] * pair_candles['typical_price'] - # replace quoteVolume with range sum + # replace quoteVolume with range quoteVolume sum calculated above filtered_tickers[i]['quoteVolume'] = pair_candles['quoteVolume'].sum() else: filtered_tickers[i]['quoteVolume'] = 0 - # print(p['symbol'], " range quote volume = ",filtered_tickers[i]['quoteVolume']) - if self._min_value > 0: filtered_tickers = [ v for v in filtered_tickers if v[self._sort_key] > self._min_value] From 9919061c7899e7e7e5d9c4a0ef59d78152a6a9ec Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Sun, 4 Jul 2021 11:40:45 +0200 Subject: [PATCH 021/519] PEP8 compliance --- freqtrade/plugins/pairlist/VolumePairList.py | 62 +++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 5820bc667..40e6afa07 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -4,15 +4,16 @@ Volume PairList provider Provides dynamic pair list based on trade volumes """ import logging -import arrow from typing import Any, Dict, List +import arrow from cachetools.ttl import TTLCache from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes -from freqtrade.plugins.pairlist.IPairList import IPairList from freqtrade.misc import format_ms_time +from freqtrade.plugins.pairlist.IPairList import IPairList + logger = logging.getLogger(__name__) @@ -44,8 +45,9 @@ class VolumePairList(IPairList): if (self._lookback_days > 0) & (self._lookback_period > 0): raise OperationalException( - f'Ambigous configuration: lookback_days and lookback_period both set in pairlist config. ' - f'Please set lookback_days only or lookback_period and lookback_timeframe and restart the bot.' + 'Ambigous configuration: lookback_days and lookback_period both set in pairlist ' + 'config. Please set lookback_days only or lookback_period and lookback_timeframe ' + 'and restart the bot.' ) # overwrite lookback timeframe and days when lookback_days is set @@ -53,17 +55,18 @@ class VolumePairList(IPairList): self._lookback_timeframe = '1d' self._lookback_period = self._lookback_days - # get timeframe in minutes and seconds + # get timeframe in minutes and seconds self._tf_in_min = timeframe_to_minutes(self._lookback_timeframe) self._tf_in_sec = self._tf_in_min * 60 - + # wether to use range lookback or not self._use_range = (self._tf_in_min > 0) & (self._lookback_period > 0) if self._use_range & (self._refresh_period < self._tf_in_sec): raise OperationalException( - f'Refresh period of {self._refresh_period} seconds is smaller than one timeframe of {self._lookback_timeframe}. ' - f'Please adjust refresh_period to at least {self._tf_in_sec} and restart the bot.' + f'Refresh period of {self._refresh_period} seconds is smaller than one ' + f'timeframe of {self._lookback_timeframe}. Please adjust refresh_period ' + f'to at least {self._tf_in_sec} and restart the bot.' ) if not self._exchange.exchange_has('fetchTickers'): @@ -76,7 +79,6 @@ class VolumePairList(IPairList): raise OperationalException( f'key {self._sort_key} not in {SORT_VALUES}') - if self._lookback_period < 0: raise OperationalException("VolumeFilter requires lookback_period to be >= 0") if self._lookback_period > exchange.ohlcv_candle_limit(self._lookback_timeframe): @@ -136,15 +138,16 @@ class VolumePairList(IPairList): :param tickers: Tickers (from exchange.get_tickers()). May be cached. :return: new whitelist """ - # Use the incoming pairlist. + # Use the incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] # get lookback period in ms, for exchange ohlcv fetch - if self._use_range == True: + if self._use_range: since_ms = int(arrow.utcnow() - .floor('minute') - .shift(minutes=-(self._lookback_period * self._tf_in_min) - self._tf_in_min) - .float_timestamp) * 1000 + .floor('minute') + .shift(minutes=-(self._lookback_period * self._tf_in_min) + - self._tf_in_min) + .float_timestamp) * 1000 to_ms = int(arrow.utcnow() .floor('minute') @@ -152,20 +155,35 @@ class VolumePairList(IPairList): .float_timestamp) * 1000 # todo: utc date output for starting date - self.log_once(f"Using volume range of {self._lookback_period} candles, timeframe: {self._lookback_timeframe}, starting from {format_ms_time(since_ms)} till {format_ms_time(to_ms)}", logger.info) - needed_pairs = [(p, self._lookback_timeframe) for p in [s['symbol'] for s in filtered_tickers] if p not in self._pair_cache] + self.log_once(f"Using volume range of {self._lookback_period} candles, timeframe: " + f"{self._lookback_timeframe}, starting from {format_ms_time(since_ms)} " + f"till {format_ms_time(to_ms)}", logger.info) + needed_pairs = [ + (p, self._lookback_timeframe) for p in + [ + s['symbol'] for s in filtered_tickers + ] if p not in self._pair_cache + ] # Get all candles candles = {} if needed_pairs: - candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, cache=False) + candles = self._exchange.refresh_latest_ohlcv( + needed_pairs, since_ms=since_ms, cache=False + ) - for i,p in enumerate(filtered_tickers): - pair_candles = candles[(p['symbol'], self._lookback_timeframe)] if (p['symbol'], self._lookback_timeframe) in candles else None + for i, p in enumerate(filtered_tickers): + pair_candles = candles[ + (p['symbol'], self._lookback_timeframe) + ] if (p['symbol'], self._lookback_timeframe) in candles else None # in case of candle data calculate typical price and quoteVolume for candle if not pair_candles.empty: - pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] + pair_candles['close']) / 3 - pair_candles['quoteVolume'] = pair_candles['volume'] * pair_candles['typical_price'] + pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] + + pair_candles['close']) / 3 + pair_candles['quoteVolume'] = ( + pair_candles['volume'] * pair_candles['typical_price'] + ) + # replace quoteVolume with range quoteVolume sum calculated above filtered_tickers[i]['quoteVolume'] = pair_candles['quoteVolume'].sum() else: @@ -179,7 +197,7 @@ class VolumePairList(IPairList): # Validate whitelist to only have active market pairs pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers]) - pairs = self.verify_blacklist(pairs, logger.info) + pairs = self.verify_blacklist(pairs, logger.info) # Limit pairlist to the requested number of pairs pairs = pairs[:self._number_pairs] From 9e548657e03d279a143ad304233031d3393faff9 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 21:08:46 +0700 Subject: [PATCH 022/519] fix testcase --- tests/rpc/test_rpc_telegram.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 241805736..4784f1172 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -515,8 +515,6 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick patch_get_signal(freqtradebot, (True, False)) telegram._balance(update=update, context=MagicMock()) - print('msg_mock.call_args_list') - print(msg_mock.call_args_list) result = msg_mock.call_args_list[0][0][0] assert msg_mock.call_count == 1 assert '*BTC:*' in result @@ -528,7 +526,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick assert 'Balance:' in result assert 'Est. BTC:' in result assert 'BTC: 12.00000000' in result - assert "*3 Other Currencies:*" in result + assert "*3 Other Currencies (< 0.0001 BTC):*" in result assert 'BTC: 0.00000309' in result From 2d5ced780116088aca71216c29683021754b22dd Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 21:59:59 +0700 Subject: [PATCH 023/519] fix testcase --- config_full.json.example | 2 +- docs/includes/pairlists.md | 4 ++-- freqtrade/plugins/pairlist/AgeFilter.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config_full.json.example b/config_full.json.example index 6df4a8253..d404391a4 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -67,7 +67,7 @@ "sort_key": "quoteVolume", "refresh_period": 1800 }, - {"method": "AgeFilter", "min_days_listed": 10, "max_days_listed": 7300}, + {"method": "AgeFilter", "min_days_listed": 10}, {"method": "PrecisionFilter"}, {"method": "PriceFilter", "low_price_ratio": 0.01, "min_price": 0.00000010}, {"method": "SpreadFilter", "max_spread_ratio": 0.005}, diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index b07bde62f..908eaa459 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -81,7 +81,7 @@ Filtering instances (not the first position in the list) will not apply any cach #### AgeFilter -Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`). +Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`) or more than `max_days_listed` days (defaults `None` mean infinity). When pairs are first listed on an exchange they can suffer huge price drops and volatility in the first few days while the pair goes through its price-discovery period. Bots can often @@ -212,7 +212,7 @@ The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets, "number_assets": 20, "sort_key": "quoteVolume" }, - {"method": "AgeFilter", "min_days_listed": 10, "max_days_listed": 7300}, + {"method": "AgeFilter", "min_days_listed": 10}, {"method": "PrecisionFilter"}, {"method": "PriceFilter", "low_price_ratio": 0.01}, {"method": "SpreadFilter", "max_spread_ratio": 0.005}, diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 63a9ecfeb..ef3953776 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -37,7 +37,7 @@ class AgeFilter(IPairList): f"({exchange.ohlcv_candle_limit('1d')})") if self._max_days_listed and self._max_days_listed <= self._min_days_listed: raise OperationalException("AgeFilter max_days_listed <= min_days_listed not permitted") - if self._max_days_listed > exchange.ohlcv_candle_limit('1d'): + if self._max_days_listed and self._max_days_listed > exchange.ohlcv_candle_limit('1d'): raise OperationalException("AgeFilter requires max_days_listed to not exceed " "exchange max request size " f"({exchange.ohlcv_candle_limit('1d')})") From c3cf71bba8465675aab47c14233664ce89d58ebe Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Sun, 4 Jul 2021 22:04:39 +0700 Subject: [PATCH 024/519] sort import --- freqtrade/rpc/telegram.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 0bacf0504..171a53ca1 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -11,6 +11,7 @@ from html import escape from itertools import chain from math import isnan from typing import Any, Callable, Dict, List, Optional, Union + import arrow from tabulate import tabulate from telegram import (CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, @@ -23,7 +24,7 @@ from freqtrade.__init__ import __version__ from freqtrade.constants import DUST_PER_COIN from freqtrade.enums import RPCMessageType from freqtrade.exceptions import OperationalException -from freqtrade.misc import chunks, round_coin_value, plural +from freqtrade.misc import chunks, plural, round_coin_value from freqtrade.rpc import RPC, RPCException, RPCHandler From 85c7b557503d1c25330101266ab898642ca41272 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Sun, 4 Jul 2021 20:46:24 +0200 Subject: [PATCH 025/519] improvements: - `float_timestamp` switched to `int_timestamp` - added documentation to pairlists.md --- docs/includes/pairlists.md | 31 +++++++++++++++++++- freqtrade/plugins/pairlist/VolumePairList.py | 4 +-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index f19c5a181..3f8baab29 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -63,7 +63,7 @@ The `refresh_period` setting allows to define the period (in seconds), at which The pairlist cache (`refresh_period`) on `VolumePairList` is only applicable to generating pairlists. Filtering instances (not the first position in the list) will not apply any cache and will always use up-to-date data. -`VolumePairList` is based on the ticker data from exchange, as reported by the ccxt library: +`VolumePairList` is per default based on the ticker data from exchange, as reported by the ccxt library: * The `quoteVolume` is the amount of quote (stake) currency traded (bought or sold) in last 24 hours. @@ -76,6 +76,35 @@ Filtering instances (not the first position in the list) will not apply any cach }], ``` +`VolumePairList` can also operate in an advanced mode to build volume over a given timerange of specified candle size. It utilizes exchange historical candle data, builds a typical price (calculated by (open+high+low)/3), and multiplies it with every candle data's volume. The sum is the `quoteVolume` over the given range. This allows different scenarios, for a more smoothened volume, when using longer ranges with larger candle sizes, or the opposite when using a short range with small candles. + +For convenience `lookback_days` can be specified, which will imply that 1d candles will be used for the lookback. In the example below the pairlist would be created based on the last 7 days: + +```json +"pairlists": [{ + "method": "VolumePairList", + "number_assets": 20, + "sort_key": "quoteVolume", + "refresh_period": 86400, + "lookback_days": 7 +}], +``` +!!! Warning "Range look back and refresh period" + When used in conjuction with `lookback_days` and `lookback_timeframe` the `refresh_period` can not be smaller than the candle size in seconds. As this will result in unnecessary requests to the exchanges API. + +More sophisticated approach can be used, by using `lookback_timeframe` for candle size and `lookback_period` which specifies the amount of candles. This example will build the volume pairs based on a rolling period of 3 days of 1h candles: + +```json +"pairlists": [{ + "method": "VolumePairList", + "number_assets": 20, + "sort_key": "quoteVolume", + "refresh_period": 3600, + "lookback_timeframe": "1h", + "lookback_timeframe": 72 +}], +``` + !!! Note `VolumePairList` does not support backtesting mode. diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 40e6afa07..352d028ac 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -147,12 +147,12 @@ class VolumePairList(IPairList): .floor('minute') .shift(minutes=-(self._lookback_period * self._tf_in_min) - self._tf_in_min) - .float_timestamp) * 1000 + .int_timestamp) * 1000 to_ms = int(arrow.utcnow() .floor('minute') .shift(minutes=-self._tf_in_min) - .float_timestamp) * 1000 + .int_timestamp) * 1000 # todo: utc date output for starting date self.log_once(f"Using volume range of {self._lookback_period} candles, timeframe: " From 7ac55e5415f94d7db285c20d093a902b039d05de Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Sun, 4 Jul 2021 21:08:42 +0200 Subject: [PATCH 026/519] AgeFilter, RangeStabilityFilter, VolatilityFilter changed `float_timestamp` to `int_timestamp` --- freqtrade/plugins/pairlist/AgeFilter.py | 4 ++-- freqtrade/plugins/pairlist/VolatilityFilter.py | 2 +- freqtrade/plugins/pairlist/rangestabilityfilter.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 8f623b062..09d8588c1 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -64,7 +64,7 @@ class AgeFilter(IPairList): since_ms = int(arrow.utcnow() .floor('day') .shift(days=-self._min_days_listed - 1) - .float_timestamp) * 1000 + .int_timestamp) * 1000 candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, cache=False) if self._enabled: for p in deepcopy(pairlist): @@ -89,7 +89,7 @@ class AgeFilter(IPairList): if len(daily_candles) >= self._min_days_listed: # We have fetched at least the minimum required number of daily candles # Add to cache, store the time we last checked this symbol - self._symbolsChecked[pair] = int(arrow.utcnow().float_timestamp) * 1000 + self._symbolsChecked[pair] = int(arrow.utcnow().int_timestamp) * 1000 return True else: self.log_once(f"Removed {pair} from whitelist, because age " diff --git a/freqtrade/plugins/pairlist/VolatilityFilter.py b/freqtrade/plugins/pairlist/VolatilityFilter.py index 5ae8e3e9f..a50bf55b9 100644 --- a/freqtrade/plugins/pairlist/VolatilityFilter.py +++ b/freqtrade/plugins/pairlist/VolatilityFilter.py @@ -72,7 +72,7 @@ class VolatilityFilter(IPairList): since_ms = int(arrow.utcnow() .floor('day') .shift(days=-self._days - 1) - .float_timestamp) * 1000 + .int_timestamp) * 1000 # Get all candles candles = {} if needed_pairs: diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index 8be61166b..6f013c750 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -65,7 +65,7 @@ class RangeStabilityFilter(IPairList): since_ms = int(arrow.utcnow() .floor('day') .shift(days=-self._days - 1) - .float_timestamp) * 1000 + .int_timestamp) * 1000 # Get all candles candles = {} if needed_pairs: From 0d787fde5890f1bcf42284246bd3694da72d85db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 03:01:11 +0000 Subject: [PATCH 027/519] Bump plotly from 5.0.0 to 5.1.0 Bumps [plotly](https://github.com/plotly/plotly.py) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.0.0...v5.1.0) --- updated-dependencies: - dependency-name: plotly dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index 0563e5df2..e03fd4d66 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.0.0 +plotly==5.1.0 From 2f97846bd87cd5ee3898549303c6282452795b30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 03:01:18 +0000 Subject: [PATCH 028/519] Bump ccxt from 1.52.4 to 1.52.40 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.52.4 to 1.52.40. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.52.4...1.52.40) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a983e4797..d7f918486 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.0 pandas==1.2.5 -ccxt==1.52.4 +ccxt==1.52.40 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 7ae5f47242d1fece46a2422bef8bc292385348da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 03:01:24 +0000 Subject: [PATCH 029/519] Bump sqlalchemy from 1.4.19 to 1.4.20 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.19 to 1.4.20. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/master/CHANGES) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a983e4797..7d52cdc2e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ ccxt==1.52.4 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 -SQLAlchemy==1.4.19 +SQLAlchemy==1.4.20 python-telegram-bot==13.6 arrow==1.1.1 cachetools==4.2.2 From d1555a10957f32a9c08129ff30995de26fb3cab2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 03:01:28 +0000 Subject: [PATCH 030/519] Bump fastapi from 0.65.2 to 0.66.0 Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.65.2 to 0.66.0. - [Release notes](https://github.com/tiangolo/fastapi/releases) - [Commits](https://github.com/tiangolo/fastapi/compare/0.65.2...0.66.0) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a983e4797..3e9131ded 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ python-rapidjson==1.4 sdnotify==0.3.2 # API Server -fastapi==0.65.2 +fastapi==0.66.0 uvicorn==0.14.0 pyjwt==2.1.0 aiofiles==0.7.0 From 0c8afea38271c2519be61179a09160ef6f1806fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 04:30:54 +0000 Subject: [PATCH 031/519] Bump pandas from 1.2.5 to 1.3.0 Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.2.5 to 1.3.0. - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Changelog](https://github.com/pandas-dev/pandas/blob/master/RELEASE.md) - [Commits](https://github.com/pandas-dev/pandas/compare/v1.2.5...v1.3.0) --- updated-dependencies: - dependency-name: pandas dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e1c784608..0003ec031 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ numpy==1.21.0 -pandas==1.2.5 +pandas==1.3.0 ccxt==1.52.40 # Pin cryptography for now due to rust build errors with piwheels From ac7598ff1450ae94d7569341f045b3aa38e558d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 04:31:00 +0000 Subject: [PATCH 032/519] Bump python-telegram-bot from 13.6 to 13.7 Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 13.6 to 13.7. - [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases) - [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst) - [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v13.6...v13.7) --- updated-dependencies: - dependency-name: python-telegram-bot dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e1c784608..b289c13ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ ccxt==1.52.40 cryptography==3.4.7 aiohttp==3.7.4.post0 SQLAlchemy==1.4.20 -python-telegram-bot==13.6 +python-telegram-bot==13.7 arrow==1.1.1 cachetools==4.2.2 requests==2.25.1 From 5626ca5a06dcbc29cad9a925945351765188ace1 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Mon, 5 Jul 2021 10:39:22 +0200 Subject: [PATCH 033/519] removed unnecessary casting to int() --- freqtrade/plugins/pairlist/AgeFilter.py | 8 ++++---- freqtrade/plugins/pairlist/VolatilityFilter.py | 8 ++++---- freqtrade/plugins/pairlist/rangestabilityfilter.py | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 09d8588c1..744b1268d 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -61,10 +61,10 @@ class AgeFilter(IPairList): if not needed_pairs: return pairlist - since_ms = int(arrow.utcnow() - .floor('day') - .shift(days=-self._min_days_listed - 1) - .int_timestamp) * 1000 + since_ms = (arrow.utcnow() + .floor('day') + .shift(days=-self._min_days_listed - 1) + .int_timestamp) * 1000 candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, cache=False) if self._enabled: for p in deepcopy(pairlist): diff --git a/freqtrade/plugins/pairlist/VolatilityFilter.py b/freqtrade/plugins/pairlist/VolatilityFilter.py index a50bf55b9..9383e5d06 100644 --- a/freqtrade/plugins/pairlist/VolatilityFilter.py +++ b/freqtrade/plugins/pairlist/VolatilityFilter.py @@ -69,10 +69,10 @@ class VolatilityFilter(IPairList): """ needed_pairs = [(p, '1d') for p in pairlist if p not in self._pair_cache] - since_ms = int(arrow.utcnow() - .floor('day') - .shift(days=-self._days - 1) - .int_timestamp) * 1000 + since_ms = (arrow.utcnow() + .floor('day') + .shift(days=-self._days - 1) + .int_timestamp) * 1000 # Get all candles candles = {} if needed_pairs: diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index 6f013c750..a6d1820de 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -62,10 +62,10 @@ class RangeStabilityFilter(IPairList): """ needed_pairs = [(p, '1d') for p in pairlist if p not in self._pair_cache] - since_ms = int(arrow.utcnow() - .floor('day') - .shift(days=-self._days - 1) - .int_timestamp) * 1000 + since_ms = (arrow.utcnow() + .floor('day') + .shift(days=-self._days - 1) + .int_timestamp) * 1000 # Get all candles candles = {} if needed_pairs: From 346d66748b65cf833696bc342d4c08582ad88e1a Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Mon, 5 Jul 2021 12:50:56 +0200 Subject: [PATCH 034/519] first version of OffsetFilter --- freqtrade/constants.py | 6 +-- freqtrade/plugins/pairlist/OffsetFilter.py | 54 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 freqtrade/plugins/pairlist/OffsetFilter.py diff --git a/freqtrade/constants.py b/freqtrade/constants.py index f4c32387b..acd143708 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -26,9 +26,9 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily', 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily'] AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', - 'AgeFilter', 'PerformanceFilter', 'PrecisionFilter', - 'PriceFilter', 'RangeStabilityFilter', 'ShuffleFilter', - 'SpreadFilter', 'VolatilityFilter'] + 'AgeFilter', 'OffsetFilter', 'PerformanceFilter', + 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', + 'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter'] AVAILABLE_PROTECTIONS = ['CooldownPeriod', 'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] DRY_RUN_WALLET = 1000 diff --git a/freqtrade/plugins/pairlist/OffsetFilter.py b/freqtrade/plugins/pairlist/OffsetFilter.py new file mode 100644 index 000000000..4579204d9 --- /dev/null +++ b/freqtrade/plugins/pairlist/OffsetFilter.py @@ -0,0 +1,54 @@ +""" +Offset pair list filter +""" +import logging +from typing import Any, Dict, List + +from freqtrade.exceptions import OperationalException +from freqtrade.plugins.pairlist.IPairList import IPairList + + +logger = logging.getLogger(__name__) + + +class OffsetFilter(IPairList): + + def __init__(self, exchange, pairlistmanager, + config: Dict[str, Any], pairlistconfig: Dict[str, Any], + pairlist_pos: int) -> None: + super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + + self._offset = pairlistconfig.get('offset', 0) + + if self._offset < 0: + raise OperationalException("OffsetFilter requires offset to be >= 0") + + @property + def needstickers(self) -> bool: + """ + Boolean property defining if tickers are necessary. + If no Pairlist requires tickers, an empty Dict is passed + as tickers argument to filter_pairlist + """ + return False + + def short_desc(self) -> str: + """ + Short whitelist method description - used for startup-messages + """ + return f"{self.name} - Offseting pairs by {self._offset}." + + def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: + """ + Filters and sorts pairlist and returns the whitelist again. + Called on each bot iteration - please use internal caching if necessary + :param pairlist: pairlist to filter or sort + :param tickers: Tickers (from exchange.get_tickers()). May be cached. + :return: new whitelist + """ + if self._offset > len(pairlist): + self.log_once(f"Offset of {self._offset} is larger than " + + f"pair count of {len(pairlist)}", logger.warn) + pairs = pairlist[self._offset:] + self.log_once(f"Searching {len(pairs)} pairs: {pairs}", logger.info) + return pairs From 10998eb0faa8da854b777bdf2b4605caa03dfb7c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 5 Jul 2021 19:51:14 +0200 Subject: [PATCH 035/519] Remove further usages of int(int_timestamp) --- freqtrade/data/history/history_utils.py | 6 +++--- freqtrade/exchange/exchange.py | 2 +- freqtrade/plugins/pairlist/AgeFilter.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index eecb63d07..1459dfd78 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -194,8 +194,8 @@ def _download_pair_history(datadir: Path, new_data = exchange.get_historic_ohlcv(pair=pair, timeframe=timeframe, since_ms=since_ms if since_ms else - int(arrow.utcnow().shift( - days=-new_pairs_days).float_timestamp) * 1000 + arrow.utcnow().shift( + days=-new_pairs_days).int_timestamp * 1000 ) # TODO: Maybe move parsing to exchange class (?) new_dataframe = ohlcv_to_dataframe(new_data, timeframe, pair, @@ -272,7 +272,7 @@ def _download_trades_history(exchange: Exchange, if timerange.stoptype == 'date': until = timerange.stopts * 1000 else: - since = int(arrow.utcnow().shift(days=-new_pairs_days).float_timestamp) * 1000 + since = arrow.utcnow().shift(days=-new_pairs_days).int_timestamp * 1000 trades = data_handler.trades_load(pair) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 235f03269..42e86db3e 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -578,7 +578,7 @@ class Exchange: 'side': side, 'remaining': _amount, 'datetime': arrow.utcnow().isoformat(), - 'timestamp': int(arrow.utcnow().int_timestamp * 1000), + 'timestamp': arrow.utcnow().int_timestamp * 1000, 'status': "closed" if ordertype == "market" else "open", 'fee': None, 'info': {} diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 744b1268d..4c364bfce 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -89,7 +89,7 @@ class AgeFilter(IPairList): if len(daily_candles) >= self._min_days_listed: # We have fetched at least the minimum required number of daily candles # Add to cache, store the time we last checked this symbol - self._symbolsChecked[pair] = int(arrow.utcnow().int_timestamp) * 1000 + self._symbolsChecked[pair] = arrow.utcnow().int_timestamp * 1000 return True else: self.log_once(f"Removed {pair} from whitelist, because age " From 1e87225e916283fc27f18efb8f1d605b84d0cecd Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Mon, 5 Jul 2021 20:59:27 +0200 Subject: [PATCH 036/519] added `test_VolumePairList_range` to test_pairlist.py --- freqtrade/plugins/pairlist/VolumePairList.py | 3 +- tests/plugins/test_pairlist.py | 95 ++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 352d028ac..14b9d7024 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -171,13 +171,12 @@ class VolumePairList(IPairList): candles = self._exchange.refresh_latest_ohlcv( needed_pairs, since_ms=since_ms, cache=False ) - for i, p in enumerate(filtered_tickers): pair_candles = candles[ (p['symbol'], self._lookback_timeframe) ] if (p['symbol'], self._lookback_timeframe) in candles else None # in case of candle data calculate typical price and quoteVolume for candle - if not pair_candles.empty: + if pair_candles is not None and not pair_candles.empty: pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low'] + pair_candles['close']) / 3 pair_candles['quoteVolume'] = ( diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index ae8f6e958..7812ee733 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -507,6 +507,101 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t assert log_has_re(r'^Removed .* from whitelist, because volatility.*$', caplog) +@pytest.mark.parametrize("pairlists,base_currency,volumefilter_result", [ + # default refresh of 1800 to small for daily candle lookback + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", + "lookback_days": 1}], + "BTC", "default_refresh_too_short"), # OperationalException expected + # ambigous configuration with lookback days and period + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", + "lookback_days": 1, "lookback_period": 1}], + "BTC", "lookback_days_and_period"), # OperationalException expected + # negative lookback period + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", + "lookback_timeframe": "1d", "lookback_period": -1}], + "BTC", "lookback_period_negative"), # OperationalException expected + # lookback range exceedes exchange limit + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", + "lookback_timeframe": "1m", "lookback_period": 2000, "refresh_period": 3600}], + "BTC", 'lookback_exceeds_exchange_request_size'), # OperationalException expected + # expecing pairs as given + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", + "lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}], + "BTC", ['LTC/BTC', 'XRP/BTC', 'ETH/BTC', 'TKN/BTC', 'HOT/BTC']), + # expecting pairs from default tickers, because 1h candles are not available + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", + "lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}], + "BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC']), +]) +def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history, + pairlists, base_currency, volumefilter_result, caplog) -> None: + whitelist_conf['pairlists'] = pairlists + whitelist_conf['stake_currency'] = base_currency + + ohlcv_history_high_vola = ohlcv_history.copy() + ohlcv_history_high_vola.loc[ohlcv_history_high_vola.index == 1, 'close'] = 0.00090 + + # create candles for high volume + ohlcv_history_high_volume = ohlcv_history.copy() + ohlcv_history_high_volume.loc[ohlcv_history_high_volume.index == 1, 'volume'] = 10 + + ohlcv_data = { + ('ETH/BTC', '1d'): ohlcv_history, + ('TKN/BTC', '1d'): ohlcv_history, + ('LTC/BTC', '1d'): ohlcv_history_high_volume, + ('XRP/BTC', '1d'): ohlcv_history_high_vola, + ('HOT/BTC', '1d'): ohlcv_history, + } + + mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) + + if volumefilter_result == 'default_refresh_too_short': + with pytest.raises(OperationalException, + match=r'Refresh period of [0-9]+ seconds is smaller than one timeframe ' + r'of [0-9]+.*\. Please adjust refresh_period to at least [0-9]+ ' + r'and restart the bot\.'): + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) + return + elif volumefilter_result == 'lookback_days_and_period': + with pytest.raises(OperationalException, + match=r'Ambigous configuration: lookback_days and lookback_period both ' + r'set in pairlist config\..*'): + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) + elif volumefilter_result == 'lookback_period_negative': + with pytest.raises(OperationalException, + match=r'VolumeFilter requires lookback_period to be >= 0'): + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) + elif volumefilter_result == 'lookback_exceeds_exchange_request_size': + with pytest.raises(OperationalException, + match=r'VolumeFilter requires lookback_period to not exceed ' + r'exchange max request size \([0-9]+\)'): + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) + else: + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_tickers=tickers, + markets=PropertyMock(return_value=shitcoinmarkets) + ) + + # remove ohlcv when looback_timeframe != 1d + # to enforce fallback to ticker data + if 'lookback_timeframe' in pairlists[0]: + if pairlists[0]['lookback_timeframe'] != '1d': + ohlcv_data = [] + + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + refresh_latest_ohlcv=MagicMock(return_value=ohlcv_data), + ) + + freqtrade.pairlists.refresh_pairlist() + whitelist = freqtrade.pairlists.whitelist + + assert isinstance(whitelist, list) + assert whitelist == volumefilter_result + + def test_PrecisionFilter_error(mocker, whitelist_conf) -> None: whitelist_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PrecisionFilter"}] del whitelist_conf['stoploss'] From dec523eef0c384630c325789dd8b6eae83ee2d38 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 6 Jul 2021 07:20:05 +0200 Subject: [PATCH 037/519] Display verison of installed FreqUI --- freqtrade/rpc/api_server/web_ui.py | 11 +++++++++++ tests/rpc/test_rpc_apiserver.py | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/freqtrade/rpc/api_server/web_ui.py b/freqtrade/rpc/api_server/web_ui.py index a8c737e04..76c8ed8f2 100644 --- a/freqtrade/rpc/api_server/web_ui.py +++ b/freqtrade/rpc/api_server/web_ui.py @@ -18,6 +18,17 @@ async def fallback(): return FileResponse(str(Path(__file__).parent / 'ui/fallback_file.html')) +@router_ui.get('/ui_version', include_in_schema=False) +async def ui_version(): + from freqtrade.commands.deploy_commands import read_ui_version + uibase = Path(__file__).parent / 'ui/installed/' + version = read_ui_version(uibase) + + return { + "version": version if version else "not_installed", + } + + @router_ui.get('/{rest_of_path:path}', include_in_schema=False) async def index_html(rest_of_path: str): """ diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index b8dd112c9..89da68da7 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -105,6 +105,15 @@ def test_api_ui_fallback(botclient): assert rc.status_code == 200 +def test_api_ui_version(botclient, mocker): + ftbot, client = botclient + + mocker.patch('freqtrade.commands.deploy_commands.read_ui_version', return_value='0.1.2') + rc = client_get(client, "/ui_version") + assert rc.status_code == 200 + assert rc.json()['version'] == '0.1.2' + + def test_api_auth(): with pytest.raises(ValueError): create_token({'identity': {'u': 'Freqtrade'}}, 'secret1234', token_type="NotATokenType") From 502c69dce3b2e9b8d95e87bc293948165b5e34fc Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 6 Jul 2021 19:36:42 +0700 Subject: [PATCH 038/519] change short desc --- freqtrade/plugins/pairlist/AgeFilter.py | 24 +++++++++++++++--------- tests/plugins/test_pairlist.py | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index ef3953776..c482f6217 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -55,10 +55,13 @@ class AgeFilter(IPairList): """ Short whitelist method description - used for startup-messages """ - return (f"{self.name} - Filtering pairs with age less than " - f"{self._min_days_listed} {plural(self._min_days_listed, 'day')}" - " or more than " - f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}") + return ( + f"{self.name} - Filtering pairs with age less than " + f"{self._min_days_listed} {plural(self._min_days_listed, 'day')}" + ) + ( + " or more than " + f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}" + ) if self._max_days_listed else '' def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ @@ -105,10 +108,13 @@ class AgeFilter(IPairList): self._symbolsChecked[pair] = int(arrow.utcnow().float_timestamp) * 1000 return True else: - self.log_once(f"Removed {pair} from whitelist, because age " - f"{len(daily_candles)} is less than {self._min_days_listed} " - f"{plural(self._min_days_listed, 'day')} or more than " - f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}", - logger.info) + self.log_once(( + f"Removed {pair} from whitelist, because age " + f"{len(daily_candles)} is less than {self._min_days_listed} " + f"{plural(self._min_days_listed, 'day')}" + ) + ( + " or more than " + f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}" + ) if self.max_days_listed else '', logger.info) return False return False diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 03d8fc563..5724ca39c 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -487,7 +487,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t if pairlist['method'] == 'AgeFilter' and pairlist['min_days_listed'] and \ len(ohlcv_history) < pairlist['min_days_listed']: assert log_has_re(r'^Removed .* from whitelist, because age .* is less than ' - r'.* day.* or more than .* day', caplog) + r'.* day.*', caplog) if pairlist['method'] == 'AgeFilter' and pairlist['max_days_listed'] and \ len(ohlcv_history) > pairlist['max_days_listed']: assert log_has_re(r'^Removed .* from whitelist, because age .* is less than ' From ef137546fe1f1ed536f7daafcb1021d091a3f292 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 22 May 2020 19:57:17 +0200 Subject: [PATCH 039/519] Add webserver entrypoint --- freqtrade/commands/__init__.py | 2 +- freqtrade/commands/arguments.py | 11 ++++++++++- freqtrade/commands/trade_commands.py | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index 784b99bed..a13d0ba49 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -19,4 +19,4 @@ from freqtrade.commands.list_commands import (start_list_exchanges, start_list_h from freqtrade.commands.optimize_commands import start_backtesting, start_edge, start_hyperopt from freqtrade.commands.pairlist_commands import start_test_pairlist from freqtrade.commands.plot_commands import start_plot_dataframe, start_plot_profit -from freqtrade.commands.trade_commands import start_trading +from freqtrade.commands.trade_commands import start_trading, start_webserver diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index ba37237f6..1efe450ff 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -16,6 +16,8 @@ ARGS_STRATEGY = ["strategy", "strategy_path"] ARGS_TRADE = ["db_url", "sd_notify", "dry_run", "dry_run_wallet", "fee"] +ARGS_WEBSERVER: List[str] = [] + ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv", "max_open_trades", "stake_amount", "fee", "pairs"] @@ -176,7 +178,8 @@ class Arguments: start_list_markets, start_list_strategies, start_list_timeframes, start_new_config, start_new_hyperopt, start_new_strategy, start_plot_dataframe, start_plot_profit, - start_show_trades, start_test_pairlist, start_trading) + start_show_trades, start_test_pairlist, start_trading, + start_webserver) subparsers = self.parser.add_subparsers(dest='command', # Use custom message when no subhandler is added @@ -384,3 +387,9 @@ class Arguments: ) plot_profit_cmd.set_defaults(func=start_plot_profit) self._build_args(optionlist=ARGS_PLOT_PROFIT, parser=plot_profit_cmd) + + # Add webserver subcommand + webserver_cmd = subparsers.add_parser('webserver', help='Webserver module.', + parents=[_common_parser, _strategy_parser]) + webserver_cmd.set_defaults(func=start_webserver) + self._build_args(optionlist=ARGS_WEBSERVER, parser=webserver_cmd) diff --git a/freqtrade/commands/trade_commands.py b/freqtrade/commands/trade_commands.py index 535844844..69ea5998f 100644 --- a/freqtrade/commands/trade_commands.py +++ b/freqtrade/commands/trade_commands.py @@ -27,3 +27,11 @@ def start_trading(args: Dict[str, Any]) -> int: logger.info("worker found ... calling exit") worker.exit() return 0 + + +def start_webserver(args: Dict[str, Any]) -> int: + """ + Main entry point for webserver mode + """ + + print(args) From 97e8ec91f0c469808ac0f7f9e9880f73ffed50a9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 23 May 2020 10:44:43 +0200 Subject: [PATCH 040/519] Save configuration file paths --- freqtrade/configuration/configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 1d2e3f802..46e6f4e36 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -71,7 +71,7 @@ class Configuration: # Merge config options, overwriting old values config = deep_merge_dicts(load_config_file(path), config) - + config['config_files'] = files # Normalize config if 'internals' not in config: config['internals'] = {} From 800e314bfda53b6ccac6ca36f6a13fbb44d3d1f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 31 Dec 2020 19:46:54 +0100 Subject: [PATCH 041/519] Store backtesting results in backtest instance --- freqtrade/optimize/backtesting.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7c6b7cbc3..f883c0cbf 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -57,6 +57,7 @@ class Backtesting: LoggingMixin.show_output = False self.config = config + self.results: Optional[Dict[str, Any]] = None # Reset keys for backtesting remove_credentials(self.config) @@ -537,11 +538,12 @@ class Backtesting: for strat in self.strategylist: min_date, max_date = self.backtest_one_strategy(strat, data, timerange) if len(self.strategylist) > 0: - stats = generate_backtest_stats(data, self.all_results, - min_date=min_date, max_date=max_date) + + self.results = generate_backtest_stats(data, self.all_results, + min_date=min_date, max_date=max_date) if self.config.get('export', 'none') == 'trades': - store_backtest_stats(self.config['exportfilename'], stats) + store_backtest_stats(self.config['exportfilename'], self.results) # Show backtest results - show_backtest_results(self.config, stats) + show_backtest_results(self.config, self.results) From 02b84bd0188df372a1346a95e6c84b6cc2b4b2c1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 31 Dec 2020 20:02:27 +0100 Subject: [PATCH 042/519] Introduce webserver mode for fastapi --- freqtrade/commands/trade_commands.py | 10 +++++--- freqtrade/enums/runmode.py | 1 + freqtrade/rpc/api_server/webserver.py | 35 ++++++++++++++++++++++++--- freqtrade/rpc/rpc_manager.py | 7 +++--- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/freqtrade/commands/trade_commands.py b/freqtrade/commands/trade_commands.py index 69ea5998f..ca1451778 100644 --- a/freqtrade/commands/trade_commands.py +++ b/freqtrade/commands/trade_commands.py @@ -1,7 +1,6 @@ import logging from typing import Any, Dict - logger = logging.getLogger(__name__) @@ -29,9 +28,14 @@ def start_trading(args: Dict[str, Any]) -> int: return 0 -def start_webserver(args: Dict[str, Any]) -> int: +def start_webserver(args: Dict[str, Any]) -> None: """ Main entry point for webserver mode """ + from freqtrade.rpc.api_server import ApiServer + from freqtrade.configuration import Configuration + from freqtrade.enums import RunMode - print(args) + # Initialize configuration + config = Configuration(args, RunMode.WEBSERVER).get_config() + ApiServer(config, standalone=True) diff --git a/freqtrade/enums/runmode.py b/freqtrade/enums/runmode.py index 7826d1d0c..6545aaec7 100644 --- a/freqtrade/enums/runmode.py +++ b/freqtrade/enums/runmode.py @@ -14,6 +14,7 @@ class RunMode(Enum): UTIL_EXCHANGE = "util_exchange" UTIL_NO_EXCHANGE = "util_no_exchange" PLOT = "plot" + WEBSERVER = "webserver" OTHER = "other" diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index a43d4abe6..9cbd5512a 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -8,6 +8,7 @@ from fastapi import Depends, FastAPI from fastapi.middleware.cors import CORSMiddleware from starlette.responses import JSONResponse +from freqtrade.exceptions import OperationalException from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer from freqtrade.rpc.rpc import RPC, RPCException, RPCHandler @@ -28,16 +29,30 @@ class FTJSONResponse(JSONResponse): class ApiServer(RPCHandler): + __instance = None + __initialized = False + _rpc: RPC _has_rpc: bool = False _config: Dict[str, Any] = {} - def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None: - super().__init__(rpc, config) + def __new__(cls, *args, **kwargs): + """ + This class is a singleton. + We'll only have one instance of it around. + """ + if ApiServer.__instance is None: + ApiServer.__instance = object.__new__(cls) + ApiServer.__initialized = False + return ApiServer.__instance + + def __init__(self, config: Dict[str, Any], standalone: bool = False) -> None: + if self.__initialized and (standalone or self._standalone): + return + self._standalone: bool = standalone + self._config = config self._server = None - ApiServer._rpc = rpc - ApiServer._has_rpc = True ApiServer._config = config api_config = self._config['api_server'] @@ -50,6 +65,18 @@ class ApiServer(RPCHandler): self.start_api() + def add_rpc_handler(self, rpc: RPC): + """ + Attach rpc handler + """ + if not self._rpc: + self._rpc = rpc + ApiServer._rpc = rpc + ApiServer._has_rpc = True + else: + # This should not happen assuming we didn't mess up. + raise OperationalException('RPC Handler already attached.') + def cleanup(self) -> None: """ Cleanup pending module resources """ if self._server: diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 18ed68041..8814f70a0 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -36,15 +36,16 @@ class RPCManager: if config.get('api_server', {}).get('enabled', False): logger.info('Enabling rpc.api_server') from freqtrade.rpc.api_server import ApiServer - - self.registered_modules.append(ApiServer(self._rpc, config)) + apiserver = ApiServer(config) + apiserver.add_rpc_handler(self._rpc) + self.registered_modules.append(apiserver) def cleanup(self) -> None: """ Stops all enabled rpc modules """ logger.info('Cleaning up rpc modules ...') while self.registered_modules: mod = self.registered_modules.pop() - logger.debug('Cleaning up rpc.%s ...', mod.name) + logger.info('Cleaning up rpc.%s ...', mod.name) mod.cleanup() del mod From df55259737608a304c2fddf545bed5a8d45434f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Jan 2021 14:18:15 +0100 Subject: [PATCH 043/519] Add start_trading endpoint --- freqtrade/rpc/api_server/api_v1.py | 1 + freqtrade/rpc/api_server/webserver.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 965664028..dd1df6353 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -256,3 +256,4 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option 'pair_interval': pair_interval, } return result + diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index 9cbd5512a..f53c8b0c6 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -34,6 +34,7 @@ class ApiServer(RPCHandler): _rpc: RPC _has_rpc: bool = False + _bgtask_running: bool = False _config: Dict[str, Any] = {} def __new__(cls, *args, **kwargs): @@ -47,13 +48,13 @@ class ApiServer(RPCHandler): return ApiServer.__instance def __init__(self, config: Dict[str, Any], standalone: bool = False) -> None: + ApiServer._config = config if self.__initialized and (standalone or self._standalone): return self._standalone: bool = standalone - self._config = config self._server = None + ApiServer.__initialized = True - ApiServer._config = config api_config = self._config['api_server'] self.app = FastAPI(title="Freqtrade API", @@ -69,7 +70,7 @@ class ApiServer(RPCHandler): """ Attach rpc handler """ - if not self._rpc: + if not self._has_rpc: self._rpc = rpc ApiServer._rpc = rpc ApiServer._has_rpc = True From 5c18c8726d18006c770cbd035d574a06b1ad6521 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Jan 2021 15:13:32 +0100 Subject: [PATCH 044/519] Implement backtesting with fastapi --- freqtrade/rpc/api_server/api_schemas.py | 16 ++++ freqtrade/rpc/api_server/api_v1.py | 104 ++++++++++++++++++++++-- freqtrade/rpc/api_server/webserver.py | 2 + 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index a0f1c05a6..eccfd8157 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -313,3 +313,19 @@ class PairHistory(BaseModel): json_encoders = { datetime: lambda v: v.strftime(DATETIME_PRINT_FORMAT), } + + +class BacktestRequest(BaseModel): + strategy: str + timeframe: Optional[str] + timerange: Optional[str] + max_open_trades: Optional[int] + stake_amount: Optional[int] + + +class BacktestResponse(BaseModel): + status: str + running: bool + status_msg: str + # TODO: Properly type backtestresult... + backtest_result: Optional[Dict[str, Any]] diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index dd1df6353..9166a1cee 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -1,8 +1,10 @@ +import asyncio +import logging from copy import deepcopy from pathlib import Path from typing import List, Optional -from fastapi import APIRouter, Depends +from fastapi import APIRouter, BackgroundTasks, Depends from fastapi.exceptions import HTTPException from freqtrade import __version__ @@ -10,18 +12,21 @@ from freqtrade.constants import USERPATH_STRATEGIES from freqtrade.data.history import get_datahandler from freqtrade.exceptions import OperationalException from freqtrade.rpc import RPC -from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload, - BlacklistResponse, Count, Daily, - DeleteLockRequest, DeleteTrade, ForceBuyPayload, - ForceBuyResponse, ForceSellPayload, Locks, Logs, - OpenTradeSchema, PairHistory, PerformanceEntry, - Ping, PlotConfig, Profit, ResultMsg, ShowConfig, - Stats, StatusMsg, StrategyListResponse, - StrategyResponse, Version, WhitelistResponse) +from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, BacktestRequest, BacktestResponse, + Balances, BlacklistPayload, BlacklistResponse, + Count, Daily, DeleteLockRequest, DeleteTrade, + ForceBuyPayload, ForceBuyResponse, + ForceSellPayload, Locks, Logs, OpenTradeSchema, + PairHistory, PerformanceEntry, Ping, PlotConfig, + Profit, ResultMsg, ShowConfig, Stats, StatusMsg, + StrategyListResponse, StrategyResponse, Version, + WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException +logger = logging.getLogger(__name__) + # Public API, requires no auth. router_public = APIRouter() # Private API, protected by authentication @@ -257,3 +262,84 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option } return result + +@router.post('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) +async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: BackgroundTasks, + config=Depends(get_config)): + """Start backtesting if not done so already""" + if ApiServer._bgtask_running: + raise RPCException('Bot Background task already running') + + btconfig = deepcopy(config) + settings = dict(bt_settings) + # Pydantic models will contain all keys, but non-provided ones are None + for setting in settings.keys(): + if settings[setting] is not None: + btconfig[setting] = settings[setting] + + # Start backtesting + # Initialize backtesting object + def run_backtest(): + from freqtrade.optimize.backtesting import Backtesting + asyncio.set_event_loop(asyncio.new_event_loop()) + try: + ApiServer._backtesting = Backtesting(btconfig) + ApiServer._backtesting.start() + finally: + ApiServer._bgtask_running = False + + background_tasks.add_task(run_backtest) + ApiServer._bgtask_running = True + + return { + "status": "running", + "running": True, + "status_msg": "Backtest started", + } + + +@router.get('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) +def api_get_backtest(): + """ + Get backtesting result. + Returns Result after backtesting has been ran. + """ + if not ApiServer._backtesting: + return { + "status": "not_started", + "running": False, + "status_msg": "Backtesting not yet executed" + } + if ApiServer._bgtask_running: + return { + "status": "running", + "running": True, + "status_msg": "Backtest running", + } + + return { + "status": "ended", + "running": False, + "status_msg": "Backtest ended", + "backtest_result": ApiServer._backtesting.results, + } + + +@router.delete('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) +def api_delete_backtest(): + """Reset backtesting""" + if ApiServer._bgtask_running: + return { + "status": "running", + "running": True, + "status_msg": "Backtest running", + } + if ApiServer._backtesting: + del ApiServer._backtesting + ApiServer._backtesting = None + logger.info("Backtesting reset") + return { + "status": "reset", + "running": False, + "status_msg": "Backtesting reset", + } diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index f53c8b0c6..f2ebf785f 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -33,6 +33,8 @@ class ApiServer(RPCHandler): __initialized = False _rpc: RPC + # Backtesting type: Backtesting + _backtesting = None _has_rpc: bool = False _bgtask_running: bool = False _config: Dict[str, Any] = {} From edb8c4f0e5898fe159ef716b468d6cbd5e97843a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Jan 2021 16:12:10 +0100 Subject: [PATCH 045/519] Fix tests for webserver mode --- freqtrade/rpc/api_server/webserver.py | 8 ++++++++ tests/rpc/test_rpc_apiserver.py | 22 ++++++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index f2ebf785f..f3905fd8f 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -86,6 +86,14 @@ class ApiServer(RPCHandler): logger.info("Stopping API Server") self._server.cleanup() + @classmethod + def shutdown(cls): + cls.__initialized = False + del cls.__instance + cls.__instance = None + cls._has_rpc = False + cls._rpc = None + def send_msg(self, msg: Dict[str, str]) -> None: pass diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 89da68da7..a8ffd3eff 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -48,9 +48,13 @@ def botclient(default_conf, mocker): ftbot = get_patched_freqtradebot(mocker, default_conf) rpc = RPC(ftbot) mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock()) - apiserver = ApiServer(rpc, default_conf) - yield ftbot, TestClient(apiserver.app) - # Cleanup ... ? + try: + apiserver = ApiServer(default_conf) + apiserver.add_rpc_handler(rpc) + yield ftbot, TestClient(apiserver.app) + # Cleanup ... ? + finally: + ApiServer.shutdown() def client_post(client, url, data={}): @@ -235,8 +239,10 @@ def test_api__init__(default_conf, mocker): }}) mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock()) mocker.patch('freqtrade.rpc.api_server.webserver.ApiServer.start_api', MagicMock()) - apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) + apiserver = ApiServer(default_conf) + apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf))) assert apiserver._config == default_conf + ApiServer.shutdown() def test_api_UvicornServer(mocker): @@ -301,7 +307,8 @@ def test_api_run(default_conf, mocker, caplog): server_mock = MagicMock() mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock) - apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) + apiserver = ApiServer(default_conf) + apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf))) assert server_mock.call_count == 1 assert apiserver._config == default_conf @@ -344,6 +351,7 @@ def test_api_run(default_conf, mocker, caplog): MagicMock(side_effect=Exception)) apiserver.start_api() assert log_has("Api server failed to start.", caplog) + ApiServer.shutdown() def test_api_cleanup(default_conf, mocker, caplog): @@ -359,11 +367,13 @@ def test_api_cleanup(default_conf, mocker, caplog): server_mock.cleanup = MagicMock() mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock) - apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) + apiserver = ApiServer(default_conf) + apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf))) apiserver.cleanup() assert apiserver._server.cleanup.call_count == 1 assert log_has("Stopping API Server", caplog) + ApiServer.shutdown() def test_api_reloadconf(botclient): From f96d7dfe6dbf78b11c878749e49b01b27a9af7e6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 6 Jan 2021 15:05:54 +0100 Subject: [PATCH 046/519] Allow backtesting to reuse data Allow activating / deactivating protections dynamically --- freqtrade/rpc/api_server/api_schemas.py | 1 + freqtrade/rpc/api_server/api_v1.py | 58 +++++++++++++++++++------ freqtrade/rpc/api_server/webserver.py | 5 ++- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index eccfd8157..bb611f287 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -321,6 +321,7 @@ class BacktestRequest(BaseModel): timerange: Optional[str] max_open_trades: Optional[int] stake_amount: Optional[int] + enable_protections: bool class BacktestResponse(BaseModel): diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 9166a1cee..c162a57dc 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -280,11 +280,40 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac # Start backtesting # Initialize backtesting object def run_backtest(): - from freqtrade.optimize.backtesting import Backtesting + from freqtrade.optimize.optimize_reports import generate_backtest_stats + from freqtrade.resolvers import StrategyResolver asyncio.set_event_loop(asyncio.new_event_loop()) try: - ApiServer._backtesting = Backtesting(btconfig) - ApiServer._backtesting.start() + # Reload strategy + strat = StrategyResolver.load_strategy(btconfig) + + if (not ApiServer._bt + or ApiServer._lastbacktestconfig.get('timeframe') != strat.timeframe + or ApiServer._lastbacktestconfig.get('enable_protections') != btconfig.get('enable_protections') + or ApiServer._lastbacktestconfig.get('protections') != btconfig.get('protections', [])): + # TODO: Investigate if enabling protections can be dynamically ingested from here... + from freqtrade.optimize.backtesting import Backtesting + ApiServer._bt = Backtesting(btconfig) + # Reset data if backtesting is reloaded + # TODO: is this always necessary?? + ApiServer._backtestdata = None + + if (not ApiServer._backtestdata or not ApiServer._bt_timerange + or ApiServer._lastbacktestconfig.get('timerange') != btconfig['timerange']): + ApiServer._lastbacktestconfig['timerange'] = btconfig['timerange'] + ApiServer._lastbacktestconfig['protections'] = btconfig.get('protections', []) + ApiServer._lastbacktestconfig['enable_protections'] = btconfig.get('enable_protections') + ApiServer._lastbacktestconfig['timeframe'] = strat.timeframe + ApiServer._backtestdata, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() + + min_date, max_date = ApiServer._bt.backtest_one_strategy( + strat, ApiServer._backtestdata, + ApiServer._bt_timerange) + ApiServer._bt.results = generate_backtest_stats( + ApiServer._backtestdata, ApiServer._bt.all_results, + min_date=min_date, max_date=max_date) + logger.info("Backtesting finished.") + finally: ApiServer._bgtask_running = False @@ -304,12 +333,6 @@ def api_get_backtest(): Get backtesting result. Returns Result after backtesting has been ran. """ - if not ApiServer._backtesting: - return { - "status": "not_started", - "running": False, - "status_msg": "Backtesting not yet executed" - } if ApiServer._bgtask_running: return { "status": "running", @@ -317,11 +340,18 @@ def api_get_backtest(): "status_msg": "Backtest running", } + if not ApiServer._bt: + return { + "status": "not_started", + "running": False, + "status_msg": "Backtesting not yet executed" + } + return { "status": "ended", "running": False, "status_msg": "Backtest ended", - "backtest_result": ApiServer._backtesting.results, + "backtest_result": ApiServer._bt.results, } @@ -334,9 +364,11 @@ def api_delete_backtest(): "running": True, "status_msg": "Backtest running", } - if ApiServer._backtesting: - del ApiServer._backtesting - ApiServer._backtesting = None + if ApiServer._bt: + del ApiServer._bt + ApiServer._bt = None + del ApiServer._backtestdata + ApiServer._backtestdata = None logger.info("Backtesting reset") return { "status": "reset", diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index f3905fd8f..eccbadcb3 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -34,7 +34,10 @@ class ApiServer(RPCHandler): _rpc: RPC # Backtesting type: Backtesting - _backtesting = None + _bt = None + _backtestdata = None + _bt_timerange = None + _lastbacktestconfig: Dict[str, Any] = {} _has_rpc: bool = False _bgtask_running: bool = False _config: Dict[str, Any] = {} From 06b6726029d80903e52d6aed2c4e7599b106de12 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 28 Feb 2021 09:56:13 +0100 Subject: [PATCH 047/519] Support compounding key --- freqtrade/rpc/api_server/api_schemas.py | 3 ++- freqtrade/rpc/api_server/api_v1.py | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index bb611f287..bf5382131 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -320,8 +320,9 @@ class BacktestRequest(BaseModel): timeframe: Optional[str] timerange: Optional[str] max_open_trades: Optional[int] - stake_amount: Optional[int] + stake_amount: Optional[Union[float, str]] enable_protections: bool + dry_run_wallet: Optional[float] class BacktestResponse(BaseModel): diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index c162a57dc..d4f138ee8 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -285,12 +285,15 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac asyncio.set_event_loop(asyncio.new_event_loop()) try: # Reload strategy + lastconfig = ApiServer._lastbacktestconfig strat = StrategyResolver.load_strategy(btconfig) if (not ApiServer._bt - or ApiServer._lastbacktestconfig.get('timeframe') != strat.timeframe - or ApiServer._lastbacktestconfig.get('enable_protections') != btconfig.get('enable_protections') - or ApiServer._lastbacktestconfig.get('protections') != btconfig.get('protections', [])): + or lastconfig.get('timeframe') != strat.timeframe + or lastconfig.get('enable_protections') != btconfig.get('enable_protections') + or lastconfig.get('protections') != btconfig.get('protections', []) + or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0) + ): # TODO: Investigate if enabling protections can be dynamically ingested from here... from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) @@ -299,11 +302,12 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac ApiServer._backtestdata = None if (not ApiServer._backtestdata or not ApiServer._bt_timerange - or ApiServer._lastbacktestconfig.get('timerange') != btconfig['timerange']): - ApiServer._lastbacktestconfig['timerange'] = btconfig['timerange'] - ApiServer._lastbacktestconfig['protections'] = btconfig.get('protections', []) - ApiServer._lastbacktestconfig['enable_protections'] = btconfig.get('enable_protections') - ApiServer._lastbacktestconfig['timeframe'] = strat.timeframe + or lastconfig.get('timerange') != btconfig['timerange']): + lastconfig['timerange'] = btconfig['timerange'] + lastconfig['protections'] = btconfig.get('protections', []) + lastconfig['enable_protections'] = btconfig.get('enable_protections') + lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') + lastconfig['timeframe'] = strat.timeframe ApiServer._backtestdata, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() min_date, max_date = ApiServer._bt.backtest_one_strategy( From 048008756ff8390c34f32a583bef05d1205e079d Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 11 Mar 2021 19:16:18 +0100 Subject: [PATCH 048/519] Add progress tracking for backtesting --- freqtrade/enums/__init__.py | 1 + freqtrade/enums/backteststate.py | 14 +++++++++++++ freqtrade/optimize/backtesting.py | 28 ++++++++++++++++++++++++- freqtrade/rpc/api_server/api_schemas.py | 2 ++ freqtrade/rpc/api_server/api_v1.py | 14 +++++++++++-- 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 freqtrade/enums/backteststate.py diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 78163d86f..ac5f804c9 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -1,4 +1,5 @@ # flake8: noqa: F401 +from freqtrade.enums.backteststate import BacktestState from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType diff --git a/freqtrade/enums/backteststate.py b/freqtrade/enums/backteststate.py new file mode 100644 index 000000000..4c1bd7cbc --- /dev/null +++ b/freqtrade/enums/backteststate.py @@ -0,0 +1,14 @@ +from enum import Enum + + +class BacktestState(Enum): + """ + Bot application states + """ + STARTUP = 1 + DATALOAD = 2 + ANALYZE = 3 + BACKTEST = 4 + + def __str__(self): + return f"{self.name.lower()}" diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index f883c0cbf..9168a0ddb 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -17,7 +17,7 @@ from freqtrade.data import history from freqtrade.data.btanalysis import trade_list_to_dataframe from freqtrade.data.converter import trim_dataframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import SellType +from freqtrade.enums import BacktestState, SellType from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.mixins import LoggingMixin @@ -118,11 +118,26 @@ class Backtesting: # Get maximum required startup period self.required_startup = max([strat.startup_candle_count for strat in self.strategylist]) + self._progress = 0 + self._max_steps = 0 + self._action = BacktestState.STARTUP + def __del__(self): LoggingMixin.show_output = True PairLocks.use_db = True Trade.use_db = True + def set_progress(self, action: BacktestState, progress: float): + self._progress = 0 + self._action = action + + def get_progress(self) -> float: + + return round(self._progress / self._max_steps, 5) if self._max_steps > 0 else 0 + + def get_action(self) -> str: + return str(self._action) + def _set_strategy(self, strategy: IStrategy): """ Load strategy into backtesting @@ -145,6 +160,8 @@ class Backtesting: Loads backtest data and returns the data combined with the timerange as tuple. """ + self.set_progress(BacktestState.DATALOAD, 0) + timerange = TimeRange.parse_timerange(None if self.config.get( 'timerange') is None else str(self.config.get('timerange'))) @@ -168,6 +185,7 @@ class Backtesting: timerange.adjust_start_if_necessary(timeframe_to_seconds(self.timeframe), self.required_startup, min_date) + self.set_progress(BacktestState.DATALOAD, 1) return data, timerange def prepare_backtest(self, enable_protections): @@ -404,6 +422,9 @@ class Backtesting: open_trades: Dict[str, List[LocalTrade]] = defaultdict(list) open_trade_count = 0 + self.set_progress(BacktestState.BACKTEST, 0) + self._max_steps = int((end_date - start_date) / timedelta(minutes=self.timeframe_min)) + # Loop timerange and get candle for each pair at that point in time while tmp <= end_date: open_trade_count_start = open_trade_count @@ -463,6 +484,7 @@ class Backtesting: self.protections.global_stop(tmp) # Move time one configured time_interval ahead. + self._progress += 1 tmp += timedelta(minutes=self.timeframe_min) trades += self.handle_left_open(open_trades, data=data) @@ -478,6 +500,8 @@ class Backtesting: } def backtest_one_strategy(self, strat: IStrategy, data: Dict[str, Any], timerange: TimeRange): + self.set_progress(BacktestState.ANALYZE, 0) + self._max_steps = 0 logger.info("Running backtesting for Strategy %s", strat.get_strategy_name()) backtest_start_time = datetime.now(timezone.utc) self._set_strategy(strat) @@ -504,6 +528,8 @@ class Backtesting: "No data left after adjusting for startup candles.") min_date, max_date = history.get_timerange(preprocessed) + self.set_progress(BacktestState.BACKTEST, 0) + logger.info(f'Backtesting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} ' f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} ' f'({(max_date - min_date).days} days).') diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index bf5382131..9a4ac5cd0 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -329,5 +329,7 @@ class BacktestResponse(BaseModel): status: str running: bool status_msg: str + step: str + progress: float # TODO: Properly type backtestresult... backtest_result: Optional[Dict[str, Any]] diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index d4f138ee8..30b4c998c 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -23,6 +23,7 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, BacktestReques WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException +from freqtrade.state import BacktestState logger = logging.getLogger(__name__) @@ -292,8 +293,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac or lastconfig.get('timeframe') != strat.timeframe or lastconfig.get('enable_protections') != btconfig.get('enable_protections') or lastconfig.get('protections') != btconfig.get('protections', []) - or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0) - ): + or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0)): # TODO: Investigate if enabling protections can be dynamically ingested from here... from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) @@ -327,6 +327,8 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac return { "status": "running", "running": True, + "progress": 0, + "step": str(BacktestState.STARTUP), "status_msg": "Backtest started", } @@ -341,6 +343,8 @@ def api_get_backtest(): return { "status": "running", "running": True, + "step": ApiServer._bt.get_action() if ApiServer._bt else str(BacktestState.STARTUP), + "progress": ApiServer._bt.get_progress() if ApiServer._bt else 0, "status_msg": "Backtest running", } @@ -348,6 +352,8 @@ def api_get_backtest(): return { "status": "not_started", "running": False, + "step": "", + "progress": 0, "status_msg": "Backtesting not yet executed" } @@ -355,6 +361,8 @@ def api_get_backtest(): "status": "ended", "running": False, "status_msg": "Backtest ended", + "step": "finished", + "progress": 1, "backtest_result": ApiServer._bt.results, } @@ -366,6 +374,7 @@ def api_delete_backtest(): return { "status": "running", "running": True, + "progress": 0, "status_msg": "Backtest running", } if ApiServer._bt: @@ -377,5 +386,6 @@ def api_delete_backtest(): return { "status": "reset", "running": False, + "progress": 0, "status_msg": "Backtesting reset", } From 37b15e830a6b67ba5b7b89a2d393c650568bf82c Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 12 Mar 2021 19:11:17 +0100 Subject: [PATCH 049/519] Add trade count to progress --- freqtrade/rpc/api_server/api_schemas.py | 1 + freqtrade/rpc/api_server/api_v1.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 9a4ac5cd0..f10c501f7 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -331,5 +331,6 @@ class BacktestResponse(BaseModel): status_msg: str step: str progress: float + trade_count: Optional[float] # TODO: Properly type backtestresult... backtest_result: Optional[Dict[str, Any]] diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 30b4c998c..339f8565a 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -339,12 +339,14 @@ def api_get_backtest(): Get backtesting result. Returns Result after backtesting has been ran. """ + from freqtrade.persistence import Trade if ApiServer._bgtask_running: return { "status": "running", "running": True, "step": ApiServer._bt.get_action() if ApiServer._bt else str(BacktestState.STARTUP), "progress": ApiServer._bt.get_progress() if ApiServer._bt else 0, + "trade_count": Trade.get_trades_proxy(is_open=False), "status_msg": "Backtest running", } From 03140a0ecbee240446bae65791a12f69feb2092a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Mar 2021 10:45:44 +0100 Subject: [PATCH 050/519] Run webserver in main thread when using webserver mode --- freqtrade/rpc/api_server/api_v1.py | 4 ++-- freqtrade/rpc/api_server/webserver.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 339f8565a..fd3cb345e 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -339,14 +339,14 @@ def api_get_backtest(): Get backtesting result. Returns Result after backtesting has been ran. """ - from freqtrade.persistence import Trade + from freqtrade.persistence import LocalTrade if ApiServer._bgtask_running: return { "status": "running", "running": True, "step": ApiServer._bt.get_action() if ApiServer._bt else str(BacktestState.STARTUP), "progress": ApiServer._bt.get_progress() if ApiServer._bt else 0, - "trade_count": Trade.get_trades_proxy(is_open=False), + "trade_count": len(LocalTrade.trades), "status_msg": "Backtest running", } diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index eccbadcb3..ac394b59d 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -166,6 +166,9 @@ class ApiServer(RPCHandler): ) try: self._server = UvicornServer(uvconfig) - self._server.run_in_thread() + if self._standalone: + self._server.run() + else: + self._server.run_in_thread() except Exception: logger.exception("Api server failed to start.") From 134c61126e8006004a6f48ea35281fc3aad2c1f6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Mar 2021 15:56:36 +0100 Subject: [PATCH 051/519] Properly track bt progress ... --- freqtrade/enums/backteststate.py | 3 ++- freqtrade/optimize/backtesting.py | 34 +++++++++++------------------- freqtrade/optimize/bt_progress.py | 34 ++++++++++++++++++++++++++++++ freqtrade/rpc/api_server/api_v1.py | 10 ++++----- 4 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 freqtrade/optimize/bt_progress.py diff --git a/freqtrade/enums/backteststate.py b/freqtrade/enums/backteststate.py index 4c1bd7cbc..490814497 100644 --- a/freqtrade/enums/backteststate.py +++ b/freqtrade/enums/backteststate.py @@ -8,7 +8,8 @@ class BacktestState(Enum): STARTUP = 1 DATALOAD = 2 ANALYZE = 3 - BACKTEST = 4 + CONVERT = 4 + BACKTEST = 5 def __str__(self): return f"{self.name.lower()}" diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 9168a0ddb..c8d4efa74 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -21,6 +21,7 @@ from freqtrade.enums import BacktestState, SellType from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.mixins import LoggingMixin +from freqtrade.optimize.bt_progress import BTProgress from freqtrade.optimize.optimize_reports import (generate_backtest_stats, show_backtest_results, store_backtest_stats) from freqtrade.persistence import LocalTrade, PairLocks, Trade @@ -118,26 +119,13 @@ class Backtesting: # Get maximum required startup period self.required_startup = max([strat.startup_candle_count for strat in self.strategylist]) - self._progress = 0 - self._max_steps = 0 - self._action = BacktestState.STARTUP + self.progress = BTProgress() def __del__(self): LoggingMixin.show_output = True PairLocks.use_db = True Trade.use_db = True - def set_progress(self, action: BacktestState, progress: float): - self._progress = 0 - self._action = action - - def get_progress(self) -> float: - - return round(self._progress / self._max_steps, 5) if self._max_steps > 0 else 0 - - def get_action(self) -> str: - return str(self._action) - def _set_strategy(self, strategy: IStrategy): """ Load strategy into backtesting @@ -160,7 +148,7 @@ class Backtesting: Loads backtest data and returns the data combined with the timerange as tuple. """ - self.set_progress(BacktestState.DATALOAD, 0) + self.progress.init_step(BacktestState.DATALOAD, 1) timerange = TimeRange.parse_timerange(None if self.config.get( 'timerange') is None else str(self.config.get('timerange'))) @@ -185,7 +173,7 @@ class Backtesting: timerange.adjust_start_if_necessary(timeframe_to_seconds(self.timeframe), self.required_startup, min_date) - self.set_progress(BacktestState.DATALOAD, 1) + self.progress.set_new_value(1) return data, timerange def prepare_backtest(self, enable_protections): @@ -210,8 +198,11 @@ class Backtesting: # and eventually change the constants for indexes at the top headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] data: Dict = {} + self.progress.init_step(BacktestState.CONVERT, len(processed)) + # Create dict with data for pair, pair_data in processed.items(): + self.progress.increment() if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist @@ -422,8 +413,8 @@ class Backtesting: open_trades: Dict[str, List[LocalTrade]] = defaultdict(list) open_trade_count = 0 - self.set_progress(BacktestState.BACKTEST, 0) - self._max_steps = int((end_date - start_date) / timedelta(minutes=self.timeframe_min)) + self.progress.init_step(BacktestState.BACKTEST, int( + (end_date - start_date) / timedelta(minutes=self.timeframe_min))) # Loop timerange and get candle for each pair at that point in time while tmp <= end_date: @@ -484,7 +475,7 @@ class Backtesting: self.protections.global_stop(tmp) # Move time one configured time_interval ahead. - self._progress += 1 + self.progress.increment() tmp += timedelta(minutes=self.timeframe_min) trades += self.handle_left_open(open_trades, data=data) @@ -500,8 +491,8 @@ class Backtesting: } def backtest_one_strategy(self, strat: IStrategy, data: Dict[str, Any], timerange: TimeRange): - self.set_progress(BacktestState.ANALYZE, 0) - self._max_steps = 0 + self.progress.init_step(BacktestState.ANALYZE, 0) + logger.info("Running backtesting for Strategy %s", strat.get_strategy_name()) backtest_start_time = datetime.now(timezone.utc) self._set_strategy(strat) @@ -528,7 +519,6 @@ class Backtesting: "No data left after adjusting for startup candles.") min_date, max_date = history.get_timerange(preprocessed) - self.set_progress(BacktestState.BACKTEST, 0) logger.info(f'Backtesting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} ' f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} ' diff --git a/freqtrade/optimize/bt_progress.py b/freqtrade/optimize/bt_progress.py new file mode 100644 index 000000000..8d4fd1737 --- /dev/null +++ b/freqtrade/optimize/bt_progress.py @@ -0,0 +1,34 @@ + +from freqtrade.enums import BacktestState + + +class BTProgress: + _action: BacktestState = BacktestState.STARTUP + _progress: float = 0 + _max_steps: float = 0 + + def __init__(self): + pass + + def init_step(self, action: BacktestState, max_steps: float): + self._action = action + self._max_steps = max_steps + self._proress = 0 + + def set_new_value(self, new_value: float): + self._progress = new_value + + def increment(self): + self._progress += 1 + + @property + def progress(self): + """ + Get progress as ratio, capped to be between 0 and 1 (to avoid small calculation errors). + """ + return max(min(round(self._progress / self._max_steps, 5) + if self._max_steps > 0 else 0, 1), 0) + + @property + def action(self): + return str(self._action) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index fd3cb345e..0e3b16baf 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -291,6 +291,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac if (not ApiServer._bt or lastconfig.get('timeframe') != strat.timeframe + or lastconfig.get('stake_amount') != btconfig.get('stake_amount') or lastconfig.get('enable_protections') != btconfig.get('enable_protections') or lastconfig.get('protections') != btconfig.get('protections', []) or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0)): @@ -298,11 +299,10 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) # Reset data if backtesting is reloaded - # TODO: is this always necessary?? - ApiServer._backtestdata = None if (not ApiServer._backtestdata or not ApiServer._bt_timerange - or lastconfig.get('timerange') != btconfig['timerange']): + or lastconfig.get('timerange') != btconfig['timerange'] + or lastconfig.get('timeframe') != strat.timeframe): lastconfig['timerange'] = btconfig['timerange'] lastconfig['protections'] = btconfig.get('protections', []) lastconfig['enable_protections'] = btconfig.get('enable_protections') @@ -344,8 +344,8 @@ def api_get_backtest(): return { "status": "running", "running": True, - "step": ApiServer._bt.get_action() if ApiServer._bt else str(BacktestState.STARTUP), - "progress": ApiServer._bt.get_progress() if ApiServer._bt else 0, + "step": ApiServer._bt.progress.action if ApiServer._bt else str(BacktestState.STARTUP), + "progress": ApiServer._bt.progress.progress if ApiServer._bt else 0, "trade_count": len(LocalTrade.trades), "status_msg": "Backtest running", } From 85663060107e8e13b4bedf74e2db17a3fbbbcbd4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 1 Apr 2021 07:49:54 +0200 Subject: [PATCH 052/519] Add test for start_websever --- freqtrade/commands/trade_commands.py | 3 ++- tests/commands/test_commands.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/commands/trade_commands.py b/freqtrade/commands/trade_commands.py index ca1451778..3c3f3db9f 100644 --- a/freqtrade/commands/trade_commands.py +++ b/freqtrade/commands/trade_commands.py @@ -1,6 +1,7 @@ import logging from typing import Any, Dict + logger = logging.getLogger(__name__) @@ -32,9 +33,9 @@ def start_webserver(args: Dict[str, Any]) -> None: """ Main entry point for webserver mode """ - from freqtrade.rpc.api_server import ApiServer from freqtrade.configuration import Configuration from freqtrade.enums import RunMode + from freqtrade.rpc.api_server import ApiServer # Initialize configuration config = Configuration(args, RunMode.WEBSERVER).get_config() diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index dcceb3ea1..bef421c89 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -16,6 +16,7 @@ from freqtrade.commands import (start_convert_data, start_create_userdir, start_ start_test_pairlist, start_trading) from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, get_ui_download_url, read_ui_version) +from freqtrade.commands.trade_commands import start_webserver from freqtrade.configuration import setup_utils_configuration from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException @@ -58,6 +59,18 @@ def test_start_trading_fail(mocker, caplog): assert log_has('Fatal exception!', caplog) +def test_start_webserver(mocker, caplog): + + api_server_mock = mocker.patch("freqtrade.rpc.api_server.ApiServer", ) + + args = [ + 'webserver', + '-c', 'config_bittrex.json.example' + ] + start_webserver(get_args(args)) + assert api_server_mock.call_count == 1 + + def test_list_exchanges(capsys): args = [ From 804d99cce91628075de9c7fd317f9bca8270cccf Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 1 Apr 2021 07:55:29 +0200 Subject: [PATCH 053/519] Move backtesting api to it's own file --- freqtrade/rpc/api_server/api_backtest.py | 149 +++++++++++++++++++++++ freqtrade/rpc/api_server/api_v1.py | 134 +------------------- freqtrade/rpc/api_server/webserver.py | 4 + 3 files changed, 155 insertions(+), 132 deletions(-) create mode 100644 freqtrade/rpc/api_server/api_backtest.py diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py new file mode 100644 index 000000000..ea566f1f1 --- /dev/null +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -0,0 +1,149 @@ + +import asyncio +import logging +from copy import deepcopy + +from fastapi import APIRouter, BackgroundTasks, Depends + +from freqtrade.enums import BacktestState +from freqtrade.rpc.api_server.api_schemas import BacktestRequest, BacktestResponse +from freqtrade.rpc.api_server.deps import get_config +from freqtrade.rpc.api_server.webserver import ApiServer +from freqtrade.rpc.rpc import RPCException + + +logger = logging.getLogger(__name__) + +# Private API, protected by authentication +router = APIRouter() + + +@router.post('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) +async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: BackgroundTasks, + config=Depends(get_config)): + """Start backtesting if not done so already""" + if ApiServer._bgtask_running: + raise RPCException('Bot Background task already running') + + btconfig = deepcopy(config) + settings = dict(bt_settings) + # Pydantic models will contain all keys, but non-provided ones are None + for setting in settings.keys(): + if settings[setting] is not None: + btconfig[setting] = settings[setting] + + # Start backtesting + # Initialize backtesting object + def run_backtest(): + from freqtrade.optimize.optimize_reports import generate_backtest_stats + from freqtrade.resolvers import StrategyResolver + asyncio.set_event_loop(asyncio.new_event_loop()) + try: + # Reload strategy + lastconfig = ApiServer._lastbacktestconfig + strat = StrategyResolver.load_strategy(btconfig) + + if (not ApiServer._bt + or lastconfig.get('timeframe') != strat.timeframe + or lastconfig.get('stake_amount') != btconfig.get('stake_amount') + or lastconfig.get('enable_protections') != btconfig.get('enable_protections') + or lastconfig.get('protections') != btconfig.get('protections', []) + or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0)): + # TODO: Investigate if enabling protections can be dynamically ingested from here... + from freqtrade.optimize.backtesting import Backtesting + ApiServer._bt = Backtesting(btconfig) + # Reset data if backtesting is reloaded + + if (not ApiServer._backtestdata or not ApiServer._bt_timerange + or lastconfig.get('timerange') != btconfig['timerange'] + or lastconfig.get('timeframe') != strat.timeframe): + lastconfig['timerange'] = btconfig['timerange'] + lastconfig['protections'] = btconfig.get('protections', []) + lastconfig['enable_protections'] = btconfig.get('enable_protections') + lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') + lastconfig['timeframe'] = strat.timeframe + ApiServer._backtestdata, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() + + min_date, max_date = ApiServer._bt.backtest_one_strategy( + strat, ApiServer._backtestdata, + ApiServer._bt_timerange) + ApiServer._bt.results = generate_backtest_stats( + ApiServer._backtestdata, ApiServer._bt.all_results, + min_date=min_date, max_date=max_date) + logger.info("Backtesting finished.") + + finally: + ApiServer._bgtask_running = False + + background_tasks.add_task(run_backtest) + ApiServer._bgtask_running = True + + return { + "status": "running", + "running": True, + "progress": 0, + "step": str(BacktestState.STARTUP), + "status_msg": "Backtest started", + } + + +@router.get('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) +def api_get_backtest(): + """ + Get backtesting result. + Returns Result after backtesting has been ran. + """ + from freqtrade.persistence import LocalTrade + if ApiServer._bgtask_running: + return { + "status": "running", + "running": True, + "step": ApiServer._bt.progress.action if ApiServer._bt else str(BacktestState.STARTUP), + "progress": ApiServer._bt.progress.progress if ApiServer._bt else 0, + "trade_count": len(LocalTrade.trades), + "status_msg": "Backtest running", + } + + if not ApiServer._bt: + return { + "status": "not_started", + "running": False, + "step": "", + "progress": 0, + "status_msg": "Backtesting not yet executed" + } + + return { + "status": "ended", + "running": False, + "status_msg": "Backtest ended", + "step": "finished", + "progress": 1, + "backtest_result": ApiServer._bt.results, + } + + +@router.delete('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) +def api_delete_backtest(): + """Reset backtesting""" + if ApiServer._bgtask_running: + return { + "status": "running", + "running": True, + "step": "", + "progress": 0, + "status_msg": "Backtest running", + } + if ApiServer._bt: + del ApiServer._bt + ApiServer._bt = None + del ApiServer._backtestdata + ApiServer._backtestdata = None + logger.info("Backtesting reset") + return { + "status": "reset", + "running": False, + "step": "", + "progress": 0, + "status_msg": "Backtesting reset", + } diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 0e3b16baf..3f3f799b0 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -1,10 +1,9 @@ -import asyncio import logging from copy import deepcopy from pathlib import Path from typing import List, Optional -from fastapi import APIRouter, BackgroundTasks, Depends +from fastapi import APIRouter, Depends from fastapi.exceptions import HTTPException from freqtrade import __version__ @@ -12,7 +11,7 @@ from freqtrade.constants import USERPATH_STRATEGIES from freqtrade.data.history import get_datahandler from freqtrade.exceptions import OperationalException from freqtrade.rpc import RPC -from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, BacktestRequest, BacktestResponse, +from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload, BlacklistResponse, Count, Daily, DeleteLockRequest, DeleteTrade, ForceBuyPayload, ForceBuyResponse, @@ -23,7 +22,6 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, BacktestReques WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException -from freqtrade.state import BacktestState logger = logging.getLogger(__name__) @@ -263,131 +261,3 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option } return result - -@router.post('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) -async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: BackgroundTasks, - config=Depends(get_config)): - """Start backtesting if not done so already""" - if ApiServer._bgtask_running: - raise RPCException('Bot Background task already running') - - btconfig = deepcopy(config) - settings = dict(bt_settings) - # Pydantic models will contain all keys, but non-provided ones are None - for setting in settings.keys(): - if settings[setting] is not None: - btconfig[setting] = settings[setting] - - # Start backtesting - # Initialize backtesting object - def run_backtest(): - from freqtrade.optimize.optimize_reports import generate_backtest_stats - from freqtrade.resolvers import StrategyResolver - asyncio.set_event_loop(asyncio.new_event_loop()) - try: - # Reload strategy - lastconfig = ApiServer._lastbacktestconfig - strat = StrategyResolver.load_strategy(btconfig) - - if (not ApiServer._bt - or lastconfig.get('timeframe') != strat.timeframe - or lastconfig.get('stake_amount') != btconfig.get('stake_amount') - or lastconfig.get('enable_protections') != btconfig.get('enable_protections') - or lastconfig.get('protections') != btconfig.get('protections', []) - or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0)): - # TODO: Investigate if enabling protections can be dynamically ingested from here... - from freqtrade.optimize.backtesting import Backtesting - ApiServer._bt = Backtesting(btconfig) - # Reset data if backtesting is reloaded - - if (not ApiServer._backtestdata or not ApiServer._bt_timerange - or lastconfig.get('timerange') != btconfig['timerange'] - or lastconfig.get('timeframe') != strat.timeframe): - lastconfig['timerange'] = btconfig['timerange'] - lastconfig['protections'] = btconfig.get('protections', []) - lastconfig['enable_protections'] = btconfig.get('enable_protections') - lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') - lastconfig['timeframe'] = strat.timeframe - ApiServer._backtestdata, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() - - min_date, max_date = ApiServer._bt.backtest_one_strategy( - strat, ApiServer._backtestdata, - ApiServer._bt_timerange) - ApiServer._bt.results = generate_backtest_stats( - ApiServer._backtestdata, ApiServer._bt.all_results, - min_date=min_date, max_date=max_date) - logger.info("Backtesting finished.") - - finally: - ApiServer._bgtask_running = False - - background_tasks.add_task(run_backtest) - ApiServer._bgtask_running = True - - return { - "status": "running", - "running": True, - "progress": 0, - "step": str(BacktestState.STARTUP), - "status_msg": "Backtest started", - } - - -@router.get('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) -def api_get_backtest(): - """ - Get backtesting result. - Returns Result after backtesting has been ran. - """ - from freqtrade.persistence import LocalTrade - if ApiServer._bgtask_running: - return { - "status": "running", - "running": True, - "step": ApiServer._bt.progress.action if ApiServer._bt else str(BacktestState.STARTUP), - "progress": ApiServer._bt.progress.progress if ApiServer._bt else 0, - "trade_count": len(LocalTrade.trades), - "status_msg": "Backtest running", - } - - if not ApiServer._bt: - return { - "status": "not_started", - "running": False, - "step": "", - "progress": 0, - "status_msg": "Backtesting not yet executed" - } - - return { - "status": "ended", - "running": False, - "status_msg": "Backtest ended", - "step": "finished", - "progress": 1, - "backtest_result": ApiServer._bt.results, - } - - -@router.delete('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest']) -def api_delete_backtest(): - """Reset backtesting""" - if ApiServer._bgtask_running: - return { - "status": "running", - "running": True, - "progress": 0, - "status_msg": "Backtest running", - } - if ApiServer._bt: - del ApiServer._bt - ApiServer._bt = None - del ApiServer._backtestdata - ApiServer._backtestdata = None - logger.info("Backtesting reset") - return { - "status": "reset", - "running": False, - "progress": 0, - "status_msg": "Backtesting reset", - } diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index ac394b59d..0b58d79ff 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -112,12 +112,16 @@ class ApiServer(RPCHandler): from freqtrade.rpc.api_server.api_v1 import router as api_v1 from freqtrade.rpc.api_server.api_v1 import router_public as api_v1_public from freqtrade.rpc.api_server.web_ui import router_ui + from freqtrade.rpc.api_server.api_backtest import router as api_backtest app.include_router(api_v1_public, prefix="/api/v1") app.include_router(api_v1, prefix="/api/v1", dependencies=[Depends(http_basic_or_jwt_token)], ) + app.include_router(api_backtest, prefix="/api/v1", + dependencies=[Depends(http_basic_or_jwt_token)], + ) app.include_router(router_login, prefix="/api/v1", tags=["auth"]) # UI Router MUST be last! app.include_router(router_ui, prefix='') From b44d215b90a5253d1069a43f6e9b7f6a751108c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 1 Apr 2021 19:59:49 +0200 Subject: [PATCH 054/519] Add test for backtest via APII --- freqtrade/rpc/api_server/api_backtest.py | 6 +-- tests/rpc/test_rpc_apiserver.py | 66 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index ea566f1f1..48aee0cc5 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -70,7 +70,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac ApiServer._bt.results = generate_backtest_stats( ApiServer._backtestdata, ApiServer._bt.all_results, min_date=min_date, max_date=max_date) - logger.info("Backtesting finished.") + logger.info("Backtest finished.") finally: ApiServer._bgtask_running = False @@ -110,7 +110,7 @@ def api_get_backtest(): "running": False, "step": "", "progress": 0, - "status_msg": "Backtesting not yet executed" + "status_msg": "Backtest not yet executed" } return { @@ -145,5 +145,5 @@ def api_delete_backtest(): "running": False, "step": "", "progress": 0, - "status_msg": "Backtesting reset", + "status_msg": "Backtest reset", } diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index a8ffd3eff..329d5e32f 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -2,6 +2,7 @@ Unit test file for rpc/api_server.py """ +import json from datetime import datetime, timedelta, timezone from pathlib import Path from unittest.mock import ANY, MagicMock, PropertyMock @@ -1227,3 +1228,68 @@ def test_list_available_pairs(botclient): assert rc.json()['length'] == 1 assert rc.json()['pairs'] == ['XRP/ETH'] assert len(rc.json()['pair_interval']) == 1 + + +def test_api_backtesting(botclient, mocker, fee): + ftbot, client = botclient + mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + + # Backtesting not started yet + rc = client_get(client, f"{BASE_URI}/backtest") + assert_response(rc) + + result = rc.json() + assert result['status'] == 'not_started' + assert not result['running'] + assert result['status_msg'] == 'Backtest not yet executed' + assert result['progress'] == 0 + + # Reset backtesting + rc = client_delete(client, f"{BASE_URI}/backtest") + assert_response(rc) + result = rc.json() + assert result['status'] == 'reset' + assert not result['running'] + assert result['status_msg'] == 'Backtest reset' + + # bt_mock = mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy', + # return_value=(1, 2)) + # stats_mock = mocker.patch('freqtrade.optimize.optimize_reports.generate_backtest_stats') + # bt_mock.load_bt_data = MagicMock(return_value=(xxx, 'asdfadf')) + # start backtesting + data = { + "strategy": "DefaultStrategy", + "timeframe": "5m", + "timerange": "20180110-20180111", + "max_open_trades": 3, + "stake_amount": 100, + "dry_run_wallet": 1000, + "enable_protections": False + } + rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data)) + assert_response(rc) + result = rc.json() + + assert result['status'] == 'running' + assert result['progress'] == 0 + assert result['running'] + assert result['status_msg'] == 'Backtest started' + + rc = client_get(client, f"{BASE_URI}/backtest") + assert_response(rc) + + result = rc.json() + assert result['status'] == 'ended' + assert not result['running'] + assert result['status_msg'] == 'Backtest ended' + assert result['progress'] == 1 + assert result['backtest_result'] + + # Delete backtesting to avoid leakage since the backtest-object may stick around. + rc = client_delete(client, f"{BASE_URI}/backtest") + assert_response(rc) + + result = rc.json() + assert result['status'] == 'reset' + assert not result['running'] + assert result['status_msg'] == 'Backtest reset' From 17b3cc20976a4b63e622d64f82808c28db23498a Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 2 Apr 2021 19:58:08 +0200 Subject: [PATCH 055/519] Return numeric value, not empty string --- freqtrade/rpc/rpc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 538e95f40..c81f57e90 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -118,9 +118,9 @@ class RPC: 'bot_name': config.get('bot_name', 'freqtrade'), 'timeframe': config.get('timeframe'), 'timeframe_ms': timeframe_to_msecs(config['timeframe'] - ) if 'timeframe' in config else '', + ) if 'timeframe' in config else 0, 'timeframe_min': timeframe_to_minutes(config['timeframe'] - ) if 'timeframe' in config else '', + ) if 'timeframe' in config else 0, 'exchange': config['exchange']['name'], 'strategy': config['strategy'], 'forcebuy_enabled': config.get('forcebuy_enable', False), From 129c7b02d021f91d1ff810038639df955c155680 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 2 Apr 2021 20:00:14 +0200 Subject: [PATCH 056/519] Not all config values are mandatory in webserver mode --- freqtrade/rpc/api_server/api_schemas.py | 8 ++++---- freqtrade/rpc/api_server/api_v1.py | 18 ++++++++---------- freqtrade/rpc/api_server/webserver.py | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index f10c501f7..12ca87edb 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -118,17 +118,17 @@ class ShowConfig(BaseModel): stake_currency_decimals: int max_open_trades: int minimal_roi: Dict[str, Any] - stoploss: float - trailing_stop: bool + stoploss: Optional[float] + trailing_stop: Optional[bool] trailing_stop_positive: Optional[float] trailing_stop_positive_offset: Optional[float] trailing_only_offset_is_reached: Optional[bool] use_custom_stoploss: Optional[bool] - timeframe: str + timeframe: Optional[str] timeframe_ms: int timeframe_min: int exchange: str - strategy: str + strategy: Optional[str] forcebuy_enabled: bool ask_strategy: Dict[str, Any] bid_strategy: Dict[str, Any] diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 3f3f799b0..f875a9e3c 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -11,15 +11,14 @@ from freqtrade.constants import USERPATH_STRATEGIES from freqtrade.data.history import get_datahandler from freqtrade.exceptions import OperationalException from freqtrade.rpc import RPC -from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, - Balances, BlacklistPayload, BlacklistResponse, - Count, Daily, DeleteLockRequest, DeleteTrade, - ForceBuyPayload, ForceBuyResponse, - ForceSellPayload, Locks, Logs, OpenTradeSchema, - PairHistory, PerformanceEntry, Ping, PlotConfig, - Profit, ResultMsg, ShowConfig, Stats, StatusMsg, - StrategyListResponse, StrategyResponse, Version, - WhitelistResponse) +from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload, + BlacklistResponse, Count, Daily, + DeleteLockRequest, DeleteTrade, ForceBuyPayload, + ForceBuyResponse, ForceSellPayload, Locks, Logs, + OpenTradeSchema, PairHistory, PerformanceEntry, + Ping, PlotConfig, Profit, ResultMsg, ShowConfig, + Stats, StatusMsg, StrategyListResponse, + StrategyResponse, Version, WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException @@ -260,4 +259,3 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option 'pair_interval': pair_interval, } return result - diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index 0b58d79ff..5a59883ea 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -109,10 +109,10 @@ class ApiServer(RPCHandler): def configure_app(self, app: FastAPI, config): from freqtrade.rpc.api_server.api_auth import http_basic_or_jwt_token, router_login + from freqtrade.rpc.api_server.api_backtest import router as api_backtest from freqtrade.rpc.api_server.api_v1 import router as api_v1 from freqtrade.rpc.api_server.api_v1 import router_public as api_v1_public from freqtrade.rpc.api_server.web_ui import router_ui - from freqtrade.rpc.api_server.api_backtest import router as api_backtest app.include_router(api_v1_public, prefix="/api/v1") From 830b2548bcb2498d635ece241d135aef0e33b03c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 5 Apr 2021 19:58:53 +0200 Subject: [PATCH 057/519] Add backtest stopping --- freqtrade/optimize/backtesting.py | 6 +++++- freqtrade/rpc/api_server/api_backtest.py | 25 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index c8d4efa74..a1cf94774 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -120,6 +120,7 @@ class Backtesting: self.required_startup = max([strat.startup_candle_count for strat in self.strategylist]) self.progress = BTProgress() + self.abort = False def __del__(self): LoggingMixin.show_output = True @@ -202,6 +203,8 @@ class Backtesting: # Create dict with data for pair, pair_data in processed.items(): + if self.abort: + raise DependencyException("Stop requested") self.progress.increment() if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist @@ -419,7 +422,8 @@ class Backtesting: # Loop timerange and get candle for each pair at that point in time while tmp <= end_date: open_trade_count_start = open_trade_count - + if self.abort: + raise DependencyException("Stop requested") for i, pair in enumerate(data): row_index = indexes[pair] try: diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 48aee0cc5..bd202ea64 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -6,6 +6,7 @@ from copy import deepcopy from fastapi import APIRouter, BackgroundTasks, Depends from freqtrade.enums import BacktestState +from freqtrade.exceptions import DependencyException from freqtrade.rpc.api_server.api_schemas import BacktestRequest, BacktestResponse from freqtrade.rpc.api_server.deps import get_config from freqtrade.rpc.api_server.webserver import ApiServer @@ -64,6 +65,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac lastconfig['timeframe'] = strat.timeframe ApiServer._backtestdata, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() + ApiServer._bt.abort = False min_date, max_date = ApiServer._bt.backtest_one_strategy( strat, ApiServer._backtestdata, ApiServer._bt_timerange) @@ -72,6 +74,9 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac min_date=min_date, max_date=max_date) logger.info("Backtest finished.") + except DependencyException as e: + logger.info(f"Backtesting caused an error: {e}") + pass finally: ApiServer._bgtask_running = False @@ -147,3 +152,23 @@ def api_delete_backtest(): "progress": 0, "status_msg": "Backtest reset", } + + +@router.get('/backtest/abort', response_model=BacktestResponse, tags=['webserver', 'backtest']) +def api_backtest_abort(): + if not ApiServer._bgtask_running: + return { + "status": "not_running", + "running": False, + "step": "", + "progress": 0, + "status_msg": "Backtest ended", + } + ApiServer._bt.abort = True + return { + "status": "stopping", + "running": False, + "step": "", + "progress": 0, + "status_msg": "Backtest ended", + } From 2ec22f1d97090b10506c298f9a119dd4eb14e6f4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 24 May 2021 07:47:38 +0200 Subject: [PATCH 058/519] Add Sorting to available pair list --- freqtrade/rpc/api_server/api_v1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index f875a9e3c..61d69707e 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -252,7 +252,7 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option pair_interval = sorted(pair_interval, key=lambda x: x[0]) pairs = list({x[0] for x in pair_interval}) - + pairs.sort() result = { 'length': len(pairs), 'pairs': pairs, From e5b1657ab30f5b8b05840b74de8c8995ee3982a8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 20 Jun 2021 12:54:05 +0200 Subject: [PATCH 059/519] Properly remove rpc handler --- freqtrade/rpc/api_server/webserver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index 5a59883ea..e9becc936 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -76,7 +76,6 @@ class ApiServer(RPCHandler): Attach rpc handler """ if not self._has_rpc: - self._rpc = rpc ApiServer._rpc = rpc ApiServer._has_rpc = True else: @@ -85,7 +84,9 @@ class ApiServer(RPCHandler): def cleanup(self) -> None: """ Cleanup pending module resources """ - if self._server: + ApiServer._has_rpc = False + del ApiServer._rpc + if self._server and not self._standalone: logger.info("Stopping API Server") self._server.cleanup() From 5474d5ee64b65a178a195e97ca16b3e797e34f33 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 20 Jun 2021 15:18:41 +0200 Subject: [PATCH 060/519] Move webserver start command to seperate file --- freqtrade/commands/__init__.py | 3 ++- freqtrade/commands/trade_commands.py | 13 ------------- freqtrade/commands/webserver_commands.py | 15 +++++++++++++++ freqtrade/rpc/api_server/api_backtest.py | 1 - tests/commands/test_commands.py | 3 +-- 5 files changed, 18 insertions(+), 17 deletions(-) create mode 100644 freqtrade/commands/webserver_commands.py diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index a13d0ba49..04e46ee23 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -19,4 +19,5 @@ from freqtrade.commands.list_commands import (start_list_exchanges, start_list_h from freqtrade.commands.optimize_commands import start_backtesting, start_edge, start_hyperopt from freqtrade.commands.pairlist_commands import start_test_pairlist from freqtrade.commands.plot_commands import start_plot_dataframe, start_plot_profit -from freqtrade.commands.trade_commands import start_trading, start_webserver +from freqtrade.commands.trade_commands import start_trading +from freqtrade.commands.webserver_commands import start_webserver diff --git a/freqtrade/commands/trade_commands.py b/freqtrade/commands/trade_commands.py index 3c3f3db9f..535844844 100644 --- a/freqtrade/commands/trade_commands.py +++ b/freqtrade/commands/trade_commands.py @@ -27,16 +27,3 @@ def start_trading(args: Dict[str, Any]) -> int: logger.info("worker found ... calling exit") worker.exit() return 0 - - -def start_webserver(args: Dict[str, Any]) -> None: - """ - Main entry point for webserver mode - """ - from freqtrade.configuration import Configuration - from freqtrade.enums import RunMode - from freqtrade.rpc.api_server import ApiServer - - # Initialize configuration - config = Configuration(args, RunMode.WEBSERVER).get_config() - ApiServer(config, standalone=True) diff --git a/freqtrade/commands/webserver_commands.py b/freqtrade/commands/webserver_commands.py new file mode 100644 index 000000000..9a5975227 --- /dev/null +++ b/freqtrade/commands/webserver_commands.py @@ -0,0 +1,15 @@ +from typing import Any, Dict + +from freqtrade.enums import RunMode + + +def start_webserver(args: Dict[str, Any]) -> None: + """ + Main entry point for webserver mode + """ + from freqtrade.configuration import Configuration + from freqtrade.rpc.api_server import ApiServer + + # Initialize configuration + config = Configuration(args, RunMode.WEBSERVER).get_config() + ApiServer(config, standalone=True) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index bd202ea64..bdd9bc522 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -1,4 +1,3 @@ - import asyncio import logging from copy import deepcopy diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index bef421c89..dfbbbb2a6 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -13,10 +13,9 @@ from freqtrade.commands import (start_convert_data, start_create_userdir, start_ start_list_data, start_list_exchanges, start_list_hyperopts, start_list_markets, start_list_strategies, start_list_timeframes, start_new_hyperopt, start_new_strategy, start_show_trades, - start_test_pairlist, start_trading) + start_test_pairlist, start_trading, start_webserver) from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, get_ui_download_url, read_ui_version) -from freqtrade.commands.trade_commands import start_webserver from freqtrade.configuration import setup_utils_configuration from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException From 005da9718346e6b870210acf3b8b4d0582c06883 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 6 Jul 2021 06:28:47 +0200 Subject: [PATCH 061/519] extract backtesting abort functionality --- freqtrade/optimize/backtesting.py | 15 +++++++++++---- freqtrade/rpc/api_server/api_backtest.py | 2 +- tests/optimize/test_backtesting.py | 14 ++++++++++++++ tests/rpc/test_rpc_apiserver.py | 11 +++++++---- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index a1cf94774..e507ab2fd 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -189,6 +189,15 @@ class Backtesting: self.rejected_trades = 0 self.dataprovider.clear_cache() + def check_abort(self): + """ + Check if abort was requested, raise DependencyException if that's the case + Only applies to Interactive backtest mode (webserver mode) + """ + if self.abort: + self.abort = False + raise DependencyException("Stop requested") + def _get_ohlcv_as_lists(self, processed: Dict[str, DataFrame]) -> Dict[str, Tuple]: """ Helper function to convert a processed dataframes into lists for performance reasons. @@ -203,8 +212,7 @@ class Backtesting: # Create dict with data for pair, pair_data in processed.items(): - if self.abort: - raise DependencyException("Stop requested") + self.check_abort() self.progress.increment() if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist @@ -422,8 +430,7 @@ class Backtesting: # Loop timerange and get candle for each pair at that point in time while tmp <= end_date: open_trade_count_start = open_trade_count - if self.abort: - raise DependencyException("Stop requested") + self.check_abort() for i, pair in enumerate(data): row_index = indexes[pair] try: diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index bdd9bc522..cd40438d2 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -52,8 +52,8 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac # TODO: Investigate if enabling protections can be dynamically ingested from here... from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) - # Reset data if backtesting is reloaded + # Only reload data if timeframe or timerange changed. if (not ApiServer._backtestdata or not ApiServer._bt_timerange or lastconfig.get('timerange') != btconfig['timerange'] or lastconfig.get('timeframe') != strat.timeframe): diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 30d86f979..5427bbf92 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -346,6 +346,20 @@ def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None: assert processed['UNITTEST/BTC'].equals(processed2['UNITTEST/BTC']) +def test_backtest_abort(default_conf, mocker, testdatadir) -> None: + patch_exchange(mocker) + backtesting = Backtesting(default_conf) + backtesting.check_abort() + + backtesting.abort = True + + with pytest.raises(DependencyException, match="Stop requested"): + backtesting.check_abort() + # abort flag resets + assert backtesting.abort is False + assert backtesting.progress.progress == 0 + + def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None: def get_timerange(input1): return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 329d5e32f..c3a958ff5 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1252,10 +1252,6 @@ def test_api_backtesting(botclient, mocker, fee): assert not result['running'] assert result['status_msg'] == 'Backtest reset' - # bt_mock = mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy', - # return_value=(1, 2)) - # stats_mock = mocker.patch('freqtrade.optimize.optimize_reports.generate_backtest_stats') - # bt_mock.load_bt_data = MagicMock(return_value=(xxx, 'asdfadf')) # start backtesting data = { "strategy": "DefaultStrategy", @@ -1285,6 +1281,13 @@ def test_api_backtesting(botclient, mocker, fee): assert result['progress'] == 1 assert result['backtest_result'] + rc = client_get(client, f"{BASE_URI}/backtest/abort") + assert_response(rc) + result = rc.json() + assert result['status'] == 'not_running' + assert not result['running'] + assert result['status_msg'] == 'Backtest ended' + # Delete backtesting to avoid leakage since the backtest-object may stick around. rc = client_delete(client, f"{BASE_URI}/backtest") assert_response(rc) From 36d4a15d248ebc655803a828c64824e84fb23bb8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 6 Jul 2021 06:46:58 +0200 Subject: [PATCH 062/519] quickly document webserver mode --- docs/utils.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/utils.md b/docs/utils.md index 524fefc21..789462de4 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -614,6 +614,48 @@ Show whitelist when using a [dynamic pairlist](plugins.md#pairlists). freqtrade test-pairlist --config config.json --quote USDT BTC ``` +## Webserver mode + +!!! Warning "Experimental" + Webserver mode is an experimental mode to increase backesting and strategy development productivity. + There may still be bugs - so if you happen to stumble across these, please report them as github issues, thanks. + +Run freqtrade in webserver mode. +Freqtrade will start the webserver and allow FreqUI to start and control backtesting processes. +This has the advantage that data will not be reloaded between backtesting runs (as long as timeframe and timerange remain identical). +FreqUI will also show the backtesting results. + +``` +usage: freqtrade webserver [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] + [--userdir PATH] [-s NAME] [--strategy-path PATH] + +optional arguments: + -h, --help show this help message and exit + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + +``` + ## List Hyperopt results You can list the hyperoptimization epochs the Hyperopt module evaluated previously with the `hyperopt-list` sub-command. From a4bd862323deb86febe3bfa39af124642df909df Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 6 Jul 2021 20:29:04 +0200 Subject: [PATCH 063/519] Fix fluky test --- tests/rpc/test_rpc_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/rpc/test_rpc_manager.py b/tests/rpc/test_rpc_manager.py index 918022386..596b5ae20 100644 --- a/tests/rpc/test_rpc_manager.py +++ b/tests/rpc/test_rpc_manager.py @@ -5,6 +5,7 @@ from unittest.mock import MagicMock from freqtrade.enums import RPCMessageType from freqtrade.rpc import RPCManager +from freqtrade.rpc.api_server.webserver import ApiServer from tests.conftest import get_patched_freqtradebot, log_has @@ -190,3 +191,4 @@ def test_init_apiserver_enabled(mocker, default_conf, caplog) -> None: assert len(rpc_manager.registered_modules) == 1 assert 'apiserver' in [mod.name for mod in rpc_manager.registered_modules] assert run_mock.call_count == 1 + ApiServer.shutdown() From b7a9853d9ab245d9158536bb5110c72e96c09fe7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 6 Jul 2021 21:04:52 +0200 Subject: [PATCH 064/519] Increase test coverage --- tests/rpc/test_rpc_apiserver.py | 37 ++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index c3a958ff5..f145db3e0 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -17,7 +17,7 @@ from requests.auth import _basic_auth_str from freqtrade.__init__ import __version__ from freqtrade.enums import RunMode, State -from freqtrade.exceptions import ExchangeError +from freqtrade.exceptions import ExchangeError, OperationalException from freqtrade.loggers import setup_logging, setup_logging_pre from freqtrade.persistence import PairLocks, Trade from freqtrade.rpc import RPC @@ -243,6 +243,9 @@ def test_api__init__(default_conf, mocker): apiserver = ApiServer(default_conf) apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf))) assert apiserver._config == default_conf + with pytest.raises(OperationalException, match="RPC Handler already attached."): + apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf))) + ApiServer.shutdown() @@ -1288,6 +1291,38 @@ def test_api_backtesting(botclient, mocker, fee): assert not result['running'] assert result['status_msg'] == 'Backtest ended' + # Simulate running backtest + ApiServer._bgtask_running = True + rc = client_get(client, f"{BASE_URI}/backtest/abort") + assert_response(rc) + result = rc.json() + assert result['status'] == 'stopping' + assert not result['running'] + assert result['status_msg'] == 'Backtest ended' + + # Get running backtest... + rc = client_get(client, f"{BASE_URI}/backtest") + assert_response(rc) + result = rc.json() + assert result['status'] == 'running' + assert result['running'] + assert result['step'] == "backtest" + assert result['status_msg'] == "Backtest running" + + # Try delete with task still running + rc = client_delete(client, f"{BASE_URI}/backtest") + assert_response(rc) + result = rc.json() + assert result['status'] == 'running' + + # Post to backtest that's still running + rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data)) + assert_response(rc, 502) + result = rc.json() + assert 'Bot Background task already running' in result['error'] + + ApiServer._bgtask_running = False + # Delete backtesting to avoid leakage since the backtest-object may stick around. rc = client_delete(client, f"{BASE_URI}/backtest") assert_response(rc) From d1104bd434e4c60ba3d7f2582c093a0027c65af0 Mon Sep 17 00:00:00 2001 From: octaviusgus Date: Tue, 6 Jul 2021 22:47:39 +0200 Subject: [PATCH 065/519] fix daily profit data and daily profit curve example --- freqtrade/optimize/optimize_reports.py | 2 +- freqtrade/templates/strategy_analysis_example.ipynb | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 89cf70437..5dc8554c4 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -272,7 +272,7 @@ def generate_daily_stats(results: DataFrame) -> Dict[str, Any]: winning_days = sum(daily_profit > 0) draw_days = sum(daily_profit == 0) losing_days = sum(daily_profit < 0) - daily_profit_list = daily_profit.tolist() + daily_profit_list = [(str(idx.date()), val) for idx, val in daily_profit.iteritems()] return { 'backtest_best_day': best_rel, diff --git a/freqtrade/templates/strategy_analysis_example.ipynb b/freqtrade/templates/strategy_analysis_example.ipynb index f3b0d8d03..99720ae6e 100644 --- a/freqtrade/templates/strategy_analysis_example.ipynb +++ b/freqtrade/templates/strategy_analysis_example.ipynb @@ -215,13 +215,18 @@ "stats = load_backtest_stats(backtest_dir)\n", "strategy_stats = stats['strategy'][strategy]\n", "\n", + "dates = []\n", + "profits = []\n", + "for date_profit in strategy_stats['daily_profit']:\n", + " dates.append(date_profit[0])\n", + " profits.append(date_profit[1])\n", + "\n", "equity = 0\n", "equity_daily = []\n", - "for dp in strategy_stats['daily_profit']:\n", + "for daily_profit in profits:\n", " equity_daily.append(equity)\n", - " equity += float(dp)\n", + " equity += float(daily_profit)\n", "\n", - "dates = pd.date_range(strategy_stats['backtest_start'], strategy_stats['backtest_end'])\n", "\n", "df = pd.DataFrame({'dates': dates,'equity_daily': equity_daily})\n", "\n", From 638bed3dac45206eb001e0512aeb938ae063c20d Mon Sep 17 00:00:00 2001 From: user Date: Wed, 7 Jul 2021 06:46:51 +0000 Subject: [PATCH 066/519] Add RangeStabilityFilterMax pairlist filter --- freqtrade/constants.py | 2 +- .../pairlist/rangestabilityfiltermax.py | 109 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 freqtrade/plugins/pairlist/rangestabilityfiltermax.py diff --git a/freqtrade/constants.py b/freqtrade/constants.py index f4c32387b..089569842 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -28,7 +28,7 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'AgeFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', 'ShuffleFilter', - 'SpreadFilter', 'VolatilityFilter'] + 'SpreadFilter', 'VolatilityFilter', 'RangeStabilityFilterMax'] AVAILABLE_PROTECTIONS = ['CooldownPeriod', 'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] DRY_RUN_WALLET = 1000 diff --git a/freqtrade/plugins/pairlist/rangestabilityfiltermax.py b/freqtrade/plugins/pairlist/rangestabilityfiltermax.py new file mode 100644 index 000000000..98f65904b --- /dev/null +++ b/freqtrade/plugins/pairlist/rangestabilityfiltermax.py @@ -0,0 +1,109 @@ +""" +Rate of change pairlist filter +""" +import logging +from copy import deepcopy +from typing import Any, Dict, List, Optional + +import arrow +from cachetools.ttl import TTLCache +from pandas import DataFrame + +from freqtrade.exceptions import OperationalException +from freqtrade.misc import plural +from freqtrade.plugins.pairlist.IPairList import IPairList + + +logger = logging.getLogger(__name__) + + +class RangeStabilityFilterMax(IPairList): + + def __init__(self, exchange, pairlistmanager, + config: Dict[str, Any], pairlistconfig: Dict[str, Any], + pairlist_pos: int) -> None: + super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + + self._days = pairlistconfig.get('lookback_days', 10) + self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', 0.02) + self._refresh_period = pairlistconfig.get('refresh_period', 1440) + + self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) + + if self._days < 1: + raise OperationalException("RangeStabilityFilter requires lookback_days to be >= 1") + if self._days > exchange.ohlcv_candle_limit('1d'): + raise OperationalException("RangeStabilityFilter requires lookback_days to not " + "exceed exchange max request size " + f"({exchange.ohlcv_candle_limit('1d')})") + + @property + def needstickers(self) -> bool: + """ + Boolean property defining if tickers are necessary. + If no Pairlist requires tickers, an empty List is passed + as tickers argument to filter_pairlist + """ + return False + + def short_desc(self) -> str: + """ + Short whitelist method description - used for startup-messages + """ + return (f"{self.name} - Filtering pairs with rate of change below " + f"{self._max_rate_of_change} over the last {plural(self._days, 'day')}.") + + def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: + """ + Validate trading range + :param pairlist: pairlist to filter or sort + :param tickers: Tickers (from exchange.get_tickers()). May be cached. + :return: new allowlist + """ + needed_pairs = [(p, '1d') for p in pairlist if p not in self._pair_cache] + + since_ms = int(arrow.utcnow() + .floor('day') + .shift(days=-self._days - 1) + .float_timestamp) * 1000 + # Get all candles + candles = {} + if needed_pairs: + candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, + cache=False) + + if self._enabled: + for p in deepcopy(pairlist): + daily_candles = candles[(p, '1d')] if (p, '1d') in candles else None + if not self._validate_pair_loc(p, daily_candles): + pairlist.remove(p) + return pairlist + + def _validate_pair_loc(self, pair: str, daily_candles: Optional[DataFrame]) -> bool: + """ + Validate trading range + :param pair: Pair that's currently validated + :param ticker: ticker dict as returned from ccxt.load_markets() + :return: True if the pair can stay, false if it should be removed + """ + # Check symbol in cache + cached_res = self._pair_cache.get(pair, None) + if cached_res is not None: + return cached_res + + result = False + if daily_candles is not None and not daily_candles.empty: + highest_high = daily_candles['high'].max() + lowest_low = daily_candles['low'].min() + pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0 + if pct_change <= self._max_rate_of_change: + result = True + else: + self.log_once(f"Removed {pair} from whitelist, because rate of change " + f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, " + f"which is above the threshold of {self._max_rate_of_change}.", + logger.info) + result = False + self._pair_cache[pair] = result + + return result From 3c3772703bdc8897abcf03c5c4ee6bf45c27e802 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Wed, 7 Jul 2021 09:46:05 +0200 Subject: [PATCH 067/519] changed quoteVolume to be built over a rolling period using lookback_period to avoid pair_candles being larger than requested lookback_period --- freqtrade/plugins/pairlist/VolumePairList.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 14b9d7024..d6b8aaaa3 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -183,8 +183,15 @@ class VolumePairList(IPairList): pair_candles['volume'] * pair_candles['typical_price'] ) + # ensure that a rolling sum over the lookback_period is built + # if pair_candles contains more candles than lookback_period + quoteVolume = (pair_candles['quoteVolume'] + .rolling(self._lookback_period) + .sum() + .iloc[-1]) + # replace quoteVolume with range quoteVolume sum calculated above - filtered_tickers[i]['quoteVolume'] = pair_candles['quoteVolume'].sum() + filtered_tickers[i]['quoteVolume'] = quoteVolume else: filtered_tickers[i]['quoteVolume'] = 0 From 8b485d197a6778499f362275ce0c521bca2778a3 Mon Sep 17 00:00:00 2001 From: sauces1313 Date: Wed, 7 Jul 2021 07:54:17 +0000 Subject: [PATCH 068/519] Update Plugins doc --- docs/includes/pairlists.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index f19c5a181..0368e8a6f 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -174,6 +174,24 @@ If the trading range over the last 10 days is <1%, remove the pair from the whit !!! Tip This Filter can be used to automatically remove stable coin pairs, which have a very low trading range, and are therefore extremely difficult to trade with profit. +#### RangeStabilityFilterMax + +Same function as `RangeStabilityFilter` but instead of a minimum value, it uses a maximum value for rate of change, i.e. `max_rate_of_change` as seen in the example below. + +```json +"pairlists": [ + { + "method": "RangeStabilityFilterMax", + "lookback_days": 10, + "max_rate_of_change": 1.01, + "refresh_period": 1440 + } +] +``` + +!!! Tip + This Filter can be used to automatically remove pairs with extreme high/low variance over a given amount of time (`lookback_days`). + #### VolatilityFilter Volatility is the degree of historical variation of a pairs over time, is is measured by the standard deviation of logarithmic daily returns. Returns are assumed to be normally distributed, although actual distribution might be different. In a normal distribution, 68% of observations fall within one standard deviation and 95% of observations fall within two standard deviations. Assuming a volatility of 0.05 means that the expected returns for 20 out of 30 days is expected to be less than 5% (one standard deviation). Volatility is a positive ratio of the expected deviation of return and can be greater than 1.00. Please refer to the wikipedia definition of [`volatility`](https://en.wikipedia.org/wiki/Volatility_(finance)). From 8b0a02db8eef64840ac57ba6a5313047f69f6bdb Mon Sep 17 00:00:00 2001 From: sauces1313 Date: Wed, 7 Jul 2021 08:11:13 +0000 Subject: [PATCH 069/519] Correct exception messages --- freqtrade/plugins/pairlist/rangestabilityfiltermax.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/rangestabilityfiltermax.py b/freqtrade/plugins/pairlist/rangestabilityfiltermax.py index 98f65904b..e0cf5b9b4 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfiltermax.py +++ b/freqtrade/plugins/pairlist/rangestabilityfiltermax.py @@ -31,9 +31,9 @@ class RangeStabilityFilterMax(IPairList): self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) if self._days < 1: - raise OperationalException("RangeStabilityFilter requires lookback_days to be >= 1") + raise OperationalException("RangeStabilityFilterMax requires lookback_days to be >= 1") if self._days > exchange.ohlcv_candle_limit('1d'): - raise OperationalException("RangeStabilityFilter requires lookback_days to not " + raise OperationalException("RangeStabilityFilterMax requires lookback_days to not " "exceed exchange max request size " f"({exchange.ohlcv_candle_limit('1d')})") From f30e300f181046e6e09f3c896d59563d09e0e119 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Wed, 7 Jul 2021 11:28:35 +0200 Subject: [PATCH 070/519] adjusted `test_pairlist.py` for fixed rolling sum --- tests/plugins/test_pairlist.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 7812ee733..f6f86d0b5 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -527,7 +527,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t # expecing pairs as given ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}], - "BTC", ['LTC/BTC', 'XRP/BTC', 'ETH/BTC', 'TKN/BTC', 'HOT/BTC']), + "BTC", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']), # expecting pairs from default tickers, because 1h candles are not available ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume", "lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}], @@ -541,16 +541,20 @@ def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history_high_vola = ohlcv_history.copy() ohlcv_history_high_vola.loc[ohlcv_history_high_vola.index == 1, 'close'] = 0.00090 - # create candles for high volume + # create candles for medium overall volume with last candle high volume + ohlcv_history_medium_volume = ohlcv_history.copy() + ohlcv_history_medium_volume.loc[ohlcv_history_medium_volume.index == 2, 'volume'] = 5 + + # create candles for high volume with all candles high volume ohlcv_history_high_volume = ohlcv_history.copy() - ohlcv_history_high_volume.loc[ohlcv_history_high_volume.index == 1, 'volume'] = 10 + ohlcv_history_high_volume.loc[:, 'volume'] = 10 ohlcv_data = { ('ETH/BTC', '1d'): ohlcv_history, ('TKN/BTC', '1d'): ohlcv_history, - ('LTC/BTC', '1d'): ohlcv_history_high_volume, + ('LTC/BTC', '1d'): ohlcv_history_medium_volume, ('XRP/BTC', '1d'): ohlcv_history_high_vola, - ('HOT/BTC', '1d'): ohlcv_history, + ('HOT/BTC', '1d'): ohlcv_history_high_volume, } mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) From 6cea0ef2d72e1e66bb6e496b820193b58f9d4624 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Wed, 7 Jul 2021 11:48:26 +0200 Subject: [PATCH 071/519] documentation for `OffsetFilter` --- docs/includes/pairlists.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index f19c5a181..3ae3b89ec 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -23,6 +23,7 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged * [`StaticPairList`](#static-pair-list) (default, if not configured differently) * [`VolumePairList`](#volume-pair-list) * [`AgeFilter`](#agefilter) +* [`OffsetFilter`](#offsetfilter) * [`PerformanceFilter`](#performancefilter) * [`PrecisionFilter`](#precisionfilter) * [`PriceFilter`](#pricefilter) @@ -89,6 +90,30 @@ be caught out buying before the pair has finished dropping in price. This filter allows freqtrade to ignore pairs until they have been listed for at least `min_days_listed` days. +#### OffsetFilter + +Offsets an incoming pairlist by a given `offset` value. + +As an example it can be used in conjunction with `VolumeFilter` to remove the top X volume pairs. Or to split +a larger pairlist on two bot instances. + +Example to remove the first 10 pairs from the pairlist: + +```json +"pairlists": [{ + "method": "OffsetFilter", + "offset": 10 +}], +``` + +!!! Warning + When `OffsetFilter` is used to split a larger pairlist among multiple bots in combination with `VolumeFilter` + it can not be guaranteed that pairs won't overlap due to slightly different refresh intervals for the + `VolumeFilter` + +!!! Note + An offset larger then the total length of the incoming pairlist will result in an empty pairlist. + #### PerformanceFilter Sorts pairs by past trade performance, as follows: From c44e87cd30a7a43a968319df326858280fffbf72 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Wed, 7 Jul 2021 12:06:55 +0200 Subject: [PATCH 072/519] added tests for `OffsetFilter to `test_pairlist.py` --- tests/plugins/test_pairlist.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index ae8f6e958..975273e60 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -417,7 +417,19 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): ([{"method": "StaticPairList"}, {"method": "VolatilityFilter", "lookback_days": 3, "min_volatility": 0.002, "max_volatility": 0.004, "refresh_period": 1440}], - "BTC", ['ETH/BTC', 'TKN/BTC']) + "BTC", ['ETH/BTC', 'TKN/BTC']), + # VolumePairList with no offset = unchanged pairlist + ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, + {"method": "OffsetFilter", "offset": 0}], + "USDT", ['ETH/USDT', 'NANO/USDT', 'ADAHALF/USDT', 'ADADOUBLE/USDT']), + # VolumePairList with offset = 2 + ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, + {"method": "OffsetFilter", "offset": 2}], + "USDT", ['ADAHALF/USDT', 'ADADOUBLE/USDT']), + # VolumePairList with higher offset, than total pairlist + ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, + {"method": "OffsetFilter", "offset": 100}], + "USDT", []) ]) def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history, pairlists, base_currency, @@ -695,6 +707,18 @@ def test_agefilter_caching(mocker, markets, whitelist_conf_agefilter, tickers, o assert freqtrade.exchange.refresh_latest_ohlcv.call_count == previous_call_count + 1 +def test_OffsetFilter_error(mocker, whitelist_conf) -> None: + whitelist_conf['pairlists'] = ( + [{"method": "StaticPairList"}, {"method": "OffsetFilter", "offset": -1}] + ) + + mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) + + with pytest.raises(OperationalException, + match=r'OffsetFilter requires offset to be >= 0'): + PairListManager(MagicMock, whitelist_conf) + + def test_rangestabilityfilter_checks(mocker, default_conf, markets, tickers): default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, {'method': 'RangeStabilityFilter', 'lookback_days': 99999}] From 00a1931f40e1351191fb030cdd649671aee7380c Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 7 Jul 2021 21:24:44 +0700 Subject: [PATCH 073/519] fix test --- freqtrade/plugins/pairlist/AgeFilter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 44c5d2e5e..810a87082 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -102,7 +102,7 @@ class AgeFilter(IPairList): if daily_candles is not None: if len(daily_candles) >= self._min_days_listed and \ - len(daily_candles) <= self._max_days_listed: + (True if not self._max_days_listed else len(daily_candles) <= self._max_days_listed): # We have fetched at least the minimum required number of daily candles # Add to cache, store the time we last checked this symbol self._symbolsChecked[pair] = arrow.utcnow().int_timestamp * 1000 @@ -112,9 +112,9 @@ class AgeFilter(IPairList): f"Removed {pair} from whitelist, because age " f"{len(daily_candles)} is less than {self._min_days_listed} " f"{plural(self._min_days_listed, 'day')}" - ) + ( + ) + (( " or more than " f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}" - ) if self.max_days_listed else '', logger.info) + ) if self._max_days_listed else ''), logger.info) return False return False From 8248d1acd19c1b229fecd894d480bbd320912fc3 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 7 Jul 2021 22:10:22 +0700 Subject: [PATCH 074/519] run flake8 --- freqtrade/plugins/pairlist/AgeFilter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 810a87082..133285884 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -102,7 +102,8 @@ class AgeFilter(IPairList): if daily_candles is not None: if len(daily_candles) >= self._min_days_listed and \ - (True if not self._max_days_listed else len(daily_candles) <= self._max_days_listed): + (True if not self._max_days_listed + else len(daily_candles) <= self._max_days_listed): # We have fetched at least the minimum required number of daily candles # Add to cache, store the time we last checked this symbol self._symbolsChecked[pair] = arrow.utcnow().int_timestamp * 1000 From 682f880630dbf31bd99cf604d94256c989f90df1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 7 Jul 2021 20:05:56 +0200 Subject: [PATCH 075/519] Slightly simplify if statement, add additional test --- freqtrade/plugins/pairlist/AgeFilter.py | 7 ++++--- tests/plugins/test_pairlist.py | 10 +++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 133285884..23250e5c1 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -101,9 +101,10 @@ class AgeFilter(IPairList): return True if daily_candles is not None: - if len(daily_candles) >= self._min_days_listed and \ - (True if not self._max_days_listed - else len(daily_candles) <= self._max_days_listed): + if ( + len(daily_candles) >= self._min_days_listed + and (not self._max_days_listed or len(daily_candles) <= self._max_days_listed) + ): # We have fetched at least the minimum required number of daily candles # Add to cache, store the time we last checked this symbol self._symbolsChecked[pair] = arrow.utcnow().int_timestamp * 1000 diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 5724ca39c..f8c5acba3 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -321,6 +321,14 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, {"method": "AgeFilter", "min_days_listed": 1, "max_days_listed": 2}], "BTC", []), + # AgeFilter and VolumePairList LTC/BTC has 6 candles - removes all + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, + {"method": "AgeFilter", "min_days_listed": 4, "max_days_listed": 5}], + "BTC", []), + # AgeFilter and VolumePairList LTC/BTC has 6 candles - passes + ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, + {"method": "AgeFilter", "min_days_listed": 4, "max_days_listed": 10}], + "BTC", ["LTC/BTC"]), # Precisionfilter and quote volume ([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"}, {"method": "PrecisionFilter"}], @@ -436,7 +444,7 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t ohlcv_data = { ('ETH/BTC', '1d'): ohlcv_history, ('TKN/BTC', '1d'): ohlcv_history, - ('LTC/BTC', '1d'): ohlcv_history, + ('LTC/BTC', '1d'): ohlcv_history.append(ohlcv_history), ('XRP/BTC', '1d'): ohlcv_history, ('HOT/BTC', '1d'): ohlcv_history_high_vola, } From 4d4ed82db8d41904d45302df2999bc660f8daed3 Mon Sep 17 00:00:00 2001 From: nightshift2k <82537832+nightshift2k@users.noreply.github.com> Date: Wed, 7 Jul 2021 20:29:52 +0200 Subject: [PATCH 076/519] Update docs/includes/pairlists.md Co-authored-by: Matthias --- docs/includes/pairlists.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 3ae3b89ec..5b2e892e8 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -109,7 +109,7 @@ Example to remove the first 10 pairs from the pairlist: !!! Warning When `OffsetFilter` is used to split a larger pairlist among multiple bots in combination with `VolumeFilter` it can not be guaranteed that pairs won't overlap due to slightly different refresh intervals for the - `VolumeFilter` + `VolumeFilter`. !!! Note An offset larger then the total length of the incoming pairlist will result in an empty pairlist. From 5a2bc192d4728f7c26e2ded30a68a833df0aa6c1 Mon Sep 17 00:00:00 2001 From: nightshift2k <82537832+nightshift2k@users.noreply.github.com> Date: Wed, 7 Jul 2021 20:29:55 +0200 Subject: [PATCH 077/519] Update docs/includes/pairlists.md Co-authored-by: Matthias --- docs/includes/pairlists.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 5b2e892e8..ecb6231ec 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -100,10 +100,12 @@ a larger pairlist on two bot instances. Example to remove the first 10 pairs from the pairlist: ```json -"pairlists": [{ +"pairlists": [ + { "method": "OffsetFilter", "offset": 10 -}], + } +], ``` !!! Warning From 7dc826d6b398f4220d3d96b9a88fa2fe29d6da93 Mon Sep 17 00:00:00 2001 From: nightshift2k Date: Wed, 7 Jul 2021 20:43:37 +0200 Subject: [PATCH 078/519] warning for range based lookback performance more readable formatting of examples --- docs/includes/pairlists.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 3f8baab29..9ea9b41a4 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -68,12 +68,14 @@ Filtering instances (not the first position in the list) will not apply any cach * The `quoteVolume` is the amount of quote (stake) currency traded (bought or sold) in last 24 hours. ```json -"pairlists": [{ +"pairlists": [ + { "method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume", "refresh_period": 1800 -}], + } +], ``` `VolumePairList` can also operate in an advanced mode to build volume over a given timerange of specified candle size. It utilizes exchange historical candle data, builds a typical price (calculated by (open+high+low)/3), and multiplies it with every candle data's volume. The sum is the `quoteVolume` over the given range. This allows different scenarios, for a more smoothened volume, when using longer ranges with larger candle sizes, or the opposite when using a short range with small candles. @@ -81,28 +83,36 @@ Filtering instances (not the first position in the list) will not apply any cach For convenience `lookback_days` can be specified, which will imply that 1d candles will be used for the lookback. In the example below the pairlist would be created based on the last 7 days: ```json -"pairlists": [{ +"pairlists": [ + { "method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume", "refresh_period": 86400, "lookback_days": 7 -}], + } +], ``` !!! Warning "Range look back and refresh period" When used in conjuction with `lookback_days` and `lookback_timeframe` the `refresh_period` can not be smaller than the candle size in seconds. As this will result in unnecessary requests to the exchanges API. +!!! Warning "Performance implications when using lookback range" + If used in first position in combination with lookback, the computation of the range based volume can be time and ressource consuming, as it downloads candles for all tradable pairs. Hence it's highly advised to use the standard approach with `VolumeFilter` to narrow the pairlist down for further range volume calculation. + + More sophisticated approach can be used, by using `lookback_timeframe` for candle size and `lookback_period` which specifies the amount of candles. This example will build the volume pairs based on a rolling period of 3 days of 1h candles: ```json -"pairlists": [{ +"pairlists": [ + { "method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume", "refresh_period": 3600, "lookback_timeframe": "1h", - "lookback_timeframe": 72 -}], + "lookback_period": 72 + } +], ``` !!! Note From e5da7ff6db70ce4894f58e9b36a683631e921327 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Jul 2021 07:02:40 +0200 Subject: [PATCH 079/519] Fix typos and improve wording in docs --- docs/includes/pairlists.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 9ea9b41a4..5a771e055 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -78,7 +78,7 @@ Filtering instances (not the first position in the list) will not apply any cach ], ``` -`VolumePairList` can also operate in an advanced mode to build volume over a given timerange of specified candle size. It utilizes exchange historical candle data, builds a typical price (calculated by (open+high+low)/3), and multiplies it with every candle data's volume. The sum is the `quoteVolume` over the given range. This allows different scenarios, for a more smoothened volume, when using longer ranges with larger candle sizes, or the opposite when using a short range with small candles. +`VolumePairList` can also operate in an advanced mode to build volume over a given timerange of specified candle size. It utilizes exchange historical candle data, builds a typical price (calculated by (open+high+low)/3) and multiplies the typical price with every candle's volume. The sum is the `quoteVolume` over the given range. This allows different scenarios, for a more smoothened volume, when using longer ranges with larger candle sizes, or the opposite when using a short range with small candles. For convenience `lookback_days` can be specified, which will imply that 1d candles will be used for the lookback. In the example below the pairlist would be created based on the last 7 days: @@ -93,12 +93,12 @@ For convenience `lookback_days` can be specified, which will imply that 1d candl } ], ``` + !!! Warning "Range look back and refresh period" - When used in conjuction with `lookback_days` and `lookback_timeframe` the `refresh_period` can not be smaller than the candle size in seconds. As this will result in unnecessary requests to the exchanges API. + When used in conjunction with `lookback_days` and `lookback_timeframe` the `refresh_period` can not be smaller than the candle size in seconds. As this will result in unnecessary requests to the exchanges API. !!! Warning "Performance implications when using lookback range" - If used in first position in combination with lookback, the computation of the range based volume can be time and ressource consuming, as it downloads candles for all tradable pairs. Hence it's highly advised to use the standard approach with `VolumeFilter` to narrow the pairlist down for further range volume calculation. - + If used in first position in combination with lookback, the computation of the range based volume can be time and resource consuming, as it downloads candles for all tradable pairs. Hence it's highly advised to use the standard approach with `VolumeFilter` to narrow the pairlist down for further range volume calculation. More sophisticated approach can be used, by using `lookback_timeframe` for candle size and `lookback_period` which specifies the amount of candles. This example will build the volume pairs based on a rolling period of 3 days of 1h candles: From 8be0241573f0cf1bf1bac92b5e92d4c85ab44a76 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Jul 2021 07:07:56 +0200 Subject: [PATCH 080/519] Build images for feat/** branches --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42959c3b5..2606cac4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: - master - stable - develop + - feat/** tags: release: types: [published] From 863391122ffa55895fec87212159c653f3c148cf Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 8 Jul 2021 13:42:52 +0700 Subject: [PATCH 081/519] fix short desc not appear --- freqtrade/plugins/pairlist/AgeFilter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 133285884..a49842e94 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -58,10 +58,10 @@ class AgeFilter(IPairList): return ( f"{self.name} - Filtering pairs with age less than " f"{self._min_days_listed} {plural(self._min_days_listed, 'day')}" - ) + ( + ) + (( " or more than " f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}" - ) if self._max_days_listed else '' + ) if self._max_days_listed else '') def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ From 03861945a39af7441f8898f00bf7416f11f7d7ca Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Jul 2021 20:04:47 +0200 Subject: [PATCH 082/519] Update documentation page too. --- docs/strategy_analysis_example.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index 27c620c3d..27192aa2f 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -148,13 +148,18 @@ import pandas as pd stats = load_backtest_stats(backtest_dir) strategy_stats = stats['strategy'][strategy] +dates = [] +profits = [] +for date_profit in strategy_stats['daily_profit']: + dates.append(date_profit[0]) + profits.append(date_profit[1]) + equity = 0 equity_daily = [] -for dp in strategy_stats['daily_profit']: +for daily_profit in profits: equity_daily.append(equity) - equity += float(dp) + equity += float(daily_profit) -dates = pd.date_range(strategy_stats['backtest_start'], strategy_stats['backtest_end']) df = pd.DataFrame({'dates': dates,'equity_daily': equity_daily}) From 2f33b97b95c62ab0fec089098a6cf1eeae01d041 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 9 Jul 2021 07:19:44 +0200 Subject: [PATCH 083/519] Validate startup candles for backtesting correctly closes #5250 --- freqtrade/optimize/backtesting.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7c6b7cbc3..8f818047d 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -116,6 +116,7 @@ class Backtesting: # Get maximum required startup period self.required_startup = max([strat.startup_candle_count for strat in self.strategylist]) + self.exchange.validate_required_startup_candles(self.required_startup, self.timeframe) def __del__(self): LoggingMixin.show_output = True From 6129c5ca9e9ccd418f8f9c6659db54f2a87d8579 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 9 Jul 2021 20:46:38 +0200 Subject: [PATCH 084/519] Fix deprecation warnings from pandas 1.3.0 closes #5251 --- freqtrade/optimize/hyperopt_tools.py | 2 +- freqtrade/optimize/optimize_reports.py | 5 +++-- freqtrade/plugins/pairlist/OffsetFilter.py | 2 +- freqtrade/rpc/rpc.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 90976d34e..439016c14 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -75,7 +75,7 @@ class HyperoptTools(): if fn: HyperoptTools.export_params(params, strategy_name, fn.with_suffix('.json')) else: - logger.warn("Strategy not found, not exporting parameter file.") + logger.warning("Strategy not found, not exporting parameter file.") @staticmethod def has_space(config: Dict[str, Any], space: str) -> bool: diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 5dc8554c4..eefacbbab 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -325,8 +325,9 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None worst_pair = min([pair for pair in pair_results if pair['key'] != 'TOTAL'], key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None - results['open_timestamp'] = results['open_date'].astype(int64) // 1e6 - results['close_timestamp'] = results['close_date'].astype(int64) // 1e6 + if not results.empty: + results['open_timestamp'] = results['open_date'].view(int64) // 1e6 + results['close_timestamp'] = results['close_date'].view(int64) // 1e6 backtest_days = (max_date - min_date).days strat_stats = { diff --git a/freqtrade/plugins/pairlist/OffsetFilter.py b/freqtrade/plugins/pairlist/OffsetFilter.py index 4579204d9..573a573a6 100644 --- a/freqtrade/plugins/pairlist/OffsetFilter.py +++ b/freqtrade/plugins/pairlist/OffsetFilter.py @@ -48,7 +48,7 @@ class OffsetFilter(IPairList): """ if self._offset > len(pairlist): self.log_once(f"Offset of {self._offset} is larger than " + - f"pair count of {len(pairlist)}", logger.warn) + f"pair count of {len(pairlist)}", logger.warning) pairs = pairlist[self._offset:] self.log_once(f"Searching {len(pairs)} pairs: {pairs}", logger.info) return pairs diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 538e95f40..e0aaefe50 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -761,7 +761,7 @@ class RPC: sell_signals = 0 if has_content: - dataframe.loc[:, '__date_ts'] = dataframe.loc[:, 'date'].astype(int64) // 1000 // 1000 + dataframe.loc[:, '__date_ts'] = dataframe.loc[:, 'date'].view(int64) // 1000 // 1000 # Move open to seperate column when signal for easy plotting if 'buy' in dataframe.columns: buy_mask = (dataframe['buy'] == 1) From cf6f706078fc5ddb75901d76e6ae8b87fd32330f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 10:01:15 +0200 Subject: [PATCH 085/519] Don't build for feat branches that breaks CI for PR's --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2606cac4d..42959c3b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,6 @@ on: - master - stable - develop - - feat/** tags: release: types: [published] From e4e2340f91ffacdfe971be8387cb9d1c36ad074f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 10:02:05 +0200 Subject: [PATCH 086/519] Fix bug where currencies are duplicated in case there is dust --- freqtrade/rpc/telegram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 171a53ca1..319a6c9c0 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -600,8 +600,8 @@ class Telegram(RPCHandler): ) total_dust_balance = 0 total_dust_currencies = 0 - curr_output = '' for curr in result['currencies']: + curr_output = '' if curr['est_stake'] > balance_dust_level: curr_output = ( f"*{curr['currency']}:*\n" From 72a103f32d3c9c70c54eea9849902f6a9c6641ae Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 10:13:23 +0200 Subject: [PATCH 087/519] Properly test webserver startup in standalone mode --- tests/rpc/test_rpc_apiserver.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index f145db3e0..ad8f2c972 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -17,7 +17,7 @@ from requests.auth import _basic_auth_str from freqtrade.__init__ import __version__ from freqtrade.enums import RunMode, State -from freqtrade.exceptions import ExchangeError, OperationalException +from freqtrade.exceptions import DependencyException, ExchangeError, OperationalException from freqtrade.loggers import setup_logging, setup_logging_pre from freqtrade.persistence import PairLocks, Trade from freqtrade.rpc import RPC @@ -308,7 +308,10 @@ def test_api_run(default_conf, mocker, caplog): }}) mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock()) - server_mock = MagicMock() + server_inst_mock = MagicMock() + server_inst_mock.run_in_thread = MagicMock() + server_inst_mock.run = MagicMock() + server_mock = MagicMock(return_value=server_inst_mock) mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', server_mock) apiserver = ApiServer(default_conf) @@ -318,6 +321,8 @@ def test_api_run(default_conf, mocker, caplog): assert apiserver._config == default_conf apiserver.start_api() assert server_mock.call_count == 2 + assert server_inst_mock.run_in_thread.call_count == 2 + assert server_inst_mock.run.call_count == 0 assert server_mock.call_args_list[0][0][0].host == "127.0.0.1" assert server_mock.call_args_list[0][0][0].port == 8080 assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI) @@ -336,6 +341,8 @@ def test_api_run(default_conf, mocker, caplog): apiserver.start_api() assert server_mock.call_count == 1 + assert server_inst_mock.run_in_thread.call_count == 1 + assert server_inst_mock.run.call_count == 0 assert server_mock.call_args_list[0][0][0].host == "0.0.0.0" assert server_mock.call_args_list[0][0][0].port == 8089 assert isinstance(server_mock.call_args_list[0][0][0].app, FastAPI) @@ -349,6 +356,17 @@ def test_api_run(default_conf, mocker, caplog): "Please make sure that this is intentional!", caplog) assert log_has_re("SECURITY WARNING - `jwt_secret_key` seems to be default.*", caplog) + server_mock.reset_mock() + apiserver._standalone = True + apiserver.start_api() + assert server_inst_mock.run_in_thread.call_count == 0 + assert server_inst_mock.run.call_count == 1 + + apiserver1 = ApiServer(default_conf) + assert id(apiserver1) == id(apiserver) + + apiserver._standalone = False + # Test crashing API server caplog.clear() mocker.patch('freqtrade.rpc.api_server.webserver.UvicornServer', @@ -1233,7 +1251,7 @@ def test_list_available_pairs(botclient): assert len(rc.json()['pair_interval']) == 1 -def test_api_backtesting(botclient, mocker, fee): +def test_api_backtesting(botclient, mocker, fee, caplog): ftbot, client = botclient mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) @@ -1323,6 +1341,11 @@ def test_api_backtesting(botclient, mocker, fee): ApiServer._bgtask_running = False + mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy', + side_effect=DependencyException()) + rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data)) + assert log_has("Backtesting caused an error: ", caplog) + # Delete backtesting to avoid leakage since the backtest-object may stick around. rc = client_delete(client, f"{BASE_URI}/backtest") assert_response(rc) From ad26b0dad057eaa95d798ba4f06ba627452e91f1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 10:59:00 +0200 Subject: [PATCH 088/519] Don't void backtest object when not necessary --- freqtrade/optimize/bt_progress.py | 1 - freqtrade/rpc/api_server/api_backtest.py | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/bt_progress.py b/freqtrade/optimize/bt_progress.py index 8d4fd1737..d295956c7 100644 --- a/freqtrade/optimize/bt_progress.py +++ b/freqtrade/optimize/bt_progress.py @@ -1,4 +1,3 @@ - from freqtrade.enums import BacktestState diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index cd40438d2..8bf87a044 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -45,17 +45,16 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac if (not ApiServer._bt or lastconfig.get('timeframe') != strat.timeframe - or lastconfig.get('stake_amount') != btconfig.get('stake_amount') - or lastconfig.get('enable_protections') != btconfig.get('enable_protections') - or lastconfig.get('protections') != btconfig.get('protections', []) or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0)): - # TODO: Investigate if enabling protections can be dynamically ingested from here... from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) # Only reload data if timeframe or timerange changed. if (not ApiServer._backtestdata or not ApiServer._bt_timerange or lastconfig.get('timerange') != btconfig['timerange'] + or lastconfig.get('stake_amount') != btconfig.get('stake_amount') + or lastconfig.get('enable_protections') != btconfig.get('enable_protections') + or lastconfig.get('protections') != btconfig.get('protections', []) or lastconfig.get('timeframe') != strat.timeframe): lastconfig['timerange'] = btconfig['timerange'] lastconfig['protections'] = btconfig.get('protections', []) From 52ae95b2a5b0625c71e1ce067a27c415320d1e37 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 11:19:23 +0200 Subject: [PATCH 089/519] Improve naming of apiserver variables --- freqtrade/commands/arguments.py | 2 +- freqtrade/rpc/api_server/api_backtest.py | 36 +++++++++++++----------- freqtrade/rpc/api_server/webserver.py | 4 +-- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 1efe450ff..1143db394 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -390,6 +390,6 @@ class Arguments: # Add webserver subcommand webserver_cmd = subparsers.add_parser('webserver', help='Webserver module.', - parents=[_common_parser, _strategy_parser]) + parents=[_common_parser]) webserver_cmd.set_defaults(func=start_webserver) self._build_args(optionlist=ARGS_WEBSERVER, parser=webserver_cmd) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 8bf87a044..76b4a8169 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -40,35 +40,39 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac asyncio.set_event_loop(asyncio.new_event_loop()) try: # Reload strategy - lastconfig = ApiServer._lastbacktestconfig + lastconfig = ApiServer._bt_last_config strat = StrategyResolver.load_strategy(btconfig) - if (not ApiServer._bt - or lastconfig.get('timeframe') != strat.timeframe - or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0)): + if ( + not ApiServer._bt + or lastconfig.get('timeframe') != strat.timeframe + or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0) + ): from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) # Only reload data if timeframe or timerange changed. - if (not ApiServer._backtestdata or not ApiServer._bt_timerange - or lastconfig.get('timerange') != btconfig['timerange'] - or lastconfig.get('stake_amount') != btconfig.get('stake_amount') - or lastconfig.get('enable_protections') != btconfig.get('enable_protections') - or lastconfig.get('protections') != btconfig.get('protections', []) - or lastconfig.get('timeframe') != strat.timeframe): + if ( + not ApiServer._bt_data + or not ApiServer._bt_timerange + or lastconfig.get('timerange') != btconfig['timerange'] + or lastconfig.get('stake_amount') != btconfig.get('stake_amount') + or lastconfig.get('enable_protections') != btconfig.get('enable_protections') + or lastconfig.get('protections') != btconfig.get('protections', []) + or lastconfig.get('timeframe') != strat.timeframe + ): lastconfig['timerange'] = btconfig['timerange'] lastconfig['protections'] = btconfig.get('protections', []) lastconfig['enable_protections'] = btconfig.get('enable_protections') lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') lastconfig['timeframe'] = strat.timeframe - ApiServer._backtestdata, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() + ApiServer._bt_data, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() ApiServer._bt.abort = False min_date, max_date = ApiServer._bt.backtest_one_strategy( - strat, ApiServer._backtestdata, - ApiServer._bt_timerange) + strat, ApiServer._bt_data, ApiServer._bt_timerange) ApiServer._bt.results = generate_backtest_stats( - ApiServer._backtestdata, ApiServer._bt.all_results, + ApiServer._bt_data, ApiServer._bt.all_results, min_date=min_date, max_date=max_date) logger.info("Backtest finished.") @@ -140,8 +144,8 @@ def api_delete_backtest(): if ApiServer._bt: del ApiServer._bt ApiServer._bt = None - del ApiServer._backtestdata - ApiServer._backtestdata = None + del ApiServer._bt_data + ApiServer._bt_data = None logger.info("Backtesting reset") return { "status": "reset", diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index e9becc936..235063191 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -35,9 +35,9 @@ class ApiServer(RPCHandler): _rpc: RPC # Backtesting type: Backtesting _bt = None - _backtestdata = None + _bt_data = None _bt_timerange = None - _lastbacktestconfig: Dict[str, Any] = {} + _bt_last_config: Dict[str, Any] = {} _has_rpc: bool = False _bgtask_running: bool = False _config: Dict[str, Any] = {} From f658cfa3491a7050debd8f86898e3f751d92b46e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Jul 2021 11:13:27 +0200 Subject: [PATCH 090/519] Remove Slack As the community is mostly active on discord, there's little point in linking people to Slack as well --- CONTRIBUTING.md | 2 +- README.md | 10 +++------- docs/developer.md | 2 +- docs/faq.md | 2 +- docs/includes/protections.md | 2 +- docs/index.md | 8 ++------ freqtrade/rpc/rpc_manager.py | 4 ++-- 7 files changed, 11 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 040cf3e98..c4ccc1b9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Few pointers for contributions: - New features need to contain unit tests, must conform to PEP8 (max-line-length = 100) and should be documented with the introduction PR. - PR's can be declared as `[WIP]` - which signify Work in Progress Pull Requests (which are not finished). -If you are unsure, discuss the feature on our [discord server](https://discord.gg/p7nuUNVfP7), on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR. +If you are unsure, discuss the feature on our [discord server](https://discord.gg/p7nuUNVfP7) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a Pull Request. ## Getting started diff --git a/README.md b/README.md index 4082995f0..8cba78136 100644 --- a/README.md +++ b/README.md @@ -142,13 +142,9 @@ The project is currently setup in two main branches: ## Support -### Help / Discord / Slack +### Help / Discord -For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join our slack channel. - -Please check out our [discord server](https://discord.gg/p7nuUNVfP7). - -You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw). +For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join the Freqtrade [discord server](https://discord.gg/p7nuUNVfP7). ### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue) @@ -179,7 +175,7 @@ to understand the requirements before sending your pull-requests. Coding is not a necessity to contribute - maybe start with improving our documentation? Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase. -**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [discord](https://discord.gg/p7nuUNVfP7) or [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. +**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [discord](https://discord.gg/p7nuUNVfP7) (please use the #dev channel for this). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. **Important:** Always create your PR against the `develop` branch, not `stable`. diff --git a/docs/developer.md b/docs/developer.md index d0731a233..dd56a367c 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -2,7 +2,7 @@ This page is intended for developers of Freqtrade, people who want to contribute to the Freqtrade codebase or documentation, or people who want to understand the source code of the application they're running. -All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel on [discord](https://discord.gg/p7nuUNVfP7) or [slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) where you can ask questions. +All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel on [discord](https://discord.gg/p7nuUNVfP7) where you can ask questions. ## Documentation diff --git a/docs/faq.md b/docs/faq.md index d015ae50e..b8a3a44d8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -172,7 +172,7 @@ freqtrade hyperopt --hyperopt SampleHyperopt --hyperopt-loss SharpeHyperOptLossD ### Why does it take a long time to run hyperopt? -* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) - or the Freqtrade [discord community](https://discord.gg/p7nuUNVfP7). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you. +* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [discord community](https://discord.gg/p7nuUNVfP7). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you. * If you wonder why it can take from 20 minutes to days to do 1000 epochs here are some answers: diff --git a/docs/includes/protections.md b/docs/includes/protections.md index 3ea2dde61..5dcc83738 100644 --- a/docs/includes/protections.md +++ b/docs/includes/protections.md @@ -1,7 +1,7 @@ ## Protections !!! Warning "Beta feature" - This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord, Slack or via Github Issue. + This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord or via Github Issue. Protections will protect your strategy from unexpected events and market conditions by temporarily stop trading for either one pair, or for all pairs. All protection end times are rounded up to the next candle to avoid sudden, unexpected intra-candle buys. diff --git a/docs/index.md b/docs/index.md index 8ecb085de..8077cd303 100644 --- a/docs/index.md +++ b/docs/index.md @@ -73,13 +73,9 @@ Alternatively ## Support -### Help / Discord / Slack +### Help / Discord -For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join our slack channel. - -Please check out our [discord server](https://discord.gg/p7nuUNVfP7). - -You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw). +For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join the Freqtrade [discord server](https://discord.gg/p7nuUNVfP7). ## Ready to try? diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 18ed68041..b3f45ab41 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -1,5 +1,5 @@ """ -This module contains class to manage RPC communications (Telegram, Slack, ...) +This module contains class to manage RPC communications (Telegram, API, ...) """ import logging from typing import Any, Dict, List @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) class RPCManager: """ - Class to manage RPC objects (Telegram, Slack, ...) + Class to manage RPC objects (Telegram, API, ...) """ def __init__(self, freqtrade) -> None: """ Initializes all enabled rpc modules """ From 0e4466ca1e46ebad19cc28a8ef9ac21123c48de4 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sun, 11 Jul 2021 12:20:31 +0300 Subject: [PATCH 091/519] Implement strategy-controlled stake sizes. Expose `self.wallet` to a strategy. --- docs/strategy-advanced.md | 31 ++++++++++++++++++++++ freqtrade/freqtradebot.py | 27 ++++++++++--------- freqtrade/optimize/backtesting.py | 15 ++++++++++- freqtrade/strategy/interface.py | 17 ++++++++++++ freqtrade/wallets.py | 43 +++++++++++++++++++++++++++---- tests/rpc/test_rpc.py | 2 +- tests/test_freqtradebot.py | 20 ++++++++++++++ 7 files changed, 136 insertions(+), 19 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index b06cf3ecb..e2fd30575 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -521,6 +521,37 @@ class AwesomeStrategy(IStrategy): ``` +### Stake size management + +It is possible to manage your risk by reducing or increasing or reducing stake amount when placing a new trade. + +```python +class AwesomeStrategy(IStrategy): + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + if current_candle['fastk_rsi_1h'] > current_candle['fastd_rsi_1h']: + if self.config['stake_amount'] == 'unlimited': + # Use entire available wallet during favorable conditions when in compounding mode. + return max_stake + else: + # Compound profits during favorable conditions instead of using a static stake. + return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake +``` + +!!! Tip + You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed, as returned value will be clamped to supported range and this acton will be logged. + +!!! Tip + Returning `0` or `None` will prevent trades from being placed. + --- ## Derived strategies diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7a7371357..efbf15fca 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -424,16 +424,10 @@ class FreqtradeBot(LoggingMixin): if buy and not sell: stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge) - if not stake_amount: - logger.debug(f"Stake amount is 0, ignoring possible trade for {pair}.") - return False - - logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: " - f"{stake_amount} ...") bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {}) if ((bid_check_dom.get('enabled', False)) and - (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): + (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): if self._check_depth_of_market_buy(pair, bid_check_dom): return self.execute_buy(pair, stake_amount) else: @@ -488,13 +482,22 @@ class FreqtradeBot(LoggingMixin): min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, buy_limit_requested, self.strategy.stoploss) - if min_stake_amount is not None and min_stake_amount > stake_amount: - logger.warning( - f"Can't open a new trade for {pair}: stake amount " - f"is too small ({stake_amount} < {min_stake_amount})" - ) + + if not self.edge: + max_stake_amount = self.wallets.get_available_stake_amount() + stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, + default_retval=None)( + pair=pair, current_time=datetime.now(timezone.utc), + current_rate=buy_limit_requested, proposed_stake=stake_amount, + min_stake=min_stake_amount, max_stake=max_stake_amount) + stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount) + + if not stake_amount: return False + logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: " + f"{stake_amount} ...") + amount = stake_amount / buy_limit_requested order_type = self.strategy.order_types['buy'] if forcebuy: diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8f818047d..885e67e0b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -129,6 +129,8 @@ class Backtesting: """ self.strategy: IStrategy = strategy strategy.dp = self.dataprovider + # Attach Wallets to Strategy baseclass + IStrategy.wallets = self.wallets # Set stoploss_on_exchange to false for backtesting, # since a "perfect" stoploss-sell is assumed anyway # And the regular "stoploss" function would not apply to that case @@ -312,7 +314,18 @@ class Backtesting: stake_amount = self.wallets.get_trade_stake_amount(pair, None) except DependencyException: return None - min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, row[OPEN_IDX], -0.05) + + min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, row[OPEN_IDX], -0.05) or 0 + max_stake_amount = self.wallets.get_available_stake_amount() + + stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, + default_retval=None)( + pair=pair, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], + proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) + stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount) + + if not stake_amount: + return None order_type = self.strategy.order_types['buy'] time_in_force = self.strategy.order_time_in_force['sell'] diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 26bcb0369..f9772e1df 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -304,6 +304,23 @@ class IStrategy(ABC, HyperStrategyMixin): """ return None + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + """ + Customize stake size for each new trade. This method is not called when edge module is + enabled. + + :param pair: Pair that's currently analyzed + :param current_time: datetime object, containing the current datetime + :param current_rate: Rate, calculated based on pricing settings in ask_strategy. + :param proposed_stake: A stake amount proposed by the bot. + :param min_stake: Minimal stake size allowed by exchange. + :param max_stake: Balance available for trading. + :return: A stake size, which is between min_stake and max_stake. + """ + return proposed_stake + def informative_pairs(self) -> ListPairsWithTimeframes: """ Define additional, informative pair/interval combinations to be cached from the exchange. diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 1b2ec4550..42144dad7 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -131,7 +131,22 @@ class Wallets: def get_all_balances(self) -> Dict[str, Any]: return self._wallets - def _get_available_stake_amount(self, val_tied_up: float) -> float: + def get_total_stake_amount(self): + """ + Return the total currently available balance in stake currency, including tied up stake and + respecting tradable_balance_ratio. + Calculated as + ( + free amount) * tradable_balance_ratio + """ + # Ensure % is used from the overall balance + # Otherwise we'd risk lowering stakes with each open trade. + # (tied up + current free) * ratio) - tied up + val_tied_up = Trade.total_open_trades_stakes() + available_amount = ((val_tied_up + self.get_free(self._config['stake_currency'])) * + self._config['tradable_balance_ratio']) + return available_amount + + def get_available_stake_amount(self) -> float: """ Return the total currently available balance in stake currency, respecting tradable_balance_ratio. @@ -142,9 +157,7 @@ class Wallets: # Ensure % is used from the overall balance # Otherwise we'd risk lowering stakes with each open trade. # (tied up + current free) * ratio) - tied up - available_amount = ((val_tied_up + self.get_free(self._config['stake_currency'])) * - self._config['tradable_balance_ratio']) - val_tied_up - return available_amount + return self.get_total_stake_amount() - Trade.total_open_trades_stakes() def _calculate_unlimited_stake_amount(self, available_amount: float, val_tied_up: float) -> float: @@ -193,7 +206,7 @@ class Wallets: # Ensure wallets are uptodate. self.update() val_tied_up = Trade.total_open_trades_stakes() - available_amount = self._get_available_stake_amount(val_tied_up) + available_amount = self.get_available_stake_amount() if edge: stake_amount = edge.stake_amount( @@ -209,3 +222,23 @@ class Wallets: available_amount, val_tied_up) return self._check_available_stake_amount(stake_amount, available_amount) + + def _validate_stake_amount(self, pair, stake_amount, min_stake_amount): + if not stake_amount: + logger.debug(f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.") + return 0 + + max_stake_amount = self.get_available_stake_amount() + if min_stake_amount is not None and stake_amount < min_stake_amount: + stake_amount = min_stake_amount + logger.info( + f"Stake amount for pair {pair} is too small ({stake_amount} < {min_stake_amount}), " + f"adjusting to {min_stake_amount}." + ) + if stake_amount > max_stake_amount: + stake_amount = max_stake_amount + logger.info( + f"Stake amount for pair {pair} is too big ({stake_amount} > {max_stake_amount}), " + f"adjusting to {max_stake_amount}." + ) + return stake_amount diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index cc29dc157..0049e59bb 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -886,7 +886,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> # Test not buying freqtradebot = get_patched_freqtradebot(mocker, default_conf) - freqtradebot.config['stake_amount'] = 0.0000001 + freqtradebot.config['stake_amount'] = 0 patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) pair = 'TKN/BTC' diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 99e11e893..117589b12 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -413,6 +413,26 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord patch_get_signal(freqtrade) + assert freqtrade.create_trade('ETH/BTC') + + +def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, + fee, mocker) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + buy_mock = MagicMock(return_value=limit_buy_order_open) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + fetch_ticker=ticker, + buy=buy_mock, + get_fee=fee, + ) + + freqtrade = FreqtradeBot(default_conf) + freqtrade.config['stake_amount'] = 0 + + patch_get_signal(freqtrade) + assert not freqtrade.create_trade('ETH/BTC') From 7ea0a74c532780a89b5fd885c804e437fe0291b9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Jul 2021 14:10:41 +0200 Subject: [PATCH 092/519] Default to proposed stake --- docs/strategy-advanced.md | 6 ++++-- freqtrade/freqtradebot.py | 2 +- freqtrade/optimize/backtesting.py | 2 +- tests/optimize/test_backtesting.py | 11 +++++++++++ tests/test_freqtradebot.py | 21 ++++++++++++++++++++- 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index e2fd30575..4dba9ad4a 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -523,7 +523,7 @@ class AwesomeStrategy(IStrategy): ### Stake size management -It is possible to manage your risk by reducing or increasing or reducing stake amount when placing a new trade. +It is possible to manage your risk by reducing or increasing stake amount when placing a new trade. ```python class AwesomeStrategy(IStrategy): @@ -546,8 +546,10 @@ class AwesomeStrategy(IStrategy): return proposed_stake ``` +Freqtrade will fall back to the `proposed_stake` value should your code raise an exception. The exception itself will be logged. + !!! Tip - You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed, as returned value will be clamped to supported range and this acton will be logged. + You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed as the returned value will be clamped to supported range and this acton will be logged. !!! Tip Returning `0` or `None` will prevent trades from being placed. diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index efbf15fca..5ef109387 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -486,7 +486,7 @@ class FreqtradeBot(LoggingMixin): if not self.edge: max_stake_amount = self.wallets.get_available_stake_amount() stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, - default_retval=None)( + default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), current_rate=buy_limit_requested, proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 885e67e0b..83cd8c2bb 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -319,7 +319,7 @@ class Backtesting: max_stake_amount = self.wallets.get_available_stake_amount() stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, - default_retval=None)( + default_retval=stake_amount)( pair=pair, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 30d86f979..a1881c306 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -496,6 +496,17 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: trade = backtesting._enter_trade(pair, row=row) assert trade is not None + backtesting.strategy.custom_stake_amount = lambda **kwargs: 123.5 + trade = backtesting._enter_trade(pair, row=row) + assert trade + assert trade.stake_amount == 123.5 + + # In case of error - use proposed stake + backtesting.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 + trade = backtesting._enter_trade(pair, row=row) + assert trade + assert trade.stake_amount == 495 + # Stake-amount too high! mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 117589b12..addf72bbb 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -397,7 +397,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order_open, def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order_open, - fee, mocker) -> None: + fee, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) buy_mock = MagicMock(return_value=limit_buy_order_open) @@ -414,6 +414,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord patch_get_signal(freqtrade) assert freqtrade.create_trade('ETH/BTC') + assert log_has_re(r"Stake amount for pair .* is too small.*", caplog) def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, @@ -862,6 +863,24 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order assert trade.open_rate == 0.5 assert trade.stake_amount == 40.495905365 + # Test with custom stake + limit_buy_order['status'] = 'open' + limit_buy_order['id'] = '556' + + freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0 + assert freqtrade.execute_buy(pair, stake_amount) + trade = Trade.query.all()[4] + assert trade + assert trade.stake_amount == 150 + + # Exception case + limit_buy_order['id'] = '557' + freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 + assert freqtrade.execute_buy(pair, stake_amount) + trade = Trade.query.all()[5] + assert trade + assert trade.stake_amount == 2.0 + # In case of the order is rejected and not filled at all limit_buy_order['status'] = 'rejected' limit_buy_order['amount'] = 90.99181073 From 8b78a3bde2b41e53061f4621982166bb5ece694e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Jul 2021 21:01:12 +0200 Subject: [PATCH 093/519] Quick fix for trades opening below min-trade amount --- freqtrade/wallets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 42144dad7..ece496c90 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -229,6 +229,10 @@ class Wallets: return 0 max_stake_amount = self.get_available_stake_amount() + + if min_stake_amount > max_stake_amount: + logger.warning("Minimum stake amount > available balance.") + return 0 if min_stake_amount is not None and stake_amount < min_stake_amount: stake_amount = min_stake_amount logger.info( From f4caf9b93c9bc985530d45c8b3aee8db22d3f7f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 03:01:29 +0000 Subject: [PATCH 094/519] Bump ccxt from 1.52.40 to 1.52.83 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.52.40 to 1.52.83. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.52.40...1.52.83) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 528dc2ce6..08a04d5f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.0 pandas==1.3.0 -ccxt==1.52.40 +ccxt==1.52.83 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 21ef08d9a8b4d49873f70c3ba39bf3099a39fff1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 03:01:34 +0000 Subject: [PATCH 095/519] Bump mkdocs-material from 7.1.9 to 7.1.10 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.1.9 to 7.1.10. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.1.9...7.1.10) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index d11e5ea4e..e346fe5a5 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.1 -mkdocs-material==7.1.9 +mkdocs-material==7.1.10 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From 81c50aca0116f9f14b1d73ed4aa7df151cb6696b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 03:01:38 +0000 Subject: [PATCH 096/519] Bump isort from 5.9.1 to 5.9.2 Bumps [isort](https://github.com/pycqa/isort) from 5.9.1 to 5.9.2. - [Release notes](https://github.com/pycqa/isort/releases) - [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) - [Commits](https://github.com/pycqa/isort/compare/5.9.1...5.9.2) --- updated-dependencies: - dependency-name: isort dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index c73acbe9c..e25f810cd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -13,7 +13,7 @@ pytest-asyncio==0.15.1 pytest-cov==2.12.1 pytest-mock==3.6.1 pytest-random-order==1.0.4 -isort==5.9.1 +isort==5.9.2 # Convert jupyter notebooks to markdown documents nbconvert==6.1.0 From ed77889d6b4f51a3ee847de4888c6b4f030f35b0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 12 Jul 2021 06:52:59 +0200 Subject: [PATCH 097/519] Add explicit tests for _validate_stake_amount --- tests/test_wallets.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_wallets.py b/tests/test_wallets.py index ff303e2ec..d083a63f6 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -170,3 +170,22 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r freqtrade.config['max_open_trades'] = 0 result = freqtrade.wallets.get_trade_stake_amount('NEO/USDT') assert result == 0 + + +@pytest.mark.parametrize('stake_amount,min_stake_amount,max_stake_amount,expected', [ + (22, 11, 50, 22), + (100, 11, 500, 100), + (1000, 11, 500, 500), # Above max-stake + (20, 15, 10, 0), # Minimum stake > max-stake + (1, 11, 100, 11), # Below min stake + (1, 15, 10, 0), # Below min stake and min_stake > max_stake + +]) +def test__validate_stake_amount(mocker, default_conf, + stake_amount, min_stake_amount, max_stake_amount, expected): + freqtrade = get_patched_freqtradebot(mocker, default_conf) + + mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount", + return_value=max_stake_amount) + res = freqtrade.wallets._validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount) + assert res == expected From b41c2344405c438eb3f81e60eb8eeb3cef1510ad Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 12:18:55 +0200 Subject: [PATCH 098/519] Extract Closed profit calculation to trade object --- freqtrade/persistence/models.py | 13 +++++++++++++ freqtrade/wallets.py | 4 +--- tests/test_persistence.py | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index b4c299120..8dcfc6c94 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -801,6 +801,19 @@ class Trade(_DECL_BASE, LocalTrade): Trade.is_open.is_(False), ]).all() + @staticmethod + def get_total_closed_profit() -> float: + """ + Retrieves total realized profit + """ + if Trade.use_db: + total_profit = Trade.query.with_entities( + func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False)).scalar() + else: + total_profit = sum( + t.close_profit_abs for t in LocalTrade.get_trades_proxy(is_open=False)) + return total_profit or 0 + @staticmethod def total_open_trades_stakes() -> float: """ diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index ece496c90..3d80cc892 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -70,9 +70,7 @@ class Wallets: # If not backtesting... # TODO: potentially remove the ._log workaround to determine backtest mode. if self._log: - closed_trades = Trade.get_trades_proxy(is_open=False) - tot_profit = sum( - [trade.close_profit_abs for trade in closed_trades if trade.close_profit_abs]) + tot_profit = Trade.get_total_closed_profit() else: tot_profit = LocalTrade.total_profit tot_in_trades = sum([trade.stake_amount for trade in open_trades]) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 1576aaa5a..89d07ca74 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -1124,6 +1124,21 @@ def test_total_open_trades_stakes(fee, use_db): Trade.use_db = True +@pytest.mark.usefixtures("init_persistence") +@pytest.mark.parametrize('use_db', [True, False]) +def test_get_total_closed_profit(fee, use_db): + + Trade.use_db = use_db + Trade.reset_trades() + res = Trade.get_total_closed_profit() + assert res == 0 + create_mock_trades(fee, use_db) + res = Trade.get_total_closed_profit() + assert res == 0.000739127 + + Trade.use_db = True + + @pytest.mark.usefixtures("init_persistence") @pytest.mark.parametrize('use_db', [True, False]) def test_get_trades_proxy(fee, use_db): @@ -1298,6 +1313,7 @@ def test_Trade_object_idem(): 'open_date', 'get_best_pair', 'get_overall_performance', + 'get_total_closed_profit', 'total_open_trades_stakes', 'get_sold_trades_without_assigned_fees', 'get_open_trades_without_assigned_fees', From 786374690406d00cad290a583c87972c436dc527 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 12:30:00 +0200 Subject: [PATCH 099/519] Add available_capital parameter --- freqtrade/constants.py | 4 ++++ freqtrade/wallets.py | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index acd143708..2f93ace1c 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -113,6 +113,10 @@ CONF_SCHEMA = { 'maximum': 1, 'default': 0.99 }, + 'available_capital': { + 'type': 'number', + 'minimum': 0, + }, 'amend_last_stake_amount': {'type': 'boolean', 'default': False}, 'last_stake_amount_min_ratio': { 'type': 'number', 'minimum': 0.0, 'maximum': 1.0, 'default': 0.5 diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 3d80cc892..0ece65b2b 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -136,12 +136,18 @@ class Wallets: Calculated as ( + free amount) * tradable_balance_ratio """ - # Ensure % is used from the overall balance - # Otherwise we'd risk lowering stakes with each open trade. - # (tied up + current free) * ratio) - tied up val_tied_up = Trade.total_open_trades_stakes() - available_amount = ((val_tied_up + self.get_free(self._config['stake_currency'])) * - self._config['tradable_balance_ratio']) + if "available_capital" in self._config: + starting_balance = self._config['available_capital'] + tot_profit = Trade.get_total_closed_profit() + available_amount = starting_balance + tot_profit + + else: + # Ensure % is used from the overall balance + # Otherwise we'd risk lowering stakes with each open trade. + # (tied up + current free) * ratio) - tied up + available_amount = ((val_tied_up + self.get_free(self._config['stake_currency'])) * + self._config['tradable_balance_ratio']) return available_amount def get_available_stake_amount(self) -> float: @@ -152,10 +158,9 @@ class Wallets: ( + free amount) * tradable_balance_ratio - """ - # Ensure % is used from the overall balance - # Otherwise we'd risk lowering stakes with each open trade. - # (tied up + current free) * ratio) - tied up - return self.get_total_stake_amount() - Trade.total_open_trades_stakes() + + free = self.get_free(self._config['stake_currency']) + return min(self.get_total_stake_amount() - Trade.total_open_trades_stakes(), free) def _calculate_unlimited_stake_amount(self, available_amount: float, val_tied_up: float) -> float: From 6a8e8875a2a7893e9ad28eebce7e6cddd912e833 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Jul 2021 12:39:02 +0200 Subject: [PATCH 100/519] Test new behaviour --- tests/test_wallets.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/test_wallets.py b/tests/test_wallets.py index d083a63f6..a44ca243b 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -121,13 +121,19 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None: freqtrade.wallets.get_trade_stake_amount('ETH/BTC') -@pytest.mark.parametrize("balance_ratio,result1,result2", [ - (1, 50, 66.66666), - (0.99, 49.5, 66.0), - (0.50, 25, 33.3333), +@pytest.mark.parametrize("balance_ratio,capital,result1,result2", [ + (1, None, 50, 66.66666), + (0.99, None, 49.5, 66.0), + (0.50, None, 25, 33.3333), + # Tests with capital ignore balance_ratio + (1, 100, 50, 0.0), + (0.99, 200, 50, 66.66666), + (0.99, 150, 50, 50), + (0.50, 50, 25, 0.0), + (0.50, 10, 5, 0.0), ]) -def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, result1, - result2, limit_buy_order_open, +def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, capital, + result1, result2, limit_buy_order_open, fee, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -141,6 +147,8 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r conf['dry_run_wallet'] = 100 conf['max_open_trades'] = 2 conf['tradable_balance_ratio'] = balance_ratio + if capital is not None: + conf['available_capital'] = capital freqtrade = get_patched_freqtradebot(mocker, conf) From 40db424363556d95cfbfd5cd71faa9a50ca86af6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Jul 2021 10:44:36 +0200 Subject: [PATCH 101/519] Add documentation for available capital setting --- docs/configuration.md | 17 +++++++++++++++++ freqtrade/wallets.py | 1 - 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 5c6236e58..ad7436cb2 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -52,6 +52,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `stake_currency` | **Required.** Crypto-currency used for trading.
**Datatype:** String | `stake_amount` | **Required.** Amount of crypto-currency your bot will use for each trade. Set it to `"unlimited"` to allow the bot to use all available balance. [More information below](#configuring-amount-per-trade).
**Datatype:** Positive float or `"unlimited"`. | `tradable_balance_ratio` | Ratio of the total account balance the bot is allowed to trade. [More information below](#configuring-amount-per-trade).
*Defaults to `0.99` 99%).*
**Datatype:** Positive float between `0.1` and `1.0`. +| `available_capital` | Available starting capital for the bot. Useful when running multiple bots on the same exchange account.[More information below](#configuring-amount-per-trade).
**Datatype:** Positive float. | `amend_last_stake_amount` | Use reduced last stake amount if necessary. [More information below](#configuring-amount-per-trade).
*Defaults to `false`.*
**Datatype:** Boolean | `last_stake_amount_min_ratio` | Defines minimum stake amount that has to be left and executed. Applies only to the last stake amount when it's amended to a reduced value (i.e. if `amend_last_stake_amount` is set to `true`). [More information below](#configuring-amount-per-trade).
*Defaults to `0.5`.*
**Datatype:** Float (as ratio) | `amount_reserve_percent` | Reserve some amount in min pair stake amount. The bot will reserve `amount_reserve_percent` + stoploss value when calculating min pair stake amount in order to avoid possible trade refusals.
*Defaults to `0.05` (5%).*
**Datatype:** Positive Float as ratio. @@ -192,9 +193,25 @@ You can configure the "untouched" amount by using the `tradable_balance_ratio` s For example, if you have 10 ETH available in your wallet on the exchange and `tradable_balance_ratio=0.5` (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers this as available balance. The rest of the wallet is untouched by the trades. +!!! Danger + This setting should **not** be used when running multiple bots on the same exchange. Please look at [Available Capital to the bot](#assign-available-capital) instead. + !!! Warning The `tradable_balance_ratio` setting applies to the current balance (free balance + tied up in trades). Therefore, assuming the starting balance of 1000, a configuration with `tradable_balance_ratio=0.99` will not guarantee that 10 currency units will always remain available on the exchange. For example, the free amount may reduce to 5 units if the total balance is reduced to 500 (either by a losing streak, or by withdrawing balance). +#### Assign available Capital + +To fully utilize compounding profits when using multiple bots on the same exchange account, you'll want to limit each bot to a certain starting balance. +This can be accomplished by setting `available_capital` to the desired starting balance. + +Assuming your account has 10.000 USDT and you want to run 2 different strategies on this exchange. +You'd set `available_capital=5000` - granting each bot an initial capital of 5000 USDT. +The bot will then split this starting balance equally into `max_open_trades` buckets. +Profitable trades will result in increased stake-sizes for this bot - without affecting stake-sizes of the other bot. + +!!! Warning "Incompatible with `tradable_balance_ratio`" + Setting this option will replace any configuration of `tradable_balance_ratio`. + #### Amend last stake amount Assuming we have the tradable balance of 1000 USDT, `stake_amount=400`, and `max_open_trades=3`. diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 0ece65b2b..0048dbf48 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -158,7 +158,6 @@ class Wallets: ( + free amount) * tradable_balance_ratio - """ - free = self.get_free(self._config['stake_currency']) return min(self.get_total_stake_amount() - Trade.total_open_trades_stakes(), free) From f94dbcd0853e87e6cb6996cbf8587ed5732b0138 Mon Sep 17 00:00:00 2001 From: anasyusef Date: Mon, 12 Jul 2021 12:02:10 +0000 Subject: [PATCH 102/519] feat: censor password from logs --- .terraform/modules/vpc | 1 + freqtrade/misc.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 160000 .terraform/modules/vpc diff --git a/.terraform/modules/vpc b/.terraform/modules/vpc new file mode 160000 index 000000000..88e1c6ec7 --- /dev/null +++ b/.terraform/modules/vpc @@ -0,0 +1 @@ +Subproject commit 88e1c6ec7a8f6337b0d6bb9840bb70c8b3f14051 diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 967f08299..79c6aec67 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -8,6 +8,7 @@ from datetime import datetime from pathlib import Path from typing import Any, Iterator, List from typing.io import IO +from urllib.parse import urlparse import rapidjson @@ -214,3 +215,16 @@ def chunks(lst: List[Any], n: int) -> Iterator[List[Any]]: """ for chunk in range(0, len(lst), n): yield (lst[chunk:chunk + n]) + + +def parse_db_uri_for_logging(uri: str): + """ + Helper method to parse the DB URI and return the same DB URI with the password censored + if it contains it. Otherwise, return the DB URI unchanged + :param uri: DB URI to parse for logging + """ + parsed_db_uri = urlparse(uri) + if not parsed_db_uri.netloc: # No need for censoring as no password was provided + return uri + pwd = parsed_db_uri.netloc.split(':')[1].split('@')[0] + return parsed_db_uri.geturl().replace(f':{pwd}@', ':****@') From 6a53e2c764ff2aaad68e83269604f364b11cdf82 Mon Sep 17 00:00:00 2001 From: anasyusef Date: Mon, 12 Jul 2021 12:08:01 +0000 Subject: [PATCH 103/519] feat: apply censoring to logging --- freqtrade/commands/list_commands.py | 4 ++-- freqtrade/configuration/configuration.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index cd26aa60e..410b9b72b 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -14,7 +14,7 @@ from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import market_is_active, validate_exchanges -from freqtrade.misc import plural +from freqtrade.misc import parse_db_uri_for_logging, plural from freqtrade.resolvers import ExchangeResolver, StrategyResolver @@ -225,7 +225,7 @@ def start_show_trades(args: Dict[str, Any]) -> None: if 'db_url' not in config: raise OperationalException("--db-url is required for this command.") - logger.info(f'Using DB: "{config["db_url"]}"') + logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"') init_db(config['db_url'], clean_open_orders=False) tfilter = [] diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 1d2e3f802..ea202355a 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -15,7 +15,7 @@ from freqtrade.configuration.load_config import load_config_file, load_file from freqtrade.enums import NON_UTIL_MODES, TRADING_MODES, RunMode from freqtrade.exceptions import OperationalException from freqtrade.loggers import setup_logging -from freqtrade.misc import deep_merge_dicts +from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging logger = logging.getLogger(__name__) @@ -144,7 +144,7 @@ class Configuration: config['db_url'] = constants.DEFAULT_DB_PROD_URL logger.info('Dry run is disabled') - logger.info(f'Using DB: "{config["db_url"]}"') + logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"') def _process_common_options(self, config: Dict[str, Any]) -> None: From c78b2075d84bb1bcd685e141c4bcd7149fdca95c Mon Sep 17 00:00:00 2001 From: anasyusef Date: Mon, 12 Jul 2021 12:27:59 +0000 Subject: [PATCH 104/519] feat: add one additional asterisk --- freqtrade/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 79c6aec67..6f439866b 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -227,4 +227,4 @@ def parse_db_uri_for_logging(uri: str): if not parsed_db_uri.netloc: # No need for censoring as no password was provided return uri pwd = parsed_db_uri.netloc.split(':')[1].split('@')[0] - return parsed_db_uri.geturl().replace(f':{pwd}@', ':****@') + return parsed_db_uri.geturl().replace(f':{pwd}@', ':*****@') From 313cf6a01307f9d7826781c13e184b31374ffee4 Mon Sep 17 00:00:00 2001 From: anasyusef Date: Mon, 12 Jul 2021 12:28:34 +0000 Subject: [PATCH 105/519] test: add test for parsing db uri --- tests/test_misc.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index e6ba70aee..5b5a1400f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -9,7 +9,7 @@ import pytest from freqtrade.misc import (decimals_per_coin, file_dump_json, file_load_json, format_ms_time, pair_to_filename, plural, render_template, render_template_with_fallback, round_coin_value, safe_value_fallback, - safe_value_fallback2, shorten_date) + safe_value_fallback2, shorten_date, parse_db_uri_for_logging) def test_decimals_per_coin(): @@ -179,3 +179,18 @@ def test_render_template_fallback(mocker): ) assert isinstance(val, str) assert 'if self.dp' in val + +def test_parse_db_uri_for_logging() -> None: + postgresql_conn_uri = "postgresql+psycopg2://scott123:scott123@host/dbname" + mariadb_conn_uri = "mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company" + mysql_conn_uri = "mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4" + sqlite_conn_uri = "sqlite:////freqtrade/user_data/tradesv3.sqlite" + censored_pwd = "*****" + + get_pwd = lambda x: x.split(':')[2].split('@')[0] + + assert get_pwd(parse_db_uri_for_logging(postgresql_conn_uri)) == censored_pwd + assert get_pwd(parse_db_uri_for_logging(mariadb_conn_uri)) == censored_pwd + assert get_pwd(parse_db_uri_for_logging(mysql_conn_uri)) == censored_pwd + assert sqlite_conn_uri == parse_db_uri_for_logging(sqlite_conn_uri) + From 8def18b002a524a6206a4dd88519717879d41259 Mon Sep 17 00:00:00 2001 From: anasyusef Date: Mon, 12 Jul 2021 12:31:13 +0000 Subject: [PATCH 106/519] style: apply flake8 formatting --- tests/test_misc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 5b5a1400f..221c7b712 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -7,9 +7,9 @@ from unittest.mock import MagicMock import pytest from freqtrade.misc import (decimals_per_coin, file_dump_json, file_load_json, format_ms_time, - pair_to_filename, plural, render_template, + pair_to_filename, parse_db_uri_for_logging, plural, render_template, render_template_with_fallback, round_coin_value, safe_value_fallback, - safe_value_fallback2, shorten_date, parse_db_uri_for_logging) + safe_value_fallback2, shorten_date) def test_decimals_per_coin(): @@ -180,6 +180,7 @@ def test_render_template_fallback(mocker): assert isinstance(val, str) assert 'if self.dp' in val + def test_parse_db_uri_for_logging() -> None: postgresql_conn_uri = "postgresql+psycopg2://scott123:scott123@host/dbname" mariadb_conn_uri = "mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company" @@ -187,10 +188,9 @@ def test_parse_db_uri_for_logging() -> None: sqlite_conn_uri = "sqlite:////freqtrade/user_data/tradesv3.sqlite" censored_pwd = "*****" - get_pwd = lambda x: x.split(':')[2].split('@')[0] + def get_pwd(x): return x.split(':')[2].split('@')[0] assert get_pwd(parse_db_uri_for_logging(postgresql_conn_uri)) == censored_pwd assert get_pwd(parse_db_uri_for_logging(mariadb_conn_uri)) == censored_pwd assert get_pwd(parse_db_uri_for_logging(mysql_conn_uri)) == censored_pwd assert sqlite_conn_uri == parse_db_uri_for_logging(sqlite_conn_uri) - From 91e5562ae01e68a6f5b98f24c9926d76cc205066 Mon Sep 17 00:00:00 2001 From: anasyusef Date: Mon, 12 Jul 2021 12:31:13 +0000 Subject: [PATCH 107/519] style: apply flake8 formatting --- .terraform/modules/vpc | 1 - tests/test_misc.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 160000 .terraform/modules/vpc diff --git a/.terraform/modules/vpc b/.terraform/modules/vpc deleted file mode 160000 index 88e1c6ec7..000000000 --- a/.terraform/modules/vpc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 88e1c6ec7a8f6337b0d6bb9840bb70c8b3f14051 diff --git a/tests/test_misc.py b/tests/test_misc.py index 5b5a1400f..221c7b712 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -7,9 +7,9 @@ from unittest.mock import MagicMock import pytest from freqtrade.misc import (decimals_per_coin, file_dump_json, file_load_json, format_ms_time, - pair_to_filename, plural, render_template, + pair_to_filename, parse_db_uri_for_logging, plural, render_template, render_template_with_fallback, round_coin_value, safe_value_fallback, - safe_value_fallback2, shorten_date, parse_db_uri_for_logging) + safe_value_fallback2, shorten_date) def test_decimals_per_coin(): @@ -180,6 +180,7 @@ def test_render_template_fallback(mocker): assert isinstance(val, str) assert 'if self.dp' in val + def test_parse_db_uri_for_logging() -> None: postgresql_conn_uri = "postgresql+psycopg2://scott123:scott123@host/dbname" mariadb_conn_uri = "mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company" @@ -187,10 +188,9 @@ def test_parse_db_uri_for_logging() -> None: sqlite_conn_uri = "sqlite:////freqtrade/user_data/tradesv3.sqlite" censored_pwd = "*****" - get_pwd = lambda x: x.split(':')[2].split('@')[0] + def get_pwd(x): return x.split(':')[2].split('@')[0] assert get_pwd(parse_db_uri_for_logging(postgresql_conn_uri)) == censored_pwd assert get_pwd(parse_db_uri_for_logging(mariadb_conn_uri)) == censored_pwd assert get_pwd(parse_db_uri_for_logging(mysql_conn_uri)) == censored_pwd assert sqlite_conn_uri == parse_db_uri_for_logging(sqlite_conn_uri) - From 2bf7705f2c0c46a9d38a087ced290a59f90f4bae Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Mon, 12 Jul 2021 23:05:35 -0600 Subject: [PATCH 108/519] Renamed example config files so they are .json so that syntax highlighting is all correct. Explicitly listed each one in .gitignore to prevent a real config file from being uploaded accidently --- .gitignore | 5 +++++ .../config_binance.example.json | 0 .../config_bittrex.example.json | 0 .../config_ftx.example.json | 0 .../config_full.example.json | 0 .../config_kraken.example.json | 0 6 files changed, 5 insertions(+) rename config_binance.json.example => config_examples/config_binance.example.json (100%) rename config_bittrex.json.example => config_examples/config_bittrex.example.json (100%) rename config_ftx.json.example => config_examples/config_ftx.example.json (100%) rename config_full.json.example => config_examples/config_full.example.json (100%) rename config_kraken.json.example => config_examples/config_kraken.example.json (100%) diff --git a/.gitignore b/.gitignore index 4720ff5cb..16df71194 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,8 @@ target/ #exceptions !*.gitkeep +!config_examples/config_binance.example.json +!config_examples/config_bittrex.example.json +!config_examples/config_ftx.example.json +!config_examples/config_full.example.json +!config_examples/config_kraken.example.json diff --git a/config_binance.json.example b/config_examples/config_binance.example.json similarity index 100% rename from config_binance.json.example rename to config_examples/config_binance.example.json diff --git a/config_bittrex.json.example b/config_examples/config_bittrex.example.json similarity index 100% rename from config_bittrex.json.example rename to config_examples/config_bittrex.example.json diff --git a/config_ftx.json.example b/config_examples/config_ftx.example.json similarity index 100% rename from config_ftx.json.example rename to config_examples/config_ftx.example.json diff --git a/config_full.json.example b/config_examples/config_full.example.json similarity index 100% rename from config_full.json.example rename to config_examples/config_full.example.json diff --git a/config_kraken.json.example b/config_examples/config_kraken.example.json similarity index 100% rename from config_kraken.json.example rename to config_examples/config_kraken.example.json From 29e2b858ca1fff6e23e276b0086e545356694431 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jul 2021 20:40:06 +0200 Subject: [PATCH 109/519] Improve wording in docs --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index ad7436cb2..73b0e9c9a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -194,7 +194,7 @@ You can configure the "untouched" amount by using the `tradable_balance_ratio` s For example, if you have 10 ETH available in your wallet on the exchange and `tradable_balance_ratio=0.5` (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers this as available balance. The rest of the wallet is untouched by the trades. !!! Danger - This setting should **not** be used when running multiple bots on the same exchange. Please look at [Available Capital to the bot](#assign-available-capital) instead. + This setting should **not** be used when running multiple bots on the same account. Please look at [Available Capital to the bot](#assign-available-capital) instead. !!! Warning The `tradable_balance_ratio` setting applies to the current balance (free balance + tied up in trades). Therefore, assuming the starting balance of 1000, a configuration with `tradable_balance_ratio=0.99` will not guarantee that 10 currency units will always remain available on the exchange. For example, the free amount may reduce to 5 units if the total balance is reduced to 500 (either by a losing streak, or by withdrawing balance). From 362436f7d23c8b4ebb7bec942c3bf2a259192a34 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Mon, 12 Jul 2021 23:05:35 -0600 Subject: [PATCH 110/519] Renamed example config files so they are .json so that syntax highlighting is all correct. Explicitly listed each one in .gitignore to prevent a real config file from being uploaded accidently --- .github/workflows/ci.yml | 12 ++--- .gitignore | 5 ++ .travis.yml | 4 +- build_helpers/publish_docker_multi.sh | 2 +- .../config_binance.example.json | 0 .../config_bittrex.example.json | 0 .../config_ftx.example.json | 0 .../config_full.example.json | 0 .../config_kraken.example.json | 0 docs/configuration.md | 2 +- tests/commands/test_commands.py | 54 +++++++++---------- tests/test_arguments.py | 2 +- tests/test_configuration.py | 2 +- tests/test_main.py | 20 +++---- tests/test_plotting.py | 4 +- 15 files changed, 56 insertions(+), 51 deletions(-) rename config_binance.json.example => config_examples/config_binance.example.json (100%) rename config_bittrex.json.example => config_examples/config_bittrex.example.json (100%) rename config_ftx.json.example => config_examples/config_ftx.example.json (100%) rename config_full.json.example => config_examples/config_full.example.json (100%) rename config_kraken.json.example => config_examples/config_kraken.example.json (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42959c3b5..5890f56a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,13 +79,13 @@ jobs: - name: Backtesting run: | - cp config_bittrex.json.example config.json + cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy - name: Hyperopt run: | - cp config_bittrex.json.example config.json + cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all @@ -172,13 +172,13 @@ jobs: - name: Backtesting run: | - cp config_bittrex.json.example config.json + cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy - name: Hyperopt run: | - cp config_bittrex.json.example config.json + cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all @@ -239,13 +239,13 @@ jobs: - name: Backtesting run: | - cp config_bittrex.json.example config.json + cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy - name: Hyperopt run: | - cp config_bittrex.json.example config.json + cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all diff --git a/.gitignore b/.gitignore index 4720ff5cb..16df71194 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,8 @@ target/ #exceptions !*.gitkeep +!config_examples/config_binance.example.json +!config_examples/config_bittrex.example.json +!config_examples/config_ftx.example.json +!config_examples/config_full.example.json +!config_examples/config_kraken.example.json diff --git a/.travis.yml b/.travis.yml index 4535c44cb..f2a6d508d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,12 +26,12 @@ jobs: # - coveralls || true name: pytest - script: - - cp config_bittrex.json.example config.json + - cp config_examples/config_bittrex.example.json config.json - freqtrade create-userdir --userdir user_data - freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy name: backtest - script: - - cp config_bittrex.json.example config.json + - cp config_examples/config_bittrex.example.json config.json - freqtrade create-userdir --userdir user_data - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily name: hyperopt diff --git a/build_helpers/publish_docker_multi.sh b/build_helpers/publish_docker_multi.sh index a6b06ce7d..057ecbbf2 100755 --- a/build_helpers/publish_docker_multi.sh +++ b/build_helpers/publish_docker_multi.sh @@ -52,7 +52,7 @@ docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${TAG} -t fre docker tag freqtrade:$TAG_PLOT ${IMAGE_NAME}:$TAG_PLOT # Run backtest -docker run --rm -v $(pwd)/config_bittrex.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy +docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy if [ $? -ne 0 ]; then echo "failed running backtest" diff --git a/config_binance.json.example b/config_examples/config_binance.example.json similarity index 100% rename from config_binance.json.example rename to config_examples/config_binance.example.json diff --git a/config_bittrex.json.example b/config_examples/config_bittrex.example.json similarity index 100% rename from config_bittrex.json.example rename to config_examples/config_bittrex.example.json diff --git a/config_ftx.json.example b/config_examples/config_ftx.example.json similarity index 100% rename from config_ftx.json.example rename to config_examples/config_ftx.example.json diff --git a/config_full.json.example b/config_examples/config_full.example.json similarity index 100% rename from config_full.json.example rename to config_examples/config_full.example.json diff --git a/config_kraken.json.example b/config_examples/config_kraken.example.json similarity index 100% rename from config_kraken.json.example rename to config_examples/config_kraken.example.json diff --git a/docs/configuration.md b/docs/configuration.md index 5c6236e58..c84a0ac34 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -556,7 +556,7 @@ You should also make sure to read the [Exchanges](exchanges.md) section of the d To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration. -An example for this can be found in `config_full.json.example` +An example for this can be found in `config_examples/config_full.example.json` ``` json "ccxt_async_config": { diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index dcceb3ea1..de0cb80e4 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -26,7 +26,7 @@ from tests.conftest_trades import MOCK_TRADE_COUNT def test_setup_utils_configuration(): args = [ - 'list-exchanges', '--config', 'config_bittrex.json.example', + 'list-exchanges', '--config', 'config_examples/config_bittrex.example.json', ] config = setup_utils_configuration(get_args(args), RunMode.OTHER) @@ -45,7 +45,7 @@ def test_start_trading_fail(mocker, caplog): exitmock = mocker.patch("freqtrade.worker.Worker.exit", MagicMock()) args = [ 'trade', - '-c', 'config_bittrex.json.example' + '-c', 'config_examples/config_bittrex.example.json' ] start_trading(get_args(args)) assert exitmock.call_count == 1 @@ -127,10 +127,10 @@ def test_list_timeframes(mocker, capsys): match=r"This command requires a configured exchange.*"): start_list_timeframes(pargs) - # Test with --config config_bittrex.json.example + # Test with --config config_examples/config_bittrex.example.json args = [ "list-timeframes", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', ] start_list_timeframes(get_args(args)) captured = capsys.readouterr() @@ -174,7 +174,7 @@ def test_list_timeframes(mocker, capsys): # Test with --one-column args = [ "list-timeframes", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--one-column", ] start_list_timeframes(get_args(args)) @@ -214,10 +214,10 @@ def test_list_markets(mocker, markets, capsys): match=r"This command requires a configured exchange.*"): start_list_markets(pargs, False) - # Test with --config config_bittrex.json.example + # Test with --config config_examples/config_bittrex.example.json args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--print-list", ] start_list_markets(get_args(args), False) @@ -244,7 +244,7 @@ def test_list_markets(mocker, markets, capsys): # Test with --all: all markets args = [ "list-markets", "--all", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--print-list", ] start_list_markets(get_args(args), False) @@ -257,7 +257,7 @@ def test_list_markets(mocker, markets, capsys): # Test list-pairs subcommand: active pairs args = [ "list-pairs", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--print-list", ] start_list_markets(get_args(args), True) @@ -269,7 +269,7 @@ def test_list_markets(mocker, markets, capsys): # Test list-pairs subcommand with --all: all pairs args = [ "list-pairs", "--all", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--print-list", ] start_list_markets(get_args(args), True) @@ -282,7 +282,7 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=ETH, LTC args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--base", "ETH", "LTC", "--print-list", ] @@ -295,7 +295,7 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--base", "LTC", "--print-list", ] @@ -308,7 +308,7 @@ def test_list_markets(mocker, markets, capsys): # active markets, quote=USDT, USD args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--quote", "USDT", "USD", "--print-list", ] @@ -321,7 +321,7 @@ def test_list_markets(mocker, markets, capsys): # active markets, quote=USDT args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--quote", "USDT", "--print-list", ] @@ -334,7 +334,7 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=USDT args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--base", "LTC", "--quote", "USDT", "--print-list", ] @@ -347,7 +347,7 @@ def test_list_markets(mocker, markets, capsys): # active pairs, base=LTC, quote=USDT args = [ "list-pairs", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--base", "LTC", "--quote", "USD", "--print-list", ] @@ -360,7 +360,7 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=USDT, NONEXISTENT args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--base", "LTC", "--quote", "USDT", "NONEXISTENT", "--print-list", ] @@ -373,7 +373,7 @@ def test_list_markets(mocker, markets, capsys): # active markets, base=LTC, quote=NONEXISTENT args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--base", "LTC", "--quote", "NONEXISTENT", "--print-list", ] @@ -386,7 +386,7 @@ def test_list_markets(mocker, markets, capsys): # Test tabular output args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', ] start_list_markets(get_args(args), False) captured = capsys.readouterr() @@ -396,7 +396,7 @@ def test_list_markets(mocker, markets, capsys): # Test tabular output, no markets found args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--base", "LTC", "--quote", "NONEXISTENT", ] start_list_markets(get_args(args), False) @@ -408,7 +408,7 @@ def test_list_markets(mocker, markets, capsys): # Test --print-json args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--print-json" ] start_list_markets(get_args(args), False) @@ -420,7 +420,7 @@ def test_list_markets(mocker, markets, capsys): # Test --print-csv args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--print-csv" ] start_list_markets(get_args(args), False) @@ -432,7 +432,7 @@ def test_list_markets(mocker, markets, capsys): # Test --one-column args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--one-column" ] start_list_markets(get_args(args), False) @@ -444,7 +444,7 @@ def test_list_markets(mocker, markets, capsys): # Test --one-column args = [ "list-markets", - '--config', 'config_bittrex.json.example', + '--config', 'config_examples/config_bittrex.example.json', "--one-column" ] with pytest.raises(OperationalException, match=r"Cannot get markets.*"): @@ -887,7 +887,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): patched_configuration_load_config_file(mocker, default_conf) args = [ 'test-pairlist', - '-c', 'config_bittrex.json.example' + '-c', 'config_examples/config_bittrex.example.json' ] start_test_pairlist(get_args(args)) @@ -901,7 +901,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): args = [ 'test-pairlist', - '-c', 'config_bittrex.json.example', + '-c', 'config_examples/config_bittrex.example.json', '--one-column', ] start_test_pairlist(get_args(args)) @@ -910,7 +910,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): args = [ 'test-pairlist', - '-c', 'config_bittrex.json.example', + '-c', 'config_examples/config_bittrex.example.json', '--print-json', ] start_test_pairlist(get_args(args)) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 0d81dea28..fd6f162fd 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -172,7 +172,7 @@ def test_download_data_options() -> None: def test_plot_dataframe_options() -> None: args = [ 'plot-dataframe', - '-c', 'config_bittrex.json.example', + '-c', 'config_examples/config_bittrex.example.json', '--indicators1', 'sma10', 'sma100', '--indicators2', 'macd', 'fastd', 'fastk', '--plot-limit', '30', diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 8edd09c5a..34db892b2 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -28,7 +28,7 @@ from tests.conftest import log_has, log_has_re, patched_configuration_load_confi @pytest.fixture(scope="function") def all_conf(): - config_file = Path(__file__).parents[1] / "config_full.json.example" + config_file = Path(__file__).parents[1] / "config_examples/config_full.example.json" conf = load_config_file(str(config_file)) return conf diff --git a/tests/test_main.py b/tests/test_main.py index 3546a3bab..4f769ca30 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -67,12 +67,12 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) - args = ['trade', '-c', 'config_bittrex.json.example'] + args = ['trade', '-c', 'config_examples/config_bittrex.example.json'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has('Using config: config_bittrex.json.example ...', caplog) + assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog) assert log_has('Fatal exception!', caplog) @@ -85,12 +85,12 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.wallets.Wallets.update', MagicMock()) mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) - args = ['trade', '-c', 'config_bittrex.json.example'] + args = ['trade', '-c', 'config_examples/config_bittrex.example.json'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has('Using config: config_bittrex.json.example ...', caplog) + assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog) assert log_has('SIGINT received, aborting ...', caplog) @@ -106,12 +106,12 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) - args = ['trade', '-c', 'config_bittrex.json.example'] + args = ['trade', '-c', 'config_examples/config_bittrex.example.json'] # Test Main + the KeyboardInterrupt exception with pytest.raises(SystemExit): main(args) - assert log_has('Using config: config_bittrex.json.example ...', caplog) + assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog) assert log_has('Oh snap!', caplog) @@ -157,12 +157,12 @@ def test_main_reload_config(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) - args = Arguments(['trade', '-c', 'config_bittrex.json.example']).get_parsed_arg() + args = Arguments(['trade', '-c', 'config_examples/config_bittrex.example.json']).get_parsed_arg() worker = Worker(args=args, config=default_conf) with pytest.raises(SystemExit): - main(['trade', '-c', 'config_bittrex.json.example']) + main(['trade', '-c', 'config_examples/config_bittrex.example.json']) - assert log_has('Using config: config_bittrex.json.example ...', caplog) + assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog) assert worker_mock.call_count == 4 assert reconfigure_mock.call_count == 1 assert isinstance(worker.freqtrade, FreqtradeBot) @@ -180,7 +180,7 @@ def test_reconfigure(mocker, default_conf) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) - args = Arguments(['trade', '-c', 'config_bittrex.json.example']).get_parsed_arg() + args = Arguments(['trade', '-c', 'config_examples/config_bittrex.example.json']).get_parsed_arg() worker = Worker(args=args, config=default_conf) freqtrade = worker.freqtrade diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 20f159e3a..ecadc3f8b 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -364,7 +364,7 @@ def test_start_plot_dataframe(mocker): aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock()) args = [ "plot-dataframe", - "--config", "config_bittrex.json.example", + "--config", "config_examples/config_bittrex.example.json", "--pairs", "ETH/BTC" ] start_plot_dataframe(get_args(args)) @@ -408,7 +408,7 @@ def test_start_plot_profit(mocker): aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock()) args = [ "plot-profit", - "--config", "config_bittrex.json.example", + "--config", "config_examples/config_bittrex.example.json", "--pairs", "ETH/BTC" ] start_plot_profit(get_args(args)) From 288c92301f74abca72611c77c9ebc65e9f298a9a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Jul 2021 06:50:14 +0200 Subject: [PATCH 111/519] Improve docs wording --- docs/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 73b0e9c9a..6207770b8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -165,7 +165,7 @@ Values set in the configuration file always overwrite values set in the strategy ### Configuring amount per trade -There are several methods to configure how much of the stake currency the bot will use to enter a trade. All methods respect the [available balance configuration](#available-balance) as explained below. +There are several methods to configure how much of the stake currency the bot will use to enter a trade. All methods respect the [available balance configuration](#tradable-balance) as explained below. #### Minimum trade stake @@ -184,7 +184,7 @@ To limit this calculation in case of large stoploss values, the calculated minim !!! Warning Since the limits on exchanges are usually stable and are not updated often, some pairs can show pretty high minimum limits, simply because the price increased a lot since the last limit adjustment by the exchange. -#### Available balance +#### Tradable balance By default, the bot assumes that the `complete amount - 1%` is at it's disposal, and when using [dynamic stake amount](#dynamic-stake-amount), it will split the complete balance into `max_open_trades` buckets per trade. Freqtrade will reserve 1% for eventual fees when entering a trade and will therefore not touch that by default. From f5c47767cb509836ab7fcd1fd87d629172d21bbb Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Jul 2021 20:51:42 +0200 Subject: [PATCH 112/519] Provide available capital to api --- freqtrade/rpc/api_server/api_schemas.py | 1 + freqtrade/rpc/rpc.py | 1 + 2 files changed, 2 insertions(+) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index a0f1c05a6..40101e609 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -115,6 +115,7 @@ class ShowConfig(BaseModel): dry_run: bool stake_currency: str stake_amount: Union[float, str] + available_capital: Optional[float] stake_currency_decimals: int max_open_trades: int minimal_roi: Dict[str, Any] diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index e0aaefe50..e173673be 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -106,6 +106,7 @@ class RPC: 'stake_currency': config['stake_currency'], 'stake_currency_decimals': decimals_per_coin(config['stake_currency']), 'stake_amount': config['stake_amount'], + 'available_capital': config.get('available_capital'), 'max_open_trades': (config['max_open_trades'] if config['max_open_trades'] != float('inf') else -1), 'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {}, From c9c7f84e8c2726b2d423f70ca1e569b21487a084 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Jul 2021 20:55:11 +0200 Subject: [PATCH 113/519] Calculate relative profit based on assumed starting balance --- freqtrade/rpc/api_server/api_schemas.py | 4 ++++ freqtrade/rpc/rpc.py | 17 +++++++++++++---- freqtrade/rpc/telegram.py | 8 ++++---- freqtrade/wallets.py | 13 +++++++++++++ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 40101e609..d3eec9be6 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -67,12 +67,16 @@ class Profit(BaseModel): profit_closed_ratio_mean: float profit_closed_percent_sum: float profit_closed_ratio_sum: float + profit_closed_percent: float + profit_closed_ratio: float profit_closed_fiat: float profit_all_coin: float profit_all_percent_mean: float profit_all_ratio_mean: float profit_all_percent_sum: float profit_all_ratio_sum: float + profit_all_percent: float + profit_all_ratio: float profit_all_fiat: float trade_count: int closed_trade_count: int diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index e173673be..d6eaf7ca6 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -397,7 +397,12 @@ class RPC: profit_all_coin_sum = round(sum(profit_all_coin), 8) profit_all_ratio_mean = float(mean(profit_all_ratio) if profit_all_ratio else 0.0) + # Doing the sum is not right - overall profit needs to be based on initial capital profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0 + starting_balance = self._freqtrade.wallets.get_starting_balance() + profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance + profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance + profit_all_fiat = self._fiat_converter.convert_amount( profit_all_coin_sum, stake_currency, @@ -411,14 +416,18 @@ class RPC: 'profit_closed_coin': profit_closed_coin_sum, 'profit_closed_percent_mean': round(profit_closed_ratio_mean * 100, 2), 'profit_closed_ratio_mean': profit_closed_ratio_mean, - 'profit_closed_percent_sum': round(profit_closed_ratio_sum * 100, 2), - 'profit_closed_ratio_sum': profit_closed_ratio_sum, + 'profit_closed_percent_sum': round(profit_closed_ratio_sum * 100, 2), # Deprecated + 'profit_closed_ratio_sum': profit_closed_ratio_sum, # Deprecated + 'profit_closed_ratio': profit_closed_ratio_fromstart, + 'profit_closed_percent': round(profit_closed_ratio_fromstart * 100, 2), 'profit_closed_fiat': profit_closed_fiat, 'profit_all_coin': profit_all_coin_sum, 'profit_all_percent_mean': round(profit_all_ratio_mean * 100, 2), 'profit_all_ratio_mean': profit_all_ratio_mean, - 'profit_all_percent_sum': round(profit_all_ratio_sum * 100, 2), - 'profit_all_ratio_sum': profit_all_ratio_sum, + 'profit_all_percent_sum': round(profit_all_ratio_sum * 100, 2), # Deprecated + 'profit_all_ratio_sum': profit_all_ratio_sum, # Deprecated + 'profit_all_ratio': profit_all_ratio_fromstart, + 'profit_all_percent': round(profit_all_ratio_fromstart * 100, 2), 'profit_all_fiat': profit_all_fiat, 'trade_count': len(trades), 'closed_trade_count': len([t for t in trades if not t.is_open]), diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 319a6c9c0..263a3fc6d 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -494,11 +494,11 @@ class Telegram(RPCHandler): start_date) profit_closed_coin = stats['profit_closed_coin'] profit_closed_percent_mean = stats['profit_closed_percent_mean'] - profit_closed_percent_sum = stats['profit_closed_percent_sum'] + profit_closed_percent = stats['profit_closed_percent'] profit_closed_fiat = stats['profit_closed_fiat'] profit_all_coin = stats['profit_all_coin'] profit_all_percent_mean = stats['profit_all_percent_mean'] - profit_all_percent_sum = stats['profit_all_percent_sum'] + profit_all_percent = stats['profit_all_percent'] profit_all_fiat = stats['profit_all_fiat'] trade_count = stats['trade_count'] first_trade_date = stats['first_trade_date'] @@ -514,7 +514,7 @@ class Telegram(RPCHandler): markdown_msg = ("*ROI:* Closed trades\n" f"∙ `{round_coin_value(profit_closed_coin, stake_cur)} " f"({profit_closed_percent_mean:.2f}%) " - f"({profit_closed_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" + f"({profit_closed_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" f"∙ `{round_coin_value(profit_closed_fiat, fiat_disp_cur)}`\n") else: markdown_msg = "`No closed trade` \n" @@ -523,7 +523,7 @@ class Telegram(RPCHandler): f"*ROI:* All trades\n" f"∙ `{round_coin_value(profit_all_coin, stake_cur)} " f"({profit_all_percent_mean:.2f}%) " - f"({profit_all_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" + f"({profit_all_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n" f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n" f"*Total Trade Count:* `{trade_count}`\n" f"*{'First Trade opened' if not timescale else 'Showing Profit since'}:* " diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 0048dbf48..e51a01afc 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -129,6 +129,19 @@ class Wallets: def get_all_balances(self) -> Dict[str, Any]: return self._wallets + def get_starting_balance(self) -> float: + """ + Retrieves starting balance - based on either available capital, + or by using current balance subtracting + """ + if "available_capital" in self._config: + return self._config['available_capital'] + else: + tot_profit = Trade.get_total_closed_profit() + open_stakes = Trade.total_open_trades_stakes() + available_balance = self.get_free(self._config['stake_currency']) + return available_balance - tot_profit + open_stakes + def get_total_stake_amount(self): """ Return the total currently available balance in stake currency, including tied up stake and From 02d716a8be1963d7741a20d0c435a307793ba006 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Jul 2021 20:59:47 +0200 Subject: [PATCH 114/519] Fix api test --- tests/rpc/test_rpc_apiserver.py | 4 ++++ tests/rpc/test_rpc_telegram.py | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 89da68da7..4dea2bbfa 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -677,12 +677,16 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): 'profit_all_ratio_mean': -0.6641100666666667, 'profit_all_percent_sum': -398.47, 'profit_all_ratio_sum': -3.9846604, + 'profit_all_percent': -4.41, + 'profit_all_ratio': -0.044063014216106644, 'profit_closed_coin': 0.00073913, 'profit_closed_fiat': 9.124559849999999, 'profit_closed_ratio_mean': 0.0075, 'profit_closed_percent_mean': 0.75, 'profit_closed_ratio_sum': 0.015, 'profit_closed_percent_sum': 1.5, + 'profit_closed_ratio': 7.391275897987988e-07, + 'profit_closed_percent': 0.0, 'trade_count': 6, 'closed_trade_count': 2, 'winning_trades': 2, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 4784f1172..cccc78117 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -452,7 +452,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, assert msg_mock.call_count == 1 assert 'No closed trade' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] - assert ('∙ `-0.00000500 BTC (-0.50%) (-0.5 \N{GREEK CAPITAL LETTER SIGMA}%)`' + mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=0.01) + assert ('∙ `-0.00000500 BTC (-0.50%) (-0.0 \N{GREEK CAPITAL LETTER SIGMA}%)`' in msg_mock.call_args_list[-1][0][0]) msg_mock.reset_mock() @@ -466,11 +467,11 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0] - assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`' + assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`' in msg_mock.call_args_list[-1][0][0]) assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] - assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`' + assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`' in msg_mock.call_args_list[-1][0][0]) assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] From 697bf92f6f10d6419982e8e45a1c83039be6a4cd Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 14 Jul 2021 21:10:25 +0200 Subject: [PATCH 115/519] Add test for get_starting_balance method --- tests/test_wallets.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_wallets.py b/tests/test_wallets.py index a44ca243b..25f2ad89f 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -197,3 +197,28 @@ def test__validate_stake_amount(mocker, default_conf, return_value=max_stake_amount) res = freqtrade.wallets._validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount) assert res == expected + + +@pytest.mark.parametrize('available_capital,closed_profit,open_stakes,free,expected', [ + (None, 10, 100, 910, 1000), + (None, 0, 0, 2500, 2500), + (None, 500, 0, 2500, 2000), + (None, 500, 0, 2500, 2000), + # Only available balance matters when it's set. + (100, 0, 0, 0, 100), + (1000, 0, 2, 5, 1000), + (1235, 2250, 2, 5, 1235), +]) +def test_get_starting_balance(mocker, default_conf, available_capital, closed_profit, + open_stakes, free, expected): + if available_capital: + default_conf['available_capital'] = available_capital + mocker.patch("freqtrade.persistence.models.Trade.get_total_closed_profit", + return_value=closed_profit) + mocker.patch("freqtrade.persistence.models.Trade.total_open_trades_stakes", + return_value=open_stakes) + mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=free) + + freqtrade = get_patched_freqtradebot(mocker, default_conf) + + assert freqtrade.wallets.get_starting_balance() == expected From cde041f7024fcac948418cd3b1d87adad7f3435a Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 14 Jul 2021 19:20:12 -0600 Subject: [PATCH 116/519] install hdf5 and c-blosc on mac if using python3.9 --- setup.sh | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/setup.sh b/setup.sh index 631c31df2..ee3fee0a3 100755 --- a/setup.sh +++ b/setup.sh @@ -17,6 +17,15 @@ function check_installed_python() { exit 2 fi + which python3.9 + if [ $? -eq 0 ]; then + echo "using Python 3.9" + PYTHON=python3.9 + check_installed_pip + return + fi + + which python3.8 if [ $? -eq 0 ]; then echo "using Python 3.8" @@ -25,13 +34,6 @@ function check_installed_python() { return fi - which python3.9 - if [ $? -eq 0 ]; then - echo "using Python 3.9" - PYTHON=python3.9 - check_installed_pip - return - fi which python3.7 if [ $? -eq 0 ]; then @@ -122,6 +124,25 @@ function install_talib() { cd .. } +function install_mac_newer_python_dependencies() { + + if [ ! $(brew --prefix --installed hdf5 2>/dev/null) ] + then + echo "-------------------------" + echo "Installing hdf5" + echo "-------------------------" + brew install hdf5 + fi + + if [ ! $(brew --prefix --installed c-blosc 2>/dev/null) ] + then + echo "-------------------------" + echo "Installing c-blosc" + echo "-------------------------" + brew install c-blosc + fi +} + # Install bot MacOS function install_macos() { if [ ! -x "$(command -v brew)" ] @@ -131,6 +152,12 @@ function install_macos() { echo "-------------------------" /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" fi + #Gets number after decimal in python version + version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g' ) + + if [[ $version -ge 9 ]]; then #Checks if python version >= 3.9 + install_mac_newer_python_dependencies + fi install_talib test_and_fix_python_on_mac } From 74d7497a47b3fe9ddc40895a3b3eb8b54d97f153 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 14 Jul 2021 19:25:51 -0600 Subject: [PATCH 117/519] Setup script tries to install python3.9 instead of 3.8 with this fix, python versions are also checked for in a loop instead of copy and pasted code --- setup.sh | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/setup.sh b/setup.sh index ee3fee0a3..1393b012a 100755 --- a/setup.sh +++ b/setup.sh @@ -17,37 +17,17 @@ function check_installed_python() { exit 2 fi - which python3.9 - if [ $? -eq 0 ]; then - echo "using Python 3.9" - PYTHON=python3.9 - check_installed_pip - return - fi + for v in {9..7}; do + PYTHON="python3.${v}" + which $PYTHON + if [ $? -eq 0 ]; then + check_installed_pip + return + fi + done - - which python3.8 - if [ $? -eq 0 ]; then - echo "using Python 3.8" - PYTHON=python3.8 - check_installed_pip - return - fi - - - which python3.7 - if [ $? -eq 0 ]; then - echo "using Python 3.7" - PYTHON=python3.7 - check_installed_pip - return - fi - - - if [ -z ${PYTHON} ]; then - echo "No usable python found. Please make sure to have python3.7 or newer installed" - exit 1 - fi + echo "No usable python found. Please make sure to have python3.7 or newer installed" + exit 1 } function updateenv() { From 65ce7c983888b43b9a871125f2f33987bd181724 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 14 Jul 2021 20:01:43 -0600 Subject: [PATCH 118/519] Added echo python3.* line back in --- setup.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.sh b/setup.sh index 1393b012a..78cb499a8 100755 --- a/setup.sh +++ b/setup.sh @@ -21,6 +21,8 @@ function check_installed_python() { PYTHON="python3.${v}" which $PYTHON if [ $? -eq 0 ]; then + echo "using ${PYTHON}" + check_installed_pip return fi From 07e3f824006be83a83fa37c29cf05a72ba3b353a Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 15 Jul 2021 01:03:32 -0600 Subject: [PATCH 119/519] Changed to python3.8 installing first, removed test_and_fix_python_on_mac --- setup.sh | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/setup.sh b/setup.sh index 78cb499a8..3bcbfc48d 100755 --- a/setup.sh +++ b/setup.sh @@ -17,7 +17,8 @@ function check_installed_python() { exit 2 fi - for v in {9..7}; do + for v in 8 9 7 + do PYTHON="python3.${v}" which $PYTHON if [ $? -eq 0 ]; then @@ -141,7 +142,6 @@ function install_macos() { install_mac_newer_python_dependencies fi install_talib - test_and_fix_python_on_mac } # Install bot Debian_ubuntu @@ -198,19 +198,6 @@ function reset() { updateenv } -function test_and_fix_python_on_mac() { - - if ! [ -x "$(command -v python3.6)" ] - then - echo "-------------------------" - echo "Fixing Python" - echo "-------------------------" - echo "Python 3.6 is not linked in your system. Fixing it..." - brew link --overwrite python - echo - fi -} - function config() { echo "-------------------------" From 2928ee22ce4bc4dc8b81107fb00d537ac7924c54 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 15 Jul 2021 14:32:23 +0000 Subject: [PATCH 120/519] Remove compose file for devcontainer --- .devcontainer/devcontainer.json | 33 ++++++++++++++------------------ .devcontainer/docker-compose.yml | 24 ----------------------- 2 files changed, 14 insertions(+), 43 deletions(-) delete mode 100644 .devcontainer/docker-compose.yml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1882e3bdf..41b8475ec 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,11 +1,20 @@ { "name": "freqtrade Develop", - - "dockerComposeFile": [ - "docker-compose.yml" + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ + 8080 ], + "mounts": [ + "source=freqtrade-bashhistory,target=/home/ftuser/commandhistory,type=volume" + ], + // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "ftuser", - "service": "ft_vscode", + "postCreateCommand": "freqtrade create-userdir --userdir user_data/", "workspaceFolder": "/freqtrade/", @@ -25,20 +34,6 @@ "ms-python.vscode-pylance", "davidanson.vscode-markdownlint", "ms-azuretools.vscode-docker", + "vscode-icons-team.vscode-icons", ], - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Uncomment the next line if you want start specific services in your Docker Compose config. - // "runServices": [], - - // Uncomment the next line if you want to keep your containers running after VS Code shuts down. - // "shutdownAction": "none", - - // Uncomment the next line to run commands after the container is created - for example installing curl. - // "postCreateCommand": "sudo apt-get update && apt-get install -y git", - - // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "ftuser" } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml deleted file mode 100644 index 20ec247d1..000000000 --- a/.devcontainer/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -version: '3' -services: - ft_vscode: - build: - context: .. - dockerfile: ".devcontainer/Dockerfile" - volumes: - # Allow git usage within container - - "${HOME}/.ssh:/home/ftuser/.ssh:ro" - - "${HOME}/.gitconfig:/home/ftuser/.gitconfig:ro" - - ..:/freqtrade:cached - # Persist bash-history - - freqtrade-vscode-server:/home/ftuser/.vscode-server - - freqtrade-bashhistory:/home/ftuser/commandhistory - # Expose API port - ports: - - "127.0.0.1:8080:8080" - command: /bin/sh -c "while sleep 1000; do :; done" - - -volumes: - freqtrade-vscode-server: - freqtrade-bashhistory: From 2e95df4d8dde6ff7ff4be9ed41af9da742ba3b18 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 15 Jul 2021 07:11:44 +0200 Subject: [PATCH 121/519] Update docs for /profit output --- docs/telegram-usage.md | 8 ++++++-- freqtrade/rpc/rpc.py | 8 ++++---- tests/test_wallets.py | 2 ++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index f5d9744b4..b020b00db 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -245,10 +245,10 @@ current max Return a summary of your profit/loss and performance. > **ROI:** Close trades -> ∙ `0.00485701 BTC (258.45%)` +> ∙ `0.00485701 BTC (2.2%) (15.2 Σ%)` > ∙ `62.968 USD` > **ROI:** All trades -> ∙ `0.00255280 BTC (143.43%)` +> ∙ `0.00255280 BTC (1.5%) (6.43 Σ%)` > ∙ `33.095 EUR` > > **Total Trade Count:** `138` @@ -257,6 +257,10 @@ Return a summary of your profit/loss and performance. > **Avg. Duration:** `2:33:45` > **Best Performing:** `PAY/BTC: 50.23%` +The relative profit of `1.2%` is the average profit per trade. +The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`. +Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits. + ### /forcesell > **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)` diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index d6eaf7ca6..db89443bf 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -416,16 +416,16 @@ class RPC: 'profit_closed_coin': profit_closed_coin_sum, 'profit_closed_percent_mean': round(profit_closed_ratio_mean * 100, 2), 'profit_closed_ratio_mean': profit_closed_ratio_mean, - 'profit_closed_percent_sum': round(profit_closed_ratio_sum * 100, 2), # Deprecated - 'profit_closed_ratio_sum': profit_closed_ratio_sum, # Deprecated + 'profit_closed_percent_sum': round(profit_closed_ratio_sum * 100, 2), + 'profit_closed_ratio_sum': profit_closed_ratio_sum, 'profit_closed_ratio': profit_closed_ratio_fromstart, 'profit_closed_percent': round(profit_closed_ratio_fromstart * 100, 2), 'profit_closed_fiat': profit_closed_fiat, 'profit_all_coin': profit_all_coin_sum, 'profit_all_percent_mean': round(profit_all_ratio_mean * 100, 2), 'profit_all_ratio_mean': profit_all_ratio_mean, - 'profit_all_percent_sum': round(profit_all_ratio_sum * 100, 2), # Deprecated - 'profit_all_ratio_sum': profit_all_ratio_sum, # Deprecated + 'profit_all_percent_sum': round(profit_all_ratio_sum * 100, 2), + 'profit_all_ratio_sum': profit_all_ratio_sum, 'profit_all_ratio': profit_all_ratio_fromstart, 'profit_all_percent': round(profit_all_ratio_fromstart * 100, 2), 'profit_all_fiat': profit_all_fiat, diff --git a/tests/test_wallets.py b/tests/test_wallets.py index 25f2ad89f..64db3b9cd 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -204,10 +204,12 @@ def test__validate_stake_amount(mocker, default_conf, (None, 0, 0, 2500, 2500), (None, 500, 0, 2500, 2000), (None, 500, 0, 2500, 2000), + (None, -70, 0, 1930, 2000), # Only available balance matters when it's set. (100, 0, 0, 0, 100), (1000, 0, 2, 5, 1000), (1235, 2250, 2, 5, 1235), + (1235, -2250, 2, 5, 1235), ]) def test_get_starting_balance(mocker, default_conf, available_capital, closed_profit, open_stakes, free, expected): From b7dc2989e70588c988360fd7c345ba6b9afa8721 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 16 Jul 2021 02:03:25 -0600 Subject: [PATCH 122/519] flake8 adjustments --- tests/test_main.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 4f769ca30..59a5bb0f7 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -157,7 +157,11 @@ def test_main_reload_config(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) - args = Arguments(['trade', '-c', 'config_examples/config_bittrex.example.json']).get_parsed_arg() + args = Arguments([ + 'trade', + '-c', + 'config_examples/config_bittrex.example.json' + ]).get_parsed_arg() worker = Worker(args=args, config=default_conf) with pytest.raises(SystemExit): main(['trade', '-c', 'config_examples/config_bittrex.example.json']) @@ -180,7 +184,11 @@ def test_reconfigure(mocker, default_conf) -> None: mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.init_db', MagicMock()) - args = Arguments(['trade', '-c', 'config_examples/config_bittrex.example.json']).get_parsed_arg() + args = Arguments([ + 'trade', + '-c', + 'config_examples/config_bittrex.example.json' + ]).get_parsed_arg() worker = Worker(args=args, config=default_conf) freqtrade = worker.freqtrade From d652e6fcc45ef7cac219f1e9275b16e5ede65e02 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 16 Jul 2021 19:57:39 +0200 Subject: [PATCH 123/519] Don't log from wallet in backtest mode --- freqtrade/wallets.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index e51a01afc..237c1dc2c 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -246,18 +246,21 @@ class Wallets: max_stake_amount = self.get_available_stake_amount() if min_stake_amount > max_stake_amount: - logger.warning("Minimum stake amount > available balance.") + if self._log: + logger.warning("Minimum stake amount > available balance.") return 0 if min_stake_amount is not None and stake_amount < min_stake_amount: stake_amount = min_stake_amount - logger.info( - f"Stake amount for pair {pair} is too small ({stake_amount} < {min_stake_amount}), " - f"adjusting to {min_stake_amount}." - ) + if self._log: + logger.info( + f"Stake amount for pair {pair} is too small " + f"({stake_amount} < {min_stake_amount}), adjusting to {min_stake_amount}." + ) if stake_amount > max_stake_amount: stake_amount = max_stake_amount - logger.info( - f"Stake amount for pair {pair} is too big ({stake_amount} > {max_stake_amount}), " - f"adjusting to {max_stake_amount}." - ) + if self._log: + logger.info( + f"Stake amount for pair {pair} is too big " + f"({stake_amount} > {max_stake_amount}), adjusting to {max_stake_amount}." + ) return stake_amount From 53a8c693b8eb7db0f7bca6e3608544f4cd687a32 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys <19151258+rokups@users.noreply.github.com> Date: Sat, 17 Jul 2021 09:21:03 +0300 Subject: [PATCH 124/519] Honor skip_pair_validation setting when downloading pairs. --- freqtrade/commands/data_commands.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index 3877e0801..141e85f14 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -48,7 +48,8 @@ def start_download_data(args: Dict[str, Any]) -> None: # Init exchange exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False) # Manual validations of relevant settings - exchange.validate_pairs(config['pairs']) + if not config['exchange'].get('skip_pair_validation', False): + exchange.validate_pairs(config['pairs']) expanded_pairs = expand_pairlist(config['pairs'], list(exchange.markets)) logger.info(f"About to download pairs: {expanded_pairs}, " From 7c27525bd8599fdad582d24e90bc8bee61688834 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 17 Jul 2021 21:58:54 -0600 Subject: [PATCH 125/519] Merge get_buy_rate and get_sell_rate --- freqtrade/exchange/exchange.py | 97 +++++++++++---------------------- freqtrade/freqtradebot.py | 11 ++-- freqtrade/rpc/rpc.py | 20 ++++--- tests/exchange/test_exchange.py | 26 ++++----- tests/rpc/test_rpc.py | 6 +- tests/rpc/test_rpc_apiserver.py | 2 +- tests/test_freqtradebot.py | 20 +++---- 7 files changed, 77 insertions(+), 105 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 42e86db3e..18060cb57 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -551,7 +551,7 @@ class Exchange: amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent', DEFAULT_AMOUNT_RESERVE_PERCENT) amount_reserve_percent = ( - amount_reserve_percent / (1 - abs(stoploss)) if abs(stoploss) != 1 else 1.5 + amount_reserve_percent / (1 - abs(stoploss)) if abs(stoploss) != 1 else 1.5 ) # it should not be more than 50% amount_reserve_percent = max(min(amount_reserve_percent, 1.5), 1) @@ -999,94 +999,61 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def get_buy_rate(self, pair: str, refresh: bool) -> float: + def get_rate(self, pair: str, refresh: bool, side: str = "buy") -> float: """ Calculates bid target between current ask price and last price :param pair: Pair to get rate for :param refresh: allow cached data + :param side: "buy" or "sell" :return: float: Price :raises PricingError if orderbook price could not be determined. """ + cache_rate: TTLCache = self._buy_rate_cache if side == "buy" else self._sell_rate_cache + [strat_name, name] = ['bid_strategy', 'Buy'] if side == "buy" else ['ask_strategy', 'Sell'] + if not refresh: - rate = self._buy_rate_cache.get(pair) + rate = cache_rate.get(pair) # Check if cache has been invalidated if rate: - logger.debug(f"Using cached buy rate for {pair}.") + logger.debug(f"Using cached {side} rate for {pair}.") return rate - bid_strategy = self._config.get('bid_strategy', {}) - if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False): + strategy = self._config.get(strat_name, {}) - order_book_top = bid_strategy.get('order_book_top', 1) + if strategy.get('use_order_book', False) and ('use_order_book' in strategy): + + order_book_top = strategy.get('order_book_top', 1) order_book = self.fetch_l2_order_book(pair, order_book_top) logger.debug('order_book %s', order_book) # top 1 = index 0 try: - rate_from_l2 = order_book[f"{bid_strategy['price_side']}s"][order_book_top - 1][0] + rate = order_book[f"{strategy['price_side']}s"][order_book_top - 1][0] except (IndexError, KeyError) as e: logger.warning( - "Buy Price from orderbook could not be determined." - f"Orderbook: {order_book}" - ) - raise PricingError from e - logger.info(f"Buy price from orderbook {bid_strategy['price_side'].capitalize()} side " - f"- top {order_book_top} order book buy rate {rate_from_l2:.8f}") - used_rate = rate_from_l2 - else: - logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price") - ticker = self.fetch_ticker(pair) - ticker_rate = ticker[bid_strategy['price_side']] - if ticker['last'] and ticker_rate > ticker['last']: - balance = bid_strategy['ask_last_balance'] - ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) - used_rate = ticker_rate - - self._buy_rate_cache[pair] = used_rate - - return used_rate - - def get_sell_rate(self, pair: str, refresh: bool) -> float: - """ - Get sell rate - either using ticker bid or first bid based on orderbook - or remain static in any other case since it's not updating. - :param pair: Pair to get rate for - :param refresh: allow cached data - :return: Bid rate - :raises PricingError if price could not be determined. - """ - if not refresh: - rate = self._sell_rate_cache.get(pair) - # Check if cache has been invalidated - if rate: - logger.debug(f"Using cached sell rate for {pair}.") - return rate - - ask_strategy = self._config.get('ask_strategy', {}) - if ask_strategy.get('use_order_book', False): - logger.debug( - f"Getting price from order book {ask_strategy['price_side'].capitalize()} side." - ) - order_book_top = ask_strategy.get('order_book_top', 1) - order_book = self.fetch_l2_order_book(pair, order_book_top) - try: - rate = order_book[f"{ask_strategy['price_side']}s"][order_book_top - 1][0] - except (IndexError, KeyError) as e: - logger.warning( - f"Sell Price at location {order_book_top} from orderbook could not be " + f"{name} Price at location {order_book_top} from orderbook could not be " f"determined. Orderbook: {order_book}" ) raise PricingError from e + + logger.info(f"{name} price from orderbook {strategy['price_side'].capitalize()}" + f"side - top {order_book_top} order book {side} rate {rate:.8f}") else: + logger.info(f"Using Last {strategy['price_side'].capitalize()} / Last Price") ticker = self.fetch_ticker(pair) - ticker_rate = ticker[ask_strategy['price_side']] - if ticker['last'] and ticker_rate < ticker['last']: - balance = ask_strategy.get('bid_last_balance', 0.0) - ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last']) + ticker_rate = ticker[strategy['price_side']] + if ticker['last']: + if side == 'buy' and ticker_rate > ticker['last']: + balance = strategy['ask_last_balance'] + ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) + elif side == 'sell' and ticker_rate < ticker['last']: + balance = strategy.get('bid_last_balance', 0.0) + ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last']) rate = ticker_rate - if rate is None: - raise PricingError(f"Sell-Rate for {pair} was empty.") - self._sell_rate_cache[pair] = rate + if rate is None and side == "sell": + raise PricingError(f"{name}-Rate for {pair} was empty.") + cache_rate[pair] = rate + return rate # Fee handling @@ -1318,8 +1285,8 @@ class Exchange: self._pairs_last_refresh_time[(pair, timeframe)] = ticks[-1][0] // 1000 # keeping parsed dataframe in cache ohlcv_df = ohlcv_to_dataframe( - ticks, timeframe, pair=pair, fill_missing=True, - drop_incomplete=self._ohlcv_partial_candle) + ticks, timeframe, pair=pair, fill_missing=True, + drop_incomplete=self._ohlcv_partial_candle) results_df[(pair, timeframe)] = ohlcv_df if cache: self._klines[(pair, timeframe)] = ohlcv_df diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5ef109387..d430dbc48 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -475,7 +475,7 @@ class FreqtradeBot(LoggingMixin): buy_limit_requested = price else: # Calculate price - buy_limit_requested = self.exchange.get_buy_rate(pair, True) + buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy") if not buy_limit_requested: raise PricingError('Could not determine buy price.') @@ -609,7 +609,7 @@ class FreqtradeBot(LoggingMixin): """ Sends rpc notification when a buy cancel occurred. """ - current_rate = self.exchange.get_buy_rate(trade.pair, False) + current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="buy") msg = { 'trade_id': trade.id, @@ -695,7 +695,7 @@ class FreqtradeBot(LoggingMixin): (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) logger.debug('checking sell') - sell_rate = self.exchange.get_sell_rate(trade.pair, True) + sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") if self._check_and_execute_sell(trade, sell_rate, buy, sell): return True @@ -1132,7 +1132,8 @@ class FreqtradeBot(LoggingMixin): profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested profit_trade = trade.calc_profit(rate=profit_rate) # Use cached rates here - it was updated seconds ago. - current_rate = self.exchange.get_sell_rate(trade.pair, False) if not fill else None + current_rate = self.exchange.get_rate( + trade.pair, refresh=False, side="sell") if not fill else None profit_ratio = trade.calc_profit_ratio(profit_rate) gain = "profit" if profit_ratio > 0 else "loss" @@ -1177,7 +1178,7 @@ class FreqtradeBot(LoggingMixin): profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested profit_trade = trade.calc_profit(rate=profit_rate) - current_rate = self.exchange.get_sell_rate(trade.pair, False) + current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="sell") profit_ratio = trade.calc_profit_ratio(profit_rate) gain = "profit" if profit_ratio > 0 else "loss" diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index db89443bf..d1d44ad16 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -154,7 +154,8 @@ class RPC: # calculate profit and send message to user if trade.is_open: try: - current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) + current_rate = self._freqtrade.exchange.get_rate( + trade.pair, refresh=False, side="sell") except (ExchangeError, PricingError): current_rate = NAN else: @@ -213,7 +214,8 @@ class RPC: for trade in trades: # calculate profit and send message to user try: - current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) + current_rate = self._freqtrade.exchange.get_rate( + trade.pair, refresh=False, side="sell") except (PricingError, ExchangeError): current_rate = NAN trade_percent = (100 * trade.calc_profit_ratio(current_rate)) @@ -272,10 +274,10 @@ class RPC: 'date': key, 'abs_profit': value["amount"], 'fiat_value': self._fiat_converter.convert_amount( - value['amount'], - stake_currency, - fiat_display_currency - ) if self._fiat_converter else 0, + value['amount'], + stake_currency, + fiat_display_currency + ) if self._fiat_converter else 0, 'trade_count': value["trades"], } for key, value in profit_days.items() @@ -372,7 +374,8 @@ class RPC: else: # Get current rate try: - current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) + current_rate = self._freqtrade.exchange.get_rate( + trade.pair, refresh=False, side="sell") except (PricingError, ExchangeError): current_rate = NAN profit_ratio = trade.calc_profit_ratio(rate=current_rate) @@ -551,7 +554,8 @@ class RPC: if not fully_canceled: # Get current rate and execute sell - current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) + current_rate = self._freqtrade.exchange.get_rate( + trade.pair, refresh=False, side="sell") sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL) self._freqtrade.execute_sell(trade, current_rate, sell_reason) # ---- EOF def _exec_forcesell ---- diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 524dc873c..02adf01c4 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1783,14 +1783,14 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': ask, 'last': last, 'bid': bid}) - assert exchange.get_buy_rate('ETH/BTC', True) == expected + assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected assert not log_has("Using cached buy rate for ETH/BTC.", caplog) - assert exchange.get_buy_rate('ETH/BTC', False) == expected + assert exchange.get_rate('ETH/BTC', refresh=False, side="buy") == expected assert log_has("Using cached buy rate for ETH/BTC.", caplog) # Running a 2nd time with Refresh on! caplog.clear() - assert exchange.get_buy_rate('ETH/BTC', True) == expected + assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected assert not log_has("Using cached buy rate for ETH/BTC.", caplog) @@ -1825,12 +1825,12 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, # Test regular mode exchange = get_patched_exchange(mocker, default_conf) - rate = exchange.get_sell_rate(pair, True) + rate = exchange.get_rate(pair, refresh=True, side="sell") assert not log_has("Using cached sell rate for ETH/BTC.", caplog) assert isinstance(rate, float) assert rate == expected # Use caching - rate = exchange.get_sell_rate(pair, False) + rate = exchange.get_rate(pair, refresh=False, side="sell") assert rate == expected assert log_has("Using cached sell rate for ETH/BTC.", caplog) @@ -1848,11 +1848,11 @@ def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, o pair = "ETH/BTC" mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2) exchange = get_patched_exchange(mocker, default_conf) - rate = exchange.get_sell_rate(pair, True) + rate = exchange.get_rate(pair, refresh=True, side="sell") assert not log_has("Using cached sell rate for ETH/BTC.", caplog) assert isinstance(rate, float) assert rate == expected - rate = exchange.get_sell_rate(pair, False) + rate = exchange.get_rate(pair, refresh=False, side="sell") assert rate == expected assert log_has("Using cached sell rate for ETH/BTC.", caplog) @@ -1868,7 +1868,7 @@ def test_get_sell_rate_orderbook_exception(default_conf, mocker, caplog): return_value={'bids': [[]], 'asks': [[]]}) exchange = get_patched_exchange(mocker, default_conf) with pytest.raises(PricingError): - exchange.get_sell_rate(pair, True) + exchange.get_rate(pair, refresh=True, side="sell") assert log_has_re(r"Sell Price at location 1 from orderbook could not be determined\..*", caplog) @@ -1881,18 +1881,18 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog): return_value={'ask': None, 'bid': 0.12, 'last': None}) exchange = get_patched_exchange(mocker, default_conf) with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."): - exchange.get_sell_rate(pair, True) + exchange.get_rate(pair, refresh=True, side="sell") exchange._config['ask_strategy']['price_side'] = 'bid' - assert exchange.get_sell_rate(pair, True) == 0.12 + assert exchange.get_rate(pair, refresh=True, side="sell") == 0.12 # Reverse sides mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': 0.13, 'bid': None, 'last': None}) with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."): - exchange.get_sell_rate(pair, True) + exchange.get_rate(pair, refresh=True, side="sell") exchange._config['ask_strategy']['price_side'] = 'ask' - assert exchange.get_sell_rate(pair, True) == 0.13 + assert exchange.get_rate(pair, refresh=True, side="sell") == 0.13 def make_fetch_ohlcv_mock(data): @@ -2203,7 +2203,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name): ({'status': 'canceled', 'filled': 10.0}, False), ({'status': 'unknown', 'filled': 10.0}, False), ({'result': 'testest123'}, False), - ]) +]) def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, result): exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) assert exchange.check_order_canceled_empty(order) == result diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 0049e59bb..fad24f9e2 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -109,7 +109,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'exchange': 'binance', } - mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', + mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) results = rpc._rpc_trade_status() assert isnan(results[0]['current_profit']) @@ -217,7 +217,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert '-0.41% (-0.06)' == result[0][3] assert '-0.06' == f'{fiat_profit_sum:.2f}' - mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', + mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') assert 'instantly' == result[0][2] @@ -427,7 +427,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, assert prec_satoshi(stats['best_rate'], 6.2) # Test non-available pair - mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', + mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) assert stats['trade_count'] == 2 diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 4dea2bbfa..d127d0ef8 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -847,7 +847,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'exchange': 'binance', } - mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', + mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) rc = client_get(client, f"{BASE_URI}/status") diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index addf72bbb..4912a2a4d 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -161,7 +161,7 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: (True, 0.0022, 3, 0.5, [0.001, 0.001, 0.0]), (True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]), (True, 0.0022, 3, 1, [0.001, 0.001, 0.0]), - ]) +]) def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_open, amend_last, wallet, max_open, lsamr, expected) -> None: patch_RPCManager(mocker) @@ -784,7 +784,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order buy_mm = MagicMock(return_value=limit_buy_order_open) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - get_buy_rate=buy_rate_mock, + get_rate=buy_rate_mock, fetch_ticker=MagicMock(return_value={ 'bid': 0.00001172, 'ask': 0.00001173, @@ -824,7 +824,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order_open['id'] = '33' fix_price = 0.06 assert freqtrade.execute_buy(pair, stake_amount, fix_price) - # Make sure get_buy_rate wasn't called again + # Make sure get_rate wasn't called again assert buy_rate_mock.call_count == 0 assert buy_mm.call_count == 2 @@ -893,7 +893,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order assert not freqtrade.execute_buy(pair, stake_amount) # Fail to get price... - mocker.patch('freqtrade.exchange.Exchange.get_buy_rate', MagicMock(return_value=0.0)) + mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0)) with pytest.raises(PricingError, match="Could not determine buy price."): freqtrade.execute_buy(pair, stake_amount) @@ -909,7 +909,7 @@ def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) - 'last': 0.00001172 }), buy=MagicMock(return_value=limit_buy_order), - get_buy_rate=MagicMock(return_value=0.11), + get_rate=MagicMock(return_value=0.11), get_min_pair_stake_amount=MagicMock(return_value=1), get_fee=fee, ) @@ -2513,7 +2513,7 @@ def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None: 'freqtrade.exchange.Exchange', cancel_order=cancel_order_mock, ) - mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', return_value=0.245441) + mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.245441) freqtrade = FreqtradeBot(default_conf) @@ -3956,7 +3956,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: """ - test if function get_buy_rate will return the order book price + test if function get_rate will return the order book price instead of the ask rate """ patch_exchange(mocker) @@ -3974,7 +3974,7 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: default_conf['telegram']['enabled'] = False freqtrade = FreqtradeBot(default_conf) - assert freqtrade.exchange.get_buy_rate('ETH/BTC', True) == 0.043935 + assert freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") == 0.043935 assert ticker_mock.call_count == 0 @@ -3996,8 +3996,8 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None freqtrade = FreqtradeBot(default_conf) # orderbook shall be used even if tickers would be lower. with pytest.raises(PricingError): - freqtrade.exchange.get_buy_rate('ETH/BTC', refresh=True) - assert log_has_re(r'Buy Price from orderbook could not be determined.', caplog) + freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") + assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog) def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: From 44df5eeacf727f7ff22a626904bf6e6b8f4aca62 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 18 Jul 2021 00:00:18 -0600 Subject: [PATCH 126/519] Adjusted docstring, and conditional near end of buy_rate --- freqtrade/exchange/exchange.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 18060cb57..65108ca34 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1001,7 +1001,10 @@ class Exchange: def get_rate(self, pair: str, refresh: bool, side: str = "buy") -> float: """ - Calculates bid target between current ask price and last price + Calculates bid/ask target + bid rate - between current ask price and last price + ask rate - either using ticker bid or first bid based on orderbook + or remain static in any other case since it's not updating. :param pair: Pair to get rate for :param refresh: allow cached data :param side: "buy" or "sell" @@ -1050,7 +1053,7 @@ class Exchange: ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last']) rate = ticker_rate - if rate is None and side == "sell": + if rate is None: raise PricingError(f"{name}-Rate for {pair} was empty.") cache_rate[pair] = rate From 51cc9032486fc6c2292e63c0b66942a305870600 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jul 2021 20:29:49 +0200 Subject: [PATCH 127/519] Run ci on selfhosted runner --- .github/workflows/ci.yml | 30 ++++++++++ build_helpers/publish_docker_arm64.sh | 81 +++++++++++++++++++++++++++ build_helpers/publish_docker_multi.sh | 26 +++------ 3 files changed, 118 insertions(+), 19 deletions(-) create mode 100755 build_helpers/publish_docker_arm64.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5890f56a7..454432079 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: - master - stable - develop + - test_build_arm64 tags: release: types: [published] @@ -334,6 +335,7 @@ jobs: runs-on: ubuntu-20.04 if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade' + steps: - uses: actions/checkout@v2 @@ -411,3 +413,31 @@ jobs: channel: '#notifications' url: ${{ secrets.SLACK_WEBHOOK }} + + deploy_arm: + needs: [ deploy ] + # Only run on 64bit machines + runs-on: [self-hosted, linux, ARM64] + if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade' + + steps: + - uses: actions/checkout@v2 + + - name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF##*/})" + id: extract_branch + + - name: Dockerhub login + env: + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + run: | + echo "${DOCKER_PASSWORD}" | docker login --username ${DOCKER_USERNAME} --password-stdin + + - name: Build and test and push docker images + env: + IMAGE_NAME: freqtradeorg/freqtrade + BRANCH_NAME: ${{ steps.extract_branch.outputs.branch }} + run: | + build_helpers/publish_docker_arm64.sh diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh new file mode 100755 index 000000000..710e3065b --- /dev/null +++ b/build_helpers/publish_docker_arm64.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +# Use BuildKit, otherwise building on ARM fails +export DOCKER_BUILDKIT=1 + +# Replace / with _ to create a valid tag +TAG=$(echo "${BRANCH_NAME}" | sed -e "s/\//_/g") +TAG_PLOT=${TAG}_plot +TAG_PI="${TAG}_pi" + +TAG_ARM=${TAG}_arm +TAG_PLOT_ARM=${TAG_PLOT}_arm +CACHE_IMAGE=freqtradeorg/freqtrade_cache + +echo "Running for ${TAG}" + +# Add commit and commit_message to docker container +echo "${GITHUB_SHA}" > freqtrade_commit + +if [ "${GITHUB_EVENT_NAME}" = "schedule" ]; then + echo "event ${GITHUB_EVENT_NAME}: full rebuild - skipping cache" + # Build regular image + docker build -t freqtrade:${TAG_ARM} . + +else + echo "event ${GITHUB_EVENT_NAME}: building with cache" + # Build regular image + docker pull ${IMAGE_NAME}:${TAG_ARM} + docker build --cache-from ${IMAGE_NAME}:${TAG_ARM} -t freqtrade:${TAG_ARM} . + +fi + +if [ $? -ne 0 ]; then + echo "failed building multiarch images" + return 1 +fi +# Tag image for upload and next build step +docker tag freqtrade:$TAG_ARM ${CACHE_IMAGE}:$TAG_ARM + +docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${TAG_ARM} -t freqtrade:${TAG_PLOT_ARM} -f docker/Dockerfile.plot . + +docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM + +# Run backtest +docker run --rm -v $(pwd)/config_bittrex.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy + +if [ $? -ne 0 ]; then + echo "failed running backtest" + return 1 +fi + +docker images + +# docker push ${IMAGE_NAME} +docker push ${CACHE_IMAGE}:$TAG_PLOT_ARM +docker push ${CACHE_IMAGE}:$TAG_ARM + +# Create multiarch image +# Make sure that all images contained here are pushed to github first. +# Otherwise installation might fail. +echo "create manifests" + +docker manifest create --amend ${IMAGE_NAME}:${TAG} ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} +docker manifest push -p ${IMAGE_NAME}:${TAG} + + +docker manifest create --amend ${IMAGE_NAME}:${TAG_PLOT} ${CACHE_IMAGE}:${TAG_PLOT_ARM} ${CACHE_IMAGE}:${TAG_PLOT} +docker manifest push -p ${IMAGE_NAME}:${TAG_PLOT} + +Tag as latest for develop builds +if [ "${TAG}" = "develop" ]; then + docker manifest create --amend ${IMAGE_NAME}:latest ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} + docker manifest push -p ${IMAGE_NAME}:latest +fi + +docker images + +if [ $? -ne 0 ]; then + echo "failed building image" + return 1 +fi diff --git a/build_helpers/publish_docker_multi.sh b/build_helpers/publish_docker_multi.sh index 057ecbbf2..f2c6079ff 100755 --- a/build_helpers/publish_docker_multi.sh +++ b/build_helpers/publish_docker_multi.sh @@ -9,7 +9,8 @@ TAG_PI="${TAG}_pi" PI_PLATFORM="linux/arm/v7" echo "Running for ${TAG}" -CACHE_TAG=freqtradeorg/freqtrade_cache:${TAG}_cache +CACHE_IMAGE=freqtradeorg/freqtrade_cache +CACHE_TAG=${CACHE_IMAGE}:${TAG}_cache # Add commit and commit_message to docker container echo "${GITHUB_SHA}" > freqtrade_commit @@ -45,11 +46,11 @@ if [ $? -ne 0 ]; then return 1 fi # Tag image for upload and next build step -docker tag freqtrade:$TAG ${IMAGE_NAME}:$TAG +docker tag freqtrade:$TAG ${CACHE_IMAGE}:$TAG docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${TAG} -t freqtrade:${TAG_PLOT} -f docker/Dockerfile.plot . -docker tag freqtrade:$TAG_PLOT ${IMAGE_NAME}:$TAG_PLOT +docker tag freqtrade:$TAG_PLOT ${CACHE_IMAGE}:$TAG_PLOT # Run backtest docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy @@ -61,22 +62,9 @@ fi docker images -docker push ${IMAGE_NAME} -docker push ${IMAGE_NAME}:$TAG_PLOT -docker push ${IMAGE_NAME}:$TAG - -# Create multiarch image -# Make sure that all images contained here are pushed to github first. -# Otherwise installation might fail. - -docker manifest create freqtradeorg/freqtrade:${TAG} ${IMAGE_NAME}:${TAG} ${IMAGE_NAME}:${TAG_PI} -docker manifest push freqtradeorg/freqtrade:${TAG} - -# Tag as latest for develop builds -if [ "${TAG}" = "develop" ]; then - docker manifest create freqtradeorg/freqtrade:latest ${IMAGE_NAME}:${TAG} ${IMAGE_NAME}:${TAG_PI} - docker manifest push freqtradeorg/freqtrade:latest -fi +docker push ${CACHE_IMAGE} +docker push ${CACHE_IMAGE}:$TAG_PLOT +docker push ${CACHE_IMAGE}:$TAG docker images From 0282d13221f7b07b241a65c36423ff30dc851fd3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 16 Jul 2021 20:11:27 +0200 Subject: [PATCH 128/519] Fix PI image caching --- .github/workflows/ci.yml | 1 - build_helpers/publish_docker_arm64.sh | 7 +++---- build_helpers/publish_docker_multi.sh | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 454432079..eb767efb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,6 @@ on: - master - stable - develop - - test_build_arm64 tags: release: types: [published] diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh index 710e3065b..981b559c3 100755 --- a/build_helpers/publish_docker_arm64.sh +++ b/build_helpers/publish_docker_arm64.sh @@ -55,7 +55,7 @@ docker images docker push ${CACHE_IMAGE}:$TAG_PLOT_ARM docker push ${CACHE_IMAGE}:$TAG_ARM -# Create multiarch image +# Create multi-arch image # Make sure that all images contained here are pushed to github first. # Otherwise installation might fail. echo "create manifests" @@ -63,14 +63,13 @@ echo "create manifests" docker manifest create --amend ${IMAGE_NAME}:${TAG} ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} docker manifest push -p ${IMAGE_NAME}:${TAG} - docker manifest create --amend ${IMAGE_NAME}:${TAG_PLOT} ${CACHE_IMAGE}:${TAG_PLOT_ARM} ${CACHE_IMAGE}:${TAG_PLOT} docker manifest push -p ${IMAGE_NAME}:${TAG_PLOT} Tag as latest for develop builds if [ "${TAG}" = "develop" ]; then - docker manifest create --amend ${IMAGE_NAME}:latest ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} - docker manifest push -p ${IMAGE_NAME}:latest + docker tag ${IMAGE_NAME}:develop ${IMAGE_NAME}:latest + docker push ${IMAGE_NAME}:latest fi docker images diff --git a/build_helpers/publish_docker_multi.sh b/build_helpers/publish_docker_multi.sh index f2c6079ff..4961cb9a7 100755 --- a/build_helpers/publish_docker_multi.sh +++ b/build_helpers/publish_docker_multi.sh @@ -10,7 +10,7 @@ TAG_PI="${TAG}_pi" PI_PLATFORM="linux/arm/v7" echo "Running for ${TAG}" CACHE_IMAGE=freqtradeorg/freqtrade_cache -CACHE_TAG=${CACHE_IMAGE}:${TAG}_cache +CACHE_TAG=${CACHE_IMAGE}:${TAG_PI}_cache # Add commit and commit_message to docker container echo "${GITHUB_SHA}" > freqtrade_commit From adef5d89f3c61dbf6527de83eddc597dea51d590 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 18 Jul 2021 11:06:14 +0200 Subject: [PATCH 129/519] Fix failing test after webserver merge --- tests/commands/test_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 5e9b1c7db..c0268038a 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -64,7 +64,7 @@ def test_start_webserver(mocker, caplog): args = [ 'webserver', - '-c', 'config_bittrex.json.example' + '-c', 'config_examples/config_bittrex.example.json' ] start_webserver(get_args(args)) assert api_server_mock.call_count == 1 From 365479f5e0a19a2e08e4f3309ce342d58c401d27 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Jul 2021 19:36:15 +0200 Subject: [PATCH 130/519] Remove startup-candles after populating buy/sell signals closes #5242 --- freqtrade/optimize/backtesting.py | 29 ++++++++++++++------------ freqtrade/optimize/hyperopt.py | 7 +++---- tests/optimize/test_backtest_detail.py | 1 + tests/optimize/test_backtesting.py | 1 + 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index ebd4135d9..f52e9a49f 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -15,7 +15,7 @@ from freqtrade.configuration import TimeRange, remove_credentials, validate_conf from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.data import history from freqtrade.data.btanalysis import trade_list_to_dataframe -from freqtrade.data.converter import trim_dataframes +from freqtrade.data.converter import trim_dataframe, trim_dataframes from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import BacktestState, SellType from freqtrade.exceptions import DependencyException, OperationalException @@ -116,6 +116,9 @@ class Backtesting: self.wallets = Wallets(self.config, self.exchange, log=False) + self.timerange = TimeRange.parse_timerange( + None if self.config.get('timerange') is None else str(self.config.get('timerange'))) + # Get maximum required startup period self.required_startup = max([strat.startup_candle_count for strat in self.strategylist]) self.exchange.validate_required_startup_candles(self.required_startup, self.timeframe) @@ -154,14 +157,11 @@ class Backtesting: """ self.progress.init_step(BacktestState.DATALOAD, 1) - timerange = TimeRange.parse_timerange(None if self.config.get( - 'timerange') is None else str(self.config.get('timerange'))) - data = history.load_data( datadir=self.config['datadir'], pairs=self.pairlists.whitelist, timeframe=self.timeframe, - timerange=timerange, + timerange=self.timerange, startup_candles=self.required_startup, fail_without_data=True, data_format=self.config.get('dataformat_ohlcv', 'json'), @@ -174,11 +174,11 @@ class Backtesting: f'({(max_date - min_date).days} days).') # Adjust startts forward if not enough data is available - timerange.adjust_start_if_necessary(timeframe_to_seconds(self.timeframe), - self.required_startup, min_date) + self.timerange.adjust_start_if_necessary(timeframe_to_seconds(self.timeframe), + self.required_startup, min_date) self.progress.set_new_value(1) - return data, timerange + return data, self.timerange def prepare_backtest(self, enable_protections): """ @@ -223,7 +223,9 @@ class Backtesting: df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() - + # Trim startup period from analyzed dataframe + df_analyzed = trim_dataframe(df_analyzed, self.timerange, + startup_candles=self.required_startup) # To avoid using data from future, we use buy/sell signals shifted # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) @@ -537,14 +539,15 @@ class Backtesting: preprocessed = self.strategy.ohlcvdata_to_dataframe(data) # Trim startup period from analyzed dataframe - preprocessed = trim_dataframes(preprocessed, timerange, self.required_startup) + preprocessed_tmp = trim_dataframes(preprocessed, timerange, self.required_startup) - if not preprocessed: + if not preprocessed_tmp: raise OperationalException( "No data left after adjusting for startup candles.") - min_date, max_date = history.get_timerange(preprocessed) - + # Use preprocessed_tmp for date generation (the trimmed dataframe). + # Backtesting will re-trim the dataframes after buy/sell signal generation. + min_date, max_date = history.get_timerange(preprocessed_tmp) logger.info(f'Backtesting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} ' f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} ' f'({(max_date - min_date).days} days).') diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 80ae8886e..cb6884385 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -378,16 +378,15 @@ class Hyperopt: preprocessed = self.backtesting.strategy.ohlcvdata_to_dataframe(data) - # Trim startup period from analyzed dataframe + # Trim startup period from analyzed dataframe to get correct dates for output. processed = trim_dataframes(preprocessed, timerange, self.backtesting.required_startup) - self.min_date, self.max_date = get_timerange(processed) logger.info(f'Hyperopting with data from {self.min_date.strftime(DATETIME_PRINT_FORMAT)} ' f'up to {self.max_date.strftime(DATETIME_PRINT_FORMAT)} ' f'({(self.max_date - self.min_date).days} days)..') - - dump(processed, self.data_pickle_file) + # Store non-trimmed data - will be trimmed after signal generation. + dump(preprocessed, self.data_pickle_file) def start(self) -> None: self.random_state = self._set_random_state(self.config.get('hyperopt_random_state', None)) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 0bf197739..af3c1317b 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -575,6 +575,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: frame = _build_backtest_dataframe(data.data) backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) + backtesting.required_startup = 0 backtesting.strategy.advise_buy = lambda a, m: frame backtesting.strategy.advise_sell = lambda a, m: frame backtesting.strategy.use_custom_stoploss = data.use_custom_stoploss diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 77b9e23fa..2998157a5 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -727,6 +727,7 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): pair='UNITTEST/BTC', datadir=testdatadir) default_conf['timeframe'] = '1m' backtesting = Backtesting(default_conf) + backtesting.required_startup = 0 backtesting._set_strategy(backtesting.strategylist[0]) backtesting.strategy.advise_buy = _trend_alternate # Override backtesting.strategy.advise_sell = _trend_alternate # Override From ffeff9c0f754863e4c698978841d69835f3472af Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 18 Jul 2021 19:33:25 +0200 Subject: [PATCH 131/519] Update ARM image building --- build_helpers/publish_docker_arm64.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh index 981b559c3..756d5e41d 100755 --- a/build_helpers/publish_docker_arm64.sh +++ b/build_helpers/publish_docker_arm64.sh @@ -42,7 +42,7 @@ docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${TAG_ARM docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM # Run backtest -docker run --rm -v $(pwd)/config_bittrex.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy +docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy if [ $? -ne 0 ]; then echo "failed running backtest" From f705293353552e41c58f8a892c64db1c1f4f2bee Mon Sep 17 00:00:00 2001 From: George Muravei-Alkhavoi Date: Mon, 19 Jul 2021 00:25:24 +0300 Subject: [PATCH 132/519] Dataprovider caching and trimming to timerange of historical informative. --- freqtrade/data/dataprovider.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 391ed4587..aa3efbd60 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -14,7 +14,8 @@ from freqtrade.constants import ListPairsWithTimeframes, PairWithTimeframe from freqtrade.data.history import load_pair_history from freqtrade.enums import RunMode from freqtrade.exceptions import ExchangeError, OperationalException -from freqtrade.exchange import Exchange +from freqtrade.exchange import Exchange, timeframe_to_seconds +from freqtrade.configuration import TimeRange logger = logging.getLogger(__name__) @@ -31,6 +32,7 @@ class DataProvider: self._pairlists = pairlists self.__cached_pairs: Dict[PairWithTimeframe, Tuple[DataFrame, datetime]] = {} self.__slice_index: Optional[int] = None + self.__cached_pairs_backtesting: Dict[PairWithTimeframe, DataFrame] = {} def _set_dataframe_max_index(self, limit_index: int): """ @@ -62,11 +64,20 @@ class DataProvider: :param pair: pair to get the data for :param timeframe: timeframe to get data for """ - return load_pair_history(pair=pair, - timeframe=timeframe or self._config['timeframe'], - datadir=self._config['datadir'], - data_format=self._config.get('dataformat_ohlcv', 'json') - ) + saved_pair = (pair, timeframe) + if saved_pair not in self.__cached_pairs_backtesting: + timerange = TimeRange.parse_timerange(None if self._config.get( + 'timerange') is None else str(self._config.get('timerange'))) + # Move informative start time respecting startup_candle_count + timerange.subtract_start(timeframe_to_seconds(timeframe) * self._config.get('startup_candle_count', 0)) + self.__cached_pairs_backtesting[saved_pair] = load_pair_history( + pair=pair, + timeframe=timeframe or self._config['timeframe'], + datadir=self._config['datadir'], + timerange=timerange, + data_format=self._config.get('dataformat_ohlcv', 'json') + ) + return self.__cached_pairs_backtesting[saved_pair].copy() def get_pair_dataframe(self, pair: str, timeframe: str = None) -> DataFrame: """ From ab786abf7f9ebfbea04b0e1e39cca105023a95ea Mon Sep 17 00:00:00 2001 From: George Muravei-Alkhavoi Date: Mon, 19 Jul 2021 00:47:51 +0300 Subject: [PATCH 133/519] Fix intendation. --- freqtrade/data/dataprovider.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index aa3efbd60..cdee0f078 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -10,12 +10,12 @@ from typing import Any, Dict, List, Optional, Tuple from pandas import DataFrame +from freqtrade.configuration import TimeRange from freqtrade.constants import ListPairsWithTimeframes, PairWithTimeframe from freqtrade.data.history import load_pair_history from freqtrade.enums import RunMode from freqtrade.exceptions import ExchangeError, OperationalException from freqtrade.exchange import Exchange, timeframe_to_seconds -from freqtrade.configuration import TimeRange logger = logging.getLogger(__name__) @@ -64,12 +64,14 @@ class DataProvider: :param pair: pair to get the data for :param timeframe: timeframe to get data for """ - saved_pair = (pair, timeframe) + saved_pair = (pair, str(timeframe)) if saved_pair not in self.__cached_pairs_backtesting: timerange = TimeRange.parse_timerange(None if self._config.get( 'timerange') is None else str(self._config.get('timerange'))) # Move informative start time respecting startup_candle_count - timerange.subtract_start(timeframe_to_seconds(timeframe) * self._config.get('startup_candle_count', 0)) + timerange.subtract_start( + timeframe_to_seconds(str(timeframe)) * self._config.get('startup_candle_count', 0) + ) self.__cached_pairs_backtesting[saved_pair] = load_pair_history( pair=pair, timeframe=timeframe or self._config['timeframe'], From e9d9668e8ac8dc2c5abfbcd5a60d90f58605e734 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 03:00:56 +0000 Subject: [PATCH 134/519] Bump ta-lib from 0.4.20 to 0.4.21 Bumps [ta-lib](https://github.com/mrjbq7/ta-lib) from 0.4.20 to 0.4.21. - [Release notes](https://github.com/mrjbq7/ta-lib/releases) - [Changelog](https://github.com/mrjbq7/ta-lib/blob/master/CHANGELOG) - [Commits](https://github.com/mrjbq7/ta-lib/compare/TA_Lib-0.4.20...TA_Lib-0.4.21) --- updated-dependencies: - dependency-name: ta-lib dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 08a04d5f5..318d02d93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ requests==2.25.1 urllib3==1.26.6 wrapt==1.12.1 jsonschema==3.2.0 -TA-Lib==0.4.20 +TA-Lib==0.4.21 technical==1.3.0 tabulate==0.8.9 pycoingecko==2.2.0 From c1f1dfb36e371da3b9a1ada48bdcfb5ae22ed311 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 03:01:00 +0000 Subject: [PATCH 135/519] Bump mkdocs-material from 7.1.10 to 7.1.11 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.1.10 to 7.1.11. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.1.10...7.1.11) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index e346fe5a5..a4c7a4969 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.1 -mkdocs-material==7.1.10 +mkdocs-material==7.1.11 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From 31b3b499991ccfc884ee9cad4e536437c44fffb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 03:01:04 +0000 Subject: [PATCH 136/519] Bump requests from 2.25.1 to 2.26.0 Bumps [requests](https://github.com/psf/requests) from 2.25.1 to 2.26.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.25.1...v2.26.0) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 08a04d5f5..94972617a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ SQLAlchemy==1.4.20 python-telegram-bot==13.7 arrow==1.1.1 cachetools==4.2.2 -requests==2.25.1 +requests==2.26.0 urllib3==1.26.6 wrapt==1.12.1 jsonschema==3.2.0 From 4a26889743403be64f01d7da9f93f16506ef8e2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 03:01:10 +0000 Subject: [PATCH 137/519] Bump ccxt from 1.52.83 to 1.53.25 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.52.83 to 1.53.25. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.52.83...1.53.25) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 08a04d5f5..ecb51ca0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.0 pandas==1.3.0 -ccxt==1.52.83 +ccxt==1.53.25 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 7efad98e47d29b11bb10ededf464aae98c19832b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 03:01:13 +0000 Subject: [PATCH 138/519] Bump questionary from 1.9.0 to 1.10.0 Bumps [questionary](https://github.com/tmbo/questionary) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/tmbo/questionary/releases) - [Commits](https://github.com/tmbo/questionary/compare/1.9.0...1.10.0) --- updated-dependencies: - dependency-name: questionary dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 08a04d5f5..3aae4fe47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -39,5 +39,5 @@ aiofiles==0.7.0 # Support for colorized terminal output colorama==0.4.4 # Building config files interactively -questionary==1.9.0 +questionary==1.10.0 prompt-toolkit==3.0.19 From b7c951eaccba6b61e19c2cda2f1f0b9f00449f11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 03:01:19 +0000 Subject: [PATCH 139/519] Bump sqlalchemy from 1.4.20 to 1.4.21 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.20 to 1.4.21. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/master/CHANGES) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 08a04d5f5..c73c684c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ ccxt==1.52.83 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 -SQLAlchemy==1.4.20 +SQLAlchemy==1.4.21 python-telegram-bot==13.7 arrow==1.1.1 cachetools==4.2.2 From bff353a2998104a321314feb9f7dd83bb2a3ebab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 04:41:25 +0000 Subject: [PATCH 140/519] Bump numpy from 1.21.0 to 1.21.1 Bumps [numpy](https://github.com/numpy/numpy) from 1.21.0 to 1.21.1. - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt) - [Commits](https://github.com/numpy/numpy/compare/v1.21.0...v1.21.1) --- updated-dependencies: - dependency-name: numpy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 210705c2b..94c496398 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy==1.21.0 +numpy==1.21.1 pandas==1.3.0 ccxt==1.53.25 From fe8de98832e38ef7b22e41d581249dc62baf3887 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 04:47:28 +0000 Subject: [PATCH 141/519] Bump mkdocs from 1.2.1 to 1.2.2 Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 1.2.1 to 1.2.2. - [Release notes](https://github.com/mkdocs/mkdocs/releases) - [Commits](https://github.com/mkdocs/mkdocs/compare/1.2.1...1.2.2) --- updated-dependencies: - dependency-name: mkdocs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index a4c7a4969..32a1df58e 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ -mkdocs==1.2.1 +mkdocs==1.2.2 mkdocs-material==7.1.11 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From d13524f7c19a152c266c8a793185ced6bc52cc2c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 19 Jul 2021 19:25:32 +0200 Subject: [PATCH 142/519] Update ta-lib wheels --- .../TA_Lib-0.4.20-cp37-cp37m-win_amd64.whl | Bin 482853 -> 0 bytes .../TA_Lib-0.4.20-cp38-cp38-win_amd64.whl | Bin 505119 -> 0 bytes .../TA_Lib-0.4.21-cp37-cp37m-win_amd64.whl | Bin 0 -> 489824 bytes .../TA_Lib-0.4.21-cp38-cp38-win_amd64.whl | Bin 0 -> 511999 bytes .../TA_Lib-0.4.21-cp39-cp39-win_amd64.whl | Bin 0 -> 508461 bytes build_helpers/install_windows.ps1 | 7 +++++-- docs/windows_installation.md | 2 +- 7 files changed, 6 insertions(+), 3 deletions(-) delete mode 100644 build_helpers/TA_Lib-0.4.20-cp37-cp37m-win_amd64.whl delete mode 100644 build_helpers/TA_Lib-0.4.20-cp38-cp38-win_amd64.whl create mode 100644 build_helpers/TA_Lib-0.4.21-cp37-cp37m-win_amd64.whl create mode 100644 build_helpers/TA_Lib-0.4.21-cp38-cp38-win_amd64.whl create mode 100644 build_helpers/TA_Lib-0.4.21-cp39-cp39-win_amd64.whl diff --git a/build_helpers/TA_Lib-0.4.20-cp37-cp37m-win_amd64.whl b/build_helpers/TA_Lib-0.4.20-cp37-cp37m-win_amd64.whl deleted file mode 100644 index b4eee9c4775af60eb6fac2677676f992d5626495..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482853 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

$OFBoXKawxAg@Lwe%;wpm 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

-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

#8-Tmay+;xf`Awhy~cNbN!xK32to{a5v84y=g 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 z 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

t>%GHxX4 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%0DGdGr>KKM7HHrM_ob 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

f3g=x~{{_Z=aBM`qwps6&$HpkkVgjR+ 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

>)$I_mC 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�HxuRWMd)(PMbc@}9-Tux~p) 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 diff --git a/build_helpers/TA_Lib-0.4.20-cp38-cp38-win_amd64.whl b/build_helpers/TA_Lib-0.4.20-cp38-cp38-win_amd64.whl deleted file mode 100644 index de7393fded23db8ad909f90caae35c622b4aa767..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505119 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

UWouqJ9g?umh8M`$nZcFcs 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≺HDA4GlQGu5sUD6*Z~{p%(G}>yrH5j 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�DYaGy?f&PBwUA1ihuEloW0Kn%sa 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%0Ddy*4niMtaZMwoys`yoMQAY%=|x;Z-0Nvf0J+j z?t3Z5xT)bu@zXNZU(C0E`#`v$Z>As*mHGC8`9euITJlr*_I2tM%(ov@(T?Za z7k8vuGH&?)mT&*O9M88urIqsSM-=Ay_S0%Z8sY7!vV8l{GV*+TRbiTMZ*M@lGHdvk z^6hn}gnavV)E;)(slyB8+y99}{RQ&vuNtI$`}OWD-+l|Nr(Sl(aL}la7r}5&scnZd zvd|DWW}TMh+b?yO^6eLE75VnjQ-Nj6I8v6qlp*BXA3Y-F+aERn!G5eIBiIBhkzn6s z6v(#^=p- zzT!PF+=pSzaAm+2XO&bNJDIPaV+;b-R)zBCi0wK-$V1b)CUQ(~PbjFi;pG2#{d{)H zr`FHmSA`|<;K`p~KVQ2o6mB*7GwbJ-H43humpKaS=Q1TI=dF&haQ&<)4eRG84$}I0 z`bRs(`Z=p4te;EPVC&}wsEC>M^VM{^ey%wQ@!Z~+&#j*a92M5jWi#0Nd3y|SQ>S^* z{|VLc`Wc^`ZT$O~+4Zw^NyYm4Vmc7h*b&-D&S>*w0(Kurg$%c$wM_jUp` z1*e%=KkxDt$Vr%81=r8%X)Kx5N9S)m%7$@ljli7jE)?t&W*v`l; z>G}6myvGsmfOj|i#jC2oRU7bC$5nI}=A-odJKXM2bk$|N5tGnC+7@XFmu*#QDPr2ox2bcslXdMoloL)y#O#=sSo2^+S6_wg1+65GNUL$)xI z*cQeZVGCmnJ(*;E8t%qtv_?Xawe$68#)h2}#9sRpi6Y5UG59emU)Yp*cUCA@IYqX4 z5!(fo@xugZLpCKoRw+o32i&EHPR5 zZHJr0xr6GTH2Ap5BdY&J{MCyI$leR*97Q@Z`%ELXm!!#O8%7A-W>kOs`odB)A+{a9 zR2q&NkImPI#6ZiEbQCOZSJ*woM{q1bEeesdm3u3XOCVY8ChsR?X)YuDvv$*ebV`or zt=JeYyCoPTp0rZM0yt34!q>t%78x9wvzWK|BSU&lTT!3+)$D+>HHmp z-F?NsTpo+b5t~&e0Qw_|#}7OB2I>y_^AX1;mEo9`B0;~sPw>*LS;j1>mhb8zw2s}z z$R(hPyf+xQd<;r<+7ItHSM;}hlHZ9)u-+H_nf^;vIp;ej(^4!G%y7g)o9{VClh2To zjoNwPN4jb&XF66slPfQJ>h?P}RoHSo1`2F>?;(P=LO=GoE=YEUOe0DT=|f2R{tyAx z?OBRFVfoImn^(!!tid>A9v7;lQ3@(?3S$#n!DurxZ<4co@sFXFBVX>RC!x@auoCjS6<2xJ+Vd9OV%K|*?UC|&QNEU@b;vS0XACBvv6rH1MwGH zn{cSs=2Nyj6-vagQk+DuY{HAn0jU@OENI@B<3|n|aZio& zl(D@lUNUSNNDi^!%(~nw-p=vRIMbg7Rtizc3j<{OWst$bRES#5&r&h89O3#IEmDM0q@(cHOb zAcYQ13Zo#p#rx(2Ik?9Gb=XZxCI0i^AXiy>H?d1Uabq|}p~{RkZ5<{RLe>l2mWt8u%(Md`I15P(7s~!*A1aFHqq@o@5(xn}$$;>Z$Jz32Y zziNvJr2p!koBfRb&caXV>o4UnDmTY={$)f>$YKEhYBXD+B_J2*m3cOF4H$x@b~_)q5bp^`)VFOBQ-`ecP>O5?tB z2y#UY=73$QmE9!E4}V!!61Dh{+u#U8)aYIsk5i324bA;_&Du&$%>YU1XnvUDQ}o;mK_1#UXAhL@!RXfr&?h<~+z+nh@|Tnb1sjl?enVNkG|mCBT4eBe zHeV?2H@Pk*9=9AmEz-gjmZzmIZ)K#;d6?v3mIcFA_Yh71lb z{E7m>ZT^&(U_y*PF2p8J@{4;bA}uy*!II%W@PpXYVc&nI5nC;g|6OyXKcLN&(mL~6h=J>Fy`U8*SQn{-y-5FE;46&DaeR-{ z@r$EKFvzymF8L(7mE~shhaWccFxd;G|I^2BW1+*!kgkU0XyIeVXcMO(@mVaoXUCJp@KjTCmB$uovf8!e zGn{_HWVf1imzJMTdPh8C6=5PjPZQtN>u0AE6?uoQh3}^v7QfsMA!KKGR}m`*^+8wV zrK9&?$ia-`-n0O^rxpH_KO*W z=v^1~ffeDwu6}v~NZas6(TyzPsRdB4eCHZ6*v(;}m;e8P2Y;0OJla8<=)@3F97;yR zx;;UGkaso%?Q^}Iuc^=Q-s%rq)M{LCjl@?UMO#%V@y=ry4w|ROFSODTamdC?MDWp~ z7}ot7ju&c+?hVi~K^C_~QAmqe`;LL)6%|oq&{JJ(F_0$HBO)VB$YGE`6oWX7QEeI72GY^{;>)~(ft(|_!~dD(dPs!Ql$ zGpvKjlhw9P7JZZf=YZUU^(PSyimB+08~Qy}aV&1}=!)Ll` ze_*?YWMp0dzV#k7sWPEfD8?H)D@R|i9h z>5_5^p3bw`dkzVzES%`sr~g<8XC!nJ*>xoQSh6w^^6a!pV{L`ExTny!D^_z6t0(<%zs;O{ z_sB4?u32Y4U@0s>%Sos{{1dx*#hg%)J33Qpjk4y*&bS=LG=+^DkCum8^?G+T>Xj^xD^wFR5IaaX0 zQ1;!Z)r=p!F=L=G&23uh?9npzD&|ut8{+bKN(R1h>^|rQW5nFw!X8LkB^mC*q@p|6 zTHqMDy;^sWxUWZ*k6@uDz-4?1#Xna{u+liK)y@R!mlUT1^&uLG;)u(U|F&mVAn6K0 zcciyh-T-BYpCnQ@%J+s8`4n?g!g4nq#9N48mNQy5>DvIJ@h;;{^UPhohd~x?lg@OY z-wg|05~v|uy!j=E9+wwUZ3a}QX*w3lj}VC;3J>YSU_4xgd-XuuhG6>;KR^j^A>gmw zE%-=}wCr&jPnfZ}cm)c*4vh*dfkGFTP;Ox^p>Te*$4OC~hX^P|?b6otuZ&a|K-4W1 z`qA?3%AanV=-82JDOK4tU9@aY+tvt|2H6GnF)$c)-+3Pb`2w@thual1`bo#~0RYwo z=^#KkZ9r&otjp6sbNm=L-{GuV@WN!TgWf}oi-0)}eUahllsQnBYzj0ng&=(HdnUUZ z8hbE5PCCZR0|8pPk_qU}l5iOF0XlkR05hl!H=s*&{o4K(JaHJ)`G*}#)32qkjp?;P zgOlkPIiMf41K|P7lJ@j(%Ifr}(c1k$b9i`6#pv_KJ%kj%VNu}}mHAE~|B#h2MXpRf0n=&!v8DgJjW|NM;7uXWGasraK!^r4aRiG&ryJK8uG)tc;&EIq zo;-vX=y~5Sl1l=?qJeaTfPUe~MR;jt(^7zkNv3AoXfKu_0Mb{JF3&5Ny*8QAAeJxm zAPIzhoj)8G#azr-BI*}{AM3g6Lg80i=)d=ph$98?tC(u7Ux+!<jU>$z=8yO5Y1#Ys4wpY>-+v79K=^Yh=I2gm!i9N9zO#N%5^+hbmB(1k-a2!J z7Cj2>7AZ~MA zf|>IOhK|-~%!A>Nl$OElFS2l`TbNa_qzH^>=tvdyIO*Pa1PrL^nx;JqLYqRo!}uQ} zd4fMpi&LWoY?{({6|%2!1N_oIyEF8KUJ9V@|fSMdUYvY#Odm`*Py)J z;?IM{pYmlL4-|q6a2r_eMd2W-;<;OQ?poJ%#X~{65Y~LzQk?E!Uid4A=@nVAHGJX| z+}Mm4L0L0CUMio&a~giXrnbDx8+Z;@p8Z%H!X2YykIKPzBNXvIL0QavIw4$B8O1ey zUhGBozD8F7q=O2Q{5+!}vHh9xWu98HqfgQ&`r`_LH<&WNta#!B^14wy{RiUY3D)xA zOY^zJOaI(xt_oWyy-(?RtNVy59|m(&AFIO+L1ILpmw|6r$C#e`abTaGCu^D|b#eD> zO6AZLQJ;k&t8GZ(%`7sFpk(1scQjlyeoXCUH*B3tVJ>M#)_DxnRx97tC1sNXSMxDz z?&pG^-$HGea#We`5bJ2Sh*YUt1UoeZm?jpS@VBW3uvRfTqY|WB#&Tk(TMyb1+^ife z{Hd#QP5Vu9fU;w^Gzf>V8JoPyM@AD;cTW4$yYr zU@owz*8Gq_Y@&v7xk*Q4!b$XN41+l>VhTurF^GM$cpl3H{yi@lv*D?Fx2YlqK)Wj0 zDa!Zl&DObIes^9naI{+POUx4F=$yAU(;-l5vaCWGY`Usq9GrMnLVZp8ID0O-*YKOc zf|&440bb&fS9?79cYR(ayXlTbDykIESVsJEO@m)|H`k4R{Sarp;*L#ck+G@fy`d)UmR3BO2jgZAimJ&mcO)Z*9TD99;n zlo0;A9~c%2ppinG{U+@k2A%25zDUXPuWHtqik4_NEqUn9GdyiTczLz^wFWE9a>Fo5 zHkM~TLSCqAJNER0DZ%E|852wQSUT#4#{xQMWE<0B;!tEZxi))?miee0p z1GUu^@ALHGP|?_nl9jf0Ici>V9t8<4TQ<#XmMWK8q(iK(5oXu-jt zXAXI%7s8Awt0uveg~g=#OxsyKhNg|4CJBb7j*D@pph#!2yEjfR)ES^Lr=Vg|=QDYy zpU$&+H<7E=yQ^Q)Ru`3;Li(Eq(pJ9|+^QJ3f^C|7=$p8B9a)lBCwEt?lUE~3Rxyg` zbB|=4WM{kGh%2g!Duv9o*Vxk*6X$)2mVCd?AJQ%S9&9oN;x^@eI`~+(D(Ow+41V9_ zDkEMMv+C*Y+FIOv;x;(xV*-6=vaewZ$>Ij?{!I*~4$T!ry=t`LKJWa?7uBq0?nu0s?X>0L z%ipExBtjWP9`f?H7HK;CT0CpbE&)=Rh?oiheA4DE_FmHP4hbLDqV($~^64WL7N1FX zmyg0JDvUHr2lv_Szf_6iF4p3pLzp$+f!ZgZ@f*zw?P(5z4$&fg)H|mHvx(`7H-QUs zHEuH%oPoJAHTq9(jrEtYW_8C+XdU14mpqOBnl&Hm zl67nbj64dDfQ#cskCd#B!NvI3#po_RR;|WeQFJDtDC-$PJdSA$l>N&fb8bI#=coz{ z>oRJFL%Kjo?1>zGz9em2~z3YzP5$)*sDp}@!C~9#3d?H>6z1*g^gS$1Zr|8w0L&Y z4hX42?@JwS0bA{5gf4i#Y@af}e0R*~bA~5ngqLC?1;w+HEX~(0b z)on%|vPH_by8&KyrRZYcqrfwMoF446c8wL(ZZPlg0`Q*&QbsE;Tdg3X!X#OSCPuGh<&r$H_ zZUV*>hkb9H=+Of{2;mn?;>YRZ7t>Lgt>&FYTU{vDt#KAnc=~D838cXqXKW&ykKPpo zv*i{e*T^WNbFF0`N+M9fbg!CR$;L zJes1Y=Ioa+Q1^k)L`N)$412BpiE8L!SuI~s1Gac!UZ$%=yw@rHFZ1Dh$it#LbQ*v0 zy&t@{Z|I86S1d%!=TZ`7WAbxavvQE*+S7rT;D%7>R=6wW6$a|e@w*$+2^~g~M7V)~ z*-xy3E5RtIoJt5LKm=2RMnY7!{WIZtF8g--&~sw|L<~#dhx^T1NUF_pY*$6K@+~rAj}oHn%coC zv8ero9eDv`g0;49(oujN%}j=X@!%<%*$5v8;*9oI`z6Z=r-W2!G&Nf8Tc{hUkZb zHPn0Ii%l^uz=|Gj@DZr7=*3tt<-gI+kJk-HpyK}tY=Bi!B8@;;HVP0wCHm9Wu{CbH zK2Lyvp4xR?K6TXN*L6e3SZ)XO0E7)E08vaWVh&H?6na_1S%I+798Ln*7=~FaeX~Un z{j$wn#Q)AX9{+lRhrtivVF-(0*M}i0@hGrHd(?KN%Xf)O3?VaL+KIXqwo$OgDtrva z)d1x$t?dT<3vZb*I)vC;7?UF!R~$tU!o7?)CHj%BgNqi;N>O%a9IjwL%1OYKI0{@T z`W8FZRo#ys8tIxQpw$ETxdt7A?p;CH;E<4QGxpaZ$eu_v7(fAYrhe*!@xFq`k zu5Q^oAjmGnO(Y%Yjl{VCP>6+#bl*??e*1hxcRh9bGZnsKV}=m0e;%T~(U=aKIt9Id zm0$y%v;J=eKwW^iaI=C3?1kM=Ie?#EE7T(dSe{v8PC#~^Fv78W4Xg(Q@QeJ{_aejgxUZ)a zSP@A`GXT-riHtJ?#5e6z5Hq3)?(s^#QX;(9NaHyMscInvAXo^27PDmFT7yG|+x ze7S26fCnsVRs$Ce#sOdZl7+oJF=hYlY%r)36Q)8p%K^`h{}adxToE z>fmv@r=YL@bN{6no=x|9Z$1r3lZVY+dqKTcRH61sWQY}M$dMFK<$lVn5a{axYp4%{ zk1#{Mlx6`O6KAyguintXTh|c{&-N{4Knh;C$Qf60O(O<6yn;pO$+2|y1VyITVWE4z>>oKU{5 ze#LHT_zxRp$yxyUt5dU#Z#u*Kn}jH>hnbn1B=)X~)2ekBqxf zfZv2@UA1}EUH|l!a}E<5*;6^{wk>W)=r2lNl}HlS(#KEI>ilQBV`O}q{FAt+*!HQV^(OZO*2w82+-EfY(qvDQ>RbgMVkQSC)>K9-0};vgb$Z7uL) z?D@z9FWN!d;z5unvtBJWn{d!?&Du=O>954|sSzOq5$px|5*$yc1(VBO^`@Qr4)LTv zzYGzALf2HY!8|q-;(qmGj~EY)H@>eYjsw)widT2HnCG^xX{eb$Z>1;c1qKFMOyVT6 zx1p_&nbhhATFvdT!21se8fpbwI=`RCO+2sdYq;c@DT9{V4KaDk8JX|p|M1+E^i1uy zYA{j1*AZ40S9CBo&Fs-aoAdlg+jn(7^5~YR&sb7=t&-jN`G;VGtGgB{bX_c5Z(TQkILD%$k=`&-k!Nq+<~J?~BD5skNtWQn$X(%Byf=VhyiQV^qbLV>{*5 z6Yk!*UrI^b>NDnHFjTqkQmXhQN3u#X|&rcfCPeR^ZNq*Z89^S;dnJ0;5 zSw@K+I!aMmEB$U{`_vwmmIj-8z7zQ@ ze`I?0EvmfP$Rz(mwj!Y40``?Mjv&~%X(BmHw#*oi|zSwW`f zuMtDh?Vz@Ki*hEr?*fSa$G_Dz%%MPM?eHtNkW0s>ZRcU)#w)6v&sK*o#w?f`7cXf= zO$y02_|niMR2F`d+d$#G4$PPGpK)Ewk6YgZ9XDTPEL24in5!>%V zb!Le0#1!9pPnsv zM2(MP@zosBZkS(?K$>As)PKw5t%bHSsNPDwkoxEvi<>9m+Sqq8*s4FyCG+vjB>e); z5e@#`@=(1Q`LP>>`!;@vTXm;rh$sWoj?A;3n64yyN~u=)rC;_`8F!SfO-@M z;Y~&b+rXC`H_3t|=9}D|p?+F|{+9G17AiCKPGzy9_ng-6hW|+tllN9VX6~*QZmhlr(mf~x zIPCG*ZG{mi1Dp75h1JV#)z$xmT$VZFJ20!UHxHt&~Oeh?E3tjHgmL}2~W`DlBUDoS$4w?p>h-$;yD9I6#Q zUX*A&T`lHYJeLTmgv%srBIIx)5+bZp8Ror06?(b3pc3{+Ume+Ta}w#)62~(X>p(ELTIMWK)wh z&gbHs484LbL!@Y;Z8cW0mrk*oj8?s_kVdr(c9b3!(``c0bevt2E|Ekfcp4txU=L$P z(C{H6OQ;BGO&@j3LKh+=E0cTUXbgx%W>gB4|0BG!BQ9{e5RpNKsVGD~z7Zj==li^c znqHcWe`Hv(g;)%c;+4nRK-H739G~m-wJTdn5~-lx2osqOiJ}GfJ4amVlk6ls*zcNWimuc3?pJ8rDAza zQ{&-Lw|*dX+zQIsG%yA^$;(sEm&h2EMFEvvi;-4e!Z_*b4FBPl&AH~`#6G^E&dXSN2@9;Xf#*d^p-P7Qoj?5#r{U{< z+TL6UX5mw#4029-bW-~s9e()Zh0fy}-C_HZ7PsGulJsV|n;}x!;3JK78A;#L?{{1r zU>GDIz!n%vQcaA}^yd}^`TpAKq^=eucG5k10jmcrz@gtLXHm^C1f4B_l8%F0XH!3O zV_eHi6?*Kap4Fh|b=Sv6P7vD5{Ocps0|ht~y-fRONC1H4>2?!!I16#=whrPs$-h6k zpl*aim&BQ0z`0(qGI;P>F!M7wAcoFhead-?jHVZC+Kn-#z^OLP_%7hYL zWB1MaPvE*v0?new*O_!^Rjh!0f^`51@^3mY1sf6%6d`?C1q0b#jhCsmd}bU%;T>Hj5w)tVrF20}1Kr zieNmH5KQs+HAd4*XT)%5yvZ;qDViPfBR>g_{+zi$!*G0HL~(ZnyEg)E$|7W+!8an< z5bz#S%75?9u%E%v?yb*(d+^|s4Wp{{PHG4k2aHlxN#-yOswqL85<8PA0hs#~oQHt6 zP;M7p3RhpgR52QT7E0cm0t~;=`=~Gm57usUsY7aba*bHE5^}0jr*qI?Q8=c3Xe)aN zULd^Sb^9_5n(EMG0swn4h2j)WOX?3tLCkbOnv<|aP>p5j^^v{V_DR_BR98;2ArSs7H60$k zA%*&D%ia6G7x0+t>xWheXMc)Aj;@_Gc;5wxhtIm9dN75$$_@)KH~tI@xP<@3ER$aH z%jdZP^rCNE=JHMVeE5NjNDRbRTT0mln3TQ1Y^kJPJHw|Jw#`39Jq ze128VMgGfH@c+8tXD~P4caL37uY+A?R}hQjSHK3|dW7qS?vmKaQH`O55!O6ndTYlZmFhZW70?)q64^$mXYu~*WmUpn< z#0TF^&-Y=3)}`o$R4bcL@VJAOp6GMp5^vQih2a+#s!*hlp874TiR+*Y#f#;K!3T{N zez1LE`U{KDSCto*!jB0rEJiWT4;m&p)$4$nC`k|bpvV`N-vRkB!uxS~Tl00Abcy*l z&wn9y%PXoG9TtqQW_oJFKvKMoob8vXYUNi_0sGJwJ9i=n@F zrIAtA&A%Tx!@t<~ zKdT-2SVwF8Ds>{ZG)P9(3pBr>1L6;Am0BhQpvM@ z;kJ#5VBqjBWo$K@%8lBXcEGrz)DVNtU>Qeh8nux3U#Bx0TBau6zW3gsF)*hR!@s={ zlS>OHfd!2Y?a3-`uS(72!2j%BXnWH)lYZ;S6n|$UHMY&bFA|!2)Q(7UiCmE%1lju% ziV5ASKCe5p>;B`(_#>;aqk3B|0ge=-PCdu*EACC18qviuOvTA%9?w^b!+>~BC-B&( z4SECoIl|0tnc2Gpc7T}Wtsizu2}6>~&u3Mh5#^XlEKIlKeYz4n<*y7xrJOEdWjV|{jf{59eeWI~$s66<`}^WA)<%yOysHQQ zQu#jTkB`=9yZ4R4EIgi*0-~K|f1<0nhhCzpG!|uj)%&niSMotRV+&Yc{8D!BvU>F& zA&tcGGxdpi_v4b9xxMqD`Y@n=bE#3sc=ir*f)m&xm@fYX#`*@@v1}M{OI(1A+5ni5 zl-VPc*bpQCuAxaiiOc^4WLMvL-}qZd-D8?Eng$10FF;XRHcLgE_c$`^{%iL=`Fp3h z4S}}@C-<~mis!pwSc7c;G>SbiP{B9NchdJmN;~nWNay2!ax;60ZOSH@LF%+#%N!RU z7I+nE3Cb&E&8W0h3#nw@p{+0#0r2G42rM|&>$*s8?NEbBZwoV9T@0ixj{@J~w}~3z)dgI|`TkPB|6R{tLXk$T?Xs)T-CZUa zPbWVCH}HL^|05#q@Z5K>zs#fKsvaOu^R>@ohfZNtP1YfX`0&QF)Z4inFQ>DymupoJ z7B{aM^~41Fp7D;#F*|Ur>LiQ?4M}tY6g>EkA|+dCGgdSV>D}?Tcp2u(vQ{iR-~IL$ z&LA8ceS*x;!CjzusU}|#^fPi%ZZo6tx0$4WQ^B=Ubz@AGrqe`>U*IF`Z)zf=;(R)QX7m67m2VLw0h_=Me@g#JbBkWYI1EM!7eA(}(qxUs@ zyS+5xGd^+*)}-WY*OR7(fuDCaHQIMqE{aRlrnfU7iimsW^iwtS!+3&Rk5GHIe(4}z z?ahx+o|Cb1+^=^fMJ%b`Ca=DojmgvMv^$R%pV>Y0i{Z9WwIkN2TliF`O zX@~Ga7NdMT5}z@l|5_cki1rKn)I1iKF=-YvCn<7MKAD8dL-ASgg*48=9`9&wJUI=P6VuDisLMQuUfOn0J{}3Q}hOY>?rJ zHBwcuLlWgP)`?76;m0h&rN}Yu)m0(VHS3vf7t~otz#_0B9@!3}nc^J}ikad^cr3k{ zx%9tH8uj>lUs^Yrvu3j1UgiVY#UVLij(J z2Ce3U2RjibqThzpEW$!sN69S?X8xnRdrJj3{BNd$r`5XBk;K6i#_$iWADvj2zJvE+Ji+^p3eBg>xY`iO@9YY_Wl1&fA#s1EGUA~xxP9}F zw30|S=v17Wz^8d_4}>K-5>CF=WwHIww`;X8?t{zOHPeIT;A2<}@vHRT25{I}k#twVSL4}t z4>{LM3LL);l+TK6J*r$aIRL2=Q)ikvt_RGo;;+lxmeU%9QkwqKrH%>KMx>H&3zAS_x`?W9xR0|GKww z`EzbqUJvx$txj6~TDGX*Ch^Xec1uI^0YqzraaL44P~z@)77e}EirJlgJ_cdZ&APrA z;Gak?(wv1dI;}lDzhu8?N0C1H|SigsS$ppY2O%*r5$q)O?|UduI!UkySdK z`^k8jdvcl=u9$dgpm;K@VT+a#G5%KPGzdqtocwBsWAc}h&L3a$44}|;5AYnc0ub5< zRaJd8sm8|r_F|86n%~PvOLj9=JvFWT=I-+hzU!GJivZdDlejcgN zFUXx5GV0+LTrEp@<9yV?xRsD9$eM!T%ox;inCYsnh%?yaCUbGt=!<#?2^h;W_t9P> z4Q|04$?&Km)jVUa(#^RbxXG00x_We|ve4#`gn*TGI4Fa|*BTd9$eeFBQ+WD~O7j<4 zgUm&THF`6Xz$1UNl+3I{%Yw7?HtGEn@m+hI@9=R7`^KWpI_Dj2cu^dg{=195czH7Q z=2kKdl%Y9aU@lsZ`|{mGRZ{eAA=W{K3~dh3_26 z_p;&3-8PdqW0GSr<-~iUyJ8w!d5=d0^TwA*)}!jS-OD7+=DA3$)C@mc9uF_JE%x37g}7wKBE5GV>i%W9*v*K;`&6D|YzT(kJTp zQ^gs2N+a~v^N-0iN~iF%Z%VS zpo>&+2-#N_%6Ao9;FoS^vqz#n|AtY<}ybHa62M`0%o_U1jp1 zfrnEizhE;a+^H<&56z+J$cpUCex#@&z4KlghYjAs(r8x!U(xy;d6{Jca1oTNU&*IA>4 z{>W|O8%kCY*Fw?cRK2@YBd8P74gzKAcxP!qdspDOvR)gdyaWVpmPV^63GIc89$t zX@2ht3d8h=cKsX?GnPvNF4pDh*|iN%Io&oyLB|;ZbAIesp;3@9CCFF7Oghp-O_k-7 zACW)Ze!#v`vGMjbRZCUninKt!9@TI#FxiG?{K$H{;Cmzm`T7~gm+Q8S{X?%+;KRkW zbc*9)_pO28oJkEw#CzQ@1$0X4W%WtCY6zl#E^zcH#}^EW@sFyV%&97qAKN`A6y5jN zfthY({&TZZot7f{jc=}Df{vqwlOKaMQ8UDVYOQxAlOHRE^U~ae(GDo9Pv~g;w`MIm zcTSx1bG<~_n!t)IB4XF8!qby-v_8wbGsFy**2bQ<*r@y!K4K$~uk^!KueCjutv$G# zgiDzeSIspH5M&BWt|%Fe5D50G#M5H!Ch6<9IMHuWblZGZ2#zT8nz9;?=LJGLcLIah zwmx9_dYxsO={jhxnb*GCu)*Cvpwu&o4K{p5M}zxnvbaiu`;ul@+rzx2e!BTbFjI^{ zz77-yh|(QWe!m~_?zF@^4x7`+M+&kQ5e++1$lw_R7u_yRLlx=Y3gO|-9?;0jS?HVT zcJ6>xGUG$o{dmy9C3m;ji8V(&ruha_8qU&NETX7>5?zla<5fpPr`!9pMa21azpscX zD+q#9m_&5WAS>K)XONESSm#8adC?`@a7(BHd%Jvn(%26oRv6ReKbWKvn%Ml7Jqc{cX&H6RF$sZnPFAwCI{3$e+Kz} zL2qU)p=>k-SpLzLx3yQeeRJ_os{g_W)A|hJ=DB+h70l|thhlO`5nMshJLotgly{NY zo%&(SWT=E%~GH>6WQ;TnUfq^1lRp*BJ~n>}GA{Q8pxvGwR(2$E2e*K_@GJSB+YFyaa*K8EG1oLax=MVZz{gu`VDSVqiMnd>6#d4t-T&LK zqf0U|tkERPi#cBO)lz0x+MmDv_}2wNfmA%||11=NUx}ns`NTK<+l5Qsv!4^Xy*F#; zG&u{cI!(MQ^;WrY*gYiwMoNnQ9}Kc+Gz-eWUbxvkaQy*~O1Rm5huwO!yYCeNoflWx zXYs?dzS-5Jjwj6$;hE&U`0?!Bj=I$~eq5d9?XHMBd2z+y2A4t$p*R4`MLNU8wR>Q9iRuzToiNcb=qT7zZn z(%0-g_+yWJg_fNv2}(ae_JUP_wQ*2j^Q~&3e0RW4!`@w@s}%hV(om?`=i{ipRU+Zq zvYTV*G&Fb1+4PFI6FH&G8kOf5{+V+e;Pg579udpMSrDCZ0-|l6Ao)3Fvdde@Q~5(` zr_ae&6ns8!a>_*3pGD_${9^VyTgw}2{I5Wu8vALLou@M*W|;=BEa zO(*UFW6XO0iVnJ?(ieLrA)0>i;`W$5+brp0hF6E6SuH&8izz73~r z`8jRW-#G$LrQ~Lcnp4l{ltm|$%y>CGNHMiPV$PuFL|(JUuFRs|pq8IQxRZe0;Q2k^ z`Ly@<$&#$#%#xQD>vPT_qXh%YK)<`s-91l!;-xOXI>qK)VDeY{(9{F-WYmlBa_@B6 zg+K$|Ga8mz#6Z#yo*(jPo+Q@Nla;%rbRau2=--dqtg++~9G_3y-zTxFV_gcyY==SG zdZI)+V@9ke7fT2OOlbfuR(k)51pXgOUmg$D_y1o?L`bqk5|R*-t+JD3 zC%Xwr_GOAFOeS0QWZ%+QvkoDKFjMw@-v?vg$1-CZ#@z3He}2Ee=5fzC_sqG^^E%Jh z^PGFn>r6)b-+4#Z$-(lvh&8>)M;4~Q_#Lvy|F#Tks9iPkAhOoS;l1X{-MZacg{;Py7 z#Op^(H{?rBEJUh?mIRqd75qw*o&DpIU8x1BJL6-nTas5HCT$4*^IDa&kC(p8uE)&! zP6#y)uf7@YjOWp;vW~RLO}kU=IpC}1KgHtee9aThW9v~qD->5b=f}Ntcp~xOG_5y}9Kz*EzLR zRd$)R*PDvOJP)7rwvj?j6>6vTyV~~jy`0{@R1Hl!{g)JQkE>wxU6HQkAD)+Zn#o7W zW=V!$nZnjLYnLAkzge74qJ-KnwuJn0`%dfSF7tKP`Az5TMkA-rtB4LI^V$$H+^2{g z@{lc6YWacJbo;Gb8`A{3j@AAN$B~@bwA69A1RCaR0ykSS`Y$=lAQVD-9RTm^T^yO|89-{DUi1aZM{#-5T>;w2$W>|9dZewZ=zik;65}lkf75 z-^7b)c$;eF1JjFen+Y?SDyd?FgJ1czzMTcm<7Gr0V&8}#Q4LQg=Kb6*! z!#Bj6u^PW4b?s_gm{0#nv96O;`l-lsm())#)@|eOatfIStv`ErXgIQE#Ms(bpx&Q1 z4NfOen=hVTFqdh!SfKwUEM;i@nbk(^Iqx5@Z_oV&PCVQmcFQDQ9^^eK>X>*-8c6w3 z`7Yz0bsow6PozOfwn?f0DTVp{-KX^nduquJeg(1rP9hK35}M{oYx(Bgp>0(WqAFqW zVYBOUnRC=vMTq~j>>Q{)yNhI{IZGSvn7*_$6iI3l&U^furIdVqbBX4O64wInpWOiC zRc(XJ^0cH4*aoc)+lxb@!@{iT^`qDCZf_TGMjY92-=>-l5&qD!DxNi+XK>7`{H}&K zlBDg+UOEkNYW?n4;O|$kIU6^gkP#;=T-*T)*W4K+= z-}(=8_sE#E*mb`FgYPR1qI8Wfryb?>s(6`%8~hM)k95;!Exi@6w5`t1>gYEtOD!%f zO4-Kn@(llDAxLESJ)cjPHIX9CHhv_fVzv?p679y!E$>&X`F@V3--sIieo9dHvDMgN zx0)dIDo^>S{8mpHOVG-=kyOKoL+=tKPJIgr5}Vr>BK()co=#f((z*y$W`*@tOH?_L2r$!drfTC~%69a*Df8BN`u)MLqK~bGnyKRp$?Ri&Af2 zdCB^T8XP6QTaWiB9{r22r$$JJ?A|ry{VYXU4%l@vvgNzm_Nk!d^?7+c%HvUL zkaJ_L$y0zt^!zv9$or__a^D!D3DCp8trD zS|hNZ**IsV5G|HkUnr3-WiD#e-M_cT+pFN&R2HUb>M%R0H!z^hF76U>#b0)Q#lXzR z>t0WS5-o8F6T+pA`1Mu?(=xK97j z7FpbvK7CNxx?^~yVx*I=UGL>Q>~2^EV({Rs8(H;~U9gzV#8CPzxqm_6(UkjiM6P@1O}y}d zfX1oi1_JQCS8p``vLPkD^pTkYHH@NMDqSafFb>Q3zP=3%tFult`b13%3|3H+^Q`sbAnH%C zh<88!-0YW8Hc-twI48e2mBU^%TWMwA)0&BwJ6EgjC-T|9_wt2t#Ml`nhSKz7T!CNw zDtl`ZKBz?JPwv}Ad~vL^vk*!%d(Hy8ZJvhYSL*K2mm;NU2HZ_R3@$m|Bh{${40Xf~ zTZvSyED3%mm+~pCcIeMM6{*@-Dv&1$X$H_GAmo?Y6%Fgn^!^b;Z!D1N4XFEfxbt5< zqUkAF*5>?B`epSrz|kntCez#D$G#4`bEK*h1Ay;b5#&{Nv_1Xu*n3qqA%>DI4Cq*^gJz4+W ze&m+ucY`%n-(e_u(UP@pJvZJ&0VG-`}m~v-f zQ~YW)9QyaA!ZFmbW?JRbZeq0?qt{V=rhJ&@6rb+L3Dve6zrX#nZkiX4q80z_% zYwDX4yseEr`NY-*lkc}^U9!lD$G_8DcWW|p9vcX{;7pex;s?EU*FJeF5D9lP z9PoOfE1;mAa(!*RGHqaZ%q)8MC((KRJv~zO7<1J`Q9nHxZ~BgPL6T%*1bG?|C$o25 zx9qw0U3!5XD6{s@!Fav*+F5UpUm=&a#GkepKlhqwRKH?qe?Uy?Fx`<#y!NfBIj)|Vj!H=3+QiQZ3Z zgpErC`hrqPBk^m~)AzPx0!L5r0uy|JDJS2>WJFm~2=6vzKmR&{nKOJ$;J$u3>Gn?g z<9&JmD81^BIPctskGOT&-zst2T*TZ*I=FhGs_nGHlbY ze-~E(MGgp+ej3x{95=Gu$qx*mtvnsL0$m8NxGiT?S8OryL{%DzHu-#h3ak0ZAm-o` zT*7IwJxC_tsy+`U?X1sDfnCrGrNyngb9(G17*(W(hs%U3as-T8PoS7S#09S*OSCrj>rzYga>**0er^VZMxG}5PjrRu z78>n8$?(b?bc_-}7wA*2veH7Al)mukw)cIRD&6f#+9DGr(}d-ytQmxKg3ECfS=3 zyF=lDu_;Sv>b^@7AKfm!{>IVD9PIi0%2a7mYgXv5uZ1syT>P```@QdIeS3~pago`W z=#=yIEVnO!#`jMpXsu6HRVJF=eX*gQpQtzXgrf)gHZR@4!aPFYnqM({{D2{5ASEIX zS!2$hO+WVFa;R?A@0kr$xE>cMkqyKJ)Cm}#O$rc9i>yRTnxs}PR%Q03uh+Cl7?6`wf5cv(&zOJW9YF+Snw@GXeI@3D{rY&Qa3@Vp$L+ZNhp`Nq@Qhtg16||75jkyAWQC3Cn?BDmOUj_oLR`LbQ)HAEi3crpME3d_t6zT=1k_%^QeK}^2@?s?>>zy0Nj(*vpJuG}BsKAX!q zOmq`P=C>DDk!Ms<8T@IdlhDeE2lR6l_z?!;+q4EHIcI^5LXm8k|JVZ>lv=aYPln;s zz~bC8a|h=`#CE?A#Mx=MK-H`Fnk>-geeSihA_{cHiaWiFW_R0UlE8ro0O2n zgr8Qii&NvB*UJZPA>_^d`^4wB`hB*emoCMK;VD~8+NsmDk_{KbdVZi*sO|2<**NUR zScY!c*(G;a&ve*7hh4WToiTM!RJ;L4V#x02cjLb|x(t)6p&=BJ3JsU)|?wRO-j3Au% zYxA(b@R<;06JDzHPT~uAjHz&Z;%A$f&HnZA!nY)wm^J+>o8R-5|D@w6=GWQx1t;W! zPl!nHo6DJN@s#N9qA`SofC^kjikYYuxxBQ|U(^~zS%N&>7o5hXQhxu5*>CrZC{zk= zRDo|m9Zne=oEwU-k0Qc8)SeK_LbTvtMK7JLo4p)B5a?M?jFyV?l&IN*DN7#60fd)d zsgf?c=f0yFO`ej|d*=hkHPukc@p4Zd<#!{KotqZtqg5k~0?D=>wyqPRx5N-nD@T1b z^HgmK`whDUkCOdWFp5B~QUaF$Op}nqB>Ltr5Mg?vbO_>OBdm`Fy(c5sCjg6APvOGV zLnlfCFMk0><^u>5>l+7PL~e?{#3V52Oy~cZPTj-Ye65GOS2{{ACiSf|chHtBDbpm&s@Y(t^nd(sMXf7R=`#U~$1+ z@;5Ko-eT|`pc;_u&jN^oV10RuK;A3B@hkIL-CV&aA{X}yNU#||hcD5E+fh}Whtz_A8=BEA!1O@$$_dJU{1@`TtRL4@B9&yDgapfK$TMF3uDJHU10 zQ>Cgju(o};gYX%`3j`ozxm^atf{hMvyxR{rN9@zYhoehsM;z{X&kcj}VmwlQ=A<^f zeF#Qedm7n-B8FrxecD#DgE263E#8q|`klEUpYzXXUY<|s;Vu4F>b`D+{VT3vY}#4e zQ?k4Pb4Ok_Qmf1+nZjMV_Q+}4qq@2-dOjx4s#}v$haHHHC=bLXh00HQ*&1x$Rk4?@ z{PB4$!k65hZ8nw_FfFx~M3a8cZ@EkgG9qG?#MUAZhyLBT79X&bu@s@Wb>R+C>D>O% zhbHlf?_v&5{hDajE^ACg-$9D5Dn1tajgufbs2!ivXPLhc&GD^|=A*(5#n15cU_fe{ zrk0kMWkyt-aGtMEi|6y~j>xpAveXC3rMV`kAZ2#-BS7PKG4sW!01XfXI;no#t9Gm3 zUWii5-`!A$g`AnSvN^$h*k#e-I?TjF@40u7k;VPCJHCV4UB)dW4| zgq8J=&sv*E2}2gYjmpP;myD6)%Ri`Qyx@%4{$EoIt;;vPpIVmF|L7UpOl*_{WT)lW z^>v_)Vv}5#3r~#hU3@?Obkxu4`rf{dWw}50hGOds`>s&gf$pl&v0mc%W<$a4PGeZ) zL;A@RA$(Da{@&PTkKoTL>-PO?nRT6QqV*SEJaQ){ri`#Vy*R3I`lBM<_0^*RkJmi5 zHcEgh+67J9hYk8{P z+-n$m`7z&QKgm?&hzibA{^&9B!u|`PQF-U{OQP|9MU7e4>zxi4?i4`=8r1a@-6=nn*iuCwE2WIkio9yVijxyO)`e zmue4}mvET;{C_+TCoq08FZRn9QH5(KE z&a=%q`4cVMT`lKZQj$rOi7N3}Hz8k=xA(ziK|2nAyQ>ICyVA-m< zBe3dO!M)MfS73Sew<2SIuX0nKS8L?Aj|(aBql=Y7gUa2X6&+j72N%H_=@Cr=mla#T zZLh;3SD7E$7`5F-3oEvETI>qN3&-h896yLHcjX&eF5w0Gxexkv8P<(lId=iR5em*9 z)QggmyV1Q9@<=b)3PX=(aAO}C6W0N7#hk3N+_TZ&1sqg!pUUJ>Y`#wcTxo_t{|h`2 z^O|DgGYD{H?*WG$6q}3t{qzh;6dYG5Aj@Y3l%L-~Z0Kk21J4T2FsQqS{dTxOQCp@o zU|b+me_oD?1kW`9{4EFnZ)r`-EkHlF{S9!$e{pG;}JXBsi!KukU-A0Tx z8-g&$3K~t2CnI37b^g9*#E<4r&JyZpnXTx(4YWTl*g>}aG>#s$`e~f87y75y%o+;* z$ke#W#K1DB;+C4eJ7EklX>MRh8ke(XROhbdO@uqpGk66&l+vksm}lE`RQP10W&rru z*bFVqKKR<+K5*@sR^i^kSMUhbU5Ww!CikY6>$sfdnEEC+{akACyiA4Tv}Elpv;Cz_ z?lbgc_$GHf#|7E>cuZrt6VAvo zE>~9H?AK0;*gXqN&e`Nn(L_9FoN2Y%NqxTf9iV_PUXOUFcdybm@neO1*vjiVSw8%F0jd z+{ToB8}0n=T?6&iCPOxdmCNOaZG>%O|01NeoIc@Lv9mXSvmU+wN>@iKt zlFuB7I+0c&%QQrxQOb_9Y?-lIZ`hLQmD~5DNSNRlA|dec82MKB2gcFE zE7x9etPk0fWD3$ipiDCD8f-sb-M3(r$Vdv#!wpOLTsN-!i{9s;ja;L0VUcZ=4}?~) z?rV3ia%ju;#4+I_#cY%X**s$SXC@dZWqi}aEPSO(iDAEI7x(Wz#(%m_IFpyE^7gTL z?2ikx8qbCLr{9{pcdIbBXZAx!7h|m3`E(Prj2V~YB*cP3_?4;IZ?!w>m(QV223WN7 zl(5g=-SsKyf3vnM8&Y+L_)pU19~>L}f-$g}-TB9xpiI~woxbz&WmQ$)Pqf2|`Yk%Os;-Ky<{d_Y17ahkKcbI}ETbMnLC-=W) zT5i#%RT|$%9S9V|zfXPHgyy^_soNdDTI6Q>XYT8u(;n?T!m;@*hC8Qijbkt3W*0|t z%)BGr{^CowldeVIB(#O9}%F87Cb}#Nf<{KJ&9Yu zY@3dJ>qjdk?K_9%C*L#_-{p?i2>GXsRAi-GUjO5r5CtXi*R5T!tv>A8ZT{Hr2DXE9*J9-e6phpS`Z~=Q zKgNy7E51LQ5F~`KgC~@ylJ{7iJefNz6+fO`zB($c_;aS*2g#>en#(m>Ia)J)I%;~9 ztY2pH)wNC;S^eMjiQ28o_nv2+bn!j1fi9cwgJ&*%Oj{3qF;hM0+iL+d=H!yPdu#u8 zzv;TDUCYCpKozW(_zpnC3= z<%>Ty_-~T>G%cs2k%Ck9gn;T!1r?fx!yv|s$DZJ(x1Gx~_Q2Uo{UwaZ(^vc#E#ISv zEX+<-^HE^Q!tJi=KjGU!N{HDrm+6nHc8()iDhzAdIzL6Lv{>}tu9bxgHq^R?^}jU? zxwh0SLp^w%iY^LiTbbZ4D`o1-Q&HjY&#Rz>%hg2oan>&#y{8|dt*DlB&F$2$gtiqw z=j!%t6wbOj()(6<9v3c{dhp=7zWFW<9rOJS2l*eb8hKc^vAxgbZJiX6^P%M` zKdG~g zk;bcc6Pav9Uq9xsyrbkE$6YeAig@3p)9`85NN$uQn3)|sB|y);@21g962x?^A}&@O zWYK}4k;>*3Pk!6^?fHyfD{KbW+6d$Xz2c9jGn;qYusj|iJ9}8M8p`0k%pK-o=1lm| zjRS+$BaSCm(d~&Z-=EnD9FY^grkOK&o?JfY|GxXzflMJO8jLC}mZwcA)mNPPYh)=9 z{37x5jIply9m4e>To>IRtqr$~Olhg3$DHm#(q;XZ*o^Zp#@ z_Pj5khJxY;!JGakNH)*r{mmy6b|eCBfK5GIKhU2Kcm!wJ4x<vKmP>PW zm^{FCS8Ywzj(sdN$sGHWm1YmAHtVUBV^3<>3^hPrLe?1Umc?4j$anjg^j5NlQ7Yu- zDpS5Bem>USRhX>@7gw1+z7RbyuD6kEHI{pHRkwOEOUwOGQ~owbZd9j=VrO@%_!{FQ zS)PNR)nt4N>G0Ysh zj6CehR9Q%p%ij9`ziVBY2J>B+w>bnlmwflnABX>CD`I}rgDuz4xQM=dwZV9AOy*I9 zxaf~W2!=69jw>rQXHzhmy-OD+Hq&k7#Z~+(Rn&j3#K(O8rJEk^X@R$k+=esR>yF&* zx36;MMw0n?%xMKW&F}-mI+M=Te=N>Fpk_KYdRzg#S3$3k>$r8A$E^+Yn`Kb~WiIbd zKHFbK-E4&|m+t$>54{R(fw4L$CZ`_VLL?`lc`}#9@77EoNVG>BocDSA_O|cmRcg)# z`5&xw8=v&!{4}P5K^b5{dl{FXwdA+zD8-ZUY!bR-egi)ITIE2!)mFTa6ZMwPber3M?RqmB36Md5On zoXdwKA&=f3_qRVjpRMyPr--pa6H_-cn- zUU&bl1n#Q8XY;veWa!5(tN+B+B$vwP)PxEouz2990aqhpty~8Zy`ip)M93=6V16AL z(*tF)aLv!!G~@CO;DSNE1?hQ9KEg0GFcGnSM|mVX!f}*{&GUbN@vdQ8MdkK;uA=mi zp(Ol3@*7&FGFP{N({`8?20!@hX6vWTrhC?$S8(v3tkd*r8WkE+9~v7D6h_H4Z(PB6 z{-{tx`Nq)Kjq;PiA-rz~Z> z;k@0}rHr2Z5iQ4P>gsO=C~iysRUL^omk(hweGZy!6N{wcb@w>7yJacF6{b$c=K&sL z+5IqRbEeXkOIf1H3C(2^6*`c&^PVpyAmoc zo+|M^%Av2kt?TLgL(9i4-O+)v+wML}wuRBJWi3PLxTUJo{3vg|{z$=V$8MfDRUso4 zpDf?m&XoyR@8uyAvlvS#w;~xKQ~J{IW1p@TQgFN7@vQ^Xzei5Sl9@a2veM+?6Id#5 z!`7qlZ7Li;uN>(X!zy8g$+mY0p)_!+45=3pznD*?^+S$2%v1P*Hxo0=s#b=67cxIA z36@LgFSt?*fEyWnBYPe$#CxsONaYroM~OOL5cN%}S-SET-RG}?X7@kL{CORaN>*3A zR3FRxn3p}0!X7*|?(D84qqLX2DyW@5#qQ+(=goI{n(;EmYUt3^7H2#CEJ0qNP{>Ly zi0BZ2o^F*TidB;9{+KpnNf>PtbDUY=|N@{qNVXNR4F2` z_fDXNP=_AJjQ-Hv&XoQOmS-vRXLsh`GCq#k+-W&gg5AM{;RP)O@Pdy4E@4!MM!G~r znh9&sbe?cWb%5snMR+aDahn~gIcmCE?^WpH3k zGQ_5veqN{F*~3*eH@V9>RJX8>>Alw)XS0ykHX6&lsVdFZT-3T*{V%qDEReM4{C>E@ z_Tl{mjc0*8?KQQ7jrV?EVD!B1S2*Apnt7+WtT5S=t6Ai6L&LpaUM1{XcODCF-LX(K zOfP74yF>i_0IwHMTi21skoTOvRWCkhNH2cOvku`@SNZ!p#=I{$aaeyXq0K|d^T$2> z6N_KBRsven@2v#LG725So3)24XxQ}sZnKnkltkw$e%#S4pgcSJLGFc>ye?RRxQGh> z#2&*=r_8%iDK+8J4%N>QC^^eM$UwH z^&Wyv*t%<6SYE)4&i^#_!zS5ZVM=&%t@JO-yZQ$r6S|do+VO8^2Out1o9%R8gx!^=Ie~?^G67V=_VX3&Fkf&M>LwfpshD z)~*)#LXR44_10`x)Fbg?{$FVry0t^yZH!|clKRr4Z{z*(ih|YxBxdw4>JkGh?6j7K zC^|j!1|74#2PGiu>f)iRFKsIj{ivpGOY~$0|JZcclq3mOzxs0-o4-|f=wGOi(T`g8 zc!*1$v#e2Gs)L~qd=_^_`J!56H45bco&{{u-UC4VVL~s^9S$p@*S9KM!3JImhkjLx zf)Rs;qBwADDtS+39a-lyR*&D4#_9u;q3)b-Nn=^(!=UzDZ{neLo0LKr*Momkb1V%TEKR z$%08RF8GTvvUiCxF$KsTBUgJztmwZ@BALeCsCrbi1Yvly#U?rS!tVZOPOfEPBHmu% zLCsM}JXA>Vjon^PYG_+3x3o-8QZuTx$7P)5AucDbb%gcRUM#muRE2kVt9sdAR73A8 zT1ImflFWC4JKzNNz-mY&G+{pJrZ5l7@u8VJH7@>(mO-RV-zxTeUB~a`XU8%&VyI6R zPhgwU#$baV&Or+P(4m)cWsiWYnobymZ68W-pZh>hjSDEzZ2irSfO@;Sl1$S$zPa!O zPJIFghP8G#Y8+>P-!!P_K|tSCzd_^MtAD5<&%@BVC9U(29!L5(sKvRW3xE(>I+O&# zEdNEl(ujkq&lP36pl7_*VO}Ntgx(ukVG?M3sMDA24x13@jK0-y1=k!~=|zu{t1y?H`Es#&6NU?u(8@tAXH zGL*?W_joDq0u|10RH_3NBdxMS(=J4+5!M?cZ|V>i1B_fLm)UbjGp=1Q`ZsM_VBXlJ z8&URcmt3P63fi8GCXyWb!Hs=+OBvD+$DjfnB1j3-ADr}so%0YkhF?!8-+E9pVQ-=` zk^@^sGDg{;CIu;BBNx%5yFt7Y>o=Amu3ad(W}Q`RjG8bS8EodcD|dTW*T(9fY*pa3 z#K`;0M7&4%O+>Ur6DrfQPDNsU*WD#t9uA>`ap803GB)q_pe9`I67dFqF`cMMo`YoYI$z;&!U9fuMtPJt9x|^$F}?PuO7!8dQ%3y*BO)Hc+Jeg3;KR9{o9n%S^YHt1d}n>5 zlcjlR8QUgoi54wvTE?yk-$Wmj#cP6rhmLaz?jNLCNXZ{0RAc;EgJG^uXp%$>T5sd- zvt@af3lm8`JnT=*CTVf|H__ZP!N)Z|DSuISrLPe1I`#vnb^b{(xj$xOrZqDO!4ngP7gs zGCsG9Hx>Bx$0fE0wdwyR3L{1x1syLG4JcE-UsHY_=DNtJ_CVqb0ZF%v9S{6T#4p}r z#pyMPJPz5qc5GS{6a@X+C~)r+q-hn4vVIXas~x?IFMhebYBM~K{bGF$cT4;dTC}8a zE(37sK|z*vS0JyWz(Y*2)?fp{^F-(tiSBQFMRBUud}BJFrv#2+B{tUGsN&i~zCx?W ze%UI*LmZkC>f#<~X@3lpGy2+$s_(yp<__)Z14bzE*avX>XeuuLhSG*QlVM5H6vUXZmN8Z$ zrTOY_ojlmEA)0mD?g@?nHQU2qZisI#MN;9?lT3GektG-N=Z}tIB7E%5e*Lt#S?{>6 zWvfI+oZg4HW469ypTg)MX!Gmqt2W6G)1b~6at5qEHD@Wq##{I&uCxvYd7;&fvJeWF zp2_w}mrj9lYde$1sKyz?rYw$OUiU%o_Ml9f#l?P+h^>Vh5>i8!ul_}zi>GMGx=L!Jw9e=oXy70ta9Cx-ixJV3x(UKYbK zCe7`-+ePRQM78%ougs&B)`5~4W_D>ye$aus!D3MN`PwDYm=m3_S^h-?w6!2d)kgrd ztpQO{oKMfQO=#>mJ*=w4;~P{)KzTH0nxaRi@|GatZ%Sz;Wi;*Fo=6{7bA~~w(?)5y z?9t0=Z1B3vY$dMu$9}R!8PkB{_mbKJ{H0|G-Qb_4KQlV3AhEFG?t@Mtz(#!6U0H$} z7xd)0LHftwew3eQoR4huZm)`%>>Ir&Fl}e?J$wQ6U({0#Y8;!`Uzmt_&l@x=kt|C( z+|U=m6&nXDoWRg{@#1Cd4xDdHDrgEtf^nG`ccWBlef$eQji-0ZqH-SMHWn;-4MYaP ztBjnRXjaX{V;N>)9EuXQkZ{CHy)kof4!gL;hco?a#A8`<%w~RZPqZwx1BJ`l=Xafh zh|*%${=DRe`Cm^I!?{H|kq*%hQlJr`kHL}kC;=v7y{oYdLBGCBaPN2;lFk|mdR*;` z!mdWcuE5zPUH-(MSznIZhn4D$oF>a9D`#S$GYSjevA#SzyieQj%!s+`S)5RrqyR;;i&j8%4*q`cBNj2PEy{|rcfm0D0DNAu|Lv0w`d_5*z&baAfeoeNrh zsg(pJsiS*84~5;pQQdaSbxlz|c9<1ABcmEXchlRw3`xHb2G!?&m;oe%XRbhc$zTWk zyZ0SfdG!st8a79kr3r>*T>nbM-8_bE%2Tak9)pT+!MBf$XX}HXn#^HI!e78l3j?ChWZ3$mm&Z6nlw-I{f(W?Oig^hw zw7YVfrKFwUPiF$oli7}@WEadBy(V!FV6fQY>?h!){+na82;T>g*RQC2l3&Ksb)n+i zAASSaa{J(*JN{T_-UIJ0>Vdlot2k30UjVmh#?7uqFJn5F$Mkf>0c24N3U6cqHpiZV zy{=Ay=nWHbE|d}y)GP_+Z*=2ujYv8i{gD9pP*=1S(`}7{U67?92r!I`QB|)JNiuD> zmND~|CfTM^;Iv@Nmjq=OKudq=ilXqMxrtv6P$i8Z<}`6otN9}B5m?mVA{q6P4|hp< zoudskf3Ui&fP-1gIY;Z>)Gq%oO-2pCrS`YL7_HP>15B7By1U)$gLm78K<)KhNy+YV3y@r2DqM}J%y%I9!amyaHwY^y9wFaK zz}gPx!m9+@ zl1)~YF%l&V>{(>lH}7zo$-vKw+EmNvk{$vsvgzv9yLf_V5W!uu>dF#E)L<3!Pvr4L zKMN7(mPmre2;7@dvb_!HidAlgRFGxsrcVvmmod`G$A`p=$$&#a51gx{1B~LP03ZpN zbifCkiijk=id95FL){;dpsUQTFR~8LDdM&*ya>1jL;Q|`|84LiNu(iY=1E-P@~Qo( z*|7#PO7a@+dzR+}Xy##>e^F3w5OM7|=&jumTNgCmXSc zu}{R6{)V~QwJt+0h%n;1EbV20Ub4nA)_{wJhDqj+A5= z8o3rm#NChCMGhJEk=^+gAa4`@qAZfGfTe?GQQz~s;28q00SnB@09$q^Tv9MysqtDD z+^+4ugAmE|0{Ctxx_X3zKCA7ni7E-o_R4KX$Ap5_nKb8+bjX%)(1?jCYUzgCnb!9` z1-oZqu=zgVo_7}zm8+=Dxz-Ku?mzZPC5V0|xMR#N5pWL0MBE%G`!ym+&`A((aB{Q& z_Clw+PwS0tctW={cd|7lBcla1dtWIlq3$8z9>NERW~`H-eSixEt0{o<0pEFo+&

*f45Mdn$6 z{J`$a2h4*<7+=u9lhwOXycV^v z&BV}V)LjdZQx0b~XqF-GWmrgux4cR|vIWCRCa702X*ZUzi`yXb(V*7<2mba ziNF`vb`^I=P~oG^J4nzl17wLo$xQ^5D?9lK;EriQ6&Tbtfo86h^BIb^0?i!3hr55S zGielddN@h7jQLb`^*EVc%gh#5Ut~u_DM$gxfD;n*TUS{ZJQy>)g864tLc}RT-aG{G z&7i!T$>T~-2F6%dF@t0CM5MF-ci?W~;xfh?95aaf$0TU&z~k(z2xxsZOeF9qrIcHA z7pYF&3}!0ttbR3G#>A)*QC1-OpFzwn43dAcf*we6%;|ePRP9>@zGvcPOojSLsu}fv z;D6#Q`49ZN5A9;4+F*qh2oaFX6@x}i>K*lr^xKzG$^`T`C96Sm)!%q6LRxIVgH^9_H_}XdM{|M$E8YCE#q*bUJ zyt9&t4ox~TTEa9+rh!U=GD=1@YM~iKnf&)b0YlR3Mty4xmmbdckx|5LV1@}u;j%_5 zmkc7w;ZSb{JFZYTCRS+Qxr$9g-9*pK2<_qT8`n_6j+1ACGnFP2moYPJq{E9pTnbho zaj0;p-E0)h|3bcZG!$_=JzK6Sa|JW1qXgh%lA-mpZ%H*<3}MjWS#X)*$Oc?ysHgNj zMUSq1CgNr}KwWS82rQ_APC9HSL7~U6?rUI%HP<`@>u!mzz5?Rid=YIT0tHJ+gR-8v zn2chsT-iibkWtp@WR%%cqDYbk3EE@6ODlk5yKM)j^bv4=E(DzQHM_oLkS!%mUxAAZ zvmFz94)@LNO2ws3bL7PVJgd0^=T`!*$v%pyrjjfh(;76Aqu~nTxT{qmnXb$`9 zl{y7WpFm1ZUbX3kW6PE@;EyMAp{@OJ|LkRKDL!2KEXyZ{6@&{? z)l_RLHRAW`zoLEfSd{2BoQ&GChAA*Wx#k163QfKb;3GRwM!O?#M7$|juNKrYJOWlv zm2XsPPT>M2JwkSvjOton#V8Denobf_^a_i+?u%8RqVFF5PtjY0!czwMj)Ij0<;#2& z5%&Wm``i@}-y5Kyhkz932riqvN=Sx|lHXFW%7IYdt{Z)D|4V-G$Hqwj+g;ay3SH1z zqd;pF8ibvC-UCEgqjl*_2<~QuZd>cmz(Hk^03@qMPbRAU*D*)~0h}MNom^UlCa2wS zQN`BsM=*IK_}_m*rl;+`>9~2Ru4uAwI|1i(?zR@n`@%BTxeN7&^*<$3{68gg{3$CH zNG_d&7zka~GLhlvfG0$mbJQ#S1%-UBksk>vAb@7Q30CmpIt((Q83!c0J;X&WADa0$yI4pR&q6JgmWC*S zRC_JLFX`b5xoQ+cbCrD#f!A5)@q7yxiDIMxD~3V}yiN zI(?G@K_Sy|l+;GREqNYX80vjhFJm&VlVliv{CU}n3V+&y$~)!5u_>>!MnlhEtr=9X(j(%u@<10` zxD1(nDux@{es_GhZ+(}v60rK8Y(E8MyNca+z^DH(?-pqH zKbXzl+mfK1QS&1U zEP^0tR9{|6eOG#%OmKfw0Wu9W=$}AQW9`FA#CZrE*T^GaXwT^XVeV?Ts$Y}boe7Nt zJ*;8`bXXO1fZWtL4=J-H%nZ18klFJe$LDLIKM7s%-!GbLcLM8hAss%vRM9qj`EX-+ zD)To9Dq9W1esxU=nzH@v^Uj@u)&1YP{*;01J!DexJR~9GCYo9-`FQM#FjGMi>RVU& z!6A#*k89`K%>}9SF5apqk7-}zW>L8K$gEa|-B$0ZErDGA_N7^d-{*SRJHG9czuM8p z|3}l82SW9K@3&GZq)?V@*|!jq7!obYlCm_UD2g;BN=7#+YnEguDhVNx49QH{cd2ZR zx|T7@Hq6+@EU(|o`}6(%baLpC=L)R-@T}=Iis08vEIj(4eQ?CywVeEq1+JxOttCQ@U-1 zar(_du0uSpLe%#CoJD_%cpawu5M2u0bZJb)tE$l|W1y7&7Vp3kW=U6pzF^5s|Csy%mi{1x=vtyQP%xXFnB zR=?vewq<*Z2b86%QZ8UFSDm|Y#pPTvuWHJ=tHrOixr;XG;l&+0ly<3qxivatTYKW` zdU@Ng5Bpz=KmKLIwpu(b)KeAkb=Ht?uNNTXXG`71qqi|=VxU-itycyAX~V@>56C~$kBm&6zgu^VzUh>+^t7?gQkt}zos@r4 zwz|-nAh$Ew?#FXZ#Oq_dhTCZ1SN6U* z6ZPWGD>410fjtQ)mg8naXC;-sgn8=z_Vl4z+*e7=`A*4KVzye|Ke)=&rvh~wI_LED z7T;k;?VP+iIwLx~ows17eS%dRbAjlZ5w9cpB2dQ|O?6FT5;ygi^TfY4$f4D0-hvZ( z$47&03x??E2rEPR{qu_JCx?SOjbu^pddmu1Mu_Ex(crFjSybHgU*!|pk9}Vb4e6)O z)mmiVX8tiYZC`HM>P&R78lJ1QsY4rXiKF4W~aB;}j1E{C>%AZbwc_UZvP}thG-T&Fm6lUi@QL zzr4dI39o4-CE@bxHqo%j5|8LRQ&_1CS=2L`hv;x-lM6FIf%3qHh<;ODA>K)ASZ?+? zhe!7c;AsP=hJ&Aeh@>sq3njaF(3#w!v&7QCHxY(Nb@BigilS(Ix<6u>-FCgyw{zJKStAo{@|*KNXDY?Q^N7YuxQ z=1kNqe$e&=b3KiQWxRMeVsb z)t{CLbqq)a=rTcbWSLmr_mN7<&=yB#2eCeIt z^R?ya$F8Evc;XaA7IQ|cW4ZZyPNQ4yl-LBz*glEQiI4lT?9;p(PjeAxa0Z_~p>wX6 zE91NU`hImhVShg4?z5D}9+JcKcd>KvIYuX2m$T(Zc(z;K=T7Xj;!f;u__d|r`C}5- zc_A`|R_!}uU}Gqba18z(XaiZ1UL|1{sO-gw*oYuz&e(n;Eut+2d|UM|z-*!zNjh>T z?(O`mKU}Ei^#X~hXXYF?e}Q@Qy>k0sea-qWH3y$k5f5t&(6QoM*zD*0B0g?xUF{ub z4+chnT`<*M6Q5Sa&FbGffi&%PMsBWRs9hV1qf4g7q-7+I9f(Vd>|c88&}db)?bXBI z(QeL9ykE%j1df$0Ug+nazBxU+^egn_;CemukGFMk`k;ST53RR(IjE|%?E*;m_vl{j zj04k020)O>9qynOW(k>{=*FPUrk+cg>Bu{SQ-;8~cHa`Ry49pB)fxrWQjt1FCgWgx z!o(Fj3vJ-JHx~G7n#MR7Q@|BB?)p=|ETqfKPT%-saf4sDJzIc@Ngs&FPyvcUnJo=$ zUANG4YW~LXw&zj>8YOT!@Nf4rkXbmQS1Ziz z{@usI&=7VyxBKNOVs9It;iWmI-ZlMZV6eOF&5kSR$@XPrwhtC(x@I>HE`O)Pak{6# ze@mMfuha4m_(2nhvuZQD$!>=(@T6Zzs4~KV5O&%RE zAteST_|GzY7_vZd7vCZzR|-Ngm))?@-s~K5)IdI(0G>r%Wt^v%hzN;yHXPM;`&}?l zgWlzKOTM@_z{|vhTxo0%O9OVfY0p(WTHDz4ThsLp8@lqwr$SRz*^gY$2 zeBK;P=;Z;%$4&BN)5BbA1B%yc12CbgJbN-h9v-7`d1BgNA}|kpD5wL~z4yRGXHF|2 zh67s)bn)p2Unj_)OC*3N(vabmP#e=t(8GSjdq;i{H(R=CjDD_!-=J%9gHtdQZ#h)Q zK*BBY;u~67h#-2(A)HJ8Q``Xz94KVxLz6PP=-V4#x!pdp=qs7k17lddJ)?d`2Oq3r zo~hH-{XV3?tq=apN<=3DSEP@W0{^knHbPQ~J-y^QKQwG{JK<#dd*J^;17#b*qfQPk zsN4-qc2=l@f@07DCnxjeAJ=yUbi_W<`@S=V3e;ZDBRBW!0@HmI zFto|dT}6diSu1-F5!hAZY%>OiOd4)Zm~yQZ>|ef0#JMC4TvE`pkkbo|cIm8VAlkhV z!UUY(bkawa?J&{+U+HeKMW%2$rA z3S){f?3Xs=Ffk5r062RanAkSO$?-%KP;EB+hkgwGXKO;P0k`{Iq;BQK;ddhpAJd8y z+z;PCk(Sx|3G9i3bY!#`l_`Q9DDziA+-@r&6fa$k3XEc@+6RoCwWdu(+TNIPHdE+fgCbQBS2(TAz++m#sHX#$z8h za4)+xm%}p(?vqRT-pfyLrzxx>TnFf7PLf&uSjtc|`a52A6;K zkqZ6`;CLs;1w`ZQDu#B#Mmw-~9%++_a_L}V#;df1)tK;Eyw1u2IFg+Qewd8_=PR{< zG0;uHw#JW6oH&pZspStEf;1!TNGo^O2G)pZXINpl#+CgpRv5sa6Af<f{BpX;E$NC*|PUq?L8a`;MEo=wAT zoN(+UyktQy7u%%DtTYxq1pAlm1`%V(_9lMb7LASx^6xy6oK;-aIb#0#GsMD4B{9ACoe0!Jab2piW_?{iav7 zdr|6_YvEH~L8y6Z%(4CUDBWYFPwiMcA!73!H^N{uF`zS#1V&C5EXUp|TEcY1 zu83^VMQjfTbjD|!rcx)4vZS=Nnuksilg-^ye?4@Ri*@8v5lQ5mK~@AJXXxip`F7F1uT=+Mbi#8|5=DZrmHtg0we_4&pl`JLEDCE&77|T7*d`QH#reTBL{H2$y zsP10ATn@D$pUwS56`{;&PIB7?!$Ar3!mSm1F&c7h_lmvZn#VtT`DeNH?JsaZ^%o{r z{wI4Q|6~SoL$;8q>;H-)knR{-e2S=R?j~^IfumIHz&aIb=B^o}rQzn?f+fr`H*%We z`dCL>6?9};_&y4@MMHGDQ1R^3I~52?c92-EWipnysVz_U>1Gmu^jvk4;S$@ zvoHSuL913rRB0ADWLgbIP6px2tGl=nij?_Q59$&oY3vmP`wPrHY9Jgm55<`&sw`tF z>hUpJPS8`{K*&i$qY6kWKWgZ8wpm!)N^z;Sd)rLWT8P-cV6zJ_^ zqLZ(RcO)zhJf$Jx-3(Ii)0MzYiDMGTH*SRY16&BY0c~0kVv6t`KXdyW3w`w-_&jVO zjBlrly=>Ay zm5K}4W$Rq2R1E zdn7J49n2J&&LHAuUiV*0w1B1twvG~0B(&kCo*P#`c0fX93CmAG+})&x&U5wmS7t>q zy&EE&;L< zyk%Jt@)oF@v=Vp*FUP_%aF~T38@E1E_hmwDYB%9+ZUtWT@dr7XU`!59FE;^{D70m4 zaN>ntubVi56v@o{80?qgn&PL@HcVMQEM14bs)@!MZaHrBhKhanbO7jGppcy+jk{Ks z)?4T}Oa08{65LZbI5~*RjUPf>E(dWr2A0rpW^1@y{3^ud3Nk+{#9y`(_6gWJKi>HX zuPPWZs{Le`(W$p@vytS@I9!0il;yS&j=+y?c>m6K@YM4!-j6XIM|56rbSAX{IXIy6 zj)a95(LZp_vM+h$Q4iU&25Bl{X&W;mcB)~zTn>ouYWJ{NgXQG*|AXbJH=xN6*ceSe zVdJ{4;k6T(=)vg0!dISwR3wv!FkEcXMi5TI83-U@p!3BN>8tYI{MW+pA5Y>EehVet z+B~^}QWE+DI^azMUssJfsly{>{-h=}_uBiXproXzY?6}iY5F2&=y&b)TZ_VOO*BTUT%4(c~xcYBa5A2|mWXwh1I9|wg ze5&%s<0`!BhaMWTl~Iec#Pfd`JGlCW|G9TnkHrFM(QXy3JZ1BCv~&!4W~RNG$so1* zz1*wDL@QgSEX#EO7RduM22||Q{XP~-1?K?PTprcF`dD{OBNjEhf~v^&?S42-#1nT! zeOp81?=}o_AR>=4;p%7g3YPQCF+C!OnP}Td{M4q_r-ZYma9dD1o8ksqar2 zFXHCqMlRvLEUI9(VADj{Ha4(wC14UEUx#=7b7&_}d^OP7$NnpUDs1T4wa~MJ7GMb9 zc}LJoBw~v0!$Fj8npA0^M{zHzct-F<=AUDx=JlO(B@;PW^Q}_@U};`%j2t8o0T(wBUbkD+zk?MH)uLH6B$iG%%THNmzAx-@+XUzxCC2YzMFF|@!poq zzI$Z-71X$p zc|e_?QR0@i$R(s>dV&3gRszB4(poqn1m9xg{@m5%6>uzq?*1_Q0`W}!V0Oaq$ZkSH zb_)pq@L8s4E5zmZ=xm()e&S}6TOXu!fe0UdDmadbW<=wswjB2_jC4ZyA@U48p2 ztQPU(o~}M?`m8Id-YaD4vCfkmK zYfQqky0>^k)c|WKeGE~Jc6_#FuyF-X?ssY@bRIGkE(cJH<>*O4>YdUn* zJ9Kf_8Z3Wag>U~IRj+TBPg^7fupvAwj!DLo@9%p_yD6jW0=t55S*kBpY}_e+lfp#PycQ?4(N&qo zh`j#{o~ojur46u~uo$NddS@8j=T9_lj%4I8U$QGJv4>Omg}|7Bm)B#VB<-ZWH3zr; z+{r{KRim!kMc6n#d7_c;6)qv}6d>>DAcW48ER$kcgQ949(jKp#&- zAk+n=2r9;$x$=K#Jj5d##1;ldK4a?fDgROFV&fcsMr)Nsjka`j01=ol2S$@{IEez_ z(*r}w=sCPNU;Y)fdBx7u7N`QrCWR8%f%nxz&MW>M()wvQ|1sKNt3zkD>A&*-iIx}d(9$bPy z1mnfZh0H~58HhzxeId(ybK-{XuBqIe;wFt=SC4YxN*g)*R(;R?eW_8x-y((c+`(j(M2beq+YZUQ zb80rALm5gO+>iZAA<8hSWU4%NR=L&%6q?W6yBvzMwHxP>kAI>!23)g)xpO-ed)gfW z!;NdeuqYA7(Ww&)F-9-wQ&-S(IBD=`It)(?((9wE%XMAcFc`ta$M`&AvQ+M z%VgLA^67RiAshnn8!hAHZ{ZTe0G}1;>GxnT4?I|zh;s^b2Us%~V()M0;X4AD)T)gv zRBY})Ab$N)IBqk;ZzWJT*4n~#*NXk&Tp}o04}rKo9Q453y<_&%Au{xf1tt3mney7Q z(C7>j9Cz9zgsp3M~%9}*`%IRdaDHZJ$fsN|30<)gDG zggj|Yz&=%J^(u@EOTK)8tFd&S9pmJQ>*z32^Kz8z2aP_U8;V1Io?k^z3ndBB|E{1- zmNG1Q+8SD!7*(J1>AhPGmSD7hpZy&Ko`d%kE&l`hCh}KNZ6#)(?8Y_H>0=|<3{uvY zz2Uvm4O5m^LJBU=cj-jS#g>W8GlFzdb#C!s!bd1EwF^B0GK(4r0STWMc;`!+HaWVw z4K=~MD+{MhY;$|@?ZS~9T!1-|!`q{}2Q^+}`;$XIf!<&_D2YqKk*A>i>pg!2Zfpu) z3e<-o_4z0bqd=hQ5~;Um92@^}0N5W-#ckGuF-FoDy`c|NgO3j0pFt_s{K~*JYiz;m zczjzX^@iUgjh83l1T@=q5k!NE7#7TvdS!JnW<38v@l<#fM>RC*l&SL`dCLDqG_6grHH}hMY3S~BQA61Ar zy%Q0J_8(@bcY94EMVI4nP9O1%i*X#B;%1_J%|G}alUfqqqE)}vxt>{m8UOAL^6 zUQx?IB97eM;^v<|FzzA-dB)D*$#6FAbaCBYdGwQX+3e{XPb_Y15Dp$~ zT0<%k`B#ZS(v8T610d6)O!oZgDHzmaM?{L#AJa=V!t!_z^l!u5QS607@?%ZU;6U3d z3@TyrO3!uw<392hYk^#&-G>ivxo7|U%;K(LvXN^Z@QJ7cRQFn-z5v}DvV-;!p0&hD zwu>D@KA06NuOB?Kf)_t~rk(KT5Luu+bDl}H+rdPA?JZlpzD{Jm6zc{aa`PpM`w6S2 zc*F04EA}_GF-YU0Opk}Tm9`A^DwQP?GZLSeIg z^fPt2gy>__H}0)@y?UGqQ{-_2Rp)=@@xUWS|KadB7=z9pnSmx2YfL$(Wo*PCZHBFg zAJ6F}V&m8;nKfI#{f}m9N(gaq!jNWe*?OvU=r)AkEn52j<;+e*WQ8gr!>XZeM9-?K zaB$+nT*5ZUdwy}x!F4#in<12k!TZA8gf|B_ZonmU^J{x+zmE90da$OX57@(LvFnFw z)?EqyF@59$zy__kc(QcS_Rdq^=SoflWrLZpuol8zCs{G2swgofk=Dk5tn#w)5Uv2igqsaFlEYR>nG4Yhvuo+fWf^SpOTOhx#P zszi4DD2?!a1D8Fj%=wJld~9=b^!EKTi@E=HjO3u}={Ya>zI<6s3Y8-ru8RGA^rU{z z;;-*_3Xu#8pHKUa-aJ?nR=B_D4)Z|)v8?#jii@YfvWz9ro*gXkvW3rMToMZokh@-zs$|CgNCflitUdy`MT7ou{+i z5_~JwXrr0Dcu?mlG@8*9O)p7cY{OiWT^3)@fOtJe1IeL=RtKZh2t>0&q>>H%O{dIGFs`!bh z6IJ~B*HFj#b~y*%+vVf})?7FL)4bNpDl3N7vHLvv?wo%VTzA*v=hW2)2V$0$qb6%a zW~Qhcl7fg9XVM9O9%;A#8^IpX+O^C3v-QcJ1n( zjZ1N~&_{Z%%5EpNM#_6m+W);;fSuPSyGi=+*iU1Pd#kY5L>k@rt2flUvP@kDM^Ep~ zj@8BoZaOgZ*ri*0<7`jewuZPr?U61iLWYVin*35f{#Ik9m!2oa>200Ze9`r(6$~ieRj(MrKdz6d{_w9?m>jrzn)*0b{)MxnJ9(O?ULC^@|cWOCT0QD z+R-_;5BnaPV{)rEHG^f5GCh%UmlZtfaI}57^AJn&N)4T9T)c@6M$|SQd0I8kyjZiT z8!Vq14ZeAzYtDa#>uTtDbId?uWp+e{UMrM!%tIGdpfhP0fOUj-3kRv?Hm819Y zm?L{1pHX?&ylncz)-iw7nW&!=>h@N&eyT{k+lDm@msR|J3Ll z^wlRk;<|m$9p9Qn+DyvJ5M(;^)xXq2<&5-}rbqeDn8fg?$2bSNk&Y0byBlxn+r~Ix zH6!`&3XyQxZ8JfYc^_Y>X8%0uv3CQ{*|M&GYI7Wa+e3AKXC#ObYQoGvO;(iPPkCa* z<34uu-zod<1|FVb-n2V6%0l|hiKvPKJ;dwqaB$OG8;8Ev>jkzj)8-9>l~#-=G8&fM z$>;EZ*Z57@q(~^ub(YI0bD`wM7ZgZUk7}II^ zwIhKukWE9>8s;&^=7Yzj4ZGGJJz~cnn&P=4KfvI`KfcRt-V{aamN97M^47Yt4zEas zHEV<1%q_P!9%(s3a9~OCxdHc`JO4UKuRb)7;;FRro{0wR4*vUU;Fe-m>}L z$A=OxM3o^XZpM=)S8kK)?BlFmpFiQmZw_eA{}Er$ZQk%{>__wI0pR!mV_bW8!xBd1 zfR6ERMv-X^X~uUNaaLVdT^K_IW$Og#A4|DTb0xo%vIBS6k>=KVgV)YDgzc0;SB zO&~2*De%^1Zu2czQ(C$4f-BfYGq?2a_7P>VDfT%R;2u&X3%_0xW^UjFXbiT&O99m+b#G%{ujMB0X&WR z?f8<8ku3{2UHrhJSV>s*+Avef$TWbmjeZj}eh9)HH1)rK9-$CPs)7dPUGC0$(%%pcgT-PLAGsh`Cq;I0R$pJXH_ ze(*MvdT0|%>I3~h>euQ8doZrv%q3sFC5#4~F6wncL7MO+XQ5FAjJZ`)Yih$Yqzq4v zAst7(?t8!*7!*jsQdNwJo4#x4!0ojrOv<0VOev_4GFJg|LfEeVDWp1DVTo*EU2k4Y z0`PlUyh4>%B0eve$B0F_UG}6RYTIGMF*OP2u8bNJR(Qae>_@N@_FyVa7BFJT+~#nh zB>HCxB)c1fTuGuHD@!>N6QsdJP!`%iu;fhxTCmJ_$aDnhxKRtrMy(ey7aiy&(Ysd6 zAQn!7MZC{(jz0v;dFPn+x=>S-=NqWl5=UWlD(KKL7tNf){v#s!+VkvqhyBDek0~!P z(Ky~qoM{BgiRN>|;L;V}pF6#X<86;P@fJ^UHPZL?kCA!zboz$OJ(=?fvV#VHask$` zAlI&YA_1=z7*~!HU{$GK8&;LdVe2N%HWg>I3Jq`JLKOZR!HM6?#DAEHhsTbRoRGKU znad*6NS6HEjM;?{;LcO9jHSM1$NP_GNTqJlMLn{bz+WpbZcRlCXlXb)0b8lNqDnm6 zuuWd<_?yS3%`dE-h(W3f)1EIXz|-xeSlP{UYBK8G;4lB%hzV&hCb4k8@|aCWv2BNy z@D8gEup$lx4ZD=l!#2e68w)$Q&DCB&E-TTmO|1u{UHpd>%hAwm=#m-+`?bDqNB{Ju zpspX9pusB}^y_a@k=rop99Lz>zw2X3)xz4wJd_A!U;irQ*6re|)13@09_cu?WAMy$ z^_2IoDv16CJ$X$6b%JMC#WWo#NQ2YYgx2I}q6)Se9@rTUNnKPQBH#LqCu2$)MQHq;dIP|b)W>IZ)HYG;nPP0o@ z75Q)7*WV-!kh`cWki|u(yge2@r@YIpR$SkeR*$T@OnFB?$~9Lzw0hf%u67YoSF_*kpX|F_?ii%dXC>Dc z6)kx?slOi|z*9E%Bk9=8qXy?RM~_&?K$S{zPu*%p(DW8&zJFi;!1i7J{k!j85$x+P zmVE2vYIir|R!HY)1?9`hxW3X-C(8bk(tkB)oLzTa7wqppa%)pKu8Th)uiLC`P9hXv^sXqN!f27Ie{LlrQ@^LS*7WH?!Yr?9pya#D%aXjx=Z|78!%YE=t|r0 z1J84fom2Uwqw=yY$_BX-V-`2|Uv8^yLaP71qAqyZh+aO2F{EM3i=_I4Q)XXhyVr-8eB8EW88 z2iUA&RNHB;wVgB)PrFR&&CGwKl&53a-5)5RSZ&c&pv0YPEuIH-j*fsw&nmk>I>=we z2*C1u*FLrkp7@(P`C<{-ceFTY%b*6HsGd;%6zuXOMcjZz@uMdy*9`v7s0~ba*^nxQ`CkabE;n(EW

DGL& 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

B+?Da~w`|Dt??kPf`Bn<0*Uq`tQTA%B|%C|M4 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,&rCZ>M1 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

`|+VX~-vl3J;!P|jnGqQbCRf)kX!XGX3HPQ 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
xvnO!zY>doFg6q?=qLt0;Xo31j`_$LENwTw& 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 literal 0 HcmV?d00001 diff --git a/build_helpers/TA_Lib-0.4.21-cp39-cp39-win_amd64.whl b/build_helpers/TA_Lib-0.4.21-cp39-cp39-win_amd64.whl new file mode 100644 index 0000000000000000000000000000000000000000..da9d745583f4ae12b0c22050470c9f9511417de4 GIT binary patch literal 508461 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

    ?DS>yQZpk_DF%w$x87G}_L5g* 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

    kxJOj5DawW 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

    E$5do2yih$A}AfY0ygcA`^8P1|hx&#bT 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 B(z0O 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 literal 0 HcmV?d00001 diff --git a/build_helpers/install_windows.ps1 b/build_helpers/install_windows.ps1 index d2a27dd26..ec38ea212 100644 --- a/build_helpers/install_windows.ps1 +++ b/build_helpers/install_windows.ps1 @@ -6,10 +6,13 @@ python -m pip install --upgrade pip $pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" if ($pyv -eq '3.7') { - pip install build_helpers\TA_Lib-0.4.20-cp37-cp37m-win_amd64.whl + pip install build_helpers\TA_Lib-0.4.21-cp37-cp37m-win_amd64.whl } if ($pyv -eq '3.8') { - pip install build_helpers\TA_Lib-0.4.20-cp38-cp38-win_amd64.whl + pip install build_helpers\TA_Lib-0.4.21-cp38-cp38-win_amd64.whl +} +if ($pyv -eq '3.9') { + pip install build_helpers\TA_Lib-0.4.21-cp39-cp39-win_amd64.whl } pip install -r requirements-dev.txt diff --git a/docs/windows_installation.md b/docs/windows_installation.md index edc0a1404..2db0ae913 100644 --- a/docs/windows_installation.md +++ b/docs/windows_installation.md @@ -23,7 +23,7 @@ git clone https://github.com/freqtrade/freqtrade.git Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows). -As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which needs to be downloaded and installed using `pip install TA_Lib‑0.4.20‑cp38‑cp38‑win_amd64.whl` (make sure to use the version matching your python version). +As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which need to be downloaded and installed using `pip install TA_Lib-0.4.21-cp38-cp38-win_amd64.whl` (make sure to use the version matching your python version). Freqtrade provides these dependencies for the latest 2 Python versions (3.7 and 3.8) and for 64bit Windows. Other versions must be downloaded from the above link. From b0bfbb6558cb0a569fee52fbb2dbed2a20e73397 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Mon, 19 Jul 2021 11:37:52 -0600 Subject: [PATCH 143/519] removed buy and sell merge, updated strategy name, removed default side for get_rate --- freqtrade/exchange/exchange.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 65108ca34..91b278077 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -999,7 +999,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def get_rate(self, pair: str, refresh: bool, side: str = "buy") -> float: + def get_rate(self, pair: str, refresh: bool, side: str) -> float: """ Calculates bid/ask target bid rate - between current ask price and last price @@ -1021,16 +1021,16 @@ class Exchange: logger.debug(f"Using cached {side} rate for {pair}.") return rate - strategy = self._config.get(strat_name, {}) + conf_strategy = self._config.get(strat_name, {}) - if strategy.get('use_order_book', False) and ('use_order_book' in strategy): + if conf_strategy.get('use_order_book', False) and ('use_order_book' in conf_strategy): - order_book_top = strategy.get('order_book_top', 1) + order_book_top = conf_strategy.get('order_book_top', 1) order_book = self.fetch_l2_order_book(pair, order_book_top) logger.debug('order_book %s', order_book) # top 1 = index 0 try: - rate = order_book[f"{strategy['price_side']}s"][order_book_top - 1][0] + rate = order_book[f"{conf_strategy['price_side']}s"][order_book_top - 1][0] except (IndexError, KeyError) as e: logger.warning( f"{name} Price at location {order_book_top} from orderbook could not be " @@ -1038,18 +1038,18 @@ class Exchange: ) raise PricingError from e - logger.info(f"{name} price from orderbook {strategy['price_side'].capitalize()}" + logger.info(f"{name} price from orderbook {conf_strategy['price_side'].capitalize()}" f"side - top {order_book_top} order book {side} rate {rate:.8f}") else: - logger.info(f"Using Last {strategy['price_side'].capitalize()} / Last Price") + logger.info(f"Using Last {conf_strategy['price_side'].capitalize()} / Last Price") ticker = self.fetch_ticker(pair) - ticker_rate = ticker[strategy['price_side']] + ticker_rate = ticker[conf_strategy['price_side']] if ticker['last']: if side == 'buy' and ticker_rate > ticker['last']: - balance = strategy['ask_last_balance'] + balance = conf_strategy['ask_last_balance'] ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) elif side == 'sell' and ticker_rate < ticker['last']: - balance = strategy.get('bid_last_balance', 0.0) + balance = conf_strategy.get('bid_last_balance', 0.0) ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last']) rate = ticker_rate From 9e63bdbac9b866154fbabb8d75c48c63d26c09a2 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 04:58:20 +0700 Subject: [PATCH 144/519] feat: add buy signal name --- freqtrade/enums/__init__.py | 2 +- freqtrade/enums/signaltype.py | 7 +++++++ freqtrade/freqtradebot.py | 9 +++++---- freqtrade/optimize/backtesting.py | 4 +++- freqtrade/persistence/models.py | 3 +++ freqtrade/strategy/interface.py | 10 ++++++---- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 78163d86f..bcf68d622 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -2,5 +2,5 @@ from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType -from freqtrade.enums.signaltype import SignalType +from freqtrade.enums.signaltype import SignalType, SignalNameType from freqtrade.enums.state import State diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index d636f378a..5ba2f8b4b 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -7,3 +7,10 @@ class SignalType(Enum): """ BUY = "buy" SELL = "sell" + + +class SignalNameType(Enum): + """ + Enum to distinguish between buy and sell signals + """ + BUY_SIGNAL_NAME = "buy_signal_name" diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7a7371357..037bb954a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -420,7 +420,7 @@ class FreqtradeBot(LoggingMixin): return False # running get_signal on historical data fetched - (buy, sell) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df) + (buy, sell, buy_signal_name) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df) if buy and not sell: stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge) @@ -435,11 +435,11 @@ class FreqtradeBot(LoggingMixin): if ((bid_check_dom.get('enabled', False)) and (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): if self._check_depth_of_market_buy(pair, bid_check_dom): - return self.execute_buy(pair, stake_amount) + return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) else: return False - return self.execute_buy(pair, stake_amount) + return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) else: return False @@ -468,7 +468,7 @@ class FreqtradeBot(LoggingMixin): return False def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False) -> bool: + forcebuy: bool = False, buy_signal_name: str = '') -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY @@ -562,6 +562,7 @@ class FreqtradeBot(LoggingMixin): exchange=self.exchange.id, open_order_id=order_id, strategy=self.strategy.get_strategy_name(), + buy_signal_name=buy_signal_name, timeframe=timeframe_to_minutes(self.config['timeframe']) ) trade.orders.append(order_obj) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7c6b7cbc3..2b8bd1e2b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -42,6 +42,7 @@ CLOSE_IDX = 3 SELL_IDX = 4 LOW_IDX = 5 HIGH_IDX = 6 +BUY_SIGNAL_NAME_IDX = 7 class Backtesting: @@ -189,7 +190,7 @@ class Backtesting: """ # Every change to this headers list must evaluate further usages of the resulting tuple # and eventually change the constants for indexes at the top - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_signal_name'] data: Dict = {} # Create dict with data for pair, pair_data in processed.items(): @@ -332,6 +333,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, + buy_signal_name=row[BUY_SIGNAL_NAME_IDX], exchange='backtesting', ) return trade diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index b4c299120..8d77ac27a 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -257,6 +257,7 @@ class LocalTrade(): sell_reason: str = '' sell_order_status: str = '' strategy: str = '' + buy_signal_name: str = '' timeframe: Optional[int] = None def __init__(self, **kwargs): @@ -288,6 +289,7 @@ class LocalTrade(): 'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None, 'stake_amount': round(self.stake_amount, 8), 'strategy': self.strategy, + 'buy_signal_name': self.buy_signal_name, 'timeframe': self.timeframe, 'fee_open': self.fee_open, @@ -703,6 +705,7 @@ class Trade(_DECL_BASE, LocalTrade): sell_reason = Column(String(100), nullable=True) sell_order_status = Column(String(100), nullable=True) strategy = Column(String(100), nullable=True) + buy_signal_name = Column(String(100), nullable=True) timeframe = Column(Integer, nullable=True) def __init__(self, **kwargs): diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7aa7e57d9..bd77fbb21 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,7 @@ from pandas import DataFrame from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import SellType, SignalType +from freqtrade.enums import SellType, SignalType, SignalNameType from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date @@ -506,7 +506,9 @@ class IStrategy(ABC, HyperStrategyMixin): ) return False, False - (buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1 + (buy, sell, buy_signal_name) = latest[SignalType.BUY.value] == 1,\ + latest[SignalType.SELL.value] == 1,\ + latest.get(SignalNameType.BUY_SIGNAL_NAME.value, '') logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) @@ -514,8 +516,8 @@ class IStrategy(ABC, HyperStrategyMixin): current_time=datetime.now(timezone.utc), timeframe_seconds=timeframe_seconds, buy=buy): - return False, sell - return buy, sell + return False, sell, buy_signal_name + return buy, sell, buy_signal_name def ignore_expired_candle(self, latest_date: datetime, current_time: datetime, timeframe_seconds: int, buy: bool): From 104711a9bf80b72e3541af00e2f78b5c22aaaa49 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 05:04:25 +0700 Subject: [PATCH 145/519] get_signal signature --- freqtrade/strategy/interface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index bd77fbb21..cbd19e156 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -478,7 +478,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: raise StrategyError(message) - def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool]: + def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, str]: """ Calculates current signal based based on the buy / sell columns of the dataframe. Used by Bot to get the signal to buy or sell @@ -489,7 +489,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ if not isinstance(dataframe, DataFrame) or dataframe.empty: logger.warning(f'Empty candle (OHLCV) data for pair {pair}') - return False, False + return False, False, '' latest_date = dataframe['date'].max() latest = dataframe.loc[dataframe['date'] == latest_date].iloc[-1] @@ -504,7 +504,7 @@ class IStrategy(ABC, HyperStrategyMixin): 'Outdated history for pair %s. Last tick is %s minutes old', pair, int((arrow.utcnow() - latest_date).total_seconds() // 60) ) - return False, False + return False, False, '' (buy, sell, buy_signal_name) = latest[SignalType.BUY.value] == 1,\ latest[SignalType.SELL.value] == 1,\ From 550a9de097b62c89365a0c69dcdddc1bb9174022 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Mon, 19 Jul 2021 15:01:32 -0600 Subject: [PATCH 146/519] Fixed setup for python3.9 on ubuntu --- setup.sh | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/setup.sh b/setup.sh index 3bcbfc48d..a85bd3104 100755 --- a/setup.sh +++ b/setup.sh @@ -4,8 +4,12 @@ function check_installed_pip() { ${PYTHON} -m pip > /dev/null if [ $? -ne 0 ]; then - echo "pip not found (called as '${PYTHON} -m pip'). Please make sure that pip is available for ${PYTHON}." - exit 1 + echo "-----------------------------" + echo "Installing Pip for ${PYTHON}" + echo "-----------------------------" + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + ${PYTHON} get-pip.py + rm get-pip.py fi } @@ -17,13 +21,12 @@ function check_installed_python() { exit 2 fi - for v in 8 9 7 + for v in 9 8 7 do PYTHON="python3.${v}" which $PYTHON if [ $? -eq 0 ]; then echo "using ${PYTHON}" - check_installed_pip return fi @@ -136,7 +139,7 @@ function install_macos() { /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" fi #Gets number after decimal in python version - version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g' ) + version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g') if [[ $version -ge 9 ]]; then #Checks if python version >= 3.9 install_mac_newer_python_dependencies @@ -147,7 +150,7 @@ function install_macos() { # Install bot Debian_ubuntu function install_debian() { sudo apt-get update - sudo apt-get install -y build-essential autoconf libtool pkg-config make wget git libpython3-dev + sudo apt-get install -y build-essential autoconf libtool pkg-config make wget git $(echo lib${PYTHON}-dev ${PYTHON}-venv) install_talib } @@ -236,12 +239,12 @@ function install() { } function plot() { -echo " ------------------------------------------ -Installing dependencies for Plotting scripts ------------------------------------------ -" -${PYTHON} -m pip install plotly --upgrade + echo " + ----------------------------------------- + Installing dependencies for Plotting scripts + ----------------------------------------- + " + ${PYTHON} -m pip install plotly --upgrade } function help() { From 7d040052189d94884609bd004796f53d81ecc437 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 16:14:48 +0700 Subject: [PATCH 147/519] add test and migration --- freqtrade/enums/signaltype.py | 2 +- freqtrade/freqtradebot.py | 2 +- freqtrade/persistence/migrations.py | 7 ++++--- tests/strategy/test_interface.py | 19 ++++++++++++------- tests/test_freqtradebot.py | 2 +- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index 5ba2f8b4b..6f29699f1 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -11,6 +11,6 @@ class SignalType(Enum): class SignalNameType(Enum): """ - Enum to distinguish between buy and sell signals + Enum for signal name """ BUY_SIGNAL_NAME = "buy_signal_name" diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 037bb954a..852d5bf78 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -690,7 +690,7 @@ class FreqtradeBot(LoggingMixin): analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, self.strategy.timeframe) - (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) + (buy, sell, _) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) logger.debug('checking sell') sell_rate = self.exchange.get_sell_rate(trade.pair, True) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 00c9b91eb..4fa1cb742 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -47,6 +47,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col min_rate = get_column_def(cols, 'min_rate', 'null') sell_reason = get_column_def(cols, 'sell_reason', 'null') strategy = get_column_def(cols, 'strategy', 'null') + buy_signal_name = get_column_def(cols, 'buy_signal_name', 'null') # If ticker-interval existed use that, else null. if has_column(cols, 'ticker_interval'): timeframe = get_column_def(cols, 'timeframe', 'ticker_interval') @@ -80,7 +81,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col stake_amount, amount, amount_requested, open_date, close_date, open_order_id, stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct, stoploss_order_id, stoploss_last_update, - max_rate, min_rate, sell_reason, sell_order_status, strategy, + max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_signal_name, timeframe, open_trade_value, close_profit_abs ) select id, lower(exchange), @@ -103,7 +104,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col {stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update, {max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason, {sell_order_status} sell_order_status, - {strategy} strategy, {timeframe} timeframe, + {strategy} strategy, {buy_signal_name} buy_signal_name, {timeframe} timeframe, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs from {table_back_name} """)) @@ -160,7 +161,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None: table_back_name = get_backup_name(tabs, 'trades_bak') # Check for latest column - if not has_column(cols, 'open_trade_value'): + if not has_column(cols, 'open_trade_value') or not has_column(cols, 'buy_signal_name'): logger.info(f'Running database migration for trades - backup: {table_back_name}') migrate_trades_table(decl_base, inspector, engine, table_back_name, cols) # Reread columns - the above recreated the table! diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 2beb4465d..c02a43e72 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -37,15 +37,20 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): mocked_history['buy'] = 0 mocked_history.loc[1, 'sell'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, '') mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, '') mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 0 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, '') + mocked_history.loc[1, 'sell'] = 0 + mocked_history.loc[1, 'buy'] = 1 + mocked_history.loc[1, 'buy_signal_name'] = 'buy_signal_01' + + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, 'buy_signal_01') def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): @@ -62,15 +67,15 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) + assert (False, False, '') == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) + assert (False, False, '') == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False) == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) + assert (False, False, '') == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) assert log_has('Empty candle (OHLCV) data for pair baz', caplog) @@ -106,7 +111,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) + assert (False, False, '') == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 99e11e893..2309c96f0 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -736,7 +736,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: refresh_latest_ohlcv=refresh_mock, ) inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")]) - mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False)) + mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False, '')) mocker.patch('time.sleep', return_value=None) freqtrade = FreqtradeBot(default_conf) From ec526b3f963792e78bafaf772e01dacbdb92ba51 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 16:22:04 +0700 Subject: [PATCH 148/519] fix testcase --- tests/conftest.py | 2 +- tests/test_freqtradebot.py | 42 +++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index a843d9397..58af2fe90 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -182,7 +182,7 @@ def get_patched_worker(mocker, config) -> Worker: return Worker(args=None, config=config) -def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None: +def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False, '')) -> None: """ :param mocker: mocker to patch IStrategy class :param value: which value IStrategy.get_signal() must return diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 2309c96f0..cc89ce00c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -515,7 +515,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None: ) default_conf['stake_amount'] = 10 freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(False, False)) + patch_get_signal(freqtrade, value=(False, False, '')) Trade.query = MagicMock() Trade.query.filter = MagicMock() @@ -1818,7 +1818,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limi assert trade.is_open is True freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.open_order_id == limit_sell_order['id'] @@ -1843,7 +1843,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, ) freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.enter_positions() @@ -1854,7 +1854,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert nb_trades == 0 # Buy is triggering, so buying ... - patch_get_signal(freqtrade, value=(True, False)) + patch_get_signal(freqtrade, value=(True, False, '')) freqtrade.enter_positions() trades = Trade.query.all() nb_trades = len(trades) @@ -1862,7 +1862,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are not triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(False, False)) + patch_get_signal(freqtrade, value=(False, False, '')) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1870,7 +1870,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1878,7 +1878,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Sell is triggering, guess what : we are Selling! - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) trades = Trade.query.all() assert freqtrade.handle_trade(trades[0]) is True @@ -1896,7 +1896,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, ) freqtrade = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtrade, value=(True, False)) + patch_get_signal(freqtrade, value=(True, False, '')) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) freqtrade.enter_positions() @@ -1909,7 +1909,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, # we might just want to check if we are in a sell condition without # executing # if ROI is reached we must sell - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) @@ -1935,10 +1935,10 @@ def test_handle_trade_use_sell_signal( trade = Trade.query.first() trade.is_open = True - patch_get_signal(freqtrade, value=(False, False)) + patch_get_signal(freqtrade, value=(False, False, '')) assert not freqtrade.handle_trade(trade) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) @@ -2974,7 +2974,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is False freqtrade.strategy.sell_profit_offset = 0.0 @@ -3009,7 +3009,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, limit_bu trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3040,7 +3040,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, limit_buy_o trade = Trade.query.first() trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is False @@ -3072,7 +3072,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, limit_buy_ trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3101,7 +3101,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ trade = Trade.query.first() amnt = trade.amount trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985)) assert freqtrade.handle_trade(trade) is True @@ -3220,11 +3220,11 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) assert freqtrade.handle_trade(trade) is False # Test if buy-signal is absent (should sell due to roi = true) - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.ROI.value @@ -3485,11 +3485,11 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b trade = Trade.query.first() trade.update(limit_buy_order) # Sell due to min_roi_reached - patch_get_signal(freqtrade, value=(True, True)) + patch_get_signal(freqtrade, value=(True, True, '')) assert freqtrade.handle_trade(trade) is True # Test if buy-signal is absent - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -4017,7 +4017,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.wallets.update() assert trade.is_open is True - patch_get_signal(freqtrade, value=(False, True)) + patch_get_signal(freqtrade, value=(False, True, '')) assert freqtrade.handle_trade(trade) is True assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0] From d31d38a85f3a274c9a8f989c80c3a37b052b596c Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 16:39:20 +0700 Subject: [PATCH 149/519] add doc --- docs/strategy-advanced.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 3436604a9..dd38d2cbc 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -114,6 +114,32 @@ class AwesomeStrategy(IStrategy): See [Dataframe access](#dataframe-access) for more information about dataframe use in strategy callbacks. +## Buy Signal Name + +When your strategy has multiple buy signal, you can name it. +Then you can access you buy signal on `custom_sell` + +```python +def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['rsi'] < 35) & + (dataframe['volume'] > 0) + ), + ['buy', 'buy_signal_name']] = 1, 'buy_signal_rsi' + + return dataframe + +def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + if trade.buy_signal_name == 'buy_signal_rsi' and last_candle['rsi'] > 80: + return 'sell_signal_rsi' + return None + +``` + ## Custom stoploss The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss. From ed30c023cdb3d2d1c655f75b242a84f78a712ca7 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 19:08:14 +0700 Subject: [PATCH 150/519] fix some testcase --- freqtrade/optimize/backtesting.py | 2 + freqtrade/strategy/interface.py | 1 + tests/optimize/test_backtesting.py | 1 + tests/plugins/test_pairlist.py | 10 +-- tests/rpc/test_rpc.py | 36 ++++---- tests/rpc/test_rpc_apiserver.py | 138 ++++++++++++++--------------- tests/rpc/test_rpc_telegram.py | 68 +++++++------- tests/test_freqtradebot.py | 34 +++---- 8 files changed, 148 insertions(+), 142 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 2b8bd1e2b..35e524f69 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -197,6 +197,7 @@ class Backtesting: if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist + pair_data.loc[:, 'buy_signal_name'] = '' # cleanup if buy_signal_name is exist df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() @@ -205,6 +206,7 @@ class Backtesting: # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1) + df_analyzed.loc[:, 'buy_signal_name'] = df_analyzed.loc[:, 'buy_signal_name'].shift(1) df_analyzed.drop(df_analyzed.head(1).index, inplace=True) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index cbd19e156..6ff499199 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -404,6 +404,7 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug("Skipping TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 + dataframe['buy_signal_name'] = '' # Other Defs in strategy that want to be called every loop here # twitter_sell = self.watch_twitter_feed(dataframe, metadata) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 30d86f979..102d62273 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -482,6 +482,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: 0, # Sell 0.00099, # Low 0.0012, # High + '', # Buy Signal Name ] trade = backtesting._enter_trade(pair, row=row) assert isinstance(trade, LocalTrade) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index ae8f6e958..3dee47599 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -769,20 +769,20 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo get_tickers=tickers ) - ftbot = get_patched_freqtradebot(mocker, default_conf) - ftbot.pairlists.refresh_pairlist() + freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade.pairlists.refresh_pairlist() - assert len(ftbot.pairlists.whitelist) == 5 + assert len(freqtrade.pairlists.whitelist) == 5 tickers.return_value['ETH/BTC']['ask'] = 0.0 del tickers.return_value['TKN/BTC'] del tickers.return_value['LTC/BTC'] mocker.patch.multiple('freqtrade.exchange.Exchange', get_tickers=tickers) - ftbot.pairlists.refresh_pairlist() + freqtrade.pairlists.refresh_pairlist() assert log_has_re(r'Removed .* invalid ticker data.*', caplog) - assert len(ftbot.pairlists.whitelist) == 2 + assert len(freqtrade.pairlists.whitelist) == 2 @pytest.mark.parametrize("pairlistconfig,desc_expected,exception_expected", [ diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index cc29dc157..3d1896a4d 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -35,7 +35,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -69,6 +69,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, + 'buy_signal_name': ANY, 'timeframe': 5, 'open_order_id': ANY, 'close_date': None, @@ -135,6 +136,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, + 'buy_signal_name': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, @@ -190,7 +192,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: ) del default_conf['fiat_display_currency'] freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -237,7 +239,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -369,7 +371,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -457,7 +459,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -524,7 +526,7 @@ def test_rpc_balance_handle_error(default_conf, mocker): ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() with pytest.raises(RPCException, match="Error getting current tickers."): @@ -565,7 +567,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): ) default_conf['dry_run'] = False freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -610,7 +612,7 @@ def test_rpc_start(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -631,7 +633,7 @@ def test_rpc_stop(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -653,7 +655,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -685,7 +687,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -803,7 +805,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) # Create some test data @@ -836,7 +838,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) counts = rpc._rpc_count() @@ -861,7 +863,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'ETH/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -887,7 +889,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> # Test not buying freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot.config['stake_amount'] = 0.0000001 - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'TKN/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -900,7 +902,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'trader is not running'): @@ -911,7 +913,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'Forcebuy not enabled.'): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index b8dd112c9..1ab7f178e 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -45,11 +45,11 @@ def botclient(default_conf, mocker): "password": _TEST_PASS, }}) - ftbot = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(ftbot) + freqtrade = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(freqtrade) mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock()) apiserver = ApiServer(rpc, default_conf) - yield ftbot, TestClient(apiserver.app) + yield freqtrade, TestClient(apiserver.app) # Cleanup ... ? @@ -83,7 +83,7 @@ def assert_response(response, expected_code=200, needs_cors=True): def test_api_not_found(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/invalid_url") assert_response(rc, 404) @@ -91,7 +91,7 @@ def test_api_not_found(botclient): def test_api_ui_fallback(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, "/favicon.ico") assert rc.status_code == 200 @@ -126,7 +126,7 @@ def test_api_auth(): def test_api_unauthorized(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client.get(f"{BASE_URI}/ping") assert_response(rc, needs_cors=False) assert rc.json() == {'status': 'pong'} @@ -137,20 +137,20 @@ def test_api_unauthorized(botclient): assert rc.json() == {'detail': 'Unauthorized'} # Change only username - ftbot.config['api_server']['username'] = 'Ftrader' + freqtrade.config['api_server']['username'] = 'Ftrader' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} # Change only password - ftbot.config['api_server']['username'] = _TEST_USER - ftbot.config['api_server']['password'] = 'WrongPassword' + freqtrade.config['api_server']['username'] = _TEST_USER + freqtrade.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} - ftbot.config['api_server']['username'] = 'Ftrader' - ftbot.config['api_server']['password'] = 'WrongPassword' + freqtrade.config['api_server']['username'] = 'Ftrader' + freqtrade.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) @@ -158,7 +158,7 @@ def test_api_unauthorized(botclient): def test_api_token_login(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client.post(f"{BASE_URI}/token/login", data=None, headers={'Authorization': _basic_auth_str('WRONG_USER', 'WRONG_PASS'), @@ -177,7 +177,7 @@ def test_api_token_login(botclient): def test_api_token_refresh(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_post(client, f"{BASE_URI}/token/login") assert_response(rc) rc = client.post(f"{BASE_URI}/token/refresh", @@ -190,12 +190,12 @@ def test_api_token_refresh(botclient): def test_api_stop_workflow(botclient): - ftbot, client = botclient - assert ftbot.state == State.RUNNING + freqtrade, client = botclient + assert freqtrade.state == State.RUNNING rc = client_post(client, f"{BASE_URI}/stop") assert_response(rc) assert rc.json() == {'status': 'stopping trader ...'} - assert ftbot.state == State.STOPPED + assert freqtrade.state == State.STOPPED # Stop bot again rc = client_post(client, f"{BASE_URI}/stop") @@ -206,7 +206,7 @@ def test_api_stop_workflow(botclient): rc = client_post(client, f"{BASE_URI}/start") assert_response(rc) assert rc.json() == {'status': 'starting trader ...'} - assert ftbot.state == State.RUNNING + assert freqtrade.state == State.RUNNING # Call start again rc = client_post(client, f"{BASE_URI}/start") @@ -358,32 +358,32 @@ def test_api_cleanup(default_conf, mocker, caplog): def test_api_reloadconf(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_post(client, f"{BASE_URI}/reload_config") assert_response(rc) assert rc.json() == {'status': 'Reloading config ...'} - assert ftbot.state == State.RELOAD_CONFIG + assert freqtrade.state == State.RELOAD_CONFIG def test_api_stopbuy(botclient): - ftbot, client = botclient - assert ftbot.config['max_open_trades'] != 0 + freqtrade, client = botclient + assert freqtrade.config['max_open_trades'] != 0 rc = client_post(client, f"{BASE_URI}/stopbuy") assert_response(rc) assert rc.json() == {'status': 'No more buy will occur from now. Run /reload_config to reset.'} - assert ftbot.config['max_open_trades'] == 0 + assert freqtrade.config['max_open_trades'] == 0 def test_api_balance(botclient, mocker, rpc_balance): - ftbot, client = botclient + freqtrade, client = botclient - ftbot.config['dry_run'] = False + freqtrade.config['dry_run'] = False mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', side_effect=lambda a, b: f"{a}/{b}") - ftbot.wallets.update() + freqtrade.wallets.update() rc = client_get(client, f"{BASE_URI}/balance") assert_response(rc) @@ -400,8 +400,8 @@ def test_api_balance(botclient, mocker, rpc_balance): def test_api_count(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -422,13 +422,13 @@ def test_api_count(botclient, mocker, ticker, fee, markets): assert rc.json()["current"] == 4 assert rc.json()["max"] == 1 - ftbot.config['max_open_trades'] = float('inf') + freqtrade.config['max_open_trades'] = float('inf') rc = client_get(client, f"{BASE_URI}/count") assert rc.json()["max"] == -1 def test_api_locks(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/locks") assert_response(rc) @@ -462,8 +462,8 @@ def test_api_locks(botclient): def test_api_show_config(botclient, mocker): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) rc = client_get(client, f"{BASE_URI}/show_config") assert_response(rc) @@ -480,8 +480,8 @@ def test_api_show_config(botclient, mocker): def test_api_daily(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -498,8 +498,8 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): def test_api_trades(botclient, mocker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets) @@ -526,8 +526,8 @@ def test_api_trades(botclient, mocker, fee, markets): def test_api_trade_single(botclient, mocker, fee, ticker, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -546,8 +546,8 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): def test_api_delete_trade(botclient, mocker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) stoploss_mock = MagicMock() cancel_mock = MagicMock() mocker.patch.multiple( @@ -562,7 +562,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): create_mock_trades(fee) Trade.query.session.flush() - ftbot.strategy.order_types['stoploss_on_exchange'] = True + freqtrade.strategy.order_types['stoploss_on_exchange'] = True trades = Trade.query.all() trades[1].stoploss_order_id = '1234' assert len(trades) > 2 @@ -588,7 +588,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): def test_api_logs(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/logs") assert_response(rc) assert len(rc.json()) == 2 @@ -620,8 +620,8 @@ def test_api_logs(botclient): def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -636,8 +636,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -683,8 +683,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -711,8 +711,8 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_performance(botclient, fee): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) trade = Trade( pair='LTC/ETH', @@ -757,8 +757,8 @@ def test_api_performance(botclient, fee): def test_api_status(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient - patch_get_signal(ftbot, (True, False)) + freqtrade, client = botclient + patch_get_signal(freqtrade, (True, False, '')) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -845,7 +845,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): def test_api_version(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/version") assert_response(rc) @@ -853,7 +853,7 @@ def test_api_version(botclient): def test_api_blacklist(botclient, mocker): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/blacklist") assert_response(rc) @@ -888,7 +888,7 @@ def test_api_blacklist(botclient, mocker): def test_api_whitelist(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/whitelist") assert_response(rc) @@ -900,7 +900,7 @@ def test_api_whitelist(botclient): def test_api_forcebuy(botclient, mocker, fee): - ftbot, client = botclient + freqtrade, client = botclient rc = client_post(client, f"{BASE_URI}/forcebuy", data='{"pair": "ETH/BTC"}') @@ -908,7 +908,7 @@ def test_api_forcebuy(botclient, mocker, fee): assert rc.json() == {"error": "Error querying /api/v1/forcebuy: Forcebuy not enabled."} # enable forcebuy - ftbot.config['forcebuy_enable'] = True + freqtrade.config['forcebuy_enable'] = True fbuy_mock = MagicMock(return_value=None) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) @@ -990,7 +990,7 @@ def test_api_forcebuy(botclient, mocker, fee): def test_api_forcesell(botclient, mocker, ticker, fee, markets): - ftbot, client = botclient + freqtrade, client = botclient mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -999,14 +999,14 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets), _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_get_signal(ftbot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') assert_response(rc, 502) assert rc.json() == {"error": "Error querying /api/v1/forcesell: invalid argument"} - ftbot.enter_positions() + freqtrade.enter_positions() rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') @@ -1015,7 +1015,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): def test_api_pair_candles(botclient, ohlcv_history): - ftbot, client = botclient + freqtrade, client = botclient timeframe = '5m' amount = 3 @@ -1043,7 +1043,7 @@ def test_api_pair_candles(botclient, ohlcv_history): ohlcv_history.loc[1, 'buy'] = 1 ohlcv_history['sell'] = 0 - ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) + freqtrade.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) rc = client_get(client, f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}") @@ -1081,7 +1081,7 @@ def test_api_pair_candles(botclient, ohlcv_history): def test_api_pair_history(botclient, ohlcv_history): - ftbot, client = botclient + freqtrade, client = botclient timeframe = '5m' # No pair @@ -1134,21 +1134,21 @@ def test_api_pair_history(botclient, ohlcv_history): def test_api_plot_config(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) assert rc.json() == {} - ftbot.strategy.plot_config = {'main_plot': {'sma': {}}, + freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}}} rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) - assert rc.json() == ftbot.strategy.plot_config + assert rc.json() == freqtrade.strategy.plot_config assert isinstance(rc.json()['main_plot'], dict) assert isinstance(rc.json()['subplots'], dict) - ftbot.strategy.plot_config = {'main_plot': {'sma': {}}} + freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}} rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) @@ -1157,7 +1157,7 @@ def test_api_plot_config(botclient): def test_api_strategies(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/strategies") @@ -1170,7 +1170,7 @@ def test_api_strategies(botclient): def test_api_strategy(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/strategy/DefaultStrategy") @@ -1185,7 +1185,7 @@ def test_api_strategy(botclient): def test_list_available_pairs(botclient): - ftbot, client = botclient + freqtrade, client = botclient rc = client_get(client, f"{BASE_URI}/available_pairs") diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 782ae69c6..642b55975 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -55,7 +55,7 @@ class DummyCls(Telegram): raise Exception('test') -def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): +def get_telegram_testobject(mocker, default_conf, mock=True, freqtrade=None): msg_mock = MagicMock() if mock: mocker.patch.multiple( @@ -63,12 +63,12 @@ def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): _init=MagicMock(), _send_msg=msg_mock ) - if not ftbot: - ftbot = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(ftbot) + if not freqtrade: + freqtrade = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(freqtrade) telegram = Telegram(rpc, default_conf) - return telegram, ftbot, msg_mock + return telegram, freqtrade, msg_mock def test_telegram__init__(default_conf, mocker) -> None: @@ -115,11 +115,11 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None: patch_exchange(mocker) caplog.set_level(logging.DEBUG) default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) + freqtrade = FreqtradeBot(default_conf) + rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) @@ -135,11 +135,11 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) + freqtrade = FreqtradeBot(default_conf) + rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) @@ -152,10 +152,10 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) + freqtrade = FreqtradeBot(default_conf) + rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False)) + patch_get_signal(freqtrade, (True, False, '')) dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False @@ -228,7 +228,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.state = State.STOPPED # Status is also enabled when stopped @@ -285,7 +285,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.state = State.STOPPED # Status table is also enabled when stopped @@ -329,7 +329,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -400,7 +400,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Try invalid data msg_mock.reset_mock() @@ -432,7 +432,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -486,7 +486,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._stats(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -512,7 +512,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick side_effect=lambda a, b: f"{a}/{b}") telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -532,7 +532,7 @@ def test_balance_handle_empty_response(default_conf, update, mocker) -> None: mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.config['dry_run'] = False telegram._balance(update=update, context=MagicMock()) @@ -545,7 +545,7 @@ def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -574,7 +574,7 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None }) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._balance(update=update, context=MagicMock()) assert msg_mock.call_count > 1 @@ -673,7 +673,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -732,7 +732,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -793,7 +793,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -834,7 +834,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Trader is not running freqtradebot.state = State.STOPPED @@ -872,7 +872,7 @@ def test_forcebuy_handle(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # /forcebuy ETH/BTC context = MagicMock() @@ -901,7 +901,7 @@ def test_forcebuy_handle_exception(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) update.message.text = '/forcebuy ETH/Nonepair' telegram._forcebuy(update=update, context=MagicMock()) @@ -918,7 +918,7 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) context = MagicMock() context.args = [] @@ -946,7 +946,7 @@ def test_performance_handle(default_conf, update, ticker, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) # Create some test data freqtradebot.enter_positions() @@ -974,7 +974,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) freqtradebot.state = State.STOPPED telegram._count(update=update, context=MagicMock()) @@ -1003,7 +1003,7 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) + patch_get_signal(freqtradebot, (True, False, '')) telegram._locks(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'No active locks.' in msg_mock.call_args_list[0][0][0] diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index cc89ce00c..583ec7ddd 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1972,16 +1972,16 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open def test_bot_loop_start_called_once(mocker, default_conf, caplog): - ftbot = get_patched_freqtradebot(mocker, default_conf) + freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade') - patch_get_signal(ftbot) - ftbot.strategy.bot_loop_start = MagicMock(side_effect=ValueError) - ftbot.strategy.analyze = MagicMock() + patch_get_signal(freqtrade) + freqtrade.strategy.bot_loop_start = MagicMock(side_effect=ValueError) + freqtrade.strategy.analyze = MagicMock() - ftbot.process() + freqtrade.process() assert log_has_re(r'Strategy caused the following exception.*', caplog) - assert ftbot.strategy.bot_loop_start.call_count == 1 - assert ftbot.strategy.analyze.call_count == 1 + assert freqtrade.strategy.bot_loop_start.call_count == 1 + assert freqtrade.strategy.analyze.call_count == 1 def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -4044,14 +4044,14 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker): reinit_mock = MagicMock() mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock) - ftbot = get_patched_freqtradebot(mocker, default_conf) - ftbot.startup() + freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade.startup() assert reinit_mock.call_count == 1 reinit_mock.reset_mock() - ftbot = get_patched_freqtradebot(mocker, edge_conf) - ftbot.startup() + freqtrade = get_patched_freqtradebot(mocker, edge_conf) + freqtrade.startup() assert reinit_mock.call_count == 0 @@ -4070,17 +4070,17 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ get_fee=fee, ) - bot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(bot) - assert bot.wallets.get_free('BTC') == 0.002 + freqtrade = get_patched_freqtradebot(mocker, default_conf) + patch_get_signal(freqtrade) + assert freqtrade.wallets.get_free('BTC') == 0.002 - n = bot.enter_positions() + n = freqtrade.enter_positions() assert n == 2 trades = Trade.query.all() assert len(trades) == 2 - bot.config['max_open_trades'] = 3 - n = bot.enter_positions() + freqtrade.config['max_open_trades'] = 3 + n = freqtrade.enter_positions() assert n == 0 assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", From 3d8b2d601d04b301d76cfbc1f745c24753c0d6be Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 20:23:47 +0700 Subject: [PATCH 151/519] fix test persistance --- tests/test_persistence.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 89d07ca74..8e486d145 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -861,6 +861,7 @@ def test_to_json(default_conf, fee): open_date=arrow.utcnow().shift(hours=-2).datetime, open_rate=0.123, exchange='binance', + buy_signal_name='', open_order_id='dry_run_buy_12345' ) result = trade.to_json() @@ -910,6 +911,7 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, + 'buy_signal_name': '', 'timeframe': None, 'exchange': 'binance', } @@ -926,6 +928,7 @@ def test_to_json(default_conf, fee): close_date=arrow.utcnow().shift(hours=-1).datetime, open_rate=0.123, close_rate=0.125, + buy_signal_name='buys_signal_001', exchange='binance', ) result = trade.to_json() @@ -975,6 +978,7 @@ def test_to_json(default_conf, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, + 'buy_signal_name': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', } From c558fc0b17c10e1ca2eb7bf1020d3712b6a9ab9c Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 20:40:32 +0700 Subject: [PATCH 152/519] fix feedback --- freqtrade/enums/signaltype.py | 2 +- freqtrade/persistence/migrations.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index 6f29699f1..8529c6b79 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -11,6 +11,6 @@ class SignalType(Enum): class SignalNameType(Enum): """ - Enum for signal name + Enum for signal columns """ BUY_SIGNAL_NAME = "buy_signal_name" diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 4fa1cb742..f6a345ed1 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -161,7 +161,14 @@ def check_migrate(engine, decl_base, previous_tables) -> None: table_back_name = get_backup_name(tabs, 'trades_bak') # Check for latest column - if not has_column(cols, 'open_trade_value') or not has_column(cols, 'buy_signal_name'): + if not has_column(cols, 'open_trade_value'): + logger.info(f'Running database migration for trades - backup: {table_back_name}') + migrate_trades_table(decl_base, inspector, engine, table_back_name, cols) + # Reread columns - the above recreated the table! + inspector = inspect(engine) + cols = inspector.get_columns('trades') + + if not has_column(cols, 'buy_signal_name'): logger.info(f'Running database migration for trades - backup: {table_back_name}') migrate_trades_table(decl_base, inspector, engine, table_back_name, cols) # Reread columns - the above recreated the table! From cbfedf8b29144325306952a3581bf56f8c912d4b Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 23:25:00 +0700 Subject: [PATCH 153/519] fix backtest testcase --- freqtrade/data/btanalysis.py | 2 +- freqtrade/optimize/backtesting.py | 2 + tests/optimize/__init__.py | 3 +- tests/optimize/test_backtest_detail.py | 449 +++++++++++++------------ 4 files changed, 238 insertions(+), 218 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index e7af5eab8..f6122c5aa 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -30,7 +30,7 @@ BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date', 'fee_open', 'fee_close', 'trade_duration', 'profit_ratio', 'profit_abs', 'sell_reason', 'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs', - 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', ] + 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'buy_signal_name'] def get_latest_optimize_filename(directory: Union[Path, str], variant: str) -> str: diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7fdc70e70..843d3331e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -453,6 +453,8 @@ class Backtesting: row_index = indexes[pair] try: row = data[pair][row_index] + print('weeee') + print(row) except IndexError: # missing Data for one pair at the end. # Warnings for this are shown during data loading diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index ca91019e6..f77e6f70f 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -18,6 +18,7 @@ class BTrade(NamedTuple): sell_reason: SellType open_tick: int close_tick: int + buy_signal_name: Optional[str] = '' class BTContainer(NamedTuple): @@ -43,7 +44,7 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_signal_name'] frame = DataFrame.from_records(data, columns=columns) frame['date'] = frame['date'].apply(_get_frame_time_from_offset) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 0bf197739..46c8e303d 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -14,13 +14,13 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, # Test 0: Sell with signal sell in candle 3 # Test with Stop-loss at 1% tc0 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], # exit with stoploss hit - [3, 5010, 5000, 4980, 5010, 6172, 0, 1], - [4, 5010, 4987, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], # exit with stoploss hit + [3, 5010, 5000, 4980, 5010, 6172, 0, 1, ''], + [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -28,13 +28,13 @@ tc0 = BTContainer(data=[ # Test 1: Stop-Loss Triggered 1% loss # Test with Stop-loss at 1% tc1 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit - [3, 4975, 5000, 4980, 4977, 6172, 0, 0], - [4, 4977, 4987, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4600, 4600, 6172, 0, 0, ''], # exit with stoploss hit + [3, 4975, 5000, 4980, 4977, 6172, 0, 0, ''], + [4, 4977, 4987, 4977, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -43,13 +43,13 @@ tc1 = BTContainer(data=[ # Test 2: Minus 4% Low, minus 1% close # Test with Stop-Loss at 3% tc2 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4962, 4975, 6172, 0, 0], - [3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit - [4, 4962, 4987, 4937, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4962, 4975, 6172, 0, 0, ''], + [3, 4975, 5000, 4800, 4962, 6172, 0, 0, ''], # exit with stoploss hit + [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.03, roi={"0": 1}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -62,14 +62,14 @@ tc2 = BTContainer(data=[ # Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 1, 0], - [4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle) - [5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit - [6, 4950, 4975, 4975, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4800, 4975, 6172, 0, 0, ''], # exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 1, 0, ''], + [4, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], # enter trade 2 (signal on last candle) + [5, 4962, 4987, 4000, 4000, 6172, 0, 0, ''], # exit with stoploss hit + [6, 4950, 4975, 4975, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 1}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)] @@ -80,13 +80,13 @@ tc3 = BTContainer(data=[ # Test with Stop-loss at 2% ROI 6% # Stop-Loss Triggered 2% Loss tc4 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4937, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5750, 4850, 5750, 6172, 0, 0, ''], # Exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], + [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 0.06}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -94,13 +94,13 @@ tc4 = BTContainer(data=[ # Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain # stop-loss: 1%, ROI: 3% tc5 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4980, 4987, 6172, 1, 0], - [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5025, 4975, 4987, 6172, 0, 0], - [3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4980, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4980, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5025, 4975, 4987, 6172, 0, 0, ''], + [3, 4975, 6000, 4975, 6000, 6172, 0, 0, ''], # ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -108,13 +108,13 @@ tc5 = BTContainer(data=[ # Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss # stop-loss: 2% ROI: 5% tc6 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5300, 4850, 5050, 6172, 0, 0, ''], # Exit with stoploss + [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 0.05}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -122,13 +122,13 @@ tc6 = BTContainer(data=[ # Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain # stop-loss: 2% ROI: 3% tc7 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4975, 5000, 4950, 4962, 6172, 0, 0], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], + [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.02, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)] ) @@ -137,12 +137,12 @@ tc7 = BTContainer(data=[ # Test 8: trailing_stop should raise so candle 3 causes a stoploss. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2 tc8 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0], - [2, 5000, 5250, 4750, 4850, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], + [2, 5000, 5250, 4750, 4850, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.055, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -151,12 +151,12 @@ tc8 = BTContainer(data=[ # Test 9: trailing_stop should raise - high and low in same candle. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3 tc9 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0], - [2, 5000, 5050, 4950, 5000, 6172, 0, 0], - [3, 5000, 5200, 4550, 4850, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], + [2, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], + [3, 5000, 5200, 4550, 4850, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.064, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -165,12 +165,12 @@ tc9 = BTContainer(data=[ # without applying trailing_stop_positive since stoploss_offset is at 10%. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc10 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.1, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.10, trailing_stop_positive=0.03, @@ -181,12 +181,12 @@ tc10 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc11 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 5000, 5150, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 5000, 5150, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -197,12 +197,12 @@ tc11 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc12 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -212,12 +212,12 @@ tc12 = BTContainer(data=[ # Test 13: Buy and sell ROI on same candle # stop-loss: 10% (should not apply), ROI: 1% tc13 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0], - [4, 4750, 4950, 4850, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4850, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1)] ) @@ -225,12 +225,12 @@ tc13 = BTContainer(data=[ # Test 14 - Buy and Stoploss on same candle # stop-loss: 5%, ROI: 10% (should not apply) tc14 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4600, 5100, 6172, 0, 0], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5100, 4600, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.05, roi={"0": 0.10}, profit_perc=-0.05, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] ) @@ -239,12 +239,12 @@ tc14 = BTContainer(data=[ # Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle # stop-loss: 5%, ROI: 10% (should not apply) tc15 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5100, 4900, 5100, 6172, 1, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5100, 4900, 5100, 6172, 1, 0, ''], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.05, roi={"0": 0.01}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=2, close_tick=2)] @@ -254,13 +254,13 @@ tc15 = BTContainer(data=[ # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration) tc16 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], + [3, 4975, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -270,13 +270,13 @@ tc16 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe. tc17 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], + [3, 4980, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -286,13 +286,13 @@ tc17 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses open_rate as sell-price tc18 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5200, 5220, 4940, 4962, 6172, 0, 0], # Sell on ROI (sells on open) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], + [3, 5200, 5220, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI (sells on open) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -301,13 +301,13 @@ tc18 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc19 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5000, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], + [3, 5000, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -316,13 +316,13 @@ tc19 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc20 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0], - [3, 5200, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], + [3, 5200, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10, "119": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -332,12 +332,12 @@ tc20 = BTContainer(data=[ # which cannot happen in reality # stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle tc21 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -348,12 +348,12 @@ tc21 = BTContainer(data=[ # applying a positive trailing stop of 3% - ROI should apply before trailing stop. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2 tc22 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -367,12 +367,12 @@ tc22 = BTContainer(data=[ # Stoploss would trigger in this candle too, but it's no longer relevant. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell) tc23 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5251, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5251, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.1, "119": 0.03}, profit_perc=0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -383,13 +383,13 @@ tc23 = BTContainer(data=[ # Stoploss at 1%. # Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle) tc24 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5000, 4855, 5010, 6172, 0, 1], # Triggers stoploss + sellsignal - [4, 5010, 4987, 4977, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5000, 4855, 5010, 6172, 0, 1, ''], # Triggers stoploss + sellsignal + [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -398,13 +398,13 @@ tc24 = BTContainer(data=[ # Stoploss at 1%. # Sell-signal wins over stoploss tc25 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5000, 4986, 5010, 6172, 0, 1], - [4, 5010, 4987, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5000, 4986, 5010, 6172, 0, 1, ''], + [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], # Triggers stoploss + sellsignal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -413,13 +413,13 @@ tc25 = BTContainer(data=[ # Stoploss at 10% (irrelevant), ROI at 5% (will trigger) # Sell-signal wins over stoploss tc26 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5251, 4986, 5010, 6172, 0, 1], # Triggers ROI, sell-signal - [4, 5010, 4987, 4855, 4995, 6172, 0, 0], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5251, 4986, 5010, 6172, 0, 1, ''], # Triggers ROI, sell-signal + [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -429,13 +429,13 @@ tc26 = BTContainer(data=[ # TODO: figure out if sell-signal should win over ROI # Sell-signal wins over stoploss tc27 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0], - [3, 5010, 5012, 4986, 5010, 6172, 0, 1], # sell-signal - [4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], + [3, 5010, 5012, 4986, 5010, 6172, 0, 1, ''], # sell-signal + [4, 5010, 5251, 4855, 4995, 6172, 0, 0, ''], # Triggers ROI, sell-signal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=4)] ) @@ -445,12 +445,12 @@ tc27 = BTContainer(data=[ # therefore "open" will be used # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc28 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -461,12 +461,12 @@ tc28 = BTContainer(data=[ # high of stoploss candle. # stop-loss: 10%, ROI: 10% (should not apply) tc29 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5050, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5050, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], # Triggers trailing-stoploss + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.02, trailing_stop=True, trailing_stop_positive=0.03, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)] @@ -475,12 +475,12 @@ tc29 = BTContainer(data=[ # Test 30: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc30 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_stop_positive=0.01, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] @@ -489,12 +489,12 @@ tc30 = BTContainer(data=[ # Test 31: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc31 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, @@ -504,18 +504,33 @@ tc31 = BTContainer(data=[ # Test 32: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) tc32 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5050, 4950, 5000, 6172, 1, 0], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] ) +# Test 33: trailing_stop should be triggered immediately on trade open candle. +# stop-loss: 1%, ROI: 10% (should not apply) +tc33 = BTContainer(data=[ + # D O H L C V B S SN + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 'buy_signal_01'], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, + trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, + trailing_stop_positive=0.01, use_custom_stoploss=True, + trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1, buy_signal_name='buy_signal_01')] +) + TESTS = [ tc0, tc1, @@ -550,6 +565,7 @@ TESTS = [ tc30, tc31, tc32, + tc33, ] @@ -598,5 +614,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: for c, trade in enumerate(data.trades): res = results.iloc[c] assert res.sell_reason == trade.sell_reason.value + assert res.buy_signal_name == trade.buy_signal_name assert res.open_date == _get_frame_time_from_offset(trade.open_tick) assert res.close_date == _get_frame_time_from_offset(trade.close_tick) From 5d04d6ffa72812efbc74aada0471c5cc59a388df Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 23:40:32 +0700 Subject: [PATCH 154/519] fix edge testcase --- freqtrade/optimize/backtesting.py | 2 -- tests/edge/test_edge.py | 47 +++++++++++++++--------------- tests/optimize/test_backtesting.py | 1 + 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 843d3331e..7fdc70e70 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -453,8 +453,6 @@ class Backtesting: row_index = indexes[pair] try: row = data[pair][row_index] - print('weeee') - print(row) except IndexError: # missing Data for one pair at the end. # Warnings for this are shown during data loading diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py index 0655b3a0f..254134ce7 100644 --- a/tests/edge/test_edge.py +++ b/tests/edge/test_edge.py @@ -29,7 +29,6 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, tests_start_time = arrow.get(2018, 10, 3) timeframe_in_minute = 60 -_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7} # Helpers for this test file @@ -77,23 +76,23 @@ def _time_on_candle(number): # End helper functions # Open trade should be removed from the end tc0 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1]], # enter trade (signal on last candle) + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1, '']], # enter trade (signal on last candle) stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[] ) # Two complete trades within dataframe(with sell hit for all) tc1 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1], # enter trade (signal on last candle) - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], # exit at open - [3, 5000, 5025, 4975, 4987, 6172, 1, 0], # no action - [4, 5000, 5025, 4975, 4987, 6172, 0, 0], # should enter the trade - [5, 5000, 5025, 4975, 4987, 6172, 0, 1], # no action - [6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # enter trade (signal on last candle) + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # exit at open + [3, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], # no action + [4, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should enter the trade + [5, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # no action + [6, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should sell ], stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=2), @@ -102,10 +101,10 @@ tc1 = BTContainer(data=[ # 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss tc2 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4600, 4987, 6172, 0, 0], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4600, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], ], stop_loss=-0.01, roi={"0": float('inf')}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -113,10 +112,10 @@ tc2 = BTContainer(data=[ # 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss tc3 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4800, 4987, 6172, 0, 0], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4800, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -124,10 +123,10 @@ tc3 = BTContainer(data=[ # 5) Stoploss and sell are hit. should sell on stoploss tc4 = BTContainer(data=[ - # D O H L C V B S - [0, 5000, 5025, 4975, 4987, 6172, 1, 0], - [1, 5000, 5025, 4800, 4987, 6172, 0, 1], # enter trade, stoploss hit, sell signal - [2, 5000, 5025, 4975, 4987, 6172, 0, 0], + # D O H L C V B S SN + [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], + [1, 5000, 5025, 4800, 4987, 6172, 0, 1, ''], # enter trade, stoploss hit, sell signal + [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 655464344..64795d064 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -584,6 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'min_rate': [0.1038, 0.10302485], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], + 'buy_signal_name': ['', ''], }) pd.testing.assert_frame_equal(results, expected) data_pair = processed[pair] From 66a7070170b919ad0d020e55282ae0cb0873fdef Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Tue, 20 Jul 2021 23:56:03 +0700 Subject: [PATCH 155/519] run linter --- freqtrade/enums/__init__.py | 2 +- freqtrade/freqtradebot.py | 12 ++++++++++-- freqtrade/strategy/interface.py | 2 +- tests/optimize/test_backtest_detail.py | 7 ++++++- tests/rpc/test_rpc_apiserver.py | 6 ++++-- tests/strategy/test_interface.py | 12 ++++++++++-- tests/test_freqtradebot.py | 5 ++++- 7 files changed, 36 insertions(+), 10 deletions(-) diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 60f984c33..71e9d7d9e 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -3,5 +3,5 @@ from freqtrade.enums.backteststate import BacktestState from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType -from freqtrade.enums.signaltype import SignalType, SignalNameType +from freqtrade.enums.signaltype import SignalNameType, SignalType from freqtrade.enums.state import State diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8d3b24b10..95c769730 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -420,7 +420,11 @@ class FreqtradeBot(LoggingMixin): return False # running get_signal on historical data fetched - (buy, sell, buy_signal_name) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df) + (buy, sell, buy_signal_name) = self.strategy.get_signal( + pair, + self.strategy.timeframe, + analyzed_df + ) if buy and not sell: stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge) @@ -693,7 +697,11 @@ class FreqtradeBot(LoggingMixin): analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, self.strategy.timeframe) - (buy, sell, _) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) + (buy, sell, _) = self.strategy.get_signal( + trade.pair, + self.strategy.timeframe, + analyzed_df + ) logger.debug('checking sell') sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7158ad8e8..8ee0cea59 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,7 @@ from pandas import DataFrame from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import SellType, SignalType, SignalNameType +from freqtrade.enums import SellType, SignalNameType, SignalType from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 46c8e303d..d1bf28d58 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -528,7 +528,12 @@ tc33 = BTContainer(data=[ stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, - trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1, buy_signal_name='buy_signal_01')] + trades=[BTrade( + sell_reason=SellType.TRAILING_STOP_LOSS, + open_tick=1, + close_tick=1, + buy_signal_name='buy_signal_01' + )] ) TESTS = [ diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index afd273998..74d40b611 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1185,8 +1185,10 @@ def test_api_plot_config(botclient): assert_response(rc) assert rc.json() == {} - freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}, - 'subplots': {'RSI': {'rsi': {'color': 'red'}}}} + freqtrade.strategy.plot_config = { + 'main_plot': {'sma': {}}, + 'subplots': {'RSI': {'rsi': {'color': 'red'}}} + } rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) assert rc.json() == freqtrade.strategy.plot_config diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index aec07266d..3678cee4a 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -76,7 +76,11 @@ def test_get_signal_empty(default_conf, mocker, caplog): assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False, '') == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) + assert (False, False, '') == _STRATEGY.get_signal( + 'baz', + default_conf['timeframe'], + DataFrame([]) + ) assert log_has('Empty candle (OHLCV) data for pair baz', caplog) @@ -112,7 +116,11 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False, '') == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) + assert (False, False, '') == _STRATEGY.get_signal( + 'xyz', + default_conf['timeframe'], + mocked_history + ) assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 48a0f06e8..6372e6d36 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -757,7 +757,10 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: refresh_latest_ohlcv=refresh_mock, ) inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")]) - mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False, '')) + mocker.patch( + 'freqtrade.strategy.interface.IStrategy.get_signal', + return_value=(False, False, '') + ) mocker.patch('time.sleep', return_value=None) freqtrade = FreqtradeBot(default_conf) From db1e676639d08baa5591fdc5621891c60ef7faf5 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 21 Jul 2021 00:48:03 +0700 Subject: [PATCH 156/519] retrigger checks From 1ea29a918a9fb55f32f39fd098389b6427e64e3d Mon Sep 17 00:00:00 2001 From: George Muravei-Alkhavoi Date: Wed, 21 Jul 2021 00:09:09 +0300 Subject: [PATCH 157/519] Fix webserver timerange problem. --- freqtrade/rpc/api_server/api_backtest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 76b4a8169..2fa66645b 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -47,15 +47,15 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac not ApiServer._bt or lastconfig.get('timeframe') != strat.timeframe or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0) + or lastconfig.get('timerange') != btconfig['timerange'] ): from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) - # Only reload data if timeframe or timerange changed. + # Only reload data if timeframe changed. if ( not ApiServer._bt_data or not ApiServer._bt_timerange - or lastconfig.get('timerange') != btconfig['timerange'] or lastconfig.get('stake_amount') != btconfig.get('stake_amount') or lastconfig.get('enable_protections') != btconfig.get('enable_protections') or lastconfig.get('protections') != btconfig.get('protections', []) From 49886874aafcf1763e563f55cb61fedff0d3ac60 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 21 Jul 2021 20:05:35 +0700 Subject: [PATCH 158/519] rename to buy_tag --- docs/strategy-advanced.md | 6 +++--- freqtrade/data/btanalysis.py | 2 +- freqtrade/enums/__init__.py | 2 +- freqtrade/enums/signaltype.py | 4 ++-- freqtrade/freqtradebot.py | 12 ++++++------ freqtrade/optimize/backtesting.py | 12 ++++++------ freqtrade/persistence/migrations.py | 8 ++++---- freqtrade/persistence/models.py | 8 ++++---- freqtrade/strategy/interface.py | 12 ++++++------ tests/optimize/__init__.py | 4 ++-- tests/optimize/test_backtest_detail.py | 4 ++-- tests/optimize/test_backtesting.py | 4 ++-- tests/rpc/test_rpc.py | 4 ++-- tests/strategy/test_interface.py | 2 +- tests/test_persistence.py | 10 +++++----- 15 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 0b52af17b..8ae0c1d16 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -114,7 +114,7 @@ class AwesomeStrategy(IStrategy): See [Dataframe access](#dataframe-access) for more information about dataframe use in strategy callbacks. -## Buy Signal Name +## Buy Tag When your strategy has multiple buy signal, you can name it. Then you can access you buy signal on `custom_sell` @@ -126,7 +126,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: (dataframe['rsi'] < 35) & (dataframe['volume'] > 0) ), - ['buy', 'buy_signal_name']] = 1, 'buy_signal_rsi' + ['buy', 'buy_tag']] = 1, 'buy_signal_rsi' return dataframe @@ -134,7 +134,7 @@ def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_r current_profit: float, **kwargs): dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() - if trade.buy_signal_name == 'buy_signal_rsi' and last_candle['rsi'] > 80: + if trade.buy_tag == 'buy_signal_rsi' and last_candle['rsi'] > 80: return 'sell_signal_rsi' return None diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index f6122c5aa..d62712cbb 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -30,7 +30,7 @@ BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date', 'fee_open', 'fee_close', 'trade_duration', 'profit_ratio', 'profit_abs', 'sell_reason', 'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs', - 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'buy_signal_name'] + 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'buy_tag'] def get_latest_optimize_filename(directory: Union[Path, str], variant: str) -> str: diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 71e9d7d9e..d803baf31 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -3,5 +3,5 @@ from freqtrade.enums.backteststate import BacktestState from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.selltype import SellType -from freqtrade.enums.signaltype import SignalNameType, SignalType +from freqtrade.enums.signaltype import SignalTagType, SignalType from freqtrade.enums.state import State diff --git a/freqtrade/enums/signaltype.py b/freqtrade/enums/signaltype.py index 8529c6b79..d2995d57a 100644 --- a/freqtrade/enums/signaltype.py +++ b/freqtrade/enums/signaltype.py @@ -9,8 +9,8 @@ class SignalType(Enum): SELL = "sell" -class SignalNameType(Enum): +class SignalTagType(Enum): """ Enum for signal columns """ - BUY_SIGNAL_NAME = "buy_signal_name" + BUY_TAG = "buy_tag" diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 95c769730..cb9157d33 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -420,7 +420,7 @@ class FreqtradeBot(LoggingMixin): return False # running get_signal on historical data fetched - (buy, sell, buy_signal_name) = self.strategy.get_signal( + (buy, sell, buy_tag) = self.strategy.get_signal( pair, self.strategy.timeframe, analyzed_df @@ -431,13 +431,13 @@ class FreqtradeBot(LoggingMixin): bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {}) if ((bid_check_dom.get('enabled', False)) and - (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): + (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): if self._check_depth_of_market_buy(pair, bid_check_dom): - return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) + return self.execute_buy(pair, stake_amount, buy_tag=buy_tag) else: return False - return self.execute_buy(pair, stake_amount, buy_signal_name=buy_signal_name) + return self.execute_buy(pair, stake_amount, buy_tag=buy_tag) else: return False @@ -466,7 +466,7 @@ class FreqtradeBot(LoggingMixin): return False def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False, buy_signal_name: str = '') -> bool: + forcebuy: bool = False, buy_tag: str = '') -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY @@ -569,7 +569,7 @@ class FreqtradeBot(LoggingMixin): exchange=self.exchange.id, open_order_id=order_id, strategy=self.strategy.get_strategy_name(), - buy_signal_name=buy_signal_name, + buy_tag=buy_tag, timeframe=timeframe_to_minutes(self.config['timeframe']) ) trade.orders.append(order_obj) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7fdc70e70..5099c07ef 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -43,7 +43,7 @@ CLOSE_IDX = 3 SELL_IDX = 4 LOW_IDX = 5 HIGH_IDX = 6 -BUY_SIGNAL_NAME_IDX = 7 +buy_tag_IDX = 7 class Backtesting: @@ -210,7 +210,7 @@ class Backtesting: """ # Every change to this headers list must evaluate further usages of the resulting tuple # and eventually change the constants for indexes at the top - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_signal_name'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_tag'] data: Dict = {} self.progress.init_step(BacktestState.CONVERT, len(processed)) @@ -221,7 +221,7 @@ class Backtesting: if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist - pair_data.loc[:, 'buy_signal_name'] = '' # cleanup if buy_signal_name is exist + pair_data.loc[:, 'buy_tag'] = '' # cleanup if buy_tag is exist df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() @@ -230,7 +230,7 @@ class Backtesting: # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1) - df_analyzed.loc[:, 'buy_signal_name'] = df_analyzed.loc[:, 'buy_signal_name'].shift(1) + df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) df_analyzed.drop(df_analyzed.head(1).index, inplace=True) @@ -265,7 +265,7 @@ class Backtesting: # Worst case: price reaches stop_positive_offset and dives down. stop_rate = (sell_row[OPEN_IDX] * (1 + abs(self.strategy.trailing_stop_positive_offset) - - abs(self.strategy.trailing_stop_positive))) + abs(self.strategy.trailing_stop_positive))) else: # Worst case: price ticks tiny bit above open and dives down. stop_rate = sell_row[OPEN_IDX] * (1 - abs(trade.stop_loss_pct)) @@ -370,7 +370,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_signal_name=row[BUY_SIGNAL_NAME_IDX], + buy_tag=row[buy_tag_IDX], exchange='backtesting', ) return trade diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index f6a345ed1..035452437 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -47,7 +47,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col min_rate = get_column_def(cols, 'min_rate', 'null') sell_reason = get_column_def(cols, 'sell_reason', 'null') strategy = get_column_def(cols, 'strategy', 'null') - buy_signal_name = get_column_def(cols, 'buy_signal_name', 'null') + buy_tag = get_column_def(cols, 'buy_tag', 'null') # If ticker-interval existed use that, else null. if has_column(cols, 'ticker_interval'): timeframe = get_column_def(cols, 'timeframe', 'ticker_interval') @@ -81,7 +81,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col stake_amount, amount, amount_requested, open_date, close_date, open_order_id, stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct, stoploss_order_id, stoploss_last_update, - max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_signal_name, + max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag, timeframe, open_trade_value, close_profit_abs ) select id, lower(exchange), @@ -104,7 +104,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col {stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update, {max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason, {sell_order_status} sell_order_status, - {strategy} strategy, {buy_signal_name} buy_signal_name, {timeframe} timeframe, + {strategy} strategy, {buy_tag} buy_tag, {timeframe} timeframe, {open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs from {table_back_name} """)) @@ -168,7 +168,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None: inspector = inspect(engine) cols = inspector.get_columns('trades') - if not has_column(cols, 'buy_signal_name'): + if not has_column(cols, 'buy_tag'): logger.info(f'Running database migration for trades - backup: {table_back_name}') migrate_trades_table(decl_base, inspector, engine, table_back_name, cols) # Reread columns - the above recreated the table! diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 058e892ec..f42166762 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -257,7 +257,7 @@ class LocalTrade(): sell_reason: str = '' sell_order_status: str = '' strategy: str = '' - buy_signal_name: str = '' + buy_tag: str = '' timeframe: Optional[int] = None def __init__(self, **kwargs): @@ -289,7 +289,7 @@ class LocalTrade(): 'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None, 'stake_amount': round(self.stake_amount, 8), 'strategy': self.strategy, - 'buy_signal_name': self.buy_signal_name, + 'buy_tag': self.buy_tag, 'timeframe': self.timeframe, 'fee_open': self.fee_open, @@ -638,7 +638,7 @@ class LocalTrade(): # skip case if trailing-stop changed the stoploss already. if (trade.stop_loss == trade.initial_stop_loss - and trade.initial_stop_loss_pct != desired_stoploss): + and trade.initial_stop_loss_pct != desired_stoploss): # Stoploss value got changed logger.info(f"Stoploss for {trade} needs adjustment...") @@ -705,7 +705,7 @@ class Trade(_DECL_BASE, LocalTrade): sell_reason = Column(String(100), nullable=True) sell_order_status = Column(String(100), nullable=True) strategy = Column(String(100), nullable=True) - buy_signal_name = Column(String(100), nullable=True) + buy_tag = Column(String(100), nullable=True) timeframe = Column(Integer, nullable=True) def __init__(self, **kwargs): diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 8ee0cea59..a5517403d 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,7 @@ from pandas import DataFrame from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import SellType, SignalNameType, SignalType +from freqtrade.enums import SellType, SignalTagType, SignalType from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date @@ -422,7 +422,7 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug("Skipping TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 - dataframe['buy_signal_name'] = '' + dataframe['buy_tag'] = '' # Other Defs in strategy that want to be called every loop here # twitter_sell = self.watch_twitter_feed(dataframe, metadata) @@ -525,9 +525,9 @@ class IStrategy(ABC, HyperStrategyMixin): ) return False, False, '' - (buy, sell, buy_signal_name) = latest[SignalType.BUY.value] == 1,\ + (buy, sell, buy_tag) = latest[SignalType.BUY.value] == 1,\ latest[SignalType.SELL.value] == 1,\ - latest.get(SignalNameType.BUY_SIGNAL_NAME.value, '') + latest.get(SignalTagType.BUY_TAG.value, '') logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) @@ -535,8 +535,8 @@ class IStrategy(ABC, HyperStrategyMixin): current_time=datetime.now(timezone.utc), timeframe_seconds=timeframe_seconds, buy=buy): - return False, sell, buy_signal_name - return buy, sell, buy_signal_name + return False, sell, buy_tag + return buy, sell, buy_tag def ignore_expired_candle(self, latest_date: datetime, current_time: datetime, timeframe_seconds: int, buy: bool): diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index f77e6f70f..240f3f871 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -18,7 +18,7 @@ class BTrade(NamedTuple): sell_reason: SellType open_tick: int close_tick: int - buy_signal_name: Optional[str] = '' + buy_tag: Optional[str] = '' class BTContainer(NamedTuple): @@ -44,7 +44,7 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_signal_name'] + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_tag'] frame = DataFrame.from_records(data, columns=columns) frame['date'] = frame['date'].apply(_get_frame_time_from_offset) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index d1bf28d58..f7077ef56 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -532,7 +532,7 @@ tc33 = BTContainer(data=[ sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1, - buy_signal_name='buy_signal_01' + buy_tag='buy_signal_01' )] ) @@ -619,6 +619,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: for c, trade in enumerate(data.trades): res = results.iloc[c] assert res.sell_reason == trade.sell_reason.value - assert res.buy_signal_name == trade.buy_signal_name + assert res.buy_tag == trade.buy_tag assert res.open_date == _get_frame_time_from_offset(trade.open_tick) assert res.close_date == _get_frame_time_from_offset(trade.close_tick) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 64795d064..873b27b4d 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -584,7 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'min_rate': [0.1038, 0.10302485], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], - 'buy_signal_name': ['', ''], + 'buy_tag': ['', ''], }) pd.testing.assert_frame_equal(results, expected) data_pair = processed[pair] @@ -859,7 +859,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'locks': [], 'rejected_signals': 20, 'final_balance': 1000, - }) + }) mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', PropertyMock(return_value=['UNITTEST/BTC'])) mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index df8679470..889aa05cf 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -69,7 +69,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, - 'buy_signal_name': ANY, + 'buy_tag': ANY, 'timeframe': 5, 'open_order_id': ANY, 'close_date': None, @@ -136,7 +136,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: 'min_rate': ANY, 'max_rate': ANY, 'strategy': ANY, - 'buy_signal_name': ANY, + 'buy_tag': ANY, 'timeframe': ANY, 'open_order_id': ANY, 'close_date': None, diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 3678cee4a..c812f3007 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -49,7 +49,7 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, '') mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - mocked_history.loc[1, 'buy_signal_name'] = 'buy_signal_01' + mocked_history.loc[1, 'buy_tag'] = 'buy_signal_01' assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, 'buy_signal_01') diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 8e486d145..af4979919 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -861,7 +861,7 @@ def test_to_json(default_conf, fee): open_date=arrow.utcnow().shift(hours=-2).datetime, open_rate=0.123, exchange='binance', - buy_signal_name='', + buy_tag='', open_order_id='dry_run_buy_12345' ) result = trade.to_json() @@ -911,7 +911,7 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, - 'buy_signal_name': '', + 'buy_tag': '', 'timeframe': None, 'exchange': 'binance', } @@ -928,7 +928,7 @@ def test_to_json(default_conf, fee): close_date=arrow.utcnow().shift(hours=-1).datetime, open_rate=0.123, close_rate=0.125, - buy_signal_name='buys_signal_001', + buy_tag='buys_signal_001', exchange='binance', ) result = trade.to_json() @@ -978,7 +978,7 @@ def test_to_json(default_conf, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': None, - 'buy_signal_name': 'buys_signal_001', + 'buy_tag': 'buys_signal_001', 'timeframe': None, 'exchange': 'binance', } @@ -1323,7 +1323,7 @@ def test_Trade_object_idem(): 'get_open_trades_without_assigned_fees', 'get_open_order_trades', 'get_trades', - ) + ) # Parent (LocalTrade) should have the same attributes for item in trade: From f5a660f845163369659855883a3982bc06fc1816 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Wed, 21 Jul 2021 20:19:56 +0700 Subject: [PATCH 159/519] caps BUY_TAG_IDX --- freqtrade/optimize/backtesting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 5099c07ef..fdf341e21 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -43,7 +43,7 @@ CLOSE_IDX = 3 SELL_IDX = 4 LOW_IDX = 5 HIGH_IDX = 6 -buy_tag_IDX = 7 +BUY_TAG_IDX = 7 class Backtesting: @@ -370,7 +370,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_tag=row[buy_tag_IDX], + buy_tag=row[BUY_TAG_IDX], exchange='backtesting', ) return trade From 235c1afd09dccbc714828e1618f7545ca3dfd776 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 01:53:15 +0700 Subject: [PATCH 160/519] add buy_tag on telegram --- freqtrade/freqtradebot.py | 3 ++ freqtrade/rpc/telegram.py | 27 +++++++++++----- freqtrade/strategy/interface.py | 6 ++-- tests/conftest.py | 2 +- tests/rpc/test_rpc.py | 34 ++++++++++---------- tests/rpc/test_rpc_apiserver.py | 24 +++++++-------- tests/rpc/test_rpc_telegram.py | 53 ++++++++++++++++++-------------- tests/strategy/test_interface.py | 14 ++++----- tests/test_freqtradebot.py | 42 ++++++++++++------------- tests/test_persistence.py | 4 +-- 10 files changed, 115 insertions(+), 94 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index cb9157d33..7756aa1d7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -595,6 +595,7 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY, + 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'limit': trade.open_rate, @@ -619,6 +620,7 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY_CANCEL, + 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'limit': trade.open_rate, @@ -639,6 +641,7 @@ class FreqtradeBot(LoggingMixin): msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY_FILL, + 'buy_tag': trade.buy_tag, 'exchange': self.exchange.name.capitalize(), 'pair': trade.pair, 'open_rate': trade.open_rate, diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 263a3fc6d..a1f6a7e33 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -208,15 +208,25 @@ class Telegram(RPCHandler): else: msg['stake_amount_fiat'] = 0 - message = (f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}" - f" (#{msg['trade_id']})\n" - f"*Amount:* `{msg['amount']:.8f}`\n" - f"*Open Rate:* `{msg['limit']:.8f}`\n" - f"*Current Rate:* `{msg['current_rate']:.8f}`\n" - f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}") - + content = [] + content.append( + f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}" + f" (#{msg['trade_id']})\n" + ) + if msg.get('buy_tag', None): + content.append(f"*Buy Tag:* `{msg['buy_tag']}`\n") + content.append(f"*Amount:* `{msg['amount']:.8f}`\n") + content.append(f"*Open Rate:* `{msg['limit']:.8f}`\n") + content.append(f"*Current Rate:* `{msg['current_rate']:.8f}`\n") + content.append( + f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" + ) if msg.get('fiat_currency', None): - message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" + content.append( + f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" + ) + + message = ''.join(content) message += ")`" return message @@ -354,6 +364,7 @@ class Telegram(RPCHandler): "*Trade ID:* `{trade_id}` `(since {open_date_hum})`", "*Current Pair:* {pair}", "*Amount:* `{amount} ({stake_amount} {base_currency})`", + "*Buy Tag:* `{buy_tag}`" if r['buy_tag'] else "", "*Open Rate:* `{open_rate:.8f}`", "*Close Rate:* `{close_rate}`" if r['close_rate'] else "", "*Current Rate:* `{current_rate:.8f}`", diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a5517403d..0206e8839 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -508,7 +508,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ if not isinstance(dataframe, DataFrame) or dataframe.empty: logger.warning(f'Empty candle (OHLCV) data for pair {pair}') - return False, False, '' + return False, False, None latest_date = dataframe['date'].max() latest = dataframe.loc[dataframe['date'] == latest_date].iloc[-1] @@ -523,11 +523,11 @@ class IStrategy(ABC, HyperStrategyMixin): 'Outdated history for pair %s. Last tick is %s minutes old', pair, int((arrow.utcnow() - latest_date).total_seconds() // 60) ) - return False, False, '' + return False, False, None (buy, sell, buy_tag) = latest[SignalType.BUY.value] == 1,\ latest[SignalType.SELL.value] == 1,\ - latest.get(SignalTagType.BUY_TAG.value, '') + latest.get(SignalTagType.BUY_TAG.value, None) logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) diff --git a/tests/conftest.py b/tests/conftest.py index 139d989a9..5f3a63c39 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -182,7 +182,7 @@ def get_patched_worker(mocker, config) -> Worker: return Worker(args=None, config=config) -def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False, '')) -> None: +def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False, None)) -> None: """ :param mocker: mocker to patch IStrategy class :param value: which value IStrategy.get_signal() must return diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 889aa05cf..1e8d38841 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -35,7 +35,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -192,7 +192,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: ) del default_conf['fiat_display_currency'] freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -239,7 +239,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -371,7 +371,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -459,7 +459,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -526,7 +526,7 @@ def test_rpc_balance_handle_error(default_conf, mocker): ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() with pytest.raises(RPCException, match="Error getting current tickers."): @@ -567,7 +567,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): ) default_conf['dry_run'] = False freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -612,7 +612,7 @@ def test_rpc_start(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -633,7 +633,7 @@ def test_rpc_stop(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -655,7 +655,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -687,7 +687,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -805,7 +805,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) # Create some test data @@ -838,7 +838,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) counts = rpc._rpc_count() @@ -863,7 +863,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -889,7 +889,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> # Test not buying freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot.config['stake_amount'] = 0 - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'TKN/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -902,7 +902,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'trader is not running'): @@ -913,7 +913,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'Forcebuy not enabled.'): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 74d40b611..f7d935b64 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -442,7 +442,7 @@ def test_api_balance(botclient, mocker, rpc_balance): def test_api_count(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -504,7 +504,7 @@ def test_api_locks(botclient): def test_api_show_config(botclient, mocker): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) rc = client_get(client, f"{BASE_URI}/show_config") assert_response(rc) @@ -522,7 +522,7 @@ def test_api_show_config(botclient, mocker): def test_api_daily(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -540,7 +540,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): def test_api_trades(botclient, mocker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets) @@ -568,7 +568,7 @@ def test_api_trades(botclient, mocker, fee, markets): def test_api_trade_single(botclient, mocker, fee, ticker, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -588,7 +588,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): def test_api_delete_trade(botclient, mocker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) stoploss_mock = MagicMock() cancel_mock = MagicMock() mocker.patch.multiple( @@ -662,7 +662,7 @@ def test_api_logs(botclient): def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -678,7 +678,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -729,7 +729,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -757,7 +757,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_performance(botclient, fee): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) trade = Trade( pair='LTC/ETH', @@ -803,7 +803,7 @@ def test_api_performance(botclient, fee): def test_api_status(botclient, mocker, ticker, fee, markets): freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -1044,7 +1044,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets), _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index df624e136..ab66d18e8 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -119,7 +119,7 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None: rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) @@ -139,7 +139,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) @@ -155,7 +155,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None freqtrade = FreqtradeBot(default_conf) rpc = RPC(freqtrade) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, '')) + patch_get_signal(freqtrade, (True, False, None)) dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False @@ -185,6 +185,7 @@ def test_telegram_status(default_conf, update, mocker) -> None: 'current_rate': 1.098e-05, 'amount': 90.99181074, 'stake_amount': 90.99181074, + 'buy_tag': None, 'close_profit_pct': None, 'profit': -0.0059, 'profit_pct': -0.59, @@ -228,7 +229,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.state = State.STOPPED # Status is also enabled when stopped @@ -285,7 +286,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.state = State.STOPPED # Status table is also enabled when stopped @@ -329,7 +330,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -400,7 +401,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Try invalid data msg_mock.reset_mock() @@ -432,7 +433,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -487,7 +488,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._stats(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -513,7 +514,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick side_effect=lambda a, b: f"{a}/{b}") telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -536,7 +537,7 @@ def test_balance_handle_empty_response(default_conf, update, mocker) -> None: mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.config['dry_run'] = False telegram._balance(update=update, context=MagicMock()) @@ -549,7 +550,7 @@ def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -578,7 +579,7 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None }) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._balance(update=update, context=MagicMock()) assert msg_mock.call_count > 1 @@ -677,7 +678,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -736,7 +737,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -797,7 +798,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -838,7 +839,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Trader is not running freqtradebot.state = State.STOPPED @@ -876,7 +877,7 @@ def test_forcebuy_handle(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # /forcebuy ETH/BTC context = MagicMock() @@ -905,7 +906,7 @@ def test_forcebuy_handle_exception(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) update.message.text = '/forcebuy ETH/Nonepair' telegram._forcebuy(update=update, context=MagicMock()) @@ -922,7 +923,7 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) context = MagicMock() context.args = [] @@ -950,7 +951,7 @@ def test_performance_handle(default_conf, update, ticker, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) # Create some test data freqtradebot.enter_positions() @@ -978,7 +979,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) freqtradebot.state = State.STOPPED telegram._count(update=update, context=MagicMock()) @@ -1007,7 +1008,7 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, '')) + patch_get_signal(freqtradebot, (True, False, None)) telegram._locks(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'No active locks.' in msg_mock.call_args_list[0][0][0] @@ -1253,6 +1254,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: msg = { 'type': RPCMessageType.BUY, 'trade_id': 1, + 'buy_tag': 'buy_signal_01', 'exchange': 'Binance', 'pair': 'ETH/BTC', 'limit': 1.099e-05, @@ -1270,6 +1272,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: telegram.send_msg(msg) assert msg_mock.call_args[0][0] \ == '\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' \ + '*Buy Tag:* `buy_signal_01`\n' \ '*Amount:* `1333.33333333`\n' \ '*Open Rate:* `0.00001099`\n' \ '*Current Rate:* `0.00001099`\n' \ @@ -1297,6 +1300,7 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY_CANCEL, + 'buy_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/BTC', @@ -1314,6 +1318,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY_FILL, + 'buy_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/USDT', @@ -1498,6 +1503,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: telegram.send_msg({ 'type': RPCMessageType.BUY, + 'buy_tag': 'buy_signal_01', 'trade_id': 1, 'exchange': 'Binance', 'pair': 'ETH/BTC', @@ -1512,6 +1518,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: 'open_date': arrow.utcnow().shift(hours=-1) }) assert msg_mock.call_args[0][0] == ('\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' + '*Buy Tag:* `buy_signal_01`\n' '*Amount:* `1333.33333333`\n' '*Open Rate:* `0.00001099`\n' '*Current Rate:* `0.00001099`\n' diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index c812f3007..792353f7a 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -38,15 +38,15 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): mocked_history['buy'] = 0 mocked_history.loc[1, 'sell'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, '') + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, '') + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 0 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, '') + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 mocked_history.loc[1, 'buy_tag'] = 'buy_signal_01' @@ -68,15 +68,15 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False, '') == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) + assert (False, False, None) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False, '') == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) + assert (False, False, None) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False, '') == _STRATEGY.get_signal( + assert (False, False, None) == _STRATEGY.get_signal( 'baz', default_conf['timeframe'], DataFrame([]) @@ -116,7 +116,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False, '') == _STRATEGY.get_signal( + assert (False, False, None) == _STRATEGY.get_signal( 'xyz', default_conf['timeframe'], mocked_history diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6372e6d36..abbef7858 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -536,7 +536,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None: ) default_conf['stake_amount'] = 10 freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(False, False, '')) + patch_get_signal(freqtrade, value=(False, False, None)) Trade.query = MagicMock() Trade.query.filter = MagicMock() @@ -1860,7 +1860,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limi assert trade.is_open is True freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.open_order_id == limit_sell_order['id'] @@ -1885,7 +1885,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, ) freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) freqtrade.enter_positions() @@ -1896,7 +1896,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert nb_trades == 0 # Buy is triggering, so buying ... - patch_get_signal(freqtrade, value=(True, False, '')) + patch_get_signal(freqtrade, value=(True, False, None)) freqtrade.enter_positions() trades = Trade.query.all() nb_trades = len(trades) @@ -1904,7 +1904,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are not triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(False, False, '')) + patch_get_signal(freqtrade, value=(False, False, None)) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1912,7 +1912,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Buy and Sell are triggering, so doing nothing ... - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trades[0]) is False trades = Trade.query.all() nb_trades = len(trades) @@ -1920,7 +1920,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert trades[0].is_open is True # Sell is triggering, guess what : we are Selling! - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) trades = Trade.query.all() assert freqtrade.handle_trade(trades[0]) is True @@ -1938,7 +1938,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, ) freqtrade = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtrade, value=(True, False, '')) + patch_get_signal(freqtrade, value=(True, False, None)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) freqtrade.enter_positions() @@ -1951,7 +1951,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, # we might just want to check if we are in a sell condition without # executing # if ROI is reached we must sell - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) @@ -1977,10 +1977,10 @@ def test_handle_trade_use_sell_signal( trade = Trade.query.first() trade.is_open = True - patch_get_signal(freqtrade, value=(False, False, '')) + patch_get_signal(freqtrade, value=(False, False, None)) assert not freqtrade.handle_trade(trade) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) @@ -3016,7 +3016,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is False freqtrade.strategy.sell_profit_offset = 0.0 @@ -3051,7 +3051,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, limit_bu trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3082,7 +3082,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, limit_buy_o trade = Trade.query.first() trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is False @@ -3114,7 +3114,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, limit_buy_ trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -3143,7 +3143,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ trade = Trade.query.first() amnt = trade.amount trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985)) assert freqtrade.handle_trade(trade) is True @@ -3262,11 +3262,11 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trade) is False # Test if buy-signal is absent (should sell due to roi = true) - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.ROI.value @@ -3527,11 +3527,11 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b trade = Trade.query.first() trade.update(limit_buy_order) # Sell due to min_roi_reached - patch_get_signal(freqtrade, value=(True, True, '')) + patch_get_signal(freqtrade, value=(True, True, None)) assert freqtrade.handle_trade(trade) is True # Test if buy-signal is absent - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -4059,7 +4059,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.wallets.update() assert trade.is_open is True - patch_get_signal(freqtrade, value=(False, True, '')) + patch_get_signal(freqtrade, value=(False, True, None)) assert freqtrade.handle_trade(trade) is True assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0] diff --git a/tests/test_persistence.py b/tests/test_persistence.py index af4979919..8b927be8b 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -861,7 +861,7 @@ def test_to_json(default_conf, fee): open_date=arrow.utcnow().shift(hours=-2).datetime, open_rate=0.123, exchange='binance', - buy_tag='', + buy_tag=None, open_order_id='dry_run_buy_12345' ) result = trade.to_json() @@ -911,7 +911,7 @@ def test_to_json(default_conf, fee): 'min_rate': None, 'max_rate': None, 'strategy': None, - 'buy_tag': '', + 'buy_tag': None, 'timeframe': None, 'exchange': 'binance', } From 46f2a20a98237aa86df2adf9804901635f66db16 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:00:51 +0700 Subject: [PATCH 161/519] run flake8 --- tests/strategy/test_interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 792353f7a..751f08344 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -68,7 +68,9 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False, None) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) + assert (False, False, None) == _STRATEGY.get_signal( + 'foo', default_conf['timeframe'], DataFrame() + ) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() From 25e329623ffad470264b52d8d54adc447149cebb Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:11:54 +0700 Subject: [PATCH 162/519] change signature --- freqtrade/strategy/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 0206e8839..49fae0ed6 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -497,7 +497,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: raise StrategyError(message) - def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, str]: + def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, Optional[str]]: """ Calculates current signal based based on the buy / sell columns of the dataframe. Used by Bot to get the signal to buy or sell From 643b6b950e951ab97ed2ed78a6532b5c8a23cb5e Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:23:34 +0700 Subject: [PATCH 163/519] run flake8 --- freqtrade/strategy/interface.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 49fae0ed6..a6329aaa1 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -497,7 +497,12 @@ class IStrategy(ABC, HyperStrategyMixin): else: raise StrategyError(message) - def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool, Optional[str]]: + def get_signal( + self, + pair: str, + timeframe: str, + dataframe: DataFrame + ) -> Tuple[bool, bool, Optional[str]]: """ Calculates current signal based based on the buy / sell columns of the dataframe. Used by Bot to get the signal to buy or sell From dd809f756bb378ab787685e50eea62797b63d321 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 02:34:20 +0700 Subject: [PATCH 164/519] run mypy --- freqtrade/freqtradebot.py | 2 +- freqtrade/persistence/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7756aa1d7..d0ffddd72 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -466,7 +466,7 @@ class FreqtradeBot(LoggingMixin): return False def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False, buy_tag: str = '') -> bool: + forcebuy: bool = False, buy_tag: Optional[str] = None) -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index f42166762..43fbec8c0 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -257,7 +257,7 @@ class LocalTrade(): sell_reason: str = '' sell_order_status: str = '' strategy: str = '' - buy_tag: str = '' + buy_tag: Optional[str] = None timeframe: Optional[int] = None def __init__(self, **kwargs): From b01daa8bbca7d7937dfdf5c85675eaf9d598d646 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Thu, 22 Jul 2021 13:09:05 +0700 Subject: [PATCH 165/519] expose buy_tag to api --- freqtrade/rpc/api_server/api_schemas.py | 1 + tests/rpc/test_rpc_apiserver.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index c6b6a6d28..318762136 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -151,6 +151,7 @@ class TradeSchema(BaseModel): amount_requested: float stake_amount: float strategy: str + buy_tag: Optional[str] timeframe: int fee_open: Optional[float] fee_open_cost: Optional[float] diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index f7d935b64..fef44cab2 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -875,6 +875,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'sell_reason': None, 'sell_order_status': None, 'strategy': 'DefaultStrategy', + 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', } @@ -1029,6 +1030,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'sell_reason': None, 'sell_order_status': None, 'strategy': 'DefaultStrategy', + 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', } From 986aafcdf9fc4e60cbde0e5bd524732b2808a3da Mon Sep 17 00:00:00 2001 From: Fausto Gutierrez <28719096+faustogut@users.noreply.github.com> Date: Thu, 22 Jul 2021 14:16:50 +0200 Subject: [PATCH 166/519] Fix configuration.md typos --- docs/configuration.md | 78 +++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index e1c7e77d5..5d4b1f2c3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -5,11 +5,11 @@ By default, these settings are configured via the configuration file (see below) ## The Freqtrade configuration file -The bot uses a set of configuration parameters during its operation that all together conform the bot configuration. It normally reads its configuration from a file (Freqtrade configuration file). +The bot uses a set of configuration parameters during its operation that all together conform to the bot configuration. It normally reads its configuration from a file (Freqtrade configuration file). Per default, the bot loads the configuration from the `config.json` file, located in the current working directory. -You can specify a different configuration file used by the bot with the `-c/--config` command line option. +You can specify a different configuration file used by the bot with the `-c/--config` command-line option. Multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream. @@ -25,30 +25,30 @@ Multiple configuration files can be specified and used by the bot or the bot can If you used the [Quick start](installation.md/#quick-start) method for installing the bot, the installation script should have already created the default configuration file (`config.json`) for you. -If default configuration file is not created we recommend you to use `freqtrade new-config --config config.json` to generate a basic configuration file. +If the default configuration file is not created we recommend you to use `freqtrade new-config --config config.json` to generate a basic configuration file. -The Freqtrade configuration file is to be written in the JSON format. +The Freqtrade configuration file is to be written in JSON format. Additionally to the standard JSON syntax, you may use one-line `// ...` and multi-line `/* ... */` comments in your configuration files and trailing commas in the lists of parameters. -Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates syntax of the configuration file at startup and will warn you if you made any errors editing it, pointing out problematic lines. +Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates the syntax of the configuration file at startup and will warn you if you made any errors editing it, pointing out problematic lines. ## Configuration parameters The table below will list all configuration parameters available. Freqtrade can also load many options via command line (CLI) arguments (check out the commands `--help` output for details). -The prevelance for all Options is as follows: +The prevalence for all Options is as follows: - CLI arguments override any other option -- Configuration files are used in sequence (last file wins), and override Strategy configurations. -- Strategy configurations are only used if they are not set via configuration or via command line arguments. These options are marked with [Strategy Override](#parameters-in-the-strategy) in the below table. +- Configuration files are used in sequence (the last file wins) and override Strategy configurations. +- Strategy configurations are only used if they are not set via configuration or command-line arguments. These options are marked with [Strategy Override](#parameters-in-the-strategy) in the below table. Mandatory parameters are marked as **Required**, which means that they are required to be set in one of the possible ways. | Parameter | Description | |------------|-------------| -| `max_open_trades` | **Required.** Number of open trades your bot is allowed to have. Only one open trade per pair is possible, so the length of your pairlist is another limitation which can apply. If -1 then it is ignored (i.e. potentially unlimited open trades, limited by the pairlist). [More information below](#configuring-amount-per-trade).
    **Datatype:** Positive integer or -1. +| `max_open_trades` | **Required.** Number of open trades your bot is allowed to have. Only one open trade per pair is possible, so the length of your pairlist is another limitation that can apply. If -1 then it is ignored (i.e. potentially unlimited open trades, limited by the pairlist). [More information below](#configuring-amount-per-trade).
    **Datatype:** Positive integer or -1. | `stake_currency` | **Required.** Crypto-currency used for trading.
    **Datatype:** String | `stake_amount` | **Required.** Amount of crypto-currency your bot will use for each trade. Set it to `"unlimited"` to allow the bot to use all available balance. [More information below](#configuring-amount-per-trade).
    **Datatype:** Positive float or `"unlimited"`. | `tradable_balance_ratio` | Ratio of the total account balance the bot is allowed to trade. [More information below](#configuring-amount-per-trade).
    *Defaults to `0.99` 99%).*
    **Datatype:** Positive float between `0.1` and `1.0`. @@ -141,7 +141,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi ### Parameters in the strategy -The following parameters can be set in configuration file or strategy. +The following parameters can be set in the configuration file or strategy. Values set in the configuration file always overwrite values set in the strategy. * `minimal_roi` @@ -169,15 +169,15 @@ There are several methods to configure how much of the stake currency the bot wi #### Minimum trade stake -The minimum stake amount will depend by exchange and pair, and is usually listed in the exchange support pages. +The minimum stake amount will depend on exchange and pair and is usually listed in the exchange support pages. Assuming the minimum tradable amount for XRP/USD is 20 XRP (given by the exchange), and the price is 0.6$. -The minimum stake amount to buy this pair is therefore `20 * 0.6 ~= 12`. +The minimum stake amount to buy this pair is, therefore, `20 * 0.6 ~= 12`. This exchange has also a limit on USD - where all orders must be > 10$ - which however does not apply in this case. To guarantee safe execution, freqtrade will not allow buying with a stake-amount of 10.1$, instead, it'll make sure that there's enough space to place a stoploss below the pair (+ an offset, defined by `amount_reserve_percent`, which defaults to 5%). -With a reserve of 5%, the minimum stake amount would be ~12.6$ (`12 * (1 + 0.05)`). If we take in account a stoploss of 10% on top of that - we'd end up with a value of ~14$ (`12.6 / (1 - 0.1)`). +With a reserve of 5%, the minimum stake amount would be ~12.6$ (`12 * (1 + 0.05)`). If we take into account a stoploss of 10% on top of that - we'd end up with a value of ~14$ (`12.6 / (1 - 0.1)`). To limit this calculation in case of large stoploss values, the calculated minimum stake-limit will never be more than 50% above the real limit. @@ -191,13 +191,13 @@ Freqtrade will reserve 1% for eventual fees when entering a trade and will there You can configure the "untouched" amount by using the `tradable_balance_ratio` setting. -For example, if you have 10 ETH available in your wallet on the exchange and `tradable_balance_ratio=0.5` (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers this as available balance. The rest of the wallet is untouched by the trades. +For example, if you have 10 ETH available in your wallet on the exchange and `tradable_balance_ratio=0.5` (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers this as an available balance. The rest of the wallet is untouched by the trades. !!! Danger This setting should **not** be used when running multiple bots on the same account. Please look at [Available Capital to the bot](#assign-available-capital) instead. !!! Warning - The `tradable_balance_ratio` setting applies to the current balance (free balance + tied up in trades). Therefore, assuming the starting balance of 1000, a configuration with `tradable_balance_ratio=0.99` will not guarantee that 10 currency units will always remain available on the exchange. For example, the free amount may reduce to 5 units if the total balance is reduced to 500 (either by a losing streak, or by withdrawing balance). + The `tradable_balance_ratio` setting applies to the current balance (free balance + tied up in trades). Therefore, assuming the starting balance of 1000, a configuration with `tradable_balance_ratio=0.99` will not guarantee that 10 currency units will always remain available on the exchange. For example, the free amount may reduce to 5 units if the total balance is reduced to 500 (either by a losing streak or by withdrawing balance). #### Assign available Capital @@ -207,7 +207,7 @@ This can be accomplished by setting `available_capital` to the desired starting Assuming your account has 10.000 USDT and you want to run 2 different strategies on this exchange. You'd set `available_capital=5000` - granting each bot an initial capital of 5000 USDT. The bot will then split this starting balance equally into `max_open_trades` buckets. -Profitable trades will result in increased stake-sizes for this bot - without affecting stake-sizes of the other bot. +Profitable trades will result in increased stake-sizes for this bot - without affecting the stake-sizes of the other bot. !!! Warning "Incompatible with `tradable_balance_ratio`" Setting this option will replace any configuration of `tradable_balance_ratio`. @@ -215,9 +215,9 @@ Profitable trades will result in increased stake-sizes for this bot - without af #### Amend last stake amount Assuming we have the tradable balance of 1000 USDT, `stake_amount=400`, and `max_open_trades=3`. -The bot would open 2 trades, and will be unable to fill the last trading slot, since the requested 400 USDT are no longer available, since 800 USDT are already tied in other trades. +The bot would open 2 trades and will be unable to fill the last trading slot, since the requested 400 USDT are no longer available since 800 USDT are already tied in other trades. -To overcome this, the option `amend_last_stake_amount` can be set to `True`, which will enable the bot to reduce stake_amount to the available balance in order to fill the last trade slot. +To overcome this, the option `amend_last_stake_amount` can be set to `True`, which will enable the bot to reduce stake_amount to the available balance to fill the last trade slot. In the example above this would mean: @@ -245,7 +245,7 @@ For example, the bot will at most use (0.05 BTC x 3) = 0.15 BTC, assuming a conf #### Dynamic stake amount -Alternatively, you can use a dynamic stake amount, which will use the available balance on the exchange, and divide that equally by the amount of allowed trades (`max_open_trades`). +Alternatively, you can use a dynamic stake amount, which will use the available balance on the exchange, and divide that equally by the number of allowed trades (`max_open_trades`). To configure this, set `stake_amount="unlimited"`. We also recommend to set `tradable_balance_ratio=0.99` (99%) - to keep a minimum balance for eventual fees. @@ -263,18 +263,18 @@ To allow the bot to trade all the available `stake_currency` in your account (mi ``` !!! Tip "Compounding profits" - This configuration will allow increasing / decreasing stakes depending on the performance of the bot (lower stake if bot is loosing, higher stakes if the bot has a winning record, since higher balances are available), and will result in profit compounding. + This configuration will allow increasing/decreasing stakes depending on the performance of the bot (lower stake if the bot is losing, higher stakes if the bot has a winning record since higher balances are available), and will result in profit compounding. !!! Note "When using Dry-Run Mode" - When using `"stake_amount" : "unlimited",` in combination with Dry-Run, Backtesting or Hyperopt, the balance will be simulated starting with a stake of `dry_run_wallet` which will evolve over time. - It is therefore important to set `dry_run_wallet` to a sensible value (like 0.05 or 0.01 for BTC and 1000 or 100 for USDT, for example), otherwise it may simulate trades with 100 BTC (or more) or 0.05 USDT (or less) at once - which may not correspond to your real available balance or is less than the exchange minimal limit for the order amount for the stake currency. + When using `"stake_amount" : "unlimited",` in combination with Dry-Run, Backtesting or Hyperopt, the balance will be simulated starting with a stake of `dry_run_wallet` which will evolve. + It is therefore important to set `dry_run_wallet` to a sensible value (like 0.05 or 0.01 for BTC and 1000 or 100 for USDT, for example), otherwise, it may simulate trades with 100 BTC (or more) or 0.05 USDT (or less) at once - which may not correspond to your real available balance or is less than the exchange minimal limit for the order amount for the stake currency. --8<-- "includes/pricing.md" ### Understand minimal_roi The `minimal_roi` configuration parameter is a JSON object where the key is a duration -in minutes and the value is the minimum ROI as ratio. +in minutes and the value is the minimum ROI as a ratio. See the example below: ```json @@ -289,7 +289,7 @@ See the example below: Most of the strategy files already include the optimal `minimal_roi` value. This parameter can be set in either Strategy or Configuration file. If you use it in the configuration file, it will override the `minimal_roi` value from the strategy file. -If it is not set in either Strategy or Configuration, a default of 1000% `{"0": 10}` is used, and minimal roi is disabled unless your trade generates 1000% profit. +If it is not set in either Strategy or Configuration, a default of 1000% `{"0": 10}` is used, and minimal ROI is disabled unless your trade generates 1000% profit. !!! Note "Special case to forcesell after a specific time" A special case presents using `"": -1` as ROI. This forces the bot to sell a trade after N Minutes, no matter if it's positive or negative, so represents a time-limited force-sell. @@ -335,7 +335,7 @@ the buy order is fulfilled. `order_types` set in the configuration file overwrites values set in the strategy as a whole, so you need to configure the whole `order_types` dictionary in one place. If this is configured, the following 4 values (`buy`, `sell`, `stoploss` and -`stoploss_on_exchange`) need to be present, otherwise the bot will fail to start. +`stoploss_on_exchange`) need to be present, otherwise, the bot will fail to start. For information on (`emergencysell`,`forcesell`, `forcebuy`, `stoploss_on_exchange`,`stoploss_on_exchange_interval`,`stoploss_on_exchange_limit_ratio`) please see stop loss documentation [stop loss on exchange](stoploss.md) @@ -386,7 +386,7 @@ Configuration: If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new stoploss order. !!! Warning "Warning: stoploss_on_exchange failures" - If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencysell` value in the `order_types` dictionary - however this is not advised. + If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencysell` value in the `order_types` dictionary - however, this is not advised. ### Understand order_time_in_force @@ -396,12 +396,12 @@ is executed on the exchange. Three commonly used time in force are: **GTC (Good Till Canceled):** This is most of the time the default time in force. It means the order will remain -on exchange till it is canceled by user. It can be fully or partially fulfilled. +on exchange till it is cancelled by the user. It can be fully or partially fulfilled. If partially fulfilled, the remaining will stay on the exchange till cancelled. **FOK (Fill Or Kill):** -It means if the order is not executed immediately AND fully then it is canceled by the exchange. +It means if the order is not executed immediately AND fully then it is cancelled by the exchange. **IOC (Immediate Or Canceled):** @@ -422,7 +422,7 @@ The possible values are: `gtc` (default), `fok` or `ioc`. ``` !!! Warning - This is an ongoing work. For now it is supported only for binance. + This is ongoing work. For now, it is supported only for binance. Please don't change the default value unless you know what you are doing and have researched the impact of using different values. ### Exchange configuration @@ -431,7 +431,7 @@ Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports exchange markets and trading APIs. The complete up-to-date list can be found in the [CCXT repo homepage](https://github.com/ccxt/ccxt/tree/master/python). However, the bot was tested by the development team with only Bittrex, Binance and Kraken, - so the these are the only officially supported exchanges: + so these are the only officially supported exchanges: - [Bittrex](https://bittrex.com/): "bittrex" - [Binance](https://www.binance.com/): "binance" @@ -457,11 +457,11 @@ A exchange configuration for "binance" would look as follows: }, ``` -This configuration enables binance, as well as rate limiting to avoid bans from the exchange. +This configuration enables binance, as well as rate-limiting to avoid bans from the exchange. `"rateLimit": 200` defines a wait-event of 0.2s between each call. This can also be completely disabled by setting `"enableRateLimit"` to false. !!! Note - Optimal settings for rate limiting depend on the exchange and the size of the whitelist, so an ideal parameter will vary on many other settings. + Optimal settings for rate-limiting depend on the exchange and the size of the whitelist, so an ideal parameter will vary on many other settings. We try to provide sensible defaults per exchange where possible, if you encounter bans please make sure that `"enableRateLimit"` is enabled and increase the `"rateLimit"` parameter step by step. ### What values can be used for fiat_display_currency? @@ -475,7 +475,7 @@ The valid values are: "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD" ``` -In addition to fiat currencies, a range of cryto currencies are supported. +In addition to fiat currencies, a range of crypto currencies is supported. The valid values are: @@ -486,7 +486,7 @@ The valid values are: ## Using Dry-run mode We recommend starting the bot in the Dry-run mode to see how your bot will -behave and what is the performance of your strategy. In the Dry-run mode the +behave and what is the performance of your strategy. In the Dry-run mode, the bot does not engage your money. It only runs a live simulation without creating trades on the exchange. @@ -512,7 +512,7 @@ creating trades on the exchange. Once you will be happy with your bot performance running in the Dry-run mode, you can switch it to production mode. !!! Note - A simulated wallet is available during dry-run mode, and will assume a starting capital of `dry_run_wallet` (defaults to 1000). + A simulated wallet is available during dry-run mode and will assume a starting capital of `dry_run_wallet` (defaults to 1000). ### Considerations for dry-run @@ -520,7 +520,7 @@ Once you will be happy with your bot performance running in the Dry-run mode, yo * Wallets (`/balance`) are simulated based on `dry_run_wallet`. * Orders are simulated, and will not be posted to the exchange. * Market orders fill based on orderbook volume the moment the order is placed. -* Limit orders fill once price reaches the defined level - or time out based on `unfilledtimeout` settings. +* Limit orders fill once the price reaches the defined level - or time out based on `unfilledtimeout` settings. * In combination with `stoploss_on_exchange`, the stop_loss price is assumed to be filled. * Open orders (not trades, which are stored in the database) are reset on bot restart. @@ -533,7 +533,7 @@ you run it in production mode. ### Setup your exchange account You will need to create API Keys (usually you get `key` and `secret`, some exchanges require an additional `password`) from the Exchange website and you'll need to insert this into the appropriate fields in the configuration or when asked by the `freqtrade new-config` command. -API Keys are usually only required for live trading (trading for real money, bot running in "production mode", executing real orders on the exchange) and are not required for the bot running in dry-run (trade simulation) mode. When you setup the bot in dry-run mode, you may fill these fields with empty values. +API Keys are usually only required for live trading (trading for real money, bot running in "production mode", executing real orders on the exchange) and are not required for the bot running in dry-run (trade simulation) mode. When you set up the bot in dry-run mode, you may fill these fields with empty values. ### To switch your bot in production mode @@ -545,7 +545,7 @@ API Keys are usually only required for live trading (trading for real money, bot "dry_run": false, ``` -**Insert your Exchange API key (change them by fake api keys):** +**Insert your Exchange API key (change them by fake API keys):** ```json { @@ -563,7 +563,7 @@ API Keys are usually only required for live trading (trading for real money, bot You should also make sure to read the [Exchanges](exchanges.md) section of the documentation to be aware of potential configuration details specific to your exchange. !!! Hint "Keep your secrets secret" - To keep your secrets secret, we recommend to use a 2nd configuration for your API keys. + To keep your secrets secret, we recommend using a 2nd configuration for your API keys. Simply use the above snippet in a new configuration file (e.g. `config-private.json`) and keep your settings in this file. You can then start the bot with `freqtrade trade --config user_data/config.json --config user_data/config-private.json <...>` to have your keys loaded. From 65b4705b67b932903737dc4a70d67250435ae1a1 Mon Sep 17 00:00:00 2001 From: Kevin Julian Date: Fri, 23 Jul 2021 01:16:10 +0700 Subject: [PATCH 167/519] Update docs/strategy-advanced.md Co-authored-by: Matthias --- docs/strategy-advanced.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 8ae0c1d16..32113fede 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -116,7 +116,7 @@ See [Dataframe access](#dataframe-access) for more information about dataframe u ## Buy Tag -When your strategy has multiple buy signal, you can name it. +When your strategy has multiple buy signals, you can name the signal that triggered. Then you can access you buy signal on `custom_sell` ```python From dd923c34713b9d2a316261e516294c4a4b4f929c Mon Sep 17 00:00:00 2001 From: Kevin Julian Date: Fri, 23 Jul 2021 01:16:24 +0700 Subject: [PATCH 168/519] Update docs/strategy-advanced.md Co-authored-by: Matthias --- docs/strategy-advanced.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 32113fede..62b1533a6 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -126,7 +126,7 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: (dataframe['rsi'] < 35) & (dataframe['volume'] > 0) ), - ['buy', 'buy_tag']] = 1, 'buy_signal_rsi' + ['buy', 'buy_tag']] = (1, 'buy_signal_rsi') return dataframe From b84a1d0c92596f9a5fae34611da8eafe5ba4e981 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 22 Jul 2021 20:21:04 +0200 Subject: [PATCH 169/519] Don't crash when *_params is not defined in strategy closes #5407 --- freqtrade/strategy/hyper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/strategy/hyper.py b/freqtrade/strategy/hyper.py index fa3384660..b067e19d5 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -338,8 +338,8 @@ class HyperStrategyMixin(object): params = self.load_params_from_file() params = params.get('params', {}) self._ft_params_from_file = params - buy_params = deep_merge_dicts(params.get('buy', {}), getattr(self, 'buy_params', None)) - sell_params = deep_merge_dicts(params.get('sell', {}), getattr(self, 'sell_params', None)) + buy_params = deep_merge_dicts(params.get('buy', {}), getattr(self, 'buy_params', {})) + sell_params = deep_merge_dicts(params.get('sell', {}), getattr(self, 'sell_params', {})) self._load_params(buy_params, 'buy', hyperopt) self._load_params(sell_params, 'sell', hyperopt) From 5fe18be4b57163971329905afb9e8833f77ed154 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 01:25:15 +0700 Subject: [PATCH 170/519] add note buy_tag and split 3 assignment for get_signal --- docs/strategy-advanced.md | 4 ++++ freqtrade/strategy/interface.py | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 62b1533a6..fb78a8796 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -140,6 +140,10 @@ def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_r ``` +!!! Note + `buy_tag` is limited to 100 characters, remaining data will be truncated. + + ## Custom stoploss The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss. diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a6329aaa1..35c162c38 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -530,9 +530,10 @@ class IStrategy(ABC, HyperStrategyMixin): ) return False, False, None - (buy, sell, buy_tag) = latest[SignalType.BUY.value] == 1,\ - latest[SignalType.SELL.value] == 1,\ - latest.get(SignalTagType.BUY_TAG.value, None) + buy = latest[SignalType.BUY.value] == 1 + sell = latest[SignalType.SELL.value] == 1 + buy_tag = latest.get(SignalTagType.BUY_TAG.value, None) + logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell)) timeframe_seconds = timeframe_to_seconds(timeframe) From 65fc094c9f4ac42e5092aefab5aa5160b076207e Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 09:31:51 +0700 Subject: [PATCH 171/519] add to webhook-config --- docs/webhook-config.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/webhook-config.md b/docs/webhook-config.md index 8ce6edc18..288afc384 100644 --- a/docs/webhook-config.md +++ b/docs/webhook-config.md @@ -83,6 +83,7 @@ Possible parameters are: * `fiat_currency` * `order_type` * `current_rate` +* `buy_tag` ### Webhookbuycancel @@ -100,6 +101,7 @@ Possible parameters are: * `fiat_currency` * `order_type` * `current_rate` +* `buy_tag` ### Webhookbuyfill @@ -115,6 +117,7 @@ Possible parameters are: * `stake_amount` * `stake_currency` * `fiat_currency` +* `buy_tag` ### Webhooksell From aea5da0c738bc281bc9513f2c18ac1812ffdddda Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 11:42:43 +0700 Subject: [PATCH 172/519] changes testcase --- freqtrade/optimize/backtesting.py | 13 +- freqtrade/strategy/interface.py | 2 +- tests/optimize/__init__.py | 6 +- tests/optimize/test_backtest_detail.py | 442 ++++++++++++------------- tests/optimize/test_backtesting.py | 2 +- 5 files changed, 236 insertions(+), 229 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index fdf341e21..0c586883b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -210,7 +210,7 @@ class Backtesting: """ # Every change to this headers list must evaluate further usages of the resulting tuple # and eventually change the constants for indexes at the top - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_tag'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] data: Dict = {} self.progress.init_step(BacktestState.CONVERT, len(processed)) @@ -218,10 +218,13 @@ class Backtesting: for pair, pair_data in processed.items(): self.check_abort() self.progress.increment() + has_buy_tag = 'buy_tag' in pair_data + headers = headers + ['buy_tag'] if has_buy_tag else headers if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist - pair_data.loc[:, 'buy_tag'] = '' # cleanup if buy_tag is exist + if has_buy_tag: + pair_data.loc[:, 'buy_tag'] = None # cleanup if buy_tag is exist df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() @@ -230,7 +233,8 @@ class Backtesting: # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1) - df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) + if has_buy_tag: + df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) df_analyzed.drop(df_analyzed.head(1).index, inplace=True) @@ -361,6 +365,7 @@ class Backtesting: if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount): # Enter trade + has_buy_tag = len(row) >= BUY_TAG_IDX + 1 trade = LocalTrade( pair=pair, open_rate=row[OPEN_IDX], @@ -370,7 +375,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_tag=row[BUY_TAG_IDX], + buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None, exchange='backtesting', ) return trade diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 35c162c38..f10a12fa9 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -422,7 +422,7 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug("Skipping TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 - dataframe['buy_tag'] = '' + dataframe['buy_tag'] = None # Other Defs in strategy that want to be called every loop here # twitter_sell = self.watch_twitter_feed(dataframe, metadata) diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index 240f3f871..80fce9ca5 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -18,7 +18,7 @@ class BTrade(NamedTuple): sell_reason: SellType open_tick: int close_tick: int - buy_tag: Optional[str] = '' + buy_tag: Optional[str] = None class BTContainer(NamedTuple): @@ -44,7 +44,9 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell', 'buy_tag'] + + columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] + columns = columns + ['buy_tag'] if len(data[0]) == 9 else columns frame = DataFrame.from_records(data, columns=columns) frame['date'] = frame['date'].apply(_get_frame_time_from_offset) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index f7077ef56..0d4647308 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -14,13 +14,13 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, # Test 0: Sell with signal sell in candle 3 # Test with Stop-loss at 1% tc0 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], # exit with stoploss hit - [3, 5010, 5000, 4980, 5010, 6172, 0, 1, ''], - [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], # exit with stoploss hit + [3, 5010, 5000, 4980, 5010, 6172, 0, 1], + [4, 5010, 4987, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -28,13 +28,13 @@ tc0 = BTContainer(data=[ # Test 1: Stop-Loss Triggered 1% loss # Test with Stop-loss at 1% tc1 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4600, 4600, 6172, 0, 0, ''], # exit with stoploss hit - [3, 4975, 5000, 4980, 4977, 6172, 0, 0, ''], - [4, 4977, 4987, 4977, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4980, 4977, 6172, 0, 0], + [4, 4977, 4987, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -43,13 +43,13 @@ tc1 = BTContainer(data=[ # Test 2: Minus 4% Low, minus 1% close # Test with Stop-Loss at 3% tc2 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4962, 4975, 6172, 0, 0, ''], - [3, 4975, 5000, 4800, 4962, 6172, 0, 0, ''], # exit with stoploss hit - [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4962, 4975, 6172, 0, 0], + [3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.03, roi={"0": 1}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -62,14 +62,14 @@ tc2 = BTContainer(data=[ # Trade-A: Stop-Loss Triggered 2% Loss # Trade-B: Stop-Loss Triggered 2% Loss tc3 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4800, 4975, 6172, 0, 0, ''], # exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 1, 0, ''], - [4, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], # enter trade 2 (signal on last candle) - [5, 4962, 4987, 4000, 4000, 6172, 0, 0, ''], # exit with stoploss hit - [6, 4950, 4975, 4975, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 1, 0], + [4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle) + [5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit + [6, 4950, 4975, 4975, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 1}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)] @@ -80,13 +80,13 @@ tc3 = BTContainer(data=[ # Test with Stop-loss at 2% ROI 6% # Stop-Loss Triggered 2% Loss tc4 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5750, 4850, 5750, 6172, 0, 0, ''], # Exit with stoploss hit - [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], - [4, 4962, 4987, 4937, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4937, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 0.06}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -94,13 +94,13 @@ tc4 = BTContainer(data=[ # Test 5: Drops 0.5% Closes +20%, ROI triggers 3% Gain # stop-loss: 1%, ROI: 3% tc5 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4980, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4980, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5025, 4975, 4987, 6172, 0, 0, ''], - [3, 4975, 6000, 4975, 6000, 6172, 0, 0, ''], # ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4980, 4987, 6172, 1, 0], + [1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5025, 4975, 4987, 6172, 0, 0], + [3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -108,13 +108,13 @@ tc5 = BTContainer(data=[ # Test 6: Drops 3% / Recovers 6% Positive / Closes 1% positve, Stop-Loss triggers 2% Loss # stop-loss: 2% ROI: 5% tc6 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5300, 4850, 5050, 6172, 0, 0, ''], # Exit with stoploss - [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 0.05}, profit_perc=-0.02, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)] ) @@ -122,13 +122,13 @@ tc6 = BTContainer(data=[ # Test 7: 6% Positive / 1% Negative / Close 1% Positve, ROI Triggers 3% Gain # stop-loss: 2% ROI: 3% tc7 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], - [3, 4975, 5000, 4950, 4962, 6172, 0, 0, ''], - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4975, 5000, 4950, 4962, 6172, 0, 0], + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.02, roi={"0": 0.03}, profit_perc=0.03, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)] ) @@ -137,12 +137,12 @@ tc7 = BTContainer(data=[ # Test 8: trailing_stop should raise so candle 3 causes a stoploss. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 2 tc8 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], - [2, 5000, 5250, 4750, 4850, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0], + [2, 5000, 5250, 4750, 4850, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.055, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -151,12 +151,12 @@ tc8 = BTContainer(data=[ # Test 9: trailing_stop should raise - high and low in same candle. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted in candle 3 tc9 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], - [2, 5000, 5050, 4950, 5000, 6172, 0, 0, ''], - [3, 5000, 5200, 4550, 4850, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5000, 6172, 0, 0], + [2, 5000, 5050, 4950, 5000, 6172, 0, 0], + [3, 5000, 5200, 4550, 4850, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.064, trailing_stop=True, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -165,12 +165,12 @@ tc9 = BTContainer(data=[ # without applying trailing_stop_positive since stoploss_offset is at 10%. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc10 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.1, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.10, trailing_stop_positive=0.03, @@ -181,12 +181,12 @@ tc10 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc11 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 5000, 5150, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 5000, 5150, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -197,12 +197,12 @@ tc11 = BTContainer(data=[ # applying a positive trailing stop of 3% since stop_positive_offset is reached. # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc12 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.019, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -212,12 +212,12 @@ tc12 = BTContainer(data=[ # Test 13: Buy and sell ROI on same candle # stop-loss: 10% (should not apply), ROI: 1% tc13 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5100, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4850, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0], + [4, 4750, 4950, 4850, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1)] ) @@ -225,12 +225,12 @@ tc13 = BTContainer(data=[ # Test 14 - Buy and Stoploss on same candle # stop-loss: 5%, ROI: 10% (should not apply) tc14 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5100, 4600, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4850, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4600, 5100, 6172, 0, 0], + [2, 5100, 5251, 4850, 5100, 6172, 0, 0], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.05, roi={"0": 0.10}, profit_perc=-0.05, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] ) @@ -239,12 +239,12 @@ tc14 = BTContainer(data=[ # Test 15 - Buy and ROI on same candle, followed by buy and Stoploss on next candle # stop-loss: 5%, ROI: 10% (should not apply) tc15 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5100, 4900, 5100, 6172, 1, 0, ''], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4850, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5100, 4900, 5100, 6172, 1, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4850, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.05, roi={"0": 0.01}, profit_perc=-0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=1), BTrade(sell_reason=SellType.STOP_LOSS, open_tick=2, close_tick=2)] @@ -254,13 +254,13 @@ tc15 = BTContainer(data=[ # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration) tc16 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], - [3, 4975, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -270,13 +270,13 @@ tc16 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe. tc17 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5050, 6172, 0, 0, ''], - [3, 4980, 5000, 4940, 4962, 6172, 0, 0, ''], # ForceSell on ROI (roi=-1) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5050, 6172, 0, 0], + [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -286,13 +286,13 @@ tc17 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses open_rate as sell-price tc18 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], - [3, 5200, 5220, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI (sells on open) - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4950, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5200, 5220, 4940, 4962, 6172, 0, 0], # Sell on ROI (sells on open) + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.04, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -301,13 +301,13 @@ tc18 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc19 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], - [3, 5000, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5000, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "120": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -316,13 +316,13 @@ tc19 = BTContainer(data=[ # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # uses calculated ROI (1%) as sell rate, otherwise identical to tc18 tc20 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], - [2, 4987, 5300, 4950, 5200, 6172, 0, 0, ''], - [3, 5200, 5300, 4940, 4962, 6172, 0, 0, ''], # Sell on ROI - [4, 4962, 4987, 4972, 4950, 6172, 0, 0, ''], - [5, 4550, 4975, 4925, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], + [2, 4987, 5300, 4950, 5200, 6172, 0, 0], + [3, 5200, 5300, 4940, 4962, 6172, 0, 0], # Sell on ROI + [4, 4962, 4987, 4972, 4950, 6172, 0, 0], + [5, 4550, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "119": 0.01}, profit_perc=0.01, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -332,12 +332,12 @@ tc20 = BTContainer(data=[ # which cannot happen in reality # stop-loss: 10%, ROI: 4%, Trailing stop adjusted at the sell candle tc21 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 4650, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 4650, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -348,12 +348,12 @@ tc21 = BTContainer(data=[ # applying a positive trailing stop of 3% - ROI should apply before trailing stop. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2 tc22 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.04}, profit_perc=0.04, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -367,12 +367,12 @@ tc22 = BTContainer(data=[ # Stoploss would trigger in this candle too, but it's no longer relevant. # stop-loss: 10%, ROI: 4%, stoploss adjusted candle 2, ROI adjusted in candle 3 (causing the sell) tc23 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5251, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5251, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.1, "119": 0.03}, profit_perc=0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -383,13 +383,13 @@ tc23 = BTContainer(data=[ # Stoploss at 1%. # Stoploss wins over Sell-signal (because sell-signal is acted on in the next candle) tc24 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5000, 4855, 5010, 6172, 0, 1, ''], # Triggers stoploss + sellsignal - [4, 5010, 4987, 4977, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5000, 4855, 5010, 6172, 0, 1], # Triggers stoploss + sellsignal + [4, 5010, 4987, 4977, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=-0.01, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)] ) @@ -398,13 +398,13 @@ tc24 = BTContainer(data=[ # Stoploss at 1%. # Sell-signal wins over stoploss tc25 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5000, 4986, 5010, 6172, 0, 1, ''], - [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], # Triggers stoploss + sellsignal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5000, 4986, 5010, 6172, 0, 1], + [4, 5010, 4987, 4855, 4995, 6172, 0, 0], # Triggers stoploss + sellsignal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 1}, profit_perc=0.002, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=4)] ) @@ -413,13 +413,13 @@ tc25 = BTContainer(data=[ # Stoploss at 10% (irrelevant), ROI at 5% (will trigger) # Sell-signal wins over stoploss tc26 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5251, 4986, 5010, 6172, 0, 1, ''], # Triggers ROI, sell-signal - [4, 5010, 4987, 4855, 4995, 6172, 0, 0, ''], - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5251, 4986, 5010, 6172, 0, 1], # Triggers ROI, sell-signal + [4, 5010, 4987, 4855, 4995, 6172, 0, 0], + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)] ) @@ -429,13 +429,13 @@ tc26 = BTContainer(data=[ # TODO: figure out if sell-signal should win over ROI # Sell-signal wins over stoploss tc27 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4987, 5012, 4986, 4600, 6172, 0, 0, ''], - [3, 5010, 5012, 4986, 5010, 6172, 0, 1, ''], # sell-signal - [4, 5010, 5251, 4855, 4995, 6172, 0, 0, ''], # Triggers ROI, sell-signal acted on - [5, 4995, 4995, 4995, 4950, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4987, 5012, 4986, 4600, 6172, 0, 0], + [3, 5010, 5012, 4986, 5010, 6172, 0, 1], # sell-signal + [4, 5010, 5251, 4855, 4995, 6172, 0, 0], # Triggers ROI, sell-signal acted on + [5, 4995, 4995, 4995, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.05}, profit_perc=0.05, use_sell_signal=True, trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=4)] ) @@ -445,12 +445,12 @@ tc27 = BTContainer(data=[ # therefore "open" will be used # stop-loss: 10%, ROI: 10% (should not apply), stoploss adjusted candle 2 tc28 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 4950, 5100, 6172, 0, 0, ''], - [2, 5100, 5251, 5100, 5100, 6172, 0, 0, ''], - [3, 4850, 5050, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 4950, 5100, 6172, 0, 0], + [2, 5100, 5251, 5100, 5100, 6172, 0, 0], + [3, 4850, 5050, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.03, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.05, trailing_stop_positive=0.03, @@ -461,12 +461,12 @@ tc28 = BTContainer(data=[ # high of stoploss candle. # stop-loss: 10%, ROI: 10% (should not apply) tc29 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5050, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], # Triggers trailing-stoploss - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5050, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Triggers trailing-stoploss + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.02, trailing_stop=True, trailing_stop_positive=0.03, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=2)] @@ -475,12 +475,12 @@ tc29 = BTContainer(data=[ # Test 30: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc30 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_stop_positive=0.01, trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)] @@ -489,12 +489,12 @@ tc30 = BTContainer(data=[ # Test 31: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 10%, ROI: 10% (should not apply) tc31 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10}, profit_perc=0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, @@ -504,12 +504,12 @@ tc31 = BTContainer(data=[ # Test 32: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) tc32 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5050, 4950, 5000, 6172, 1, 0, ''], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + # D O H L C V B S + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, @@ -519,12 +519,12 @@ tc32 = BTContainer(data=[ # Test 33: trailing_stop should be triggered immediately on trade open candle. # stop-loss: 1%, ROI: 10% (should not apply) tc33 = BTContainer(data=[ - # D O H L C V B S SN + # D O H L C V B S BT [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 'buy_signal_01'], - [1, 5000, 5500, 5000, 4900, 6172, 0, 0, ''], # enter trade (signal on last candle) and stop - [2, 4900, 5250, 4500, 5100, 6172, 0, 0, ''], - [3, 5100, 5100, 4650, 4750, 6172, 0, 0, ''], - [4, 4750, 4950, 4350, 4750, 6172, 0, 0, '']], + [1, 5000, 5500, 5000, 4900, 6172, 0, 0, None], # enter trade (signal on last candle) and stop + [2, 4900, 5250, 4500, 5100, 6172, 0, 0, None], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, None], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, None]], stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True, trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02, trailing_stop_positive=0.01, use_custom_stoploss=True, diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 873b27b4d..87f968d6b 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -584,7 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'min_rate': [0.1038, 0.10302485], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], - 'buy_tag': ['', ''], + 'buy_tag': [None, None], }) pd.testing.assert_frame_equal(results, expected) data_pair = processed[pair] From 8032257fdf8362e63e5f60b37db62e41ccbc7fc8 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:30:25 +0700 Subject: [PATCH 173/519] revert test_pairlist --- tests/plugins/test_pairlist.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 510e9a0ae..b15126a33 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -925,20 +925,20 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo get_tickers=tickers ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - freqtrade.pairlists.refresh_pairlist() + ftbot = get_patched_freqtradebot(mocker, default_conf) + ftbot.pairlists.refresh_pairlist() - assert len(freqtrade.pairlists.whitelist) == 5 + assert len(ftbot.pairlists.whitelist) == 5 tickers.return_value['ETH/BTC']['ask'] = 0.0 del tickers.return_value['TKN/BTC'] del tickers.return_value['LTC/BTC'] mocker.patch.multiple('freqtrade.exchange.Exchange', get_tickers=tickers) - freqtrade.pairlists.refresh_pairlist() + ftbot.pairlists.refresh_pairlist() assert log_has_re(r'Removed .* invalid ticker data.*', caplog) - assert len(freqtrade.pairlists.whitelist) == 2 + assert len(ftbot.pairlists.whitelist) == 2 @pytest.mark.parametrize("pairlistconfig,desc_expected,exception_expected", [ From acfaa39e5446e0f7f8b491f92c74b305b818687b Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:34:18 +0700 Subject: [PATCH 174/519] revert back test_rpc_api_server --- tests/rpc/test_rpc_apiserver.py | 138 ++++++++++++++++---------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index fef44cab2..803ef7e5d 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -46,13 +46,13 @@ def botclient(default_conf, mocker): "password": _TEST_PASS, }}) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(freqtrade) + ftbot = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(ftbot) mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api', MagicMock()) try: apiserver = ApiServer(default_conf) apiserver.add_rpc_handler(rpc) - yield freqtrade, TestClient(apiserver.app) + yield ftbot, TestClient(apiserver.app) # Cleanup ... ? finally: ApiServer.shutdown() @@ -88,7 +88,7 @@ def assert_response(response, expected_code=200, needs_cors=True): def test_api_not_found(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/invalid_url") assert_response(rc, 404) @@ -96,7 +96,7 @@ def test_api_not_found(botclient): def test_api_ui_fallback(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, "/favicon.ico") assert rc.status_code == 200 @@ -140,7 +140,7 @@ def test_api_auth(): def test_api_unauthorized(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client.get(f"{BASE_URI}/ping") assert_response(rc, needs_cors=False) assert rc.json() == {'status': 'pong'} @@ -151,20 +151,20 @@ def test_api_unauthorized(botclient): assert rc.json() == {'detail': 'Unauthorized'} # Change only username - freqtrade.config['api_server']['username'] = 'Ftrader' + ftbot.config['api_server']['username'] = 'Ftrader' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} # Change only password - freqtrade.config['api_server']['username'] = _TEST_USER - freqtrade.config['api_server']['password'] = 'WrongPassword' + ftbot.config['api_server']['username'] = _TEST_USER + ftbot.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) assert rc.json() == {'detail': 'Unauthorized'} - freqtrade.config['api_server']['username'] = 'Ftrader' - freqtrade.config['api_server']['password'] = 'WrongPassword' + ftbot.config['api_server']['username'] = 'Ftrader' + ftbot.config['api_server']['password'] = 'WrongPassword' rc = client_get(client, f"{BASE_URI}/version") assert_response(rc, 401) @@ -172,7 +172,7 @@ def test_api_unauthorized(botclient): def test_api_token_login(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client.post(f"{BASE_URI}/token/login", data=None, headers={'Authorization': _basic_auth_str('WRONG_USER', 'WRONG_PASS'), @@ -191,7 +191,7 @@ def test_api_token_login(botclient): def test_api_token_refresh(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/token/login") assert_response(rc) rc = client.post(f"{BASE_URI}/token/refresh", @@ -204,12 +204,12 @@ def test_api_token_refresh(botclient): def test_api_stop_workflow(botclient): - freqtrade, client = botclient - assert freqtrade.state == State.RUNNING + ftbot, client = botclient + assert ftbot.state == State.RUNNING rc = client_post(client, f"{BASE_URI}/stop") assert_response(rc) assert rc.json() == {'status': 'stopping trader ...'} - assert freqtrade.state == State.STOPPED + assert ftbot.state == State.STOPPED # Stop bot again rc = client_post(client, f"{BASE_URI}/stop") @@ -220,7 +220,7 @@ def test_api_stop_workflow(botclient): rc = client_post(client, f"{BASE_URI}/start") assert_response(rc) assert rc.json() == {'status': 'starting trader ...'} - assert freqtrade.state == State.RUNNING + assert ftbot.state == State.RUNNING # Call start again rc = client_post(client, f"{BASE_URI}/start") @@ -399,32 +399,32 @@ def test_api_cleanup(default_conf, mocker, caplog): def test_api_reloadconf(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/reload_config") assert_response(rc) assert rc.json() == {'status': 'Reloading config ...'} - assert freqtrade.state == State.RELOAD_CONFIG + assert ftbot.state == State.RELOAD_CONFIG def test_api_stopbuy(botclient): - freqtrade, client = botclient - assert freqtrade.config['max_open_trades'] != 0 + ftbot, client = botclient + assert ftbot.config['max_open_trades'] != 0 rc = client_post(client, f"{BASE_URI}/stopbuy") assert_response(rc) assert rc.json() == {'status': 'No more buy will occur from now. Run /reload_config to reset.'} - assert freqtrade.config['max_open_trades'] == 0 + assert ftbot.config['max_open_trades'] == 0 def test_api_balance(botclient, mocker, rpc_balance): - freqtrade, client = botclient + ftbot, client = botclient - freqtrade.config['dry_run'] = False + ftbot.config['dry_run'] = False mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', side_effect=lambda a, b: f"{a}/{b}") - freqtrade.wallets.update() + ftbot.wallets.update() rc = client_get(client, f"{BASE_URI}/balance") assert_response(rc) @@ -441,8 +441,8 @@ def test_api_balance(botclient, mocker, rpc_balance): def test_api_count(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -463,13 +463,13 @@ def test_api_count(botclient, mocker, ticker, fee, markets): assert rc.json()["current"] == 4 assert rc.json()["max"] == 1 - freqtrade.config['max_open_trades'] = float('inf') + ftbot.config['max_open_trades'] = float('inf') rc = client_get(client, f"{BASE_URI}/count") assert rc.json()["max"] == -1 def test_api_locks(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/locks") assert_response(rc) @@ -503,8 +503,8 @@ def test_api_locks(botclient): def test_api_show_config(botclient, mocker): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) rc = client_get(client, f"{BASE_URI}/show_config") assert_response(rc) @@ -521,8 +521,8 @@ def test_api_show_config(botclient, mocker): def test_api_daily(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -539,8 +539,8 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): def test_api_trades(botclient, mocker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets) @@ -567,8 +567,8 @@ def test_api_trades(botclient, mocker, fee, markets): def test_api_trade_single(botclient, mocker, fee, ticker, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -587,8 +587,8 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): def test_api_delete_trade(botclient, mocker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) stoploss_mock = MagicMock() cancel_mock = MagicMock() mocker.patch.multiple( @@ -603,7 +603,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): create_mock_trades(fee) Trade.query.session.flush() - freqtrade.strategy.order_types['stoploss_on_exchange'] = True + ftbot.strategy.order_types['stoploss_on_exchange'] = True trades = Trade.query.all() trades[1].stoploss_order_id = '1234' assert len(trades) > 2 @@ -629,7 +629,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets): def test_api_logs(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/logs") assert_response(rc) assert len(rc.json()) == 2 @@ -661,8 +661,8 @@ def test_api_logs(botclient): def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -677,8 +677,8 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -728,8 +728,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -756,8 +756,8 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_performance(botclient, fee): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) trade = Trade( pair='LTC/ETH', @@ -802,8 +802,8 @@ def test_api_performance(botclient, fee): def test_api_status(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient - patch_get_signal(freqtrade, (True, False, None)) + ftbot, client = botclient + patch_get_signal(ftbot, (True, False, None)) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -891,7 +891,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): def test_api_version(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/version") assert_response(rc) @@ -899,7 +899,7 @@ def test_api_version(botclient): def test_api_blacklist(botclient, mocker): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/blacklist") assert_response(rc) @@ -934,7 +934,7 @@ def test_api_blacklist(botclient, mocker): def test_api_whitelist(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/whitelist") assert_response(rc) @@ -946,7 +946,7 @@ def test_api_whitelist(botclient): def test_api_forcebuy(botclient, mocker, fee): - freqtrade, client = botclient + ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/forcebuy", data='{"pair": "ETH/BTC"}') @@ -954,7 +954,7 @@ def test_api_forcebuy(botclient, mocker, fee): assert rc.json() == {"error": "Error querying /api/v1/forcebuy: Forcebuy not enabled."} # enable forcebuy - freqtrade.config['forcebuy_enable'] = True + ftbot.config['forcebuy_enable'] = True fbuy_mock = MagicMock(return_value=None) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) @@ -1037,7 +1037,7 @@ def test_api_forcebuy(botclient, mocker, fee): def test_api_forcesell(botclient, mocker, ticker, fee, markets): - freqtrade, client = botclient + ftbot, client = botclient mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -1046,14 +1046,14 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets), _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(ftbot, (True, False, None)) rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') assert_response(rc, 502) assert rc.json() == {"error": "Error querying /api/v1/forcesell: invalid argument"} - freqtrade.enter_positions() + ftbot.enter_positions() rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') @@ -1062,7 +1062,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): def test_api_pair_candles(botclient, ohlcv_history): - freqtrade, client = botclient + ftbot, client = botclient timeframe = '5m' amount = 3 @@ -1090,7 +1090,7 @@ def test_api_pair_candles(botclient, ohlcv_history): ohlcv_history.loc[1, 'buy'] = 1 ohlcv_history['sell'] = 0 - freqtrade.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) + ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history) rc = client_get(client, f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}") @@ -1128,7 +1128,7 @@ def test_api_pair_candles(botclient, ohlcv_history): def test_api_pair_history(botclient, ohlcv_history): - freqtrade, client = botclient + ftbot, client = botclient timeframe = '5m' # No pair @@ -1181,23 +1181,23 @@ def test_api_pair_history(botclient, ohlcv_history): def test_api_plot_config(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) assert rc.json() == {} - freqtrade.strategy.plot_config = { + ftbot.strategy.plot_config = { 'main_plot': {'sma': {}}, 'subplots': {'RSI': {'rsi': {'color': 'red'}}} } rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) - assert rc.json() == freqtrade.strategy.plot_config + assert rc.json() == ftbot.strategy.plot_config assert isinstance(rc.json()['main_plot'], dict) assert isinstance(rc.json()['subplots'], dict) - freqtrade.strategy.plot_config = {'main_plot': {'sma': {}}} + ftbot.strategy.plot_config = {'main_plot': {'sma': {}}} rc = client_get(client, f"{BASE_URI}/plot_config") assert_response(rc) @@ -1206,7 +1206,7 @@ def test_api_plot_config(botclient): def test_api_strategies(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/strategies") @@ -1219,7 +1219,7 @@ def test_api_strategies(botclient): def test_api_strategy(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/strategy/DefaultStrategy") @@ -1234,7 +1234,7 @@ def test_api_strategy(botclient): def test_list_available_pairs(botclient): - freqtrade, client = botclient + ftbot, client = botclient rc = client_get(client, f"{BASE_URI}/available_pairs") From ba0fa1120a6537d1d08af191e19b243556064f29 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:39:18 +0700 Subject: [PATCH 175/519] revert rename naming --- tests/rpc/test_rpc_telegram.py | 26 +++++++++++++------------- tests/test_freqtradebot.py | 34 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index ab66d18e8..5aeebb2d7 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -63,12 +63,12 @@ def get_telegram_testobject(mocker, default_conf, mock=True, freqtrade=None): _init=MagicMock(), _send_msg=msg_mock ) - if not freqtrade: - freqtrade = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(freqtrade) + if not ftbot: + ftbot = get_patched_freqtradebot(mocker, default_conf) + rpc = RPC(ftbot) telegram = Telegram(rpc, default_conf) - return telegram, freqtrade, msg_mock + return telegram, ftbot, msg_mock def test_telegram__init__(default_conf, mocker) -> None: @@ -115,11 +115,11 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None: patch_exchange(mocker) caplog.set_level(logging.DEBUG) default_conf['telegram']['enabled'] = False - freqtrade = FreqtradeBot(default_conf) - rpc = RPC(freqtrade) + bot = FreqtradeBot(default_conf) + rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(bot, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) @@ -135,11 +135,11 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: update.message = Message(randint(1, 100), datetime.utcnow(), chat) default_conf['telegram']['enabled'] = False - freqtrade = FreqtradeBot(default_conf) - rpc = RPC(freqtrade) + bot = FreqtradeBot(default_conf) + rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(bot, (True, False, None)) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) @@ -152,10 +152,10 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None default_conf['telegram']['enabled'] = False - freqtrade = FreqtradeBot(default_conf) - rpc = RPC(freqtrade) + bot = FreqtradeBot(default_conf) + rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(freqtrade, (True, False, None)) + patch_get_signal(bot, (True, False, None)) dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index abbef7858..b3a5bc409 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2014,16 +2014,16 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open def test_bot_loop_start_called_once(mocker, default_conf, caplog): - freqtrade = get_patched_freqtradebot(mocker, default_conf) + ftbot = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade') - patch_get_signal(freqtrade) - freqtrade.strategy.bot_loop_start = MagicMock(side_effect=ValueError) - freqtrade.strategy.analyze = MagicMock() + patch_get_signal(ftbot) + ftbot.strategy.bot_loop_start = MagicMock(side_effect=ValueError) + ftbot.strategy.analyze = MagicMock() - freqtrade.process() + ftbot.process() assert log_has_re(r'Strategy caused the following exception.*', caplog) - assert freqtrade.strategy.bot_loop_start.call_count == 1 - assert freqtrade.strategy.analyze.call_count == 1 + assert ftbot.strategy.bot_loop_start.call_count == 1 + assert ftbot.strategy.analyze.call_count == 1 def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -4086,14 +4086,14 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker): reinit_mock = MagicMock() mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - freqtrade.startup() + ftbot = get_patched_freqtradebot(mocker, default_conf) + ftbot.startup() assert reinit_mock.call_count == 1 reinit_mock.reset_mock() - freqtrade = get_patched_freqtradebot(mocker, edge_conf) - freqtrade.startup() + ftbot = get_patched_freqtradebot(mocker, edge_conf) + ftbot.startup() assert reinit_mock.call_count == 0 @@ -4112,17 +4112,17 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ get_fee=fee, ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtrade) - assert freqtrade.wallets.get_free('BTC') == 0.002 + bot = get_patched_freqtradebot(mocker, default_conf) + patch_get_signal(bot) + assert bot.wallets.get_free('BTC') == 0.002 - n = freqtrade.enter_positions() + n = bot.enter_positions() assert n == 2 trades = Trade.query.all() assert len(trades) == 2 - freqtrade.config['max_open_trades'] = 3 - n = freqtrade.enter_positions() + bot.config['max_open_trades'] = 3 + n = bot.enter_positions() assert n == 0 assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", From b9c2489b73629babd1dcc918fb4ec8c826d686dd Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 18:41:29 +0700 Subject: [PATCH 176/519] remove SN --- tests/edge/test_edge.py | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py index 254134ce7..7bdc940df 100644 --- a/tests/edge/test_edge.py +++ b/tests/edge/test_edge.py @@ -76,23 +76,23 @@ def _time_on_candle(number): # End helper functions # Open trade should be removed from the end tc0 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1, '']], # enter trade (signal on last candle) + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1]], # enter trade (signal on last candle) stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[] ) # Two complete trades within dataframe(with sell hit for all) tc1 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # enter trade (signal on last candle) - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # exit at open - [3, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], # no action - [4, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should enter the trade - [5, 5000, 5025, 4975, 4987, 6172, 0, 1, ''], # no action - [6, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], # should sell + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4975, 4987, 6172, 0, 1], # enter trade (signal on last candle) + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], # exit at open + [3, 5000, 5025, 4975, 4987, 6172, 1, 0], # no action + [4, 5000, 5025, 4975, 4987, 6172, 0, 0], # should enter the trade + [5, 5000, 5025, 4975, 4987, 6172, 0, 1], # no action + [6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell ], stop_loss=-0.99, roi={"0": float('inf')}, profit_perc=0.00, trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=2), @@ -101,10 +101,10 @@ tc1 = BTContainer(data=[ # 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss tc2 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4600, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4600, 4987, 6172, 0, 0], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], ], stop_loss=-0.01, roi={"0": float('inf')}, profit_perc=-0.01, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -112,10 +112,10 @@ tc2 = BTContainer(data=[ # 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss tc3 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4800, 4987, 6172, 0, 0, ''], # enter trade, stoploss hit - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4800, 4987, 6172, 0, 0], # enter trade, stoploss hit + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] @@ -123,10 +123,10 @@ tc3 = BTContainer(data=[ # 5) Stoploss and sell are hit. should sell on stoploss tc4 = BTContainer(data=[ - # D O H L C V B S SN - [0, 5000, 5025, 4975, 4987, 6172, 1, 0, ''], - [1, 5000, 5025, 4800, 4987, 6172, 0, 1, ''], # enter trade, stoploss hit, sell signal - [2, 5000, 5025, 4975, 4987, 6172, 0, 0, ''], + # D O H L C V B S + [0, 5000, 5025, 4975, 4987, 6172, 1, 0], + [1, 5000, 5025, 4800, 4987, 6172, 0, 1], # enter trade, stoploss hit, sell signal + [2, 5000, 5025, 4975, 4987, 6172, 0, 0], ], stop_loss=-0.03, roi={"0": float('inf')}, profit_perc=-0.03, trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)] From 7a0cb95ffb00c1c14a8b6a62b3134017ed919214 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 20:43:27 +0700 Subject: [PATCH 177/519] fix testcase --- tests/rpc/test_rpc_telegram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 5aeebb2d7..b678c3363 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -55,7 +55,7 @@ class DummyCls(Telegram): raise Exception('test') -def get_telegram_testobject(mocker, default_conf, mock=True, freqtrade=None): +def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): msg_mock = MagicMock() if mock: mocker.patch.multiple( From 0fcbe097c064df7943e21bcf7e7d3c0f6f84daf0 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 21:06:38 +0700 Subject: [PATCH 178/519] remove blankspace --- tests/optimize/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index 80fce9ca5..f29d8d585 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -44,7 +44,6 @@ def _get_frame_time_from_offset(offset): def _build_backtest_dataframe(data): - columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell'] columns = columns + ['buy_tag'] if len(data[0]) == 9 else columns From b7ba2f115e46dcb7455f33a5154fef19053aae22 Mon Sep 17 00:00:00 2001 From: kevinjulian Date: Fri, 23 Jul 2021 21:20:32 +0700 Subject: [PATCH 179/519] retrigger checks From 06e7f379b39087134ed8c756eb28a700668b23a0 Mon Sep 17 00:00:00 2001 From: raphael Date: Fri, 23 Jul 2021 16:32:30 -0400 Subject: [PATCH 180/519] Fix code to get Bittrex US-restricted markets Old code was no longer working --- docs/exchanges.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index e54f97714..72f5e4ba4 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -77,8 +77,8 @@ You can get a list of restricted markets by using the following snippet: ``` python import ccxt ct = ccxt.bittrex() -_ = ct.load_markets() -res = [ f"{x['MarketCurrency']}/{x['BaseCurrency']}" for x in ct.publicGetMarkets()['result'] if x['IsRestricted']] +ct.load_markets() +res = [f"{x['quoteCurrencySymbol']}/{x['baseCurrencySymbol']}" for x in ct.publicGetMarkets() if 'US' in x['prohibitedIn']] print(res) ``` From b42afb9daebc5bde5118cefeb6a4a6ea1ab57d51 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 24 Jul 2021 17:07:06 -0600 Subject: [PATCH 181/519] get_rate checks if side is buy for some console output --- freqtrade/exchange/exchange.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 91b278077..bd3282604 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1038,10 +1038,13 @@ class Exchange: ) raise PricingError from e - logger.info(f"{name} price from orderbook {conf_strategy['price_side'].capitalize()}" - f"side - top {order_book_top} order book {side} rate {rate:.8f}") + if side == "buy": + price_side = {conf_strategy['price_side'].capitalize()} + logger.info(f"{name} price from orderbook {price_side}" + f"side - top {order_book_top} order book {side} rate {rate:.8f}") else: - logger.info(f"Using Last {conf_strategy['price_side'].capitalize()} / Last Price") + if side == "buy": + logger.info(f"Using Last {conf_strategy['price_side'].capitalize()} / Last Price") ticker = self.fetch_ticker(pair) ticker_rate = ticker[conf_strategy['price_side']] if ticker['last']: From 34c8a5afaff2c06fc0a09df0d4eba6ad2464bcc6 Mon Sep 17 00:00:00 2001 From: sauces1313 Date: Sun, 25 Jul 2021 07:24:55 +0000 Subject: [PATCH 182/519] remove second filter, add max option --- docs/includes/pairlists.md | 24 +--- freqtrade/constants.py | 2 +- .../plugins/pairlist/rangestabilityfilter.py | 12 +- .../pairlist/rangestabilityfiltermax.py | 109 ------------------ tests/plugins/test_pairlist.py | 18 +-- 5 files changed, 26 insertions(+), 139 deletions(-) delete mode 100644 freqtrade/plugins/pairlist/rangestabilityfiltermax.py diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 0368e8a6f..e9d2f45e7 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -155,10 +155,10 @@ If `DOGE/BTC` maximum bid is 0.00000026 and minimum ask is 0.00000027, the ratio #### RangeStabilityFilter -Removes pairs where the difference between lowest low and highest high over `lookback_days` days is below `min_rate_of_change`. Since this is a filter that requires additional data, the results are cached for `refresh_period`. +Removes pairs where the difference between lowest low and highest high over `lookback_days` days is below `min_rate_of_change` or above `max_rate_of_change`. Since this is a filter that requires additional data, the results are cached for `refresh_period`. In the below example: -If the trading range over the last 10 days is <1%, remove the pair from the whitelist. +If the trading range over the last 10 days is <1% or >99%, remove the pair from the whitelist. ```json "pairlists": [ @@ -166,6 +166,7 @@ If the trading range over the last 10 days is <1%, remove the pair from the whit "method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01, + "max_rate_of_change": 0.99, "refresh_period": 1440 } ] @@ -173,24 +174,7 @@ If the trading range over the last 10 days is <1%, remove the pair from the whit !!! Tip This Filter can be used to automatically remove stable coin pairs, which have a very low trading range, and are therefore extremely difficult to trade with profit. - -#### RangeStabilityFilterMax - -Same function as `RangeStabilityFilter` but instead of a minimum value, it uses a maximum value for rate of change, i.e. `max_rate_of_change` as seen in the example below. - -```json -"pairlists": [ - { - "method": "RangeStabilityFilterMax", - "lookback_days": 10, - "max_rate_of_change": 1.01, - "refresh_period": 1440 - } -] -``` - -!!! Tip - This Filter can be used to automatically remove pairs with extreme high/low variance over a given amount of time (`lookback_days`). + Additionally, it can also be used to automatically remove pairs with extreme high/low variance over a given amount of time. #### VolatilityFilter diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 089569842..f4c32387b 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -28,7 +28,7 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'AgeFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', 'ShuffleFilter', - 'SpreadFilter', 'VolatilityFilter', 'RangeStabilityFilterMax'] + 'SpreadFilter', 'VolatilityFilter'] AVAILABLE_PROTECTIONS = ['CooldownPeriod', 'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5'] DRY_RUN_WALLET = 1000 diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index a6d1820de..105568f17 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -26,6 +26,7 @@ class RangeStabilityFilter(IPairList): self._days = pairlistconfig.get('lookback_days', 10) self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01) + self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', 0.99) self._refresh_period = pairlistconfig.get('refresh_period', 1440) self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) @@ -51,7 +52,8 @@ class RangeStabilityFilter(IPairList): Short whitelist method description - used for startup-messages """ return (f"{self.name} - Filtering pairs with rate of change below " - f"{self._min_rate_of_change} over the last {plural(self._days, 'day')}.") + f"{self._min_rate_of_change} and above " + f"{self._max_rate_of_change} over the last {plural(self._days, 'day')}.") def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ @@ -104,6 +106,14 @@ class RangeStabilityFilter(IPairList): f"which is below the threshold of {self._min_rate_of_change}.", logger.info) result = False + if pct_change <= self._max_rate_of_change: + result = True + else: + self.log_once(f"Removed {pair} from whitelist, because rate of change " + f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, " + f"which is above the threshold of {self._max_rate_of_change}.", + logger.info) + result = False self._pair_cache[pair] = result return result diff --git a/freqtrade/plugins/pairlist/rangestabilityfiltermax.py b/freqtrade/plugins/pairlist/rangestabilityfiltermax.py deleted file mode 100644 index e0cf5b9b4..000000000 --- a/freqtrade/plugins/pairlist/rangestabilityfiltermax.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -Rate of change pairlist filter -""" -import logging -from copy import deepcopy -from typing import Any, Dict, List, Optional - -import arrow -from cachetools.ttl import TTLCache -from pandas import DataFrame - -from freqtrade.exceptions import OperationalException -from freqtrade.misc import plural -from freqtrade.plugins.pairlist.IPairList import IPairList - - -logger = logging.getLogger(__name__) - - -class RangeStabilityFilterMax(IPairList): - - def __init__(self, exchange, pairlistmanager, - config: Dict[str, Any], pairlistconfig: Dict[str, Any], - pairlist_pos: int) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) - - self._days = pairlistconfig.get('lookback_days', 10) - self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', 0.02) - self._refresh_period = pairlistconfig.get('refresh_period', 1440) - - self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) - - if self._days < 1: - raise OperationalException("RangeStabilityFilterMax requires lookback_days to be >= 1") - if self._days > exchange.ohlcv_candle_limit('1d'): - raise OperationalException("RangeStabilityFilterMax requires lookback_days to not " - "exceed exchange max request size " - f"({exchange.ohlcv_candle_limit('1d')})") - - @property - def needstickers(self) -> bool: - """ - Boolean property defining if tickers are necessary. - If no Pairlist requires tickers, an empty List is passed - as tickers argument to filter_pairlist - """ - return False - - def short_desc(self) -> str: - """ - Short whitelist method description - used for startup-messages - """ - return (f"{self.name} - Filtering pairs with rate of change below " - f"{self._max_rate_of_change} over the last {plural(self._days, 'day')}.") - - def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: - """ - Validate trading range - :param pairlist: pairlist to filter or sort - :param tickers: Tickers (from exchange.get_tickers()). May be cached. - :return: new allowlist - """ - needed_pairs = [(p, '1d') for p in pairlist if p not in self._pair_cache] - - since_ms = int(arrow.utcnow() - .floor('day') - .shift(days=-self._days - 1) - .float_timestamp) * 1000 - # Get all candles - candles = {} - if needed_pairs: - candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms, - cache=False) - - if self._enabled: - for p in deepcopy(pairlist): - daily_candles = candles[(p, '1d')] if (p, '1d') in candles else None - if not self._validate_pair_loc(p, daily_candles): - pairlist.remove(p) - return pairlist - - def _validate_pair_loc(self, pair: str, daily_candles: Optional[DataFrame]) -> bool: - """ - Validate trading range - :param pair: Pair that's currently validated - :param ticker: ticker dict as returned from ccxt.load_markets() - :return: True if the pair can stay, false if it should be removed - """ - # Check symbol in cache - cached_res = self._pair_cache.get(pair, None) - if cached_res is not None: - return cached_res - - result = False - if daily_candles is not None and not daily_candles.empty: - highest_high = daily_candles['high'].max() - lowest_low = daily_candles['low'].min() - pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0 - if pct_change <= self._max_rate_of_change: - result = True - else: - self.log_once(f"Removed {pair} from whitelist, because rate of change " - f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, " - f"which is above the threshold of {self._max_rate_of_change}.", - logger.info) - result = False - self._pair_cache[pair] = result - - return result diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index ae8f6e958..550587da9 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -412,7 +412,7 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): "USDT", ['NANO/USDT']), ([{"method": "StaticPairList"}, {"method": "RangeStabilityFilter", "lookback_days": 10, - "min_rate_of_change": 0.01, "refresh_period": 1440}], + "min_rate_of_change": 0.01, "max_rate_of_change": 0.99, "refresh_period": 1440}], "BTC", ['ETH/BTC', 'TKN/BTC', 'HOT/BTC']), ([{"method": "StaticPairList"}, {"method": "VolatilityFilter", "lookback_days": 3, @@ -718,15 +718,16 @@ def test_rangestabilityfilter_checks(mocker, default_conf, markets, tickers): get_patched_freqtradebot(mocker, default_conf) -@pytest.mark.parametrize('min_rate_of_change,expected_length', [ - (0.01, 5), - (0.05, 0), # Setting rate_of_change to 5% removes all pairs from the whitelist. +@pytest.mark.parametrize('min_rate_of_change,max_rate_of_change,expected_length', [ + (0.01, 0.99, 5), + (0.05, 0.0, 0), # Setting min rate_of_change to 5% removes all pairs from the whitelist. ]) def test_rangestabilityfilter_caching(mocker, markets, default_conf, tickers, ohlcv_history, - min_rate_of_change, expected_length): + min_rate_of_change, max_rate_of_change, expected_length): default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}, {'method': 'RangeStabilityFilter', 'lookback_days': 2, - 'min_rate_of_change': min_rate_of_change}] + 'min_rate_of_change': min_rate_of_change, + "max_rate_of_change": max_rate_of_change}] mocker.patch.multiple('freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -828,9 +829,10 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo None, "PriceFilter requires max_value to be >= 0" ), # OperationalException expected - ({"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01}, + ({"method": "RangeStabilityFilter", "lookback_days": 10, + "min_rate_of_change": 0.01, "max_rate_of_change": 0.99}, "[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below " - "0.01 over the last days.'}]", + "0.01 and above 0.99 over the last days.'}]", None ), ]) From 1e32a3ca0927bc6f19c092b0f86e801ef2f9c4f0 Mon Sep 17 00:00:00 2001 From: Rik Helsen Date: Sun, 25 Jul 2021 15:54:49 +0200 Subject: [PATCH 183/519] :memo: Docs - StrategyAdvanced - Added `current_time` to `confirm_trade_entry/exit()` examples --- docs/strategy-advanced.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 4dba9ad4a..cd0f60ce6 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -454,7 +454,7 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, - time_in_force: str, **kwargs) -> bool: + time_in_force: str, current_time: datetime, **kwargs) -> bool: """ Called right before placing a buy order. Timing for this function is critical, so avoid doing heavy computations or @@ -469,6 +469,7 @@ class AwesomeStrategy(IStrategy): :param amount: Amount in target (quote) currency that's going to be traded. :param rate: Rate that's going to be used when using limit orders :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). + :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the buy-order is placed on the exchange. False aborts the process @@ -490,7 +491,8 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, - rate: float, time_in_force: str, sell_reason: str, **kwargs) -> bool: + rate: float, time_in_force: str, sell_reason: str, + current_time: datetime, **kwargs) -> bool: """ Called right before placing a regular sell order. Timing for this function is critical, so avoid doing heavy computations or @@ -508,6 +510,7 @@ class AwesomeStrategy(IStrategy): :param sell_reason: Sell reason. Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', 'sell_signal', 'force_sell', 'emergency_sell'] + :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the sell-order is placed on the exchange. False aborts the process From 05f74bdf5382dd362ac0cd6584ec17937ef9ba06 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 25 Jul 2021 16:13:04 -0600 Subject: [PATCH 184/519] Changed log ouput to debug in exchange.get_rate --- freqtrade/exchange/exchange.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index bd3282604..ed2ab0a61 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1037,14 +1037,11 @@ class Exchange: f"determined. Orderbook: {order_book}" ) raise PricingError from e - - if side == "buy": - price_side = {conf_strategy['price_side'].capitalize()} - logger.info(f"{name} price from orderbook {price_side}" - f"side - top {order_book_top} order book {side} rate {rate:.8f}") + price_side = {conf_strategy['price_side'].capitalize()} + logger.debug(f"{name} price from orderbook {price_side}" + f"side - top {order_book_top} order book {side} rate {rate:.8f}") else: - if side == "buy": - logger.info(f"Using Last {conf_strategy['price_side'].capitalize()} / Last Price") + logger.debug(f"Using Last {conf_strategy['price_side'].capitalize()} / Last Price") ticker = self.fetch_ticker(pair) ticker_rate = ticker[conf_strategy['price_side']] if ticker['last']: From 11937fd1bfe0f0d182e92e0fc5b9ee3f69576c69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 03:00:57 +0000 Subject: [PATCH 185/519] Bump mkdocs-material from 7.1.11 to 7.2.1 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.1.11 to 7.2.1. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.1.11...7.2.1) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 32a1df58e..6a904c951 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.1.11 +mkdocs-material==7.2.1 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From 7d428f9cb98bc3b6fecbd650c4a4e272c6a15f1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 03:01:05 +0000 Subject: [PATCH 186/519] Bump fastapi from 0.66.0 to 0.67.0 Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.66.0 to 0.67.0. - [Release notes](https://github.com/tiangolo/fastapi/releases) - [Commits](https://github.com/tiangolo/fastapi/compare/0.66.0...0.67.0) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8e26dfc6c..b3d9225c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ python-rapidjson==1.4 sdnotify==0.3.2 # API Server -fastapi==0.66.0 +fastapi==0.67.0 uvicorn==0.14.0 pyjwt==2.1.0 aiofiles==0.7.0 From 0f82174c525c9b18f921971cd3bc4fe42dcbd5f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 03:01:16 +0000 Subject: [PATCH 187/519] Bump sqlalchemy from 1.4.21 to 1.4.22 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.21 to 1.4.22. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/master/CHANGES) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8e26dfc6c..55200def3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ ccxt==1.53.25 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 -SQLAlchemy==1.4.21 +SQLAlchemy==1.4.22 python-telegram-bot==13.7 arrow==1.1.1 cachetools==4.2.2 From 6a3838ea4bce3364253aded4493b6ec69fca0baf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 03:01:20 +0000 Subject: [PATCH 188/519] Bump coveralls from 3.1.0 to 3.2.0 Bumps [coveralls](https://github.com/TheKevJames/coveralls-python) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/TheKevJames/coveralls-python/releases) - [Changelog](https://github.com/TheKevJames/coveralls-python/blob/master/CHANGELOG.md) - [Commits](https://github.com/TheKevJames/coveralls-python/compare/3.1.0...3.2.0) --- updated-dependencies: - dependency-name: coveralls dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e25f810cd..537936a86 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,7 +3,7 @@ -r requirements-plot.txt -r requirements-hyperopt.txt -coveralls==3.1.0 +coveralls==3.2.0 flake8==3.9.2 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.3.0 From 0fac9c9cf277f16af3c175fc9414b811a5765077 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 03:01:25 +0000 Subject: [PATCH 189/519] Bump pandas from 1.3.0 to 1.3.1 Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.3.0 to 1.3.1. - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Changelog](https://github.com/pandas-dev/pandas/blob/master/RELEASE.md) - [Commits](https://github.com/pandas-dev/pandas/compare/v1.3.0...v1.3.1) --- updated-dependencies: - dependency-name: pandas dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8e26dfc6c..291aa666a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ numpy==1.21.1 -pandas==1.3.0 +pandas==1.3.1 ccxt==1.53.25 # Pin cryptography for now due to rust build errors with piwheels From a451a9727462c530257c85a5a0e082e2fa179094 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jul 2021 05:39:35 +0000 Subject: [PATCH 190/519] Bump ccxt from 1.53.25 to 1.53.72 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.53.25 to 1.53.72. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.53.25...1.53.72) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4cfe5f42c..d7545946c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.1 pandas==1.3.1 -ccxt==1.53.25 +ccxt==1.53.72 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 25c527ee678f5aa0b38bc59520d50829128716af Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 24 Jul 2021 19:30:34 -0600 Subject: [PATCH 191/519] combined exchange.buy and exchange.sell, Adding dummy mock to create_order in tests in test_freqtradebot --- freqtrade/exchange/exchange.py | 37 ++---- freqtrade/freqtradebot.py | 16 +-- tests/exchange/test_exchange.py | 68 ++++++----- tests/exchange/test_kraken.py | 7 +- tests/rpc/test_rpc.py | 2 +- tests/test_freqtradebot.py | 209 +++++++++++++++++++++----------- tests/test_wallets.py | 4 +- 7 files changed, 200 insertions(+), 143 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index ed2ab0a61..54b89e451 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -689,7 +689,16 @@ class Exchange: # Order handling def create_order(self, pair: str, ordertype: str, side: str, amount: float, - rate: float, params: Dict = {}) -> Dict: + rate: float, time_in_force: str = 'gtc') -> Dict: + + if self._config['dry_run']: + dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate) + return dry_order + + params = self._params.copy() + if time_in_force != 'gtc' and ordertype != 'market': + params.update({'timeInForce': time_in_force}) + try: # Set the precision for amount and price(rate) as accepted by the exchange amount = self.amount_to_precision(pair, amount) @@ -720,32 +729,6 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def buy(self, pair: str, ordertype: str, amount: float, - rate: float, time_in_force: str) -> Dict: - - if self._config['dry_run']: - dry_order = self.create_dry_run_order(pair, ordertype, "buy", amount, rate) - return dry_order - - params = self._params.copy() - if time_in_force != 'gtc' and ordertype != 'market': - params.update({'timeInForce': time_in_force}) - - return self.create_order(pair, ordertype, 'buy', amount, rate, params) - - def sell(self, pair: str, ordertype: str, amount: float, - rate: float, time_in_force: str = 'gtc') -> Dict: - - if self._config['dry_run']: - dry_order = self.create_dry_run_order(pair, ordertype, "sell", amount, rate) - return dry_order - - params = self._params.copy() - if time_in_force != 'gtc' and ordertype != 'market': - params.update({'timeInForce': time_in_force}) - - return self.create_order(pair, ordertype, 'sell', amount, rate, params) - def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool: """ Verify stop_loss against stoploss-order value (limit or price) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d430dbc48..25b8d154b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -510,9 +510,9 @@ class FreqtradeBot(LoggingMixin): logger.info(f"User requested abortion of buying {pair}") return False amount = self.exchange.amount_to_precision(pair, amount) - order = self.exchange.buy(pair=pair, ordertype=order_type, - amount=amount, rate=buy_limit_requested, - time_in_force=time_in_force) + order = self.exchange.create_order(pair=pair, ordertype=order_type, side="buy", + amount=amount, rate=buy_limit_requested, + time_in_force=time_in_force) order_obj = Order.parse_from_ccxt_object(order, pair, 'buy') order_id = order['id'] order_status = order.get('status', None) @@ -1094,11 +1094,11 @@ class FreqtradeBot(LoggingMixin): try: # Execute sell and update trade record - order = self.exchange.sell(pair=trade.pair, - ordertype=order_type, - amount=amount, rate=limit, - time_in_force=time_in_force - ) + order = self.exchange.create_order(pair=trade.pair, + ordertype=order_type, side="sell", + amount=amount, rate=limit, + time_in_force=time_in_force + ) except InsufficientFundsError as e: logger.warning(f"Unable to place order {e}.") # Try to figure out what went wrong diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 02adf01c4..bacb7edbb 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1056,8 +1056,8 @@ def test_buy_dry_run(default_conf, mocker): default_conf['dry_run'] = True exchange = get_patched_exchange(mocker, default_conf) - order = exchange.buy(pair='ETH/BTC', ordertype='limit', - amount=1, rate=200, time_in_force='gtc') + order = exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy", + amount=1, rate=200, time_in_force='gtc') assert 'id' in order assert 'dry_run_buy_' in order['id'] @@ -1080,8 +1080,8 @@ def test_buy_prod(default_conf, mocker, exchange_name): mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - order = exchange.buy(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", + amount=1, rate=200, time_in_force=time_in_force) assert 'id' in order assert 'info' in order @@ -1094,9 +1094,10 @@ def test_buy_prod(default_conf, mocker, exchange_name): api_mock.create_order.reset_mock() order_type = 'limit' - order = exchange.buy( + order = exchange.create_order( pair='ETH/BTC', ordertype=order_type, + side="buy", amount=1, rate=200, time_in_force=time_in_force) @@ -1110,32 +1111,32 @@ def test_buy_prod(default_conf, mocker, exchange_name): with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("Not enough funds")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.buy(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", + amount=1, rate=200, time_in_force=time_in_force) with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.buy(pair='ETH/BTC', ordertype='limit', - amount=1, rate=200, time_in_force=time_in_force) + exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy", + amount=1, rate=200, time_in_force=time_in_force) with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.buy(pair='ETH/BTC', ordertype='market', - amount=1, rate=200, time_in_force=time_in_force) + exchange.create_order(pair='ETH/BTC', ordertype='market', side="buy", + amount=1, rate=200, time_in_force=time_in_force) with pytest.raises(TemporaryError): api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("Network disconnect")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.buy(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", + amount=1, rate=200, time_in_force=time_in_force) with pytest.raises(OperationalException): api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("Unknown error")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.buy(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", + amount=1, rate=200, time_in_force=time_in_force) @pytest.mark.parametrize("exchange_name", EXCHANGES) @@ -1157,8 +1158,8 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name): order_type = 'limit' time_in_force = 'ioc' - order = exchange.buy(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", + amount=1, rate=200, time_in_force=time_in_force) assert 'id' in order assert 'info' in order @@ -1174,8 +1175,8 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name): order_type = 'market' time_in_force = 'ioc' - order = exchange.buy(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", + amount=1, rate=200, time_in_force=time_in_force) assert 'id' in order assert 'info' in order @@ -1193,7 +1194,8 @@ def test_sell_dry_run(default_conf, mocker): default_conf['dry_run'] = True exchange = get_patched_exchange(mocker, default_conf) - order = exchange.sell(pair='ETH/BTC', ordertype='limit', amount=1, rate=200) + order = exchange.create_order(pair='ETH/BTC', ordertype='limit', + side="sell", amount=1, rate=200) assert 'id' in order assert 'dry_run_sell_' in order['id'] @@ -1216,7 +1218,8 @@ def test_sell_prod(default_conf, mocker, exchange_name): mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, + side="sell", amount=1, rate=200) assert 'id' in order assert 'info' in order @@ -1229,7 +1232,8 @@ def test_sell_prod(default_conf, mocker, exchange_name): api_mock.create_order.reset_mock() order_type = 'limit' - order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, + side="sell", amount=1, rate=200) assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' assert api_mock.create_order.call_args[0][1] == order_type assert api_mock.create_order.call_args[0][2] == 'sell' @@ -1240,28 +1244,28 @@ def test_sell_prod(default_conf, mocker, exchange_name): with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200) + exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200) with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.sell(pair='ETH/BTC', ordertype='limit', amount=1, rate=200) + exchange.create_order(pair='ETH/BTC', ordertype='limit', side="sell", amount=1, rate=200) # Market orders don't require price, so the behaviour is slightly different with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.sell(pair='ETH/BTC', ordertype='market', amount=1, rate=200) + exchange.create_order(pair='ETH/BTC', ordertype='market', side="sell", amount=1, rate=200) with pytest.raises(TemporaryError): api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No Connection")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200) + exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200) with pytest.raises(OperationalException): api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200) + exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200) @pytest.mark.parametrize("exchange_name", EXCHANGES) @@ -1283,8 +1287,8 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name): order_type = 'limit' time_in_force = 'ioc' - order = exchange.sell(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", + amount=1, rate=200, time_in_force=time_in_force) assert 'id' in order assert 'info' in order @@ -1299,8 +1303,8 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name): order_type = 'market' time_in_force = 'ioc' - order = exchange.sell(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", + amount=1, rate=200, time_in_force=time_in_force) assert 'id' in order assert 'info' in order @@ -2186,7 +2190,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name): assert exchange.cancel_order(order_id='123', pair='TKN/BTC') == {} assert exchange.cancel_stoploss_order(order_id='123', pair='TKN/BTC') == {} - order = exchange.buy('ETH/BTC', 'limit', 5, 0.55, 'gtc') + order = exchange.create_order('ETH/BTC', 'limit', "sell", 5, 0.55, 'gtc') cancel_order = exchange.cancel_order(order_id=order['id'], pair='ETH/BTC') assert order['id'] == cancel_order['id'] diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index ed22cde92..eb79dfc10 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -31,8 +31,8 @@ def test_buy_kraken_trading_agreement(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken") - order = exchange.buy(pair='ETH/BTC', ordertype=order_type, - amount=1, rate=200, time_in_force=time_in_force) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", + amount=1, rate=200, time_in_force=time_in_force) assert 'id' in order assert 'info' in order @@ -63,7 +63,8 @@ def test_sell_kraken_trading_agreement(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken") - order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200) + order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, + side="sell", amount=1, rate=200) assert 'id' in order assert 'info' in order diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index fad24f9e2..05a4316f8 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -857,7 +857,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> get_balances=MagicMock(return_value=ticker), fetch_ticker=ticker, get_fee=fee, - buy=buy_mm + create_order=buy_mm ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 4912a2a4d..31e3a4ff7 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -169,7 +169,7 @@ def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_b mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee ) default_conf['dry_run_wallet'] = wallet @@ -384,7 +384,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order_open, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=buy_mock, + create_order=buy_mock, get_fee=fee, ) default_conf['stake_amount'] = 0.0005 @@ -404,7 +404,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=buy_mock, + create_order=buy_mock, get_fee=fee, ) @@ -425,7 +425,7 @@ def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_op mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=buy_mock, + create_order=buy_mock, get_fee=fee, ) @@ -444,7 +444,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order_open, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) default_conf['max_open_trades'] = 0 @@ -464,7 +464,7 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) @@ -487,7 +487,7 @@ def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_o mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), + create_order=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, ) default_conf['exchange']['pair_whitelist'] = [] @@ -507,7 +507,7 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), + create_order=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -556,7 +556,7 @@ def test_create_trades_multiple_trades(default_conf, ticker, fee, mocker, limit_ mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -577,7 +577,7 @@ def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_orde mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -606,7 +606,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, limit_buy mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), fetch_order=MagicMock(return_value=limit_buy_order), get_fee=fee, ) @@ -641,7 +641,7 @@ def test_process_exchange_failures(default_conf, ticker, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(side_effect=TemporaryError) + create_order=MagicMock(side_effect=TemporaryError) ) sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) @@ -658,7 +658,7 @@ def test_process_operational_exception(default_conf, ticker, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(side_effect=OperationalException) + create_order=MagicMock(side_effect=OperationalException) ) worker = Worker(args=None, config=default_conf) patch_get_signal(worker.freqtrade) @@ -676,7 +676,7 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order_open, fee, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), fetch_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) @@ -703,7 +703,7 @@ def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), + create_order=MagicMock(return_value={'id': limit_buy_order['id']}), fetch_order=MagicMock(return_value=limit_buy_order), get_fee=fee, ) @@ -753,7 +753,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(side_effect=TemporaryError), + create_order=MagicMock(side_effect=TemporaryError), refresh_latest_ohlcv=refresh_mock, ) inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")]) @@ -790,7 +790,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order 'ask': 0.00001173, 'last': 0.00001172 }), - buy=buy_mm, + create_order=buy_mm, get_min_pair_stake_amount=MagicMock(return_value=1), get_fee=fee, ) @@ -839,7 +839,8 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order['cost'] = 100 limit_buy_order['id'] = '444' - mocker.patch('freqtrade.exchange.Exchange.buy', MagicMock(return_value=limit_buy_order)) + mocker.patch('freqtrade.exchange.Exchange.create_order', + MagicMock(return_value=limit_buy_order)) assert freqtrade.execute_buy(pair, stake_amount) trade = Trade.query.all()[2] assert trade @@ -855,7 +856,8 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order['price'] = 0.5 limit_buy_order['cost'] = 40.495905365 limit_buy_order['id'] = '555' - mocker.patch('freqtrade.exchange.Exchange.buy', MagicMock(return_value=limit_buy_order)) + mocker.patch('freqtrade.exchange.Exchange.create_order', + MagicMock(return_value=limit_buy_order)) assert freqtrade.execute_buy(pair, stake_amount) trade = Trade.query.all()[3] assert trade @@ -889,7 +891,8 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order['price'] = 0.5 limit_buy_order['cost'] = 0.0 limit_buy_order['id'] = '66' - mocker.patch('freqtrade.exchange.Exchange.buy', MagicMock(return_value=limit_buy_order)) + mocker.patch('freqtrade.exchange.Exchange.create_order', + MagicMock(return_value=limit_buy_order)) assert not freqtrade.execute_buy(pair, stake_amount) # Fail to get price... @@ -908,7 +911,7 @@ def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) - 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value=limit_buy_order), + create_order=MagicMock(return_value=limit_buy_order), get_rate=MagicMock(return_value=0.11), get_min_pair_stake_amount=MagicMock(return_value=1), get_fee=fee, @@ -970,8 +973,10 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - sell=MagicMock(return_value={'id': limit_sell_order['id']}), + create_order=MagicMock(side_effect=[ + {'id': limit_buy_order['id']}, + {'id': limit_sell_order['id']}, + ]), get_fee=fee, ) mocker.patch.multiple( @@ -1087,8 +1092,10 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - sell=MagicMock(return_value={'id': limit_sell_order['id']}), + create_order=MagicMock(side_effect=[ + {'id': limit_buy_order['id']}, + {'id': limit_sell_order['id']}, + ]), get_fee=fee, ) mocker.patch.multiple( @@ -1116,7 +1123,10 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, limit_buy_order_open, limit_sell_order): rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) - sell_mock = MagicMock(return_value={'id': limit_sell_order['id']}) + create_order_mock = MagicMock(side_effect=[ + limit_buy_order_open, + {'id': limit_sell_order['id']} + ]) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ @@ -1124,8 +1134,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value=limit_buy_order_open), - sell=sell_mock, + create_order=create_order_mock, get_fee=fee, ) mocker.patch.multiple( @@ -1147,10 +1156,10 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert log_has("Selling the trade forcefully", caplog) # Should call a market sell - assert sell_mock.call_count == 1 - assert sell_mock.call_args[1]['ordertype'] == 'market' - assert sell_mock.call_args[1]['pair'] == trade.pair - assert sell_mock.call_args[1]['amount'] == trade.amount + assert create_order_mock.call_count == 2 + assert create_order_mock.call_args[1]['ordertype'] == 'market' + assert create_order_mock.call_args[1]['pair'] == trade.pair + assert create_order_mock.call_args[1]['amount'] == trade.amount # Rpc is sending first buy, then sell assert rpc_mock.call_count == 2 @@ -1171,8 +1180,10 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value=limit_buy_order_open), - sell=sell_mock, + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + sell_mock, + ]), get_fee=fee, fetch_order=MagicMock(return_value={'status': 'canceled'}), ) @@ -1212,8 +1223,10 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - sell=MagicMock(return_value={'id': limit_sell_order['id']}), + create_order=MagicMock(side_effect=[ + {'id': limit_buy_order['id']}, + {'id': limit_sell_order['id']}, + ]), get_fee=fee, ) mocker.patch.multiple( @@ -1318,8 +1331,10 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - sell=MagicMock(return_value={'id': limit_sell_order['id']}), + create_order=MagicMock(side_effect=[ + {'id': limit_buy_order['id']}, + {'id': limit_sell_order['id']}, + ]), get_fee=fee, ) mocker.patch.multiple( @@ -1391,8 +1406,10 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - sell=MagicMock(return_value={'id': limit_sell_order['id']}), + create_order=MagicMock(side_effect=[ + {'id': limit_buy_order['id']}, + {'id': limit_sell_order['id']}, + ]), get_fee=fee, ) mocker.patch.multiple( @@ -1502,8 +1519,10 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value={'id': limit_buy_order['id']}), - sell=MagicMock(return_value={'id': limit_sell_order['id']}), + create_order=MagicMock(side_effect=[ + {'id': limit_buy_order['id']}, + {'id': limit_sell_order['id']}, + ]), get_fee=fee, stoploss=stoploss, ) @@ -1840,8 +1859,10 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limi 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value=limit_buy_order), - sell=MagicMock(return_value=limit_sell_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order, + limit_sell_order_open, + ]), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -1877,7 +1898,10 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) @@ -1930,7 +1954,10 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) @@ -1954,15 +1981,18 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, caplog) -def test_handle_trade_use_sell_signal( - default_conf, ticker, limit_buy_order_open, fee, mocker, caplog) -> None: +def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open, + limit_sell_order_open, fee, mocker, caplog) -> None: # use_sell_signal is True buy default caplog.set_level(logging.DEBUG) patch_RPCManager(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + limit_sell_order_open, + ]), get_fee=fee, ) @@ -1990,7 +2020,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -2746,13 +2776,16 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', side_effect=InvalidOrderException()) mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300)) - sellmock = MagicMock(return_value={'id': '12345555'}) + create_order_mock = MagicMock(side_effect=[ + {'id': '12345554'}, + {'id': '12345555'}, + ]) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, - sell=sellmock + create_order=create_order_mock, ) freqtrade.strategy.order_types['stoploss_on_exchange'] = True @@ -2767,7 +2800,7 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c freqtrade.execute_sell(trade=trade, limit=1234, sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) - assert sellmock.call_count == 1 + assert create_order_mock.call_count == 2 assert log_has('Could not cancel stoploss order abcd', caplog) @@ -2963,7 +2996,10 @@ def test_execute_sell_insufficient_funds_error(default_conf, ticker, fee, 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, - sell=MagicMock(side_effect=InsufficientFundsError()) + create_order=MagicMock(side_effect=[ + {'id': 1234553382}, + InsufficientFundsError(), + ]), ) patch_get_signal(freqtrade) @@ -2996,7 +3032,10 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) default_conf.update({ @@ -3033,7 +3072,10 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, limit_bu 'ask': 0.00002173, 'last': 0.00002172 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) default_conf.update({ @@ -3064,7 +3106,10 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, limit_buy_o 'ask': 0.00000173, 'last': 0.00000172 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) default_conf.update({ @@ -3094,7 +3139,10 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, limit_buy_ 'ask': 0.0000173, 'last': 0.0000172 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) default_conf.update({ @@ -3127,7 +3175,10 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ 'ask': 0.00002173, 'last': 0.00002172 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) @@ -3245,7 +3296,10 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order 'ask': 0.0000173, 'last': 0.0000172 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) default_conf['ignore_roi_if_buy_signal'] = True @@ -3279,7 +3333,10 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, 'ask': 0.00001099, 'last': 0.00001099 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) default_conf['trailing_stop'] = True @@ -3331,7 +3388,10 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or 'ask': buy_price - 0.000001, 'last': buy_price - 0.000001 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) default_conf['trailing_stop'] = True @@ -3388,7 +3448,10 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde 'ask': buy_price - 0.000001, 'last': buy_price - 0.000001 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + ]), get_fee=fee, ) patch_whitelist(mocker, default_conf) @@ -3448,7 +3511,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ 'ask': buy_price, 'last': buy_price }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) patch_whitelist(mocker, default_conf) @@ -3508,7 +3571,11 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b 'ask': 0.00000173, 'last': 0.00000172 }), - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + {'id': 1234553382}, + {'id': 1234553383} + ]), get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=False), ) @@ -3905,7 +3972,7 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_open, mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) @@ -3942,7 +4009,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value={'id': limit_buy_order['id']}), + create_order=MagicMock(return_value={'id': limit_buy_order['id']}), get_fee=fee, ) # Save state of current whitelist @@ -4039,8 +4106,10 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value=limit_buy_order_open), - sell=MagicMock(return_value=limit_sell_order_open), + create_order=MagicMock(side_effect=[ + limit_buy_order_open, + limit_sell_order_open, + ]), get_fee=fee, ) freqtrade = FreqtradeBot(default_conf) @@ -4105,7 +4174,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) diff --git a/tests/test_wallets.py b/tests/test_wallets.py index 64db3b9cd..9f58cb71d 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -125,7 +125,7 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None: (1, None, 50, 66.66666), (0.99, None, 49.5, 66.0), (0.50, None, 25, 33.3333), - # Tests with capital ignore balance_ratio + # Tests with capital ignore balance_ratio (1, 100, 50, 0.0), (0.99, 200, 50, 66.66666), (0.99, 150, 50, 50), @@ -138,7 +138,7 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, - buy=MagicMock(return_value=limit_buy_order_open), + create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee ) From cf4d1875dd2f34c74f64fa416b23aa4804cc1a6c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 29 Jul 2021 06:56:37 +0200 Subject: [PATCH 192/519] Use prohibitedIn instead of isRestricted --- docs/exchanges.md | 5 +++-- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index 72f5e4ba4..29b9bb533 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -77,8 +77,9 @@ You can get a list of restricted markets by using the following snippet: ``` python import ccxt ct = ccxt.bittrex() -ct.load_markets() -res = [f"{x['quoteCurrencySymbol']}/{x['baseCurrencySymbol']}" for x in ct.publicGetMarkets() if 'US' in x['prohibitedIn']] +lm = ct.load_markets() + +res = [p for p, x in lm.items() if 'US' in x['info']['prohibitedIn']] print(res) ``` diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 91b278077..47ce8b4ba 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -387,7 +387,7 @@ class Exchange: # its contents depend on the exchange. # It can also be a string or similar ... so we need to verify that first. elif (isinstance(self.markets[pair].get('info', None), dict) - and self.markets[pair].get('info', {}).get('IsRestricted', False)): + and self.markets[pair].get('info', {}).get('prohibitedIn', False)): # Warn users about restricted pairs in whitelist. # We cannot determine reliably if Users are affected. logger.warning(f"Pair {pair} is restricted for some users on this exchange." diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 02adf01c4..bf77a9460 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -673,7 +673,7 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog): api_mock = MagicMock() type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/BTC': {'quote': 'BTC', 'info': {'IsRestricted': True}}, + 'XRP/BTC': {'quote': 'BTC', 'info': {'prohibitedIn': ['US']}}, 'NEO/BTC': {'quote': 'BTC', 'info': 'TestString'}, # info can also be a string ... }) mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) From 6490b82ad61d4ec0796b62fe68ecdcf606dfc970 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 29 Jul 2021 19:48:36 +0200 Subject: [PATCH 193/519] Update docker-documentation for multiarch builds --- docs/docker_quickstart.md | 91 ++++++++------------------------------- 1 file changed, 17 insertions(+), 74 deletions(-) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index cb66fc7e2..1fa229225 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -24,82 +24,21 @@ Freqtrade provides an official Docker image on [Dockerhub](https://hub.docker.co Create a new directory and place the [docker-compose file](https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml) in this directory. -=== "PC/MAC/Linux" - ``` bash - mkdir ft_userdata - cd ft_userdata/ - # Download the docker-compose file from the repository - curl https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml -o docker-compose.yml +``` bash +mkdir ft_userdata +cd ft_userdata/ +# Download the docker-compose file from the repository +curl https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml -o docker-compose.yml - # Pull the freqtrade image - docker-compose pull +# Pull the freqtrade image +docker-compose pull - # Create user directory structure - docker-compose run --rm freqtrade create-userdir --userdir user_data +# Create user directory structure +docker-compose run --rm freqtrade create-userdir --userdir user_data - # Create configuration - Requires answering interactive questions - docker-compose run --rm freqtrade new-config --config user_data/config.json - ``` - -=== "RaspberryPi" - ``` bash - mkdir ft_userdata - cd ft_userdata/ - # Download the docker-compose file from the repository - curl https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml -o docker-compose.yml - - # Edit the compose file to use an image named `*_pi` (stable_pi or develop_pi) - - # Pull the freqtrade image - docker-compose pull - - # Create user directory structure - docker-compose run --rm freqtrade create-userdir --userdir user_data - - # Create configuration - Requires answering interactive questions - docker-compose run --rm freqtrade new-config --config user_data/config.json - ``` - - !!! Note "Change your docker Image" - You have to change the docker image in the docker-compose file for your Raspberry build to work properly. - ``` yml - image: freqtradeorg/freqtrade:stable_pi - # image: freqtradeorg/freqtrade:develop_pi - ``` - -=== "ARM 64 Systenms (Mac M1, Raspberry Pi 4, Jetson Nano)" - In case of a Mac M1, make sure that your docker installation is running in native mode - Arm64 images are not yet provided via Docker Hub and need to be build locally first. - Depending on the device, this may take a few minutes (Apple M1) or multiple hours (Raspberry Pi) - - ``` bash - # Clone Freqtrade repository - git clone https://github.com/freqtrade/freqtrade.git - cd freqtrade - # Optionally switch to the stable version - git checkout stable - - # Modify your docker-compose file to enable building and change the image name - # (see the Note Box below for necessary changes) - - # Build image - docker-compose build - - # Create user directory structure - docker-compose run --rm freqtrade create-userdir --userdir user_data - - # Create configuration - Requires answering interactive questions - docker-compose run --rm freqtrade new-config --config user_data/config.json - ``` - - !!! Note "Change your docker Image" - You have to change the docker image in the docker-compose file for your arm64 build to work properly. - ``` yml - image: freqtradeorg/freqtrade:custom_arm64 - build: - context: . - dockerfile: "Dockerfile" - ``` +# Create configuration - Requires answering interactive questions +docker-compose run --rm freqtrade new-config --config user_data/config.json +``` The above snippet creates a new directory called `ft_userdata`, downloads the latest compose file and pulls the freqtrade image. The last 2 steps in the snippet create the directory with `user_data`, as well as (interactively) the default configuration based on your selections. @@ -117,7 +56,7 @@ The last 2 steps in the snippet create the directory with `user_data`, as well a The `SampleStrategy` is run by default. -!!! Warning "`SampleStrategy` is just a demo!" +!!! Danger "`SampleStrategy` is just a demo!" The `SampleStrategy` is there for your reference and give you ideas for your own strategy. Please always backtest your strategy and use dry-run for some time before risking real money! You will find more information about Strategy development in the [Strategy documentation](strategy-customization.md). @@ -167,6 +106,10 @@ Advanced users may edit the docker-compose file further to include all possible All freqtrade arguments will be available by running `docker-compose run --rm freqtrade `. +!!! Warning "`docker-compose` for trade commands" + Trade commands (`freqtrade trade <...>`) should not be ran via `docker-compose run` - but should use `docker-compose up -d` instead. + This makes sure that the container is properly started (including port forwardings) and will make sure that the container will restart after a system reboot. + !!! Note "`docker-compose run --rm`" Including `--rm` will remove the container after completion, and is highly recommended for all modes except trading mode (running with `freqtrade trade` command). From 03064731ac0bf3a62d91557cad1b8f939e1ff156 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 29 Jul 2021 19:49:19 +0200 Subject: [PATCH 194/519] Version bump 2021.7 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index d76b655d7..34e1bfc5d 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.6' +__version__ = '2021.7' if __version__ == 'develop': From aa34889c047e13607a1b60ccca01cf63d6f57bab Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Jul 2021 07:14:35 +0200 Subject: [PATCH 195/519] Don't run migrations twice --- freqtrade/persistence/migrations.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 035452437..a2d88cb31 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -161,13 +161,6 @@ def check_migrate(engine, decl_base, previous_tables) -> None: table_back_name = get_backup_name(tabs, 'trades_bak') # Check for latest column - if not has_column(cols, 'open_trade_value'): - logger.info(f'Running database migration for trades - backup: {table_back_name}') - migrate_trades_table(decl_base, inspector, engine, table_back_name, cols) - # Reread columns - the above recreated the table! - inspector = inspect(engine) - cols = inspector.get_columns('trades') - if not has_column(cols, 'buy_tag'): logger.info(f'Running database migration for trades - backup: {table_back_name}') migrate_trades_table(decl_base, inspector, engine, table_back_name, cols) From 35bf2a59a80323f8ddc107c11406e687cc13a969 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Jul 2021 21:02:55 +0200 Subject: [PATCH 196/519] Improve test reliability (fix fluky test) --- tests/rpc/test_rpc_apiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 803ef7e5d..68f23e0fd 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -656,7 +656,7 @@ def test_api_logs(botclient): # Help debugging random test failure print(f"rc={rc.json()}") print(f"rc1={rc1.json()}") - assert rc1.json()['log_count'] == 5 + assert rc1.json()['log_count'] > 2 assert len(rc1.json()['logs']) == rc1.json()['log_count'] From 499af5c42bbfd07a30370e662014718002259de6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Jul 2021 21:04:04 +0200 Subject: [PATCH 197/519] Update webservermode docs closes #5345 --- docs/utils.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index 789462de4..6395fb6f9 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -627,7 +627,7 @@ FreqUI will also show the backtesting results. ``` usage: freqtrade webserver [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [-s NAME] [--strategy-path PATH] + [--userdir PATH] optional arguments: -h, --help show this help message and exit @@ -648,12 +648,6 @@ Common arguments: --userdir PATH, --user-data-dir PATH Path to userdata directory. -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - ``` ## List Hyperopt results From ab3c75341508a7bdee9597f026681f1a1594a4bf Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Jul 2021 21:23:09 +0200 Subject: [PATCH 198/519] Fix develop_plot building --- build_helpers/publish_docker_arm64.sh | 10 +++++----- build_helpers/publish_docker_multi.sh | 2 +- docker/Dockerfile.plot | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh index 756d5e41d..08793d339 100755 --- a/build_helpers/publish_docker_arm64.sh +++ b/build_helpers/publish_docker_arm64.sh @@ -37,7 +37,7 @@ fi # Tag image for upload and next build step docker tag freqtrade:$TAG_ARM ${CACHE_IMAGE}:$TAG_ARM -docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${TAG_ARM} -t freqtrade:${TAG_PLOT_ARM} -f docker/Dockerfile.plot . +docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG_ARM} -t freqtrade:${TAG_PLOT_ARM} -f docker/Dockerfile.plot . docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM @@ -63,13 +63,13 @@ echo "create manifests" docker manifest create --amend ${IMAGE_NAME}:${TAG} ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} docker manifest push -p ${IMAGE_NAME}:${TAG} -docker manifest create --amend ${IMAGE_NAME}:${TAG_PLOT} ${CACHE_IMAGE}:${TAG_PLOT_ARM} ${CACHE_IMAGE}:${TAG_PLOT} +docker manifest create ${IMAGE_NAME}:${TAG_PLOT} ${CACHE_IMAGE}:${TAG_PLOT_ARM} ${CACHE_IMAGE}:${TAG_PLOT} docker manifest push -p ${IMAGE_NAME}:${TAG_PLOT} -Tag as latest for develop builds +# Tag as latest for develop builds if [ "${TAG}" = "develop" ]; then - docker tag ${IMAGE_NAME}:develop ${IMAGE_NAME}:latest - docker push ${IMAGE_NAME}:latest + docker manifest create ${IMAGE_NAME}:latest ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} + docker manifest push -p ${IMAGE_NAME}:latest fi docker images diff --git a/build_helpers/publish_docker_multi.sh b/build_helpers/publish_docker_multi.sh index 4961cb9a7..4010eed45 100755 --- a/build_helpers/publish_docker_multi.sh +++ b/build_helpers/publish_docker_multi.sh @@ -48,7 +48,7 @@ fi # Tag image for upload and next build step docker tag freqtrade:$TAG ${CACHE_IMAGE}:$TAG -docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${TAG} -t freqtrade:${TAG_PLOT} -f docker/Dockerfile.plot . +docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG} -t freqtrade:${TAG_PLOT} -f docker/Dockerfile.plot . docker tag freqtrade:$TAG_PLOT ${CACHE_IMAGE}:$TAG_PLOT diff --git a/docker/Dockerfile.plot b/docker/Dockerfile.plot index d2fc3618a..e7f6bbb16 100644 --- a/docker/Dockerfile.plot +++ b/docker/Dockerfile.plot @@ -1,5 +1,6 @@ -ARG sourceimage=develop -FROM freqtradeorg/freqtrade:${sourceimage} +ARG sourceimage=freqtradeorg/freqtrade +ARG sourcetag=develop +FROM ${sourceimage}:${sourcetag} # Install dependencies COPY requirements-plot.txt /freqtrade/ From 6abd352c0f15d42a73d6cd85afb9e055a45c6649 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Jul 2021 08:40:57 +0200 Subject: [PATCH 199/519] Add test for backtesting dataframe cache --- tests/optimize/test_backtesting.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 1c495f123..deaaf9f2f 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -739,6 +739,9 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): # 100 buys signals results = result['results'] assert len(results) == 100 + # Cached data should be 200 (no change since required_startup is 0) + assert len(backtesting.dataprovider.get_analyzed_dataframe('UNITTEST/BTC', '1m')[0]) == 200 + # One trade was force-closed at the end assert len(results.loc[results['is_open']]) == 0 @@ -794,6 +797,10 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) # make sure we don't have trades with more than configured max_open_trades assert len(evaluate_result_multi(results['results'], '5m', 3)) == 0 + # Cached data correctly removed amounts + removed_candles = len(data[pair]) - 1 - backtesting.strategy.startup_candle_count + assert len(backtesting.dataprovider.get_analyzed_dataframe(pair, '5m')[0]) == removed_candles + backtest_conf = { 'processed': processed, 'start_date': min_date, From b1cbc75e93740e3f62ec5ed6c1a5b0e2d0c4ff80 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Jul 2021 08:45:04 +0200 Subject: [PATCH 200/519] Properly cache pair dataframe in backtesting (without startup-range). --- freqtrade/optimize/backtesting.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index b9ce69edd..2974e49e2 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -240,6 +240,9 @@ class Backtesting: df_analyzed.drop(df_analyzed.head(1).index, inplace=True) + # Update dataprovider cache + self.dataprovider._set_cached_df(pair, self.timeframe, df_analyzed) + # Convert from Pandas to list for performance reasons # (Looping Pandas is slow.) data[pair] = df_analyzed.values.tolist() @@ -434,10 +437,6 @@ class Backtesting: trades: List[LocalTrade] = [] self.prepare_backtest(enable_protections) - # Update dataprovider cache - for pair, dataframe in processed.items(): - self.dataprovider._set_cached_df(pair, self.timeframe, dataframe) - # Use dict of lists with data for performance # (looping lists is a lot faster than pandas DataFrames) data: Dict = self._get_ohlcv_as_lists(processed) From 1ccc89d1e9f9f95eaebc50248936f9da289baddd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Jul 2021 10:00:24 +0200 Subject: [PATCH 201/519] Store fully analyzed dataframe --- freqtrade/optimize/backtesting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 2974e49e2..628289b7f 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -227,7 +227,7 @@ class Backtesting: pair_data.loc[:, 'buy_tag'] = None # cleanup if buy_tag is exist df_analyzed = self.strategy.advise_sell( - self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy() + self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair}).copy() # Trim startup period from analyzed dataframe df_analyzed = trim_dataframe(df_analyzed, self.timerange, startup_candles=self.required_startup) @@ -245,7 +245,7 @@ class Backtesting: # Convert from Pandas to list for performance reasons # (Looping Pandas is slow.) - data[pair] = df_analyzed.values.tolist() + data[pair] = df_analyzed[headers].values.tolist() return data def _get_close_rate(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple, From c5e3348b89d92087d3ad41cfb50486fbc8fae7ec Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Jul 2021 16:36:28 +0200 Subject: [PATCH 202/519] Migrations for indexes should run in a seperate session closes #5349 --- docs/sql_cheatsheet.md | 2 +- freqtrade/persistence/migrations.py | 18 +++--- tests/test_persistence.py | 90 ----------------------------- 3 files changed, 8 insertions(+), 102 deletions(-) diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index 477396931..caa3f53a6 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -110,7 +110,7 @@ DELETE FROM trades WHERE id = 31; Freqtrade supports PostgreSQL by using SQLAlchemy, which supports multiple different database systems. Installation: -`pip install psycopg2` +`pip install psycopg2-binary` Usage: `... --db-url postgresql+psycopg2://:@localhost:5432/` diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index a2d88cb31..1839c4130 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -65,7 +65,8 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col # Schema migration necessary with engine.begin() as connection: connection.execute(text(f"alter table trades rename to {table_back_name}")) - # drop indexes on backup table + with engine.begin() as connection: + # drop indexes on backup table in new session for index in inspector.get_indexes(table_back_name): connection.execute(text(f"drop index {index['name']}")) # let SQLAlchemy create the schema as required @@ -76,7 +77,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col connection.execute(text(f"""insert into trades (id, exchange, pair, is_open, fee_open, fee_open_cost, fee_open_currency, - fee_close, fee_close_cost, fee_open_currency, open_rate, + fee_close, fee_close_cost, fee_close_currency, open_rate, open_rate_requested, close_rate, close_rate_requested, close_profit, stake_amount, amount, amount_requested, open_date, close_date, open_order_id, stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct, @@ -84,14 +85,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag, timeframe, open_trade_value, close_profit_abs ) - select id, lower(exchange), - case - when instr(pair, '_') != 0 then - substr(pair, instr(pair, '_') + 1) || '/' || - substr(pair, 1, instr(pair, '_') - 1) - else pair - end - pair, + select id, lower(exchange), pair, is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost, {fee_open_currency} fee_open_currency, {fee_close} fee_close, {fee_close_cost} fee_close_cost, {fee_close_currency} fee_close_currency, @@ -132,7 +126,9 @@ def migrate_orders_table(decl_base, inspector, engine, table_back_name: str, col with engine.begin() as connection: connection.execute(text(f"alter table orders rename to {table_back_name}")) - # drop indexes on backup table + + with engine.begin() as connection: + # drop indexes on backup table in new session for index in inspector.get_indexes(table_back_name): connection.execute(text(f"drop index {index['name']}")) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 8b927be8b..84e7702e0 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -442,96 +442,6 @@ def test_clean_dry_run_db(default_conf, fee): assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 1 -def test_migrate_old(mocker, default_conf, fee): - """ - Test Database migration(starting with old pairformat) - """ - amount = 103.223 - create_table_old = """CREATE TABLE IF NOT EXISTS "trades" ( - id INTEGER NOT NULL, - exchange VARCHAR NOT NULL, - pair VARCHAR NOT NULL, - is_open BOOLEAN NOT NULL, - fee FLOAT NOT NULL, - open_rate FLOAT, - close_rate FLOAT, - close_profit FLOAT, - stake_amount FLOAT NOT NULL, - amount FLOAT, - open_date DATETIME NOT NULL, - close_date DATETIME, - open_order_id VARCHAR, - PRIMARY KEY (id), - CHECK (is_open IN (0, 1)) - );""" - insert_table_old = """INSERT INTO trades (exchange, pair, is_open, open_order_id, fee, - open_rate, stake_amount, amount, open_date) - VALUES ('binance', 'BTC_ETC', 1, '123123', {fee}, - 0.00258580, {stake}, {amount}, - '2017-11-28 12:44:24.000000') - """.format(fee=fee.return_value, - stake=default_conf.get("stake_amount"), - amount=amount - ) - insert_table_old2 = """INSERT INTO trades (exchange, pair, is_open, fee, - open_rate, close_rate, stake_amount, amount, open_date) - VALUES ('binance', 'BTC_ETC', 0, {fee}, - 0.00258580, 0.00268580, {stake}, {amount}, - '2017-11-28 12:44:24.000000') - """.format(fee=fee.return_value, - stake=default_conf.get("stake_amount"), - amount=amount - ) - engine = create_engine('sqlite://') - mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) - - # Create table using the old format - with engine.begin() as connection: - connection.execute(text(create_table_old)) - connection.execute(text(insert_table_old)) - connection.execute(text(insert_table_old2)) - # Run init to test migration - init_db(default_conf['db_url'], default_conf['dry_run']) - - assert len(Trade.query.filter(Trade.id == 1).all()) == 1 - trade = Trade.query.filter(Trade.id == 1).first() - assert trade.fee_open == fee.return_value - assert trade.fee_close == fee.return_value - assert trade.open_rate_requested is None - assert trade.close_rate_requested is None - assert trade.is_open == 1 - assert trade.amount == amount - assert trade.amount_requested == amount - assert trade.stake_amount == default_conf.get("stake_amount") - assert trade.pair == "ETC/BTC" - assert trade.exchange == "binance" - assert trade.max_rate == 0.0 - assert trade.stop_loss == 0.0 - assert trade.initial_stop_loss == 0.0 - assert trade.open_trade_value == trade._calc_open_trade_value() - assert trade.close_profit_abs is None - assert trade.fee_open_cost is None - assert trade.fee_open_currency is None - assert trade.fee_close_cost is None - assert trade.fee_close_currency is None - assert trade.timeframe is None - - trade = Trade.query.filter(Trade.id == 2).first() - assert trade.close_rate is not None - assert trade.is_open == 0 - assert trade.open_rate_requested is None - assert trade.close_rate_requested is None - assert trade.close_rate is not None - assert pytest.approx(trade.close_profit_abs) == trade.calc_profit() - assert trade.sell_order_status is None - - # Should've created one order - assert len(Order.query.all()) == 1 - order = Order.query.first() - assert order.order_id == '123123' - assert order.ft_order_side == 'buy' - - def test_migrate_new(mocker, default_conf, fee, caplog): """ Test Database migration (starting with new pairformat) From 6f8519d0a3fdaf5e58a70b53730209943b7d8619 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Jul 2021 17:43:10 +0200 Subject: [PATCH 203/519] Add environment variable support --- docs/configuration.md | 50 +++++++++++++------ freqtrade/configuration/configuration.py | 6 +++ freqtrade/configuration/environment_vars.py | 54 +++++++++++++++++++++ freqtrade/constants.py | 1 + tests/test_configuration.py | 35 ++++++++++++- 5 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 freqtrade/configuration/environment_vars.py diff --git a/docs/configuration.md b/docs/configuration.md index 5d4b1f2c3..fd4806fe6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -11,6 +11,37 @@ Per default, the bot loads the configuration from the `config.json` file, locate You can specify a different configuration file used by the bot with the `-c/--config` command-line option. +If you used the [Quick start](installation.md/#quick-start) method for installing +the bot, the installation script should have already created the default configuration file (`config.json`) for you. + +If the default configuration file is not created we recommend to use `freqtrade new-config --config config.json` to generate a basic configuration file. + +The Freqtrade configuration file is to be written in JSON format. + +Additionally to the standard JSON syntax, you may use one-line `// ...` and multi-line `/* ... */` comments in your configuration files and trailing commas in the lists of parameters. + +Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates the syntax of the configuration file at startup and will warn you if you made any errors editing it, pointing out problematic lines. + +### Environment variables + +Set options in the Freqtrade configuration via environment variables. +This takes priority over the corresponding value in configuration or strategy. + +Environment variables must be prefixed with `FREQTRADE__` to be loaded to the freqtrade configuration. + +`__` serves as level separator, so the format used should correspond to `FREQTRADE__{section}__{key}`. +As such - an environment variable defined as `export FREQTRADE__STAKE_AMOUNT=200` would result in `{stake_amount: 200}`. + +A more complex example might be `export FREQTRADE__EXCHANGE__KEY=` to keep your exchange key secret. This will move the value to the `exchange.key` section of the configuration. +Using this scheme, all configuration settings will also be available as environment variables. + +Please note that Environment variables will overwrite corresponding settings in your configuration, but command line Arguments will always win. + +!!! Note + Environment variables detected are logged at startup - so if you can't find why a value is not what you think it should be based on the configuration, make sure it's not loaded from an environment variable. + +### Multiple configuration files + Multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream. !!! Tip "Use multiple configuration files to keep secrets secret" @@ -22,17 +53,6 @@ Multiple configuration files can be specified and used by the bot or the bot can The 2nd file should only specify what you intend to override. If a key is in more than one of the configurations, then the "last specified configuration" wins (in the above example, `config-private.json`). -If you used the [Quick start](installation.md/#quick-start) method for installing -the bot, the installation script should have already created the default configuration file (`config.json`) for you. - -If the default configuration file is not created we recommend you to use `freqtrade new-config --config config.json` to generate a basic configuration file. - -The Freqtrade configuration file is to be written in JSON format. - -Additionally to the standard JSON syntax, you may use one-line `// ...` and multi-line `/* ... */` comments in your configuration files and trailing commas in the lists of parameters. - -Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates the syntax of the configuration file at startup and will warn you if you made any errors editing it, pointing out problematic lines. - ## Configuration parameters The table below will list all configuration parameters available. @@ -41,6 +61,7 @@ Freqtrade can also load many options via command line (CLI) arguments (check out The prevalence for all Options is as follows: - CLI arguments override any other option +- [Environment Variables](#environment-variables) - Configuration files are used in sequence (the last file wins) and override Strategy configurations. - Strategy configurations are only used if they are not set via configuration or command-line arguments. These options are marked with [Strategy Override](#parameters-in-the-strategy) in the below table. @@ -526,9 +547,10 @@ Once you will be happy with your bot performance running in the Dry-run mode, yo ## Switch to production mode -In production mode, the bot will engage your money. Be careful, since a wrong -strategy can lose all your money. Be aware of what you are doing when -you run it in production mode. +In production mode, the bot will engage your money. Be careful, since a wrong strategy can lose all your money. +Be aware of what you are doing when you run it in production mode. + +When switching to Production mode, please make sure to use a different / fresh database to avoid dry-run trades messing with your exchange money and eventually tainting your statistics. ### Setup your exchange account diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index bd30adcae..4dd5b7203 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -11,6 +11,7 @@ from freqtrade import constants from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings from freqtrade.configuration.directory_operations import create_datadir, create_userdata_dir +from freqtrade.configuration.environment_vars import enironment_vars_to_dict from freqtrade.configuration.load_config import load_config_file, load_file from freqtrade.enums import NON_UTIL_MODES, TRADING_MODES, RunMode from freqtrade.exceptions import OperationalException @@ -71,6 +72,11 @@ class Configuration: # Merge config options, overwriting old values config = deep_merge_dicts(load_config_file(path), config) + + # Load environment variables + env_data = enironment_vars_to_dict() + config = deep_merge_dicts(env_data, config) + config['config_files'] = files # Normalize config if 'internals' not in config: diff --git a/freqtrade/configuration/environment_vars.py b/freqtrade/configuration/environment_vars.py new file mode 100644 index 000000000..4c190ed04 --- /dev/null +++ b/freqtrade/configuration/environment_vars.py @@ -0,0 +1,54 @@ +import logging +import os +from typing import Any, Dict + +from freqtrade.constants import ENV_VAR_PREFIX +from freqtrade.misc import deep_merge_dicts + + +logger = logging.getLogger(__name__) + + +def get_var_typed(val): + try: + return int(val) + except ValueError: + try: + return float(val) + except ValueError: + if val.lower() in ('t', 'true'): + return True + elif val.lower() in ('f', 'false'): + return False + # keep as string + return val + + +def flat_vars_to_nested_dict(env_dict: Dict[str, Any], prefix: str) -> Dict[str, Any]: + """ + Environment variables must be prefixed with FREQTRADE. + FREQTRADE__{section}__{key} + :param env_dict: Dictionary to validate - usually os.environ + :param prefix: Prefix to consider (usually FREQTRADE__) + :return: Nested dict based on available and relevant variables. + """ + relevant_vars: Dict[str, Any] = {} + + for env_var, val in sorted(env_dict.items()): + if env_var.startswith(prefix): + logger.info(f"Loading variable '{env_var}'") + key = env_var.replace(prefix, '') + for k in reversed(key.split('__')): + val = {k.lower(): get_var_typed(val) if type(val) != dict else val} + relevant_vars = deep_merge_dicts(val, relevant_vars) + + return relevant_vars + + +def enironment_vars_to_dict() -> Dict[str, Any]: + """ + Read environment variables and return a nested dict for relevant variables + Relevant variables must follow the FREQTRADE__{section}__{key} pattern + :return: Nested dict based on available and relevant variables. + """ + return flat_vars_to_nested_dict(os.environ.copy(), ENV_VAR_PREFIX) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 2f93ace1c..b48644c58 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -47,6 +47,7 @@ USERPATH_STRATEGIES = 'strategies' USERPATH_NOTEBOOKS = 'notebooks' TELEGRAM_SETTING_OPTIONS = ['on', 'off', 'silent'] +ENV_VAR_PREFIX = 'FREQTRADE__' # Define decimals per coin for outputs diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 34db892b2..7012333e9 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -18,8 +18,9 @@ from freqtrade.configuration.deprecated_settings import (check_conflicting_setti process_deprecated_setting, process_removed_setting, process_temporary_deprecated_settings) +from freqtrade.configuration.environment_vars import flat_vars_to_nested_dict from freqtrade.configuration.load_config import load_config_file, load_file, log_config_error_range -from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL +from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL, ENV_VAR_PREFIX from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.loggers import _set_loggers, setup_logging, setup_logging_pre @@ -1349,3 +1350,35 @@ def test_process_deprecated_ticker_interval(mocker, default_conf, caplog): with pytest.raises(OperationalException, match=r"Both 'timeframe' and 'ticker_interval' detected."): process_temporary_deprecated_settings(config) + + +def test_flat_vars_to_nested_dict(caplog): + + test_args = { + 'FREQTRADE__EXCHANGE__SOME_SETTING': 'true', + 'FREQTRADE__EXCHANGE__SOME_FALSE_SETTING': 'false', + 'FREQTRADE__EXCHANGE__CONFIG__whatever': 'sometime', + 'FREQTRADE__ASK_STRATEGY__PRICE_SIDE': 'bid', + 'FREQTRADE__ASK_STRATEGY__cccc': '500', + 'FREQTRADE__STAKE_AMOUNT': '200.05', + 'NOT_RELEVANT': '200.0', # Will be ignored + } + expected = { + 'stake_amount': 200.05, + 'ask_strategy': { + 'price_side': 'bid', + 'cccc': 500, + }, + 'exchange': { + 'config': { + 'whatever': 'sometime', + }, + 'some_setting': True, + 'some_false_setting': False, + } + } + res = flat_vars_to_nested_dict(test_args, ENV_VAR_PREFIX) + assert res == expected + + assert log_has("Loading variable 'FREQTRADE__EXCHANGE__SOME_SETTING'", caplog) + assert not log_has("Loading variable 'NOT_RELEVANT'", caplog) From 90a61b17650dac04b4863ec417b8c661ebf888b2 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 31 Jul 2021 19:52:55 -0600 Subject: [PATCH 204/519] Changed tests in tests/test_persistence.py to use usdt prices --- tests/conftest.py | 89 +++++++- tests/test_persistence.py | 459 +++++++++++++++++++++++++------------- 2 files changed, 388 insertions(+), 160 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5f3a63c39..1924e1f95 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -812,7 +812,7 @@ def shitcoinmarkets(markets): "future": False, "active": True }, - }) + }) return shitmarkets @@ -1115,7 +1115,7 @@ def order_book_l2_usd(): [25.576, 262.016], [25.577, 178.557], [25.578, 78.614] - ], + ], 'timestamp': None, 'datetime': None, 'nonce': 2372149736 @@ -2084,3 +2084,88 @@ def saved_hyperopt_results(): ].total_seconds() return hyperopt_res + + +@pytest.fixture(scope='function') +def limit_buy_order_usdt_open(): + return { + 'id': 'mocked_limit_buy', + 'type': 'limit', + 'side': 'buy', + 'symbol': 'mocked', + 'datetime': arrow.utcnow().isoformat(), + 'timestamp': arrow.utcnow().int_timestamp, + 'price': 2.00, + 'amount': 30.0, + 'filled': 0.0, + 'cost': 60.0, + 'remaining': 30.0, + 'status': 'open' + } + + +@pytest.fixture(scope='function') +def limit_buy_order_usdt(limit_buy_order_usdt_open): + order = deepcopy(limit_buy_order_usdt_open) + order['status'] = 'closed' + order['filled'] = order['amount'] + order['remaining'] = 0.0 + return order + + +@pytest.fixture +def limit_sell_order_usdt_open(): + return { + 'id': 'mocked_limit_sell', + 'type': 'limit', + 'side': 'sell', + 'pair': 'mocked', + 'datetime': arrow.utcnow().isoformat(), + 'timestamp': arrow.utcnow().int_timestamp, + 'price': 2.20, + 'amount': 30.0, + 'filled': 0.0, + 'remaining': 30.0, + 'status': 'open' + } + + +@pytest.fixture +def limit_sell_order_usdt(limit_sell_order_usdt_open): + order = deepcopy(limit_sell_order_usdt_open) + order['remaining'] = 0.0 + order['filled'] = order['amount'] + order['status'] = 'closed' + return order + + +@pytest.fixture(scope='function') +def market_buy_order_usdt(): + return { + 'id': 'mocked_market_buy', + 'type': 'market', + 'side': 'buy', + 'symbol': 'mocked', + 'datetime': arrow.utcnow().isoformat(), + 'price': 2.00, + 'amount': 30.0, + 'filled': 30.0, + 'remaining': 0.0, + 'status': 'closed' + } + + +@pytest.fixture +def market_sell_order_usdt(): + return { + 'id': 'mocked_limit_sell', + 'type': 'market', + 'side': 'sell', + 'symbol': 'mocked', + 'datetime': arrow.utcnow().isoformat(), + 'price': 2.20, + 'amount': 30.0, + 'filled': 30.0, + 'remaining': 0.0, + 'status': 'closed' + } diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 84e7702e0..43c97e658 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, C0103 import logging from datetime import datetime, timedelta, timezone +from math import isclose from pathlib import Path from types import FunctionType from unittest.mock import MagicMock @@ -64,40 +65,39 @@ def test_init_dryrun_db(default_conf, tmpdir): @pytest.mark.usefixtures("init_persistence") -def test_update_with_binance(limit_buy_order, limit_sell_order, fee, caplog): +def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, caplog): """ - On this test we will buy and sell a crypto currency. + On this test we will buy and sell a crypto currency. + fee: 0.25% quote + open_rate: 2.00 quote + close_rate: 2.20 quote + amount: = 30.0 crypto + stake_amount + 1x,-1x: 60.0 quote + borrowed + 1x: 0 quote + open_value: (amount * open_rate) ± (amount * open_rate * fee) + 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + amount_closed: + 1x, 3x : amount + close_value: + 1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest + binance,kraken 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 + total_profit: + 1x, 3x : close_value - open_value + binance,kraken 1x: 65.835 - 60.15 = 5.685 + total_profit_ratio: + 1x, 3x : ((close_value/open_value) - 1) * leverage + binance 1x: ((65.835 / 60.15) - 1) * 1 = 0.0945137157107232 - Buy - - Buy: 90.99181073 Crypto at 0.00001099 BTC - (90.99181073*0.00001099 = 0.0009999 BTC) - - Buying fee: 0.25% - - Total cost of buy trade: 0.001002500 BTC - ((90.99181073*0.00001099) + ((90.99181073*0.00001099)*0.0025)) - - Sell - - Sell: 90.99181073 Crypto at 0.00001173 BTC - (90.99181073*0.00001173 = 0,00106733394 BTC) - - Selling fee: 0.25% - - Total cost of sell trade: 0.001064666 BTC - ((90.99181073*0.00001173) - ((90.99181073*0.00001173)*0.0025)) - - Profit/Loss: +0.000062166 BTC - (Sell:0.001064666 - Buy:0.001002500) - Profit/Loss percentage: 0.0620 - ((0.001064666/0.001002500)-1 = 6.20%) - - :param limit_buy_order: - :param limit_sell_order: - :return: """ trade = Trade( id=2, - pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.01, - amount=5, + pair='ADA/USDT', + stake_amount=60.0, + open_rate=2.0, + amount=30.0, is_open=True, open_date=arrow.utcnow().datetime, fee_open=fee.return_value, @@ -109,35 +109,36 @@ def test_update_with_binance(limit_buy_order, limit_sell_order, fee, caplog): assert trade.close_date is None trade.open_order_id = 'something' - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) assert trade.open_order_id is None - assert trade.open_rate == 0.00001099 + assert trade.open_rate == 2.00 assert trade.close_profit is None assert trade.close_date is None assert log_has_re(r"LIMIT_BUY has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).", + r"pair=ADA/USDT, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) caplog.clear() trade.open_order_id = 'something' - trade.update(limit_sell_order) + trade.update(limit_sell_order_usdt) assert trade.open_order_id is None - assert trade.close_rate == 0.00001173 - assert trade.close_profit == 0.06201058 + assert trade.close_rate == 2.20 + assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_date is not None assert log_has_re(r"LIMIT_SELL has been fulfilled for Trade\(id=2, " - r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).", + r"pair=ADA/USDT, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) + caplog.clear() @pytest.mark.usefixtures("init_persistence") -def test_update_market_order(market_buy_order, market_sell_order, fee, caplog): +def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee, caplog): trade = Trade( id=1, - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.01, + pair='ADA/USDT', + stake_amount=60.0, + open_rate=2.0, + amount=30.0, is_open=True, fee_open=fee.return_value, fee_close=fee.return_value, @@ -146,61 +147,60 @@ def test_update_market_order(market_buy_order, market_sell_order, fee, caplog): ) trade.open_order_id = 'something' - trade.update(market_buy_order) + trade.update(market_buy_order_usdt) assert trade.open_order_id is None - assert trade.open_rate == 0.00004099 + assert trade.open_rate == 2.0 assert trade.close_profit is None assert trade.close_date is None assert log_has_re(r"MARKET_BUY has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).", + r"pair=ADA/USDT, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) caplog.clear() trade.is_open = True trade.open_order_id = 'something' - trade.update(market_sell_order) + trade.update(market_sell_order_usdt) assert trade.open_order_id is None - assert trade.close_rate == 0.00004173 - assert trade.close_profit == 0.01297561 + assert trade.close_rate == 2.2 + assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_date is not None assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, " - r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).", + r"pair=ADA/USDT, amount=30.00000000, open_rate=2.00000000, open_since=.*\).", caplog) @pytest.mark.usefixtures("init_persistence") -def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee): +def test_calc_open_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.01, - amount=5, + pair='ADA/USDT', + stake_amount=60.0, + open_rate=2.0, + amount=30.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', ) trade.open_order_id = 'something' - trade.update(limit_buy_order) - assert trade._calc_open_trade_value() == 0.0010024999999225068 + trade.update(limit_buy_order_usdt) + assert trade._calc_open_trade_value() == 60.15 + trade.update(limit_sell_order_usdt) + assert isclose(trade.calc_close_trade_value(), 65.835) - trade.update(limit_sell_order) - assert trade.calc_close_trade_value() == 0.0010646656050132426 - - # Profit in BTC - assert trade.calc_profit() == 0.00006217 + # Profit in USDT + assert trade.calc_profit() == 5.685 # Profit in percent - assert trade.calc_profit_ratio() == 0.06201058 + assert trade.calc_profit_ratio() == round(0.0945137157107232, 8) @pytest.mark.usefixtures("init_persistence") -def test_trade_close(limit_buy_order, limit_sell_order, fee): +def test_trade_close(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.01, - amount=5, + pair='ADA/USDT', + stake_amount=60.0, + open_rate=2.0, + amount=30.0, is_open=True, fee_open=fee.return_value, fee_close=fee.return_value, @@ -210,9 +210,9 @@ def test_trade_close(limit_buy_order, limit_sell_order, fee): assert trade.close_profit is None assert trade.close_date is None assert trade.is_open is True - trade.close(0.02) + trade.close(2.2) assert trade.is_open is False - assert trade.close_profit == 0.99002494 + assert trade.close_profit == round(0.0945137157107232, 8) assert trade.close_date is not None new_date = arrow.Arrow(2020, 2, 2, 15, 6, 1).datetime, @@ -220,34 +220,34 @@ def test_trade_close(limit_buy_order, limit_sell_order, fee): # Close should NOT update close_date if the trade has been closed already assert trade.is_open is False trade.close_date = new_date - trade.close(0.02) + trade.close(2.2) assert trade.close_date == new_date @pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price_exception(limit_buy_order, fee): +def test_calc_close_trade_price_exception(limit_buy_order_usdt, fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - open_rate=0.1, - amount=5, + pair='ADA/USDT', + stake_amount=60.0, + open_rate=2.0, + amount=30.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', ) trade.open_order_id = 'something' - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) assert trade.calc_close_trade_value() == 0.0 @pytest.mark.usefixtures("init_persistence") -def test_update_open_order(limit_buy_order): +def test_update_open_order(limit_buy_order_usdt): trade = Trade( - pair='ETH/BTC', - stake_amount=1.00, - open_rate=0.01, - amount=5, + pair='ADA/USDT', + stake_amount=60.0, + open_rate=2.0, + amount=30.0, fee_open=0.1, fee_close=0.1, exchange='binance', @@ -257,8 +257,8 @@ def test_update_open_order(limit_buy_order): assert trade.close_profit is None assert trade.close_date is None - limit_buy_order['status'] = 'open' - trade.update(limit_buy_order) + limit_buy_order_usdt['status'] = 'open' + trade.update(limit_buy_order_usdt) assert trade.open_order_id is None assert trade.close_profit is None @@ -266,127 +266,270 @@ def test_update_open_order(limit_buy_order): @pytest.mark.usefixtures("init_persistence") -def test_update_invalid_order(limit_buy_order): +def test_update_invalid_order(limit_buy_order_usdt): trade = Trade( - pair='ETH/BTC', - stake_amount=1.00, - amount=5, - open_rate=0.001, + pair='ADA/USDT', + stake_amount=60.0, + amount=30.0, + open_rate=2.0, fee_open=0.1, fee_close=0.1, exchange='binance', ) - limit_buy_order['type'] = 'invalid' + limit_buy_order_usdt['type'] = 'invalid' with pytest.raises(ValueError, match=r'Unknown order type'): - trade.update(limit_buy_order) + trade.update(limit_buy_order_usdt) @pytest.mark.usefixtures("init_persistence") -def test_calc_open_trade_value(limit_buy_order, fee): +def test_calc_open_trade_value(limit_buy_order_usdt, fee): + # 10 minute limit trade on Binance/Kraken + # fee: 0.25 %, 0.3% quote + # open_rate: 2.00 quote + # amount: = 30.0 crypto + # stake_amount + # 1x, -1x: 60.0 quote + # open_value: (amount * open_rate) ± (amount * open_rate * fee) + # 0.25% fee + # 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + # 0.3% fee + # 1x, 3x: 30 * 2 + 30 * 2 * 0.003 = 60.18 quote trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + pair='ADA/USDT', + stake_amount=60.0, + amount=30.0, + open_rate=2.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', ) trade.open_order_id = 'open_trade' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) # Buy @ 2.0 # Get the open rate price with the standard fee rate - assert trade._calc_open_trade_value() == 0.0010024999999225068 + assert trade._calc_open_trade_value() == 60.15 trade.fee_open = 0.003 # Get the open rate price with a custom fee rate - assert trade._calc_open_trade_value() == 0.001002999999922468 + assert trade._calc_open_trade_value() == 60.18 @pytest.mark.usefixtures("init_persistence") -def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee): +def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + pair='ADA/USDT', + stake_amount=60.0, + amount=30.0, + open_rate=2.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', ) trade.open_order_id = 'close_trade' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) # Buy @ 2.0 # Get the close rate price with a custom close rate and a regular fee rate - assert trade.calc_close_trade_value(rate=0.00001234) == 0.0011200318470471794 - + assert trade.calc_close_trade_value(rate=2.5) == 74.8125 # Get the close rate price with a custom close rate and a custom fee rate - assert trade.calc_close_trade_value(rate=0.00001234, fee=0.003) == 0.0011194704275749754 - + assert trade.calc_close_trade_value(rate=2.5, fee=0.003) == 74.775 # Test when we apply a Sell order, and ask price with a custom fee rate - trade.update(limit_sell_order) - assert trade.calc_close_trade_value(fee=0.005) == 0.0010619972701635854 + trade.update(limit_sell_order_usdt) + assert trade.calc_close_trade_value(fee=0.005) == 65.67 @pytest.mark.usefixtures("init_persistence") -def test_calc_profit(limit_buy_order, limit_sell_order, fee): +def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee): + """ + 10 minute limit trade on Binance/Kraken at 1x, 3x leverage + arguments: + fee: + 0.25% quote + 0.30% quote + interest_rate: 0.05% per 4 hrs + open_rate: 2.0 quote + close_rate: + 1.9 quote + 2.1 quote + 2.2 quote + amount: = 30.0 crypto + stake_amount + 1x,-1x: 60.0 quote + 3x,-3x: 20.0 quote + hours: 1/6 (10 minutes) + borrowed + 1x: 0 quote + 3x: 40 quote + -1x: 30 crypto + -3x: 30 crypto + time-periods: + kraken: (1 + 1) 4hr_periods = 2 4hr_periods + binance: 1/24 24hr_periods + interest: borrowed * interest_rate * time-periods + 1x : / + binance 3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote + kraken 3x: 40 * 0.0005 * 2 = 0.040 quote + binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto + kraken -1x,-3x: 30 * 0.0005 * 2 = 0.030 crypto + open_value: (amount * open_rate) ± (amount * open_rate * fee) + 0.0025 fee + 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.85 quote + 0.003 fee: Is only applied to close rate in this test + amount_closed: + 1x, 3x = amount + -1x, -3x = amount + interest + binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto + kraken -1x,-3x: 30 + 0.03 = 30.03 crypto + close_value: + equations: + 1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest + -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee) + 2.1 quote + bin,krak 1x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) = 62.8425 + bin 3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.0008333333 = 62.8416666667 + krak 3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.040 = 62.8025 + bin -1x,-3x: (30.000625 * 2.1) + (30.000625 * 2.1 * 0.0025) = 63.15881578125 + krak -1x,-3x: (30.03 * 2.1) + (30.03 * 2.1 * 0.0025) = 63.2206575 + 1.9 quote + bin,krak 1x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) = 56.8575 + bin 3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.0008333333 = 56.85666667 + krak 3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.040 = 56.8175 + bin -1x,-3x: (30.000625 * 1.9) + (30.000625 * 1.9 * 0.0025) = 57.14369046875 + krak -1x,-3x: (30.03 * 1.9) + (30.03 * 1.9 * 0.0025) = 57.1996425 + 2.2 quote + bin,krak 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 + bin 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667 + krak 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040 = 65.795 + bin -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025) = 66.1663784375 + krak -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025) = 66.231165 + total_profit: + equations: + 1x, 3x : close_value - open_value + -1x,-3x: open_value - close_value + 2.1 quote + binance,kraken 1x: 62.8425 - 60.15 = 2.6925 + binance 3x: 62.84166667 - 60.15 = 2.69166667 + kraken 3x: 62.8025 - 60.15 = 2.6525 + binance -1x,-3x: 59.850 - 63.15881578125 = -3.308815781249997 + kraken -1x,-3x: 59.850 - 63.2206575 = -3.3706575 + 1.9 quote + binance,kraken 1x: 56.8575 - 60.15 = -3.2925 + binance 3x: 56.85666667 - 60.15 = -3.29333333 + kraken 3x: 56.8175 - 60.15 = -3.3325 + binance -1x,-3x: 59.850 - 57.14369046875 = 2.7063095312499996 + kraken -1x,-3x: 59.850 - 57.1996425 = 2.6503575 + 2.2 quote + binance,kraken 1x: 65.835 - 60.15 = 5.685 + binance 3x: 65.83416667 - 60.15 = 5.68416667 + kraken 3x: 65.795 - 60.15 = 5.645 + binance -1x,-3x: 59.850 - 66.1663784375 = -6.316378437499999 + kraken -1x,-3x: 59.850 - 66.231165 = -6.381165 + total_profit_ratio: + equations: + 1x, 3x : ((close_value/open_value) - 1) * leverage + -1x,-3x: (1 - (close_value/open_value)) * leverage + 2.1 quote + binance,kraken 1x: (62.8425 / 60.15) - 1 = 0.04476309226932673 + binance 3x: ((62.84166667 / 60.15) - 1)*3 = 0.13424771421446402 + kraken 3x: ((62.8025 / 60.15) - 1)*3 = 0.13229426433915248 + binance -1x: 1 - (63.15881578125 / 59.850) = -0.05528514254385963 + binance -3x: (1 - (63.15881578125 / 59.850))*3 = -0.1658554276315789 + kraken -1x: 1 - (63.2206575 / 59.850) = -0.05631842105263152 + kraken -3x: (1 - (63.2206575 / 59.850))*3 = -0.16895526315789455 + 1.9 quote + binance,kraken 1x: (56.8575 / 60.15) - 1 = -0.05473815461346632 + binance 3x: ((56.85666667 / 60.15) - 1)*3 = -0.16425602643391513 + kraken 3x: ((56.8175 / 60.15) - 1)*3 = -0.16620947630922667 + binance -1x: 1 - (57.14369046875 / 59.850) = 0.045218204365079395 + binance -3x: (1 - (57.14369046875 / 59.850))*3 = 0.13565461309523819 + kraken -1x: 1 - (57.1996425 / 59.850) = 0.04428333333333334 + kraken -3x: (1 - (57.1996425 / 59.850))*3 = 0.13285000000000002 + 2.2 quote + binance,kraken 1x: (65.835 / 60.15) - 1 = 0.0945137157107232 + binance 3x: ((65.83416667 / 60.15) - 1)*3 = 0.2834995845386534 + kraken 3x: ((65.795 / 60.15) - 1)*3 = 0.2815461346633419 + binance -1x: 1 - (66.1663784375 / 59.850) = -0.1055368159983292 + binance -3x: (1 - (66.1663784375 / 59.850))*3 = -0.3166104479949876 + kraken -1x: 1 - (66.231165 / 59.850) = -0.106619298245614 + kraken -3x: (1 - (66.231165 / 59.850))*3 = -0.319857894736842 + fee: 0.003, 1x + close_value: + 2.1 quote: (30.00 * 2.1) - (30.00 * 2.1 * 0.003) = 62.811 + 1.9 quote: (30.00 * 1.9) - (30.00 * 1.9 * 0.003) = 56.829 + 2.2 quote: (30.00 * 2.2) - (30.00 * 2.2 * 0.003) = 65.802 + total_profit + fee: 0.003, 1x + 2.1 quote: 62.811 - 60.15 = 2.6610000000000014 + 1.9 quote: 56.829 - 60.15 = -3.320999999999998 + 2.2 quote: 65.802 - 60.15 = 5.652000000000008 + total_profit_ratio + fee: 0.003, 1x + 2.1 quote: (62.811 / 60.15) - 1 = 0.04423940149625927 + 1.9 quote: (56.829 / 60.15) - 1 = -0.05521197007481293 + 2.2 quote: (65.802 / 60.15) - 1 = 0.09396508728179565 + """ trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + pair='ADA/USDT', + stake_amount=60.0, + amount=30.0, + open_rate=2.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', ) trade.open_order_id = 'something' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) # Buy @ 2.0 # Custom closing rate and regular fee rate - # Higher than open rate - assert trade.calc_profit(rate=0.00001234) == 0.00011753 - # Lower than open rate - assert trade.calc_profit(rate=0.00000123) == -0.00089086 + # Higher than open rate - 2.1 quote + assert trade.calc_profit(rate=2.1) == 2.6925 + # Lower than open rate - 1.9 quote + assert trade.calc_profit(rate=1.9) == round(-3.292499999999997, 8) - # Custom closing rate and custom fee rate - # Higher than open rate - assert trade.calc_profit(rate=0.00001234, fee=0.003) == 0.00011697 - # Lower than open rate - assert trade.calc_profit(rate=0.00000123, fee=0.003) == -0.00089092 + # fee 0.003 + # Higher than open rate - 2.1 quote + assert trade.calc_profit(rate=2.1, fee=0.003) == 2.661 + # Lower than open rate - 1.9 quote + assert trade.calc_profit(rate=1.9, fee=0.003) == round(-3.320999999999998, 8) - # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 - trade.update(limit_sell_order) - assert trade.calc_profit() == 0.00006217 + # Test when we apply a Sell order. Sell higher than open rate @ 2.2 + trade.update(limit_sell_order_usdt) + assert trade.calc_profit() == round(5.684999999999995, 8) # Test with a custom fee rate on the close trade - assert trade.calc_profit(fee=0.003) == 0.00006163 + assert trade.calc_profit(fee=0.003) == round(5.652000000000008, 8) @pytest.mark.usefixtures("init_persistence") -def test_calc_profit_ratio(limit_buy_order, limit_sell_order, fee): +def test_calc_profit_ratio(limit_buy_order_usdt, limit_sell_order_usdt, fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, - open_rate=0.00001099, + pair='ADA/USDT', + stake_amount=60.0, + amount=30.0, + open_rate=2.0, fee_open=fee.return_value, fee_close=fee.return_value, - exchange='binance', + exchange='binance' ) trade.open_order_id = 'something' - trade.update(limit_buy_order) # Buy @ 0.00001099 + trade.update(limit_buy_order_usdt) # Buy @ 2.0 - # Get percent of profit with a custom rate (Higher than open rate) - assert trade.calc_profit_ratio(rate=0.00001234) == 0.11723875 + # Higher than open rate - 2.1 quote + assert trade.calc_profit_ratio(rate=2.1) == round(0.04476309226932673, 8) + # Lower than open rate - 1.9 quote + assert trade.calc_profit_ratio(rate=1.9) == round(-0.05473815461346632, 8) - # Get percent of profit with a custom rate (Lower than open rate) - assert trade.calc_profit_ratio(rate=0.00000123) == -0.88863828 + # fee 0.003 + # Higher than open rate - 2.1 quote + assert trade.calc_profit_ratio(rate=2.1, fee=0.003) == round(0.04423940149625927, 8) + # Lower than open rate - 1.9 quote + assert trade.calc_profit_ratio(rate=1.9, fee=0.003) == round(-0.05521197007481293, 8) - # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 - trade.update(limit_sell_order) - assert trade.calc_profit_ratio() == 0.06201058 + # Test when we apply a Sell order. Sell higher than open rate @ 2.2 + trade.update(limit_sell_order_usdt) + assert trade.calc_profit_ratio() == round(0.0945137157107232, 8) # Test with a custom fee rate on the close trade - assert trade.calc_profit_ratio(fee=0.003) == 0.06147824 + assert trade.calc_profit_ratio(fee=0.003) == round(0.09396508728179565, 8) trade.open_trade_value = 0.0 assert trade.calc_profit_ratio(fee=0.003) == 0.0 @@ -397,7 +540,7 @@ def test_clean_dry_run_db(default_conf, fee): # Simulate dry_run entries trade = Trade( - pair='ETH/BTC', + pair='ADA/USDT', stake_amount=0.001, amount=123.0, fee_open=fee.return_value, @@ -664,9 +807,9 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog): def test_adjust_stop_loss(fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, + pair='ADA/USDT', + stake_amount=30.0, + amount=30, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', @@ -716,9 +859,9 @@ def test_adjust_stop_loss(fee): def test_adjust_min_max_rates(fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - amount=5, + pair='ADA/USDT', + stake_amount=30.0, + amount=30.0, fee_open=fee.return_value, fee_close=fee.return_value, exchange='binance', @@ -897,11 +1040,11 @@ def test_to_json(default_conf, fee): def test_stoploss_reinitialization(default_conf, fee): init_db(default_conf['db_url']) trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, + pair='ADA/USDT', + stake_amount=30.0, fee_open=fee.return_value, open_date=arrow.utcnow().shift(hours=-2).datetime, - amount=10, + amount=30.0, fee_close=fee.return_value, exchange='binance', open_rate=1, @@ -956,11 +1099,11 @@ def test_stoploss_reinitialization(default_conf, fee): def test_update_fee(fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, + pair='ADA/USDT', + stake_amount=30.0, fee_open=fee.return_value, open_date=arrow.utcnow().shift(hours=-2).datetime, - amount=10, + amount=30.0, fee_close=fee.return_value, exchange='binance', open_rate=1, @@ -995,11 +1138,11 @@ def test_update_fee(fee): def test_fee_updated(fee): trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, + pair='ADA/USDT', + stake_amount=30.0, fee_open=fee.return_value, open_date=arrow.utcnow().shift(hours=-2).datetime, - amount=10, + amount=30.0, fee_close=fee.return_value, exchange='binance', open_rate=1, From e9ef9a6d28c9d9ea571dd218e24512e11c6da8af Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Aug 2021 10:31:35 +0200 Subject: [PATCH 205/519] Use .view() to convert dates to enums part of #5314 - fixing deprecation warning. --- freqtrade/data/history/jsondatahandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/data/history/jsondatahandler.py b/freqtrade/data/history/jsondatahandler.py index 990e75bd9..24d6e814b 100644 --- a/freqtrade/data/history/jsondatahandler.py +++ b/freqtrade/data/history/jsondatahandler.py @@ -62,7 +62,7 @@ class JsonDataHandler(IDataHandler): filename = self._pair_data_filename(self._datadir, pair, timeframe) _data = data.copy() # Convert date to int - _data['date'] = _data['date'].astype(np.int64) // 1000 // 1000 + _data['date'] = _data['date'].view(np.int64) // 1000 // 1000 # Reset index, select only appropriate columns and save as json _data.reset_index(drop=True).loc[:, self._columns].to_json( From 047df0c2128cc821a41c5e8118629c3d52bc32b6 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sun, 1 Aug 2021 03:01:47 -0600 Subject: [PATCH 206/519] Removed leverage references --- tests/test_persistence.py | 151 +++++++++++--------------------------- 1 file changed, 41 insertions(+), 110 deletions(-) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 43c97e658..f7bcad806 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -73,22 +73,20 @@ def test_update_limit_order(limit_buy_order_usdt, limit_sell_order_usdt, fee, ca close_rate: 2.20 quote amount: = 30.0 crypto stake_amount - 1x,-1x: 60.0 quote + 60.0 quote borrowed - 1x: 0 quote - open_value: (amount * open_rate) ± (amount * open_rate * fee) - 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote - amount_closed: - 1x, 3x : amount + 0 quote + open_value: (amount * open_rate) + (amount * open_rate * fee) + 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote close_value: - 1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest - binance,kraken 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 + (amount * close_rate) - (amount * close_rate * fee) - interest + (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 total_profit: - 1x, 3x : close_value - open_value - binance,kraken 1x: 65.835 - 60.15 = 5.685 + close_value - open_value + 65.835 - 60.15 = 5.685 total_profit_ratio: - 1x, 3x : ((close_value/open_value) - 1) * leverage - binance 1x: ((65.835 / 60.15) - 1) * 1 = 0.0945137157107232 + ((close_value/open_value) - 1) * leverage + ((65.835 / 60.15) - 1) * 1 = 0.0945137157107232 """ @@ -283,17 +281,18 @@ def test_update_invalid_order(limit_buy_order_usdt): @pytest.mark.usefixtures("init_persistence") def test_calc_open_trade_value(limit_buy_order_usdt, fee): - # 10 minute limit trade on Binance/Kraken - # fee: 0.25 %, 0.3% quote - # open_rate: 2.00 quote - # amount: = 30.0 crypto - # stake_amount - # 1x, -1x: 60.0 quote - # open_value: (amount * open_rate) ± (amount * open_rate * fee) - # 0.25% fee - # 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote - # 0.3% fee - # 1x, 3x: 30 * 2 + 30 * 2 * 0.003 = 60.18 quote + """ + fee: 0.25 %, 0.3% quote + open_rate: 2.00 quote + amount: = 30.0 crypto + stake_amount + 60.0 quote + open_value: (amount * open_rate) + (amount * open_rate * fee) + 0.25% fee + 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + 0.3% fee + 30 * 2 + 30 * 2 * 0.003 = 60.18 quote + """ trade = Trade( pair='ADA/USDT', stake_amount=60.0, @@ -339,12 +338,10 @@ def test_calc_close_trade_price(limit_buy_order_usdt, limit_sell_order_usdt, fee @pytest.mark.usefixtures("init_persistence") def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee): """ - 10 minute limit trade on Binance/Kraken at 1x, 3x leverage arguments: fee: 0.25% quote 0.30% quote - interest_rate: 0.05% per 4 hrs open_rate: 2.0 quote close_rate: 1.9 quote @@ -352,117 +349,51 @@ def test_calc_profit(limit_buy_order_usdt, limit_sell_order_usdt, fee): 2.2 quote amount: = 30.0 crypto stake_amount - 1x,-1x: 60.0 quote - 3x,-3x: 20.0 quote - hours: 1/6 (10 minutes) - borrowed - 1x: 0 quote - 3x: 40 quote - -1x: 30 crypto - -3x: 30 crypto - time-periods: - kraken: (1 + 1) 4hr_periods = 2 4hr_periods - binance: 1/24 24hr_periods - interest: borrowed * interest_rate * time-periods - 1x : / - binance 3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote - kraken 3x: 40 * 0.0005 * 2 = 0.040 quote - binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto - kraken -1x,-3x: 30 * 0.0005 * 2 = 0.030 crypto - open_value: (amount * open_rate) ± (amount * open_rate * fee) + 60.0 quote + open_value: (amount * open_rate) + (amount * open_rate * fee) 0.0025 fee - 1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote - -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.85 quote + 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote + 30 * 2 - 30 * 2 * 0.0025 = 59.85 quote 0.003 fee: Is only applied to close rate in this test - amount_closed: - 1x, 3x = amount - -1x, -3x = amount + interest - binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto - kraken -1x,-3x: 30 + 0.03 = 30.03 crypto close_value: equations: - 1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest - -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee) + (amount_closed * close_rate) - (amount_closed * close_rate * fee) 2.1 quote - bin,krak 1x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) = 62.8425 - bin 3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.0008333333 = 62.8416666667 - krak 3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.040 = 62.8025 - bin -1x,-3x: (30.000625 * 2.1) + (30.000625 * 2.1 * 0.0025) = 63.15881578125 - krak -1x,-3x: (30.03 * 2.1) + (30.03 * 2.1 * 0.0025) = 63.2206575 + (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) = 62.8425 1.9 quote - bin,krak 1x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) = 56.8575 - bin 3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.0008333333 = 56.85666667 - krak 3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.040 = 56.8175 - bin -1x,-3x: (30.000625 * 1.9) + (30.000625 * 1.9 * 0.0025) = 57.14369046875 - krak -1x,-3x: (30.03 * 1.9) + (30.03 * 1.9 * 0.0025) = 57.1996425 + (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) = 56.8575 2.2 quote - bin,krak 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 - bin 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667 - krak 3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040 = 65.795 - bin -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025) = 66.1663784375 - krak -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025) = 66.231165 + (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) = 65.835 total_profit: equations: - 1x, 3x : close_value - open_value - -1x,-3x: open_value - close_value + close_value - open_value 2.1 quote - binance,kraken 1x: 62.8425 - 60.15 = 2.6925 - binance 3x: 62.84166667 - 60.15 = 2.69166667 - kraken 3x: 62.8025 - 60.15 = 2.6525 - binance -1x,-3x: 59.850 - 63.15881578125 = -3.308815781249997 - kraken -1x,-3x: 59.850 - 63.2206575 = -3.3706575 + 62.8425 - 60.15 = 2.6925 1.9 quote - binance,kraken 1x: 56.8575 - 60.15 = -3.2925 - binance 3x: 56.85666667 - 60.15 = -3.29333333 - kraken 3x: 56.8175 - 60.15 = -3.3325 - binance -1x,-3x: 59.850 - 57.14369046875 = 2.7063095312499996 - kraken -1x,-3x: 59.850 - 57.1996425 = 2.6503575 + 56.8575 - 60.15 = -3.2925 2.2 quote - binance,kraken 1x: 65.835 - 60.15 = 5.685 - binance 3x: 65.83416667 - 60.15 = 5.68416667 - kraken 3x: 65.795 - 60.15 = 5.645 - binance -1x,-3x: 59.850 - 66.1663784375 = -6.316378437499999 - kraken -1x,-3x: 59.850 - 66.231165 = -6.381165 + 65.835 - 60.15 = 5.685 total_profit_ratio: equations: - 1x, 3x : ((close_value/open_value) - 1) * leverage - -1x,-3x: (1 - (close_value/open_value)) * leverage + ((close_value/open_value) - 1) * leverage 2.1 quote - binance,kraken 1x: (62.8425 / 60.15) - 1 = 0.04476309226932673 - binance 3x: ((62.84166667 / 60.15) - 1)*3 = 0.13424771421446402 - kraken 3x: ((62.8025 / 60.15) - 1)*3 = 0.13229426433915248 - binance -1x: 1 - (63.15881578125 / 59.850) = -0.05528514254385963 - binance -3x: (1 - (63.15881578125 / 59.850))*3 = -0.1658554276315789 - kraken -1x: 1 - (63.2206575 / 59.850) = -0.05631842105263152 - kraken -3x: (1 - (63.2206575 / 59.850))*3 = -0.16895526315789455 + (62.8425 / 60.15) - 1 = 0.04476309226932673 1.9 quote - binance,kraken 1x: (56.8575 / 60.15) - 1 = -0.05473815461346632 - binance 3x: ((56.85666667 / 60.15) - 1)*3 = -0.16425602643391513 - kraken 3x: ((56.8175 / 60.15) - 1)*3 = -0.16620947630922667 - binance -1x: 1 - (57.14369046875 / 59.850) = 0.045218204365079395 - binance -3x: (1 - (57.14369046875 / 59.850))*3 = 0.13565461309523819 - kraken -1x: 1 - (57.1996425 / 59.850) = 0.04428333333333334 - kraken -3x: (1 - (57.1996425 / 59.850))*3 = 0.13285000000000002 + (56.8575 / 60.15) - 1 = -0.05473815461346632 2.2 quote - binance,kraken 1x: (65.835 / 60.15) - 1 = 0.0945137157107232 - binance 3x: ((65.83416667 / 60.15) - 1)*3 = 0.2834995845386534 - kraken 3x: ((65.795 / 60.15) - 1)*3 = 0.2815461346633419 - binance -1x: 1 - (66.1663784375 / 59.850) = -0.1055368159983292 - binance -3x: (1 - (66.1663784375 / 59.850))*3 = -0.3166104479949876 - kraken -1x: 1 - (66.231165 / 59.850) = -0.106619298245614 - kraken -3x: (1 - (66.231165 / 59.850))*3 = -0.319857894736842 - fee: 0.003, 1x + (65.835 / 60.15) - 1 = 0.0945137157107232 + fee: 0.003 close_value: 2.1 quote: (30.00 * 2.1) - (30.00 * 2.1 * 0.003) = 62.811 1.9 quote: (30.00 * 1.9) - (30.00 * 1.9 * 0.003) = 56.829 2.2 quote: (30.00 * 2.2) - (30.00 * 2.2 * 0.003) = 65.802 total_profit - fee: 0.003, 1x + fee: 0.003 2.1 quote: 62.811 - 60.15 = 2.6610000000000014 1.9 quote: 56.829 - 60.15 = -3.320999999999998 2.2 quote: 65.802 - 60.15 = 5.652000000000008 total_profit_ratio - fee: 0.003, 1x + fee: 0.003 2.1 quote: (62.811 / 60.15) - 1 = 0.04423940149625927 1.9 quote: (56.829 / 60.15) - 1 = -0.05521197007481293 2.2 quote: (65.802 / 60.15) - 1 = 0.09396508728179565 From 056bc93bc6a6772dc55ffd993e08ec571b6d80c2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Aug 2021 19:16:26 +0200 Subject: [PATCH 207/519] backtesting needs startup_candle_count fixes informative-pair loading being different between --strategy-list and --strategy. --- freqtrade/optimize/backtesting.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 628289b7f..45e60e013 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -122,6 +122,8 @@ class Backtesting: # Get maximum required startup period self.required_startup = max([strat.startup_candle_count for strat in self.strategylist]) + # Add maximum startup candle count to configuration for informative pairs support + self.config['startup_candle_count'] = self.required_startup self.exchange.validate_required_startup_candles(self.required_startup, self.timeframe) self.progress = BTProgress() From 059c32b0675607986af0dc2c00738d3b4106bcce Mon Sep 17 00:00:00 2001 From: sauces1313 Date: Mon, 2 Aug 2021 02:49:49 +0000 Subject: [PATCH 208/519] Check for and default to 'None' --- .../plugins/pairlist/rangestabilityfilter.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index 105568f17..63184c726 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -26,7 +26,7 @@ class RangeStabilityFilter(IPairList): self._days = pairlistconfig.get('lookback_days', 10) self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01) - self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', 0.99) + self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', None) self._refresh_period = pairlistconfig.get('refresh_period', 1440) self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) @@ -106,14 +106,16 @@ class RangeStabilityFilter(IPairList): f"which is below the threshold of {self._min_rate_of_change}.", logger.info) result = False - if pct_change <= self._max_rate_of_change: - result = True - else: - self.log_once(f"Removed {pair} from whitelist, because rate of change " - f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, " - f"which is above the threshold of {self._max_rate_of_change}.", - logger.info) - result = False + if self._max_rate_of_change: + if pct_change <= self._max_rate_of_change: + result = True + else: + self.log_once( + f"Removed {pair} from whitelist, because rate of change " + f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, " + f"which is above the threshold of {self._max_rate_of_change}.", + logger.info) + result = False self._pair_cache[pair] = result return result From b3f057e7c0dbf9a45c1889b4a57a57e7b5646f1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:01:13 +0000 Subject: [PATCH 209/519] Bump isort from 5.9.2 to 5.9.3 Bumps [isort](https://github.com/pycqa/isort) from 5.9.2 to 5.9.3. - [Release notes](https://github.com/pycqa/isort/releases) - [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md) - [Commits](https://github.com/pycqa/isort/compare/5.9.2...5.9.3) --- updated-dependencies: - dependency-name: isort dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 537936a86..70e61e71a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -13,7 +13,7 @@ pytest-asyncio==0.15.1 pytest-cov==2.12.1 pytest-mock==3.6.1 pytest-random-order==1.0.4 -isort==5.9.2 +isort==5.9.3 # Convert jupyter notebooks to markdown documents nbconvert==6.1.0 From 4f05d31b942f5eacc108ad39d91cd2f81f65aa54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:01:19 +0000 Subject: [PATCH 210/519] Bump ccxt from 1.53.72 to 1.54.24 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.53.72 to 1.54.24. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.53.72...1.54.24) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3be66bff7..bd1510380 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.1 pandas==1.3.1 -ccxt==1.53.72 +ccxt==1.54.24 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 4afcea9a1ccdadbb124363ef1009e0eb8c70514b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:01:25 +0000 Subject: [PATCH 211/519] Bump fastapi from 0.67.0 to 0.68.0 Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.67.0 to 0.68.0. - [Release notes](https://github.com/tiangolo/fastapi/releases) - [Commits](https://github.com/tiangolo/fastapi/compare/0.67.0...0.68.0) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3be66bff7..15040270e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ python-rapidjson==1.4 sdnotify==0.3.2 # API Server -fastapi==0.67.0 +fastapi==0.68.0 uvicorn==0.14.0 pyjwt==2.1.0 aiofiles==0.7.0 From 849b8197a93de3ea9b5a9d796ddfd74095923c78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:01:30 +0000 Subject: [PATCH 212/519] Bump scipy from 1.7.0 to 1.7.1 Bumps [scipy](https://github.com/scipy/scipy) from 1.7.0 to 1.7.1. - [Release notes](https://github.com/scipy/scipy/releases) - [Commits](https://github.com/scipy/scipy/compare/v1.7.0...v1.7.1) --- updated-dependencies: - dependency-name: scipy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 83e23e3ec..d7f22634b 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -2,7 +2,7 @@ -r requirements.txt # Required for hyperopt -scipy==1.7.0 +scipy==1.7.1 scikit-learn==0.24.2 scikit-optimize==0.8.1 filelock==3.0.12 From 7fd3fc98c0135990f8d29e08aab0e8d8893bfd1a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:01:35 +0000 Subject: [PATCH 213/519] Bump types-requests from 2.25.0 to 2.25.1 Bumps [types-requests](https://github.com/python/typeshed) from 2.25.0 to 2.25.1. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 537936a86..e260f8fb8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -21,5 +21,5 @@ nbconvert==6.1.0 # mypy types types-cachetools==0.1.9 types-filelock==0.1.4 -types-requests==2.25.0 +types-requests==2.25.1 types-tabulate==0.1.1 From 3a19e1610de5aef8468e475e23a2d033b3303f05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:01:38 +0000 Subject: [PATCH 214/519] Bump mkdocs-material from 7.2.1 to 7.2.2 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.2.1 to 7.2.2. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.2.1...7.2.2) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 6a904c951..047821f2d 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.2.1 +mkdocs-material==7.2.2 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From b63eda3a2b7e694a32d524c0413a30f737ef9d8d Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Aug 2021 07:15:09 +0200 Subject: [PATCH 215/519] Some minor cleanup and improved test coverage --- freqtrade/plugins/pairlist/rangestabilityfilter.py | 7 +++++-- tests/plugins/test_pairlist.py | 12 +++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index 63184c726..ef7f2cbcb 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -51,9 +51,12 @@ class RangeStabilityFilter(IPairList): """ Short whitelist method description - used for startup-messages """ + max_rate_desc = "" + if self._max_rate_of_change: + max_rate_desc = (f" and above {self._max_rate_of_change}") return (f"{self.name} - Filtering pairs with rate of change below " - f"{self._min_rate_of_change} and above " - f"{self._max_rate_of_change} over the last {plural(self._days, 'day')}.") + f"{self._min_rate_of_change}{max_rate_desc} over the " + f"last {plural(self._days, 'day')}.") def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index a3f5b0875..5f0701a22 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -425,8 +425,12 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): "USDT", ['NANO/USDT']), ([{"method": "StaticPairList"}, {"method": "RangeStabilityFilter", "lookback_days": 10, - "min_rate_of_change": 0.01, "max_rate_of_change": 0.99, "refresh_period": 1440}], + "min_rate_of_change": 0.01, "refresh_period": 1440}], "BTC", ['ETH/BTC', 'TKN/BTC', 'HOT/BTC']), + ([{"method": "StaticPairList"}, + {"method": "RangeStabilityFilter", "lookback_days": 10, + "max_rate_of_change": 0.01, "refresh_period": 1440}], + "BTC", []), # All removed because of max_rate_of_change being 0.017 ([{"method": "StaticPairList"}, {"method": "VolatilityFilter", "lookback_days": 3, "min_volatility": 0.002, "max_volatility": 0.004, "refresh_period": 1440}], @@ -985,6 +989,12 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo None, "PriceFilter requires max_value to be >= 0" ), # OperationalException expected + ({"method": "RangeStabilityFilter", "lookback_days": 10, + "min_rate_of_change": 0.01}, + "[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below " + "0.01 over the last days.'}]", + None + ), ({"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01, "max_rate_of_change": 0.99}, "[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below " From 3c5f06d5c054f939b53b6a0e6ad61ef6af7092fa Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Aug 2021 08:31:33 +0200 Subject: [PATCH 216/519] Update tests/exchange/test_exchange.py --- tests/exchange/test_exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index bacb7edbb..a2ce09eab 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2190,7 +2190,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name): assert exchange.cancel_order(order_id='123', pair='TKN/BTC') == {} assert exchange.cancel_stoploss_order(order_id='123', pair='TKN/BTC') == {} - order = exchange.create_order('ETH/BTC', 'limit', "sell", 5, 0.55, 'gtc') + order = exchange.create_order('ETH/BTC', 'limit', "buy", 5, 0.55, 'gtc') cancel_order = exchange.cancel_order(order_id=order['id'], pair='ETH/BTC') assert order['id'] == cancel_order['id'] From c981641441fc329f788e31e599629ee16df9061b Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Aug 2021 20:17:58 +0200 Subject: [PATCH 217/519] Don't fail if strategy doesn't contain sell signal --- freqtrade/strategy/interface.py | 8 +++++--- tests/strategy/test_interface.py | 25 +++++++++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index f10a12fa9..6f3e047eb 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -483,8 +483,6 @@ class IStrategy(ABC, HyperStrategyMixin): message = "No dataframe returned (return statement missing?)." elif 'buy' not in dataframe: message = "Buy column not set." - elif 'sell' not in dataframe: - message = "Sell column not set." elif df_len != len(dataframe): message = message_template.format("length") elif df_close != dataframe["close"].iloc[-1]: @@ -531,7 +529,11 @@ class IStrategy(ABC, HyperStrategyMixin): return False, False, None buy = latest[SignalType.BUY.value] == 1 - sell = latest[SignalType.SELL.value] == 1 + + sell = False + if SignalType.SELL.value in latest: + sell = latest[SignalType.SELL.value] == 1 + buy_tag = latest.get(SignalTagType.BUY_TAG.value, None) logger.debug('trigger: %s (pair=%s) buy=%s sell=%s', diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 751f08344..d8c87506c 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -126,6 +126,27 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) +def test_get_signal_no_sell_column(default_conf, mocker, caplog, ohlcv_history): + # default_conf defines a 5m interval. we check interval * 2 + 5m + # this is necessary as the last candle is removed (partial candles) by default + ohlcv_history.loc[1, 'date'] = arrow.utcnow() + # Take a copy to correctly modify the call + mocked_history = ohlcv_history.copy() + # Intentionally don't set sell column + # mocked_history['sell'] = 0 + mocked_history['buy'] = 0 + mocked_history.loc[1, 'buy'] = 1 + + caplog.set_level(logging.INFO) + mocker.patch.object(_STRATEGY, 'assert_df') + + assert (True, False, None) == _STRATEGY.get_signal( + 'xyz', + default_conf['timeframe'], + mocked_history + ) + + def test_ignore_expired_candle(default_conf): default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) @@ -197,10 +218,6 @@ def test_assert_df(ohlcv_history, caplog): match="Buy column not set"): _STRATEGY.assert_df(ohlcv_history.drop('buy', axis=1), len(ohlcv_history), ohlcv_history.loc[df_len, 'close'], ohlcv_history.loc[0, 'date']) - with pytest.raises(StrategyError, - match="Sell column not set"): - _STRATEGY.assert_df(ohlcv_history.drop('sell', axis=1), len(ohlcv_history), - ohlcv_history.loc[df_len, 'close'], ohlcv_history.loc[0, 'date']) _STRATEGY.disable_dataframe_checks = True caplog.clear() From e70a742005e286ecb7e1385ec6af97caf6b3a363 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Aug 2021 21:12:10 +0200 Subject: [PATCH 218/519] Reorder space methods in hyperopt --- freqtrade/optimize/hyperopt.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index cb6884385..7022f67c5 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -264,17 +264,14 @@ class Hyperopt: def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict: """ - Used Optimize function. Called once per epoch to optimize whatever is configured. + Used Optimize function. + Called once per epoch to optimize whatever is configured. Keep this function as optimized as possible! """ backtest_start_time = datetime.now(timezone.utc) params_dict = self._get_params_dict(self.dimensions, raw_params) # Apply parameters - if HyperoptTools.has_space(self.config, 'roi'): - self.backtesting.strategy.minimal_roi = ( # type: ignore - self.custom_hyperopt.generate_roi_table(params_dict)) - if HyperoptTools.has_space(self.config, 'buy'): self.backtesting.strategy.advise_buy = ( # type: ignore self.custom_hyperopt.buy_strategy_generator(params_dict)) @@ -283,6 +280,10 @@ class Hyperopt: self.backtesting.strategy.advise_sell = ( # type: ignore self.custom_hyperopt.sell_strategy_generator(params_dict)) + if HyperoptTools.has_space(self.config, 'roi'): + self.backtesting.strategy.minimal_roi = ( # type: ignore + self.custom_hyperopt.generate_roi_table(params_dict)) + if HyperoptTools.has_space(self.config, 'stoploss'): self.backtesting.strategy.stoploss = params_dict['stoploss'] From 4ab03f7e378bc4dd6e2031c1b199180fdb70e3d3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Aug 2021 21:17:56 +0200 Subject: [PATCH 219/519] Don't load fallback methods for autohyperopt --- freqtrade/optimize/hyperopt.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 7022f67c5..0ed3d4cbf 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -102,16 +102,17 @@ class Hyperopt: self.num_epochs_saved = 0 self.current_best_epoch: Optional[Dict[str, Any]] = None - # Populate functions here (hasattr is slow so should not be run during "regular" operations) - if hasattr(self.custom_hyperopt, 'populate_indicators'): - self.backtesting.strategy.advise_indicators = ( # type: ignore - self.custom_hyperopt.populate_indicators) # type: ignore - if hasattr(self.custom_hyperopt, 'populate_buy_trend'): - self.backtesting.strategy.advise_buy = ( # type: ignore - self.custom_hyperopt.populate_buy_trend) # type: ignore - if hasattr(self.custom_hyperopt, 'populate_sell_trend'): - self.backtesting.strategy.advise_sell = ( # type: ignore - self.custom_hyperopt.populate_sell_trend) # type: ignore + if not self.auto_hyperopt: + # Populate "fallback" functions here (hasattr is slow so should not be run during "regular" operations) + if hasattr(self.custom_hyperopt, 'populate_indicators'): + self.backtesting.strategy.advise_indicators = ( # type: ignore + self.custom_hyperopt.populate_indicators) # type: ignore + if hasattr(self.custom_hyperopt, 'populate_buy_trend'): + self.backtesting.strategy.advise_buy = ( # type: ignore + self.custom_hyperopt.populate_buy_trend) # type: ignore + if hasattr(self.custom_hyperopt, 'populate_sell_trend'): + self.backtesting.strategy.advise_sell = ( # type: ignore + self.custom_hyperopt.populate_sell_trend) # type: ignore # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set if self.config.get('use_max_market_positions', True): From dfc17f2bd1f3779c68475dde405c314b83a85f90 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Aug 2021 07:21:11 +0200 Subject: [PATCH 220/519] Fix ci failure --- freqtrade/optimize/hyperopt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 0ed3d4cbf..a69e5a5a2 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -103,7 +103,8 @@ class Hyperopt: self.current_best_epoch: Optional[Dict[str, Any]] = None if not self.auto_hyperopt: - # Populate "fallback" functions here (hasattr is slow so should not be run during "regular" operations) + # Populate "fallback" functions here + # (hasattr is slow so should not be run during "regular" operations) if hasattr(self.custom_hyperopt, 'populate_indicators'): self.backtesting.strategy.advise_indicators = ( # type: ignore self.custom_hyperopt.populate_indicators) # type: ignore From f11f5d17e91e05e2f2ac7729ccf0b4f29a352bd6 Mon Sep 17 00:00:00 2001 From: axel Date: Sat, 31 Jul 2021 00:05:45 -0400 Subject: [PATCH 221/519] add feature custom entry price for live --- docs/strategy-advanced.md | 29 +++++++++- freqtrade/constants.py | 2 + freqtrade/freqtradebot.py | 73 ++++++++++++++++++++++++ freqtrade/resolvers/strategy_resolver.py | 1 + freqtrade/rpc/api_server/api_schemas.py | 1 + freqtrade/rpc/rpc.py | 1 + freqtrade/strategy/interface.py | 58 +++++++++++++++++++ tests/strategy/test_interface.py | 28 +++++++++ tests/test_freqtradebot.py | 30 ++++++++++ 9 files changed, 222 insertions(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 0704473fb..8c99f1d2e 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -357,6 +357,33 @@ See [Dataframe access](#dataframe-access) for more information about dataframe u --- +## Custom order entry price rules + +By default, freqtrade use the orderbook to automatically set an order price, you also have the option to create custom order prices based on your strategy. + +You can use this feature by setting the `use_custom_entry_price` option to `true` in config and creating a custom_entry_price function. + +### Custom order entry price exemple +``` python +from datetime import datetime, timedelta, timezone +from freqtrade.persistence import Trade + +class AwesomeStrategy(IStrategy): + + # ... populate_* methods + + def custom_entry_price(self, pair: str, current_time: datetime, + current_rate, **kwargs) -> float: + + dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, + timeframe=self.timeframe) + entryprice = dataframe['bollinger_10_lowerband'].iat[-1] + + return entryprice + +``` + + ## Custom order timeout rules Simple, time-based order-timeouts can be configured either via strategy or in the configuration in the `unfilledtimeout` section. @@ -366,7 +393,7 @@ However, freqtrade also offers a custom callback for both order types, which all !!! Note Unfilled order timeouts are not relevant during backtesting or hyperopt, and are only relevant during real (live) trading. Therefore these methods are only called in these circumstances. -### Custom order timeout example +## Custom order timeout example A simple example, which applies different unfilled-timeouts depending on the price of the asset can be seen below. It applies a tight timeout for higher priced assets, while allowing more time to fill on cheap coins. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index b48644c58..f118ac700 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -433,6 +433,8 @@ SCHEMA_MINIMAL_REQUIRED = [ CANCEL_REASON = { "TIMEOUT": "cancelled due to timeout", + "ENTRYPRICECHANGED": "Custom entry price changed", + "EXITPRICECHANGED": "Custom exit price changed", "PARTIALLY_FILLED_KEEP_OPEN": "partially filled - keeping order open", "PARTIALLY_FILLED": "partially filled", "FULLY_CANCELLED": "fully cancelled", diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 09aa06adf..4abc7e508 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -169,6 +169,7 @@ class FreqtradeBot(LoggingMixin): with self._sell_lock: # Check and handle any timed out open orders self.check_handle_timedout() + self.check_handle_custom_entryprice_outdated() # Protect from collisions with forcesell. # Without this, freqtrade my try to recreate stoploss_on_exchange orders @@ -480,6 +481,14 @@ class FreqtradeBot(LoggingMixin): else: # Calculate price buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy") + if self.config.get('use_custom_entry_price', False): + buy_rate = self.exchange.get_rate(pair, refresh=True, side="buy") + custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, + default_retval=stake_amount)( + pair=pair, current_time=datetime.now(timezone.utc), + current_rate=buy_rate) + + buy_limit_requested = custom_entry_price if not buy_limit_requested: raise PricingError('Could not determine buy price.') @@ -911,6 +920,70 @@ class FreqtradeBot(LoggingMixin): order=order))): self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['TIMEOUT']) + def _check_entryprice_outdated(self, side: str, order: dict) -> bool: + """ + Check if entry price is outdated by comparing it to the new prefered entry price + , and if the order is still open and price outdated + """ + #print("check_entryprice_outdated") + if self.config.get('use_custom_entry_price', False): + order_prefered_entry_price = order['price'] # order['trade'] + + #print(order) + #order_open_rate_requested = order.trade['open_rate_requested'] + #print("order_trade_object : {}".format(order['trade'])) + + # get pep from strategy data provider + pair = order['symbol'] + old_prefered_entry_price = order_prefered_entry_price + #new_prefered_entry_price = self.strategy.custom_info[pair]['pep_long'].iloc[-1] #buy_limit_requested + new_prefered_entry_price = self.strategy.entryprice + + old_prefered_entry_price_rounded = self.exchange.price_to_precision(pair, order_prefered_entry_price) + new_prefered_entry_price_rounded = self.exchange.price_to_precision(pair, new_prefered_entry_price) + + if old_prefered_entry_price_rounded != new_prefered_entry_price_rounded: + print("order['symbol']: {}".format(order['symbol'])) + print("new_prefered_entry_price: {}, old_prefered_entry_price: {}".format(new_prefered_entry_price, old_prefered_entry_price)) + print("rounded new pep: {}, rounded old pep: {}".format(new_prefered_entry_price_rounded, old_prefered_entry_price_rounded)) + print("Delta in prefered entry price, order to cancel") + return True + else: + return False + else: + return False + + def check_handle_custom_entryprice_outdated(self) -> None: + """ + Check if any orders prefered entryprice change and cancel if necessary + :return: None + """ + + for trade in Trade.get_open_order_trades(): + try: + if not trade.open_order_id: + continue + order = self.exchange.fetch_order(trade.open_order_id, trade.pair) + except (ExchangeError): + logger.info('Cannot query order for %s due to %s', trade, traceback.format_exc()) + continue + + fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order) + + # Refresh entryprice value if order is open + if (order['status'] == 'open'): + self.strategy.entryprice = strategy_safe_wrapper(self.strategy.custom_entry_price)( + pair=trade.pair, current_time=datetime.now(timezone.utc), + current_rate=trade.open_rate_requested) + + if (order['side'] == 'buy' and (order['status'] == 'open') and ( + self._check_entryprice_outdated('buy', order))): + self.handle_cancel_buy(trade, order, constants.CANCEL_REASON['ENTRYPRICECHANGED']) + + elif (order['side'] == 'sell' and (order['status'] == 'open') and ( + self._check_entryprice_outdated('sell', order))): + self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['EXITPRICECHANGED']) + def cancel_all_open_orders(self) -> None: """ Cancel all orders that are currently open diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 1239b78b3..3248ed385 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -79,6 +79,7 @@ class StrategyResolver(IResolver): ("trailing_stop_positive_offset", 0.0), ("trailing_only_offset_is_reached", None), ("use_custom_stoploss", None), + ("use_custom_entry_price", None), ("process_only_new_candles", None), ("order_types", None), ("order_time_in_force", None), diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 318762136..c66a01490 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -129,6 +129,7 @@ class ShowConfig(BaseModel): trailing_stop_positive_offset: Optional[float] trailing_only_offset_is_reached: Optional[bool] use_custom_stoploss: Optional[bool] + use_custom_entry_price: Optional[bool] timeframe: Optional[str] timeframe_ms: int timeframe_min: int diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 902975fde..2983c1dfa 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -116,6 +116,7 @@ class RPC: 'trailing_stop_positive_offset': config.get('trailing_stop_positive_offset'), 'trailing_only_offset_is_reached': config.get('trailing_only_offset_is_reached'), 'use_custom_stoploss': config.get('use_custom_stoploss'), + 'use_custom_entry_price': config.get('use_custom_entry_price'), 'bot_name': config.get('bot_name', 'freqtrade'), 'timeframe': config.get('timeframe'), 'timeframe_ms': timeframe_to_msecs(config['timeframe'] diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6f3e047eb..af5be2711 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -69,6 +69,10 @@ class IStrategy(ABC, HyperStrategyMixin): # associated stoploss stoploss: float + # custom order price + entryprice: Optional[float] = None + exitprice: Optional[float] = None + # trailing stoploss trailing_stop: bool = False trailing_stop_positive: Optional[float] = None @@ -280,6 +284,24 @@ class IStrategy(ABC, HyperStrategyMixin): """ return self.stoploss + def custom_entry_price(self, pair: str, current_time: datetime, current_rate: float, + **kwargs) -> float: + """ + Custom entry price logic, returning the new entry price. + + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ + + When not implemented by a strategy, returns None, orderbook is used to set entry price + Only called when use_custom_entry_price is set to True. + + :param pair: Pair that's currently analyzed + :param current_time: datetime object, containing the current datetime + :param current_rate: Rate, calculated based on pricing settings in ask_strategy. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New entry price value if provided + """ + return self.entryprice + def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]: """ @@ -635,6 +657,42 @@ class IStrategy(ABC, HyperStrategyMixin): # logger.debug(f"{trade.pair} - No sell signal.") return SellCheckTuple(sell_type=SellType.NONE) + def entry_price_reached(self, pair: str, current_rate: float, + current_time: datetime, low: float = None, + high: float = None, side: str = "long") -> bool: + """ + Based on current candle low ,decides if entry price was reached + :param current_rate: current rate + :param low: Low value of this candle, only set in backtesting + :param high: High value of this candle, only set in backtesting + """ + + if self.use_custom_entry_price: + entry_price_value = strategy_safe_wrapper(self.custom_entry_price, default_retval=None + )(pair=pair, + current_time=current_time, + current_rate=current_rate) + # Sanity check - error cases will return None + if side == "long": + if entry_price_value > low: + return True + else: + logger.info(f"Entry failed because entry price {entry_price_value} \ + higher than candle low in long side") + return False + + elif side == "short": + if entry_price_value < high: + return True + else: + logger.info(f"Entry failed because entry price {entry_price_value} \ + higher than candle high in short side") + return False + + else: + logger.warning("CustomEntryPrice function did not return valid entry price") + return False + def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime, current_profit: float, force_stoploss: float, low: float = None, diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index d8c87506c..7102c1a49 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -431,6 +431,34 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili strategy.custom_stoploss = original_stopvalue +@pytest.mark.parametrize( + 'current_rate, exp_custom_entry', 'expected_result', 'use_custom_entry_price', 'custom_entry' [ + # Profit, adjusted stoploss(absolute), profit for 2nd call, enable trailing, + # enable custom stoploss, expected after 1st call, expected after 2nd call + (99, 98, False, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # custom_entry_price pice - (price * 0.01) + (97.8, 98, True, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # price stayed under entry price + (97.8, 98, True, True, lambda current_rate, **kwargs: current_rate + (current_rate * 0.01)), # entry price over current price + (99.9, 98, True, False, None), # feature not activated + ]) +def test_entry_price_reached(default_conf, current_rate, exp_custom_entry, candle_ohlc, + expected_result, use_custom_entry_price, custom_entry) -> None: + + default_conf.update({'strategy': 'DefaultStrategy'}) + + + strategy = StrategyResolver.load_strategy(default_conf) + + strategy.use_custom_entry_price = use_custom_entry_price + custom_entry_price = custom_entry + if use_custom_entry_price: + strategy.custom_entry_price = custom_entry(current_rate) + + now = arrow.utcnow().datetime + entry_flag = strategy.entry_price_reached(current_rate=current_rate, low= None, high=None) + + + pass + def test_custom_sell(default_conf, fee, caplog) -> None: default_conf.update({'strategy': 'DefaultStrategy'}) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b1e02a99b..f6b8f5544 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -904,6 +904,36 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order with pytest.raises(PricingError, match="Could not determine buy price."): freqtrade.execute_buy(pair, stake_amount) +def test_execute_buy_custom_entry_price(mocker, default_conf, fee, limit_buy_order, limit_buy_order_open) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + default_conf.update({'use_custom_entry_price': True}) + freqtrade = FreqtradeBot(default_conf) + freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False) + stake_amount = 3 + bid = 2304 + buy_rate_mock = MagicMock(return_value=bid) + buy_mm = MagicMock(return_value=limit_buy_order_open) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_rate=buy_rate_mock, + fetch_ticker=MagicMock(return_value={ + 'bid': 2304, + 'ask': 0.00001173, + 'last': 2304 + }), + buy=buy_mm, + get_min_pair_stake_amount=MagicMock(return_value=1), + get_fee=fee, + ) + pair = 'ETH/USDT' + + # Test calling with custom entry price option activated + limit_buy_order_open['id'] = '55' + assert freqtrade.execute_buy(pair, stake_amount) + # Make sure get_rate called to provide current_rate param to custom_entry_price + assert buy_rate_mock.call_count == 1 + def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) From 5284112b69828b91d42a398c2d6cad9c1a151121 Mon Sep 17 00:00:00 2001 From: axel Date: Sun, 1 Aug 2021 02:09:59 -0400 Subject: [PATCH 222/519] fix in custom entry function output,remove changes related to outdated prices, doc exemple minor changes --- docs/strategy-advanced.md | 4 +- freqtrade/freqtradebot.py | 65 --------------------------------- freqtrade/strategy/interface.py | 10 ++--- 3 files changed, 5 insertions(+), 74 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 8c99f1d2e..a8e54bbcf 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -373,7 +373,7 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods def custom_entry_price(self, pair: str, current_time: datetime, - current_rate, **kwargs) -> float: + proposed_rate, **kwargs) -> float: dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) @@ -393,7 +393,7 @@ However, freqtrade also offers a custom callback for both order types, which all !!! Note Unfilled order timeouts are not relevant during backtesting or hyperopt, and are only relevant during real (live) trading. Therefore these methods are only called in these circumstances. -## Custom order timeout example +### Custom order timeout example A simple example, which applies different unfilled-timeouts depending on the price of the asset can be seen below. It applies a tight timeout for higher priced assets, while allowing more time to fill on cheap coins. diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4abc7e508..c8e930a36 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -169,7 +169,6 @@ class FreqtradeBot(LoggingMixin): with self._sell_lock: # Check and handle any timed out open orders self.check_handle_timedout() - self.check_handle_custom_entryprice_outdated() # Protect from collisions with forcesell. # Without this, freqtrade my try to recreate stoploss_on_exchange orders @@ -920,70 +919,6 @@ class FreqtradeBot(LoggingMixin): order=order))): self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['TIMEOUT']) - def _check_entryprice_outdated(self, side: str, order: dict) -> bool: - """ - Check if entry price is outdated by comparing it to the new prefered entry price - , and if the order is still open and price outdated - """ - #print("check_entryprice_outdated") - if self.config.get('use_custom_entry_price', False): - order_prefered_entry_price = order['price'] # order['trade'] - - #print(order) - #order_open_rate_requested = order.trade['open_rate_requested'] - #print("order_trade_object : {}".format(order['trade'])) - - # get pep from strategy data provider - pair = order['symbol'] - old_prefered_entry_price = order_prefered_entry_price - #new_prefered_entry_price = self.strategy.custom_info[pair]['pep_long'].iloc[-1] #buy_limit_requested - new_prefered_entry_price = self.strategy.entryprice - - old_prefered_entry_price_rounded = self.exchange.price_to_precision(pair, order_prefered_entry_price) - new_prefered_entry_price_rounded = self.exchange.price_to_precision(pair, new_prefered_entry_price) - - if old_prefered_entry_price_rounded != new_prefered_entry_price_rounded: - print("order['symbol']: {}".format(order['symbol'])) - print("new_prefered_entry_price: {}, old_prefered_entry_price: {}".format(new_prefered_entry_price, old_prefered_entry_price)) - print("rounded new pep: {}, rounded old pep: {}".format(new_prefered_entry_price_rounded, old_prefered_entry_price_rounded)) - print("Delta in prefered entry price, order to cancel") - return True - else: - return False - else: - return False - - def check_handle_custom_entryprice_outdated(self) -> None: - """ - Check if any orders prefered entryprice change and cancel if necessary - :return: None - """ - - for trade in Trade.get_open_order_trades(): - try: - if not trade.open_order_id: - continue - order = self.exchange.fetch_order(trade.open_order_id, trade.pair) - except (ExchangeError): - logger.info('Cannot query order for %s due to %s', trade, traceback.format_exc()) - continue - - fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order) - - # Refresh entryprice value if order is open - if (order['status'] == 'open'): - self.strategy.entryprice = strategy_safe_wrapper(self.strategy.custom_entry_price)( - pair=trade.pair, current_time=datetime.now(timezone.utc), - current_rate=trade.open_rate_requested) - - if (order['side'] == 'buy' and (order['status'] == 'open') and ( - self._check_entryprice_outdated('buy', order))): - self.handle_cancel_buy(trade, order, constants.CANCEL_REASON['ENTRYPRICECHANGED']) - - elif (order['side'] == 'sell' and (order['status'] == 'open') and ( - self._check_entryprice_outdated('sell', order))): - self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['EXITPRICECHANGED']) - def cancel_all_open_orders(self) -> None: """ Cancel all orders that are currently open diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index af5be2711..401934f7a 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -69,10 +69,6 @@ class IStrategy(ABC, HyperStrategyMixin): # associated stoploss stoploss: float - # custom order price - entryprice: Optional[float] = None - exitprice: Optional[float] = None - # trailing stoploss trailing_stop: bool = False trailing_stop_positive: Optional[float] = None @@ -284,7 +280,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ return self.stoploss - def custom_entry_price(self, pair: str, current_time: datetime, current_rate: float, + def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, **kwargs) -> float: """ Custom entry price logic, returning the new entry price. @@ -296,11 +292,11 @@ class IStrategy(ABC, HyperStrategyMixin): :param pair: Pair that's currently analyzed :param current_time: datetime object, containing the current datetime - :param current_rate: Rate, calculated based on pricing settings in ask_strategy. + :param proposed_rate: Rate, calculated based on pricing settings in ask_strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: New entry price value if provided """ - return self.entryprice + return proposed_rate def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]: From 3d8c3ffd38dc8f08edf91e11ba19d57ac6765125 Mon Sep 17 00:00:00 2001 From: axel Date: Sun, 1 Aug 2021 02:21:23 -0400 Subject: [PATCH 223/519] fix syntax error in unit test --- tests/strategy/test_interface.py | 41 ++++++++++---------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 7102c1a49..f973c5bf7 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -38,20 +38,15 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): mocked_history['buy'] = 0 mocked_history.loc[1, 'sell'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, None) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, None) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 0 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, None) - mocked_history.loc[1, 'sell'] = 0 - mocked_history.loc[1, 'buy'] = 1 - mocked_history.loc[1, 'buy_tag'] = 'buy_signal_01' - - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, 'buy_signal_01') + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False) def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): @@ -68,21 +63,15 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False, None) == _STRATEGY.get_signal( - 'foo', default_conf['timeframe'], DataFrame() - ) + assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False, None) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) + assert (False, False) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False, None) == _STRATEGY.get_signal( - 'baz', - default_conf['timeframe'], - DataFrame([]) - ) + assert (False, False) == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) assert log_has('Empty candle (OHLCV) data for pair baz', caplog) @@ -118,11 +107,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False, None) == _STRATEGY.get_signal( - 'xyz', - default_conf['timeframe'], - mocked_history - ) + assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) @@ -432,13 +417,13 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili @pytest.mark.parametrize( - 'current_rate, exp_custom_entry', 'expected_result', 'use_custom_entry_price', 'custom_entry' [ - # Profit, adjusted stoploss(absolute), profit for 2nd call, enable trailing, + 'current_rate, expected_result, use_custom_entry_price, custom_entry', [ + # current rate, expected result value, profit for 2nd call, enable trailing, # enable custom stoploss, expected after 1st call, expected after 2nd call - (99, 98, False, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # custom_entry_price pice - (price * 0.01) - (97.8, 98, True, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # price stayed under entry price - (97.8, 98, True, True, lambda current_rate, **kwargs: current_rate + (current_rate * 0.01)), # entry price over current price - (99.9, 98, True, False, None), # feature not activated + (99, False, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # custom_entry_price pice - (price * 0.01) + (97.8, True, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # price stayed under entry price + (97.8, True, True, lambda current_rate, **kwargs: current_rate + (current_rate * 0.01)), # entry price over current price + (99.9, True, False, None), # feature not activated ]) def test_entry_price_reached(default_conf, current_rate, exp_custom_entry, candle_ohlc, expected_result, use_custom_entry_price, custom_entry) -> None: From d9c9b7d7fc6449d60c451ff8e3eb72726e16a9ea Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 16:02:10 -0400 Subject: [PATCH 224/519] restore interface test file --- tests/strategy/test_interface.py | 57 ++++++++++++-------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index f973c5bf7..d8c87506c 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -38,15 +38,20 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history): mocked_history['buy'] = 0 mocked_history.loc[1, 'sell'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 1 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, None) mocked_history.loc[1, 'sell'] = 0 mocked_history.loc[1, 'buy'] = 0 - assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False) + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, None) + mocked_history.loc[1, 'sell'] = 0 + mocked_history.loc[1, 'buy'] = 1 + mocked_history.loc[1, 'buy_tag'] = 'buy_signal_01' + + assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, 'buy_signal_01') def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): @@ -63,15 +68,21 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_empty(default_conf, mocker, caplog): - assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame()) + assert (False, False, None) == _STRATEGY.get_signal( + 'foo', default_conf['timeframe'], DataFrame() + ) assert log_has('Empty candle (OHLCV) data for pair foo', caplog) caplog.clear() - assert (False, False) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) + assert (False, False, None) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None) assert log_has('Empty candle (OHLCV) data for pair bar', caplog) caplog.clear() - assert (False, False) == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([])) + assert (False, False, None) == _STRATEGY.get_signal( + 'baz', + default_conf['timeframe'], + DataFrame([]) + ) assert log_has('Empty candle (OHLCV) data for pair baz', caplog) @@ -107,7 +118,11 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): caplog.set_level(logging.INFO) mocker.patch.object(_STRATEGY, 'assert_df') - assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history) + assert (False, False, None) == _STRATEGY.get_signal( + 'xyz', + default_conf['timeframe'], + mocked_history + ) assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog) @@ -416,34 +431,6 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili strategy.custom_stoploss = original_stopvalue -@pytest.mark.parametrize( - 'current_rate, expected_result, use_custom_entry_price, custom_entry', [ - # current rate, expected result value, profit for 2nd call, enable trailing, - # enable custom stoploss, expected after 1st call, expected after 2nd call - (99, False, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # custom_entry_price pice - (price * 0.01) - (97.8, True, True, lambda current_rate, **kwargs: current_rate - (current_rate * 0.01)), # price stayed under entry price - (97.8, True, True, lambda current_rate, **kwargs: current_rate + (current_rate * 0.01)), # entry price over current price - (99.9, True, False, None), # feature not activated - ]) -def test_entry_price_reached(default_conf, current_rate, exp_custom_entry, candle_ohlc, - expected_result, use_custom_entry_price, custom_entry) -> None: - - default_conf.update({'strategy': 'DefaultStrategy'}) - - - strategy = StrategyResolver.load_strategy(default_conf) - - strategy.use_custom_entry_price = use_custom_entry_price - custom_entry_price = custom_entry - if use_custom_entry_price: - strategy.custom_entry_price = custom_entry(current_rate) - - now = arrow.utcnow().datetime - entry_flag = strategy.entry_price_reached(current_rate=current_rate, low= None, high=None) - - - pass - def test_custom_sell(default_conf, fee, caplog) -> None: default_conf.update({'strategy': 'DefaultStrategy'}) From 53fb8b05e72587d334e1a5865a9a6773b523edae Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 16:19:29 -0400 Subject: [PATCH 225/519] remove short logic in entry_price_reached function --- freqtrade/strategy/interface.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 401934f7a..366ae3504 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -281,7 +281,7 @@ class IStrategy(ABC, HyperStrategyMixin): return self.stoploss def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, - **kwargs) -> float: + **kwargs) -> float: """ Custom entry price logic, returning the new entry price. @@ -654,8 +654,8 @@ class IStrategy(ABC, HyperStrategyMixin): return SellCheckTuple(sell_type=SellType.NONE) def entry_price_reached(self, pair: str, current_rate: float, - current_time: datetime, low: float = None, - high: float = None, side: str = "long") -> bool: + current_time: datetime, low: float = None, + high: float = None) -> bool: """ Based on current candle low ,decides if entry price was reached :param current_rate: current rate @@ -668,23 +668,12 @@ class IStrategy(ABC, HyperStrategyMixin): )(pair=pair, current_time=current_time, current_rate=current_rate) - # Sanity check - error cases will return None - if side == "long": + + if entry_price_value is not None: if entry_price_value > low: return True else: - logger.info(f"Entry failed because entry price {entry_price_value} \ - higher than candle low in long side") return False - - elif side == "short": - if entry_price_value < high: - return True - else: - logger.info(f"Entry failed because entry price {entry_price_value} \ - higher than candle high in short side") - return False - else: logger.warning("CustomEntryPrice function did not return valid entry price") return False From 00939b63f2bb05394af2023694b30b15be73ceef Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 16:25:18 -0400 Subject: [PATCH 226/519] flake 8 fixes --- freqtrade/freqtradebot.py | 2 +- freqtrade/strategy/interface.py | 8 ++++---- tests/test_freqtradebot.py | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c8e930a36..5b60c0ea3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -483,7 +483,7 @@ class FreqtradeBot(LoggingMixin): if self.config.get('use_custom_entry_price', False): buy_rate = self.exchange.get_rate(pair, refresh=True, side="buy") custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, - default_retval=stake_amount)( + default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), current_rate=buy_rate) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 366ae3504..750f6f39b 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -664,10 +664,10 @@ class IStrategy(ABC, HyperStrategyMixin): """ if self.use_custom_entry_price: - entry_price_value = strategy_safe_wrapper(self.custom_entry_price, default_retval=None - )(pair=pair, - current_time=current_time, - current_rate=current_rate) + entry_price_value = strategy_safe_wrapper(self.custom_entry_price, default_retval=None)( + pair=pair, + current_time=current_time, + current_rate=current_rate) if entry_price_value is not None: if entry_price_value > low: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index f6b8f5544..6aaa17094 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -904,7 +904,8 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order with pytest.raises(PricingError, match="Could not determine buy price."): freqtrade.execute_buy(pair, stake_amount) -def test_execute_buy_custom_entry_price(mocker, default_conf, fee, limit_buy_order, limit_buy_order_open) -> None: + +def test_execute_buy_custom_entry_price(mocker, default_conf, fee, limit_buy_order_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) default_conf.update({'use_custom_entry_price': True}) From 42e24d8b4b76a4e850c8b4687166f0bc79b82d22 Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 16:35:16 -0400 Subject: [PATCH 227/519] remove price change cancel reason in contants, will be added in another PR --- freqtrade/constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index f118ac700..b48644c58 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -433,8 +433,6 @@ SCHEMA_MINIMAL_REQUIRED = [ CANCEL_REASON = { "TIMEOUT": "cancelled due to timeout", - "ENTRYPRICECHANGED": "Custom entry price changed", - "EXITPRICECHANGED": "Custom exit price changed", "PARTIALLY_FILLED_KEEP_OPEN": "partially filled - keeping order open", "PARTIALLY_FILLED": "partially filled", "FULLY_CANCELLED": "fully cancelled", From 16146357b329a759654c302e391e304848adc951 Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 16:39:35 -0400 Subject: [PATCH 228/519] reuse buy_limit_requested as rate input for custom entry price --- freqtrade/freqtradebot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5b60c0ea3..ffe223899 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -481,11 +481,10 @@ class FreqtradeBot(LoggingMixin): # Calculate price buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy") if self.config.get('use_custom_entry_price', False): - buy_rate = self.exchange.get_rate(pair, refresh=True, side="buy") custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), - current_rate=buy_rate) + current_rate=buy_limit_requested) buy_limit_requested = custom_entry_price From b3dafb378e32c39e6eba60e61b2acfc8ea79bf08 Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 16:54:28 -0400 Subject: [PATCH 229/519] remove use_custom_entry_price as a config option --- freqtrade/freqtradebot.py | 11 +++++------ freqtrade/resolvers/strategy_resolver.py | 1 - freqtrade/rpc/api_server/api_schemas.py | 1 - freqtrade/rpc/rpc.py | 1 - freqtrade/strategy/interface.py | 23 ++++++++++------------- tests/test_freqtradebot.py | 1 - 6 files changed, 15 insertions(+), 23 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ffe223899..ad8d0b9c4 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -480,13 +480,12 @@ class FreqtradeBot(LoggingMixin): else: # Calculate price buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy") - if self.config.get('use_custom_entry_price', False): - custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, - default_retval=stake_amount)( - pair=pair, current_time=datetime.now(timezone.utc), - current_rate=buy_limit_requested) + custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, + default_retval=stake_amount)( + pair=pair, current_time=datetime.now(timezone.utc), + current_rate=buy_limit_requested) - buy_limit_requested = custom_entry_price + buy_limit_requested = custom_entry_price if not buy_limit_requested: raise PricingError('Could not determine buy price.') diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 3248ed385..1239b78b3 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -79,7 +79,6 @@ class StrategyResolver(IResolver): ("trailing_stop_positive_offset", 0.0), ("trailing_only_offset_is_reached", None), ("use_custom_stoploss", None), - ("use_custom_entry_price", None), ("process_only_new_candles", None), ("order_types", None), ("order_time_in_force", None), diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index c66a01490..318762136 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -129,7 +129,6 @@ class ShowConfig(BaseModel): trailing_stop_positive_offset: Optional[float] trailing_only_offset_is_reached: Optional[bool] use_custom_stoploss: Optional[bool] - use_custom_entry_price: Optional[bool] timeframe: Optional[str] timeframe_ms: int timeframe_min: int diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 2983c1dfa..902975fde 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -116,7 +116,6 @@ class RPC: 'trailing_stop_positive_offset': config.get('trailing_stop_positive_offset'), 'trailing_only_offset_is_reached': config.get('trailing_only_offset_is_reached'), 'use_custom_stoploss': config.get('use_custom_stoploss'), - 'use_custom_entry_price': config.get('use_custom_entry_price'), 'bot_name': config.get('bot_name', 'freqtrade'), 'timeframe': config.get('timeframe'), 'timeframe_ms': timeframe_to_msecs(config['timeframe'] diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 750f6f39b..d04524687 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -288,7 +288,6 @@ class IStrategy(ABC, HyperStrategyMixin): For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ When not implemented by a strategy, returns None, orderbook is used to set entry price - Only called when use_custom_entry_price is set to True. :param pair: Pair that's currently analyzed :param current_time: datetime object, containing the current datetime @@ -662,21 +661,19 @@ class IStrategy(ABC, HyperStrategyMixin): :param low: Low value of this candle, only set in backtesting :param high: High value of this candle, only set in backtesting """ + entry_price_value = strategy_safe_wrapper(self.custom_entry_price, default_retval=None)( + pair=pair, + current_time=current_time, + current_rate=current_rate) - if self.use_custom_entry_price: - entry_price_value = strategy_safe_wrapper(self.custom_entry_price, default_retval=None)( - pair=pair, - current_time=current_time, - current_rate=current_rate) - - if entry_price_value is not None: - if entry_price_value > low: - return True - else: - return False + if entry_price_value is not None: + if entry_price_value > low: + return True else: - logger.warning("CustomEntryPrice function did not return valid entry price") return False + else: + logger.warning("CustomEntryPrice function did not return valid entry price") + return False def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime, current_profit: float, diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6aaa17094..08e02cffb 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -908,7 +908,6 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order def test_execute_buy_custom_entry_price(mocker, default_conf, fee, limit_buy_order_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) - default_conf.update({'use_custom_entry_price': True}) freqtrade = FreqtradeBot(default_conf) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False) stake_amount = 3 From b644233eada98d37b435a38f4fc69cecdda3c451 Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 17:03:27 -0400 Subject: [PATCH 230/519] rename custom_entry_price kwarg to align it to the interface --- freqtrade/freqtradebot.py | 2 +- freqtrade/strategy/interface.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ad8d0b9c4..2592ccc91 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -483,7 +483,7 @@ class FreqtradeBot(LoggingMixin): custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), - current_rate=buy_limit_requested) + proposed_rate=buy_limit_requested) buy_limit_requested = custom_entry_price diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index d04524687..1cbc334f0 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -664,7 +664,7 @@ class IStrategy(ABC, HyperStrategyMixin): entry_price_value = strategy_safe_wrapper(self.custom_entry_price, default_retval=None)( pair=pair, current_time=current_time, - current_rate=current_rate) + proposed_rate=current_rate) if entry_price_value is not None: if entry_price_value > low: From bc3e6deb1c1efa5c9c3120f4c2d07c33bff5ef47 Mon Sep 17 00:00:00 2001 From: axel Date: Tue, 3 Aug 2021 17:09:52 -0400 Subject: [PATCH 231/519] remove specific test for buy with custom entry --- tests/test_freqtradebot.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 08e02cffb..b1e02a99b 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -905,36 +905,6 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order freqtrade.execute_buy(pair, stake_amount) -def test_execute_buy_custom_entry_price(mocker, default_conf, fee, limit_buy_order_open) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - freqtrade = FreqtradeBot(default_conf) - freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False) - stake_amount = 3 - bid = 2304 - buy_rate_mock = MagicMock(return_value=bid) - buy_mm = MagicMock(return_value=limit_buy_order_open) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - get_rate=buy_rate_mock, - fetch_ticker=MagicMock(return_value={ - 'bid': 2304, - 'ask': 0.00001173, - 'last': 2304 - }), - buy=buy_mm, - get_min_pair_stake_amount=MagicMock(return_value=1), - get_fee=fee, - ) - pair = 'ETH/USDT' - - # Test calling with custom entry price option activated - limit_buy_order_open['id'] = '55' - assert freqtrade.execute_buy(pair, stake_amount) - # Make sure get_rate called to provide current_rate param to custom_entry_price - assert buy_rate_mock.call_count == 1 - - def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch.multiple( From 6410a6528be7a34da85baacb259340f2d7afdba0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 06:46:21 +0200 Subject: [PATCH 232/519] Add missing methods to advanced strategy template --- freqtrade/strategy/interface.py | 6 +-- .../subtemplates/strategy_methods_advanced.j2 | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6f3e047eb..bf5cc10af 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -288,10 +288,10 @@ class IStrategy(ABC, HyperStrategyMixin): time. This method is not called when sell signal is set. This method should be overridden to create sell signals that depend on trade parameters. For - example you could implement a stoploss relative to candle when trade was opened, or a custom - 1:2 risk-reward ROI. + example you could implement a sell relative to the candle when the trade was opened, + or a custom 1:2 risk-reward ROI. - Custom sell reason max length is 64. Exceeding this limit will raise OperationalException. + Custom sell reason max length is 64. Exceeding characters will be removed. :param pair: Pair that's currently analyzed :param trade: trade object. diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index 2a9ac0690..2df23f365 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -12,6 +12,23 @@ def bot_loop_start(self, **kwargs) -> None: """ pass +def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + """ + Customize stake size for each new trade. This method is not called when edge module is + enabled. + + :param pair: Pair that's currently analyzed + :param current_time: datetime object, containing the current datetime + :param current_rate: Rate, calculated based on pricing settings in ask_strategy. + :param proposed_stake: A stake amount proposed by the bot. + :param min_stake: Minimal stake size allowed by exchange. + :param max_stake: Balance available for trading. + :return: A stake size, which is between min_stake and max_stake. + """ + return proposed_stake + use_custom_stoploss = True def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime', @@ -38,6 +55,30 @@ def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime', """ return self.stoploss +def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs) -> 'Optional[Union[str, bool]]': + """ + Custom sell signal logic indicating that specified position should be sold. Returning a + string or True from this method is equal to setting sell signal on a candle at specified + time. This method is not called when sell signal is set. + + This method should be overridden to create sell signals that depend on trade parameters. For + example you could implement a sell relative to the candle when the trade was opened, + or a custom 1:2 risk-reward ROI. + + Custom sell reason max length is 64. Exceeding characters will be removed. + + :param pair: Pair that's currently analyzed + :param trade: trade object. + :param current_time: datetime object, containing the current datetime + :param current_rate: Rate, calculated based on pricing settings in ask_strategy. + :param current_profit: Current profit (as ratio), calculated based on current_rate. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return: To execute sell, return a string with custom sell reason or True. Otherwise return + None or False. + """ + return None + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: 'datetime', **kwargs) -> bool: """ From 74a5cb3c21d33bcee48a64470c4d9117efa86fc3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 06:49:53 +0200 Subject: [PATCH 233/519] Remove protections from full config They are supposed to be configured in the strategy --- config_examples/config_full.example.json | 27 ------------------------ 1 file changed, 27 deletions(-) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index d404391a4..3ca413281 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -78,33 +78,6 @@ "refresh_period": 1440 } ], - "protections": [ - { - "method": "StoplossGuard", - "lookback_period_candles": 60, - "trade_limit": 4, - "stop_duration_candles": 60, - "only_per_pair": false - }, - { - "method": "CooldownPeriod", - "stop_duration_candles": 20 - }, - { - "method": "MaxDrawdown", - "lookback_period_candles": 200, - "trade_limit": 20, - "stop_duration_candles": 10, - "max_allowed_drawdown": 0.2 - }, - { - "method": "LowProfitPairs", - "lookback_period_candles": 360, - "trade_limit": 1, - "stop_duration_candles": 2, - "required_profit": 0.02 - } - ], "exchange": { "name": "binance", "sandbox": false, From 800b2eeaf0e5629c5f5fe8877ae414859db25e50 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Aug 2021 06:38:15 +0200 Subject: [PATCH 234/519] Load protections as part of backtest() this enables different values in hyperopt per epoch --- freqtrade/optimize/backtesting.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 45e60e013..3079e326d 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -146,6 +146,8 @@ class Backtesting: # since a "perfect" stoploss-sell is assumed anyway # And the regular "stoploss" function would not apply to that case self.strategy.order_types['stoploss_on_exchange'] = False + + def _load_protections(self, strategy: IStrategy): if self.config.get('enable_protections', False): conf = self.config if hasattr(strategy, 'protections'): @@ -194,6 +196,7 @@ class Backtesting: Trade.reset_trades() self.rejected_trades = 0 self.dataprovider.clear_cache() + self._load_protections(self.strategy) def check_abort(self): """ From 544e0da6c2c84cc918978dce0146089b011744f4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Aug 2021 07:10:04 +0200 Subject: [PATCH 235/519] Add protection parameter space --- freqtrade/commands/cli_options.py | 2 +- freqtrade/optimize/hyperopt.py | 17 +++++++++++++++-- freqtrade/optimize/hyperopt_auto.py | 3 +++ freqtrade/optimize/hyperopt_interface.py | 7 +++++++ freqtrade/strategy/hyper.py | 16 +++++++++++----- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index f56a2bf18..215ed3f6e 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -218,7 +218,7 @@ AVAILABLE_CLI_OPTIONS = { "spaces": Arg( '--spaces', help='Specify which parameters to hyperopt. Space-separated list.', - choices=['all', 'buy', 'sell', 'roi', 'stoploss', 'trailing', 'default'], + choices=['all', 'buy', 'sell', 'roi', 'stoploss', 'trailing', 'protection', 'default'], nargs='+', default='default', ), diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index a69e5a5a2..18ed66b4a 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -66,6 +66,7 @@ class Hyperopt: def __init__(self, config: Dict[str, Any]) -> None: self.buy_space: List[Dimension] = [] self.sell_space: List[Dimension] = [] + self.protection_space: List[Dimension] = [] self.roi_space: List[Dimension] = [] self.stoploss_space: List[Dimension] = [] self.trailing_space: List[Dimension] = [] @@ -191,6 +192,8 @@ class Hyperopt: result['buy'] = {p.name: params.get(p.name) for p in self.buy_space} if HyperoptTools.has_space(self.config, 'sell'): result['sell'] = {p.name: params.get(p.name) for p in self.sell_space} + if HyperoptTools.has_space(self.config, 'protection'): + result['protection'] = {p.name: params.get(p.name) for p in self.protection_space} if HyperoptTools.has_space(self.config, 'roi'): result['roi'] = {str(k): v for k, v in self.custom_hyperopt.generate_roi_table(params).items()} @@ -241,6 +244,10 @@ class Hyperopt: """ Assign the dimensions in the hyperoptimization space. """ + if self.auto_hyperopt and HyperoptTools.has_space(self.config, 'protection'): + # Protections can only be optimized when using the Parameter interface + logger.debug("Hyperopt has 'protection' space") + self.protection_space = self.custom_hyperopt.protection_space() if HyperoptTools.has_space(self.config, 'buy'): logger.debug("Hyperopt has 'buy' space") @@ -261,8 +268,8 @@ class Hyperopt: if HyperoptTools.has_space(self.config, 'trailing'): logger.debug("Hyperopt has 'trailing' space") self.trailing_space = self.custom_hyperopt.trailing_space() - self.dimensions = (self.buy_space + self.sell_space + self.roi_space + - self.stoploss_space + self.trailing_space) + self.dimensions = (self.buy_space + self.sell_space + self.protection_space + + self.roi_space + self.stoploss_space + self.trailing_space) def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict: """ @@ -282,6 +289,12 @@ class Hyperopt: self.backtesting.strategy.advise_sell = ( # type: ignore self.custom_hyperopt.sell_strategy_generator(params_dict)) + if HyperoptTools.has_space(self.config, 'protection'): + for attr_name, attr in self.backtesting.strategy.enumerate_parameters('protection'): + if attr.optimize: + # noinspection PyProtectedMember + attr.value = params_dict[attr_name] + if HyperoptTools.has_space(self.config, 'roi'): self.backtesting.strategy.minimal_roi = ( # type: ignore self.custom_hyperopt.generate_roi_table(params_dict)) diff --git a/freqtrade/optimize/hyperopt_auto.py b/freqtrade/optimize/hyperopt_auto.py index f86204406..03f7dd21e 100644 --- a/freqtrade/optimize/hyperopt_auto.py +++ b/freqtrade/optimize/hyperopt_auto.py @@ -73,6 +73,9 @@ class HyperOptAuto(IHyperOpt): def sell_indicator_space(self) -> List['Dimension']: return self._get_indicator_space('sell', 'sell_indicator_space') + def protection_space(self) -> List['Dimension']: + return self._get_indicator_space('protection', 'indicator_space') + def generate_roi_table(self, params: Dict) -> Dict[int, float]: return self._get_func('generate_roi_table')(params) diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 889854cad..500798627 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -57,6 +57,13 @@ class IHyperOpt(ABC): """ raise OperationalException(_format_exception_message('sell_strategy_generator', 'sell')) + def protection_space(self) -> List[Dimension]: + """ + Create a protection space. + Only supported by the Parameter interface. + """ + raise OperationalException(_format_exception_message('indicator_space', 'protection')) + def indicator_space(self) -> List[Dimension]: """ Create an indicator space. diff --git a/freqtrade/strategy/hyper.py b/freqtrade/strategy/hyper.py index b067e19d5..e143dda7e 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -283,6 +283,7 @@ class HyperStrategyMixin(object): self.config = config self.ft_buy_params: List[BaseParameter] = [] self.ft_sell_params: List[BaseParameter] = [] + self.ft_protection_params: List[BaseParameter] = [] self._load_hyper_params(config.get('runmode') == RunMode.HYPEROPT) @@ -292,11 +293,11 @@ class HyperStrategyMixin(object): :param category: :return: """ - if category not in ('buy', 'sell', None): - raise OperationalException('Category must be one of: "buy", "sell", None.') + if category not in ('buy', 'sell', 'protection', None): + raise OperationalException('Category must be one of: "buy", "sell", "protection", None.') if category is None: - params = self.ft_buy_params + self.ft_sell_params + params = self.ft_buy_params + self.ft_sell_params + self.ft_protection_params else: params = getattr(self, f"ft_{category}_params") @@ -324,9 +325,10 @@ class HyperStrategyMixin(object): params: Dict = { 'buy': list(cls.detect_parameters('buy')), 'sell': list(cls.detect_parameters('sell')), + 'protection': list(cls.detect_parameters('protection')), } params.update({ - 'count': len(params['buy'] + params['sell']) + 'count': len(params['buy'] + params['sell'] + params['protection']) }) return params @@ -340,9 +342,12 @@ class HyperStrategyMixin(object): self._ft_params_from_file = params buy_params = deep_merge_dicts(params.get('buy', {}), getattr(self, 'buy_params', {})) sell_params = deep_merge_dicts(params.get('sell', {}), getattr(self, 'sell_params', {})) + protection_params = deep_merge_dicts(params.get('protection', {}), + getattr(self, 'protection_params', {})) self._load_params(buy_params, 'buy', hyperopt) self._load_params(sell_params, 'sell', hyperopt) + self._load_params(protection_params, 'protection', hyperopt) def load_params_from_file(self) -> Dict: filename_str = getattr(self, '__file__', '') @@ -397,7 +402,8 @@ class HyperStrategyMixin(object): """ params = { 'buy': {}, - 'sell': {} + 'sell': {}, + 'protection': {}, } for name, p in self.enumerate_parameters(): if not p.optimize or not p.in_space: From 091bf7c4d2940e72ed00639b362b8aff1d009f84 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Aug 2021 07:14:31 +0200 Subject: [PATCH 236/519] Output protection space --- freqtrade/optimize/hyperopt_tools.py | 8 +++++--- freqtrade/plugins/protections/iprotection.py | 6 +++--- tests/optimize/test_hyperopt.py | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 439016c14..94b724d9a 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -82,8 +82,8 @@ class HyperoptTools(): """ Tell if the space value is contained in the configuration """ - # The 'trailing' space is not included in the 'default' set of spaces - if space == 'trailing': + # 'trailing' and 'protection spaces are not included in the 'default' set of spaces + if space in ('trailing', 'protection'): return any(s in config['spaces'] for s in [space, 'all']) else: return any(s in config['spaces'] for s in [space, 'all', 'default']) @@ -149,7 +149,7 @@ class HyperoptTools(): if print_json: result_dict: Dict = {} - for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']: + for s in ['buy', 'sell', 'protection', 'roi', 'stoploss', 'trailing']: HyperoptTools._params_update_for_json(result_dict, params, non_optimized, s) print(rapidjson.dumps(result_dict, default=str, number_mode=rapidjson.NM_NATIVE)) @@ -158,6 +158,8 @@ class HyperoptTools(): non_optimized) HyperoptTools._params_pretty_print(params, 'sell', "Sell hyperspace params:", non_optimized) + HyperoptTools._params_pretty_print(params, 'protection', + "Protection hyperspace params:", non_optimized) HyperoptTools._params_pretty_print(params, 'roi', "ROI table:", non_optimized) HyperoptTools._params_pretty_print(params, 'stoploss', "Stoploss:", non_optimized) HyperoptTools._params_pretty_print(params, 'trailing', "Trailing stop:", non_optimized) diff --git a/freqtrade/plugins/protections/iprotection.py b/freqtrade/plugins/protections/iprotection.py index d034beefc..e6bb49064 100644 --- a/freqtrade/plugins/protections/iprotection.py +++ b/freqtrade/plugins/protections/iprotection.py @@ -27,17 +27,17 @@ class IProtection(LoggingMixin, ABC): self._protection_config = protection_config tf_in_min = timeframe_to_minutes(config['timeframe']) if 'stop_duration_candles' in protection_config: - self._stop_duration_candles = protection_config.get('stop_duration_candles', 1) + self._stop_duration_candles = int(protection_config.get('stop_duration_candles', 1)) self._stop_duration = (tf_in_min * self._stop_duration_candles) else: self._stop_duration_candles = None self._stop_duration = protection_config.get('stop_duration', 60) if 'lookback_period_candles' in protection_config: - self._lookback_period_candles = protection_config.get('lookback_period_candles', 1) + self._lookback_period_candles = int(protection_config.get('lookback_period_candles', 1)) self._lookback_period = tf_in_min * self._lookback_period_candles else: self._lookback_period_candles = None - self._lookback_period = protection_config.get('lookback_period', 60) + self._lookback_period = int(protection_config.get('lookback_period', 60)) LoggingMixin.__init__(self, logger) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 14fea573f..ef4b85beb 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -577,6 +577,7 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: "20.0": 0.02, "50.0": 0.01, "110.0": 0}, + 'protection': {}, 'sell': {'sell-adx-enabled': False, 'sell-adx-value': 0, 'sell-fastd-enabled': True, @@ -592,7 +593,7 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: 'trailing_stop_positive': 0.02, 'trailing_stop_positive_offset': 0.07}}, 'params_dict': optimizer_param, - 'params_not_optimized': {'buy': {}, 'sell': {}}, + 'params_not_optimized': {'buy': {}, 'protection': {}, 'sell': {}}, 'results_metrics': ANY, 'total_profit': 3.1e-08 } From a6454cfc39453dbceb9fbba2bf896b45d64334ee Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 07:14:47 +0200 Subject: [PATCH 237/519] Autoenable protections when protection-space is selected --- freqtrade/optimize/hyperopt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 18ed66b4a..3eab709da 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -247,6 +247,8 @@ class Hyperopt: if self.auto_hyperopt and HyperoptTools.has_space(self.config, 'protection'): # Protections can only be optimized when using the Parameter interface logger.debug("Hyperopt has 'protection' space") + # Enable Protections if protection space is selected. + self.config['enable_protections'] = True self.protection_space = self.custom_hyperopt.protection_space() if HyperoptTools.has_space(self.config, 'buy'): From ceed3c663b3eb5498d2eeff232f4122945e58126 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 07:15:14 +0200 Subject: [PATCH 238/519] Document using protections --- docs/hyperopt.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 4fba925d0..95a1cfcc5 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -326,7 +326,7 @@ There are four parameter types each suited for different purposes. !!! Warning Hyperoptable parameters cannot be used in `populate_indicators` - as hyperopt does not recalculate indicators for each epoch, so the starting value would be used in this case. -### Optimizing an indicator parameter +## Optimizing an indicator parameter Assuming you have a simple strategy in mind - a EMA cross strategy (2 Moving averages crossing) - and you'd like to find the ideal parameters for this strategy. @@ -413,6 +413,94 @@ While this strategy is most likely too simple to provide consistent profit, it s While this may slow down the hyperopt startup speed, the overall performance will increase as the Hyperopt execution itself may pick the same value for multiple epochs (changing other values). You should however try to use space ranges as small as possible. Every new column will require more memory, and every possibility hyperopt can try will increase the search space. +## Optimizing protections + +Freqtrade can also optimize protections. How you optimize protections is up to you, and the following should be considered as example only. + +The strategy will simply need to define the "protections" entry as property returning a list of protection configurations. + +``` python +from pandas import DataFrame +from functools import reduce + +import talib.abstract as ta + +from freqtrade.strategy import IStrategy +from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter +import freqtrade.vendor.qtpylib.indicators as qtpylib + +class MyAwesomeStrategy(IStrategy): + stoploss = -0.05 + timeframe = '15m' + # Define the parameter spaces + coolback_lookback = IntParameter(2, 48, default=5, space="protection", optimize=True) + stop_duration = IntParameter(12, 200, default=5, space="protection", optimize=True) + use_stop_protection = CategoricalParameter([True, False], default=True, space="protection", optimize=True) + + + @property + def protections(self): + prot = [] + + prot.append({ + "method": "CooldownPeriod", + "stop_duration_candles": self.coolback_lookback.value + }) + if self.use_stop_protection.value: + prot.append({ + "method": "StoplossGuard", + "lookback_period_candles": 24 * 3, + "trade_limit": 4, + "stop_duration_candles": self.stop_duration.value, + "only_per_pair": False + }) + + return protection + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # ... + +``` + +You can then run hyperopt as follows: +`freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy MyAwesomeStrategy --spaces protection` + +!!! Note + The protection space is not part of the default space, and is only available with the Parameters Hyperopt interface, not with the legacy hyperopt interface (which required separate hyperopt files). + Freqtrade will also automatically change the "--enable-protections" flag if the protection space is selected. + +### Migrating from previous property setups + +A migration from a previous setup is pretty simple, and can be accomplished by converting the protections entry to a property. +In simple terms, the following configuration will be converted to the below. + +``` python +class MyAwesomeStrategy(IStrategy): + protections = [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 4 + } + ] +``` + +Result + +``` python +class MyAwesomeStrategy(IStrategy): + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 4 + } + ] +``` + +You will then obviously also change potential interesting entries to parameters to allow hyper-optimization. + ## Loss-functions Each hyperparameter tuning requires a target. This is usually defined as a loss function (sometimes also called objective function), which should decrease for more desirable results, and increase for bad results. From a661e0db6e373ade53690a6214f2e7b2b84ea333 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 19:43:16 +0200 Subject: [PATCH 239/519] Deprecate protections from config --- docs/hyperopt.md | 4 + docs/includes/protections.md | 149 ++++++++++-------- .../configuration/deprecated_settings.py | 3 + tests/test_configuration.py | 13 +- 4 files changed, 101 insertions(+), 68 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 95a1cfcc5..e11c93748 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -469,6 +469,10 @@ You can then run hyperopt as follows: The protection space is not part of the default space, and is only available with the Parameters Hyperopt interface, not with the legacy hyperopt interface (which required separate hyperopt files). Freqtrade will also automatically change the "--enable-protections" flag if the protection space is selected. +!!! Warning + If protections are defined as property, entries from the configuration will be ignored. + It is therefore recommended to not define protections in the configuration. + ### Migrating from previous property setups A migration from a previous setup is pretty simple, and can be accomplished by converting the protections entry to a property. diff --git a/docs/includes/protections.md b/docs/includes/protections.md index 5dcc83738..0757d2f6d 100644 --- a/docs/includes/protections.md +++ b/docs/includes/protections.md @@ -15,6 +15,10 @@ All protection end times are rounded up to the next candle to avoid sudden, unex !!! Note "Backtesting" Protections are supported by backtesting and hyperopt, but must be explicitly enabled by using the `--enable-protections` flag. +!!! Warning "Setting protections from the configuration" + Setting protections from the configuration via `"protections": [],` key should be considered deprecated and will be removed in a future version. + It is also no longer guaranteed that your protections apply to the strategy in cases where the strategy defines [protections as property](hyperopt.md#optimizing-protections). + ### Available Protections * [`StoplossGuard`](#stoploss-guard) Stop trading if a certain amount of stoploss occurred within a certain time window. @@ -47,15 +51,17 @@ This applies across all pairs, unless `only_per_pair` is set to true, which will The below example stops trading for all pairs for 4 candles after the last trade if the bot hit stoploss 4 times within the last 24 candles. ``` python -protections = [ - { - "method": "StoplossGuard", - "lookback_period_candles": 24, - "trade_limit": 4, - "stop_duration_candles": 4, - "only_per_pair": False - } -] +@property +def protections(self): + return [ + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 4, + "only_per_pair": False + } + ] ``` !!! Note @@ -69,15 +75,17 @@ protections = [ The below sample stops trading for 12 candles if max-drawdown is > 20% considering all pairs - with a minimum of `trade_limit` trades - within the last 48 candles. If desired, `lookback_period` and/or `stop_duration` can be used. ``` python -protections = [ - { - "method": "MaxDrawdown", - "lookback_period_candles": 48, - "trade_limit": 20, - "stop_duration_candles": 12, - "max_allowed_drawdown": 0.2 - }, -] +@property +def protections(self): + return [ + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 20, + "stop_duration_candles": 12, + "max_allowed_drawdown": 0.2 + }, + ] ``` #### Low Profit Pairs @@ -88,15 +96,17 @@ If that ratio is below `required_profit`, that pair will be locked for `stop_dur The below example will stop trading a pair for 60 minutes if the pair does not have a required profit of 2% (and a minimum of 2 trades) within the last 6 candles. ``` python -protections = [ - { - "method": "LowProfitPairs", - "lookback_period_candles": 6, - "trade_limit": 2, - "stop_duration": 60, - "required_profit": 0.02 - } -] +@property +def protections(self): + return [ + { + "method": "LowProfitPairs", + "lookback_period_candles": 6, + "trade_limit": 2, + "stop_duration": 60, + "required_profit": 0.02 + } + ] ``` #### Cooldown Period @@ -106,12 +116,14 @@ protections = [ The below example will stop trading a pair for 2 candles after closing a trade, allowing this pair to "cool down". ``` python -protections = [ - { - "method": "CooldownPeriod", - "stop_duration_candles": 2 - } -] +@property +def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 2 + } + ] ``` !!! Note @@ -136,39 +148,42 @@ from freqtrade.strategy import IStrategy class AwesomeStrategy(IStrategy) timeframe = '1h' - protections = [ - { - "method": "CooldownPeriod", - "stop_duration_candles": 5 - }, - { - "method": "MaxDrawdown", - "lookback_period_candles": 48, - "trade_limit": 20, - "stop_duration_candles": 4, - "max_allowed_drawdown": 0.2 - }, - { - "method": "StoplossGuard", - "lookback_period_candles": 24, - "trade_limit": 4, - "stop_duration_candles": 2, - "only_per_pair": False - }, - { - "method": "LowProfitPairs", - "lookback_period_candles": 6, - "trade_limit": 2, - "stop_duration_candles": 60, - "required_profit": 0.02 - }, - { - "method": "LowProfitPairs", - "lookback_period_candles": 24, - "trade_limit": 4, - "stop_duration_candles": 2, - "required_profit": 0.01 - } - ] + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 5 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 20, + "stop_duration_candles": 4, + "max_allowed_drawdown": 0.2 + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "only_per_pair": False + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 6, + "trade_limit": 2, + "stop_duration_candles": 60, + "required_profit": 0.02 + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "required_profit": 0.01 + } + ] # ... ``` diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 1b162f7c9..529d4cd6a 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -110,3 +110,6 @@ def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None: "Please remove 'ticker_interval' from your configuration to continue operating." ) config['timeframe'] = config['ticker_interval'] + + if 'protections' in config: + logger.warning("DEPRECATED: Setting 'protections' in the configuration is deprecated.") diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 7012333e9..9dce304c7 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1330,7 +1330,7 @@ def test_process_removed_setting(mocker, default_conf, caplog): 'sectionB', 'somesetting') -def test_process_deprecated_ticker_interval(mocker, default_conf, caplog): +def test_process_deprecated_ticker_interval(default_conf, caplog): message = "DEPRECATED: Please use 'timeframe' instead of 'ticker_interval." config = deepcopy(default_conf) process_temporary_deprecated_settings(config) @@ -1352,6 +1352,17 @@ def test_process_deprecated_ticker_interval(mocker, default_conf, caplog): process_temporary_deprecated_settings(config) +def test_process_deprecated_protections(default_conf, caplog): + message = "DEPRECATED: Setting 'protections' in the configuration is deprecated." + config = deepcopy(default_conf) + process_temporary_deprecated_settings(config) + assert not log_has(message, caplog) + + config['protections'] = [] + process_temporary_deprecated_settings(config) + assert log_has(message, caplog) + + def test_flat_vars_to_nested_dict(caplog): test_args = { From 3c412233338afbd1648786e0b5dbaf484d0988e5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 20:01:28 +0200 Subject: [PATCH 240/519] Add test for protections-hyperopt --- docs/hyperopt.md | 4 ++-- freqtrade/strategy/hyper.py | 3 ++- tests/optimize/test_hyperopt.py | 8 ++++++++ tests/strategy/strats/hyperoptable_strategy.py | 11 +++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index e11c93748..f869b9ac1 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -433,7 +433,7 @@ class MyAwesomeStrategy(IStrategy): stoploss = -0.05 timeframe = '15m' # Define the parameter spaces - coolback_lookback = IntParameter(2, 48, default=5, space="protection", optimize=True) + cooldown_lookback = IntParameter(2, 48, default=5, space="protection", optimize=True) stop_duration = IntParameter(12, 200, default=5, space="protection", optimize=True) use_stop_protection = CategoricalParameter([True, False], default=True, space="protection", optimize=True) @@ -444,7 +444,7 @@ class MyAwesomeStrategy(IStrategy): prot.append({ "method": "CooldownPeriod", - "stop_duration_candles": self.coolback_lookback.value + "stop_duration_candles": self.cooldown_lookback.value }) if self.use_stop_protection.value: prot.append({ diff --git a/freqtrade/strategy/hyper.py b/freqtrade/strategy/hyper.py index e143dda7e..b9f586ac5 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -294,7 +294,8 @@ class HyperStrategyMixin(object): :return: """ if category not in ('buy', 'sell', 'protection', None): - raise OperationalException('Category must be one of: "buy", "sell", "protection", None.') + raise OperationalException( + 'Category must be one of: "buy", "sell", "protection", None.') if category is None: params = self.ft_buy_params + self.ft_sell_params + self.ft_protection_params diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index ef4b85beb..077fb516d 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -1003,6 +1003,8 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: hyperopt_conf.update({ 'strategy': 'HyperoptableStrategy', 'user_data_dir': Path(tmpdir), + 'hyperopt_random_state': 42, + 'spaces': ['buy', 'sell', 'protection'] }) hyperopt = Hyperopt(hyperopt_conf) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) @@ -1010,12 +1012,18 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: assert hyperopt.backtesting.strategy.buy_rsi.in_space is True assert hyperopt.backtesting.strategy.buy_rsi.value == 35 + assert hyperopt.backtesting.strategy.sell_rsi.value == 74 + assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30 buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range assert isinstance(buy_rsi_range, range) # Range from 0 - 50 (inclusive) assert len(list(buy_rsi_range)) == 51 hyperopt.start() + # All values should've changed. + assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value != 30 + assert hyperopt.backtesting.strategy.buy_rsi.value != 35 + assert hyperopt.backtesting.strategy.sell_rsi.value != 74 def test_SKDecimal(): diff --git a/tests/strategy/strats/hyperoptable_strategy.py b/tests/strategy/strats/hyperoptable_strategy.py index cc4734e13..9d332e243 100644 --- a/tests/strategy/strats/hyperoptable_strategy.py +++ b/tests/strategy/strats/hyperoptable_strategy.py @@ -64,6 +64,17 @@ class HyperoptableStrategy(IStrategy): sell_rsi = IntParameter(low=50, high=100, default=70, space='sell') sell_minusdi = DecimalParameter(low=0, high=1, default=0.5001, decimals=3, space='sell', load=False) + protection_cooldown_lookback = IntParameter([0, 50], default=30) + + @property + def protections(self): + prot = [] + + prot.append({ + "method": "CooldownPeriod", + "stop_duration_candles": self.protection_cooldown_lookback.value + }) + return prot def informative_pairs(self): """ From b73768acd1244d5863bcf27fe868f6dfaa11f4f0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 20:10:32 +0200 Subject: [PATCH 241/519] Fix bug in property overwriting prevention --- freqtrade/resolvers/strategy_resolver.py | 2 +- tests/optimize/test_hyperopt.py | 2 +- tests/strategy/test_interface.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 1239b78b3..0a18809b4 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -119,7 +119,7 @@ class StrategyResolver(IResolver): - default (if not None) """ if (attribute in config - and not isinstance(getattr(type(strategy), 'my_property', None), property)): + and not isinstance(getattr(type(strategy), attribute, None), property)): # Ensure Properties are not overwritten setattr(strategy, attribute, config[attribute]) logger.info("Override strategy '%s' with value in config file: %s.", diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 077fb516d..4fba80bc3 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -1004,7 +1004,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: 'strategy': 'HyperoptableStrategy', 'user_data_dir': Path(tmpdir), 'hyperopt_random_state': 42, - 'spaces': ['buy', 'sell', 'protection'] + 'spaces': ['all'] }) hyperopt = Hyperopt(hyperopt_conf) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index d8c87506c..9593cd02e 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -734,7 +734,7 @@ def test_auto_hyperopt_interface(default_conf): assert isinstance(all_params, dict) assert len(all_params['buy']) == 2 assert len(all_params['sell']) == 2 - assert all_params['count'] == 4 + assert all_params['count'] == 5 strategy.__class__.sell_rsi = IntParameter([0, 10], default=5, space='buy') From ad0e4a8567592edb9078a27cbc2c0a8bd5fd178a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Aug 2021 20:52:56 +0200 Subject: [PATCH 242/519] Add BooleanParameter --- docs/hyperopt.md | 13 ++++++----- freqtrade/plugins/protections/iprotection.py | 3 +++ freqtrade/strategy/__init__.py | 4 ++-- freqtrade/strategy/hyper.py | 22 +++++++++++++++++++ freqtrade/templates/base_strategy.py.j2 | 4 ++-- freqtrade/templates/sample_strategy.py | 4 ++-- .../strategy/strats/hyperoptable_strategy.py | 14 +++++++----- tests/strategy/test_interface.py | 18 ++++++++++++--- 8 files changed, 61 insertions(+), 21 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index f869b9ac1..96f9ff177 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -253,7 +253,7 @@ We continue to define hyperoptable parameters: class MyAwesomeStrategy(IStrategy): buy_adx = DecimalParameter(20, 40, decimals=1, default=30.1, space="buy") buy_rsi = IntParameter(20, 40, default=30, space="buy") - buy_adx_enabled = CategoricalParameter([True, False], default=True, space="buy") + buy_adx_enabled = BooleanParameter(default=True, space="buy") buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") ``` @@ -316,6 +316,7 @@ There are four parameter types each suited for different purposes. * `DecimalParameter` - defines a floating point parameter with a limited number of decimals (default 3). Should be preferred instead of `RealParameter` in most cases. * `RealParameter` - defines a floating point parameter with upper and lower boundaries and no precision limit. Rarely used as it creates a space with a near infinite number of possibilities. * `CategoricalParameter` - defines a parameter with a predetermined number of choices. +* `BooleanParameter` - Shorthand for `CategoricalParameter([True, False])` - great for "enable" parameters. !!! Tip "Disabling parameter optimization" Each parameter takes two boolean parameters: @@ -336,8 +337,8 @@ from functools import reduce import talib.abstract as ta -from freqtrade.strategy import IStrategy -from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) import freqtrade.vendor.qtpylib.indicators as qtpylib class MyAwesomeStrategy(IStrategy): @@ -425,8 +426,8 @@ from functools import reduce import talib.abstract as ta -from freqtrade.strategy import IStrategy -from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) import freqtrade.vendor.qtpylib.indicators as qtpylib class MyAwesomeStrategy(IStrategy): @@ -435,7 +436,7 @@ class MyAwesomeStrategy(IStrategy): # Define the parameter spaces cooldown_lookback = IntParameter(2, 48, default=5, space="protection", optimize=True) stop_duration = IntParameter(12, 200, default=5, space="protection", optimize=True) - use_stop_protection = CategoricalParameter([True, False], default=True, space="protection", optimize=True) + use_stop_protection = BooleanParameter(default=True, space="protection", optimize=True) @property diff --git a/freqtrade/plugins/protections/iprotection.py b/freqtrade/plugins/protections/iprotection.py index e6bb49064..e0a89e334 100644 --- a/freqtrade/plugins/protections/iprotection.py +++ b/freqtrade/plugins/protections/iprotection.py @@ -25,6 +25,9 @@ class IProtection(LoggingMixin, ABC): def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None: self._config = config self._protection_config = protection_config + self._stop_duration_candles: Optional[int] = None + self._lookback_period_candles: Optional[int] = None + tf_in_min = timeframe_to_minutes(config['timeframe']) if 'stop_duration_candles' in protection_config: self._stop_duration_candles = int(protection_config.get('stop_duration_candles', 1)) diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index bd49165df..be655fc33 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -1,7 +1,7 @@ # flake8: noqa: F401 from freqtrade.exchange import (timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, timeframe_to_prev_date, timeframe_to_seconds) -from freqtrade.strategy.hyper import (CategoricalParameter, DecimalParameter, IntParameter, - RealParameter) +from freqtrade.strategy.hyper import (BooleanParameter, CategoricalParameter, DecimalParameter, + IntParameter, RealParameter) from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.strategy_helper import merge_informative_pair, stoploss_from_open diff --git a/freqtrade/strategy/hyper.py b/freqtrade/strategy/hyper.py index b9f586ac5..dad282d7e 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -270,6 +270,28 @@ class CategoricalParameter(BaseParameter): return [self.value] +class BooleanParameter(CategoricalParameter): + + def __init__(self, *, default: Optional[Any] = None, + space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable Boolean Parameter. + It's a shortcut to `CategoricalParameter([True, False])`. + :param default: A default value. If not specified, first item from specified space will be + used. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter field + name is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.Categorical. + """ + + categories = [True, False] + super().__init__(categories=categories, default=default, space=space, optimize=optimize, + load=load, **kwargs) + + class HyperStrategyMixin(object): """ A helper base class which allows HyperOptAuto class to reuse implementations of buy/sell diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index 13fc0853a..06d7cbc5c 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -6,8 +6,8 @@ import numpy as np # noqa import pandas as pd # noqa from pandas import DataFrame -from freqtrade.strategy import IStrategy -from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) # -------------------------------- # Add your lib to import here diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index 282b2f8e2..574819949 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -6,8 +6,8 @@ import numpy as np # noqa import pandas as pd # noqa from pandas import DataFrame -from freqtrade.strategy import IStrategy -from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) # -------------------------------- # Add your lib to import here diff --git a/tests/strategy/strats/hyperoptable_strategy.py b/tests/strategy/strats/hyperoptable_strategy.py index 9d332e243..88bdd078e 100644 --- a/tests/strategy/strats/hyperoptable_strategy.py +++ b/tests/strategy/strats/hyperoptable_strategy.py @@ -4,7 +4,8 @@ import talib.abstract as ta from pandas import DataFrame import freqtrade.vendor.qtpylib.indicators as qtpylib -from freqtrade.strategy import DecimalParameter, IntParameter, IStrategy, RealParameter +from freqtrade.strategy import (BooleanParameter, DecimalParameter, IntParameter, IStrategy, + RealParameter) class HyperoptableStrategy(IStrategy): @@ -64,16 +65,17 @@ class HyperoptableStrategy(IStrategy): sell_rsi = IntParameter(low=50, high=100, default=70, space='sell') sell_minusdi = DecimalParameter(low=0, high=1, default=0.5001, decimals=3, space='sell', load=False) + protection_enabled = BooleanParameter(default=True) protection_cooldown_lookback = IntParameter([0, 50], default=30) @property def protections(self): prot = [] - - prot.append({ - "method": "CooldownPeriod", - "stop_duration_candles": self.protection_cooldown_lookback.value - }) + if self.protection_enabled.value: + prot.append({ + "method": "CooldownPeriod", + "stop_duration_candles": self.protection_cooldown_lookback.value + }) return prot def informative_pairs(self): diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 9593cd02e..0ad6d6f32 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -16,8 +16,8 @@ from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.optimize.space import SKDecimal from freqtrade.persistence import PairLocks, Trade from freqtrade.resolvers import StrategyResolver -from freqtrade.strategy.hyper import (BaseParameter, CategoricalParameter, DecimalParameter, - IntParameter, RealParameter) +from freqtrade.strategy.hyper import (BaseParameter, BooleanParameter, CategoricalParameter, + DecimalParameter, IntParameter, RealParameter) from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from tests.conftest import log_has, log_has_re @@ -717,6 +717,17 @@ def test_hyperopt_parameters(): assert len(list(catpar.range)) == 3 assert list(catpar.range) == ['buy_rsi', 'buy_macd', 'buy_none'] + boolpar = BooleanParameter(default=True, space='buy') + assert boolpar.value is True + assert isinstance(boolpar.get_space(''), Categorical) + assert isinstance(boolpar.range, list) + assert len(list(boolpar.range)) == 1 + + boolpar.in_space = True + assert len(list(boolpar.range)) == 2 + + assert list(boolpar.range) == [True, False] + def test_auto_hyperopt_interface(default_conf): default_conf.update({'strategy': 'HyperoptableStrategy'}) @@ -734,7 +745,8 @@ def test_auto_hyperopt_interface(default_conf): assert isinstance(all_params, dict) assert len(all_params['buy']) == 2 assert len(all_params['sell']) == 2 - assert all_params['count'] == 5 + # Number of Hyperoptable parameters + assert all_params['count'] == 6 strategy.__class__.sell_rsi = IntParameter([0, 10], default=5, space='buy') From 2cf781f3dd8a0c87ec08b0ff08ebb2a5b63d6786 Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 4 Aug 2021 18:32:39 -0400 Subject: [PATCH 243/519] add freqtradebot execute_buy test in custom entry price case --- tests/test_freqtradebot.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b1e02a99b..7ea67162d 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -904,6 +904,15 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order with pytest.raises(PricingError, match="Could not determine buy price."): freqtrade.execute_buy(pair, stake_amount) + # In case of custom entry price + limit_buy_order['status'] = 'open' + limit_buy_order['id'] = '5566' + freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.77 + assert freqtrade.execute_buy(pair, stake_amount) + trade = Trade.query.all()[6] + assert trade + assert trade.open_rate_requested == 0.77 + def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) From d9b9eecd4d039cc0bc37baeb4075481b2f628b56 Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 4 Aug 2021 18:47:14 -0400 Subject: [PATCH 244/519] remove entry price reached method --- freqtrade/strategy/interface.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 1cbc334f0..3d9ad0915 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -652,29 +652,6 @@ class IStrategy(ABC, HyperStrategyMixin): # logger.debug(f"{trade.pair} - No sell signal.") return SellCheckTuple(sell_type=SellType.NONE) - def entry_price_reached(self, pair: str, current_rate: float, - current_time: datetime, low: float = None, - high: float = None) -> bool: - """ - Based on current candle low ,decides if entry price was reached - :param current_rate: current rate - :param low: Low value of this candle, only set in backtesting - :param high: High value of this candle, only set in backtesting - """ - entry_price_value = strategy_safe_wrapper(self.custom_entry_price, default_retval=None)( - pair=pair, - current_time=current_time, - proposed_rate=current_rate) - - if entry_price_value is not None: - if entry_price_value > low: - return True - else: - return False - else: - logger.warning("CustomEntryPrice function did not return valid entry price") - return False - def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime, current_profit: float, force_stoploss: float, low: float = None, From f9f519fd3c55f7e2f2040ec9cf3592bc22154711 Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 4 Aug 2021 18:54:17 -0400 Subject: [PATCH 245/519] add custom_exit_price function to interface --- freqtrade/strategy/interface.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 3d9ad0915..e1aba7d7a 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -297,6 +297,23 @@ class IStrategy(ABC, HyperStrategyMixin): """ return proposed_rate + def custom_exit_price(self, pair: str, current_time: datetime, proposed_rate: float, + **kwargs) -> float: + """ + Custom exit price logic, returning the new exit price. + + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ + + When not implemented by a strategy, returns None, orderbook is used to set exit price + + :param pair: Pair that's currently analyzed + :param current_time: datetime object, containing the current datetime + :param proposed_rate: Rate, calculated based on pricing settings in ask_strategy. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New exit price value if provided + """ + return proposed_rate + def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> Optional[Union[str, bool]]: """ From f243ad4af0b8b1a5e8350db05f0717b16734bd27 Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 4 Aug 2021 19:09:55 -0400 Subject: [PATCH 246/519] add custom_exit_price in strategy interface --- freqtrade/strategy/interface.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index e1aba7d7a..f7d0a0aae 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -297,8 +297,9 @@ class IStrategy(ABC, HyperStrategyMixin): """ return proposed_rate - def custom_exit_price(self, pair: str, current_time: datetime, proposed_rate: float, - **kwargs) -> float: + def custom_exit_price(self, pair: str, trade: Trade, + current_time: datetime, proposed_rate: float, + current_rate: float, current_profit: float, **kwargs) -> float: """ Custom exit price logic, returning the new exit price. @@ -307,8 +308,10 @@ class IStrategy(ABC, HyperStrategyMixin): When not implemented by a strategy, returns None, orderbook is used to set exit price :param pair: Pair that's currently analyzed + :param trade: trade object. :param current_time: datetime object, containing the current datetime :param proposed_rate: Rate, calculated based on pricing settings in ask_strategy. + :param current_profit: Current profit (as ratio), calculated based on current_rate. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: New exit price value if provided """ From cb3b0cf311d43583d18fe1b67878a8e1d286b834 Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 4 Aug 2021 23:09:40 -0400 Subject: [PATCH 247/519] add custom_exit_price in interface and freqtradebot --- freqtrade/freqtradebot.py | 8 ++++++++ freqtrade/strategy/interface.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2592ccc91..8d50b46e3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1082,6 +1082,14 @@ class FreqtradeBot(LoggingMixin): and self.strategy.order_types['stoploss_on_exchange']: limit = trade.stop_loss + # set custom_exit_price if available + current_profit = trade.calc_profit_ratio(limit) + limit = strategy_safe_wrapper(self.strategy.custom_exit_price, + default_retval=limit)( + pair=trade.pair, trade=trade, + current_time=datetime.now(timezone.utc), + proposed_rate=limit, current_profit=current_profit) + # First cancelling stoploss on exchange ... if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id: try: diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index f7d0a0aae..4c0e1c8a9 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -299,7 +299,7 @@ class IStrategy(ABC, HyperStrategyMixin): def custom_exit_price(self, pair: str, trade: Trade, current_time: datetime, proposed_rate: float, - current_rate: float, current_profit: float, **kwargs) -> float: + current_profit: float, **kwargs) -> float: """ Custom exit price logic, returning the new exit price. From eee5f174fca4954e9a20d46c0cbded00547dcd49 Mon Sep 17 00:00:00 2001 From: Kamontat Chantrachirathumrong Date: Thu, 5 Aug 2021 20:33:15 +0700 Subject: [PATCH 248/519] update link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8cba78136..e10c15c11 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ hesitate to read the source code and understand the mechanism of this bot. Please read the [exchange specific notes](docs/exchanges.md) to learn about eventual, special configurations needed for each exchange. - [X] [Bittrex](https://bittrex.com/) -- [X] [Binance](https://www.binance.com/) ([*Note for binance users](docs/exchanges.md#blacklists)) +- [X] [Binance](https://www.binance.com/) ([*Note for binance users](docs/exchanges.md#binance-blacklist)) - [X] [Kraken](https://kraken.com/) - [X] [FTX](https://ftx.com) - [ ] [potentially many others](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ From 90c194de1f6ef83a8899bd0ed7873656f70c802a Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 5 Aug 2021 19:27:38 +0200 Subject: [PATCH 249/519] Align Readme and documentation index in supported exchange lists --- README.md | 2 +- docs/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e10c15c11..78ea3cecd 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ hesitate to read the source code and understand the mechanism of this bot. Please read the [exchange specific notes](docs/exchanges.md) to learn about eventual, special configurations needed for each exchange. -- [X] [Bittrex](https://bittrex.com/) - [X] [Binance](https://www.binance.com/) ([*Note for binance users](docs/exchanges.md#binance-blacklist)) +- [X] [Bittrex](https://bittrex.com/) - [X] [Kraken](https://kraken.com/) - [X] [FTX](https://ftx.com) - [ ] [potentially many others](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ diff --git a/docs/index.md b/docs/index.md index 8077cd303..05eaa7552 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,7 +36,7 @@ Freqtrade is a crypto-currency algorithmic trading software developed in python Please read the [exchange specific notes](exchanges.md) to learn about eventual, special configurations needed for each exchange. -- [X] [Binance](https://www.binance.com/) ([*Note for binance users](exchanges.md#blacklists)) +- [X] [Binance](https://www.binance.com/) ([*Note for binance users](docs/exchanges.md#binance-blacklist)) - [X] [Bittrex](https://bittrex.com/) - [X] [FTX](https://ftx.com) - [X] [Kraken](https://kraken.com/) From 0aeebc9d53dad23f8ed2f02b0b91b11d6276cbea Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 5 Aug 2021 17:57:45 -0400 Subject: [PATCH 250/519] add test for custom exit price --- tests/test_freqtradebot.py | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 7ea67162d..c73e51dec 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2724,6 +2724,70 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) } == last_msg +def test_execute_sell_custom_exit_price(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: + rpc_mock = patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + fetch_ticker=ticker, + get_fee=fee, + _is_dry_limit_order_filled=MagicMock(return_value=False), + ) + patch_whitelist(mocker, default_conf) + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False) + + # Create some test data + freqtrade.enter_positions() + rpc_mock.reset_mock() + + trade = Trade.query.first() + assert trade + assert freqtrade.strategy.confirm_trade_exit.call_count == 0 + + # Increase the price and sell it + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + fetch_ticker=ticker_sell_up + ) + + freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) + + # Set a custom exit price + freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05 + + freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL)) + + # Sell price must be different to default bid price + + assert freqtrade.strategy.confirm_trade_exit.call_count == 1 + + assert rpc_mock.call_count == 1 + last_msg = rpc_mock.call_args_list[-1][0][0] + assert { + 'trade_id': 1, + 'type': RPCMessageType.SELL, + 'exchange': 'Binance', + 'pair': 'ETH/BTC', + 'gain': 'profit', + 'limit': 1.170e-05, + 'amount': 91.07468123, + 'order_type': 'limit', + 'open_rate': 1.098e-05, + 'current_rate': 1.173e-05, + 'profit_amount': 6.041e-05, + 'profit_ratio': 0.06025919, + 'stake_currency': 'BTC', + 'fiat_currency': 'USD', + 'sell_reason': SellType.SELL_SIGNAL.value, + 'open_date': ANY, + 'close_date': ANY, + 'close_rate': ANY, + } == last_msg + + def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: rpc_mock = patch_RPCManager(mocker) From 84d082033b21cbffb02f4fd283a8bd33936733e4 Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 5 Aug 2021 18:00:31 -0400 Subject: [PATCH 251/519] fix default retval for strategy custom_entry_price --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8d50b46e3..99f5d2894 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -481,7 +481,7 @@ class FreqtradeBot(LoggingMixin): # Calculate price buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy") custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, - default_retval=stake_amount)( + default_retval=buy_limit_requested)( pair=pair, current_time=datetime.now(timezone.utc), proposed_rate=buy_limit_requested) From 0985b11267b890aeeccaed79ebc4ed9644c39f99 Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 5 Aug 2021 18:16:16 -0400 Subject: [PATCH 252/519] add doc for custom exit price --- docs/strategy-advanced.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index a8e54bbcf..f59cb8ef5 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -357,13 +357,13 @@ See [Dataframe access](#dataframe-access) for more information about dataframe u --- -## Custom order entry price rules +## Custom order price rules By default, freqtrade use the orderbook to automatically set an order price, you also have the option to create custom order prices based on your strategy. -You can use this feature by setting the `use_custom_entry_price` option to `true` in config and creating a custom_entry_price function. +You can use this feature by creating a custom_entry_price function in your strategy file to customize entry prices and custom_exit_price for exits. -### Custom order entry price exemple +### Custom order entry and exit price exemple ``` python from datetime import datetime, timedelta, timezone from freqtrade.persistence import Trade @@ -373,13 +373,23 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods def custom_entry_price(self, pair: str, current_time: datetime, - proposed_rate, **kwargs) -> float: + proposed_rate, **kwargs) -> float: dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) - entryprice = dataframe['bollinger_10_lowerband'].iat[-1] + proposed_entryprice = dataframe['bollinger_10_lowerband'].iat[-1] - return entryprice + return proposed_entryprice + + def custom_exit_price(self, pair: str, trade: Trade, + current_time: datetime, proposed_rate: float, + current_profit: float, **kwargs) -> float: + + dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, + timeframe=self.timeframe) + proposed_exitprice = dataframe['bollinger_10_upperband'].iat[-1] + + return proposed_exitprice ``` From b9356a55644979c494f65e3235ab3b38a8cee1b1 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 6 Aug 2021 16:19:36 -0600 Subject: [PATCH 253/519] Autopep8 formatting --- freqtrade/commands/build_config_commands.py | 2 +- freqtrade/commands/deploy_commands.py | 14 ++++----- freqtrade/commands/hyperopt_commands.py | 8 ++--- freqtrade/configuration/check_exchange.py | 8 ++--- freqtrade/configuration/config_validation.py | 4 +-- .../configuration/deprecated_settings.py | 2 +- freqtrade/constants.py | 2 +- freqtrade/edge/edge_positioning.py | 6 ++-- freqtrade/main.py | 2 +- freqtrade/optimize/hyperopt.py | 6 ++-- freqtrade/optimize/hyperopt_tools.py | 2 +- freqtrade/optimize/optimize_reports.py | 6 ++-- freqtrade/plot/plotting.py | 4 +-- freqtrade/plugins/pairlist/IPairList.py | 2 +- freqtrade/plugins/pairlist/VolumePairList.py | 8 ++--- freqtrade/plugins/pairlistmanager.py | 14 ++++----- .../plugins/protections/stoploss_guard.py | 6 ++-- freqtrade/resolvers/__init__.py | 3 -- freqtrade/resolvers/strategy_resolver.py | 2 +- freqtrade/rpc/api_server/api_v1.py | 4 +-- freqtrade/rpc/fiat_convert.py | 2 +- freqtrade/rpc/rpc_manager.py | 1 + freqtrade/rpc/telegram.py | 22 +++++++------- freqtrade/strategy/strategy_helper.py | 2 +- tests/config_test_comments.json | 26 ++++++++-------- tests/optimize/test_hyperopt.py | 2 +- tests/plugins/test_protections.py | 4 +-- tests/rpc/test_fiat_convert.py | 6 ++-- tests/rpc/test_rpc_apiserver.py | 6 ++-- tests/test_arguments.py | 2 +- tests/test_configuration.py | 30 +++++++++---------- 31 files changed, 104 insertions(+), 104 deletions(-) diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index b3f912433..852cab92e 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -193,7 +193,7 @@ def deploy_new_config(config_path: Path, selections: Dict[str, Any]) -> None: selections['exchange'] = render_template( templatefile=f"subtemplates/exchange_{exchange_template}.j2", arguments=selections - ) + ) except TemplateNotFound: selections['exchange'] = render_template( templatefile="subtemplates/exchange_generic.j2", diff --git a/freqtrade/commands/deploy_commands.py b/freqtrade/commands/deploy_commands.py index cc0d653b9..eb65579e2 100644 --- a/freqtrade/commands/deploy_commands.py +++ b/freqtrade/commands/deploy_commands.py @@ -38,15 +38,15 @@ def deploy_new_strategy(strategy_name: str, strategy_path: Path, subtemplate: st indicators = render_template_with_fallback( templatefile=f"subtemplates/indicators_{subtemplate}.j2", templatefallbackfile=f"subtemplates/indicators_{fallback}.j2", - ) + ) buy_trend = render_template_with_fallback( templatefile=f"subtemplates/buy_trend_{subtemplate}.j2", templatefallbackfile=f"subtemplates/buy_trend_{fallback}.j2", - ) + ) sell_trend = render_template_with_fallback( templatefile=f"subtemplates/sell_trend_{subtemplate}.j2", templatefallbackfile=f"subtemplates/sell_trend_{fallback}.j2", - ) + ) plot_config = render_template_with_fallback( templatefile=f"subtemplates/plot_config_{subtemplate}.j2", templatefallbackfile=f"subtemplates/plot_config_{fallback}.j2", @@ -97,19 +97,19 @@ def deploy_new_hyperopt(hyperopt_name: str, hyperopt_path: Path, subtemplate: st buy_guards = render_template_with_fallback( templatefile=f"subtemplates/hyperopt_buy_guards_{subtemplate}.j2", templatefallbackfile=f"subtemplates/hyperopt_buy_guards_{fallback}.j2", - ) + ) sell_guards = render_template_with_fallback( templatefile=f"subtemplates/hyperopt_sell_guards_{subtemplate}.j2", templatefallbackfile=f"subtemplates/hyperopt_sell_guards_{fallback}.j2", - ) + ) buy_space = render_template_with_fallback( templatefile=f"subtemplates/hyperopt_buy_space_{subtemplate}.j2", templatefallbackfile=f"subtemplates/hyperopt_buy_space_{fallback}.j2", - ) + ) sell_space = render_template_with_fallback( templatefile=f"subtemplates/hyperopt_sell_space_{subtemplate}.j2", templatefallbackfile=f"subtemplates/hyperopt_sell_space_{fallback}.j2", - ) + ) strategy_text = render_template(templatefile='base_hyperopt.py.j2', arguments={"hyperopt": hyperopt_name, diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 5a2727795..4694d1111 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -187,7 +187,7 @@ def _hyperopt_filter_epochs_trade_count(epochs: List, filteroptions: dict) -> Li x for x in epochs if x['results_metrics'].get( 'trade_count', x['results_metrics'].get('total_trades') - ) < filteroptions['filter_max_trades'] + ) < filteroptions['filter_max_trades'] ] return epochs @@ -239,7 +239,7 @@ def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List: x for x in epochs if x['results_metrics'].get( 'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100 - ) < filteroptions['filter_max_avg_profit'] + ) < filteroptions['filter_max_avg_profit'] ] if filteroptions['filter_min_total_profit'] is not None: epochs = _hyperopt_filter_epochs_trade(epochs, 0) @@ -247,7 +247,7 @@ def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List: x for x in epochs if x['results_metrics'].get( 'profit', x['results_metrics'].get('profit_total_abs', 0) - ) > filteroptions['filter_min_total_profit'] + ) > filteroptions['filter_min_total_profit'] ] if filteroptions['filter_max_total_profit'] is not None: epochs = _hyperopt_filter_epochs_trade(epochs, 0) @@ -255,7 +255,7 @@ def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List: x for x in epochs if x['results_metrics'].get( 'profit', x['results_metrics'].get('profit_total_abs', 0) - ) < filteroptions['filter_max_total_profit'] + ) < filteroptions['filter_max_total_profit'] ] return epochs diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index f282447d4..c4f038103 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -51,10 +51,10 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: if not is_exchange_known_ccxt(exchange): raise OperationalException( - f'Exchange "{exchange}" is not known to the ccxt library ' - f'and therefore not available for the bot.\n' - f'The following exchanges are available for Freqtrade: ' - f'{", ".join(available_exchanges())}' + f'Exchange "{exchange}" is not known to the ccxt library ' + f'and therefore not available for the bot.\n' + f'The following exchanges are available for Freqtrade: ' + f'{", ".join(available_exchanges())}' ) valid, reason = validate_exchange(exchange) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index aad03e983..85ff4408f 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -115,7 +115,7 @@ def _validate_trailing_stoploss(conf: Dict[str, Any]) -> None: if conf.get('stoploss') == 0.0: raise OperationalException( 'The config stoploss needs to be different from 0 to avoid problems with sell orders.' - ) + ) # Skip if trailing stoploss is not activated if not conf.get('trailing_stop', False): return @@ -180,7 +180,7 @@ def _validate_protections(conf: Dict[str, Any]) -> None: raise OperationalException( "Protections must specify either `stop_duration` or `stop_duration_candles`.\n" f"Please fix the protection {prot.get('method')}" - ) + ) if ('lookback_period' in prot and 'lookback_period_candles' in prot): raise OperationalException( diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 1b162f7c9..e59e51f87 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -108,5 +108,5 @@ def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None: raise OperationalException( "Both 'timeframe' and 'ticker_interval' detected." "Please remove 'ticker_interval' from your configuration to continue operating." - ) + ) config['timeframe'] = config['ticker_interval'] diff --git a/freqtrade/constants.py b/freqtrade/constants.py index b48644c58..de4bc99b4 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -280,7 +280,7 @@ CONF_SCHEMA = { 'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS, 'default': 'off' - }, + }, } }, 'reload': {'type': 'boolean'}, diff --git a/freqtrade/edge/edge_positioning.py b/freqtrade/edge/edge_positioning.py index 977b7e4ec..243043d31 100644 --- a/freqtrade/edge/edge_positioning.py +++ b/freqtrade/edge/edge_positioning.py @@ -231,12 +231,12 @@ class Edge: 'Minimum expectancy and minimum winrate are met only for %s,' ' so other pairs are filtered out.', self._final_pairs - ) + ) else: logger.info( 'Edge removed all pairs as no pair with minimum expectancy ' 'and minimum winrate was found !' - ) + ) return self._final_pairs @@ -247,7 +247,7 @@ class Edge: final = [] for pair, info in self._cached_pairs.items(): if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \ - info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)): + info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)): final.append({ 'Pair': pair, 'Winrate': info.winrate, diff --git a/freqtrade/main.py b/freqtrade/main.py index 84d4b24f8..2fd3d32bb 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -44,7 +44,7 @@ def main(sysargv: List[str] = None) -> None: "as `freqtrade trade [options...]`.\n" "To see the full list of options available, please use " "`freqtrade --help` or `freqtrade --help`." - ) + ) except SystemExit as e: return_code = e diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index a69e5a5a2..d40bbb73b 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -444,9 +444,9 @@ class Hyperopt: ' [', progressbar.ETA(), ', ', progressbar.Timer(), ']', ] with progressbar.ProgressBar( - max_value=self.total_epochs, redirect_stdout=False, redirect_stderr=False, - widgets=widgets - ) as pbar: + max_value=self.total_epochs, redirect_stdout=False, redirect_stderr=False, + widgets=widgets + ) as pbar: EVALS = ceil(self.total_epochs / jobs) for i in range(EVALS): # Correct the number of epochs to be processed for the last diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 439016c14..51f1f977a 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -203,7 +203,7 @@ class HyperoptTools(): elif space == "roi": result = result[:-1] + f'{appendix}\n' minimal_roi_result = rapidjson.dumps({ - str(k): v for k, v in (space_params or no_params).items() + str(k): v for k, v in (space_params or no_params).items() }, default=str, indent=4, number_mode=rapidjson.NM_NATIVE) result += f"minimal_roi = {minimal_roi_result}" elif space == "trailing": diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index eefacbbab..7bb60228a 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -31,7 +31,7 @@ def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> N filename = Path.joinpath( recordfilename.parent, f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}' - ).with_suffix(recordfilename.suffix) + ).with_suffix(recordfilename.suffix) file_dump_json(filename, stats) latest_filename = Path.joinpath(filename.parent, LAST_BT_RESULT_FN) @@ -173,7 +173,7 @@ def generate_strategy_comparison(all_results: Dict) -> List[Dict]: for strategy, results in all_results.items(): tabular_data.append(_generate_result_line( results['results'], results['config']['dry_run_wallet'], strategy) - ) + ) try: max_drawdown_per, _, _, _, _ = calculate_max_drawdown(results['results'], value_col='profit_ratio') @@ -604,7 +604,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency']) stake_amount = round_coin_value( strat_results['stake_amount'], strat_results['stake_currency'] - ) if strat_results['stake_amount'] != UNLIMITED_STAKE_AMOUNT else 'unlimited' + ) if strat_results['stake_amount'] != UNLIMITED_STAKE_AMOUNT else 'unlimited' message = ("No trades made. " f"Your starting balance was {start_balance}, " diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 061460975..2fbf343ce 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -334,8 +334,8 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: ) elif indicator_b not in data: logger.info( - 'fill_to: "%s" ignored. Reason: This indicator is not ' - 'in your strategy.', indicator_b + 'fill_to: "%s" ignored. Reason: This indicator is not ' + 'in your strategy.', indicator_b ) return fig diff --git a/freqtrade/plugins/pairlist/IPairList.py b/freqtrade/plugins/pairlist/IPairList.py index 74348b1a7..bfde2ace0 100644 --- a/freqtrade/plugins/pairlist/IPairList.py +++ b/freqtrade/plugins/pairlist/IPairList.py @@ -144,7 +144,7 @@ class IPairList(LoggingMixin, ABC): markets = self._exchange.markets if not markets: raise OperationalException( - 'Markets not loaded. Make sure that exchange is initialized correctly.') + 'Markets not loaded. Make sure that exchange is initialized correctly.') sanitized_whitelist: List[str] = [] for pair in pairlist: diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index d6b8aaaa3..901fde2d0 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -120,9 +120,9 @@ class VolumePairList(IPairList): # Use fresh pairlist # Check if pair quote currency equals to the stake currency. filtered_tickers = [ - v for k, v in tickers.items() - if (self._exchange.get_pair_quote_currency(k) == self._stake_currency - and v[self._sort_key] is not None)] + v for k, v in tickers.items() + if (self._exchange.get_pair_quote_currency(k) == self._stake_currency + and v[self._sort_key] is not None)] pairlist = [s['symbol'] for s in filtered_tickers] pairlist = self.filter_pairlist(pairlist, tickers) @@ -197,7 +197,7 @@ class VolumePairList(IPairList): if self._min_value > 0: filtered_tickers = [ - v for v in filtered_tickers if v[self._sort_key] > self._min_value] + v for v in filtered_tickers if v[self._sort_key] > self._min_value] sorted_tickers = sorted(filtered_tickers, reverse=True, key=lambda t: t[self._sort_key]) diff --git a/freqtrade/plugins/pairlistmanager.py b/freqtrade/plugins/pairlistmanager.py index 03f4760b8..face79729 100644 --- a/freqtrade/plugins/pairlistmanager.py +++ b/freqtrade/plugins/pairlistmanager.py @@ -28,13 +28,13 @@ class PairListManager(): self._tickers_needed = False for pairlist_handler_config in self._config.get('pairlists', None): pairlist_handler = PairListResolver.load_pairlist( - pairlist_handler_config['method'], - exchange=exchange, - pairlistmanager=self, - config=config, - pairlistconfig=pairlist_handler_config, - pairlist_pos=len(self._pairlist_handlers) - ) + pairlist_handler_config['method'], + exchange=exchange, + pairlistmanager=self, + config=config, + pairlistconfig=pairlist_handler_config, + pairlist_pos=len(self._pairlist_handlers) + ) self._tickers_needed |= pairlist_handler.needstickers self._pairlist_handlers.append(pairlist_handler) diff --git a/freqtrade/plugins/protections/stoploss_guard.py b/freqtrade/plugins/protections/stoploss_guard.py index 45d393411..40edf1204 100644 --- a/freqtrade/plugins/protections/stoploss_guard.py +++ b/freqtrade/plugins/protections/stoploss_guard.py @@ -54,9 +54,9 @@ class StoplossGuard(IProtection): trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until) trades = [trade for trade in trades1 if (str(trade.sell_reason) in ( - SellType.TRAILING_STOP_LOSS.value, SellType.STOP_LOSS.value, - SellType.STOPLOSS_ON_EXCHANGE.value) - and trade.close_profit and trade.close_profit < 0)] + SellType.TRAILING_STOP_LOSS.value, SellType.STOP_LOSS.value, + SellType.STOPLOSS_ON_EXCHANGE.value) + and trade.close_profit and trade.close_profit < 0)] if len(trades) < self._trade_limit: return False, None, None diff --git a/freqtrade/resolvers/__init__.py b/freqtrade/resolvers/__init__.py index ef24bf481..2f70a788a 100644 --- a/freqtrade/resolvers/__init__.py +++ b/freqtrade/resolvers/__init__.py @@ -8,6 +8,3 @@ from freqtrade.resolvers.exchange_resolver import ExchangeResolver from freqtrade.resolvers.pairlist_resolver import PairListResolver from freqtrade.resolvers.protection_resolver import ProtectionResolver from freqtrade.resolvers.strategy_resolver import StrategyResolver - - - diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 1239b78b3..82942bd68 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -50,7 +50,7 @@ class StrategyResolver(IResolver): if 'timeframe' not in config: logger.warning( "DEPRECATED: Please migrate to using 'timeframe' instead of 'ticker_interval'." - ) + ) strategy.timeframe = strategy.ticker_interval if strategy._ft_params_from_file: diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 61d69707e..f2361fda8 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -199,8 +199,8 @@ def pair_history(pair: str, timeframe: str, timerange: str, strategy: str, config=Depends(get_config)): config = deepcopy(config) config.update({ - 'strategy': strategy, - }) + 'strategy': strategy, + }) return RPC._rpc_analysed_history_full(config, pair, timeframe, timerange) diff --git a/freqtrade/rpc/fiat_convert.py b/freqtrade/rpc/fiat_convert.py index 199e6a7db..cdc09b437 100644 --- a/freqtrade/rpc/fiat_convert.py +++ b/freqtrade/rpc/fiat_convert.py @@ -62,7 +62,7 @@ class CryptoToFiatConverter: # If the request is not a 429 error we want to raise the normal error logger.error( "Could not load FIAT Cryptocurrency map for the following problem: {}".format( - request_exception + request_exception ) ) except (Exception) as exception: diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 67842e849..8085ece94 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -15,6 +15,7 @@ class RPCManager: """ Class to manage RPC objects (Telegram, API, ...) """ + def __init__(self, freqtrade) -> None: """ Initializes all enabled rpc modules """ self.registered_modules: List[RPCHandler] = [] diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index a1f6a7e33..a988d2b60 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -77,7 +77,6 @@ class Telegram(RPCHandler): """ This class handles all telegram communication """ def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None: - """ Init the Telegram call, and init the super class RPCHandler :param rpc: instance of RPC Helper class @@ -270,7 +269,7 @@ class Telegram(RPCHandler): noti = '' if msg_type == RPCMessageType.SELL: sell_noti = self._config['telegram'] \ - .get('notification_settings', {}).get(str(msg_type), {}) + .get('notification_settings', {}).get(str(msg_type), {}) # For backward compatibility sell still can be string if isinstance(sell_noti, str): noti = sell_noti @@ -278,7 +277,7 @@ class Telegram(RPCHandler): noti = sell_noti.get(str(msg['sell_reason']), default_noti) else: noti = self._config['telegram'] \ - .get('notification_settings', {}).get(str(msg_type), default_noti) + .get('notification_settings', {}).get(str(msg_type), default_noti) if noti == 'off': logger.info(f"Notification '{msg_type}' not sent.") @@ -541,7 +540,7 @@ class Telegram(RPCHandler): f"`{first_trade_date}`\n" f"*Latest Trade opened:* `{latest_trade_date}\n`" f"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`" - ) + ) if stats['closed_trade_count'] > 0: markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n" f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`") @@ -576,13 +575,14 @@ class Telegram(RPCHandler): sell_reasons_msg = tabulate( sell_reasons_tabulate, headers=['Sell Reason', 'Sells', 'Wins', 'Losses'] - ) + ) durations = stats['durations'] - duration_msg = tabulate([ - ['Wins', str(timedelta(seconds=durations['wins'])) - if durations['wins'] != 'N/A' else 'N/A'], - ['Losses', str(timedelta(seconds=durations['losses'])) - if durations['losses'] != 'N/A' else 'N/A'] + duration_msg = tabulate( + [ + ['Wins', str(timedelta(seconds=durations['wins'])) + if durations['wins'] != 'N/A' else 'N/A'], + ['Losses', str(timedelta(seconds=durations['losses'])) + if durations['losses'] != 'N/A' else 'N/A'] ], headers=['', 'Avg. Duration'] ) @@ -1100,7 +1100,7 @@ class Telegram(RPCHandler): if reload_able: reply_markup = InlineKeyboardMarkup([ [InlineKeyboardButton("Refresh", callback_data=callback_path)], - ]) + ]) else: reply_markup = InlineKeyboardMarkup([[]]) msg += "\nUpdated: {}".format(datetime.now().ctime()) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 22b6f0be5..e089ebf31 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -38,7 +38,7 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, # Detailed explanation in https://github.com/freqtrade/freqtrade/issues/4073 informative['date_merge'] = ( informative["date"] + pd.to_timedelta(minutes_inf, 'm') - pd.to_timedelta(minutes, 'm') - ) + ) else: raise ValueError("Tried to merge a faster timeframe to a slower timeframe." "This would create new rows, and can throw off your regular indicators.") diff --git a/tests/config_test_comments.json b/tests/config_test_comments.json index 48a087dec..19d82c454 100644 --- a/tests/config_test_comments.json +++ b/tests/config_test_comments.json @@ -6,8 +6,8 @@ */ "stake_currency": "BTC", "stake_amount": 0.05, - "fiat_display_currency": "USD", // C++-style comment - "amount_reserve_percent" : 0.05, // And more, tabs before this comment + "fiat_display_currency": "USD", // C++-style comment + "amount_reserve_percent": 0.05, // And more, tabs before this comment "dry_run": false, "timeframe": "5m", "trailing_stop": false, @@ -15,15 +15,15 @@ "trailing_stop_positive_offset": 0.0051, "trailing_only_offset_is_reached": false, "minimal_roi": { - "40": 0.0, - "30": 0.01, - "20": 0.02, - "0": 0.04 + "40": 0.0, + "30": 0.01, + "20": 0.02, + "0": 0.04 }, "stoploss": -0.10, "unfilledtimeout": { "buy": 10, - "sell": 30, // Trailing comma should also be accepted now + "sell": 30, // Trailing comma should also be accepted now }, "bid_strategy": { "use_order_book": false, @@ -34,7 +34,7 @@ "bids_to_ask_delta": 1 } }, - "ask_strategy":{ + "ask_strategy": { "use_order_book": false, "order_book_min": 1, "order_book_max": 9 @@ -64,7 +64,9 @@ "key": "your_exchange_key", "secret": "your_exchange_secret", "password": "", - "ccxt_config": {"enableRateLimit": true}, + "ccxt_config": { + "enableRateLimit": true + }, "ccxt_async_config": { "enableRateLimit": false, "rateLimit": 500, @@ -103,8 +105,8 @@ "remove_pumps": false }, "telegram": { -// We can now comment out some settings -// "enabled": true, + // We can now comment out some settings + // "enabled": true, "enabled": false, "token": "your_telegram_token", "chat_id": "your_telegram_chat_id" @@ -124,4 +126,4 @@ }, "strategy": "DefaultStrategy", "strategy_path": "user_data/strategies/" -} +} \ No newline at end of file diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 14fea573f..b5197e73f 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -399,7 +399,7 @@ def test_hyperopt_format_results(hyperopt): 'rejected_signals': 2, 'backtest_start_time': 1619718665, 'backtest_end_time': 1619718665, - } + } results_metrics = generate_strategy_stats({'XRP/BTC': None}, '', bt_result, Arrow(2017, 11, 14, 19, 32, 00), Arrow(2017, 12, 14, 19, 32, 00), market_change=0) diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index 9ec47dade..c0a9ae72a 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -93,7 +93,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog): Trade.query.session.add(generate_mock_trade( 'XRP/BTC', fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, min_ago_open=200, min_ago_close=30, - )) + )) assert not freqtrade.protections.global_stop() assert not log_has_re(message, caplog) @@ -150,7 +150,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair Trade.query.session.add(generate_mock_trade( pair, fee.return_value, False, sell_reason=SellType.STOP_LOSS.value, min_ago_open=200, min_ago_close=30, profit_rate=0.9, - )) + )) assert not freqtrade.protections.stop_per_pair(pair) assert not freqtrade.protections.global_stop() diff --git a/tests/rpc/test_fiat_convert.py b/tests/rpc/test_fiat_convert.py index 5174f9416..9fb1122f5 100644 --- a/tests/rpc/test_fiat_convert.py +++ b/tests/rpc/test_fiat_convert.py @@ -139,9 +139,9 @@ def test_fiat_too_many_requests_response(mocker, caplog): assert length_cryptomap == 0 assert fiat_convert._backoff > datetime.datetime.now().timestamp() assert log_has( - 'Too many requests for Coingecko API, backing off and trying again later.', - caplog - ) + 'Too many requests for Coingecko API, backing off and trying again later.', + caplog + ) def test_fiat_invalid_response(mocker, caplog): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 68f23e0fd..1517b6fcc 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -942,7 +942,7 @@ def test_api_whitelist(botclient): "whitelist": ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'], "length": 4, "method": ["StaticPairList"] - } + } def test_api_forcebuy(botclient, mocker, fee): @@ -1033,7 +1033,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', - } + } def test_api_forcesell(botclient, mocker, ticker, fee, markets): @@ -1215,7 +1215,7 @@ def test_api_strategies(botclient): 'DefaultStrategy', 'HyperoptableStrategy', 'TestStrategyLegacy' - ]} + ]} def test_api_strategy(botclient): diff --git a/tests/test_arguments.py b/tests/test_arguments.py index fd6f162fd..5374881fa 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -125,7 +125,7 @@ def test_parse_args_backtesting_custom() -> None: '--strategy-list', 'DefaultStrategy', 'SampleStrategy' - ] + ] call_args = Arguments(args).get_parsed_arg() assert call_args['config'] == ['test_conf.json'] assert call_args['verbosity'] == 0 diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 7012333e9..f97ccd488 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1130,17 +1130,17 @@ def test_pairlist_resolving_fallback(mocker): @pytest.mark.parametrize("setting", [ - ("ask_strategy", "use_sell_signal", True, - None, "use_sell_signal", False), - ("ask_strategy", "sell_profit_only", True, - None, "sell_profit_only", False), - ("ask_strategy", "sell_profit_offset", 0.1, - None, "sell_profit_offset", 0.01), - ("ask_strategy", "ignore_roi_if_buy_signal", True, - None, "ignore_roi_if_buy_signal", False), - ("ask_strategy", "ignore_buying_expired_candle_after", 5, - None, "ignore_buying_expired_candle_after", 6), - ]) + ("ask_strategy", "use_sell_signal", True, + None, "use_sell_signal", False), + ("ask_strategy", "sell_profit_only", True, + None, "sell_profit_only", False), + ("ask_strategy", "sell_profit_offset", 0.1, + None, "sell_profit_offset", 0.01), + ("ask_strategy", "ignore_roi_if_buy_signal", True, + None, "ignore_roi_if_buy_signal", False), + ("ask_strategy", "ignore_buying_expired_candle_after", 5, + None, "ignore_buying_expired_candle_after", 6), +]) def test_process_temporary_deprecated_settings(mocker, default_conf, setting, caplog): patched_configuration_load_config_file(mocker, default_conf) @@ -1180,10 +1180,10 @@ def test_process_temporary_deprecated_settings(mocker, default_conf, setting, ca @pytest.mark.parametrize("setting", [ - ("experimental", "use_sell_signal", False), - ("experimental", "sell_profit_only", True), - ("experimental", "ignore_roi_if_buy_signal", True), - ]) + ("experimental", "use_sell_signal", False), + ("experimental", "sell_profit_only", True), + ("experimental", "ignore_roi_if_buy_signal", True), +]) def test_process_removed_settings(mocker, default_conf, setting): patched_configuration_load_config_file(mocker, default_conf) From 5393c55b513028a3159da23a19e241050766651b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Aug 2021 16:01:20 +0200 Subject: [PATCH 254/519] Document min_value for VolumePairList closes #5260 --- docs/includes/pairlists.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 995e49a2d..b92b05af9 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -58,7 +58,7 @@ This option must be configured along with `exchange.skip_pair_validation` in the When used in the chain of Pairlist Handlers in a non-leading position (after StaticPairList and other Pairlist Filters), `VolumePairList` considers outputs of previous Pairlist Handlers, adding its sorting/selection of the pairs by the trading volume. -When used on the leading position of the chain of Pairlist Handlers, it does not consider `pair_whitelist` configuration setting, but selects the top assets from all available markets (with matching stake-currency) on the exchange. +When used in the leading position of the chain of Pairlist Handlers, the `pair_whitelist` configuration setting is ignored. Instead, `VolumePairList` selects the top assets from all available markets with matching stake-currency on the exchange. The `refresh_period` setting allows to define the period (in seconds), at which the pairlist will be refreshed. Defaults to 1800s (30 minutes). The pairlist cache (`refresh_period`) on `VolumePairList` is only applicable to generating pairlists. @@ -74,11 +74,14 @@ Filtering instances (not the first position in the list) will not apply any cach "method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume", + "min_value": 0, "refresh_period": 1800 } ], ``` +You can define a minimum volume with `min_value` - which will filter out pairs with a volume lower than the specified value in the specified timerange. + `VolumePairList` can also operate in an advanced mode to build volume over a given timerange of specified candle size. It utilizes exchange historical candle data, builds a typical price (calculated by (open+high+low)/3) and multiplies the typical price with every candle's volume. The sum is the `quoteVolume` over the given range. This allows different scenarios, for a more smoothened volume, when using longer ranges with larger candle sizes, or the opposite when using a short range with small candles. For convenience `lookback_days` can be specified, which will imply that 1d candles will be used for the lookback. In the example below the pairlist would be created based on the last 7 days: @@ -89,6 +92,7 @@ For convenience `lookback_days` can be specified, which will imply that 1d candl "method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume", + "min_value": 0, "refresh_period": 86400, "lookback_days": 7 } From cf70b34ff0504dcbffc1106bfbefd521bc1c3357 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Aug 2021 16:15:42 +0200 Subject: [PATCH 255/519] Add min_value to all volumepairlist samples --- docs/includes/pairlists.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index b92b05af9..6e23c9003 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -113,6 +113,7 @@ More sophisticated approach can be used, by using `lookback_timeframe` for candl "method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume", + "min_value": 0, "refresh_period": 3600, "lookback_timeframe": "1h", "lookback_period": 72 From 7eaadb2630a99c8f467751bdff071b52cdb098b2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Aug 2021 09:50:50 +0200 Subject: [PATCH 256/519] Add custom-* methods to bot-basics closes #5370 --- docs/bot-basics.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 943af0362..44181abfa 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -35,7 +35,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Calls `check_buy_timeout()` strategy callback for open buy orders. * Calls `check_sell_timeout()` strategy callback for open sell orders. * Verifies existing positions and eventually places sell orders. - * Considers stoploss, ROI and sell-signal. + * Considers stoploss, ROI and sell-signal, `custom_sell()` and `custom_stoploss()`. * Determine sell-price based on `ask_strategy` configuration setting. * Before a sell order is placed, `confirm_trade_exit()` strategy callback is called. * Check if trade-slots are still available (if `max_open_trades` is reached). @@ -52,9 +52,10 @@ This loop will be repeated again and again until the bot is stopped. * Load historic data for configured pairlist. * Calls `bot_loop_start()` once. * Calculate indicators (calls `populate_indicators()` once per pair). -* Calculate buy / sell signals (calls `populate_buy_trend()` and `populate_sell_trend()` once per pair) -* Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy) +* Calculate buy / sell signals (calls `populate_buy_trend()` and `populate_sell_trend()` once per pair). * Loops per candle simulating entry and exit points. + * Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy). + * Call `custom_stoploss()` and `custom_sell()` to find custom exit points. * Generate backtest report output !!! Note From 0ae4eccea5ad7cd773e0d0e309617bfcf6a1e4ae Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Aug 2021 10:05:28 +0200 Subject: [PATCH 257/519] Refactor Hyperopt-list and hyperopt-show to reduce some duplicate code --- freqtrade/commands/hyperopt_commands.py | 180 +------------------ freqtrade/optimize/hyperopt_epoch_filters.py | 142 +++++++++++++++ freqtrade/optimize/hyperopt_tools.py | 28 ++- 3 files changed, 174 insertions(+), 176 deletions(-) create mode 100644 freqtrade/optimize/hyperopt_epoch_filters.py diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 4694d1111..089529d15 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -1,6 +1,6 @@ import logging from operator import itemgetter -from typing import Any, Dict, List +from typing import Any, Dict from colorama import init as colorama_init @@ -28,30 +28,12 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: no_details = config.get('hyperopt_list_no_details', False) no_header = False - filteroptions = { - 'only_best': config.get('hyperopt_list_best', False), - 'only_profitable': config.get('hyperopt_list_profitable', False), - 'filter_min_trades': config.get('hyperopt_list_min_trades', 0), - 'filter_max_trades': config.get('hyperopt_list_max_trades', 0), - 'filter_min_avg_time': config.get('hyperopt_list_min_avg_time', None), - 'filter_max_avg_time': config.get('hyperopt_list_max_avg_time', None), - 'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None), - 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), - 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), - 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None), - 'filter_min_objective': config.get('hyperopt_list_min_objective', None), - 'filter_max_objective': config.get('hyperopt_list_max_objective', None), - } - results_file = get_latest_hyperopt_file( config['user_data_dir'] / 'hyperopt_results', config.get('hyperoptexportfilename')) # Previous evaluations - epochs = HyperoptTools.load_previous_results(results_file) - total_epochs = len(epochs) - - epochs = hyperopt_filter_epochs(epochs, filteroptions) + epochs, total_epochs = HyperoptTools.load_filtered_results(results_file, config) if print_colorized: colorama_init(autoreset=True) @@ -59,7 +41,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: if not export_csv: try: print(HyperoptTools.get_result_table(config, epochs, total_epochs, - not filteroptions['only_best'], + not config.get('hyperopt_list_best', False), print_colorized, 0)) except KeyboardInterrupt: print('User interrupted..') @@ -71,7 +53,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: if epochs and export_csv: HyperoptTools.export_csv_file( - config, epochs, total_epochs, not filteroptions['only_best'], export_csv + config, epochs, total_epochs, not config.get('hyperopt_list_best', False), export_csv ) @@ -91,26 +73,9 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None: n = config.get('hyperopt_show_index', -1) - filteroptions = { - 'only_best': config.get('hyperopt_list_best', False), - 'only_profitable': config.get('hyperopt_list_profitable', False), - 'filter_min_trades': config.get('hyperopt_list_min_trades', 0), - 'filter_max_trades': config.get('hyperopt_list_max_trades', 0), - 'filter_min_avg_time': config.get('hyperopt_list_min_avg_time', None), - 'filter_max_avg_time': config.get('hyperopt_list_max_avg_time', None), - 'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None), - 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), - 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), - 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None), - 'filter_min_objective': config.get('hyperopt_list_min_objective', None), - 'filter_max_objective': config.get('hyperopt_list_max_objective', None) - } - # Previous evaluations - epochs = HyperoptTools.load_previous_results(results_file) - total_epochs = len(epochs) + epochs, total_epochs = HyperoptTools.load_filtered_results(results_file, config) - epochs = hyperopt_filter_epochs(epochs, filteroptions) filtered_epochs = len(epochs) if n > filtered_epochs: @@ -137,138 +102,3 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None: HyperoptTools.show_epoch_details(val, total_epochs, print_json, no_header, header_str="Epoch details") - - -def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: - """ - Filter our items from the list of hyperopt results - TODO: after 2021.5 remove all "legacy" mode queries. - """ - if filteroptions['only_best']: - epochs = [x for x in epochs if x['is_best']] - if filteroptions['only_profitable']: - epochs = [x for x in epochs if x['results_metrics'].get( - 'profit', x['results_metrics'].get('profit_total', 0)) > 0] - - epochs = _hyperopt_filter_epochs_trade_count(epochs, filteroptions) - - epochs = _hyperopt_filter_epochs_duration(epochs, filteroptions) - - epochs = _hyperopt_filter_epochs_profit(epochs, filteroptions) - - epochs = _hyperopt_filter_epochs_objective(epochs, filteroptions) - - logger.info(f"{len(epochs)} " + - ("best " if filteroptions['only_best'] else "") + - ("profitable " if filteroptions['only_profitable'] else "") + - "epochs found.") - return epochs - - -def _hyperopt_filter_epochs_trade(epochs: List, trade_count: int): - """ - Filter epochs with trade-counts > trades - """ - return [ - x for x in epochs - if x['results_metrics'].get( - 'trade_count', x['results_metrics'].get('total_trades', 0) - ) > trade_count - ] - - -def _hyperopt_filter_epochs_trade_count(epochs: List, filteroptions: dict) -> List: - - if filteroptions['filter_min_trades'] > 0: - epochs = _hyperopt_filter_epochs_trade(epochs, filteroptions['filter_min_trades']) - - if filteroptions['filter_max_trades'] > 0: - epochs = [ - x for x in epochs - if x['results_metrics'].get( - 'trade_count', x['results_metrics'].get('total_trades') - ) < filteroptions['filter_max_trades'] - ] - return epochs - - -def _hyperopt_filter_epochs_duration(epochs: List, filteroptions: dict) -> List: - - def get_duration_value(x): - # Duration in minutes ... - if 'duration' in x['results_metrics']: - return x['results_metrics']['duration'] - else: - # New mode - if 'holding_avg_s' in x['results_metrics']: - avg = x['results_metrics']['holding_avg_s'] - return avg // 60 - raise OperationalException( - "Holding-average not available. Please omit the filter on average time, " - "or rerun hyperopt with this version") - - if filteroptions['filter_min_avg_time'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - epochs = [ - x for x in epochs - if get_duration_value(x) > filteroptions['filter_min_avg_time'] - ] - if filteroptions['filter_max_avg_time'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - epochs = [ - x for x in epochs - if get_duration_value(x) < filteroptions['filter_max_avg_time'] - ] - - return epochs - - -def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List: - - if filteroptions['filter_min_avg_profit'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - epochs = [ - x for x in epochs - if x['results_metrics'].get( - 'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100 - ) > filteroptions['filter_min_avg_profit'] - ] - if filteroptions['filter_max_avg_profit'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - epochs = [ - x for x in epochs - if x['results_metrics'].get( - 'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100 - ) < filteroptions['filter_max_avg_profit'] - ] - if filteroptions['filter_min_total_profit'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - epochs = [ - x for x in epochs - if x['results_metrics'].get( - 'profit', x['results_metrics'].get('profit_total_abs', 0) - ) > filteroptions['filter_min_total_profit'] - ] - if filteroptions['filter_max_total_profit'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - epochs = [ - x for x in epochs - if x['results_metrics'].get( - 'profit', x['results_metrics'].get('profit_total_abs', 0) - ) < filteroptions['filter_max_total_profit'] - ] - return epochs - - -def _hyperopt_filter_epochs_objective(epochs: List, filteroptions: dict) -> List: - - if filteroptions['filter_min_objective'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - - epochs = [x for x in epochs if x['loss'] < filteroptions['filter_min_objective']] - if filteroptions['filter_max_objective'] is not None: - epochs = _hyperopt_filter_epochs_trade(epochs, 0) - - epochs = [x for x in epochs if x['loss'] > filteroptions['filter_max_objective']] - - return epochs diff --git a/freqtrade/optimize/hyperopt_epoch_filters.py b/freqtrade/optimize/hyperopt_epoch_filters.py new file mode 100644 index 000000000..2084c5ba8 --- /dev/null +++ b/freqtrade/optimize/hyperopt_epoch_filters.py @@ -0,0 +1,142 @@ +import logging +from typing import List + +from freqtrade.exceptions import OperationalException + + +logger = logging.getLogger(__name__) + + +def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: + """ + Filter our items from the list of hyperopt results + TODO: after 2021.5 remove all "legacy" mode queries. + """ + if filteroptions['only_best']: + epochs = [x for x in epochs if x['is_best']] + if filteroptions['only_profitable']: + epochs = [x for x in epochs if x['results_metrics'].get( + 'profit', x['results_metrics'].get('profit_total', 0)) > 0] + + epochs = _hyperopt_filter_epochs_trade_count(epochs, filteroptions) + + epochs = _hyperopt_filter_epochs_duration(epochs, filteroptions) + + epochs = _hyperopt_filter_epochs_profit(epochs, filteroptions) + + epochs = _hyperopt_filter_epochs_objective(epochs, filteroptions) + + logger.info(f"{len(epochs)} " + + ("best " if filteroptions['only_best'] else "") + + ("profitable " if filteroptions['only_profitable'] else "") + + "epochs found.") + return epochs + + +def _hyperopt_filter_epochs_trade(epochs: List, trade_count: int): + """ + Filter epochs with trade-counts > trades + """ + return [ + x for x in epochs + if x['results_metrics'].get( + 'trade_count', x['results_metrics'].get('total_trades', 0) + ) > trade_count + ] + + +def _hyperopt_filter_epochs_trade_count(epochs: List, filteroptions: dict) -> List: + + if filteroptions['filter_min_trades'] > 0: + epochs = _hyperopt_filter_epochs_trade(epochs, filteroptions['filter_min_trades']) + + if filteroptions['filter_max_trades'] > 0: + epochs = [ + x for x in epochs + if x['results_metrics'].get( + 'trade_count', x['results_metrics'].get('total_trades') + ) < filteroptions['filter_max_trades'] + ] + return epochs + + +def _hyperopt_filter_epochs_duration(epochs: List, filteroptions: dict) -> List: + + def get_duration_value(x): + # Duration in minutes ... + if 'duration' in x['results_metrics']: + return x['results_metrics']['duration'] + else: + # New mode + if 'holding_avg_s' in x['results_metrics']: + avg = x['results_metrics']['holding_avg_s'] + return avg // 60 + raise OperationalException( + "Holding-average not available. Please omit the filter on average time, " + "or rerun hyperopt with this version") + + if filteroptions['filter_min_avg_time'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + epochs = [ + x for x in epochs + if get_duration_value(x) > filteroptions['filter_min_avg_time'] + ] + if filteroptions['filter_max_avg_time'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + epochs = [ + x for x in epochs + if get_duration_value(x) < filteroptions['filter_max_avg_time'] + ] + + return epochs + + +def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List: + + if filteroptions['filter_min_avg_profit'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + epochs = [ + x for x in epochs + if x['results_metrics'].get( + 'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100 + ) > filteroptions['filter_min_avg_profit'] + ] + if filteroptions['filter_max_avg_profit'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + epochs = [ + x for x in epochs + if x['results_metrics'].get( + 'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100 + ) < filteroptions['filter_max_avg_profit'] + ] + if filteroptions['filter_min_total_profit'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + epochs = [ + x for x in epochs + if x['results_metrics'].get( + 'profit', x['results_metrics'].get('profit_total_abs', 0) + ) > filteroptions['filter_min_total_profit'] + ] + if filteroptions['filter_max_total_profit'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + epochs = [ + x for x in epochs + if x['results_metrics'].get( + 'profit', x['results_metrics'].get('profit_total_abs', 0) + ) < filteroptions['filter_max_total_profit'] + ] + return epochs + + +def _hyperopt_filter_epochs_objective(epochs: List, filteroptions: dict) -> List: + + if filteroptions['filter_min_objective'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + + epochs = [x for x in epochs if x['loss'] < filteroptions['filter_min_objective']] + if filteroptions['filter_max_objective'] is not None: + epochs = _hyperopt_filter_epochs_trade(epochs, 0) + + epochs = [x for x in epochs if x['loss'] > filteroptions['filter_max_objective']] + + return epochs diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 52aa85c84..ed9583bdc 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -4,7 +4,7 @@ import logging from copy import deepcopy from datetime import datetime, timezone from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple import numpy as np import rapidjson @@ -15,6 +15,7 @@ from pandas import isna, json_normalize from freqtrade.constants import FTHYPT_FILEVERSION, USERPATH_STRATEGIES from freqtrade.exceptions import OperationalException from freqtrade.misc import deep_merge_dicts, round_coin_value, round_dict, safe_value_fallback2 +from freqtrade.optimize.hyperopt_epoch_filters import hyperopt_filter_epochs logger = logging.getLogger(__name__) @@ -130,6 +131,31 @@ class HyperoptTools(): logger.info(f"Loaded {len(epochs)} previous evaluations from disk.") return epochs + @staticmethod + def load_filtered_results(results_file: Path, config: Dict[str, Any]) -> Tuple[List, int]: + filteroptions = { + 'only_best': config.get('hyperopt_list_best', False), + 'only_profitable': config.get('hyperopt_list_profitable', False), + 'filter_min_trades': config.get('hyperopt_list_min_trades', 0), + 'filter_max_trades': config.get('hyperopt_list_max_trades', 0), + 'filter_min_avg_time': config.get('hyperopt_list_min_avg_time', None), + 'filter_max_avg_time': config.get('hyperopt_list_max_avg_time', None), + 'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None), + 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), + 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), + 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None), + 'filter_min_objective': config.get('hyperopt_list_min_objective', None), + 'filter_max_objective': config.get('hyperopt_list_max_objective', None), + } + + # Previous evaluations + epochs = HyperoptTools.load_previous_results(results_file) + total_epochs = len(epochs) + + epochs = hyperopt_filter_epochs(epochs, filteroptions) + + return epochs, total_epochs + @staticmethod def show_epoch_details(results, total_epochs: int, print_json: bool, no_header: bool = False, header_str: str = None) -> None: From faf16a64e507dc684d527a804abef266e39c26c6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Aug 2021 10:22:45 +0200 Subject: [PATCH 258/519] Remove legacy hyperopt file support --- freqtrade/optimize/hyperopt_epoch_filters.py | 50 +- tests/commands/test_commands.py | 464 +++++++++---------- tests/conftest.py | 132 ------ 3 files changed, 249 insertions(+), 397 deletions(-) diff --git a/freqtrade/optimize/hyperopt_epoch_filters.py b/freqtrade/optimize/hyperopt_epoch_filters.py index 2084c5ba8..b70db94af 100644 --- a/freqtrade/optimize/hyperopt_epoch_filters.py +++ b/freqtrade/optimize/hyperopt_epoch_filters.py @@ -10,13 +10,12 @@ logger = logging.getLogger(__name__) def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: """ Filter our items from the list of hyperopt results - TODO: after 2021.5 remove all "legacy" mode queries. """ if filteroptions['only_best']: epochs = [x for x in epochs if x['is_best']] if filteroptions['only_profitable']: - epochs = [x for x in epochs if x['results_metrics'].get( - 'profit', x['results_metrics'].get('profit_total', 0)) > 0] + epochs = [x for x in epochs + if x['results_metrics'].get('profit_total', 0) > 0] epochs = _hyperopt_filter_epochs_trade_count(epochs, filteroptions) @@ -38,10 +37,7 @@ def _hyperopt_filter_epochs_trade(epochs: List, trade_count: int): Filter epochs with trade-counts > trades """ return [ - x for x in epochs - if x['results_metrics'].get( - 'trade_count', x['results_metrics'].get('total_trades', 0) - ) > trade_count + x for x in epochs if x['results_metrics'].get('total_trades', 0) > trade_count ] @@ -53,9 +49,7 @@ def _hyperopt_filter_epochs_trade_count(epochs: List, filteroptions: dict) -> Li if filteroptions['filter_max_trades'] > 0: epochs = [ x for x in epochs - if x['results_metrics'].get( - 'trade_count', x['results_metrics'].get('total_trades') - ) < filteroptions['filter_max_trades'] + if x['results_metrics'].get('total_trades') < filteroptions['filter_max_trades'] ] return epochs @@ -64,16 +58,12 @@ def _hyperopt_filter_epochs_duration(epochs: List, filteroptions: dict) -> List: def get_duration_value(x): # Duration in minutes ... - if 'duration' in x['results_metrics']: - return x['results_metrics']['duration'] - else: - # New mode - if 'holding_avg_s' in x['results_metrics']: - avg = x['results_metrics']['holding_avg_s'] - return avg // 60 - raise OperationalException( - "Holding-average not available. Please omit the filter on average time, " - "or rerun hyperopt with this version") + if 'holding_avg_s' in x['results_metrics']: + avg = x['results_metrics']['holding_avg_s'] + return avg // 60 + raise OperationalException( + "Holding-average not available. Please omit the filter on average time, " + "or rerun hyperopt with this version") if filteroptions['filter_min_avg_time'] is not None: epochs = _hyperopt_filter_epochs_trade(epochs, 0) @@ -97,33 +87,29 @@ def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List: epochs = _hyperopt_filter_epochs_trade(epochs, 0) epochs = [ x for x in epochs - if x['results_metrics'].get( - 'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100 - ) > filteroptions['filter_min_avg_profit'] + if x['results_metrics'].get('profit_mean', 0) * 100 + > filteroptions['filter_min_avg_profit'] ] if filteroptions['filter_max_avg_profit'] is not None: epochs = _hyperopt_filter_epochs_trade(epochs, 0) epochs = [ x for x in epochs - if x['results_metrics'].get( - 'avg_profit', x['results_metrics'].get('profit_mean', 0) * 100 - ) < filteroptions['filter_max_avg_profit'] + if x['results_metrics'].get('profit_mean', 0) * 100 + < filteroptions['filter_max_avg_profit'] ] if filteroptions['filter_min_total_profit'] is not None: epochs = _hyperopt_filter_epochs_trade(epochs, 0) epochs = [ x for x in epochs - if x['results_metrics'].get( - 'profit', x['results_metrics'].get('profit_total_abs', 0) - ) > filteroptions['filter_min_total_profit'] + if x['results_metrics'].get('profit_total_abs', 0) + > filteroptions['filter_min_total_profit'] ] if filteroptions['filter_max_total_profit'] is not None: epochs = _hyperopt_filter_epochs_trade(epochs, 0) epochs = [ x for x in epochs - if x['results_metrics'].get( - 'profit', x['results_metrics'].get('profit_total_abs', 0) - ) < filteroptions['filter_max_total_profit'] + if x['results_metrics'].get('profit_total_abs', 0) + < filteroptions['filter_max_total_profit'] ] return epochs diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index c0268038a..80dd04b27 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -938,241 +938,239 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): pytest.fail(f'Expected well formed JSON, but failed to parse: {captured.out}') -def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, - saved_hyperopt_results_legacy, tmpdir): +def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, tmpdir): csv_file = Path(tmpdir) / "test.csv" - for res in (saved_hyperopt_results, saved_hyperopt_results_legacy): - mocker.patch( - 'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results', - MagicMock(return_value=res) - ) + mocker.patch( + 'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results', + MagicMock(return_value=saved_hyperopt_results) + ) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", - " 6/12", " 7/12", " 8/12", " 9/12", " 10/12", - " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--best", - "--no-details", - "--no-color", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 1/12", " 5/12", " 10/12"]) - assert all(x not in captured.out - for x in [" 2/12", " 3/12", " 4/12", " 6/12", " 7/12", " 8/12", " 9/12", - " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--profitable", - "--no-details", - "--no-color", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 2/12", " 10/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", - " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--profitable", - "--no-color", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 2/12", " 10/12", "Best result:", "Buy hyperspace params", - "Sell hyperspace params", "ROI table", "Stoploss"]) - assert all(x not in captured.out - for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", - " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - "--min-trades", "20", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 3/12", " 6/12", " 7/12", " 9/12", " 11/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 2/12", " 4/12", " 5/12", " 8/12", " 10/12", " 12/12"]) - args = [ - "hyperopt-list", - "--profitable", - "--no-details", - "--no-color", - "--max-trades", "20", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 2/12", " 10/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", - " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--profitable", - "--no-details", - "--no-color", - "--min-avg-profit", "0.11", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 2/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", - " 10/12", " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - "--max-avg-profit", "0.10", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 1/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", - " 11/12"]) - assert all(x not in captured.out - for x in [" 2/12", " 4/12", " 10/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - "--min-total-profit", "0.4", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 10/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", - " 9/12", " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - "--max-total-profit", "0.4", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", - " 9/12", " 11/12"]) - assert all(x not in captured.out - for x in [" 4/12", " 10/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - "--min-objective", "0.1", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 10/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", - " 9/12", " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--max-objective", "0.1", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", - " 9/12", " 11/12"]) - assert all(x not in captured.out - for x in [" 4/12", " 10/12", " 12/12"]) - args = [ - "hyperopt-list", - "--profitable", - "--no-details", - "--no-color", - "--min-avg-time", "2000", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 10/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", - " 8/12", " 9/12", " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - "--max-avg-time", "1500", - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - assert all(x in captured.out - for x in [" 2/12", " 6/12"]) - assert all(x not in captured.out - for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12" - " 9/12", " 10/12", " 11/12", " 12/12"]) - args = [ - "hyperopt-list", - "--no-details", - "--no-color", - "--export-csv", - str(csv_file), - ] - pargs = get_args(args) - pargs['config'] = None - start_hyperopt_list(pargs) - captured = capsys.readouterr() - log_has("CSV file created: test_file.csv", caplog) - assert csv_file.is_file() - line = csv_file.read_text() - assert ('Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,"3,930.0 m",0.43662' in line - or "Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,2 days 17:30:00,0.43662" in line) - csv_file.unlink() + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", + " 6/12", " 7/12", " 8/12", " 9/12", " 10/12", + " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--best", + "--no-details", + "--no-color", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 1/12", " 5/12", " 10/12"]) + assert all(x not in captured.out + for x in [" 2/12", " 3/12", " 4/12", " 6/12", " 7/12", " 8/12", " 9/12", + " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--profitable", + "--no-details", + "--no-color", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 2/12", " 10/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", + " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--profitable", + "--no-color", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 2/12", " 10/12", "Best result:", "Buy hyperspace params", + "Sell hyperspace params", "ROI table", "Stoploss"]) + assert all(x not in captured.out + for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", + " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + "--min-trades", "20", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 3/12", " 6/12", " 7/12", " 9/12", " 11/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 2/12", " 4/12", " 5/12", " 8/12", " 10/12", " 12/12"]) + args = [ + "hyperopt-list", + "--profitable", + "--no-details", + "--no-color", + "--max-trades", "20", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 2/12", " 10/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", + " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--profitable", + "--no-details", + "--no-color", + "--min-avg-profit", "0.11", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 2/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", + " 10/12", " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + "--max-avg-profit", "0.10", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 1/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", " 9/12", + " 11/12"]) + assert all(x not in captured.out + for x in [" 2/12", " 4/12", " 10/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + "--min-total-profit", "0.4", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 10/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", + " 9/12", " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + "--max-total-profit", "0.4", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", + " 9/12", " 11/12"]) + assert all(x not in captured.out + for x in [" 4/12", " 10/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + "--min-objective", "0.1", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 10/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", " 8/12", + " 9/12", " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--max-objective", "0.1", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 5/12", " 6/12", " 7/12", " 8/12", + " 9/12", " 11/12"]) + assert all(x not in captured.out + for x in [" 4/12", " 10/12", " 12/12"]) + args = [ + "hyperopt-list", + "--profitable", + "--no-details", + "--no-color", + "--min-avg-time", "2000", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 10/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 2/12", " 3/12", " 4/12", " 5/12", " 6/12", " 7/12", + " 8/12", " 9/12", " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + "--max-avg-time", "1500", + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in [" 2/12", " 6/12"]) + assert all(x not in captured.out + for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12" + " 9/12", " 10/12", " 11/12", " 12/12"]) + args = [ + "hyperopt-list", + "--no-details", + "--no-color", + "--export-csv", + str(csv_file), + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + log_has("CSV file created: test_file.csv", caplog) + assert csv_file.is_file() + line = csv_file.read_text() + assert ('Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,"3,930.0 m",0.43662' in line + or "Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,2 days 17:30:00,0.43662" in line) + csv_file.unlink() def test_hyperopt_show(mocker, capsys, saved_hyperopt_results): diff --git a/tests/conftest.py b/tests/conftest.py index 1924e1f95..0c9a96e2b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1814,138 +1814,6 @@ def open_trade(): ) -@pytest.fixture -def saved_hyperopt_results_legacy(): - return [ - { - 'loss': 0.4366182531161519, - 'params_dict': { - 'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1190, 'roi_t2': 541, 'roi_t3': 408, 'roi_p1': 0.026035863879169705, 'roi_p2': 0.12508730043628782, 'roi_p3': 0.27766427921605896, 'stoploss': -0.2562930402099556}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4287874435315165, 408: 0.15112316431545753, 949: 0.026035863879169705, 2139: 0}, 'stoploss': {'stoploss': -0.2562930402099556}}, # noqa: E501 - 'results_metrics': {'trade_count': 2, 'avg_profit': -1.254995, 'median_profit': -1.2222, 'total_profit': -0.00125625, 'profit': -2.50999, 'duration': 3930.0}, # noqa: E501 - 'results_explanation': ' 2 trades. Avg profit -1.25%. Total profit -0.00125625 BTC ( -2.51Σ%). Avg duration 3930.0 min.', # noqa: E501 - 'total_profit': -0.00125625, - 'current_epoch': 1, - 'is_initial_point': True, - 'is_best': True - }, { - 'loss': 20.0, - 'params_dict': { - 'mfi-value': 17, 'fastd-value': 38, 'adx-value': 48, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 96, 'sell-fastd-value': 68, 'sell-adx-value': 63, 'sell-rsi-value': 81, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 334, 'roi_t2': 683, 'roi_t3': 140, 'roi_p1': 0.06403981740598495, 'roi_p2': 0.055519840060645045, 'roi_p3': 0.3253712811342459, 'stoploss': -0.338070047333259}, # noqa: E501 - 'params_details': { - 'buy': {'mfi-value': 17, 'fastd-value': 38, 'adx-value': 48, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, # noqa: E501 - 'sell': {'sell-mfi-value': 96, 'sell-fastd-value': 68, 'sell-adx-value': 63, 'sell-rsi-value': 81, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, # noqa: E501 - 'roi': {0: 0.4449309386008759, 140: 0.11955965746663, 823: 0.06403981740598495, 1157: 0}, # noqa: E501 - 'stoploss': {'stoploss': -0.338070047333259}}, - 'results_metrics': {'trade_count': 1, 'avg_profit': 0.12357, 'median_profit': -1.2222, 'total_profit': 6.185e-05, 'profit': 0.12357, 'duration': 1200.0}, # noqa: E501 - 'results_explanation': ' 1 trades. Avg profit 0.12%. Total profit 0.00006185 BTC ( 0.12Σ%). Avg duration 1200.0 min.', # noqa: E501 - 'total_profit': 6.185e-05, - 'current_epoch': 2, - 'is_initial_point': True, - 'is_best': False - }, { - 'loss': 14.241196856510731, - 'params_dict': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 889, 'roi_t2': 533, 'roi_t3': 263, 'roi_p1': 0.04759065393663096, 'roi_p2': 0.1488819964638463, 'roi_p3': 0.4102801822104605, 'stoploss': -0.05394588767607611}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.6067528326109377, 263: 0.19647265040047726, 796: 0.04759065393663096, 1685: 0}, 'stoploss': {'stoploss': -0.05394588767607611}}, # noqa: E501 - 'results_metrics': {'trade_count': 621, 'avg_profit': -0.43883302093397747, 'median_profit': -1.2222, 'total_profit': -0.13639474, 'profit': -272.515306, 'duration': 1691.207729468599}, # noqa: E501 - 'results_explanation': ' 621 trades. Avg profit -0.44%. Total profit -0.13639474 BTC (-272.52Σ%). Avg duration 1691.2 min.', # noqa: E501 - 'total_profit': -0.13639474, - 'current_epoch': 3, - 'is_initial_point': True, - 'is_best': False - }, { - 'loss': 100000, - 'params_dict': {'mfi-value': 13, 'fastd-value': 35, 'adx-value': 39, 'rsi-value': 29, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 54, 'sell-adx-value': 63, 'sell-rsi-value': 93, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1402, 'roi_t2': 676, 'roi_t3': 215, 'roi_p1': 0.06264755784937427, 'roi_p2': 0.14258587851894644, 'roi_p3': 0.20671291201040828, 'stoploss': -0.11818343570194478}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 13, 'fastd-value': 35, 'adx-value': 39, 'rsi-value': 29, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 54, 'sell-adx-value': 63, 'sell-rsi-value': 93, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.411946348378729, 215: 0.2052334363683207, 891: 0.06264755784937427, 2293: 0}, 'stoploss': {'stoploss': -0.11818343570194478}}, # noqa: E501 - 'results_metrics': {'trade_count': 0, 'avg_profit': None, 'median_profit': None, 'total_profit': 0, 'profit': 0.0, 'duration': None}, # noqa: E501 - 'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501 - 'total_profit': 0, 'current_epoch': 4, 'is_initial_point': True, 'is_best': False - }, { - 'loss': 0.22195522184191518, - 'params_dict': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 1269, 'roi_t2': 601, 'roi_t3': 444, 'roi_p1': 0.07280999507931168, 'roi_p2': 0.08946698095898986, 'roi_p3': 0.1454876733325284, 'stoploss': -0.18181041180901014}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3077646493708299, 444: 0.16227697603830155, 1045: 0.07280999507931168, 2314: 0}, 'stoploss': {'stoploss': -0.18181041180901014}}, # noqa: E501 - 'results_metrics': {'trade_count': 14, 'avg_profit': -0.3539515, 'median_profit': -1.2222, 'total_profit': -0.002480140000000001, 'profit': -4.955321, 'duration': 3402.8571428571427}, # noqa: E501 - 'results_explanation': ' 14 trades. Avg profit -0.35%. Total profit -0.00248014 BTC ( -4.96Σ%). Avg duration 3402.9 min.', # noqa: E501 - 'total_profit': -0.002480140000000001, - 'current_epoch': 5, - 'is_initial_point': True, - 'is_best': True - }, { - 'loss': 0.545315889154162, - 'params_dict': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower', 'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 319, 'roi_t2': 556, 'roi_t3': 216, 'roi_p1': 0.06251955472249589, 'roi_p2': 0.11659519602202795, 'roi_p3': 0.0953744132197762, 'stoploss': -0.024551752215582423}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.2744891639643, 216: 0.17911475074452382, 772: 0.06251955472249589, 1091: 0}, 'stoploss': {'stoploss': -0.024551752215582423}}, # noqa: E501 - 'results_metrics': {'trade_count': 39, 'avg_profit': -0.21400679487179478, 'median_profit': -1.2222, 'total_profit': -0.0041773, 'profit': -8.346264999999997, 'duration': 636.9230769230769}, # noqa: E501 - 'results_explanation': ' 39 trades. Avg profit -0.21%. Total profit -0.00417730 BTC ( -8.35Σ%). Avg duration 636.9 min.', # noqa: E501 - 'total_profit': -0.0041773, - 'current_epoch': 6, - 'is_initial_point': True, - 'is_best': False - }, { - 'loss': 4.713497421432944, - 'params_dict': {'mfi-value': 13, 'fastd-value': 41, 'adx-value': 21, 'rsi-value': 29, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower', 'sell-mfi-value': 99, 'sell-fastd-value': 60, 'sell-adx-value': 81, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 771, 'roi_t2': 620, 'roi_t3': 145, 'roi_p1': 0.0586919200378493, 'roi_p2': 0.04984118697312542, 'roi_p3': 0.37521058680247044, 'stoploss': -0.14613268022709905}, # noqa: E501 - 'params_details': { - 'buy': {'mfi-value': 13, 'fastd-value': 41, 'adx-value': 21, 'rsi-value': 29, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 99, 'sell-fastd-value': 60, 'sell-adx-value': 81, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.4837436938134452, 145: 0.10853310701097472, 765: 0.0586919200378493, 1536: 0}, # noqa: E501 - 'stoploss': {'stoploss': -0.14613268022709905}}, # noqa: E501 - 'results_metrics': {'trade_count': 318, 'avg_profit': -0.39833954716981146, 'median_profit': -1.2222, 'total_profit': -0.06339929, 'profit': -126.67197600000004, 'duration': 3140.377358490566}, # noqa: E501 - 'results_explanation': ' 318 trades. Avg profit -0.40%. Total profit -0.06339929 BTC (-126.67Σ%). Avg duration 3140.4 min.', # noqa: E501 - 'total_profit': -0.06339929, - 'current_epoch': 7, - 'is_initial_point': True, - 'is_best': False - }, { - 'loss': 20.0, # noqa: E501 - 'params_dict': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal', 'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 1149, 'roi_t2': 375, 'roi_t3': 289, 'roi_p1': 0.05571820757172588, 'roi_p2': 0.0606240398618907, 'roi_p3': 0.1729012220156157, 'stoploss': -0.1588514289110401}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.2892434694492323, 289: 0.11634224743361658, 664: 0.05571820757172588, 1813: 0}, 'stoploss': {'stoploss': -0.1588514289110401}}, # noqa: E501 - 'results_metrics': {'trade_count': 1, 'avg_profit': 0.0, 'median_profit': 0.0, 'total_profit': 0.0, 'profit': 0.0, 'duration': 5340.0}, # noqa: E501 - 'results_explanation': ' 1 trades. Avg profit 0.00%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration 5340.0 min.', # noqa: E501 - 'total_profit': 0.0, - 'current_epoch': 8, - 'is_initial_point': True, - 'is_best': False - }, { - 'loss': 2.4731817780991223, - 'params_dict': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1012, 'roi_t2': 584, 'roi_t3': 422, 'roi_p1': 0.036764323603472565, 'roi_p2': 0.10335480573205287, 'roi_p3': 0.10322347377503042, 'stoploss': -0.2780610808108503}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.2433426031105559, 422: 0.14011912933552545, 1006: 0.036764323603472565, 2018: 0}, 'stoploss': {'stoploss': -0.2780610808108503}}, # noqa: E501 - 'results_metrics': {'trade_count': 229, 'avg_profit': -0.38433433624454144, 'median_profit': -1.2222, 'total_profit': -0.044050070000000004, 'profit': -88.01256299999999, 'duration': 6505.676855895196}, # noqa: E501 - 'results_explanation': ' 229 trades. Avg profit -0.38%. Total profit -0.04405007 BTC ( -88.01Σ%). Avg duration 6505.7 min.', # noqa: E501 - 'total_profit': -0.044050070000000004, # noqa: E501 - 'current_epoch': 9, - 'is_initial_point': True, - 'is_best': False - }, { - 'loss': -0.2604606005845212, # noqa: E501 - 'params_dict': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 792, 'roi_t2': 464, 'roi_t3': 215, 'roi_p1': 0.04594053535385903, 'roi_p2': 0.09623192684243963, 'roi_p3': 0.04428219070850663, 'stoploss': -0.16992287161634415}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.18645465290480528, 215: 0.14217246219629864, 679: 0.04594053535385903, 1471: 0}, 'stoploss': {'stoploss': -0.16992287161634415}}, # noqa: E501 - 'results_metrics': {'trade_count': 4, 'avg_profit': 0.1080385, 'median_profit': -1.2222, 'total_profit': 0.00021629, 'profit': 0.432154, 'duration': 2850.0}, # noqa: E501 - 'results_explanation': ' 4 trades. Avg profit 0.11%. Total profit 0.00021629 BTC ( 0.43Σ%). Avg duration 2850.0 min.', # noqa: E501 - 'total_profit': 0.00021629, - 'current_epoch': 10, - 'is_initial_point': True, - 'is_best': True - }, { - 'loss': 4.876465945994304, # noqa: E501 - 'params_dict': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower', 'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 579, 'roi_t2': 614, 'roi_t3': 273, 'roi_p1': 0.05307643172744114, 'roi_p2': 0.1352282078262871, 'roi_p3': 0.1913307406325751, 'stoploss': -0.25728526022513887}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3796353801863034, 273: 0.18830463955372825, 887: 0.05307643172744114, 1466: 0}, 'stoploss': {'stoploss': -0.25728526022513887}}, # noqa: E501 - 'results_metrics': {'trade_count': 117, 'avg_profit': -1.2698609145299145, 'median_profit': -1.2222, 'total_profit': -0.07436117, 'profit': -148.573727, 'duration': 4282.5641025641025}, # noqa: E501 - 'results_explanation': ' 117 trades. Avg profit -1.27%. Total profit -0.07436117 BTC (-148.57Σ%). Avg duration 4282.6 min.', # noqa: E501 - 'total_profit': -0.07436117, - 'current_epoch': 11, - 'is_initial_point': True, - 'is_best': False - }, { - 'loss': 100000, - 'params_dict': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1156, 'roi_t2': 581, 'roi_t3': 408, 'roi_p1': 0.06860454019988212, 'roi_p2': 0.12473718444931989, 'roi_p3': 0.2896360635226823, 'stoploss': -0.30889015124682806}, # noqa: E501 - 'params_details': {'buy': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4829777881718843, 408: 0.19334172464920202, 989: 0.06860454019988212, 2145: 0}, 'stoploss': {'stoploss': -0.30889015124682806}}, # noqa: E501 - 'results_metrics': {'trade_count': 0, 'avg_profit': None, 'median_profit': None, 'total_profit': 0, 'profit': 0.0, 'duration': None}, # noqa: E501 - 'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501 - 'total_profit': 0, - 'current_epoch': 12, - 'is_initial_point': True, - 'is_best': False - } - ] - - @pytest.fixture def saved_hyperopt_results(): hyperopt_res = [ From be240566ba1df4eb282b79f17e69adbd02a2e401 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Aug 2021 10:57:20 +0200 Subject: [PATCH 259/519] Fix random test failure --- tests/strategy/test_interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 0ad6d6f32..dc51f0811 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -552,6 +552,7 @@ def test__analyze_ticker_internal_skip_analyze(ohlcv_history, mocker, caplog) -> def test_is_pair_locked(default_conf): default_conf.update({'strategy': 'DefaultStrategy'}) PairLocks.timeframe = default_conf['timeframe'] + PairLocks.use_db = True strategy = StrategyResolver.load_strategy(default_conf) # No lock should be present assert len(PairLocks.get_pair_locks(None)) == 0 From 3bd0c3d0098bbcab9a9b8444e785ff43fbfc9fe7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Aug 2021 11:02:54 +0200 Subject: [PATCH 260/519] Remove legacy code from export to csv --- freqtrade/optimize/hyperopt_tools.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index ed9583bdc..b51db4db2 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -459,21 +459,14 @@ class HyperoptTools(): trials['Best'] = '' trials['Stake currency'] = config['stake_currency'] - if 'results_metrics.total_trades' in trials: - base_metrics = ['Best', 'current_epoch', 'results_metrics.total_trades', - 'results_metrics.profit_mean', 'results_metrics.profit_median', - 'results_metrics.profit_total', - 'Stake currency', - 'results_metrics.profit_total_abs', 'results_metrics.holding_avg', - 'loss', 'is_initial_point', 'is_best'] - perc_multi = 100 - else: - perc_multi = 1 - base_metrics = ['Best', 'current_epoch', 'results_metrics.trade_count', - 'results_metrics.avg_profit', 'results_metrics.median_profit', - 'results_metrics.total_profit', - 'Stake currency', 'results_metrics.profit', 'results_metrics.duration', - 'loss', 'is_initial_point', 'is_best'] + base_metrics = ['Best', 'current_epoch', 'results_metrics.total_trades', + 'results_metrics.profit_mean', 'results_metrics.profit_median', + 'results_metrics.profit_total', + 'Stake currency', + 'results_metrics.profit_total_abs', 'results_metrics.holding_avg', + 'loss', 'is_initial_point', 'is_best'] + perc_multi = 100 + param_metrics = [("params_dict."+param) for param in results[0]['params_dict'].keys()] trials = trials[base_metrics + param_metrics] @@ -501,11 +494,6 @@ class HyperoptTools(): trials['Avg profit'] = trials['Avg profit'].apply( lambda x: f'{x * perc_multi:,.2f}%' if not isna(x) else "" ) - if perc_multi == 1: - trials['Avg duration'] = trials['Avg duration'].apply( - lambda x: f'{x:,.1f} m' if isinstance( - x, float) else f"{x.total_seconds() // 60:,.1f} m" if not isna(x) else "" - ) trials['Objective'] = trials['Objective'].apply( lambda x: f'{x:,.5f}' if x != 100000 else "" ) From 32e8e3b24258e1a7d12bde1d107d222ef1ee8fd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 03:01:19 +0000 Subject: [PATCH 261/519] Bump types-cachetools from 0.1.9 to 0.1.10 Bumps [types-cachetools](https://github.com/python/typeshed) from 0.1.9 to 0.1.10. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-cachetools dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index c1f7d6486..7b7727bd5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -19,7 +19,7 @@ isort==5.9.3 nbconvert==6.1.0 # mypy types -types-cachetools==0.1.9 +types-cachetools==0.1.10 types-filelock==0.1.4 types-requests==2.25.1 types-tabulate==0.1.1 From bad25b753c679218557fce23488b13bfe20bc609 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 03:01:25 +0000 Subject: [PATCH 262/519] Bump ccxt from 1.54.24 to 1.54.62 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.54.24 to 1.54.62. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.54.24...1.54.62) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6c2ef56c3..60175c12f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.1 pandas==1.3.1 -ccxt==1.54.24 +ccxt==1.54.62 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 59626b4ffcaa7f348d8e6986b274d32a65b54009 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 03:01:27 +0000 Subject: [PATCH 263/519] Bump types-tabulate from 0.1.1 to 0.8.2 Bumps [types-tabulate](https://github.com/python/typeshed) from 0.1.1 to 0.8.2. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-tabulate dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index c1f7d6486..cbb59be4e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -22,4 +22,4 @@ nbconvert==6.1.0 types-cachetools==0.1.9 types-filelock==0.1.4 types-requests==2.25.1 -types-tabulate==0.1.1 +types-tabulate==0.8.2 From b89a993890224ead95a8652bc0a83dde89c87838 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 04:47:41 +0000 Subject: [PATCH 264/519] Bump types-filelock from 0.1.4 to 0.1.5 Bumps [types-filelock](https://github.com/python/typeshed) from 0.1.4 to 0.1.5. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-filelock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 8f0cc0b34..7128721c5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,6 +20,6 @@ nbconvert==6.1.0 # mypy types types-cachetools==0.1.10 -types-filelock==0.1.4 +types-filelock==0.1.5 types-requests==2.25.1 types-tabulate==0.8.2 From 47f641d12f9d7813f80369f2691a1633168331a1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 07:03:13 +0200 Subject: [PATCH 265/519] Remove hyperopt-pickle result support --- freqtrade/optimize/hyperopt_tools.py | 17 ++++------------- tests/optimize/test_hyperopt_tools.py | 27 +++------------------------ 2 files changed, 7 insertions(+), 37 deletions(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index b51db4db2..0bb6aba15 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -89,18 +89,6 @@ class HyperoptTools(): else: return any(s in config['spaces'] for s in [space, 'all', 'default']) - @staticmethod - def _read_results_pickle(results_file: Path) -> List: - """ - Read hyperopt results from pickle file - LEGACY method - new files are written as json and cannot be read with this method. - """ - from joblib import load - - logger.info(f"Reading pickled epochs from '{results_file}'") - data = load(results_file) - return data - @staticmethod def _read_results(results_file: Path) -> List: """ @@ -120,7 +108,10 @@ class HyperoptTools(): epochs: List = [] if results_file.is_file() and results_file.stat().st_size > 0: if results_file.suffix == '.pickle': - epochs = HyperoptTools._read_results_pickle(results_file) + raise OperationalException( + "Legacy hyperopt results are no longer supported." + "Please rerun hyperopt or use an older version to load this file." + ) else: epochs = HyperoptTools._read_results(results_file) # Detection of some old format, without 'is_best' field saved diff --git a/tests/optimize/test_hyperopt_tools.py b/tests/optimize/test_hyperopt_tools.py index 44b4a7a03..d59a44da7 100644 --- a/tests/optimize/test_hyperopt_tools.py +++ b/tests/optimize/test_hyperopt_tools.py @@ -10,7 +10,7 @@ import rapidjson from freqtrade.constants import FTHYPT_FILEVERSION from freqtrade.exceptions import OperationalException from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer -from tests.conftest import log_has, log_has_re +from tests.conftest import log_has # Functions for recurrent object patching @@ -37,31 +37,10 @@ def test_save_results_saves_epochs(hyperopt, tmpdir, caplog) -> None: assert len(hyperopt_epochs) == 2 -def test_load_previous_results(testdatadir, caplog) -> None: - - results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle' - - hyperopt_epochs = HyperoptTools.load_previous_results(results_file) - - assert len(hyperopt_epochs) == 5 - assert log_has_re(r"Reading pickled epochs from .*", caplog) - - caplog.clear() - - # Modern version - results_file = testdatadir / 'strategy_SampleStrategy.fthypt' - - hyperopt_epochs = HyperoptTools.load_previous_results(results_file) - - assert len(hyperopt_epochs) == 5 - assert log_has_re(r"Reading epochs from .*", caplog) - - def test_load_previous_results2(mocker, testdatadir, caplog) -> None: - mocker.patch('freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results_pickle', - return_value=[{'asdf': '222'}]) results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle' - with pytest.raises(OperationalException, match=r"The file .* incompatible.*"): + with pytest.raises(OperationalException, + match=r"Legacy hyperopt results are no longer supported.*"): HyperoptTools.load_previous_results(results_file) From 5919992ad2ab67ad3aaffd92bdaa6ccec30fd537 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 05:24:31 +0000 Subject: [PATCH 266/519] Bump types-requests from 2.25.1 to 2.25.6 Bumps [types-requests](https://github.com/python/typeshed) from 2.25.1 to 2.25.6. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 7128721c5..9629bbea1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -21,5 +21,5 @@ nbconvert==6.1.0 # mypy types types-cachetools==0.1.10 types-filelock==0.1.5 -types-requests==2.25.1 +types-requests==2.25.6 types-tabulate==0.8.2 From f17942b68fa23a5dc727cba463e92f0a71e8cfb3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 11:18:18 +0200 Subject: [PATCH 267/519] Fix random test failure --- freqtrade/optimize/backtesting.py | 3 +++ tests/optimize/test_backtesting.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 3079e326d..85137a8ef 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -130,6 +130,9 @@ class Backtesting: self.abort = False def __del__(self): + self.cleanup() + + def cleanup(self): LoggingMixin.show_output = True PairLocks.use_db = True Trade.use_db = True diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index deaaf9f2f..b859e9017 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -535,6 +535,8 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: trade = backtesting._enter_trade(pair, row=row) assert trade is None + backtesting.cleanup() + def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: default_conf['use_sell_signal'] = False From 927ac24f82f66c3932936ea451f39efde193adfd Mon Sep 17 00:00:00 2001 From: pcassimans Date: Mon, 9 Aug 2021 14:21:59 +0200 Subject: [PATCH 268/519] Update README.md Fix Typo of Kukoin to Kucoin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78ea3cecd..309fab94b 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Please read the [exchange specific notes](docs/exchanges.md) to learn about even Exchanges confirmed working by the community: - [X] [Bitvavo](https://bitvavo.com/) -- [X] [Kukoin](https://www.kucoin.com/) +- [X] [Kucoin](https://www.kucoin.com/) ## Documentation From 519c256b88e31f8347bd5bf7395a08d0c774fdae Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 14:43:35 +0200 Subject: [PATCH 269/519] Fix kucoin typo in index.md as well --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 05eaa7552..fd3b8f224 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,7 +47,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual, Exchanges confirmed working by the community: - [X] [Bitvavo](https://bitvavo.com/) -- [X] [Kukoin](https://www.kucoin.com/) +- [X] [Kucoin](https://www.kucoin.com/) ## Requirements From a5f796bc9764040be80478deb246fe08b814be9f Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 14:53:18 +0200 Subject: [PATCH 270/519] refactor ohlcvdata_to_dataframe to advise_all_indicators --- freqtrade/edge/edge_positioning.py | 2 +- freqtrade/optimize/backtesting.py | 5 +++-- freqtrade/optimize/hyperopt.py | 2 +- freqtrade/strategy/interface.py | 2 +- tests/data/test_history.py | 6 +++--- tests/optimize/test_backtesting.py | 18 +++++++++--------- tests/optimize/test_hyperopt.py | 24 ++++++++++++------------ tests/strategy/test_interface.py | 8 ++++---- 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/freqtrade/edge/edge_positioning.py b/freqtrade/edge/edge_positioning.py index 243043d31..f12b1b37d 100644 --- a/freqtrade/edge/edge_positioning.py +++ b/freqtrade/edge/edge_positioning.py @@ -151,7 +151,7 @@ class Edge: # Fake run-mode to Edge prior_rm = self.config['runmode'] self.config['runmode'] = RunMode.EDGE - preprocessed = self.strategy.ohlcvdata_to_dataframe(data) + preprocessed = self.strategy.advise_all_indicators(data) self.config['runmode'] = prior_rm # Print timeframe diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 85137a8ef..825d1dd25 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -533,7 +533,8 @@ class Backtesting: 'final_balance': self.wallets.get_total(self.strategy.config['stake_currency']), } - def backtest_one_strategy(self, strat: IStrategy, data: Dict[str, Any], timerange: TimeRange): + def backtest_one_strategy(self, strat: IStrategy, data: Dict[str, DataFrame], + timerange: TimeRange): self.progress.init_step(BacktestState.ANALYZE, 0) logger.info("Running backtesting for Strategy %s", strat.get_strategy_name()) @@ -552,7 +553,7 @@ class Backtesting: max_open_trades = 0 # need to reprocess data every time to populate signals - preprocessed = self.strategy.ohlcvdata_to_dataframe(data) + preprocessed = self.strategy.advise_all_indicators(data) # Trim startup period from analyzed dataframe preprocessed_tmp = trim_dataframes(preprocessed, timerange, self.required_startup) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 0db78aa39..901900121 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -394,7 +394,7 @@ class Hyperopt: data, timerange = self.backtesting.load_bt_data() logger.info("Dataload complete. Calculating indicators") - preprocessed = self.backtesting.strategy.ohlcvdata_to_dataframe(data) + preprocessed = self.backtesting.strategy.advise_all_indicators(data) # Trim startup period from analyzed dataframe to get correct dates for output. processed = trim_dataframes(preprocessed, timerange, self.backtesting.required_startup) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index bf5cc10af..d4f10301a 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -732,7 +732,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: return current_profit > roi - def ohlcvdata_to_dataframe(self, data: Dict[str, DataFrame]) -> Dict[str, DataFrame]: + def advise_all_indicators(self, data: Dict[str, DataFrame]) -> Dict[str, DataFrame]: """ Populates indicators for given candle (OHLCV) data (for multiple pairs) Does not run advise_buy or advise_sell! diff --git a/tests/data/test_history.py b/tests/data/test_history.py index d203d0792..9cfe861ea 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -381,7 +381,7 @@ def test_get_timerange(default_conf, mocker, testdatadir) -> None: default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) - data = strategy.ohlcvdata_to_dataframe( + data = strategy.advise_all_indicators( load_data( datadir=testdatadir, timeframe='1m', @@ -399,7 +399,7 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir) default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) - data = strategy.ohlcvdata_to_dataframe( + data = strategy.advise_all_indicators( load_data( datadir=testdatadir, timeframe='1m', @@ -424,7 +424,7 @@ def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> No strategy = StrategyResolver.load_strategy(default_conf) timerange = TimeRange('index', 'index', 200, 250) - data = strategy.ohlcvdata_to_dataframe( + data = strategy.advise_all_indicators( load_data( datadir=testdatadir, timeframe='5m', diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index b859e9017..8e3d4063a 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -85,7 +85,7 @@ def simple_backtest(config, contour, mocker, testdatadir) -> None: backtesting._set_strategy(backtesting.strategylist[0]) data = load_data_test(contour, testdatadir) - processed = backtesting.strategy.ohlcvdata_to_dataframe(data) + processed = backtesting.strategy.advise_all_indicators(data) min_date, max_date = get_timerange(processed) assert isinstance(processed, dict) results = backtesting.backtest( @@ -107,7 +107,7 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'): patch_exchange(mocker) backtesting = Backtesting(conf) backtesting._set_strategy(backtesting.strategylist[0]) - processed = backtesting.strategy.ohlcvdata_to_dataframe(data) + processed = backtesting.strategy.advise_all_indicators(data) min_date, max_date = get_timerange(processed) return { 'processed': processed, @@ -289,7 +289,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None: backtesting._set_strategy(backtesting.strategylist[0]) assert backtesting.config == default_conf assert backtesting.timeframe == '5m' - assert callable(backtesting.strategy.ohlcvdata_to_dataframe) + assert callable(backtesting.strategy.advise_all_indicators) assert callable(backtesting.strategy.advise_buy) assert callable(backtesting.strategy.advise_sell) assert isinstance(backtesting.strategy.dp, DataProvider) @@ -335,14 +335,14 @@ def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None: fill_up_missing=True) backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) - processed = backtesting.strategy.ohlcvdata_to_dataframe(data) + processed = backtesting.strategy.advise_all_indicators(data) assert len(processed['UNITTEST/BTC']) == 102 # Load strategy to compare the result between Backtesting function and strategy are the same default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) - processed2 = strategy.ohlcvdata_to_dataframe(data) + processed2 = strategy.advise_all_indicators(data) assert processed['UNITTEST/BTC'].equals(processed2['UNITTEST/BTC']) @@ -549,7 +549,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: timerange = TimeRange('date', None, 1517227800, 0) data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], timerange=timerange) - processed = backtesting.strategy.ohlcvdata_to_dataframe(data) + processed = backtesting.strategy.advise_all_indicators(data) min_date, max_date = get_timerange(processed) result = backtesting.backtest( processed=processed, @@ -614,7 +614,7 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None timerange = TimeRange.parse_timerange('1510688220-1510700340') data = history.load_data(datadir=testdatadir, timeframe='1m', pairs=['UNITTEST/BTC'], timerange=timerange) - processed = backtesting.strategy.ohlcvdata_to_dataframe(data) + processed = backtesting.strategy.advise_all_indicators(data) min_date, max_date = get_timerange(processed) results = backtesting.backtest( processed=processed, @@ -633,7 +633,7 @@ def test_processed(default_conf, mocker, testdatadir) -> None: backtesting._set_strategy(backtesting.strategylist[0]) dict_of_tickerrows = load_data_test('raise', testdatadir) - dataframes = backtesting.strategy.ohlcvdata_to_dataframe(dict_of_tickerrows) + dataframes = backtesting.strategy.advise_all_indicators(dict_of_tickerrows) dataframe = dataframes['UNITTEST/BTC'] cols = dataframe.columns # assert the dataframe got some of the indicator columns @@ -782,7 +782,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) backtesting.strategy.advise_buy = _trend_alternate_hold # Override backtesting.strategy.advise_sell = _trend_alternate_hold # Override - processed = backtesting.strategy.ohlcvdata_to_dataframe(data) + processed = backtesting.strategy.advise_all_indicators(data) min_date, max_date = get_timerange(processed) backtest_conf = { 'processed': processed, diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index d146e84f1..0ca79d268 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -351,7 +351,7 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None: del hyperopt_conf['timeframe'] hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.start() @@ -426,7 +426,7 @@ def test_hyperopt_format_results(hyperopt): def test_populate_indicators(hyperopt, testdatadir) -> None: data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) - dataframes = hyperopt.backtesting.strategy.ohlcvdata_to_dataframe(data) + dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], {'pair': 'UNITTEST/BTC'}) @@ -438,7 +438,7 @@ def test_populate_indicators(hyperopt, testdatadir) -> None: def test_buy_strategy_generator(hyperopt, testdatadir) -> None: data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) - dataframes = hyperopt.backtesting.strategy.ohlcvdata_to_dataframe(data) + dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], {'pair': 'UNITTEST/BTC'}) @@ -463,7 +463,7 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None: def test_sell_strategy_generator(hyperopt, testdatadir) -> None: data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) - dataframes = hyperopt.backtesting.strategy.ohlcvdata_to_dataframe(data) + dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], {'pair': 'UNITTEST/BTC'}) @@ -660,7 +660,7 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None: }) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.start() @@ -713,7 +713,7 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None: hyperopt_conf.update({'print_json': True}) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.start() @@ -761,7 +761,7 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None: }) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.start() @@ -805,7 +805,7 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non hyperopt_conf.update({'spaces': 'roi stoploss'}) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) del hyperopt.custom_hyperopt.__class__.buy_strategy_generator @@ -844,7 +844,7 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None: hyperopt_conf.update({'spaces': 'all', }) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) del hyperopt.custom_hyperopt.__class__.buy_strategy_generator @@ -886,7 +886,7 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None: hyperopt_conf.update({'spaces': 'buy'}) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) # TODO: sell_strategy_generator() is actually not called because @@ -940,7 +940,7 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None: hyperopt_conf.update({'spaces': 'sell', }) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) # TODO: buy_strategy_generator() is actually not called because @@ -985,7 +985,7 @@ def test_simplified_interface_failed(mocker, hyperopt_conf, method, space) -> No hyperopt_conf.update({'spaces': space}) hyperopt = Hyperopt(hyperopt_conf) - hyperopt.backtesting.strategy.ohlcvdata_to_dataframe = MagicMock() + hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) delattr(hyperopt.custom_hyperopt.__class__, method) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index dc51f0811..686a36c96 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -228,25 +228,25 @@ def test_assert_df(ohlcv_history, caplog): _STRATEGY.disable_dataframe_checks = False -def test_ohlcvdata_to_dataframe(default_conf, testdatadir) -> None: +def test_advise_all_indicators(default_conf, testdatadir) -> None: default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) timerange = TimeRange.parse_timerange('1510694220-1510700340') data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange, fill_up_missing=True) - processed = strategy.ohlcvdata_to_dataframe(data) + processed = strategy.advise_all_indicators(data) assert len(processed['UNITTEST/BTC']) == 102 # partial candle was removed -def test_ohlcvdata_to_dataframe_copy(mocker, default_conf, testdatadir) -> None: +def test_advise_all_indicators_copy(mocker, default_conf, testdatadir) -> None: default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) aimock = mocker.patch('freqtrade.strategy.interface.IStrategy.advise_indicators') timerange = TimeRange.parse_timerange('1510694220-1510700340') data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange, fill_up_missing=True) - strategy.ohlcvdata_to_dataframe(data) + strategy.advise_all_indicators(data) assert aimock.call_count == 1 # Ensure that a copy of the dataframe is passed to advice_indicators assert aimock.call_args_list[0][0][0] is not data From 895b912c719da2f613c5ae5da47cfb375bf771b8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 14:40:02 +0200 Subject: [PATCH 271/519] Fix recently introduced lookahead bias in backtesting closes #5388 --- freqtrade/optimize/backtesting.py | 4 +++- tests/optimize/test_backtesting.py | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 825d1dd25..3a864173f 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -466,6 +466,8 @@ class Backtesting: for i, pair in enumerate(data): row_index = indexes[pair] try: + # Row is treated as "current incomplete candle". + # Buy / sell signals are shifted by 1 to compensate for this. row = data[pair][row_index] except IndexError: # missing Data for one pair at the end. @@ -476,8 +478,8 @@ class Backtesting: if row[DATE_IDX] > tmp: continue - row_index += 1 self.dataprovider._set_dataframe_max_index(row_index) + row_index += 1 indexes[pair] = row_index # without positionstacking, we can only have one open trade per pair. diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 8e3d4063a..ff9b81c30 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument import random +from datetime import timedelta from pathlib import Path from unittest.mock import MagicMock, PropertyMock @@ -741,8 +742,13 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): # 100 buys signals results = result['results'] assert len(results) == 100 - # Cached data should be 200 (no change since required_startup is 0) - assert len(backtesting.dataprovider.get_analyzed_dataframe('UNITTEST/BTC', '1m')[0]) == 200 + # Cached data should be 199 (missing 1 candle at the start) + analyzed_df = backtesting.dataprovider.get_analyzed_dataframe('UNITTEST/BTC', '1m')[0] + assert len(analyzed_df) == 199 + # Expect last candle to be 1 below end date (as the last candle is assumed as "incomplete" + # during backtesting) + expected_last_candle_date = backtest_conf['end_date'] - timedelta(minutes=1) + assert analyzed_df.iloc[-1]['date'].to_pydatetime() == expected_last_candle_date # One trade was force-closed at the end assert len(results.loc[results['is_open']]) == 0 @@ -774,7 +780,8 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) data = trim_dictlist(data, -500) # Remove data for one pair from the beginning of the data - data[pair] = data[pair][tres:].reset_index() + if tres > 0: + data[pair] = data[pair][tres:].reset_index() default_conf['timeframe'] = '5m' backtesting = Backtesting(default_conf) @@ -800,8 +807,11 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) assert len(evaluate_result_multi(results['results'], '5m', 3)) == 0 # Cached data correctly removed amounts - removed_candles = len(data[pair]) - 1 - backtesting.strategy.startup_candle_count + offset = 2 if tres == 0 else 1 + removed_candles = len(data[pair]) - offset - backtesting.strategy.startup_candle_count assert len(backtesting.dataprovider.get_analyzed_dataframe(pair, '5m')[0]) == removed_candles + assert len(backtesting.dataprovider.get_analyzed_dataframe( + 'NXT/BTC', '5m')[0]) == len(data['NXT/BTC']) - 2 - backtesting.strategy.startup_candle_count backtest_conf = { 'processed': processed, From 5bfb9edf02922c38aef0f501634eb116c74f7515 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 15:42:17 +0200 Subject: [PATCH 272/519] Only query date once from list --- freqtrade/optimize/backtesting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 3a864173f..5efb5101f 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -323,14 +323,14 @@ class Backtesting: return sell_row[OPEN_IDX] def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]: - + sell_candle_time = sell_row[DATE_IDX].to_pydatetime() sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX], # type: ignore - sell_row[DATE_IDX].to_pydatetime(), sell_row[BUY_IDX], + sell_candle_time, sell_row[BUY_IDX], sell_row[SELL_IDX], low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX]) if sell.sell_flag: - trade.close_date = sell_row[DATE_IDX].to_pydatetime() + trade.close_date = sell_candle_time trade.sell_reason = sell.sell_reason trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60) closerate = self._get_close_rate(sell_row, trade, sell, trade_dur) @@ -342,7 +342,7 @@ class Backtesting: rate=closerate, time_in_force=time_in_force, sell_reason=sell.sell_reason, - current_time=sell_row[DATE_IDX].to_pydatetime()): + current_time=sell_candle_time): return None trade.close(closerate, show_msg=False) From cf27968b97cc1491b61923a0a95f2183aacf0592 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 19:38:56 +0200 Subject: [PATCH 273/519] Properly preserve trade's low during backtesting --- freqtrade/persistence/models.py | 4 ++-- freqtrade/strategy/interface.py | 2 +- tests/optimize/test_backtesting.py | 2 +- tests/strategy/test_interface.py | 2 +- tests/test_persistence.py | 13 +++++++++---- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 43fbec8c0..a45274266 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -354,12 +354,12 @@ class LocalTrade(): LocalTrade.trades_open = [] LocalTrade.total_profit = 0 - def adjust_min_max_rates(self, current_price: float) -> None: + def adjust_min_max_rates(self, current_price: float, current_price_low: float) -> None: """ Adjust the max_rate and min_rate. """ self.max_rate = max(current_price, self.max_rate or self.open_rate) - self.min_rate = min(current_price, self.min_rate or self.open_rate) + self.min_rate = min(current_price_low, self.min_rate or self.open_rate) def _set_new_stoploss(self, new_loss: float, stoploss: float): """Assign new stop value""" diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index d4f10301a..bb8980a53 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -568,7 +568,7 @@ class IStrategy(ABC, HyperStrategyMixin): current_rate = rate current_profit = trade.calc_profit_ratio(current_rate) - trade.adjust_min_max_rates(high or current_rate) + trade.adjust_min_max_rates(high or current_rate, low or current_rate) stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, current_time=date, current_profit=current_profit, diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index ff9b81c30..57b2b8733 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -584,7 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'initial_stop_loss_ratio': [-0.1, -0.1], 'stop_loss_abs': [0.0940005, 0.09272236], 'stop_loss_ratio': [-0.1, -0.1], - 'min_rate': [0.1038, 0.10302485], + 'min_rate': [0.10370188, 0.10300000000000001], 'max_rate': [0.10501, 0.1038888], 'is_open': [False, False], 'buy_tag': [None, None], diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 686a36c96..cb4b8bd63 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -398,7 +398,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili exchange='binance', open_rate=1, ) - trade.adjust_min_max_rates(trade.open_rate) + trade.adjust_min_max_rates(trade.open_rate, trade.open_rate) strategy.trailing_stop = trailing strategy.trailing_stop_positive = -0.05 strategy.use_custom_stoploss = custom diff --git a/tests/test_persistence.py b/tests/test_persistence.py index f7bcad806..105cee23a 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -799,25 +799,30 @@ def test_adjust_min_max_rates(fee): open_rate=1, ) - trade.adjust_min_max_rates(trade.open_rate) + trade.adjust_min_max_rates(trade.open_rate, trade.open_rate) assert trade.max_rate == 1 assert trade.min_rate == 1 # check min adjusted, max remained - trade.adjust_min_max_rates(0.96) + trade.adjust_min_max_rates(0.96, 0.96) assert trade.max_rate == 1 assert trade.min_rate == 0.96 # check max adjusted, min remains - trade.adjust_min_max_rates(1.05) + trade.adjust_min_max_rates(1.05, 1.05) assert trade.max_rate == 1.05 assert trade.min_rate == 0.96 # current rate "in the middle" - no adjustment - trade.adjust_min_max_rates(1.03) + trade.adjust_min_max_rates(1.03, 1.03) assert trade.max_rate == 1.05 assert trade.min_rate == 0.96 + # current rate "in the middle" - no adjustment + trade.adjust_min_max_rates(1.10, 0.91) + assert trade.max_rate == 1.10 + assert trade.min_rate == 0.91 + @pytest.mark.usefixtures("init_persistence") @pytest.mark.parametrize('use_db', [True, False]) From 3f160c71443281d4118a79ee5c11af3891c384d5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Aug 2021 07:09:38 +0200 Subject: [PATCH 274/519] Cache dataframe before cutting the first candle This allows providing the "current closed" candle in all cases. --- freqtrade/optimize/backtesting.py | 6 +++--- tests/optimize/test_backtesting.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 5efb5101f..fce27d39b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -246,11 +246,11 @@ class Backtesting: if has_buy_tag: df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) - df_analyzed.drop(df_analyzed.head(1).index, inplace=True) - # Update dataprovider cache self.dataprovider._set_cached_df(pair, self.timeframe, df_analyzed) + df_analyzed = df_analyzed.drop(df_analyzed.head(1).index) + # Convert from Pandas to list for performance reasons # (Looping Pandas is slow.) data[pair] = df_analyzed[headers].values.tolist() @@ -478,9 +478,9 @@ class Backtesting: if row[DATE_IDX] > tmp: continue - self.dataprovider._set_dataframe_max_index(row_index) row_index += 1 indexes[pair] = row_index + self.dataprovider._set_dataframe_max_index(row_index) # without positionstacking, we can only have one open trade per pair. # max_open_trades must be respected diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 57b2b8733..998b2d837 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -742,9 +742,9 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): # 100 buys signals results = result['results'] assert len(results) == 100 - # Cached data should be 199 (missing 1 candle at the start) + # Cached data should be 200 analyzed_df = backtesting.dataprovider.get_analyzed_dataframe('UNITTEST/BTC', '1m')[0] - assert len(analyzed_df) == 199 + assert len(analyzed_df) == 200 # Expect last candle to be 1 below end date (as the last candle is assumed as "incomplete" # during backtesting) expected_last_candle_date = backtest_conf['end_date'] - timedelta(minutes=1) @@ -807,11 +807,11 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) assert len(evaluate_result_multi(results['results'], '5m', 3)) == 0 # Cached data correctly removed amounts - offset = 2 if tres == 0 else 1 + offset = 1 if tres == 0 else 0 removed_candles = len(data[pair]) - offset - backtesting.strategy.startup_candle_count assert len(backtesting.dataprovider.get_analyzed_dataframe(pair, '5m')[0]) == removed_candles assert len(backtesting.dataprovider.get_analyzed_dataframe( - 'NXT/BTC', '5m')[0]) == len(data['NXT/BTC']) - 2 - backtesting.strategy.startup_candle_count + 'NXT/BTC', '5m')[0]) == len(data['NXT/BTC']) - 1 - backtesting.strategy.startup_candle_count backtest_conf = { 'processed': processed, From 039d6384edf79d87556f5fa66b5917d1a77f9a6b Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Aug 2021 09:48:26 +0200 Subject: [PATCH 275/519] Stream hyperopt-result in small batches Avoiding memory-exhaustion on huge hyperopt results closes #5305 closes #5149 --- freqtrade/optimize/hyperopt_epoch_filters.py | 12 ++--- freqtrade/optimize/hyperopt_tools.py | 54 +++++++++++--------- tests/commands/test_commands.py | 24 +++++++-- tests/optimize/test_hyperopt_tools.py | 24 +++++++-- 4 files changed, 78 insertions(+), 36 deletions(-) diff --git a/freqtrade/optimize/hyperopt_epoch_filters.py b/freqtrade/optimize/hyperopt_epoch_filters.py index b70db94af..80cc89d4b 100644 --- a/freqtrade/optimize/hyperopt_epoch_filters.py +++ b/freqtrade/optimize/hyperopt_epoch_filters.py @@ -7,7 +7,7 @@ from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) -def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: +def hyperopt_filter_epochs(epochs: List, filteroptions: dict, log: bool = True) -> List: """ Filter our items from the list of hyperopt results """ @@ -24,11 +24,11 @@ def hyperopt_filter_epochs(epochs: List, filteroptions: dict) -> List: epochs = _hyperopt_filter_epochs_profit(epochs, filteroptions) epochs = _hyperopt_filter_epochs_objective(epochs, filteroptions) - - logger.info(f"{len(epochs)} " + - ("best " if filteroptions['only_best'] else "") + - ("profitable " if filteroptions['only_profitable'] else "") + - "epochs found.") + if log: + logger.info(f"{len(epochs)} " + + ("best " if filteroptions['only_best'] else "") + + ("profitable " if filteroptions['only_profitable'] else "") + + "epochs found.") return epochs diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 0bb6aba15..b2e024f65 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -4,7 +4,7 @@ import logging from copy import deepcopy from datetime import datetime, timezone from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, Iterator, List, Optional, Tuple import numpy as np import rapidjson @@ -90,37 +90,33 @@ class HyperoptTools(): return any(s in config['spaces'] for s in [space, 'all', 'default']) @staticmethod - def _read_results(results_file: Path) -> List: + def _read_results(results_file: Path, batch_size: int = 10) -> Iterator[List[Any]]: """ - Read hyperopt results from file + Stream hyperopt results from file """ import rapidjson logger.info(f"Reading epochs from '{results_file}'") with results_file.open('r') as f: - data = [rapidjson.loads(line) for line in f] - return data + data = [] + for line in f: + data += [rapidjson.loads(line)] + if len(data) >= batch_size: + yield data + data = [] + yield data @staticmethod - def load_previous_results(results_file: Path) -> List: - """ - Load data for epochs from the file if we have one - """ - epochs: List = [] + def _test_hyperopt_results_exist(results_file) -> bool: if results_file.is_file() and results_file.stat().st_size > 0: if results_file.suffix == '.pickle': raise OperationalException( "Legacy hyperopt results are no longer supported." "Please rerun hyperopt or use an older version to load this file." ) - else: - epochs = HyperoptTools._read_results(results_file) - # Detection of some old format, without 'is_best' field saved - if epochs[0].get('is_best') is None: - raise OperationalException( - "The file with HyperoptTools results is incompatible with this version " - "of Freqtrade and cannot be loaded.") - logger.info(f"Loaded {len(epochs)} previous evaluations from disk.") - return epochs + return True + else: + # No file found. + return False @staticmethod def load_filtered_results(results_file: Path, config: Dict[str, Any]) -> Tuple[List, int]: @@ -138,12 +134,24 @@ class HyperoptTools(): 'filter_min_objective': config.get('hyperopt_list_min_objective', None), 'filter_max_objective': config.get('hyperopt_list_max_objective', None), } + if not HyperoptTools._test_hyperopt_results_exist(results_file): + # No file found. + return [], 0 - # Previous evaluations - epochs = HyperoptTools.load_previous_results(results_file) - total_epochs = len(epochs) + epochs = [] + total_epochs = 0 + for epochs_tmp in HyperoptTools._read_results(results_file): + if total_epochs == 0 and epochs_tmp[0].get('is_best') is None: + raise OperationalException( + "The file with HyperoptTools results is incompatible with this version " + "of Freqtrade and cannot be loaded.") + total_epochs += len(epochs_tmp) + epochs += hyperopt_filter_epochs(epochs_tmp, filteroptions, log=False) - epochs = hyperopt_filter_epochs(epochs, filteroptions) + logger.info(f"Loaded {total_epochs} previous evaluations from disk.") + + # Final filter run ... + epochs = hyperopt_filter_epochs(epochs, filteroptions, log=True) return epochs, total_epochs diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 80dd04b27..fc5101979 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -941,8 +941,16 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, tmpdir): csv_file = Path(tmpdir) / "test.csv" mocker.patch( - 'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results', - MagicMock(return_value=saved_hyperopt_results) + 'freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist', + return_value=True + ) + + def fake_iterator(*args, **kwargs): + yield from [saved_hyperopt_results] + + mocker.patch( + 'freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results', + side_effect=fake_iterator ) args = [ @@ -1175,8 +1183,16 @@ def test_hyperopt_list(mocker, capsys, caplog, saved_hyperopt_results, tmpdir): def test_hyperopt_show(mocker, capsys, saved_hyperopt_results): mocker.patch( - 'freqtrade.optimize.hyperopt_tools.HyperoptTools.load_previous_results', - MagicMock(return_value=saved_hyperopt_results) + 'freqtrade.optimize.hyperopt_tools.HyperoptTools._test_hyperopt_results_exist', + return_value=True + ) + + def fake_iterator(*args, **kwargs): + yield from [saved_hyperopt_results] + + mocker.patch( + 'freqtrade.optimize.hyperopt_tools.HyperoptTools._read_results', + side_effect=fake_iterator ) mocker.patch('freqtrade.commands.hyperopt_commands.show_backtest_result') diff --git a/tests/optimize/test_hyperopt_tools.py b/tests/optimize/test_hyperopt_tools.py index d59a44da7..cbcb13384 100644 --- a/tests/optimize/test_hyperopt_tools.py +++ b/tests/optimize/test_hyperopt_tools.py @@ -20,9 +20,14 @@ def create_results() -> List[Dict]: def test_save_results_saves_epochs(hyperopt, tmpdir, caplog) -> None: + + hyperopt.results_file = Path(tmpdir / 'ut_results.fthypt') + + hyperopt_epochs = HyperoptTools.load_filtered_results(hyperopt.results_file, {}) + assert hyperopt_epochs == ([], 0) + # Test writing to temp dir and reading again epochs = create_results() - hyperopt.results_file = Path(tmpdir / 'ut_results.fthypt') caplog.set_level(logging.DEBUG) @@ -33,15 +38,28 @@ def test_save_results_saves_epochs(hyperopt, tmpdir, caplog) -> None: hyperopt._save_result(epochs[0]) assert log_has(f"2 epochs saved to '{hyperopt.results_file}'.", caplog) - hyperopt_epochs = HyperoptTools.load_previous_results(hyperopt.results_file) + hyperopt_epochs = HyperoptTools.load_filtered_results(hyperopt.results_file, {}) assert len(hyperopt_epochs) == 2 + assert hyperopt_epochs[1] == 2 + assert len(hyperopt_epochs[0]) == 2 + + result_gen = HyperoptTools._read_results(hyperopt.results_file, 1) + epoch = next(result_gen) + assert len(epoch) == 1 + assert epoch[0] == epochs[0] + epoch = next(result_gen) + assert len(epoch) == 1 + epoch = next(result_gen) + assert len(epoch) == 0 + with pytest.raises(StopIteration): + next(result_gen) def test_load_previous_results2(mocker, testdatadir, caplog) -> None: results_file = testdatadir / 'hyperopt_results_SampleStrategy.pickle' with pytest.raises(OperationalException, match=r"Legacy hyperopt results are no longer supported.*"): - HyperoptTools.load_previous_results(results_file) + HyperoptTools.load_filtered_results(results_file, {}) @pytest.mark.parametrize("spaces, expected_results", [ From 65d025923d9453b2173de63c898232847e31ad7a Mon Sep 17 00:00:00 2001 From: ipqhjjybj <250657661@qq.com> Date: Wed, 11 Aug 2021 14:35:16 +0800 Subject: [PATCH 276/519] add code --- freqtrade/optimize/backtesting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index fce27d39b..a8fd9c04a 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -504,7 +504,7 @@ class Backtesting: open_trades[pair].append(trade) LocalTrade.add_bt_trade(trade) - for trade in open_trades[pair]: + for trade in list(open_trades[pair]): # also check the buying candle for sell conditions. trade_entry = self._get_sell_trade_entry(trade, row) # Sell occurred From 61c076563fb7c778b93d9e056f5403423aa9406f Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 11 Aug 2021 12:11:29 +0200 Subject: [PATCH 277/519] Add max-slippage limiting for dry-run orders to avoid insane market order fills --- freqtrade/exchange/exchange.py | 13 ++++++++++++- tests/exchange/test_exchange.py | 23 ++++++++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index c6f60e08a..cde643cff 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -618,6 +618,8 @@ class Exchange: if self.exchange_has('fetchL2OrderBook'): ob = self.fetch_l2_order_book(pair, 20) ob_type = 'asks' if side == 'buy' else 'bids' + slippage = 0.05 + max_slippage_val = rate * ((1 + slippage) if side == 'buy' else (1 - slippage)) remaining_amount = amount filled_amount = 0 @@ -626,7 +628,9 @@ class Exchange: book_entry_coin_volume = book_entry[1] if remaining_amount > 0: if remaining_amount < book_entry_coin_volume: + # Orderbook at this slot bigger than remaining amount filled_amount += remaining_amount * book_entry_price + break else: filled_amount += book_entry_coin_volume * book_entry_price remaining_amount -= book_entry_coin_volume @@ -635,7 +639,14 @@ class Exchange: else: # If remaining_amount wasn't consumed completely (break was not called) filled_amount += remaining_amount * book_entry_price - forecast_avg_filled_price = filled_amount / amount + forecast_avg_filled_price = max(filled_amount, 0) / amount + # Limit max. slippage to specified value + if side == 'buy': + forecast_avg_filled_price = min(forecast_avg_filled_price, max_slippage_val) + + else: + forecast_avg_filled_price = max(forecast_avg_filled_price, max_slippage_val) + return self.price_to_precision(pair, forecast_avg_filled_price) return rate diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index a3ebbe8bd..9ac9f84e5 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -984,16 +984,21 @@ def test_create_dry_run_order_limit_fill(default_conf, mocker, side, startprice, assert order['fee'] -@pytest.mark.parametrize("side,amount,endprice", [ - ("buy", 1, 25.566), - ("buy", 100, 25.5672), # Requires interpolation - ("buy", 1000, 25.575), # More than orderbook return - ("sell", 1, 25.563), - ("sell", 100, 25.5625), # Requires interpolation - ("sell", 1000, 25.5555), # More than orderbook return +@pytest.mark.parametrize("side,rate,amount,endprice", [ + # spread is 25.263-25.266 + ("buy", 25.564, 1, 25.566), + ("buy", 25.564, 100, 25.5672), # Requires interpolation + ("buy", 25.590, 100, 25.5672), # Price above spread ... average is lower + ("buy", 25.564, 1000, 25.575), # More than orderbook return + ("buy", 24.000, 100000, 25.200), # Run into max_slippage of 5% + ("sell", 25.564, 1, 25.563), + ("sell", 25.564, 100, 25.5625), # Requires interpolation + ("sell", 25.510, 100, 25.5625), # price below spread - average is higher + ("sell", 25.564, 1000, 25.5555), # More than orderbook return + ("sell", 27, 10000, 25.65), # max-slippage 5% ]) @pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_create_dry_run_order_market_fill(default_conf, mocker, side, amount, endprice, +def test_create_dry_run_order_market_fill(default_conf, mocker, side, rate, amount, endprice, exchange_name, order_book_l2_usd): default_conf['dry_run'] = True exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) @@ -1003,7 +1008,7 @@ def test_create_dry_run_order_market_fill(default_conf, mocker, side, amount, en ) order = exchange.create_dry_run_order( - pair='LTC/USDT', ordertype='market', side=side, amount=amount, rate=25.5) + pair='LTC/USDT', ordertype='market', side=side, amount=amount, rate=rate) assert 'id' in order assert f'dry_run_{side}_' in order["id"] assert order["side"] == side From f6267c75143b35c0270428f70d7936f7880fc917 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 11 Aug 2021 10:18:25 +0300 Subject: [PATCH 278/519] Fix buy_tag not being saved to trade object. Column is mistakenly excluded because advise_buy() creating this column runs after code detecting presence of buy_tag column. --- freqtrade/optimize/backtesting.py | 10 +++------- tests/optimize/__init__.py | 2 ++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index fce27d39b..06464d40b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -218,7 +218,7 @@ class Backtesting: """ # Every change to this headers list must evaluate further usages of the resulting tuple # and eventually change the constants for indexes at the top - headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_tag'] data: Dict = {} self.progress.init_step(BacktestState.CONVERT, len(processed)) @@ -226,13 +226,10 @@ class Backtesting: for pair, pair_data in processed.items(): self.check_abort() self.progress.increment() - has_buy_tag = 'buy_tag' in pair_data - headers = headers + ['buy_tag'] if has_buy_tag else headers if not pair_data.empty: pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist - if has_buy_tag: - pair_data.loc[:, 'buy_tag'] = None # cleanup if buy_tag is exist + pair_data.loc[:, 'buy_tag'] = None # cleanup if buy_tag is exist df_analyzed = self.strategy.advise_sell( self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair}).copy() @@ -243,8 +240,7 @@ class Backtesting: # from the previous candle df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1) df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1) - if has_buy_tag: - df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) + df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1) # Update dataprovider cache self.dataprovider._set_cached_df(pair, self.timeframe, df_analyzed) diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index f29d8d585..6ad2d300b 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -52,4 +52,6 @@ def _build_backtest_dataframe(data): # Ensure floats are in place for column in ['open', 'high', 'low', 'close', 'volume']: frame[column] = frame[column].astype('float64') + if 'buy_tag' not in columns: + frame['buy_tag'] = None return frame From fad253ad5141978b5d35d878414feeead837fab8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 12 Aug 2021 06:53:55 +0200 Subject: [PATCH 279/519] Version bump ccxt to 1.54.74 closes #5401 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 60175c12f..0e107d8e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.1 pandas==1.3.1 -ccxt==1.54.62 +ccxt==1.54.74 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From e03784d98dc016790cb42eb194adf2c2be752091 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 12 Aug 2021 05:02:36 +0000 Subject: [PATCH 280/519] Fix filled exception closes #5404 --- freqtrade/persistence/models.py | 2 +- tests/test_persistence.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index a45274266..5eaca7966 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -161,7 +161,7 @@ class Order(_DECL_BASE): self.ft_is_open = True if self.status in ('closed', 'canceled', 'cancelled'): self.ft_is_open = False - if order.get('filled', 0) > 0: + if (order.get('filled', 0.0) or 0.0) > 0: self.order_filled_date = datetime.now(timezone.utc) self.order_update_date = datetime.now(timezone.utc) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 105cee23a..d036b045e 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -1224,6 +1224,11 @@ def test_update_order_from_ccxt(caplog): assert o.ft_is_open assert o.order_filled_date is None + # Order is unfilled, "filled" not set + # https://github.com/freqtrade/freqtrade/issues/5404 + ccxt_order.update({'filled': None, 'remaining': 20.0, 'status': 'canceled'}) + o.update_from_ccxt_object(ccxt_order) + # Order has been closed ccxt_order.update({'filled': 20.0, 'remaining': 0.0, 'status': 'closed'}) o.update_from_ccxt_object(ccxt_order) From ae11be39706b37eb6733b12cee814cdf351b53d9 Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 12 Aug 2021 14:47:01 -0400 Subject: [PATCH 281/519] manage None or string value returned by custom_entry_price and add unit test for those cases --- freqtrade/freqtradebot.py | 12 ++++++++---- tests/test_freqtradebot.py | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 99f5d2894..99fe1c5a3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -479,13 +479,17 @@ class FreqtradeBot(LoggingMixin): buy_limit_requested = price else: # Calculate price - buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy") + proposed_buy_rate = self.exchange.get_rate(pair, refresh=True, side="buy") custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, - default_retval=buy_limit_requested)( + default_retval=proposed_buy_rate)( pair=pair, current_time=datetime.now(timezone.utc), - proposed_rate=buy_limit_requested) + proposed_rate=proposed_buy_rate) - buy_limit_requested = custom_entry_price + if custom_entry_price and (isinstance(custom_entry_price, int) + or isinstance(custom_entry_price, float)): + buy_limit_requested = custom_entry_price + else: + buy_limit_requested = proposed_buy_rate if not buy_limit_requested: raise PricingError('Could not determine buy price.') diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index c73e51dec..69a4fa530 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -913,6 +913,30 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order assert trade assert trade.open_rate_requested == 0.77 + # In case of custom entry price set to None + limit_buy_order['status'] = 'open' + limit_buy_order['id'] = '5567' + freqtrade.strategy.custom_entry_price = lambda **kwargs: None + + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_rate=MagicMock(return_value=10), + ) + + assert freqtrade.execute_buy(pair, stake_amount) + trade = Trade.query.all()[7] + assert trade + assert trade.open_rate_requested == 10 + + # In case of custom entry price not float type + limit_buy_order['status'] = 'open' + limit_buy_order['id'] = '5568' + freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price" + assert freqtrade.execute_buy(pair, stake_amount) + trade = Trade.query.all()[8] + assert trade + assert trade.open_rate_requested == 10 + def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) From b098ce4e76abe78f27fee9be8860399ebc831f7a Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 12 Aug 2021 15:13:14 -0400 Subject: [PATCH 282/519] add function get_valid_price to validate type of custom entry or exit price and use default proposed price if invalid --- freqtrade/freqtradebot.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 99fe1c5a3..99670d612 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -485,11 +485,7 @@ class FreqtradeBot(LoggingMixin): pair=pair, current_time=datetime.now(timezone.utc), proposed_rate=proposed_buy_rate) - if custom_entry_price and (isinstance(custom_entry_price, int) - or isinstance(custom_entry_price, float)): - buy_limit_requested = custom_entry_price - else: - buy_limit_requested = proposed_buy_rate + buy_limit_requested = self.get_valid_price(custom_entry_price, proposed_buy_rate) if not buy_limit_requested: raise PricingError('Could not determine buy price.') @@ -1087,12 +1083,15 @@ class FreqtradeBot(LoggingMixin): limit = trade.stop_loss # set custom_exit_price if available + proposed_limit_rate = limit current_profit = trade.calc_profit_ratio(limit) - limit = strategy_safe_wrapper(self.strategy.custom_exit_price, - default_retval=limit)( + custom_exit_price = strategy_safe_wrapper(self.strategy.custom_exit_price, + default_retval=proposed_limit_rate)( pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc), - proposed_rate=limit, current_profit=current_profit) + proposed_rate=proposed_limit_rate, current_profit=current_profit) + + limit = self.get_valid_price(custom_exit_price, proposed_limit_rate) # First cancelling stoploss on exchange ... if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id: @@ -1393,3 +1392,17 @@ class FreqtradeBot(LoggingMixin): amount=amount, fee_abs=fee_abs) else: return amount + + def get_valid_price(self, custom_price: float, proposed_price: float) -> float: + """ + Return the valid price. + Check if the custom price is of the good type if not return proposed_price + :return: valid price for the order + """ + if custom_price and (isinstance(custom_price, int) + or isinstance(custom_price, float)): + valid_price = custom_price + else: + valid_price = proposed_price + + return valid_price From dbf7f34ecb180672a79454817725b15d058e4480 Mon Sep 17 00:00:00 2001 From: axel Date: Thu, 12 Aug 2021 15:30:49 -0400 Subject: [PATCH 283/519] add unit test to function get_valid_price --- freqtrade/freqtradebot.py | 10 +++++++--- tests/test_freqtradebot.py | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 99670d612..2225ddd89 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1399,9 +1399,13 @@ class FreqtradeBot(LoggingMixin): Check if the custom price is of the good type if not return proposed_price :return: valid price for the order """ - if custom_price and (isinstance(custom_price, int) - or isinstance(custom_price, float)): - valid_price = custom_price + if custom_price: + if isinstance(custom_price, int): + valid_price = float(custom_price) + elif isinstance(custom_price, float): + valid_price = custom_price + else: + valid_price = proposed_price else: valid_price = proposed_price diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 69a4fa530..a67f5b290 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4588,3 +4588,27 @@ def test_refind_lost_order(mocker, default_conf, fee, caplog): freqtrade.refind_lost_order(trades[4]) assert log_has(f"Error updating {order['id']}.", caplog) + + +def test_get_valid_price(mocker, default_conf) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + freqtrade = FreqtradeBot(default_conf) + + custom_price_string = "10" + custom_price_float = 10.0 + custom_price_int = 10 + + proposed_price = 12.2 + + valid_price_from_string = freqtrade.get_valid_price(custom_price_string, proposed_price) + valid_price_from_int = freqtrade.get_valid_price(custom_price_int, proposed_price) + valid_price_from_float = freqtrade.get_valid_price(custom_price_float, proposed_price) + + assert isinstance(valid_price_from_string, float) + assert isinstance(valid_price_from_int, float) + assert isinstance(valid_price_from_float, float) + + assert valid_price_from_string == proposed_price + assert valid_price_from_int == custom_price_int + assert valid_price_from_float == custom_price_float From 20cc60bfde456dafd94bc59ab797018f62fb20ad Mon Sep 17 00:00:00 2001 From: axel Date: Fri, 13 Aug 2021 11:06:15 -0400 Subject: [PATCH 284/519] update get_valid_price function and test cases to handle inputs with try catch --- freqtrade/freqtradebot.py | 6 ++---- tests/test_freqtradebot.py | 6 +++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2225ddd89..9a1b2ab0c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1400,11 +1400,9 @@ class FreqtradeBot(LoggingMixin): :return: valid price for the order """ if custom_price: - if isinstance(custom_price, int): + try: valid_price = float(custom_price) - elif isinstance(custom_price, float): - valid_price = custom_price - else: + except ValueError: valid_price = proposed_price else: valid_price = proposed_price diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index a67f5b290..a475ced48 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4596,19 +4596,23 @@ def test_get_valid_price(mocker, default_conf) -> None: freqtrade = FreqtradeBot(default_conf) custom_price_string = "10" + custom_price_badstring = "10abc" custom_price_float = 10.0 custom_price_int = 10 proposed_price = 12.2 valid_price_from_string = freqtrade.get_valid_price(custom_price_string, proposed_price) + valid_price_from_badstring = freqtrade.get_valid_price(custom_price_badstring, proposed_price) valid_price_from_int = freqtrade.get_valid_price(custom_price_int, proposed_price) valid_price_from_float = freqtrade.get_valid_price(custom_price_float, proposed_price) assert isinstance(valid_price_from_string, float) + assert isinstance(valid_price_from_badstring, float) assert isinstance(valid_price_from_int, float) assert isinstance(valid_price_from_float, float) - assert valid_price_from_string == proposed_price + assert valid_price_from_string == custom_price_float + assert valid_price_from_badstring == proposed_price assert valid_price_from_int == custom_price_int assert valid_price_from_float == custom_price_float From 0a6c0c429ae5fb354a06d8409231216aa387e0f6 Mon Sep 17 00:00:00 2001 From: axel Date: Fri, 13 Aug 2021 11:12:33 -0400 Subject: [PATCH 285/519] add a note concerning default custom entry or exit price in documentation --- docs/strategy-advanced.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index f59cb8ef5..17fdddc37 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -363,6 +363,9 @@ By default, freqtrade use the orderbook to automatically set an order price, you You can use this feature by creating a custom_entry_price function in your strategy file to customize entry prices and custom_exit_price for exits. +!!!Note +If your custom pricing function return None or an invalid value, a default entry or exit price will be chosen based on the current rate. + ### Custom order entry and exit price exemple ``` python from datetime import datetime, timedelta, timezone From c7147311f8d75635cff229ceccc88656903da045 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Aug 2021 17:14:38 +0200 Subject: [PATCH 286/519] Fix json syntax error in config template --- freqtrade/templates/base_config.json.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/templates/base_config.json.j2 b/freqtrade/templates/base_config.json.j2 index 03a6c4855..a5782f7cd 100644 --- a/freqtrade/templates/base_config.json.j2 +++ b/freqtrade/templates/base_config.json.j2 @@ -25,7 +25,7 @@ "ask_strategy": { "price_side": "ask", "use_order_book": true, - "order_book_top": 1, + "order_book_top": 1 }, {{ exchange | indent(4) }}, "pairlists": [ From db5a9443967eae8c0ed97229d5a510a3ffe14715 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Aug 2021 08:46:19 +0200 Subject: [PATCH 287/519] Cleanup GHA node after building images --- build_helpers/publish_docker_arm64.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh index 08793d339..e7b69b2dc 100755 --- a/build_helpers/publish_docker_arm64.sh +++ b/build_helpers/publish_docker_arm64.sh @@ -74,7 +74,5 @@ fi docker images -if [ $? -ne 0 ]; then - echo "failed building image" - return 1 -fi +# Cleanup old images from arm64 node. +docker image prune -a --force --filter "until=24h" From bb472ff98b06a8ae4059b742e56b22f1d2aca244 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Aug 2021 08:47:14 +0200 Subject: [PATCH 288/519] Improve new-exchange documentation --- docs/developer.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/developer.md b/docs/developer.md index dd56a367c..bd138212b 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -240,11 +240,18 @@ The `IProtection` parent class provides a helper method for this in `calculate_l !!! Note This section is a Work in Progress and is not a complete guide on how to test a new exchange with Freqtrade. +!!! Note + Make sure to use an up-to-date version of CCXT before running any of the below tests. + You can get the latest version of ccxt by running `pip install -U ccxt` with activated virtual environment. + Native docker is not supported for these tests, however the available dev-container will support all required actions and eventually necessary changes. + Most exchanges supported by CCXT should work out of the box. To quickly test the public endpoints of an exchange, add a configuration for your exchange to `test_ccxt_compat.py` and run these tests with `pytest --longrun tests/exchange/test_ccxt_compat.py`. Completing these tests successfully a good basis point (it's a requirement, actually), however these won't guarantee correct exchange functioning, as this only tests public endpoints, but no private endpoint (like generate order or similar). +Also try to use `freqtrade download-data` for an extended timerange and verify that the data downloaded correctly (no holes, the specified timerange was actually downloaded). + ### Stoploss On Exchange Check if the new exchange supports Stoploss on Exchange orders through their API. From 0f7ddabec80428e80f036618e65d4e72d7605af7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Aug 2021 09:05:03 +0200 Subject: [PATCH 289/519] Slightly reword documentation --- docs/bot-basics.md | 5 +++-- docs/strategy-advanced.md | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 943af0362..b34594f46 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -36,11 +36,12 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Calls `check_sell_timeout()` strategy callback for open sell orders. * Verifies existing positions and eventually places sell orders. * Considers stoploss, ROI and sell-signal. - * Determine sell-price based on `ask_strategy` configuration setting. + * Determine sell-price based on `ask_strategy` configuration setting or by using the `custom_exit_price()` callback. * Before a sell order is placed, `confirm_trade_exit()` strategy callback is called. * Check if trade-slots are still available (if `max_open_trades` is reached). * Verifies buy signal trying to enter new positions. - * Determine buy-price based on `bid_strategy` configuration setting. + * Determine buy-price based on `bid_strategy` configuration setting, or by using the `custom_entry_price()` callback. + * Determine stake size by calling the `custom_stake_amount()` callback. * Before a buy order is placed, `confirm_trade_entry()` strategy callback is called. This loop will be repeated again and again until the bot is stopped. diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 17fdddc37..e53f20693 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -359,14 +359,15 @@ See [Dataframe access](#dataframe-access) for more information about dataframe u ## Custom order price rules -By default, freqtrade use the orderbook to automatically set an order price, you also have the option to create custom order prices based on your strategy. +By default, freqtrade use the orderbook to automatically set an order price([Relevant documentation](configuration.md#prices-used-for-orders)), you also have the option to create custom order prices based on your strategy. -You can use this feature by creating a custom_entry_price function in your strategy file to customize entry prices and custom_exit_price for exits. +You can use this feature by creating a `custom_entry_price()` function in your strategy file to customize entry prices and custom_exit_price for exits. -!!!Note -If your custom pricing function return None or an invalid value, a default entry or exit price will be chosen based on the current rate. +!!! Note + If your custom pricing function return None or an invalid value, price will fall back to `proposed_rate`, which is based on the regular pricing configuration. + +### Custom order entry and exit price example -### Custom order entry and exit price exemple ``` python from datetime import datetime, timedelta, timezone from freqtrade.persistence import Trade @@ -380,9 +381,9 @@ class AwesomeStrategy(IStrategy): dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) - proposed_entryprice = dataframe['bollinger_10_lowerband'].iat[-1] + new_entryprice = dataframe['bollinger_10_lowerband'].iat[-1] - return proposed_entryprice + return new_entryprice def custom_exit_price(self, pair: str, trade: Trade, current_time: datetime, proposed_rate: float, @@ -390,12 +391,17 @@ class AwesomeStrategy(IStrategy): dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) - proposed_exitprice = dataframe['bollinger_10_upperband'].iat[-1] + new_exitprice = dataframe['bollinger_10_upperband'].iat[-1] - return proposed_exitprice + return new_exitprice ``` +!!! Warning + Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. + +!!! Warning "No backtesting support" + Custom entry-prices are currently not supported during backtesting. ## Custom order timeout rules From 123971d2713a65d51421f46958ac481e03d9f518 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Aug 2021 16:02:12 +0200 Subject: [PATCH 290/519] Don't change passed in parameter variable --- freqtrade/rpc/api_server/api_v1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index f2361fda8..7e613f184 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -223,11 +223,11 @@ def list_strategies(config=Depends(get_config)): @router.get('/strategy/{strategy}', response_model=StrategyResponse, tags=['strategy']) def get_strategy(strategy: str, config=Depends(get_config)): - config = deepcopy(config) + config_ = deepcopy(config) from freqtrade.resolvers.strategy_resolver import StrategyResolver try: - strategy_obj = StrategyResolver._load_strategy(strategy, config, - extra_dir=config.get('strategy_path')) + strategy_obj = StrategyResolver._load_strategy(strategy, config_, + extra_dir=config_.get('strategy_path')) except OperationalException: raise HTTPException(status_code=404, detail='Strategy not found') From 88172fab824c50c9ccba0585137351e1ce031d73 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Aug 2021 15:45:02 +0200 Subject: [PATCH 291/519] Allow "detailed" backtesting timeframe to look into the candle --- freqtrade/commands/arguments.py | 2 +- freqtrade/commands/cli_options.py | 4 ++ freqtrade/configuration/configuration.py | 3 ++ freqtrade/optimize/backtesting.py | 54 ++++++++++++++++++++++-- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 1143db394..a10ea5568 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -22,7 +22,7 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv", "max_open_trades", "stake_amount", "fee", "pairs"] ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", - "enable_protections", "dry_run_wallet", + "enable_protections", "dry_run_wallet", "detail_timeframe", "strategy_list", "export", "exportfilename"] ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 215ed3f6e..a168a44cf 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -135,6 +135,10 @@ AVAILABLE_CLI_OPTIONS = { help='Override the value of the `stake_amount` configuration setting.', ), # Backtesting + "detail_timeframe": Arg( + '--timeframe-detail', + help='Specify detail timeframe for backtesting (`1m`, `5m`, `30m`, `1h`, `1d`).', + ), "position_stacking": Arg( '--eps', '--enable-position-stacking', help='Allow buying the same pair multiple times (position stacking).', diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 4dd5b7203..1d95dfb03 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -242,6 +242,9 @@ class Configuration: except ValueError: pass + self._args_to_config(config, argname='detail_timeframe', + logstring='Parameter --detail-timeframe detected, ' + 'using {} for intra-candle backtesting') self._args_to_config(config, argname='stake_amount', logstring='Parameter --stake-amount detected, ' 'overriding stake_amount to: {} ...') diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index eecc7af54..2106f76c6 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -86,6 +86,16 @@ class Backtesting: "configuration or as cli argument `--timeframe 5m`") self.timeframe = str(self.config.get('timeframe')) self.timeframe_min = timeframe_to_minutes(self.timeframe) + # Load detail timeframe if specified + self.timeframe_detail = str(self.config.get('detail_timeframe', '')) + if self.timeframe_detail: + self.timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail) + if self.timeframe_min <= self.timeframe_detail_min: + raise OperationalException( + "Detail timeframe must be smaller than strategy timeframe.") + + else: + self.timeframe_detail_min = 0 self.pairlists = PairListManager(self.exchange, self.config) if 'VolumePairList' in self.pairlists.name_list: @@ -158,7 +168,7 @@ class Backtesting: conf['protections'] = strategy.protections self.protections = ProtectionManager(self.config, strategy.protections) - def load_bt_data(self) -> Tuple[Dict[str, DataFrame], TimeRange]: + def load_bt_data(self) -> Tuple[Dict[str, DataFrame], TimeRange, Dict[str, DataFrame]]: """ Loads backtest data and returns the data combined with the timerange as tuple. @@ -174,6 +184,18 @@ class Backtesting: fail_without_data=True, data_format=self.config.get('dataformat_ohlcv', 'json'), ) + if self.timeframe_detail: + detail_data = history.load_data( + datadir=self.config['datadir'], + pairs=self.pairlists.whitelist, + timeframe=self.timeframe_detail, + timerange=self.timerange, + startup_candles=0, + fail_without_data=True, + data_format=self.config.get('dataformat_ohlcv', 'json'), + ) + else: + detail_data = None min_date, max_date = history.get_timerange(data) @@ -186,7 +208,7 @@ class Backtesting: self.required_startup, min_date) self.progress.set_new_value(1) - return data, self.timerange + return data, self.timerange, detail_data def prepare_backtest(self, enable_protections): """ @@ -318,7 +340,8 @@ class Backtesting: else: return sell_row[OPEN_IDX] - def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]: + def _get_sell_trade_entry_for_candle(self, trade: LocalTrade, + sell_row: Tuple) -> Optional[LocalTrade]: sell_candle_time = sell_row[DATE_IDX].to_pydatetime() sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX], # type: ignore sell_candle_time, sell_row[BUY_IDX], @@ -346,6 +369,29 @@ class Backtesting: return None + def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]: + if self.timeframe_detail: + sell_candle_time = sell_row[DATE_IDX].to_pydatetime() + sell_candle_end = sell_candle_time + timedelta(minutes=self.timeframe_min) + + detail_data = self.detail_data[trade.pair] + detail_data = detail_data.loc[ + (detail_data['date'] >= sell_candle_time) & + (detail_data['date'] < sell_candle_end) + ] + detail_data['buy'] = sell_row[BUY_IDX] + detail_data['sell'] = sell_row[SELL_IDX] + headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] + for det_row in detail_data[headers].values.tolist(): + res = self._get_sell_trade_entry_for_candle(trade, det_row) + if res: + return res + + return None + + else: + return self._get_sell_trade_entry_for_candle(trade, sell_row) + def _enter_trade(self, pair: str, row: List) -> Optional[LocalTrade]: try: stake_amount = self.wallets.get_trade_stake_amount(pair, None) @@ -591,7 +637,7 @@ class Backtesting: """ data: Dict[str, Any] = {} - data, timerange = self.load_bt_data() + data, timerange, self.detail_data = self.load_bt_data() logger.info("Dataload complete. Calculating indicators") for strat in self.strategylist: From 8405ccc15eff2de0eb9183dd8d5243e60552559c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Aug 2021 15:34:43 +0200 Subject: [PATCH 292/519] Seperate detail data loading from regular backest-data loading --- freqtrade/commands/arguments.py | 2 +- freqtrade/commands/cli_options.py | 2 +- freqtrade/configuration/configuration.py | 4 +-- freqtrade/optimize/backtesting.py | 39 ++++++++++++++---------- freqtrade/optimize/optimize_reports.py | 1 + freqtrade/rpc/api_server/api_backtest.py | 3 ++ freqtrade/rpc/api_server/api_schemas.py | 1 + 7 files changed, 32 insertions(+), 20 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index a10ea5568..899998310 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -22,7 +22,7 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv", "max_open_trades", "stake_amount", "fee", "pairs"] ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", - "enable_protections", "dry_run_wallet", "detail_timeframe", + "enable_protections", "dry_run_wallet", "timeframe_detail", "strategy_list", "export", "exportfilename"] ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index a168a44cf..8f10bbd0a 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -135,7 +135,7 @@ AVAILABLE_CLI_OPTIONS = { help='Override the value of the `stake_amount` configuration setting.', ), # Backtesting - "detail_timeframe": Arg( + "timeframe_detail": Arg( '--timeframe-detail', help='Specify detail timeframe for backtesting (`1m`, `5m`, `30m`, `1h`, `1d`).', ), diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 1d95dfb03..b3dfebe86 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -242,9 +242,9 @@ class Configuration: except ValueError: pass - self._args_to_config(config, argname='detail_timeframe', + self._args_to_config(config, argname='timeframe_detail', logstring='Parameter --detail-timeframe detected, ' - 'using {} for intra-candle backtesting') + 'using {} for intra-candle backtesting ...') self._args_to_config(config, argname='stake_amount', logstring='Parameter --stake-amount detected, ' 'overriding stake_amount to: {} ...') diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 2106f76c6..156ff48be 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -87,7 +87,7 @@ class Backtesting: self.timeframe = str(self.config.get('timeframe')) self.timeframe_min = timeframe_to_minutes(self.timeframe) # Load detail timeframe if specified - self.timeframe_detail = str(self.config.get('detail_timeframe', '')) + self.timeframe_detail = str(self.config.get('timeframe_detail', '')) if self.timeframe_detail: self.timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail) if self.timeframe_min <= self.timeframe_detail_min: @@ -96,6 +96,7 @@ class Backtesting: else: self.timeframe_detail_min = 0 + self.detail_data: Dict[str, DataFrame] = {} self.pairlists = PairListManager(self.exchange, self.config) if 'VolumePairList' in self.pairlists.name_list: @@ -168,7 +169,7 @@ class Backtesting: conf['protections'] = strategy.protections self.protections = ProtectionManager(self.config, strategy.protections) - def load_bt_data(self) -> Tuple[Dict[str, DataFrame], TimeRange, Dict[str, DataFrame]]: + def load_bt_data(self) -> Tuple[Dict[str, DataFrame], TimeRange]: """ Loads backtest data and returns the data combined with the timerange as tuple. @@ -184,18 +185,6 @@ class Backtesting: fail_without_data=True, data_format=self.config.get('dataformat_ohlcv', 'json'), ) - if self.timeframe_detail: - detail_data = history.load_data( - datadir=self.config['datadir'], - pairs=self.pairlists.whitelist, - timeframe=self.timeframe_detail, - timerange=self.timerange, - startup_candles=0, - fail_without_data=True, - data_format=self.config.get('dataformat_ohlcv', 'json'), - ) - else: - detail_data = None min_date, max_date = history.get_timerange(data) @@ -208,7 +197,24 @@ class Backtesting: self.required_startup, min_date) self.progress.set_new_value(1) - return data, self.timerange, detail_data + return data, self.timerange + + def load_bt_data_detail(self) -> None: + """ + Loads backtest detail data (smaller timeframe) if necessary. + """ + if self.timeframe_detail: + self.detail_data = history.load_data( + datadir=self.config['datadir'], + pairs=self.pairlists.whitelist, + timeframe=self.timeframe_detail, + timerange=self.timerange, + startup_candles=0, + fail_without_data=True, + data_format=self.config.get('dataformat_ohlcv', 'json'), + ) + else: + self.detail_data = {} def prepare_backtest(self, enable_protections): """ @@ -637,7 +643,8 @@ class Backtesting: """ data: Dict[str, Any] = {} - data, timerange, self.detail_data = self.load_bt_data() + data, timerange = self.load_bt_data() + self.load_bt_data_detail() logger.info("Dataload complete. Calculating indicators") for strat in self.strategylist: diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 7bb60228a..8bde48670 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -368,6 +368,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'max_open_trades_setting': (config['max_open_trades'] if config['max_open_trades'] != float('inf') else -1), 'timeframe': config['timeframe'], + 'timeframe_detail': config.get('timeframe_detail', ''), 'timerange': config.get('timerange', ''), 'enable_protections': config.get('enable_protections', False), 'strategy_name': strategy, diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 2fa66645b..4623c187e 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -46,11 +46,14 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac if ( not ApiServer._bt or lastconfig.get('timeframe') != strat.timeframe + or lastconfig.get('timeframe_detail') != btconfig.get('timeframe_detail') or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0) or lastconfig.get('timerange') != btconfig['timerange'] ): from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) + if ApiServer._bt.timeframe_detail: + ApiServer._bt.load_bt_data_detail() # Only reload data if timeframe changed. if ( diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 318762136..3adbebc16 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -324,6 +324,7 @@ class PairHistory(BaseModel): class BacktestRequest(BaseModel): strategy: str timeframe: Optional[str] + timeframe_detail: Optional[str] timerange: Optional[str] max_open_trades: Optional[int] stake_amount: Optional[Union[float, str]] From 3406b889b62fbf39ee4dd3b334ae669e0ebd8d1b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Aug 2021 16:56:49 +0200 Subject: [PATCH 293/519] First test --- freqtrade/configuration/configuration.py | 2 +- tests/optimize/test_backtesting.py | 108 +++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index b3dfebe86..94b108f2b 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -243,7 +243,7 @@ class Configuration: pass self._args_to_config(config, argname='timeframe_detail', - logstring='Parameter --detail-timeframe detected, ' + logstring='Parameter --timeframe-detail detected, ' 'using {} for intra-candle backtesting ...') self._args_to_config(config, argname='stake_amount', logstring='Parameter --stake-amount detected, ' diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 998b2d837..1b3fede72 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -441,6 +441,15 @@ def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) -> with pytest.raises(OperationalException, match='VolumePairList not allowed for backtesting.'): Backtesting(default_conf) + default_conf.update({ + 'pairlists': [{"method": "StaticPairList"}], + 'timeframe_detail': '1d', + }) + + with pytest.raises(OperationalException, + match='Detail timeframe must be smaller than strategy timeframe.'): + Backtesting(default_conf) + def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, tickers) -> None: mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) @@ -1042,3 +1051,102 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat assert 'LEFT OPEN TRADES REPORT' in captured.out assert '2017-11-14 21:17:00 -> 2017-11-14 22:58:00 | Max open trades : 1' in captured.out assert 'STRATEGY SUMMARY' in captured.out + + +@pytest.mark.filterwarnings("ignore:deprecated") +def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker, + caplog, testdatadir, capsys): + # Tests detail-data loading + default_conf.update({ + "use_sell_signal": True, + "sell_profit_only": False, + "sell_profit_offset": 0.0, + "ignore_roi_if_buy_signal": False, + }) + patch_exchange(mocker) + result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'], + 'profit_ratio': [0.0, 0.0], + 'profit_abs': [0.0, 0.0], + 'open_date': pd.to_datetime(['2018-01-29 18:40:00', + '2018-01-30 03:30:00', ], utc=True + ), + 'close_date': pd.to_datetime(['2018-01-29 20:45:00', + '2018-01-30 05:35:00', ], utc=True), + 'trade_duration': [235, 40], + 'is_open': [False, False], + 'stake_amount': [0.01, 0.01], + 'open_rate': [0.104445, 0.10302485], + 'close_rate': [0.104969, 0.103541], + 'sell_reason': [SellType.ROI, SellType.ROI] + }) + result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'], + 'profit_ratio': [0.03, 0.01, 0.1], + 'profit_abs': [0.01, 0.02, 0.2], + 'open_date': pd.to_datetime(['2018-01-29 18:40:00', + '2018-01-30 03:30:00', + '2018-01-30 05:30:00'], utc=True + ), + 'close_date': pd.to_datetime(['2018-01-29 20:45:00', + '2018-01-30 05:35:00', + '2018-01-30 08:30:00'], utc=True), + 'trade_duration': [47, 40, 20], + 'is_open': [False, False, False], + 'stake_amount': [0.01, 0.01, 0.01], + 'open_rate': [0.104445, 0.10302485, 0.122541], + 'close_rate': [0.104969, 0.103541, 0.123541], + 'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS] + }) + backtestmock = MagicMock(side_effect=[ + { + 'results': result1, + 'config': default_conf, + 'locks': [], + 'rejected_signals': 20, + 'final_balance': 1000, + }, + { + 'results': result2, + 'config': default_conf, + 'locks': [], + 'rejected_signals': 20, + 'final_balance': 1000, + } + ]) + mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', + PropertyMock(return_value=['XRP/ETH'])) + mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) + + patched_configuration_load_config_file(mocker, default_conf) + + args = [ + 'backtesting', + '--config', 'config.json', + '--datadir', str(testdatadir), + '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), + '--timeframe', '5m', + '--timeframe-detail', '1m', + '--strategy-list', + 'DefaultStrategy' + ] + args = get_args(args) + start_backtesting(args) + + # check the logs, that will contain the backtest result + exists = [ + 'Parameter -i/--timeframe detected ... Using timeframe: 5m ...', + 'Parameter --timeframe-detail detected, using 1m for intra-candle backtesting ...', + f'Using data directory: {testdatadir} ...', + 'Loading data from 2019-10-11 00:00:00 ' + 'up to 2019-10-13 11:10:00 (2 days).', + 'Backtesting with data from 2019-10-11 01:40:00 ' + 'up to 2019-10-13 11:10:00 (2 days).', + 'Running backtesting for Strategy DefaultStrategy', + ] + + for line in exists: + assert log_has(line, caplog) + + captured = capsys.readouterr() + assert 'BACKTESTING REPORT' in captured.out + assert 'SELL REASON STATS' in captured.out + assert 'LEFT OPEN TRADES REPORT' in captured.out From fa4ec9f83e337b4bd0bee1ca798a4e6c310758c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Aug 2021 17:36:02 +0200 Subject: [PATCH 294/519] Add explicit test for get_sell_trade_entry --- freqtrade/optimize/backtesting.py | 5 +- tests/optimize/test_backtesting.py | 88 +++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 156ff48be..4b52e104b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -376,7 +376,7 @@ class Backtesting: return None def _get_sell_trade_entry(self, trade: LocalTrade, sell_row: Tuple) -> Optional[LocalTrade]: - if self.timeframe_detail: + if self.timeframe_detail and trade.pair in self.detail_data: sell_candle_time = sell_row[DATE_IDX].to_pydatetime() sell_candle_end = sell_candle_time + timedelta(minutes=self.timeframe_min) @@ -385,6 +385,9 @@ class Backtesting: (detail_data['date'] >= sell_candle_time) & (detail_data['date'] < sell_candle_end) ] + if len(detail_data) == 0: + # Fall back to "regular" data if no detail data was found for this candle + return self._get_sell_trade_entry_for_candle(trade, sell_row) detail_data['buy'] = sell_row[BUY_IDX] detail_data['sell'] = sell_row[SELL_IDX] headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 1b3fede72..1b4285339 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1,7 +1,7 @@ # pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument import random -from datetime import timedelta +from datetime import datetime, timedelta, timezone from pathlib import Path from unittest.mock import MagicMock, PropertyMock @@ -500,7 +500,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: pair = 'UNITTEST/BTC' row = [ pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0), - 1, # Sell + 1, # Buy 0.001, # Open 0.0011, # Close 0, # Sell @@ -548,6 +548,88 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None: backtesting.cleanup() +def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None: + default_conf['use_sell_signal'] = False + mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) + patch_exchange(mocker) + default_conf['timeframe_detail'] = '1m' + default_conf['max_open_trades'] = 2 + backtesting = Backtesting(default_conf) + backtesting._set_strategy(backtesting.strategylist[0]) + pair = 'UNITTEST/BTC' + row = [ + pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=55, tzinfo=timezone.utc), + 1, # Buy + 200, # Open + 201, # Close + 0, # Sell + 195, # Low + 201.5, # High + '', # Buy Signal Name + ] + + trade = backtesting._enter_trade(pair, row=row) + assert isinstance(trade, LocalTrade) + + row_sell = [ + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc), + 0, # Buy + 200, # Open + 201, # Close + 0, # Sell + 195, # Low + 210.5, # High + '', # Buy Signal Name + ] + row_detail = pd.DataFrame( + [ + [ + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc), + 1, 200, 199, 0, 197, 200.1, '', + ], [ + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=1, tzinfo=timezone.utc), + 0, 199, 199.5, 0, 199, 199.7, '', + ], [ + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=2, tzinfo=timezone.utc), + 0, 199.5, 200.5, 0, 199, 200.8, '', + ], [ + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=3, tzinfo=timezone.utc), + 0, 200.5, 210.5, 0, 193, 210.5, '', # ROI sell (?) + ], [ + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=4, tzinfo=timezone.utc), + 0, 200, 199, 0, 193, 200.1, '', + ], + ], columns=["date", "buy", "open", "close", "sell", "low", "high", "buy_tag"] + ) + + # No data available. + res = backtesting._get_sell_trade_entry(trade, row_sell) + assert res is not None + assert res.sell_reason == SellType.ROI.value + assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc) + + # Enter new trade + trade = backtesting._enter_trade(pair, row=row) + assert isinstance(trade, LocalTrade) + # Assign empty ... no result. + backtesting.detail_data[pair] = pd.DataFrame( + [], columns=["date", "buy", "open", "close", "sell", "low", "high", "buy_tag"]) + + res = backtesting._get_sell_trade_entry(trade, row) + assert res is None + + # Assign backtest-detail data + backtesting.detail_data[pair] = row_detail + + res = backtesting._get_sell_trade_entry(trade, row_sell) + assert res is not None + assert res.sell_reason == SellType.ROI.value + # Sell at minute 3 (not available above!) + assert res.close_date_utc == datetime(2020, 1, 1, 5, 3, tzinfo=timezone.utc) + assert round(res.close_rate, 3) == round(209.0225, 3) + + def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: default_conf['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) @@ -1127,7 +1209,7 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker, '--timeframe-detail', '1m', '--strategy-list', 'DefaultStrategy' - ] + ] args = get_args(args) start_backtesting(args) From 4f10a885290c5703e0f93e6880db8af757e20fca Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Aug 2021 15:14:53 +0200 Subject: [PATCH 295/519] Reduce verbosity when incompatible pairs are detected --- freqtrade/plugins/pairlist/IPairList.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/freqtrade/plugins/pairlist/IPairList.py b/freqtrade/plugins/pairlist/IPairList.py index bfde2ace0..0155f918b 100644 --- a/freqtrade/plugins/pairlist/IPairList.py +++ b/freqtrade/plugins/pairlist/IPairList.py @@ -150,18 +150,20 @@ class IPairList(LoggingMixin, ABC): for pair in pairlist: # pair is not in the generated dynamic market or has the wrong stake currency if pair not in markets: - logger.warning(f"Pair {pair} is not compatible with exchange " - f"{self._exchange.name}. Removing it from whitelist..") + self.log_once(f"Pair {pair} is not compatible with exchange " + f"{self._exchange.name}. Removing it from whitelist..", + logger.warning) continue if not self._exchange.market_is_tradable(markets[pair]): - logger.warning(f"Pair {pair} is not tradable with Freqtrade." - "Removing it from whitelist..") + self.log_once(f"Pair {pair} is not tradable with Freqtrade." + "Removing it from whitelist..", logger.warning) continue if self._exchange.get_pair_quote_currency(pair) != self._config['stake_currency']: - logger.warning(f"Pair {pair} is not compatible with your stake currency " - f"{self._config['stake_currency']}. Removing it from whitelist..") + self.log_once(f"Pair {pair} is not compatible with your stake currency " + f"{self._config['stake_currency']}. Removing it from whitelist..", + logger.warning) continue # Check if market is active From ed6776c5cdb8d2472cb0ef3669e3b4b4fe987021 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Aug 2021 17:06:16 +0200 Subject: [PATCH 296/519] Fix disappearing pairlist bug --- freqtrade/plugins/pairlist/VolumePairList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 901fde2d0..8d01aeee1 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -126,7 +126,7 @@ class VolumePairList(IPairList): pairlist = [s['symbol'] for s in filtered_tickers] pairlist = self.filter_pairlist(pairlist, tickers) - self._pair_cache['pairlist'] = pairlist + self._pair_cache['pairlist'] = pairlist.copy() return pairlist From 322ea2481e7be80355199d803aab29bf5f52d827 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Aug 2021 19:28:36 +0200 Subject: [PATCH 297/519] Add log-message for edge-case --- freqtrade/plugins/pairlist/rangestabilityfilter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index ef7f2cbcb..3e5a002ff 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -120,5 +120,6 @@ class RangeStabilityFilter(IPairList): logger.info) result = False self._pair_cache[pair] = result - + else: + self.log_once(f"Removed {pair} from whitelist, no candles found.", logger.info) return result From dda82765893d4474e4a90d0b274dfab36939c382 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Aug 2021 20:01:54 +0200 Subject: [PATCH 298/519] Update documentation for sell_profit_offset As highlighed in #5393 --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index fd4806fe6..fab3004a5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -105,7 +105,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `ask_strategy.order_book_top` | Bot will use the top N rate in Order Book "price_side" to sell. I.e. a value of 2 will allow the bot to pick the 2nd ask rate in [Order Book Asks](#sell-price-with-orderbook-enabled)
    *Defaults to `1`.*
    **Datatype:** Positive Integer | `use_sell_signal` | Use sell signals produced by the strategy in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy).
    *Defaults to `true`.*
    **Datatype:** Boolean | `sell_profit_only` | Wait until the bot reaches `sell_profit_offset` before taking a sell decision. [Strategy Override](#parameters-in-the-strategy).
    *Defaults to `false`.*
    **Datatype:** Boolean -| `sell_profit_offset` | Sell-signal is only active above this value. [Strategy Override](#parameters-in-the-strategy).
    *Defaults to `0.0`.*
    **Datatype:** Float (as ratio) +| `sell_profit_offset` | Sell-signal is only active above this value. Only active in combination with `sell_profit_only=True`. [Strategy Override](#parameters-in-the-strategy).
    *Defaults to `0.0`.*
    **Datatype:** Float (as ratio) | `ignore_roi_if_buy_signal` | Do not sell if the buy signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy).
    *Defaults to `false`.*
    **Datatype:** Boolean | `ignore_buying_expired_candle_after` | Specifies the number of seconds until a buy signal is no longer used.
    **Datatype:** Integer | `order_types` | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).
    **Datatype:** Dict From 37d4545123cbfbf5e52cddf703396a38105f9157 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 03:01:06 +0000 Subject: [PATCH 299/519] Bump ccxt from 1.54.74 to 1.55.6 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.54.74 to 1.55.6. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.54.74...1.55.6) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0e107d8e0..3880e3ced 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.1 pandas==1.3.1 -ccxt==1.54.74 +ccxt==1.55.6 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From e7b6a996dffbdffdfd9ab5a9de1ca87fdb5f91e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 03:01:10 +0000 Subject: [PATCH 300/519] Bump mkdocs-material from 7.2.2 to 7.2.4 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.2.2 to 7.2.4. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.2.2...7.2.4) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 047821f2d..8fa7341c9 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.2.2 +mkdocs-material==7.2.4 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From 7125793249cb1cb6b97b2206fe8580c07150d04d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 03:01:16 +0000 Subject: [PATCH 301/519] Bump uvicorn from 0.14.0 to 0.15.0 Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.14.0 to 0.15.0. - [Release notes](https://github.com/encode/uvicorn/releases) - [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md) - [Commits](https://github.com/encode/uvicorn/compare/0.14.0...0.15.0) --- updated-dependencies: - dependency-name: uvicorn dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0e107d8e0..ae6b24eb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,7 +32,7 @@ sdnotify==0.3.2 # API Server fastapi==0.68.0 -uvicorn==0.14.0 +uvicorn==0.15.0 pyjwt==2.1.0 aiofiles==0.7.0 From d8607b2ce880163027f6736702934c94949257f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 03:01:18 +0000 Subject: [PATCH 302/519] Bump flake8-tidy-imports from 4.3.0 to 4.4.1 Bumps [flake8-tidy-imports](https://github.com/adamchainz/flake8-tidy-imports) from 4.3.0 to 4.4.1. - [Release notes](https://github.com/adamchainz/flake8-tidy-imports/releases) - [Changelog](https://github.com/adamchainz/flake8-tidy-imports/blob/main/HISTORY.rst) - [Commits](https://github.com/adamchainz/flake8-tidy-imports/compare/4.3.0...4.4.1) --- updated-dependencies: - dependency-name: flake8-tidy-imports dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 9629bbea1..b20cb6693 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ coveralls==3.2.0 flake8==3.9.2 flake8-type-annotations==0.1.0 -flake8-tidy-imports==4.3.0 +flake8-tidy-imports==4.4.1 mypy==0.910 pytest==6.2.4 pytest-asyncio==0.15.1 From a10fd6690675d481f26df78d0fa3f9147684140e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 03:01:21 +0000 Subject: [PATCH 303/519] Bump plotly from 5.1.0 to 5.2.1 Bumps [plotly](https://github.com/plotly/plotly.py) from 5.1.0 to 5.2.1. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.1.0...v5.2.1) --- updated-dependencies: - dependency-name: plotly dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index e03fd4d66..d835ed5d9 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.1.0 +plotly==5.2.1 From 3878e5186e19a3f5fbe3f14c1b6300320891496e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 03:01:34 +0000 Subject: [PATCH 304/519] Bump numpy from 1.21.1 to 1.21.2 Bumps [numpy](https://github.com/numpy/numpy) from 1.21.1 to 1.21.2. - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt) - [Commits](https://github.com/numpy/numpy/compare/v1.21.1...v1.21.2) --- updated-dependencies: - dependency-name: numpy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0e107d8e0..1d0951e6d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy==1.21.1 +numpy==1.21.2 pandas==1.3.1 ccxt==1.54.74 From 108a6cb897342e4f436d41f845dc9d0198e58770 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 04:28:42 +0000 Subject: [PATCH 305/519] Bump pandas from 1.3.1 to 1.3.2 Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Changelog](https://github.com/pandas-dev/pandas/blob/master/RELEASE.md) - [Commits](https://github.com/pandas-dev/pandas/compare/v1.3.1...v1.3.2) --- updated-dependencies: - dependency-name: pandas dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 863475d86..e93e6211f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ numpy==1.21.2 -pandas==1.3.1 +pandas==1.3.2 ccxt==1.55.6 # Pin cryptography for now due to rust build errors with piwheels From abddb3ef257e6046b65595b0da2d19145adebccc Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Aug 2021 06:38:36 +0200 Subject: [PATCH 306/519] Add test for directory traversal --- tests/rpc/test_rpc_apiserver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 1517b6fcc..edf5ce3c5 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -109,6 +109,10 @@ def test_api_ui_fallback(botclient): rc = client_get(client, "/something") assert rc.status_code == 200 + rc = client_get(client, '%2F%2F%2Fetc/passwd') + assert rc.status_code == 200 + assert '`freqtrade install-ui`' in rc.text + def test_api_ui_version(botclient, mocker): ftbot, client = botclient From 6b2ef36a567a4aba47113fb56dfa9377f70797ad Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Aug 2021 06:45:43 +0200 Subject: [PATCH 307/519] Prevent directory traversal in UI Serving Checking for directory base closes #5427 --- freqtrade/rpc/api_server/web_ui.py | 7 +++++-- tests/rpc/test_rpc_apiserver.py | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/api_server/web_ui.py b/freqtrade/rpc/api_server/web_ui.py index 76c8ed8f2..0168930cf 100644 --- a/freqtrade/rpc/api_server/web_ui.py +++ b/freqtrade/rpc/api_server/web_ui.py @@ -37,8 +37,11 @@ async def index_html(rest_of_path: str): if rest_of_path.startswith('api') or rest_of_path.startswith('.'): raise HTTPException(status_code=404, detail="Not Found") uibase = Path(__file__).parent / 'ui/installed/' - if (uibase / rest_of_path).is_file(): - return FileResponse(str(uibase / rest_of_path)) + filename = uibase / rest_of_path + # It's security relevant to check "relative_to". + # Without this, Directory-traversal is possible. + if filename.is_file() and filename.is_relative_to(uibase): + return FileResponse(str(filename)) index_file = uibase / 'index.html' if not index_file.is_file(): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index edf5ce3c5..3d02e8188 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -109,6 +109,7 @@ def test_api_ui_fallback(botclient): rc = client_get(client, "/something") assert rc.status_code == 200 + # Test directory traversal rc = client_get(client, '%2F%2F%2Fetc/passwd') assert rc.status_code == 200 assert '`freqtrade install-ui`' in rc.text From 4b65206e6b8e19ef7696a8f7daaad3a441aa352e Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Aug 2021 07:08:50 +0200 Subject: [PATCH 308/519] Add compat code for is_relative_to --- freqtrade/rpc/api_server/web_ui.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/web_ui.py b/freqtrade/rpc/api_server/web_ui.py index 0168930cf..9aae0afae 100644 --- a/freqtrade/rpc/api_server/web_ui.py +++ b/freqtrade/rpc/api_server/web_ui.py @@ -29,6 +29,15 @@ async def ui_version(): } +def is_relative_to(path, base) -> bool: + # Helper function simulating behaviour of is_relative_to, which was only added in python 3.9 + try: + path.relative_to(base) + except ValueError: + pass + return False + + @router_ui.get('/{rest_of_path:path}', include_in_schema=False) async def index_html(rest_of_path: str): """ @@ -40,7 +49,7 @@ async def index_html(rest_of_path: str): filename = uibase / rest_of_path # It's security relevant to check "relative_to". # Without this, Directory-traversal is possible. - if filename.is_file() and filename.is_relative_to(uibase): + if filename.is_file() and is_relative_to(filename, uibase): return FileResponse(str(filename)) index_file = uibase / 'index.html' From 4115121c249077ff123c2d15ac22df62ed375ed4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Aug 2021 07:26:58 +0200 Subject: [PATCH 309/519] Fix missing return statement in is_Relative_to --- freqtrade/rpc/api_server/web_ui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/rpc/api_server/web_ui.py b/freqtrade/rpc/api_server/web_ui.py index 9aae0afae..b04269c61 100644 --- a/freqtrade/rpc/api_server/web_ui.py +++ b/freqtrade/rpc/api_server/web_ui.py @@ -33,6 +33,7 @@ def is_relative_to(path, base) -> bool: # Helper function simulating behaviour of is_relative_to, which was only added in python 3.9 try: path.relative_to(base) + return True except ValueError: pass return False From f24a951ec5cbc5ee3804d5eddfbf692acff07aeb Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 16 Aug 2021 19:16:24 +0700 Subject: [PATCH 310/519] fix: typo spelling grammar --- docs/exchanges.md | 2 +- docs/strategy_analysis_example.md | 2 +- freqtrade/data/btanalysis.py | 2 +- freqtrade/data/converter.py | 4 ++-- freqtrade/exchange/exchange.py | 2 +- freqtrade/freqtradebot.py | 2 +- freqtrade/plot/plotting.py | 2 +- freqtrade/rpc/rpc.py | 2 +- tests/data/test_converter.py | 2 +- tests/data/test_dataprovider.py | 2 +- tests/data/test_history.py | 6 +++--- tests/strategy/test_interface.py | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index 29b9bb533..5f54a524e 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -105,7 +105,7 @@ To use subaccounts with FTX, you need to edit the configuration and add the foll ## Kucoin -Kucoin requries a passphrase for each api key, you will therefore need to add this key into the configuration so your exchange section looks as follows: +Kucoin requires a passphrase for each api key, you will therefore need to add this key into the configuration so your exchange section looks as follows: ```json "exchange": { diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index 27192aa2f..dd7e07824 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -228,7 +228,7 @@ graph = generate_candlestick_graph(pair=pair, # Show graph inline # graph.show() -# Render graph in a seperate window +# Render graph in a separate window graph.show(renderer="browser") ``` diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index d62712cbb..7d97661c4 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -19,7 +19,7 @@ logger = logging.getLogger(__name__) BT_DATA_COLUMNS_OLD = ["pair", "profit_percent", "open_date", "close_date", "index", "trade_duration", "open_rate", "close_rate", "open_at_end", "sell_reason"] -# Mid-term format, crated by BacktestResult Named Tuple +# Mid-term format, created by BacktestResult Named Tuple BT_DATA_COLUMNS_MID = ['pair', 'profit_percent', 'open_date', 'close_date', 'trade_duration', 'open_rate', 'close_rate', 'open_at_end', 'sell_reason', 'fee_open', 'fee_close', 'amount', 'profit_abs', 'profit_ratio'] diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 040f58d62..ca6464965 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -242,7 +242,7 @@ def convert_trades_format(config: Dict[str, Any], convert_from: str, convert_to: :param config: Config dictionary :param convert_from: Source format :param convert_to: Target format - :param erase: Erase souce data (does not apply if source and target format are identical) + :param erase: Erase source data (does not apply if source and target format are identical) """ from freqtrade.data.history.idatahandler import get_datahandler src = get_datahandler(config['datadir'], convert_from) @@ -267,7 +267,7 @@ def convert_ohlcv_format(config: Dict[str, Any], convert_from: str, convert_to: :param config: Config dictionary :param convert_from: Source format :param convert_to: Target format - :param erase: Erase souce data (does not apply if source and target format are identical) + :param erase: Erase source data (does not apply if source and target format are identical) """ from freqtrade.data.history.idatahandler import get_datahandler src = get_datahandler(config['datadir'], convert_from) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index cde643cff..9aa5b98a8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1497,7 +1497,7 @@ class Exchange: :returns List of trade data """ if not self.exchange_has("fetchTrades"): - raise OperationalException("This exchange does not suport downloading Trades.") + raise OperationalException("This exchange does not support downloading Trades.") return asyncio.get_event_loop().run_until_complete( self._async_get_trade_history(pair=pair, since=since, diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 09aa06adf..fb15d6e5c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -977,7 +977,7 @@ class FreqtradeBot(LoggingMixin): # if trade is partially complete, edit the stake details for the trade # and close the order # cancel_order may not contain the full order dict, so we need to fallback - # to the order dict aquired before cancelling. + # to the order dict acquired before cancelling. # we need to fall back to the values from order if corder does not contain these keys. trade.amount = filled_amount trade.stake_amount = trade.amount * trade.open_rate diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 2fbf343ce..509c03e90 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -538,7 +538,7 @@ def load_and_plot_trades(config: Dict[str, Any]): - Initializes plot-script - Get candle (OHLCV) data - Generate Dafaframes populated with indicators and signals based on configured strategy - - Load trades excecuted during the selected period + - Load trades executed during the selected period - Generate Plotly plot objects - Generate plot files :return: None diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 902975fde..0264003a5 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -776,7 +776,7 @@ class RPC: if has_content: dataframe.loc[:, '__date_ts'] = dataframe.loc[:, 'date'].view(int64) // 1000 // 1000 - # Move open to seperate column when signal for easy plotting + # Move open to separate column when signal for easy plotting if 'buy' in dataframe.columns: buy_mask = (dataframe['buy'] == 1) buy_signals = int(buy_mask.sum()) diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py index 802fd4b12..6c95a9f18 100644 --- a/tests/data/test_converter.py +++ b/tests/data/test_converter.py @@ -119,7 +119,7 @@ def test_ohlcv_fill_up_missing_data2(caplog): # 3rd candle has been filled row = data2.loc[2, :] assert row['volume'] == 0 - # close shoult match close of previous candle + # close should match close of previous candle assert row['close'] == data.loc[1, 'close'] assert row['open'] == row['close'] assert row['high'] == row['close'] diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index e43309743..0f42068c1 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -66,7 +66,7 @@ def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history): hdf5loadmock.assert_not_called() jsonloadmock.assert_called_once() - # Swiching to dataformat hdf5 + # Switching to dataformat hdf5 hdf5loadmock.reset_mock() jsonloadmock.reset_mock() default_conf["dataformat_ohlcv"] = "hdf5" diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 9cfe861ea..13d22ebb7 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -200,15 +200,15 @@ def test_load_cached_data_for_updating(mocker, testdatadir) -> None: assert start_ts == test_data[0][0] - 1000 # timeframe starts in the center of the cached data - # should return the chached data w/o the last item + # should return the cached data w/o the last item timerange = TimeRange('date', None, test_data[0][0] / 1000 + 1, 0) data, start_ts = _load_cached_data_for_updating('UNITTEST/BTC', '1m', timerange, data_handler) assert_frame_equal(data, test_data_df.iloc[:-1]) assert test_data[-2][0] <= start_ts < test_data[-1][0] - # timeframe starts after the chached data - # should return the chached data w/o the last item + # timeframe starts after the cached data + # should return the cached data w/o the last item timerange = TimeRange('date', None, test_data[-1][0] / 1000 + 100, 0) data, start_ts = _load_cached_data_for_updating('UNITTEST/BTC', '1m', timerange, data_handler) assert_frame_equal(data, test_data_df.iloc[:-1]) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index cb4b8bd63..eea6a85d2 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -630,7 +630,7 @@ def test_strategy_safe_wrapper_error(caplog, error): assert ret caplog.clear() - # Test supressing error + # Test suppressing error ret = strategy_safe_wrapper(failing_method, message='DeadBeef', supress_error=True)() assert log_has_re(r'DeadBeef.*', caplog) From 0264d77d8670e43304b58ad1ea2d39cff6550596 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Aug 2021 20:02:25 +0200 Subject: [PATCH 311/519] Fix test for fixed typo --- tests/exchange/test_exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 9ac9f84e5..a5099a3ce 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2182,7 +2182,7 @@ def test_get_historic_trades_notsupported(default_conf, mocker, caplog, exchange pair = 'ETH/BTC' with pytest.raises(OperationalException, - match="This exchange does not suport downloading Trades."): + match="This exchange does not support downloading Trades."): exchange.get_historic_trades(pair, since=trades_history[0][0], until=trades_history[-1][0]) From 3ea4b2ba00a4d3cef42def46dc0b32a7ef7b7603 Mon Sep 17 00:00:00 2001 From: axel Date: Mon, 16 Aug 2021 15:18:57 -0400 Subject: [PATCH 312/519] add custom_price_max_distance_percent security to get_valid_price, update tests --- freqtrade/freqtradebot.py | 17 ++++++++++++++--- tests/test_freqtradebot.py | 14 +++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9a1b2ab0c..13632bad1 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1401,10 +1401,21 @@ class FreqtradeBot(LoggingMixin): """ if custom_price: try: - valid_price = float(custom_price) + valid_custom_price = float(custom_price) except ValueError: - valid_price = proposed_price + valid_custom_price = proposed_price else: - valid_price = proposed_price + valid_custom_price = proposed_price + + cust_p_max_dist_pct = self.config.get('custom_price_max_distance_percent', 2.0) + min_custom_price_allowed = proposed_price - ((proposed_price * cust_p_max_dist_pct) / 100) + max_custom_price_allowed = proposed_price + ((proposed_price * cust_p_max_dist_pct) / 100) + + if valid_custom_price > max_custom_price_allowed: + valid_price = max_custom_price_allowed + elif valid_custom_price < min_custom_price_allowed: + valid_price = min_custom_price_allowed + else: + valid_price = valid_custom_price return valid_price diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index a475ced48..80bcabdb6 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4594,19 +4594,25 @@ def test_get_valid_price(mocker, default_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker) freqtrade = FreqtradeBot(default_conf) + freqtrade.config['custom_price_max_distance_percent'] = 2.0 custom_price_string = "10" custom_price_badstring = "10abc" custom_price_float = 10.0 custom_price_int = 10 - proposed_price = 12.2 + custom_price_over_max_alwd = 11.0 + custom_price_under_min_alwd = 9.0 + proposed_price = 10.1 valid_price_from_string = freqtrade.get_valid_price(custom_price_string, proposed_price) valid_price_from_badstring = freqtrade.get_valid_price(custom_price_badstring, proposed_price) valid_price_from_int = freqtrade.get_valid_price(custom_price_int, proposed_price) valid_price_from_float = freqtrade.get_valid_price(custom_price_float, proposed_price) + valid_price_at_max_alwd = freqtrade.get_valid_price(custom_price_over_max_alwd, proposed_price) + valid_price_at_min_alwd = freqtrade.get_valid_price(custom_price_under_min_alwd, proposed_price) + assert isinstance(valid_price_from_string, float) assert isinstance(valid_price_from_badstring, float) assert isinstance(valid_price_from_int, float) @@ -4616,3 +4622,9 @@ def test_get_valid_price(mocker, default_conf) -> None: assert valid_price_from_badstring == proposed_price assert valid_price_from_int == custom_price_int assert valid_price_from_float == custom_price_float + + assert valid_price_at_max_alwd != custom_price_over_max_alwd + assert valid_price_at_max_alwd > proposed_price + + assert valid_price_at_min_alwd != custom_price_under_min_alwd + assert valid_price_at_min_alwd < proposed_price From faff40577a48acc161593e685c707664c5d9083d Mon Sep 17 00:00:00 2001 From: axel Date: Mon, 16 Aug 2021 15:33:05 -0400 Subject: [PATCH 313/519] fix test_execute_buy In case of custom entry price --- tests/test_freqtradebot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 80bcabdb6..5b5e3ce28 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -905,13 +905,14 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order freqtrade.execute_buy(pair, stake_amount) # In case of custom entry price + mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.50) limit_buy_order['status'] = 'open' limit_buy_order['id'] = '5566' - freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.77 + freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508 assert freqtrade.execute_buy(pair, stake_amount) trade = Trade.query.all()[6] assert trade - assert trade.open_rate_requested == 0.77 + assert trade.open_rate_requested == 0.508 # In case of custom entry price set to None limit_buy_order['status'] = 'open' From 17daba321bfbc755443c5399c9ad3569f11fc38b Mon Sep 17 00:00:00 2001 From: axel Date: Mon, 16 Aug 2021 23:09:30 -0400 Subject: [PATCH 314/519] add custom_price_max_distance_percent config option in constants --- freqtrade/constants.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index de4bc99b4..2f51f45f7 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -191,6 +191,9 @@ CONF_SCHEMA = { }, 'required': ['price_side'] }, + 'custom_price_max_distance_percent': { + 'type': 'number', 'minimum': 0.0 + }, 'order_types': { 'type': 'object', 'properties': { From f08d673a52f4034e367c26ca911911b793d6a734 Mon Sep 17 00:00:00 2001 From: axel Date: Mon, 16 Aug 2021 23:26:08 -0400 Subject: [PATCH 315/519] add details and exemple of custom_price_max_distance_percent usage in doc --- docs/strategy-advanced.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index e53f20693..babcc5e7b 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -398,7 +398,10 @@ class AwesomeStrategy(IStrategy): ``` !!! Warning - Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. + Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. By default the maximum allowed distance between the current price and the custom price is 2%, this value can be changed in config with the `custom_price_max_distance_percent` parameter. + +_Exemple_ +If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_percent` is set to 2%, The retained valid custom entry price will be 98. !!! Warning "No backtesting support" Custom entry-prices are currently not supported during backtesting. From f47191582872279de090667cb5fc8f703125569c Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 17 Aug 2021 06:41:13 +0200 Subject: [PATCH 316/519] Add test for refresh_latest_ohlcv caching --- tests/exchange/test_exchange.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index a5099a3ce..27eeed39b 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1564,13 +1564,16 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: pairs = [('IOTA/ETH', '5m'), ('XRP/ETH', '5m')] # empty dicts assert not exchange._klines - exchange.refresh_latest_ohlcv(pairs, cache=False) + res = exchange.refresh_latest_ohlcv(pairs, cache=False) # No caching assert not exchange._klines + + assert len(res) == len(pairs) assert exchange._api_async.fetch_ohlcv.call_count == 2 exchange._api_async.fetch_ohlcv.reset_mock() - exchange.refresh_latest_ohlcv(pairs) + res = exchange.refresh_latest_ohlcv(pairs) + assert len(res) == len(pairs) assert log_has(f'Refreshing candle (OHLCV) data for {len(pairs)} pairs', caplog) assert exchange._klines @@ -1587,12 +1590,16 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: assert exchange.klines(pair, copy=False) is exchange.klines(pair, copy=False) # test caching - exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')]) + res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')]) + assert len(res) == len(pairs) assert exchange._api_async.fetch_ohlcv.call_count == 2 assert log_has(f"Using cached candle (OHLCV) data for pair {pairs[0][0]}, " f"timeframe {pairs[0][1]} ...", caplog) + res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m'), ('XRP/ETH', '1d')], + cache=False) + assert len(res) == 3 @pytest.mark.asyncio From 9758bed250450eb54de455987de2c2c5b93ed6be Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 17 Aug 2021 06:44:20 +0200 Subject: [PATCH 317/519] Fix refresh_latest_ohlcv bug --- freqtrade/exchange/exchange.py | 7 ++++++- freqtrade/plugins/pairlist/VolumePairList.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 9aa5b98a8..dbd72aca4 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1253,7 +1253,7 @@ class Exchange: logger.debug("Refreshing candle (OHLCV) data for %d pairs", len(pair_list)) input_coroutines = [] - + cached_pairs = [] # Gather coroutines to run for pair, timeframe in set(pair_list): if (((pair, timeframe) not in self._klines) @@ -1265,6 +1265,7 @@ class Exchange: "Using cached candle (OHLCV) data for pair %s, timeframe %s ...", pair, timeframe ) + cached_pairs.append((pair, timeframe)) results = asyncio.get_event_loop().run_until_complete( asyncio.gather(*input_coroutines, return_exceptions=True)) @@ -1287,6 +1288,10 @@ class Exchange: results_df[(pair, timeframe)] = ohlcv_df if cache: self._klines[(pair, timeframe)] = ohlcv_df + # Return cached klines + for pair, timeframe in cached_pairs: + results_df[(pair, timeframe)] = self.klines((pair, timeframe), copy=False) + return results_df def _now_is_time_to_refresh(self, pair: str, timeframe: str) -> bool: diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 8d01aeee1..23291d39e 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -115,7 +115,7 @@ class VolumePairList(IPairList): pairlist = self._pair_cache.get('pairlist') if pairlist: # Item found - no refresh necessary - return pairlist + return pairlist.copy() else: # Use fresh pairlist # Check if pair quote currency equals to the stake currency. From 37e3d20357943e18e0f0197a11e911aa4d2031e0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 17 Aug 2021 06:54:10 +0200 Subject: [PATCH 318/519] Fix no-event-loop available closes #5433 --- freqtrade/rpc/api_server/uvicorn_threaded.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/api_server/uvicorn_threaded.py b/freqtrade/rpc/api_server/uvicorn_threaded.py index 2f72cb74c..b63999f51 100644 --- a/freqtrade/rpc/api_server/uvicorn_threaded.py +++ b/freqtrade/rpc/api_server/uvicorn_threaded.py @@ -32,8 +32,11 @@ class UvicornServer(uvicorn.Server): asyncio_setup() else: asyncio.set_event_loop(uvloop.new_event_loop()) - - loop = asyncio.get_event_loop() + try: + loop = asyncio.get_event_loop() + except RuntimeError: + # When running in a thread, we'll not have an eventloop yet. + loop = asyncio.new_event_loop() loop.run_until_complete(self.serve(sockets=sockets)) @contextlib.contextmanager From 4164f9385313f98bce671aaa2fc9182bd5ad72d5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 17 Aug 2021 20:41:08 +0200 Subject: [PATCH 319/519] Simplify fiat_convert and handle multi-mappings --- freqtrade/rpc/fiat_convert.py | 40 ++++++++++++++++++++-------------- tests/rpc/test_fiat_convert.py | 39 +++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/freqtrade/rpc/fiat_convert.py b/freqtrade/rpc/fiat_convert.py index cdc09b437..f4e82261e 100644 --- a/freqtrade/rpc/fiat_convert.py +++ b/freqtrade/rpc/fiat_convert.py @@ -5,7 +5,7 @@ e.g BTC to USD import datetime import logging -from typing import Dict +from typing import Dict, List from cachetools.ttl import TTLCache from pycoingecko import CoinGeckoAPI @@ -25,8 +25,7 @@ class CryptoToFiatConverter: """ __instance = None _coingekko: CoinGeckoAPI = None - - _cryptomap: Dict = {} + _coinlistings: List[Dict] = [] _backoff: float = 0.0 def __new__(cls): @@ -49,9 +48,8 @@ class CryptoToFiatConverter: def _load_cryptomap(self) -> None: try: - coinlistings = self._coingekko.get_coins_list() - # Create mapping table from symbol to coingekko_id - self._cryptomap = {x['symbol']: x['id'] for x in coinlistings} + # Use list-comprehension to ensure we get a list. + self._coinlistings = [x for x in self._coingekko.get_coins_list()] except RequestException as request_exception: if "429" in str(request_exception): logger.warning( @@ -69,6 +67,24 @@ class CryptoToFiatConverter: logger.error( f"Could not load FIAT Cryptocurrency map for the following problem: {exception}") + def _get_gekko_id(self, crypto_symbol): + if not self._coinlistings: + if self._backoff <= datetime.datetime.now().timestamp(): + self._load_cryptomap() + # Still not loaded. + if not self._coinlistings: + return None + else: + return None + found = [x for x in self._coinlistings if x['symbol'] == crypto_symbol] + if len(found) == 1: + return found[0]['id'] + + if len(found) > 0: + # Wrong! + logger.warning(f"Found multiple mappings in goingekko for {crypto_symbol}.") + return None + def convert_amount(self, crypto_amount: float, crypto_symbol: str, fiat_symbol: str) -> float: """ Convert an amount of crypto-currency to fiat @@ -143,22 +159,14 @@ class CryptoToFiatConverter: if crypto_symbol == fiat_symbol: return 1.0 - if self._cryptomap == {}: - if self._backoff <= datetime.datetime.now().timestamp(): - self._load_cryptomap() - # return 0.0 if we still don't have data to check, no reason to proceed - if self._cryptomap == {}: - return 0.0 - else: - return 0.0 + _gekko_id = self._get_gekko_id(crypto_symbol) - if crypto_symbol not in self._cryptomap: + if not _gekko_id: # return 0 for unsupported stake currencies (fiat-convert should not break the bot) logger.warning("unsupported crypto-symbol %s - returning 0.0", crypto_symbol) return 0.0 try: - _gekko_id = self._cryptomap[crypto_symbol] return float( self._coingekko.get_price( ids=_gekko_id, diff --git a/tests/rpc/test_fiat_convert.py b/tests/rpc/test_fiat_convert.py index 9fb1122f5..2fe5d4a56 100644 --- a/tests/rpc/test_fiat_convert.py +++ b/tests/rpc/test_fiat_convert.py @@ -22,7 +22,7 @@ def test_fiat_convert_is_supported(mocker): def test_fiat_convert_find_price(mocker): fiat_convert = CryptoToFiatConverter() - fiat_convert._cryptomap = {} + fiat_convert._coinlistings = {} fiat_convert._backoff = 0 mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._load_cryptomap', return_value=None) @@ -44,7 +44,7 @@ def test_fiat_convert_find_price(mocker): def test_fiat_convert_unsupported_crypto(mocker, caplog): - mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[]) + mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._coinlistings', return_value=[]) fiat_convert = CryptoToFiatConverter() assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0 assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog) @@ -88,9 +88,9 @@ def test_fiat_convert_two_FIAT(mocker): def test_loadcryptomap(mocker): fiat_convert = CryptoToFiatConverter() - assert len(fiat_convert._cryptomap) == 2 + assert len(fiat_convert._coinlistings) == 2 - assert fiat_convert._cryptomap["btc"] == "bitcoin" + assert fiat_convert._get_gekko_id("btc") == "bitcoin" def test_fiat_init_network_exception(mocker): @@ -102,11 +102,10 @@ def test_fiat_init_network_exception(mocker): ) # with pytest.raises(RequestEsxception): fiat_convert = CryptoToFiatConverter() - fiat_convert._cryptomap = {} + fiat_convert._coinlistings = {} fiat_convert._load_cryptomap() - length_cryptomap = len(fiat_convert._cryptomap) - assert length_cryptomap == 0 + assert len(fiat_convert._coinlistings) == 0 def test_fiat_convert_without_network(mocker): @@ -132,11 +131,10 @@ def test_fiat_too_many_requests_response(mocker, caplog): ) # with pytest.raises(RequestEsxception): fiat_convert = CryptoToFiatConverter() - fiat_convert._cryptomap = {} + fiat_convert._coinlistings = {} fiat_convert._load_cryptomap() - length_cryptomap = len(fiat_convert._cryptomap) - assert length_cryptomap == 0 + assert len(fiat_convert._coinlistings) == 0 assert fiat_convert._backoff > datetime.datetime.now().timestamp() assert log_has( 'Too many requests for Coingecko API, backing off and trying again later.', @@ -144,20 +142,33 @@ def test_fiat_too_many_requests_response(mocker, caplog): ) +def test_fiat_multiple_coins(mocker, caplog): + fiat_convert = CryptoToFiatConverter() + fiat_convert._coinlistings = [ + {'id': 'helium', 'symbol': 'hnt', 'name': 'Helium'}, + {'id': 'hymnode', 'symbol': 'hnt', 'name': 'Hymnode'}, + {'id': 'bitcoin', 'symbol': 'btc', 'name': 'Bitcoin'}, + ] + + assert fiat_convert._get_gekko_id('btc') == 'bitcoin' + assert fiat_convert._get_gekko_id('hnt') is None + + assert log_has('Found multiple mappings in goingekko for hnt.', caplog) + + def test_fiat_invalid_response(mocker, caplog): # Because CryptoToFiatConverter is a Singleton we reset the listings - listmock = MagicMock(return_value="{'novalidjson':DEADBEEFf}") + listmock = MagicMock(return_value=None) mocker.patch.multiple( 'freqtrade.rpc.fiat_convert.CoinGeckoAPI', get_coins_list=listmock, ) # with pytest.raises(RequestEsxception): fiat_convert = CryptoToFiatConverter() - fiat_convert._cryptomap = {} + fiat_convert._coinlistings = [] fiat_convert._load_cryptomap() - length_cryptomap = len(fiat_convert._cryptomap) - assert length_cryptomap == 0 + assert len(fiat_convert._coinlistings) == 0 assert log_has_re('Could not load FIAT Cryptocurrency map for the following problem: .*', caplog) From 6e41add40e3d223f26d9acd9054571e1b7e947b7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 18 Aug 2021 06:25:30 +0200 Subject: [PATCH 320/519] Version bump ccxt closes #5437 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3bd34fad8..0e00c289a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.2 pandas==1.3.2 -ccxt==1.55.6 +ccxt==1.55.13 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From acb00cd0728efe1e2bee29fa3ed2ae2b7173935b Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 18 Aug 2021 06:25:52 +0200 Subject: [PATCH 321/519] Use realistic threshold for "get_fee" test --- tests/exchange/test_ccxt_compat.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index dce10da84..ffca9ec81 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -142,8 +142,8 @@ class TestCCXTExchange(): def test_ccxt_get_fee(self, exchange): exchange, exchangename = exchange pair = EXCHANGES[exchangename]['pair'] - - assert 0 < exchange.get_fee(pair, 'limit', 'buy') < 1 - assert 0 < exchange.get_fee(pair, 'limit', 'sell') < 1 - assert 0 < exchange.get_fee(pair, 'market', 'buy') < 1 - assert 0 < exchange.get_fee(pair, 'market', 'sell') < 1 + threshold = 0.01 + assert 0 < exchange.get_fee(pair, 'limit', 'buy') < threshold + assert 0 < exchange.get_fee(pair, 'limit', 'sell') < threshold + assert 0 < exchange.get_fee(pair, 'market', 'buy') < threshold + assert 0 < exchange.get_fee(pair, 'market', 'sell') < threshold From 2fb9f6e2f4d4621a9f87c27ee22588344901e29e Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 18 Aug 2021 05:07:37 -0400 Subject: [PATCH 322/519] rename custom price max distance option in config, update formula and test associated --- docs/strategy-advanced.md | 4 ++-- freqtrade/constants.py | 2 +- freqtrade/freqtradebot.py | 6 +++--- tests/test_freqtradebot.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index babcc5e7b..a0ae7201f 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -398,10 +398,10 @@ class AwesomeStrategy(IStrategy): ``` !!! Warning - Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. By default the maximum allowed distance between the current price and the custom price is 2%, this value can be changed in config with the `custom_price_max_distance_percent` parameter. + Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. By default the maximum allowed distance between the current price and the custom price is 2%, this value can be changed in config with the `custom_price_max_distance_ratio` parameter. _Exemple_ -If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_percent` is set to 2%, The retained valid custom entry price will be 98. +If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_ratio` is set to 2%, The retained valid custom entry price will be 98. !!! Warning "No backtesting support" Custom entry-prices are currently not supported during backtesting. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 2f51f45f7..cde276ac0 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -191,7 +191,7 @@ CONF_SCHEMA = { }, 'required': ['price_side'] }, - 'custom_price_max_distance_percent': { + 'custom_price_max_distance_ratio': { 'type': 'number', 'minimum': 0.0 }, 'order_types': { diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index bd62934c5..caf201451 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1407,9 +1407,9 @@ class FreqtradeBot(LoggingMixin): else: valid_custom_price = proposed_price - cust_p_max_dist_pct = self.config.get('custom_price_max_distance_percent', 2.0) - min_custom_price_allowed = proposed_price - ((proposed_price * cust_p_max_dist_pct) / 100) - max_custom_price_allowed = proposed_price + ((proposed_price * cust_p_max_dist_pct) / 100) + cust_p_max_dist_r = self.config.get('custom_price_max_distance_ratio', 0.02) + min_custom_price_allowed = proposed_price - (proposed_price * cust_p_max_dist_r) + max_custom_price_allowed = proposed_price + (proposed_price * cust_p_max_dist_r) if valid_custom_price > max_custom_price_allowed: valid_price = max_custom_price_allowed diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 5b5e3ce28..21bad5c64 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4595,7 +4595,7 @@ def test_get_valid_price(mocker, default_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker) freqtrade = FreqtradeBot(default_conf) - freqtrade.config['custom_price_max_distance_percent'] = 2.0 + freqtrade.config['custom_price_max_distance_ratio'] = 0.02 custom_price_string = "10" custom_price_badstring = "10abc" From 9469c6dfa9bc78bd6c9ff6ae17f0156ada4d6b39 Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 18 Aug 2021 05:10:29 -0400 Subject: [PATCH 323/519] small cosmetic changes in doc related to custom entry and exit exemple --- docs/strategy-advanced.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index a0ae7201f..f5f2d9197 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -400,8 +400,8 @@ class AwesomeStrategy(IStrategy): !!! Warning Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. By default the maximum allowed distance between the current price and the custom price is 2%, this value can be changed in config with the `custom_price_max_distance_ratio` parameter. -_Exemple_ -If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_ratio` is set to 2%, The retained valid custom entry price will be 98. +!!! Exemple + If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_ratio` is set to 2%, The retained valid custom entry price will be 98. !!! Warning "No backtesting support" Custom entry-prices are currently not supported during backtesting. From ffd60f392be7a83cf2bf786314322eba4e27bacf Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 18 Aug 2021 05:22:45 -0400 Subject: [PATCH 324/519] add custom price max distance ratio option in configuration.md --- docs/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration.md b/docs/configuration.md index fab3004a5..09198e019 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -110,6 +110,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `ignore_buying_expired_candle_after` | Specifies the number of seconds until a buy signal is no longer used.
    **Datatype:** Integer | `order_types` | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).
    **Datatype:** Dict | `order_time_in_force` | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy).
    **Datatype:** Dict +| `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price.
    *Defaults to `0.02` 2%).*
    **Datatype:** Positive float | `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
    **Datatype:** String | `exchange.sandbox` | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details.
    **Datatype:** Boolean | `exchange.key` | API key to use for the exchange. Only required when you are in production mode.
    **Keep it in secret, do not disclose publicly.**
    **Datatype:** String From d97fc1e484ceb29a93f31e259556e9f74abda717 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 18 Aug 2021 19:55:11 +0200 Subject: [PATCH 325/519] Update docs/strategy-advanced.md --- docs/strategy-advanced.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index f5f2d9197..b039f542f 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -400,7 +400,7 @@ class AwesomeStrategy(IStrategy): !!! Warning Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. By default the maximum allowed distance between the current price and the custom price is 2%, this value can be changed in config with the `custom_price_max_distance_ratio` parameter. -!!! Exemple +!!! Example If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_ratio` is set to 2%, The retained valid custom entry price will be 98. !!! Warning "No backtesting support" From 9951f510795c241f516d6046b12699be885e84b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 18 Aug 2021 20:20:11 +0200 Subject: [PATCH 326/519] Update test to ensure direction of movement is correct --- freqtrade/freqtradebot.py | 12 ++++-------- tests/test_freqtradebot.py | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index caf201451..e7a2a3784 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1411,11 +1411,7 @@ class FreqtradeBot(LoggingMixin): min_custom_price_allowed = proposed_price - (proposed_price * cust_p_max_dist_r) max_custom_price_allowed = proposed_price + (proposed_price * cust_p_max_dist_r) - if valid_custom_price > max_custom_price_allowed: - valid_price = max_custom_price_allowed - elif valid_custom_price < min_custom_price_allowed: - valid_price = min_custom_price_allowed - else: - valid_price = valid_custom_price - - return valid_price + # Bracket between min_custom_price_allowed and max_custom_price_allowed + return max( + min(valid_custom_price, max_custom_price_allowed), + min_custom_price_allowed) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 21bad5c64..a2bb01a4b 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4624,8 +4624,8 @@ def test_get_valid_price(mocker, default_conf) -> None: assert valid_price_from_int == custom_price_int assert valid_price_from_float == custom_price_float - assert valid_price_at_max_alwd != custom_price_over_max_alwd + assert valid_price_at_max_alwd < custom_price_over_max_alwd assert valid_price_at_max_alwd > proposed_price - assert valid_price_at_min_alwd != custom_price_under_min_alwd + assert valid_price_at_min_alwd > custom_price_under_min_alwd assert valid_price_at_min_alwd < proposed_price From dc0b4d07d48b78541701c5939ada74c31304c200 Mon Sep 17 00:00:00 2001 From: topscoder <86197446+topscoder@users.noreply.github.com> Date: Wed, 18 Aug 2021 20:52:11 +0200 Subject: [PATCH 327/519] Fix typo Reseting -> Resetting --- setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index a85bd3104..feb0241f8 100755 --- a/setup.sh +++ b/setup.sh @@ -163,7 +163,7 @@ function update() { # Reset Develop or Stable branch function reset() { echo "----------------------------" - echo "Reseting branch and virtual env" + echo "Resetting branch and virtual env" echo "----------------------------" if [ "1" == $(git branch -vv |grep -cE "\* develop|\* stable") ] From ba5abb20bd07211022b0db1a4cf47fa5ec7e9609 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Aug 2021 06:30:27 +0200 Subject: [PATCH 328/519] Run compat ci for gate.io --- requirements.txt | 2 +- tests/exchange/test_ccxt_compat.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0e00c289a..09109baaf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.2 pandas==1.3.2 -ccxt==1.55.13 +ccxt==1.55.20 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index ffca9ec81..3a32d108b 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -42,6 +42,11 @@ EXCHANGES = { 'hasQuoteVolume': True, 'timeframe': '5m', }, + 'gateio': { + 'pair': 'BTC/USDT', + 'hasQuoteVolume': True, + 'timeframe': '5m', + }, } From 695a1e21bf0b622bffc78dfd56073367cd713cc7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Aug 2021 06:51:04 +0200 Subject: [PATCH 329/519] Set gate.io download limit to 1000 candles --- freqtrade/exchange/__init__.py | 1 + freqtrade/exchange/gateio.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 freqtrade/exchange/gateio.py diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 015e0c869..b0c88a51a 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -15,6 +15,7 @@ from freqtrade.exchange.exchange import (available_exchanges, ccxt_exchanges, timeframe_to_seconds, validate_exchange, validate_exchanges) from freqtrade.exchange.ftx import Ftx +from freqtrade.exchange.gateio import Gateio from freqtrade.exchange.hitbtc import Hitbtc from freqtrade.exchange.kraken import Kraken from freqtrade.exchange.kucoin import Kucoin diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py new file mode 100644 index 000000000..9c910a10d --- /dev/null +++ b/freqtrade/exchange/gateio.py @@ -0,0 +1,23 @@ +""" Gate.io exchange subclass """ +import logging +from typing import Dict + +from freqtrade.exchange import Exchange + + +logger = logging.getLogger(__name__) + + +class Gateio(Exchange): + """ + Gate.io exchange class. Contains adjustments needed for Freqtrade to work + with this exchange. + + Please note that this exchange is not included in the list of exchanges + officially supported by the Freqtrade development team. So some features + may still not work as expected. + """ + + _ft_has: Dict = { + "ohlcv_candle_limit": 1000, + } From 40ad4510194e3eba6512fa1f2ca7d071ae38babd Mon Sep 17 00:00:00 2001 From: Masoud Azizi Date: Thu, 19 Aug 2021 18:34:02 +0000 Subject: [PATCH 330/519] Download-data log process added Download-data log process added pytest assert solved --- freqtrade/data/history/history_utils.py | 33 ++++++++++++++----------- tests/data/test_history.py | 10 +++++--- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index 1459dfd78..6f125aaa9 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -117,10 +117,11 @@ def refresh_data(datadir: Path, :param timerange: Limit data to be loaded to this timerange """ data_handler = get_datahandler(datadir, data_format) - for pair in pairs: - _download_pair_history(pair=pair, timeframe=timeframe, - datadir=datadir, timerange=timerange, - exchange=exchange, data_handler=data_handler) + for idx, pair in enumerate(pairs): + process = f'{idx}/{len(pairs)}' + _download_pair_history(pair=pair, process=process, + timeframe=timeframe, datadir=datadir, + timerange=timerange, exchange=exchange, data_handler=data_handler) def _load_cached_data_for_updating(pair: str, timeframe: str, timerange: Optional[TimeRange], @@ -153,13 +154,14 @@ def _load_cached_data_for_updating(pair: str, timeframe: str, timerange: Optiona return data, start_ms -def _download_pair_history(datadir: Path, +def _download_pair_history(pair: str, *, + datadir: Path, exchange: Exchange, - pair: str, *, - new_pairs_days: int = 30, timeframe: str = '5m', - timerange: Optional[TimeRange] = None, - data_handler: IDataHandler = None) -> bool: + process: str = '', + new_pairs_days: int = 30, + data_handler: IDataHandler = None, + timerange: Optional[TimeRange] = None) -> bool: """ Download latest candles from the exchange for the pair and timeframe passed in parameters The data is downloaded starting from the last correct data that @@ -177,7 +179,7 @@ def _download_pair_history(datadir: Path, try: logger.info( - f'Download history data for pair: "{pair}", timeframe: {timeframe} ' + f'Download history data for pair: "{pair}" ({process}), timeframe: {timeframe} ' f'and store in {datadir}.' ) @@ -234,7 +236,7 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes """ pairs_not_available = [] data_handler = get_datahandler(datadir, data_format) - for pair in pairs: + for idx, pair in enumerate(pairs, start=1): if pair not in exchange.markets: pairs_not_available.append(pair) logger.info(f"Skipping pair {pair}...") @@ -247,10 +249,11 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes f'Deleting existing data for pair {pair}, interval {timeframe}.') logger.info(f'Downloading pair {pair}, interval {timeframe}.') - _download_pair_history(datadir=datadir, exchange=exchange, - pair=pair, timeframe=str(timeframe), - new_pairs_days=new_pairs_days, - timerange=timerange, data_handler=data_handler) + process = f'{idx}/{len(pairs)}' + _download_pair_history(pair=pair, process=process, + datadir=datadir, exchange=exchange, + timerange=timerange, data_handler=data_handler, + timeframe=str(timeframe), new_pairs_days=new_pairs_days) return pairs_not_available diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 13d22ebb7..bcc2bf052 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -133,8 +133,8 @@ def test_load_data_with_new_pair_1min(ohlcv_history_list, mocker, caplog, load_pair_history(datadir=tmpdir1, timeframe='1m', pair='MEME/BTC') assert file.is_file() assert log_has_re( - 'Download history data for pair: "MEME/BTC", timeframe: 1m ' - 'and store in .*', caplog + 'Download history data for pair: "MEME/BTC" (0/1), timeframe: 1m ' + 'and store in', caplog ) @@ -278,8 +278,10 @@ def test_download_pair_history2(mocker, default_conf, testdatadir) -> None: return_value=None) mocker.patch('freqtrade.exchange.Exchange.get_historic_ohlcv', return_value=tick) exchange = get_patched_exchange(mocker, default_conf) - _download_pair_history(testdatadir, exchange, pair="UNITTEST/BTC", timeframe='1m') - _download_pair_history(testdatadir, exchange, pair="UNITTEST/BTC", timeframe='3m') + _download_pair_history(datadir=testdatadir, exchange=exchange, pair="UNITTEST/BTC", + timeframe='1m') + _download_pair_history(datadir=testdatadir, exchange=exchange, pair="UNITTEST/BTC", + timeframe='3m') assert json_dump_mock.call_count == 2 From 127f470bc3928b052fc5ac00d85f4422ae4e5f1f Mon Sep 17 00:00:00 2001 From: Masoud Azizi Date: Fri, 20 Aug 2021 20:28:10 +0000 Subject: [PATCH 331/519] .* ADDED --- tests/data/test_history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/data/test_history.py b/tests/data/test_history.py index bcc2bf052..e9d2c3638 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -133,8 +133,8 @@ def test_load_data_with_new_pair_1min(ohlcv_history_list, mocker, caplog, load_pair_history(datadir=tmpdir1, timeframe='1m', pair='MEME/BTC') assert file.is_file() assert log_has_re( - 'Download history data for pair: "MEME/BTC" (0/1), timeframe: 1m ' - 'and store in', caplog + r'Download history data for pair: "MEME/BTC" \(0/1\), timeframe: 1m ' + r'and store in .*', caplog ) From 56759cea7b8697f3e2f687e8e2f47bdbf001c593 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 22 Aug 2021 14:37:45 +0200 Subject: [PATCH 332/519] Add "high fee rate" blocker to fees from trades part of #5415 (potentially fixing this) --- freqtrade/freqtradebot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e7a2a3784..41d61f946 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1381,7 +1381,9 @@ class FreqtradeBot(LoggingMixin): if fee_currency: # fee_rate should use mean fee_rate = sum(fee_rate_array) / float(len(fee_rate_array)) if fee_rate_array else None - trade.update_fee(fee_cost, fee_currency, fee_rate, order.get('side', '')) + if fee_rate is not None and fee_rate < 0.02: + # Only update if fee-rate is < 2% + trade.update_fee(fee_cost, fee_currency, fee_rate, order.get('side', '')) if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC): logger.warning(f"Amount {amount} does not match amount {trade.amount}") From 04b4deab588897d258ec8f44851875f86e99b3fa Mon Sep 17 00:00:00 2001 From: JoeSchr Date: Sun, 22 Aug 2021 20:14:36 +0300 Subject: [PATCH 333/519] Update strategy-advanced.md Add `` for consistency --- docs/strategy-advanced.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index b039f542f..4409af6ea 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -361,7 +361,7 @@ See [Dataframe access](#dataframe-access) for more information about dataframe u By default, freqtrade use the orderbook to automatically set an order price([Relevant documentation](configuration.md#prices-used-for-orders)), you also have the option to create custom order prices based on your strategy. -You can use this feature by creating a `custom_entry_price()` function in your strategy file to customize entry prices and custom_exit_price for exits. +You can use this feature by creating a `custom_entry_price()` function in your strategy file to customize entry prices and `custom_exit_price()` for exits. !!! Note If your custom pricing function return None or an invalid value, price will fall back to `proposed_rate`, which is based on the regular pricing configuration. From 3026583ed480cdaab0be78554fcc0d68cf85ed00 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 22 Aug 2021 19:40:09 +0200 Subject: [PATCH 334/519] Reduce verbosity of "is in blacklist" logging --- freqtrade/plugins/pairlist/VolumePairList.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 23291d39e..c70e4a904 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -4,6 +4,7 @@ Volume PairList provider Provides dynamic pair list based on trade volumes """ import logging +from functools import partial from typing import Any, Dict, List import arrow @@ -203,7 +204,7 @@ class VolumePairList(IPairList): # Validate whitelist to only have active market pairs pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers]) - pairs = self.verify_blacklist(pairs, logger.info) + pairs = self.verify_blacklist(pairs, partial(self.log_once, logmethod=logger.info)) # Limit pairlist to the requested number of pairs pairs = pairs[:self._number_pairs] From 4ee467f8575ae2d2f3488bad00ba4ee774b4e8bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 03:01:26 +0000 Subject: [PATCH 335/519] Bump ccxt from 1.55.20 to 1.55.28 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.55.20 to 1.55.28. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.55.20...1.55.28) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 09109baaf..f7934c903 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.2 pandas==1.3.2 -ccxt==1.55.20 +ccxt==1.55.28 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 0f9bfcf8b0699b9935d96d7bf18e3091693d0245 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 03:01:28 +0000 Subject: [PATCH 336/519] Bump types-cachetools from 0.1.10 to 4.2.0 Bumps [types-cachetools](https://github.com/python/typeshed) from 0.1.10 to 4.2.0. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-cachetools dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index b20cb6693..67ee0035b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -19,7 +19,7 @@ isort==5.9.3 nbconvert==6.1.0 # mypy types -types-cachetools==0.1.10 +types-cachetools==4.2.0 types-filelock==0.1.5 types-requests==2.25.6 types-tabulate==0.8.2 From 90f1845eafa03f9a6236904bc78a75190bad0065 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 03:01:33 +0000 Subject: [PATCH 337/519] Bump prompt-toolkit from 3.0.19 to 3.0.20 Bumps [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) from 3.0.19 to 3.0.20. - [Release notes](https://github.com/prompt-toolkit/python-prompt-toolkit/releases) - [Changelog](https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/CHANGELOG) - [Commits](https://github.com/prompt-toolkit/python-prompt-toolkit/compare/3.0.19...3.0.20) --- updated-dependencies: - dependency-name: prompt-toolkit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 09109baaf..11b48cbc3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -40,4 +40,4 @@ aiofiles==0.7.0 colorama==0.4.4 # Building config files interactively questionary==1.10.0 -prompt-toolkit==3.0.19 +prompt-toolkit==3.0.20 From 345c7ab64b8a7422bdc555605c7c1cb1669c61d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 03:01:42 +0000 Subject: [PATCH 338/519] Bump sqlalchemy from 1.4.22 to 1.4.23 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.22 to 1.4.23. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/master/CHANGES) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 09109baaf..1a46fc828 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ ccxt==1.55.20 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 -SQLAlchemy==1.4.22 +SQLAlchemy==1.4.23 python-telegram-bot==13.7 arrow==1.1.1 cachetools==4.2.2 From 60b476611cae94d3134c6d6670b9c0c2cbae9474 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Aug 2021 20:51:41 +0200 Subject: [PATCH 339/519] Simplify a number of tests by relying on default arguments --- tests/rpc/test_rpc.py | 34 ++++++++++++------------ tests/rpc/test_rpc_apiserver.py | 24 ++++++++--------- tests/rpc/test_rpc_telegram.py | 46 ++++++++++++++++----------------- tests/test_freqtradebot.py | 4 +-- 4 files changed, 54 insertions(+), 54 deletions(-) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 136fa157c..0ba42c4ce 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -35,7 +35,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -192,7 +192,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: ) del default_conf['fiat_display_currency'] freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -239,7 +239,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -371,7 +371,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -459,7 +459,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) stake_currency = default_conf['stake_currency'] fiat_display_currency = default_conf['fiat_display_currency'] @@ -526,7 +526,7 @@ def test_rpc_balance_handle_error(default_conf, mocker): ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() with pytest.raises(RPCException, match="Error getting current tickers."): @@ -567,7 +567,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): ) default_conf['dry_run'] = False freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -612,7 +612,7 @@ def test_rpc_start(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -633,7 +633,7 @@ def test_rpc_stop(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -655,7 +655,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING @@ -687,7 +687,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) freqtradebot.state = State.STOPPED @@ -805,7 +805,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) # Create some test data @@ -838,7 +838,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) counts = rpc._rpc_count() @@ -863,7 +863,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) pair = 'ETH/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -889,7 +889,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> # Test not buying freqtradebot = get_patched_freqtradebot(mocker, default_conf) freqtradebot.config['stake_amount'] = 0 - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) pair = 'TKN/BTC' trade = rpc._rpc_forcebuy(pair, None) @@ -902,7 +902,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'trader is not running'): @@ -913,7 +913,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) pair = 'ETH/BTC' with pytest.raises(RPCException, match=r'Forcebuy not enabled.'): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 3d02e8188..68bd01911 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -447,7 +447,7 @@ def test_api_balance(botclient, mocker, rpc_balance): def test_api_count(botclient, mocker, ticker, fee, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -509,7 +509,7 @@ def test_api_locks(botclient): def test_api_show_config(botclient, mocker): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) rc = client_get(client, f"{BASE_URI}/show_config") assert_response(rc) @@ -527,7 +527,7 @@ def test_api_show_config(botclient, mocker): def test_api_daily(botclient, mocker, ticker, fee, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -545,7 +545,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): def test_api_trades(botclient, mocker, fee, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets) @@ -573,7 +573,7 @@ def test_api_trades(botclient, mocker, fee, markets): def test_api_trade_single(botclient, mocker, fee, ticker, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', markets=PropertyMock(return_value=markets), @@ -593,7 +593,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets): def test_api_delete_trade(botclient, mocker, fee, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) stoploss_mock = MagicMock() cancel_mock = MagicMock() mocker.patch.multiple( @@ -667,7 +667,7 @@ def test_api_logs(botclient): def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -683,7 +683,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_profit(botclient, mocker, ticker, fee, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -734,7 +734,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets): @pytest.mark.usefixtures("init_persistence") def test_api_stats(botclient, mocker, ticker, fee, markets,): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -762,7 +762,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_performance(botclient, fee): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) trade = Trade( pair='LTC/ETH', @@ -808,7 +808,7 @@ def test_api_performance(botclient, fee): def test_api_status(botclient, mocker, ticker, fee, markets): ftbot, client = botclient - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_balances=MagicMock(return_value=ticker), @@ -1051,7 +1051,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets), _is_dry_limit_order_filled=MagicMock(return_value=False), ) - patch_get_signal(ftbot, (True, False, None)) + patch_get_signal(ftbot) rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index b678c3363..8596c7c44 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -119,7 +119,7 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None: rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False, None)) + patch_get_signal(bot) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is True assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) @@ -139,7 +139,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False, None)) + patch_get_signal(bot) dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state['called'] is False assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) @@ -155,7 +155,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None bot = FreqtradeBot(default_conf) rpc = RPC(bot) dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot, (True, False, None)) + patch_get_signal(bot) dummy.dummy_exception(update=update, context=MagicMock()) assert dummy.state['called'] is False @@ -229,7 +229,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) freqtradebot.state = State.STOPPED # Status is also enabled when stopped @@ -286,7 +286,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) freqtradebot.state = State.STOPPED # Status table is also enabled when stopped @@ -330,7 +330,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # Create some test data freqtradebot.enter_positions() @@ -401,7 +401,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # Try invalid data msg_mock.reset_mock() @@ -433,7 +433,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) telegram._profit(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -488,7 +488,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) telegram._stats(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -514,7 +514,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick side_effect=lambda a, b: f"{a}/{b}") telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -537,7 +537,7 @@ def test_balance_handle_empty_response(default_conf, update, mocker) -> None: mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) freqtradebot.config['dry_run'] = False telegram._balance(update=update, context=MagicMock()) @@ -550,7 +550,7 @@ def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -579,7 +579,7 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None }) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) telegram._balance(update=update, context=MagicMock()) assert msg_mock.call_count > 1 @@ -678,7 +678,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # Create some test data freqtradebot.enter_positions() @@ -737,7 +737,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # Create some test data freqtradebot.enter_positions() @@ -798,7 +798,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None freqtradebot = FreqtradeBot(default_conf) rpc = RPC(freqtradebot) telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # Create some test data freqtradebot.enter_positions() @@ -839,7 +839,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # Trader is not running freqtradebot.state = State.STOPPED @@ -877,7 +877,7 @@ def test_forcebuy_handle(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock) telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # /forcebuy ETH/BTC context = MagicMock() @@ -906,7 +906,7 @@ def test_forcebuy_handle_exception(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) update.message.text = '/forcebuy ETH/Nonepair' telegram._forcebuy(update=update, context=MagicMock()) @@ -923,7 +923,7 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None: telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) context = MagicMock() context.args = [] @@ -951,7 +951,7 @@ def test_performance_handle(default_conf, update, ticker, fee, get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) # Create some test data freqtradebot.enter_positions() @@ -979,7 +979,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) freqtradebot.state = State.STOPPED telegram._count(update=update, context=MagicMock()) @@ -1008,7 +1008,7 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None get_fee=fee, ) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False, None)) + patch_get_signal(freqtradebot) telegram._locks(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'No active locks.' in msg_mock.call_args_list[0][0][0] diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index a2bb01a4b..a859787cb 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1954,7 +1954,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open, assert nb_trades == 0 # Buy is triggering, so buying ... - patch_get_signal(freqtrade, value=(True, False, None)) + patch_get_signal(freqtrade) freqtrade.enter_positions() trades = Trade.query.all() nb_trades = len(trades) @@ -1999,7 +1999,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, ) freqtrade = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtrade, value=(True, False, None)) + patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) freqtrade.enter_positions() From 8a9407bac9ff906db1d6391e6ce1177b9af1fa79 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Aug 2021 07:13:09 +0200 Subject: [PATCH 340/519] Don't hard-fail when fetch_ticker doesn't return a value closes #5477 --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index dbd72aca4..79ee94149 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1038,7 +1038,7 @@ class Exchange: logger.debug(f"Using Last {conf_strategy['price_side'].capitalize()} / Last Price") ticker = self.fetch_ticker(pair) ticker_rate = ticker[conf_strategy['price_side']] - if ticker['last']: + if ticker['last'] and ticker_rate: if side == 'buy' and ticker_rate > ticker['last']: balance = conf_strategy['ask_last_balance'] ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 27eeed39b..f6ac4c459 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1851,6 +1851,31 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, assert log_has("Using cached sell rate for ETH/BTC.", caplog) +@pytest.mark.parametrize("entry,side,ask,bid,last,last_ab,expected", [ + ('buy', 'ask', None, 4, 4, 0, 4), # ask not available + ('buy', 'ask', None, None, 4, 0, 4), # ask not available + ('buy', 'bid', 6, None, 4, 0, 5), # bid not available + ('buy', 'bid', None, None, 4, 0, 5), # No rate available + ('sell', 'ask', None, 4, 4, 0, 4), # ask not available + ('sell', 'ask', None, None, 4, 0, 4), # ask not available + ('sell', 'bid', 6, None, 4, 0, 5), # bid not available + ('sell', 'bid', None, None, 4, 0, 5), # bid not available +]) +def test_get_ticker_rate_error(mocker, entry, default_conf, caplog, side, ask, bid, + last, last_ab, expected) -> None: + caplog.set_level(logging.DEBUG) + default_conf['bid_strategy']['ask_last_balance'] = last_ab + default_conf['bid_strategy']['price_side'] = side + default_conf['ask_strategy']['price_side'] = side + default_conf['ask_strategy']['ask_last_balance'] = last_ab + exchange = get_patched_exchange(mocker, default_conf) + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', + return_value={'ask': ask, 'last': last, 'bid': bid}) + + with pytest.raises(PricingError): + exchange.get_rate('ETH/BTC', refresh=True, side=entry) + + @pytest.mark.parametrize('side,expected', [ ('bid', 0.043936), # Value from order_book_l2 fiture - bids side ('ask', 0.043949), # Value from order_book_l2 fiture - asks side From 4b36276e4f0e5b451f2449f9afebc6e90ea143f9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Aug 2021 19:54:26 +0200 Subject: [PATCH 341/519] Improve has_space test --- tests/optimize/test_hyperopt_tools.py | 47 +++++++++++++++++++-------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/tests/optimize/test_hyperopt_tools.py b/tests/optimize/test_hyperopt_tools.py index cbcb13384..e845b2a77 100644 --- a/tests/optimize/test_hyperopt_tools.py +++ b/tests/optimize/test_hyperopt_tools.py @@ -64,34 +64,53 @@ def test_load_previous_results2(mocker, testdatadir, caplog) -> None: @pytest.mark.parametrize("spaces, expected_results", [ (['buy'], - {'buy': True, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False}), + {'buy': True, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False, + 'protection': False}), (['sell'], - {'buy': False, 'sell': True, 'roi': False, 'stoploss': False, 'trailing': False}), + {'buy': False, 'sell': True, 'roi': False, 'stoploss': False, 'trailing': False, + 'protection': False}), (['roi'], - {'buy': False, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False}), + {'buy': False, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False, + 'protection': False}), (['stoploss'], - {'buy': False, 'sell': False, 'roi': False, 'stoploss': True, 'trailing': False}), + {'buy': False, 'sell': False, 'roi': False, 'stoploss': True, 'trailing': False, + 'protection': False}), (['trailing'], - {'buy': False, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': True}), + {'buy': False, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': True, + 'protection': False}), (['buy', 'sell', 'roi', 'stoploss'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}), + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False, + 'protection': False}), (['buy', 'sell', 'roi', 'stoploss', 'trailing'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}), + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, + 'protection': False}), (['buy', 'roi'], - {'buy': True, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False}), + {'buy': True, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False, + 'protection': False}), (['all'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}), + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, + 'protection': True}), (['default'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}), + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False, + 'protection': False}), (['default', 'trailing'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}), + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, + 'protection': False}), (['all', 'buy'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}), + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, + 'protection': True}), (['default', 'buy'], - {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}), + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False, + 'protection': False}), + (['all'], + {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True, + 'protection': True}), + (['protection'], + {'buy': False, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False, + 'protection': True}), ]) def test_has_space(hyperopt_conf, spaces, expected_results): - for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']: + for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing', 'protection']: hyperopt_conf.update({'spaces': spaces}) assert HyperoptTools.has_space(hyperopt_conf, s) == expected_results[s] From 23d21d8ace2e1e58b52e2c0d601cfc972f0031c4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Aug 2021 19:57:10 +0200 Subject: [PATCH 342/519] Fix wrong message if protection-space is missing closes #5480 --- freqtrade/optimize/hyperopt_auto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt_auto.py b/freqtrade/optimize/hyperopt_auto.py index 03f7dd21e..43e92d9c6 100644 --- a/freqtrade/optimize/hyperopt_auto.py +++ b/freqtrade/optimize/hyperopt_auto.py @@ -74,7 +74,7 @@ class HyperOptAuto(IHyperOpt): return self._get_indicator_space('sell', 'sell_indicator_space') def protection_space(self) -> List['Dimension']: - return self._get_indicator_space('protection', 'indicator_space') + return self._get_indicator_space('protection', 'protection_space') def generate_roi_table(self, params: Dict) -> Dict[int, float]: return self._get_func('generate_roi_table')(params) From 3745966c6c0b95b549790a8652c90236079846cb Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Aug 2021 20:01:00 +0200 Subject: [PATCH 343/519] Update help-docstring for hyperopt --- docs/hyperopt.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 96f9ff177..a9f7425f4 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -48,7 +48,7 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--hyperopt-path PATH] [--eps] [--dmmp] [--enable-protections] [--dry-run-wallet DRY_RUN_WALLET] [-e INT] - [--spaces {all,buy,sell,roi,stoploss,trailing,default} [{all,buy,sell,roi,stoploss,trailing,default} ...]] + [--spaces {all,buy,sell,roi,stoploss,trailing,protection,default} [{all,buy,sell,roi,stoploss,trailing,protection,default} ...]] [--print-all] [--no-color] [--print-json] [-j JOBS] [--random-state INT] [--min-trades INT] [--hyperopt-loss NAME] [--disable-param-export] @@ -92,7 +92,7 @@ optional arguments: Starting balance, used for backtesting / hyperopt and dry-runs. -e INT, --epochs INT Specify number of epochs (default: 100). - --spaces {all,buy,sell,roi,stoploss,trailing,default} [{all,buy,sell,roi,stoploss,trailing,default} ...] + --spaces {all,buy,sell,roi,stoploss,trailing,protection,default} [{all,buy,sell,roi,stoploss,trailing,protection,default} ...] Specify which parameters to hyperopt. Space-separated list. --print-all Print all results, not only the best ones. From 68dd349094969d48343849134280d9c8ca2920b5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Aug 2021 20:04:10 +0200 Subject: [PATCH 344/519] Update hyperopt docs regarding protections --- docs/hyperopt.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index a9f7425f4..c5a52553b 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -576,7 +576,8 @@ Legal values are: * `roi`: just optimize the minimal profit table for your strategy * `stoploss`: search for the best stoploss value * `trailing`: search for the best trailing stop values -* `default`: `all` except `trailing` +* `protection`: search for the best protection parameters (read the [protections section](#optimizing-protections) on how to properly define these) +* `default`: `all` except `trailing` and `protection` * space-separated list of any of the above values for example `--spaces roi stoploss` The default Hyperopt Search Space, used when no `--space` command line option is specified, does not include the `trailing` hyperspace. We recommend you to run optimization for the `trailing` hyperspace separately, when the best parameters for other hyperspaces were found, validated and pasted into your custom strategy. From 7fb570cc58fb8157afde1df0e26cd72eabafcd6a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Aug 2021 20:28:55 +0200 Subject: [PATCH 345/519] hyperopt Fallback methods should not be used. --- freqtrade/optimize/hyperopt.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 901900121..e0b35df32 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -107,13 +107,25 @@ class Hyperopt: # Populate "fallback" functions here # (hasattr is slow so should not be run during "regular" operations) if hasattr(self.custom_hyperopt, 'populate_indicators'): - self.backtesting.strategy.advise_indicators = ( # type: ignore + logger.warning( + "DEPRECATED: Using `populate_indicators()` in the hyperopt file is deprecated. " + "Please move these methods to your strategy." + ) + self.backtesting.strategy.populate_indicators = ( # type: ignore self.custom_hyperopt.populate_indicators) # type: ignore if hasattr(self.custom_hyperopt, 'populate_buy_trend'): - self.backtesting.strategy.advise_buy = ( # type: ignore + logger.warning( + "DEPRECATED: Using `populate_buy_trend()` in the hyperopt file is deprecated. " + "Please move these methods to your strategy." + ) + self.backtesting.strategy.populate_buy_trend = ( # type: ignore self.custom_hyperopt.populate_buy_trend) # type: ignore if hasattr(self.custom_hyperopt, 'populate_sell_trend'): - self.backtesting.strategy.advise_sell = ( # type: ignore + logger.warning( + "DEPRECATED: Using `populate_sell_trend()` in the hyperopt file is deprecated. " + "Please move these methods to your strategy." + ) + self.backtesting.strategy.populate_sell_trend = ( # type: ignore self.custom_hyperopt.populate_sell_trend) # type: ignore # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set From 63844d39f62957cc0c89ab6d1f9dfdd9c194f9e5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 06:48:26 +0200 Subject: [PATCH 346/519] Rename execute_buy to execute_entry --- freqtrade/freqtradebot.py | 8 ++++---- freqtrade/rpc/rpc.py | 2 +- tests/test_freqtradebot.py | 42 +++++++++++++++++++------------------- tests/test_wallets.py | 4 ++-- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 41d61f946..d92156ef7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -433,11 +433,11 @@ class FreqtradeBot(LoggingMixin): if ((bid_check_dom.get('enabled', False)) and (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): if self._check_depth_of_market_buy(pair, bid_check_dom): - return self.execute_buy(pair, stake_amount, buy_tag=buy_tag) + return self.execute_entry(pair, stake_amount, buy_tag=buy_tag) else: return False - return self.execute_buy(pair, stake_amount, buy_tag=buy_tag) + return self.execute_entry(pair, stake_amount, buy_tag=buy_tag) else: return False @@ -465,8 +465,8 @@ class FreqtradeBot(LoggingMixin): logger.info(f"Bids to asks delta for {pair} does not satisfy condition.") return False - def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None, - forcebuy: bool = False, buy_tag: Optional[str] = None) -> bool: + def execute_entry(self, pair: str, stake_amount: float, price: Optional[float] = None, + forcebuy: bool = False, buy_tag: Optional[str] = None) -> bool: """ Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 0264003a5..51bf0ab3a 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -613,7 +613,7 @@ class RPC: stakeamount = self._freqtrade.wallets.get_trade_stake_amount(pair) # execute buy - if self._freqtrade.execute_buy(pair, stakeamount, price, forcebuy=True): + if self._freqtrade.execute_entry(pair, stakeamount, price, forcebuy=True): Trade.commit() trade = Trade.get_trades([Trade.is_open.is_(True), Trade.pair == pair]).first() return trade diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index a859787cb..9a5a537ef 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -185,7 +185,7 @@ def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_b limit_buy_order_open['id'] = str(i) result = freqtrade.wallets.get_trade_stake_amount('ETH/BTC') assert pytest.approx(result) == expected[i] - freqtrade.execute_buy('ETH/BTC', result) + freqtrade.execute_entry('ETH/BTC', result) else: with pytest.raises(DependencyException): freqtrade.wallets.get_trade_stake_amount('ETH/BTC') @@ -584,8 +584,8 @@ def test_create_trades_preopen(default_conf, ticker, fee, mocker, limit_buy_orde patch_get_signal(freqtrade) # Create 2 existing trades - freqtrade.execute_buy('ETH/BTC', default_conf['stake_amount']) - freqtrade.execute_buy('NEO/BTC', default_conf['stake_amount']) + freqtrade.execute_entry('ETH/BTC', default_conf['stake_amount']) + freqtrade.execute_entry('NEO/BTC', default_conf['stake_amount']) assert len(Trade.get_open_trades()) == 2 # Change order_id for new orders @@ -776,7 +776,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: assert ("ETH/BTC", default_conf["timeframe"]) in refresh_mock.call_args[0][0] -def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order_open) -> None: +def test_execute_entry(mocker, default_conf, fee, limit_buy_order, limit_buy_order_open) -> None: patch_RPCManager(mocker) patch_exchange(mocker) freqtrade = FreqtradeBot(default_conf) @@ -799,7 +799,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order ) pair = 'ETH/BTC' - assert not freqtrade.execute_buy(pair, stake_amount) + assert not freqtrade.execute_entry(pair, stake_amount) assert buy_rate_mock.call_count == 1 assert buy_mm.call_count == 0 assert freqtrade.strategy.confirm_trade_entry.call_count == 1 @@ -807,7 +807,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order_open['id'] = '22' freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) assert buy_rate_mock.call_count == 1 assert buy_mm.call_count == 1 call_args = buy_mm.call_args_list[0][1] @@ -826,7 +826,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order # Test calling with price limit_buy_order_open['id'] = '33' fix_price = 0.06 - assert freqtrade.execute_buy(pair, stake_amount, fix_price) + assert freqtrade.execute_entry(pair, stake_amount, fix_price) # Make sure get_rate wasn't called again assert buy_rate_mock.call_count == 0 @@ -844,7 +844,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order mocker.patch('freqtrade.exchange.Exchange.create_order', MagicMock(return_value=limit_buy_order)) - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[2] assert trade assert trade.open_order_id is None @@ -861,7 +861,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order['id'] = '555' mocker.patch('freqtrade.exchange.Exchange.create_order', MagicMock(return_value=limit_buy_order)) - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[3] assert trade assert trade.open_order_id == '555' @@ -873,7 +873,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order['id'] = '556' freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0 - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[4] assert trade assert trade.stake_amount == 150 @@ -881,7 +881,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order # Exception case limit_buy_order['id'] = '557' freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[5] assert trade assert trade.stake_amount == 2.0 @@ -896,20 +896,20 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order['id'] = '66' mocker.patch('freqtrade.exchange.Exchange.create_order', MagicMock(return_value=limit_buy_order)) - assert not freqtrade.execute_buy(pair, stake_amount) + assert not freqtrade.execute_entry(pair, stake_amount) # Fail to get price... mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0)) with pytest.raises(PricingError, match="Could not determine buy price."): - freqtrade.execute_buy(pair, stake_amount) + freqtrade.execute_entry(pair, stake_amount) # In case of custom entry price mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.50) limit_buy_order['status'] = 'open' limit_buy_order['id'] = '5566' freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508 - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[6] assert trade assert trade.open_rate_requested == 0.508 @@ -924,7 +924,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order get_rate=MagicMock(return_value=10), ) - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[7] assert trade assert trade.open_rate_requested == 10 @@ -933,13 +933,13 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order limit_buy_order['status'] = 'open' limit_buy_order['id'] = '5568' freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price" - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) trade = Trade.query.all()[8] assert trade assert trade.open_rate_requested == 10 -def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: +def test_execute_entry_confirm_error(mocker, default_conf, fee, limit_buy_order) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -957,18 +957,18 @@ def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) - pair = 'ETH/BTC' freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=ValueError) - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) limit_buy_order['id'] = '222' freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=Exception) - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) limit_buy_order['id'] = '2223' freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) - assert freqtrade.execute_buy(pair, stake_amount) + assert freqtrade.execute_entry(pair, stake_amount) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False) - assert not freqtrade.execute_buy(pair, stake_amount) + assert not freqtrade.execute_entry(pair, stake_amount) def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None: diff --git a/tests/test_wallets.py b/tests/test_wallets.py index 9f58cb71d..53e3b758e 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -157,13 +157,13 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r assert result == result1 # create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)' - freqtrade.execute_buy('ETH/USDT', result) + freqtrade.execute_entry('ETH/USDT', result) result = freqtrade.wallets.get_trade_stake_amount('LTC/USDT') assert result == result1 # create 2 trades, order amount should be None - freqtrade.execute_buy('LTC/BTC', result) + freqtrade.execute_entry('LTC/BTC', result) result = freqtrade.wallets.get_trade_stake_amount('XRP/USDT') assert result == 0 From c4be52d1c3100bdb5525510c1a8ff7c5f9bb2c37 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 06:53:42 +0200 Subject: [PATCH 347/519] rename execute_sell to execute_trade_exit --- freqtrade/freqtradebot.py | 8 ++--- freqtrade/rpc/rpc.py | 2 +- tests/test_freqtradebot.py | 72 ++++++++++++++++++++------------------ tests/test_integration.py | 2 +- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d92156ef7..a7451d632 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -745,7 +745,7 @@ class FreqtradeBot(LoggingMixin): trade.stoploss_order_id = None logger.error(f'Unable to place a stoploss order on exchange. {e}') logger.warning('Selling the trade forcefully') - self.execute_sell(trade, trade.stop_loss, sell_reason=SellCheckTuple( + self.execute_trade_exit(trade, trade.stop_loss, sell_reason=SellCheckTuple( sell_type=SellType.EMERGENCY_SELL)) except ExchangeError: @@ -863,7 +863,7 @@ class FreqtradeBot(LoggingMixin): if should_sell.sell_flag: logger.info(f'Executing Sell for {trade.pair}. Reason: {should_sell.sell_type}') - self.execute_sell(trade, sell_rate, should_sell) + self.execute_trade_exit(trade, sell_rate, should_sell) return True return False @@ -1064,9 +1064,9 @@ class FreqtradeBot(LoggingMixin): raise DependencyException( f"Not enough amount to sell. Trade-amount: {amount}, Wallet: {wallet_amount}") - def execute_sell(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool: + def execute_trade_exit(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool: """ - Executes a limit sell for the given trade and limit + Executes a trade exit for the given trade and limit :param trade: Trade instance :param limit: limit rate for the sell order :param sell_reason: Reason the sell was triggered diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 51bf0ab3a..95a37452b 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -557,7 +557,7 @@ class RPC: current_rate = self._freqtrade.exchange.get_rate( trade.pair, refresh=False, side="sell") sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL) - self._freqtrade.execute_sell(trade, current_rate, sell_reason) + self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason) # ---- EOF def _exec_forcesell ---- if self._freqtrade.state != State.RUNNING: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 9a5a537ef..75b67e59c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2007,7 +2007,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, trade = Trade.query.first() trade.is_open = True - # FIX: sniffing logs, suggest handle_trade should not execute_sell + # FIX: sniffing logs, suggest handle_trade should not execute_trade_exit # instead that responsibility should be moved out of handle_trade(), # we might just want to check if we are in a sell condition without # executing @@ -2633,7 +2633,7 @@ def test_handle_cancel_sell_cancel_exception(mocker, default_conf) -> None: assert freqtrade.handle_cancel_sell(trade, order, reason) == 'error cancelling order' -def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: +def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2661,16 +2661,16 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N fetch_ticker=ticker_sell_up ) # Prevented sell ... - freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.ROI)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert rpc_mock.call_count == 0 assert freqtrade.strategy.confirm_trade_exit.call_count == 1 # Repatch with true freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True) - freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.ROI)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert freqtrade.strategy.confirm_trade_exit.call_count == 1 assert rpc_mock.call_count == 1 @@ -2697,7 +2697,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N } == last_msg -def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: +def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2722,8 +2722,8 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) fetch_ticker=ticker_sell_down ) - freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] @@ -2749,7 +2749,8 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) } == last_msg -def test_execute_sell_custom_exit_price(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: +def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_sell_up, + mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2782,8 +2783,8 @@ def test_execute_sell_custom_exit_price(default_conf, ticker, fee, ticker_sell_u # Set a custom exit price freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05 - freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL)) # Sell price must be different to default bid price @@ -2813,8 +2814,8 @@ def test_execute_sell_custom_exit_price(default_conf, ticker, fee, ticker_sell_u } == last_msg -def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee, - ticker_sell_down, mocker) -> None: +def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, ticker, fee, + ticker_sell_down, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2844,8 +2845,8 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe # Setting trade stoploss to 0.01 trade.stop_loss = 0.00001099 * 0.99 - freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert rpc_mock.call_count == 2 last_msg = rpc_mock.call_args_list[-1][0][0] @@ -2872,7 +2873,8 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe } == last_msg -def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, caplog) -> None: +def test_execute_trade_exit_sloe_cancel_exception( + mocker, default_conf, ticker, fee, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', side_effect=InvalidOrderException()) @@ -2899,14 +2901,14 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c freqtrade.config['dry_run'] = False trade.stoploss_order_id = "abcd" - freqtrade.execute_sell(trade=trade, limit=1234, - sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) + freqtrade.execute_trade_exit(trade=trade, limit=1234, + sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert create_order_mock.call_count == 2 assert log_has('Could not cancel stoploss order abcd', caplog) -def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, - mocker) -> None: +def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, + mocker) -> None: default_conf['exchange']['name'] = 'binance' rpc_mock = patch_RPCManager(mocker) @@ -2950,8 +2952,8 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke fetch_ticker=ticker_sell_up ) - freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) trade = Trade.query.first() assert trade @@ -2959,8 +2961,8 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke assert rpc_mock.call_count == 3 -def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, fee, - mocker) -> None: +def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf, ticker, fee, + mocker) -> None: default_conf['exchange']['name'] = 'binance' rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) @@ -3031,8 +3033,8 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL -def test_execute_sell_market_order(default_conf, ticker, fee, - ticker_sell_up, mocker) -> None: +def test_execute_trade_exit_market_order(default_conf, ticker, fee, + ticker_sell_up, mocker) -> None: rpc_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -3058,8 +3060,8 @@ def test_execute_sell_market_order(default_conf, ticker, fee, ) freqtrade.config['order_types']['sell'] = 'market' - freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.ROI)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.ROI)) assert not trade.is_open assert trade.close_profit == 0.0620716 @@ -3089,8 +3091,8 @@ def test_execute_sell_market_order(default_conf, ticker, fee, } == last_msg -def test_execute_sell_insufficient_funds_error(default_conf, ticker, fee, - ticker_sell_up, mocker) -> None: +def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, + ticker_sell_up, mocker) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds') mocker.patch.multiple( @@ -3117,8 +3119,8 @@ def test_execute_sell_insufficient_funds_error(default_conf, ticker, fee, ) sell_reason = SellCheckTuple(sell_type=SellType.ROI) - assert not freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], - sell_reason=sell_reason) + assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], + sell_reason=sell_reason) assert mock_insuf.call_count == 1 @@ -3374,8 +3376,8 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo fetch_ticker=ticker_sell_down ) - freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'], - sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) + freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], + sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) trade.close(ticker_sell_down()['bid']) assert freqtrade.strategy.is_pair_locked(trade.pair) diff --git a/tests/test_integration.py b/tests/test_integration.py index b12959a03..215927098 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -9,7 +9,7 @@ from freqtrade.strategy.interface import SellCheckTuple from tests.conftest import get_patched_freqtradebot, patch_get_signal -def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, +def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee, limit_buy_order, mocker) -> None: """ Tests workflow of selling stoploss_on_exchange. From 1f3ccc2587d8e8c8d4726906a483cb7ad6f456cb Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 07:00:15 +0200 Subject: [PATCH 348/519] DefaultStrategy does not need to be limited --- config_examples/config_full.example.json | 2 +- docs/backtesting.md | 2 +- freqtrade/commands/cli_options.py | 2 +- freqtrade/commands/deploy_commands.py | 2 -- tests/commands/test_commands.py | 11 ----------- 5 files changed, 3 insertions(+), 16 deletions(-) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index 3ca413281..d0f3f0df6 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -174,7 +174,7 @@ "heartbeat_interval": 60 }, "disable_dataframe_checks": false, - "strategy": "DefaultStrategy", + "strategy": "SampleStrategy", "strategy_path": "user_data/strategies/", "dataformat_ohlcv": "json", "dataformat_trades": "jsongz" diff --git a/docs/backtesting.md b/docs/backtesting.md index 89980c670..f750e0c4c 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -62,7 +62,7 @@ optional arguments: this together with `--export trades`, the strategy- name is injected into the filename (so `backtest- data.json` becomes `backtest-data- - DefaultStrategy.json` + SampleStrategy.json` --export {none,trades} Export backtest results (default: trades). --export-filename PATH diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 215ed3f6e..42be5abd6 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -162,7 +162,7 @@ AVAILABLE_CLI_OPTIONS = { 'Please note that ticker-interval needs to be set either in config ' 'or via command line. When using this together with `--export trades`, ' 'the strategy-name is injected into the filename ' - '(so `backtest-data.json` becomes `backtest-data-DefaultStrategy.json`', + '(so `backtest-data.json` becomes `backtest-data-SampleStrategy.json`', nargs='+', ), "export": Arg( diff --git a/freqtrade/commands/deploy_commands.py b/freqtrade/commands/deploy_commands.py index eb65579e2..ec558e7b1 100644 --- a/freqtrade/commands/deploy_commands.py +++ b/freqtrade/commands/deploy_commands.py @@ -74,8 +74,6 @@ def start_new_strategy(args: Dict[str, Any]) -> None: config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) if "strategy" in args and args["strategy"]: - if args["strategy"] == "DefaultStrategy": - raise OperationalException("DefaultStrategy is not allowed as name.") new_path = config['user_data_dir'] / USERPATH_STRATEGIES / (args['strategy'] + '.py') diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index fc5101979..df2eb2c10 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -510,17 +510,6 @@ def test_start_new_strategy(mocker, caplog): start_new_strategy(get_args(args)) -def test_start_new_strategy_DefaultStrat(mocker, caplog): - args = [ - "new-strategy", - "--strategy", - "DefaultStrategy" - ] - with pytest.raises(OperationalException, - match=r"DefaultStrategy is not allowed as name\."): - start_new_strategy(get_args(args)) - - def test_start_new_strategy_no_arg(mocker, caplog): args = [ "new-strategy", From fbf8eb4526601e90f8878985a42a77ba1a46227f Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 07:04:33 +0200 Subject: [PATCH 349/519] Rename test-legacy strategy --- tests/commands/test_commands.py | 4 ++-- tests/optimize/test_backtesting.py | 10 +++++----- tests/rpc/test_rpc_apiserver.py | 2 +- tests/strategy/strats/failing_strategy.py | 2 +- tests/strategy/strats/legacy_strategy.py | 2 +- tests/strategy/test_interface.py | 2 +- tests/strategy/test_strategy_loading.py | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index df2eb2c10..0f34f6e10 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -816,7 +816,7 @@ def test_start_list_strategies(mocker, caplog, capsys): # pargs['config'] = None start_list_strategies(pargs) captured = capsys.readouterr() - assert "TestStrategyLegacy" in captured.out + assert "TestStrategyLegacyV1" in captured.out assert "legacy_strategy.py" not in captured.out assert "DefaultStrategy" in captured.out @@ -831,7 +831,7 @@ def test_start_list_strategies(mocker, caplog, capsys): # pargs['config'] = None start_list_strategies(pargs) captured = capsys.readouterr() - assert "TestStrategyLegacy" in captured.out + assert "TestStrategyLegacyV1" in captured.out assert "legacy_strategy.py" in captured.out assert "DefaultStrategy" in captured.out diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 998b2d837..80f7aa2d6 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -473,7 +473,7 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti Backtesting(default_conf) # Multiple strategies - default_conf['strategy_list'] = ['DefaultStrategy', 'TestStrategyLegacy'] + default_conf['strategy_list'] = ['DefaultStrategy', 'TestStrategyLegacyV1'] with pytest.raises(OperationalException, match='PrecisionFilter not allowed for backtesting multiple strategies.'): Backtesting(default_conf) @@ -909,7 +909,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): '--disable-max-market-positions', '--strategy-list', 'DefaultStrategy', - 'TestStrategyLegacy', + 'TestStrategyLegacyV1', ] args = get_args(args) start_backtesting(args) @@ -932,7 +932,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'up to 2017-11-14 22:58:00 (0 days).', 'Parameter --enable-position-stacking detected ...', 'Running backtesting for Strategy DefaultStrategy', - 'Running backtesting for Strategy TestStrategyLegacy', + 'Running backtesting for Strategy TestStrategyLegacyV1', ] for line in exists: @@ -1013,7 +1013,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat '--disable-max-market-positions', '--strategy-list', 'DefaultStrategy', - 'TestStrategyLegacy', + 'TestStrategyLegacyV1', ] args = get_args(args) start_backtesting(args) @@ -1030,7 +1030,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'up to 2017-11-14 22:58:00 (0 days).', 'Parameter --enable-position-stacking detected ...', 'Running backtesting for Strategy DefaultStrategy', - 'Running backtesting for Strategy TestStrategyLegacy', + 'Running backtesting for Strategy TestStrategyLegacyV1', ] for line in exists: diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 68bd01911..9a20256cd 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1219,7 +1219,7 @@ def test_api_strategies(botclient): assert rc.json() == {'strategies': [ 'DefaultStrategy', 'HyperoptableStrategy', - 'TestStrategyLegacy' + 'TestStrategyLegacyV1' ]} diff --git a/tests/strategy/strats/failing_strategy.py b/tests/strategy/strats/failing_strategy.py index f8eaac3c3..a65a0ddc2 100644 --- a/tests/strategy/strats/failing_strategy.py +++ b/tests/strategy/strats/failing_strategy.py @@ -5,5 +5,5 @@ import nonexiting_module # noqa from freqtrade.strategy.interface import IStrategy -class TestStrategyLegacy(IStrategy): +class TestStrategyLegacyV1(IStrategy): pass diff --git a/tests/strategy/strats/legacy_strategy.py b/tests/strategy/strats/legacy_strategy.py index 9ef00b110..ebfce632b 100644 --- a/tests/strategy/strats/legacy_strategy.py +++ b/tests/strategy/strats/legacy_strategy.py @@ -10,7 +10,7 @@ from freqtrade.strategy.interface import IStrategy # -------------------------------- # This class is a sample. Feel free to customize it. -class TestStrategyLegacy(IStrategy): +class TestStrategyLegacyV1(IStrategy): """ This is a test strategy using the legacy function headers, which will be removed in a future update. diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index eea6a85d2..d03c7d2c1 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -603,7 +603,7 @@ def test_is_pair_locked(default_conf): def test_is_informative_pairs_callback(default_conf): - default_conf.update({'strategy': 'TestStrategyLegacy'}) + default_conf.update({'strategy': 'TestStrategyLegacyV1'}) strategy = StrategyResolver.load_strategy(default_conf) # Should return empty # Uses fallback to base implementation diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index 115a2fbde..b173a562b 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -330,7 +330,7 @@ def test_strategy_override_use_sell_profit_only(caplog, default_conf): @pytest.mark.filterwarnings("ignore:deprecated") def test_deprecate_populate_indicators(result, default_conf): default_location = Path(__file__).parent / "strats" - default_conf.update({'strategy': 'TestStrategyLegacy', + default_conf.update({'strategy': 'TestStrategyLegacyV1', 'strategy_path': default_location}) strategy = StrategyResolver.load_strategy(default_conf) with warnings.catch_warnings(record=True) as w: @@ -365,7 +365,7 @@ def test_deprecate_populate_indicators(result, default_conf): def test_call_deprecated_function(result, monkeypatch, default_conf, caplog): default_location = Path(__file__).parent / "strats" del default_conf['timeframe'] - default_conf.update({'strategy': 'TestStrategyLegacy', + default_conf.update({'strategy': 'TestStrategyLegacyV1', 'strategy_path': default_location}) strategy = StrategyResolver.load_strategy(default_conf) metadata = {'pair': 'ETH/BTC'} From 58ecb34a668528da2ed74c4a799a63a12b6e761b Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 07:13:18 +0200 Subject: [PATCH 350/519] Allow DefaultHyperopts as hyperopt name --- freqtrade/commands/deploy_commands.py | 2 -- tests/commands/test_commands.py | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/freqtrade/commands/deploy_commands.py b/freqtrade/commands/deploy_commands.py index ec558e7b1..c98335e0b 100644 --- a/freqtrade/commands/deploy_commands.py +++ b/freqtrade/commands/deploy_commands.py @@ -126,8 +126,6 @@ def start_new_hyperopt(args: Dict[str, Any]) -> None: config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) if 'hyperopt' in args and args['hyperopt']: - if args['hyperopt'] == 'DefaultHyperopt': - raise OperationalException("DefaultHyperopt is not allowed as name.") new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args['hyperopt'] + '.py') diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 0f34f6e10..a2ab37e61 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -541,17 +541,6 @@ def test_start_new_hyperopt(mocker, caplog): start_new_hyperopt(get_args(args)) -def test_start_new_hyperopt_DefaultHyperopt(mocker, caplog): - args = [ - "new-hyperopt", - "--hyperopt", - "DefaultHyperopt" - ] - with pytest.raises(OperationalException, - match=r"DefaultHyperopt is not allowed as name\."): - start_new_hyperopt(get_args(args)) - - def test_start_new_hyperopt_no_arg(mocker): args = [ "new-hyperopt", From 0d8e105a339dcd96ac883dbfd23ca210bbd8b3eb Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 07:14:49 +0200 Subject: [PATCH 351/519] Rename legacystrategy file --- tests/commands/test_commands.py | 4 ++-- .../strats/{legacy_strategy.py => legacy_strategy_v1.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/strategy/strats/{legacy_strategy.py => legacy_strategy_v1.py} (100%) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index a2ab37e61..9f21e93d7 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -806,7 +806,7 @@ def test_start_list_strategies(mocker, caplog, capsys): start_list_strategies(pargs) captured = capsys.readouterr() assert "TestStrategyLegacyV1" in captured.out - assert "legacy_strategy.py" not in captured.out + assert "legacy_strategy_v1.py" not in captured.out assert "DefaultStrategy" in captured.out # Test regular output @@ -821,7 +821,7 @@ def test_start_list_strategies(mocker, caplog, capsys): start_list_strategies(pargs) captured = capsys.readouterr() assert "TestStrategyLegacyV1" in captured.out - assert "legacy_strategy.py" in captured.out + assert "legacy_strategy_v1.py" in captured.out assert "DefaultStrategy" in captured.out diff --git a/tests/strategy/strats/legacy_strategy.py b/tests/strategy/strats/legacy_strategy_v1.py similarity index 100% rename from tests/strategy/strats/legacy_strategy.py rename to tests/strategy/strats/legacy_strategy_v1.py From df1c0540abffd71269a13c0dc4cea925f1b95597 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 07:18:26 +0200 Subject: [PATCH 352/519] Rename Hyperopt Test Class --- tests/commands/test_commands.py | 4 ++-- tests/optimize/conftest.py | 2 +- ..._hyperopt.py => hyperopt_test_sep_file.py} | 2 +- tests/optimize/test_hyperopt.py | 20 +++++++++---------- 4 files changed, 14 insertions(+), 14 deletions(-) rename tests/optimize/hyperopts/{default_hyperopt.py => hyperopt_test_sep_file.py} (99%) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 9f21e93d7..5a177267c 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -839,7 +839,7 @@ def test_start_list_hyperopts(mocker, caplog, capsys): captured = capsys.readouterr() assert "TestHyperoptLegacy" not in captured.out assert "legacy_hyperopt.py" not in captured.out - assert "DefaultHyperOpt" in captured.out + assert "HyperoptTestSepFile" in captured.out assert "test_hyperopt.py" not in captured.out # Test regular output @@ -854,7 +854,7 @@ def test_start_list_hyperopts(mocker, caplog, capsys): captured = capsys.readouterr() assert "TestHyperoptLegacy" not in captured.out assert "legacy_hyperopt.py" not in captured.out - assert "DefaultHyperOpt" in captured.out + assert "HyperoptTestSepFile" in captured.out def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index a7fd238d1..95c9fef97 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -16,7 +16,7 @@ def hyperopt_conf(default_conf): hyperconf.update({ 'datadir': Path(default_conf['datadir']), 'runmode': RunMode.HYPEROPT, - 'hyperopt': 'DefaultHyperOpt', + 'hyperopt': 'HyperoptTestSepFile', 'hyperopt_loss': 'ShortTradeDurHyperOptLoss', 'hyperopt_path': str(Path(__file__).parent / 'hyperopts'), 'epochs': 1, diff --git a/tests/optimize/hyperopts/default_hyperopt.py b/tests/optimize/hyperopts/hyperopt_test_sep_file.py similarity index 99% rename from tests/optimize/hyperopts/default_hyperopt.py rename to tests/optimize/hyperopts/hyperopt_test_sep_file.py index 2e2bca3d0..0fa1e1959 100644 --- a/tests/optimize/hyperopts/default_hyperopt.py +++ b/tests/optimize/hyperopts/hyperopt_test_sep_file.py @@ -11,7 +11,7 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.optimize.hyperopt_interface import IHyperOpt -class DefaultHyperOpt(IHyperOpt): +class HyperoptTestSepFile(IHyperOpt): """ Default hyperopt provided by the Freqtrade bot. You can override it with your own Hyperopt diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 0ca79d268..94f4abc7a 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -22,7 +22,7 @@ from freqtrade.strategy.hyper import IntParameter from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) -from .hyperopts.default_hyperopt import DefaultHyperOpt +from .hyperopts.hyperopt_test_sep_file import HyperoptTestSepFile def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None: @@ -31,7 +31,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpt', + '--hyperopt', 'HyperoptTestSepFile', ] config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) @@ -63,7 +63,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpt', + '--hyperopt', 'HyperoptTestSepFile', '--datadir', '/foo/bar', '--timeframe', '1m', '--timerange', ':100', @@ -115,7 +115,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpt', + '--hyperopt', 'HyperoptTestSepFile', '--stake-amount', '1', '--starting-balance', '2' ] @@ -136,7 +136,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None def test_hyperoptresolver(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) - hyperopt = DefaultHyperOpt + hyperopt = HyperoptTestSepFile delattr(hyperopt, 'populate_indicators') delattr(hyperopt, 'populate_buy_trend') delattr(hyperopt, 'populate_sell_trend') @@ -144,7 +144,7 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object', MagicMock(return_value=hyperopt(default_conf)) ) - default_conf.update({'hyperopt': 'DefaultHyperOpt'}) + default_conf.update({'hyperopt': 'HyperoptTestSepFile'}) x = HyperOptResolver.load_hyperopt(default_conf) assert not hasattr(x, 'populate_indicators') assert not hasattr(x, 'populate_buy_trend') @@ -184,7 +184,7 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpt', + '--hyperopt', 'HyperoptTestSepFile', '--hyperopt-path', str(Path(__file__).parent / "hyperopts"), '--epochs', '5', @@ -205,7 +205,7 @@ def test_start(mocker, hyperopt_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpt', + '--hyperopt', 'HyperoptTestSepFile', '--hyperopt-loss', 'SharpeHyperOptLossDaily', '--epochs', '5' ] @@ -229,7 +229,7 @@ def test_start_no_data(mocker, hyperopt_conf) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpt', + '--hyperopt', 'HyperoptTestSepFile', '--hyperopt-loss', 'SharpeHyperOptLossDaily', '--epochs', '5' ] @@ -247,7 +247,7 @@ def test_start_filelock(mocker, hyperopt_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'DefaultHyperOpt', + '--hyperopt', 'HyperoptTestSepFile', '--hyperopt-loss', 'SharpeHyperOptLossDaily', '--epochs', '5' ] From 6d96b1127989fc40a5f0027ec5067d11645bee5b Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 07:25:53 +0200 Subject: [PATCH 353/519] Rename DefaultStrategy --- build_helpers/publish_docker_arm64.sh | 2 +- build_helpers/publish_docker_multi.sh | 2 +- tests/commands/test_commands.py | 4 +- tests/conftest.py | 2 +- tests/conftest_trades.py | 8 ++-- tests/data/test_btanalysis.py | 6 +-- tests/data/test_history.py | 6 +-- tests/optimize/test_backtesting.py | 26 ++++++------ tests/optimize/test_edge_cli.py | 6 +-- tests/optimize/test_hyperopt.py | 2 +- tests/optimize/test_hyperopt_tools.py | 16 +++---- tests/optimize/test_optimize_reports.py | 2 +- tests/rpc/test_rpc_apiserver.py | 28 ++++++------- tests/rpc/test_rpc_telegram.py | 4 +- tests/strategy/strats/default_strategy.py | 4 +- tests/strategy/test_default_strategy.py | 16 +++---- tests/strategy/test_interface.py | 26 ++++++------ tests/strategy/test_strategy_loading.py | 42 +++++++++---------- tests/test_arguments.py | 2 +- tests/test_configuration.py | 6 +-- tests/test_plotting.py | 3 -- .../testdata/backtest-result_multistrat.json | 2 +- tests/testdata/backtest-result_new.json | 2 +- 23 files changed, 107 insertions(+), 110 deletions(-) diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh index e7b69b2dc..1ad8074d4 100755 --- a/build_helpers/publish_docker_arm64.sh +++ b/build_helpers/publish_docker_arm64.sh @@ -42,7 +42,7 @@ docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${CACHE_I docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM # Run backtest -docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy +docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy StrategyTestV2 if [ $? -ne 0 ]; then echo "failed running backtest" diff --git a/build_helpers/publish_docker_multi.sh b/build_helpers/publish_docker_multi.sh index 4010eed45..dd6ac841e 100755 --- a/build_helpers/publish_docker_multi.sh +++ b/build_helpers/publish_docker_multi.sh @@ -53,7 +53,7 @@ docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${CACHE_IMAGE docker tag freqtrade:$TAG_PLOT ${CACHE_IMAGE}:$TAG_PLOT # Run backtest -docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy +docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy StrategyTestV2 if [ $? -ne 0 ]; then echo "failed running backtest" diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 5a177267c..1da9e5100 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -807,7 +807,7 @@ def test_start_list_strategies(mocker, caplog, capsys): captured = capsys.readouterr() assert "TestStrategyLegacyV1" in captured.out assert "legacy_strategy_v1.py" not in captured.out - assert "DefaultStrategy" in captured.out + assert "StrategyTestV2" in captured.out # Test regular output args = [ @@ -822,7 +822,7 @@ def test_start_list_strategies(mocker, caplog, capsys): captured = capsys.readouterr() assert "TestStrategyLegacyV1" in captured.out assert "legacy_strategy_v1.py" in captured.out - assert "DefaultStrategy" in captured.out + assert "StrategyTestV2" in captured.out def test_start_list_hyperopts(mocker, caplog, capsys): diff --git a/tests/conftest.py b/tests/conftest.py index 0c9a96e2b..5e08e7097 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -323,7 +323,7 @@ def get_default_conf(testdatadir): "user_data_dir": Path("user_data"), "verbosity": 3, "strategy_path": str(Path(__file__).parent / "strategy" / "strats"), - "strategy": "DefaultStrategy", + "strategy": "StrategyTestV2", "disableparamexport": True, "internals": {}, "export": "none", diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index b92b51144..024803be0 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -33,7 +33,7 @@ def mock_trade_1(fee): open_rate=0.123, exchange='binance', open_order_id='dry_run_buy_12345', - strategy='DefaultStrategy', + strategy='StrategyTestV2', timeframe=5, ) o = Order.parse_from_ccxt_object(mock_order_1(), 'ETH/BTC', 'buy') @@ -87,7 +87,7 @@ def mock_trade_2(fee): exchange='binance', is_open=False, open_order_id='dry_run_sell_12345', - strategy='DefaultStrategy', + strategy='StrategyTestV2', timeframe=5, sell_reason='sell_signal', open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), @@ -146,7 +146,7 @@ def mock_trade_3(fee): close_profit_abs=0.000155, exchange='binance', is_open=False, - strategy='DefaultStrategy', + strategy='StrategyTestV2', timeframe=5, sell_reason='roi', open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), @@ -189,7 +189,7 @@ def mock_trade_4(fee): open_rate=0.123, exchange='binance', open_order_id='prod_buy_12345', - strategy='DefaultStrategy', + strategy='StrategyTestV2', timeframe=5, ) o = Order.parse_from_ccxt_object(mock_order_4(), 'ETC/BTC', 'buy') diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index 6bde60926..1dcd04a80 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -93,7 +93,7 @@ def test_load_backtest_data_new_format(testdatadir): def test_load_backtest_data_multi(testdatadir): filename = testdatadir / "backtest-result_multistrat.json" - for strategy in ('DefaultStrategy', 'TestStrategy'): + for strategy in ('StrategyTestV2', 'TestStrategy'): bt_data = load_backtest_data(filename, strategy=strategy) assert isinstance(bt_data, DataFrame) assert set(bt_data.columns) == set(BT_DATA_COLUMNS_MID) @@ -128,7 +128,7 @@ def test_load_trades_from_db(default_conf, fee, mocker): for col in BT_DATA_COLUMNS: if col not in ['index', 'open_at_end']: assert col in trades.columns - trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='DefaultStrategy') + trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='StrategyTestV2') assert len(trades) == 4 trades = load_trades_from_db(db_url=default_conf['db_url'], strategy='NoneStrategy') assert len(trades) == 0 @@ -186,7 +186,7 @@ def test_load_trades(default_conf, mocker): db_url=default_conf.get('db_url'), exportfilename=default_conf.get('exportfilename'), no_trades=False, - strategy="DefaultStrategy", + strategy="StrategyTestV2", ) assert db_mock.call_count == 1 diff --git a/tests/data/test_history.py b/tests/data/test_history.py index e9d2c3638..575a590e7 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -380,7 +380,7 @@ def test_file_dump_json_tofile(testdatadir) -> None: def test_get_timerange(default_conf, mocker, testdatadir) -> None: patch_exchange(mocker) - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) data = strategy.advise_all_indicators( @@ -398,7 +398,7 @@ def test_get_timerange(default_conf, mocker, testdatadir) -> None: def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir) -> None: patch_exchange(mocker) - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) data = strategy.advise_all_indicators( @@ -422,7 +422,7 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog, testdatadir) def test_validate_backtest_data(default_conf, mocker, caplog, testdatadir) -> None: patch_exchange(mocker) - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) timerange = TimeRange('index', 'index', 200, 250) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 80f7aa2d6..6442f6cb7 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -155,7 +155,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca args = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--export', 'none' ] @@ -190,7 +190,7 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> args = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--datadir', '/foo/bar', '--timeframe', '1m', '--enable-position-stacking', @@ -240,7 +240,7 @@ def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog) args = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--stake-amount', '1', '--starting-balance', '2' ] @@ -251,7 +251,7 @@ def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog) args = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--stake-amount', '1', '--starting-balance', '0.5' ] @@ -269,7 +269,7 @@ def test_start(mocker, fee, default_conf, caplog) -> None: args = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', ] pargs = get_args(args) start_backtesting(pargs) @@ -302,7 +302,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None: def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None: patch_exchange(mocker) del default_conf['timeframe'] - default_conf['strategy_list'] = ['DefaultStrategy', + default_conf['strategy_list'] = ['StrategyTestV2', 'SampleStrategy'] mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) @@ -340,7 +340,7 @@ def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None: assert len(processed['UNITTEST/BTC']) == 102 # Load strategy to compare the result between Backtesting function and strategy are the same - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) processed2 = strategy.advise_all_indicators(data) @@ -473,7 +473,7 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti Backtesting(default_conf) # Multiple strategies - default_conf['strategy_list'] = ['DefaultStrategy', 'TestStrategyLegacyV1'] + default_conf['strategy_list'] = ['StrategyTestV2', 'TestStrategyLegacyV1'] with pytest.raises(OperationalException, match='PrecisionFilter not allowed for backtesting multiple strategies.'): Backtesting(default_conf) @@ -837,7 +837,7 @@ def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir): args = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--datadir', str(testdatadir), '--timeframe', '1m', '--timerange', '1510694220-1510700340', @@ -908,7 +908,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): '--enable-position-stacking', '--disable-max-market-positions', '--strategy-list', - 'DefaultStrategy', + 'StrategyTestV2', 'TestStrategyLegacyV1', ] args = get_args(args) @@ -931,7 +931,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'Backtesting with data from 2017-11-14 21:17:00 ' 'up to 2017-11-14 22:58:00 (0 days).', 'Parameter --enable-position-stacking detected ...', - 'Running backtesting for Strategy DefaultStrategy', + 'Running backtesting for Strategy StrategyTestV2', 'Running backtesting for Strategy TestStrategyLegacyV1', ] @@ -1012,7 +1012,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat '--enable-position-stacking', '--disable-max-market-positions', '--strategy-list', - 'DefaultStrategy', + 'StrategyTestV2', 'TestStrategyLegacyV1', ] args = get_args(args) @@ -1029,7 +1029,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'Backtesting with data from 2017-11-14 21:17:00 ' 'up to 2017-11-14 22:58:00 (0 days).', 'Parameter --enable-position-stacking detected ...', - 'Running backtesting for Strategy DefaultStrategy', + 'Running backtesting for Strategy StrategyTestV2', 'Running backtesting for Strategy TestStrategyLegacyV1', ] diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py index 6818a573b..18d5f1c76 100644 --- a/tests/optimize/test_edge_cli.py +++ b/tests/optimize/test_edge_cli.py @@ -16,7 +16,7 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca args = [ 'edge', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', ] config = setup_optimize_configuration(get_args(args), RunMode.EDGE) @@ -46,7 +46,7 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N args = [ 'edge', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--datadir', '/foo/bar', '--timeframe', '1m', '--timerange', ':100', @@ -80,7 +80,7 @@ def test_start(mocker, fee, edge_conf, caplog) -> None: args = [ 'edge', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', ] pargs = get_args(args) start_edge(pargs) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 94f4abc7a..565d6077a 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -125,7 +125,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None args = [ 'hyperopt', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--stake-amount', '1', '--starting-balance', '0.5' ] diff --git a/tests/optimize/test_hyperopt_tools.py b/tests/optimize/test_hyperopt_tools.py index e845b2a77..9f917c2a7 100644 --- a/tests/optimize/test_hyperopt_tools.py +++ b/tests/optimize/test_hyperopt_tools.py @@ -167,7 +167,7 @@ def test__pprint_dict(): def test_get_strategy_filename(default_conf): - x = HyperoptTools.get_strategy_filename(default_conf, 'DefaultStrategy') + x = HyperoptTools.get_strategy_filename(default_conf, 'StrategyTestV2') assert isinstance(x, Path) assert x == Path(__file__).parents[1] / 'strategy/strats/default_strategy.py' @@ -177,7 +177,7 @@ def test_get_strategy_filename(default_conf): def test_export_params(tmpdir): - filename = Path(tmpdir) / "DefaultStrategy.json" + filename = Path(tmpdir) / "StrategyTestV2.json" assert not filename.is_file() params = { "params_details": { @@ -205,12 +205,12 @@ def test_export_params(tmpdir): } } - HyperoptTools.export_params(params, "DefaultStrategy", filename) + HyperoptTools.export_params(params, "StrategyTestV2", filename) assert filename.is_file() content = rapidjson.load(filename.open('r')) - assert content['strategy_name'] == 'DefaultStrategy' + assert content['strategy_name'] == 'StrategyTestV2' assert 'params' in content assert "buy" in content["params"] assert "sell" in content["params"] @@ -223,7 +223,7 @@ def test_try_export_params(default_conf, tmpdir, caplog, mocker): default_conf['disableparamexport'] = False export_mock = mocker.patch("freqtrade.optimize.hyperopt_tools.HyperoptTools.export_params") - filename = Path(tmpdir) / "DefaultStrategy.json" + filename = Path(tmpdir) / "StrategyTestV2.json" assert not filename.is_file() params = { "params_details": { @@ -252,16 +252,16 @@ def test_try_export_params(default_conf, tmpdir, caplog, mocker): FTHYPT_FILEVERSION: 2, } - HyperoptTools.try_export_params(default_conf, "DefaultStrategy22", params) + HyperoptTools.try_export_params(default_conf, "StrategyTestV222", params) assert log_has("Strategy not found, not exporting parameter file.", caplog) assert export_mock.call_count == 0 caplog.clear() - HyperoptTools.try_export_params(default_conf, "DefaultStrategy", params) + HyperoptTools.try_export_params(default_conf, "StrategyTestV2", params) assert export_mock.call_count == 1 - assert export_mock.call_args_list[0][0][1] == 'DefaultStrategy' + assert export_mock.call_args_list[0][0][1] == 'StrategyTestV2' assert export_mock.call_args_list[0][0][2].name == 'default_strategy.json' diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 3f31efb95..83caefd2d 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -52,7 +52,7 @@ def test_text_table_bt_results(): def test_generate_backtest_stats(default_conf, testdatadir, tmpdir): - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) StrategyResolver.load_strategy(default_conf) results = {'DefStrat': { diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 9a20256cd..0256fb632 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -879,7 +879,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): 'open_trade_value': 15.1668225, 'sell_reason': None, 'sell_order_status': None, - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', @@ -984,7 +984,7 @@ def test_api_forcebuy(botclient, mocker, fee): close_rate=0.265441, id=22, timeframe=5, - strategy="DefaultStrategy" + strategy="StrategyTestV2" )) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) @@ -1034,7 +1034,7 @@ def test_api_forcebuy(botclient, mocker, fee): 'open_trade_value': 0.24605460, 'sell_reason': None, 'sell_order_status': None, - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'buy_tag': None, 'timeframe': 5, 'exchange': 'binance', @@ -1101,7 +1101,7 @@ def test_api_pair_candles(botclient, ohlcv_history): f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}") assert_response(rc) assert 'strategy' in rc.json() - assert rc.json()['strategy'] == 'DefaultStrategy' + assert rc.json()['strategy'] == 'StrategyTestV2' assert 'columns' in rc.json() assert 'data_start_ts' in rc.json() assert 'data_start' in rc.json() @@ -1139,19 +1139,19 @@ def test_api_pair_history(botclient, ohlcv_history): # No pair rc = client_get(client, f"{BASE_URI}/pair_history?timeframe={timeframe}" - "&timerange=20180111-20180112&strategy=DefaultStrategy") + "&timerange=20180111-20180112&strategy=StrategyTestV2") assert_response(rc, 422) # No Timeframe rc = client_get(client, f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC" - "&timerange=20180111-20180112&strategy=DefaultStrategy") + "&timerange=20180111-20180112&strategy=StrategyTestV2") assert_response(rc, 422) # No timerange rc = client_get(client, f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}" - "&strategy=DefaultStrategy") + "&strategy=StrategyTestV2") assert_response(rc, 422) # No strategy @@ -1163,14 +1163,14 @@ def test_api_pair_history(botclient, ohlcv_history): # Working rc = client_get(client, f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}" - "&timerange=20180111-20180112&strategy=DefaultStrategy") + "&timerange=20180111-20180112&strategy=StrategyTestV2") assert_response(rc, 200) assert rc.json()['length'] == 289 assert len(rc.json()['data']) == rc.json()['length'] assert 'columns' in rc.json() assert 'data' in rc.json() assert rc.json()['pair'] == 'UNITTEST/BTC' - assert rc.json()['strategy'] == 'DefaultStrategy' + assert rc.json()['strategy'] == 'StrategyTestV2' assert rc.json()['data_start'] == '2018-01-11 00:00:00+00:00' assert rc.json()['data_start_ts'] == 1515628800000 assert rc.json()['data_stop'] == '2018-01-12 00:00:00+00:00' @@ -1179,7 +1179,7 @@ def test_api_pair_history(botclient, ohlcv_history): # No data found rc = client_get(client, f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}" - "&timerange=20200111-20200112&strategy=DefaultStrategy") + "&timerange=20200111-20200112&strategy=StrategyTestV2") assert_response(rc, 502) assert rc.json()['error'] == ("Error querying /api/v1/pair_history: " "No data for UNITTEST/BTC, 5m in 20200111-20200112 found.") @@ -1217,7 +1217,7 @@ def test_api_strategies(botclient): assert_response(rc) assert rc.json() == {'strategies': [ - 'DefaultStrategy', + 'StrategyTestV2', 'HyperoptableStrategy', 'TestStrategyLegacyV1' ]} @@ -1226,10 +1226,10 @@ def test_api_strategies(botclient): def test_api_strategy(botclient): ftbot, client = botclient - rc = client_get(client, f"{BASE_URI}/strategy/DefaultStrategy") + rc = client_get(client, f"{BASE_URI}/strategy/StrategyTestV2") assert_response(rc) - assert rc.json()['strategy'] == 'DefaultStrategy' + assert rc.json()['strategy'] == 'StrategyTestV2' data = (Path(__file__).parents[1] / "strategy/strats/default_strategy.py").read_text() assert rc.json()['code'] == data @@ -1288,7 +1288,7 @@ def test_api_backtesting(botclient, mocker, fee, caplog): # start backtesting data = { - "strategy": "DefaultStrategy", + "strategy": "StrategyTestV2", "timeframe": "5m", "timerange": "20180110-20180111", "max_open_trades": 3, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 8596c7c44..2013dad7d 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1236,7 +1236,7 @@ def test_show_config_handle(default_conf, update, mocker) -> None: assert msg_mock.call_count == 1 assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0] assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0] - assert '*Strategy:* `DefaultStrategy`' in msg_mock.call_args_list[0][0][0] + assert '*Strategy:* `StrategyTestV2`' in msg_mock.call_args_list[0][0][0] assert '*Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() @@ -1245,7 +1245,7 @@ def test_show_config_handle(default_conf, update, mocker) -> None: assert msg_mock.call_count == 1 assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0] assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0] - assert '*Strategy:* `DefaultStrategy`' in msg_mock.call_args_list[0][0][0] + assert '*Strategy:* `StrategyTestV2`' in msg_mock.call_args_list[0][0][0] assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0] diff --git a/tests/strategy/strats/default_strategy.py b/tests/strategy/strats/default_strategy.py index 7171b93ae..53e39526f 100644 --- a/tests/strategy/strats/default_strategy.py +++ b/tests/strategy/strats/default_strategy.py @@ -7,9 +7,9 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.strategy.interface import IStrategy -class DefaultStrategy(IStrategy): +class StrategyTestV2(IStrategy): """ - Default Strategy provided by freqtrade bot. + Strategy used by tests freqtrade bot. Please do not modify this strategy, it's intended for internal use only. Please look at the SampleStrategy in the user_data/strategy directory or strategy repository https://github.com/freqtrade/freqtrade-strategies diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index 92ac9f63a..2566c8478 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -4,20 +4,20 @@ from pandas import DataFrame from freqtrade.persistence.models import Trade -from .strats.default_strategy import DefaultStrategy +from .strats.default_strategy import StrategyTestV2 def test_default_strategy_structure(): - assert hasattr(DefaultStrategy, 'minimal_roi') - assert hasattr(DefaultStrategy, 'stoploss') - assert hasattr(DefaultStrategy, 'timeframe') - assert hasattr(DefaultStrategy, 'populate_indicators') - assert hasattr(DefaultStrategy, 'populate_buy_trend') - assert hasattr(DefaultStrategy, 'populate_sell_trend') + assert hasattr(StrategyTestV2, 'minimal_roi') + assert hasattr(StrategyTestV2, 'stoploss') + assert hasattr(StrategyTestV2, 'timeframe') + assert hasattr(StrategyTestV2, 'populate_indicators') + assert hasattr(StrategyTestV2, 'populate_buy_trend') + assert hasattr(StrategyTestV2, 'populate_sell_trend') def test_default_strategy(result, fee): - strategy = DefaultStrategy({}) + strategy = StrategyTestV2({}) metadata = {'pair': 'ETH/BTC'} assert type(strategy.minimal_roi) is dict diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index d03c7d2c1..9023f7981 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -22,11 +22,11 @@ from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from tests.conftest import log_has, log_has_re -from .strats.default_strategy import DefaultStrategy +from .strats.default_strategy import StrategyTestV2 # Avoid to reinit the same object again and again -_STRATEGY = DefaultStrategy(config={}) +_STRATEGY = StrategyTestV2(config={}) _STRATEGY.dp = DataProvider({}, None, None) @@ -148,7 +148,7 @@ def test_get_signal_no_sell_column(default_conf, mocker, caplog, ohlcv_history): def test_ignore_expired_candle(default_conf): - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) strategy.ignore_buying_expired_candle_after = 60 @@ -229,7 +229,7 @@ def test_assert_df(ohlcv_history, caplog): def test_advise_all_indicators(default_conf, testdatadir) -> None: - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) timerange = TimeRange.parse_timerange('1510694220-1510700340') @@ -240,7 +240,7 @@ def test_advise_all_indicators(default_conf, testdatadir) -> None: def test_advise_all_indicators_copy(mocker, default_conf, testdatadir) -> None: - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) aimock = mocker.patch('freqtrade.strategy.interface.IStrategy.advise_indicators') timerange = TimeRange.parse_timerange('1510694220-1510700340') @@ -258,7 +258,7 @@ def test_min_roi_reached(default_conf, fee) -> None: min_roi_list = [{20: 0.05, 55: 0.01, 0: 0.1}, {0: 0.1, 20: 0.05, 55: 0.01}] for roi in min_roi_list: - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) strategy.minimal_roi = roi trade = Trade( @@ -297,7 +297,7 @@ def test_min_roi_reached2(default_conf, fee) -> None: }, ] for roi in min_roi_list: - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) strategy.minimal_roi = roi trade = Trade( @@ -332,7 +332,7 @@ def test_min_roi_reached3(default_conf, fee) -> None: 30: 0.05, 55: 0.30, } - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) strategy.minimal_roi = min_roi trade = Trade( @@ -385,7 +385,7 @@ def test_min_roi_reached3(default_conf, fee) -> None: def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, trailing, custom, profit2, adjusted2, expected2, custom_stop) -> None: - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) trade = Trade( @@ -433,7 +433,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili def test_custom_sell(default_conf, fee, caplog) -> None: - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) trade = Trade( @@ -487,7 +487,7 @@ def test_analyze_ticker_default(ohlcv_history, mocker, caplog) -> None: advise_sell=sell_mock, ) - strategy = DefaultStrategy({}) + strategy = StrategyTestV2({}) strategy.analyze_ticker(ohlcv_history, {'pair': 'ETH/BTC'}) assert ind_mock.call_count == 1 assert buy_mock.call_count == 1 @@ -518,7 +518,7 @@ def test__analyze_ticker_internal_skip_analyze(ohlcv_history, mocker, caplog) -> advise_sell=sell_mock, ) - strategy = DefaultStrategy({}) + strategy = StrategyTestV2({}) strategy.dp = DataProvider({}, None, None) strategy.process_only_new_candles = True @@ -550,7 +550,7 @@ def test__analyze_ticker_internal_skip_analyze(ohlcv_history, mocker, caplog) -> @pytest.mark.usefixtures("init_persistence") def test_is_pair_locked(default_conf): - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) PairLocks.timeframe = default_conf['timeframe'] PairLocks.use_db = True strategy = StrategyResolver.load_strategy(default_conf) diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index b173a562b..2cbc9d0c6 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -18,7 +18,7 @@ def test_search_strategy(): s, _ = StrategyResolver._search_object( directory=default_location, - object_name='DefaultStrategy', + object_name='StrategyTestV2', add_source=True, ) assert issubclass(s, IStrategy) @@ -74,10 +74,10 @@ def test_load_strategy_base64(result, caplog, default_conf): def test_load_strategy_invalid_directory(result, caplog, default_conf): - default_conf['strategy'] = 'DefaultStrategy' + default_conf['strategy'] = 'StrategyTestV2' extra_dir = Path.cwd() / 'some/path' with pytest.raises(OperationalException): - StrategyResolver._load_strategy('DefaultStrategy', config=default_conf, + StrategyResolver._load_strategy('StrategyTestV2', config=default_conf, extra_dir=extra_dir) assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog) @@ -100,7 +100,7 @@ def test_load_strategy_noname(default_conf): def test_strategy(result, default_conf): - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) metadata = {'pair': 'ETH/BTC'} @@ -127,7 +127,7 @@ def test_strategy(result, default_conf): def test_strategy_override_minimal_roi(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'minimal_roi': { "20": 0.1, "0": 0.5 @@ -144,7 +144,7 @@ def test_strategy_override_minimal_roi(caplog, default_conf): def test_strategy_override_stoploss(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'stoploss': -0.5 }) strategy = StrategyResolver.load_strategy(default_conf) @@ -156,7 +156,7 @@ def test_strategy_override_stoploss(caplog, default_conf): def test_strategy_override_trailing_stop(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'trailing_stop': True }) strategy = StrategyResolver.load_strategy(default_conf) @@ -169,7 +169,7 @@ def test_strategy_override_trailing_stop(caplog, default_conf): def test_strategy_override_trailing_stop_positive(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'trailing_stop_positive': -0.1, 'trailing_stop_positive_offset': -0.2 @@ -189,7 +189,7 @@ def test_strategy_override_timeframe(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'timeframe': 60, 'stake_currency': 'ETH' }) @@ -205,7 +205,7 @@ def test_strategy_override_process_only_new_candles(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'process_only_new_candles': True }) strategy = StrategyResolver.load_strategy(default_conf) @@ -225,7 +225,7 @@ def test_strategy_override_order_types(caplog, default_conf): 'stoploss_on_exchange': True, } default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'order_types': order_types }) strategy = StrategyResolver.load_strategy(default_conf) @@ -239,12 +239,12 @@ def test_strategy_override_order_types(caplog, default_conf): " 'stoploss_on_exchange': True}.", caplog) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'order_types': {'buy': 'market'} }) # Raise error for invalid configuration with pytest.raises(ImportError, - match=r"Impossible to load Strategy 'DefaultStrategy'. " + match=r"Impossible to load Strategy 'StrategyTestV2'. " r"Order-types mapping is incomplete."): StrategyResolver.load_strategy(default_conf) @@ -258,7 +258,7 @@ def test_strategy_override_order_tif(caplog, default_conf): } default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'order_time_in_force': order_time_in_force }) strategy = StrategyResolver.load_strategy(default_conf) @@ -271,12 +271,12 @@ def test_strategy_override_order_tif(caplog, default_conf): " {'buy': 'fok', 'sell': 'gtc'}.", caplog) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'order_time_in_force': {'buy': 'fok'} }) # Raise error for invalid configuration with pytest.raises(ImportError, - match=r"Impossible to load Strategy 'DefaultStrategy'. " + match=r"Impossible to load Strategy 'StrategyTestV2'. " r"Order-time-in-force mapping is incomplete."): StrategyResolver.load_strategy(default_conf) @@ -284,7 +284,7 @@ def test_strategy_override_order_tif(caplog, default_conf): def test_strategy_override_use_sell_signal(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', }) strategy = StrategyResolver.load_strategy(default_conf) assert strategy.use_sell_signal @@ -294,7 +294,7 @@ def test_strategy_override_use_sell_signal(caplog, default_conf): assert default_conf['use_sell_signal'] default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'use_sell_signal': False, }) strategy = StrategyResolver.load_strategy(default_conf) @@ -307,7 +307,7 @@ def test_strategy_override_use_sell_signal(caplog, default_conf): def test_strategy_override_use_sell_profit_only(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', }) strategy = StrategyResolver.load_strategy(default_conf) assert not strategy.sell_profit_only @@ -317,7 +317,7 @@ def test_strategy_override_use_sell_profit_only(caplog, default_conf): assert not default_conf['sell_profit_only'] default_conf.update({ - 'strategy': 'DefaultStrategy', + 'strategy': 'StrategyTestV2', 'sell_profit_only': True, }) strategy = StrategyResolver.load_strategy(default_conf) @@ -395,7 +395,7 @@ def test_call_deprecated_function(result, monkeypatch, default_conf, caplog): def test_strategy_interface_versioning(result, monkeypatch, default_conf): - default_conf.update({'strategy': 'DefaultStrategy'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) metadata = {'pair': 'ETH/BTC'} diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 5374881fa..fca5c6ab9 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -123,7 +123,7 @@ def test_parse_args_backtesting_custom() -> None: '-c', 'test_conf.json', '--ticker-interval', '1m', '--strategy-list', - 'DefaultStrategy', + 'StrategyTestV2', 'SampleStrategy' ] call_args = Arguments(args).get_parsed_arg() diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 7c555a39e..9aea4fa11 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -404,7 +404,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> arglist = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', ] args = Arguments(arglist).get_parsed_arg() @@ -441,7 +441,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non arglist = [ 'backtesting', '--config', 'config.json', - '--strategy', 'DefaultStrategy', + '--strategy', 'StrategyTestV2', '--datadir', '/foo/bar', '--userdir', "/tmp/freqtrade", '--ticker-interval', '1m', @@ -498,7 +498,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non '--ticker-interval', '1m', '--export', 'trades', '--strategy-list', - 'DefaultStrategy', + 'StrategyTestV2', 'TestStrategy' ] diff --git a/tests/test_plotting.py b/tests/test_plotting.py index ecadc3f8b..51301a464 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -70,7 +70,6 @@ def test_add_indicators(default_conf, testdatadir, caplog): indicators1 = {"ema10": {}} indicators2 = {"macd": {"color": "red"}} - default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) # Generate buy/sell signals and indicators @@ -112,7 +111,6 @@ def test_add_areas(default_conf, testdatadir, caplog): "fill_to": "macdhist"}} ind_plain = {"macd": {"fill_to": "macdhist"}} - default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) # Generate buy/sell signals and indicators @@ -239,7 +237,6 @@ def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir) data = history.load_pair_history(pair=pair, timeframe='1m', datadir=testdatadir, timerange=timerange) - default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) # Generate buy/sell signals and indicators diff --git a/tests/testdata/backtest-result_multistrat.json b/tests/testdata/backtest-result_multistrat.json index 6999050b6..553783dfa 100644 --- a/tests/testdata/backtest-result_multistrat.json +++ b/tests/testdata/backtest-result_multistrat.json @@ -1 +1 @@ -{"strategy": {"DefaultStrategy": {"trades": [{"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:20:00+00:00", "trade_duration": 5, "open_rate": 9.64e-05, "close_rate": 0.00010074887218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1037.344398340249, "profit_abs": 0.00399999999999999}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:30:00+00:00", "trade_duration": 15, "open_rate": 4.756e-05, "close_rate": 4.9705563909774425e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2102.6072329688814, "profit_abs": 0.00399999999999999}, {"pair": "XLM/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:35:00+00:00", "trade_duration": 10, "open_rate": 3.339e-05, "close_rate": 3.489631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2994.908655286014, "profit_abs": 0.0040000000000000036}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:40:00+00:00", "trade_duration": 15, "open_rate": 9.696e-05, "close_rate": 0.00010133413533834584, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1031.3531353135315, "profit_abs": 0.00399999999999999}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 07:35:00+00:00", "close_date": "2018-01-10 08:35:00+00:00", "trade_duration": 60, "open_rate": 0.0943, "close_rate": 0.09477268170426063, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0604453870625663, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 07:40:00+00:00", "close_date": "2018-01-10 08:10:00+00:00", "trade_duration": 30, "open_rate": 0.02719607, "close_rate": 0.02760503345864661, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.677001860930642, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 08:15:00+00:00", "close_date": "2018-01-10 09:55:00+00:00", "trade_duration": 100, "open_rate": 0.04634952, "close_rate": 0.046581848421052625, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1575196463739, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 14:45:00+00:00", "close_date": "2018-01-10 15:50:00+00:00", "trade_duration": 65, "open_rate": 3.066e-05, "close_rate": 3.081368421052631e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3261.5786040443577, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 16:35:00+00:00", "close_date": "2018-01-10 17:15:00+00:00", "trade_duration": 40, "open_rate": 0.0168999, "close_rate": 0.016984611278195488, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.917194776300452, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 16:40:00+00:00", "close_date": "2018-01-10 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.09132568, "close_rate": 0.0917834528320802, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0949822656672252, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 18:50:00+00:00", "close_date": "2018-01-10 19:45:00+00:00", "trade_duration": 55, "open_rate": 0.08898003, "close_rate": 0.08942604518796991, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1238476768326557, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 22:15:00+00:00", "close_date": "2018-01-10 23:00:00+00:00", "trade_duration": 45, "open_rate": 0.08560008, "close_rate": 0.08602915308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1682232072680307, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 22:50:00+00:00", "close_date": "2018-01-10 23:20:00+00:00", "trade_duration": 30, "open_rate": 0.00249083, "close_rate": 0.0025282860902255634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 40.147260150231055, "profit_abs": 0.000999999999999987}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 23:15:00+00:00", "close_date": "2018-01-11 00:15:00+00:00", "trade_duration": 60, "open_rate": 3.022e-05, "close_rate": 3.037147869674185e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3309.0668431502318, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-10 23:40:00+00:00", "close_date": "2018-01-11 00:05:00+00:00", "trade_duration": 25, "open_rate": 0.002437, "close_rate": 0.0024980776942355883, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.03405826836274, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 00:00:00+00:00", "close_date": "2018-01-11 00:35:00+00:00", "trade_duration": 35, "open_rate": 0.04771803, "close_rate": 0.04843559436090225, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0956439316543456, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-11 03:40:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 45, "open_rate": 3.651e-05, "close_rate": 3.2859000000000005e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2738.9756231169545, "profit_abs": -0.01047499999999997}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 03:55:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 30, "open_rate": 0.08824105, "close_rate": 0.08956798308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1332594070446804, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 04:00:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 50, "open_rate": 0.00243, "close_rate": 0.002442180451127819, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.1522633744856, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:55:00+00:00", "trade_duration": 25, "open_rate": 0.04545064, "close_rate": 0.046589753784461146, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.200189040242338, "profit_abs": 0.001999999999999988}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 20, "open_rate": 3.372e-05, "close_rate": 3.456511278195488e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2965.599051008304, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:55:00+00:00", "close_date": "2018-01-11 05:15:00+00:00", "trade_duration": 20, "open_rate": 0.02644, "close_rate": 0.02710265664160401, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7821482602118004, "profit_abs": 0.001999999999999988}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:20:00+00:00", "close_date": "2018-01-11 12:00:00+00:00", "trade_duration": 40, "open_rate": 0.08812, "close_rate": 0.08856170426065162, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1348161597821154, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:35:00+00:00", "close_date": "2018-01-11 12:15:00+00:00", "trade_duration": 40, "open_rate": 0.02683577, "close_rate": 0.026970285137844607, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7263696923919087, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 14:00:00+00:00", "close_date": "2018-01-11 14:25:00+00:00", "trade_duration": 25, "open_rate": 4.919e-05, "close_rate": 5.04228320802005e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.9335230737956, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 19:25:00+00:00", "close_date": "2018-01-11 20:35:00+00:00", "trade_duration": 70, "open_rate": 0.08784896, "close_rate": 0.08828930566416039, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1383174029607181, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:35:00+00:00", "close_date": "2018-01-11 23:30:00+00:00", "trade_duration": 55, "open_rate": 5.105e-05, "close_rate": 5.130588972431077e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1958.8638589618022, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:25:00+00:00", "trade_duration": 30, "open_rate": 3.96e-05, "close_rate": 4.019548872180451e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2525.252525252525, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:35:00+00:00", "trade_duration": 40, "open_rate": 2.885e-05, "close_rate": 2.899461152882205e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3466.204506065858, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 23:30:00+00:00", "close_date": "2018-01-12 00:05:00+00:00", "trade_duration": 35, "open_rate": 0.02645, "close_rate": 0.026847744360902256, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.780718336483932, "profit_abs": 0.0010000000000000148}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 23:55:00+00:00", "close_date": "2018-01-12 01:15:00+00:00", "trade_duration": 80, "open_rate": 0.048, "close_rate": 0.04824060150375939, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0833333333333335, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-12 21:15:00+00:00", "close_date": "2018-01-12 21:40:00+00:00", "trade_duration": 25, "open_rate": 4.692e-05, "close_rate": 4.809593984962405e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2131.287297527707, "profit_abs": 0.001999999999999974}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 00:55:00+00:00", "close_date": "2018-01-13 06:20:00+00:00", "trade_duration": 325, "open_rate": 0.00256966, "close_rate": 0.0025825405012531327, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.91565421106294, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 10:55:00+00:00", "close_date": "2018-01-13 11:35:00+00:00", "trade_duration": 40, "open_rate": 6.262e-05, "close_rate": 6.293388471177944e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1596.933886937081, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 13:05:00+00:00", "close_date": "2018-01-15 14:10:00+00:00", "trade_duration": 2945, "open_rate": 4.73e-05, "close_rate": 4.753709273182957e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2114.1649048625795, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:30:00+00:00", "close_date": "2018-01-13 14:45:00+00:00", "trade_duration": 75, "open_rate": 6.063e-05, "close_rate": 6.0933909774436085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1649.348507339601, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:40:00+00:00", "close_date": "2018-01-13 23:30:00+00:00", "trade_duration": 590, "open_rate": 0.00011082, "close_rate": 0.00011137548872180448, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 902.3641941887746, "profit_abs": -2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 15:15:00+00:00", "close_date": "2018-01-13 15:55:00+00:00", "trade_duration": 40, "open_rate": 5.93e-05, "close_rate": 5.9597243107769415e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1686.3406408094436, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 16:30:00+00:00", "close_date": "2018-01-13 17:10:00+00:00", "trade_duration": 40, "open_rate": 0.04850003, "close_rate": 0.04874313791979949, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0618543947292407, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 22:05:00+00:00", "close_date": "2018-01-14 06:25:00+00:00", "trade_duration": 500, "open_rate": 0.09825019, "close_rate": 0.09874267215538848, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0178097365511456, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 00:20:00+00:00", "close_date": "2018-01-14 22:55:00+00:00", "trade_duration": 1355, "open_rate": 6.018e-05, "close_rate": 6.048165413533834e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1661.681621801263, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 12:45:00+00:00", "close_date": "2018-01-14 13:25:00+00:00", "trade_duration": 40, "open_rate": 0.09758999, "close_rate": 0.0980791628822055, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.024695258191952, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-14 15:30:00+00:00", "close_date": "2018-01-14 16:00:00+00:00", "trade_duration": 30, "open_rate": 0.00311, "close_rate": 0.0031567669172932328, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.154340836012864, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 20:45:00+00:00", "close_date": "2018-01-14 22:15:00+00:00", "trade_duration": 90, "open_rate": 0.00312401, "close_rate": 0.003139669197994987, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.010140812609436, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 23:35:00+00:00", "close_date": "2018-01-15 00:30:00+00:00", "trade_duration": 55, "open_rate": 0.0174679, "close_rate": 0.017555458395989976, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.724786608579165, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 23:45:00+00:00", "close_date": "2018-01-15 00:25:00+00:00", "trade_duration": 40, "open_rate": 0.07346846, "close_rate": 0.07383672295739348, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.3611282991367997, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 02:25:00+00:00", "close_date": "2018-01-15 03:05:00+00:00", "trade_duration": 40, "open_rate": 0.097994, "close_rate": 0.09848519799498744, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.020470641059657, "profit_abs": -2.7755575615628914e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 07:20:00+00:00", "close_date": "2018-01-15 08:00:00+00:00", "trade_duration": 40, "open_rate": 0.09659, "close_rate": 0.09707416040100247, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0353038616834043, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-15 08:20:00+00:00", "close_date": "2018-01-15 08:55:00+00:00", "trade_duration": 35, "open_rate": 9.987e-05, "close_rate": 0.00010137180451127818, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1001.3016921998599, "profit_abs": 0.0010000000000000009}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-15 12:10:00+00:00", "close_date": "2018-01-16 02:50:00+00:00", "trade_duration": 880, "open_rate": 0.0948969, "close_rate": 0.09537257368421052, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0537752023511833, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:10:00+00:00", "close_date": "2018-01-15 17:40:00+00:00", "trade_duration": 210, "open_rate": 0.071, "close_rate": 0.07135588972431077, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4084507042253522, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:30:00+00:00", "close_date": "2018-01-15 15:10:00+00:00", "trade_duration": 40, "open_rate": 0.04600501, "close_rate": 0.046235611553884705, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.173676301776698, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:10:00+00:00", "close_date": "2018-01-15 19:25:00+00:00", "trade_duration": 75, "open_rate": 9.438e-05, "close_rate": 9.485308270676693e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1059.5465140919687, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:35:00+00:00", "close_date": "2018-01-15 19:15:00+00:00", "trade_duration": 40, "open_rate": 0.03040001, "close_rate": 0.030552391002506264, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.2894726021471703, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-15 20:25:00+00:00", "close_date": "2018-01-16 08:25:00+00:00", "trade_duration": 720, "open_rate": 5.837e-05, "close_rate": 5.2533e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1713.2088401576154, "profit_abs": -0.010474999999999984}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 20:40:00+00:00", "close_date": "2018-01-15 22:00:00+00:00", "trade_duration": 80, "open_rate": 0.046036, "close_rate": 0.04626675689223057, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1722130506560084, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 00:30:00+00:00", "close_date": "2018-01-16 01:10:00+00:00", "trade_duration": 40, "open_rate": 0.0028685, "close_rate": 0.0028828784461152877, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 34.86142583231654, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 01:15:00+00:00", "close_date": "2018-01-16 02:35:00+00:00", "trade_duration": 80, "open_rate": 0.06731755, "close_rate": 0.0676549813283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4854967241083492, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 07:45:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 55, "open_rate": 0.09217614, "close_rate": 0.09263817578947368, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0848794492804754, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:55:00+00:00", "trade_duration": 20, "open_rate": 0.0165, "close_rate": 0.016913533834586467, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.0606060606060606, "profit_abs": 0.0020000000000000018}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 5, "open_rate": 7.953e-05, "close_rate": 8.311781954887218e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1257.387149503332, "profit_abs": 0.00399999999999999}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 08:45:00+00:00", "close_date": "2018-01-16 09:50:00+00:00", "trade_duration": 65, "open_rate": 0.045202, "close_rate": 0.04542857644110275, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2122914915269236, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:45:00+00:00", "trade_duration": 30, "open_rate": 5.248e-05, "close_rate": 5.326917293233082e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1905.487804878049, "profit_abs": 0.0010000000000000009}, {"pair": "XMR/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:55:00+00:00", "trade_duration": 40, "open_rate": 0.02892318, "close_rate": 0.02906815834586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.457434486802627, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 09:50:00+00:00", "close_date": "2018-01-16 10:10:00+00:00", "trade_duration": 20, "open_rate": 5.158e-05, "close_rate": 5.287273182957392e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1938.735944164405, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:35:00+00:00", "trade_duration": 30, "open_rate": 0.02828232, "close_rate": 0.02870761804511278, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5357778286929786, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:40:00+00:00", "trade_duration": 35, "open_rate": 0.04357584, "close_rate": 0.044231115789473675, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.294849623093898, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 13:45:00+00:00", "close_date": "2018-01-16 14:20:00+00:00", "trade_duration": 35, "open_rate": 5.362e-05, "close_rate": 5.442631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1864.975755315181, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 17:30:00+00:00", "close_date": "2018-01-16 18:25:00+00:00", "trade_duration": 55, "open_rate": 5.302e-05, "close_rate": 5.328576441102756e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.0807242549984, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:45:00+00:00", "trade_duration": 30, "open_rate": 0.09129999, "close_rate": 0.09267292218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0952903718828448, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:35:00+00:00", "trade_duration": 20, "open_rate": 3.808e-05, "close_rate": 3.903438596491228e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2626.0504201680674, "profit_abs": 0.0020000000000000018}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 19:00:00+00:00", "close_date": "2018-01-16 19:30:00+00:00", "trade_duration": 30, "open_rate": 0.02811012, "close_rate": 0.028532828571428567, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.557437677249333, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 60, "open_rate": 0.00258379, "close_rate": 0.002325411, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.702835756775904, "profit_abs": -0.010474999999999984}, {"pair": "NXT/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 80, "open_rate": 2.559e-05, "close_rate": 2.3031e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3907.7764751856193, "profit_abs": -0.010474999999999998}, {"pair": "TRX/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:35:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 50, "open_rate": 7.62e-05, "close_rate": 6.858e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1312.3359580052495, "profit_abs": -0.010474999999999984}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:35:00+00:00", "trade_duration": 5, "open_rate": 0.00229844, "close_rate": 0.002402129022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 43.507770487809125, "profit_abs": 0.004000000000000017}, {"pair": "LTC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:40:00+00:00", "trade_duration": 10, "open_rate": 0.0151, "close_rate": 0.015781203007518795, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.622516556291391, "profit_abs": 0.00399999999999999}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:40:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 5, "open_rate": 0.00235676, "close_rate": 0.00246308, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 42.431134269081284, "profit_abs": 0.0040000000000000036}, {"pair": "DASH/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 22:45:00+00:00", "close_date": "2018-01-16 23:05:00+00:00", "trade_duration": 20, "open_rate": 0.0630692, "close_rate": 0.06464988170426066, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.585559988076589, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:50:00+00:00", "close_date": "2018-01-16 22:55:00+00:00", "trade_duration": 5, "open_rate": 2.2e-05, "close_rate": 2.299248120300751e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 4545.454545454546, "profit_abs": 0.003999999999999976}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-17 03:30:00+00:00", "close_date": "2018-01-17 04:00:00+00:00", "trade_duration": 30, "open_rate": 4.974e-05, "close_rate": 5.048796992481203e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2010.454362685967, "profit_abs": 0.0010000000000000009}, {"pair": "TRX/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-17 03:55:00+00:00", "close_date": "2018-01-17 04:15:00+00:00", "trade_duration": 20, "open_rate": 7.108e-05, "close_rate": 7.28614536340852e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1406.8655036578502, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 09:35:00+00:00", "close_date": "2018-01-17 10:15:00+00:00", "trade_duration": 40, "open_rate": 0.04327, "close_rate": 0.04348689223057644, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.3110700254217704, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:20:00+00:00", "close_date": "2018-01-17 17:00:00+00:00", "trade_duration": 400, "open_rate": 4.997e-05, "close_rate": 5.022047619047618e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2001.2007204322595, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:25:00+00:00", "trade_duration": 55, "open_rate": 0.06836818, "close_rate": 0.06871087764411027, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4626687444363737, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:10:00+00:00", "trade_duration": 40, "open_rate": 3.63e-05, "close_rate": 3.648195488721804e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2754.8209366391184, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:30:00+00:00", "close_date": "2018-01-17 22:05:00+00:00", "trade_duration": 575, "open_rate": 0.0281, "close_rate": 0.02824085213032581, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5587188612099645, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:35:00+00:00", "close_date": "2018-01-17 16:55:00+00:00", "trade_duration": 260, "open_rate": 0.08651001, "close_rate": 0.08694364413533832, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1559355963546878, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 05:00:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 55, "open_rate": 5.633e-05, "close_rate": 5.6612355889724306e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1775.2529735487308, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 05:20:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 35, "open_rate": 0.06988494, "close_rate": 0.07093584135338346, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.430923457900944, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 07:35:00+00:00", "close_date": "2018-01-18 08:15:00+00:00", "trade_duration": 40, "open_rate": 5.545e-05, "close_rate": 5.572794486215538e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1803.4265103697026, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 09:00:00+00:00", "close_date": "2018-01-18 09:40:00+00:00", "trade_duration": 40, "open_rate": 0.01633527, "close_rate": 0.016417151052631574, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.121723118136401, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 16:40:00+00:00", "close_date": "2018-01-18 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.00269734, "close_rate": 0.002710860501253133, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.073561360451414, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-18 18:05:00+00:00", "close_date": "2018-01-18 18:30:00+00:00", "trade_duration": 25, "open_rate": 4.475e-05, "close_rate": 4.587155388471177e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2234.63687150838, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 18:25:00+00:00", "close_date": "2018-01-18 18:55:00+00:00", "trade_duration": 30, "open_rate": 2.79e-05, "close_rate": 2.8319548872180444e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3584.2293906810037, "profit_abs": 0.000999999999999987}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 20:10:00+00:00", "close_date": "2018-01-18 20:50:00+00:00", "trade_duration": 40, "open_rate": 0.04439326, "close_rate": 0.04461578260651629, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2525942001105577, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 21:30:00+00:00", "close_date": "2018-01-19 00:35:00+00:00", "trade_duration": 185, "open_rate": 4.49e-05, "close_rate": 4.51250626566416e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2227.1714922049, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 21:55:00+00:00", "close_date": "2018-01-19 05:05:00+00:00", "trade_duration": 430, "open_rate": 0.02855, "close_rate": 0.028693107769423555, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.502626970227671, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 22:10:00+00:00", "close_date": "2018-01-18 22:50:00+00:00", "trade_duration": 40, "open_rate": 5.796e-05, "close_rate": 5.8250526315789473e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1725.3278122843342, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 23:50:00+00:00", "close_date": "2018-01-19 00:30:00+00:00", "trade_duration": 40, "open_rate": 0.04340323, "close_rate": 0.04362079005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.303975994413319, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-19 16:45:00+00:00", "close_date": "2018-01-19 17:35:00+00:00", "trade_duration": 50, "open_rate": 0.04454455, "close_rate": 0.04476783095238095, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.244943545282195, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:15:00+00:00", "close_date": "2018-01-19 19:55:00+00:00", "trade_duration": 160, "open_rate": 5.62e-05, "close_rate": 5.648170426065162e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1779.3594306049824, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:20:00+00:00", "close_date": "2018-01-19 20:15:00+00:00", "trade_duration": 175, "open_rate": 4.339e-05, "close_rate": 4.360749373433584e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2304.6784973496196, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-20 04:45:00+00:00", "close_date": "2018-01-20 17:35:00+00:00", "trade_duration": 770, "open_rate": 0.0001009, "close_rate": 0.00010140576441102755, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 991.0802775024778, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 15:15:00+00:00", "trade_duration": 625, "open_rate": 0.00270505, "close_rate": 0.002718609147869674, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.96789338459548, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 07:00:00+00:00", "trade_duration": 130, "open_rate": 0.03000002, "close_rate": 0.030150396040100245, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.3333311111125927, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 09:00:00+00:00", "close_date": "2018-01-20 09:40:00+00:00", "trade_duration": 40, "open_rate": 5.46e-05, "close_rate": 5.4873684210526304e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1831.5018315018317, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-20 18:25:00+00:00", "close_date": "2018-01-25 03:50:00+00:00", "trade_duration": 6325, "open_rate": 0.03082222, "close_rate": 0.027739998, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.244412634781012, "profit_abs": -0.010474999999999998}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 22:25:00+00:00", "close_date": "2018-01-20 23:15:00+00:00", "trade_duration": 50, "open_rate": 0.08969999, "close_rate": 0.09014961401002504, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1148273260677064, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 02:50:00+00:00", "close_date": "2018-01-21 14:30:00+00:00", "trade_duration": 700, "open_rate": 0.01632501, "close_rate": 0.01640683962406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.125570520324337, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 10:20:00+00:00", "close_date": "2018-01-21 11:00:00+00:00", "trade_duration": 40, "open_rate": 0.070538, "close_rate": 0.07089157393483708, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.417675579120474, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 15:50:00+00:00", "close_date": "2018-01-21 18:45:00+00:00", "trade_duration": 175, "open_rate": 5.301e-05, "close_rate": 5.327571428571427e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.4365214110546, "profit_abs": -2.7755575615628914e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 16:20:00+00:00", "close_date": "2018-01-21 17:00:00+00:00", "trade_duration": 40, "open_rate": 3.955e-05, "close_rate": 3.9748245614035085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2528.4450063211125, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:45:00+00:00", "trade_duration": 30, "open_rate": 0.00258505, "close_rate": 0.002623922932330827, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.6839712964933, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:55:00+00:00", "trade_duration": 40, "open_rate": 3.903e-05, "close_rate": 3.922563909774435e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2562.1316935690497, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 00:35:00+00:00", "close_date": "2018-01-22 10:35:00+00:00", "trade_duration": 600, "open_rate": 5.236e-05, "close_rate": 5.262245614035087e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1909.8548510313217, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 01:30:00+00:00", "close_date": "2018-01-22 02:10:00+00:00", "trade_duration": 40, "open_rate": 9.028e-05, "close_rate": 9.07325313283208e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1107.6650420912717, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 12:25:00+00:00", "close_date": "2018-01-22 14:35:00+00:00", "trade_duration": 130, "open_rate": 0.002687, "close_rate": 0.002700468671679198, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.21622627465575, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 13:15:00+00:00", "close_date": "2018-01-22 13:55:00+00:00", "trade_duration": 40, "open_rate": 4.168e-05, "close_rate": 4.188892230576441e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2399.232245681382, "profit_abs": 1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-22 14:00:00+00:00", "close_date": "2018-01-22 14:30:00+00:00", "trade_duration": 30, "open_rate": 8.821e-05, "close_rate": 8.953646616541353e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1133.6583153837435, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 15:55:00+00:00", "close_date": "2018-01-22 16:40:00+00:00", "trade_duration": 45, "open_rate": 5.172e-05, "close_rate": 5.1979248120300745e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1933.4880123743235, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-22 16:05:00+00:00", "close_date": "2018-01-22 16:25:00+00:00", "trade_duration": 20, "open_rate": 3.026e-05, "close_rate": 3.101839598997494e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3304.692663582287, "profit_abs": 0.0020000000000000157}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 19:50:00+00:00", "close_date": "2018-01-23 00:10:00+00:00", "trade_duration": 260, "open_rate": 0.07064, "close_rate": 0.07099408521303258, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.415628539071348, "profit_abs": 1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 21:25:00+00:00", "close_date": "2018-01-22 22:05:00+00:00", "trade_duration": 40, "open_rate": 0.01644483, "close_rate": 0.01652726022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.080938507725528, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-23 00:05:00+00:00", "close_date": "2018-01-23 00:35:00+00:00", "trade_duration": 30, "open_rate": 4.331e-05, "close_rate": 4.3961278195488714e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2308.935580697299, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-23 01:50:00+00:00", "close_date": "2018-01-23 02:15:00+00:00", "trade_duration": 25, "open_rate": 3.2e-05, "close_rate": 3.2802005012531326e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3125.0000000000005, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 04:25:00+00:00", "close_date": "2018-01-23 05:15:00+00:00", "trade_duration": 50, "open_rate": 0.09167706, "close_rate": 0.09213659413533835, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0907854156754153, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 07:35:00+00:00", "close_date": "2018-01-23 09:00:00+00:00", "trade_duration": 85, "open_rate": 0.0692498, "close_rate": 0.06959691679197995, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4440474918339115, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 10:50:00+00:00", "close_date": "2018-01-23 13:05:00+00:00", "trade_duration": 135, "open_rate": 3.182e-05, "close_rate": 3.197949874686716e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3142.677561282213, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 11:05:00+00:00", "close_date": "2018-01-23 16:05:00+00:00", "trade_duration": 300, "open_rate": 0.04088, "close_rate": 0.04108491228070175, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4461839530332683, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 14:55:00+00:00", "close_date": "2018-01-23 15:35:00+00:00", "trade_duration": 40, "open_rate": 5.15e-05, "close_rate": 5.175814536340851e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1941.747572815534, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 16:35:00+00:00", "close_date": "2018-01-24 00:05:00+00:00", "trade_duration": 450, "open_rate": 0.09071698, "close_rate": 0.09117170170426064, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1023294646713329, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 17:25:00+00:00", "close_date": "2018-01-23 18:45:00+00:00", "trade_duration": 80, "open_rate": 3.128e-05, "close_rate": 3.1436791979949865e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3196.9309462915603, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 20:15:00+00:00", "close_date": "2018-01-23 22:00:00+00:00", "trade_duration": 105, "open_rate": 9.555e-05, "close_rate": 9.602894736842104e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1046.5724751439038, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 22:30:00+00:00", "close_date": "2018-01-23 23:10:00+00:00", "trade_duration": 40, "open_rate": 0.04080001, "close_rate": 0.0410045213283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.450979791426522, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 23:50:00+00:00", "close_date": "2018-01-24 03:35:00+00:00", "trade_duration": 225, "open_rate": 5.163e-05, "close_rate": 5.18887969924812e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1936.8584156498162, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 00:20:00+00:00", "close_date": "2018-01-24 01:50:00+00:00", "trade_duration": 90, "open_rate": 0.04040781, "close_rate": 0.04061035541353383, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.474769110228938, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 06:45:00+00:00", "close_date": "2018-01-24 07:25:00+00:00", "trade_duration": 40, "open_rate": 5.132e-05, "close_rate": 5.157724310776942e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1948.5580670303975, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-24 14:15:00+00:00", "close_date": "2018-01-24 14:25:00+00:00", "trade_duration": 10, "open_rate": 5.198e-05, "close_rate": 5.432496240601503e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1923.8168526356292, "profit_abs": 0.0040000000000000036}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 14:50:00+00:00", "close_date": "2018-01-24 16:35:00+00:00", "trade_duration": 105, "open_rate": 3.054e-05, "close_rate": 3.069308270676692e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3274.3942370661425, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 15:10:00+00:00", "close_date": "2018-01-24 16:15:00+00:00", "trade_duration": 65, "open_rate": 9.263e-05, "close_rate": 9.309431077694236e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1079.5638562020945, "profit_abs": 2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 22:40:00+00:00", "close_date": "2018-01-24 23:25:00+00:00", "trade_duration": 45, "open_rate": 5.514e-05, "close_rate": 5.54163909774436e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1813.5654697134569, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 00:50:00+00:00", "close_date": "2018-01-25 01:30:00+00:00", "trade_duration": 40, "open_rate": 4.921e-05, "close_rate": 4.9456666666666664e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.1072952651903, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 08:15:00+00:00", "close_date": "2018-01-25 12:15:00+00:00", "trade_duration": 240, "open_rate": 0.0026, "close_rate": 0.002613032581453634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.46153846153847, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 10:25:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 350, "open_rate": 0.02799871, "close_rate": 0.028139054411027563, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.571593119825878, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 11:00:00+00:00", "close_date": "2018-01-25 11:45:00+00:00", "trade_duration": 45, "open_rate": 0.04078902, "close_rate": 0.0409934762406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4516401717913303, "profit_abs": -1.3877787807814457e-17}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:05:00+00:00", "close_date": "2018-01-25 13:45:00+00:00", "trade_duration": 40, "open_rate": 2.89e-05, "close_rate": 2.904486215538847e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3460.2076124567475, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:20:00+00:00", "close_date": "2018-01-25 14:05:00+00:00", "trade_duration": 45, "open_rate": 0.041103, "close_rate": 0.04130903007518797, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4329124394813033, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-25 15:45:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 30, "open_rate": 5.428e-05, "close_rate": 5.509624060150376e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1842.2991893883568, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 17:45:00+00:00", "close_date": "2018-01-25 23:15:00+00:00", "trade_duration": 330, "open_rate": 5.414e-05, "close_rate": 5.441137844611528e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1847.063169560399, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 21:15:00+00:00", "close_date": "2018-01-25 21:55:00+00:00", "trade_duration": 40, "open_rate": 0.04140777, "close_rate": 0.0416153277443609, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.415005686130888, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 02:05:00+00:00", "close_date": "2018-01-26 02:45:00+00:00", "trade_duration": 40, "open_rate": 0.00254309, "close_rate": 0.002555837318295739, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.32224183965177, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 02:55:00+00:00", "close_date": "2018-01-26 15:10:00+00:00", "trade_duration": 735, "open_rate": 5.607e-05, "close_rate": 5.6351052631578935e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1783.4849295523454, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 06:10:00+00:00", "close_date": "2018-01-26 09:25:00+00:00", "trade_duration": 195, "open_rate": 0.00253806, "close_rate": 0.0025507821052631577, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.400171784748984, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 07:25:00+00:00", "close_date": "2018-01-26 09:55:00+00:00", "trade_duration": 150, "open_rate": 0.0415, "close_rate": 0.04170802005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4096385542168677, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-26 09:55:00+00:00", "close_date": "2018-01-26 10:25:00+00:00", "trade_duration": 30, "open_rate": 5.321e-05, "close_rate": 5.401015037593984e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1879.3459875963165, "profit_abs": 0.000999999999999987}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 16:05:00+00:00", "close_date": "2018-01-26 16:45:00+00:00", "trade_duration": 40, "open_rate": 0.02772046, "close_rate": 0.02785940967418546, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.6074437437185387, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 23:35:00+00:00", "close_date": "2018-01-27 00:15:00+00:00", "trade_duration": 40, "open_rate": 0.09461341, "close_rate": 0.09508766268170424, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0569326272036914, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 00:35:00+00:00", "close_date": "2018-01-27 01:30:00+00:00", "trade_duration": 55, "open_rate": 5.615e-05, "close_rate": 5.643145363408521e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1780.9439002671415, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.07877175, "open_date": "2018-01-27 00:45:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 4560, "open_rate": 5.556e-05, "close_rate": 5.144e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1799.8560115190785, "profit_abs": -0.007896868250539965}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 02:30:00+00:00", "close_date": "2018-01-27 11:25:00+00:00", "trade_duration": 535, "open_rate": 0.06900001, "close_rate": 0.06934587471177944, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492751522789635, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 06:25:00+00:00", "close_date": "2018-01-27 07:05:00+00:00", "trade_duration": 40, "open_rate": 0.09449985, "close_rate": 0.0949735334586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.058202737887944, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.04815133, "open_date": "2018-01-27 09:40:00+00:00", "close_date": "2018-01-30 04:40:00+00:00", "trade_duration": 4020, "open_rate": 0.0410697, "close_rate": 0.03928809, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4348850855983852, "profit_abs": -0.004827170578309559}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 11:45:00+00:00", "close_date": "2018-01-27 12:30:00+00:00", "trade_duration": 45, "open_rate": 0.0285, "close_rate": 0.02864285714285714, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5087719298245617, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 12:35:00+00:00", "close_date": "2018-01-27 15:25:00+00:00", "trade_duration": 170, "open_rate": 0.02866372, "close_rate": 0.02880739779448621, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.4887307020861216, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 15:50:00+00:00", "close_date": "2018-01-27 16:50:00+00:00", "trade_duration": 60, "open_rate": 0.095381, "close_rate": 0.09585910025062656, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0484268355332824, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 17:05:00+00:00", "close_date": "2018-01-27 17:45:00+00:00", "trade_duration": 40, "open_rate": 0.06759092, "close_rate": 0.06792972160401002, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4794886650455417, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 23:40:00+00:00", "close_date": "2018-01-28 01:05:00+00:00", "trade_duration": 85, "open_rate": 0.00258501, "close_rate": 0.002597967443609022, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.684569885609726, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 02:25:00+00:00", "close_date": "2018-01-28 08:10:00+00:00", "trade_duration": 345, "open_rate": 0.06698502, "close_rate": 0.0673207845112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4928710926711672, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 10:25:00+00:00", "close_date": "2018-01-28 16:30:00+00:00", "trade_duration": 365, "open_rate": 0.0677177, "close_rate": 0.06805713709273183, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4767187899175547, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-28 20:35:00+00:00", "close_date": "2018-01-28 21:35:00+00:00", "trade_duration": 60, "open_rate": 5.215e-05, "close_rate": 5.2411403508771925e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1917.5455417066157, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-28 22:00:00+00:00", "close_date": "2018-01-28 22:30:00+00:00", "trade_duration": 30, "open_rate": 0.00273809, "close_rate": 0.002779264285714285, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.5218089982433, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-29 00:00:00+00:00", "close_date": "2018-01-29 00:30:00+00:00", "trade_duration": 30, "open_rate": 0.00274632, "close_rate": 0.002787618045112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.412362725392526, "profit_abs": 0.0010000000000000148}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-29 02:15:00+00:00", "close_date": "2018-01-29 03:00:00+00:00", "trade_duration": 45, "open_rate": 0.01622478, "close_rate": 0.016306107218045113, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.163411768911504, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 03:05:00+00:00", "close_date": "2018-01-29 03:45:00+00:00", "trade_duration": 40, "open_rate": 0.069, "close_rate": 0.06934586466165413, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492753623188406, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 05:20:00+00:00", "close_date": "2018-01-29 06:55:00+00:00", "trade_duration": 95, "open_rate": 8.755e-05, "close_rate": 8.798884711779448e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1142.204454597373, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 07:00:00+00:00", "close_date": "2018-01-29 19:25:00+00:00", "trade_duration": 745, "open_rate": 0.06825763, "close_rate": 0.06859977350877192, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4650376815016872, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 19:45:00+00:00", "close_date": "2018-01-29 20:25:00+00:00", "trade_duration": 40, "open_rate": 0.06713892, "close_rate": 0.06747545593984962, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4894490408841845, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0199116, "open_date": "2018-01-29 23:30:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 315, "open_rate": 8.934e-05, "close_rate": 8.8e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1119.3194537721067, "profit_abs": -0.0019961383478844796}], "results_per_pair": [{"key": "TRX/BTC", "trades": 15, "profit_mean": 0.0023467073333333323, "profit_mean_pct": 0.23467073333333321, "profit_sum": 0.035200609999999986, "profit_sum_pct": 3.5200609999999988, "profit_total_abs": 0.0035288616521155086, "profit_total_pct": 1.1733536666666662, "duration_avg": "2:28:00", "wins": 9, "draws": 2, "losses": 4}, {"key": "ADA/BTC", "trades": 29, "profit_mean": -0.0011598141379310352, "profit_mean_pct": -0.11598141379310352, "profit_sum": -0.03363461000000002, "profit_sum_pct": -3.3634610000000023, "profit_total_abs": -0.0033718682505400333, "profit_total_pct": -1.1211536666666675, "duration_avg": "5:35:00", "wins": 9, "draws": 11, "losses": 9}, {"key": "XLM/BTC", "trades": 21, "profit_mean": 0.0026243899999999994, "profit_mean_pct": 0.2624389999999999, "profit_sum": 0.05511218999999999, "profit_sum_pct": 5.511218999999999, "profit_total_abs": 0.005525000000000002, "profit_total_pct": 1.8370729999999995, "duration_avg": "3:21:00", "wins": 12, "draws": 3, "losses": 6}, {"key": "ETH/BTC", "trades": 21, "profit_mean": 0.0009500057142857142, "profit_mean_pct": 0.09500057142857142, "profit_sum": 0.01995012, "profit_sum_pct": 1.9950119999999998, "profit_total_abs": 0.0019999999999999463, "profit_total_pct": 0.6650039999999999, "duration_avg": "2:17:00", "wins": 5, "draws": 10, "losses": 6}, {"key": "XMR/BTC", "trades": 16, "profit_mean": -0.0027899012500000007, "profit_mean_pct": -0.2789901250000001, "profit_sum": -0.04463842000000001, "profit_sum_pct": -4.463842000000001, "profit_total_abs": -0.0044750000000000345, "profit_total_pct": -1.4879473333333337, "duration_avg": "8:41:00", "wins": 6, "draws": 5, "losses": 5}, {"key": "ZEC/BTC", "trades": 21, "profit_mean": -0.00039290904761904774, "profit_mean_pct": -0.03929090476190478, "profit_sum": -0.008251090000000003, "profit_sum_pct": -0.8251090000000003, "profit_total_abs": -0.000827170578309569, "profit_total_pct": -0.27503633333333344, "duration_avg": "4:17:00", "wins": 8, "draws": 7, "losses": 6}, {"key": "NXT/BTC", "trades": 12, "profit_mean": -0.0012261025000000006, "profit_mean_pct": -0.12261025000000006, "profit_sum": -0.014713230000000008, "profit_sum_pct": -1.4713230000000008, "profit_total_abs": -0.0014750000000000874, "profit_total_pct": -0.4904410000000003, "duration_avg": "0:57:00", "wins": 4, "draws": 3, "losses": 5}, {"key": "LTC/BTC", "trades": 8, "profit_mean": 0.00748129625, "profit_mean_pct": 0.748129625, "profit_sum": 0.05985037, "profit_sum_pct": 5.985037, "profit_total_abs": 0.006000000000000019, "profit_total_pct": 1.9950123333333334, "duration_avg": "1:59:00", "wins": 5, "draws": 2, "losses": 1}, {"key": "ETC/BTC", "trades": 20, "profit_mean": 0.0022568569999999997, "profit_mean_pct": 0.22568569999999996, "profit_sum": 0.04513713999999999, "profit_sum_pct": 4.513713999999999, "profit_total_abs": 0.004525000000000001, "profit_total_pct": 1.504571333333333, "duration_avg": "1:45:00", "wins": 11, "draws": 4, "losses": 5}, {"key": "DASH/BTC", "trades": 16, "profit_mean": 0.0018703237499999997, "profit_mean_pct": 0.18703237499999997, "profit_sum": 0.029925179999999996, "profit_sum_pct": 2.9925179999999996, "profit_total_abs": 0.002999999999999961, "profit_total_pct": 0.9975059999999999, "duration_avg": "3:03:00", "wins": 4, "draws": 7, "losses": 5}, {"key": "TOTAL", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}], "sell_reason_summary": [{"sell_reason": "roi", "trades": 170, "wins": 73, "draws": 54, "losses": 43, "profit_mean": 0.005398268352941177, "profit_mean_pct": 0.54, "profit_sum": 0.91770562, "profit_sum_pct": 91.77, "profit_total_abs": 0.09199999999999964, "profit_pct_total": 30.59}, {"sell_reason": "stop_loss", "trades": 6, "wins": 0, "draws": 0, "losses": 6, "profit_mean": -0.10448878000000002, "profit_mean_pct": -10.45, "profit_sum": -0.6269326800000001, "profit_sum_pct": -62.69, "profit_total_abs": -0.06284999999999992, "profit_pct_total": -20.9}, {"sell_reason": "force_sell", "trades": 3, "wins": 0, "draws": 0, "losses": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.89, "profit_sum": -0.14683468, "profit_sum_pct": -14.68, "profit_total_abs": -0.014720177176734003, "profit_pct_total": -4.89}], "left_open_trades": [{"key": "TRX/BTC", "trades": 1, "profit_mean": -0.0199116, "profit_mean_pct": -1.9911600000000003, "profit_sum": -0.0199116, "profit_sum_pct": -1.9911600000000003, "profit_total_abs": -0.0019961383478844796, "profit_total_pct": -0.6637200000000001, "duration_avg": "5:15:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ADA/BTC", "trades": 1, "profit_mean": -0.07877175, "profit_mean_pct": -7.877175, "profit_sum": -0.07877175, "profit_sum_pct": -7.877175, "profit_total_abs": -0.007896868250539965, "profit_total_pct": -2.625725, "duration_avg": "3 days, 4:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ZEC/BTC", "trades": 1, "profit_mean": -0.04815133, "profit_mean_pct": -4.815133, "profit_sum": -0.04815133, "profit_sum_pct": -4.815133, "profit_total_abs": -0.004827170578309559, "profit_total_pct": -1.6050443333333335, "duration_avg": "2 days, 19:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "TOTAL", "trades": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.894489333333333, "profit_sum": -0.14683468, "profit_sum_pct": -14.683468, "profit_total_abs": -0.014720177176734003, "profit_total_pct": -4.8944893333333335, "duration_avg": "2 days, 1:25:00", "wins": 0, "draws": 0, "losses": 3}], "total_trades": 179, "backtest_start": "2018-01-30 04:45:00+00:00", "backtest_start_ts": 1517287500, "backtest_end": "2018-01-30 04:45:00+00:00", "backtest_end_ts": 1517287500, "backtest_days": 0, "trades_per_day": null, "market_change": 0.25, "stake_amount": 0.1, "max_drawdown": 0.21142322000000008, "drawdown_start": "2018-01-24 14:25:00+00:00", "drawdown_start_ts": 1516803900.0, "drawdown_end": "2018-01-30 04:45:00+00:00", "drawdown_end_ts": 1517287500.0, "pairlist": ["TRX/BTC", "ADA/BTC", "XLM/BTC", "ETH/BTC", "XMR/BTC", "ZEC/BTC","NXT/BTC", "LTC/BTC", "ETC/BTC", "DASH/BTC"]}, "TestStrategy": {"trades": [{"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:20:00+00:00", "trade_duration": 5, "open_rate": 9.64e-05, "close_rate": 0.00010074887218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1037.344398340249, "profit_abs": 0.00399999999999999}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:30:00+00:00", "trade_duration": 15, "open_rate": 4.756e-05, "close_rate": 4.9705563909774425e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2102.6072329688814, "profit_abs": 0.00399999999999999}, {"pair": "XLM/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:35:00+00:00", "trade_duration": 10, "open_rate": 3.339e-05, "close_rate": 3.489631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2994.908655286014, "profit_abs": 0.0040000000000000036}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:40:00+00:00", "trade_duration": 15, "open_rate": 9.696e-05, "close_rate": 0.00010133413533834584, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1031.3531353135315, "profit_abs": 0.00399999999999999}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 07:35:00+00:00", "close_date": "2018-01-10 08:35:00+00:00", "trade_duration": 60, "open_rate": 0.0943, "close_rate": 0.09477268170426063, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0604453870625663, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 07:40:00+00:00", "close_date": "2018-01-10 08:10:00+00:00", "trade_duration": 30, "open_rate": 0.02719607, "close_rate": 0.02760503345864661, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.677001860930642, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 08:15:00+00:00", "close_date": "2018-01-10 09:55:00+00:00", "trade_duration": 100, "open_rate": 0.04634952, "close_rate": 0.046581848421052625, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1575196463739, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 14:45:00+00:00", "close_date": "2018-01-10 15:50:00+00:00", "trade_duration": 65, "open_rate": 3.066e-05, "close_rate": 3.081368421052631e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3261.5786040443577, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 16:35:00+00:00", "close_date": "2018-01-10 17:15:00+00:00", "trade_duration": 40, "open_rate": 0.0168999, "close_rate": 0.016984611278195488, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.917194776300452, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 16:40:00+00:00", "close_date": "2018-01-10 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.09132568, "close_rate": 0.0917834528320802, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0949822656672252, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 18:50:00+00:00", "close_date": "2018-01-10 19:45:00+00:00", "trade_duration": 55, "open_rate": 0.08898003, "close_rate": 0.08942604518796991, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1238476768326557, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 22:15:00+00:00", "close_date": "2018-01-10 23:00:00+00:00", "trade_duration": 45, "open_rate": 0.08560008, "close_rate": 0.08602915308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1682232072680307, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 22:50:00+00:00", "close_date": "2018-01-10 23:20:00+00:00", "trade_duration": 30, "open_rate": 0.00249083, "close_rate": 0.0025282860902255634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 40.147260150231055, "profit_abs": 0.000999999999999987}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 23:15:00+00:00", "close_date": "2018-01-11 00:15:00+00:00", "trade_duration": 60, "open_rate": 3.022e-05, "close_rate": 3.037147869674185e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3309.0668431502318, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-10 23:40:00+00:00", "close_date": "2018-01-11 00:05:00+00:00", "trade_duration": 25, "open_rate": 0.002437, "close_rate": 0.0024980776942355883, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.03405826836274, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 00:00:00+00:00", "close_date": "2018-01-11 00:35:00+00:00", "trade_duration": 35, "open_rate": 0.04771803, "close_rate": 0.04843559436090225, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0956439316543456, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-11 03:40:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 45, "open_rate": 3.651e-05, "close_rate": 3.2859000000000005e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2738.9756231169545, "profit_abs": -0.01047499999999997}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 03:55:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 30, "open_rate": 0.08824105, "close_rate": 0.08956798308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1332594070446804, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 04:00:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 50, "open_rate": 0.00243, "close_rate": 0.002442180451127819, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.1522633744856, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:55:00+00:00", "trade_duration": 25, "open_rate": 0.04545064, "close_rate": 0.046589753784461146, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.200189040242338, "profit_abs": 0.001999999999999988}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 20, "open_rate": 3.372e-05, "close_rate": 3.456511278195488e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2965.599051008304, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:55:00+00:00", "close_date": "2018-01-11 05:15:00+00:00", "trade_duration": 20, "open_rate": 0.02644, "close_rate": 0.02710265664160401, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7821482602118004, "profit_abs": 0.001999999999999988}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:20:00+00:00", "close_date": "2018-01-11 12:00:00+00:00", "trade_duration": 40, "open_rate": 0.08812, "close_rate": 0.08856170426065162, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1348161597821154, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:35:00+00:00", "close_date": "2018-01-11 12:15:00+00:00", "trade_duration": 40, "open_rate": 0.02683577, "close_rate": 0.026970285137844607, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7263696923919087, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 14:00:00+00:00", "close_date": "2018-01-11 14:25:00+00:00", "trade_duration": 25, "open_rate": 4.919e-05, "close_rate": 5.04228320802005e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.9335230737956, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 19:25:00+00:00", "close_date": "2018-01-11 20:35:00+00:00", "trade_duration": 70, "open_rate": 0.08784896, "close_rate": 0.08828930566416039, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1383174029607181, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:35:00+00:00", "close_date": "2018-01-11 23:30:00+00:00", "trade_duration": 55, "open_rate": 5.105e-05, "close_rate": 5.130588972431077e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1958.8638589618022, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:25:00+00:00", "trade_duration": 30, "open_rate": 3.96e-05, "close_rate": 4.019548872180451e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2525.252525252525, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:35:00+00:00", "trade_duration": 40, "open_rate": 2.885e-05, "close_rate": 2.899461152882205e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3466.204506065858, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 23:30:00+00:00", "close_date": "2018-01-12 00:05:00+00:00", "trade_duration": 35, "open_rate": 0.02645, "close_rate": 0.026847744360902256, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.780718336483932, "profit_abs": 0.0010000000000000148}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 23:55:00+00:00", "close_date": "2018-01-12 01:15:00+00:00", "trade_duration": 80, "open_rate": 0.048, "close_rate": 0.04824060150375939, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0833333333333335, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-12 21:15:00+00:00", "close_date": "2018-01-12 21:40:00+00:00", "trade_duration": 25, "open_rate": 4.692e-05, "close_rate": 4.809593984962405e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2131.287297527707, "profit_abs": 0.001999999999999974}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 00:55:00+00:00", "close_date": "2018-01-13 06:20:00+00:00", "trade_duration": 325, "open_rate": 0.00256966, "close_rate": 0.0025825405012531327, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.91565421106294, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 10:55:00+00:00", "close_date": "2018-01-13 11:35:00+00:00", "trade_duration": 40, "open_rate": 6.262e-05, "close_rate": 6.293388471177944e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1596.933886937081, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 13:05:00+00:00", "close_date": "2018-01-15 14:10:00+00:00", "trade_duration": 2945, "open_rate": 4.73e-05, "close_rate": 4.753709273182957e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2114.1649048625795, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:30:00+00:00", "close_date": "2018-01-13 14:45:00+00:00", "trade_duration": 75, "open_rate": 6.063e-05, "close_rate": 6.0933909774436085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1649.348507339601, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:40:00+00:00", "close_date": "2018-01-13 23:30:00+00:00", "trade_duration": 590, "open_rate": 0.00011082, "close_rate": 0.00011137548872180448, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 902.3641941887746, "profit_abs": -2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 15:15:00+00:00", "close_date": "2018-01-13 15:55:00+00:00", "trade_duration": 40, "open_rate": 5.93e-05, "close_rate": 5.9597243107769415e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1686.3406408094436, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 16:30:00+00:00", "close_date": "2018-01-13 17:10:00+00:00", "trade_duration": 40, "open_rate": 0.04850003, "close_rate": 0.04874313791979949, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0618543947292407, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 22:05:00+00:00", "close_date": "2018-01-14 06:25:00+00:00", "trade_duration": 500, "open_rate": 0.09825019, "close_rate": 0.09874267215538848, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0178097365511456, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 00:20:00+00:00", "close_date": "2018-01-14 22:55:00+00:00", "trade_duration": 1355, "open_rate": 6.018e-05, "close_rate": 6.048165413533834e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1661.681621801263, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 12:45:00+00:00", "close_date": "2018-01-14 13:25:00+00:00", "trade_duration": 40, "open_rate": 0.09758999, "close_rate": 0.0980791628822055, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.024695258191952, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-14 15:30:00+00:00", "close_date": "2018-01-14 16:00:00+00:00", "trade_duration": 30, "open_rate": 0.00311, "close_rate": 0.0031567669172932328, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.154340836012864, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 20:45:00+00:00", "close_date": "2018-01-14 22:15:00+00:00", "trade_duration": 90, "open_rate": 0.00312401, "close_rate": 0.003139669197994987, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.010140812609436, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 23:35:00+00:00", "close_date": "2018-01-15 00:30:00+00:00", "trade_duration": 55, "open_rate": 0.0174679, "close_rate": 0.017555458395989976, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.724786608579165, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 23:45:00+00:00", "close_date": "2018-01-15 00:25:00+00:00", "trade_duration": 40, "open_rate": 0.07346846, "close_rate": 0.07383672295739348, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.3611282991367997, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 02:25:00+00:00", "close_date": "2018-01-15 03:05:00+00:00", "trade_duration": 40, "open_rate": 0.097994, "close_rate": 0.09848519799498744, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.020470641059657, "profit_abs": -2.7755575615628914e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 07:20:00+00:00", "close_date": "2018-01-15 08:00:00+00:00", "trade_duration": 40, "open_rate": 0.09659, "close_rate": 0.09707416040100247, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0353038616834043, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-15 08:20:00+00:00", "close_date": "2018-01-15 08:55:00+00:00", "trade_duration": 35, "open_rate": 9.987e-05, "close_rate": 0.00010137180451127818, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1001.3016921998599, "profit_abs": 0.0010000000000000009}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-15 12:10:00+00:00", "close_date": "2018-01-16 02:50:00+00:00", "trade_duration": 880, "open_rate": 0.0948969, "close_rate": 0.09537257368421052, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0537752023511833, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:10:00+00:00", "close_date": "2018-01-15 17:40:00+00:00", "trade_duration": 210, "open_rate": 0.071, "close_rate": 0.07135588972431077, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4084507042253522, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:30:00+00:00", "close_date": "2018-01-15 15:10:00+00:00", "trade_duration": 40, "open_rate": 0.04600501, "close_rate": 0.046235611553884705, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.173676301776698, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:10:00+00:00", "close_date": "2018-01-15 19:25:00+00:00", "trade_duration": 75, "open_rate": 9.438e-05, "close_rate": 9.485308270676693e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1059.5465140919687, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:35:00+00:00", "close_date": "2018-01-15 19:15:00+00:00", "trade_duration": 40, "open_rate": 0.03040001, "close_rate": 0.030552391002506264, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.2894726021471703, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-15 20:25:00+00:00", "close_date": "2018-01-16 08:25:00+00:00", "trade_duration": 720, "open_rate": 5.837e-05, "close_rate": 5.2533e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1713.2088401576154, "profit_abs": -0.010474999999999984}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 20:40:00+00:00", "close_date": "2018-01-15 22:00:00+00:00", "trade_duration": 80, "open_rate": 0.046036, "close_rate": 0.04626675689223057, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1722130506560084, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 00:30:00+00:00", "close_date": "2018-01-16 01:10:00+00:00", "trade_duration": 40, "open_rate": 0.0028685, "close_rate": 0.0028828784461152877, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 34.86142583231654, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 01:15:00+00:00", "close_date": "2018-01-16 02:35:00+00:00", "trade_duration": 80, "open_rate": 0.06731755, "close_rate": 0.0676549813283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4854967241083492, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 07:45:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 55, "open_rate": 0.09217614, "close_rate": 0.09263817578947368, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0848794492804754, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:55:00+00:00", "trade_duration": 20, "open_rate": 0.0165, "close_rate": 0.016913533834586467, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.0606060606060606, "profit_abs": 0.0020000000000000018}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 5, "open_rate": 7.953e-05, "close_rate": 8.311781954887218e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1257.387149503332, "profit_abs": 0.00399999999999999}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 08:45:00+00:00", "close_date": "2018-01-16 09:50:00+00:00", "trade_duration": 65, "open_rate": 0.045202, "close_rate": 0.04542857644110275, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2122914915269236, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:45:00+00:00", "trade_duration": 30, "open_rate": 5.248e-05, "close_rate": 5.326917293233082e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1905.487804878049, "profit_abs": 0.0010000000000000009}, {"pair": "XMR/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:55:00+00:00", "trade_duration": 40, "open_rate": 0.02892318, "close_rate": 0.02906815834586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.457434486802627, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 09:50:00+00:00", "close_date": "2018-01-16 10:10:00+00:00", "trade_duration": 20, "open_rate": 5.158e-05, "close_rate": 5.287273182957392e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1938.735944164405, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:35:00+00:00", "trade_duration": 30, "open_rate": 0.02828232, "close_rate": 0.02870761804511278, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5357778286929786, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:40:00+00:00", "trade_duration": 35, "open_rate": 0.04357584, "close_rate": 0.044231115789473675, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.294849623093898, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 13:45:00+00:00", "close_date": "2018-01-16 14:20:00+00:00", "trade_duration": 35, "open_rate": 5.362e-05, "close_rate": 5.442631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1864.975755315181, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 17:30:00+00:00", "close_date": "2018-01-16 18:25:00+00:00", "trade_duration": 55, "open_rate": 5.302e-05, "close_rate": 5.328576441102756e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.0807242549984, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:45:00+00:00", "trade_duration": 30, "open_rate": 0.09129999, "close_rate": 0.09267292218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0952903718828448, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:35:00+00:00", "trade_duration": 20, "open_rate": 3.808e-05, "close_rate": 3.903438596491228e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2626.0504201680674, "profit_abs": 0.0020000000000000018}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 19:00:00+00:00", "close_date": "2018-01-16 19:30:00+00:00", "trade_duration": 30, "open_rate": 0.02811012, "close_rate": 0.028532828571428567, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.557437677249333, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 60, "open_rate": 0.00258379, "close_rate": 0.002325411, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.702835756775904, "profit_abs": -0.010474999999999984}, {"pair": "NXT/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 80, "open_rate": 2.559e-05, "close_rate": 2.3031e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3907.7764751856193, "profit_abs": -0.010474999999999998}, {"pair": "TRX/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:35:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 50, "open_rate": 7.62e-05, "close_rate": 6.858e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1312.3359580052495, "profit_abs": -0.010474999999999984}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:35:00+00:00", "trade_duration": 5, "open_rate": 0.00229844, "close_rate": 0.002402129022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 43.507770487809125, "profit_abs": 0.004000000000000017}, {"pair": "LTC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:40:00+00:00", "trade_duration": 10, "open_rate": 0.0151, "close_rate": 0.015781203007518795, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.622516556291391, "profit_abs": 0.00399999999999999}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:40:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 5, "open_rate": 0.00235676, "close_rate": 0.00246308, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 42.431134269081284, "profit_abs": 0.0040000000000000036}, {"pair": "DASH/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 22:45:00+00:00", "close_date": "2018-01-16 23:05:00+00:00", "trade_duration": 20, "open_rate": 0.0630692, "close_rate": 0.06464988170426066, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.585559988076589, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:50:00+00:00", "close_date": "2018-01-16 22:55:00+00:00", "trade_duration": 5, "open_rate": 2.2e-05, "close_rate": 2.299248120300751e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 4545.454545454546, "profit_abs": 0.003999999999999976}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-17 03:30:00+00:00", "close_date": "2018-01-17 04:00:00+00:00", "trade_duration": 30, "open_rate": 4.974e-05, "close_rate": 5.048796992481203e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2010.454362685967, "profit_abs": 0.0010000000000000009}, {"pair": "TRX/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-17 03:55:00+00:00", "close_date": "2018-01-17 04:15:00+00:00", "trade_duration": 20, "open_rate": 7.108e-05, "close_rate": 7.28614536340852e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1406.8655036578502, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 09:35:00+00:00", "close_date": "2018-01-17 10:15:00+00:00", "trade_duration": 40, "open_rate": 0.04327, "close_rate": 0.04348689223057644, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.3110700254217704, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:20:00+00:00", "close_date": "2018-01-17 17:00:00+00:00", "trade_duration": 400, "open_rate": 4.997e-05, "close_rate": 5.022047619047618e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2001.2007204322595, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:25:00+00:00", "trade_duration": 55, "open_rate": 0.06836818, "close_rate": 0.06871087764411027, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4626687444363737, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:10:00+00:00", "trade_duration": 40, "open_rate": 3.63e-05, "close_rate": 3.648195488721804e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2754.8209366391184, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:30:00+00:00", "close_date": "2018-01-17 22:05:00+00:00", "trade_duration": 575, "open_rate": 0.0281, "close_rate": 0.02824085213032581, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5587188612099645, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:35:00+00:00", "close_date": "2018-01-17 16:55:00+00:00", "trade_duration": 260, "open_rate": 0.08651001, "close_rate": 0.08694364413533832, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1559355963546878, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 05:00:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 55, "open_rate": 5.633e-05, "close_rate": 5.6612355889724306e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1775.2529735487308, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 05:20:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 35, "open_rate": 0.06988494, "close_rate": 0.07093584135338346, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.430923457900944, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 07:35:00+00:00", "close_date": "2018-01-18 08:15:00+00:00", "trade_duration": 40, "open_rate": 5.545e-05, "close_rate": 5.572794486215538e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1803.4265103697026, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 09:00:00+00:00", "close_date": "2018-01-18 09:40:00+00:00", "trade_duration": 40, "open_rate": 0.01633527, "close_rate": 0.016417151052631574, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.121723118136401, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 16:40:00+00:00", "close_date": "2018-01-18 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.00269734, "close_rate": 0.002710860501253133, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.073561360451414, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-18 18:05:00+00:00", "close_date": "2018-01-18 18:30:00+00:00", "trade_duration": 25, "open_rate": 4.475e-05, "close_rate": 4.587155388471177e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2234.63687150838, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 18:25:00+00:00", "close_date": "2018-01-18 18:55:00+00:00", "trade_duration": 30, "open_rate": 2.79e-05, "close_rate": 2.8319548872180444e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3584.2293906810037, "profit_abs": 0.000999999999999987}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 20:10:00+00:00", "close_date": "2018-01-18 20:50:00+00:00", "trade_duration": 40, "open_rate": 0.04439326, "close_rate": 0.04461578260651629, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2525942001105577, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 21:30:00+00:00", "close_date": "2018-01-19 00:35:00+00:00", "trade_duration": 185, "open_rate": 4.49e-05, "close_rate": 4.51250626566416e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2227.1714922049, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 21:55:00+00:00", "close_date": "2018-01-19 05:05:00+00:00", "trade_duration": 430, "open_rate": 0.02855, "close_rate": 0.028693107769423555, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.502626970227671, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 22:10:00+00:00", "close_date": "2018-01-18 22:50:00+00:00", "trade_duration": 40, "open_rate": 5.796e-05, "close_rate": 5.8250526315789473e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1725.3278122843342, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 23:50:00+00:00", "close_date": "2018-01-19 00:30:00+00:00", "trade_duration": 40, "open_rate": 0.04340323, "close_rate": 0.04362079005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.303975994413319, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-19 16:45:00+00:00", "close_date": "2018-01-19 17:35:00+00:00", "trade_duration": 50, "open_rate": 0.04454455, "close_rate": 0.04476783095238095, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.244943545282195, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:15:00+00:00", "close_date": "2018-01-19 19:55:00+00:00", "trade_duration": 160, "open_rate": 5.62e-05, "close_rate": 5.648170426065162e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1779.3594306049824, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:20:00+00:00", "close_date": "2018-01-19 20:15:00+00:00", "trade_duration": 175, "open_rate": 4.339e-05, "close_rate": 4.360749373433584e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2304.6784973496196, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-20 04:45:00+00:00", "close_date": "2018-01-20 17:35:00+00:00", "trade_duration": 770, "open_rate": 0.0001009, "close_rate": 0.00010140576441102755, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 991.0802775024778, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 15:15:00+00:00", "trade_duration": 625, "open_rate": 0.00270505, "close_rate": 0.002718609147869674, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.96789338459548, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 07:00:00+00:00", "trade_duration": 130, "open_rate": 0.03000002, "close_rate": 0.030150396040100245, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.3333311111125927, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 09:00:00+00:00", "close_date": "2018-01-20 09:40:00+00:00", "trade_duration": 40, "open_rate": 5.46e-05, "close_rate": 5.4873684210526304e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1831.5018315018317, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-20 18:25:00+00:00", "close_date": "2018-01-25 03:50:00+00:00", "trade_duration": 6325, "open_rate": 0.03082222, "close_rate": 0.027739998, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.244412634781012, "profit_abs": -0.010474999999999998}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 22:25:00+00:00", "close_date": "2018-01-20 23:15:00+00:00", "trade_duration": 50, "open_rate": 0.08969999, "close_rate": 0.09014961401002504, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1148273260677064, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 02:50:00+00:00", "close_date": "2018-01-21 14:30:00+00:00", "trade_duration": 700, "open_rate": 0.01632501, "close_rate": 0.01640683962406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.125570520324337, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 10:20:00+00:00", "close_date": "2018-01-21 11:00:00+00:00", "trade_duration": 40, "open_rate": 0.070538, "close_rate": 0.07089157393483708, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.417675579120474, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 15:50:00+00:00", "close_date": "2018-01-21 18:45:00+00:00", "trade_duration": 175, "open_rate": 5.301e-05, "close_rate": 5.327571428571427e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.4365214110546, "profit_abs": -2.7755575615628914e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 16:20:00+00:00", "close_date": "2018-01-21 17:00:00+00:00", "trade_duration": 40, "open_rate": 3.955e-05, "close_rate": 3.9748245614035085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2528.4450063211125, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:45:00+00:00", "trade_duration": 30, "open_rate": 0.00258505, "close_rate": 0.002623922932330827, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.6839712964933, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:55:00+00:00", "trade_duration": 40, "open_rate": 3.903e-05, "close_rate": 3.922563909774435e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2562.1316935690497, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 00:35:00+00:00", "close_date": "2018-01-22 10:35:00+00:00", "trade_duration": 600, "open_rate": 5.236e-05, "close_rate": 5.262245614035087e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1909.8548510313217, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 01:30:00+00:00", "close_date": "2018-01-22 02:10:00+00:00", "trade_duration": 40, "open_rate": 9.028e-05, "close_rate": 9.07325313283208e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1107.6650420912717, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 12:25:00+00:00", "close_date": "2018-01-22 14:35:00+00:00", "trade_duration": 130, "open_rate": 0.002687, "close_rate": 0.002700468671679198, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.21622627465575, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 13:15:00+00:00", "close_date": "2018-01-22 13:55:00+00:00", "trade_duration": 40, "open_rate": 4.168e-05, "close_rate": 4.188892230576441e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2399.232245681382, "profit_abs": 1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-22 14:00:00+00:00", "close_date": "2018-01-22 14:30:00+00:00", "trade_duration": 30, "open_rate": 8.821e-05, "close_rate": 8.953646616541353e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1133.6583153837435, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 15:55:00+00:00", "close_date": "2018-01-22 16:40:00+00:00", "trade_duration": 45, "open_rate": 5.172e-05, "close_rate": 5.1979248120300745e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1933.4880123743235, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-22 16:05:00+00:00", "close_date": "2018-01-22 16:25:00+00:00", "trade_duration": 20, "open_rate": 3.026e-05, "close_rate": 3.101839598997494e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3304.692663582287, "profit_abs": 0.0020000000000000157}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 19:50:00+00:00", "close_date": "2018-01-23 00:10:00+00:00", "trade_duration": 260, "open_rate": 0.07064, "close_rate": 0.07099408521303258, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.415628539071348, "profit_abs": 1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 21:25:00+00:00", "close_date": "2018-01-22 22:05:00+00:00", "trade_duration": 40, "open_rate": 0.01644483, "close_rate": 0.01652726022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.080938507725528, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-23 00:05:00+00:00", "close_date": "2018-01-23 00:35:00+00:00", "trade_duration": 30, "open_rate": 4.331e-05, "close_rate": 4.3961278195488714e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2308.935580697299, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-23 01:50:00+00:00", "close_date": "2018-01-23 02:15:00+00:00", "trade_duration": 25, "open_rate": 3.2e-05, "close_rate": 3.2802005012531326e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3125.0000000000005, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 04:25:00+00:00", "close_date": "2018-01-23 05:15:00+00:00", "trade_duration": 50, "open_rate": 0.09167706, "close_rate": 0.09213659413533835, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0907854156754153, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 07:35:00+00:00", "close_date": "2018-01-23 09:00:00+00:00", "trade_duration": 85, "open_rate": 0.0692498, "close_rate": 0.06959691679197995, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4440474918339115, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 10:50:00+00:00", "close_date": "2018-01-23 13:05:00+00:00", "trade_duration": 135, "open_rate": 3.182e-05, "close_rate": 3.197949874686716e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3142.677561282213, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 11:05:00+00:00", "close_date": "2018-01-23 16:05:00+00:00", "trade_duration": 300, "open_rate": 0.04088, "close_rate": 0.04108491228070175, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4461839530332683, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 14:55:00+00:00", "close_date": "2018-01-23 15:35:00+00:00", "trade_duration": 40, "open_rate": 5.15e-05, "close_rate": 5.175814536340851e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1941.747572815534, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 16:35:00+00:00", "close_date": "2018-01-24 00:05:00+00:00", "trade_duration": 450, "open_rate": 0.09071698, "close_rate": 0.09117170170426064, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1023294646713329, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 17:25:00+00:00", "close_date": "2018-01-23 18:45:00+00:00", "trade_duration": 80, "open_rate": 3.128e-05, "close_rate": 3.1436791979949865e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3196.9309462915603, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 20:15:00+00:00", "close_date": "2018-01-23 22:00:00+00:00", "trade_duration": 105, "open_rate": 9.555e-05, "close_rate": 9.602894736842104e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1046.5724751439038, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 22:30:00+00:00", "close_date": "2018-01-23 23:10:00+00:00", "trade_duration": 40, "open_rate": 0.04080001, "close_rate": 0.0410045213283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.450979791426522, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 23:50:00+00:00", "close_date": "2018-01-24 03:35:00+00:00", "trade_duration": 225, "open_rate": 5.163e-05, "close_rate": 5.18887969924812e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1936.8584156498162, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 00:20:00+00:00", "close_date": "2018-01-24 01:50:00+00:00", "trade_duration": 90, "open_rate": 0.04040781, "close_rate": 0.04061035541353383, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.474769110228938, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 06:45:00+00:00", "close_date": "2018-01-24 07:25:00+00:00", "trade_duration": 40, "open_rate": 5.132e-05, "close_rate": 5.157724310776942e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1948.5580670303975, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-24 14:15:00+00:00", "close_date": "2018-01-24 14:25:00+00:00", "trade_duration": 10, "open_rate": 5.198e-05, "close_rate": 5.432496240601503e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1923.8168526356292, "profit_abs": 0.0040000000000000036}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 14:50:00+00:00", "close_date": "2018-01-24 16:35:00+00:00", "trade_duration": 105, "open_rate": 3.054e-05, "close_rate": 3.069308270676692e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3274.3942370661425, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 15:10:00+00:00", "close_date": "2018-01-24 16:15:00+00:00", "trade_duration": 65, "open_rate": 9.263e-05, "close_rate": 9.309431077694236e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1079.5638562020945, "profit_abs": 2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 22:40:00+00:00", "close_date": "2018-01-24 23:25:00+00:00", "trade_duration": 45, "open_rate": 5.514e-05, "close_rate": 5.54163909774436e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1813.5654697134569, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 00:50:00+00:00", "close_date": "2018-01-25 01:30:00+00:00", "trade_duration": 40, "open_rate": 4.921e-05, "close_rate": 4.9456666666666664e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.1072952651903, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 08:15:00+00:00", "close_date": "2018-01-25 12:15:00+00:00", "trade_duration": 240, "open_rate": 0.0026, "close_rate": 0.002613032581453634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.46153846153847, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 10:25:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 350, "open_rate": 0.02799871, "close_rate": 0.028139054411027563, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.571593119825878, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 11:00:00+00:00", "close_date": "2018-01-25 11:45:00+00:00", "trade_duration": 45, "open_rate": 0.04078902, "close_rate": 0.0409934762406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4516401717913303, "profit_abs": -1.3877787807814457e-17}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:05:00+00:00", "close_date": "2018-01-25 13:45:00+00:00", "trade_duration": 40, "open_rate": 2.89e-05, "close_rate": 2.904486215538847e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3460.2076124567475, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:20:00+00:00", "close_date": "2018-01-25 14:05:00+00:00", "trade_duration": 45, "open_rate": 0.041103, "close_rate": 0.04130903007518797, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4329124394813033, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-25 15:45:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 30, "open_rate": 5.428e-05, "close_rate": 5.509624060150376e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1842.2991893883568, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 17:45:00+00:00", "close_date": "2018-01-25 23:15:00+00:00", "trade_duration": 330, "open_rate": 5.414e-05, "close_rate": 5.441137844611528e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1847.063169560399, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 21:15:00+00:00", "close_date": "2018-01-25 21:55:00+00:00", "trade_duration": 40, "open_rate": 0.04140777, "close_rate": 0.0416153277443609, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.415005686130888, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 02:05:00+00:00", "close_date": "2018-01-26 02:45:00+00:00", "trade_duration": 40, "open_rate": 0.00254309, "close_rate": 0.002555837318295739, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.32224183965177, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 02:55:00+00:00", "close_date": "2018-01-26 15:10:00+00:00", "trade_duration": 735, "open_rate": 5.607e-05, "close_rate": 5.6351052631578935e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1783.4849295523454, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 06:10:00+00:00", "close_date": "2018-01-26 09:25:00+00:00", "trade_duration": 195, "open_rate": 0.00253806, "close_rate": 0.0025507821052631577, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.400171784748984, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 07:25:00+00:00", "close_date": "2018-01-26 09:55:00+00:00", "trade_duration": 150, "open_rate": 0.0415, "close_rate": 0.04170802005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4096385542168677, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-26 09:55:00+00:00", "close_date": "2018-01-26 10:25:00+00:00", "trade_duration": 30, "open_rate": 5.321e-05, "close_rate": 5.401015037593984e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1879.3459875963165, "profit_abs": 0.000999999999999987}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 16:05:00+00:00", "close_date": "2018-01-26 16:45:00+00:00", "trade_duration": 40, "open_rate": 0.02772046, "close_rate": 0.02785940967418546, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.6074437437185387, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 23:35:00+00:00", "close_date": "2018-01-27 00:15:00+00:00", "trade_duration": 40, "open_rate": 0.09461341, "close_rate": 0.09508766268170424, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0569326272036914, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 00:35:00+00:00", "close_date": "2018-01-27 01:30:00+00:00", "trade_duration": 55, "open_rate": 5.615e-05, "close_rate": 5.643145363408521e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1780.9439002671415, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.07877175, "open_date": "2018-01-27 00:45:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 4560, "open_rate": 5.556e-05, "close_rate": 5.144e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1799.8560115190785, "profit_abs": -0.007896868250539965}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 02:30:00+00:00", "close_date": "2018-01-27 11:25:00+00:00", "trade_duration": 535, "open_rate": 0.06900001, "close_rate": 0.06934587471177944, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492751522789635, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 06:25:00+00:00", "close_date": "2018-01-27 07:05:00+00:00", "trade_duration": 40, "open_rate": 0.09449985, "close_rate": 0.0949735334586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.058202737887944, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.04815133, "open_date": "2018-01-27 09:40:00+00:00", "close_date": "2018-01-30 04:40:00+00:00", "trade_duration": 4020, "open_rate": 0.0410697, "close_rate": 0.03928809, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4348850855983852, "profit_abs": -0.004827170578309559}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 11:45:00+00:00", "close_date": "2018-01-27 12:30:00+00:00", "trade_duration": 45, "open_rate": 0.0285, "close_rate": 0.02864285714285714, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5087719298245617, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 12:35:00+00:00", "close_date": "2018-01-27 15:25:00+00:00", "trade_duration": 170, "open_rate": 0.02866372, "close_rate": 0.02880739779448621, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.4887307020861216, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 15:50:00+00:00", "close_date": "2018-01-27 16:50:00+00:00", "trade_duration": 60, "open_rate": 0.095381, "close_rate": 0.09585910025062656, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0484268355332824, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 17:05:00+00:00", "close_date": "2018-01-27 17:45:00+00:00", "trade_duration": 40, "open_rate": 0.06759092, "close_rate": 0.06792972160401002, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4794886650455417, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 23:40:00+00:00", "close_date": "2018-01-28 01:05:00+00:00", "trade_duration": 85, "open_rate": 0.00258501, "close_rate": 0.002597967443609022, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.684569885609726, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 02:25:00+00:00", "close_date": "2018-01-28 08:10:00+00:00", "trade_duration": 345, "open_rate": 0.06698502, "close_rate": 0.0673207845112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4928710926711672, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 10:25:00+00:00", "close_date": "2018-01-28 16:30:00+00:00", "trade_duration": 365, "open_rate": 0.0677177, "close_rate": 0.06805713709273183, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4767187899175547, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-28 20:35:00+00:00", "close_date": "2018-01-28 21:35:00+00:00", "trade_duration": 60, "open_rate": 5.215e-05, "close_rate": 5.2411403508771925e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1917.5455417066157, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-28 22:00:00+00:00", "close_date": "2018-01-28 22:30:00+00:00", "trade_duration": 30, "open_rate": 0.00273809, "close_rate": 0.002779264285714285, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.5218089982433, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-29 00:00:00+00:00", "close_date": "2018-01-29 00:30:00+00:00", "trade_duration": 30, "open_rate": 0.00274632, "close_rate": 0.002787618045112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.412362725392526, "profit_abs": 0.0010000000000000148}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-29 02:15:00+00:00", "close_date": "2018-01-29 03:00:00+00:00", "trade_duration": 45, "open_rate": 0.01622478, "close_rate": 0.016306107218045113, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.163411768911504, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 03:05:00+00:00", "close_date": "2018-01-29 03:45:00+00:00", "trade_duration": 40, "open_rate": 0.069, "close_rate": 0.06934586466165413, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492753623188406, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 05:20:00+00:00", "close_date": "2018-01-29 06:55:00+00:00", "trade_duration": 95, "open_rate": 8.755e-05, "close_rate": 8.798884711779448e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1142.204454597373, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 07:00:00+00:00", "close_date": "2018-01-29 19:25:00+00:00", "trade_duration": 745, "open_rate": 0.06825763, "close_rate": 0.06859977350877192, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4650376815016872, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 19:45:00+00:00", "close_date": "2018-01-29 20:25:00+00:00", "trade_duration": 40, "open_rate": 0.06713892, "close_rate": 0.06747545593984962, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4894490408841845, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0199116, "open_date": "2018-01-29 23:30:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 315, "open_rate": 8.934e-05, "close_rate": 8.8e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1119.3194537721067, "profit_abs": -0.0019961383478844796}], "results_per_pair": [{"key": "TRX/BTC", "trades": 15, "profit_mean": 0.0023467073333333323, "profit_mean_pct": 0.23467073333333321, "profit_sum": 0.035200609999999986, "profit_sum_pct": 3.5200609999999988, "profit_total_abs": 0.0035288616521155086, "profit_total_pct": 1.1733536666666662, "duration_avg": "2:28:00", "wins": 9, "draws": 2, "losses": 4}, {"key": "ADA/BTC", "trades": 29, "profit_mean": -0.0011598141379310352, "profit_mean_pct": -0.11598141379310352, "profit_sum": -0.03363461000000002, "profit_sum_pct": -3.3634610000000023, "profit_total_abs": -0.0033718682505400333, "profit_total_pct": -1.1211536666666675, "duration_avg": "5:35:00", "wins": 9, "draws": 11, "losses": 9}, {"key": "XLM/BTC", "trades": 21, "profit_mean": 0.0026243899999999994, "profit_mean_pct": 0.2624389999999999, "profit_sum": 0.05511218999999999, "profit_sum_pct": 5.511218999999999, "profit_total_abs": 0.005525000000000002, "profit_total_pct": 1.8370729999999995, "duration_avg": "3:21:00", "wins": 12, "draws": 3, "losses": 6}, {"key": "ETH/BTC", "trades": 21, "profit_mean": 0.0009500057142857142, "profit_mean_pct": 0.09500057142857142, "profit_sum": 0.01995012, "profit_sum_pct": 1.9950119999999998, "profit_total_abs": 0.0019999999999999463, "profit_total_pct": 0.6650039999999999, "duration_avg": "2:17:00", "wins": 5, "draws": 10, "losses": 6}, {"key": "XMR/BTC", "trades": 16, "profit_mean": -0.0027899012500000007, "profit_mean_pct": -0.2789901250000001, "profit_sum": -0.04463842000000001, "profit_sum_pct": -4.463842000000001, "profit_total_abs": -0.0044750000000000345, "profit_total_pct": -1.4879473333333337, "duration_avg": "8:41:00", "wins": 6, "draws": 5, "losses": 5}, {"key": "ZEC/BTC", "trades": 21, "profit_mean": -0.00039290904761904774, "profit_mean_pct": -0.03929090476190478, "profit_sum": -0.008251090000000003, "profit_sum_pct": -0.8251090000000003, "profit_total_abs": -0.000827170578309569, "profit_total_pct": -0.27503633333333344, "duration_avg": "4:17:00", "wins": 8, "draws": 7, "losses": 6}, {"key": "NXT/BTC", "trades": 12, "profit_mean": -0.0012261025000000006, "profit_mean_pct": -0.12261025000000006, "profit_sum": -0.014713230000000008, "profit_sum_pct": -1.4713230000000008, "profit_total_abs": -0.0014750000000000874, "profit_total_pct": -0.4904410000000003, "duration_avg": "0:57:00", "wins": 4, "draws": 3, "losses": 5}, {"key": "LTC/BTC", "trades": 8, "profit_mean": 0.00748129625, "profit_mean_pct": 0.748129625, "profit_sum": 0.05985037, "profit_sum_pct": 5.985037, "profit_total_abs": 0.006000000000000019, "profit_total_pct": 1.9950123333333334, "duration_avg": "1:59:00", "wins": 5, "draws": 2, "losses": 1}, {"key": "ETC/BTC", "trades": 20, "profit_mean": 0.0022568569999999997, "profit_mean_pct": 0.22568569999999996, "profit_sum": 0.04513713999999999, "profit_sum_pct": 4.513713999999999, "profit_total_abs": 0.004525000000000001, "profit_total_pct": 1.504571333333333, "duration_avg": "1:45:00", "wins": 11, "draws": 4, "losses": 5}, {"key": "DASH/BTC", "trades": 16, "profit_mean": 0.0018703237499999997, "profit_mean_pct": 0.18703237499999997, "profit_sum": 0.029925179999999996, "profit_sum_pct": 2.9925179999999996, "profit_total_abs": 0.002999999999999961, "profit_total_pct": 0.9975059999999999, "duration_avg": "3:03:00", "wins": 4, "draws": 7, "losses": 5}, {"key": "TOTAL", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}], "sell_reason_summary": [{"sell_reason": "roi", "trades": 170, "wins": 73, "draws": 54, "losses": 43, "profit_mean": 0.005398268352941177, "profit_mean_pct": 0.54, "profit_sum": 0.91770562, "profit_sum_pct": 91.77, "profit_total_abs": 0.09199999999999964, "profit_pct_total": 30.59}, {"sell_reason": "stop_loss", "trades": 6, "wins": 0, "draws": 0, "losses": 6, "profit_mean": -0.10448878000000002, "profit_mean_pct": -10.45, "profit_sum": -0.6269326800000001, "profit_sum_pct": -62.69, "profit_total_abs": -0.06284999999999992, "profit_pct_total": -20.9}, {"sell_reason": "force_sell", "trades": 3, "wins": 0, "draws": 0, "losses": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.89, "profit_sum": -0.14683468, "profit_sum_pct": -14.68, "profit_total_abs": -0.014720177176734003, "profit_pct_total": -4.89}], "left_open_trades": [{"key": "TRX/BTC", "trades": 1, "profit_mean": -0.0199116, "profit_mean_pct": -1.9911600000000003, "profit_sum": -0.0199116, "profit_sum_pct": -1.9911600000000003, "profit_total_abs": -0.0019961383478844796, "profit_total_pct": -0.6637200000000001, "duration_avg": "5:15:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ADA/BTC", "trades": 1, "profit_mean": -0.07877175, "profit_mean_pct": -7.877175, "profit_sum": -0.07877175, "profit_sum_pct": -7.877175, "profit_total_abs": -0.007896868250539965, "profit_total_pct": -2.625725, "duration_avg": "3 days, 4:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ZEC/BTC", "trades": 1, "profit_mean": -0.04815133, "profit_mean_pct": -4.815133, "profit_sum": -0.04815133, "profit_sum_pct": -4.815133, "profit_total_abs": -0.004827170578309559, "profit_total_pct": -1.6050443333333335, "duration_avg": "2 days, 19:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "TOTAL", "trades": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.894489333333333, "profit_sum": -0.14683468, "profit_sum_pct": -14.683468, "profit_total_abs": -0.014720177176734003, "profit_total_pct": -4.8944893333333335, "duration_avg": "2 days, 1:25:00", "wins": 0, "draws": 0, "losses": 3}], "total_trades": 179, "backtest_start": "2018-01-30 04:45:00+00:00", "backtest_start_ts": 1517287500, "backtest_end": "2018-01-30 04:45:00+00:00", "backtest_end_ts": 1517287500, "backtest_days": 0, "trades_per_day": null, "market_change": 0.25, "stake_amount": 0.1, "max_drawdown": 0.21142322000000008, "drawdown_start": "2018-01-24 14:25:00+00:00", "drawdown_start_ts": 1516803900.0, "drawdown_end": "2018-01-30 04:45:00+00:00", "drawdown_end_ts": 1517287500.0,"pairlist": ["TRX/BTC", "ADA/BTC", "XLM/BTC", "ETH/BTC", "XMR/BTC", "ZEC/BTC","NXT/BTC", "LTC/BTC", "ETC/BTC", "DASH/BTC"]}}, "strategy_comparison": [{"key": "DefaultStrategy", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}, {"key": "TestStrategy", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}]} +{"strategy": {"StrategyTestV2": {"trades": [{"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:20:00+00:00", "trade_duration": 5, "open_rate": 9.64e-05, "close_rate": 0.00010074887218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1037.344398340249, "profit_abs": 0.00399999999999999}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:30:00+00:00", "trade_duration": 15, "open_rate": 4.756e-05, "close_rate": 4.9705563909774425e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2102.6072329688814, "profit_abs": 0.00399999999999999}, {"pair": "XLM/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:35:00+00:00", "trade_duration": 10, "open_rate": 3.339e-05, "close_rate": 3.489631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2994.908655286014, "profit_abs": 0.0040000000000000036}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:40:00+00:00", "trade_duration": 15, "open_rate": 9.696e-05, "close_rate": 0.00010133413533834584, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1031.3531353135315, "profit_abs": 0.00399999999999999}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 07:35:00+00:00", "close_date": "2018-01-10 08:35:00+00:00", "trade_duration": 60, "open_rate": 0.0943, "close_rate": 0.09477268170426063, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0604453870625663, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 07:40:00+00:00", "close_date": "2018-01-10 08:10:00+00:00", "trade_duration": 30, "open_rate": 0.02719607, "close_rate": 0.02760503345864661, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.677001860930642, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 08:15:00+00:00", "close_date": "2018-01-10 09:55:00+00:00", "trade_duration": 100, "open_rate": 0.04634952, "close_rate": 0.046581848421052625, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1575196463739, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 14:45:00+00:00", "close_date": "2018-01-10 15:50:00+00:00", "trade_duration": 65, "open_rate": 3.066e-05, "close_rate": 3.081368421052631e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3261.5786040443577, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 16:35:00+00:00", "close_date": "2018-01-10 17:15:00+00:00", "trade_duration": 40, "open_rate": 0.0168999, "close_rate": 0.016984611278195488, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.917194776300452, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 16:40:00+00:00", "close_date": "2018-01-10 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.09132568, "close_rate": 0.0917834528320802, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0949822656672252, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 18:50:00+00:00", "close_date": "2018-01-10 19:45:00+00:00", "trade_duration": 55, "open_rate": 0.08898003, "close_rate": 0.08942604518796991, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1238476768326557, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 22:15:00+00:00", "close_date": "2018-01-10 23:00:00+00:00", "trade_duration": 45, "open_rate": 0.08560008, "close_rate": 0.08602915308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1682232072680307, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 22:50:00+00:00", "close_date": "2018-01-10 23:20:00+00:00", "trade_duration": 30, "open_rate": 0.00249083, "close_rate": 0.0025282860902255634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 40.147260150231055, "profit_abs": 0.000999999999999987}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 23:15:00+00:00", "close_date": "2018-01-11 00:15:00+00:00", "trade_duration": 60, "open_rate": 3.022e-05, "close_rate": 3.037147869674185e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3309.0668431502318, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-10 23:40:00+00:00", "close_date": "2018-01-11 00:05:00+00:00", "trade_duration": 25, "open_rate": 0.002437, "close_rate": 0.0024980776942355883, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.03405826836274, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 00:00:00+00:00", "close_date": "2018-01-11 00:35:00+00:00", "trade_duration": 35, "open_rate": 0.04771803, "close_rate": 0.04843559436090225, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0956439316543456, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-11 03:40:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 45, "open_rate": 3.651e-05, "close_rate": 3.2859000000000005e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2738.9756231169545, "profit_abs": -0.01047499999999997}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 03:55:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 30, "open_rate": 0.08824105, "close_rate": 0.08956798308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1332594070446804, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 04:00:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 50, "open_rate": 0.00243, "close_rate": 0.002442180451127819, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.1522633744856, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:55:00+00:00", "trade_duration": 25, "open_rate": 0.04545064, "close_rate": 0.046589753784461146, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.200189040242338, "profit_abs": 0.001999999999999988}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 20, "open_rate": 3.372e-05, "close_rate": 3.456511278195488e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2965.599051008304, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:55:00+00:00", "close_date": "2018-01-11 05:15:00+00:00", "trade_duration": 20, "open_rate": 0.02644, "close_rate": 0.02710265664160401, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7821482602118004, "profit_abs": 0.001999999999999988}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:20:00+00:00", "close_date": "2018-01-11 12:00:00+00:00", "trade_duration": 40, "open_rate": 0.08812, "close_rate": 0.08856170426065162, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1348161597821154, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:35:00+00:00", "close_date": "2018-01-11 12:15:00+00:00", "trade_duration": 40, "open_rate": 0.02683577, "close_rate": 0.026970285137844607, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7263696923919087, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 14:00:00+00:00", "close_date": "2018-01-11 14:25:00+00:00", "trade_duration": 25, "open_rate": 4.919e-05, "close_rate": 5.04228320802005e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.9335230737956, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 19:25:00+00:00", "close_date": "2018-01-11 20:35:00+00:00", "trade_duration": 70, "open_rate": 0.08784896, "close_rate": 0.08828930566416039, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1383174029607181, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:35:00+00:00", "close_date": "2018-01-11 23:30:00+00:00", "trade_duration": 55, "open_rate": 5.105e-05, "close_rate": 5.130588972431077e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1958.8638589618022, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:25:00+00:00", "trade_duration": 30, "open_rate": 3.96e-05, "close_rate": 4.019548872180451e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2525.252525252525, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:35:00+00:00", "trade_duration": 40, "open_rate": 2.885e-05, "close_rate": 2.899461152882205e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3466.204506065858, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 23:30:00+00:00", "close_date": "2018-01-12 00:05:00+00:00", "trade_duration": 35, "open_rate": 0.02645, "close_rate": 0.026847744360902256, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.780718336483932, "profit_abs": 0.0010000000000000148}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 23:55:00+00:00", "close_date": "2018-01-12 01:15:00+00:00", "trade_duration": 80, "open_rate": 0.048, "close_rate": 0.04824060150375939, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0833333333333335, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-12 21:15:00+00:00", "close_date": "2018-01-12 21:40:00+00:00", "trade_duration": 25, "open_rate": 4.692e-05, "close_rate": 4.809593984962405e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2131.287297527707, "profit_abs": 0.001999999999999974}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 00:55:00+00:00", "close_date": "2018-01-13 06:20:00+00:00", "trade_duration": 325, "open_rate": 0.00256966, "close_rate": 0.0025825405012531327, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.91565421106294, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 10:55:00+00:00", "close_date": "2018-01-13 11:35:00+00:00", "trade_duration": 40, "open_rate": 6.262e-05, "close_rate": 6.293388471177944e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1596.933886937081, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 13:05:00+00:00", "close_date": "2018-01-15 14:10:00+00:00", "trade_duration": 2945, "open_rate": 4.73e-05, "close_rate": 4.753709273182957e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2114.1649048625795, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:30:00+00:00", "close_date": "2018-01-13 14:45:00+00:00", "trade_duration": 75, "open_rate": 6.063e-05, "close_rate": 6.0933909774436085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1649.348507339601, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:40:00+00:00", "close_date": "2018-01-13 23:30:00+00:00", "trade_duration": 590, "open_rate": 0.00011082, "close_rate": 0.00011137548872180448, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 902.3641941887746, "profit_abs": -2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 15:15:00+00:00", "close_date": "2018-01-13 15:55:00+00:00", "trade_duration": 40, "open_rate": 5.93e-05, "close_rate": 5.9597243107769415e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1686.3406408094436, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 16:30:00+00:00", "close_date": "2018-01-13 17:10:00+00:00", "trade_duration": 40, "open_rate": 0.04850003, "close_rate": 0.04874313791979949, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0618543947292407, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 22:05:00+00:00", "close_date": "2018-01-14 06:25:00+00:00", "trade_duration": 500, "open_rate": 0.09825019, "close_rate": 0.09874267215538848, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0178097365511456, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 00:20:00+00:00", "close_date": "2018-01-14 22:55:00+00:00", "trade_duration": 1355, "open_rate": 6.018e-05, "close_rate": 6.048165413533834e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1661.681621801263, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 12:45:00+00:00", "close_date": "2018-01-14 13:25:00+00:00", "trade_duration": 40, "open_rate": 0.09758999, "close_rate": 0.0980791628822055, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.024695258191952, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-14 15:30:00+00:00", "close_date": "2018-01-14 16:00:00+00:00", "trade_duration": 30, "open_rate": 0.00311, "close_rate": 0.0031567669172932328, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.154340836012864, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 20:45:00+00:00", "close_date": "2018-01-14 22:15:00+00:00", "trade_duration": 90, "open_rate": 0.00312401, "close_rate": 0.003139669197994987, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.010140812609436, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 23:35:00+00:00", "close_date": "2018-01-15 00:30:00+00:00", "trade_duration": 55, "open_rate": 0.0174679, "close_rate": 0.017555458395989976, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.724786608579165, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 23:45:00+00:00", "close_date": "2018-01-15 00:25:00+00:00", "trade_duration": 40, "open_rate": 0.07346846, "close_rate": 0.07383672295739348, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.3611282991367997, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 02:25:00+00:00", "close_date": "2018-01-15 03:05:00+00:00", "trade_duration": 40, "open_rate": 0.097994, "close_rate": 0.09848519799498744, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.020470641059657, "profit_abs": -2.7755575615628914e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 07:20:00+00:00", "close_date": "2018-01-15 08:00:00+00:00", "trade_duration": 40, "open_rate": 0.09659, "close_rate": 0.09707416040100247, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0353038616834043, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-15 08:20:00+00:00", "close_date": "2018-01-15 08:55:00+00:00", "trade_duration": 35, "open_rate": 9.987e-05, "close_rate": 0.00010137180451127818, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1001.3016921998599, "profit_abs": 0.0010000000000000009}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-15 12:10:00+00:00", "close_date": "2018-01-16 02:50:00+00:00", "trade_duration": 880, "open_rate": 0.0948969, "close_rate": 0.09537257368421052, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0537752023511833, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:10:00+00:00", "close_date": "2018-01-15 17:40:00+00:00", "trade_duration": 210, "open_rate": 0.071, "close_rate": 0.07135588972431077, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4084507042253522, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:30:00+00:00", "close_date": "2018-01-15 15:10:00+00:00", "trade_duration": 40, "open_rate": 0.04600501, "close_rate": 0.046235611553884705, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.173676301776698, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:10:00+00:00", "close_date": "2018-01-15 19:25:00+00:00", "trade_duration": 75, "open_rate": 9.438e-05, "close_rate": 9.485308270676693e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1059.5465140919687, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:35:00+00:00", "close_date": "2018-01-15 19:15:00+00:00", "trade_duration": 40, "open_rate": 0.03040001, "close_rate": 0.030552391002506264, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.2894726021471703, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-15 20:25:00+00:00", "close_date": "2018-01-16 08:25:00+00:00", "trade_duration": 720, "open_rate": 5.837e-05, "close_rate": 5.2533e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1713.2088401576154, "profit_abs": -0.010474999999999984}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 20:40:00+00:00", "close_date": "2018-01-15 22:00:00+00:00", "trade_duration": 80, "open_rate": 0.046036, "close_rate": 0.04626675689223057, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1722130506560084, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 00:30:00+00:00", "close_date": "2018-01-16 01:10:00+00:00", "trade_duration": 40, "open_rate": 0.0028685, "close_rate": 0.0028828784461152877, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 34.86142583231654, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 01:15:00+00:00", "close_date": "2018-01-16 02:35:00+00:00", "trade_duration": 80, "open_rate": 0.06731755, "close_rate": 0.0676549813283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4854967241083492, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 07:45:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 55, "open_rate": 0.09217614, "close_rate": 0.09263817578947368, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0848794492804754, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:55:00+00:00", "trade_duration": 20, "open_rate": 0.0165, "close_rate": 0.016913533834586467, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.0606060606060606, "profit_abs": 0.0020000000000000018}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 5, "open_rate": 7.953e-05, "close_rate": 8.311781954887218e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1257.387149503332, "profit_abs": 0.00399999999999999}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 08:45:00+00:00", "close_date": "2018-01-16 09:50:00+00:00", "trade_duration": 65, "open_rate": 0.045202, "close_rate": 0.04542857644110275, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2122914915269236, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:45:00+00:00", "trade_duration": 30, "open_rate": 5.248e-05, "close_rate": 5.326917293233082e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1905.487804878049, "profit_abs": 0.0010000000000000009}, {"pair": "XMR/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:55:00+00:00", "trade_duration": 40, "open_rate": 0.02892318, "close_rate": 0.02906815834586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.457434486802627, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 09:50:00+00:00", "close_date": "2018-01-16 10:10:00+00:00", "trade_duration": 20, "open_rate": 5.158e-05, "close_rate": 5.287273182957392e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1938.735944164405, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:35:00+00:00", "trade_duration": 30, "open_rate": 0.02828232, "close_rate": 0.02870761804511278, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5357778286929786, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:40:00+00:00", "trade_duration": 35, "open_rate": 0.04357584, "close_rate": 0.044231115789473675, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.294849623093898, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 13:45:00+00:00", "close_date": "2018-01-16 14:20:00+00:00", "trade_duration": 35, "open_rate": 5.362e-05, "close_rate": 5.442631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1864.975755315181, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 17:30:00+00:00", "close_date": "2018-01-16 18:25:00+00:00", "trade_duration": 55, "open_rate": 5.302e-05, "close_rate": 5.328576441102756e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.0807242549984, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:45:00+00:00", "trade_duration": 30, "open_rate": 0.09129999, "close_rate": 0.09267292218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0952903718828448, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:35:00+00:00", "trade_duration": 20, "open_rate": 3.808e-05, "close_rate": 3.903438596491228e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2626.0504201680674, "profit_abs": 0.0020000000000000018}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 19:00:00+00:00", "close_date": "2018-01-16 19:30:00+00:00", "trade_duration": 30, "open_rate": 0.02811012, "close_rate": 0.028532828571428567, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.557437677249333, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 60, "open_rate": 0.00258379, "close_rate": 0.002325411, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.702835756775904, "profit_abs": -0.010474999999999984}, {"pair": "NXT/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 80, "open_rate": 2.559e-05, "close_rate": 2.3031e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3907.7764751856193, "profit_abs": -0.010474999999999998}, {"pair": "TRX/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:35:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 50, "open_rate": 7.62e-05, "close_rate": 6.858e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1312.3359580052495, "profit_abs": -0.010474999999999984}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:35:00+00:00", "trade_duration": 5, "open_rate": 0.00229844, "close_rate": 0.002402129022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 43.507770487809125, "profit_abs": 0.004000000000000017}, {"pair": "LTC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:40:00+00:00", "trade_duration": 10, "open_rate": 0.0151, "close_rate": 0.015781203007518795, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.622516556291391, "profit_abs": 0.00399999999999999}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:40:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 5, "open_rate": 0.00235676, "close_rate": 0.00246308, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 42.431134269081284, "profit_abs": 0.0040000000000000036}, {"pair": "DASH/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 22:45:00+00:00", "close_date": "2018-01-16 23:05:00+00:00", "trade_duration": 20, "open_rate": 0.0630692, "close_rate": 0.06464988170426066, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.585559988076589, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:50:00+00:00", "close_date": "2018-01-16 22:55:00+00:00", "trade_duration": 5, "open_rate": 2.2e-05, "close_rate": 2.299248120300751e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 4545.454545454546, "profit_abs": 0.003999999999999976}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-17 03:30:00+00:00", "close_date": "2018-01-17 04:00:00+00:00", "trade_duration": 30, "open_rate": 4.974e-05, "close_rate": 5.048796992481203e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2010.454362685967, "profit_abs": 0.0010000000000000009}, {"pair": "TRX/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-17 03:55:00+00:00", "close_date": "2018-01-17 04:15:00+00:00", "trade_duration": 20, "open_rate": 7.108e-05, "close_rate": 7.28614536340852e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1406.8655036578502, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 09:35:00+00:00", "close_date": "2018-01-17 10:15:00+00:00", "trade_duration": 40, "open_rate": 0.04327, "close_rate": 0.04348689223057644, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.3110700254217704, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:20:00+00:00", "close_date": "2018-01-17 17:00:00+00:00", "trade_duration": 400, "open_rate": 4.997e-05, "close_rate": 5.022047619047618e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2001.2007204322595, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:25:00+00:00", "trade_duration": 55, "open_rate": 0.06836818, "close_rate": 0.06871087764411027, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4626687444363737, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:10:00+00:00", "trade_duration": 40, "open_rate": 3.63e-05, "close_rate": 3.648195488721804e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2754.8209366391184, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:30:00+00:00", "close_date": "2018-01-17 22:05:00+00:00", "trade_duration": 575, "open_rate": 0.0281, "close_rate": 0.02824085213032581, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5587188612099645, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:35:00+00:00", "close_date": "2018-01-17 16:55:00+00:00", "trade_duration": 260, "open_rate": 0.08651001, "close_rate": 0.08694364413533832, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1559355963546878, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 05:00:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 55, "open_rate": 5.633e-05, "close_rate": 5.6612355889724306e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1775.2529735487308, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 05:20:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 35, "open_rate": 0.06988494, "close_rate": 0.07093584135338346, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.430923457900944, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 07:35:00+00:00", "close_date": "2018-01-18 08:15:00+00:00", "trade_duration": 40, "open_rate": 5.545e-05, "close_rate": 5.572794486215538e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1803.4265103697026, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 09:00:00+00:00", "close_date": "2018-01-18 09:40:00+00:00", "trade_duration": 40, "open_rate": 0.01633527, "close_rate": 0.016417151052631574, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.121723118136401, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 16:40:00+00:00", "close_date": "2018-01-18 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.00269734, "close_rate": 0.002710860501253133, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.073561360451414, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-18 18:05:00+00:00", "close_date": "2018-01-18 18:30:00+00:00", "trade_duration": 25, "open_rate": 4.475e-05, "close_rate": 4.587155388471177e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2234.63687150838, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 18:25:00+00:00", "close_date": "2018-01-18 18:55:00+00:00", "trade_duration": 30, "open_rate": 2.79e-05, "close_rate": 2.8319548872180444e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3584.2293906810037, "profit_abs": 0.000999999999999987}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 20:10:00+00:00", "close_date": "2018-01-18 20:50:00+00:00", "trade_duration": 40, "open_rate": 0.04439326, "close_rate": 0.04461578260651629, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2525942001105577, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 21:30:00+00:00", "close_date": "2018-01-19 00:35:00+00:00", "trade_duration": 185, "open_rate": 4.49e-05, "close_rate": 4.51250626566416e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2227.1714922049, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 21:55:00+00:00", "close_date": "2018-01-19 05:05:00+00:00", "trade_duration": 430, "open_rate": 0.02855, "close_rate": 0.028693107769423555, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.502626970227671, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 22:10:00+00:00", "close_date": "2018-01-18 22:50:00+00:00", "trade_duration": 40, "open_rate": 5.796e-05, "close_rate": 5.8250526315789473e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1725.3278122843342, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 23:50:00+00:00", "close_date": "2018-01-19 00:30:00+00:00", "trade_duration": 40, "open_rate": 0.04340323, "close_rate": 0.04362079005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.303975994413319, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-19 16:45:00+00:00", "close_date": "2018-01-19 17:35:00+00:00", "trade_duration": 50, "open_rate": 0.04454455, "close_rate": 0.04476783095238095, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.244943545282195, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:15:00+00:00", "close_date": "2018-01-19 19:55:00+00:00", "trade_duration": 160, "open_rate": 5.62e-05, "close_rate": 5.648170426065162e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1779.3594306049824, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:20:00+00:00", "close_date": "2018-01-19 20:15:00+00:00", "trade_duration": 175, "open_rate": 4.339e-05, "close_rate": 4.360749373433584e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2304.6784973496196, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-20 04:45:00+00:00", "close_date": "2018-01-20 17:35:00+00:00", "trade_duration": 770, "open_rate": 0.0001009, "close_rate": 0.00010140576441102755, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 991.0802775024778, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 15:15:00+00:00", "trade_duration": 625, "open_rate": 0.00270505, "close_rate": 0.002718609147869674, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.96789338459548, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 07:00:00+00:00", "trade_duration": 130, "open_rate": 0.03000002, "close_rate": 0.030150396040100245, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.3333311111125927, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 09:00:00+00:00", "close_date": "2018-01-20 09:40:00+00:00", "trade_duration": 40, "open_rate": 5.46e-05, "close_rate": 5.4873684210526304e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1831.5018315018317, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-20 18:25:00+00:00", "close_date": "2018-01-25 03:50:00+00:00", "trade_duration": 6325, "open_rate": 0.03082222, "close_rate": 0.027739998, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.244412634781012, "profit_abs": -0.010474999999999998}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 22:25:00+00:00", "close_date": "2018-01-20 23:15:00+00:00", "trade_duration": 50, "open_rate": 0.08969999, "close_rate": 0.09014961401002504, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1148273260677064, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 02:50:00+00:00", "close_date": "2018-01-21 14:30:00+00:00", "trade_duration": 700, "open_rate": 0.01632501, "close_rate": 0.01640683962406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.125570520324337, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 10:20:00+00:00", "close_date": "2018-01-21 11:00:00+00:00", "trade_duration": 40, "open_rate": 0.070538, "close_rate": 0.07089157393483708, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.417675579120474, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 15:50:00+00:00", "close_date": "2018-01-21 18:45:00+00:00", "trade_duration": 175, "open_rate": 5.301e-05, "close_rate": 5.327571428571427e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.4365214110546, "profit_abs": -2.7755575615628914e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 16:20:00+00:00", "close_date": "2018-01-21 17:00:00+00:00", "trade_duration": 40, "open_rate": 3.955e-05, "close_rate": 3.9748245614035085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2528.4450063211125, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:45:00+00:00", "trade_duration": 30, "open_rate": 0.00258505, "close_rate": 0.002623922932330827, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.6839712964933, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:55:00+00:00", "trade_duration": 40, "open_rate": 3.903e-05, "close_rate": 3.922563909774435e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2562.1316935690497, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 00:35:00+00:00", "close_date": "2018-01-22 10:35:00+00:00", "trade_duration": 600, "open_rate": 5.236e-05, "close_rate": 5.262245614035087e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1909.8548510313217, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 01:30:00+00:00", "close_date": "2018-01-22 02:10:00+00:00", "trade_duration": 40, "open_rate": 9.028e-05, "close_rate": 9.07325313283208e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1107.6650420912717, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 12:25:00+00:00", "close_date": "2018-01-22 14:35:00+00:00", "trade_duration": 130, "open_rate": 0.002687, "close_rate": 0.002700468671679198, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.21622627465575, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 13:15:00+00:00", "close_date": "2018-01-22 13:55:00+00:00", "trade_duration": 40, "open_rate": 4.168e-05, "close_rate": 4.188892230576441e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2399.232245681382, "profit_abs": 1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-22 14:00:00+00:00", "close_date": "2018-01-22 14:30:00+00:00", "trade_duration": 30, "open_rate": 8.821e-05, "close_rate": 8.953646616541353e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1133.6583153837435, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 15:55:00+00:00", "close_date": "2018-01-22 16:40:00+00:00", "trade_duration": 45, "open_rate": 5.172e-05, "close_rate": 5.1979248120300745e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1933.4880123743235, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-22 16:05:00+00:00", "close_date": "2018-01-22 16:25:00+00:00", "trade_duration": 20, "open_rate": 3.026e-05, "close_rate": 3.101839598997494e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3304.692663582287, "profit_abs": 0.0020000000000000157}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 19:50:00+00:00", "close_date": "2018-01-23 00:10:00+00:00", "trade_duration": 260, "open_rate": 0.07064, "close_rate": 0.07099408521303258, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.415628539071348, "profit_abs": 1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 21:25:00+00:00", "close_date": "2018-01-22 22:05:00+00:00", "trade_duration": 40, "open_rate": 0.01644483, "close_rate": 0.01652726022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.080938507725528, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-23 00:05:00+00:00", "close_date": "2018-01-23 00:35:00+00:00", "trade_duration": 30, "open_rate": 4.331e-05, "close_rate": 4.3961278195488714e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2308.935580697299, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-23 01:50:00+00:00", "close_date": "2018-01-23 02:15:00+00:00", "trade_duration": 25, "open_rate": 3.2e-05, "close_rate": 3.2802005012531326e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3125.0000000000005, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 04:25:00+00:00", "close_date": "2018-01-23 05:15:00+00:00", "trade_duration": 50, "open_rate": 0.09167706, "close_rate": 0.09213659413533835, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0907854156754153, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 07:35:00+00:00", "close_date": "2018-01-23 09:00:00+00:00", "trade_duration": 85, "open_rate": 0.0692498, "close_rate": 0.06959691679197995, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4440474918339115, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 10:50:00+00:00", "close_date": "2018-01-23 13:05:00+00:00", "trade_duration": 135, "open_rate": 3.182e-05, "close_rate": 3.197949874686716e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3142.677561282213, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 11:05:00+00:00", "close_date": "2018-01-23 16:05:00+00:00", "trade_duration": 300, "open_rate": 0.04088, "close_rate": 0.04108491228070175, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4461839530332683, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 14:55:00+00:00", "close_date": "2018-01-23 15:35:00+00:00", "trade_duration": 40, "open_rate": 5.15e-05, "close_rate": 5.175814536340851e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1941.747572815534, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 16:35:00+00:00", "close_date": "2018-01-24 00:05:00+00:00", "trade_duration": 450, "open_rate": 0.09071698, "close_rate": 0.09117170170426064, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1023294646713329, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 17:25:00+00:00", "close_date": "2018-01-23 18:45:00+00:00", "trade_duration": 80, "open_rate": 3.128e-05, "close_rate": 3.1436791979949865e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3196.9309462915603, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 20:15:00+00:00", "close_date": "2018-01-23 22:00:00+00:00", "trade_duration": 105, "open_rate": 9.555e-05, "close_rate": 9.602894736842104e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1046.5724751439038, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 22:30:00+00:00", "close_date": "2018-01-23 23:10:00+00:00", "trade_duration": 40, "open_rate": 0.04080001, "close_rate": 0.0410045213283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.450979791426522, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 23:50:00+00:00", "close_date": "2018-01-24 03:35:00+00:00", "trade_duration": 225, "open_rate": 5.163e-05, "close_rate": 5.18887969924812e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1936.8584156498162, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 00:20:00+00:00", "close_date": "2018-01-24 01:50:00+00:00", "trade_duration": 90, "open_rate": 0.04040781, "close_rate": 0.04061035541353383, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.474769110228938, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 06:45:00+00:00", "close_date": "2018-01-24 07:25:00+00:00", "trade_duration": 40, "open_rate": 5.132e-05, "close_rate": 5.157724310776942e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1948.5580670303975, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-24 14:15:00+00:00", "close_date": "2018-01-24 14:25:00+00:00", "trade_duration": 10, "open_rate": 5.198e-05, "close_rate": 5.432496240601503e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1923.8168526356292, "profit_abs": 0.0040000000000000036}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 14:50:00+00:00", "close_date": "2018-01-24 16:35:00+00:00", "trade_duration": 105, "open_rate": 3.054e-05, "close_rate": 3.069308270676692e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3274.3942370661425, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 15:10:00+00:00", "close_date": "2018-01-24 16:15:00+00:00", "trade_duration": 65, "open_rate": 9.263e-05, "close_rate": 9.309431077694236e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1079.5638562020945, "profit_abs": 2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 22:40:00+00:00", "close_date": "2018-01-24 23:25:00+00:00", "trade_duration": 45, "open_rate": 5.514e-05, "close_rate": 5.54163909774436e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1813.5654697134569, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 00:50:00+00:00", "close_date": "2018-01-25 01:30:00+00:00", "trade_duration": 40, "open_rate": 4.921e-05, "close_rate": 4.9456666666666664e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.1072952651903, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 08:15:00+00:00", "close_date": "2018-01-25 12:15:00+00:00", "trade_duration": 240, "open_rate": 0.0026, "close_rate": 0.002613032581453634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.46153846153847, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 10:25:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 350, "open_rate": 0.02799871, "close_rate": 0.028139054411027563, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.571593119825878, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 11:00:00+00:00", "close_date": "2018-01-25 11:45:00+00:00", "trade_duration": 45, "open_rate": 0.04078902, "close_rate": 0.0409934762406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4516401717913303, "profit_abs": -1.3877787807814457e-17}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:05:00+00:00", "close_date": "2018-01-25 13:45:00+00:00", "trade_duration": 40, "open_rate": 2.89e-05, "close_rate": 2.904486215538847e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3460.2076124567475, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:20:00+00:00", "close_date": "2018-01-25 14:05:00+00:00", "trade_duration": 45, "open_rate": 0.041103, "close_rate": 0.04130903007518797, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4329124394813033, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-25 15:45:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 30, "open_rate": 5.428e-05, "close_rate": 5.509624060150376e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1842.2991893883568, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 17:45:00+00:00", "close_date": "2018-01-25 23:15:00+00:00", "trade_duration": 330, "open_rate": 5.414e-05, "close_rate": 5.441137844611528e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1847.063169560399, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 21:15:00+00:00", "close_date": "2018-01-25 21:55:00+00:00", "trade_duration": 40, "open_rate": 0.04140777, "close_rate": 0.0416153277443609, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.415005686130888, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 02:05:00+00:00", "close_date": "2018-01-26 02:45:00+00:00", "trade_duration": 40, "open_rate": 0.00254309, "close_rate": 0.002555837318295739, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.32224183965177, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 02:55:00+00:00", "close_date": "2018-01-26 15:10:00+00:00", "trade_duration": 735, "open_rate": 5.607e-05, "close_rate": 5.6351052631578935e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1783.4849295523454, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 06:10:00+00:00", "close_date": "2018-01-26 09:25:00+00:00", "trade_duration": 195, "open_rate": 0.00253806, "close_rate": 0.0025507821052631577, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.400171784748984, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 07:25:00+00:00", "close_date": "2018-01-26 09:55:00+00:00", "trade_duration": 150, "open_rate": 0.0415, "close_rate": 0.04170802005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4096385542168677, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-26 09:55:00+00:00", "close_date": "2018-01-26 10:25:00+00:00", "trade_duration": 30, "open_rate": 5.321e-05, "close_rate": 5.401015037593984e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1879.3459875963165, "profit_abs": 0.000999999999999987}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 16:05:00+00:00", "close_date": "2018-01-26 16:45:00+00:00", "trade_duration": 40, "open_rate": 0.02772046, "close_rate": 0.02785940967418546, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.6074437437185387, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 23:35:00+00:00", "close_date": "2018-01-27 00:15:00+00:00", "trade_duration": 40, "open_rate": 0.09461341, "close_rate": 0.09508766268170424, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0569326272036914, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 00:35:00+00:00", "close_date": "2018-01-27 01:30:00+00:00", "trade_duration": 55, "open_rate": 5.615e-05, "close_rate": 5.643145363408521e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1780.9439002671415, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.07877175, "open_date": "2018-01-27 00:45:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 4560, "open_rate": 5.556e-05, "close_rate": 5.144e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1799.8560115190785, "profit_abs": -0.007896868250539965}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 02:30:00+00:00", "close_date": "2018-01-27 11:25:00+00:00", "trade_duration": 535, "open_rate": 0.06900001, "close_rate": 0.06934587471177944, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492751522789635, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 06:25:00+00:00", "close_date": "2018-01-27 07:05:00+00:00", "trade_duration": 40, "open_rate": 0.09449985, "close_rate": 0.0949735334586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.058202737887944, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.04815133, "open_date": "2018-01-27 09:40:00+00:00", "close_date": "2018-01-30 04:40:00+00:00", "trade_duration": 4020, "open_rate": 0.0410697, "close_rate": 0.03928809, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4348850855983852, "profit_abs": -0.004827170578309559}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 11:45:00+00:00", "close_date": "2018-01-27 12:30:00+00:00", "trade_duration": 45, "open_rate": 0.0285, "close_rate": 0.02864285714285714, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5087719298245617, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 12:35:00+00:00", "close_date": "2018-01-27 15:25:00+00:00", "trade_duration": 170, "open_rate": 0.02866372, "close_rate": 0.02880739779448621, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.4887307020861216, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 15:50:00+00:00", "close_date": "2018-01-27 16:50:00+00:00", "trade_duration": 60, "open_rate": 0.095381, "close_rate": 0.09585910025062656, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0484268355332824, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 17:05:00+00:00", "close_date": "2018-01-27 17:45:00+00:00", "trade_duration": 40, "open_rate": 0.06759092, "close_rate": 0.06792972160401002, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4794886650455417, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 23:40:00+00:00", "close_date": "2018-01-28 01:05:00+00:00", "trade_duration": 85, "open_rate": 0.00258501, "close_rate": 0.002597967443609022, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.684569885609726, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 02:25:00+00:00", "close_date": "2018-01-28 08:10:00+00:00", "trade_duration": 345, "open_rate": 0.06698502, "close_rate": 0.0673207845112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4928710926711672, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 10:25:00+00:00", "close_date": "2018-01-28 16:30:00+00:00", "trade_duration": 365, "open_rate": 0.0677177, "close_rate": 0.06805713709273183, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4767187899175547, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-28 20:35:00+00:00", "close_date": "2018-01-28 21:35:00+00:00", "trade_duration": 60, "open_rate": 5.215e-05, "close_rate": 5.2411403508771925e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1917.5455417066157, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-28 22:00:00+00:00", "close_date": "2018-01-28 22:30:00+00:00", "trade_duration": 30, "open_rate": 0.00273809, "close_rate": 0.002779264285714285, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.5218089982433, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-29 00:00:00+00:00", "close_date": "2018-01-29 00:30:00+00:00", "trade_duration": 30, "open_rate": 0.00274632, "close_rate": 0.002787618045112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.412362725392526, "profit_abs": 0.0010000000000000148}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-29 02:15:00+00:00", "close_date": "2018-01-29 03:00:00+00:00", "trade_duration": 45, "open_rate": 0.01622478, "close_rate": 0.016306107218045113, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.163411768911504, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 03:05:00+00:00", "close_date": "2018-01-29 03:45:00+00:00", "trade_duration": 40, "open_rate": 0.069, "close_rate": 0.06934586466165413, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492753623188406, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 05:20:00+00:00", "close_date": "2018-01-29 06:55:00+00:00", "trade_duration": 95, "open_rate": 8.755e-05, "close_rate": 8.798884711779448e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1142.204454597373, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 07:00:00+00:00", "close_date": "2018-01-29 19:25:00+00:00", "trade_duration": 745, "open_rate": 0.06825763, "close_rate": 0.06859977350877192, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4650376815016872, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 19:45:00+00:00", "close_date": "2018-01-29 20:25:00+00:00", "trade_duration": 40, "open_rate": 0.06713892, "close_rate": 0.06747545593984962, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4894490408841845, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0199116, "open_date": "2018-01-29 23:30:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 315, "open_rate": 8.934e-05, "close_rate": 8.8e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1119.3194537721067, "profit_abs": -0.0019961383478844796}], "results_per_pair": [{"key": "TRX/BTC", "trades": 15, "profit_mean": 0.0023467073333333323, "profit_mean_pct": 0.23467073333333321, "profit_sum": 0.035200609999999986, "profit_sum_pct": 3.5200609999999988, "profit_total_abs": 0.0035288616521155086, "profit_total_pct": 1.1733536666666662, "duration_avg": "2:28:00", "wins": 9, "draws": 2, "losses": 4}, {"key": "ADA/BTC", "trades": 29, "profit_mean": -0.0011598141379310352, "profit_mean_pct": -0.11598141379310352, "profit_sum": -0.03363461000000002, "profit_sum_pct": -3.3634610000000023, "profit_total_abs": -0.0033718682505400333, "profit_total_pct": -1.1211536666666675, "duration_avg": "5:35:00", "wins": 9, "draws": 11, "losses": 9}, {"key": "XLM/BTC", "trades": 21, "profit_mean": 0.0026243899999999994, "profit_mean_pct": 0.2624389999999999, "profit_sum": 0.05511218999999999, "profit_sum_pct": 5.511218999999999, "profit_total_abs": 0.005525000000000002, "profit_total_pct": 1.8370729999999995, "duration_avg": "3:21:00", "wins": 12, "draws": 3, "losses": 6}, {"key": "ETH/BTC", "trades": 21, "profit_mean": 0.0009500057142857142, "profit_mean_pct": 0.09500057142857142, "profit_sum": 0.01995012, "profit_sum_pct": 1.9950119999999998, "profit_total_abs": 0.0019999999999999463, "profit_total_pct": 0.6650039999999999, "duration_avg": "2:17:00", "wins": 5, "draws": 10, "losses": 6}, {"key": "XMR/BTC", "trades": 16, "profit_mean": -0.0027899012500000007, "profit_mean_pct": -0.2789901250000001, "profit_sum": -0.04463842000000001, "profit_sum_pct": -4.463842000000001, "profit_total_abs": -0.0044750000000000345, "profit_total_pct": -1.4879473333333337, "duration_avg": "8:41:00", "wins": 6, "draws": 5, "losses": 5}, {"key": "ZEC/BTC", "trades": 21, "profit_mean": -0.00039290904761904774, "profit_mean_pct": -0.03929090476190478, "profit_sum": -0.008251090000000003, "profit_sum_pct": -0.8251090000000003, "profit_total_abs": -0.000827170578309569, "profit_total_pct": -0.27503633333333344, "duration_avg": "4:17:00", "wins": 8, "draws": 7, "losses": 6}, {"key": "NXT/BTC", "trades": 12, "profit_mean": -0.0012261025000000006, "profit_mean_pct": -0.12261025000000006, "profit_sum": -0.014713230000000008, "profit_sum_pct": -1.4713230000000008, "profit_total_abs": -0.0014750000000000874, "profit_total_pct": -0.4904410000000003, "duration_avg": "0:57:00", "wins": 4, "draws": 3, "losses": 5}, {"key": "LTC/BTC", "trades": 8, "profit_mean": 0.00748129625, "profit_mean_pct": 0.748129625, "profit_sum": 0.05985037, "profit_sum_pct": 5.985037, "profit_total_abs": 0.006000000000000019, "profit_total_pct": 1.9950123333333334, "duration_avg": "1:59:00", "wins": 5, "draws": 2, "losses": 1}, {"key": "ETC/BTC", "trades": 20, "profit_mean": 0.0022568569999999997, "profit_mean_pct": 0.22568569999999996, "profit_sum": 0.04513713999999999, "profit_sum_pct": 4.513713999999999, "profit_total_abs": 0.004525000000000001, "profit_total_pct": 1.504571333333333, "duration_avg": "1:45:00", "wins": 11, "draws": 4, "losses": 5}, {"key": "DASH/BTC", "trades": 16, "profit_mean": 0.0018703237499999997, "profit_mean_pct": 0.18703237499999997, "profit_sum": 0.029925179999999996, "profit_sum_pct": 2.9925179999999996, "profit_total_abs": 0.002999999999999961, "profit_total_pct": 0.9975059999999999, "duration_avg": "3:03:00", "wins": 4, "draws": 7, "losses": 5}, {"key": "TOTAL", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}], "sell_reason_summary": [{"sell_reason": "roi", "trades": 170, "wins": 73, "draws": 54, "losses": 43, "profit_mean": 0.005398268352941177, "profit_mean_pct": 0.54, "profit_sum": 0.91770562, "profit_sum_pct": 91.77, "profit_total_abs": 0.09199999999999964, "profit_pct_total": 30.59}, {"sell_reason": "stop_loss", "trades": 6, "wins": 0, "draws": 0, "losses": 6, "profit_mean": -0.10448878000000002, "profit_mean_pct": -10.45, "profit_sum": -0.6269326800000001, "profit_sum_pct": -62.69, "profit_total_abs": -0.06284999999999992, "profit_pct_total": -20.9}, {"sell_reason": "force_sell", "trades": 3, "wins": 0, "draws": 0, "losses": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.89, "profit_sum": -0.14683468, "profit_sum_pct": -14.68, "profit_total_abs": -0.014720177176734003, "profit_pct_total": -4.89}], "left_open_trades": [{"key": "TRX/BTC", "trades": 1, "profit_mean": -0.0199116, "profit_mean_pct": -1.9911600000000003, "profit_sum": -0.0199116, "profit_sum_pct": -1.9911600000000003, "profit_total_abs": -0.0019961383478844796, "profit_total_pct": -0.6637200000000001, "duration_avg": "5:15:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ADA/BTC", "trades": 1, "profit_mean": -0.07877175, "profit_mean_pct": -7.877175, "profit_sum": -0.07877175, "profit_sum_pct": -7.877175, "profit_total_abs": -0.007896868250539965, "profit_total_pct": -2.625725, "duration_avg": "3 days, 4:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ZEC/BTC", "trades": 1, "profit_mean": -0.04815133, "profit_mean_pct": -4.815133, "profit_sum": -0.04815133, "profit_sum_pct": -4.815133, "profit_total_abs": -0.004827170578309559, "profit_total_pct": -1.6050443333333335, "duration_avg": "2 days, 19:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "TOTAL", "trades": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.894489333333333, "profit_sum": -0.14683468, "profit_sum_pct": -14.683468, "profit_total_abs": -0.014720177176734003, "profit_total_pct": -4.8944893333333335, "duration_avg": "2 days, 1:25:00", "wins": 0, "draws": 0, "losses": 3}], "total_trades": 179, "backtest_start": "2018-01-30 04:45:00+00:00", "backtest_start_ts": 1517287500, "backtest_end": "2018-01-30 04:45:00+00:00", "backtest_end_ts": 1517287500, "backtest_days": 0, "trades_per_day": null, "market_change": 0.25, "stake_amount": 0.1, "max_drawdown": 0.21142322000000008, "drawdown_start": "2018-01-24 14:25:00+00:00", "drawdown_start_ts": 1516803900.0, "drawdown_end": "2018-01-30 04:45:00+00:00", "drawdown_end_ts": 1517287500.0, "pairlist": ["TRX/BTC", "ADA/BTC", "XLM/BTC", "ETH/BTC", "XMR/BTC", "ZEC/BTC","NXT/BTC", "LTC/BTC", "ETC/BTC", "DASH/BTC"]}, "TestStrategy": {"trades": [{"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:20:00+00:00", "trade_duration": 5, "open_rate": 9.64e-05, "close_rate": 0.00010074887218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1037.344398340249, "profit_abs": 0.00399999999999999}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:30:00+00:00", "trade_duration": 15, "open_rate": 4.756e-05, "close_rate": 4.9705563909774425e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2102.6072329688814, "profit_abs": 0.00399999999999999}, {"pair": "XLM/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:35:00+00:00", "trade_duration": 10, "open_rate": 3.339e-05, "close_rate": 3.489631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2994.908655286014, "profit_abs": 0.0040000000000000036}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:40:00+00:00", "trade_duration": 15, "open_rate": 9.696e-05, "close_rate": 0.00010133413533834584, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1031.3531353135315, "profit_abs": 0.00399999999999999}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 07:35:00+00:00", "close_date": "2018-01-10 08:35:00+00:00", "trade_duration": 60, "open_rate": 0.0943, "close_rate": 0.09477268170426063, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0604453870625663, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 07:40:00+00:00", "close_date": "2018-01-10 08:10:00+00:00", "trade_duration": 30, "open_rate": 0.02719607, "close_rate": 0.02760503345864661, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.677001860930642, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 08:15:00+00:00", "close_date": "2018-01-10 09:55:00+00:00", "trade_duration": 100, "open_rate": 0.04634952, "close_rate": 0.046581848421052625, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1575196463739, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 14:45:00+00:00", "close_date": "2018-01-10 15:50:00+00:00", "trade_duration": 65, "open_rate": 3.066e-05, "close_rate": 3.081368421052631e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3261.5786040443577, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 16:35:00+00:00", "close_date": "2018-01-10 17:15:00+00:00", "trade_duration": 40, "open_rate": 0.0168999, "close_rate": 0.016984611278195488, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.917194776300452, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 16:40:00+00:00", "close_date": "2018-01-10 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.09132568, "close_rate": 0.0917834528320802, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0949822656672252, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 18:50:00+00:00", "close_date": "2018-01-10 19:45:00+00:00", "trade_duration": 55, "open_rate": 0.08898003, "close_rate": 0.08942604518796991, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1238476768326557, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 22:15:00+00:00", "close_date": "2018-01-10 23:00:00+00:00", "trade_duration": 45, "open_rate": 0.08560008, "close_rate": 0.08602915308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1682232072680307, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 22:50:00+00:00", "close_date": "2018-01-10 23:20:00+00:00", "trade_duration": 30, "open_rate": 0.00249083, "close_rate": 0.0025282860902255634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 40.147260150231055, "profit_abs": 0.000999999999999987}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 23:15:00+00:00", "close_date": "2018-01-11 00:15:00+00:00", "trade_duration": 60, "open_rate": 3.022e-05, "close_rate": 3.037147869674185e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3309.0668431502318, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-10 23:40:00+00:00", "close_date": "2018-01-11 00:05:00+00:00", "trade_duration": 25, "open_rate": 0.002437, "close_rate": 0.0024980776942355883, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.03405826836274, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 00:00:00+00:00", "close_date": "2018-01-11 00:35:00+00:00", "trade_duration": 35, "open_rate": 0.04771803, "close_rate": 0.04843559436090225, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0956439316543456, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-11 03:40:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 45, "open_rate": 3.651e-05, "close_rate": 3.2859000000000005e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2738.9756231169545, "profit_abs": -0.01047499999999997}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 03:55:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 30, "open_rate": 0.08824105, "close_rate": 0.08956798308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1332594070446804, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 04:00:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 50, "open_rate": 0.00243, "close_rate": 0.002442180451127819, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.1522633744856, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:55:00+00:00", "trade_duration": 25, "open_rate": 0.04545064, "close_rate": 0.046589753784461146, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.200189040242338, "profit_abs": 0.001999999999999988}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 20, "open_rate": 3.372e-05, "close_rate": 3.456511278195488e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2965.599051008304, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:55:00+00:00", "close_date": "2018-01-11 05:15:00+00:00", "trade_duration": 20, "open_rate": 0.02644, "close_rate": 0.02710265664160401, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7821482602118004, "profit_abs": 0.001999999999999988}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:20:00+00:00", "close_date": "2018-01-11 12:00:00+00:00", "trade_duration": 40, "open_rate": 0.08812, "close_rate": 0.08856170426065162, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1348161597821154, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:35:00+00:00", "close_date": "2018-01-11 12:15:00+00:00", "trade_duration": 40, "open_rate": 0.02683577, "close_rate": 0.026970285137844607, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7263696923919087, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 14:00:00+00:00", "close_date": "2018-01-11 14:25:00+00:00", "trade_duration": 25, "open_rate": 4.919e-05, "close_rate": 5.04228320802005e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.9335230737956, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 19:25:00+00:00", "close_date": "2018-01-11 20:35:00+00:00", "trade_duration": 70, "open_rate": 0.08784896, "close_rate": 0.08828930566416039, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1383174029607181, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:35:00+00:00", "close_date": "2018-01-11 23:30:00+00:00", "trade_duration": 55, "open_rate": 5.105e-05, "close_rate": 5.130588972431077e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1958.8638589618022, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:25:00+00:00", "trade_duration": 30, "open_rate": 3.96e-05, "close_rate": 4.019548872180451e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2525.252525252525, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:35:00+00:00", "trade_duration": 40, "open_rate": 2.885e-05, "close_rate": 2.899461152882205e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3466.204506065858, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 23:30:00+00:00", "close_date": "2018-01-12 00:05:00+00:00", "trade_duration": 35, "open_rate": 0.02645, "close_rate": 0.026847744360902256, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.780718336483932, "profit_abs": 0.0010000000000000148}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 23:55:00+00:00", "close_date": "2018-01-12 01:15:00+00:00", "trade_duration": 80, "open_rate": 0.048, "close_rate": 0.04824060150375939, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0833333333333335, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-12 21:15:00+00:00", "close_date": "2018-01-12 21:40:00+00:00", "trade_duration": 25, "open_rate": 4.692e-05, "close_rate": 4.809593984962405e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2131.287297527707, "profit_abs": 0.001999999999999974}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 00:55:00+00:00", "close_date": "2018-01-13 06:20:00+00:00", "trade_duration": 325, "open_rate": 0.00256966, "close_rate": 0.0025825405012531327, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.91565421106294, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 10:55:00+00:00", "close_date": "2018-01-13 11:35:00+00:00", "trade_duration": 40, "open_rate": 6.262e-05, "close_rate": 6.293388471177944e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1596.933886937081, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 13:05:00+00:00", "close_date": "2018-01-15 14:10:00+00:00", "trade_duration": 2945, "open_rate": 4.73e-05, "close_rate": 4.753709273182957e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2114.1649048625795, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:30:00+00:00", "close_date": "2018-01-13 14:45:00+00:00", "trade_duration": 75, "open_rate": 6.063e-05, "close_rate": 6.0933909774436085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1649.348507339601, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:40:00+00:00", "close_date": "2018-01-13 23:30:00+00:00", "trade_duration": 590, "open_rate": 0.00011082, "close_rate": 0.00011137548872180448, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 902.3641941887746, "profit_abs": -2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 15:15:00+00:00", "close_date": "2018-01-13 15:55:00+00:00", "trade_duration": 40, "open_rate": 5.93e-05, "close_rate": 5.9597243107769415e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1686.3406408094436, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 16:30:00+00:00", "close_date": "2018-01-13 17:10:00+00:00", "trade_duration": 40, "open_rate": 0.04850003, "close_rate": 0.04874313791979949, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0618543947292407, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 22:05:00+00:00", "close_date": "2018-01-14 06:25:00+00:00", "trade_duration": 500, "open_rate": 0.09825019, "close_rate": 0.09874267215538848, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0178097365511456, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 00:20:00+00:00", "close_date": "2018-01-14 22:55:00+00:00", "trade_duration": 1355, "open_rate": 6.018e-05, "close_rate": 6.048165413533834e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1661.681621801263, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 12:45:00+00:00", "close_date": "2018-01-14 13:25:00+00:00", "trade_duration": 40, "open_rate": 0.09758999, "close_rate": 0.0980791628822055, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.024695258191952, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-14 15:30:00+00:00", "close_date": "2018-01-14 16:00:00+00:00", "trade_duration": 30, "open_rate": 0.00311, "close_rate": 0.0031567669172932328, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.154340836012864, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 20:45:00+00:00", "close_date": "2018-01-14 22:15:00+00:00", "trade_duration": 90, "open_rate": 0.00312401, "close_rate": 0.003139669197994987, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.010140812609436, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 23:35:00+00:00", "close_date": "2018-01-15 00:30:00+00:00", "trade_duration": 55, "open_rate": 0.0174679, "close_rate": 0.017555458395989976, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.724786608579165, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 23:45:00+00:00", "close_date": "2018-01-15 00:25:00+00:00", "trade_duration": 40, "open_rate": 0.07346846, "close_rate": 0.07383672295739348, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.3611282991367997, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 02:25:00+00:00", "close_date": "2018-01-15 03:05:00+00:00", "trade_duration": 40, "open_rate": 0.097994, "close_rate": 0.09848519799498744, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.020470641059657, "profit_abs": -2.7755575615628914e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 07:20:00+00:00", "close_date": "2018-01-15 08:00:00+00:00", "trade_duration": 40, "open_rate": 0.09659, "close_rate": 0.09707416040100247, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0353038616834043, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-15 08:20:00+00:00", "close_date": "2018-01-15 08:55:00+00:00", "trade_duration": 35, "open_rate": 9.987e-05, "close_rate": 0.00010137180451127818, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1001.3016921998599, "profit_abs": 0.0010000000000000009}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-15 12:10:00+00:00", "close_date": "2018-01-16 02:50:00+00:00", "trade_duration": 880, "open_rate": 0.0948969, "close_rate": 0.09537257368421052, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0537752023511833, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:10:00+00:00", "close_date": "2018-01-15 17:40:00+00:00", "trade_duration": 210, "open_rate": 0.071, "close_rate": 0.07135588972431077, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4084507042253522, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:30:00+00:00", "close_date": "2018-01-15 15:10:00+00:00", "trade_duration": 40, "open_rate": 0.04600501, "close_rate": 0.046235611553884705, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.173676301776698, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:10:00+00:00", "close_date": "2018-01-15 19:25:00+00:00", "trade_duration": 75, "open_rate": 9.438e-05, "close_rate": 9.485308270676693e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1059.5465140919687, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:35:00+00:00", "close_date": "2018-01-15 19:15:00+00:00", "trade_duration": 40, "open_rate": 0.03040001, "close_rate": 0.030552391002506264, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.2894726021471703, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-15 20:25:00+00:00", "close_date": "2018-01-16 08:25:00+00:00", "trade_duration": 720, "open_rate": 5.837e-05, "close_rate": 5.2533e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1713.2088401576154, "profit_abs": -0.010474999999999984}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 20:40:00+00:00", "close_date": "2018-01-15 22:00:00+00:00", "trade_duration": 80, "open_rate": 0.046036, "close_rate": 0.04626675689223057, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1722130506560084, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 00:30:00+00:00", "close_date": "2018-01-16 01:10:00+00:00", "trade_duration": 40, "open_rate": 0.0028685, "close_rate": 0.0028828784461152877, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 34.86142583231654, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 01:15:00+00:00", "close_date": "2018-01-16 02:35:00+00:00", "trade_duration": 80, "open_rate": 0.06731755, "close_rate": 0.0676549813283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4854967241083492, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 07:45:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 55, "open_rate": 0.09217614, "close_rate": 0.09263817578947368, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0848794492804754, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:55:00+00:00", "trade_duration": 20, "open_rate": 0.0165, "close_rate": 0.016913533834586467, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.0606060606060606, "profit_abs": 0.0020000000000000018}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 5, "open_rate": 7.953e-05, "close_rate": 8.311781954887218e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1257.387149503332, "profit_abs": 0.00399999999999999}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 08:45:00+00:00", "close_date": "2018-01-16 09:50:00+00:00", "trade_duration": 65, "open_rate": 0.045202, "close_rate": 0.04542857644110275, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2122914915269236, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:45:00+00:00", "trade_duration": 30, "open_rate": 5.248e-05, "close_rate": 5.326917293233082e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1905.487804878049, "profit_abs": 0.0010000000000000009}, {"pair": "XMR/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:55:00+00:00", "trade_duration": 40, "open_rate": 0.02892318, "close_rate": 0.02906815834586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.457434486802627, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 09:50:00+00:00", "close_date": "2018-01-16 10:10:00+00:00", "trade_duration": 20, "open_rate": 5.158e-05, "close_rate": 5.287273182957392e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1938.735944164405, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:35:00+00:00", "trade_duration": 30, "open_rate": 0.02828232, "close_rate": 0.02870761804511278, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5357778286929786, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:40:00+00:00", "trade_duration": 35, "open_rate": 0.04357584, "close_rate": 0.044231115789473675, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.294849623093898, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 13:45:00+00:00", "close_date": "2018-01-16 14:20:00+00:00", "trade_duration": 35, "open_rate": 5.362e-05, "close_rate": 5.442631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1864.975755315181, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 17:30:00+00:00", "close_date": "2018-01-16 18:25:00+00:00", "trade_duration": 55, "open_rate": 5.302e-05, "close_rate": 5.328576441102756e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.0807242549984, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:45:00+00:00", "trade_duration": 30, "open_rate": 0.09129999, "close_rate": 0.09267292218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0952903718828448, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:35:00+00:00", "trade_duration": 20, "open_rate": 3.808e-05, "close_rate": 3.903438596491228e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2626.0504201680674, "profit_abs": 0.0020000000000000018}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 19:00:00+00:00", "close_date": "2018-01-16 19:30:00+00:00", "trade_duration": 30, "open_rate": 0.02811012, "close_rate": 0.028532828571428567, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.557437677249333, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 60, "open_rate": 0.00258379, "close_rate": 0.002325411, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.702835756775904, "profit_abs": -0.010474999999999984}, {"pair": "NXT/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 80, "open_rate": 2.559e-05, "close_rate": 2.3031e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3907.7764751856193, "profit_abs": -0.010474999999999998}, {"pair": "TRX/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:35:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 50, "open_rate": 7.62e-05, "close_rate": 6.858e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1312.3359580052495, "profit_abs": -0.010474999999999984}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:35:00+00:00", "trade_duration": 5, "open_rate": 0.00229844, "close_rate": 0.002402129022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 43.507770487809125, "profit_abs": 0.004000000000000017}, {"pair": "LTC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:40:00+00:00", "trade_duration": 10, "open_rate": 0.0151, "close_rate": 0.015781203007518795, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.622516556291391, "profit_abs": 0.00399999999999999}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:40:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 5, "open_rate": 0.00235676, "close_rate": 0.00246308, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 42.431134269081284, "profit_abs": 0.0040000000000000036}, {"pair": "DASH/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 22:45:00+00:00", "close_date": "2018-01-16 23:05:00+00:00", "trade_duration": 20, "open_rate": 0.0630692, "close_rate": 0.06464988170426066, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.585559988076589, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:50:00+00:00", "close_date": "2018-01-16 22:55:00+00:00", "trade_duration": 5, "open_rate": 2.2e-05, "close_rate": 2.299248120300751e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 4545.454545454546, "profit_abs": 0.003999999999999976}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-17 03:30:00+00:00", "close_date": "2018-01-17 04:00:00+00:00", "trade_duration": 30, "open_rate": 4.974e-05, "close_rate": 5.048796992481203e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2010.454362685967, "profit_abs": 0.0010000000000000009}, {"pair": "TRX/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-17 03:55:00+00:00", "close_date": "2018-01-17 04:15:00+00:00", "trade_duration": 20, "open_rate": 7.108e-05, "close_rate": 7.28614536340852e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1406.8655036578502, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 09:35:00+00:00", "close_date": "2018-01-17 10:15:00+00:00", "trade_duration": 40, "open_rate": 0.04327, "close_rate": 0.04348689223057644, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.3110700254217704, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:20:00+00:00", "close_date": "2018-01-17 17:00:00+00:00", "trade_duration": 400, "open_rate": 4.997e-05, "close_rate": 5.022047619047618e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2001.2007204322595, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:25:00+00:00", "trade_duration": 55, "open_rate": 0.06836818, "close_rate": 0.06871087764411027, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4626687444363737, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:10:00+00:00", "trade_duration": 40, "open_rate": 3.63e-05, "close_rate": 3.648195488721804e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2754.8209366391184, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:30:00+00:00", "close_date": "2018-01-17 22:05:00+00:00", "trade_duration": 575, "open_rate": 0.0281, "close_rate": 0.02824085213032581, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5587188612099645, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:35:00+00:00", "close_date": "2018-01-17 16:55:00+00:00", "trade_duration": 260, "open_rate": 0.08651001, "close_rate": 0.08694364413533832, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1559355963546878, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 05:00:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 55, "open_rate": 5.633e-05, "close_rate": 5.6612355889724306e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1775.2529735487308, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 05:20:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 35, "open_rate": 0.06988494, "close_rate": 0.07093584135338346, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.430923457900944, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 07:35:00+00:00", "close_date": "2018-01-18 08:15:00+00:00", "trade_duration": 40, "open_rate": 5.545e-05, "close_rate": 5.572794486215538e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1803.4265103697026, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 09:00:00+00:00", "close_date": "2018-01-18 09:40:00+00:00", "trade_duration": 40, "open_rate": 0.01633527, "close_rate": 0.016417151052631574, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.121723118136401, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 16:40:00+00:00", "close_date": "2018-01-18 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.00269734, "close_rate": 0.002710860501253133, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.073561360451414, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-18 18:05:00+00:00", "close_date": "2018-01-18 18:30:00+00:00", "trade_duration": 25, "open_rate": 4.475e-05, "close_rate": 4.587155388471177e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2234.63687150838, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 18:25:00+00:00", "close_date": "2018-01-18 18:55:00+00:00", "trade_duration": 30, "open_rate": 2.79e-05, "close_rate": 2.8319548872180444e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3584.2293906810037, "profit_abs": 0.000999999999999987}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 20:10:00+00:00", "close_date": "2018-01-18 20:50:00+00:00", "trade_duration": 40, "open_rate": 0.04439326, "close_rate": 0.04461578260651629, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2525942001105577, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 21:30:00+00:00", "close_date": "2018-01-19 00:35:00+00:00", "trade_duration": 185, "open_rate": 4.49e-05, "close_rate": 4.51250626566416e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2227.1714922049, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 21:55:00+00:00", "close_date": "2018-01-19 05:05:00+00:00", "trade_duration": 430, "open_rate": 0.02855, "close_rate": 0.028693107769423555, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.502626970227671, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 22:10:00+00:00", "close_date": "2018-01-18 22:50:00+00:00", "trade_duration": 40, "open_rate": 5.796e-05, "close_rate": 5.8250526315789473e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1725.3278122843342, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 23:50:00+00:00", "close_date": "2018-01-19 00:30:00+00:00", "trade_duration": 40, "open_rate": 0.04340323, "close_rate": 0.04362079005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.303975994413319, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-19 16:45:00+00:00", "close_date": "2018-01-19 17:35:00+00:00", "trade_duration": 50, "open_rate": 0.04454455, "close_rate": 0.04476783095238095, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.244943545282195, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:15:00+00:00", "close_date": "2018-01-19 19:55:00+00:00", "trade_duration": 160, "open_rate": 5.62e-05, "close_rate": 5.648170426065162e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1779.3594306049824, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:20:00+00:00", "close_date": "2018-01-19 20:15:00+00:00", "trade_duration": 175, "open_rate": 4.339e-05, "close_rate": 4.360749373433584e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2304.6784973496196, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-20 04:45:00+00:00", "close_date": "2018-01-20 17:35:00+00:00", "trade_duration": 770, "open_rate": 0.0001009, "close_rate": 0.00010140576441102755, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 991.0802775024778, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 15:15:00+00:00", "trade_duration": 625, "open_rate": 0.00270505, "close_rate": 0.002718609147869674, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.96789338459548, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 07:00:00+00:00", "trade_duration": 130, "open_rate": 0.03000002, "close_rate": 0.030150396040100245, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.3333311111125927, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 09:00:00+00:00", "close_date": "2018-01-20 09:40:00+00:00", "trade_duration": 40, "open_rate": 5.46e-05, "close_rate": 5.4873684210526304e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1831.5018315018317, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-20 18:25:00+00:00", "close_date": "2018-01-25 03:50:00+00:00", "trade_duration": 6325, "open_rate": 0.03082222, "close_rate": 0.027739998, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.244412634781012, "profit_abs": -0.010474999999999998}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 22:25:00+00:00", "close_date": "2018-01-20 23:15:00+00:00", "trade_duration": 50, "open_rate": 0.08969999, "close_rate": 0.09014961401002504, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1148273260677064, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 02:50:00+00:00", "close_date": "2018-01-21 14:30:00+00:00", "trade_duration": 700, "open_rate": 0.01632501, "close_rate": 0.01640683962406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.125570520324337, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 10:20:00+00:00", "close_date": "2018-01-21 11:00:00+00:00", "trade_duration": 40, "open_rate": 0.070538, "close_rate": 0.07089157393483708, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.417675579120474, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 15:50:00+00:00", "close_date": "2018-01-21 18:45:00+00:00", "trade_duration": 175, "open_rate": 5.301e-05, "close_rate": 5.327571428571427e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.4365214110546, "profit_abs": -2.7755575615628914e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 16:20:00+00:00", "close_date": "2018-01-21 17:00:00+00:00", "trade_duration": 40, "open_rate": 3.955e-05, "close_rate": 3.9748245614035085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2528.4450063211125, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:45:00+00:00", "trade_duration": 30, "open_rate": 0.00258505, "close_rate": 0.002623922932330827, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.6839712964933, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:55:00+00:00", "trade_duration": 40, "open_rate": 3.903e-05, "close_rate": 3.922563909774435e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2562.1316935690497, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 00:35:00+00:00", "close_date": "2018-01-22 10:35:00+00:00", "trade_duration": 600, "open_rate": 5.236e-05, "close_rate": 5.262245614035087e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1909.8548510313217, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 01:30:00+00:00", "close_date": "2018-01-22 02:10:00+00:00", "trade_duration": 40, "open_rate": 9.028e-05, "close_rate": 9.07325313283208e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1107.6650420912717, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 12:25:00+00:00", "close_date": "2018-01-22 14:35:00+00:00", "trade_duration": 130, "open_rate": 0.002687, "close_rate": 0.002700468671679198, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.21622627465575, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 13:15:00+00:00", "close_date": "2018-01-22 13:55:00+00:00", "trade_duration": 40, "open_rate": 4.168e-05, "close_rate": 4.188892230576441e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2399.232245681382, "profit_abs": 1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-22 14:00:00+00:00", "close_date": "2018-01-22 14:30:00+00:00", "trade_duration": 30, "open_rate": 8.821e-05, "close_rate": 8.953646616541353e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1133.6583153837435, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 15:55:00+00:00", "close_date": "2018-01-22 16:40:00+00:00", "trade_duration": 45, "open_rate": 5.172e-05, "close_rate": 5.1979248120300745e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1933.4880123743235, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-22 16:05:00+00:00", "close_date": "2018-01-22 16:25:00+00:00", "trade_duration": 20, "open_rate": 3.026e-05, "close_rate": 3.101839598997494e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3304.692663582287, "profit_abs": 0.0020000000000000157}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 19:50:00+00:00", "close_date": "2018-01-23 00:10:00+00:00", "trade_duration": 260, "open_rate": 0.07064, "close_rate": 0.07099408521303258, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.415628539071348, "profit_abs": 1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 21:25:00+00:00", "close_date": "2018-01-22 22:05:00+00:00", "trade_duration": 40, "open_rate": 0.01644483, "close_rate": 0.01652726022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.080938507725528, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-23 00:05:00+00:00", "close_date": "2018-01-23 00:35:00+00:00", "trade_duration": 30, "open_rate": 4.331e-05, "close_rate": 4.3961278195488714e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2308.935580697299, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-23 01:50:00+00:00", "close_date": "2018-01-23 02:15:00+00:00", "trade_duration": 25, "open_rate": 3.2e-05, "close_rate": 3.2802005012531326e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3125.0000000000005, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 04:25:00+00:00", "close_date": "2018-01-23 05:15:00+00:00", "trade_duration": 50, "open_rate": 0.09167706, "close_rate": 0.09213659413533835, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0907854156754153, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 07:35:00+00:00", "close_date": "2018-01-23 09:00:00+00:00", "trade_duration": 85, "open_rate": 0.0692498, "close_rate": 0.06959691679197995, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4440474918339115, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 10:50:00+00:00", "close_date": "2018-01-23 13:05:00+00:00", "trade_duration": 135, "open_rate": 3.182e-05, "close_rate": 3.197949874686716e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3142.677561282213, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 11:05:00+00:00", "close_date": "2018-01-23 16:05:00+00:00", "trade_duration": 300, "open_rate": 0.04088, "close_rate": 0.04108491228070175, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4461839530332683, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 14:55:00+00:00", "close_date": "2018-01-23 15:35:00+00:00", "trade_duration": 40, "open_rate": 5.15e-05, "close_rate": 5.175814536340851e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1941.747572815534, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 16:35:00+00:00", "close_date": "2018-01-24 00:05:00+00:00", "trade_duration": 450, "open_rate": 0.09071698, "close_rate": 0.09117170170426064, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1023294646713329, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 17:25:00+00:00", "close_date": "2018-01-23 18:45:00+00:00", "trade_duration": 80, "open_rate": 3.128e-05, "close_rate": 3.1436791979949865e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3196.9309462915603, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 20:15:00+00:00", "close_date": "2018-01-23 22:00:00+00:00", "trade_duration": 105, "open_rate": 9.555e-05, "close_rate": 9.602894736842104e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1046.5724751439038, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 22:30:00+00:00", "close_date": "2018-01-23 23:10:00+00:00", "trade_duration": 40, "open_rate": 0.04080001, "close_rate": 0.0410045213283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.450979791426522, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 23:50:00+00:00", "close_date": "2018-01-24 03:35:00+00:00", "trade_duration": 225, "open_rate": 5.163e-05, "close_rate": 5.18887969924812e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1936.8584156498162, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 00:20:00+00:00", "close_date": "2018-01-24 01:50:00+00:00", "trade_duration": 90, "open_rate": 0.04040781, "close_rate": 0.04061035541353383, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.474769110228938, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 06:45:00+00:00", "close_date": "2018-01-24 07:25:00+00:00", "trade_duration": 40, "open_rate": 5.132e-05, "close_rate": 5.157724310776942e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1948.5580670303975, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-24 14:15:00+00:00", "close_date": "2018-01-24 14:25:00+00:00", "trade_duration": 10, "open_rate": 5.198e-05, "close_rate": 5.432496240601503e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1923.8168526356292, "profit_abs": 0.0040000000000000036}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 14:50:00+00:00", "close_date": "2018-01-24 16:35:00+00:00", "trade_duration": 105, "open_rate": 3.054e-05, "close_rate": 3.069308270676692e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3274.3942370661425, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 15:10:00+00:00", "close_date": "2018-01-24 16:15:00+00:00", "trade_duration": 65, "open_rate": 9.263e-05, "close_rate": 9.309431077694236e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1079.5638562020945, "profit_abs": 2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 22:40:00+00:00", "close_date": "2018-01-24 23:25:00+00:00", "trade_duration": 45, "open_rate": 5.514e-05, "close_rate": 5.54163909774436e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1813.5654697134569, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 00:50:00+00:00", "close_date": "2018-01-25 01:30:00+00:00", "trade_duration": 40, "open_rate": 4.921e-05, "close_rate": 4.9456666666666664e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.1072952651903, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 08:15:00+00:00", "close_date": "2018-01-25 12:15:00+00:00", "trade_duration": 240, "open_rate": 0.0026, "close_rate": 0.002613032581453634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.46153846153847, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 10:25:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 350, "open_rate": 0.02799871, "close_rate": 0.028139054411027563, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.571593119825878, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 11:00:00+00:00", "close_date": "2018-01-25 11:45:00+00:00", "trade_duration": 45, "open_rate": 0.04078902, "close_rate": 0.0409934762406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4516401717913303, "profit_abs": -1.3877787807814457e-17}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:05:00+00:00", "close_date": "2018-01-25 13:45:00+00:00", "trade_duration": 40, "open_rate": 2.89e-05, "close_rate": 2.904486215538847e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3460.2076124567475, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:20:00+00:00", "close_date": "2018-01-25 14:05:00+00:00", "trade_duration": 45, "open_rate": 0.041103, "close_rate": 0.04130903007518797, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4329124394813033, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-25 15:45:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 30, "open_rate": 5.428e-05, "close_rate": 5.509624060150376e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1842.2991893883568, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 17:45:00+00:00", "close_date": "2018-01-25 23:15:00+00:00", "trade_duration": 330, "open_rate": 5.414e-05, "close_rate": 5.441137844611528e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1847.063169560399, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 21:15:00+00:00", "close_date": "2018-01-25 21:55:00+00:00", "trade_duration": 40, "open_rate": 0.04140777, "close_rate": 0.0416153277443609, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.415005686130888, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 02:05:00+00:00", "close_date": "2018-01-26 02:45:00+00:00", "trade_duration": 40, "open_rate": 0.00254309, "close_rate": 0.002555837318295739, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.32224183965177, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 02:55:00+00:00", "close_date": "2018-01-26 15:10:00+00:00", "trade_duration": 735, "open_rate": 5.607e-05, "close_rate": 5.6351052631578935e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1783.4849295523454, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 06:10:00+00:00", "close_date": "2018-01-26 09:25:00+00:00", "trade_duration": 195, "open_rate": 0.00253806, "close_rate": 0.0025507821052631577, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.400171784748984, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 07:25:00+00:00", "close_date": "2018-01-26 09:55:00+00:00", "trade_duration": 150, "open_rate": 0.0415, "close_rate": 0.04170802005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4096385542168677, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-26 09:55:00+00:00", "close_date": "2018-01-26 10:25:00+00:00", "trade_duration": 30, "open_rate": 5.321e-05, "close_rate": 5.401015037593984e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1879.3459875963165, "profit_abs": 0.000999999999999987}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 16:05:00+00:00", "close_date": "2018-01-26 16:45:00+00:00", "trade_duration": 40, "open_rate": 0.02772046, "close_rate": 0.02785940967418546, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.6074437437185387, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 23:35:00+00:00", "close_date": "2018-01-27 00:15:00+00:00", "trade_duration": 40, "open_rate": 0.09461341, "close_rate": 0.09508766268170424, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0569326272036914, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 00:35:00+00:00", "close_date": "2018-01-27 01:30:00+00:00", "trade_duration": 55, "open_rate": 5.615e-05, "close_rate": 5.643145363408521e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1780.9439002671415, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.07877175, "open_date": "2018-01-27 00:45:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 4560, "open_rate": 5.556e-05, "close_rate": 5.144e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1799.8560115190785, "profit_abs": -0.007896868250539965}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 02:30:00+00:00", "close_date": "2018-01-27 11:25:00+00:00", "trade_duration": 535, "open_rate": 0.06900001, "close_rate": 0.06934587471177944, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492751522789635, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 06:25:00+00:00", "close_date": "2018-01-27 07:05:00+00:00", "trade_duration": 40, "open_rate": 0.09449985, "close_rate": 0.0949735334586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.058202737887944, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.04815133, "open_date": "2018-01-27 09:40:00+00:00", "close_date": "2018-01-30 04:40:00+00:00", "trade_duration": 4020, "open_rate": 0.0410697, "close_rate": 0.03928809, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4348850855983852, "profit_abs": -0.004827170578309559}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 11:45:00+00:00", "close_date": "2018-01-27 12:30:00+00:00", "trade_duration": 45, "open_rate": 0.0285, "close_rate": 0.02864285714285714, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5087719298245617, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 12:35:00+00:00", "close_date": "2018-01-27 15:25:00+00:00", "trade_duration": 170, "open_rate": 0.02866372, "close_rate": 0.02880739779448621, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.4887307020861216, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 15:50:00+00:00", "close_date": "2018-01-27 16:50:00+00:00", "trade_duration": 60, "open_rate": 0.095381, "close_rate": 0.09585910025062656, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0484268355332824, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 17:05:00+00:00", "close_date": "2018-01-27 17:45:00+00:00", "trade_duration": 40, "open_rate": 0.06759092, "close_rate": 0.06792972160401002, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4794886650455417, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 23:40:00+00:00", "close_date": "2018-01-28 01:05:00+00:00", "trade_duration": 85, "open_rate": 0.00258501, "close_rate": 0.002597967443609022, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.684569885609726, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 02:25:00+00:00", "close_date": "2018-01-28 08:10:00+00:00", "trade_duration": 345, "open_rate": 0.06698502, "close_rate": 0.0673207845112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4928710926711672, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 10:25:00+00:00", "close_date": "2018-01-28 16:30:00+00:00", "trade_duration": 365, "open_rate": 0.0677177, "close_rate": 0.06805713709273183, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4767187899175547, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-28 20:35:00+00:00", "close_date": "2018-01-28 21:35:00+00:00", "trade_duration": 60, "open_rate": 5.215e-05, "close_rate": 5.2411403508771925e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1917.5455417066157, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-28 22:00:00+00:00", "close_date": "2018-01-28 22:30:00+00:00", "trade_duration": 30, "open_rate": 0.00273809, "close_rate": 0.002779264285714285, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.5218089982433, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-29 00:00:00+00:00", "close_date": "2018-01-29 00:30:00+00:00", "trade_duration": 30, "open_rate": 0.00274632, "close_rate": 0.002787618045112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.412362725392526, "profit_abs": 0.0010000000000000148}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-29 02:15:00+00:00", "close_date": "2018-01-29 03:00:00+00:00", "trade_duration": 45, "open_rate": 0.01622478, "close_rate": 0.016306107218045113, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.163411768911504, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 03:05:00+00:00", "close_date": "2018-01-29 03:45:00+00:00", "trade_duration": 40, "open_rate": 0.069, "close_rate": 0.06934586466165413, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492753623188406, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 05:20:00+00:00", "close_date": "2018-01-29 06:55:00+00:00", "trade_duration": 95, "open_rate": 8.755e-05, "close_rate": 8.798884711779448e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1142.204454597373, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 07:00:00+00:00", "close_date": "2018-01-29 19:25:00+00:00", "trade_duration": 745, "open_rate": 0.06825763, "close_rate": 0.06859977350877192, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4650376815016872, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 19:45:00+00:00", "close_date": "2018-01-29 20:25:00+00:00", "trade_duration": 40, "open_rate": 0.06713892, "close_rate": 0.06747545593984962, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4894490408841845, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0199116, "open_date": "2018-01-29 23:30:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 315, "open_rate": 8.934e-05, "close_rate": 8.8e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1119.3194537721067, "profit_abs": -0.0019961383478844796}], "results_per_pair": [{"key": "TRX/BTC", "trades": 15, "profit_mean": 0.0023467073333333323, "profit_mean_pct": 0.23467073333333321, "profit_sum": 0.035200609999999986, "profit_sum_pct": 3.5200609999999988, "profit_total_abs": 0.0035288616521155086, "profit_total_pct": 1.1733536666666662, "duration_avg": "2:28:00", "wins": 9, "draws": 2, "losses": 4}, {"key": "ADA/BTC", "trades": 29, "profit_mean": -0.0011598141379310352, "profit_mean_pct": -0.11598141379310352, "profit_sum": -0.03363461000000002, "profit_sum_pct": -3.3634610000000023, "profit_total_abs": -0.0033718682505400333, "profit_total_pct": -1.1211536666666675, "duration_avg": "5:35:00", "wins": 9, "draws": 11, "losses": 9}, {"key": "XLM/BTC", "trades": 21, "profit_mean": 0.0026243899999999994, "profit_mean_pct": 0.2624389999999999, "profit_sum": 0.05511218999999999, "profit_sum_pct": 5.511218999999999, "profit_total_abs": 0.005525000000000002, "profit_total_pct": 1.8370729999999995, "duration_avg": "3:21:00", "wins": 12, "draws": 3, "losses": 6}, {"key": "ETH/BTC", "trades": 21, "profit_mean": 0.0009500057142857142, "profit_mean_pct": 0.09500057142857142, "profit_sum": 0.01995012, "profit_sum_pct": 1.9950119999999998, "profit_total_abs": 0.0019999999999999463, "profit_total_pct": 0.6650039999999999, "duration_avg": "2:17:00", "wins": 5, "draws": 10, "losses": 6}, {"key": "XMR/BTC", "trades": 16, "profit_mean": -0.0027899012500000007, "profit_mean_pct": -0.2789901250000001, "profit_sum": -0.04463842000000001, "profit_sum_pct": -4.463842000000001, "profit_total_abs": -0.0044750000000000345, "profit_total_pct": -1.4879473333333337, "duration_avg": "8:41:00", "wins": 6, "draws": 5, "losses": 5}, {"key": "ZEC/BTC", "trades": 21, "profit_mean": -0.00039290904761904774, "profit_mean_pct": -0.03929090476190478, "profit_sum": -0.008251090000000003, "profit_sum_pct": -0.8251090000000003, "profit_total_abs": -0.000827170578309569, "profit_total_pct": -0.27503633333333344, "duration_avg": "4:17:00", "wins": 8, "draws": 7, "losses": 6}, {"key": "NXT/BTC", "trades": 12, "profit_mean": -0.0012261025000000006, "profit_mean_pct": -0.12261025000000006, "profit_sum": -0.014713230000000008, "profit_sum_pct": -1.4713230000000008, "profit_total_abs": -0.0014750000000000874, "profit_total_pct": -0.4904410000000003, "duration_avg": "0:57:00", "wins": 4, "draws": 3, "losses": 5}, {"key": "LTC/BTC", "trades": 8, "profit_mean": 0.00748129625, "profit_mean_pct": 0.748129625, "profit_sum": 0.05985037, "profit_sum_pct": 5.985037, "profit_total_abs": 0.006000000000000019, "profit_total_pct": 1.9950123333333334, "duration_avg": "1:59:00", "wins": 5, "draws": 2, "losses": 1}, {"key": "ETC/BTC", "trades": 20, "profit_mean": 0.0022568569999999997, "profit_mean_pct": 0.22568569999999996, "profit_sum": 0.04513713999999999, "profit_sum_pct": 4.513713999999999, "profit_total_abs": 0.004525000000000001, "profit_total_pct": 1.504571333333333, "duration_avg": "1:45:00", "wins": 11, "draws": 4, "losses": 5}, {"key": "DASH/BTC", "trades": 16, "profit_mean": 0.0018703237499999997, "profit_mean_pct": 0.18703237499999997, "profit_sum": 0.029925179999999996, "profit_sum_pct": 2.9925179999999996, "profit_total_abs": 0.002999999999999961, "profit_total_pct": 0.9975059999999999, "duration_avg": "3:03:00", "wins": 4, "draws": 7, "losses": 5}, {"key": "TOTAL", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}], "sell_reason_summary": [{"sell_reason": "roi", "trades": 170, "wins": 73, "draws": 54, "losses": 43, "profit_mean": 0.005398268352941177, "profit_mean_pct": 0.54, "profit_sum": 0.91770562, "profit_sum_pct": 91.77, "profit_total_abs": 0.09199999999999964, "profit_pct_total": 30.59}, {"sell_reason": "stop_loss", "trades": 6, "wins": 0, "draws": 0, "losses": 6, "profit_mean": -0.10448878000000002, "profit_mean_pct": -10.45, "profit_sum": -0.6269326800000001, "profit_sum_pct": -62.69, "profit_total_abs": -0.06284999999999992, "profit_pct_total": -20.9}, {"sell_reason": "force_sell", "trades": 3, "wins": 0, "draws": 0, "losses": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.89, "profit_sum": -0.14683468, "profit_sum_pct": -14.68, "profit_total_abs": -0.014720177176734003, "profit_pct_total": -4.89}], "left_open_trades": [{"key": "TRX/BTC", "trades": 1, "profit_mean": -0.0199116, "profit_mean_pct": -1.9911600000000003, "profit_sum": -0.0199116, "profit_sum_pct": -1.9911600000000003, "profit_total_abs": -0.0019961383478844796, "profit_total_pct": -0.6637200000000001, "duration_avg": "5:15:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ADA/BTC", "trades": 1, "profit_mean": -0.07877175, "profit_mean_pct": -7.877175, "profit_sum": -0.07877175, "profit_sum_pct": -7.877175, "profit_total_abs": -0.007896868250539965, "profit_total_pct": -2.625725, "duration_avg": "3 days, 4:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ZEC/BTC", "trades": 1, "profit_mean": -0.04815133, "profit_mean_pct": -4.815133, "profit_sum": -0.04815133, "profit_sum_pct": -4.815133, "profit_total_abs": -0.004827170578309559, "profit_total_pct": -1.6050443333333335, "duration_avg": "2 days, 19:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "TOTAL", "trades": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.894489333333333, "profit_sum": -0.14683468, "profit_sum_pct": -14.683468, "profit_total_abs": -0.014720177176734003, "profit_total_pct": -4.8944893333333335, "duration_avg": "2 days, 1:25:00", "wins": 0, "draws": 0, "losses": 3}], "total_trades": 179, "backtest_start": "2018-01-30 04:45:00+00:00", "backtest_start_ts": 1517287500, "backtest_end": "2018-01-30 04:45:00+00:00", "backtest_end_ts": 1517287500, "backtest_days": 0, "trades_per_day": null, "market_change": 0.25, "stake_amount": 0.1, "max_drawdown": 0.21142322000000008, "drawdown_start": "2018-01-24 14:25:00+00:00", "drawdown_start_ts": 1516803900.0, "drawdown_end": "2018-01-30 04:45:00+00:00", "drawdown_end_ts": 1517287500.0,"pairlist": ["TRX/BTC", "ADA/BTC", "XLM/BTC", "ETH/BTC", "XMR/BTC", "ZEC/BTC","NXT/BTC", "LTC/BTC", "ETC/BTC", "DASH/BTC"]}}, "strategy_comparison": [{"key": "StrategyTestV2", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}, {"key": "TestStrategy", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}]} diff --git a/tests/testdata/backtest-result_new.json b/tests/testdata/backtest-result_new.json index 5334bf80e..84f3806ea 100644 --- a/tests/testdata/backtest-result_new.json +++ b/tests/testdata/backtest-result_new.json @@ -1 +1 @@ -{"strategy": {"DefaultStrategy": {"trades": [{"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:20:00+00:00", "trade_duration": 5, "open_rate": 9.64e-05, "close_rate": 0.00010074887218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1037.344398340249, "profit_abs": 0.00399999999999999}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:30:00+00:00", "trade_duration": 15, "open_rate": 4.756e-05, "close_rate": 4.9705563909774425e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2102.6072329688814, "profit_abs": 0.00399999999999999}, {"pair": "XLM/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:35:00+00:00", "trade_duration": 10, "open_rate": 3.339e-05, "close_rate": 3.489631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2994.908655286014, "profit_abs": 0.0040000000000000036}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:40:00+00:00", "trade_duration": 15, "open_rate": 9.696e-05, "close_rate": 0.00010133413533834584, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1031.3531353135315, "profit_abs": 0.00399999999999999}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 07:35:00+00:00", "close_date": "2018-01-10 08:35:00+00:00", "trade_duration": 60, "open_rate": 0.0943, "close_rate": 0.09477268170426063, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0604453870625663, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 07:40:00+00:00", "close_date": "2018-01-10 08:10:00+00:00", "trade_duration": 30, "open_rate": 0.02719607, "close_rate": 0.02760503345864661, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.677001860930642, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 08:15:00+00:00", "close_date": "2018-01-10 09:55:00+00:00", "trade_duration": 100, "open_rate": 0.04634952, "close_rate": 0.046581848421052625, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1575196463739, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 14:45:00+00:00", "close_date": "2018-01-10 15:50:00+00:00", "trade_duration": 65, "open_rate": 3.066e-05, "close_rate": 3.081368421052631e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3261.5786040443577, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 16:35:00+00:00", "close_date": "2018-01-10 17:15:00+00:00", "trade_duration": 40, "open_rate": 0.0168999, "close_rate": 0.016984611278195488, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.917194776300452, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 16:40:00+00:00", "close_date": "2018-01-10 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.09132568, "close_rate": 0.0917834528320802, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0949822656672252, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 18:50:00+00:00", "close_date": "2018-01-10 19:45:00+00:00", "trade_duration": 55, "open_rate": 0.08898003, "close_rate": 0.08942604518796991, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1238476768326557, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 22:15:00+00:00", "close_date": "2018-01-10 23:00:00+00:00", "trade_duration": 45, "open_rate": 0.08560008, "close_rate": 0.08602915308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1682232072680307, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 22:50:00+00:00", "close_date": "2018-01-10 23:20:00+00:00", "trade_duration": 30, "open_rate": 0.00249083, "close_rate": 0.0025282860902255634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 40.147260150231055, "profit_abs": 0.000999999999999987}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 23:15:00+00:00", "close_date": "2018-01-11 00:15:00+00:00", "trade_duration": 60, "open_rate": 3.022e-05, "close_rate": 3.037147869674185e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3309.0668431502318, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-10 23:40:00+00:00", "close_date": "2018-01-11 00:05:00+00:00", "trade_duration": 25, "open_rate": 0.002437, "close_rate": 0.0024980776942355883, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.03405826836274, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 00:00:00+00:00", "close_date": "2018-01-11 00:35:00+00:00", "trade_duration": 35, "open_rate": 0.04771803, "close_rate": 0.04843559436090225, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0956439316543456, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-11 03:40:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 45, "open_rate": 3.651e-05, "close_rate": 3.2859000000000005e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2738.9756231169545, "profit_abs": -0.01047499999999997}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 03:55:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 30, "open_rate": 0.08824105, "close_rate": 0.08956798308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1332594070446804, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 04:00:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 50, "open_rate": 0.00243, "close_rate": 0.002442180451127819, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.1522633744856, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:55:00+00:00", "trade_duration": 25, "open_rate": 0.04545064, "close_rate": 0.046589753784461146, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.200189040242338, "profit_abs": 0.001999999999999988}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 20, "open_rate": 3.372e-05, "close_rate": 3.456511278195488e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2965.599051008304, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:55:00+00:00", "close_date": "2018-01-11 05:15:00+00:00", "trade_duration": 20, "open_rate": 0.02644, "close_rate": 0.02710265664160401, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7821482602118004, "profit_abs": 0.001999999999999988}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:20:00+00:00", "close_date": "2018-01-11 12:00:00+00:00", "trade_duration": 40, "open_rate": 0.08812, "close_rate": 0.08856170426065162, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1348161597821154, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:35:00+00:00", "close_date": "2018-01-11 12:15:00+00:00", "trade_duration": 40, "open_rate": 0.02683577, "close_rate": 0.026970285137844607, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7263696923919087, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 14:00:00+00:00", "close_date": "2018-01-11 14:25:00+00:00", "trade_duration": 25, "open_rate": 4.919e-05, "close_rate": 5.04228320802005e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.9335230737956, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 19:25:00+00:00", "close_date": "2018-01-11 20:35:00+00:00", "trade_duration": 70, "open_rate": 0.08784896, "close_rate": 0.08828930566416039, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1383174029607181, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:35:00+00:00", "close_date": "2018-01-11 23:30:00+00:00", "trade_duration": 55, "open_rate": 5.105e-05, "close_rate": 5.130588972431077e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1958.8638589618022, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:25:00+00:00", "trade_duration": 30, "open_rate": 3.96e-05, "close_rate": 4.019548872180451e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2525.252525252525, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:35:00+00:00", "trade_duration": 40, "open_rate": 2.885e-05, "close_rate": 2.899461152882205e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3466.204506065858, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 23:30:00+00:00", "close_date": "2018-01-12 00:05:00+00:00", "trade_duration": 35, "open_rate": 0.02645, "close_rate": 0.026847744360902256, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.780718336483932, "profit_abs": 0.0010000000000000148}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 23:55:00+00:00", "close_date": "2018-01-12 01:15:00+00:00", "trade_duration": 80, "open_rate": 0.048, "close_rate": 0.04824060150375939, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0833333333333335, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-12 21:15:00+00:00", "close_date": "2018-01-12 21:40:00+00:00", "trade_duration": 25, "open_rate": 4.692e-05, "close_rate": 4.809593984962405e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2131.287297527707, "profit_abs": 0.001999999999999974}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 00:55:00+00:00", "close_date": "2018-01-13 06:20:00+00:00", "trade_duration": 325, "open_rate": 0.00256966, "close_rate": 0.0025825405012531327, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.91565421106294, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 10:55:00+00:00", "close_date": "2018-01-13 11:35:00+00:00", "trade_duration": 40, "open_rate": 6.262e-05, "close_rate": 6.293388471177944e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1596.933886937081, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 13:05:00+00:00", "close_date": "2018-01-15 14:10:00+00:00", "trade_duration": 2945, "open_rate": 4.73e-05, "close_rate": 4.753709273182957e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2114.1649048625795, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:30:00+00:00", "close_date": "2018-01-13 14:45:00+00:00", "trade_duration": 75, "open_rate": 6.063e-05, "close_rate": 6.0933909774436085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1649.348507339601, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:40:00+00:00", "close_date": "2018-01-13 23:30:00+00:00", "trade_duration": 590, "open_rate": 0.00011082, "close_rate": 0.00011137548872180448, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 902.3641941887746, "profit_abs": -2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 15:15:00+00:00", "close_date": "2018-01-13 15:55:00+00:00", "trade_duration": 40, "open_rate": 5.93e-05, "close_rate": 5.9597243107769415e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1686.3406408094436, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 16:30:00+00:00", "close_date": "2018-01-13 17:10:00+00:00", "trade_duration": 40, "open_rate": 0.04850003, "close_rate": 0.04874313791979949, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0618543947292407, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 22:05:00+00:00", "close_date": "2018-01-14 06:25:00+00:00", "trade_duration": 500, "open_rate": 0.09825019, "close_rate": 0.09874267215538848, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0178097365511456, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 00:20:00+00:00", "close_date": "2018-01-14 22:55:00+00:00", "trade_duration": 1355, "open_rate": 6.018e-05, "close_rate": 6.048165413533834e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1661.681621801263, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 12:45:00+00:00", "close_date": "2018-01-14 13:25:00+00:00", "trade_duration": 40, "open_rate": 0.09758999, "close_rate": 0.0980791628822055, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.024695258191952, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-14 15:30:00+00:00", "close_date": "2018-01-14 16:00:00+00:00", "trade_duration": 30, "open_rate": 0.00311, "close_rate": 0.0031567669172932328, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.154340836012864, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 20:45:00+00:00", "close_date": "2018-01-14 22:15:00+00:00", "trade_duration": 90, "open_rate": 0.00312401, "close_rate": 0.003139669197994987, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.010140812609436, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 23:35:00+00:00", "close_date": "2018-01-15 00:30:00+00:00", "trade_duration": 55, "open_rate": 0.0174679, "close_rate": 0.017555458395989976, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.724786608579165, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 23:45:00+00:00", "close_date": "2018-01-15 00:25:00+00:00", "trade_duration": 40, "open_rate": 0.07346846, "close_rate": 0.07383672295739348, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.3611282991367997, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 02:25:00+00:00", "close_date": "2018-01-15 03:05:00+00:00", "trade_duration": 40, "open_rate": 0.097994, "close_rate": 0.09848519799498744, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.020470641059657, "profit_abs": -2.7755575615628914e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 07:20:00+00:00", "close_date": "2018-01-15 08:00:00+00:00", "trade_duration": 40, "open_rate": 0.09659, "close_rate": 0.09707416040100247, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0353038616834043, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-15 08:20:00+00:00", "close_date": "2018-01-15 08:55:00+00:00", "trade_duration": 35, "open_rate": 9.987e-05, "close_rate": 0.00010137180451127818, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1001.3016921998599, "profit_abs": 0.0010000000000000009}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-15 12:10:00+00:00", "close_date": "2018-01-16 02:50:00+00:00", "trade_duration": 880, "open_rate": 0.0948969, "close_rate": 0.09537257368421052, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0537752023511833, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:10:00+00:00", "close_date": "2018-01-15 17:40:00+00:00", "trade_duration": 210, "open_rate": 0.071, "close_rate": 0.07135588972431077, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4084507042253522, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:30:00+00:00", "close_date": "2018-01-15 15:10:00+00:00", "trade_duration": 40, "open_rate": 0.04600501, "close_rate": 0.046235611553884705, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.173676301776698, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:10:00+00:00", "close_date": "2018-01-15 19:25:00+00:00", "trade_duration": 75, "open_rate": 9.438e-05, "close_rate": 9.485308270676693e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1059.5465140919687, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:35:00+00:00", "close_date": "2018-01-15 19:15:00+00:00", "trade_duration": 40, "open_rate": 0.03040001, "close_rate": 0.030552391002506264, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.2894726021471703, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-15 20:25:00+00:00", "close_date": "2018-01-16 08:25:00+00:00", "trade_duration": 720, "open_rate": 5.837e-05, "close_rate": 5.2533e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1713.2088401576154, "profit_abs": -0.010474999999999984}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 20:40:00+00:00", "close_date": "2018-01-15 22:00:00+00:00", "trade_duration": 80, "open_rate": 0.046036, "close_rate": 0.04626675689223057, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1722130506560084, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 00:30:00+00:00", "close_date": "2018-01-16 01:10:00+00:00", "trade_duration": 40, "open_rate": 0.0028685, "close_rate": 0.0028828784461152877, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 34.86142583231654, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 01:15:00+00:00", "close_date": "2018-01-16 02:35:00+00:00", "trade_duration": 80, "open_rate": 0.06731755, "close_rate": 0.0676549813283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4854967241083492, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 07:45:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 55, "open_rate": 0.09217614, "close_rate": 0.09263817578947368, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0848794492804754, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:55:00+00:00", "trade_duration": 20, "open_rate": 0.0165, "close_rate": 0.016913533834586467, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.0606060606060606, "profit_abs": 0.0020000000000000018}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 5, "open_rate": 7.953e-05, "close_rate": 8.311781954887218e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1257.387149503332, "profit_abs": 0.00399999999999999}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 08:45:00+00:00", "close_date": "2018-01-16 09:50:00+00:00", "trade_duration": 65, "open_rate": 0.045202, "close_rate": 0.04542857644110275, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2122914915269236, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:45:00+00:00", "trade_duration": 30, "open_rate": 5.248e-05, "close_rate": 5.326917293233082e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1905.487804878049, "profit_abs": 0.0010000000000000009}, {"pair": "XMR/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:55:00+00:00", "trade_duration": 40, "open_rate": 0.02892318, "close_rate": 0.02906815834586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.457434486802627, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 09:50:00+00:00", "close_date": "2018-01-16 10:10:00+00:00", "trade_duration": 20, "open_rate": 5.158e-05, "close_rate": 5.287273182957392e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1938.735944164405, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:35:00+00:00", "trade_duration": 30, "open_rate": 0.02828232, "close_rate": 0.02870761804511278, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5357778286929786, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:40:00+00:00", "trade_duration": 35, "open_rate": 0.04357584, "close_rate": 0.044231115789473675, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.294849623093898, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 13:45:00+00:00", "close_date": "2018-01-16 14:20:00+00:00", "trade_duration": 35, "open_rate": 5.362e-05, "close_rate": 5.442631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1864.975755315181, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 17:30:00+00:00", "close_date": "2018-01-16 18:25:00+00:00", "trade_duration": 55, "open_rate": 5.302e-05, "close_rate": 5.328576441102756e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.0807242549984, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:45:00+00:00", "trade_duration": 30, "open_rate": 0.09129999, "close_rate": 0.09267292218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0952903718828448, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:35:00+00:00", "trade_duration": 20, "open_rate": 3.808e-05, "close_rate": 3.903438596491228e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2626.0504201680674, "profit_abs": 0.0020000000000000018}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 19:00:00+00:00", "close_date": "2018-01-16 19:30:00+00:00", "trade_duration": 30, "open_rate": 0.02811012, "close_rate": 0.028532828571428567, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.557437677249333, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 60, "open_rate": 0.00258379, "close_rate": 0.002325411, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.702835756775904, "profit_abs": -0.010474999999999984}, {"pair": "NXT/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 80, "open_rate": 2.559e-05, "close_rate": 2.3031e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3907.7764751856193, "profit_abs": -0.010474999999999998}, {"pair": "TRX/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:35:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 50, "open_rate": 7.62e-05, "close_rate": 6.858e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1312.3359580052495, "profit_abs": -0.010474999999999984}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:35:00+00:00", "trade_duration": 5, "open_rate": 0.00229844, "close_rate": 0.002402129022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 43.507770487809125, "profit_abs": 0.004000000000000017}, {"pair": "LTC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:40:00+00:00", "trade_duration": 10, "open_rate": 0.0151, "close_rate": 0.015781203007518795, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.622516556291391, "profit_abs": 0.00399999999999999}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:40:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 5, "open_rate": 0.00235676, "close_rate": 0.00246308, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 42.431134269081284, "profit_abs": 0.0040000000000000036}, {"pair": "DASH/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 22:45:00+00:00", "close_date": "2018-01-16 23:05:00+00:00", "trade_duration": 20, "open_rate": 0.0630692, "close_rate": 0.06464988170426066, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.585559988076589, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:50:00+00:00", "close_date": "2018-01-16 22:55:00+00:00", "trade_duration": 5, "open_rate": 2.2e-05, "close_rate": 2.299248120300751e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 4545.454545454546, "profit_abs": 0.003999999999999976}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-17 03:30:00+00:00", "close_date": "2018-01-17 04:00:00+00:00", "trade_duration": 30, "open_rate": 4.974e-05, "close_rate": 5.048796992481203e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2010.454362685967, "profit_abs": 0.0010000000000000009}, {"pair": "TRX/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-17 03:55:00+00:00", "close_date": "2018-01-17 04:15:00+00:00", "trade_duration": 20, "open_rate": 7.108e-05, "close_rate": 7.28614536340852e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1406.8655036578502, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 09:35:00+00:00", "close_date": "2018-01-17 10:15:00+00:00", "trade_duration": 40, "open_rate": 0.04327, "close_rate": 0.04348689223057644, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.3110700254217704, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:20:00+00:00", "close_date": "2018-01-17 17:00:00+00:00", "trade_duration": 400, "open_rate": 4.997e-05, "close_rate": 5.022047619047618e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2001.2007204322595, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:25:00+00:00", "trade_duration": 55, "open_rate": 0.06836818, "close_rate": 0.06871087764411027, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4626687444363737, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:10:00+00:00", "trade_duration": 40, "open_rate": 3.63e-05, "close_rate": 3.648195488721804e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2754.8209366391184, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:30:00+00:00", "close_date": "2018-01-17 22:05:00+00:00", "trade_duration": 575, "open_rate": 0.0281, "close_rate": 0.02824085213032581, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5587188612099645, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:35:00+00:00", "close_date": "2018-01-17 16:55:00+00:00", "trade_duration": 260, "open_rate": 0.08651001, "close_rate": 0.08694364413533832, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1559355963546878, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 05:00:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 55, "open_rate": 5.633e-05, "close_rate": 5.6612355889724306e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1775.2529735487308, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 05:20:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 35, "open_rate": 0.06988494, "close_rate": 0.07093584135338346, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.430923457900944, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 07:35:00+00:00", "close_date": "2018-01-18 08:15:00+00:00", "trade_duration": 40, "open_rate": 5.545e-05, "close_rate": 5.572794486215538e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1803.4265103697026, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 09:00:00+00:00", "close_date": "2018-01-18 09:40:00+00:00", "trade_duration": 40, "open_rate": 0.01633527, "close_rate": 0.016417151052631574, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.121723118136401, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 16:40:00+00:00", "close_date": "2018-01-18 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.00269734, "close_rate": 0.002710860501253133, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.073561360451414, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-18 18:05:00+00:00", "close_date": "2018-01-18 18:30:00+00:00", "trade_duration": 25, "open_rate": 4.475e-05, "close_rate": 4.587155388471177e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2234.63687150838, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 18:25:00+00:00", "close_date": "2018-01-18 18:55:00+00:00", "trade_duration": 30, "open_rate": 2.79e-05, "close_rate": 2.8319548872180444e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3584.2293906810037, "profit_abs": 0.000999999999999987}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 20:10:00+00:00", "close_date": "2018-01-18 20:50:00+00:00", "trade_duration": 40, "open_rate": 0.04439326, "close_rate": 0.04461578260651629, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2525942001105577, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 21:30:00+00:00", "close_date": "2018-01-19 00:35:00+00:00", "trade_duration": 185, "open_rate": 4.49e-05, "close_rate": 4.51250626566416e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2227.1714922049, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 21:55:00+00:00", "close_date": "2018-01-19 05:05:00+00:00", "trade_duration": 430, "open_rate": 0.02855, "close_rate": 0.028693107769423555, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.502626970227671, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 22:10:00+00:00", "close_date": "2018-01-18 22:50:00+00:00", "trade_duration": 40, "open_rate": 5.796e-05, "close_rate": 5.8250526315789473e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1725.3278122843342, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 23:50:00+00:00", "close_date": "2018-01-19 00:30:00+00:00", "trade_duration": 40, "open_rate": 0.04340323, "close_rate": 0.04362079005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.303975994413319, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-19 16:45:00+00:00", "close_date": "2018-01-19 17:35:00+00:00", "trade_duration": 50, "open_rate": 0.04454455, "close_rate": 0.04476783095238095, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.244943545282195, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:15:00+00:00", "close_date": "2018-01-19 19:55:00+00:00", "trade_duration": 160, "open_rate": 5.62e-05, "close_rate": 5.648170426065162e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1779.3594306049824, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:20:00+00:00", "close_date": "2018-01-19 20:15:00+00:00", "trade_duration": 175, "open_rate": 4.339e-05, "close_rate": 4.360749373433584e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2304.6784973496196, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-20 04:45:00+00:00", "close_date": "2018-01-20 17:35:00+00:00", "trade_duration": 770, "open_rate": 0.0001009, "close_rate": 0.00010140576441102755, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 991.0802775024778, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 15:15:00+00:00", "trade_duration": 625, "open_rate": 0.00270505, "close_rate": 0.002718609147869674, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.96789338459548, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 07:00:00+00:00", "trade_duration": 130, "open_rate": 0.03000002, "close_rate": 0.030150396040100245, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.3333311111125927, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 09:00:00+00:00", "close_date": "2018-01-20 09:40:00+00:00", "trade_duration": 40, "open_rate": 5.46e-05, "close_rate": 5.4873684210526304e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1831.5018315018317, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-20 18:25:00+00:00", "close_date": "2018-01-25 03:50:00+00:00", "trade_duration": 6325, "open_rate": 0.03082222, "close_rate": 0.027739998, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.244412634781012, "profit_abs": -0.010474999999999998}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 22:25:00+00:00", "close_date": "2018-01-20 23:15:00+00:00", "trade_duration": 50, "open_rate": 0.08969999, "close_rate": 0.09014961401002504, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1148273260677064, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 02:50:00+00:00", "close_date": "2018-01-21 14:30:00+00:00", "trade_duration": 700, "open_rate": 0.01632501, "close_rate": 0.01640683962406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.125570520324337, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 10:20:00+00:00", "close_date": "2018-01-21 11:00:00+00:00", "trade_duration": 40, "open_rate": 0.070538, "close_rate": 0.07089157393483708, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.417675579120474, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 15:50:00+00:00", "close_date": "2018-01-21 18:45:00+00:00", "trade_duration": 175, "open_rate": 5.301e-05, "close_rate": 5.327571428571427e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.4365214110546, "profit_abs": -2.7755575615628914e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 16:20:00+00:00", "close_date": "2018-01-21 17:00:00+00:00", "trade_duration": 40, "open_rate": 3.955e-05, "close_rate": 3.9748245614035085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2528.4450063211125, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:45:00+00:00", "trade_duration": 30, "open_rate": 0.00258505, "close_rate": 0.002623922932330827, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.6839712964933, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:55:00+00:00", "trade_duration": 40, "open_rate": 3.903e-05, "close_rate": 3.922563909774435e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2562.1316935690497, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 00:35:00+00:00", "close_date": "2018-01-22 10:35:00+00:00", "trade_duration": 600, "open_rate": 5.236e-05, "close_rate": 5.262245614035087e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1909.8548510313217, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 01:30:00+00:00", "close_date": "2018-01-22 02:10:00+00:00", "trade_duration": 40, "open_rate": 9.028e-05, "close_rate": 9.07325313283208e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1107.6650420912717, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 12:25:00+00:00", "close_date": "2018-01-22 14:35:00+00:00", "trade_duration": 130, "open_rate": 0.002687, "close_rate": 0.002700468671679198, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.21622627465575, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 13:15:00+00:00", "close_date": "2018-01-22 13:55:00+00:00", "trade_duration": 40, "open_rate": 4.168e-05, "close_rate": 4.188892230576441e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2399.232245681382, "profit_abs": 1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-22 14:00:00+00:00", "close_date": "2018-01-22 14:30:00+00:00", "trade_duration": 30, "open_rate": 8.821e-05, "close_rate": 8.953646616541353e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1133.6583153837435, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 15:55:00+00:00", "close_date": "2018-01-22 16:40:00+00:00", "trade_duration": 45, "open_rate": 5.172e-05, "close_rate": 5.1979248120300745e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1933.4880123743235, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-22 16:05:00+00:00", "close_date": "2018-01-22 16:25:00+00:00", "trade_duration": 20, "open_rate": 3.026e-05, "close_rate": 3.101839598997494e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3304.692663582287, "profit_abs": 0.0020000000000000157}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 19:50:00+00:00", "close_date": "2018-01-23 00:10:00+00:00", "trade_duration": 260, "open_rate": 0.07064, "close_rate": 0.07099408521303258, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.415628539071348, "profit_abs": 1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 21:25:00+00:00", "close_date": "2018-01-22 22:05:00+00:00", "trade_duration": 40, "open_rate": 0.01644483, "close_rate": 0.01652726022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.080938507725528, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-23 00:05:00+00:00", "close_date": "2018-01-23 00:35:00+00:00", "trade_duration": 30, "open_rate": 4.331e-05, "close_rate": 4.3961278195488714e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2308.935580697299, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-23 01:50:00+00:00", "close_date": "2018-01-23 02:15:00+00:00", "trade_duration": 25, "open_rate": 3.2e-05, "close_rate": 3.2802005012531326e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3125.0000000000005, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 04:25:00+00:00", "close_date": "2018-01-23 05:15:00+00:00", "trade_duration": 50, "open_rate": 0.09167706, "close_rate": 0.09213659413533835, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0907854156754153, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 07:35:00+00:00", "close_date": "2018-01-23 09:00:00+00:00", "trade_duration": 85, "open_rate": 0.0692498, "close_rate": 0.06959691679197995, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4440474918339115, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 10:50:00+00:00", "close_date": "2018-01-23 13:05:00+00:00", "trade_duration": 135, "open_rate": 3.182e-05, "close_rate": 3.197949874686716e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3142.677561282213, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 11:05:00+00:00", "close_date": "2018-01-23 16:05:00+00:00", "trade_duration": 300, "open_rate": 0.04088, "close_rate": 0.04108491228070175, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4461839530332683, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 14:55:00+00:00", "close_date": "2018-01-23 15:35:00+00:00", "trade_duration": 40, "open_rate": 5.15e-05, "close_rate": 5.175814536340851e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1941.747572815534, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 16:35:00+00:00", "close_date": "2018-01-24 00:05:00+00:00", "trade_duration": 450, "open_rate": 0.09071698, "close_rate": 0.09117170170426064, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1023294646713329, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 17:25:00+00:00", "close_date": "2018-01-23 18:45:00+00:00", "trade_duration": 80, "open_rate": 3.128e-05, "close_rate": 3.1436791979949865e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3196.9309462915603, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 20:15:00+00:00", "close_date": "2018-01-23 22:00:00+00:00", "trade_duration": 105, "open_rate": 9.555e-05, "close_rate": 9.602894736842104e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1046.5724751439038, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 22:30:00+00:00", "close_date": "2018-01-23 23:10:00+00:00", "trade_duration": 40, "open_rate": 0.04080001, "close_rate": 0.0410045213283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.450979791426522, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 23:50:00+00:00", "close_date": "2018-01-24 03:35:00+00:00", "trade_duration": 225, "open_rate": 5.163e-05, "close_rate": 5.18887969924812e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1936.8584156498162, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 00:20:00+00:00", "close_date": "2018-01-24 01:50:00+00:00", "trade_duration": 90, "open_rate": 0.04040781, "close_rate": 0.04061035541353383, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.474769110228938, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 06:45:00+00:00", "close_date": "2018-01-24 07:25:00+00:00", "trade_duration": 40, "open_rate": 5.132e-05, "close_rate": 5.157724310776942e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1948.5580670303975, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-24 14:15:00+00:00", "close_date": "2018-01-24 14:25:00+00:00", "trade_duration": 10, "open_rate": 5.198e-05, "close_rate": 5.432496240601503e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1923.8168526356292, "profit_abs": 0.0040000000000000036}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 14:50:00+00:00", "close_date": "2018-01-24 16:35:00+00:00", "trade_duration": 105, "open_rate": 3.054e-05, "close_rate": 3.069308270676692e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3274.3942370661425, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 15:10:00+00:00", "close_date": "2018-01-24 16:15:00+00:00", "trade_duration": 65, "open_rate": 9.263e-05, "close_rate": 9.309431077694236e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1079.5638562020945, "profit_abs": 2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 22:40:00+00:00", "close_date": "2018-01-24 23:25:00+00:00", "trade_duration": 45, "open_rate": 5.514e-05, "close_rate": 5.54163909774436e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1813.5654697134569, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 00:50:00+00:00", "close_date": "2018-01-25 01:30:00+00:00", "trade_duration": 40, "open_rate": 4.921e-05, "close_rate": 4.9456666666666664e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.1072952651903, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 08:15:00+00:00", "close_date": "2018-01-25 12:15:00+00:00", "trade_duration": 240, "open_rate": 0.0026, "close_rate": 0.002613032581453634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.46153846153847, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 10:25:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 350, "open_rate": 0.02799871, "close_rate": 0.028139054411027563, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.571593119825878, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 11:00:00+00:00", "close_date": "2018-01-25 11:45:00+00:00", "trade_duration": 45, "open_rate": 0.04078902, "close_rate": 0.0409934762406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4516401717913303, "profit_abs": -1.3877787807814457e-17}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:05:00+00:00", "close_date": "2018-01-25 13:45:00+00:00", "trade_duration": 40, "open_rate": 2.89e-05, "close_rate": 2.904486215538847e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3460.2076124567475, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:20:00+00:00", "close_date": "2018-01-25 14:05:00+00:00", "trade_duration": 45, "open_rate": 0.041103, "close_rate": 0.04130903007518797, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4329124394813033, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-25 15:45:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 30, "open_rate": 5.428e-05, "close_rate": 5.509624060150376e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1842.2991893883568, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 17:45:00+00:00", "close_date": "2018-01-25 23:15:00+00:00", "trade_duration": 330, "open_rate": 5.414e-05, "close_rate": 5.441137844611528e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1847.063169560399, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 21:15:00+00:00", "close_date": "2018-01-25 21:55:00+00:00", "trade_duration": 40, "open_rate": 0.04140777, "close_rate": 0.0416153277443609, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.415005686130888, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 02:05:00+00:00", "close_date": "2018-01-26 02:45:00+00:00", "trade_duration": 40, "open_rate": 0.00254309, "close_rate": 0.002555837318295739, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.32224183965177, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 02:55:00+00:00", "close_date": "2018-01-26 15:10:00+00:00", "trade_duration": 735, "open_rate": 5.607e-05, "close_rate": 5.6351052631578935e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1783.4849295523454, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 06:10:00+00:00", "close_date": "2018-01-26 09:25:00+00:00", "trade_duration": 195, "open_rate": 0.00253806, "close_rate": 0.0025507821052631577, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.400171784748984, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 07:25:00+00:00", "close_date": "2018-01-26 09:55:00+00:00", "trade_duration": 150, "open_rate": 0.0415, "close_rate": 0.04170802005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4096385542168677, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-26 09:55:00+00:00", "close_date": "2018-01-26 10:25:00+00:00", "trade_duration": 30, "open_rate": 5.321e-05, "close_rate": 5.401015037593984e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1879.3459875963165, "profit_abs": 0.000999999999999987}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 16:05:00+00:00", "close_date": "2018-01-26 16:45:00+00:00", "trade_duration": 40, "open_rate": 0.02772046, "close_rate": 0.02785940967418546, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.6074437437185387, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 23:35:00+00:00", "close_date": "2018-01-27 00:15:00+00:00", "trade_duration": 40, "open_rate": 0.09461341, "close_rate": 0.09508766268170424, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0569326272036914, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 00:35:00+00:00", "close_date": "2018-01-27 01:30:00+00:00", "trade_duration": 55, "open_rate": 5.615e-05, "close_rate": 5.643145363408521e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1780.9439002671415, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.07877175, "open_date": "2018-01-27 00:45:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 4560, "open_rate": 5.556e-05, "close_rate": 5.144e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1799.8560115190785, "profit_abs": -0.007896868250539965}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 02:30:00+00:00", "close_date": "2018-01-27 11:25:00+00:00", "trade_duration": 535, "open_rate": 0.06900001, "close_rate": 0.06934587471177944, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492751522789635, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 06:25:00+00:00", "close_date": "2018-01-27 07:05:00+00:00", "trade_duration": 40, "open_rate": 0.09449985, "close_rate": 0.0949735334586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.058202737887944, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.04815133, "open_date": "2018-01-27 09:40:00+00:00", "close_date": "2018-01-30 04:40:00+00:00", "trade_duration": 4020, "open_rate": 0.0410697, "close_rate": 0.03928809, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4348850855983852, "profit_abs": -0.004827170578309559}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 11:45:00+00:00", "close_date": "2018-01-27 12:30:00+00:00", "trade_duration": 45, "open_rate": 0.0285, "close_rate": 0.02864285714285714, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5087719298245617, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 12:35:00+00:00", "close_date": "2018-01-27 15:25:00+00:00", "trade_duration": 170, "open_rate": 0.02866372, "close_rate": 0.02880739779448621, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.4887307020861216, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 15:50:00+00:00", "close_date": "2018-01-27 16:50:00+00:00", "trade_duration": 60, "open_rate": 0.095381, "close_rate": 0.09585910025062656, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0484268355332824, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 17:05:00+00:00", "close_date": "2018-01-27 17:45:00+00:00", "trade_duration": 40, "open_rate": 0.06759092, "close_rate": 0.06792972160401002, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4794886650455417, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 23:40:00+00:00", "close_date": "2018-01-28 01:05:00+00:00", "trade_duration": 85, "open_rate": 0.00258501, "close_rate": 0.002597967443609022, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.684569885609726, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 02:25:00+00:00", "close_date": "2018-01-28 08:10:00+00:00", "trade_duration": 345, "open_rate": 0.06698502, "close_rate": 0.0673207845112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4928710926711672, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 10:25:00+00:00", "close_date": "2018-01-28 16:30:00+00:00", "trade_duration": 365, "open_rate": 0.0677177, "close_rate": 0.06805713709273183, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4767187899175547, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-28 20:35:00+00:00", "close_date": "2018-01-28 21:35:00+00:00", "trade_duration": 60, "open_rate": 5.215e-05, "close_rate": 5.2411403508771925e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1917.5455417066157, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-28 22:00:00+00:00", "close_date": "2018-01-28 22:30:00+00:00", "trade_duration": 30, "open_rate": 0.00273809, "close_rate": 0.002779264285714285, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.5218089982433, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-29 00:00:00+00:00", "close_date": "2018-01-29 00:30:00+00:00", "trade_duration": 30, "open_rate": 0.00274632, "close_rate": 0.002787618045112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.412362725392526, "profit_abs": 0.0010000000000000148}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-29 02:15:00+00:00", "close_date": "2018-01-29 03:00:00+00:00", "trade_duration": 45, "open_rate": 0.01622478, "close_rate": 0.016306107218045113, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.163411768911504, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 03:05:00+00:00", "close_date": "2018-01-29 03:45:00+00:00", "trade_duration": 40, "open_rate": 0.069, "close_rate": 0.06934586466165413, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492753623188406, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 05:20:00+00:00", "close_date": "2018-01-29 06:55:00+00:00", "trade_duration": 95, "open_rate": 8.755e-05, "close_rate": 8.798884711779448e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1142.204454597373, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 07:00:00+00:00", "close_date": "2018-01-29 19:25:00+00:00", "trade_duration": 745, "open_rate": 0.06825763, "close_rate": 0.06859977350877192, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4650376815016872, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 19:45:00+00:00", "close_date": "2018-01-29 20:25:00+00:00", "trade_duration": 40, "open_rate": 0.06713892, "close_rate": 0.06747545593984962, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4894490408841845, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0199116, "open_date": "2018-01-29 23:30:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 315, "open_rate": 8.934e-05, "close_rate": 8.8e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1119.3194537721067, "profit_abs": -0.0019961383478844796}], "results_per_pair": [{"key": "TRX/BTC", "trades": 15, "profit_mean": 0.0023467073333333323, "profit_mean_pct": 0.23467073333333321, "profit_sum": 0.035200609999999986, "profit_sum_pct": 3.5200609999999988, "profit_total_abs": 0.0035288616521155086, "profit_total_pct": 1.1733536666666662, "duration_avg": "2:28:00", "wins": 9, "draws": 2, "losses": 4}, {"key": "ADA/BTC", "trades": 29, "profit_mean": -0.0011598141379310352, "profit_mean_pct": -0.11598141379310352, "profit_sum": -0.03363461000000002, "profit_sum_pct": -3.3634610000000023, "profit_total_abs": -0.0033718682505400333, "profit_total_pct": -1.1211536666666675, "duration_avg": "5:35:00", "wins": 9, "draws": 11, "losses": 9}, {"key": "XLM/BTC", "trades": 21, "profit_mean": 0.0026243899999999994, "profit_mean_pct": 0.2624389999999999, "profit_sum": 0.05511218999999999, "profit_sum_pct": 5.511218999999999, "profit_total_abs": 0.005525000000000002, "profit_total_pct": 1.8370729999999995, "duration_avg": "3:21:00", "wins": 12, "draws": 3, "losses": 6}, {"key": "ETH/BTC", "trades": 21, "profit_mean": 0.0009500057142857142, "profit_mean_pct": 0.09500057142857142, "profit_sum": 0.01995012, "profit_sum_pct": 1.9950119999999998, "profit_total_abs": 0.0019999999999999463, "profit_total_pct": 0.6650039999999999, "duration_avg": "2:17:00", "wins": 5, "draws": 10, "losses": 6}, {"key": "XMR/BTC", "trades": 16, "profit_mean": -0.0027899012500000007, "profit_mean_pct": -0.2789901250000001, "profit_sum": -0.04463842000000001, "profit_sum_pct": -4.463842000000001, "profit_total_abs": -0.0044750000000000345, "profit_total_pct": -1.4879473333333337, "duration_avg": "8:41:00", "wins": 6, "draws": 5, "losses": 5}, {"key": "ZEC/BTC", "trades": 21, "profit_mean": -0.00039290904761904774, "profit_mean_pct": -0.03929090476190478, "profit_sum": -0.008251090000000003, "profit_sum_pct": -0.8251090000000003, "profit_total_abs": -0.000827170578309569, "profit_total_pct": -0.27503633333333344, "duration_avg": "4:17:00", "wins": 8, "draws": 7, "losses": 6}, {"key": "NXT/BTC", "trades": 12, "profit_mean": -0.0012261025000000006, "profit_mean_pct": -0.12261025000000006, "profit_sum": -0.014713230000000008, "profit_sum_pct": -1.4713230000000008, "profit_total_abs": -0.0014750000000000874, "profit_total_pct": -0.4904410000000003, "duration_avg": "0:57:00", "wins": 4, "draws": 3, "losses": 5}, {"key": "LTC/BTC", "trades": 8, "profit_mean": 0.00748129625, "profit_mean_pct": 0.748129625, "profit_sum": 0.05985037, "profit_sum_pct": 5.985037, "profit_total_abs": 0.006000000000000019, "profit_total_pct": 1.9950123333333334, "duration_avg": "1:59:00", "wins": 5, "draws": 2, "losses": 1}, {"key": "ETC/BTC", "trades": 20, "profit_mean": 0.0022568569999999997, "profit_mean_pct": 0.22568569999999996, "profit_sum": 0.04513713999999999, "profit_sum_pct": 4.513713999999999, "profit_total_abs": 0.004525000000000001, "profit_total_pct": 1.504571333333333, "duration_avg": "1:45:00", "wins": 11, "draws": 4, "losses": 5}, {"key": "DASH/BTC", "trades": 16, "profit_mean": 0.0018703237499999997, "profit_mean_pct": 0.18703237499999997, "profit_sum": 0.029925179999999996, "profit_sum_pct": 2.9925179999999996, "profit_total_abs": 0.002999999999999961, "profit_total_pct": 0.9975059999999999, "duration_avg": "3:03:00", "wins": 4, "draws": 7, "losses": 5}, {"key": "TOTAL", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}], "sell_reason_summary": [{"sell_reason": "roi", "trades": 170, "wins": 73, "draws": 54, "losses": 43, "profit_mean": 0.005398268352941177, "profit_mean_pct": 0.54, "profit_sum": 0.91770562, "profit_sum_pct": 91.77, "profit_total_abs": 0.09199999999999964, "profit_pct_total": 30.59}, {"sell_reason": "stop_loss", "trades": 6, "wins": 0, "draws": 0, "losses": 6, "profit_mean": -0.10448878000000002, "profit_mean_pct": -10.45, "profit_sum": -0.6269326800000001, "profit_sum_pct": -62.69, "profit_total_abs": -0.06284999999999992, "profit_pct_total": -20.9}, {"sell_reason": "force_sell", "trades": 3, "wins": 0, "draws": 0, "losses": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.89, "profit_sum": -0.14683468, "profit_sum_pct": -14.68, "profit_total_abs": -0.014720177176734003, "profit_pct_total": -4.89}], "left_open_trades": [{"key": "TRX/BTC", "trades": 1, "profit_mean": -0.0199116, "profit_mean_pct": -1.9911600000000003, "profit_sum": -0.0199116, "profit_sum_pct": -1.9911600000000003, "profit_total_abs": -0.0019961383478844796, "profit_total_pct": -0.6637200000000001, "duration_avg": "5:15:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ADA/BTC", "trades": 1, "profit_mean": -0.07877175, "profit_mean_pct": -7.877175, "profit_sum": -0.07877175, "profit_sum_pct": -7.877175, "profit_total_abs": -0.007896868250539965, "profit_total_pct": -2.625725, "duration_avg": "3 days, 4:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ZEC/BTC", "trades": 1, "profit_mean": -0.04815133, "profit_mean_pct": -4.815133, "profit_sum": -0.04815133, "profit_sum_pct": -4.815133, "profit_total_abs": -0.004827170578309559, "profit_total_pct": -1.6050443333333335, "duration_avg": "2 days, 19:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "TOTAL", "trades": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.894489333333333, "profit_sum": -0.14683468, "profit_sum_pct": -14.683468, "profit_total_abs": -0.014720177176734003, "profit_total_pct": -4.8944893333333335, "duration_avg": "2 days, 1:25:00", "wins": 0, "draws": 0, "losses": 3}], "total_trades": 179, "backtest_start": "2018-01-30 04:45:00+00:00", "backtest_start_ts": 1517287500, "backtest_end": "2018-01-30 04:45:00+00:00", "backtest_end_ts": 1517287500, "backtest_days": 0, "trades_per_day": null, "market_change": 0.25, "stake_amount": 0.1, "max_drawdown": 0.21142322000000008, "drawdown_start": "2018-01-24 14:25:00+00:00", "drawdown_start_ts": 1516803900.0, "drawdown_end": "2018-01-30 04:45:00+00:00", "drawdown_end_ts": 1517287500.0, "pairlist": ["TRX/BTC", "ADA/BTC", "XLM/BTC", "ETH/BTC", "XMR/BTC", "ZEC/BTC","NXT/BTC", "LTC/BTC", "ETC/BTC", "DASH/BTC"]}}, "strategy_comparison": [{"key": "DefaultStrategy", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}]} +{"strategy": {"StrategyTestV2": {"trades": [{"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:20:00+00:00", "trade_duration": 5, "open_rate": 9.64e-05, "close_rate": 0.00010074887218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1037.344398340249, "profit_abs": 0.00399999999999999}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:15:00+00:00", "close_date": "2018-01-10 07:30:00+00:00", "trade_duration": 15, "open_rate": 4.756e-05, "close_rate": 4.9705563909774425e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2102.6072329688814, "profit_abs": 0.00399999999999999}, {"pair": "XLM/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:35:00+00:00", "trade_duration": 10, "open_rate": 3.339e-05, "close_rate": 3.489631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2994.908655286014, "profit_abs": 0.0040000000000000036}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-10 07:25:00+00:00", "close_date": "2018-01-10 07:40:00+00:00", "trade_duration": 15, "open_rate": 9.696e-05, "close_rate": 0.00010133413533834584, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1031.3531353135315, "profit_abs": 0.00399999999999999}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 07:35:00+00:00", "close_date": "2018-01-10 08:35:00+00:00", "trade_duration": 60, "open_rate": 0.0943, "close_rate": 0.09477268170426063, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0604453870625663, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 07:40:00+00:00", "close_date": "2018-01-10 08:10:00+00:00", "trade_duration": 30, "open_rate": 0.02719607, "close_rate": 0.02760503345864661, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.677001860930642, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 08:15:00+00:00", "close_date": "2018-01-10 09:55:00+00:00", "trade_duration": 100, "open_rate": 0.04634952, "close_rate": 0.046581848421052625, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1575196463739, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 14:45:00+00:00", "close_date": "2018-01-10 15:50:00+00:00", "trade_duration": 65, "open_rate": 3.066e-05, "close_rate": 3.081368421052631e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3261.5786040443577, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 16:35:00+00:00", "close_date": "2018-01-10 17:15:00+00:00", "trade_duration": 40, "open_rate": 0.0168999, "close_rate": 0.016984611278195488, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.917194776300452, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 16:40:00+00:00", "close_date": "2018-01-10 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.09132568, "close_rate": 0.0917834528320802, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0949822656672252, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 18:50:00+00:00", "close_date": "2018-01-10 19:45:00+00:00", "trade_duration": 55, "open_rate": 0.08898003, "close_rate": 0.08942604518796991, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1238476768326557, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-10 22:15:00+00:00", "close_date": "2018-01-10 23:00:00+00:00", "trade_duration": 45, "open_rate": 0.08560008, "close_rate": 0.08602915308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1682232072680307, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-10 22:50:00+00:00", "close_date": "2018-01-10 23:20:00+00:00", "trade_duration": 30, "open_rate": 0.00249083, "close_rate": 0.0025282860902255634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 40.147260150231055, "profit_abs": 0.000999999999999987}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-10 23:15:00+00:00", "close_date": "2018-01-11 00:15:00+00:00", "trade_duration": 60, "open_rate": 3.022e-05, "close_rate": 3.037147869674185e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3309.0668431502318, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-10 23:40:00+00:00", "close_date": "2018-01-11 00:05:00+00:00", "trade_duration": 25, "open_rate": 0.002437, "close_rate": 0.0024980776942355883, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.03405826836274, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 00:00:00+00:00", "close_date": "2018-01-11 00:35:00+00:00", "trade_duration": 35, "open_rate": 0.04771803, "close_rate": 0.04843559436090225, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0956439316543456, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-11 03:40:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 45, "open_rate": 3.651e-05, "close_rate": 3.2859000000000005e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2738.9756231169545, "profit_abs": -0.01047499999999997}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 03:55:00+00:00", "close_date": "2018-01-11 04:25:00+00:00", "trade_duration": 30, "open_rate": 0.08824105, "close_rate": 0.08956798308270676, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1332594070446804, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 04:00:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 50, "open_rate": 0.00243, "close_rate": 0.002442180451127819, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 41.1522633744856, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:55:00+00:00", "trade_duration": 25, "open_rate": 0.04545064, "close_rate": 0.046589753784461146, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.200189040242338, "profit_abs": 0.001999999999999988}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:30:00+00:00", "close_date": "2018-01-11 04:50:00+00:00", "trade_duration": 20, "open_rate": 3.372e-05, "close_rate": 3.456511278195488e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2965.599051008304, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 04:55:00+00:00", "close_date": "2018-01-11 05:15:00+00:00", "trade_duration": 20, "open_rate": 0.02644, "close_rate": 0.02710265664160401, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7821482602118004, "profit_abs": 0.001999999999999988}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:20:00+00:00", "close_date": "2018-01-11 12:00:00+00:00", "trade_duration": 40, "open_rate": 0.08812, "close_rate": 0.08856170426065162, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1348161597821154, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 11:35:00+00:00", "close_date": "2018-01-11 12:15:00+00:00", "trade_duration": 40, "open_rate": 0.02683577, "close_rate": 0.026970285137844607, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.7263696923919087, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-11 14:00:00+00:00", "close_date": "2018-01-11 14:25:00+00:00", "trade_duration": 25, "open_rate": 4.919e-05, "close_rate": 5.04228320802005e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.9335230737956, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 19:25:00+00:00", "close_date": "2018-01-11 20:35:00+00:00", "trade_duration": 70, "open_rate": 0.08784896, "close_rate": 0.08828930566416039, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1383174029607181, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:35:00+00:00", "close_date": "2018-01-11 23:30:00+00:00", "trade_duration": 55, "open_rate": 5.105e-05, "close_rate": 5.130588972431077e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1958.8638589618022, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:25:00+00:00", "trade_duration": 30, "open_rate": 3.96e-05, "close_rate": 4.019548872180451e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2525.252525252525, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 22:55:00+00:00", "close_date": "2018-01-11 23:35:00+00:00", "trade_duration": 40, "open_rate": 2.885e-05, "close_rate": 2.899461152882205e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3466.204506065858, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-11 23:30:00+00:00", "close_date": "2018-01-12 00:05:00+00:00", "trade_duration": 35, "open_rate": 0.02645, "close_rate": 0.026847744360902256, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.780718336483932, "profit_abs": 0.0010000000000000148}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-11 23:55:00+00:00", "close_date": "2018-01-12 01:15:00+00:00", "trade_duration": 80, "open_rate": 0.048, "close_rate": 0.04824060150375939, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0833333333333335, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-12 21:15:00+00:00", "close_date": "2018-01-12 21:40:00+00:00", "trade_duration": 25, "open_rate": 4.692e-05, "close_rate": 4.809593984962405e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2131.287297527707, "profit_abs": 0.001999999999999974}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 00:55:00+00:00", "close_date": "2018-01-13 06:20:00+00:00", "trade_duration": 325, "open_rate": 0.00256966, "close_rate": 0.0025825405012531327, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.91565421106294, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 10:55:00+00:00", "close_date": "2018-01-13 11:35:00+00:00", "trade_duration": 40, "open_rate": 6.262e-05, "close_rate": 6.293388471177944e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1596.933886937081, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-13 13:05:00+00:00", "close_date": "2018-01-15 14:10:00+00:00", "trade_duration": 2945, "open_rate": 4.73e-05, "close_rate": 4.753709273182957e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2114.1649048625795, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:30:00+00:00", "close_date": "2018-01-13 14:45:00+00:00", "trade_duration": 75, "open_rate": 6.063e-05, "close_rate": 6.0933909774436085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1649.348507339601, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 13:40:00+00:00", "close_date": "2018-01-13 23:30:00+00:00", "trade_duration": 590, "open_rate": 0.00011082, "close_rate": 0.00011137548872180448, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 902.3641941887746, "profit_abs": -2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 15:15:00+00:00", "close_date": "2018-01-13 15:55:00+00:00", "trade_duration": 40, "open_rate": 5.93e-05, "close_rate": 5.9597243107769415e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1686.3406408094436, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 16:30:00+00:00", "close_date": "2018-01-13 17:10:00+00:00", "trade_duration": 40, "open_rate": 0.04850003, "close_rate": 0.04874313791979949, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.0618543947292407, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-13 22:05:00+00:00", "close_date": "2018-01-14 06:25:00+00:00", "trade_duration": 500, "open_rate": 0.09825019, "close_rate": 0.09874267215538848, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0178097365511456, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 00:20:00+00:00", "close_date": "2018-01-14 22:55:00+00:00", "trade_duration": 1355, "open_rate": 6.018e-05, "close_rate": 6.048165413533834e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1661.681621801263, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 12:45:00+00:00", "close_date": "2018-01-14 13:25:00+00:00", "trade_duration": 40, "open_rate": 0.09758999, "close_rate": 0.0980791628822055, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.024695258191952, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-14 15:30:00+00:00", "close_date": "2018-01-14 16:00:00+00:00", "trade_duration": 30, "open_rate": 0.00311, "close_rate": 0.0031567669172932328, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.154340836012864, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 20:45:00+00:00", "close_date": "2018-01-14 22:15:00+00:00", "trade_duration": 90, "open_rate": 0.00312401, "close_rate": 0.003139669197994987, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 32.010140812609436, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-14 23:35:00+00:00", "close_date": "2018-01-15 00:30:00+00:00", "trade_duration": 55, "open_rate": 0.0174679, "close_rate": 0.017555458395989976, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 5.724786608579165, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-14 23:45:00+00:00", "close_date": "2018-01-15 00:25:00+00:00", "trade_duration": 40, "open_rate": 0.07346846, "close_rate": 0.07383672295739348, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.3611282991367997, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 02:25:00+00:00", "close_date": "2018-01-15 03:05:00+00:00", "trade_duration": 40, "open_rate": 0.097994, "close_rate": 0.09848519799498744, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.020470641059657, "profit_abs": -2.7755575615628914e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 07:20:00+00:00", "close_date": "2018-01-15 08:00:00+00:00", "trade_duration": 40, "open_rate": 0.09659, "close_rate": 0.09707416040100247, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0353038616834043, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-15 08:20:00+00:00", "close_date": "2018-01-15 08:55:00+00:00", "trade_duration": 35, "open_rate": 9.987e-05, "close_rate": 0.00010137180451127818, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1001.3016921998599, "profit_abs": 0.0010000000000000009}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-15 12:10:00+00:00", "close_date": "2018-01-16 02:50:00+00:00", "trade_duration": 880, "open_rate": 0.0948969, "close_rate": 0.09537257368421052, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0537752023511833, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:10:00+00:00", "close_date": "2018-01-15 17:40:00+00:00", "trade_duration": 210, "open_rate": 0.071, "close_rate": 0.07135588972431077, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4084507042253522, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 14:30:00+00:00", "close_date": "2018-01-15 15:10:00+00:00", "trade_duration": 40, "open_rate": 0.04600501, "close_rate": 0.046235611553884705, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.173676301776698, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:10:00+00:00", "close_date": "2018-01-15 19:25:00+00:00", "trade_duration": 75, "open_rate": 9.438e-05, "close_rate": 9.485308270676693e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1059.5465140919687, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 18:35:00+00:00", "close_date": "2018-01-15 19:15:00+00:00", "trade_duration": 40, "open_rate": 0.03040001, "close_rate": 0.030552391002506264, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.2894726021471703, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-15 20:25:00+00:00", "close_date": "2018-01-16 08:25:00+00:00", "trade_duration": 720, "open_rate": 5.837e-05, "close_rate": 5.2533e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1713.2088401576154, "profit_abs": -0.010474999999999984}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-15 20:40:00+00:00", "close_date": "2018-01-15 22:00:00+00:00", "trade_duration": 80, "open_rate": 0.046036, "close_rate": 0.04626675689223057, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.1722130506560084, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 00:30:00+00:00", "close_date": "2018-01-16 01:10:00+00:00", "trade_duration": 40, "open_rate": 0.0028685, "close_rate": 0.0028828784461152877, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 34.86142583231654, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 01:15:00+00:00", "close_date": "2018-01-16 02:35:00+00:00", "trade_duration": 80, "open_rate": 0.06731755, "close_rate": 0.0676549813283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4854967241083492, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 07:45:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 55, "open_rate": 0.09217614, "close_rate": 0.09263817578947368, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0848794492804754, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:55:00+00:00", "trade_duration": 20, "open_rate": 0.0165, "close_rate": 0.016913533834586467, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.0606060606060606, "profit_abs": 0.0020000000000000018}, {"pair": "TRX/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 08:35:00+00:00", "close_date": "2018-01-16 08:40:00+00:00", "trade_duration": 5, "open_rate": 7.953e-05, "close_rate": 8.311781954887218e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1257.387149503332, "profit_abs": 0.00399999999999999}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 08:45:00+00:00", "close_date": "2018-01-16 09:50:00+00:00", "trade_duration": 65, "open_rate": 0.045202, "close_rate": 0.04542857644110275, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2122914915269236, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:45:00+00:00", "trade_duration": 30, "open_rate": 5.248e-05, "close_rate": 5.326917293233082e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1905.487804878049, "profit_abs": 0.0010000000000000009}, {"pair": "XMR/BTC", "profit_percent": 0.0, "open_date": "2018-01-16 09:15:00+00:00", "close_date": "2018-01-16 09:55:00+00:00", "trade_duration": 40, "open_rate": 0.02892318, "close_rate": 0.02906815834586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.457434486802627, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 09:50:00+00:00", "close_date": "2018-01-16 10:10:00+00:00", "trade_duration": 20, "open_rate": 5.158e-05, "close_rate": 5.287273182957392e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1938.735944164405, "profit_abs": 0.001999999999999988}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:35:00+00:00", "trade_duration": 30, "open_rate": 0.02828232, "close_rate": 0.02870761804511278, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5357778286929786, "profit_abs": 0.0010000000000000009}, {"pair": "ZEC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 10:05:00+00:00", "close_date": "2018-01-16 10:40:00+00:00", "trade_duration": 35, "open_rate": 0.04357584, "close_rate": 0.044231115789473675, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.294849623093898, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 13:45:00+00:00", "close_date": "2018-01-16 14:20:00+00:00", "trade_duration": 35, "open_rate": 5.362e-05, "close_rate": 5.442631578947368e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1864.975755315181, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-16 17:30:00+00:00", "close_date": "2018-01-16 18:25:00+00:00", "trade_duration": 55, "open_rate": 5.302e-05, "close_rate": 5.328576441102756e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.0807242549984, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:45:00+00:00", "trade_duration": 30, "open_rate": 0.09129999, "close_rate": 0.09267292218045112, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0952903718828448, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 18:15:00+00:00", "close_date": "2018-01-16 18:35:00+00:00", "trade_duration": 20, "open_rate": 3.808e-05, "close_rate": 3.903438596491228e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2626.0504201680674, "profit_abs": 0.0020000000000000018}, {"pair": "XMR/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-16 19:00:00+00:00", "close_date": "2018-01-16 19:30:00+00:00", "trade_duration": 30, "open_rate": 0.02811012, "close_rate": 0.028532828571428567, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.557437677249333, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 60, "open_rate": 0.00258379, "close_rate": 0.002325411, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.702835756775904, "profit_abs": -0.010474999999999984}, {"pair": "NXT/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:25:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 80, "open_rate": 2.559e-05, "close_rate": 2.3031e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3907.7764751856193, "profit_abs": -0.010474999999999998}, {"pair": "TRX/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-16 21:35:00+00:00", "close_date": "2018-01-16 22:25:00+00:00", "trade_duration": 50, "open_rate": 7.62e-05, "close_rate": 6.858e-05, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1312.3359580052495, "profit_abs": -0.010474999999999984}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:35:00+00:00", "trade_duration": 5, "open_rate": 0.00229844, "close_rate": 0.002402129022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 43.507770487809125, "profit_abs": 0.004000000000000017}, {"pair": "LTC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:30:00+00:00", "close_date": "2018-01-16 22:40:00+00:00", "trade_duration": 10, "open_rate": 0.0151, "close_rate": 0.015781203007518795, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.622516556291391, "profit_abs": 0.00399999999999999}, {"pair": "ETC/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:40:00+00:00", "close_date": "2018-01-16 22:45:00+00:00", "trade_duration": 5, "open_rate": 0.00235676, "close_rate": 0.00246308, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 42.431134269081284, "profit_abs": 0.0040000000000000036}, {"pair": "DASH/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-16 22:45:00+00:00", "close_date": "2018-01-16 23:05:00+00:00", "trade_duration": 20, "open_rate": 0.0630692, "close_rate": 0.06464988170426066, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.585559988076589, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-16 22:50:00+00:00", "close_date": "2018-01-16 22:55:00+00:00", "trade_duration": 5, "open_rate": 2.2e-05, "close_rate": 2.299248120300751e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 4545.454545454546, "profit_abs": 0.003999999999999976}, {"pair": "ADA/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-17 03:30:00+00:00", "close_date": "2018-01-17 04:00:00+00:00", "trade_duration": 30, "open_rate": 4.974e-05, "close_rate": 5.048796992481203e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2010.454362685967, "profit_abs": 0.0010000000000000009}, {"pair": "TRX/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-17 03:55:00+00:00", "close_date": "2018-01-17 04:15:00+00:00", "trade_duration": 20, "open_rate": 7.108e-05, "close_rate": 7.28614536340852e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1406.8655036578502, "profit_abs": 0.001999999999999974}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 09:35:00+00:00", "close_date": "2018-01-17 10:15:00+00:00", "trade_duration": 40, "open_rate": 0.04327, "close_rate": 0.04348689223057644, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.3110700254217704, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:20:00+00:00", "close_date": "2018-01-17 17:00:00+00:00", "trade_duration": 400, "open_rate": 4.997e-05, "close_rate": 5.022047619047618e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2001.2007204322595, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:25:00+00:00", "trade_duration": 55, "open_rate": 0.06836818, "close_rate": 0.06871087764411027, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4626687444363737, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 10:30:00+00:00", "close_date": "2018-01-17 11:10:00+00:00", "trade_duration": 40, "open_rate": 3.63e-05, "close_rate": 3.648195488721804e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2754.8209366391184, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:30:00+00:00", "close_date": "2018-01-17 22:05:00+00:00", "trade_duration": 575, "open_rate": 0.0281, "close_rate": 0.02824085213032581, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5587188612099645, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-17 12:35:00+00:00", "close_date": "2018-01-17 16:55:00+00:00", "trade_duration": 260, "open_rate": 0.08651001, "close_rate": 0.08694364413533832, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1559355963546878, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 05:00:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 55, "open_rate": 5.633e-05, "close_rate": 5.6612355889724306e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1775.2529735487308, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 05:20:00+00:00", "close_date": "2018-01-18 05:55:00+00:00", "trade_duration": 35, "open_rate": 0.06988494, "close_rate": 0.07093584135338346, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.430923457900944, "profit_abs": 0.0010000000000000009}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 07:35:00+00:00", "close_date": "2018-01-18 08:15:00+00:00", "trade_duration": 40, "open_rate": 5.545e-05, "close_rate": 5.572794486215538e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1803.4265103697026, "profit_abs": -1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 09:00:00+00:00", "close_date": "2018-01-18 09:40:00+00:00", "trade_duration": 40, "open_rate": 0.01633527, "close_rate": 0.016417151052631574, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.121723118136401, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 16:40:00+00:00", "close_date": "2018-01-18 17:20:00+00:00", "trade_duration": 40, "open_rate": 0.00269734, "close_rate": 0.002710860501253133, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.073561360451414, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-18 18:05:00+00:00", "close_date": "2018-01-18 18:30:00+00:00", "trade_duration": 25, "open_rate": 4.475e-05, "close_rate": 4.587155388471177e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2234.63687150838, "profit_abs": 0.0020000000000000018}, {"pair": "NXT/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-18 18:25:00+00:00", "close_date": "2018-01-18 18:55:00+00:00", "trade_duration": 30, "open_rate": 2.79e-05, "close_rate": 2.8319548872180444e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3584.2293906810037, "profit_abs": 0.000999999999999987}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 20:10:00+00:00", "close_date": "2018-01-18 20:50:00+00:00", "trade_duration": 40, "open_rate": 0.04439326, "close_rate": 0.04461578260651629, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.2525942001105577, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 21:30:00+00:00", "close_date": "2018-01-19 00:35:00+00:00", "trade_duration": 185, "open_rate": 4.49e-05, "close_rate": 4.51250626566416e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2227.1714922049, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-18 21:55:00+00:00", "close_date": "2018-01-19 05:05:00+00:00", "trade_duration": 430, "open_rate": 0.02855, "close_rate": 0.028693107769423555, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.502626970227671, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 22:10:00+00:00", "close_date": "2018-01-18 22:50:00+00:00", "trade_duration": 40, "open_rate": 5.796e-05, "close_rate": 5.8250526315789473e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1725.3278122843342, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-18 23:50:00+00:00", "close_date": "2018-01-19 00:30:00+00:00", "trade_duration": 40, "open_rate": 0.04340323, "close_rate": 0.04362079005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.303975994413319, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-19 16:45:00+00:00", "close_date": "2018-01-19 17:35:00+00:00", "trade_duration": 50, "open_rate": 0.04454455, "close_rate": 0.04476783095238095, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.244943545282195, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:15:00+00:00", "close_date": "2018-01-19 19:55:00+00:00", "trade_duration": 160, "open_rate": 5.62e-05, "close_rate": 5.648170426065162e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1779.3594306049824, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-19 17:20:00+00:00", "close_date": "2018-01-19 20:15:00+00:00", "trade_duration": 175, "open_rate": 4.339e-05, "close_rate": 4.360749373433584e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2304.6784973496196, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-20 04:45:00+00:00", "close_date": "2018-01-20 17:35:00+00:00", "trade_duration": 770, "open_rate": 0.0001009, "close_rate": 0.00010140576441102755, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 991.0802775024778, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 15:15:00+00:00", "trade_duration": 625, "open_rate": 0.00270505, "close_rate": 0.002718609147869674, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.96789338459548, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 04:50:00+00:00", "close_date": "2018-01-20 07:00:00+00:00", "trade_duration": 130, "open_rate": 0.03000002, "close_rate": 0.030150396040100245, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.3333311111125927, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 09:00:00+00:00", "close_date": "2018-01-20 09:40:00+00:00", "trade_duration": 40, "open_rate": 5.46e-05, "close_rate": 5.4873684210526304e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1831.5018315018317, "profit_abs": -1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.10448878, "open_date": "2018-01-20 18:25:00+00:00", "close_date": "2018-01-25 03:50:00+00:00", "trade_duration": 6325, "open_rate": 0.03082222, "close_rate": 0.027739998, "open_at_end": false, "sell_reason": "stop_loss", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.244412634781012, "profit_abs": -0.010474999999999998}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-20 22:25:00+00:00", "close_date": "2018-01-20 23:15:00+00:00", "trade_duration": 50, "open_rate": 0.08969999, "close_rate": 0.09014961401002504, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1148273260677064, "profit_abs": 0.0}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 02:50:00+00:00", "close_date": "2018-01-21 14:30:00+00:00", "trade_duration": 700, "open_rate": 0.01632501, "close_rate": 0.01640683962406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.125570520324337, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 10:20:00+00:00", "close_date": "2018-01-21 11:00:00+00:00", "trade_duration": 40, "open_rate": 0.070538, "close_rate": 0.07089157393483708, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.417675579120474, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 15:50:00+00:00", "close_date": "2018-01-21 18:45:00+00:00", "trade_duration": 175, "open_rate": 5.301e-05, "close_rate": 5.327571428571427e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1886.4365214110546, "profit_abs": -2.7755575615628914e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-21 16:20:00+00:00", "close_date": "2018-01-21 17:00:00+00:00", "trade_duration": 40, "open_rate": 3.955e-05, "close_rate": 3.9748245614035085e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2528.4450063211125, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:45:00+00:00", "trade_duration": 30, "open_rate": 0.00258505, "close_rate": 0.002623922932330827, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.6839712964933, "profit_abs": 0.0010000000000000009}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-21 21:15:00+00:00", "close_date": "2018-01-21 21:55:00+00:00", "trade_duration": 40, "open_rate": 3.903e-05, "close_rate": 3.922563909774435e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2562.1316935690497, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 00:35:00+00:00", "close_date": "2018-01-22 10:35:00+00:00", "trade_duration": 600, "open_rate": 5.236e-05, "close_rate": 5.262245614035087e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1909.8548510313217, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 01:30:00+00:00", "close_date": "2018-01-22 02:10:00+00:00", "trade_duration": 40, "open_rate": 9.028e-05, "close_rate": 9.07325313283208e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1107.6650420912717, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 12:25:00+00:00", "close_date": "2018-01-22 14:35:00+00:00", "trade_duration": 130, "open_rate": 0.002687, "close_rate": 0.002700468671679198, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 37.21622627465575, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 13:15:00+00:00", "close_date": "2018-01-22 13:55:00+00:00", "trade_duration": 40, "open_rate": 4.168e-05, "close_rate": 4.188892230576441e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2399.232245681382, "profit_abs": 1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-22 14:00:00+00:00", "close_date": "2018-01-22 14:30:00+00:00", "trade_duration": 30, "open_rate": 8.821e-05, "close_rate": 8.953646616541353e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1133.6583153837435, "profit_abs": 0.0010000000000000148}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-22 15:55:00+00:00", "close_date": "2018-01-22 16:40:00+00:00", "trade_duration": 45, "open_rate": 5.172e-05, "close_rate": 5.1979248120300745e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1933.4880123743235, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-22 16:05:00+00:00", "close_date": "2018-01-22 16:25:00+00:00", "trade_duration": 20, "open_rate": 3.026e-05, "close_rate": 3.101839598997494e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3304.692663582287, "profit_abs": 0.0020000000000000157}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 19:50:00+00:00", "close_date": "2018-01-23 00:10:00+00:00", "trade_duration": 260, "open_rate": 0.07064, "close_rate": 0.07099408521303258, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.415628539071348, "profit_abs": 1.3877787807814457e-17}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-22 21:25:00+00:00", "close_date": "2018-01-22 22:05:00+00:00", "trade_duration": 40, "open_rate": 0.01644483, "close_rate": 0.01652726022556391, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.080938507725528, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-23 00:05:00+00:00", "close_date": "2018-01-23 00:35:00+00:00", "trade_duration": 30, "open_rate": 4.331e-05, "close_rate": 4.3961278195488714e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2308.935580697299, "profit_abs": 0.0010000000000000148}, {"pair": "NXT/BTC", "profit_percent": 0.01995012, "open_date": "2018-01-23 01:50:00+00:00", "close_date": "2018-01-23 02:15:00+00:00", "trade_duration": 25, "open_rate": 3.2e-05, "close_rate": 3.2802005012531326e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3125.0000000000005, "profit_abs": 0.0020000000000000018}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 04:25:00+00:00", "close_date": "2018-01-23 05:15:00+00:00", "trade_duration": 50, "open_rate": 0.09167706, "close_rate": 0.09213659413533835, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0907854156754153, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 07:35:00+00:00", "close_date": "2018-01-23 09:00:00+00:00", "trade_duration": 85, "open_rate": 0.0692498, "close_rate": 0.06959691679197995, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4440474918339115, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 10:50:00+00:00", "close_date": "2018-01-23 13:05:00+00:00", "trade_duration": 135, "open_rate": 3.182e-05, "close_rate": 3.197949874686716e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3142.677561282213, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 11:05:00+00:00", "close_date": "2018-01-23 16:05:00+00:00", "trade_duration": 300, "open_rate": 0.04088, "close_rate": 0.04108491228070175, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4461839530332683, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 14:55:00+00:00", "close_date": "2018-01-23 15:35:00+00:00", "trade_duration": 40, "open_rate": 5.15e-05, "close_rate": 5.175814536340851e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1941.747572815534, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-23 16:35:00+00:00", "close_date": "2018-01-24 00:05:00+00:00", "trade_duration": 450, "open_rate": 0.09071698, "close_rate": 0.09117170170426064, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.1023294646713329, "profit_abs": 0.0}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 17:25:00+00:00", "close_date": "2018-01-23 18:45:00+00:00", "trade_duration": 80, "open_rate": 3.128e-05, "close_rate": 3.1436791979949865e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3196.9309462915603, "profit_abs": -2.7755575615628914e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 20:15:00+00:00", "close_date": "2018-01-23 22:00:00+00:00", "trade_duration": 105, "open_rate": 9.555e-05, "close_rate": 9.602894736842104e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1046.5724751439038, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 22:30:00+00:00", "close_date": "2018-01-23 23:10:00+00:00", "trade_duration": 40, "open_rate": 0.04080001, "close_rate": 0.0410045213283208, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.450979791426522, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-23 23:50:00+00:00", "close_date": "2018-01-24 03:35:00+00:00", "trade_duration": 225, "open_rate": 5.163e-05, "close_rate": 5.18887969924812e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1936.8584156498162, "profit_abs": 1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 00:20:00+00:00", "close_date": "2018-01-24 01:50:00+00:00", "trade_duration": 90, "open_rate": 0.04040781, "close_rate": 0.04061035541353383, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.474769110228938, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 06:45:00+00:00", "close_date": "2018-01-24 07:25:00+00:00", "trade_duration": 40, "open_rate": 5.132e-05, "close_rate": 5.157724310776942e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1948.5580670303975, "profit_abs": 0.0}, {"pair": "ADA/BTC", "profit_percent": 0.03990025, "open_date": "2018-01-24 14:15:00+00:00", "close_date": "2018-01-24 14:25:00+00:00", "trade_duration": 10, "open_rate": 5.198e-05, "close_rate": 5.432496240601503e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1923.8168526356292, "profit_abs": 0.0040000000000000036}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 14:50:00+00:00", "close_date": "2018-01-24 16:35:00+00:00", "trade_duration": 105, "open_rate": 3.054e-05, "close_rate": 3.069308270676692e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3274.3942370661425, "profit_abs": 0.0}, {"pair": "TRX/BTC", "profit_percent": 0.0, "open_date": "2018-01-24 15:10:00+00:00", "close_date": "2018-01-24 16:15:00+00:00", "trade_duration": 65, "open_rate": 9.263e-05, "close_rate": 9.309431077694236e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1079.5638562020945, "profit_abs": 2.7755575615628914e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-24 22:40:00+00:00", "close_date": "2018-01-24 23:25:00+00:00", "trade_duration": 45, "open_rate": 5.514e-05, "close_rate": 5.54163909774436e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1813.5654697134569, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 00:50:00+00:00", "close_date": "2018-01-25 01:30:00+00:00", "trade_duration": 40, "open_rate": 4.921e-05, "close_rate": 4.9456666666666664e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2032.1072952651903, "profit_abs": 1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-25 08:15:00+00:00", "close_date": "2018-01-25 12:15:00+00:00", "trade_duration": 240, "open_rate": 0.0026, "close_rate": 0.002613032581453634, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.46153846153847, "profit_abs": 1.3877787807814457e-17}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 10:25:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 350, "open_rate": 0.02799871, "close_rate": 0.028139054411027563, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.571593119825878, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 11:00:00+00:00", "close_date": "2018-01-25 11:45:00+00:00", "trade_duration": 45, "open_rate": 0.04078902, "close_rate": 0.0409934762406015, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4516401717913303, "profit_abs": -1.3877787807814457e-17}, {"pair": "NXT/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:05:00+00:00", "close_date": "2018-01-25 13:45:00+00:00", "trade_duration": 40, "open_rate": 2.89e-05, "close_rate": 2.904486215538847e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3460.2076124567475, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 13:20:00+00:00", "close_date": "2018-01-25 14:05:00+00:00", "trade_duration": 45, "open_rate": 0.041103, "close_rate": 0.04130903007518797, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4329124394813033, "profit_abs": 1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-25 15:45:00+00:00", "close_date": "2018-01-25 16:15:00+00:00", "trade_duration": 30, "open_rate": 5.428e-05, "close_rate": 5.509624060150376e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1842.2991893883568, "profit_abs": 0.0010000000000000148}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 17:45:00+00:00", "close_date": "2018-01-25 23:15:00+00:00", "trade_duration": 330, "open_rate": 5.414e-05, "close_rate": 5.441137844611528e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1847.063169560399, "profit_abs": -1.3877787807814457e-17}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-25 21:15:00+00:00", "close_date": "2018-01-25 21:55:00+00:00", "trade_duration": 40, "open_rate": 0.04140777, "close_rate": 0.0416153277443609, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.415005686130888, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 02:05:00+00:00", "close_date": "2018-01-26 02:45:00+00:00", "trade_duration": 40, "open_rate": 0.00254309, "close_rate": 0.002555837318295739, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.32224183965177, "profit_abs": 1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 02:55:00+00:00", "close_date": "2018-01-26 15:10:00+00:00", "trade_duration": 735, "open_rate": 5.607e-05, "close_rate": 5.6351052631578935e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1783.4849295523454, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETC/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 06:10:00+00:00", "close_date": "2018-01-26 09:25:00+00:00", "trade_duration": 195, "open_rate": 0.00253806, "close_rate": 0.0025507821052631577, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 39.400171784748984, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 07:25:00+00:00", "close_date": "2018-01-26 09:55:00+00:00", "trade_duration": 150, "open_rate": 0.0415, "close_rate": 0.04170802005012531, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4096385542168677, "profit_abs": 0.0}, {"pair": "XLM/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-26 09:55:00+00:00", "close_date": "2018-01-26 10:25:00+00:00", "trade_duration": 30, "open_rate": 5.321e-05, "close_rate": 5.401015037593984e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1879.3459875963165, "profit_abs": 0.000999999999999987}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-26 16:05:00+00:00", "close_date": "2018-01-26 16:45:00+00:00", "trade_duration": 40, "open_rate": 0.02772046, "close_rate": 0.02785940967418546, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.6074437437185387, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": 0.0, "open_date": "2018-01-26 23:35:00+00:00", "close_date": "2018-01-27 00:15:00+00:00", "trade_duration": 40, "open_rate": 0.09461341, "close_rate": 0.09508766268170424, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0569326272036914, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 00:35:00+00:00", "close_date": "2018-01-27 01:30:00+00:00", "trade_duration": 55, "open_rate": 5.615e-05, "close_rate": 5.643145363408521e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1780.9439002671415, "profit_abs": -1.3877787807814457e-17}, {"pair": "ADA/BTC", "profit_percent": -0.07877175, "open_date": "2018-01-27 00:45:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 4560, "open_rate": 5.556e-05, "close_rate": 5.144e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1799.8560115190785, "profit_abs": -0.007896868250539965}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 02:30:00+00:00", "close_date": "2018-01-27 11:25:00+00:00", "trade_duration": 535, "open_rate": 0.06900001, "close_rate": 0.06934587471177944, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492751522789635, "profit_abs": 0.0}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 06:25:00+00:00", "close_date": "2018-01-27 07:05:00+00:00", "trade_duration": 40, "open_rate": 0.09449985, "close_rate": 0.0949735334586466, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.058202737887944, "profit_abs": 0.0}, {"pair": "ZEC/BTC", "profit_percent": -0.04815133, "open_date": "2018-01-27 09:40:00+00:00", "close_date": "2018-01-30 04:40:00+00:00", "trade_duration": 4020, "open_rate": 0.0410697, "close_rate": 0.03928809, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 2.4348850855983852, "profit_abs": -0.004827170578309559}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 11:45:00+00:00", "close_date": "2018-01-27 12:30:00+00:00", "trade_duration": 45, "open_rate": 0.0285, "close_rate": 0.02864285714285714, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.5087719298245617, "profit_abs": 0.0}, {"pair": "XMR/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 12:35:00+00:00", "close_date": "2018-01-27 15:25:00+00:00", "trade_duration": 170, "open_rate": 0.02866372, "close_rate": 0.02880739779448621, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 3.4887307020861216, "profit_abs": -1.3877787807814457e-17}, {"pair": "ETH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 15:50:00+00:00", "close_date": "2018-01-27 16:50:00+00:00", "trade_duration": 60, "open_rate": 0.095381, "close_rate": 0.09585910025062656, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.0484268355332824, "profit_abs": 1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 17:05:00+00:00", "close_date": "2018-01-27 17:45:00+00:00", "trade_duration": 40, "open_rate": 0.06759092, "close_rate": 0.06792972160401002, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4794886650455417, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": -0.0, "open_date": "2018-01-27 23:40:00+00:00", "close_date": "2018-01-28 01:05:00+00:00", "trade_duration": 85, "open_rate": 0.00258501, "close_rate": 0.002597967443609022, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 38.684569885609726, "profit_abs": -1.3877787807814457e-17}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 02:25:00+00:00", "close_date": "2018-01-28 08:10:00+00:00", "trade_duration": 345, "open_rate": 0.06698502, "close_rate": 0.0673207845112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4928710926711672, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-28 10:25:00+00:00", "close_date": "2018-01-28 16:30:00+00:00", "trade_duration": 365, "open_rate": 0.0677177, "close_rate": 0.06805713709273183, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4767187899175547, "profit_abs": -1.3877787807814457e-17}, {"pair": "XLM/BTC", "profit_percent": 0.0, "open_date": "2018-01-28 20:35:00+00:00", "close_date": "2018-01-28 21:35:00+00:00", "trade_duration": 60, "open_rate": 5.215e-05, "close_rate": 5.2411403508771925e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1917.5455417066157, "profit_abs": 0.0}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-28 22:00:00+00:00", "close_date": "2018-01-28 22:30:00+00:00", "trade_duration": 30, "open_rate": 0.00273809, "close_rate": 0.002779264285714285, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.5218089982433, "profit_abs": 0.0010000000000000009}, {"pair": "ETC/BTC", "profit_percent": 0.00997506, "open_date": "2018-01-29 00:00:00+00:00", "close_date": "2018-01-29 00:30:00+00:00", "trade_duration": 30, "open_rate": 0.00274632, "close_rate": 0.002787618045112782, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 36.412362725392526, "profit_abs": 0.0010000000000000148}, {"pair": "LTC/BTC", "profit_percent": 0.0, "open_date": "2018-01-29 02:15:00+00:00", "close_date": "2018-01-29 03:00:00+00:00", "trade_duration": 45, "open_rate": 0.01622478, "close_rate": 0.016306107218045113, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 6.163411768911504, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 03:05:00+00:00", "close_date": "2018-01-29 03:45:00+00:00", "trade_duration": 40, "open_rate": 0.069, "close_rate": 0.06934586466165413, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4492753623188406, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 05:20:00+00:00", "close_date": "2018-01-29 06:55:00+00:00", "trade_duration": 95, "open_rate": 8.755e-05, "close_rate": 8.798884711779448e-05, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1142.204454597373, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 07:00:00+00:00", "close_date": "2018-01-29 19:25:00+00:00", "trade_duration": 745, "open_rate": 0.06825763, "close_rate": 0.06859977350877192, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4650376815016872, "profit_abs": 0.0}, {"pair": "DASH/BTC", "profit_percent": -0.0, "open_date": "2018-01-29 19:45:00+00:00", "close_date": "2018-01-29 20:25:00+00:00", "trade_duration": 40, "open_rate": 0.06713892, "close_rate": 0.06747545593984962, "open_at_end": false, "sell_reason": "roi", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1.4894490408841845, "profit_abs": -1.3877787807814457e-17}, {"pair": "TRX/BTC", "profit_percent": -0.0199116, "open_date": "2018-01-29 23:30:00+00:00", "close_date": "2018-01-30 04:45:00+00:00", "trade_duration": 315, "open_rate": 8.934e-05, "close_rate": 8.8e-05, "open_at_end": true, "sell_reason": "force_sell", "fee_open": 0.0025, "fee_close": 0.0025, "amount": 1119.3194537721067, "profit_abs": -0.0019961383478844796}], "results_per_pair": [{"key": "TRX/BTC", "trades": 15, "profit_mean": 0.0023467073333333323, "profit_mean_pct": 0.23467073333333321, "profit_sum": 0.035200609999999986, "profit_sum_pct": 3.5200609999999988, "profit_total_abs": 0.0035288616521155086, "profit_total_pct": 1.1733536666666662, "duration_avg": "2:28:00", "wins": 9, "draws": 2, "losses": 4}, {"key": "ADA/BTC", "trades": 29, "profit_mean": -0.0011598141379310352, "profit_mean_pct": -0.11598141379310352, "profit_sum": -0.03363461000000002, "profit_sum_pct": -3.3634610000000023, "profit_total_abs": -0.0033718682505400333, "profit_total_pct": -1.1211536666666675, "duration_avg": "5:35:00", "wins": 9, "draws": 11, "losses": 9}, {"key": "XLM/BTC", "trades": 21, "profit_mean": 0.0026243899999999994, "profit_mean_pct": 0.2624389999999999, "profit_sum": 0.05511218999999999, "profit_sum_pct": 5.511218999999999, "profit_total_abs": 0.005525000000000002, "profit_total_pct": 1.8370729999999995, "duration_avg": "3:21:00", "wins": 12, "draws": 3, "losses": 6}, {"key": "ETH/BTC", "trades": 21, "profit_mean": 0.0009500057142857142, "profit_mean_pct": 0.09500057142857142, "profit_sum": 0.01995012, "profit_sum_pct": 1.9950119999999998, "profit_total_abs": 0.0019999999999999463, "profit_total_pct": 0.6650039999999999, "duration_avg": "2:17:00", "wins": 5, "draws": 10, "losses": 6}, {"key": "XMR/BTC", "trades": 16, "profit_mean": -0.0027899012500000007, "profit_mean_pct": -0.2789901250000001, "profit_sum": -0.04463842000000001, "profit_sum_pct": -4.463842000000001, "profit_total_abs": -0.0044750000000000345, "profit_total_pct": -1.4879473333333337, "duration_avg": "8:41:00", "wins": 6, "draws": 5, "losses": 5}, {"key": "ZEC/BTC", "trades": 21, "profit_mean": -0.00039290904761904774, "profit_mean_pct": -0.03929090476190478, "profit_sum": -0.008251090000000003, "profit_sum_pct": -0.8251090000000003, "profit_total_abs": -0.000827170578309569, "profit_total_pct": -0.27503633333333344, "duration_avg": "4:17:00", "wins": 8, "draws": 7, "losses": 6}, {"key": "NXT/BTC", "trades": 12, "profit_mean": -0.0012261025000000006, "profit_mean_pct": -0.12261025000000006, "profit_sum": -0.014713230000000008, "profit_sum_pct": -1.4713230000000008, "profit_total_abs": -0.0014750000000000874, "profit_total_pct": -0.4904410000000003, "duration_avg": "0:57:00", "wins": 4, "draws": 3, "losses": 5}, {"key": "LTC/BTC", "trades": 8, "profit_mean": 0.00748129625, "profit_mean_pct": 0.748129625, "profit_sum": 0.05985037, "profit_sum_pct": 5.985037, "profit_total_abs": 0.006000000000000019, "profit_total_pct": 1.9950123333333334, "duration_avg": "1:59:00", "wins": 5, "draws": 2, "losses": 1}, {"key": "ETC/BTC", "trades": 20, "profit_mean": 0.0022568569999999997, "profit_mean_pct": 0.22568569999999996, "profit_sum": 0.04513713999999999, "profit_sum_pct": 4.513713999999999, "profit_total_abs": 0.004525000000000001, "profit_total_pct": 1.504571333333333, "duration_avg": "1:45:00", "wins": 11, "draws": 4, "losses": 5}, {"key": "DASH/BTC", "trades": 16, "profit_mean": 0.0018703237499999997, "profit_mean_pct": 0.18703237499999997, "profit_sum": 0.029925179999999996, "profit_sum_pct": 2.9925179999999996, "profit_total_abs": 0.002999999999999961, "profit_total_pct": 0.9975059999999999, "duration_avg": "3:03:00", "wins": 4, "draws": 7, "losses": 5}, {"key": "TOTAL", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}], "sell_reason_summary": [{"sell_reason": "roi", "trades": 170, "wins": 73, "draws": 54, "losses": 43, "profit_mean": 0.005398268352941177, "profit_mean_pct": 0.54, "profit_sum": 0.91770562, "profit_sum_pct": 91.77, "profit_total_abs": 0.09199999999999964, "profit_pct_total": 30.59}, {"sell_reason": "stop_loss", "trades": 6, "wins": 0, "draws": 0, "losses": 6, "profit_mean": -0.10448878000000002, "profit_mean_pct": -10.45, "profit_sum": -0.6269326800000001, "profit_sum_pct": -62.69, "profit_total_abs": -0.06284999999999992, "profit_pct_total": -20.9}, {"sell_reason": "force_sell", "trades": 3, "wins": 0, "draws": 0, "losses": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.89, "profit_sum": -0.14683468, "profit_sum_pct": -14.68, "profit_total_abs": -0.014720177176734003, "profit_pct_total": -4.89}], "left_open_trades": [{"key": "TRX/BTC", "trades": 1, "profit_mean": -0.0199116, "profit_mean_pct": -1.9911600000000003, "profit_sum": -0.0199116, "profit_sum_pct": -1.9911600000000003, "profit_total_abs": -0.0019961383478844796, "profit_total_pct": -0.6637200000000001, "duration_avg": "5:15:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ADA/BTC", "trades": 1, "profit_mean": -0.07877175, "profit_mean_pct": -7.877175, "profit_sum": -0.07877175, "profit_sum_pct": -7.877175, "profit_total_abs": -0.007896868250539965, "profit_total_pct": -2.625725, "duration_avg": "3 days, 4:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "ZEC/BTC", "trades": 1, "profit_mean": -0.04815133, "profit_mean_pct": -4.815133, "profit_sum": -0.04815133, "profit_sum_pct": -4.815133, "profit_total_abs": -0.004827170578309559, "profit_total_pct": -1.6050443333333335, "duration_avg": "2 days, 19:00:00", "wins": 0, "draws": 0, "losses": 1}, {"key": "TOTAL", "trades": 3, "profit_mean": -0.04894489333333333, "profit_mean_pct": -4.894489333333333, "profit_sum": -0.14683468, "profit_sum_pct": -14.683468, "profit_total_abs": -0.014720177176734003, "profit_total_pct": -4.8944893333333335, "duration_avg": "2 days, 1:25:00", "wins": 0, "draws": 0, "losses": 3}], "total_trades": 179, "backtest_start": "2018-01-30 04:45:00+00:00", "backtest_start_ts": 1517287500, "backtest_end": "2018-01-30 04:45:00+00:00", "backtest_end_ts": 1517287500, "backtest_days": 0, "trades_per_day": null, "market_change": 0.25, "stake_amount": 0.1, "max_drawdown": 0.21142322000000008, "drawdown_start": "2018-01-24 14:25:00+00:00", "drawdown_start_ts": 1516803900.0, "drawdown_end": "2018-01-30 04:45:00+00:00", "drawdown_end_ts": 1517287500.0, "pairlist": ["TRX/BTC", "ADA/BTC", "XLM/BTC", "ETH/BTC", "XMR/BTC", "ZEC/BTC","NXT/BTC", "LTC/BTC", "ETC/BTC", "DASH/BTC"]}}, "strategy_comparison": [{"key": "StrategyTestV2", "trades": 179, "profit_mean": 0.0008041243575418989, "profit_mean_pct": 0.0804124357541899, "profit_sum": 0.1439382599999999, "profit_sum_pct": 14.39382599999999, "profit_total_abs": 0.014429822823265714, "profit_total_pct": 4.797941999999996, "duration_avg": "3:40:00", "wins": 73, "draws": 54, "losses": 52}]} From 07d71f014f766a419f1264f1bbac3dec3980b7c4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 19:38:41 +0200 Subject: [PATCH 354/519] Rename defaultStrategy file --- tests/optimize/test_backtesting.py | 4 ++-- tests/optimize/test_hyperopt_tools.py | 4 ++-- tests/rpc/test_rpc_apiserver.py | 4 ++-- .../strats/{default_strategy.py => strategy_test_v2.py} | 0 tests/strategy/test_default_strategy.py | 6 +++--- tests/strategy/test_interface.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) rename tests/strategy/strats/{default_strategy.py => strategy_test_v2.py} (100%) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 6442f6cb7..0bbd110b6 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -694,7 +694,7 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir, def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir): - # Override the default buy trend function in our default_strategy + # Override the default buy trend function in our StrategyTestV2 def fun(dataframe=None, pair=None): buy_value = 1 sell_value = 1 @@ -710,7 +710,7 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir): def test_backtest_only_sell(mocker, default_conf, testdatadir): - # Override the default buy trend function in our default_strategy + # Override the default buy trend function in our StrategyTestV2 def fun(dataframe=None, pair=None): buy_value = 0 sell_value = 1 diff --git a/tests/optimize/test_hyperopt_tools.py b/tests/optimize/test_hyperopt_tools.py index 9f917c2a7..9c2b2e8fc 100644 --- a/tests/optimize/test_hyperopt_tools.py +++ b/tests/optimize/test_hyperopt_tools.py @@ -169,7 +169,7 @@ def test_get_strategy_filename(default_conf): x = HyperoptTools.get_strategy_filename(default_conf, 'StrategyTestV2') assert isinstance(x, Path) - assert x == Path(__file__).parents[1] / 'strategy/strats/default_strategy.py' + assert x == Path(__file__).parents[1] / 'strategy/strats/strategy_test_v2.py' x = HyperoptTools.get_strategy_filename(default_conf, 'NonExistingStrategy') assert x is None @@ -262,7 +262,7 @@ def test_try_export_params(default_conf, tmpdir, caplog, mocker): assert export_mock.call_count == 1 assert export_mock.call_args_list[0][0][1] == 'StrategyTestV2' - assert export_mock.call_args_list[0][0][2].name == 'default_strategy.json' + assert export_mock.call_args_list[0][0][2].name == 'strategy_test_v2.json' def test_params_print(capsys): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 0256fb632..2852486ed 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1217,8 +1217,8 @@ def test_api_strategies(botclient): assert_response(rc) assert rc.json() == {'strategies': [ - 'StrategyTestV2', 'HyperoptableStrategy', + 'StrategyTestV2', 'TestStrategyLegacyV1' ]} @@ -1231,7 +1231,7 @@ def test_api_strategy(botclient): assert_response(rc) assert rc.json()['strategy'] == 'StrategyTestV2' - data = (Path(__file__).parents[1] / "strategy/strats/default_strategy.py").read_text() + data = (Path(__file__).parents[1] / "strategy/strats/strategy_test_v2.py").read_text() assert rc.json()['code'] == data rc = client_get(client, f"{BASE_URI}/strategy/NoStrat") diff --git a/tests/strategy/strats/default_strategy.py b/tests/strategy/strats/strategy_test_v2.py similarity index 100% rename from tests/strategy/strats/default_strategy.py rename to tests/strategy/strats/strategy_test_v2.py diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index 2566c8478..6426ebe5f 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -4,10 +4,10 @@ from pandas import DataFrame from freqtrade.persistence.models import Trade -from .strats.default_strategy import StrategyTestV2 +from .strats.strategy_test_v2 import StrategyTestV2 -def test_default_strategy_structure(): +def test_strategy_test_v2_structure(): assert hasattr(StrategyTestV2, 'minimal_roi') assert hasattr(StrategyTestV2, 'stoploss') assert hasattr(StrategyTestV2, 'timeframe') @@ -16,7 +16,7 @@ def test_default_strategy_structure(): assert hasattr(StrategyTestV2, 'populate_sell_trend') -def test_default_strategy(result, fee): +def test_strategy_test_v2(result, fee): strategy = StrategyTestV2({}) metadata = {'pair': 'ETH/BTC'} diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 9023f7981..128599668 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -22,7 +22,7 @@ from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from tests.conftest import log_has, log_has_re -from .strats.default_strategy import StrategyTestV2 +from .strats.strategy_test_v2 import StrategyTestV2 # Avoid to reinit the same object again and again From 2ce458810b9764aa3c39845faade7ad9589708aa Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Aug 2021 19:39:57 +0200 Subject: [PATCH 355/519] rename default_hyperopt_loss file --- ...efault_hyperopt_loss.py => hyperopt_loss_short_trade_dur.py} | 0 tests/optimize/test_hyperoptloss.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename freqtrade/optimize/{default_hyperopt_loss.py => hyperopt_loss_short_trade_dur.py} (100%) diff --git a/freqtrade/optimize/default_hyperopt_loss.py b/freqtrade/optimize/hyperopt_loss_short_trade_dur.py similarity index 100% rename from freqtrade/optimize/default_hyperopt_loss.py rename to freqtrade/optimize/hyperopt_loss_short_trade_dur.py diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index ea0caac04..0082bcc34 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock import pytest from freqtrade.exceptions import OperationalException -from freqtrade.optimize.default_hyperopt_loss import ShortTradeDurHyperOptLoss +from freqtrade.optimize.hyperopt_loss_short_trade_dur import ShortTradeDurHyperOptLoss from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver From b3a4b0fbde824cbd48e8c2439f3e239de78c0333 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 27 Aug 2021 07:10:13 +0200 Subject: [PATCH 356/519] Version bump to 2021.8 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 34e1bfc5d..8b85ca56e 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.7' +__version__ = '2021.8' if __version__ == 'develop': From ef9c1addcfac2700d14012aa5734a66ec35da8e3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 27 Aug 2021 19:54:53 +0200 Subject: [PATCH 357/519] Add expired to list of canceled statuses --- freqtrade/constants.py | 2 ++ freqtrade/exchange/exchange.py | 5 +++-- freqtrade/freqtradebot.py | 6 +++--- freqtrade/persistence/models.py | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index cde276ac0..efcd1aaca 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -49,6 +49,8 @@ USERPATH_NOTEBOOKS = 'notebooks' TELEGRAM_SETTING_OPTIONS = ['on', 'off', 'silent'] ENV_VAR_PREFIX = 'FREQTRADE__' +NON_OPEN_EXCHANGE_STATES = ('cancelled', 'canceled', 'closed', 'expired') + # Define decimals per coin for outputs # Only used for outputs. diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 79ee94149..26f034e41 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -19,7 +19,8 @@ from ccxt.base.decimal_to_precision import (ROUND_DOWN, ROUND_UP, TICK_SIZE, TRU decimal_to_precision) from pandas import DataFrame -from freqtrade.constants import DEFAULT_AMOUNT_RESERVE_PERCENT, ListPairsWithTimeframes +from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, + ListPairsWithTimeframes) from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError, InvalidOrderException, OperationalException, PricingError, @@ -804,7 +805,7 @@ class Exchange: :param order: Order dict as returned from fetch_order() :return: True if order has been cancelled without being filled, False otherwise. """ - return (order.get('status') in ('closed', 'canceled', 'cancelled') + return (order.get('status') in NON_OPEN_EXCHANGE_STATES and order.get('filled') == 0.0) @retrier diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a7451d632..259270483 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -945,7 +945,7 @@ class FreqtradeBot(LoggingMixin): was_trade_fully_canceled = False # Cancelled orders may have the status of 'canceled' or 'closed' - if order['status'] not in ('cancelled', 'canceled', 'closed'): + if order['status'] not in constants.NON_OPEN_EXCHANGE_STATES: filled_val = order.get('filled', 0.0) or 0.0 filled_stake = filled_val * trade.open_rate minstake = self.exchange.get_min_pair_stake_amount( @@ -961,7 +961,7 @@ class FreqtradeBot(LoggingMixin): # Avoid race condition where the order could not be cancelled coz its already filled. # Simply bailing here is the only safe way - as this order will then be # handled in the next iteration. - if corder.get('status') not in ('cancelled', 'canceled', 'closed'): + if corder.get('status') not in constants.NON_OPEN_EXCHANGE_STATES: logger.warning(f"Order {trade.open_order_id} for {trade.pair} not cancelled.") return False else: @@ -1142,7 +1142,7 @@ class FreqtradeBot(LoggingMixin): trade.close_rate_requested = limit trade.sell_reason = sell_reason.sell_reason # In case of market sell orders the order can be closed immediately - if order.get('status', 'unknown') == 'closed': + if order.get('status', 'unknown') in ('closed', 'expired'): self.update_trade_state(trade, trade.open_order_id, order) Trade.commit() diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 5eaca7966..8c8c1e0a9 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -13,7 +13,7 @@ from sqlalchemy.orm import Query, declarative_base, relationship, scoped_session from sqlalchemy.pool import StaticPool from sqlalchemy.sql.schema import UniqueConstraint -from freqtrade.constants import DATETIME_PRINT_FORMAT +from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES from freqtrade.enums import SellType from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.misc import safe_value_fallback @@ -159,7 +159,7 @@ class Order(_DECL_BASE): self.order_date = datetime.fromtimestamp(order['timestamp'] / 1000, tz=timezone.utc) self.ft_is_open = True - if self.status in ('closed', 'canceled', 'cancelled'): + if self.status in NON_OPEN_EXCHANGE_STATES: self.ft_is_open = False if (order.get('filled', 0.0) or 0.0) > 0: self.order_filled_date = datetime.now(timezone.utc) From ac0dada962ea23cc2bd10337280f27da91405b65 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 28 Aug 2021 10:43:49 +0200 Subject: [PATCH 358/519] Update Version to develop again --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 8b85ca56e..e96e7f530 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2021.8' +__version__ = 'develop' if __version__ == 'develop': From 482e65453f84c7fa7c3aed9bac37e78664078edd Mon Sep 17 00:00:00 2001 From: Pan Long Date: Sat, 28 Aug 2021 21:57:54 +0800 Subject: [PATCH 359/519] Remove extra comma after the last element in binance pair_blacklist. --- freqtrade/templates/subtemplates/exchange_binance.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index 03aa0560c..38ba4fa5c 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -36,6 +36,6 @@ "BNB/TUSD", "BNB/USDC", "BNB/USDS", - "BNB/USDT", + "BNB/USDT" ] } From f79b30e886771e4bfefdd465ff129d79173b8d12 Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 28 Aug 2021 18:51:43 +0200 Subject: [PATCH 360/519] Docs: Minor fixes --- docs/advanced-hyperopt.md | 2 +- docs/hyperopt.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/advanced-hyperopt.md b/docs/advanced-hyperopt.md index 5e71df67c..8f233438b 100644 --- a/docs/advanced-hyperopt.md +++ b/docs/advanced-hyperopt.md @@ -335,7 +335,7 @@ Once the optimized parameters and conditions have been implemented into your str To achieve same results (number of trades, their durations, profit, etc.) than during Hyperopt, please use same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting. -Should results don't match, please double-check to make sure you transferred all conditions correctly. +Should results not match, please double-check to make sure you transferred all conditions correctly. Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy. You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`). diff --git a/docs/hyperopt.md b/docs/hyperopt.md index c5a52553b..7377a63c0 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -827,8 +827,8 @@ After you run Hyperopt for the desired amount of epochs, you can later list all Once the optimized strategy has been implemented into your strategy, you should backtest this strategy to make sure everything is working as expected. -To achieve same results (number of trades, their durations, profit, etc.) than during Hyperopt, please use same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting. +To achieve same the results (number of trades, their durations, profit, etc.) as during Hyperopt, please use the same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting. -Should results don't match, please double-check to make sure you transferred all conditions correctly. +Should results not match, please double-check to make sure you transferred all conditions correctly. Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy. You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`). From 89581ad25cd7b81d418dc15d835c5b48591a3aba Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Aug 2021 08:24:31 +0200 Subject: [PATCH 361/519] Fix typo in protections hyperopt doc closes #5499 --- docs/hyperopt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 7377a63c0..1eb90f1bc 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -456,7 +456,7 @@ class MyAwesomeStrategy(IStrategy): "only_per_pair": False }) - return protection + return prot def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # ... From 1895230afe642a12150857a254935632a2d9ee09 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Aug 2021 09:18:46 +0200 Subject: [PATCH 362/519] Clarify exception on load when markets could not be loaded closes #5498 --- freqtrade/exchange/exchange.py | 7 +++++++ tests/exchange/test_exchange.py | 11 +++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 26f034e41..ecf3302d8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -352,9 +352,16 @@ class Exchange: def validate_stakecurrency(self, stake_currency: str) -> None: """ Checks stake-currency against available currencies on the exchange. + Only runs on startup. If markets have not been loaded, there's been a problem with + the connection to the exchange. :param stake_currency: Stake-currency to validate :raise: OperationalException if stake-currency is not available. """ + if not self._markets: + raise OperationalException( + 'Could not load markets, therefore cannot start. ' + 'Please investigate the above error for more details.' + ) quote_currencies = self.get_quote_currencies() if stake_currency not in quote_currencies: raise OperationalException( diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index f6ac4c459..42da5dddc 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -557,7 +557,7 @@ def test_reload_markets_exception(default_conf, mocker, caplog): @pytest.mark.parametrize("stake_currency", ['ETH', 'BTC', 'USDT']) -def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog): +def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog): default_conf['stake_currency'] = stake_currency api_mock = MagicMock() type(api_mock).load_markets = MagicMock(return_value={ @@ -571,7 +571,7 @@ def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog): Exchange(default_conf) -def test_validate_stake_currency_error(default_conf, mocker, caplog): +def test_validate_stakecurrency_error(default_conf, mocker, caplog): default_conf['stake_currency'] = 'XRP' api_mock = MagicMock() type(api_mock).load_markets = MagicMock(return_value={ @@ -587,6 +587,13 @@ def test_validate_stake_currency_error(default_conf, mocker, caplog): 'Available currencies are: BTC, ETH, USDT'): Exchange(default_conf) + type(api_mock).load_markets = MagicMock(side_effect=ccxt.NetworkError('No connection.')) + mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) + + with pytest.raises(OperationalException, + match=r'Could not load markets, therefore cannot start\. Please.*'): + Exchange(default_conf) + def test_get_quote_currencies(default_conf, mocker): ex = get_patched_exchange(mocker, default_conf) From c14d8ea8277a4112bed80d072c9e3517c07f249c Mon Sep 17 00:00:00 2001 From: Pan Long Date: Sun, 29 Aug 2021 16:34:01 +0800 Subject: [PATCH 363/519] Export HDF5 and CBLOSC paths. This is needed if homebrew isn't installed in the standard path, say, /usr/local/. --- setup.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.sh b/setup.sh index feb0241f8..e5f81578d 100755 --- a/setup.sh +++ b/setup.sh @@ -119,6 +119,7 @@ function install_mac_newer_python_dependencies() { echo "-------------------------" brew install hdf5 fi + export HDF5_DIR=$(brew --prefix) if [ ! $(brew --prefix --installed c-blosc 2>/dev/null) ] then @@ -127,6 +128,7 @@ function install_mac_newer_python_dependencies() { echo "-------------------------" brew install c-blosc fi + export CBLOSC_DIR=$(brew --prefix) } # Install bot MacOS From 20878290a0a34de24328c74dbed31323e2455ff0 Mon Sep 17 00:00:00 2001 From: Pan Long Date: Mon, 30 Aug 2021 01:02:48 +0800 Subject: [PATCH 364/519] Surround "unlimited" by double quotes in build config. --- freqtrade/commands/build_config_commands.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 852cab92e..7e3534921 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -66,16 +66,22 @@ def ask_user_config() -> Dict[str, Any]: { "type": "text", "name": "stake_amount", - "message": "Please insert your stake amount:", + "message": f"Please insert your stake amount (Integer or '{UNLIMITED_STAKE_AMOUNT}'):", "default": "0.01", "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val), + "filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"' + if val == UNLIMITED_STAKE_AMOUNT + else val }, { "type": "text", "name": "max_open_trades", "message": f"Please insert max_open_trades (Integer or '{UNLIMITED_STAKE_AMOUNT}'):", "default": "3", - "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_int(val) + "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_int(val), + "filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"' + if val == UNLIMITED_STAKE_AMOUNT + else val }, { "type": "text", From c17595b314fb1e6dcfff38695c51e5c07f2b38fe Mon Sep 17 00:00:00 2001 From: LoveIsGrief Date: Sat, 28 Aug 2021 19:39:50 +0200 Subject: [PATCH 365/519] Docs: Mention Performance Warning for strategies Related to #5408 --- docs/strategy-customization.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index cfea60d22..194517b2b 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -781,6 +781,8 @@ Printing more than a few rows is also possible (simply use `print(dataframe)` i ## Common mistakes when developing strategies +### Peeking into the future while backtesting + Backtesting analyzes the whole time-range at once for performance reasons. Because of this, strategy authors need to make sure that strategies do not look-ahead into the future. This is a common pain-point, which can cause huge differences between backtesting and dry/live run methods, since they all use data which is not available during dry/live runs, so these strategies will perform well during backtesting, but will fail / perform badly in real conditions. @@ -791,6 +793,33 @@ The following lists some common patterns which should be avoided to prevent frus - don't use `dataframe['volume'].mean()`. This uses the full DataFrame for backtesting, including data from the future. Use `dataframe['volume'].rolling().mean()` instead - don't use `.resample('1h')`. This uses the left border of the interval, so moves data from an hour to the start of the hour. Use `.resample('1h', label='right')` instead. +### Performance warning + +When executing a strategy, one can sometimes be greeted by the following in the logs + +> PerformanceWarning: DataFrame is highly fragmented. + +This is a warning from [`pandas`](https://github.com/pandas-dev/pandas) and as the warning continues to say: + use `pd.concat(axis=1)`. For example + +```python +for i in range(100): + dataframe[i] = ta.indicator(dataframe, param=i) +``` + +should be rewritten to + +```python +frames = [dataframe] +for i in range(100): + frames.append({ + str(i): ta.indicator(dataframe, param=i) + }) + +# Append columns to existing dataframe +merged_frame = pd.concat(frames, axis=1) +``` + ## Further strategy ideas To get additional Ideas for strategies, head over to our [strategy repository](https://github.com/freqtrade/freqtrade-strategies). Feel free to use them as they are - but results will depend on the current market situation, pairs used etc. - therefore please backtest the strategy for your exchange/desired pairs first, evaluate carefully, use at your own risk. From c64ebeb6e28e274ced0d8fef9f2ad1bce3b7c56c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 03:01:05 +0000 Subject: [PATCH 366/519] Bump cryptography from 3.4.7 to 3.4.8 Bumps [cryptography](https://github.com/pyca/cryptography) from 3.4.7 to 3.4.8. - [Release notes](https://github.com/pyca/cryptography/releases) - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/3.4.7...3.4.8) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1bccbd725..8305a83f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ pandas==1.3.2 ccxt==1.55.28 # Pin cryptography for now due to rust build errors with piwheels -cryptography==3.4.7 +cryptography==3.4.8 aiohttp==3.7.4.post0 SQLAlchemy==1.4.23 python-telegram-bot==13.7 From d0504c47ef5a67242947bd26e8ad80bc4dab6dc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 03:01:09 +0000 Subject: [PATCH 367/519] Bump plotly from 5.2.1 to 5.3.0 Bumps [plotly](https://github.com/plotly/plotly.py) from 5.2.1 to 5.3.0. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.2.1...v5.3.0) --- updated-dependencies: - dependency-name: plotly dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index d835ed5d9..62836a729 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.2.1 +plotly==5.3.0 From 26451e8c0119f92c9f2db1e068ebfb5d64d83d51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 03:01:14 +0000 Subject: [PATCH 368/519] Bump ccxt from 1.55.28 to 1.55.56 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.55.28 to 1.55.56. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.55.28...1.55.56) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1bccbd725..f6c1a984d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.2 pandas==1.3.2 -ccxt==1.55.28 +ccxt==1.55.56 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.7 aiohttp==3.7.4.post0 From 9e0ab9c2ca03a0cced009c1c13ecbe922c1b15af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 03:01:19 +0000 Subject: [PATCH 369/519] Bump fastapi from 0.68.0 to 0.68.1 Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.68.0 to 0.68.1. - [Release notes](https://github.com/tiangolo/fastapi/releases) - [Commits](https://github.com/tiangolo/fastapi/compare/0.68.0...0.68.1) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1bccbd725..0d89322c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ python-rapidjson==1.4 sdnotify==0.3.2 # API Server -fastapi==0.68.0 +fastapi==0.68.1 uvicorn==0.15.0 pyjwt==2.1.0 aiofiles==0.7.0 From 45c6f906919a11029585c3810719c11e84c4cace Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 03:01:24 +0000 Subject: [PATCH 370/519] Bump mkdocs-material from 7.2.4 to 7.2.5 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.2.4 to 7.2.5. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.2.4...7.2.5) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 8fa7341c9..d820c9412 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.2.4 +mkdocs-material==7.2.5 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From 2a0c95a2e7b2a75929cd1b017ce69d44805bab6c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Aug 2021 20:00:52 +0200 Subject: [PATCH 371/519] Update freqtrade/commands/build_config_commands.py --- freqtrade/commands/build_config_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 7e3534921..1fe90e83a 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -66,7 +66,7 @@ def ask_user_config() -> Dict[str, Any]: { "type": "text", "name": "stake_amount", - "message": f"Please insert your stake amount (Integer or '{UNLIMITED_STAKE_AMOUNT}'):", + "message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):", "default": "0.01", "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val), "filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"' From 4cc1f2b4a4f5ce0f94fed3bef8374cf9eb4501ba Mon Sep 17 00:00:00 2001 From: Yehya <69044426+yehya-dev@users.noreply.github.com> Date: Tue, 31 Aug 2021 10:48:09 +0530 Subject: [PATCH 372/519] Update bot-basics.md Term usage mistake (The first in the pair is the base and second is the quote) --- docs/bot-basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index e7ff27040..80443a0bf 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -7,7 +7,7 @@ This page provides you some basic concepts on how Freqtrade works and operates. * **Strategy**: Your trading strategy, telling the bot what to do. * **Trade**: Open position. * **Open Order**: Order which is currently placed on the exchange, and is not yet complete. -* **Pair**: Tradable pair, usually in the format of Quote/Base (e.g. XRP/USDT). +* **Pair**: Tradable pair, usually in the format of Base/Quote (e.g. XRP/USDT). * **Timeframe**: Candle length to use (e.g. `"5m"`, `"1h"`, ...). * **Indicators**: Technical indicators (SMA, EMA, RSI, ...). * **Limit order**: Limit orders which execute at the defined limit price or better. From da5f8c87aee1bef44ca905908ebd951d067e43c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 31 Aug 2021 07:18:32 +0200 Subject: [PATCH 373/519] Add stake_currency to strategy interface allows type-completion in editors --- freqtrade/strategy/interface.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index c51860011..91963f223 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -120,6 +120,8 @@ class IStrategy(ABC, HyperStrategyMixin): # and wallets - access to the current balance. dp: Optional[DataProvider] = None wallets: Optional[Wallets] = None + # Filled from configuration + stake_currency: str # container variable for strategy source code __source__: str = '' From 1cbe303434bed66ddf20496caa4cf3014a46d0b0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 31 Aug 2021 19:58:08 +0200 Subject: [PATCH 374/519] Add documentation for --detail-timeframe --- docs/backtesting.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 89980c670..3e3bfc9fe 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -18,6 +18,7 @@ usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-p PAIRS [PAIRS ...]] [--eps] [--dmmp] [--enable-protections] [--dry-run-wallet DRY_RUN_WALLET] + [--timeframe-detail TIMEFRAME_DETAIL] [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] [--export {none,trades}] [--export-filename PATH] @@ -55,6 +56,9 @@ optional arguments: --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET Starting balance, used for backtesting / hyperopt and dry-runs. + --timeframe-detail TIMEFRAME_DETAIL + Specify detail timeframe for backtesting (`1m`, `5m`, + `30m`, `1h`, `1d`). --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] Provide a space-separated list of strategies to backtest. Please note that ticker-interval needs to be @@ -425,7 +429,12 @@ It contains some useful key metrics about performance of your strategy on backte - `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command). - `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column. -### Assumptions made by backtesting +### Further backtest-result analysis + +To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file). +You can then load the trades to perform further analysis as shown in our [data analysis](data-analysis.md#backtesting) backtesting section. + +## Assumptions made by backtesting Since backtesting lacks some detailed information about what happens within a candle, it needs to take a few assumptions: @@ -456,10 +465,30 @@ Also, keep in mind that past results don't guarantee future success. In addition to the above assumptions, strategy authors should carefully read the [Common Mistakes](strategy-customization.md#common-mistakes-when-developing-strategies) section, to avoid using data in backtesting which is not available in real market conditions. -### Further backtest-result analysis +### Improved backtest accuracy -To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file). -You can then load the trades to perform further analysis as shown in our [data analysis](data-analysis.md#backtesting) backtesting section. +One big limitation of backtesting is it's inability to know how prices moved intra-candle (was high before close, or viceversa?). +So assuming you run backtesting with a 1h timeframe, there will be 4 prices for that candle (Open, High, Low, Close). + +While backtesting does take some assumptions (read above) about this - this can never be perfect, and will always be biased in one way or the other. +To mitigate this, freqtrade can use a lower (faster) timeframe to simulate intra-candle movements. + +To utilize this, you can append `--timeframe-detail 5m` to your regular backtesting command. + +``` bash +freqtrade backtesting --strategy AwesomeStrategy --timeframe 1h --timeframe-detail 5m +``` + +This will load 1h data as well as 5m data for the timeframe. The strategy will be analyzed with the 1h timeframe - and for every "open trade candle" (candles where a trade is open) the 5m data will be used to simulate intra-candle movements. +All callback functions (`custom_sell()`, `custom_stoploss()`, ... ) will be running for each 5m candle once the trade is opened (so 12 times in the above example of 1h timeframe, and 5m detailed timeframe). + +`--timeframe-detail` must be smaller than the original timeframe, otherwise backtesting will fail to start. + +Obviously this will require more memory (5m data is bigger than 1h data), and will also impact runtime (depending on the amount of trades and trade durations). +Also, data must be available / downloaded already. + +!!! Tip + You can use this function as the last part of strategy development, to ensure your strategy is not exploiting one of the [backtesting assumptions](#assumptions-made-by-backtesting). Strategies that perform similarly well with this mode have a good chance to perform well in dry/live modes too (although only forward-testing (dry-mode) can really confirm a strategy). ## Backtesting multiple strategies From 87fa49d52981d214d2e9854f0c9e097dd5ca6920 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 03:01:49 +0000 Subject: [PATCH 375/519] Bump python from 3.9.6-slim-buster to 3.9.7-slim-buster Bumps python from 3.9.6-slim-buster to 3.9.7-slim-buster. --- updated-dependencies: - dependency-name: python dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 804b1086b..4c4722452 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9.6-slim-buster as base +FROM python:3.9.7-slim-buster as base # Setup env ENV LANG C.UTF-8 From 93c1dff71bbae773b922794f956013e0d0b9c908 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Sep 2021 20:43:07 +0200 Subject: [PATCH 376/519] Allow adding new additional headers --- freqtrade/exchange/exchange.py | 9 ++++++++- tests/exchange/test_exchange.py | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index ecf3302d8..3dc295563 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -54,6 +54,9 @@ class Exchange: # Parameters to add directly to buy/sell calls (like agreeing to trading agreement) _params: Dict = {} + # Additional headers - added to the ccxt object + _headers: Dict = {} + # Dict to specify which options each exchange implements # This defines defaults, which can be selectively overridden by subclasses using _ft_has # or by specifying them in the configuration. @@ -169,7 +172,7 @@ class Exchange: asyncio.get_event_loop().run_until_complete(self._api_async.close()) def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt, - ccxt_kwargs: dict = None) -> ccxt.Exchange: + ccxt_kwargs: Dict = {}) -> ccxt.Exchange: """ Initialize ccxt with given config and return valid ccxt instance. @@ -188,6 +191,10 @@ class Exchange: } if ccxt_kwargs: logger.info('Applying additional ccxt config: %s', ccxt_kwargs) + if self._headers: + # Inject static headers after the above output to not confuse users. + ccxt_kwargs = deep_merge_dicts({'headers': self._headers}, ccxt_kwargs) + if ccxt_kwargs: ex_config.update(ccxt_kwargs) try: diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 42da5dddc..144063c07 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -108,6 +108,13 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog): assert hasattr(ex._api_async, 'TestKWARG') assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog) assert log_has(asynclogmsg, caplog) + # Test additional headers case + Exchange._headers = {'hello': 'world'} + ex = Exchange(conf) + + assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog) + assert ex._api.headers == {'hello': 'world'} + Exchange._headers = {} def test_destroy(default_conf, mocker, caplog): From 19ad1654836992365af7536189095662205d768a Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 08:48:53 +0200 Subject: [PATCH 377/519] Add time_in_force for kucoin --- docs/configuration.md | 4 ++-- docs/exchanges.md | 5 +++++ freqtrade/exchange/binance.py | 1 + freqtrade/exchange/exchange.py | 4 +++- freqtrade/exchange/kucoin.py | 2 ++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 09198e019..6ccea4c73 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -444,8 +444,8 @@ The possible values are: `gtc` (default), `fok` or `ioc`. ``` !!! Warning - This is ongoing work. For now, it is supported only for binance. - Please don't change the default value unless you know what you are doing and have researched the impact of using different values. + This is ongoing work. For now, it is supported only for binance and kucoin. + Please don't change the default value unless you know what you are doing and have researched the impact of using different values for your particular exchange. ### Exchange configuration diff --git a/docs/exchanges.md b/docs/exchanges.md index 5f54a524e..81ad670a3 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -4,6 +4,9 @@ This page combines common gotchas and informations which are exchange-specific a ## Binance +Binance supports [time_in_force](configuration.md#understand-order_time_in_force). + + !!! Tip "Stoploss on Exchange" Binance supports `stoploss_on_exchange` and uses stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it. @@ -115,6 +118,8 @@ Kucoin requires a passphrase for each api key, you will therefore need to add th "password": "your_exchange_api_key_password", ``` +Kucoin supports [time_in_force](configuration.md#understand-order_time_in_force). + ### Kucoin Blacklists For Kucoin, please add `"KCS/"` to your blacklist to avoid issues. diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 0c470cb24..189f5f481 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -18,6 +18,7 @@ class Binance(Exchange): _ft_has: Dict = { "stoploss_on_exchange": True, "order_time_in_force": ['gtc', 'fok', 'ioc'], + "time_in_force_parameter": "timeInForce", "ohlcv_candle_limit": 1000, "trades_pagination": "id", "trades_pagination_arg": "fromId", diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 3dc295563..80f20b17e 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -63,6 +63,7 @@ class Exchange: _ft_has_default: Dict = { "stoploss_on_exchange": False, "order_time_in_force": ["gtc"], + "time_in_force_parameter": "timeInForce", "ohlcv_params": {}, "ohlcv_candle_limit": 500, "ohlcv_partial_candle": True, @@ -723,7 +724,8 @@ class Exchange: params = self._params.copy() if time_in_force != 'gtc' and ordertype != 'market': - params.update({'timeInForce': time_in_force}) + param = self._ft_has.get('time_in_force_parameter', '') + params.update({param: time_in_force}) try: # Set the precision for amount and price(rate) as accepted by the exchange diff --git a/freqtrade/exchange/kucoin.py b/freqtrade/exchange/kucoin.py index 22886a1d8..5d818f6a2 100644 --- a/freqtrade/exchange/kucoin.py +++ b/freqtrade/exchange/kucoin.py @@ -21,4 +21,6 @@ class Kucoin(Exchange): _ft_has: Dict = { "l2_limit_range": [20, 100], "l2_limit_range_required": False, + "order_time_in_force": ['gtc', 'fok', 'ioc'], + "time_in_force_parameter": "timeInForce", } From e64ccd8fc111e6d5eeeb193509317f0be00f1d80 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 08:54:15 +0200 Subject: [PATCH 378/519] Add new_config section for kucoin --- freqtrade/commands/build_config_commands.py | 8 ++++++++ .../templates/subtemplates/exchange_kucoin.j2 | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 freqtrade/templates/subtemplates/exchange_kucoin.j2 diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 1fe90e83a..780ad4161 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -105,6 +105,8 @@ def ask_user_config() -> Dict[str, Any]: "bittrex", "kraken", "ftx", + "kucoin", + "gateio", Separator(), "other", ], @@ -128,6 +130,12 @@ def ask_user_config() -> Dict[str, Any]: "message": "Insert Exchange Secret", "when": lambda x: not x['dry_run'] }, + { + "type": "password", + "name": "exchange_key_password", + "message": "Insert Exchange API Key password", + "when": lambda x: not x['dry_run'] and x['exchange_name'] == 'kucoin' + }, { "type": "confirm", "name": "telegram", diff --git a/freqtrade/templates/subtemplates/exchange_kucoin.j2 b/freqtrade/templates/subtemplates/exchange_kucoin.j2 new file mode 100644 index 000000000..f9dfff663 --- /dev/null +++ b/freqtrade/templates/subtemplates/exchange_kucoin.j2 @@ -0,0 +1,18 @@ +"exchange": { + "name": "{{ exchange_name | lower }}", + "key": "{{ exchange_key }}", + "secret": "{{ exchange_secret }}", + "password": "{{ exchange_key_password }}", + "ccxt_config": { + "enableRateLimit": true + "rateLimit": 200 + }, + "ccxt_async_config": { + "enableRateLimit": true, + "rateLimit": 200 + }, + "pair_whitelist": [ + ], + "pair_blacklist": [ + ] +} From 68f13173bcaf4d88c2835abc66f9d0b92bf7ce7e Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 08:59:08 +0200 Subject: [PATCH 379/519] Update new-config templates to use USDT by default --- freqtrade/commands/build_config_commands.py | 4 ++-- .../subtemplates/exchange_binance.j2 | 15 ------------- .../subtemplates/exchange_bittrex.j2 | 10 --------- .../templates/subtemplates/exchange_kraken.j2 | 22 ++----------------- 4 files changed, 4 insertions(+), 47 deletions(-) diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 780ad4161..faa8a98f4 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -61,13 +61,13 @@ def ask_user_config() -> Dict[str, Any]: "type": "text", "name": "stake_currency", "message": "Please insert your stake currency:", - "default": 'BTC', + "default": 'USDT', }, { "type": "text", "name": "stake_amount", "message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):", - "default": "0.01", + "default": "100", "validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val), "filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"' if val == UNLIMITED_STAKE_AMOUNT diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index 38ba4fa5c..217002a7c 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -8,21 +8,6 @@ "rateLimit": 200 }, "pair_whitelist": [ - "ALGO/BTC", - "ATOM/BTC", - "BAT/BTC", - "BCH/BTC", - "BRD/BTC", - "EOS/BTC", - "ETH/BTC", - "IOTA/BTC", - "LINK/BTC", - "LTC/BTC", - "NEO/BTC", - "NXS/BTC", - "XMR/BTC", - "XRP/BTC", - "XTZ/BTC" ], "pair_blacklist": [ "BNB/BTC", diff --git a/freqtrade/templates/subtemplates/exchange_bittrex.j2 b/freqtrade/templates/subtemplates/exchange_bittrex.j2 index 7b27318ca..0394790ce 100644 --- a/freqtrade/templates/subtemplates/exchange_bittrex.j2 +++ b/freqtrade/templates/subtemplates/exchange_bittrex.j2 @@ -15,16 +15,6 @@ "rateLimit": 500 }, "pair_whitelist": [ - "ETH/BTC", - "LTC/BTC", - "ETC/BTC", - "DASH/BTC", - "ZEC/BTC", - "XLM/BTC", - "XRP/BTC", - "TRX/BTC", - "ADA/BTC", - "XMR/BTC" ], "pair_blacklist": [ ] diff --git a/freqtrade/templates/subtemplates/exchange_kraken.j2 b/freqtrade/templates/subtemplates/exchange_kraken.j2 index 7139a0830..4d0e4c1ff 100644 --- a/freqtrade/templates/subtemplates/exchange_kraken.j2 +++ b/freqtrade/templates/subtemplates/exchange_kraken.j2 @@ -7,28 +7,10 @@ "ccxt_async_config": { "enableRateLimit": true, "rateLimit": 1000 + // Enable the below for downoading data. + //"rateLimit": 3100 }, "pair_whitelist": [ - "ADA/EUR", - "ATOM/EUR", - "BAT/EUR", - "BCH/EUR", - "BTC/EUR", - "DAI/EUR", - "DASH/EUR", - "EOS/EUR", - "ETC/EUR", - "ETH/EUR", - "LINK/EUR", - "LTC/EUR", - "QTUM/EUR", - "REP/EUR", - "WAVES/EUR", - "XLM/EUR", - "XMR/EUR", - "XRP/EUR", - "XTZ/EUR", - "ZEC/EUR" ], "pair_blacklist": [ From c489e6825c3cc0affeb46e2a2f1f78d119e933f5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 09:18:15 +0200 Subject: [PATCH 380/519] Simplify binance blacklist --- .../templates/subtemplates/exchange_binance.j2 | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index 217002a7c..3022464c7 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -10,17 +10,6 @@ "pair_whitelist": [ ], "pair_blacklist": [ - "BNB/BTC", - "BNB/BUSD", - "BNB/ETH", - "BNB/EUR", - "BNB/NGN", - "BNB/PAX", - "BNB/RUB", - "BNB/TRY", - "BNB/TUSD", - "BNB/USDC", - "BNB/USDS", - "BNB/USDT" + "BNB/.*", ] } From b4130dfabbdd0a68119080f33c990b34d4116d77 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 09:23:26 +0200 Subject: [PATCH 381/519] Use volumePairlist instead of staticPairlist in generated config --- freqtrade/templates/base_config.json.j2 | 9 ++++++++- freqtrade/templates/subtemplates/exchange_binance.j2 | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/freqtrade/templates/base_config.json.j2 b/freqtrade/templates/base_config.json.j2 index a5782f7cd..68eebdbd4 100644 --- a/freqtrade/templates/base_config.json.j2 +++ b/freqtrade/templates/base_config.json.j2 @@ -1,3 +1,10 @@ +{%set volume_pairlist = '{ + "method": "VolumePairList", + "number_assets": 20, + "sort_key": "quoteVolume", + "min_value": 0, + "refresh_period": 1800 + }' %} { "max_open_trades": {{ max_open_trades }}, "stake_currency": "{{ stake_currency }}", @@ -29,7 +36,7 @@ }, {{ exchange | indent(4) }}, "pairlists": [ - {"method": "StaticPairList"} + {{ '{"method": "StaticPairList"}' if exchange_name == 'bittrex' else volume_pairlist }} ], "edge": { "enabled": false, diff --git a/freqtrade/templates/subtemplates/exchange_binance.j2 b/freqtrade/templates/subtemplates/exchange_binance.j2 index 3022464c7..de58b6f72 100644 --- a/freqtrade/templates/subtemplates/exchange_binance.j2 +++ b/freqtrade/templates/subtemplates/exchange_binance.j2 @@ -10,6 +10,6 @@ "pair_whitelist": [ ], "pair_blacklist": [ - "BNB/.*", + "BNB/.*" ] } From 2f92838c39639e4770b95258ca0357d4e69c7d40 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 16:18:47 +0200 Subject: [PATCH 382/519] Properly close parenteses in exchange doc --- docs/exchanges.md | 5 ++++- mkdocs.yml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index 81ad670a3..42a850acd 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -6,7 +6,6 @@ This page combines common gotchas and informations which are exchange-specific a Binance supports [time_in_force](configuration.md#understand-order_time_in_force). - !!! Tip "Stoploss on Exchange" Binance supports `stoploss_on_exchange` and uses stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it. @@ -116,6 +115,8 @@ Kucoin requires a passphrase for each api key, you will therefore need to add th "key": "your_exchange_key", "secret": "your_exchange_secret", "password": "your_exchange_api_key_password", + // ... +} ``` Kucoin supports [time_in_force](configuration.md#understand-order_time_in_force). @@ -163,6 +164,8 @@ For example, to test the order type `FOK` with Kraken, and modify candle limit t "order_time_in_force": ["gtc", "fok"], "ohlcv_candle_limit": 200 } + //... +} ``` !!! Warning diff --git a/mkdocs.yml b/mkdocs.yml index 854939ca0..05156168f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -23,10 +23,10 @@ nav: - Hyperopt: hyperopt.md - Utility Sub-commands: utils.md - Plotting: plotting.md + - Exchange-specific Notes: exchanges.md - Data Analysis: - Jupyter Notebooks: data-analysis.md - Strategy analysis: strategy_analysis_example.md - - Exchange-specific Notes: exchanges.md - Advanced Topics: - Advanced Post-installation Tasks: advanced-setup.md - Edge Positioning: edge.md From 103a8e827ea7e280b93d806d355ca1157ed55692 Mon Sep 17 00:00:00 2001 From: Rikj000 Date: Fri, 3 Sep 2021 16:37:36 +0200 Subject: [PATCH 383/519] =?UTF-8?q?=E2=9A=A1=20`setup.sh`=20-=20Use=20`bui?= =?UTF-8?q?ld=5Fhelpers/install=5Fta-lib.sh`=20for=20TA-Lib=20installation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build_helpers/install_ta-lib.sh | 2 +- setup.sh | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index dd87cf105..590b7d961 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -13,7 +13,7 @@ if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then && ./configure --prefix=${INSTALL_LOC}/ \ && make -j$(nproc) \ && which sudo && sudo make install || make install \ - && cd .. + && cd .. && rm -rf ./ta-lib/ else echo "TA-lib already installed, skipping installation" fi diff --git a/setup.sh b/setup.sh index e5f81578d..217500569 100755 --- a/setup.sh +++ b/setup.sh @@ -95,19 +95,7 @@ function install_talib() { return fi - cd build_helpers - tar zxvf ta-lib-0.4.0-src.tar.gz - cd ta-lib - sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h - ./configure --prefix=/usr/local - make - sudo make install - if [ -x "$(command -v apt-get)" ]; then - echo "Updating library path using ldconfig" - sudo ldconfig - fi - cd .. && rm -rf ./ta-lib/ - cd .. + cd build_helpers && ./install_ta-lib.sh && cd .. } function install_mac_newer_python_dependencies() { From 493fb3507316296f90f6c6384a1e84d366b82efa Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Sep 2021 22:05:40 +0200 Subject: [PATCH 384/519] Fix uvicorn not working properly on windows --- freqtrade/rpc/api_server/uvicorn_threaded.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/uvicorn_threaded.py b/freqtrade/rpc/api_server/uvicorn_threaded.py index b63999f51..79af659c7 100644 --- a/freqtrade/rpc/api_server/uvicorn_threaded.py +++ b/freqtrade/rpc/api_server/uvicorn_threaded.py @@ -5,6 +5,20 @@ import time import uvicorn +def asyncio_setup() -> None: # pragma: no cover + # Set eventloop for win32 setups + # Reverts a change done in uvicorn 0.15.0 - which now sets the eventloop + # via policy. + import sys + + if sys.version_info >= (3, 8) and sys.platform == "win32": + import asyncio + import selectors + selector = selectors.SelectSelector() + loop = asyncio.SelectorEventLoop(selector) + asyncio.set_event_loop(loop) + + class UvicornServer(uvicorn.Server): """ Multithreaded server - as found in https://github.com/encode/uvicorn/issues/742 @@ -28,7 +42,7 @@ class UvicornServer(uvicorn.Server): try: import uvloop # noqa except ImportError: # pragma: no cover - from uvicorn.loops.asyncio import asyncio_setup + asyncio_setup() else: asyncio.set_event_loop(uvloop.new_event_loop()) From eb0362c29ef7f125db4fda5590485c4c9f002f7b Mon Sep 17 00:00:00 2001 From: Rik Helsen Date: Fri, 3 Sep 2021 23:52:40 +0200 Subject: [PATCH 385/519] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20=EF=B8=8F`install?= =?UTF-8?q?=5Fta-lib.sh`=20-=20Run=20`ldconfig`=20after=20`make=20install`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build_helpers/install_ta-lib.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index 590b7d961..d12b16364 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -12,9 +12,12 @@ if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then && curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' -o config.sub \ && ./configure --prefix=${INSTALL_LOC}/ \ && make -j$(nproc) \ - && which sudo && sudo make install || make install \ - && cd .. && rm -rf ./ta-lib/ + && which sudo && sudo make install || make install + if [ -x "$(command -v apt-get)" ]; then + echo "Updating library path using ldconfig" + sudo ldconfig + fi + cd .. && rm -rf ./ta-lib/ else echo "TA-lib already installed, skipping installation" fi -# && sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \ From 4e049f65f227c7e2bf103db5cd9f97988c7a2250 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 09:14:44 +0200 Subject: [PATCH 386/519] Exclude some parts from coverage that can't really be tested --- freqtrade/__init__.py | 2 +- freqtrade/loggers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index e96e7f530..2747efc96 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -22,7 +22,7 @@ if __version__ == 'develop': # subprocess.check_output( # ['git', 'log', '--format="%h"', '-n 1'], # stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"') - except Exception: + except Exception: # pragma: no cover # git not available, ignore try: # Try Fallback to freqtrade_commit file (created by CI while building docker image) diff --git a/freqtrade/loggers.py b/freqtrade/loggers.py index fbb05d879..5c5831695 100644 --- a/freqtrade/loggers.py +++ b/freqtrade/loggers.py @@ -87,7 +87,7 @@ def setup_logging(config: Dict[str, Any]) -> None: # syslog config. The messages should be equal for this. handler_sl.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s')) logging.root.addHandler(handler_sl) - elif s[0] == 'journald': + elif s[0] == 'journald': # pragma: no cover try: from systemd.journal import JournaldLogHandler except ImportError: From 2173ff0133db834cbda72a2d35fa6a07e5d574b9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 09:16:19 +0200 Subject: [PATCH 387/519] Update PR template to not link to issues in changelog --- .github/PULL_REQUEST_TEMPLATE.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 20ef27f0f..7c0655b20 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,14 +2,16 @@ Thank you for sending your pull request. But first, have you included unit tests, and is your code PEP8 conformant? [More details](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) ## Summary + Explain in one sentence the goal of this PR Solve the issue: #___ ## Quick changelog -- -- +- +- ## What's new? + *Explain in details what this PR solve or improve. You can include visuals.* From a8f28ffb11a48ce1a8bbb26b209bfd9fa34705d0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 15:34:57 +0200 Subject: [PATCH 388/519] Increase test coverage --- tests/strategy/test_interface.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 128599668..250dcf63d 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -735,11 +735,16 @@ def test_auto_hyperopt_interface(default_conf): PairLocks.timeframe = default_conf['timeframe'] strategy = StrategyResolver.load_strategy(default_conf) + with pytest.raises(OperationalException): + next(strategy.enumerate_parameters('deadBeef')) + assert strategy.buy_rsi.value == strategy.buy_params['buy_rsi'] # PlusDI is NOT in the buy-params, so default should be used assert strategy.buy_plusdi.value == 0.5 assert strategy.sell_rsi.value == strategy.sell_params['sell_rsi'] + assert repr(strategy.sell_rsi) == 'IntParameter(74)' + # Parameter is disabled - so value from sell_param dict will NOT be used. assert strategy.sell_minusdi.value == 0.5 all_params = strategy.detect_all_parameters() From c519ecf8df2fae0e8d789c5abacb6662620d73e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 5 Sep 2021 15:40:21 +0200 Subject: [PATCH 389/519] Exclude more untestable sections from coverage --- freqtrade/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index 2fd3d32bb..6593fbcb6 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -9,7 +9,7 @@ from typing import Any, List # check min. python version -if sys.version_info < (3, 7): +if sys.version_info < (3, 7): # pragma: no cover sys.exit("Freqtrade requires Python version >= 3.7") from freqtrade.commands import Arguments @@ -46,7 +46,7 @@ def main(sysargv: List[str] = None) -> None: "`freqtrade --help` or `freqtrade --help`." ) - except SystemExit as e: + except SystemExit as e: # pragma: no cover return_code = e except KeyboardInterrupt: logger.info('SIGINT received, aborting ...') @@ -60,5 +60,5 @@ def main(sysargv: List[str] = None) -> None: sys.exit(return_code) -if __name__ == '__main__': +if __name__ == '__main__': # pragma: no cover main() From 1d24d3d5ee68624f2706e2a45ea939c87fa0b3ab Mon Sep 17 00:00:00 2001 From: lenik terenin Date: Sun, 5 Sep 2021 22:41:58 +0900 Subject: [PATCH 390/519] case insensitive blacklist Allow "btc/usdt" pairs in blacklist to match to "BTC/USDT" pairs that come from the exchange. --- freqtrade/plugins/pairlist/pairlist_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/pairlist_helpers.py b/freqtrade/plugins/pairlist/pairlist_helpers.py index 924bfb293..1de27fcbd 100644 --- a/freqtrade/plugins/pairlist/pairlist_helpers.py +++ b/freqtrade/plugins/pairlist/pairlist_helpers.py @@ -17,7 +17,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str], if keep_invalid: for pair_wc in wildcardpl: try: - comp = re.compile(pair_wc) + comp = re.compile(pair_wc, re.IGNORECASE) result_partial = [ pair for pair in available_pairs if re.fullmatch(comp, pair) ] @@ -33,7 +33,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str], else: for pair_wc in wildcardpl: try: - comp = re.compile(pair_wc) + comp = re.compile(pair_wc, re.IGNORECASE) result += [ pair for pair in available_pairs if re.fullmatch(comp, pair) ] From 4daa4b9e63960da073e8e2d5f7e00906d4117cf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:10 +0000 Subject: [PATCH 391/519] Bump pytest from 6.2.4 to 6.2.5 Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.4 to 6.2.5. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.4...6.2.5) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 67ee0035b..34d5607f3 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ flake8==3.9.2 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.4.1 mypy==0.910 -pytest==6.2.4 +pytest==6.2.5 pytest-asyncio==0.15.1 pytest-cov==2.12.1 pytest-mock==3.6.1 From 771193cbe47b0bd812e87662bdc141b911e316a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:19 +0000 Subject: [PATCH 392/519] Bump plotly from 5.3.0 to 5.3.1 Bumps [plotly](https://github.com/plotly/plotly.py) from 5.3.0 to 5.3.1. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.3.0...v5.3.1) --- updated-dependencies: - dependency-name: plotly dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index 62836a729..8e17232b0 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.3.0 +plotly==5.3.1 From 44f8d7abf2ba21c3eb0e1c0f5804d8bafb9d2e31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:23 +0000 Subject: [PATCH 393/519] Bump ccxt from 1.55.56 to 1.55.83 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.55.56 to 1.55.83. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.55.56...1.55.83) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f77edddfe..e2bed0f9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.2 pandas==1.3.2 -ccxt==1.55.56 +ccxt==1.55.83 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.8 aiohttp==3.7.4.post0 From 765e72715bf739504e53140cd29eb24ef40dda44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 03:01:28 +0000 Subject: [PATCH 394/519] Bump mkdocs-material from 7.2.5 to 7.2.6 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.2.5 to 7.2.6. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.2.5...7.2.6) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index d820c9412..9927740c2 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.2.5 +mkdocs-material==7.2.6 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From a04875eb55beab2f9d86534434daf2465b9f0d78 Mon Sep 17 00:00:00 2001 From: EnzovdWetering <36194995+EnzovdWetering@users.noreply.github.com> Date: Mon, 6 Sep 2021 17:53:44 +0200 Subject: [PATCH 395/519] Update edge.md Typo fix --- docs/edge.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/edge.md b/docs/edge.md index 237ff36f6..4402d767f 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -3,7 +3,7 @@ The `Edge Positioning` module uses probability to calculate your win rate and risk reward ratio. It will use these statistics to control your strategy trade entry points, position size and, stoploss. !!! Warning - WHen using `Edge positioning` with a dynamic whitelist (VolumePairList), make sure to also use `AgeFilter` and set it to at least `calculate_since_number_of_days` to avoid problems with missing data. + When using `Edge positioning` with a dynamic whitelist (VolumePairList), make sure to also use `AgeFilter` and set it to at least `calculate_since_number_of_days` to avoid problems with missing data. !!! Note `Edge Positioning` only considers *its own* buy/sell/stoploss signals. It ignores the stoploss, trailing stoploss, and ROI settings in the strategy configuration file. From 6bd495a32a58b6da236f53da2d166f683a21cfff Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 6 Sep 2021 19:53:32 +0200 Subject: [PATCH 396/519] Fix 0Exception error happens when wrong stake-currency is selected and /profit is called --- freqtrade/rpc/rpc.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 95a37452b..ca2e84e48 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -403,8 +403,11 @@ class RPC: # Doing the sum is not right - overall profit needs to be based on initial capital profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0 starting_balance = self._freqtrade.wallets.get_starting_balance() - profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance - profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance + profit_closed_ratio_fromstart = 0 + profit_all_ratio_fromstart = 0 + if starting_balance: + profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance + profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance profit_all_fiat = self._fiat_converter.convert_amount( profit_all_coin_sum, From 10d0987f49b8179f840895882bccebb732098192 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 6 Sep 2021 19:54:34 +0200 Subject: [PATCH 397/519] Fix docs for custom hyperopt space --- docs/advanced-hyperopt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced-hyperopt.md b/docs/advanced-hyperopt.md index 8f233438b..4a2bafd2e 100644 --- a/docs/advanced-hyperopt.md +++ b/docs/advanced-hyperopt.md @@ -80,7 +80,7 @@ To override a pre-defined space (`roi_space`, `generate_roi_table`, `stoploss_sp class MyAwesomeStrategy(IStrategy): class HyperOpt: # Define a custom stoploss space. - def stoploss_space(self): + def stoploss_space(): return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')] ``` From 880474594e5357eea9b47b8d1c8449a968bce4c3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 7 Sep 2021 06:51:31 +0200 Subject: [PATCH 398/519] have ftuser use `/bin/bash` in dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4c4722452..f7e26efe3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN mkdir /freqtrade \ && apt-get update \ && apt-get -y install sudo libatlas3-base curl sqlite3 libhdf5-serial-dev \ && apt-get clean \ - && useradd -u 1000 -G sudo -U -m ftuser \ + && useradd -u 1000 -G sudo -U -m -s /bin/bash ftuser \ && chown ftuser:ftuser /freqtrade \ # Allow sudoers && echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers From 71ff214adfe844a1ff57624094169f48dbd202e5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 7 Sep 2021 07:14:40 +0200 Subject: [PATCH 399/519] Support "initial_call" for download-data of new pairs --- freqtrade/data/history/history_utils.py | 3 ++- freqtrade/exchange/binance.py | 1 + freqtrade/exchange/exchange.py | 17 ++++++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index 6f125aaa9..e6b8db322 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -197,7 +197,8 @@ def _download_pair_history(pair: str, *, timeframe=timeframe, since_ms=since_ms if since_ms else arrow.utcnow().shift( - days=-new_pairs_days).int_timestamp * 1000 + days=-new_pairs_days).int_timestamp * 1000, + is_new_pair=data.empty ) # TODO: Maybe move parsing to exchange class (?) new_dataframe = ohlcv_to_dataframe(new_data, timeframe, pair, diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 189f5f481..7a3111f85 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -20,6 +20,7 @@ class Binance(Exchange): "order_time_in_force": ['gtc', 'fok', 'ioc'], "time_in_force_parameter": "timeInForce", "ohlcv_candle_limit": 1000, + "ohlcv_initial_call": True, "trades_pagination": "id", "trades_pagination_arg": "fromId", "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 80f20b17e..425b89f88 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1194,7 +1194,7 @@ class Exchange: # Historic data def get_historic_ohlcv(self, pair: str, timeframe: str, - since_ms: int) -> List: + since_ms: int, is_new_pair: bool = False) -> List: """ Get candle history using asyncio and returns the list of candles. Handles all async work for this. @@ -1206,7 +1206,7 @@ class Exchange: """ return asyncio.get_event_loop().run_until_complete( self._async_get_historic_ohlcv(pair=pair, timeframe=timeframe, - since_ms=since_ms)) + since_ms=since_ms, is_new_pair=is_new_pair)) def get_historic_ohlcv_as_df(self, pair: str, timeframe: str, since_ms: int) -> DataFrame: @@ -1221,9 +1221,9 @@ class Exchange: return ohlcv_to_dataframe(ticks, timeframe, pair=pair, fill_missing=True, drop_incomplete=self._ohlcv_partial_candle) - async def _async_get_historic_ohlcv(self, pair: str, - timeframe: str, - since_ms: int) -> List: + async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, + since_ms: int, is_new_pair: bool + ) -> List: """ Download historic ohlcv """ @@ -1234,6 +1234,13 @@ class Exchange: one_call, arrow.utcnow().shift(seconds=one_call // 1000).humanize(only_distance=True) ) + if self._ft_has.get('ohlcv_initial_call', False) and is_new_pair: + x = await self._async_get_candle_history(pair, timeframe, 0) + if x and x[2] and x[2][0] and x[2][0][0] > since_ms: + # Set starting date to first available candle. + since_ms = x[2][0][0] + logger.info(f"Candle-data available starting with {since_ms}.") + input_coroutines = [self._async_get_candle_history( pair, timeframe, since) for since in range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)] From 8c83c258a52c9ea4d91ed8f0d1a87e084109f828 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 7 Sep 2021 19:29:10 +0200 Subject: [PATCH 400/519] Move "first-pair_getting" to binance subclass --- freqtrade/exchange/binance.py | 21 +++++++++++++++++++-- freqtrade/exchange/exchange.py | 8 +------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 7a3111f85..284f9cdad 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -1,7 +1,8 @@ """ Binance exchange subclass """ import logging -from typing import Dict +from typing import Dict, List +import arrow import ccxt from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, @@ -20,7 +21,6 @@ class Binance(Exchange): "order_time_in_force": ['gtc', 'fok', 'ioc'], "time_in_force_parameter": "timeInForce", "ohlcv_candle_limit": 1000, - "ohlcv_initial_call": True, "trades_pagination": "id", "trades_pagination_arg": "fromId", "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], @@ -91,3 +91,20 @@ class Binance(Exchange): f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: raise OperationalException(e) from e + + async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, + since_ms: int, is_new_pair: bool + ) -> List: + """ + Overwrite to introduce "fast new pair" functionality by detecting the pair's listing date + Does not work for other exchanges, which don't return the earliest data when called with "0" + """ + if is_new_pair: + x = await self._async_get_candle_history(pair, timeframe, 0) + if x and x[2] and x[2][0] and x[2][0][0] > since_ms: + # Set starting date to first available candle. + since_ms = x[2][0][0] + logger.info(f"Candle-data available starting with " + f"{arrow.get(since_ms // 1000).isoformat()}.") + return await super()._async_get_historic_ohlcv( + pair=pair, timeframe=timeframe, since_ms=since_ms, is_new_pair=is_new_pair) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 425b89f88..79fd33dfe 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1226,6 +1226,7 @@ class Exchange: ) -> List: """ Download historic ohlcv + :param is_new_pair: used by binance subclass to allow "fast" new pair downloading """ one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe) @@ -1234,13 +1235,6 @@ class Exchange: one_call, arrow.utcnow().shift(seconds=one_call // 1000).humanize(only_distance=True) ) - if self._ft_has.get('ohlcv_initial_call', False) and is_new_pair: - x = await self._async_get_candle_history(pair, timeframe, 0) - if x and x[2] and x[2][0] and x[2][0][0] > since_ms: - # Set starting date to first available candle. - since_ms = x[2][0][0] - logger.info(f"Candle-data available starting with {since_ms}.") - input_coroutines = [self._async_get_candle_history( pair, timeframe, since) for since in range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)] From 2d66987ac7ed38961c2110680b03893693f86231 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 7 Sep 2021 19:51:07 +0200 Subject: [PATCH 401/519] Add test for "pair-startdate" detection --- freqtrade/exchange/binance.py | 2 +- tests/exchange/test_binance.py | 35 +++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 284f9cdad..8dced3894 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -104,7 +104,7 @@ class Binance(Exchange): if x and x[2] and x[2][0] and x[2][0][0] > since_ms: # Set starting date to first available candle. since_ms = x[2][0][0] - logger.info(f"Candle-data available starting with " + logger.info(f"Candle-data for {pair} available starting with " f"{arrow.get(since_ms // 1000).isoformat()}.") return await super()._async_get_historic_ohlcv( pair=pair, timeframe=timeframe, since_ms=since_ms, is_new_pair=is_new_pair) diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index f2b508761..dd85c3abe 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -1,3 +1,4 @@ +from datetime import datetime, timezone from random import randint from unittest.mock import MagicMock @@ -5,7 +6,7 @@ import ccxt import pytest from freqtrade.exceptions import DependencyException, InvalidOrderException, OperationalException -from tests.conftest import get_patched_exchange +from tests.conftest import get_mock_coro, get_patched_exchange, log_has_re from tests.exchange.test_exchange import ccxt_exceptionhandlers @@ -105,3 +106,35 @@ def test_stoploss_adjust_binance(mocker, default_conf): # Test with invalid order case order['type'] = 'stop_loss' assert not exchange.stoploss_adjust(1501, order) + + +@pytest.mark.asyncio +async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog): + ohlcv = [ + [ + int((datetime.now(timezone.utc).timestamp() - 1000) * 1000), + 1, # open + 2, # high + 3, # low + 4, # close + 5, # volume (in quote currency) + ] + ] + + exchange = get_patched_exchange(mocker, default_conf, id='binance') + # Monkey-patch async function + exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) + + pair = 'ETH/BTC' + res = await exchange._async_get_historic_ohlcv(pair, "5m", + 1500000000000, is_new_pair=False) + # Call with very old timestamp - causes tons of requests + assert exchange._api_async.fetch_ohlcv.call_count > 400 + # assert res == ohlcv + exchange._api_async.fetch_ohlcv.reset_mock() + res = await exchange._async_get_historic_ohlcv(pair, "5m", 1500000000000, is_new_pair=True) + + # Called twice - one "init" call - and one to get the actual data. + assert exchange._api_async.fetch_ohlcv.call_count == 2 + assert res == ohlcv + assert log_has_re(r"Candle-data for ETH/BTC available starting with .*", caplog) From 79ca6135a2cb599de9fa4fde397226d63dda2832 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Tue, 7 Sep 2021 21:53:38 -0600 Subject: [PATCH 402/519] added caplog clears to freqtradebot tests --- tests/test_freqtradebot.py | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 75b67e59c..ddd031f77 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -107,6 +107,7 @@ def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) + caplog.clear() def test_order_dict_live(default_conf, mocker, caplog) -> None: @@ -140,6 +141,7 @@ def test_order_dict_live(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) + caplog.clear() def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: @@ -415,6 +417,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord assert freqtrade.create_trade('ETH/BTC') assert log_has_re(r"Stake amount for pair .* is too small.*", caplog) + caplog.clear() def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, @@ -478,6 +481,7 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope n = freqtrade.enter_positions() assert n == 0 assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) + caplog.clear() def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, @@ -518,11 +522,13 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, # 0 trades, but it's not because of pairlock. assert n == 0 assert not log_has_re(message, caplog) + caplog.clear() PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=20).datetime, 'Just because') n = freqtrade.enter_positions() assert n == 0 assert log_has_re(message, caplog) + caplog.clear() def test_create_trade_no_signal(default_conf, fee, mocker) -> None: @@ -1086,6 +1092,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, assert log_has_re(r'STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.', caplog) assert trade.stoploss_order_id is None assert trade.is_open is False + caplog.clear() mocker.patch( 'freqtrade.exchange.Binance.stoploss', @@ -1115,6 +1122,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) assert freqtrade.handle_stoploss_on_exchange(trade) is False assert stoploss.call_count == 0 + caplog.clear() def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, @@ -1154,6 +1162,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, assert log_has_re(r'Stoploss order was cancelled, but unable to recreate one.*', caplog) assert trade.stoploss_order_id is None assert trade.is_open is True + caplog.clear() def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, @@ -1202,6 +1211,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert rpc_mock.call_count == 2 assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market' + caplog.clear() def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee, @@ -1428,6 +1438,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) assert cancel_mock.call_count == 1 assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog) + caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -1662,6 +1673,7 @@ def test_enter_positions(mocker, default_conf, caplog) -> None: assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) # create_trade should be called once for every pair in the whitelist. assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) + caplog.clear() def test_enter_positions_exception(mocker, default_conf, caplog) -> None: @@ -1701,6 +1713,7 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: # test amount modified by fee-logic n = freqtrade.exit_positions(trades) assert n == 0 + caplog.clear() def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1721,6 +1734,7 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) n = freqtrade.exit_positions(trades) assert n == 0 assert log_has('Unable to sell trade ETH/BTC: ', caplog) + caplog.clear() def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1743,10 +1757,12 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No ) assert not freqtrade.update_trade_state(trade, None) assert log_has_re(r'Orderid for trade .* is empty.', caplog) + caplog.clear() # Add datetime explicitly since sqlalchemy defaults apply only once written to database freqtrade.update_trade_state(trade, '123') # Test amount not modified by fee-logic assert not log_has_re(r'Applying fee to .*', caplog) + caplog.clear() assert trade.open_order_id is None assert trade.amount == limit_buy_order['amount'] @@ -1764,6 +1780,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No freqtrade.update_trade_state(trade, '123') assert log_has_re('Found open order for.*', caplog) + caplog.clear() def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee, @@ -1814,6 +1831,7 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_ assert trade.amount != amount assert trade.amount == limit_buy_order['amount'] assert log_has_re(r'Applying fee on amount for .*', caplog) + caplog.clear() def test_update_trade_state_exception(mocker, default_conf, @@ -1832,6 +1850,7 @@ def test_update_trade_state_exception(mocker, default_conf, ) freqtrade.update_trade_state(trade, trade.open_order_id) assert log_has('Could not update trade amount: ', caplog) + caplog.clear() def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: @@ -1848,6 +1867,7 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None freqtrade.update_trade_state(trade, trade.open_order_id) assert grm_mock.call_count == 0 assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog) + caplog.clear() def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order_open, @@ -2016,6 +2036,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) + caplog.clear() def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open, @@ -2048,6 +2069,7 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) + caplog.clear() def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open, limit_sell_order, @@ -2088,6 +2110,7 @@ def test_bot_loop_start_called_once(mocker, default_conf, caplog): assert log_has_re(r'Strategy caused the following exception.*', caplog) assert ftbot.strategy.bot_loop_start.call_count == 1 assert ftbot.strategy.analyze.call_count == 1 + caplog.clear() def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2202,6 +2225,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o nb_trades = len(trades) assert nb_trades == 0 assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog) + caplog.clear() def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2336,6 +2360,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, assert rpc_mock.call_count == 1 assert open_trade.is_open is True assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog) + caplog.clear() def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, @@ -2404,6 +2429,7 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap assert trades[0].open_order_id is None assert trades[0].fee_updated('buy') assert pytest.approx(trades[0].fee_open) == 0.001 + caplog.clear() def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, caplog, fee, @@ -2444,6 +2470,7 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, limit_buy_order_old_partial['remaining']) assert trades[0].open_order_id is None assert trades[0].fee_open == fee() + caplog.clear() def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None: @@ -2472,6 +2499,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}" r"\) due to Traceback \(most recent call last\):\n*", caplog) + caplog.clear() def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None: @@ -2515,6 +2543,7 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) assert log_has_re(r"Order .* for .* not cancelled.", caplog) + caplog.clear() @pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'], @@ -2536,6 +2565,7 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, assert cancel_order_mock.call_count == 0 assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog) assert nofiy_mock.call_count == 1 + caplog.clear() @pytest.mark.parametrize('cancelorder', [ @@ -2905,6 +2935,7 @@ def test_execute_trade_exit_sloe_cancel_exception( sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert create_order_mock.call_count == 2 assert log_has('Could not cancel stoploss order abcd', caplog) + caplog.clear() def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, @@ -3300,6 +3331,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ assert freqtrade.handle_trade(trade) is True assert log_has_re(r'.*Falling back to wallet-amount.', caplog) assert trade.amount != amnt + caplog.clear() def test__safe_sell_amount(default_conf, fee, caplog, mocker): @@ -3330,6 +3362,7 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker): assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 + caplog.clear() def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): @@ -3386,6 +3419,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo freqtrade.enter_positions() assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) + caplog.clear() def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3477,6 +3511,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, assert log_has("ETH/BTC - HIT STOP: current price at 0.000012, stoploss is 0.000015, " "initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value + caplog.clear() def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3524,6 +3559,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000138501 + caplog.clear() mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ @@ -3537,6 +3573,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, " f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) + caplog.clear() def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3584,6 +3621,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000138501 + caplog.clear() mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ @@ -3598,6 +3636,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value + caplog.clear() def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3648,6 +3687,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert not log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000098910 + caplog.clear() # price rises above the offset (rises 12% when the offset is 5.5%) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', @@ -3661,6 +3701,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert log_has("ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000117705 + caplog.clear() def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3722,6 +3763,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fe assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).', caplog) + caplog.clear() def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fee, fee, @@ -3747,6 +3789,7 @@ def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fe assert walletmock.call_count == 1 assert log_has_re(r'Fee amount for Trade.* was in base currency ' '- Eating Fee 0.008 into dust', caplog) + caplog.clear() def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, fee): @@ -3769,6 +3812,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found', caplog) + caplog.clear() def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -3862,6 +3906,7 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None + caplog.clear() def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee, @@ -3897,6 +3942,7 @@ def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None + caplog.clear() def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee, @@ -3925,6 +3971,7 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).', caplog) + caplog.clear() def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -4168,6 +4215,7 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None with pytest.raises(PricingError): freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog) + caplog.clear() def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: @@ -4238,6 +4286,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.handle_trade(trade) assert log_has_re(r'Sell Price at location 1 from orderbook could not be determined\..*', caplog) + caplog.clear() def test_startup_state(default_conf, mocker): @@ -4296,6 +4345,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", caplog) + caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4339,6 +4389,7 @@ def test_update_open_orders(mocker, default_conf, fee, caplog): freqtrade.update_open_orders() assert not log_has_re(r"Error updating Order .*", caplog) + caplog.clear() freqtrade.config['dry_run'] = False freqtrade.update_open_orders() @@ -4454,6 +4505,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 0 assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog) + caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4591,6 +4643,7 @@ def test_refind_lost_order(mocker, default_conf, fee, caplog): freqtrade.refind_lost_order(trades[4]) assert log_has(f"Error updating {order['id']}.", caplog) + caplog.clear() def test_get_valid_price(mocker, default_conf) -> None: From aac05029e1d13936f89c10f0ed8ccdaec3561ed7 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 01:20:52 -0600 Subject: [PATCH 403/519] safe_sell_amount -> safe_exit_amount --- freqtrade/freqtradebot.py | 4 ++-- tests/test_freqtradebot.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 259270483..0fc40cf45 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1039,7 +1039,7 @@ class FreqtradeBot(LoggingMixin): ) return reason - def _safe_sell_amount(self, pair: str, amount: float) -> float: + def _safe_exit_amount(self, pair: str, amount: float) -> float: """ Get sellable amount. Should be trade.amount - but will fall back to the available amount if necessary. @@ -1111,7 +1111,7 @@ class FreqtradeBot(LoggingMixin): # but we allow this value to be changed) order_type = self.strategy.order_types.get("forcesell", order_type) - amount = self._safe_sell_amount(trade.pair, trade.amount) + amount = self._safe_exit_amount(trade.pair, trade.amount) time_in_force = self.strategy.order_time_in_force['sell'] if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 75b67e59c..109cb01c2 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3302,7 +3302,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ assert trade.amount != amnt -def test__safe_sell_amount(default_conf, fee, caplog, mocker): +def test__safe_exit_amount(default_conf, fee, caplog, mocker): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 @@ -3322,17 +3322,17 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker): patch_get_signal(freqtrade) wallet_update.reset_mock() - assert freqtrade._safe_sell_amount(trade.pair, trade.amount) == amount_wallet + assert freqtrade._safe_exit_amount(trade.pair, trade.amount) == amount_wallet assert log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 caplog.clear() wallet_update.reset_mock() - assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet + assert freqtrade._safe_exit_amount(trade.pair, amount_wallet) == amount_wallet assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 -def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): +def test__safe_exit_amount_error(default_conf, fee, caplog, mocker): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 @@ -3350,7 +3350,7 @@ def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) with pytest.raises(DependencyException, match=r"Not enough amount to sell."): - assert freqtrade._safe_sell_amount(trade.pair, trade.amount) + assert freqtrade._safe_exit_amount(trade.pair, trade.amount) def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None: From be93c75e44fcaa1df2094b1d6200b0c884abf596 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 01:14:16 -0600 Subject: [PATCH 404/519] reupdate_buy_order_fees -> reupdate_enter_order_fees --- freqtrade/freqtradebot.py | 4 ++-- tests/test_freqtradebot.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0fc40cf45..0f16162cd 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -296,9 +296,9 @@ class FreqtradeBot(LoggingMixin): if sell_order: self.refind_lost_order(trade) else: - self.reupdate_buy_order_fees(trade) + self.reupdate_enter_order_fees(trade) - def reupdate_buy_order_fees(self, trade: Trade): + def reupdate_enter_order_fees(self, trade: Trade): """ Get buy order from database, and try to reupdate. Handles trades where the initial fee-update did not work. diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 109cb01c2..88ed11558 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4420,14 +4420,14 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf, fee): @pytest.mark.usefixtures("init_persistence") -def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): +def test_reupdate_enter_order_fees(mocker, default_conf, fee, caplog): freqtrade = get_patched_freqtradebot(mocker, default_conf) mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state') create_mock_trades(fee) trades = Trade.get_trades().all() - freqtrade.reupdate_buy_order_fees(trades[0]) + freqtrade.reupdate_enter_order_fees(trades[0]) assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 1 assert mock_uts.call_args_list[0][0][0] == trades[0] @@ -4450,7 +4450,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): ) Trade.query.session.add(trade) - freqtrade.reupdate_buy_order_fees(trade) + freqtrade.reupdate_enter_order_fees(trade) assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 0 assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog) @@ -4460,7 +4460,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): def test_handle_insufficient_funds(mocker, default_conf, fee): freqtrade = get_patched_freqtradebot(mocker, default_conf) mock_rlo = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.refind_lost_order') - mock_bof = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.reupdate_buy_order_fees') + mock_bof = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.reupdate_enter_order_fees') create_mock_trades(fee) trades = Trade.get_trades().all() From e0092a85e9769eeb7e73bbb6f6018dbd494ea1a0 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 00:53:09 -0600 Subject: [PATCH 405/519] handle_cancel_buy/sell -> handle_cancel_enter/exit --- freqtrade/freqtradebot.py | 12 +++++------ freqtrade/rpc/rpc.py | 4 ++-- tests/test_freqtradebot.py | 44 +++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0f16162cd..bf07ac0bc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -906,7 +906,7 @@ class FreqtradeBot(LoggingMixin): default_retval=False)(pair=trade.pair, trade=trade, order=order))): - self.handle_cancel_buy(trade, order, constants.CANCEL_REASON['TIMEOUT']) + self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT']) elif (order['side'] == 'sell' and (order['status'] == 'open' or fully_cancelled) and ( fully_cancelled @@ -915,7 +915,7 @@ class FreqtradeBot(LoggingMixin): default_retval=False)(pair=trade.pair, trade=trade, order=order))): - self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['TIMEOUT']) + self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT']) def cancel_all_open_orders(self) -> None: """ @@ -931,13 +931,13 @@ class FreqtradeBot(LoggingMixin): continue if order['side'] == 'buy': - self.handle_cancel_buy(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) + self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) elif order['side'] == 'sell': - self.handle_cancel_sell(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) + self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) Trade.commit() - def handle_cancel_buy(self, trade: Trade, order: Dict, reason: str) -> bool: + def handle_cancel_enter(self, trade: Trade, order: Dict, reason: str) -> bool: """ Buy cancel - cancel order :return: True if order was fully cancelled @@ -998,7 +998,7 @@ class FreqtradeBot(LoggingMixin): reason=reason) return was_trade_fully_canceled - def handle_cancel_sell(self, trade: Trade, order: Dict, reason: str) -> str: + def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str: """ Sell cancel - cancel order and update trade :return: Reason for cancel diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index ca2e84e48..caf7345a2 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -548,12 +548,12 @@ class RPC: order = self._freqtrade.exchange.fetch_order(trade.open_order_id, trade.pair) if order['side'] == 'buy': - fully_canceled = self._freqtrade.handle_cancel_buy( + fully_canceled = self._freqtrade.handle_cancel_enter( trade, order, CANCEL_REASON['FORCE_SELL']) if order['side'] == 'sell': # Cancel order - so it is placed anew with a fresh price. - self._freqtrade.handle_cancel_sell(trade, order, CANCEL_REASON['FORCE_SELL']) + self._freqtrade.handle_cancel_exit(trade, order, CANCEL_REASON['FORCE_SELL']) if not fully_canceled: # Get current rate and execute sell diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 88ed11558..1b5381264 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2453,8 +2453,8 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', - handle_cancel_buy=MagicMock(), - handle_cancel_sell=MagicMock(), + handle_cancel_enter=MagicMock(), + handle_cancel_exit=MagicMock(), ) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -2474,7 +2474,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke caplog) -def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None: +def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_buy_order = deepcopy(limit_buy_order) @@ -2493,34 +2493,34 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non limit_buy_order['filled'] = 0.0 limit_buy_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 cancel_order_mock.reset_mock() caplog.clear() limit_buy_order['filled'] = 0.01 - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 0 assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unsellable.*", caplog) caplog.clear() cancel_order_mock.reset_mock() limit_buy_order['filled'] = 2 - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 # Order remained open for some reason (cancel failed) cancel_buy_order['status'] = 'open' cancel_order_mock = MagicMock(return_value=cancel_buy_order) mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert log_has_re(r"Order .* for .* not cancelled.", caplog) @pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'], indirect=['limit_buy_order_canceled_empty']) -def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, - limit_buy_order_canceled_empty) -> None: +def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, + limit_buy_order_canceled_empty) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = mocker.patch( @@ -2532,7 +2532,7 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, reason = CANCEL_REASON['TIMEOUT'] trade = MagicMock() trade.pair = 'LTC/ETH' - assert freqtrade.handle_cancel_buy(trade, limit_buy_order_canceled_empty, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason) assert cancel_order_mock.call_count == 0 assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog) assert nofiy_mock.call_count == 1 @@ -2544,8 +2544,8 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, 'String Return value', 123 ]) -def test_handle_cancel_buy_corder_empty(mocker, default_conf, limit_buy_order, - cancelorder) -> None: +def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order, + cancelorder) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock(return_value=cancelorder) @@ -2563,16 +2563,16 @@ def test_handle_cancel_buy_corder_empty(mocker, default_conf, limit_buy_order, limit_buy_order['filled'] = 0.0 limit_buy_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 cancel_order_mock.reset_mock() limit_buy_order['filled'] = 1.0 - assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert not freqtrade.handle_cancel_enter(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 -def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None: +def test_handle_cancel_exit_limit(mocker, default_conf, fee) -> None: send_msg_mock = patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock() @@ -2598,26 +2598,26 @@ def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None: 'amount': 1, 'status': "open"} reason = CANCEL_REASON['TIMEOUT'] - assert freqtrade.handle_cancel_sell(trade, order, reason) + assert freqtrade.handle_cancel_exit(trade, order, reason) assert cancel_order_mock.call_count == 1 assert send_msg_mock.call_count == 1 send_msg_mock.reset_mock() order['amount'] = 2 - assert freqtrade.handle_cancel_sell(trade, order, reason + assert freqtrade.handle_cancel_exit(trade, order, reason ) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] # Assert cancel_order was not called (callcount remains unchanged) assert cancel_order_mock.call_count == 1 assert send_msg_mock.call_count == 1 - assert freqtrade.handle_cancel_sell(trade, order, reason + assert freqtrade.handle_cancel_exit(trade, order, reason ) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] # Message should not be iterated again assert trade.sell_order_status == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] assert send_msg_mock.call_count == 1 -def test_handle_cancel_sell_cancel_exception(mocker, default_conf) -> None: +def test_handle_cancel_exit_cancel_exception(mocker, default_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch( @@ -2630,7 +2630,7 @@ def test_handle_cancel_sell_cancel_exception(mocker, default_conf) -> None: order = {'remaining': 1, 'amount': 1, 'status': "open"} - assert freqtrade.handle_cancel_sell(trade, order, reason) == 'error cancelling order' + assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order' def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: @@ -4304,8 +4304,8 @@ def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order, limi mocker.patch('freqtrade.exchange.Exchange.fetch_order', side_effect=[ ExchangeError(), limit_sell_order, limit_buy_order, limit_sell_order]) - buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_buy') - sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_sell') + buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_enter') + sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit') freqtrade = get_patched_freqtradebot(mocker, default_conf) create_mock_trades(fee) From e1f846f22f28a9f76df1e8c47a79225ca2bc6e89 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 00:49:04 -0600 Subject: [PATCH 406/519] sell_lock -> exit_lock --- freqtrade/freqtradebot.py | 6 +++--- freqtrade/rpc/rpc.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index bf07ac0bc..0a0b20f0c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -99,7 +99,7 @@ class FreqtradeBot(LoggingMixin): self.state = State[initial_state.upper()] if initial_state else State.STOPPED # Protect sell-logic from forcesell and vice versa - self._sell_lock = Lock() + self._exit_lock = Lock() LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe)) def notify_status(self, msg: str) -> None: @@ -166,14 +166,14 @@ class FreqtradeBot(LoggingMixin): self.strategy.analyze(self.active_pair_whitelist) - with self._sell_lock: + with self._exit_lock: # Check and handle any timed out open orders self.check_handle_timedout() # Protect from collisions with forcesell. # Without this, freqtrade my try to recreate stoploss_on_exchange orders # while selling is in process, since telegram messages arrive in an different thread. - with self._sell_lock: + with self._exit_lock: trades = Trade.get_open_trades() # First process current opened trades (positions) self.exit_positions(trades) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index caf7345a2..b7b1fe603 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -566,7 +566,7 @@ class RPC: if self._freqtrade.state != State.RUNNING: raise RPCException('trader is not running') - with self._freqtrade._sell_lock: + with self._freqtrade._exit_lock: if trade_id == 'all': # Execute sell for all open orders for trade in Trade.get_open_trades(): @@ -628,7 +628,7 @@ class RPC: Handler for delete . Delete the given trade and close eventually existing open orders. """ - with self._freqtrade._sell_lock: + with self._freqtrade._exit_lock: c_count = 0 trade = Trade.get_trades(trade_filter=[Trade.id == trade_id]).first() if not trade: From 362dc20406c0419246cbe459518636337567cf9b Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 00:45:55 -0600 Subject: [PATCH 407/519] notify_buy -> notify_enter, notify_sell -> notify_exit --- freqtrade/freqtradebot.py | 26 +++++++++++++------------- tests/test_freqtradebot.py | 6 +++--- tests/test_integration.py | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0a0b20f0c..b67ae9f00 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -590,11 +590,11 @@ class FreqtradeBot(LoggingMixin): # Updating wallets self.wallets.update() - self._notify_buy(trade, order_type) + self._notify_enter(trade, order_type) return True - def _notify_buy(self, trade: Trade, order_type: str) -> None: + def _notify_enter(self, trade: Trade, order_type: str) -> None: """ Sends rpc notification when a buy occurred. """ @@ -617,7 +617,7 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_buy_cancel(self, trade: Trade, order_type: str, reason: str) -> None: + def _notify_enter_cancel(self, trade: Trade, order_type: str, reason: str) -> None: """ Sends rpc notification when a buy cancel occurred. """ @@ -643,7 +643,7 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_buy_fill(self, trade: Trade) -> None: + def _notify_enter_fill(self, trade: Trade) -> None: msg = { 'trade_id': trade.id, 'type': RPCMessageType.BUY_FILL, @@ -782,7 +782,7 @@ class FreqtradeBot(LoggingMixin): # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') - self._notify_sell(trade, "stoploss") + self._notify_exit(trade, "stoploss") return True if trade.open_order_id or not trade.is_open: @@ -994,8 +994,8 @@ class FreqtradeBot(LoggingMixin): reason += f", {constants.CANCEL_REASON['PARTIALLY_FILLED']}" self.wallets.update() - self._notify_buy_cancel(trade, order_type=self.strategy.order_types['buy'], - reason=reason) + self._notify_enter_cancel(trade, order_type=self.strategy.order_types['buy'], + reason=reason) return was_trade_fully_canceled def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str: @@ -1032,7 +1032,7 @@ class FreqtradeBot(LoggingMixin): reason = constants.CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'] self.wallets.update() - self._notify_sell_cancel( + self._notify_exit_cancel( trade, order_type=self.strategy.order_types['sell'], reason=reason @@ -1150,11 +1150,11 @@ class FreqtradeBot(LoggingMixin): self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') - self._notify_sell(trade, order_type) + self._notify_exit(trade, order_type) return True - def _notify_sell(self, trade: Trade, order_type: str, fill: bool = False) -> None: + def _notify_exit(self, trade: Trade, order_type: str, fill: bool = False) -> None: """ Sends rpc notification when a sell occurred. """ @@ -1196,7 +1196,7 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_sell_cancel(self, trade: Trade, order_type: str, reason: str) -> None: + def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str) -> None: """ Sends rpc notification when a sell cancel occurred. """ @@ -1291,13 +1291,13 @@ class FreqtradeBot(LoggingMixin): # Updating wallets when order is closed if not trade.is_open: if not stoploss_order and not trade.open_order_id: - self._notify_sell(trade, '', True) + self._notify_exit(trade, '', True) self.protections.stop_per_pair(trade.pair) self.protections.global_stop() self.wallets.update() elif not trade.open_order_id: # Buy fill - self._notify_buy_fill(trade) + self._notify_enter_fill(trade) return False diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 1b5381264..26d06d53a 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2485,7 +2485,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf, limit_buy_order) -> N mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) freqtrade = FreqtradeBot(default_conf) - freqtrade._notify_buy_cancel = MagicMock() + freqtrade._notify_enter_cancel = MagicMock() trade = MagicMock() trade.pair = 'LTC/USDT' @@ -2526,7 +2526,7 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf, cancel_order_mock = mocker.patch( 'freqtrade.exchange.Exchange.cancel_order_with_result', return_value=limit_buy_order_canceled_empty) - nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_buy_cancel') + nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_enter_cancel') freqtrade = FreqtradeBot(default_conf) reason = CANCEL_REASON['TIMEOUT'] @@ -2555,7 +2555,7 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf, limit_buy_order, ) freqtrade = FreqtradeBot(default_conf) - freqtrade._notify_buy_cancel = MagicMock() + freqtrade._notify_enter_cancel = MagicMock() trade = MagicMock() trade.pair = 'LTC/USDT' diff --git a/tests/test_integration.py b/tests/test_integration.py index 215927098..a3484d438 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -70,7 +70,7 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee, mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', create_stoploss_order=MagicMock(return_value=True), - _notify_sell=MagicMock(), + _notify_exit=MagicMock(), ) mocker.patch("freqtrade.strategy.interface.IStrategy.should_sell", should_sell_mock) wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update", MagicMock()) @@ -154,7 +154,7 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', create_stoploss_order=MagicMock(return_value=True), - _notify_sell=MagicMock(), + _notify_exit=MagicMock(), ) should_sell_mock = MagicMock(side_effect=[ SellCheckTuple(sell_type=SellType.NONE), From a1c9a4d619f3dfe5f123e928487f5dcdf1d351fc Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 01:40:22 -0600 Subject: [PATCH 408/519] freqtradebot local name changes --- freqtrade/freqtradebot.py | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b67ae9f00..5800befba 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -476,21 +476,21 @@ class FreqtradeBot(LoggingMixin): time_in_force = self.strategy.order_time_in_force['buy'] if price: - buy_limit_requested = price + enter_limit_requested = price else: # Calculate price - proposed_buy_rate = self.exchange.get_rate(pair, refresh=True, side="buy") + proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side="buy") custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, - default_retval=proposed_buy_rate)( + default_retval=proposed_enter_rate)( pair=pair, current_time=datetime.now(timezone.utc), - proposed_rate=proposed_buy_rate) + proposed_rate=proposed_enter_rate) - buy_limit_requested = self.get_valid_price(custom_entry_price, proposed_buy_rate) + enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate) - if not buy_limit_requested: + if not enter_limit_requested: raise PricingError('Could not determine buy price.') - min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, buy_limit_requested, + min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, enter_limit_requested, self.strategy.stoploss) if not self.edge: @@ -498,7 +498,7 @@ class FreqtradeBot(LoggingMixin): stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), - current_rate=buy_limit_requested, proposed_stake=stake_amount, + current_rate=enter_limit_requested, proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount) @@ -508,27 +508,27 @@ class FreqtradeBot(LoggingMixin): logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: " f"{stake_amount} ...") - amount = stake_amount / buy_limit_requested + amount = stake_amount / enter_limit_requested order_type = self.strategy.order_types['buy'] if forcebuy: # Forcebuy can define a different ordertype order_type = self.strategy.order_types.get('forcebuy', order_type) if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)( - pair=pair, order_type=order_type, amount=amount, rate=buy_limit_requested, + pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested, time_in_force=time_in_force, current_time=datetime.now(timezone.utc)): logger.info(f"User requested abortion of buying {pair}") return False amount = self.exchange.amount_to_precision(pair, amount) order = self.exchange.create_order(pair=pair, ordertype=order_type, side="buy", - amount=amount, rate=buy_limit_requested, + amount=amount, rate=enter_limit_requested, time_in_force=time_in_force) order_obj = Order.parse_from_ccxt_object(order, pair, 'buy') order_id = order['id'] order_status = order.get('status', None) # we assume the order is executed at the price requested - buy_limit_filled_price = buy_limit_requested + enter_limit_filled_price = enter_limit_requested amount_requested = amount if order_status == 'expired' or order_status == 'rejected': @@ -551,13 +551,13 @@ class FreqtradeBot(LoggingMixin): ) stake_amount = order['cost'] amount = safe_value_fallback(order, 'filled', 'amount') - buy_limit_filled_price = safe_value_fallback(order, 'average', 'price') + enter_limit_filled_price = safe_value_fallback(order, 'average', 'price') # in case of FOK the order may be filled immediately and fully elif order_status == 'closed': stake_amount = order['cost'] amount = safe_value_fallback(order, 'filled', 'amount') - buy_limit_filled_price = safe_value_fallback(order, 'average', 'price') + enter_limit_filled_price = safe_value_fallback(order, 'average', 'price') # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker') @@ -569,8 +569,8 @@ class FreqtradeBot(LoggingMixin): amount_requested=amount_requested, fee_open=fee, fee_close=fee, - open_rate=buy_limit_filled_price, - open_rate_requested=buy_limit_requested, + open_rate=enter_limit_filled_price, + open_rate_requested=enter_limit_requested, open_date=datetime.utcnow(), exchange=self.exchange.id, open_order_id=order_id, @@ -713,8 +713,8 @@ class FreqtradeBot(LoggingMixin): ) logger.debug('checking sell') - sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") - if self._check_and_execute_sell(trade, sell_rate, buy, sell): + exit_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell") + if self._check_and_execute_exit(trade, exit_rate, buy, sell): return True logger.debug('Found no sell signal for %s.', trade) @@ -744,7 +744,7 @@ class FreqtradeBot(LoggingMixin): except InvalidOrderException as e: trade.stoploss_order_id = None logger.error(f'Unable to place a stoploss order on exchange. {e}') - logger.warning('Selling the trade forcefully') + logger.warning('Exiting the trade forcefully') self.execute_trade_exit(trade, trade.stop_loss, sell_reason=SellCheckTuple( sell_type=SellType.EMERGENCY_SELL)) @@ -851,19 +851,19 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Could not create trailing stoploss order " f"for pair {trade.pair}.") - def _check_and_execute_sell(self, trade: Trade, sell_rate: float, - buy: bool, sell: bool) -> bool: + def _check_and_execute_exit(self, trade: Trade, exit_rate: float, + enter: bool, exit_: bool) -> bool: """ - Check and execute sell + Check and execute exit """ should_sell = self.strategy.should_sell( - trade, sell_rate, datetime.now(timezone.utc), buy, sell, + trade, exit_rate, datetime.now(timezone.utc), buy, sell, force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 ) if should_sell.sell_flag: logger.info(f'Executing Sell for {trade.pair}. Reason: {should_sell.sell_type}') - self.execute_trade_exit(trade, sell_rate, should_sell) + self.execute_trade_exit(trade, exit_rate, should_sell) return True return False From b2f289e4040ae676830987c3436ba85f8b29667d Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 8 Sep 2021 02:16:25 -0600 Subject: [PATCH 409/519] Fixed freqtradebot failing tests --- freqtrade/freqtradebot.py | 2 +- tests/test_freqtradebot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5800befba..7f668273c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -852,7 +852,7 @@ class FreqtradeBot(LoggingMixin): f"for pair {trade.pair}.") def _check_and_execute_exit(self, trade: Trade, exit_rate: float, - enter: bool, exit_: bool) -> bool: + buy: bool, sell: bool) -> bool: """ Check and execute exit """ diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 26d06d53a..3c5a8cfae 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1190,7 +1190,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert trade.stoploss_order_id is None assert trade.sell_reason == SellType.EMERGENCY_SELL.value assert log_has("Unable to place a stoploss order on exchange. ", caplog) - assert log_has("Selling the trade forcefully", caplog) + assert log_has("Exiting the trade forcefully", caplog) # Should call a market sell assert create_order_mock.call_count == 2 From 366247dff3bdd360b5b5fdc43c36e1396c902d91 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Thu, 9 Sep 2021 02:16:24 -0600 Subject: [PATCH 410/519] removed caplog.clears at end of functions in test_freqtradebot --- tests/test_freqtradebot.py | 45 -------------------------------------- 1 file changed, 45 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index ddd031f77..a9760978e 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -107,7 +107,6 @@ def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) - caplog.clear() def test_order_dict_live(default_conf, mocker, caplog) -> None: @@ -141,7 +140,6 @@ def test_order_dict_live(default_conf, mocker, caplog) -> None: freqtrade = FreqtradeBot(conf) assert not freqtrade.strategy.order_types['stoploss_on_exchange'] assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) - caplog.clear() def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: @@ -417,7 +415,6 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord assert freqtrade.create_trade('ETH/BTC') assert log_has_re(r"Stake amount for pair .* is too small.*", caplog) - caplog.clear() def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, @@ -481,7 +478,6 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope n = freqtrade.enter_positions() assert n == 0 assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) - caplog.clear() def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, @@ -528,7 +524,6 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, n = freqtrade.enter_positions() assert n == 0 assert log_has_re(message, caplog) - caplog.clear() def test_create_trade_no_signal(default_conf, fee, mocker) -> None: @@ -1122,7 +1117,6 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) assert freqtrade.handle_stoploss_on_exchange(trade) is False assert stoploss.call_count == 0 - caplog.clear() def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, @@ -1162,7 +1156,6 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, assert log_has_re(r'Stoploss order was cancelled, but unable to recreate one.*', caplog) assert trade.stoploss_order_id is None assert trade.is_open is True - caplog.clear() def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, @@ -1211,7 +1204,6 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, assert rpc_mock.call_count == 2 assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market' - caplog.clear() def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee, @@ -1438,7 +1430,6 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) assert cancel_mock.call_count == 1 assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog) - caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -1673,7 +1664,6 @@ def test_enter_positions(mocker, default_conf, caplog) -> None: assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) # create_trade should be called once for every pair in the whitelist. assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) - caplog.clear() def test_enter_positions_exception(mocker, default_conf, caplog) -> None: @@ -1713,7 +1703,6 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: # test amount modified by fee-logic n = freqtrade.exit_positions(trades) assert n == 0 - caplog.clear() def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1734,7 +1723,6 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) n = freqtrade.exit_positions(trades) assert n == 0 assert log_has('Unable to sell trade ETH/BTC: ', caplog) - caplog.clear() def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1780,7 +1768,6 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No freqtrade.update_trade_state(trade, '123') assert log_has_re('Found open order for.*', caplog) - caplog.clear() def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee, @@ -1831,7 +1818,6 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_ assert trade.amount != amount assert trade.amount == limit_buy_order['amount'] assert log_has_re(r'Applying fee on amount for .*', caplog) - caplog.clear() def test_update_trade_state_exception(mocker, default_conf, @@ -1850,7 +1836,6 @@ def test_update_trade_state_exception(mocker, default_conf, ) freqtrade.update_trade_state(trade, trade.open_order_id) assert log_has('Could not update trade amount: ', caplog) - caplog.clear() def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: @@ -1867,7 +1852,6 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None freqtrade.update_trade_state(trade, trade.open_order_id) assert grm_mock.call_count == 0 assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog) - caplog.clear() def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order_open, @@ -2036,7 +2020,6 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open, assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI", caplog) - caplog.clear() def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open, @@ -2069,7 +2052,6 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open assert freqtrade.handle_trade(trade) assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL", caplog) - caplog.clear() def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open, limit_sell_order, @@ -2110,7 +2092,6 @@ def test_bot_loop_start_called_once(mocker, default_conf, caplog): assert log_has_re(r'Strategy caused the following exception.*', caplog) assert ftbot.strategy.bot_loop_start.call_count == 1 assert ftbot.strategy.analyze.call_count == 1 - caplog.clear() def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2225,7 +2206,6 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o nb_trades = len(trades) assert nb_trades == 0 assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog) - caplog.clear() def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade, @@ -2360,7 +2340,6 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, assert rpc_mock.call_count == 1 assert open_trade.is_open is True assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog) - caplog.clear() def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, @@ -2429,7 +2408,6 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap assert trades[0].open_order_id is None assert trades[0].fee_updated('buy') assert pytest.approx(trades[0].fee_open) == 0.001 - caplog.clear() def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, caplog, fee, @@ -2470,7 +2448,6 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, limit_buy_order_old_partial['remaining']) assert trades[0].open_order_id is None assert trades[0].fee_open == fee() - caplog.clear() def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None: @@ -2499,7 +2476,6 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}" r"\) due to Traceback \(most recent call last\):\n*", caplog) - caplog.clear() def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None: @@ -2543,7 +2519,6 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock) assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) assert log_has_re(r"Order .* for .* not cancelled.", caplog) - caplog.clear() @pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'], @@ -2565,7 +2540,6 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf, assert cancel_order_mock.call_count == 0 assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog) assert nofiy_mock.call_count == 1 - caplog.clear() @pytest.mark.parametrize('cancelorder', [ @@ -2935,7 +2909,6 @@ def test_execute_trade_exit_sloe_cancel_exception( sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS)) assert create_order_mock.call_count == 2 assert log_has('Could not cancel stoploss order abcd', caplog) - caplog.clear() def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, @@ -3331,7 +3304,6 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ assert freqtrade.handle_trade(trade) is True assert log_has_re(r'.*Falling back to wallet-amount.', caplog) assert trade.amount != amnt - caplog.clear() def test__safe_sell_amount(default_conf, fee, caplog, mocker): @@ -3362,7 +3334,6 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker): assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) assert wallet_update.call_count == 1 - caplog.clear() def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): @@ -3419,7 +3390,6 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo freqtrade.enter_positions() assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) - caplog.clear() def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3511,7 +3481,6 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order, assert log_has("ETH/BTC - HIT STOP: current price at 0.000012, stoploss is 0.000015, " "initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value - caplog.clear() def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3573,7 +3542,6 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, " f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) - caplog.clear() def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3636,7 +3604,6 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde f"stoploss is {trade.stop_loss:.6f}, " f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog) assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value - caplog.clear() def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_open, fee, @@ -3701,7 +3668,6 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_ assert log_has("ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog) assert log_has("ETH/BTC - Adjusting stoploss...", caplog) assert trade.stop_loss == 0.0000117705 - caplog.clear() def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open, @@ -3763,7 +3729,6 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fe assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).', caplog) - caplog.clear() def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fee, fee, @@ -3789,7 +3754,6 @@ def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fe assert walletmock.call_count == 1 assert log_has_re(r'Fee amount for Trade.* was in base currency ' '- Eating Fee 0.008 into dust', caplog) - caplog.clear() def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, fee): @@ -3812,7 +3776,6 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found', caplog) - caplog.clear() def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -3906,7 +3869,6 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None - caplog.clear() def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee, @@ -3942,7 +3904,6 @@ def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None - caplog.clear() def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee, @@ -3971,7 +3932,6 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).', caplog) - caplog.clear() def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker): @@ -4215,7 +4175,6 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None with pytest.raises(PricingError): freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog) - caplog.clear() def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: @@ -4286,7 +4245,6 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o freqtrade.handle_trade(trade) assert log_has_re(r'Sell Price at location 1 from orderbook could not be determined\..*', caplog) - caplog.clear() def test_startup_state(default_conf, mocker): @@ -4345,7 +4303,6 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_ assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)", caplog) - caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4505,7 +4462,6 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog): assert log_has_re(r"Trying to reupdate buy fees for .*", caplog) assert mock_uts.call_count == 0 assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog) - caplog.clear() @pytest.mark.usefixtures("init_persistence") @@ -4643,7 +4599,6 @@ def test_refind_lost_order(mocker, default_conf, fee, caplog): freqtrade.refind_lost_order(trades[4]) assert log_has(f"Error updating {order['id']}.", caplog) - caplog.clear() def test_get_valid_price(mocker, default_conf) -> None: From 982534ddc70b7db43c598d7a48a2ff63c81aaedf Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Sep 2021 20:56:46 +0200 Subject: [PATCH 411/519] Add gate.io to list of supported exchanges --- README.md | 1 + docs/index.md | 1 + freqtrade/exchange/gateio.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 309fab94b..2c164135f 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Please read the [exchange specific notes](docs/exchanges.md) to learn about even - [X] [Bittrex](https://bittrex.com/) - [X] [Kraken](https://kraken.com/) - [X] [FTX](https://ftx.com) +- [X] [Gate.io](https://www.gate.io/ref/6266643) - [ ] [potentially many others](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ ### Community tested diff --git a/docs/index.md b/docs/index.md index fd3b8f224..7735117e2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -40,6 +40,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual, - [X] [Bittrex](https://bittrex.com/) - [X] [FTX](https://ftx.com) - [X] [Kraken](https://kraken.com/) +- [X] [Gate.io](https://www.gate.io/ref/6266643) - [ ] [potentially many others through ccxt](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ ### Community tested diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 9c910a10d..e6ee01c8a 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -21,3 +21,5 @@ class Gateio(Exchange): _ft_has: Dict = { "ohlcv_candle_limit": 1000, } + + _headers = {'X-Gate-Channel-Id': 'freqtrade'} From a19c33ba54ff051dfa20aae864510ac2a8bb6626 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 10 Sep 2021 05:52:48 +0000 Subject: [PATCH 412/519] Don't blindly create coroutines, but fire them off in batches --- freqtrade/exchange/exchange.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 79fd33dfe..48c3caa50 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -28,7 +28,7 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFun from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, BAD_EXCHANGES, EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, retrier, retrier_async) -from freqtrade.misc import deep_merge_dicts, safe_value_fallback2 +from freqtrade.misc import chunks, deep_merge_dicts, safe_value_fallback2 from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist @@ -1238,19 +1238,20 @@ class Exchange: input_coroutines = [self._async_get_candle_history( pair, timeframe, since) for since in range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)] - - results = await asyncio.gather(*input_coroutines, return_exceptions=True) - # Combine gathered results + data: List = [] - for res in results: - if isinstance(res, Exception): - logger.warning("Async code raised an exception: %s", res.__class__.__name__) - continue - # Deconstruct tuple if it's not an exception - p, _, new_data = res - if p == pair: - data.extend(new_data) + for input_coro in chunks(input_coroutines, 100): + + results = await asyncio.gather(*input_coro, return_exceptions=True) + for res in results: + if isinstance(res, Exception): + logger.warning("Async code raised an exception: %s", res.__class__.__name__) + continue + # Deconstruct tuple if it's not an exception + p, _, new_data = res + if p == pair: + data.extend(new_data) # Sort data again after extending the result - above calls return in "async order" data = sorted(data, key=lambda x: x[0]) logger.info("Downloaded data for %s with length %s.", pair, len(data)) From 8c9159f5969f593aa36754af9f19e4667223ba0a Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 10 Sep 2021 19:46:38 +0200 Subject: [PATCH 413/519] Improve comments --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 48c3caa50..e79d53e80 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1238,9 +1238,9 @@ class Exchange: input_coroutines = [self._async_get_candle_history( pair, timeframe, since) for since in range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)] - # Combine gathered results data: List = [] + # Chunk requests into batches of 100 to avoid overwelming ccxt Throttling for input_coro in chunks(input_coroutines, 100): results = await asyncio.gather(*input_coro, return_exceptions=True) From 4c4604f837d05d1d3c391031f24e8b63cef2da48 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 10 Sep 2021 19:47:56 +0200 Subject: [PATCH 414/519] Add explicit test for get_historic_ohlcv --- tests/exchange/test_exchange.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 144063c07..45c2e627c 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1551,6 +1551,32 @@ def test_get_historic_ohlcv_as_df(default_conf, mocker, exchange_name): assert 'high' in ret.columns +@pytest.mark.asyncio +@pytest.mark.parametrize("exchange_name", EXCHANGES) +async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name): + ohlcv = [ + [ + int((datetime.now(timezone.utc).timestamp() - 1000) * 1000), + 1, # open + 2, # high + 3, # low + 4, # close + 5, # volume (in quote currency) + ] + ] + exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) + # Monkey-patch async function + exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) + + pair = 'ETH/BTC' + res = await exchange._async_get_historic_ohlcv(pair, "5m", + 1500000000000, is_new_pair=False) + # Call with very old timestamp - causes tons of requests + assert exchange._api_async.fetch_ohlcv.call_count > 200 + assert res[0] == ohlcv[0] + assert log_has_re(r'Downloaded data for .* with length .*\.', caplog) + + def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: ohlcv = [ [ From a5f90a409cb5c78373bf2c477ac5dc62958cbf70 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Sep 2021 08:18:32 +0200 Subject: [PATCH 415/519] Small updates to async_history_fetch --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index e79d53e80..11de5411f 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1254,7 +1254,7 @@ class Exchange: data.extend(new_data) # Sort data again after extending the result - above calls return in "async order" data = sorted(data, key=lambda x: x[0]) - logger.info("Downloaded data for %s with length %s.", pair, len(data)) + logger.info(f"Downloaded data for {pair} with length {len(data)}.") return data def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *, diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 45c2e627c..90e19782e 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1568,7 +1568,7 @@ async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_ # Monkey-patch async function exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) - pair = 'ETH/BTC' + pair = 'ETH/USDT' res = await exchange._async_get_historic_ohlcv(pair, "5m", 1500000000000, is_new_pair=False) # Call with very old timestamp - causes tons of requests From 432c3df17e7672ab33554d161a4f7fb3e1f218ac Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 12 Sep 2021 08:04:22 +0200 Subject: [PATCH 416/519] Add documentation for Bittex/Gemini with VolumePairlist closes #5565 --- docs/exchanges.md | 6 ++++++ docs/includes/pairlists.md | 20 ++++++++++++++++++++ freqtrade/plugins/pairlist/VolumePairList.py | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index 42a850acd..c0fbdc694 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -58,6 +58,12 @@ Bittrex does not support market orders. If you have a message at the bot startup Bittrex also does not support `VolumePairlist` due to limited / split API constellation at the moment. Please use `StaticPairlist`. Other pairlists (other than `VolumePairlist`) should not be affected. +### Volume pairlist + +Bittrex does not support the direct usage of VolumePairList. This can however be worked around by using the advanced mode with `lookback_days: 1` (or more), which will emulate 24h volume. + +Read more in the [pairlist documentation](plugins.md#volumepairlist-advanced-mode). + ### Restricted markets Bittrex split its exchange into US and International versions. diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 6e23c9003..69e12d5dc 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -82,6 +82,8 @@ Filtering instances (not the first position in the list) will not apply any cach You can define a minimum volume with `min_value` - which will filter out pairs with a volume lower than the specified value in the specified timerange. +### VolumePairList Advanced mode + `VolumePairList` can also operate in an advanced mode to build volume over a given timerange of specified candle size. It utilizes exchange historical candle data, builds a typical price (calculated by (open+high+low)/3) and multiplies the typical price with every candle's volume. The sum is the `quoteVolume` over the given range. This allows different scenarios, for a more smoothened volume, when using longer ranges with larger candle sizes, or the opposite when using a short range with small candles. For convenience `lookback_days` can be specified, which will imply that 1d candles will be used for the lookback. In the example below the pairlist would be created based on the last 7 days: @@ -105,6 +107,24 @@ For convenience `lookback_days` can be specified, which will imply that 1d candl !!! Warning "Performance implications when using lookback range" If used in first position in combination with lookback, the computation of the range based volume can be time and resource consuming, as it downloads candles for all tradable pairs. Hence it's highly advised to use the standard approach with `VolumeFilter` to narrow the pairlist down for further range volume calculation. +??? Tip "Unsupported exchanges (Bittrex, Gemini)" + On some exchanges (like Bittrex and Gemini), regular VolumePairList does not work as the api does not natively provide 24h volume. This can be worked around by using candle data to build the volume. + To roughly simulate 24h volume, you can use the following configuration. + Please note that These pairlists will only refresh once per day. + + ```json + "pairlists": [ + { + "method": "VolumePairList", + "number_assets": 20, + "sort_key": "quoteVolume", + "min_value": 0, + "refresh_period": 86400, + "lookback_days": 1 + } + ], + ``` + More sophisticated approach can be used, by using `lookback_timeframe` for candle size and `lookback_period` which specifies the amount of candles. This example will build the volume pairs based on a rolling period of 3 days of 1h candles: ```json diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index c70e4a904..0ffc8a8c8 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -123,7 +123,7 @@ class VolumePairList(IPairList): filtered_tickers = [ v for k, v in tickers.items() if (self._exchange.get_pair_quote_currency(k) == self._stake_currency - and v[self._sort_key] is not None)] + and (self._use_range or v[self._sort_key] is not None))] pairlist = [s['symbol'] for s in filtered_tickers] pairlist = self.filter_pairlist(pairlist, tickers) From ebb0b8aa3fd03100d96e8feef4a4f9919fab60fe Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Sep 2021 08:36:57 +0200 Subject: [PATCH 417/519] Remove new-hyperopt command --- docs/utils.md | 35 ----- freqtrade/commands/__init__.py | 2 +- freqtrade/commands/arguments.py | 17 +-- freqtrade/commands/deploy_commands.py | 52 +------ freqtrade/templates/base_hyperopt.py.j2 | 137 ------------------ .../subtemplates/hyperopt_buy_guards_full.j2 | 8 - .../hyperopt_buy_guards_minimal.j2 | 2 - .../subtemplates/hyperopt_buy_space_full.j2 | 9 -- .../hyperopt_buy_space_minimal.j2 | 3 - .../subtemplates/hyperopt_sell_guards_full.j2 | 8 - .../hyperopt_sell_guards_minimal.j2 | 2 - .../subtemplates/hyperopt_sell_space_full.j2 | 11 -- .../hyperopt_sell_space_minimal.j2 | 5 - tests/commands/test_commands.py | 33 +---- 14 files changed, 7 insertions(+), 317 deletions(-) delete mode 100644 freqtrade/templates/base_hyperopt.py.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_buy_guards_full.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_buy_guards_minimal.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_buy_space_full.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_buy_space_minimal.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_sell_guards_full.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_sell_guards_minimal.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_sell_space_full.j2 delete mode 100644 freqtrade/templates/subtemplates/hyperopt_sell_space_minimal.j2 diff --git a/docs/utils.md b/docs/utils.md index 6395fb6f9..a1e219d6a 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -111,41 +111,6 @@ Using the advanced template (populates all optional functions and methods) freqtrade new-strategy --strategy AwesomeStrategy --template advanced ``` -## Create new hyperopt - -Creates a new hyperopt from a template similar to SampleHyperopt. -The file will be named inline with your class name, and will not overwrite existing files. - -Results will be located in `user_data/hyperopts/.py`. - -``` output -usage: freqtrade new-hyperopt [-h] [--userdir PATH] [--hyperopt NAME] - [--template {full,minimal,advanced}] - -optional arguments: - -h, --help show this help message and exit - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - --hyperopt NAME Specify hyperopt class name which will be used by the - bot. - --template {full,minimal,advanced} - Use a template which is either `minimal`, `full` - (containing multiple sample indicators) or `advanced`. - Default: `full`. -``` - -### Sample usage of new-hyperopt - -```bash -freqtrade new-hyperopt --hyperopt AwesomeHyperopt -``` - -With custom user directory - -```bash -freqtrade new-hyperopt --userdir ~/.freqtrade/ --hyperopt AwesomeHyperopt -``` - ## List Strategies and List Hyperopts Use the `list-strategies` subcommand to see all strategies in one particular directory and the `list-hyperopts` subcommand to list custom Hyperopts. diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index 04e46ee23..49b184a80 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -11,7 +11,7 @@ from freqtrade.commands.build_config_commands import start_new_config from freqtrade.commands.data_commands import (start_convert_data, start_download_data, start_list_data) from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui, - start_new_hyperopt, start_new_strategy) + start_new_strategy) from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show from freqtrade.commands.list_commands import (start_list_exchanges, start_list_hyperopts, start_list_markets, start_list_strategies, diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 899998310..9c6ce543f 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -55,8 +55,6 @@ ARGS_BUILD_CONFIG = ["config"] ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"] -ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"] - ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"] @@ -95,7 +93,7 @@ NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list "list-hyperopts", "hyperopt-list", "hyperopt-show", "plot-dataframe", "plot-profit", "show-trades"] -NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-hyperopt", "new-strategy"] +NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"] class Arguments: @@ -176,10 +174,9 @@ class Arguments: start_hyperopt_list, start_hyperopt_show, start_install_ui, start_list_data, start_list_exchanges, start_list_hyperopts, start_list_markets, start_list_strategies, - start_list_timeframes, start_new_config, start_new_hyperopt, - start_new_strategy, start_plot_dataframe, start_plot_profit, - start_show_trades, start_test_pairlist, start_trading, - start_webserver) + start_list_timeframes, start_new_config, start_new_strategy, + start_plot_dataframe, start_plot_profit, start_show_trades, + start_test_pairlist, start_trading, start_webserver) subparsers = self.parser.add_subparsers(dest='command', # Use custom message when no subhandler is added @@ -206,12 +203,6 @@ class Arguments: build_config_cmd.set_defaults(func=start_new_config) self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd) - # add new-hyperopt subcommand - build_hyperopt_cmd = subparsers.add_parser('new-hyperopt', - help="Create new hyperopt") - build_hyperopt_cmd.set_defaults(func=start_new_hyperopt) - self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd) - # add new-strategy subcommand build_strategy_cmd = subparsers.add_parser('new-strategy', help="Create new strategy") diff --git a/freqtrade/commands/deploy_commands.py b/freqtrade/commands/deploy_commands.py index c98335e0b..4f9e5bbad 100644 --- a/freqtrade/commands/deploy_commands.py +++ b/freqtrade/commands/deploy_commands.py @@ -7,7 +7,7 @@ import requests from freqtrade.configuration import setup_utils_configuration from freqtrade.configuration.directory_operations import copy_sample_files, create_userdata_dir -from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES +from freqtrade.constants import USERPATH_STRATEGIES from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.misc import render_template, render_template_with_fallback @@ -87,56 +87,6 @@ def start_new_strategy(args: Dict[str, Any]) -> None: raise OperationalException("`new-strategy` requires --strategy to be set.") -def deploy_new_hyperopt(hyperopt_name: str, hyperopt_path: Path, subtemplate: str) -> None: - """ - Deploys a new hyperopt template to hyperopt_path - """ - fallback = 'full' - buy_guards = render_template_with_fallback( - templatefile=f"subtemplates/hyperopt_buy_guards_{subtemplate}.j2", - templatefallbackfile=f"subtemplates/hyperopt_buy_guards_{fallback}.j2", - ) - sell_guards = render_template_with_fallback( - templatefile=f"subtemplates/hyperopt_sell_guards_{subtemplate}.j2", - templatefallbackfile=f"subtemplates/hyperopt_sell_guards_{fallback}.j2", - ) - buy_space = render_template_with_fallback( - templatefile=f"subtemplates/hyperopt_buy_space_{subtemplate}.j2", - templatefallbackfile=f"subtemplates/hyperopt_buy_space_{fallback}.j2", - ) - sell_space = render_template_with_fallback( - templatefile=f"subtemplates/hyperopt_sell_space_{subtemplate}.j2", - templatefallbackfile=f"subtemplates/hyperopt_sell_space_{fallback}.j2", - ) - - strategy_text = render_template(templatefile='base_hyperopt.py.j2', - arguments={"hyperopt": hyperopt_name, - "buy_guards": buy_guards, - "sell_guards": sell_guards, - "buy_space": buy_space, - "sell_space": sell_space, - }) - - logger.info(f"Writing hyperopt to `{hyperopt_path}`.") - hyperopt_path.write_text(strategy_text) - - -def start_new_hyperopt(args: Dict[str, Any]) -> None: - - config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) - - if 'hyperopt' in args and args['hyperopt']: - - new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args['hyperopt'] + '.py') - - if new_path.exists(): - raise OperationalException(f"`{new_path}` already exists. " - "Please choose another Hyperopt Name.") - deploy_new_hyperopt(args['hyperopt'], new_path, args['template']) - else: - raise OperationalException("`new-hyperopt` requires --hyperopt to be set.") - - def clean_ui_subdir(directory: Path): if directory.is_dir(): logger.info("Removing UI directory content.") diff --git a/freqtrade/templates/base_hyperopt.py.j2 b/freqtrade/templates/base_hyperopt.py.j2 deleted file mode 100644 index f6ca1477a..000000000 --- a/freqtrade/templates/base_hyperopt.py.j2 +++ /dev/null @@ -1,137 +0,0 @@ -# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement - -# --- Do not remove these libs --- -from functools import reduce -from typing import Any, Callable, Dict, List - -import numpy as np # noqa -import pandas as pd # noqa -from pandas import DataFrame -from skopt.space import Categorical, Dimension, Integer, Real # noqa - -from freqtrade.optimize.hyperopt_interface import IHyperOpt - -# -------------------------------- -# Add your lib to import here -import talib.abstract as ta # noqa -import freqtrade.vendor.qtpylib.indicators as qtpylib - - -class {{ hyperopt }}(IHyperOpt): - """ - This is a Hyperopt template to get you started. - - More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/ - - You should: - - Add any lib you need to build your hyperopt. - - You must keep: - - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - - The methods roi_space, generate_roi_table and stoploss_space are not required - and are provided by default. - However, you may override them if you need 'roi' and 'stoploss' spaces that - differ from the defaults offered by Freqtrade. - Sample implementation of these methods will be copied to `user_data/hyperopts` when - creating the user-data directory using `freqtrade create-userdir --userdir user_data`, - or is available online under the following URL: - https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py. - """ - - @staticmethod - def indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching buy strategy parameters. - """ - return [ - {{ buy_space | indent(12) }} - ] - - @staticmethod - def buy_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the buy strategy parameters to be used by Hyperopt. - """ - def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Buy strategy Hyperopt will build and use. - """ - conditions = [] - - # GUARDS AND TRENDS - {{ buy_guards | indent(12) }} - - # TRIGGERS - if 'trigger' in params: - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) - if params['trigger'] == 'sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] - )) - - # Check that the candle had volume - conditions.append(dataframe['volume'] > 0) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'buy'] = 1 - - return dataframe - - return populate_buy_trend - - @staticmethod - def sell_indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching sell strategy parameters. - """ - return [ - {{ sell_space | indent(12) }} - ] - - @staticmethod - def sell_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the sell strategy parameters to be used by Hyperopt. - """ - def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Sell strategy Hyperopt will build and use. - """ - conditions = [] - - # GUARDS AND TRENDS - {{ sell_guards | indent(12) }} - - # TRIGGERS - if 'sell-trigger' in params: - if params['sell-trigger'] == 'sell-bb_upper': - conditions.append(dataframe['close'] > dataframe['bb_upperband']) - if params['sell-trigger'] == 'sell-macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macdsignal'], dataframe['macd'] - )) - if params['sell-trigger'] == 'sell-sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['sar'], dataframe['close'] - )) - - # Check that the candle had volume - conditions.append(dataframe['volume'] > 0) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'sell'] = 1 - - return dataframe - - return populate_sell_trend - diff --git a/freqtrade/templates/subtemplates/hyperopt_buy_guards_full.j2 b/freqtrade/templates/subtemplates/hyperopt_buy_guards_full.j2 deleted file mode 100644 index 5b967f4ed..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_buy_guards_full.j2 +++ /dev/null @@ -1,8 +0,0 @@ -if params.get('mfi-enabled'): - conditions.append(dataframe['mfi'] < params['mfi-value']) -if params.get('fastd-enabled'): - conditions.append(dataframe['fastd'] < params['fastd-value']) -if params.get('adx-enabled'): - conditions.append(dataframe['adx'] > params['adx-value']) -if params.get('rsi-enabled'): - conditions.append(dataframe['rsi'] < params['rsi-value']) diff --git a/freqtrade/templates/subtemplates/hyperopt_buy_guards_minimal.j2 b/freqtrade/templates/subtemplates/hyperopt_buy_guards_minimal.j2 deleted file mode 100644 index 5e1022f59..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_buy_guards_minimal.j2 +++ /dev/null @@ -1,2 +0,0 @@ -if params.get('rsi-enabled'): - conditions.append(dataframe['rsi'] < params['rsi-value']) diff --git a/freqtrade/templates/subtemplates/hyperopt_buy_space_full.j2 b/freqtrade/templates/subtemplates/hyperopt_buy_space_full.j2 deleted file mode 100644 index 29bafbd93..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_buy_space_full.j2 +++ /dev/null @@ -1,9 +0,0 @@ -Integer(10, 25, name='mfi-value'), -Integer(15, 45, name='fastd-value'), -Integer(20, 50, name='adx-value'), -Integer(20, 40, name='rsi-value'), -Categorical([True, False], name='mfi-enabled'), -Categorical([True, False], name='fastd-enabled'), -Categorical([True, False], name='adx-enabled'), -Categorical([True, False], name='rsi-enabled'), -Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') diff --git a/freqtrade/templates/subtemplates/hyperopt_buy_space_minimal.j2 b/freqtrade/templates/subtemplates/hyperopt_buy_space_minimal.j2 deleted file mode 100644 index 5ddf537fb..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_buy_space_minimal.j2 +++ /dev/null @@ -1,3 +0,0 @@ -Integer(20, 40, name='rsi-value'), -Categorical([True, False], name='rsi-enabled'), -Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') diff --git a/freqtrade/templates/subtemplates/hyperopt_sell_guards_full.j2 b/freqtrade/templates/subtemplates/hyperopt_sell_guards_full.j2 deleted file mode 100644 index bd7b499f4..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_sell_guards_full.j2 +++ /dev/null @@ -1,8 +0,0 @@ -if params.get('sell-mfi-enabled'): - conditions.append(dataframe['mfi'] > params['sell-mfi-value']) -if params.get('sell-fastd-enabled'): - conditions.append(dataframe['fastd'] > params['sell-fastd-value']) -if params.get('sell-adx-enabled'): - conditions.append(dataframe['adx'] < params['sell-adx-value']) -if params.get('sell-rsi-enabled'): - conditions.append(dataframe['rsi'] > params['sell-rsi-value']) diff --git a/freqtrade/templates/subtemplates/hyperopt_sell_guards_minimal.j2 b/freqtrade/templates/subtemplates/hyperopt_sell_guards_minimal.j2 deleted file mode 100644 index 8b4adebf6..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_sell_guards_minimal.j2 +++ /dev/null @@ -1,2 +0,0 @@ -if params.get('sell-rsi-enabled'): - conditions.append(dataframe['rsi'] > params['sell-rsi-value']) diff --git a/freqtrade/templates/subtemplates/hyperopt_sell_space_full.j2 b/freqtrade/templates/subtemplates/hyperopt_sell_space_full.j2 deleted file mode 100644 index 46469d532..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_sell_space_full.j2 +++ /dev/null @@ -1,11 +0,0 @@ -Integer(75, 100, name='sell-mfi-value'), -Integer(50, 100, name='sell-fastd-value'), -Integer(50, 100, name='sell-adx-value'), -Integer(60, 100, name='sell-rsi-value'), -Categorical([True, False], name='sell-mfi-enabled'), -Categorical([True, False], name='sell-fastd-enabled'), -Categorical([True, False], name='sell-adx-enabled'), -Categorical([True, False], name='sell-rsi-enabled'), -Categorical(['sell-bb_upper', - 'sell-macd_cross_signal', - 'sell-sar_reversal'], name='sell-trigger') diff --git a/freqtrade/templates/subtemplates/hyperopt_sell_space_minimal.j2 b/freqtrade/templates/subtemplates/hyperopt_sell_space_minimal.j2 deleted file mode 100644 index dfb110543..000000000 --- a/freqtrade/templates/subtemplates/hyperopt_sell_space_minimal.j2 +++ /dev/null @@ -1,5 +0,0 @@ -Integer(60, 100, name='sell-rsi-value'), -Categorical([True, False], name='sell-rsi-enabled'), -Categorical(['sell-bb_upper', - 'sell-macd_cross_signal', - 'sell-sar_reversal'], name='sell-trigger') diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 1da9e5100..8db2020b5 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -12,7 +12,7 @@ from freqtrade.commands import (start_convert_data, start_create_userdir, start_ start_hyperopt_list, start_hyperopt_show, start_install_ui, start_list_data, start_list_exchanges, start_list_hyperopts, start_list_markets, start_list_strategies, start_list_timeframes, - start_new_hyperopt, start_new_strategy, start_show_trades, + start_new_strategy, start_show_trades, start_test_pairlist, start_trading, start_webserver) from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, get_ui_download_url, read_ui_version) @@ -519,37 +519,6 @@ def test_start_new_strategy_no_arg(mocker, caplog): start_new_strategy(get_args(args)) -def test_start_new_hyperopt(mocker, caplog): - wt_mock = mocker.patch.object(Path, "write_text", MagicMock()) - mocker.patch.object(Path, "exists", MagicMock(return_value=False)) - - args = [ - "new-hyperopt", - "--hyperopt", - "CoolNewhyperopt" - ] - start_new_hyperopt(get_args(args)) - - assert wt_mock.call_count == 1 - assert "CoolNewhyperopt" in wt_mock.call_args_list[0][0][0] - assert log_has_re("Writing hyperopt to .*", caplog) - - mocker.patch('freqtrade.commands.deploy_commands.setup_utils_configuration') - mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - with pytest.raises(OperationalException, - match=r".* already exists. Please choose another Hyperopt Name\."): - start_new_hyperopt(get_args(args)) - - -def test_start_new_hyperopt_no_arg(mocker): - args = [ - "new-hyperopt", - ] - with pytest.raises(OperationalException, - match="`new-hyperopt` requires --hyperopt to be set."): - start_new_hyperopt(get_args(args)) - - def test_start_install_ui(mocker): clean_mock = mocker.patch('freqtrade.commands.deploy_commands.clean_ui_subdir') get_url_mock = mocker.patch('freqtrade.commands.deploy_commands.get_ui_download_url', From dad4a49e81c3a01eb98166b6db20fb08bb1a3a9a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Sep 2021 09:06:57 +0200 Subject: [PATCH 418/519] Remove legacy hyperopt interface from hyperopt.py --- freqtrade/commands/cli_options.py | 2 +- freqtrade/optimize/hyperopt.py | 64 ++--- freqtrade/optimize/hyperopt_auto.py | 21 +- freqtrade/optimize/hyperopt_interface.py | 14 +- freqtrade/resolvers/hyperopt_resolver.py | 38 --- freqtrade/templates/sample_hyperopt.py | 174 ----------- .../templates/sample_hyperopt_advanced.py | 269 ------------------ 7 files changed, 25 insertions(+), 557 deletions(-) delete mode 100644 freqtrade/templates/sample_hyperopt.py delete mode 100644 freqtrade/templates/sample_hyperopt_advanced.py diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index cf7cb804c..a1790cb9a 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -209,7 +209,7 @@ AVAILABLE_CLI_OPTIONS = { ), "hyperopt_path": Arg( '--hyperopt-path', - help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.', + help='Specify additional lookup path for Hyperopt Loss functions.', metavar='PATH', ), "epochs": Arg( diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index e0b35df32..d37c68769 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -22,6 +22,7 @@ from pandas import DataFrame from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN from freqtrade.data.converter import trim_dataframes from freqtrade.data.history import get_timerange +from freqtrade.exceptions import OperationalException from freqtrade.misc import deep_merge_dicts, file_dump_json, plural from freqtrade.optimize.backtesting import Backtesting # Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules @@ -30,7 +31,7 @@ from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401 from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401 from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer from freqtrade.optimize.optimize_reports import generate_strategy_stats -from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver, HyperOptResolver +from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver # Suppress scikit-learn FutureWarnings from skopt @@ -80,8 +81,9 @@ class Hyperopt: self.custom_hyperopt = HyperOptAuto(self.config) self.auto_hyperopt = True else: - self.custom_hyperopt = HyperOptResolver.load_hyperopt(self.config) - self.auto_hyperopt = False + raise OperationalException( + "Using seperate Hyperopt files has been removed in 2021.9. Please convert " + "your existing Hyperopt file to the new Hyperoptable strategy interface") self.backtesting._set_strategy(self.backtesting.strategylist[0]) self.custom_hyperopt.strategy = self.backtesting.strategy @@ -103,31 +105,6 @@ class Hyperopt: self.num_epochs_saved = 0 self.current_best_epoch: Optional[Dict[str, Any]] = None - if not self.auto_hyperopt: - # Populate "fallback" functions here - # (hasattr is slow so should not be run during "regular" operations) - if hasattr(self.custom_hyperopt, 'populate_indicators'): - logger.warning( - "DEPRECATED: Using `populate_indicators()` in the hyperopt file is deprecated. " - "Please move these methods to your strategy." - ) - self.backtesting.strategy.populate_indicators = ( # type: ignore - self.custom_hyperopt.populate_indicators) # type: ignore - if hasattr(self.custom_hyperopt, 'populate_buy_trend'): - logger.warning( - "DEPRECATED: Using `populate_buy_trend()` in the hyperopt file is deprecated. " - "Please move these methods to your strategy." - ) - self.backtesting.strategy.populate_buy_trend = ( # type: ignore - self.custom_hyperopt.populate_buy_trend) # type: ignore - if hasattr(self.custom_hyperopt, 'populate_sell_trend'): - logger.warning( - "DEPRECATED: Using `populate_sell_trend()` in the hyperopt file is deprecated. " - "Please move these methods to your strategy." - ) - self.backtesting.strategy.populate_sell_trend = ( # type: ignore - self.custom_hyperopt.populate_sell_trend) # type: ignore - # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set if self.config.get('use_max_market_positions', True): self.max_open_trades = self.config['max_open_trades'] @@ -256,7 +233,7 @@ class Hyperopt: """ Assign the dimensions in the hyperoptimization space. """ - if self.auto_hyperopt and HyperoptTools.has_space(self.config, 'protection'): + if HyperoptTools.has_space(self.config, 'protection'): # Protections can only be optimized when using the Parameter interface logger.debug("Hyperopt has 'protection' space") # Enable Protections if protection space is selected. @@ -285,6 +262,15 @@ class Hyperopt: self.dimensions = (self.buy_space + self.sell_space + self.protection_space + self.roi_space + self.stoploss_space + self.trailing_space) + def assign_params(self, params_dict: Dict, category: str) -> None: + """ + Assign hyperoptable parameters + """ + for attr_name, attr in self.backtesting.strategy.enumerate_parameters(category): + if attr.optimize: + # noinspection PyProtectedMember + attr.value = params_dict[attr_name] + def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict: """ Used Optimize function. @@ -296,18 +282,13 @@ class Hyperopt: # Apply parameters if HyperoptTools.has_space(self.config, 'buy'): - self.backtesting.strategy.advise_buy = ( # type: ignore - self.custom_hyperopt.buy_strategy_generator(params_dict)) + self.assign_params(params_dict, 'buy') if HyperoptTools.has_space(self.config, 'sell'): - self.backtesting.strategy.advise_sell = ( # type: ignore - self.custom_hyperopt.sell_strategy_generator(params_dict)) + self.assign_params(params_dict, 'sell') if HyperoptTools.has_space(self.config, 'protection'): - for attr_name, attr in self.backtesting.strategy.enumerate_parameters('protection'): - if attr.optimize: - # noinspection PyProtectedMember - attr.value = params_dict[attr_name] + self.assign_params(params_dict, 'protection') if HyperoptTools.has_space(self.config, 'roi'): self.backtesting.strategy.minimal_roi = ( # type: ignore @@ -517,11 +498,10 @@ class Hyperopt: f"saved to '{self.results_file}'.") if self.current_best_epoch: - if self.auto_hyperopt: - HyperoptTools.try_export_params( - self.config, - self.backtesting.strategy.get_strategy_name(), - self.current_best_epoch) + HyperoptTools.try_export_params( + self.config, + self.backtesting.strategy.get_strategy_name(), + self.current_best_epoch) HyperoptTools.show_epoch_details(self.current_best_epoch, self.total_epochs, self.print_json) diff --git a/freqtrade/optimize/hyperopt_auto.py b/freqtrade/optimize/hyperopt_auto.py index 43e92d9c6..022f04a84 100644 --- a/freqtrade/optimize/hyperopt_auto.py +++ b/freqtrade/optimize/hyperopt_auto.py @@ -22,26 +22,6 @@ class HyperOptAuto(IHyperOpt): sell_indicator_space methods, but other hyperopt methods can be overridden as well. """ - def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable: - def populate_buy_trend(dataframe: DataFrame, metadata: dict): - for attr_name, attr in self.strategy.enumerate_parameters('buy'): - if attr.optimize: - # noinspection PyProtectedMember - attr.value = params[attr_name] - return self.strategy.populate_buy_trend(dataframe, metadata) - - return populate_buy_trend - - def sell_strategy_generator(self, params: Dict[str, Any]) -> Callable: - def populate_sell_trend(dataframe: DataFrame, metadata: dict): - for attr_name, attr in self.strategy.enumerate_parameters('sell'): - if attr.optimize: - # noinspection PyProtectedMember - attr.value = params[attr_name] - return self.strategy.populate_sell_trend(dataframe, metadata) - - return populate_sell_trend - def _get_func(self, name) -> Callable: """ Return a function defined in Strategy.HyperOpt class, or one defined in super() class. @@ -61,6 +41,7 @@ class HyperOptAuto(IHyperOpt): yield attr.get_space(attr_name) def _get_indicator_space(self, category, fallback_method_name): + # TODO: is this necessary, or can we call "generate_space" directly? indicator_space = list(self._generate_indicator_space(category)) if len(indicator_space) > 0: return indicator_space diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 500798627..814260f5e 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -5,7 +5,7 @@ This module defines the interface to apply for hyperopt import logging import math from abc import ABC -from typing import Any, Callable, Dict, List +from typing import Dict, List from skopt.space import Categorical, Dimension, Integer @@ -45,18 +45,6 @@ class IHyperOpt(ABC): IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED IHyperOpt.timeframe = str(config['timeframe']) - def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable: - """ - Create a buy strategy generator. - """ - raise OperationalException(_format_exception_message('buy_strategy_generator', 'buy')) - - def sell_strategy_generator(self, params: Dict[str, Any]) -> Callable: - """ - Create a sell strategy generator. - """ - raise OperationalException(_format_exception_message('sell_strategy_generator', 'sell')) - def protection_space(self) -> List[Dimension]: """ Create a protection space. diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index 8327a4d13..6f0263e93 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -9,7 +9,6 @@ from typing import Dict from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS from freqtrade.exceptions import OperationalException -from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.resolvers import IResolver @@ -17,43 +16,6 @@ from freqtrade.resolvers import IResolver logger = logging.getLogger(__name__) -class HyperOptResolver(IResolver): - """ - This class contains all the logic to load custom hyperopt class - """ - object_type = IHyperOpt - object_type_str = "Hyperopt" - user_subdir = USERPATH_HYPEROPTS - initial_search_path = None - - @staticmethod - def load_hyperopt(config: Dict) -> IHyperOpt: - """ - Load the custom hyperopt class from config parameter - :param config: configuration dictionary - """ - if not config.get('hyperopt'): - raise OperationalException("No Hyperopt set. Please use `--hyperopt` to specify " - "the Hyperopt class to use.") - - hyperopt_name = config['hyperopt'] - - hyperopt = HyperOptResolver.load_object(hyperopt_name, config, - kwargs={'config': config}, - extra_dir=config.get('hyperopt_path')) - - if not hasattr(hyperopt, 'populate_indicators'): - logger.info("Hyperopt class does not provide populate_indicators() method. " - "Using populate_indicators from the strategy.") - if not hasattr(hyperopt, 'populate_buy_trend'): - logger.info("Hyperopt class does not provide populate_buy_trend() method. " - "Using populate_buy_trend from the strategy.") - if not hasattr(hyperopt, 'populate_sell_trend'): - logger.info("Hyperopt class does not provide populate_sell_trend() method. " - "Using populate_sell_trend from the strategy.") - return hyperopt - - class HyperOptLossResolver(IResolver): """ This class contains all the logic to load custom hyperopt loss class diff --git a/freqtrade/templates/sample_hyperopt.py b/freqtrade/templates/sample_hyperopt.py deleted file mode 100644 index ed1af7718..000000000 --- a/freqtrade/templates/sample_hyperopt.py +++ /dev/null @@ -1,174 +0,0 @@ -# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement -# isort: skip_file - -# --- Do not remove these libs --- -from functools import reduce -from typing import Any, Callable, Dict, List - -import numpy as np # noqa -import pandas as pd # noqa -from pandas import DataFrame -from skopt.space import Categorical, Dimension, Integer, Real # noqa - -from freqtrade.optimize.hyperopt_interface import IHyperOpt - -# -------------------------------- -# Add your lib to import here -import talib.abstract as ta # noqa -import freqtrade.vendor.qtpylib.indicators as qtpylib - - -class SampleHyperOpt(IHyperOpt): - """ - This is a sample Hyperopt to inspire you. - - More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/ - - You should: - - Rename the class name to some unique name. - - Add any methods you want to build your hyperopt. - - Add any lib you need to build your hyperopt. - - An easier way to get a new hyperopt file is by using - `freqtrade new-hyperopt --hyperopt MyCoolHyperopt`. - - You must keep: - - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - - The methods roi_space, generate_roi_table and stoploss_space are not required - and are provided by default. - However, you may override them if you need 'roi' and 'stoploss' spaces that - differ from the defaults offered by Freqtrade. - Sample implementation of these methods will be copied to `user_data/hyperopts` when - creating the user-data directory using `freqtrade create-userdir --userdir user_data`, - or is available online under the following URL: - https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py. - """ - - @staticmethod - def indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching buy strategy parameters. - """ - return [ - Integer(10, 25, name='mfi-value'), - Integer(15, 45, name='fastd-value'), - Integer(20, 50, name='adx-value'), - Integer(20, 40, name='rsi-value'), - Categorical([True, False], name='mfi-enabled'), - Categorical([True, False], name='fastd-enabled'), - Categorical([True, False], name='adx-enabled'), - Categorical([True, False], name='rsi-enabled'), - Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') - ] - - @staticmethod - def buy_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the buy strategy parameters to be used by Hyperopt. - """ - def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Buy strategy Hyperopt will build and use. - """ - conditions = [] - - # GUARDS AND TRENDS - if 'mfi-enabled' in params and params['mfi-enabled']: - conditions.append(dataframe['mfi'] < params['mfi-value']) - if 'fastd-enabled' in params and params['fastd-enabled']: - conditions.append(dataframe['fastd'] < params['fastd-value']) - if 'adx-enabled' in params and params['adx-enabled']: - conditions.append(dataframe['adx'] > params['adx-value']) - if 'rsi-enabled' in params and params['rsi-enabled']: - conditions.append(dataframe['rsi'] < params['rsi-value']) - - # TRIGGERS - if 'trigger' in params: - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) - if params['trigger'] == 'sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] - )) - - # Check that volume is not 0 - conditions.append(dataframe['volume'] > 0) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'buy'] = 1 - - return dataframe - - return populate_buy_trend - - @staticmethod - def sell_indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching sell strategy parameters. - """ - return [ - Integer(75, 100, name='sell-mfi-value'), - Integer(50, 100, name='sell-fastd-value'), - Integer(50, 100, name='sell-adx-value'), - Integer(60, 100, name='sell-rsi-value'), - Categorical([True, False], name='sell-mfi-enabled'), - Categorical([True, False], name='sell-fastd-enabled'), - Categorical([True, False], name='sell-adx-enabled'), - Categorical([True, False], name='sell-rsi-enabled'), - Categorical(['sell-bb_upper', - 'sell-macd_cross_signal', - 'sell-sar_reversal'], name='sell-trigger') - ] - - @staticmethod - def sell_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the sell strategy parameters to be used by Hyperopt. - """ - def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Sell strategy Hyperopt will build and use. - """ - conditions = [] - - # GUARDS AND TRENDS - if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: - conditions.append(dataframe['mfi'] > params['sell-mfi-value']) - if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']: - conditions.append(dataframe['fastd'] > params['sell-fastd-value']) - if 'sell-adx-enabled' in params and params['sell-adx-enabled']: - conditions.append(dataframe['adx'] < params['sell-adx-value']) - if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']: - conditions.append(dataframe['rsi'] > params['sell-rsi-value']) - - # TRIGGERS - if 'sell-trigger' in params: - if params['sell-trigger'] == 'sell-bb_upper': - conditions.append(dataframe['close'] > dataframe['bb_upperband']) - if params['sell-trigger'] == 'sell-macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macdsignal'], dataframe['macd'] - )) - if params['sell-trigger'] == 'sell-sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['sar'], dataframe['close'] - )) - - # Check that volume is not 0 - conditions.append(dataframe['volume'] > 0) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'sell'] = 1 - - return dataframe - - return populate_sell_trend diff --git a/freqtrade/templates/sample_hyperopt_advanced.py b/freqtrade/templates/sample_hyperopt_advanced.py deleted file mode 100644 index cc13b6ba3..000000000 --- a/freqtrade/templates/sample_hyperopt_advanced.py +++ /dev/null @@ -1,269 +0,0 @@ -# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement -# isort: skip_file -# --- Do not remove these libs --- -from functools import reduce -from typing import Any, Callable, Dict, List - -import numpy as np # noqa -import pandas as pd # noqa -from pandas import DataFrame -from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal, Real # noqa - -from freqtrade.optimize.hyperopt_interface import IHyperOpt - -# -------------------------------- -# Add your lib to import here -import talib.abstract as ta # noqa -import freqtrade.vendor.qtpylib.indicators as qtpylib - - -class AdvancedSampleHyperOpt(IHyperOpt): - """ - This is a sample hyperopt to inspire you. - Feel free to customize it. - - More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/ - - You should: - - Rename the class name to some unique name. - - Add any methods you want to build your hyperopt. - - Add any lib you need to build your hyperopt. - - You must keep: - - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - - The methods roi_space, generate_roi_table and stoploss_space are not required - and are provided by default. - However, you may override them if you need the - 'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade. - - This sample illustrates how to override these methods. - """ - @staticmethod - def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - This method can also be loaded from the strategy, if it doesn't exist in the hyperopt class. - """ - dataframe['adx'] = ta.ADX(dataframe) - macd = ta.MACD(dataframe) - dataframe['macd'] = macd['macd'] - dataframe['macdsignal'] = macd['macdsignal'] - dataframe['mfi'] = ta.MFI(dataframe) - dataframe['rsi'] = ta.RSI(dataframe) - stoch_fast = ta.STOCHF(dataframe) - dataframe['fastd'] = stoch_fast['fastd'] - dataframe['minus_di'] = ta.MINUS_DI(dataframe) - # Bollinger bands - bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) - dataframe['bb_lowerband'] = bollinger['lower'] - dataframe['bb_upperband'] = bollinger['upper'] - dataframe['sar'] = ta.SAR(dataframe) - return dataframe - - @staticmethod - def indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching buy strategy parameters. - """ - return [ - Integer(10, 25, name='mfi-value'), - Integer(15, 45, name='fastd-value'), - Integer(20, 50, name='adx-value'), - Integer(20, 40, name='rsi-value'), - Categorical([True, False], name='mfi-enabled'), - Categorical([True, False], name='fastd-enabled'), - Categorical([True, False], name='adx-enabled'), - Categorical([True, False], name='rsi-enabled'), - Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') - ] - - @staticmethod - def buy_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the buy strategy parameters to be used by hyperopt - """ - def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Buy strategy Hyperopt will build and use - """ - conditions = [] - # GUARDS AND TRENDS - if 'mfi-enabled' in params and params['mfi-enabled']: - conditions.append(dataframe['mfi'] < params['mfi-value']) - if 'fastd-enabled' in params and params['fastd-enabled']: - conditions.append(dataframe['fastd'] < params['fastd-value']) - if 'adx-enabled' in params and params['adx-enabled']: - conditions.append(dataframe['adx'] > params['adx-value']) - if 'rsi-enabled' in params and params['rsi-enabled']: - conditions.append(dataframe['rsi'] < params['rsi-value']) - - # TRIGGERS - if 'trigger' in params: - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) - if params['trigger'] == 'sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] - )) - - # Check that volume is not 0 - conditions.append(dataframe['volume'] > 0) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'buy'] = 1 - - return dataframe - - return populate_buy_trend - - @staticmethod - def sell_indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching sell strategy parameters. - """ - return [ - Integer(75, 100, name='sell-mfi-value'), - Integer(50, 100, name='sell-fastd-value'), - Integer(50, 100, name='sell-adx-value'), - Integer(60, 100, name='sell-rsi-value'), - Categorical([True, False], name='sell-mfi-enabled'), - Categorical([True, False], name='sell-fastd-enabled'), - Categorical([True, False], name='sell-adx-enabled'), - Categorical([True, False], name='sell-rsi-enabled'), - Categorical(['sell-bb_upper', - 'sell-macd_cross_signal', - 'sell-sar_reversal'], name='sell-trigger') - ] - - @staticmethod - def sell_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the sell strategy parameters to be used by hyperopt - """ - def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Sell strategy Hyperopt will build and use - """ - # print(params) - conditions = [] - # GUARDS AND TRENDS - if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: - conditions.append(dataframe['mfi'] > params['sell-mfi-value']) - if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']: - conditions.append(dataframe['fastd'] > params['sell-fastd-value']) - if 'sell-adx-enabled' in params and params['sell-adx-enabled']: - conditions.append(dataframe['adx'] < params['sell-adx-value']) - if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']: - conditions.append(dataframe['rsi'] > params['sell-rsi-value']) - - # TRIGGERS - if 'sell-trigger' in params: - if params['sell-trigger'] == 'sell-bb_upper': - conditions.append(dataframe['close'] > dataframe['bb_upperband']) - if params['sell-trigger'] == 'sell-macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macdsignal'], dataframe['macd'] - )) - if params['sell-trigger'] == 'sell-sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['sar'], dataframe['close'] - )) - - # Check that volume is not 0 - conditions.append(dataframe['volume'] > 0) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'sell'] = 1 - - return dataframe - - return populate_sell_trend - - @staticmethod - def generate_roi_table(params: Dict) -> Dict[int, float]: - """ - Generate the ROI table that will be used by Hyperopt - - This implementation generates the default legacy Freqtrade ROI tables. - - Change it if you need different number of steps in the generated - ROI tables or other structure of the ROI tables. - - Please keep it aligned with parameters in the 'roi' optimization - hyperspace defined by the roi_space method. - """ - roi_table = {} - roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] - roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2'] - roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1'] - roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0 - - return roi_table - - @staticmethod - def roi_space() -> List[Dimension]: - """ - Values to search for each ROI steps - - Override it if you need some different ranges for the parameters in the - 'roi' optimization hyperspace. - - Please keep it aligned with the implementation of the - generate_roi_table method. - """ - return [ - Integer(10, 120, name='roi_t1'), - Integer(10, 60, name='roi_t2'), - Integer(10, 40, name='roi_t3'), - SKDecimal(0.01, 0.04, decimals=3, name='roi_p1'), - SKDecimal(0.01, 0.07, decimals=3, name='roi_p2'), - SKDecimal(0.01, 0.20, decimals=3, name='roi_p3'), - ] - - @staticmethod - def stoploss_space() -> List[Dimension]: - """ - Stoploss Value to search - - Override it if you need some different range for the parameter in the - 'stoploss' optimization hyperspace. - """ - return [ - SKDecimal(-0.35, -0.02, decimals=3, name='stoploss'), - ] - - @staticmethod - def trailing_space() -> List[Dimension]: - """ - Create a trailing stoploss space. - - You may override it in your custom Hyperopt class. - """ - return [ - # It was decided to always set trailing_stop is to True if the 'trailing' hyperspace - # is used. Otherwise hyperopt will vary other parameters that won't have effect if - # trailing_stop is set False. - # This parameter is included into the hyperspace dimensions rather than assigning - # it explicitly in the code in order to have it printed in the results along with - # other 'trailing' hyperspace parameters. - Categorical([True], name='trailing_stop'), - - SKDecimal(0.01, 0.35, decimals=3, name='trailing_stop_positive'), - - # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', - # so this intermediate parameter is used as the value of the difference between - # them. The value of the 'trailing_stop_positive_offset' is constructed in the - # generate_trailing_params() method. - # This is similar to the hyperspace dimensions used for constructing the ROI tables. - SKDecimal(0.001, 0.1, decimals=3, name='trailing_stop_positive_offset_p1'), - - Categorical([True, False], name='trailing_only_offset_is_reached'), - ] From fd6bf591f8abb888e3ad340bcc9c368096ae92c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Sep 2021 17:11:02 +0200 Subject: [PATCH 419/519] Update some tests to remove explicit hyperopt interface --- freqtrade/optimize/hyperopt.py | 3 +- freqtrade/optimize/hyperopt_auto.py | 22 ++- freqtrade/optimize/hyperopt_interface.py | 27 --- tests/commands/test_commands.py | 43 ++--- tests/optimize/conftest.py | 2 +- tests/optimize/test_hyperopt.py | 203 ++++------------------- 6 files changed, 63 insertions(+), 237 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index d37c68769..14b155546 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -79,10 +79,9 @@ class Hyperopt: if not self.config.get('hyperopt'): self.custom_hyperopt = HyperOptAuto(self.config) - self.auto_hyperopt = True else: raise OperationalException( - "Using seperate Hyperopt files has been removed in 2021.9. Please convert " + "Using separate Hyperopt files has been removed in 2021.9. Please convert " "your existing Hyperopt file to the new Hyperoptable strategy interface") self.backtesting._set_strategy(self.backtesting.strategylist[0]) diff --git a/freqtrade/optimize/hyperopt_auto.py b/freqtrade/optimize/hyperopt_auto.py index 022f04a84..1f11cec80 100644 --- a/freqtrade/optimize/hyperopt_auto.py +++ b/freqtrade/optimize/hyperopt_auto.py @@ -4,9 +4,9 @@ This module implements a convenience auto-hyperopt class, which can be used toge that implement IHyperStrategy interface. """ from contextlib import suppress -from typing import Any, Callable, Dict, List +from typing import Callable, Dict, List -from pandas import DataFrame +from freqtrade.exceptions import OperationalException with suppress(ImportError): @@ -15,6 +15,14 @@ with suppress(ImportError): from freqtrade.optimize.hyperopt_interface import IHyperOpt +def _format_exception_message(space: str) -> str: + raise OperationalException( + f"The '{space}' space is included into the hyperoptimization " + f"but no parameter for this space was not found in your Strategy. " + f"Please make sure to have parameters for this space enabled for optimization " + f"or remove the '{space}' space from hyperoptimization.") + + class HyperOptAuto(IHyperOpt): """ This class delegates functionality to Strategy(IHyperStrategy) and Strategy.HyperOpt classes. @@ -40,22 +48,22 @@ class HyperOptAuto(IHyperOpt): if attr.optimize: yield attr.get_space(attr_name) - def _get_indicator_space(self, category, fallback_method_name): + def _get_indicator_space(self, category): # TODO: is this necessary, or can we call "generate_space" directly? indicator_space = list(self._generate_indicator_space(category)) if len(indicator_space) > 0: return indicator_space else: - return self._get_func(fallback_method_name)() + _format_exception_message(category) def indicator_space(self) -> List['Dimension']: - return self._get_indicator_space('buy', 'indicator_space') + return self._get_indicator_space('buy') def sell_indicator_space(self) -> List['Dimension']: - return self._get_indicator_space('sell', 'sell_indicator_space') + return self._get_indicator_space('sell') def protection_space(self) -> List['Dimension']: - return self._get_indicator_space('protection', 'protection_space') + return self._get_indicator_space('protection') def generate_roi_table(self, params: Dict) -> Dict[int, float]: return self._get_func('generate_roi_table')(params) diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 814260f5e..8fb40f557 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -9,7 +9,6 @@ from typing import Dict, List from skopt.space import Categorical, Dimension, Integer -from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes from freqtrade.misc import round_dict from freqtrade.optimize.space import SKDecimal @@ -19,13 +18,6 @@ from freqtrade.strategy import IStrategy logger = logging.getLogger(__name__) -def _format_exception_message(method: str, space: str) -> str: - return (f"The '{space}' space is included into the hyperoptimization " - f"but {method}() method is not found in your " - f"custom Hyperopt class. You should either implement this " - f"method or remove the '{space}' space from hyperoptimization.") - - class IHyperOpt(ABC): """ Interface for freqtrade hyperopt @@ -45,25 +37,6 @@ class IHyperOpt(ABC): IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED IHyperOpt.timeframe = str(config['timeframe']) - def protection_space(self) -> List[Dimension]: - """ - Create a protection space. - Only supported by the Parameter interface. - """ - raise OperationalException(_format_exception_message('indicator_space', 'protection')) - - def indicator_space(self) -> List[Dimension]: - """ - Create an indicator space. - """ - raise OperationalException(_format_exception_message('indicator_space', 'buy')) - - def sell_indicator_space(self) -> List[Dimension]: - """ - Create a sell indicator space. - """ - raise OperationalException(_format_exception_message('sell_indicator_space', 'sell')) - def generate_roi_table(self, params: Dict) -> Dict[int, float]: """ Create a ROI table. diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 8db2020b5..a6a87d245 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -10,10 +10,10 @@ import pytest from freqtrade.commands import (start_convert_data, start_create_userdir, start_download_data, start_hyperopt_list, start_hyperopt_show, start_install_ui, - start_list_data, start_list_exchanges, start_list_hyperopts, - start_list_markets, start_list_strategies, start_list_timeframes, - start_new_strategy, start_show_trades, - start_test_pairlist, start_trading, start_webserver) + start_list_data, start_list_exchanges, start_list_markets, + start_list_strategies, start_list_timeframes, start_new_strategy, + start_show_trades, start_test_pairlist, start_trading, + start_webserver) from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, get_ui_download_url, read_ui_version) from freqtrade.configuration import setup_utils_configuration @@ -793,37 +793,20 @@ def test_start_list_strategies(mocker, caplog, capsys): assert "legacy_strategy_v1.py" in captured.out assert "StrategyTestV2" in captured.out - -def test_start_list_hyperopts(mocker, caplog, capsys): - + # Test color output args = [ - "list-hyperopts", - "--hyperopt-path", - str(Path(__file__).parent.parent / "optimize" / "hyperopts"), - "-1" + "list-strategies", + "--strategy-path", + str(Path(__file__).parent.parent / "strategy" / "strats"), ] pargs = get_args(args) # pargs['config'] = None - start_list_hyperopts(pargs) + start_list_strategies(pargs) captured = capsys.readouterr() - assert "TestHyperoptLegacy" not in captured.out - assert "legacy_hyperopt.py" not in captured.out - assert "HyperoptTestSepFile" in captured.out - assert "test_hyperopt.py" not in captured.out - - # Test regular output - args = [ - "list-hyperopts", - "--hyperopt-path", - str(Path(__file__).parent.parent / "optimize" / "hyperopts"), - ] - pargs = get_args(args) - # pargs['config'] = None - start_list_hyperopts(pargs) - captured = capsys.readouterr() - assert "TestHyperoptLegacy" not in captured.out - assert "legacy_hyperopt.py" not in captured.out - assert "HyperoptTestSepFile" in captured.out + assert "TestStrategyLegacyV1" in captured.out + assert "legacy_strategy_v1.py" in captured.out + assert "StrategyTestV2" in captured.out + assert "LOAD FAILED" in captured.out def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 95c9fef97..5c5171c3a 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -16,7 +16,7 @@ def hyperopt_conf(default_conf): hyperconf.update({ 'datadir': Path(default_conf['datadir']), 'runmode': RunMode.HYPEROPT, - 'hyperopt': 'HyperoptTestSepFile', + 'strategy': 'HyperoptableStrategy', 'hyperopt_loss': 'ShortTradeDurHyperOptLoss', 'hyperopt_path': str(Path(__file__).parent / 'hyperopts'), 'epochs': 1, diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 565d6077a..498b6e588 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -17,7 +17,6 @@ from freqtrade.optimize.hyperopt_auto import HyperOptAuto from freqtrade.optimize.hyperopt_tools import HyperoptTools from freqtrade.optimize.optimize_reports import generate_strategy_stats from freqtrade.optimize.space import SKDecimal -from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver from freqtrade.strategy.hyper import IntParameter from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) @@ -133,47 +132,6 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) -def test_hyperoptresolver(mocker, default_conf, caplog) -> None: - patched_configuration_load_config_file(mocker, default_conf) - - hyperopt = HyperoptTestSepFile - delattr(hyperopt, 'populate_indicators') - delattr(hyperopt, 'populate_buy_trend') - delattr(hyperopt, 'populate_sell_trend') - mocker.patch( - 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object', - MagicMock(return_value=hyperopt(default_conf)) - ) - default_conf.update({'hyperopt': 'HyperoptTestSepFile'}) - x = HyperOptResolver.load_hyperopt(default_conf) - assert not hasattr(x, 'populate_indicators') - assert not hasattr(x, 'populate_buy_trend') - assert not hasattr(x, 'populate_sell_trend') - assert log_has("Hyperopt class does not provide populate_indicators() method. " - "Using populate_indicators from the strategy.", caplog) - assert log_has("Hyperopt class does not provide populate_sell_trend() method. " - "Using populate_sell_trend from the strategy.", caplog) - assert log_has("Hyperopt class does not provide populate_buy_trend() method. " - "Using populate_buy_trend from the strategy.", caplog) - assert hasattr(x, "ticker_interval") # DEPRECATED - assert hasattr(x, "timeframe") - - -def test_hyperoptresolver_wrongname(default_conf) -> None: - default_conf.update({'hyperopt': "NonExistingHyperoptClass"}) - - with pytest.raises(OperationalException, match=r'Impossible to load Hyperopt.*'): - HyperOptResolver.load_hyperopt(default_conf) - - -def test_hyperoptresolver_noname(default_conf): - default_conf['hyperopt'] = '' - with pytest.raises(OperationalException, - match="No Hyperopt set. Please use `--hyperopt` to specify " - "the Hyperopt class to use."): - HyperOptResolver.load_hyperopt(default_conf) - - def test_start_not_installed(mocker, default_conf, import_fails) -> None: start_mock = MagicMock() patched_configuration_load_config_file(mocker, default_conf) @@ -196,7 +154,7 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None: start_hyperopt(pargs) -def test_start(mocker, hyperopt_conf, caplog) -> None: +def test_start_no_hyperopt_allowed(mocker, hyperopt_conf, caplog) -> None: start_mock = MagicMock() patched_configuration_load_config_file(mocker, hyperopt_conf) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) @@ -210,10 +168,8 @@ def test_start(mocker, hyperopt_conf, caplog) -> None: '--epochs', '5' ] pargs = get_args(args) - start_hyperopt(pargs) - - assert log_has('Starting freqtrade in Hyperopt mode', caplog) - assert start_mock.call_count == 1 + with pytest.raises(OperationalException, match=r"Using separate Hyperopt files has been.*"): + start_hyperopt(pargs) def test_start_no_data(mocker, hyperopt_conf) -> None: @@ -225,11 +181,11 @@ def test_start_no_data(mocker, hyperopt_conf) -> None: ) patch_exchange(mocker) - + # TODO: migrate to strategy-based hyperopt args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'HyperoptTestSepFile', + '--strategy', 'HyperoptableStrategy', '--hyperopt-loss', 'SharpeHyperOptLossDaily', '--epochs', '5' ] @@ -427,66 +383,14 @@ def test_hyperopt_format_results(hyperopt): def test_populate_indicators(hyperopt, testdatadir) -> None: data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) - dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], - {'pair': 'UNITTEST/BTC'}) + dataframe = dataframes['UNITTEST/BTC'] # Check if some indicators are generated. We will not test all of them assert 'adx' in dataframe - assert 'mfi' in dataframe + assert 'macd' in dataframe assert 'rsi' in dataframe -def test_buy_strategy_generator(hyperopt, testdatadir) -> None: - data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) - dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) - dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], - {'pair': 'UNITTEST/BTC'}) - - populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator( - { - 'adx-value': 20, - 'fastd-value': 20, - 'mfi-value': 20, - 'rsi-value': 20, - 'adx-enabled': True, - 'fastd-enabled': True, - 'mfi-enabled': True, - 'rsi-enabled': True, - 'trigger': 'bb_lower' - } - ) - result = populate_buy_trend(dataframe, {'pair': 'UNITTEST/BTC'}) - # Check if some indicators are generated. We will not test all of them - assert 'buy' in result - assert 1 in result['buy'] - - -def test_sell_strategy_generator(hyperopt, testdatadir) -> None: - data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) - dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) - dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], - {'pair': 'UNITTEST/BTC'}) - - populate_sell_trend = hyperopt.custom_hyperopt.sell_strategy_generator( - { - 'sell-adx-value': 20, - 'sell-fastd-value': 75, - 'sell-mfi-value': 80, - 'sell-rsi-value': 20, - 'sell-adx-enabled': True, - 'sell-fastd-enabled': True, - 'sell-mfi-enabled': True, - 'sell-rsi-enabled': True, - 'sell-trigger': 'sell-bb_upper' - } - ) - result = populate_sell_trend(dataframe, {'pair': 'UNITTEST/BTC'}) - # Check if some indicators are generated. We will not test all of them - print(result) - assert 'sell' in result - assert 1 in result['sell'] - - def test_generate_optimizer(mocker, hyperopt_conf) -> None: hyperopt_conf.update({'spaces': 'all', 'hyperopt_min_trades': 1, @@ -527,24 +431,12 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: mocker.patch('freqtrade.optimize.hyperopt.load', return_value={'XRP/BTC': None}) optimizer_param = { - 'adx-value': 0, - 'fastd-value': 35, - 'mfi-value': 0, - 'rsi-value': 0, - 'adx-enabled': False, - 'fastd-enabled': True, - 'mfi-enabled': False, - 'rsi-enabled': False, - 'trigger': 'macd_cross_signal', - 'sell-adx-value': 0, - 'sell-fastd-value': 75, - 'sell-mfi-value': 0, - 'sell-rsi-value': 0, - 'sell-adx-enabled': False, - 'sell-fastd-enabled': True, - 'sell-mfi-enabled': False, - 'sell-rsi-enabled': False, - 'sell-trigger': 'macd_cross_signal', + 'buy_plusdi': 0.02, + 'buy_rsi': 35, + 'sell_minusdi': 0.02, + 'sell_rsi': 75, + 'protection_cooldown_lookback': 20, + 'protection_enabled': True, 'roi_t1': 60.0, 'roi_t2': 30.0, 'roi_t3': 20.0, @@ -564,29 +456,19 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: '0.00003100 BTC ( 0.00%). ' 'Avg duration 0:50:00 min.' ), - 'params_details': {'buy': {'adx-enabled': False, - 'adx-value': 0, - 'fastd-enabled': True, - 'fastd-value': 35, - 'mfi-enabled': False, - 'mfi-value': 0, - 'rsi-enabled': False, - 'rsi-value': 0, - 'trigger': 'macd_cross_signal'}, + 'params_details': {'buy': {'buy_plusdi': 0.02, + 'buy_rsi': 35, + }, 'roi': {"0": 0.12000000000000001, "20.0": 0.02, "50.0": 0.01, "110.0": 0}, - 'protection': {}, - 'sell': {'sell-adx-enabled': False, - 'sell-adx-value': 0, - 'sell-fastd-enabled': True, - 'sell-fastd-value': 75, - 'sell-mfi-enabled': False, - 'sell-mfi-value': 0, - 'sell-rsi-enabled': False, - 'sell-rsi-value': 0, - 'sell-trigger': 'macd_cross_signal'}, + 'protection': {'protection_cooldown_lookback': 20, + 'protection_enabled': True, + }, + 'sell': {'sell_minusdi': 0.02, + 'sell_rsi': 75, + }, 'stoploss': {'stoploss': -0.4}, 'trailing': {'trailing_only_offset_is_reached': False, 'trailing_stop': True, @@ -808,11 +690,6 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) - del hyperopt.custom_hyperopt.__class__.buy_strategy_generator - del hyperopt.custom_hyperopt.__class__.sell_strategy_generator - del hyperopt.custom_hyperopt.__class__.indicator_space - del hyperopt.custom_hyperopt.__class__.sell_indicator_space - hyperopt.start() parallel.assert_called_once() @@ -843,16 +720,14 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None: hyperopt_conf.update({'spaces': 'all', }) + mocker.patch('freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space', + return_value=[]) + hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) - del hyperopt.custom_hyperopt.__class__.buy_strategy_generator - del hyperopt.custom_hyperopt.__class__.sell_strategy_generator - del hyperopt.custom_hyperopt.__class__.indicator_space - del hyperopt.custom_hyperopt.__class__.sell_indicator_space - - with pytest.raises(OperationalException, match=r"The 'buy' space is included into *"): + with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"): hyperopt.start() @@ -889,11 +764,6 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None: hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) - # TODO: sell_strategy_generator() is actually not called because - # run_optimizer_parallel() is mocked - del hyperopt.custom_hyperopt.__class__.sell_strategy_generator - del hyperopt.custom_hyperopt.__class__.sell_indicator_space - hyperopt.start() parallel.assert_called_once() @@ -943,11 +813,6 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None: hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) - # TODO: buy_strategy_generator() is actually not called because - # run_optimizer_parallel() is mocked - del hyperopt.custom_hyperopt.__class__.buy_strategy_generator - del hyperopt.custom_hyperopt.__class__.indicator_space - hyperopt.start() parallel.assert_called_once() @@ -964,13 +829,12 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None: assert hasattr(hyperopt, "position_stacking") -@pytest.mark.parametrize("method,space", [ - ('buy_strategy_generator', 'buy'), - ('indicator_space', 'buy'), - ('sell_strategy_generator', 'sell'), - ('sell_indicator_space', 'sell'), +@pytest.mark.parametrize("space", [ + ('buy'), + ('sell'), + ('protection'), ]) -def test_simplified_interface_failed(mocker, hyperopt_conf, method, space) -> None: +def test_simplified_interface_failed(mocker, hyperopt_conf, space) -> None: mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', @@ -979,6 +843,8 @@ def test_simplified_interface_failed(mocker, hyperopt_conf, method, space) -> No 'freqtrade.optimize.hyperopt.get_timerange', MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) ) + mocker.patch('freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space', + return_value=[]) patch_exchange(mocker) @@ -988,8 +854,6 @@ def test_simplified_interface_failed(mocker, hyperopt_conf, method, space) -> No hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) - delattr(hyperopt.custom_hyperopt.__class__, method) - with pytest.raises(OperationalException, match=f"The '{space}' space is included into *"): hyperopt.start() @@ -999,7 +863,6 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) (Path(tmpdir) / 'hyperopt_results').mkdir(parents=True) # No hyperopt needed - del hyperopt_conf['hyperopt'] hyperopt_conf.update({ 'strategy': 'HyperoptableStrategy', 'user_data_dir': Path(tmpdir), From 3675df83448ceaaed1c92ec4c8b7d3273e1ef461 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Sep 2021 17:52:47 +0200 Subject: [PATCH 420/519] Update documentation regarding Legacy Hyperopt --- README.md | 8 +- docs/advanced-hyperopt.md | 298 +----------------- docs/bot-usage.md | 8 +- docs/deprecated.md | 5 + docs/faq.md | 2 +- docs/hyperopt.md | 21 +- docs/utils.md | 2 - freqtrade/commands/arguments.py | 2 +- freqtrade/commands/cli_options.py | 4 +- freqtrade/constants.py | 2 - tests/exchange/test_exchange.py | 4 +- .../hyperopts/hyperopt_test_sep_file.py | 202 ------------ tests/optimize/test_hyperopt.py | 14 +- tests/plugins/test_pairlocks.py | 2 +- tests/test_directory_operations.py | 8 +- 15 files changed, 55 insertions(+), 527 deletions(-) delete mode 100644 tests/optimize/hyperopts/hyperopt_test_sep_file.py diff --git a/README.md b/README.md index 2c164135f..01effd7bc 100644 --- a/README.md +++ b/README.md @@ -79,22 +79,22 @@ For any other type of installation please refer to [Installation doc](https://ww ``` usage: freqtrade [-h] [-V] - {trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} + {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} ... Free, open source crypto trading bot positional arguments: - {trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} + {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} trade Trade module. create-userdir Create user-data directory. new-config Create new config - new-hyperopt Create new hyperopt new-strategy Create new strategy download-data Download backtesting data. convert-data Convert candle (OHLCV) data from one format to another. convert-trade-data Convert trade data from one format to another. + list-data List downloaded data. backtesting Backtesting module. edge Edge module. hyperopt Hyperopt module. @@ -108,8 +108,10 @@ positional arguments: list-timeframes Print available timeframes for the exchange. show-trades Show trades. test-pairlist Test your pairlist configuration. + install-ui Install FreqUI plot-dataframe Plot candles with indicators. plot-profit Generate plot showing profits. + webserver Webserver module. optional arguments: -h, --help show this help message and exit diff --git a/docs/advanced-hyperopt.md b/docs/advanced-hyperopt.md index 4a2bafd2e..f2f52b7dd 100644 --- a/docs/advanced-hyperopt.md +++ b/docs/advanced-hyperopt.md @@ -67,10 +67,10 @@ Currently, the arguments are: This function needs to return a floating point number (`float`). Smaller numbers will be interpreted as better results. The parameters and balancing for this is up to you. !!! Note - This function is called once per iteration - so please make sure to have this as optimized as possible to not slow hyperopt down unnecessarily. + This function is called once per epoch - so please make sure to have this as optimized as possible to not slow hyperopt down unnecessarily. -!!! Note - Please keep the arguments `*args` and `**kwargs` in the interface to allow us to extend this interface later. +!!! Note "`*args` and `**kwargs`" + Please keep the arguments `*args` and `**kwargs` in the interface to allow us to extend this interface in the future. ## Overriding pre-defined spaces @@ -82,8 +82,22 @@ class MyAwesomeStrategy(IStrategy): # Define a custom stoploss space. def stoploss_space(): return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')] + + # Define custom ROI space + def roi_space() -> List[Dimension]: + return [ + Integer(10, 120, name='roi_t1'), + Integer(10, 60, name='roi_t2'), + Integer(10, 40, name='roi_t3'), + SKDecimal(0.01, 0.04, decimals=3, name='roi_p1'), + SKDecimal(0.01, 0.07, decimals=3, name='roi_p2'), + SKDecimal(0.01, 0.20, decimals=3, name='roi_p3'), + ] ``` +!!! Note + All overrides are optional and can be mixed/matched as necessary. + ## Space options For the additional spaces, scikit-optimize (in combination with Freqtrade) provides the following space types: @@ -105,281 +119,3 @@ from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal, Assuming the definition of a rather small space (`SKDecimal(0.10, 0.15, decimals=2, name='xxx')`) - SKDecimal will have 5 possibilities (`[0.10, 0.11, 0.12, 0.13, 0.14, 0.15]`). A corresponding real space `Real(0.10, 0.15 name='xxx')` on the other hand has an almost unlimited number of possibilities (`[0.10, 0.010000000001, 0.010000000002, ... 0.014999999999, 0.01500000000]`). - ---- - -## Legacy Hyperopt - -This Section explains the configuration of an explicit Hyperopt file (separate to the strategy). - -!!! Warning "Deprecated / legacy mode" - Since the 2021.4 release you no longer have to write a separate hyperopt class, but all strategies can be hyperopted. - Please read the [main hyperopt page](hyperopt.md) for more details. - -### Prepare hyperopt file - -Configuring an explicit hyperopt file is similar to writing your own strategy, and many tasks will be similar. - -!!! Tip "About this page" - For this page, we will be using a fictional strategy called `AwesomeStrategy` - which will be optimized using the `AwesomeHyperopt` class. - -#### Create a Custom Hyperopt File - -The simplest way to get started is to use the following command, which will create a new hyperopt file from a template, which will be located under `user_data/hyperopts/AwesomeHyperopt.py`. - -Let assume you want a hyperopt file `AwesomeHyperopt.py`: - -``` bash -freqtrade new-hyperopt --hyperopt AwesomeHyperopt -``` - -#### Legacy Hyperopt checklist - -Checklist on all tasks / possibilities in hyperopt - -Depending on the space you want to optimize, only some of the below are required: - -* fill `buy_strategy_generator` - for buy signal optimization -* fill `indicator_space` - for buy signal optimization -* fill `sell_strategy_generator` - for sell signal optimization -* fill `sell_indicator_space` - for sell signal optimization - -!!! Note - `populate_indicators` needs to create all indicators any of thee spaces may use, otherwise hyperopt will not work. - -Optional in hyperopt - can also be loaded from a strategy (recommended): - -* `populate_indicators` - fallback to create indicators -* `populate_buy_trend` - fallback if not optimizing for buy space. should come from strategy -* `populate_sell_trend` - fallback if not optimizing for sell space. should come from strategy - -!!! Note - You always have to provide a strategy to Hyperopt, even if your custom Hyperopt class contains all methods. - Assuming the optional methods are not in your hyperopt file, please use `--strategy AweSomeStrategy` which contains these methods so hyperopt can use these methods instead. - -Rarely you may also need to override: - -* `roi_space` - for custom ROI optimization (if you need the ranges for the ROI parameters in the optimization hyperspace that differ from default) -* `generate_roi_table` - for custom ROI optimization (if you need the ranges for the values in the ROI table that differ from default or the number of entries (steps) in the ROI table which differs from the default 4 steps) -* `stoploss_space` - for custom stoploss optimization (if you need the range for the stoploss parameter in the optimization hyperspace that differs from default) -* `trailing_space` - for custom trailing stop optimization (if you need the ranges for the trailing stop parameters in the optimization hyperspace that differ from default) - -#### Defining a buy signal optimization - -Let's say you are curious: should you use MACD crossings or lower Bollinger -Bands to trigger your buys. And you also wonder should you use RSI or ADX to -help with those buy decisions. If you decide to use RSI or ADX, which values -should I use for them? So let's use hyperparameter optimization to solve this -mystery. - -We will start by defining a search space: - -```python - def indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching strategy parameters - """ - return [ - Integer(20, 40, name='adx-value'), - Integer(20, 40, name='rsi-value'), - Categorical([True, False], name='adx-enabled'), - Categorical([True, False], name='rsi-enabled'), - Categorical(['bb_lower', 'macd_cross_signal'], name='trigger') - ] -``` - -Above definition says: I have five parameters I want you to randomly combine -to find the best combination. Two of them are integer values (`adx-value` and `rsi-value`) and I want you test in the range of values 20 to 40. -Then we have three category variables. First two are either `True` or `False`. -We use these to either enable or disable the ADX and RSI guards. -The last one we call `trigger` and use it to decide which buy trigger we want to use. - -So let's write the buy strategy generator using these values: - -```python - @staticmethod - def buy_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the buy strategy parameters to be used by Hyperopt. - """ - def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - conditions = [] - # GUARDS AND TRENDS - if 'adx-enabled' in params and params['adx-enabled']: - conditions.append(dataframe['adx'] > params['adx-value']) - if 'rsi-enabled' in params and params['rsi-enabled']: - conditions.append(dataframe['rsi'] < params['rsi-value']) - - # TRIGGERS - if 'trigger' in params: - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) - - # Check that volume is not 0 - conditions.append(dataframe['volume'] > 0) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'buy'] = 1 - - return dataframe - - return populate_buy_trend -``` - -Hyperopt will now call `populate_buy_trend()` many times (`epochs`) with different value combinations. -It will use the given historical data and make buys based on the buy signals generated with the above function. -Based on the results, hyperopt will tell you which parameter combination produced the best results (based on the configured [loss function](#loss-functions)). - -!!! Note - The above setup expects to find ADX, RSI and Bollinger Bands in the populated indicators. - When you want to test an indicator that isn't used by the bot currently, remember to - add it to the `populate_indicators()` method in your strategy or hyperopt file. - -#### Sell optimization - -Similar to the buy-signal above, sell-signals can also be optimized. -Place the corresponding settings into the following methods - -* Inside `sell_indicator_space()` - the parameters hyperopt shall be optimizing. -* Within `sell_strategy_generator()` - populate the nested method `populate_sell_trend()` to apply the parameters. - -The configuration and rules are the same than for buy signals. -To avoid naming collisions in the search-space, please prefix all sell-spaces with `sell-`. - -### Execute Hyperopt - -Once you have updated your hyperopt configuration you can run it. -Because hyperopt tries a lot of combinations to find the best parameters it will take time to get a good result. More time usually results in better results. - -We strongly recommend to use `screen` or `tmux` to prevent any connection loss. - -```bash -freqtrade hyperopt --config config.json --hyperopt --hyperopt-loss --strategy -e 500 --spaces all -``` - -Use `` as the name of the custom hyperopt used. - -The `-e` option will set how many evaluations hyperopt will do. Since hyperopt uses Bayesian search, running too many epochs at once may not produce greater results. Experience has shown that best results are usually not improving much after 500-1000 epochs. -Doing multiple runs (executions) with a few 1000 epochs and different random state will most likely produce different results. - -The `--spaces all` option determines that all possible parameters should be optimized. Possibilities are listed below. - -!!! Note - Hyperopt will store hyperopt results with the timestamp of the hyperopt start time. - Reading commands (`hyperopt-list`, `hyperopt-show`) can use `--hyperopt-filename ` to read and display older hyperopt results. - You can find a list of filenames with `ls -l user_data/hyperopt_results/`. - -#### Running Hyperopt using methods from a strategy - -Hyperopt can reuse `populate_indicators`, `populate_buy_trend`, `populate_sell_trend` from your strategy, assuming these methods are **not** in your custom hyperopt file, and a strategy is provided. - -```bash -freqtrade hyperopt --hyperopt AwesomeHyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy AwesomeStrategy -``` - -### Understand the Hyperopt Result - -Once Hyperopt is completed you can use the result to create a new strategy. -Given the following result from hyperopt: - -``` -Best result: - - 44/100: 135 trades. Avg profit 0.57%. Total profit 0.03871918 BTC (0.7722%). Avg duration 180.4 mins. Objective: 1.94367 - -Buy hyperspace params: -{ 'adx-value': 44, - 'rsi-value': 29, - 'adx-enabled': False, - 'rsi-enabled': True, - 'trigger': 'bb_lower'} -``` - -You should understand this result like: - -* The buy trigger that worked best was `bb_lower`. -* You should not use ADX because `adx-enabled: False`) -* You should **consider** using the RSI indicator (`rsi-enabled: True` and the best value is `29.0` (`rsi-value: 29.0`) - -You have to look inside your strategy file into `buy_strategy_generator()` -method, what those values match to. - -So for example you had `rsi-value: 29.0` so we would look at `rsi`-block, that translates to the following code block: - -```python -(dataframe['rsi'] < 29.0) -``` - -Translating your whole hyperopt result as the new buy-signal would then look like: - -```python -def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: - dataframe.loc[ - ( - (dataframe['rsi'] < 29.0) & # rsi-value - dataframe['close'] < dataframe['bb_lowerband'] # trigger - ), - 'buy'] = 1 - return dataframe -``` - -### Validate backtesting results - -Once the optimized parameters and conditions have been implemented into your strategy, you should backtest the strategy to make sure everything is working as expected. - -To achieve same results (number of trades, their durations, profit, etc.) than during Hyperopt, please use same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting. - -Should results not match, please double-check to make sure you transferred all conditions correctly. -Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy. -You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`). - -### Sharing methods with your strategy - -Hyperopt classes provide access to the Strategy via the `strategy` class attribute. -This can be a great way to reduce code duplication if used correctly, but will also complicate usage for inexperienced users. - -``` python -from pandas import DataFrame -from freqtrade.strategy.interface import IStrategy -import freqtrade.vendor.qtpylib.indicators as qtpylib - -class MyAwesomeStrategy(IStrategy): - - buy_params = { - 'rsi-value': 30, - 'adx-value': 35, - } - - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - return self.buy_strategy_generator(self.buy_params, dataframe, metadata) - - @staticmethod - def buy_strategy_generator(params, dataframe: DataFrame, metadata: dict) -> DataFrame: - dataframe.loc[ - ( - qtpylib.crossed_above(dataframe['rsi'], params['rsi-value']) & - dataframe['adx'] > params['adx-value']) & - dataframe['volume'] > 0 - ) - , 'buy'] = 1 - return dataframe - -class MyAwesomeHyperOpt(IHyperOpt): - ... - @staticmethod - def buy_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the buy strategy parameters to be used by Hyperopt. - """ - def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - # Call strategy's buy strategy generator - return self.StrategyClass.buy_strategy_generator(params, dataframe, metadata) - - return populate_buy_trend -``` diff --git a/docs/bot-usage.md b/docs/bot-usage.md index b65220722..c6a7f6103 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -12,22 +12,22 @@ This page explains the different parameters of the bot and how to run it. ``` usage: freqtrade [-h] [-V] - {trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} + {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} ... Free, open source crypto trading bot positional arguments: - {trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} + {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} trade Trade module. create-userdir Create user-data directory. new-config Create new config - new-hyperopt Create new hyperopt new-strategy Create new strategy download-data Download backtesting data. convert-data Convert candle (OHLCV) data from one format to another. convert-trade-data Convert trade data from one format to another. + list-data List downloaded data. backtesting Backtesting module. edge Edge module. hyperopt Hyperopt module. @@ -41,8 +41,10 @@ positional arguments: list-timeframes Print available timeframes for the exchange. show-trades Show trades. test-pairlist Test your pairlist configuration. + install-ui Install FreqUI plot-dataframe Plot candles with indicators. plot-profit Generate plot showing profits. + webserver Webserver module. optional arguments: -h, --help show this help message and exit diff --git a/docs/deprecated.md b/docs/deprecated.md index b7ad847e6..d86a7ac7a 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -38,3 +38,8 @@ Since only quoteVolume can be compared between assets, the other options (bidVol Using `order_book_min` and `order_book_max` used to allow stepping the orderbook and trying to find the next ROI slot - trying to place sell-orders early. As this does however increase risk and provides no benefit, it's been removed for maintainability purposes in 2021.7. + +### Legacy Hyperopt mode + +Using separate hyperopt files was deprecated in 2021.4 and was removed in 2021.9. +Please switch to the new [Parametrized Strategies](hyperopt.md) to benefit from the new hyperopt interface. diff --git a/docs/faq.md b/docs/faq.md index b8a3a44d8..285625491 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -167,7 +167,7 @@ Since hyperopt uses Bayesian search, running for too many epochs may not produce It's therefore recommended to run between 500-1000 epochs over and over until you hit at least 10.000 epochs in total (or are satisfied with the result). You can best judge by looking at the results - if the bot keeps discovering better strategies, it's best to keep on going. ```bash -freqtrade hyperopt --hyperopt SampleHyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy SampleStrategy -e 1000 +freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy SampleStrategy -e 1000 ``` ### Why does it take a long time to run hyperopt? diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 1eb90f1bc..e69b761c4 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -44,9 +44,8 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--data-format-ohlcv {json,jsongz,hdf5}] [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] [--fee FLOAT] - [-p PAIRS [PAIRS ...]] [--hyperopt NAME] - [--hyperopt-path PATH] [--eps] [--dmmp] - [--enable-protections] + [-p PAIRS [PAIRS ...]] [--hyperopt-path PATH] + [--eps] [--dmmp] [--enable-protections] [--dry-run-wallet DRY_RUN_WALLET] [-e INT] [--spaces {all,buy,sell,roi,stoploss,trailing,protection,default} [{all,buy,sell,roi,stoploss,trailing,protection,default} ...]] [--print-all] [--no-color] [--print-json] [-j JOBS] @@ -73,10 +72,8 @@ optional arguments: -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] Limit command to these pairs. Pairs are space- separated. - --hyperopt NAME Specify hyperopt class name which will be used by the - bot. - --hyperopt-path PATH Specify additional lookup path for Hyperopt and - Hyperopt Loss functions. + --hyperopt-path PATH Specify additional lookup path for Hyperopt Loss + functions. --eps, --enable-position-stacking Allow buying the same pair multiple times (position stacking). @@ -558,7 +555,7 @@ For example, to use one month of data, pass `--timerange 20210101-20210201` (fro Full command: ```bash -freqtrade hyperopt --hyperopt --strategy --timerange 20210101-20210201 +freqtrade hyperopt --strategy --timerange 20210101-20210201 ``` ### Running Hyperopt with Smaller Search Space @@ -684,7 +681,7 @@ If you have the `generate_roi_table()` and `roi_space()` methods in your custom Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps). -A sample for these methods can be found in [sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py). +A sample for these methods can be found in the [overriding pre-defined spaces section](advanced-hyperopt.md#overriding-pre-defined-spaces). !!! Note "Reduced search space" To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs. @@ -726,7 +723,7 @@ If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimiza If you have the `stoploss_space()` method in your custom hyperopt file, remove it in order to utilize Stoploss hyperoptimization space generated by Freqtrade by default. -Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization. A sample for this method can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py). +Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization. A sample for this method can be found in the [overriding pre-defined spaces section](advanced-hyperopt.md#overriding-pre-defined-spaces). !!! Note "Reduced search space" To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs. @@ -764,10 +761,10 @@ As stated in the comment, you can also use it as the values of the corresponding If you are optimizing trailing stop values, Freqtrade creates the 'trailing' optimization hyperspace for you. By default, the `trailing_stop` parameter is always set to True in that hyperspace, the value of the `trailing_only_offset_is_reached` vary between True and False, the values of the `trailing_stop_positive` and `trailing_stop_positive_offset` parameters vary in the ranges 0.02...0.35 and 0.01...0.1 correspondingly, which is sufficient in most cases. -Override the `trailing_space()` method and define the desired range in it if you need values of the trailing stop parameters to vary in other ranges during hyperoptimization. A sample for this method can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py). +Override the `trailing_space()` method and define the desired range in it if you need values of the trailing stop parameters to vary in other ranges during hyperoptimization. A sample for this method can be found in the [overriding pre-defined spaces section](advanced-hyperopt.md#overriding-pre-defined-spaces). !!! Note "Reduced search space" - To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs. + To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#overriding-pre-defined-spaces) to change this to your needs. ### Reproducible results diff --git a/docs/utils.md b/docs/utils.md index a1e219d6a..ade2f4047 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -26,9 +26,7 @@ optional arguments: ├── data ├── hyperopt_results ├── hyperopts -│   ├── sample_hyperopt_advanced.py │   ├── sample_hyperopt_loss.py -│   └── sample_hyperopt.py ├── notebooks │   └── strategy_analysis_example.ipynb ├── plot diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 9c6ce543f..3574dc485 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -90,7 +90,7 @@ ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperop NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-pairs", "list-strategies", "list-data", - "list-hyperopts", "hyperopt-list", "hyperopt-show", + "hyperopt-list", "hyperopt-show", "plot-dataframe", "plot-profit", "show-trades"] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index a1790cb9a..e3c7fe464 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -1,7 +1,7 @@ """ Definition of cli arguments used in arguments.py """ -from argparse import ArgumentTypeError +from argparse import SUPPRESS, ArgumentTypeError from freqtrade import __version__, constants from freqtrade.constants import HYPEROPT_LOSS_BUILTIN @@ -203,7 +203,7 @@ AVAILABLE_CLI_OPTIONS = { # Hyperopt "hyperopt": Arg( '--hyperopt', - help='Specify hyperopt class name which will be used by the bot.', + help=SUPPRESS, metavar='NAME', required=False, ), diff --git a/freqtrade/constants.py b/freqtrade/constants.py index efcd1aaca..9ca43d459 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -69,9 +69,7 @@ DUST_PER_COIN = { # Source files with destination directories within user-directory USER_DATA_FILES = { 'sample_strategy.py': USERPATH_STRATEGIES, - 'sample_hyperopt_advanced.py': USERPATH_HYPEROPTS, 'sample_hyperopt_loss.py': USERPATH_HYPEROPTS, - 'sample_hyperopt.py': USERPATH_HYPEROPTS, 'strategy_analysis_example.ipynb': USERPATH_NOTEBOOKS, } diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 90e19782e..42a10e8d7 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -185,7 +185,7 @@ def test_exchange_resolver(default_conf, mocker, caplog): def test_validate_order_time_in_force(default_conf, mocker, caplog): caplog.set_level(logging.INFO) - # explicitly test bittrex, exchanges implementing other policies need seperate tests + # explicitly test bittrex, exchanges implementing other policies need separate tests ex = get_patched_exchange(mocker, default_conf, id="bittrex") tif = { "buy": "gtc", @@ -2464,7 +2464,7 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog): @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_fetch_stoploss_order(default_conf, mocker, exchange_name): - # Don't test FTX here - that needs a seperate test + # Don't test FTX here - that needs a separate test if exchange_name == 'ftx': return default_conf['dry_run'] = True diff --git a/tests/optimize/hyperopts/hyperopt_test_sep_file.py b/tests/optimize/hyperopts/hyperopt_test_sep_file.py deleted file mode 100644 index 0fa1e1959..000000000 --- a/tests/optimize/hyperopts/hyperopt_test_sep_file.py +++ /dev/null @@ -1,202 +0,0 @@ -# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement - -from functools import reduce -from typing import Any, Callable, Dict, List - -import talib.abstract as ta -from pandas import DataFrame -from skopt.space import Categorical, Dimension, Integer - -import freqtrade.vendor.qtpylib.indicators as qtpylib -from freqtrade.optimize.hyperopt_interface import IHyperOpt - - -class HyperoptTestSepFile(IHyperOpt): - """ - Default hyperopt provided by the Freqtrade bot. - You can override it with your own Hyperopt - """ - @staticmethod - def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Add several indicators needed for buy and sell strategies defined below. - """ - # ADX - dataframe['adx'] = ta.ADX(dataframe) - # MACD - macd = ta.MACD(dataframe) - dataframe['macd'] = macd['macd'] - dataframe['macdsignal'] = macd['macdsignal'] - # MFI - dataframe['mfi'] = ta.MFI(dataframe) - # RSI - dataframe['rsi'] = ta.RSI(dataframe) - # Stochastic Fast - stoch_fast = ta.STOCHF(dataframe) - dataframe['fastd'] = stoch_fast['fastd'] - # Minus-DI - dataframe['minus_di'] = ta.MINUS_DI(dataframe) - # Bollinger bands - bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) - dataframe['bb_lowerband'] = bollinger['lower'] - dataframe['bb_upperband'] = bollinger['upper'] - # SAR - dataframe['sar'] = ta.SAR(dataframe) - - return dataframe - - @staticmethod - def buy_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the buy strategy parameters to be used by Hyperopt. - """ - def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Buy strategy Hyperopt will build and use. - """ - conditions = [] - - # GUARDS AND TRENDS - if 'mfi-enabled' in params and params['mfi-enabled']: - conditions.append(dataframe['mfi'] < params['mfi-value']) - if 'fastd-enabled' in params and params['fastd-enabled']: - conditions.append(dataframe['fastd'] < params['fastd-value']) - if 'adx-enabled' in params and params['adx-enabled']: - conditions.append(dataframe['adx'] > params['adx-value']) - if 'rsi-enabled' in params and params['rsi-enabled']: - conditions.append(dataframe['rsi'] < params['rsi-value']) - - # TRIGGERS - if 'trigger' in params: - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) - if params['trigger'] == 'sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] - )) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'buy'] = 1 - - return dataframe - - return populate_buy_trend - - @staticmethod - def indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching buy strategy parameters. - """ - return [ - Integer(10, 25, name='mfi-value'), - Integer(15, 45, name='fastd-value'), - Integer(20, 50, name='adx-value'), - Integer(20, 40, name='rsi-value'), - Categorical([True, False], name='mfi-enabled'), - Categorical([True, False], name='fastd-enabled'), - Categorical([True, False], name='adx-enabled'), - Categorical([True, False], name='rsi-enabled'), - Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') - ] - - @staticmethod - def sell_strategy_generator(params: Dict[str, Any]) -> Callable: - """ - Define the sell strategy parameters to be used by Hyperopt. - """ - def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Sell strategy Hyperopt will build and use. - """ - conditions = [] - - # GUARDS AND TRENDS - if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: - conditions.append(dataframe['mfi'] > params['sell-mfi-value']) - if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']: - conditions.append(dataframe['fastd'] > params['sell-fastd-value']) - if 'sell-adx-enabled' in params and params['sell-adx-enabled']: - conditions.append(dataframe['adx'] < params['sell-adx-value']) - if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']: - conditions.append(dataframe['rsi'] > params['sell-rsi-value']) - - # TRIGGERS - if 'sell-trigger' in params: - if params['sell-trigger'] == 'sell-bb_upper': - conditions.append(dataframe['close'] > dataframe['bb_upperband']) - if params['sell-trigger'] == 'sell-macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macdsignal'], dataframe['macd'] - )) - if params['sell-trigger'] == 'sell-sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['sar'], dataframe['close'] - )) - - if conditions: - dataframe.loc[ - reduce(lambda x, y: x & y, conditions), - 'sell'] = 1 - - return dataframe - - return populate_sell_trend - - @staticmethod - def sell_indicator_space() -> List[Dimension]: - """ - Define your Hyperopt space for searching sell strategy parameters. - """ - return [ - Integer(75, 100, name='sell-mfi-value'), - Integer(50, 100, name='sell-fastd-value'), - Integer(50, 100, name='sell-adx-value'), - Integer(60, 100, name='sell-rsi-value'), - Categorical([True, False], name='sell-mfi-enabled'), - Categorical([True, False], name='sell-fastd-enabled'), - Categorical([True, False], name='sell-adx-enabled'), - Categorical([True, False], name='sell-rsi-enabled'), - Categorical(['sell-bb_upper', - 'sell-macd_cross_signal', - 'sell-sar_reversal'], name='sell-trigger') - ] - - def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Based on TA indicators. Should be a copy of same method from strategy. - Must align to populate_indicators in this file. - Only used when --spaces does not include buy space. - """ - dataframe.loc[ - ( - (dataframe['close'] < dataframe['bb_lowerband']) & - (dataframe['mfi'] < 16) & - (dataframe['adx'] > 25) & - (dataframe['rsi'] < 21) - ), - 'buy'] = 1 - - return dataframe - - def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - """ - Based on TA indicators. Should be a copy of same method from strategy. - Must align to populate_indicators in this file. - Only used when --spaces does not include sell space. - """ - dataframe.loc[ - ( - (qtpylib.crossed_above( - dataframe['macdsignal'], dataframe['macd'] - )) & - (dataframe['fastd'] > 54) - ), - 'sell'] = 1 - - return dataframe diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 498b6e588..b34c3a916 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -21,8 +21,6 @@ from freqtrade.strategy.hyper import IntParameter from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) -from .hyperopts.hyperopt_test_sep_file import HyperoptTestSepFile - def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) @@ -30,7 +28,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'HyperoptTestSepFile', + '--strategy', 'HyperoptableStrategy', ] config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) @@ -62,7 +60,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'HyperoptTestSepFile', + '--strategy', 'HyperoptableStrategy', '--datadir', '/foo/bar', '--timeframe', '1m', '--timerange', ':100', @@ -114,7 +112,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'HyperoptTestSepFile', + '--strategy', 'HyperoptableStrategy', '--stake-amount', '1', '--starting-balance', '2' ] @@ -142,9 +140,7 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'HyperoptTestSepFile', - '--hyperopt-path', - str(Path(__file__).parent / "hyperopts"), + '--strategy', 'HyperoptableStrategy', '--epochs', '5', '--hyperopt-loss', 'SharpeHyperOptLossDaily', ] @@ -203,7 +199,7 @@ def test_start_filelock(mocker, hyperopt_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', - '--hyperopt', 'HyperoptTestSepFile', + '--strategy', 'HyperoptableStrategy', '--hyperopt-loss', 'SharpeHyperOptLossDaily', '--epochs', '5' ] diff --git a/tests/plugins/test_pairlocks.py b/tests/plugins/test_pairlocks.py index fce3a8cd1..c694fd7c1 100644 --- a/tests/plugins/test_pairlocks.py +++ b/tests/plugins/test_pairlocks.py @@ -68,7 +68,7 @@ def test_PairLocks(use_db): # Global lock PairLocks.lock_pair('*', lock_time) assert PairLocks.is_global_lock(lock_time + timedelta(minutes=-50)) - # Global lock also locks every pair seperately + # Global lock also locks every pair separately assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-50)) assert PairLocks.is_pair_locked('XRP/USDT', lock_time + timedelta(minutes=-50)) diff --git a/tests/test_directory_operations.py b/tests/test_directory_operations.py index a11200526..905b078f9 100644 --- a/tests/test_directory_operations.py +++ b/tests/test_directory_operations.py @@ -74,16 +74,12 @@ def test_copy_sample_files(mocker, default_conf, caplog) -> None: copymock = mocker.patch('shutil.copy', MagicMock()) copy_sample_files(Path('/tmp/bar')) - assert copymock.call_count == 5 + assert copymock.call_count == 3 assert copymock.call_args_list[0][0][1] == str( Path('/tmp/bar') / 'strategies/sample_strategy.py') assert copymock.call_args_list[1][0][1] == str( - Path('/tmp/bar') / 'hyperopts/sample_hyperopt_advanced.py') - assert copymock.call_args_list[2][0][1] == str( Path('/tmp/bar') / 'hyperopts/sample_hyperopt_loss.py') - assert copymock.call_args_list[3][0][1] == str( - Path('/tmp/bar') / 'hyperopts/sample_hyperopt.py') - assert copymock.call_args_list[4][0][1] == str( + assert copymock.call_args_list[2][0][1] == str( Path('/tmp/bar') / 'notebooks/strategy_analysis_example.ipynb') From 0017b3438efcf10fad1ac0224db6c7d4c6d25a0a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Sep 2021 17:56:41 +0200 Subject: [PATCH 421/519] Remove list-hyperopts --- docs/utils.md | 46 ++++------------------------- freqtrade/commands/__init__.py | 6 ++-- freqtrade/commands/arguments.py | 19 ++++-------- freqtrade/commands/list_commands.py | 21 +------------ 4 files changed, 14 insertions(+), 78 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index ade2f4047..d8fbcacb7 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -109,11 +109,11 @@ Using the advanced template (populates all optional functions and methods) freqtrade new-strategy --strategy AwesomeStrategy --template advanced ``` -## List Strategies and List Hyperopts +## List Strategies -Use the `list-strategies` subcommand to see all strategies in one particular directory and the `list-hyperopts` subcommand to list custom Hyperopts. +Use the `list-strategies` subcommand to see all strategies in one particular directory. -These subcommands are useful for finding problems in your environment with loading strategies or hyperopt classes: modules with strategies or hyperopt classes that contain errors and failed to load are printed in red (LOAD FAILED), while strategies or hyperopt classes with duplicate names are printed in yellow (DUPLICATE NAME). +This subcommand is useful for finding problems in your environment with loading strategies: modules with strategies that contain errors and failed to load are printed in red (LOAD FAILED), while strategies with duplicate names are printed in yellow (DUPLICATE NAME). ``` usage: freqtrade list-strategies [-h] [-v] [--logfile FILE] [-V] [-c PATH] @@ -127,34 +127,6 @@ optional arguments: --no-color Disable colorization of hyperopt results. May be useful if you are redirecting output to a file. -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. -``` -``` -usage: freqtrade list-hyperopts [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [--hyperopt-path PATH] [-1] [--no-color] - -optional arguments: - -h, --help show this help message and exit - --hyperopt-path PATH Specify additional lookup path for Hyperopt and - Hyperopt Loss functions. - -1, --one-column Print output in one column. - --no-color Disable colorization of hyperopt results. May be - useful if you are redirecting output to a file. - Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). --logfile FILE Log to the file specified. Special values are: @@ -174,18 +146,16 @@ Common arguments: !!! Warning Using these commands will try to load all python files from a directory. This can be a security risk if untrusted files reside in this directory, since all module-level code is executed. -Example: Search default strategies and hyperopts directories (within the default userdir). +Example: Search default strategies directories (within the default userdir). ``` bash freqtrade list-strategies -freqtrade list-hyperopts ``` -Example: Search strategies and hyperopts directory within the userdir. +Example: Search strategies directory within the userdir. ``` bash freqtrade list-strategies --userdir ~/.freqtrade/ -freqtrade list-hyperopts --userdir ~/.freqtrade/ ``` Example: Search dedicated strategy path. @@ -194,12 +164,6 @@ Example: Search dedicated strategy path. freqtrade list-strategies --strategy-path ~/.freqtrade/strategies/ ``` -Example: Search dedicated hyperopt path. - -``` bash -freqtrade list-hyperopt --hyperopt-path ~/.freqtrade/hyperopts/ -``` - ## List Exchanges Use the `list-exchanges` subcommand to see the exchanges available for the bot. diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index 49b184a80..a6f14cff7 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -13,9 +13,9 @@ from freqtrade.commands.data_commands import (start_convert_data, start_download from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui, start_new_strategy) from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show -from freqtrade.commands.list_commands import (start_list_exchanges, start_list_hyperopts, - start_list_markets, start_list_strategies, - start_list_timeframes, start_show_trades) +from freqtrade.commands.list_commands import (start_list_exchanges, start_list_markets, + start_list_strategies, start_list_timeframes, + start_show_trades) from freqtrade.commands.optimize_commands import start_backtesting, start_edge, start_hyperopt from freqtrade.commands.pairlist_commands import start_test_pairlist from freqtrade.commands.plot_commands import start_plot_dataframe, start_plot_profit diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 3574dc485..d424f3ce7 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -172,11 +172,11 @@ class Arguments: from freqtrade.commands import (start_backtesting, start_convert_data, start_create_userdir, start_download_data, start_edge, start_hyperopt, start_hyperopt_list, start_hyperopt_show, start_install_ui, - start_list_data, start_list_exchanges, start_list_hyperopts, - start_list_markets, start_list_strategies, - start_list_timeframes, start_new_config, start_new_strategy, - start_plot_dataframe, start_plot_profit, start_show_trades, - start_test_pairlist, start_trading, start_webserver) + start_list_data, start_list_exchanges, start_list_markets, + start_list_strategies, start_list_timeframes, + start_new_config, start_new_strategy, start_plot_dataframe, + start_plot_profit, start_show_trades, start_test_pairlist, + start_trading, start_webserver) subparsers = self.parser.add_subparsers(dest='command', # Use custom message when no subhandler is added @@ -291,15 +291,6 @@ class Arguments: list_exchanges_cmd.set_defaults(func=start_list_exchanges) self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd) - # Add list-hyperopts subcommand - list_hyperopts_cmd = subparsers.add_parser( - 'list-hyperopts', - help='Print available hyperopt classes.', - parents=[_common_parser], - ) - list_hyperopts_cmd.set_defaults(func=start_list_hyperopts) - self._build_args(optionlist=ARGS_LIST_HYPEROPTS, parser=list_hyperopts_cmd) - # Add list-markets subcommand list_markets_cmd = subparsers.add_parser( 'list-markets', diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index 410b9b72b..38fb098a0 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -10,7 +10,7 @@ from colorama import init as colorama_init from tabulate import tabulate from freqtrade.configuration import setup_utils_configuration -from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES +from freqtrade.constants import USERPATH_STRATEGIES from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import market_is_active, validate_exchanges @@ -92,25 +92,6 @@ def start_list_strategies(args: Dict[str, Any]) -> None: _print_objs_tabular(strategy_objs, config.get('print_colorized', False)) -def start_list_hyperopts(args: Dict[str, Any]) -> None: - """ - Print files with HyperOpt custom classes available in the directory - """ - from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver - - config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) - - directory = Path(config.get('hyperopt_path', config['user_data_dir'] / USERPATH_HYPEROPTS)) - hyperopt_objs = HyperOptResolver.search_all_objects(directory, not args['print_one_column']) - # Sort alphabetically - hyperopt_objs = sorted(hyperopt_objs, key=lambda x: x['name']) - - if args['print_one_column']: - print('\n'.join([s['name'] for s in hyperopt_objs])) - else: - _print_objs_tabular(hyperopt_objs, config.get('print_colorized', False)) - - def start_list_timeframes(args: Dict[str, Any]) -> None: """ Print timeframes available on Exchange From 236dc4800075a30a4c7ed1e52ef45be0d785ef76 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 12 Sep 2021 07:46:17 +0200 Subject: [PATCH 422/519] Update CI to use new hyperopt interface --- .github/workflows/ci.yml | 6 +++--- .travis.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb767efb1..228a60389 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,7 +87,7 @@ jobs: run: | cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all + freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all - name: Flake8 run: | @@ -180,7 +180,7 @@ jobs: run: | cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all + freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all - name: Flake8 run: | @@ -247,7 +247,7 @@ jobs: run: | cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all + freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all - name: Flake8 run: | diff --git a/.travis.yml b/.travis.yml index f2a6d508d..15c174bfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ jobs: - script: - cp config_examples/config_bittrex.example.json config.json - freqtrade create-userdir --userdir user_data - - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily name: hyperopt - script: flake8 name: flake8 From d8f48cf0e3b7376ec8047a0a22e525175cb5fc98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Sep 2021 03:01:14 +0000 Subject: [PATCH 423/519] Bump pandas from 1.3.2 to 1.3.3 Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.3.2 to 1.3.3. - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Changelog](https://github.com/pandas-dev/pandas/blob/master/RELEASE.md) - [Commits](https://github.com/pandas-dev/pandas/compare/v1.3.2...v1.3.3) --- updated-dependencies: - dependency-name: pandas dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e2bed0f9e..9e4ec97d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ numpy==1.21.2 -pandas==1.3.2 +pandas==1.3.3 ccxt==1.55.83 # Pin cryptography for now due to rust build errors with piwheels From 81039fce28dbae0fcea77a4ab952fb91a847d5aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Sep 2021 03:01:24 +0000 Subject: [PATCH 424/519] Bump progressbar2 from 3.53.1 to 3.53.2 Bumps [progressbar2](https://github.com/WoLpH/python-progressbar) from 3.53.1 to 3.53.2. - [Release notes](https://github.com/WoLpH/python-progressbar/releases) - [Changelog](https://github.com/WoLpH/python-progressbar/blob/develop/CHANGES.rst) - [Commits](https://github.com/WoLpH/python-progressbar/compare/v3.53.1...v3.53.2) --- updated-dependencies: - dependency-name: progressbar2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index d7f22634b..7dc55a9fc 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -8,4 +8,4 @@ scikit-optimize==0.8.1 filelock==3.0.12 joblib==1.0.1 psutil==5.8.0 -progressbar2==3.53.1 +progressbar2==3.53.2 From b13bd87625d122278644f8210d07b8cad13cd352 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Sep 2021 04:31:52 +0000 Subject: [PATCH 425/519] Bump ccxt from 1.55.83 to 1.56.30 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.55.83 to 1.56.30. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.55.83...1.56.30) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9e4ec97d6..aa729dd9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.21.2 pandas==1.3.3 -ccxt==1.55.83 +ccxt==1.56.30 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.8 aiohttp==3.7.4.post0 From 3cdd06f5627b7baee7b5ec3ead09e036080e9b66 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Sep 2021 19:32:51 +0200 Subject: [PATCH 426/519] Add PeriodicCache --- freqtrade/configuration/PeriodicCache.py | 19 +++++++++++++++ freqtrade/configuration/__init__.py | 1 + requirements-dev.txt | 2 ++ tests/test_periodiccache.py | 31 ++++++++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 freqtrade/configuration/PeriodicCache.py create mode 100644 tests/test_periodiccache.py diff --git a/freqtrade/configuration/PeriodicCache.py b/freqtrade/configuration/PeriodicCache.py new file mode 100644 index 000000000..25c0c47f3 --- /dev/null +++ b/freqtrade/configuration/PeriodicCache.py @@ -0,0 +1,19 @@ +from datetime import datetime, timezone + +from cachetools.ttl import TTLCache + + +class PeriodicCache(TTLCache): + """ + Special cache that expires at "straight" times + A timer with ttl of 3600 (1h) will expire at every full hour (:00). + """ + + def __init__(self, maxsize, ttl, getsizeof=None): + def local_timer(): + ts = datetime.now(timezone.utc).timestamp() + offset = (ts % ttl) + return ts - offset + + # Init with smlight offset + super().__init__(maxsize=maxsize, ttl=ttl-1e-5, timer=local_timer, getsizeof=getsizeof) diff --git a/freqtrade/configuration/__init__.py b/freqtrade/configuration/__init__.py index 607f9cdef..b1b268092 100644 --- a/freqtrade/configuration/__init__.py +++ b/freqtrade/configuration/__init__.py @@ -1,5 +1,6 @@ # flake8: noqa: F401 +from freqtrade.configuration.PeriodicCache import PeriodicCache from freqtrade.configuration.check_exchange import check_exchange, remove_credentials from freqtrade.configuration.config_setup import setup_utils_configuration from freqtrade.configuration.config_validation import validate_config_consistency diff --git a/requirements-dev.txt b/requirements-dev.txt index 34d5607f3..4859e1cc6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,6 +14,8 @@ pytest-cov==2.12.1 pytest-mock==3.6.1 pytest-random-order==1.0.4 isort==5.9.3 +# For datetime mocking +time-machine==2.4.0 # Convert jupyter notebooks to markdown documents nbconvert==6.1.0 diff --git a/tests/test_periodiccache.py b/tests/test_periodiccache.py new file mode 100644 index 000000000..ff9b53684 --- /dev/null +++ b/tests/test_periodiccache.py @@ -0,0 +1,31 @@ +from freqtrade.configuration import PeriodicCache +import time_machine + + +def test_ttl_cache(): + + with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: + + cache = PeriodicCache(5, ttl=60) + cache1h = PeriodicCache(5, ttl=3600) + + assert cache.timer() == 1630472400.0 + cache['a'] = 1235 + cache1h['a'] = 555123 + assert 'a' in cache + assert 'a' in cache1h + + t.move_to("2021-09-01 05:00:59 +00:00") + assert 'a' in cache + assert 'a' in cache1h + + # Cache expired + t.move_to("2021-09-01 05:01:00 +00:00") + assert 'a' not in cache + assert 'a' in cache1h + + t.move_to("2021-09-01 05:59:59 +00:00") + assert 'a' in cache1h + + t.move_to("2021-09-01 06:00:00 +00:00") + assert 'a' not in cache1h From 8afb3c4b70c3458772953c8be23976bd90a264ba Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Sep 2021 19:33:28 +0200 Subject: [PATCH 427/519] Move AgeFilter cache to instance level --- freqtrade/plugins/pairlist/AgeFilter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index dc5cab31e..32fef5fb0 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -18,14 +18,14 @@ logger = logging.getLogger(__name__) class AgeFilter(IPairList): - # Checked symbols cache (dictionary of ticker symbol => timestamp) - _symbolsChecked: Dict[str, int] = {} - def __init__(self, exchange, pairlistmanager, config: Dict[str, Any], pairlistconfig: Dict[str, Any], pairlist_pos: int) -> None: super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + # Checked symbols cache (dictionary of ticker symbol => timestamp) + self._symbolsChecked: Dict[str, int] = {} + self._min_days_listed = pairlistconfig.get('min_days_listed', 10) self._max_days_listed = pairlistconfig.get('max_days_listed', None) From a12c3ecc9b2c7fa005f4104f22cde5cba23829b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Sep 2021 20:00:22 +0200 Subject: [PATCH 428/519] Remove credentials whenever dry-run is set from within the exchange --- freqtrade/configuration/__init__.py | 2 +- freqtrade/configuration/check_exchange.py | 13 ------------- freqtrade/configuration/config_setup.py | 5 ++--- freqtrade/exchange/__init__.py | 2 +- freqtrade/exchange/common.py | 13 +++++++++++++ freqtrade/exchange/exchange.py | 5 +++-- freqtrade/optimize/backtesting.py | 5 ++--- freqtrade/optimize/edge_cli.py | 6 +++--- tests/commands/test_commands.py | 2 -- tests/exchange/test_ccxt_compat.py | 2 +- tests/exchange/test_exchange.py | 19 ++++++++++++++++++- tests/test_configuration.py | 15 +-------------- 12 files changed, 45 insertions(+), 44 deletions(-) diff --git a/freqtrade/configuration/__init__.py b/freqtrade/configuration/__init__.py index 607f9cdef..730a4e47f 100644 --- a/freqtrade/configuration/__init__.py +++ b/freqtrade/configuration/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa: F401 -from freqtrade.configuration.check_exchange import check_exchange, remove_credentials +from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.config_setup import setup_utils_configuration from freqtrade.configuration.config_validation import validate_config_consistency from freqtrade.configuration.configuration import Configuration diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index c4f038103..fa1f47f9b 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -10,19 +10,6 @@ from freqtrade.exchange import (available_exchanges, is_exchange_known_ccxt, logger = logging.getLogger(__name__) -def remove_credentials(config: Dict[str, Any]) -> None: - """ - Removes exchange keys from the configuration and specifies dry-run - Used for backtesting / hyperopt / edge and utils. - Modifies the input dict! - """ - config['exchange']['key'] = '' - config['exchange']['secret'] = '' - config['exchange']['password'] = '' - config['exchange']['uid'] = '' - config['dry_run'] = True - - def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: """ Check if the exchange name in the config file is supported by Freqtrade diff --git a/freqtrade/configuration/config_setup.py b/freqtrade/configuration/config_setup.py index 22836ab19..02f2d4089 100644 --- a/freqtrade/configuration/config_setup.py +++ b/freqtrade/configuration/config_setup.py @@ -3,7 +3,6 @@ from typing import Any, Dict from freqtrade.enums import RunMode -from .check_exchange import remove_credentials from .config_validation import validate_config_consistency from .configuration import Configuration @@ -21,8 +20,8 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str configuration = Configuration(args, method) config = configuration.get_config() - # Ensure we do not use Exchange credentials - remove_credentials(config) + # Ensure these modes are using Dry-run + config['dry_run'] = True validate_config_consistency(config) return config diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index b0c88a51a..b08213d28 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa: F401 # isort: off -from freqtrade.exchange.common import MAP_EXCHANGE_CHILDCLASS +from freqtrade.exchange.common import remove_credentials, MAP_EXCHANGE_CHILDCLASS from freqtrade.exchange.exchange import Exchange # isort: on from freqtrade.exchange.bibox import Bibox diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index 694aa3aa2..7b89adf06 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -51,6 +51,19 @@ EXCHANGE_HAS_OPTIONAL = [ ] +def remove_credentials(config) -> None: + """ + Removes exchange keys from the configuration and specifies dry-run + Used for backtesting / hyperopt / edge and utils. + Modifies the input dict! + """ + if config.get('dry_run', False): + config['exchange']['key'] = '' + config['exchange']['secret'] = '' + config['exchange']['password'] = '' + config['exchange']['uid'] = '' + + def calculate_backoff(retrycount, max_retries): """ Calculate backoff diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 11de5411f..2b9b08d70 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -26,8 +26,8 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFun InvalidOrderException, OperationalException, PricingError, RetryableOrderError, TemporaryError) from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, BAD_EXCHANGES, - EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, retrier, - retrier_async) + EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, + remove_credentials, retrier, retrier_async) from freqtrade.misc import chunks, deep_merge_dicts, safe_value_fallback2 from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist @@ -104,6 +104,7 @@ class Exchange: # Holds all open sell orders for dry_run self._dry_run_open_orders: Dict[str, Any] = {} + remove_credentials(config) if config['dry_run']: logger.info('Instance is running with dry_run enabled') diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 4b52e104b..3e06bfa1b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple from pandas import DataFrame -from freqtrade.configuration import TimeRange, remove_credentials, validate_config_consistency +from freqtrade.configuration import TimeRange, validate_config_consistency from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.data import history from freqtrade.data.btanalysis import trade_list_to_dataframe @@ -61,8 +61,7 @@ class Backtesting: self.config = config self.results: Optional[Dict[str, Any]] = None - # Reset keys for backtesting - remove_credentials(self.config) + config['dry_run'] = True self.strategylist: List[IStrategy] = [] self.all_results: Dict[str, Dict] = {} diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index aab7def05..417faa685 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -7,7 +7,7 @@ import logging from typing import Any, Dict from freqtrade import constants -from freqtrade.configuration import TimeRange, remove_credentials, validate_config_consistency +from freqtrade.configuration import TimeRange, validate_config_consistency from freqtrade.edge import Edge from freqtrade.optimize.optimize_reports import generate_edge_table from freqtrade.resolvers import ExchangeResolver, StrategyResolver @@ -28,8 +28,8 @@ class EdgeCli: def __init__(self, config: Dict[str, Any]) -> None: self.config = config - # Reset keys for edge - remove_credentials(self.config) + # Ensure using dry-run + self.config['dry_run'] = True self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) self.strategy = StrategyResolver.load_strategy(self.config) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 1da9e5100..87040bfd5 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -32,8 +32,6 @@ def test_setup_utils_configuration(): config = setup_utils_configuration(get_args(args), RunMode.OTHER) assert "exchange" in config assert config['dry_run'] is True - assert config['exchange']['key'] == '' - assert config['exchange']['secret'] == '' def test_start_trading_fail(mocker, caplog): diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index 3a32d108b..0f7963e36 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -54,7 +54,7 @@ EXCHANGES = { def exchange_conf(): config = get_default_conf((Path(__file__).parent / "testdata").resolve()) config['exchange']['pair_whitelist'] = [] - config['dry_run'] = False + # config['dry_run'] = False return config diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 90e19782e..baf1f081a 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1,5 +1,6 @@ import copy import logging +from copy import deepcopy from datetime import datetime, timedelta, timezone from math import isclose from random import randint @@ -14,7 +15,7 @@ from freqtrade.exceptions import (DDosProtection, DependencyException, InvalidOr OperationalException, PricingError, TemporaryError) from freqtrade.exchange import Binance, Bittrex, Exchange, Kraken from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, API_RETRY_COUNT, - calculate_backoff) + calculate_backoff, remove_credentials) from freqtrade.exchange.exchange import (market_is_active, timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, timeframe_to_prev_date, timeframe_to_seconds) @@ -78,6 +79,22 @@ def test_init(default_conf, mocker, caplog): assert log_has('Instance is running with dry_run enabled', caplog) +def test_remove_credentials(default_conf, caplog) -> None: + conf = deepcopy(default_conf) + conf['dry_run'] = False + remove_credentials(conf) + + assert conf['exchange']['key'] != '' + assert conf['exchange']['secret'] != '' + + conf['dry_run'] = True + remove_credentials(conf) + assert conf['exchange']['key'] == '' + assert conf['exchange']['secret'] == '' + assert conf['exchange']['password'] == '' + assert conf['exchange']['uid'] == '' + + def test_init_ccxt_kwargs(default_conf, mocker, caplog): mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 9aea4fa11..1ce45e4d5 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -11,8 +11,7 @@ import pytest from jsonschema import ValidationError from freqtrade.commands import Arguments -from freqtrade.configuration import (Configuration, check_exchange, remove_credentials, - validate_config_consistency) +from freqtrade.configuration import Configuration, check_exchange, validate_config_consistency from freqtrade.configuration.config_validation import validate_config_schema from freqtrade.configuration.deprecated_settings import (check_conflicting_settings, process_deprecated_setting, @@ -617,18 +616,6 @@ def test_check_exchange(default_conf, caplog) -> None: check_exchange(default_conf) -def test_remove_credentials(default_conf, caplog) -> None: - conf = deepcopy(default_conf) - conf['dry_run'] = False - remove_credentials(conf) - - assert conf['dry_run'] is True - assert conf['exchange']['key'] == '' - assert conf['exchange']['secret'] == '' - assert conf['exchange']['password'] == '' - assert conf['exchange']['uid'] == '' - - def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) From c9ba52d7321b2cae17776c66723c837a75c3bfdf Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Sep 2021 06:30:18 +0200 Subject: [PATCH 429/519] Expire cached pairs in age-filter once per day --- freqtrade/plugins/pairlist/AgeFilter.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 32fef5fb0..1fba00123 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -11,6 +11,7 @@ from pandas import DataFrame from freqtrade.exceptions import OperationalException from freqtrade.misc import plural from freqtrade.plugins.pairlist.IPairList import IPairList +from freqtrade.configuration import PeriodicCache logger = logging.getLogger(__name__) @@ -25,6 +26,7 @@ class AgeFilter(IPairList): # Checked symbols cache (dictionary of ticker symbol => timestamp) self._symbolsChecked: Dict[str, int] = {} + self._too_young_pairs = PeriodicCache(maxsize=1000, ttl=86_400) self._min_days_listed = pairlistconfig.get('min_days_listed', 10) self._max_days_listed = pairlistconfig.get('max_days_listed', None) @@ -69,10 +71,12 @@ class AgeFilter(IPairList): :param tickers: Tickers (from exchange.get_tickers()). May be cached. :return: new allowlist """ - needed_pairs = [(p, '1d') for p in pairlist if p not in self._symbolsChecked] + needed_pairs = [ + (p, '1d') for p in pairlist + if p not in self._symbolsChecked and p not in self._too_young_pairs] if not needed_pairs: return pairlist - + logger.info(f"needed pairs {needed_pairs}") since_days = -( self._max_days_listed if self._max_days_listed else self._min_days_listed ) - 1 @@ -118,5 +122,6 @@ class AgeFilter(IPairList): " or more than " f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}" ) if self._max_days_listed else ''), logger.info) + self._too_young_pairs[pair] = arrow.utcnow().int_timestamp * 1000 return False return False From 3ce5197e8d07471e75be14df435c3d291b3b69c4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Sep 2021 06:45:26 +0200 Subject: [PATCH 430/519] Add Tests for AgeFilter caching closes #5552 --- freqtrade/configuration/__init__.py | 2 +- freqtrade/plugins/pairlist/AgeFilter.py | 11 ++-- tests/plugins/test_pairlist.py | 69 ++++++++++++++++--------- tests/test_periodiccache.py | 3 +- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/freqtrade/configuration/__init__.py b/freqtrade/configuration/__init__.py index b1b268092..dccbb14b3 100644 --- a/freqtrade/configuration/__init__.py +++ b/freqtrade/configuration/__init__.py @@ -1,8 +1,8 @@ # flake8: noqa: F401 -from freqtrade.configuration.PeriodicCache import PeriodicCache from freqtrade.configuration.check_exchange import check_exchange, remove_credentials from freqtrade.configuration.config_setup import setup_utils_configuration from freqtrade.configuration.config_validation import validate_config_consistency from freqtrade.configuration.configuration import Configuration +from freqtrade.configuration.PeriodicCache import PeriodicCache from freqtrade.configuration.timerange import TimeRange diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 1fba00123..c43bd0c4c 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -8,10 +8,10 @@ from typing import Any, Dict, List, Optional import arrow from pandas import DataFrame +from freqtrade.configuration import PeriodicCache from freqtrade.exceptions import OperationalException from freqtrade.misc import plural from freqtrade.plugins.pairlist.IPairList import IPairList -from freqtrade.configuration import PeriodicCache logger = logging.getLogger(__name__) @@ -26,7 +26,7 @@ class AgeFilter(IPairList): # Checked symbols cache (dictionary of ticker symbol => timestamp) self._symbolsChecked: Dict[str, int] = {} - self._too_young_pairs = PeriodicCache(maxsize=1000, ttl=86_400) + self._symbolsCheckFailed = PeriodicCache(maxsize=1000, ttl=86_400) self._min_days_listed = pairlistconfig.get('min_days_listed', 10) self._max_days_listed = pairlistconfig.get('max_days_listed', None) @@ -73,9 +73,10 @@ class AgeFilter(IPairList): """ needed_pairs = [ (p, '1d') for p in pairlist - if p not in self._symbolsChecked and p not in self._too_young_pairs] + if p not in self._symbolsChecked and p not in self._symbolsCheckFailed] if not needed_pairs: - return pairlist + # Remove pairs that have been removed before + return [p for p in pairlist if p not in self._symbolsCheckFailed] logger.info(f"needed pairs {needed_pairs}") since_days = -( self._max_days_listed if self._max_days_listed else self._min_days_listed @@ -122,6 +123,6 @@ class AgeFilter(IPairList): " or more than " f"{self._max_days_listed} {plural(self._max_days_listed, 'day')}" ) if self._max_days_listed else ''), logger.info) - self._too_young_pairs[pair] = arrow.utcnow().int_timestamp * 1000 + self._symbolsCheckFailed[pair] = arrow.utcnow().int_timestamp * 1000 return False return False diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 5f0701a22..3cdf6d341 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -4,6 +4,7 @@ import time from unittest.mock import MagicMock, PropertyMock import pytest +import time_machine from freqtrade.constants import AVAILABLE_PAIRLISTS from freqtrade.exceptions import OperationalException @@ -815,32 +816,52 @@ def test_agefilter_min_days_listed_too_large(mocker, default_conf, markets, tick def test_agefilter_caching(mocker, markets, whitelist_conf_agefilter, tickers, ohlcv_history): - ohlcv_data = { - ('ETH/BTC', '1d'): ohlcv_history, - ('TKN/BTC', '1d'): ohlcv_history, - ('LTC/BTC', '1d'): ohlcv_history, - } - mocker.patch.multiple('freqtrade.exchange.Exchange', - markets=PropertyMock(return_value=markets), - exchange_has=MagicMock(return_value=True), - get_tickers=tickers - ) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - refresh_latest_ohlcv=MagicMock(return_value=ohlcv_data), - ) + with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: + ohlcv_data = { + ('ETH/BTC', '1d'): ohlcv_history, + ('TKN/BTC', '1d'): ohlcv_history, + ('LTC/BTC', '1d'): ohlcv_history, + ('XRP/BTC', '1d'): ohlcv_history.iloc[[0]], + } + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + markets=PropertyMock(return_value=markets), + exchange_has=MagicMock(return_value=True), + get_tickers=tickers, + refresh_latest_ohlcv=MagicMock(return_value=ohlcv_data), + ) - freqtrade = get_patched_freqtradebot(mocker, whitelist_conf_agefilter) - assert freqtrade.exchange.refresh_latest_ohlcv.call_count == 0 - freqtrade.pairlists.refresh_pairlist() - assert len(freqtrade.pairlists.whitelist) == 3 - assert freqtrade.exchange.refresh_latest_ohlcv.call_count > 0 + freqtrade = get_patched_freqtradebot(mocker, whitelist_conf_agefilter) + assert freqtrade.exchange.refresh_latest_ohlcv.call_count == 0 + freqtrade.pairlists.refresh_pairlist() + assert len(freqtrade.pairlists.whitelist) == 3 + assert freqtrade.exchange.refresh_latest_ohlcv.call_count > 0 - previous_call_count = freqtrade.exchange.refresh_latest_ohlcv.call_count - freqtrade.pairlists.refresh_pairlist() - assert len(freqtrade.pairlists.whitelist) == 3 - # Called once for XRP/BTC - assert freqtrade.exchange.refresh_latest_ohlcv.call_count == previous_call_count + 1 + previous_call_count = freqtrade.exchange.refresh_latest_ohlcv.call_count + freqtrade.pairlists.refresh_pairlist() + assert len(freqtrade.pairlists.whitelist) == 3 + # Call to XRP/BTC cached + assert freqtrade.exchange.refresh_latest_ohlcv.call_count == previous_call_count + # Move to next day + t.move_to("2021-09-02 01:00:00 +00:00") + freqtrade.pairlists.refresh_pairlist() + assert len(freqtrade.pairlists.whitelist) == 3 + assert freqtrade.exchange.refresh_latest_ohlcv.call_count == previous_call_count + 1 + + # Move another day with fresh mocks (now the pair is old enough) + t.move_to("2021-09-03 01:00:00 +00:00") + # Called once for XRP/BTC + ohlcv_data = { + ('ETH/BTC', '1d'): ohlcv_history, + ('TKN/BTC', '1d'): ohlcv_history, + ('LTC/BTC', '1d'): ohlcv_history, + ('XRP/BTC', '1d'): ohlcv_history, + } + mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', return_value=ohlcv_data) + freqtrade.pairlists.refresh_pairlist() + assert len(freqtrade.pairlists.whitelist) == 4 + # Called once (only for XRP/BTC) + assert freqtrade.exchange.refresh_latest_ohlcv.call_count == 1 def test_OffsetFilter_error(mocker, whitelist_conf) -> None: diff --git a/tests/test_periodiccache.py b/tests/test_periodiccache.py index ff9b53684..f874f9041 100644 --- a/tests/test_periodiccache.py +++ b/tests/test_periodiccache.py @@ -1,6 +1,7 @@ -from freqtrade.configuration import PeriodicCache import time_machine +from freqtrade.configuration import PeriodicCache + def test_ttl_cache(): From 35eda8c8c7c304051cae6b2a22453f56aa0ea4a3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Sep 2021 07:07:20 +0200 Subject: [PATCH 431/519] Improve agefilter test --- tests/plugins/test_pairlist.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 3cdf6d341..34770c03d 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -821,7 +821,6 @@ def test_agefilter_caching(mocker, markets, whitelist_conf_agefilter, tickers, o ('ETH/BTC', '1d'): ohlcv_history, ('TKN/BTC', '1d'): ohlcv_history, ('LTC/BTC', '1d'): ohlcv_history, - ('XRP/BTC', '1d'): ohlcv_history.iloc[[0]], } mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -837,16 +836,28 @@ def test_agefilter_caching(mocker, markets, whitelist_conf_agefilter, tickers, o assert len(freqtrade.pairlists.whitelist) == 3 assert freqtrade.exchange.refresh_latest_ohlcv.call_count > 0 - previous_call_count = freqtrade.exchange.refresh_latest_ohlcv.call_count freqtrade.pairlists.refresh_pairlist() assert len(freqtrade.pairlists.whitelist) == 3 # Call to XRP/BTC cached - assert freqtrade.exchange.refresh_latest_ohlcv.call_count == previous_call_count - # Move to next day - t.move_to("2021-09-02 01:00:00 +00:00") + assert freqtrade.exchange.refresh_latest_ohlcv.call_count == 2 + + ohlcv_data = { + ('ETH/BTC', '1d'): ohlcv_history, + ('TKN/BTC', '1d'): ohlcv_history, + ('LTC/BTC', '1d'): ohlcv_history, + ('XRP/BTC', '1d'): ohlcv_history.iloc[[0]], + } + mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', return_value=ohlcv_data) freqtrade.pairlists.refresh_pairlist() assert len(freqtrade.pairlists.whitelist) == 3 - assert freqtrade.exchange.refresh_latest_ohlcv.call_count == previous_call_count + 1 + assert freqtrade.exchange.refresh_latest_ohlcv.call_count == 1 + + # Move to next day + t.move_to("2021-09-02 01:00:00 +00:00") + mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', return_value=ohlcv_data) + freqtrade.pairlists.refresh_pairlist() + assert len(freqtrade.pairlists.whitelist) == 3 + assert freqtrade.exchange.refresh_latest_ohlcv.call_count == 1 # Move another day with fresh mocks (now the pair is old enough) t.move_to("2021-09-03 01:00:00 +00:00") From 4e2b1764b8af34f67305f213ac29dcff3a59da37 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Sep 2021 20:24:44 +0200 Subject: [PATCH 432/519] ccxt_compat_tests must run with dry-run=False --- tests/exchange/test_ccxt_compat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index 0f7963e36..d71dbe015 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -54,7 +54,9 @@ EXCHANGES = { def exchange_conf(): config = get_default_conf((Path(__file__).parent / "testdata").resolve()) config['exchange']['pair_whitelist'] = [] - # config['dry_run'] = False + config['exchange']['key'] = '' + config['exchange']['secret'] = '' + config['dry_run'] = False return config From f7bae81d968a20e815d97d1dc72f86f0573edad2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 15 Sep 2021 19:56:12 +0200 Subject: [PATCH 433/519] Dataframe should be copied after populate_indicator Without that, PerformanceWarnings can appear throughout hyperopt which are unnecessary and missleading for users closes #5408 --- freqtrade/strategy/interface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 91963f223..00ad3faf0 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -777,10 +777,11 @@ class IStrategy(ABC, HyperStrategyMixin): Does not run advise_buy or advise_sell! Used by optimize operations only, not during dry / live runs. Using .copy() to get a fresh copy of the dataframe for every strategy run. + Also copy on output to avoid PerformanceWarnings pandas 1.3.0 started to show. Has positive effects on memory usage for whatever reason - also when using only one strategy. """ - return {pair: self.advise_indicators(pair_data.copy(), {'pair': pair}) + return {pair: self.advise_indicators(pair_data.copy(), {'pair': pair}).copy() for pair, pair_data in data.items()} def advise_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: From 57ea0c322f8018992cb6c4303a43af29da310227 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 15 Sep 2021 20:20:31 +0200 Subject: [PATCH 434/519] Rename indicator_space to buy_indicator_space --- docs/hyperopt.md | 2 +- freqtrade/optimize/hyperopt.py | 2 +- freqtrade/optimize/hyperopt_auto.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index e69b761c4..09d43939a 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -677,7 +677,7 @@ If you are optimizing ROI, Freqtrade creates the 'roi' optimization hyperspace f These ranges should be sufficient in most cases. The minutes in the steps (ROI dict keys) are scaled linearly depending on the timeframe used. The ROI values in the steps (ROI dict values) are scaled logarithmically depending on the timeframe used. -If you have the `generate_roi_table()` and `roi_space()` methods in your custom hyperopt file, remove them in order to utilize these adaptive ROI tables and the ROI hyperoptimization space generated by Freqtrade by default. +If you have the `generate_roi_table()` and `roi_space()` methods in your custom hyperopt, remove them in order to utilize these adaptive ROI tables and the ROI hyperoptimization space generated by Freqtrade by default. Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps). diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 14b155546..d047b7311 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -241,7 +241,7 @@ class Hyperopt: if HyperoptTools.has_space(self.config, 'buy'): logger.debug("Hyperopt has 'buy' space") - self.buy_space = self.custom_hyperopt.indicator_space() + self.buy_space = self.custom_hyperopt.buy_indicator_space() if HyperoptTools.has_space(self.config, 'sell'): logger.debug("Hyperopt has 'sell' space") diff --git a/freqtrade/optimize/hyperopt_auto.py b/freqtrade/optimize/hyperopt_auto.py index 1f11cec80..80fe090c2 100644 --- a/freqtrade/optimize/hyperopt_auto.py +++ b/freqtrade/optimize/hyperopt_auto.py @@ -56,7 +56,7 @@ class HyperOptAuto(IHyperOpt): else: _format_exception_message(category) - def indicator_space(self) -> List['Dimension']: + def buy_indicator_space(self) -> List['Dimension']: return self._get_indicator_space('buy') def sell_indicator_space(self) -> List['Dimension']: From 90ad1789323e2c3061110f34bd5d3c54d60b65d7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 15 Sep 2021 21:04:25 +0200 Subject: [PATCH 435/519] Remove verbosity of edge --- freqtrade/plugins/pairlist/AgeFilter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index c43bd0c4c..5627d82ce 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -77,7 +77,7 @@ class AgeFilter(IPairList): if not needed_pairs: # Remove pairs that have been removed before return [p for p in pairlist if p not in self._symbolsCheckFailed] - logger.info(f"needed pairs {needed_pairs}") + since_days = -( self._max_days_listed if self._max_days_listed else self._min_days_listed ) - 1 From c0811ae8969799857e987ed7e93d1c1e78dd3a2f Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 15 Sep 2021 21:36:53 +0200 Subject: [PATCH 436/519] Add possibility to override estimator from within hyperopt --- docs/advanced-hyperopt.md | 32 ++++++++++++++++++++++++ freqtrade/optimize/hyperopt.py | 8 ++++-- freqtrade/optimize/hyperopt_auto.py | 5 +++- freqtrade/optimize/hyperopt_interface.py | 13 +++++++++- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/docs/advanced-hyperopt.md b/docs/advanced-hyperopt.md index f2f52b7dd..f5a52ff49 100644 --- a/docs/advanced-hyperopt.md +++ b/docs/advanced-hyperopt.md @@ -98,6 +98,38 @@ class MyAwesomeStrategy(IStrategy): !!! Note All overrides are optional and can be mixed/matched as necessary. +### Overriding Base estimator + +You can define your own estimator for Hyperopt by implementing `generate_estimator()` in the Hyperopt subclass. + +```python +class MyAwesomeStrategy(IStrategy): + class HyperOpt: + def generate_estimator(): + return "RF" + +``` + +Possible values are either one of "GP", "RF", "ET", "GBRT" (Details can be found in the [scikit-optimize documentation](https://scikit-optimize.github.io/)), or "an instance of a class that inherits from `RegressorMixin` (from sklearn) and where the `predict` method has an optional `return_std` argument, which returns `std(Y | x)` along with `E[Y | x]`". + +Some research will be necessary to find additional Regressors. + +Example for `ExtraTreesRegressor` ("ET") with additional parameters: + +```python +class MyAwesomeStrategy(IStrategy): + class HyperOpt: + def generate_estimator(): + from skopt.learning import ExtraTreesRegressor + # Corresponds to "ET" - but allows additional parameters. + return ExtraTreesRegressor(n_estimators=100) + +``` + +!!! Note + While custom estimators can be provided, it's up to you as User to do research on possible parameters and analyze / understand which ones should be used. + If you're unsure about this, best use one of the Defaults (`"ET"` has proven to be the most versatile) without further parameters. + ## Space options For the additional spaces, scikit-optimize (in combination with Freqtrade) provides the following space types: diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index d047b7311..56d11934a 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -365,10 +365,14 @@ class Hyperopt: } def get_optimizer(self, dimensions: List[Dimension], cpu_count) -> Optimizer: + estimator = self.custom_hyperopt.generate_estimator() + logger.info(f"Using estimator {estimator}.") + # TODO: Impact of changing acq_optimizer to "sampling" is unclear + # (other than that it fails with other optimizers when using custom sklearn regressors) return Optimizer( dimensions, - base_estimator="ET", - acq_optimizer="auto", + base_estimator=estimator, + acq_optimizer="sampling", n_initial_points=INITIAL_POINTS, acq_optimizer_kwargs={'n_jobs': cpu_count}, random_state=self.random_state, diff --git a/freqtrade/optimize/hyperopt_auto.py b/freqtrade/optimize/hyperopt_auto.py index 80fe090c2..c1c769c72 100644 --- a/freqtrade/optimize/hyperopt_auto.py +++ b/freqtrade/optimize/hyperopt_auto.py @@ -12,7 +12,7 @@ from freqtrade.exceptions import OperationalException with suppress(ImportError): from skopt.space import Dimension -from freqtrade.optimize.hyperopt_interface import IHyperOpt +from freqtrade.optimize.hyperopt_interface import EstimatorType, IHyperOpt def _format_exception_message(space: str) -> str: @@ -79,3 +79,6 @@ class HyperOptAuto(IHyperOpt): def trailing_space(self) -> List['Dimension']: return self._get_func('trailing_space')() + + def generate_estimator(self) -> EstimatorType: + return self._get_func('generate_estimator')() diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 8fb40f557..53b4f087c 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -5,8 +5,9 @@ This module defines the interface to apply for hyperopt import logging import math from abc import ABC -from typing import Dict, List +from typing import Dict, List, Union +from sklearn.base import RegressorMixin from skopt.space import Categorical, Dimension, Integer from freqtrade.exchange import timeframe_to_minutes @@ -17,6 +18,8 @@ from freqtrade.strategy import IStrategy logger = logging.getLogger(__name__) +EstimatorType = Union[RegressorMixin, str] + class IHyperOpt(ABC): """ @@ -37,6 +40,14 @@ class IHyperOpt(ABC): IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED IHyperOpt.timeframe = str(config['timeframe']) + def generate_estimator(self) -> EstimatorType: + """ + Return base_estimator. + Can be any of "GP", "RF", "ET", "GBRT" or an instance of a class + inheriting from RegressorMixin (from sklearn). + """ + return 'ET' + def generate_roi_table(self, params: Dict) -> Dict[int, float]: """ Create a ROI table. From 994c3c3a4c5f36c02d249f4c13466b284c4991af Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 16 Sep 2021 07:13:25 +0200 Subject: [PATCH 437/519] Add some errorhandling for custom estimator --- freqtrade/optimize/hyperopt.py | 14 ++++++++++---- tests/optimize/test_hyperopt.py | 4 ++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 56d11934a..9549b4054 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -45,7 +45,7 @@ progressbar.streams.wrap_stdout() logger = logging.getLogger(__name__) -INITIAL_POINTS = 30 +INITIAL_POINTS = 5 # Keep no more than SKOPT_MODEL_QUEUE_SIZE models # in the skopt model queue, to optimize memory consumption @@ -366,13 +366,19 @@ class Hyperopt: def get_optimizer(self, dimensions: List[Dimension], cpu_count) -> Optimizer: estimator = self.custom_hyperopt.generate_estimator() + + acq_optimizer = "sampling" + if isinstance(estimator, str): + if estimator not in ("GP", "RF", "ET", "GBRT"): + raise OperationalException(f"Estimator {estimator} not supported.") + else: + acq_optimizer = "auto" + logger.info(f"Using estimator {estimator}.") - # TODO: Impact of changing acq_optimizer to "sampling" is unclear - # (other than that it fails with other optimizers when using custom sklearn regressors) return Optimizer( dimensions, base_estimator=estimator, - acq_optimizer="sampling", + acq_optimizer=acq_optimizer, n_initial_points=INITIAL_POINTS, acq_optimizer_kwargs={'n_jobs': cpu_count}, random_state=self.random_state, diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index b34c3a916..e4ce29d44 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -884,6 +884,10 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: assert hyperopt.backtesting.strategy.buy_rsi.value != 35 assert hyperopt.backtesting.strategy.sell_rsi.value != 74 + hyperopt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: 'ET1' + with pytest.raises(OperationalException, match="Estimator ET1 not supported."): + hyperopt.get_optimizer([], 2) + def test_SKDecimal(): space = SKDecimal(1, 2, decimals=2) From 457e738b4a049cbc522987612dc2e4bd91b272e9 Mon Sep 17 00:00:00 2001 From: Sergey Khliustin Date: Thu, 16 Sep 2021 14:48:02 +0300 Subject: [PATCH 438/519] Added days parameter to PerformanceFilter --- freqtrade/persistence/models.py | 28 ++++++++++++++++++- .../plugins/pairlist/PerformanceFilter.py | 15 ++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 8c8c1e0a9..91a26eba7 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -2,7 +2,7 @@ This module contains the class to persist trades into SQLite """ import logging -from datetime import datetime, timezone +from datetime import datetime, timezone, timedelta from decimal import Decimal from typing import Any, Dict, List, Optional @@ -856,6 +856,32 @@ class Trade(_DECL_BASE, LocalTrade): for pair, profit, profit_abs, count in pair_rates ] + @staticmethod + def get_performance(days: int) -> List[Dict[str, Any]]: + """ + Returns List of dicts containing all Trades, including profit and trade count + NOTE: Not supported in Backtesting. + """ + start_date = datetime.today() - timedelta(days) + pair_rates = Trade.query.with_entities( + Trade.pair, + func.sum(Trade.close_profit).label('profit_sum'), + func.sum(Trade.close_profit_abs).label('profit_sum_abs'), + func.count(Trade.pair).label('count') + ).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date))\ + .group_by(Trade.pair) \ + .order_by(desc('profit_sum_abs')) \ + .all() + return [ + { + 'pair': pair, + 'profit': profit, + 'profit_abs': profit_abs, + 'count': count + } + for pair, profit, profit_abs, count in pair_rates + ] + @staticmethod def get_best_pair(start_date: datetime = datetime.fromtimestamp(0)): """ diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 46a289ae6..4d530fe88 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -2,11 +2,12 @@ Performance pair list filter """ import logging -from typing import Dict, List +from typing import Dict, List, Any import pandas as pd from freqtrade.persistence import Trade +from freqtrade.exceptions import OperationalException from freqtrade.plugins.pairlist.IPairList import IPairList @@ -15,6 +16,13 @@ logger = logging.getLogger(__name__) class PerformanceFilter(IPairList): + def __init__(self, exchange, pairlistmanager, + config: Dict[str, Any], pairlistconfig: Dict[str, Any], + pairlist_pos: int) -> None: + super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + + self._days = pairlistconfig.get('days', 0) + @property def needstickers(self) -> bool: """ @@ -40,7 +48,10 @@ class PerformanceFilter(IPairList): """ # Get the trading performance for pairs from database try: - performance = pd.DataFrame(Trade.get_overall_performance()) + if self._days > 0: + performance = pd.DataFrame(Trade.get_performance(self._days)) + else: + performance = pd.DataFrame(Trade.get_overall_performance()) except AttributeError: # Performancefilter does not work in backtesting. self.log_once("PerformanceFilter is not available in this mode.", logger.warning) From 54ef36a4971d77467a10fa343857aec27a312e5a Mon Sep 17 00:00:00 2001 From: Sergey Khliustin Date: Fri, 17 Sep 2021 13:45:44 +0300 Subject: [PATCH 439/519] Updates after review to PerformanceFilter days param --- freqtrade/persistence/models.py | 34 ++++--------------- .../plugins/pairlist/PerformanceFilter.py | 5 +-- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 91a26eba7..222be7169 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -832,43 +832,21 @@ class Trade(_DECL_BASE, LocalTrade): return total_open_stake_amount or 0 @staticmethod - def get_overall_performance() -> List[Dict[str, Any]]: + def get_overall_performance(days=None) -> List[Dict[str, Any]]: """ Returns List of dicts containing all Trades, including profit and trade count NOTE: Not supported in Backtesting. """ + filters = [Trade.is_open.is_(False)] + if days: + start_date = datetime.today() - timedelta(days) + filters.append((Trade.close_date >= start_date)) pair_rates = Trade.query.with_entities( Trade.pair, func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit_abs).label('profit_sum_abs'), func.count(Trade.pair).label('count') - ).filter(Trade.is_open.is_(False))\ - .group_by(Trade.pair) \ - .order_by(desc('profit_sum_abs')) \ - .all() - return [ - { - 'pair': pair, - 'profit': profit, - 'profit_abs': profit_abs, - 'count': count - } - for pair, profit, profit_abs, count in pair_rates - ] - - @staticmethod - def get_performance(days: int) -> List[Dict[str, Any]]: - """ - Returns List of dicts containing all Trades, including profit and trade count - NOTE: Not supported in Backtesting. - """ - start_date = datetime.today() - timedelta(days) - pair_rates = Trade.query.with_entities( - Trade.pair, - func.sum(Trade.close_profit).label('profit_sum'), - func.sum(Trade.close_profit_abs).label('profit_sum_abs'), - func.count(Trade.pair).label('count') - ).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date))\ + ).filter(filters)\ .group_by(Trade.pair) \ .order_by(desc('profit_sum_abs')) \ .all() diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 4d530fe88..920b884a0 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -48,10 +48,7 @@ class PerformanceFilter(IPairList): """ # Get the trading performance for pairs from database try: - if self._days > 0: - performance = pd.DataFrame(Trade.get_performance(self._days)) - else: - performance = pd.DataFrame(Trade.get_overall_performance()) + performance = pd.DataFrame(Trade.get_overall_performance(self._days)) except AttributeError: # Performancefilter does not work in backtesting. self.log_once("PerformanceFilter is not available in this mode.", logger.warning) From 982deeedf04cb1be9c5ae7b1ec3598a9d09a4b9a Mon Sep 17 00:00:00 2001 From: sergeykhliustin <51409210+sergeykhliustin@users.noreply.github.com> Date: Fri, 17 Sep 2021 18:23:13 +0300 Subject: [PATCH 440/519] Update freqtrade/persistence/models.py Co-authored-by: Matthias --- freqtrade/persistence/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 222be7169..a2e30d58c 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -846,7 +846,7 @@ class Trade(_DECL_BASE, LocalTrade): func.sum(Trade.close_profit).label('profit_sum'), func.sum(Trade.close_profit_abs).label('profit_sum_abs'), func.count(Trade.pair).label('count') - ).filter(filters)\ + ).filter(*filters)\ .group_by(Trade.pair) \ .order_by(desc('profit_sum_abs')) \ .all() From 3a98fb72a491536cef7b601573c3ca45f64e8fc5 Mon Sep 17 00:00:00 2001 From: raphael Date: Fri, 17 Sep 2021 11:42:33 -0400 Subject: [PATCH 441/519] Update prepare_trials_columns() return type Was returning str, updated to pd.DataFrame --- freqtrade/optimize/hyperopt_tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index b2e024f65..2fcab0b0f 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -7,6 +7,7 @@ from pathlib import Path from typing import Any, Dict, Iterator, List, Optional, Tuple import numpy as np +import pandas as pd import rapidjson import tabulate from colorama import Fore, Style @@ -298,7 +299,7 @@ class HyperoptTools(): f"Objective: {results['loss']:.5f}") @staticmethod - def prepare_trials_columns(trials, legacy_mode: bool, has_drawdown: bool) -> str: + def prepare_trials_columns(trials, legacy_mode: bool, has_drawdown: bool) -> pd.DataFrame: trials['Best'] = '' From 5fc993231a2d138e9ff994f1d4548888551dd73c Mon Sep 17 00:00:00 2001 From: raphael Date: Fri, 17 Sep 2021 11:51:55 -0400 Subject: [PATCH 442/519] Update HyperoptTools.export_csv_file usage --- freqtrade/commands/hyperopt_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 089529d15..614c4b3f5 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -53,7 +53,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: if epochs and export_csv: HyperoptTools.export_csv_file( - config, epochs, total_epochs, not config.get('hyperopt_list_best', False), export_csv + config, epochs, export_csv ) From 124e97f3b9a7006cfcf2337255f10c8bcc3e0038 Mon Sep 17 00:00:00 2001 From: raphael Date: Fri, 17 Sep 2021 11:57:36 -0400 Subject: [PATCH 443/519] Remove ununsed variables from export_csv_file --- freqtrade/optimize/hyperopt_tools.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index b2e024f65..6a5c31d56 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -435,8 +435,7 @@ class HyperoptTools(): return table @staticmethod - def export_csv_file(config: dict, results: list, total_epochs: int, highlight_best: bool, - csv_file: str) -> None: + def export_csv_file(config: dict, results: list, csv_file: str) -> None: """ Log result to csv-file """ From fb6beb90e87e75d2997651324daaf0c5954e661a Mon Sep 17 00:00:00 2001 From: Ottavio Miele Date: Fri, 17 Sep 2021 18:03:54 +0200 Subject: [PATCH 444/519] Include Raspberry Pi armv6 (0, 0W,...) --- setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index 217500569..aee7c80b5 100755 --- a/setup.sh +++ b/setup.sh @@ -62,7 +62,7 @@ function updateenv() { then REQUIREMENTS_PLOT="-r requirements-plot.txt" fi - if [ "${SYS_ARCH}" == "armv7l" ]; then + if [ "${SYS_ARCH}" == "armv7l" ] || [ "${SYS_ARCH}" == "armv6l" ]; then echo "Detected Raspberry, installing cython, skipping hyperopt installation." ${PYTHON} -m pip install --upgrade cython else From 9525a5b96cf779dfa80abdae355e2d5c9382f505 Mon Sep 17 00:00:00 2001 From: raphael Date: Fri, 17 Sep 2021 14:10:37 -0400 Subject: [PATCH 445/519] Add type to "trials" parameter --- freqtrade/optimize/hyperopt_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 2fcab0b0f..1874b2949 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -299,7 +299,7 @@ class HyperoptTools(): f"Objective: {results['loss']:.5f}") @staticmethod - def prepare_trials_columns(trials, legacy_mode: bool, has_drawdown: bool) -> pd.DataFrame: + def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool, has_drawdown: bool) -> pd.DataFrame: trials['Best'] = '' From e715f2a253afe2ef4332166c0357595976dd7dfc Mon Sep 17 00:00:00 2001 From: raphael Date: Fri, 17 Sep 2021 14:23:26 -0400 Subject: [PATCH 446/519] Update formatting Line 302 was too long --- freqtrade/optimize/hyperopt_tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 1874b2949..ac92ad682 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -299,7 +299,8 @@ class HyperoptTools(): f"Objective: {results['loss']:.5f}") @staticmethod - def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool, has_drawdown: bool) -> pd.DataFrame: + def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool, + has_drawdown: bool) -> pd.DataFrame: trials['Best'] = '' From 4b2c1a9b8e0e487a60454125e8916026b8443c5c Mon Sep 17 00:00:00 2001 From: raphael Date: Fri, 17 Sep 2021 14:39:15 -0400 Subject: [PATCH 447/519] Remove trailing whitespace --- freqtrade/optimize/hyperopt_tools.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index ac92ad682..7a4ce5f23 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -299,9 +299,8 @@ class HyperoptTools(): f"Objective: {results['loss']:.5f}") @staticmethod - def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool, + def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool, has_drawdown: bool) -> pd.DataFrame: - trials['Best'] = '' if 'results_metrics.winsdrawslosses' not in trials.columns: From d7395e873be5560932752e00200c70fc2cd169c3 Mon Sep 17 00:00:00 2001 From: Sergey Khliustin Date: Fri, 17 Sep 2021 22:05:57 +0300 Subject: [PATCH 448/519] Removed unused OperationalException --- freqtrade/plugins/pairlist/PerformanceFilter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 920b884a0..5750b721c 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -7,7 +7,6 @@ from typing import Dict, List, Any import pandas as pd from freqtrade.persistence import Trade -from freqtrade.exceptions import OperationalException from freqtrade.plugins.pairlist.IPairList import IPairList From 12c12d42df5e0f838aad1ef971ca8530345d2d00 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Sep 2021 08:26:48 +0200 Subject: [PATCH 449/519] Add documentation for days parameter in PerformanceFilter --- docs/includes/pairlists.md | 14 ++++++++++++++ freqtrade/persistence/models.py | 4 ++-- freqtrade/plugins/pairlist/PerformanceFilter.py | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 69e12d5dc..71165f93b 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -165,6 +165,7 @@ Example to remove the first 10 pairs from the pairlist: ```json "pairlists": [ + // ... { "method": "OffsetFilter", "offset": 10 @@ -190,6 +191,19 @@ Sorts pairs by past trade performance, as follows: Trade count is used as a tie breaker. +You can use the `days` parameter to only consider performance of the past X days. +Not defining this parameter (or setting it to 0) will use all-time performance. + +```json +"pairlists": [ + // ... + { + "method": "PerformanceFilter", + "days": 10 + } +], +``` + !!! Note `PerformanceFilter` does not support backtesting mode. diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index a2e30d58c..f5f4e3a26 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -2,7 +2,7 @@ This module contains the class to persist trades into SQLite """ import logging -from datetime import datetime, timezone, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal from typing import Any, Dict, List, Optional @@ -840,7 +840,7 @@ class Trade(_DECL_BASE, LocalTrade): filters = [Trade.is_open.is_(False)] if days: start_date = datetime.today() - timedelta(days) - filters.append((Trade.close_date >= start_date)) + filters.append(Trade.close_date >= start_date) pair_rates = Trade.query.with_entities( Trade.pair, func.sum(Trade.close_profit).label('profit_sum'), diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 5750b721c..ee443b0fe 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -2,7 +2,7 @@ Performance pair list filter """ import logging -from typing import Dict, List, Any +from typing import Any, Dict, List import pandas as pd From 564e0b9a1ac89c19f2a9c1e94c04cafd4fd96afe Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Sep 2021 08:36:06 +0200 Subject: [PATCH 450/519] Switch performanceFilter to use Minutes lookback resolution closes #5060 --- docs/includes/pairlists.md | 4 ++-- freqtrade/persistence/models.py | 6 +++--- freqtrade/plugins/pairlist/PerformanceFilter.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 71165f93b..b612a4ddf 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -191,7 +191,7 @@ Sorts pairs by past trade performance, as follows: Trade count is used as a tie breaker. -You can use the `days` parameter to only consider performance of the past X days. +You can use the `minutes` parameter to only consider performance of the past X minutes (rolling window). Not defining this parameter (or setting it to 0) will use all-time performance. ```json @@ -199,7 +199,7 @@ Not defining this parameter (or setting it to 0) will use all-time performance. // ... { "method": "PerformanceFilter", - "days": 10 + "minutes": 1440 // rolling 24h } ], ``` diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index f5f4e3a26..bc5ef961a 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -832,14 +832,14 @@ class Trade(_DECL_BASE, LocalTrade): return total_open_stake_amount or 0 @staticmethod - def get_overall_performance(days=None) -> List[Dict[str, Any]]: + def get_overall_performance(minutes=None) -> List[Dict[str, Any]]: """ Returns List of dicts containing all Trades, including profit and trade count NOTE: Not supported in Backtesting. """ filters = [Trade.is_open.is_(False)] - if days: - start_date = datetime.today() - timedelta(days) + if minutes: + start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes) filters.append(Trade.close_date >= start_date) pair_rates = Trade.query.with_entities( Trade.pair, diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index ee443b0fe..301ee57ab 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -20,7 +20,7 @@ class PerformanceFilter(IPairList): pairlist_pos: int) -> None: super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) - self._days = pairlistconfig.get('days', 0) + self._minutes = pairlistconfig.get('minutes', 0) @property def needstickers(self) -> bool: @@ -47,7 +47,7 @@ class PerformanceFilter(IPairList): """ # Get the trading performance for pairs from database try: - performance = pd.DataFrame(Trade.get_overall_performance(self._days)) + performance = pd.DataFrame(Trade.get_overall_performance(self._minutes)) except AttributeError: # Performancefilter does not work in backtesting. self.log_once("PerformanceFilter is not available in this mode.", logger.warning) From 56fb25c5e521415c9880e47c4938bfe357550bec Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Sep 2021 09:10:37 +0200 Subject: [PATCH 451/519] Add test for PerformanceFilter lookback --- tests/plugins/test_pairlist.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 34770c03d..1ce8d172c 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -12,7 +12,8 @@ from freqtrade.persistence import Trade from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.plugins.pairlistmanager import PairListManager from freqtrade.resolvers import PairListResolver -from tests.conftest import get_patched_exchange, get_patched_freqtradebot, log_has, log_has_re +from tests.conftest import (create_mock_trades, get_patched_exchange, get_patched_freqtradebot, + log_has, log_has_re) @pytest.fixture(scope="function") @@ -663,6 +664,31 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None: assert log_has("PerformanceFilter is not available in this mode.", caplog) +@pytest.mark.usefixtures("init_persistence") +def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee) -> None: + whitelist_conf['exchange']['pair_whitelist'].append('XRP/BTC') + whitelist_conf['pairlists'] = [ + {"method": "StaticPairList"}, + {"method": "PerformanceFilter", "minutes": 60} + ] + mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) + exchange = get_patched_exchange(mocker, whitelist_conf) + pm = PairListManager(exchange, whitelist_conf) + pm.refresh_pairlist() + + assert pm.whitelist == ['ETH/BTC', 'TKN/BTC', 'XRP/BTC'] + + with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: + create_mock_trades(fee) + pm.refresh_pairlist() + assert pm.whitelist == ['XRP/BTC', 'ETH/BTC', 'TKN/BTC'] + + # Move to "outside" of lookback window, so original sorting is restored. + t.move_to("2021-09-01 07:00:00 +00:00") + pm.refresh_pairlist() + assert pm.whitelist == ['ETH/BTC', 'TKN/BTC', 'XRP/BTC'] + + def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None: default_conf['pairlists'] = [{'method': 'VolumePairList', 'number_assets': 10}] From d84ef34740a65df4922a13b8e8e523167637ae2e Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sat, 17 Jul 2021 18:09:08 +0300 Subject: [PATCH 452/519] A helper to calculate stoploss value from absolute price. --- docs/strategy-advanced.md | 6 ++++ docs/strategy-customization.md | 41 +++++++++++++++++++++++++++ freqtrade/strategy/__init__.py | 3 +- freqtrade/strategy/strategy_helper.py | 11 +++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 4409af6ea..2b9517f3b 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -288,6 +288,12 @@ Stoploss values returned from `custom_stoploss()` always specify a percentage re The helper function [`stoploss_from_open()`](strategy-customization.md#stoploss_from_open) can be used to convert from an open price relative stop, to a current price relative stop which can be returned from `custom_stoploss()`. +### Calculating stoploss percentage from absolute price + +Stoploss values returned from `custom_stoploss()` always specify a percentage relative to `current_rate`. In order to set a stoploss at specified absolute price level, we need to use `stop_rate` to calculate what percentage relative to the `current_rate` will give you the same result as if the percentage was specified from the open price. + +The helper function [`stoploss_from_absolute()`](strategy-customization.md#stoploss_from_absolute) can be used to convert from an absolute price, to a current price relative stop which can be returned from `custom_stoploss()`. + #### Stepped stoploss Instead of continuously trailing behind the current price, this example sets fixed stoploss price levels based on the current profit. diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index cfea60d22..1f8116deb 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -639,6 +639,47 @@ Stoploss values returned from `custom_stoploss` must specify a percentage relati Full examples can be found in the [Custom stoploss](strategy-advanced.md#custom-stoploss) section of the Documentation. +!!! Note + Providing invalid input to `stoploss_from_open()` may produce "CustomStoploss function did not return valid stoploss" warnings. + This may happen if `current_profit` parameter is below specified `open_relative_stop`. Such situations may arise when closing trade + is blocked by `confirm_trade_exit()` method. Warnings can be solved by never blocking stop loss sells by checking `sell_reason` in + `confirm_trade_exit()`, or by using `return stoploss_from_open(...) or 1` idiom, which will request to not change stop loss when + `current_profit < open_relative_stop`. + +### *stoploss_from_absolute()* + +In some situations it may be confusing to deal with stops relative to current rate. Instead, you may define a stoploss level using an absolute price. + +??? Example "Returning a stoploss using absolute price from the custom stoploss function" + + Say the open price was $100, and `current_price` is $121 (`current_profit` will be `0.21`). + + If we want a stop price at $107 price we can call `stoploss_from_absolute(107, current_rate)` which will return `0.1157024793`. 11.57% below $121 is $107, which is the same as 7% above $100. + + ``` python + + from datetime import datetime + from freqtrade.persistence import Trade + from freqtrade.strategy import IStrategy, stoploss_from_open + + class AwesomeStrategy(IStrategy): + + # ... populate_* methods + + use_custom_stoploss = True + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + # once the profit has risen above 10%, keep the stoploss at 7% above the open price + if current_profit > 0.10: + return stoploss_from_absolute(trade.open_rate * 1.07, current_rate) + + return 1 + + ``` + + Full examples can be found in the [Custom stoploss](strategy-advanced.md#custom-stoploss) section of the Documentation. ## Additional data (Wallets) diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index be655fc33..703cdabc1 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -4,4 +4,5 @@ from freqtrade.exchange import (timeframe_to_minutes, timeframe_to_msecs, timefr from freqtrade.strategy.hyper import (BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, RealParameter) from freqtrade.strategy.interface import IStrategy -from freqtrade.strategy.strategy_helper import merge_informative_pair, stoploss_from_open +from freqtrade.strategy.strategy_helper import (merge_informative_pair, + stoploss_from_absolute, stoploss_from_open) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index e089ebf31..32f7a9886 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -83,3 +83,14 @@ def stoploss_from_open(open_relative_stop: float, current_profit: float) -> floa # negative stoploss values indicate the requested stop price is higher than the current price return max(stoploss, 0.0) + + +def stoploss_from_absolute(stop_rate: float, current_rate: float) -> float: + """ + Given current price and desired stop price, return a stop loss value that is relative to current + price. + :param stop_rate: Stop loss price. + :param current_rate: Current asset price. + :return: Positive stop loss value relative to current price + """ + return 1 - (stop_rate / current_rate) From 1fdb656334208f57916fe4539ab83e9363d7b984 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sat, 17 Jul 2021 19:19:49 +0300 Subject: [PATCH 453/519] Add a decorator which can be used to declare populate_indicators() functions for informative pairs. --- docs/strategy-customization.md | 84 +++++++++- freqtrade/edge/edge_positioning.py | 2 +- freqtrade/freqtradebot.py | 2 +- freqtrade/strategy/__init__.py | 2 +- freqtrade/strategy/interface.py | 53 ++++++- freqtrade/strategy/strategy_helper.py | 148 +++++++++++++++++- tests/rpc/test_rpc_apiserver.py | 1 + .../strats/informative_decorator_strategy.py | 75 +++++++++ tests/strategy/test_interface.py | 2 +- tests/strategy/test_strategy_helpers.py | 55 +++++++ tests/strategy/test_strategy_loading.py | 6 +- 11 files changed, 414 insertions(+), 16 deletions(-) create mode 100644 tests/strategy/strats/informative_decorator_strategy.py diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 1f8116deb..526c111c5 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -679,7 +679,89 @@ In some situations it may be confusing to deal with stops relative to current ra ``` - Full examples can be found in the [Custom stoploss](strategy-advanced.md#custom-stoploss) section of the Documentation. +### *@informative()* + +In most common case it is possible to easily define informative pairs by using a decorator. All decorated `populate_indicators_*` methods run in isolation, +not having access to data from other informative pairs, in the end all informative dataframes are merged and passed to main `populate_indicators()` method. +When hyperopting, please follow instructions of [optimizing an indicator parameter](hyperopt.md#optimizing-an-indicator-parameter). + +??? Example "Fast and easy way to define informative pairs" + + Most of the time we do not need power and flexibility offered by `merge_informative_pair()`, therefore we can use a decorator to quickly define informative pairs. + + ``` python + + from datetime import datetime + from freqtrade.persistence import Trade + from freqtrade.strategy import IStrategy, informative + + class AwesomeStrategy(IStrategy): + + # This method is not required. + # def informative_pairs(self): ... + + # Define informative upper timeframe for each pair. Decorators can be stacked on same + # method. Available in populate_indicators as 'rsi_30m' and 'rsi_1h'. + @informative('30m') + @informative('1h') + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + return dataframe + + # Define BTC/STAKE informative pair. Available in populate_indicators and other methods as + # 'btc_rsi_1h'. Current stake currency should be specified as {stake} format variable + # instead of hardcoding actual stake currency. Available in populate_indicators and other + # methods as 'btc_rsi_1h'. + @informative('1h', 'BTC/{stake}') + def populate_indicators_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + return dataframe + + # Define BTC/ETH informative pair. You must specify quote currency if it is different from + # stake currency. Available in populate_indicators and other methods as 'eth_btc_rsi_1h'. + @informative('1h', 'ETH/BTC') + def populate_indicators_eth_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + return dataframe + + # Define BTC/STAKE informative pair. A custom formatter may be specified for formatting + # column names. Format string supports these format variables: + # * {asset} - full name of the asset, for example 'BTC/USDT'. + # * {base} - base currency in lower case, for example 'eth'. + # * {BASE} - same as {base}, except in upper case. + # * {quote} - quote currency in lower case, for example 'usdt'. + # * {QUOTE} - same as {quote}, except in upper case. + # * {column} - name of dataframe column. + # * {timeframe} - timeframe of informative dataframe. + # A callable `fmt(**kwargs) -> str` may be specified, to implement custom formatting. + # Available in populate_indicators and other methods as 'rsi_upper'. + @informative('1h', 'BTC/{stake}', '{name}') + def populate_indicators_btc_1h_2(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi_upper'] = ta.RSI(dataframe, timeperiod=14) + return dataframe + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Strategy timeframe indicators for current pair. + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + # Informative pairs are available in this method. + dataframe['rsi_less'] = dataframe['rsi'] < dataframe['rsi_1h'] + return dataframe + + ``` + + See docstring of `@informative()` decorator for more information. + +!!! Note + Do not use `@informative` decorator if you need to use data of one informative pair when generating another informative pair. Instead, define informative pairs + manually as described [in the DataProvider section](#complete-data-provider-sample). + +!!! Warning + Methods tagged with `@informative()` decorator must always have unique names! Re-using same name (for example when copy-pasting already defined informative method) + will overwrite previously defined method and not produce any errors due to limitations of Python programming language. In such cases you will find that indicators + created in earlier-defined methods are not available in the dataframe. Carefully review method names and make sure they are unique! + +!!! Warning + When using a legacy hyperopt implementation informative pairs defined with a decorator will not be executed. Please update your strategy if necessary. ## Additional data (Wallets) diff --git a/freqtrade/edge/edge_positioning.py b/freqtrade/edge/edge_positioning.py index f12b1b37d..1950f0d08 100644 --- a/freqtrade/edge/edge_positioning.py +++ b/freqtrade/edge/edge_positioning.py @@ -119,7 +119,7 @@ class Edge: ) # Download informative pairs too res = defaultdict(list) - for p, t in self.strategy.informative_pairs(): + for p, t in self.strategy.gather_informative_pairs(): res[t].append(p) for timeframe, inf_pairs in res.items(): timerange_startup = deepcopy(self._timerange) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7f668273c..bdc438c9a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -160,7 +160,7 @@ class FreqtradeBot(LoggingMixin): # Refreshing candles self.dataprovider.refresh(self.pairlists.create_pair_list(self.active_pair_whitelist), - self.strategy.informative_pairs()) + self.strategy.gather_informative_pairs()) strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)() diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index 703cdabc1..a7de34916 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -4,5 +4,5 @@ from freqtrade.exchange import (timeframe_to_minutes, timeframe_to_msecs, timefr from freqtrade.strategy.hyper import (BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, RealParameter) from freqtrade.strategy.interface import IStrategy -from freqtrade.strategy.strategy_helper import (merge_informative_pair, +from freqtrade.strategy.strategy_helper import (informative, merge_informative_pair, stoploss_from_absolute, stoploss_from_open) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 00ad3faf0..8e8b8b404 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -6,7 +6,7 @@ import logging import warnings from abc import ABC, abstractmethod from datetime import datetime, timedelta, timezone -from typing import Dict, List, Optional, Tuple, Union +from typing import Any, Callable, Dict, List, Optional, Tuple, Union import arrow from pandas import DataFrame @@ -19,6 +19,8 @@ from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date from freqtrade.persistence import PairLocks, Trade from freqtrade.strategy.hyper import HyperStrategyMixin +from freqtrade.strategy.strategy_helper import (InformativeData, _create_and_merge_informative_pair, + _format_pair_name) from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.wallets import Wallets @@ -134,6 +136,39 @@ class IStrategy(ABC, HyperStrategyMixin): self._last_candle_seen_per_pair: Dict[str, datetime] = {} super().__init__(config) + # Gather informative pairs from @informative-decorated methods. + self._ft_informative: Dict[ + Tuple[str, str], Tuple[InformativeData, + Callable[[Any, DataFrame, dict], DataFrame]]] = {} + for attr_name in dir(self.__class__): + cls_method = getattr(self.__class__, attr_name) + if not callable(cls_method): + continue + ft_informative = getattr(cls_method, '_ft_informative', []) + if not isinstance(ft_informative, list): + # Type check is required because mocker would return a mock object that evaluates to + # True, confusing this code. + continue + for informative_data in ft_informative: + asset = informative_data.asset + timeframe = informative_data.timeframe + if asset: + pair = _format_pair_name(self.config, asset) + if (pair, timeframe) in self._ft_informative: + raise OperationalException(f'Informative pair {pair} {timeframe} can not ' + f'be defined more than once!') + self._ft_informative[(pair, timeframe)] = (informative_data, cls_method) + elif self.dp is not None: + for pair in self.dp.current_whitelist(): + if (pair, timeframe) in self._ft_informative: + raise OperationalException(f'Informative pair {pair} {timeframe} can ' + f'not be defined more than once!') + self._ft_informative[(pair, timeframe)] = (informative_data, cls_method) + + def _format_pair(self, pair: str) -> str: + return pair.format(stake_currency=self.config['stake_currency'], + stake=self.config['stake_currency']).upper() + @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ @@ -377,6 +412,14 @@ class IStrategy(ABC, HyperStrategyMixin): # END - Intended to be overridden by strategy ### + def gather_informative_pairs(self) -> ListPairsWithTimeframes: + """ + Internal method which gathers all informative pairs (user or automatically defined). + """ + informative_pairs = self.informative_pairs() + informative_pairs += list(self._ft_informative.keys()) + return list(set(informative_pairs)) + def get_strategy_name(self) -> str: """ Returns strategy class name @@ -793,6 +836,14 @@ class IStrategy(ABC, HyperStrategyMixin): :return: a Dataframe with all mandatory indicators for the strategies """ logger.debug(f"Populating indicators for pair {metadata.get('pair')}.") + + # call populate_indicators_Nm() which were tagged with @informative decorator. + for (pair, timeframe), (informative_data, populate_fn) in self._ft_informative.items(): + if not informative_data.asset and pair != metadata['pair']: + continue + dataframe = _create_and_merge_informative_pair( + self, dataframe, metadata, informative_data, populate_fn) + if self._populate_fun_len == 2: warnings.warn("deprecated - check out the Sample strategy to see " "the current function headers!", DeprecationWarning) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 32f7a9886..aa828d330 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -1,10 +1,24 @@ -import pandas as pd +from typing import Any, Callable, NamedTuple, Optional, Union +import pandas as pd +from mypy_extensions import KwArg +from pandas import DataFrame + +from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes +class InformativeData(NamedTuple): + asset: Optional[str] + timeframe: str + fmt: Union[str, Callable[[KwArg(str)], str], None] + ffill: bool + + def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, - timeframe: str, timeframe_inf: str, ffill: bool = True) -> pd.DataFrame: + timeframe: str, timeframe_inf: str, ffill: bool = True, + append_timeframe: bool = True, + date_column: str = 'date') -> pd.DataFrame: """ Correctly merge informative samples to the original dataframe, avoiding lookahead bias. @@ -24,6 +38,8 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, :param timeframe: Timeframe of the original pair sample. :param timeframe_inf: Timeframe of the informative pair sample. :param ffill: Forwardfill missing values - optional but usually required + :param append_timeframe: Rename columns by appending timeframe. + :param date_column: A custom date column name. :return: Merged dataframe :raise: ValueError if the secondary timeframe is shorter than the dataframe timeframe """ @@ -32,25 +48,29 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, minutes = timeframe_to_minutes(timeframe) if minutes == minutes_inf: # No need to forwardshift if the timeframes are identical - informative['date_merge'] = informative["date"] + informative['date_merge'] = informative[date_column] elif minutes < minutes_inf: # Subtract "small" timeframe so merging is not delayed by 1 small candle # Detailed explanation in https://github.com/freqtrade/freqtrade/issues/4073 informative['date_merge'] = ( - informative["date"] + pd.to_timedelta(minutes_inf, 'm') - pd.to_timedelta(minutes, 'm') + informative[date_column] + pd.to_timedelta(minutes_inf, 'm') - + pd.to_timedelta(minutes, 'm') ) else: raise ValueError("Tried to merge a faster timeframe to a slower timeframe." "This would create new rows, and can throw off your regular indicators.") # Rename columns to be unique - informative.columns = [f"{col}_{timeframe_inf}" for col in informative.columns] + date_merge = 'date_merge' + if append_timeframe: + date_merge = f'date_merge_{timeframe_inf}' + informative.columns = [f"{col}_{timeframe_inf}" for col in informative.columns] # Combine the 2 dataframes # all indicators on the informative sample MUST be calculated before this point dataframe = pd.merge(dataframe, informative, left_on='date', - right_on=f'date_merge_{timeframe_inf}', how='left') - dataframe = dataframe.drop(f'date_merge_{timeframe_inf}', axis=1) + right_on=date_merge, how='left') + dataframe = dataframe.drop(date_merge, axis=1) if ffill: dataframe = dataframe.ffill() @@ -94,3 +114,117 @@ def stoploss_from_absolute(stop_rate: float, current_rate: float) -> float: :return: Positive stop loss value relative to current price """ return 1 - (stop_rate / current_rate) + + +def informative(timeframe: str, asset: str = '', + fmt: Optional[Union[str, Callable[[KwArg(str)], str]]] = None, + ffill: bool = True) -> Callable[[Callable[[Any, DataFrame, dict], DataFrame]], + Callable[[Any, DataFrame, dict], DataFrame]]: + """ + A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to + define informative indicators. + + Example usage: + + @informative('1h') + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + return dataframe + + :param timeframe: Informative timeframe. Must always be higher than strategy timeframe. + :param asset: Informative asset, for example BTC, BTC/USDT, ETH/BTC. Do not specify to use + current pair. + :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not + specified, defaults to {asset}_{name}_{timeframe} if asset is specified, or {name}_{timeframe} + otherwise. + * {asset}: name of informative asset, provided in lower-case, with / replaced with _. Stake + currency is not included in this string. + * {name}: user-specified dataframe column name. + * {timeframe}: informative timeframe. + :param ffill: ffill dataframe after mering informative pair. + """ + _asset = asset + _timeframe = timeframe + _fmt = fmt + _ffill = ffill + + def decorator(fn: Callable[[Any, DataFrame, dict], DataFrame]): + informative_pairs = getattr(fn, '_ft_informative', []) + informative_pairs.append(InformativeData(_asset, _timeframe, _fmt, _ffill)) + setattr(fn, '_ft_informative', informative_pairs) + return fn + return decorator + + +def _format_pair_name(config, pair: str) -> str: + return pair.format(stake_currency=config['stake_currency'], + stake=config['stake_currency']).upper() + + +def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, + metadata: dict, informative_data: InformativeData, + populate_indicators: Callable[[Any, DataFrame, dict], + DataFrame]): + asset = informative_data.asset or '' + timeframe = informative_data.timeframe + fmt = informative_data.fmt + ffill = informative_data.ffill + config = strategy.config + dp = strategy.dp + + if asset: + # Insert stake currency if needed. + asset = _format_pair_name(config, asset) + else: + # Not specifying an asset will define informative dataframe for current pair. + asset = metadata['pair'] + + if '/' in asset: + base, quote = asset.split('/') + else: + # When futures are supported this may need reevaluation. + # base, quote = asset, None + raise OperationalException('Not implemented.') + + # Default format. This optimizes for the common case: informative pairs using same stake + # currency. When quote currency matches stake currency, column name will omit base currency. + # This allows easily reconfiguring strategy to use different base currency. In a rare case + # where it is desired to keep quote currency in column name at all times user should specify + # fmt='{base}_{quote}_{column}_{timeframe}' format or similar. + if not fmt: + fmt = '{column}_{timeframe}' # Informatives of current pair + if asset != metadata['pair']: + if quote == config['stake_currency']: + fmt = '{base}_' + fmt # Informatives of other pair + else: + fmt = '{base}_{quote}_' + fmt # Informatives of different quote currency + + inf_metadata = {'pair': asset, 'timeframe': timeframe} + inf_dataframe = dp.get_pair_dataframe(asset, timeframe) + inf_dataframe = populate_indicators(strategy, inf_dataframe, inf_metadata) + + formatter: Any = None + if callable(fmt): + formatter = fmt # A custom user-specified formatter function. + else: + formatter = fmt.format # A default string formatter. + + fmt_args = { + 'BASE': base.upper(), + 'QUOTE': quote.upper(), + 'base': base.lower(), + 'quote': quote.lower(), + 'asset': asset, + 'timeframe': timeframe, + } + inf_dataframe.rename(columns=lambda column: formatter(column=column, **fmt_args), + inplace=True) + + date_column = formatter(column='date', **fmt_args) + if date_column in dataframe.columns: + raise OperationalException(f'Duplicate column name {date_column} exists in ' + f'dataframe! Ensure column names are unique!') + dataframe = merge_informative_pair(dataframe, inf_dataframe, strategy.timeframe, timeframe, + ffill=ffill, append_timeframe=False, + date_column=date_column) + return dataframe diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 2852486ed..43eb70938 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1218,6 +1218,7 @@ def test_api_strategies(botclient): assert_response(rc) assert rc.json() == {'strategies': [ 'HyperoptableStrategy', + 'InformativeDecoratorTest', 'StrategyTestV2', 'TestStrategyLegacyV1' ]} diff --git a/tests/strategy/strats/informative_decorator_strategy.py b/tests/strategy/strats/informative_decorator_strategy.py new file mode 100644 index 000000000..a32ad79e8 --- /dev/null +++ b/tests/strategy/strats/informative_decorator_strategy.py @@ -0,0 +1,75 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +from pandas import DataFrame + +from freqtrade.strategy import informative, merge_informative_pair +from freqtrade.strategy.interface import IStrategy + + +class InformativeDecoratorTest(IStrategy): + """ + Strategy used by tests freqtrade bot. + Please do not modify this strategy, it's intended for internal use only. + Please look at the SampleStrategy in the user_data/strategy directory + or strategy repository https://github.com/freqtrade/freqtrade-strategies + for samples and inspiration. + """ + INTERFACE_VERSION = 2 + stoploss = -0.10 + timeframe = '5m' + startup_candle_count: int = 20 + + def informative_pairs(self): + return [('BTC/USDT', '5m')] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['buy'] = 0 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['sell'] = 0 + return dataframe + + # Decorator stacking test. + @informative('30m') + @informative('1h') + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = 14 + return dataframe + + # Simple informative test. + @informative('1h', 'BTC/{stake}') + def populate_indicators_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = 14 + return dataframe + + # Quote currency different from stake currency test. + @informative('1h', 'ETH/BTC') + def populate_indicators_eth_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = 14 + return dataframe + + # Formatting test. + @informative('30m', 'BTC/{stake}', '{column}_{BASE}_{QUOTE}_{base}_{quote}_{asset}_{timeframe}') + def populate_indicators_btc_1h_2(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = 14 + return dataframe + + # Custom formatter test + @informative('30m', 'ETH/{stake}', fmt=lambda column, **kwargs: column + '_from_callable') + def populate_indicators_eth_30m(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = 14 + return dataframe + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Strategy timeframe indicators for current pair. + dataframe['rsi'] = 14 + # Informative pairs are available in this method. + dataframe['rsi_less'] = dataframe['rsi'] < dataframe['rsi_1h'] + + # Mixing manual informative pairs with decorators. + informative = self.dp.get_pair_dataframe('BTC/USDT', '5m') + informative['rsi'] = 14 + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '5m', ffill=True) + + return dataframe diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 250dcf63d..dcb9e3e64 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -607,7 +607,7 @@ def test_is_informative_pairs_callback(default_conf): strategy = StrategyResolver.load_strategy(default_conf) # Should return empty # Uses fallback to base implementation - assert [] == strategy.informative_pairs() + assert [] == strategy.gather_informative_pairs() @pytest.mark.parametrize('error', [ diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 3b84fc254..7784f3f77 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -4,6 +4,7 @@ import numpy as np import pandas as pd import pytest +from freqtrade.data.dataprovider import DataProvider from freqtrade.strategy import merge_informative_pair, stoploss_from_open, timeframe_to_minutes @@ -132,3 +133,57 @@ def test_stoploss_from_open(): assert stoploss == 0 else: assert isclose(stop_price, expected_stop_price, rel_tol=0.00001) + + +def test_informative_decorator(mocker, default_conf): + test_data_5m = generate_test_data('5m', 40) + test_data_30m = generate_test_data('30m', 40) + test_data_1h = generate_test_data('1h', 40) + data = { + ('XRP/USDT', '5m'): test_data_5m, + ('XRP/USDT', '30m'): test_data_30m, + ('XRP/USDT', '1h'): test_data_1h, + ('LTC/USDT', '5m'): test_data_5m, + ('LTC/USDT', '30m'): test_data_30m, + ('LTC/USDT', '1h'): test_data_1h, + ('BTC/USDT', '30m'): test_data_30m, + ('BTC/USDT', '5m'): test_data_5m, + ('BTC/USDT', '1h'): test_data_1h, + ('ETH/USDT', '1h'): test_data_1h, + ('ETH/USDT', '30m'): test_data_30m, + ('ETH/BTC', '1h'): test_data_1h, + } + from .strats.informative_decorator_strategy import InformativeDecoratorTest + default_conf['stake_currency'] = 'USDT' + InformativeDecoratorTest.dp = DataProvider({}, None, None) + mocker.patch.object(InformativeDecoratorTest.dp, 'current_whitelist', return_value=[ + 'XRP/USDT', 'LTC/USDT' + ]) + strategy = InformativeDecoratorTest(config=default_conf) + + assert len(strategy._ft_informative) == 8 + informative_pairs = [('XRP/USDT', '1h'), ('LTC/USDT', '1h'), ('XRP/USDT', '30m'), + ('LTC/USDT', '30m'), ('BTC/USDT', '1h'), ('BTC/USDT', '30m'), + ('BTC/USDT', '5m'), ('ETH/BTC', '1h'), ('ETH/USDT', '30m')] + for inf_pair in informative_pairs: + assert inf_pair in strategy.gather_informative_pairs() + + def test_historic_ohlcv(pair, timeframe): + return data[(pair, timeframe or strategy.timeframe)].copy() + mocker.patch('freqtrade.data.dataprovider.DataProvider.historic_ohlcv', + side_effect=test_historic_ohlcv) + + analyzed = strategy.advise_all_indicators( + {p: data[(p, strategy.timeframe)] for p in ('XRP/USDT', 'LTC/USDT')}) + expected_columns = [ + 'rsi_1h', 'rsi_30m', # Stacked informative decorators + 'btc_rsi_1h', # BTC 1h informative + 'rsi_BTC_USDT_btc_usdt_BTC/USDT_30m', # Column formatting + 'rsi_from_callable', # Custom column formatter + 'eth_btc_rsi_1h', # Quote currency not matching stake currency + 'rsi', 'rsi_less', # Non-informative columns + 'rsi_5m', # Manual informative dataframe + ] + for _, dataframe in analyzed.items(): + for col in expected_columns: + assert col in dataframe.columns diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index 2cbc9d0c6..3a30a824a 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -35,7 +35,7 @@ def test_search_all_strategies_no_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=False) assert isinstance(strategies, list) - assert len(strategies) == 3 + assert len(strategies) == 4 assert isinstance(strategies[0], dict) @@ -43,10 +43,10 @@ def test_search_all_strategies_with_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=True) assert isinstance(strategies, list) - assert len(strategies) == 4 + assert len(strategies) == 5 # with enum_failed=True search_all_objects() shall find 2 good strategies # and 1 which fails to load - assert len([x for x in strategies if x['class'] is not None]) == 3 + assert len([x for x in strategies if x['class'] is not None]) == 4 assert len([x for x in strategies if x['class'] is None]) == 1 From f2a1d9d2fc8efe238e47f5404dd6c924511e683b Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sun, 5 Sep 2021 09:54:05 +0300 Subject: [PATCH 454/519] [SQUASH] Address PR comments. --- docs/strategy-customization.md | 56 ++++++++++++++++++++------- freqtrade/strategy/interface.py | 18 ++++----- freqtrade/strategy/strategy_helper.py | 32 +++++++++------ 3 files changed, 72 insertions(+), 34 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 526c111c5..f2bf6cf7c 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -681,9 +681,47 @@ In some situations it may be confusing to deal with stops relative to current ra ### *@informative()* +``` python +def informative(timeframe: str, asset: str = '', + fmt: Optional[Union[str, Callable[[KwArg(str)], str]]] = None, + ffill: bool = True) -> Callable[[PopulateIndicators], PopulateIndicators]: + """ + A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to + define informative indicators. + + Example usage: + + @informative('1h') + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + return dataframe + + :param timeframe: Informative timeframe. Must always be equal or higher than strategy timeframe. + :param asset: Informative asset, for example BTC, BTC/USDT, ETH/BTC. Do not specify to use + current pair. + :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not + specified, defaults to: + * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake + curerncy. + * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match + stake curerncy. + * {column}_{timeframe} if asset is not specified. + Format string supports these format variables: + * {asset} - full name of the asset, for example 'BTC/USDT'. + * {base} - base currency in lower case, for example 'eth'. + * {BASE} - same as {base}, except in upper case. + * {quote} - quote currency in lower case, for example 'usdt'. + * {QUOTE} - same as {quote}, except in upper case. + * {column} - name of dataframe column. + * {timeframe} - timeframe of informative dataframe. + :param ffill: ffill dataframe after merging informative pair. + """ +``` + In most common case it is possible to easily define informative pairs by using a decorator. All decorated `populate_indicators_*` methods run in isolation, not having access to data from other informative pairs, in the end all informative dataframes are merged and passed to main `populate_indicators()` method. -When hyperopting, please follow instructions of [optimizing an indicator parameter](hyperopt.md#optimizing-an-indicator-parameter). +When hyperopting, use of hyperoptable parameter `.value` attribute is not supported. Please use `.range` attribute. See [optimizing an indicator parameter](hyperopt.md#optimizing-an-indicator-parameter) +for more information. ??? Example "Fast and easy way to define informative pairs" @@ -725,17 +763,9 @@ When hyperopting, please follow instructions of [optimizing an indicator paramet return dataframe # Define BTC/STAKE informative pair. A custom formatter may be specified for formatting - # column names. Format string supports these format variables: - # * {asset} - full name of the asset, for example 'BTC/USDT'. - # * {base} - base currency in lower case, for example 'eth'. - # * {BASE} - same as {base}, except in upper case. - # * {quote} - quote currency in lower case, for example 'usdt'. - # * {QUOTE} - same as {quote}, except in upper case. - # * {column} - name of dataframe column. - # * {timeframe} - timeframe of informative dataframe. - # A callable `fmt(**kwargs) -> str` may be specified, to implement custom formatting. - # Available in populate_indicators and other methods as 'rsi_upper'. - @informative('1h', 'BTC/{stake}', '{name}') + # column names. A callable `fmt(**kwargs) -> str` may be specified, to implement custom + # formatting. Available in populate_indicators and other methods as 'rsi_upper'. + @informative('1h', 'BTC/{stake}', '{column}') def populate_indicators_btc_1h_2(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['rsi_upper'] = ta.RSI(dataframe, timeperiod=14) return dataframe @@ -749,8 +779,6 @@ When hyperopting, please follow instructions of [optimizing an indicator paramet ``` - See docstring of `@informative()` decorator for more information. - !!! Note Do not use `@informative` decorator if you need to use data of one informative pair when generating another informative pair. Instead, define informative pairs manually as described [in the DataProvider section](#complete-data-provider-sample). diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 8e8b8b404..0546deb01 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -6,7 +6,7 @@ import logging import warnings from abc import ABC, abstractmethod from datetime import datetime, timedelta, timezone -from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from typing import Dict, List, Optional, Tuple, Union import arrow from pandas import DataFrame @@ -19,7 +19,8 @@ from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date from freqtrade.persistence import PairLocks, Trade from freqtrade.strategy.hyper import HyperStrategyMixin -from freqtrade.strategy.strategy_helper import (InformativeData, _create_and_merge_informative_pair, +from freqtrade.strategy.strategy_helper import (InformativeData, PopulateIndicators, + _create_and_merge_informative_pair, _format_pair_name) from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.wallets import Wallets @@ -138,20 +139,23 @@ class IStrategy(ABC, HyperStrategyMixin): # Gather informative pairs from @informative-decorated methods. self._ft_informative: Dict[ - Tuple[str, str], Tuple[InformativeData, - Callable[[Any, DataFrame, dict], DataFrame]]] = {} + Tuple[str, str], Tuple[InformativeData, PopulateIndicators]] = {} for attr_name in dir(self.__class__): cls_method = getattr(self.__class__, attr_name) if not callable(cls_method): continue - ft_informative = getattr(cls_method, '_ft_informative', []) + ft_informative = getattr(cls_method, '_ft_informative', None) if not isinstance(ft_informative, list): # Type check is required because mocker would return a mock object that evaluates to # True, confusing this code. continue + strategy_timeframe_minutes = timeframe_to_minutes(self.timeframe) for informative_data in ft_informative: asset = informative_data.asset timeframe = informative_data.timeframe + if timeframe_to_minutes(timeframe) < strategy_timeframe_minutes: + raise OperationalException('Informative timeframe must be equal or higher than ' + 'strategy timeframe!') if asset: pair = _format_pair_name(self.config, asset) if (pair, timeframe) in self._ft_informative: @@ -165,10 +169,6 @@ class IStrategy(ABC, HyperStrategyMixin): f'not be defined more than once!') self._ft_informative[(pair, timeframe)] = (informative_data, cls_method) - def _format_pair(self, pair: str) -> str: - return pair.format(stake_currency=self.config['stake_currency'], - stake=self.config['stake_currency']).upper() - @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index aa828d330..64d9bdea8 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -8,6 +8,9 @@ from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes +PopulateIndicators = Callable[[Any, DataFrame, dict], DataFrame] + + class InformativeData(NamedTuple): asset: Optional[str] timeframe: str @@ -118,8 +121,7 @@ def stoploss_from_absolute(stop_rate: float, current_rate: float) -> float: def informative(timeframe: str, asset: str = '', fmt: Optional[Union[str, Callable[[KwArg(str)], str]]] = None, - ffill: bool = True) -> Callable[[Callable[[Any, DataFrame, dict], DataFrame]], - Callable[[Any, DataFrame, dict], DataFrame]]: + ffill: bool = True) -> Callable[[PopulateIndicators], PopulateIndicators]: """ A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to define informative indicators. @@ -131,24 +133,32 @@ def informative(timeframe: str, asset: str = '', dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) return dataframe - :param timeframe: Informative timeframe. Must always be higher than strategy timeframe. + :param timeframe: Informative timeframe. Must always be equal or higher than strategy timeframe. :param asset: Informative asset, for example BTC, BTC/USDT, ETH/BTC. Do not specify to use current pair. :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not - specified, defaults to {asset}_{name}_{timeframe} if asset is specified, or {name}_{timeframe} - otherwise. - * {asset}: name of informative asset, provided in lower-case, with / replaced with _. Stake - currency is not included in this string. - * {name}: user-specified dataframe column name. - * {timeframe}: informative timeframe. - :param ffill: ffill dataframe after mering informative pair. + specified, defaults to: + * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake + curerncy. + * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match + stake curerncy. + * {column}_{timeframe} if asset is not specified. + Format string supports these format variables: + * {asset} - full name of the asset, for example 'BTC/USDT'. + * {base} - base currency in lower case, for example 'eth'. + * {BASE} - same as {base}, except in upper case. + * {quote} - quote currency in lower case, for example 'usdt'. + * {QUOTE} - same as {quote}, except in upper case. + * {column} - name of dataframe column. + * {timeframe} - timeframe of informative dataframe. + :param ffill: ffill dataframe after merging informative pair. """ _asset = asset _timeframe = timeframe _fmt = fmt _ffill = ffill - def decorator(fn: Callable[[Any, DataFrame, dict], DataFrame]): + def decorator(fn: PopulateIndicators): informative_pairs = getattr(fn, '_ft_informative', []) informative_pairs.append(InformativeData(_asset, _timeframe, _fmt, _ffill)) setattr(fn, '_ft_informative', informative_pairs) From dfa61b7ad2297a94b0b26309ca74c64a7c1d08a7 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 7 Sep 2021 15:40:53 +0300 Subject: [PATCH 455/519] [SQUASH] Fix informatives for each pair not being created because dataprovider was not available. Fix not being able to have informative dataframe of a pair in whitelist. --- docs/strategy-customization.md | 4 +-- freqtrade/freqtradebot.py | 10 ++++--- freqtrade/optimize/backtesting.py | 3 +- freqtrade/optimize/edge_cli.py | 3 ++ freqtrade/strategy/interface.py | 38 +++++++++++++++---------- freqtrade/strategy/strategy_helper.py | 13 ++++----- tests/strategy/test_strategy_helpers.py | 9 +++--- 7 files changed, 47 insertions(+), 33 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index f2bf6cf7c..a994d9acd 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -702,9 +702,9 @@ def informative(timeframe: str, asset: str = '', :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not specified, defaults to: * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake - curerncy. + currency. * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match - stake curerncy. + stake currency. * {column}_{timeframe} if asset is not specified. Format string supports these format variables: * {asset} - full name of the asset, for example 'BTC/USDT'. diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index bdc438c9a..b79916639 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -83,10 +83,12 @@ class FreqtradeBot(LoggingMixin): self.dataprovider = DataProvider(self.config, self.exchange, self.pairlists) - # Attach Dataprovider to Strategy baseclass - IStrategy.dp = self.dataprovider - # Attach Wallets to Strategy baseclass - IStrategy.wallets = self.wallets + # Attach Dataprovider to strategy instance + self.strategy.dp = self.dataprovider + # Attach Wallets to strategy instance + self.strategy.wallets = self.wallets + # Late initialization (may depend on dp/wallets) + self.strategy._initialize() # Initializing Edge only if enabled self.edge = Edge(self.config, self.exchange, self.strategy) if \ diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 3e06bfa1b..ef491ae5e 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -154,11 +154,12 @@ class Backtesting: self.strategy: IStrategy = strategy strategy.dp = self.dataprovider # Attach Wallets to Strategy baseclass - IStrategy.wallets = self.wallets + strategy.wallets = self.wallets # Set stoploss_on_exchange to false for backtesting, # since a "perfect" stoploss-sell is assumed anyway # And the regular "stoploss" function would not apply to that case self.strategy.order_types['stoploss_on_exchange'] = False + strategy._initialize() def _load_protections(self, strategy: IStrategy): if self.config.get('enable_protections', False): diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index 417faa685..abb5ca635 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -8,6 +8,7 @@ from typing import Any, Dict from freqtrade import constants from freqtrade.configuration import TimeRange, validate_config_consistency +from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge from freqtrade.optimize.optimize_reports import generate_edge_table from freqtrade.resolvers import ExchangeResolver, StrategyResolver @@ -33,6 +34,8 @@ class EdgeCli: self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) self.strategy = StrategyResolver.load_strategy(self.config) + self.strategy.dp = DataProvider(config, None) + self.strategy._initialize() validate_config_consistency(self.config) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 0546deb01..951979212 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -137,9 +137,13 @@ class IStrategy(ABC, HyperStrategyMixin): self._last_candle_seen_per_pair: Dict[str, datetime] = {} super().__init__(config) + def _initialize(self): + """ + Late initialization tasks, which may depend on availability of dataprovider/wallets/etc. + """ # Gather informative pairs from @informative-decorated methods. self._ft_informative: Dict[ - Tuple[str, str], Tuple[InformativeData, PopulateIndicators]] = {} + Tuple[str, str], List[Tuple[InformativeData, PopulateIndicators]]] = {} for attr_name in dir(self.__class__): cls_method = getattr(self.__class__, attr_name) if not callable(cls_method): @@ -158,16 +162,19 @@ class IStrategy(ABC, HyperStrategyMixin): 'strategy timeframe!') if asset: pair = _format_pair_name(self.config, asset) - if (pair, timeframe) in self._ft_informative: - raise OperationalException(f'Informative pair {pair} {timeframe} can not ' - f'be defined more than once!') - self._ft_informative[(pair, timeframe)] = (informative_data, cls_method) - elif self.dp is not None: + try: + self._ft_informative[(pair, timeframe)].append( + (informative_data, cls_method)) + except KeyError: + self._ft_informative[(pair, timeframe)] = [(informative_data, cls_method)] + else: for pair in self.dp.current_whitelist(): - if (pair, timeframe) in self._ft_informative: - raise OperationalException(f'Informative pair {pair} {timeframe} can ' - f'not be defined more than once!') - self._ft_informative[(pair, timeframe)] = (informative_data, cls_method) + try: + self._ft_informative[(pair, timeframe)].append( + (informative_data, cls_method)) + except KeyError: + self._ft_informative[(pair, timeframe)] = \ + [(informative_data, cls_method)] @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -838,11 +845,12 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug(f"Populating indicators for pair {metadata.get('pair')}.") # call populate_indicators_Nm() which were tagged with @informative decorator. - for (pair, timeframe), (informative_data, populate_fn) in self._ft_informative.items(): - if not informative_data.asset and pair != metadata['pair']: - continue - dataframe = _create_and_merge_informative_pair( - self, dataframe, metadata, informative_data, populate_fn) + for (pair, timeframe), informatives in self._ft_informative.items(): + for (informative_data, populate_fn) in informatives: + if not informative_data.asset and pair != metadata['pair']: + continue + dataframe = _create_and_merge_informative_pair( + self, dataframe, metadata, informative_data, populate_fn) if self._populate_fun_len == 2: warnings.warn("deprecated - check out the Sample strategy to see " diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 64d9bdea8..a4023f953 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -139,9 +139,9 @@ def informative(timeframe: str, asset: str = '', :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not specified, defaults to: * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake - curerncy. + currency. * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match - stake curerncy. + stake currency. * {column}_{timeframe} if asset is not specified. Format string supports these format variables: * {asset} - full name of the asset, for example 'BTC/USDT'. @@ -203,11 +203,10 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, # fmt='{base}_{quote}_{column}_{timeframe}' format or similar. if not fmt: fmt = '{column}_{timeframe}' # Informatives of current pair - if asset != metadata['pair']: - if quote == config['stake_currency']: - fmt = '{base}_' + fmt # Informatives of other pair - else: - fmt = '{base}_{quote}_' + fmt # Informatives of different quote currency + if quote != config['stake_currency']: + fmt = '{quote}_' + fmt # Informatives of different quote currency + if informative_data.asset: + fmt = '{base}_' + fmt # Informatives of other pair inf_metadata = {'pair': asset, 'timeframe': timeframe} inf_dataframe = dp.get_pair_dataframe(asset, timeframe) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 7784f3f77..0ee554ede 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -155,11 +155,12 @@ def test_informative_decorator(mocker, default_conf): } from .strats.informative_decorator_strategy import InformativeDecoratorTest default_conf['stake_currency'] = 'USDT' - InformativeDecoratorTest.dp = DataProvider({}, None, None) - mocker.patch.object(InformativeDecoratorTest.dp, 'current_whitelist', return_value=[ - 'XRP/USDT', 'LTC/USDT' - ]) strategy = InformativeDecoratorTest(config=default_conf) + strategy.dp = DataProvider({}, None, None) + mocker.patch.object(strategy.dp, 'current_whitelist', return_value=[ + 'XRP/USDT', 'LTC/USDT', 'BTC/USDT' + ]) + strategy._initialize() assert len(strategy._ft_informative) == 8 informative_pairs = [('XRP/USDT', '1h'), ('LTC/USDT', '1h'), ('XRP/USDT', '30m'), From f81df19b934b738bf148f51fdb5a9c7ab1dfb34f Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 7 Sep 2021 15:53:12 +0300 Subject: [PATCH 456/519] [TMP] Make tests not fail for now. --- freqtrade/strategy/interface.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 951979212..6e312e15d 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -424,7 +424,8 @@ class IStrategy(ABC, HyperStrategyMixin): Internal method which gathers all informative pairs (user or automatically defined). """ informative_pairs = self.informative_pairs() - informative_pairs += list(self._ft_informative.keys()) + if hasattr(self, '_ft_informative'): + informative_pairs += list(self._ft_informative.keys()) return list(set(informative_pairs)) def get_strategy_name(self) -> str: @@ -844,13 +845,14 @@ class IStrategy(ABC, HyperStrategyMixin): """ logger.debug(f"Populating indicators for pair {metadata.get('pair')}.") - # call populate_indicators_Nm() which were tagged with @informative decorator. - for (pair, timeframe), informatives in self._ft_informative.items(): - for (informative_data, populate_fn) in informatives: - if not informative_data.asset and pair != metadata['pair']: - continue - dataframe = _create_and_merge_informative_pair( - self, dataframe, metadata, informative_data, populate_fn) + if hasattr(self, '_ft_informative'): + # call populate_indicators_Nm() which were tagged with @informative decorator. + for (pair, timeframe), informatives in self._ft_informative.items(): + for (informative_data, populate_fn) in informatives: + if not informative_data.asset and pair != metadata['pair']: + continue + dataframe = _create_and_merge_informative_pair( + self, dataframe, metadata, informative_data, populate_fn) if self._populate_fun_len == 2: warnings.warn("deprecated - check out the Sample strategy to see " From 5dc78a0c66f385edd14db16a806e1f75bd453e83 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 10 Sep 2021 09:36:52 +0300 Subject: [PATCH 457/519] [SQUASH] Get rid of _initialize() and fix informatives for dynamic pairlists. --- freqtrade/freqtradebot.py | 2 - freqtrade/optimize/backtesting.py | 1 - freqtrade/optimize/edge_cli.py | 1 - freqtrade/strategy/interface.py | 56 ++++++++----------------- freqtrade/strategy/strategy_helper.py | 17 ++++---- tests/strategy/test_strategy_helpers.py | 3 +- 6 files changed, 27 insertions(+), 53 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b79916639..1cb8988ff 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -87,8 +87,6 @@ class FreqtradeBot(LoggingMixin): self.strategy.dp = self.dataprovider # Attach Wallets to strategy instance self.strategy.wallets = self.wallets - # Late initialization (may depend on dp/wallets) - self.strategy._initialize() # Initializing Edge only if enabled self.edge = Edge(self.config, self.exchange, self.strategy) if \ diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index ef491ae5e..79c861ee8 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -159,7 +159,6 @@ class Backtesting: # since a "perfect" stoploss-sell is assumed anyway # And the regular "stoploss" function would not apply to that case self.strategy.order_types['stoploss_on_exchange'] = False - strategy._initialize() def _load_protections(self, strategy: IStrategy): if self.config.get('enable_protections', False): diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index abb5ca635..f211da750 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -35,7 +35,6 @@ class EdgeCli: self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) self.strategy = StrategyResolver.load_strategy(self.config) self.strategy.dp = DataProvider(config, None) - self.strategy._initialize() validate_config_consistency(self.config) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6e312e15d..00c56f5df 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -121,7 +121,7 @@ class IStrategy(ABC, HyperStrategyMixin): # Class level variables (intentional) containing # the dataprovider (dp) (access to other candles, historic data, ...) # and wallets - access to the current balance. - dp: Optional[DataProvider] = None + dp: DataProvider wallets: Optional[Wallets] = None # Filled from configuration stake_currency: str @@ -137,44 +137,23 @@ class IStrategy(ABC, HyperStrategyMixin): self._last_candle_seen_per_pair: Dict[str, datetime] = {} super().__init__(config) - def _initialize(self): - """ - Late initialization tasks, which may depend on availability of dataprovider/wallets/etc. - """ # Gather informative pairs from @informative-decorated methods. - self._ft_informative: Dict[ - Tuple[str, str], List[Tuple[InformativeData, PopulateIndicators]]] = {} + self._ft_informative: List[Tuple[InformativeData, PopulateIndicators]] = [] for attr_name in dir(self.__class__): cls_method = getattr(self.__class__, attr_name) if not callable(cls_method): continue - ft_informative = getattr(cls_method, '_ft_informative', None) - if not isinstance(ft_informative, list): + informative_data_list = getattr(cls_method, '_ft_informative', None) + if not isinstance(informative_data_list, list): # Type check is required because mocker would return a mock object that evaluates to # True, confusing this code. continue strategy_timeframe_minutes = timeframe_to_minutes(self.timeframe) - for informative_data in ft_informative: - asset = informative_data.asset - timeframe = informative_data.timeframe - if timeframe_to_minutes(timeframe) < strategy_timeframe_minutes: + for informative_data in informative_data_list: + if timeframe_to_minutes(informative_data.timeframe) < strategy_timeframe_minutes: raise OperationalException('Informative timeframe must be equal or higher than ' 'strategy timeframe!') - if asset: - pair = _format_pair_name(self.config, asset) - try: - self._ft_informative[(pair, timeframe)].append( - (informative_data, cls_method)) - except KeyError: - self._ft_informative[(pair, timeframe)] = [(informative_data, cls_method)] - else: - for pair in self.dp.current_whitelist(): - try: - self._ft_informative[(pair, timeframe)].append( - (informative_data, cls_method)) - except KeyError: - self._ft_informative[(pair, timeframe)] = \ - [(informative_data, cls_method)] + self._ft_informative.append((informative_data, cls_method)) @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -424,8 +403,13 @@ class IStrategy(ABC, HyperStrategyMixin): Internal method which gathers all informative pairs (user or automatically defined). """ informative_pairs = self.informative_pairs() - if hasattr(self, '_ft_informative'): - informative_pairs += list(self._ft_informative.keys()) + for inf_data, _ in self._ft_informative: + if inf_data.asset: + pair_tf = (_format_pair_name(self.config, inf_data.asset), inf_data.timeframe) + informative_pairs.append(pair_tf) + else: + for pair in self.dp.current_whitelist(): + informative_pairs.append((pair, inf_data.timeframe)) return list(set(informative_pairs)) def get_strategy_name(self) -> str: @@ -845,14 +829,10 @@ class IStrategy(ABC, HyperStrategyMixin): """ logger.debug(f"Populating indicators for pair {metadata.get('pair')}.") - if hasattr(self, '_ft_informative'): - # call populate_indicators_Nm() which were tagged with @informative decorator. - for (pair, timeframe), informatives in self._ft_informative.items(): - for (informative_data, populate_fn) in informatives: - if not informative_data.asset and pair != metadata['pair']: - continue - dataframe = _create_and_merge_informative_pair( - self, dataframe, metadata, informative_data, populate_fn) + # call populate_indicators_Nm() which were tagged with @informative decorator. + for inf_data, populate_fn in self._ft_informative: + dataframe = _create_and_merge_informative_pair( + self, dataframe, metadata, inf_data, populate_fn) if self._populate_fun_len == 2: warnings.warn("deprecated - check out the Sample strategy to see " diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index a4023f953..15c6d8a69 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -171,14 +171,13 @@ def _format_pair_name(config, pair: str) -> str: stake=config['stake_currency']).upper() -def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, - metadata: dict, informative_data: InformativeData, - populate_indicators: Callable[[Any, DataFrame, dict], - DataFrame]): - asset = informative_data.asset or '' - timeframe = informative_data.timeframe - fmt = informative_data.fmt - ffill = informative_data.ffill +def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: dict, + inf_data: InformativeData, + populate_indicators: PopulateIndicators): + asset = inf_data.asset or '' + timeframe = inf_data.timeframe + fmt = inf_data.fmt + ffill = inf_data.ffill config = strategy.config dp = strategy.dp @@ -205,7 +204,7 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, fmt = '{column}_{timeframe}' # Informatives of current pair if quote != config['stake_currency']: fmt = '{quote}_' + fmt # Informatives of different quote currency - if informative_data.asset: + if inf_data.asset: fmt = '{base}_' + fmt # Informatives of other pair inf_metadata = {'pair': asset, 'timeframe': timeframe} diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 0ee554ede..95ca0416f 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -160,9 +160,8 @@ def test_informative_decorator(mocker, default_conf): mocker.patch.object(strategy.dp, 'current_whitelist', return_value=[ 'XRP/USDT', 'LTC/USDT', 'BTC/USDT' ]) - strategy._initialize() - assert len(strategy._ft_informative) == 8 + assert len(strategy._ft_informative) == 6 # Equal to number of decorators used informative_pairs = [('XRP/USDT', '1h'), ('LTC/USDT', '1h'), ('XRP/USDT', '30m'), ('LTC/USDT', '30m'), ('BTC/USDT', '1h'), ('BTC/USDT', '30m'), ('BTC/USDT', '5m'), ('ETH/BTC', '1h'), ('ETH/USDT', '30m')] From bb6ae682fc7d82175196741e31723c5f797ffd2f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 12 Sep 2021 09:59:10 +0200 Subject: [PATCH 458/519] Small simplifications --- docs/strategy-customization.md | 2 +- freqtrade/strategy/strategy_helper.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index a994d9acd..800dd9326 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -783,7 +783,7 @@ for more information. Do not use `@informative` decorator if you need to use data of one informative pair when generating another informative pair. Instead, define informative pairs manually as described [in the DataProvider section](#complete-data-provider-sample). -!!! Warning +!!! Warning "Duplicate method names" Methods tagged with `@informative()` decorator must always have unique names! Re-using same name (for example when copy-pasting already defined informative method) will overwrite previously defined method and not produce any errors due to limitations of Python programming language. In such cases you will find that indicators created in earlier-defined methods are not available in the dataframe. Carefully review method names and make sure they are unique! diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 15c6d8a69..746d656df 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -177,9 +177,7 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: asset = inf_data.asset or '' timeframe = inf_data.timeframe fmt = inf_data.fmt - ffill = inf_data.ffill config = strategy.config - dp = strategy.dp if asset: # Insert stake currency if needed. @@ -208,7 +206,7 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: fmt = '{base}_' + fmt # Informatives of other pair inf_metadata = {'pair': asset, 'timeframe': timeframe} - inf_dataframe = dp.get_pair_dataframe(asset, timeframe) + inf_dataframe = strategy.dp.get_pair_dataframe(asset, timeframe) inf_dataframe = populate_indicators(strategy, inf_dataframe, inf_metadata) formatter: Any = None @@ -233,6 +231,6 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: raise OperationalException(f'Duplicate column name {date_column} exists in ' f'dataframe! Ensure column names are unique!') dataframe = merge_informative_pair(dataframe, inf_dataframe, strategy.timeframe, timeframe, - ffill=ffill, append_timeframe=False, + ffill=inf_data.ffill, append_timeframe=False, date_column=date_column) return dataframe From e88c4701bb2c321ed7cd5d6fed7fa6db6f4f41f6 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sun, 12 Sep 2021 18:26:41 +0300 Subject: [PATCH 459/519] [SQUASH] Address PR comments. --- docs/strategy-customization.md | 19 ++- freqtrade/strategy/__init__.py | 5 +- freqtrade/strategy/informative_decorator.py | 134 ++++++++++++++++++++ freqtrade/strategy/interface.py | 11 +- freqtrade/strategy/strategy_helper.py | 132 ------------------- 5 files changed, 152 insertions(+), 149 deletions(-) create mode 100644 freqtrade/strategy/informative_decorator.py diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 800dd9326..671768bfa 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -652,9 +652,7 @@ In some situations it may be confusing to deal with stops relative to current ra ??? Example "Returning a stoploss using absolute price from the custom stoploss function" - Say the open price was $100, and `current_price` is $121 (`current_profit` will be `0.21`). - - If we want a stop price at $107 price we can call `stoploss_from_absolute(107, current_rate)` which will return `0.1157024793`. 11.57% below $121 is $107, which is the same as 7% above $100. + If we want to trail a stop price at 2xATR below current proce we can call `stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate)`. ``` python @@ -664,18 +662,17 @@ In some situations it may be confusing to deal with stops relative to current ra class AwesomeStrategy(IStrategy): - # ... populate_* methods - use_custom_stoploss = True + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) + return dataframe + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: - - # once the profit has risen above 10%, keep the stoploss at 7% above the open price - if current_profit > 0.10: - return stoploss_from_absolute(trade.open_rate * 1.07, current_rate) - - return 1 + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + candle = dataframe.iloc[-1].squeeze() + return stoploss_from_absolute(current_rate - (candle['atr'] * 2), current_rate) ``` diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index a7de34916..2ea0ad2b4 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -3,6 +3,7 @@ from freqtrade.exchange import (timeframe_to_minutes, timeframe_to_msecs, timefr timeframe_to_prev_date, timeframe_to_seconds) from freqtrade.strategy.hyper import (BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, RealParameter) +from freqtrade.strategy.informative_decorator import informative from freqtrade.strategy.interface import IStrategy -from freqtrade.strategy.strategy_helper import (informative, merge_informative_pair, - stoploss_from_absolute, stoploss_from_open) +from freqtrade.strategy.strategy_helper import (merge_informative_pair, stoploss_from_absolute, + stoploss_from_open) diff --git a/freqtrade/strategy/informative_decorator.py b/freqtrade/strategy/informative_decorator.py new file mode 100644 index 000000000..f09e634b0 --- /dev/null +++ b/freqtrade/strategy/informative_decorator.py @@ -0,0 +1,134 @@ +from typing import Any, Callable, NamedTuple, Optional, Union + +from mypy_extensions import KwArg +from pandas import DataFrame + +from freqtrade.exceptions import OperationalException +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +PopulateIndicators = Callable[[Any, DataFrame, dict], DataFrame] + + +class InformativeData(NamedTuple): + asset: Optional[str] + timeframe: str + fmt: Union[str, Callable[[KwArg(str)], str], None] + ffill: bool + + +def informative(timeframe: str, asset: str = '', + fmt: Optional[Union[str, Callable[[KwArg(str)], str]]] = None, + ffill: bool = True) -> Callable[[PopulateIndicators], PopulateIndicators]: + """ + A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to + define informative indicators. + + Example usage: + + @informative('1h') + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + return dataframe + + :param timeframe: Informative timeframe. Must always be equal or higher than strategy timeframe. + :param asset: Informative asset, for example BTC, BTC/USDT, ETH/BTC. Do not specify to use + current pair. + :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not + specified, defaults to: + * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake + currency. + * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match + stake currency. + * {column}_{timeframe} if asset is not specified. + Format string supports these format variables: + * {asset} - full name of the asset, for example 'BTC/USDT'. + * {base} - base currency in lower case, for example 'eth'. + * {BASE} - same as {base}, except in upper case. + * {quote} - quote currency in lower case, for example 'usdt'. + * {QUOTE} - same as {quote}, except in upper case. + * {column} - name of dataframe column. + * {timeframe} - timeframe of informative dataframe. + :param ffill: ffill dataframe after merging informative pair. + """ + _asset = asset + _timeframe = timeframe + _fmt = fmt + _ffill = ffill + + def decorator(fn: PopulateIndicators): + informative_pairs = getattr(fn, '_ft_informative', []) + informative_pairs.append(InformativeData(_asset, _timeframe, _fmt, _ffill)) + setattr(fn, '_ft_informative', informative_pairs) + return fn + return decorator + + +def _format_pair_name(config, pair: str) -> str: + return pair.format(stake_currency=config['stake_currency'], + stake=config['stake_currency']).upper() + + +def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: dict, + inf_data: InformativeData, + populate_indicators: PopulateIndicators): + asset = inf_data.asset or '' + timeframe = inf_data.timeframe + fmt = inf_data.fmt + config = strategy.config + + if asset: + # Insert stake currency if needed. + asset = _format_pair_name(config, asset) + else: + # Not specifying an asset will define informative dataframe for current pair. + asset = metadata['pair'] + + if '/' in asset: + base, quote = asset.split('/') + else: + # When futures are supported this may need reevaluation. + # base, quote = asset, None + raise OperationalException('Not implemented.') + + # Default format. This optimizes for the common case: informative pairs using same stake + # currency. When quote currency matches stake currency, column name will omit base currency. + # This allows easily reconfiguring strategy to use different base currency. In a rare case + # where it is desired to keep quote currency in column name at all times user should specify + # fmt='{base}_{quote}_{column}_{timeframe}' format or similar. + if not fmt: + fmt = '{column}_{timeframe}' # Informatives of current pair + if quote != config['stake_currency']: + fmt = '{quote}_' + fmt # Informatives of different quote currency + if inf_data.asset: + fmt = '{base}_' + fmt # Informatives of other pair + + inf_metadata = {'pair': asset, 'timeframe': timeframe} + inf_dataframe = strategy.dp.get_pair_dataframe(asset, timeframe) + inf_dataframe = populate_indicators(strategy, inf_dataframe, inf_metadata) + + formatter: Any = None + if callable(fmt): + formatter = fmt # A custom user-specified formatter function. + else: + formatter = fmt.format # A default string formatter. + + fmt_args = { + 'BASE': base.upper(), + 'QUOTE': quote.upper(), + 'base': base.lower(), + 'quote': quote.lower(), + 'asset': asset, + 'timeframe': timeframe, + } + inf_dataframe.rename(columns=lambda column: formatter(column=column, **fmt_args), + inplace=True) + + date_column = formatter(column='date', **fmt_args) + if date_column in dataframe.columns: + raise OperationalException(f'Duplicate column name {date_column} exists in ' + f'dataframe! Ensure column names are unique!') + dataframe = merge_informative_pair(dataframe, inf_dataframe, strategy.timeframe, timeframe, + ffill=inf_data.ffill, append_timeframe=False, + date_column=date_column) + return dataframe diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 00c56f5df..7420bd9fd 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -19,9 +19,9 @@ from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_next_date from freqtrade.persistence import PairLocks, Trade from freqtrade.strategy.hyper import HyperStrategyMixin -from freqtrade.strategy.strategy_helper import (InformativeData, PopulateIndicators, - _create_and_merge_informative_pair, - _format_pair_name) +from freqtrade.strategy.informative_decorator import (InformativeData, PopulateIndicators, + _create_and_merge_informative_pair, + _format_pair_name) from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.wallets import Wallets @@ -121,7 +121,7 @@ class IStrategy(ABC, HyperStrategyMixin): # Class level variables (intentional) containing # the dataprovider (dp) (access to other candles, historic data, ...) # and wallets - access to the current balance. - dp: DataProvider + dp: Optional[DataProvider] wallets: Optional[Wallets] = None # Filled from configuration stake_currency: str @@ -408,6 +408,9 @@ class IStrategy(ABC, HyperStrategyMixin): pair_tf = (_format_pair_name(self.config, inf_data.asset), inf_data.timeframe) informative_pairs.append(pair_tf) else: + if not self.dp: + raise OperationalException('@informative decorator with unspecified asset ' + 'requires DataProvider instance.') for pair in self.dp.current_whitelist(): informative_pairs.append((pair, inf_data.timeframe)) return list(set(informative_pairs)) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 746d656df..f813b39c5 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -1,23 +1,8 @@ -from typing import Any, Callable, NamedTuple, Optional, Union - import pandas as pd -from mypy_extensions import KwArg -from pandas import DataFrame -from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes -PopulateIndicators = Callable[[Any, DataFrame, dict], DataFrame] - - -class InformativeData(NamedTuple): - asset: Optional[str] - timeframe: str - fmt: Union[str, Callable[[KwArg(str)], str], None] - ffill: bool - - def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, timeframe: str, timeframe_inf: str, ffill: bool = True, append_timeframe: bool = True, @@ -117,120 +102,3 @@ def stoploss_from_absolute(stop_rate: float, current_rate: float) -> float: :return: Positive stop loss value relative to current price """ return 1 - (stop_rate / current_rate) - - -def informative(timeframe: str, asset: str = '', - fmt: Optional[Union[str, Callable[[KwArg(str)], str]]] = None, - ffill: bool = True) -> Callable[[PopulateIndicators], PopulateIndicators]: - """ - A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to - define informative indicators. - - Example usage: - - @informative('1h') - def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) - return dataframe - - :param timeframe: Informative timeframe. Must always be equal or higher than strategy timeframe. - :param asset: Informative asset, for example BTC, BTC/USDT, ETH/BTC. Do not specify to use - current pair. - :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not - specified, defaults to: - * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake - currency. - * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match - stake currency. - * {column}_{timeframe} if asset is not specified. - Format string supports these format variables: - * {asset} - full name of the asset, for example 'BTC/USDT'. - * {base} - base currency in lower case, for example 'eth'. - * {BASE} - same as {base}, except in upper case. - * {quote} - quote currency in lower case, for example 'usdt'. - * {QUOTE} - same as {quote}, except in upper case. - * {column} - name of dataframe column. - * {timeframe} - timeframe of informative dataframe. - :param ffill: ffill dataframe after merging informative pair. - """ - _asset = asset - _timeframe = timeframe - _fmt = fmt - _ffill = ffill - - def decorator(fn: PopulateIndicators): - informative_pairs = getattr(fn, '_ft_informative', []) - informative_pairs.append(InformativeData(_asset, _timeframe, _fmt, _ffill)) - setattr(fn, '_ft_informative', informative_pairs) - return fn - return decorator - - -def _format_pair_name(config, pair: str) -> str: - return pair.format(stake_currency=config['stake_currency'], - stake=config['stake_currency']).upper() - - -def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: dict, - inf_data: InformativeData, - populate_indicators: PopulateIndicators): - asset = inf_data.asset or '' - timeframe = inf_data.timeframe - fmt = inf_data.fmt - config = strategy.config - - if asset: - # Insert stake currency if needed. - asset = _format_pair_name(config, asset) - else: - # Not specifying an asset will define informative dataframe for current pair. - asset = metadata['pair'] - - if '/' in asset: - base, quote = asset.split('/') - else: - # When futures are supported this may need reevaluation. - # base, quote = asset, None - raise OperationalException('Not implemented.') - - # Default format. This optimizes for the common case: informative pairs using same stake - # currency. When quote currency matches stake currency, column name will omit base currency. - # This allows easily reconfiguring strategy to use different base currency. In a rare case - # where it is desired to keep quote currency in column name at all times user should specify - # fmt='{base}_{quote}_{column}_{timeframe}' format or similar. - if not fmt: - fmt = '{column}_{timeframe}' # Informatives of current pair - if quote != config['stake_currency']: - fmt = '{quote}_' + fmt # Informatives of different quote currency - if inf_data.asset: - fmt = '{base}_' + fmt # Informatives of other pair - - inf_metadata = {'pair': asset, 'timeframe': timeframe} - inf_dataframe = strategy.dp.get_pair_dataframe(asset, timeframe) - inf_dataframe = populate_indicators(strategy, inf_dataframe, inf_metadata) - - formatter: Any = None - if callable(fmt): - formatter = fmt # A custom user-specified formatter function. - else: - formatter = fmt.format # A default string formatter. - - fmt_args = { - 'BASE': base.upper(), - 'QUOTE': quote.upper(), - 'base': base.lower(), - 'quote': quote.lower(), - 'asset': asset, - 'timeframe': timeframe, - } - inf_dataframe.rename(columns=lambda column: formatter(column=column, **fmt_args), - inplace=True) - - date_column = formatter(column='date', **fmt_args) - if date_column in dataframe.columns: - raise OperationalException(f'Duplicate column name {date_column} exists in ' - f'dataframe! Ensure column names are unique!') - dataframe = merge_informative_pair(dataframe, inf_dataframe, strategy.timeframe, timeframe, - ffill=inf_data.ffill, append_timeframe=False, - date_column=date_column) - return dataframe From 7e6aa9390ae9c970f8b22afb591161492f833807 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sat, 18 Sep 2021 10:17:50 +0300 Subject: [PATCH 460/519] [SQUASH] Unconditionally include quote currency when asset is explicitly specified. Added docs suggesting to use string formatting to make strategy independent of configured stake currency. --- docs/strategy-customization.md | 26 +++++++++++++++++---- freqtrade/strategy/informative_decorator.py | 11 +++------ tests/strategy/test_strategy_helpers.py | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 671768bfa..2b22dd274 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -698,10 +698,7 @@ def informative(timeframe: str, asset: str = '', current pair. :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not specified, defaults to: - * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake - currency. - * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match - stake currency. + * {base}_{quote}_{column}_{timeframe} if asset is specified. * {column}_{timeframe} if asset is not specified. Format string supports these format variables: * {asset} - full name of the asset, for example 'BTC/USDT'. @@ -746,7 +743,7 @@ for more information. # Define BTC/STAKE informative pair. Available in populate_indicators and other methods as # 'btc_rsi_1h'. Current stake currency should be specified as {stake} format variable # instead of hardcoding actual stake currency. Available in populate_indicators and other - # methods as 'btc_rsi_1h'. + # methods as 'btc_usdt_rsi_1h' (when stake currency is USDT). @informative('1h', 'BTC/{stake}') def populate_indicators_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) @@ -780,6 +777,25 @@ for more information. Do not use `@informative` decorator if you need to use data of one informative pair when generating another informative pair. Instead, define informative pairs manually as described [in the DataProvider section](#complete-data-provider-sample). +!!! Note + Use string formatting when accessing informative dataframes of other pairs. This will allow easily changing stake currency in config without having to adjust strategy code. + + ``` python + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + stake = self.config['stake_currency'] + dataframe.loc[ + ( + (dataframe[f'btc_{stake}_rsi_1h'] < 35) + & + (dataframe['volume'] > 0) + ), + ['buy', 'buy_tag']] = (1, 'buy_signal_rsi') + + return dataframe + ``` + + Alternatively column renaming may be used to remove stake currency from column names: `@informative('1h', 'BTC/{stake}', fmt='{base}_{column}_{timeframe}')`. + !!! Warning "Duplicate method names" Methods tagged with `@informative()` decorator must always have unique names! Re-using same name (for example when copy-pasting already defined informative method) will overwrite previously defined method and not produce any errors due to limitations of Python programming language. In such cases you will find that indicators diff --git a/freqtrade/strategy/informative_decorator.py b/freqtrade/strategy/informative_decorator.py index f09e634b0..c8ebf5989 100644 --- a/freqtrade/strategy/informative_decorator.py +++ b/freqtrade/strategy/informative_decorator.py @@ -36,10 +36,7 @@ def informative(timeframe: str, asset: str = '', current pair. :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not specified, defaults to: - * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake - currency. - * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match - stake currency. + * {base}_{quote}_{column}_{timeframe} if asset is specified. * {column}_{timeframe} if asset is not specified. Format string supports these format variables: * {asset} - full name of the asset, for example 'BTC/USDT'. @@ -88,7 +85,7 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: base, quote = asset.split('/') else: # When futures are supported this may need reevaluation. - # base, quote = asset, None + # base, quote = asset, '' raise OperationalException('Not implemented.') # Default format. This optimizes for the common case: informative pairs using same stake @@ -98,10 +95,8 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: # fmt='{base}_{quote}_{column}_{timeframe}' format or similar. if not fmt: fmt = '{column}_{timeframe}' # Informatives of current pair - if quote != config['stake_currency']: - fmt = '{quote}_' + fmt # Informatives of different quote currency if inf_data.asset: - fmt = '{base}_' + fmt # Informatives of other pair + fmt = '{base}_{quote}_' + fmt # Informatives of other pairs inf_metadata = {'pair': asset, 'timeframe': timeframe} inf_dataframe = strategy.dp.get_pair_dataframe(asset, timeframe) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 95ca0416f..d4206ba8c 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -177,7 +177,7 @@ def test_informative_decorator(mocker, default_conf): {p: data[(p, strategy.timeframe)] for p in ('XRP/USDT', 'LTC/USDT')}) expected_columns = [ 'rsi_1h', 'rsi_30m', # Stacked informative decorators - 'btc_rsi_1h', # BTC 1h informative + 'btc_usdt_rsi_1h', # BTC 1h informative 'rsi_BTC_USDT_btc_usdt_BTC/USDT_30m', # Column formatting 'rsi_from_callable', # Custom column formatter 'eth_btc_rsi_1h', # Quote currency not matching stake currency From e4ca42faec580a3c336298d42402fec0d8e56f93 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sat, 18 Sep 2021 10:18:33 +0300 Subject: [PATCH 461/519] [SQUASH] Update stoploss_from_absolute to behave more like stoploss_from_open and add a test for it. --- freqtrade/strategy/strategy_helper.py | 16 +++++++++++++++- tests/strategy/test_strategy_helpers.py | 11 ++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index f813b39c5..175bcaccb 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -97,8 +97,22 @@ def stoploss_from_absolute(stop_rate: float, current_rate: float) -> float: """ Given current price and desired stop price, return a stop loss value that is relative to current price. + + The requested stop can be positive for a stop above the open price, or negative for + a stop below the open price. The return value is always >= 0. + + Returns 0 if the resulting stop price would be above the current price. + :param stop_rate: Stop loss price. :param current_rate: Current asset price. :return: Positive stop loss value relative to current price """ - return 1 - (stop_rate / current_rate) + + # formula is undefined for current_rate 0, return maximum value + if current_rate == 0: + return 1 + + stoploss = 1 - (stop_rate / current_rate) + + # negative stoploss values indicate the requested stop price is higher than the current price + return max(stoploss, 0.0) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index d4206ba8c..9132382fa 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -5,7 +5,8 @@ import pandas as pd import pytest from freqtrade.data.dataprovider import DataProvider -from freqtrade.strategy import merge_informative_pair, stoploss_from_open, timeframe_to_minutes +from freqtrade.strategy import (merge_informative_pair, stoploss_from_open, timeframe_to_minutes, + stoploss_from_absolute) def generate_test_data(timeframe: str, size: int): @@ -135,6 +136,14 @@ def test_stoploss_from_open(): assert isclose(stop_price, expected_stop_price, rel_tol=0.00001) +def test_stoploss_from_absolute(): + assert stoploss_from_absolute(90, 100) == 1 - (90 / 100) + assert stoploss_from_absolute(100, 100) == 0 + assert stoploss_from_absolute(110, 100) == 0 + assert stoploss_from_absolute(100, 0) == 1 + assert stoploss_from_absolute(0, 100) == 1 + + def test_informative_decorator(mocker, default_conf): test_data_5m = generate_test_data('5m', 40) test_data_30m = generate_test_data('30m', 40) From 216f75bbb91b8d72790f386dfc8b05340ade3c3d Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 18 Sep 2021 02:53:34 -0600 Subject: [PATCH 462/519] parametrized test_sell_profit_only , test__safe_exit_amount, test_order_book_bid_strategy1 --- tests/test_freqtradebot.py | 244 ++++++++++--------------------------- 1 file changed, 65 insertions(+), 179 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index eb3c77cc7..5268d40ec 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3128,16 +3128,28 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, assert mock_insuf.call_count == 1 -def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy_order_open, - fee, mocker) -> None: +@pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,sell_type', [ + # Enable profit + (True, 0.00001172, 0.00001173, False, True, SellType.SELL_SIGNAL.value), + # Disable profit + (False, 0.00002172, 0.00002173, True, False, SellType.SELL_SIGNAL.value), + # Enable loss + # * Shouldn't this be SellType.STOP_LOSS.value + (True, 0.00000172, 0.00000173, False, False, None), + # Disable loss + (False, 0.00000172, 0.00000173, True, False, SellType.SELL_SIGNAL.value), +]) +def test_sell_profit_only_enable_profit( + default_conf, limit_buy_order, limit_buy_order_open, + fee, mocker, profit_only, bid, ask, handle_first, handle_second, sell_type) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': 0.00001172, - 'ask': 0.00001173, - 'last': 0.00001172 + 'bid': bid, + 'ask': ask, + 'last': bid }), create_order=MagicMock(side_effect=[ limit_buy_order_open, @@ -3147,128 +3159,29 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy ) default_conf.update({ 'use_sell_signal': True, - 'sell_profit_only': True, + 'sell_profit_only': profit_only, 'sell_profit_offset': 0.1, }) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - + if sell_type == SellType.SELL_SIGNAL.value: + freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) + else: + freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple( + sell_type=SellType.NONE)) freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) freqtrade.wallets.update() patch_get_signal(freqtrade, value=(False, True, None)) - assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_trade(trade) is handle_first - freqtrade.strategy.sell_profit_offset = 0.0 - assert freqtrade.handle_trade(trade) is True + if handle_second: + freqtrade.strategy.sell_profit_offset = 0.0 + assert freqtrade.handle_trade(trade) is True - assert trade.sell_reason == SellType.SELL_SIGNAL.value - - -def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, limit_buy_order_open, - fee, mocker) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=MagicMock(return_value={ - 'bid': 0.00002172, - 'ask': 0.00002173, - 'last': 0.00002172 - }), - create_order=MagicMock(side_effect=[ - limit_buy_order_open, - {'id': 1234553382}, - ]), - get_fee=fee, - ) - default_conf.update({ - 'use_sell_signal': True, - 'sell_profit_only': False, - }) - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.enter_positions() - - trade = Trade.query.first() - trade.update(limit_buy_order) - freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, None)) - assert freqtrade.handle_trade(trade) is True - assert trade.sell_reason == SellType.SELL_SIGNAL.value - - -def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, limit_buy_order_open, - fee, mocker) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=MagicMock(return_value={ - 'bid': 0.00000172, - 'ask': 0.00000173, - 'last': 0.00000172 - }), - create_order=MagicMock(side_effect=[ - limit_buy_order_open, - {'id': 1234553382}, - ]), - get_fee=fee, - ) - default_conf.update({ - 'use_sell_signal': True, - 'sell_profit_only': True, - }) - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple( - sell_type=SellType.NONE)) - freqtrade.enter_positions() - - trade = Trade.query.first() - trade.update(limit_buy_order) - patch_get_signal(freqtrade, value=(False, True, None)) - assert freqtrade.handle_trade(trade) is False - - -def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, limit_buy_order_open, - fee, mocker) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=MagicMock(return_value={ - 'bid': 0.0000172, - 'ask': 0.0000173, - 'last': 0.0000172 - }), - create_order=MagicMock(side_effect=[ - limit_buy_order_open, - {'id': 1234553382}, - ]), - get_fee=fee, - ) - default_conf.update({ - 'use_sell_signal': True, - 'sell_profit_only': False, - }) - - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - - freqtrade.enter_positions() - - trade = Trade.query.first() - trade.update(limit_buy_order) - freqtrade.wallets.update() - patch_get_signal(freqtrade, value=(False, True, None)) - assert freqtrade.handle_trade(trade) is True - assert trade.sell_reason == SellType.SELL_SIGNAL.value + assert trade.sell_reason == sell_type def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_open, @@ -3306,11 +3219,15 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_ assert trade.amount != amnt -def test__safe_exit_amount(default_conf, fee, caplog, mocker): +@pytest.mark.parametrize('amount_wallet,has_err', [ + (95.29, False), + (91.29, True) +]) +def test__safe_exit_amount(default_conf, fee, caplog, mocker, amount_wallet, has_err): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 - amount_wallet = 95.29 + amount_wallet = amount_wallet mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet)) wallet_update = mocker.patch('freqtrade.wallets.Wallets.update') trade = Trade( @@ -3324,37 +3241,19 @@ def test__safe_exit_amount(default_conf, fee, caplog, mocker): ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - - wallet_update.reset_mock() - assert freqtrade._safe_exit_amount(trade.pair, trade.amount) == amount_wallet - assert log_has_re(r'.*Falling back to wallet-amount.', caplog) - assert wallet_update.call_count == 1 - caplog.clear() - wallet_update.reset_mock() - assert freqtrade._safe_exit_amount(trade.pair, amount_wallet) == amount_wallet - assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) - assert wallet_update.call_count == 1 - - -def test__safe_exit_amount_error(default_conf, fee, caplog, mocker): - patch_RPCManager(mocker) - patch_exchange(mocker) - amount = 95.33 - amount_wallet = 91.29 - mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet)) - trade = Trade( - pair='LTC/ETH', - amount=amount, - exchange='binance', - open_rate=0.245441, - open_order_id="123456", - fee_open=fee.return_value, - fee_close=fee.return_value, - ) - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - with pytest.raises(DependencyException, match=r"Not enough amount to sell."): - assert freqtrade._safe_exit_amount(trade.pair, trade.amount) + if has_err: + with pytest.raises(DependencyException, match=r"Not enough amount to sell."): + assert freqtrade._safe_exit_amount(trade.pair, trade.amount) + else: + wallet_update.reset_mock() + assert freqtrade._safe_exit_amount(trade.pair, trade.amount) == amount_wallet + assert log_has_re(r'.*Falling back to wallet-amount.', caplog) + assert wallet_update.call_count == 1 + caplog.clear() + wallet_update.reset_mock() + assert freqtrade._safe_exit_amount(trade.pair, amount_wallet) == amount_wallet + assert not log_has_re(r'.*Falling back to wallet-amount.', caplog) + assert wallet_update.call_count == 1 def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None: @@ -4131,50 +4030,37 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o assert trade is None -def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: +@pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [ + (False, 0.045, 0.046, 2, None), + (True, 0.042, 0.046, 1, {'bids': [[]], 'asks': [[]]}) +]) +def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2, exception_thrown, + ask, last, order_book_top, order_book, caplog) -> None: """ - test if function get_rate will return the order book price - instead of the ask rate + test if function get_rate will return the order book price instead of the ask rate """ patch_exchange(mocker) - ticker_mock = MagicMock(return_value={'ask': 0.045, 'last': 0.046}) + ticker_mock = MagicMock(return_value={'ask': ask, 'last': last}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_l2_order_book=order_book_l2, + fetch_l2_order_book=MagicMock(return_value=order_book) if order_book else order_book_l2, fetch_ticker=ticker_mock, - ) default_conf['exchange']['name'] = 'binance' default_conf['bid_strategy']['use_order_book'] = True - default_conf['bid_strategy']['order_book_top'] = 2 + default_conf['bid_strategy']['order_book_top'] = order_book_top default_conf['bid_strategy']['ask_last_balance'] = 0 default_conf['telegram']['enabled'] = False freqtrade = FreqtradeBot(default_conf) - assert freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") == 0.043935 - assert ticker_mock.call_count == 0 - - -def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None: - patch_exchange(mocker) - ticker_mock = MagicMock(return_value={'ask': 0.042, 'last': 0.046}) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_l2_order_book=MagicMock(return_value={'bids': [[]], 'asks': [[]]}), - fetch_ticker=ticker_mock, - - ) - default_conf['exchange']['name'] = 'binance' - default_conf['bid_strategy']['use_order_book'] = True - default_conf['bid_strategy']['order_book_top'] = 1 - default_conf['bid_strategy']['ask_last_balance'] = 0 - default_conf['telegram']['enabled'] = False - - freqtrade = FreqtradeBot(default_conf) - # orderbook shall be used even if tickers would be lower. - with pytest.raises(PricingError): - freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") - assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog) + if exception_thrown: + with pytest.raises(PricingError): + freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") + assert log_has_re( + r'Buy Price at location 1 from orderbook could not be determined.', caplog) + else: + assert freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") == 0.043935 + assert ticker_mock.call_count == 0 def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: From 42a2fdc1c520c97b27cf937e25aa7bd85a581a21 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 18 Sep 2021 03:01:08 -0600 Subject: [PATCH 463/519] parametrized test_order_dict --- tests/test_freqtradebot.py | 47 ++++++++------------------------------ 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 5268d40ec..8e8ae4da2 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -78,11 +78,15 @@ def test_bot_cleanup(mocker, default_conf, caplog) -> None: assert coo_mock.call_count == 1 -def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: +@pytest.mark.parametrize('runmode', [ + RunMode.DRY_RUN, + RunMode.LIVE +]) +def test_order_dict(default_conf, mocker, runmode, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) conf = default_conf.copy() - conf['runmode'] = RunMode.DRY_RUN + conf['runmode'] = runmode conf['order_types'] = { 'buy': 'market', 'sell': 'limit', @@ -92,45 +96,14 @@ def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: conf['bid_strategy']['price_side'] = 'ask' freqtrade = FreqtradeBot(conf) + if runmode == RunMode.LIVE: + assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) assert freqtrade.strategy.order_types['stoploss_on_exchange'] caplog.clear() # is left untouched conf = default_conf.copy() - conf['runmode'] = RunMode.DRY_RUN - conf['order_types'] = { - 'buy': 'market', - 'sell': 'limit', - 'stoploss': 'limit', - 'stoploss_on_exchange': False, - } - freqtrade = FreqtradeBot(conf) - assert not freqtrade.strategy.order_types['stoploss_on_exchange'] - assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) - - -def test_order_dict_live(default_conf, mocker, caplog) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - - conf = default_conf.copy() - conf['runmode'] = RunMode.LIVE - conf['order_types'] = { - 'buy': 'market', - 'sell': 'limit', - 'stoploss': 'limit', - 'stoploss_on_exchange': True, - } - conf['bid_strategy']['price_side'] = 'ask' - - freqtrade = FreqtradeBot(conf) - assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog) - assert freqtrade.strategy.order_types['stoploss_on_exchange'] - - caplog.clear() - # is left untouched - conf = default_conf.copy() - conf['runmode'] = RunMode.LIVE + conf['runmode'] = runmode conf['order_types'] = { 'buy': 'market', 'sell': 'limit', @@ -3139,7 +3112,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee, # Disable loss (False, 0.00000172, 0.00000173, True, False, SellType.SELL_SIGNAL.value), ]) -def test_sell_profit_only_enable_profit( +def test_sell_profit_only( default_conf, limit_buy_order, limit_buy_order_open, fee, mocker, profit_only, bid, ask, handle_first, handle_second, sell_type) -> None: patch_RPCManager(mocker) From 518a59ad41daa50c51deb8d7e70e75b3b9a8bad0 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 18 Sep 2021 03:20:00 -0600 Subject: [PATCH 464/519] parametrized test_edge_overrides_stoploss --- tests/test_freqtradebot.py | 60 ++++++++++---------------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 8e8ae4da2..debe2bee0 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -192,8 +192,14 @@ def test_edge_overrides_stake_amount(mocker, edge_conf) -> None: 'LTC/BTC', freqtrade.edge) == (999.9 * 0.5 * 0.01) / 0.21 -def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf) -> None: - +@pytest.mark.parametrize('buy_price_mult,ignore_strat_sl', [ + # Override stoploss + (0.79, False), + # Override strategy stoploss + (0.85, True) +]) +def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, + buy_price_mult, ignore_strat_sl, edge_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker) patch_edge(mocker) @@ -207,9 +213,9 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=MagicMock(return_value={ - 'bid': buy_price * 0.79, - 'ask': buy_price * 0.79, - 'last': buy_price * 0.79 + 'bid': buy_price * buy_price_mult, + 'ask': buy_price * buy_price_mult, + 'last': buy_price * buy_price_mult, }), get_fee=fee, ) @@ -226,46 +232,10 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf ############################################# # stoploss shoud be hit - assert freqtrade.handle_trade(trade) is True - assert log_has('Executing Sell for NEO/BTC. Reason: stop_loss', caplog) - assert trade.sell_reason == SellType.STOP_LOSS.value - - -def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, - mocker, edge_conf) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - patch_edge(mocker) - edge_conf['max_open_trades'] = float('inf') - - # Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2 - # Thus, if price falls 15%, stoploss should not be triggered - # - # mocking the ticker: price is falling ... - buy_price = limit_buy_order['price'] - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=MagicMock(return_value={ - 'bid': buy_price * 0.85, - 'ask': buy_price * 0.85, - 'last': buy_price * 0.85 - }), - get_fee=fee, - ) - ############################################# - - # Create a trade with "limit_buy_order" price - freqtrade = FreqtradeBot(edge_conf) - freqtrade.active_pair_whitelist = ['NEO/BTC'] - patch_get_signal(freqtrade) - freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.enter_positions() - trade = Trade.query.first() - trade.update(limit_buy_order) - ############################################# - - # stoploss shoud not be hit - assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_trade(trade) is not ignore_strat_sl + if not ignore_strat_sl: + assert log_has('Executing Sell for NEO/BTC. Reason: stop_loss', caplog) + assert trade.sell_reason == SellType.STOP_LOSS.value def test_total_open_trades_stakes(mocker, default_conf, ticker, fee) -> None: From 713e7819f7e534a36984f9f4c76a11bbfd948896 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sat, 18 Sep 2021 15:27:58 +0300 Subject: [PATCH 465/519] [SQUASH] Remove mypy import. --- freqtrade/strategy/informative_decorator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/freqtrade/strategy/informative_decorator.py b/freqtrade/strategy/informative_decorator.py index c8ebf5989..4c5f21108 100644 --- a/freqtrade/strategy/informative_decorator.py +++ b/freqtrade/strategy/informative_decorator.py @@ -1,6 +1,5 @@ from typing import Any, Callable, NamedTuple, Optional, Union -from mypy_extensions import KwArg from pandas import DataFrame from freqtrade.exceptions import OperationalException @@ -13,12 +12,12 @@ PopulateIndicators = Callable[[Any, DataFrame, dict], DataFrame] class InformativeData(NamedTuple): asset: Optional[str] timeframe: str - fmt: Union[str, Callable[[KwArg(str)], str], None] + fmt: Union[str, Callable[[Any], str], None] ffill: bool def informative(timeframe: str, asset: str = '', - fmt: Optional[Union[str, Callable[[KwArg(str)], str]]] = None, + fmt: Optional[Union[str, Callable[[Any], str]]] = None, ffill: bool = True) -> Callable[[PopulateIndicators], PopulateIndicators]: """ A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to From eab7f8f6944e77fa7ae69bf99fe885aa76aea5a5 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Sat, 18 Sep 2021 15:44:21 +0300 Subject: [PATCH 466/519] [SQUASH] Doh. --- tests/strategy/test_strategy_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 9132382fa..a01b55050 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -5,8 +5,8 @@ import pandas as pd import pytest from freqtrade.data.dataprovider import DataProvider -from freqtrade.strategy import (merge_informative_pair, stoploss_from_open, timeframe_to_minutes, - stoploss_from_absolute) +from freqtrade.strategy import (merge_informative_pair, stoploss_from_absolute, stoploss_from_open, + timeframe_to_minutes) def generate_test_data(timeframe: str, size: int): From c1895a0fc230f14e9de79a3042d77d3379a61c1f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Sep 2021 15:30:33 +0200 Subject: [PATCH 467/519] Remove warning related to legacy hyperopt --- docs/strategy-customization.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 2b22dd274..725252b30 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -801,9 +801,6 @@ for more information. will overwrite previously defined method and not produce any errors due to limitations of Python programming language. In such cases you will find that indicators created in earlier-defined methods are not available in the dataframe. Carefully review method names and make sure they are unique! -!!! Warning - When using a legacy hyperopt implementation informative pairs defined with a decorator will not be executed. Please update your strategy if necessary. - ## Additional data (Wallets) The strategy provides access to the `Wallets` object. This contains the current balances on the exchange. From ec9dbc550e66c2694bfe32a43c3442952d8b685c Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 18 Sep 2021 19:19:53 -0600 Subject: [PATCH 468/519] parametrized test_create_trade_minimal_amount --- tests/test_freqtradebot.py | 93 +++++++++++--------------------------- 1 file changed, 26 insertions(+), 67 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index debe2bee0..209c8a2a2 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -319,8 +319,16 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, freqtrade.create_trade('ETH/BTC') -def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order_open, - fee, mocker) -> None: +@pytest.mark.parametrize('stake_amount,create,amount_enough,max_open_trades', [ + (0.0005, True, True, 99), + (0.000000005, True, False, 99), + (0, False, True, 99), + (UNLIMITED_STAKE_AMOUNT, False, True, 0), +]) +def test_create_trade_minimal_amount( + default_conf, ticker, limit_buy_order_open, fee, mocker, + stake_amount, create, amount_enough, max_open_trades, caplog +) -> None: patch_RPCManager(mocker) patch_exchange(mocker) buy_mock = MagicMock(return_value=limit_buy_order_open) @@ -330,74 +338,25 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order_open, create_order=buy_mock, get_fee=fee, ) - default_conf['stake_amount'] = 0.0005 + default_conf['max_open_trades'] = max_open_trades freqtrade = FreqtradeBot(default_conf) + freqtrade.config['stake_amount'] = stake_amount patch_get_signal(freqtrade) - freqtrade.create_trade('ETH/BTC') - rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount'] - assert rate * amount <= default_conf['stake_amount'] - - -def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order_open, - fee, mocker, caplog) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - buy_mock = MagicMock(return_value=limit_buy_order_open) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - create_order=buy_mock, - get_fee=fee, - ) - - freqtrade = FreqtradeBot(default_conf) - freqtrade.config['stake_amount'] = 0.000000005 - - patch_get_signal(freqtrade) - - assert freqtrade.create_trade('ETH/BTC') - assert log_has_re(r"Stake amount for pair .* is too small.*", caplog) - - -def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open, - fee, mocker) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - buy_mock = MagicMock(return_value=limit_buy_order_open) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - create_order=buy_mock, - get_fee=fee, - ) - - freqtrade = FreqtradeBot(default_conf) - freqtrade.config['stake_amount'] = 0 - - patch_get_signal(freqtrade) - - assert not freqtrade.create_trade('ETH/BTC') - - -def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order_open, - fee, mocker) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - create_order=MagicMock(return_value=limit_buy_order_open), - get_fee=fee, - ) - default_conf['max_open_trades'] = 0 - default_conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT - - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - - assert not freqtrade.create_trade('ETH/BTC') - assert freqtrade.wallets.get_trade_stake_amount('ETH/BTC', freqtrade.edge) == 0 + if create: + assert freqtrade.create_trade('ETH/BTC') + if amount_enough: + rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount'] + assert rate * amount <= default_conf['stake_amount'] + else: + assert log_has_re( + r"Stake amount for pair .* is too small.*", + caplog + ) + else: + assert not freqtrade.create_trade('ETH/BTC') + if not max_open_trades: + assert freqtrade.wallets.get_trade_stake_amount('ETH/BTC', freqtrade.edge) == 0 def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_open, fee, From cee4ed541b8e4bec16ce8a23598531fb4d64ec57 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 18 Sep 2021 19:46:46 -0600 Subject: [PATCH 469/519] parametrized test_update_trade_state_withorderdict --- tests/test_freqtradebot.py | 104 +++++++++++++------------------------ 1 file changed, 35 insertions(+), 69 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 209c8a2a2..72d1f6150 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -359,8 +359,12 @@ def test_create_trade_minimal_amount( assert freqtrade.wallets.get_trade_stake_amount('ETH/BTC', freqtrade.edge) == 0 +@pytest.mark.parametrize('whitelist,positions', [ + (["ETH/BTC"], 1), # No pairs left + ([], 0), # No pairs in whitelist +]) def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_open, fee, - mocker, caplog) -> None: + whitelist, positions, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -369,36 +373,20 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope create_order=MagicMock(return_value=limit_buy_order_open), get_fee=fee, ) - - default_conf['exchange']['pair_whitelist'] = ["ETH/BTC"] + default_conf['exchange']['pair_whitelist'] = whitelist freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) n = freqtrade.enter_positions() - assert n == 1 - assert not log_has_re(r"No currency pair in active pair whitelist.*", caplog) - n = freqtrade.enter_positions() - assert n == 0 - assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) - - -def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, - mocker, caplog) -> None: - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - create_order=MagicMock(return_value={'id': limit_buy_order['id']}), - get_fee=fee, - ) - default_conf['exchange']['pair_whitelist'] = [] - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - - n = freqtrade.enter_positions() - assert n == 0 - assert log_has("Active pair whitelist is empty.", caplog) + assert n == positions + if positions: + assert not log_has_re(r"No currency pair in active pair whitelist.*", caplog) + n = freqtrade.enter_positions() + assert n == 0 + assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) + else: + assert n == 0 + assert log_has("Active pair whitelist is empty.", caplog) @pytest.mark.usefixtures("init_persistence") @@ -1555,30 +1543,27 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, stop_price=0.00002346 * 0.99) -def test_enter_positions(mocker, default_conf, caplog) -> None: +@pytest.mark.parametrize('return_value,side_effect,log_message', [ + (False, None, 'Found no buy signals for whitelisted currencies. Trying again...'), + (None, DependencyException, 'Unable to create trade for ETH/BTC: ') +]) +def test_enter_positions(mocker, default_conf, return_value, side_effect, + log_message, caplog) -> None: caplog.set_level(logging.DEBUG) freqtrade = get_patched_freqtradebot(mocker, default_conf) - mock_ct = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade', - MagicMock(return_value=False)) - n = freqtrade.enter_positions() - assert n == 0 - assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) - # create_trade should be called once for every pair in the whitelist. - assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) - - -def test_enter_positions_exception(mocker, default_conf, caplog) -> None: - freqtrade = get_patched_freqtradebot(mocker, default_conf) - mock_ct = mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.create_trade', - MagicMock(side_effect=DependencyException) + MagicMock( + return_value=return_value, + side_effect=side_effect + ) ) n = freqtrade.enter_positions() assert n == 0 + assert log_has(log_message, caplog) + # create_trade should be called once for every pair in the whitelist. assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) - assert log_has('Unable to create trade for ETH/BTC: ', caplog) def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -1672,8 +1657,13 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No assert log_has_re('Found open order for.*', caplog) +@pytest.mark.parametrize('initial_amount,has_rounding_fee', [ + (90.99181073 + 1e-14, True), + (8.0, False) +]) def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee, - mocker): + mocker, initial_amount, has_rounding_fee, caplog): + trades_for_order[0]['amount'] = initial_amount mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) # fetch_order should not be called!! mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError)) @@ -1694,32 +1684,8 @@ def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_ freqtrade.update_trade_state(trade, '123456', limit_buy_order) assert trade.amount != amount assert trade.amount == limit_buy_order['amount'] - - -def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_order, fee, - limit_buy_order, mocker, caplog): - trades_for_order[0]['amount'] = limit_buy_order['amount'] + 1e-14 - mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) - # fetch_order should not be called!! - mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError)) - patch_exchange(mocker) - amount = sum(x['amount'] for x in trades_for_order) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - trade = Trade( - pair='LTC/ETH', - amount=amount, - exchange='binance', - open_rate=0.245441, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_order_id='123456', - is_open=True, - open_date=arrow.utcnow().datetime, - ) - freqtrade.update_trade_state(trade, '123456', limit_buy_order) - assert trade.amount != amount - assert trade.amount == limit_buy_order['amount'] - assert log_has_re(r'Applying fee on amount for .*', caplog) + if has_rounding_fee: + assert log_has_re(r'Applying fee on amount for .*', caplog) def test_update_trade_state_exception(mocker, default_conf, From ab88217186afa23718421ad67867a9d624024878 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 19 Sep 2021 13:16:30 +0200 Subject: [PATCH 470/519] Improve /balance output to include starting balance and percentual change closes #5503 --- freqtrade/rpc/api_server/api_schemas.py | 7 +++++++ freqtrade/rpc/rpc.py | 24 ++++++++++++++++++++---- freqtrade/rpc/telegram.py | 23 +++++++++++++++-------- tests/rpc/test_rpc_apiserver.py | 14 ++++++++++---- tests/rpc/test_rpc_telegram.py | 2 ++ 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 3adbebc16..eb6082087 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -46,6 +46,13 @@ class Balances(BaseModel): value: float stake: str note: str + starting_capital: float + starting_capital_ratio: float + starting_capital_pct: float + starting_capital_fiat: float + starting_capital_fiat_ratio: float + starting_capital_fiat_pct: float + class Count(BaseModel): diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index b7b1fe603..12a444b80 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -458,6 +458,9 @@ class RPC: raise RPCException('Error getting current tickers.') self._freqtrade.wallets.update(require_update=False) + starting_capital = self._freqtrade.wallets.get_starting_balance() + starting_capital_fiat = self._fiat_converter.convert_amount( + starting_capital, stake_currency, fiat_display_currency) if self._fiat_converter else 0 for coin, balance in self._freqtrade.wallets.get_all_balances().items(): if not balance.total: @@ -493,15 +496,28 @@ class RPC: else: raise RPCException('All balances are zero.') - symbol = fiat_display_currency - value = self._fiat_converter.convert_amount(total, stake_currency, - symbol) if self._fiat_converter else 0 + value = self._fiat_converter.convert_amount( + total, stake_currency, fiat_display_currency) if self._fiat_converter else 0 + + starting_capital_ratio = 0.0 + starting_capital_fiat_ratio = 0.0 + if starting_capital: + starting_capital_ratio = (total / starting_capital) - 1 + if starting_capital_fiat: + starting_capital_fiat_ratio = (value / starting_capital_fiat) - 1 + return { 'currencies': output, 'total': total, - 'symbol': symbol, + 'symbol': fiat_display_currency, 'value': value, 'stake': stake_currency, + 'starting_capital': starting_capital, + 'starting_capital_ratio': starting_capital_ratio, + 'starting_capital_pct': round(starting_capital_ratio * 100, 2), + 'starting_capital_fiat': starting_capital_fiat, + 'starting_capital_fiat_ratio': starting_capital_fiat_ratio, + 'starting_capital_fiat_pct': round(starting_capital_fiat_ratio * 100, 2), 'note': 'Simulated balances' if self._freqtrade.config['dry_run'] else '' } diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index a988d2b60..ffb7385da 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -603,12 +603,15 @@ class Telegram(RPCHandler): output = '' if self._config['dry_run']: - output += ( - f"*Warning:* Simulated balances in Dry Mode.\n" - "This mode is still experimental!\n" - "Starting capital: " - f"`{self._config['dry_run_wallet']}` {self._config['stake_currency']}.\n" - ) + output += "*Warning:* Simulated balances in Dry Mode.\n" + + output += ("Starting capital: " + f"`{result['starting_capital']}` {self._config['stake_currency']}" + ) + output += (f" `{result['starting_capital_fiat']}` " + f"{self._config['fiat_display_currency']}.\n" + ) if result['starting_capital_fiat'] > 0 else '.\n' + total_dust_balance = 0 total_dust_currencies = 0 for curr in result['currencies']: @@ -641,9 +644,13 @@ class Telegram(RPCHandler): f"{round_coin_value(total_dust_balance, result['stake'], False)}`\n") output += ("\n*Estimated Value*:\n" - f"\t`{result['stake']}: {result['total']: .8f}`\n" + f"\t`{result['stake']}: " + f"{round_coin_value(result['total'], result['stake'], False)}`" + f" `({result['starting_capital_pct']}%)`\n" f"\t`{result['symbol']}: " - f"{round_coin_value(result['value'], result['symbol'], False)}`\n") + f"{round_coin_value(result['value'], result['symbol'], False)}`" + f" `({result['starting_capital_fiat_pct']}%)`\n" + ) self._send_msg(output, reload_able=True, callback_path="update_balance", query=update.callback_query) except RPCException as e: diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 43eb70938..7c98b2df7 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -422,20 +422,22 @@ def test_api_stopbuy(botclient): assert ftbot.config['max_open_trades'] == 0 -def test_api_balance(botclient, mocker, rpc_balance): +def test_api_balance(botclient, mocker, rpc_balance, tickers): ftbot, client = botclient ftbot.config['dry_run'] = False mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) + mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers) mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', side_effect=lambda a, b: f"{a}/{b}") ftbot.wallets.update() rc = client_get(client, f"{BASE_URI}/balance") assert_response(rc) - assert "currencies" in rc.json() - assert len(rc.json()["currencies"]) == 5 - assert rc.json()['currencies'][0] == { + response = rc.json() + assert "currencies" in response + assert len(response["currencies"]) == 5 + assert response['currencies'][0] == { 'currency': 'BTC', 'free': 12.0, 'balance': 12.0, @@ -443,6 +445,10 @@ def test_api_balance(botclient, mocker, rpc_balance): 'est_stake': 12.0, 'stake': 'BTC', } + assert 'starting_capital' in response + assert 'starting_capital_fiat' in response + assert 'starting_capital_pct' in response + assert 'starting_capital_ratio' in response def test_api_count(botclient, mocker, ticker, fee, markets): diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 2013dad7d..21f1cd000 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -576,6 +576,8 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None 'total': 100.0, 'symbol': 100.0, 'value': 1000.0, + 'starting_capital': 1000, + 'starting_capital_fiat': 1000, }) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) From ec03531771904a3a20dc31e4c64d0f13156fdcce Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 19 Sep 2021 13:29:09 +0200 Subject: [PATCH 471/519] Improve naming of variables --- freqtrade/rpc/api_server/api_schemas.py | 1 - freqtrade/rpc/rpc.py | 15 ++++++--------- freqtrade/rpc/telegram.py | 3 +-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index eb6082087..46187f571 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -54,7 +54,6 @@ class Balances(BaseModel): starting_capital_fiat_pct: float - class Count(BaseModel): current: int max: int diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 12a444b80..f6599b429 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -459,7 +459,7 @@ class RPC: self._freqtrade.wallets.update(require_update=False) starting_capital = self._freqtrade.wallets.get_starting_balance() - starting_capital_fiat = self._fiat_converter.convert_amount( + starting_cap_fiat = self._fiat_converter.convert_amount( starting_capital, stake_currency, fiat_display_currency) if self._fiat_converter else 0 for coin, balance in self._freqtrade.wallets.get_all_balances().items(): @@ -500,11 +500,8 @@ class RPC: total, stake_currency, fiat_display_currency) if self._fiat_converter else 0 starting_capital_ratio = 0.0 - starting_capital_fiat_ratio = 0.0 - if starting_capital: - starting_capital_ratio = (total / starting_capital) - 1 - if starting_capital_fiat: - starting_capital_fiat_ratio = (value / starting_capital_fiat) - 1 + starting_capital_ratio = (total / starting_capital) - 1 if starting_capital else 0.0 + starting_cap_fiat_ratio = (value / starting_cap_fiat) - 1 if starting_cap_fiat else 0.0 return { 'currencies': output, @@ -515,9 +512,9 @@ class RPC: 'starting_capital': starting_capital, 'starting_capital_ratio': starting_capital_ratio, 'starting_capital_pct': round(starting_capital_ratio * 100, 2), - 'starting_capital_fiat': starting_capital_fiat, - 'starting_capital_fiat_ratio': starting_capital_fiat_ratio, - 'starting_capital_fiat_pct': round(starting_capital_fiat_ratio * 100, 2), + 'starting_capital_fiat': starting_cap_fiat, + 'starting_capital_fiat_ratio': starting_cap_fiat_ratio, + 'starting_capital_fiat_pct': round(starting_cap_fiat_ratio * 100, 2), 'note': 'Simulated balances' if self._freqtrade.config['dry_run'] else '' } diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index ffb7385da..19c58b63d 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -649,8 +649,7 @@ class Telegram(RPCHandler): f" `({result['starting_capital_pct']}%)`\n" f"\t`{result['symbol']}: " f"{round_coin_value(result['value'], result['symbol'], False)}`" - f" `({result['starting_capital_fiat_pct']}%)`\n" - ) + f" `({result['starting_capital_fiat_pct']}%)`\n") self._send_msg(output, reload_able=True, callback_path="update_balance", query=update.callback_query) except RPCException as e: From 879bf47b32ff692e35f2509665b1ef9899eb3621 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 19 Sep 2021 19:25:36 +0200 Subject: [PATCH 472/519] Refactor telegram.py to simplify send_msg --- freqtrade/rpc/telegram.py | 51 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 19c58b63d..898446ea0 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -260,29 +260,7 @@ class Telegram(RPCHandler): return message - def send_msg(self, msg: Dict[str, Any]) -> None: - """ Send a message to telegram channel """ - - default_noti = 'on' - - msg_type = msg['type'] - noti = '' - if msg_type == RPCMessageType.SELL: - sell_noti = self._config['telegram'] \ - .get('notification_settings', {}).get(str(msg_type), {}) - # For backward compatibility sell still can be string - if isinstance(sell_noti, str): - noti = sell_noti - else: - noti = sell_noti.get(str(msg['sell_reason']), default_noti) - else: - noti = self._config['telegram'] \ - .get('notification_settings', {}).get(str(msg_type), default_noti) - - if noti == 'off': - logger.info(f"Notification '{msg_type}' not sent.") - # Notification disabled - return + def compose_message(self, msg: Dict[str, Any], msg_type: RPCMessageType) -> str: if msg_type == RPCMessageType.BUY: message = self._format_buy_msg(msg) @@ -315,6 +293,33 @@ class Telegram(RPCHandler): else: raise NotImplementedError('Unknown message type: {}'.format(msg_type)) + return message + + def send_msg(self, msg: Dict[str, Any]) -> None: + """ Send a message to telegram channel """ + + default_noti = 'on' + + msg_type = msg['type'] + noti = '' + if msg_type == RPCMessageType.SELL: + sell_noti = self._config['telegram'] \ + .get('notification_settings', {}).get(str(msg_type), {}) + # For backward compatibility sell still can be string + if isinstance(sell_noti, str): + noti = sell_noti + else: + noti = sell_noti.get(str(msg['sell_reason']), default_noti) + else: + noti = self._config['telegram'] \ + .get('notification_settings', {}).get(str(msg_type), default_noti) + + if noti == 'off': + logger.info(f"Notification '{msg_type}' not sent.") + # Notification disabled + return + + message = self.compose_message(msg, msg_type) self._send_msg(message, disable_notification=(noti == 'silent')) From 1da091dea3006b67e2400cf1390981354626f4fb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 19 Sep 2021 19:41:19 +0200 Subject: [PATCH 473/519] ProtectionManager should return the lock just created --- freqtrade/persistence/pairlock_middleware.py | 4 +++- freqtrade/plugins/protectionmanager.py | 23 ++++++++++---------- tests/plugins/test_protections.py | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/freqtrade/persistence/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index af904f693..8662fc36d 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -30,7 +30,8 @@ class PairLocks(): PairLocks.locks = [] @staticmethod - def lock_pair(pair: str, until: datetime, reason: str = None, *, now: datetime = None) -> None: + def lock_pair(pair: str, until: datetime, reason: str = None, *, + now: datetime = None) -> PairLock: """ Create PairLock from now to "until". Uses database by default, unless PairLocks.use_db is set to False, @@ -52,6 +53,7 @@ class PairLocks(): PairLock.query.session.commit() else: PairLocks.locks.append(lock) + return lock @staticmethod def get_pair_locks(pair: Optional[str], now: Optional[datetime] = None) -> List[PairLock]: diff --git a/freqtrade/plugins/protectionmanager.py b/freqtrade/plugins/protectionmanager.py index f33e5b4bc..2510d6fee 100644 --- a/freqtrade/plugins/protectionmanager.py +++ b/freqtrade/plugins/protectionmanager.py @@ -6,6 +6,7 @@ from datetime import datetime, timezone from typing import Dict, List, Optional from freqtrade.persistence import PairLocks +from freqtrade.persistence.models import PairLock from freqtrade.plugins.protections import IProtection from freqtrade.resolvers import ProtectionResolver @@ -43,30 +44,28 @@ class ProtectionManager(): """ return [{p.name: p.short_desc()} for p in self._protection_handlers] - def global_stop(self, now: Optional[datetime] = None) -> bool: + def global_stop(self, now: Optional[datetime] = None) -> Optional[PairLock]: if not now: now = datetime.now(timezone.utc) - result = False + result = None for protection_handler in self._protection_handlers: if protection_handler.has_global_stop: - result, until, reason = protection_handler.global_stop(now) + lock, until, reason = protection_handler.global_stop(now) # Early stopping - first positive result blocks further trades - if result and until: + if lock and until: if not PairLocks.is_global_lock(until): - PairLocks.lock_pair('*', until, reason, now=now) - result = True + result = PairLocks.lock_pair('*', until, reason, now=now) return result - def stop_per_pair(self, pair, now: Optional[datetime] = None) -> bool: + def stop_per_pair(self, pair, now: Optional[datetime] = None) -> Optional[PairLock]: if not now: now = datetime.now(timezone.utc) - result = False + result = None for protection_handler in self._protection_handlers: if protection_handler.has_local_stop: - result, until, reason = protection_handler.stop_per_pair(pair, now) - if result and until: + lock, until, reason = protection_handler.stop_per_pair(pair, now) + if lock and until: if not PairLocks.is_pair_locked(pair, until): - PairLocks.lock_pair(pair, until, reason, now=now) - result = True + result = PairLocks.lock_pair(pair, until, reason, now=now) return result diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index c0a9ae72a..a3cb29c9d 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -125,7 +125,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog): # Test 5m after lock-period - this should try and relock the pair, but end-time # should be the previous end-time end_time = PairLocks.get_pair_longest_lock('*').lock_end_time + timedelta(minutes=5) - assert freqtrade.protections.global_stop(end_time) + freqtrade.protections.global_stop(end_time) assert not PairLocks.is_global_lock(end_time) @@ -182,7 +182,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair min_ago_open=180, min_ago_close=30, profit_rate=0.9, )) - assert freqtrade.protections.stop_per_pair(pair) + freqtrade.protections.stop_per_pair(pair) assert freqtrade.protections.global_stop() != only_per_pair assert PairLocks.is_pair_locked(pair) assert PairLocks.is_global_lock() != only_per_pair From c91a9a92f2ccecc5aac46f25a999c0b4a97b20a7 Mon Sep 17 00:00:00 2001 From: Bernhard Millauer Date: Mon, 20 Sep 2021 14:22:24 +0200 Subject: [PATCH 474/519] Add troubleshooting information The time in wsl docker container shifts over time. Added information how to fix this issue. --- docs/docker_quickstart.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 1fa229225..f4f8c366d 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -149,6 +149,20 @@ You'll then also need to modify the `docker-compose.yml` file and uncomment the You can then run `docker-compose build` to build the docker image, and run it using the commands described above. +### Troubleshooting + +### Docker on Windows + +* Error: `"Timestamp for this request is outside of the recvWindow."` + * The market api requests require a synchronized clock but the time in the docker container shifts a bit over time into the past. + To fix this issue temporarily you need to run `wsl --shutdown` and restart docker again (a popup on windows 10 will ask you to do so). + A permanent solution is either to host the docker container on a linux host or restart the wsl from time to time with the scheduler. + ``` + taskkill /IM "Docker Desktop.exe" /F + wsl --shutdown + start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe" + ``` + ## Plotting with docker-compose Commands `freqtrade plot-profit` and `freqtrade plot-dataframe` ([Documentation](plotting.md)) are available by changing the image to `*_plot` in your docker-compose.yml file. From a0fb43c6caca7d0b0f06cf9814d43f69341339b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 19:12:59 +0200 Subject: [PATCH 475/519] Add pairlock-notification --- freqtrade/enums/rpcmessagetype.py | 1 + freqtrade/freqtradebot.py | 16 ++++++++++++++-- freqtrade/rpc/telegram.py | 5 +++++ tests/rpc/test_rpc_telegram.py | 14 ++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index 9c59f6108..889d67cf4 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -11,6 +11,7 @@ class RPCMessageType(Enum): SELL = 'sell' SELL_FILL = 'sell_fill' SELL_CANCEL = 'sell_cancel' + PROTECTION_TRIGGER = 'protection_trigger' def __repr__(self): return self.value diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1cb8988ff..451b5764a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1292,8 +1292,7 @@ class FreqtradeBot(LoggingMixin): if not trade.is_open: if not stoploss_order and not trade.open_order_id: self._notify_exit(trade, '', True) - self.protections.stop_per_pair(trade.pair) - self.protections.global_stop() + self.handle_protections(trade.pair) self.wallets.update() elif not trade.open_order_id: # Buy fill @@ -1301,6 +1300,19 @@ class FreqtradeBot(LoggingMixin): return False + def handle_protections(self, pair: str) -> None: + prot_trig = self.protections.stop_per_pair(pair) + if prot_trig: + msg = {'type': RPCMessageType.PROTECTION_TRIGGER, } + msg.update(prot_trig.to_json()) + self.rpc.send_msg(msg) + + prot_trig_glb = self.protections.global_stop() + if prot_trig_glb: + msg = {'type': RPCMessageType.PROTECTION_TRIGGER, } + msg.update(prot_trig_glb.to_json()) + self.rpc.send_msg(msg) + def apply_fee_conditional(self, trade: Trade, trade_base_currency: str, amount: float, fee_abs: float) -> float: """ diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 898446ea0..934a5182a 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -281,6 +281,11 @@ class Telegram(RPCHandler): "for {close_rate}.".format(**msg)) elif msg_type == RPCMessageType.SELL: message = self._format_sell_msg(msg) + elif msg_type == RPCMessageType.PROTECTION_TRIGGER: + message = ( + "*Protection* triggered due to {reason}. " + "{pair} will be locked until {lock_end_time}." + ).format(**msg) elif msg_type == RPCMessageType.STATUS: message = '*Status:* `{status}`'.format(**msg) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 21f1cd000..0675e4d5f 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1313,6 +1313,20 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: 'Reason: cancelled due to timeout.') +def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> None: + + telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) + time_machine.move_to("2021-09-01 05:00:00 +00:00") + lock = PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=6).datetime, 'randreason') + msg = { + 'type': RPCMessageType.PROTECTION_TRIGGER, + } + msg.update(lock.to_json()) + telegram.send_msg(msg) + assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " + "ETH/BTC will be locked until 2021-09-01 05:10:00.") + + def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: default_conf['telegram']['notification_settings']['buy_fill'] = 'on' From dd0db7ee5db04fb98b542e9541d46768b9bb2c05 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 19:23:40 +0200 Subject: [PATCH 476/519] Split protection-notification into global and per-pair --- config_examples/config_full.example.json | 4 +++- docs/telegram-usage.md | 4 +++- freqtrade/enums/rpcmessagetype.py | 1 + freqtrade/freqtradebot.py | 2 +- freqtrade/rpc/telegram.py | 6 +++++- tests/rpc/test_rpc_telegram.py | 13 +++++++++++++ 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index d0f3f0df6..c415d70b0 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -149,7 +149,9 @@ }, "sell_fill": "on", "buy_cancel": "on", - "sell_cancel": "on" + "sell_cancel": "on", + "protection_trigger": "off", + "protection_trigger_global": "on" }, "reload": true, "balance_dust_level": 0.01 diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index b020b00db..1e6fa9dae 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -93,7 +93,9 @@ Example configuration showing the different settings: "buy_cancel": "silent", "sell_cancel": "on", "buy_fill": "off", - "sell_fill": "off" + "sell_fill": "off", + "protection_trigger": "off", + "protection_trigger_global": "on" }, "reload": true, "balance_dust_level": 0.01 diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index 889d67cf4..4e3f693e5 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -12,6 +12,7 @@ class RPCMessageType(Enum): SELL_FILL = 'sell_fill' SELL_CANCEL = 'sell_cancel' PROTECTION_TRIGGER = 'protection_trigger' + PROTECTION_TRIGGER_GLOBAL = 'protection_trigger_global' def __repr__(self): return self.value diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 451b5764a..37be3173a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1309,7 +1309,7 @@ class FreqtradeBot(LoggingMixin): prot_trig_glb = self.protections.global_stop() if prot_trig_glb: - msg = {'type': RPCMessageType.PROTECTION_TRIGGER, } + msg = {'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL, } msg.update(prot_trig_glb.to_json()) self.rpc.send_msg(msg) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 934a5182a..0687f95a2 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -286,7 +286,11 @@ class Telegram(RPCHandler): "*Protection* triggered due to {reason}. " "{pair} will be locked until {lock_end_time}." ).format(**msg) - + elif msg_type == RPCMessageType.PROTECTION_TRIGGER_GLOBAL: + message = ( + "*Protection* triggered due to {reason}. " + "All pairs will be locked until {lock_end_time}." + ).format(**msg) elif msg_type == RPCMessageType.STATUS: message = '*Status:* `{status}`'.format(**msg) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 0675e4d5f..d37a74187 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1326,6 +1326,19 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " "ETH/BTC will be locked until 2021-09-01 05:10:00.") + msg_mock.reset_mock() + # Test global protection + + msg = { + 'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL, + } + lock = PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=100).datetime, 'randreason') + msg.update(lock.to_json()) + telegram.send_msg(msg) + assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " + "All pairs will be locked until 2021-09-01 06:45:00.") + + def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: From fd23ab3d647db7444d12129755ddf001e014dbcd Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 19:49:18 +0200 Subject: [PATCH 477/519] improve formatting, add tests --- freqtrade/rpc/telegram.py | 4 ++-- tests/rpc/test_rpc_telegram.py | 5 ++--- tests/test_freqtradebot.py | 23 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 0687f95a2..059ba9c41 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -284,12 +284,12 @@ class Telegram(RPCHandler): elif msg_type == RPCMessageType.PROTECTION_TRIGGER: message = ( "*Protection* triggered due to {reason}. " - "{pair} will be locked until {lock_end_time}." + "`{pair}` will be locked until `{lock_end_time}`." ).format(**msg) elif msg_type == RPCMessageType.PROTECTION_TRIGGER_GLOBAL: message = ( "*Protection* triggered due to {reason}. " - "All pairs will be locked until {lock_end_time}." + "*All pairs* will be locked until `{lock_end_time}`." ).format(**msg) elif msg_type == RPCMessageType.STATUS: message = '*Status:* `{status}`'.format(**msg) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index d37a74187..8c285a76e 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1324,7 +1324,7 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> msg.update(lock.to_json()) telegram.send_msg(msg) assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " - "ETH/BTC will be locked until 2021-09-01 05:10:00.") + "`ETH/BTC` will be locked until `2021-09-01 05:10:00`.") msg_mock.reset_mock() # Test global protection @@ -1336,8 +1336,7 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> msg.update(lock.to_json()) telegram.send_msg(msg) assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " - "All pairs will be locked until 2021-09-01 06:45:00.") - + "*All pairs* will be locked until `2021-09-01 06:45:00`.") def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index eb3c77cc7..e1df6dad9 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -526,6 +526,29 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, assert log_has_re(message, caplog) +def test_handle_protections(mocker, default_conf, fee): + default_conf['protections'] = [ + {"method": "CooldownPeriod", "stop_duration": 60}, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 4, + "only_per_pair": False + } + ] + + freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade.protections._protection_handlers[1].global_stop = MagicMock( + return_value=(True, arrow.utcnow().shift(hours=1).datetime, "asdf")) + create_mock_trades(fee) + freqtrade.handle_protections('ETC/BTC') + send_msg_mock = freqtrade.rpc.send_msg + assert send_msg_mock.call_count == 2 + assert send_msg_mock.call_args_list[0][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER + assert send_msg_mock.call_args_list[1][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER_GLOBAL + + def test_create_trade_no_signal(default_conf, fee, mocker) -> None: default_conf['dry_run'] = True From 3ce05c0d548119fd5618ba2700313a69ec10f3c5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 20:08:48 +0200 Subject: [PATCH 478/519] Add "sane" defaults to protection triggers --- docs/telegram-usage.md | 1 + freqtrade/constants.py | 9 +++++++++ tests/rpc/test_rpc_telegram.py | 2 ++ 3 files changed, 12 insertions(+) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 1e6fa9dae..b9d01a236 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -105,6 +105,7 @@ Example configuration showing the different settings: `buy` notifications are sent when the order is placed, while `buy_fill` notifications are sent when the order is filled on the exchange. `sell` notifications are sent when the order is placed, while `sell_fill` notifications are sent when the order is filled on the exchange. `*_fill` notifications are off by default and must be explicitly enabled. +`protection_trigger` notifications are sent when a protection triggers and `protection_trigger_global` notifications trigger when global protections are triggered. `balance_dust_level` will define what the `/balance` command takes as "dust" - Currencies with a balance below this will be shown. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 9ca43d459..4997108bc 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -284,6 +284,15 @@ CONF_SCHEMA = { 'enum': TELEGRAM_SETTING_OPTIONS, 'default': 'off' }, + 'protection_trigger': { + 'type': 'string', + 'enum': TELEGRAM_SETTING_OPTIONS, + 'default': 'off' + }, + 'protection_trigger_global': { + 'type': 'string', + 'enum': TELEGRAM_SETTING_OPTIONS, + }, } }, 'reload': {'type': 'boolean'}, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 8c285a76e..7dde7b803 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1315,6 +1315,8 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> None: + default_conf['telegram']['notification_settings']['protection_trigger'] = 'on' + telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) time_machine.move_to("2021-09-01 05:00:00 +00:00") lock = PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=6).datetime, 'randreason') From abddb0db66b431abf5902a8fa74dbf5f2cf16e56 Mon Sep 17 00:00:00 2001 From: Bernhard Millauer Date: Tue, 21 Sep 2021 10:13:19 +0200 Subject: [PATCH 479/519] Fix header indention Co-authored-by: Matthias --- docs/docker_quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index f4f8c366d..33b1c7ea1 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -151,7 +151,7 @@ You can then run `docker-compose build` to build the docker image, and run it us ### Troubleshooting -### Docker on Windows +#### Docker on Windows * Error: `"Timestamp for this request is outside of the recvWindow."` * The market api requests require a synchronized clock but the time in the docker container shifts a bit over time into the past. From 6fc770d97dd8d4380be3fb94e8f3dcedfa981cd7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Sep 2021 15:12:35 +0200 Subject: [PATCH 480/519] Add warning about running with docker on windows --- docs/docker_quickstart.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 33b1c7ea1..2f350d207 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -163,6 +163,10 @@ You can then run `docker-compose build` to build the docker image, and run it us start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe" ``` +!!! Warning + Due to the above, we do not recommend the usage of docker on windows for production setups, but only for experimentation, datadownload and backtesting. + Best use a linux-VPS for running freqtrade reliably. + ## Plotting with docker-compose Commands `freqtrade plot-profit` and `freqtrade plot-dataframe` ([Documentation](plotting.md)) are available by changing the image to `*_plot` in your docker-compose.yml file. From 277828bf0ebac1c370b02b4dfb6ca15628d63b1e Mon Sep 17 00:00:00 2001 From: matt ferrante Date: Tue, 21 Sep 2021 07:56:16 -0600 Subject: [PATCH 481/519] parameterize some tests --- tests/test_freqtradebot.py | 219 ++++++++++++++----------------------- 1 file changed, 84 insertions(+), 135 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 72d1f6150..d96ef71d6 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3545,8 +3545,34 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f caplog) -def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fee, mocker): - trades_for_order[0]['fee']['currency'] = 'ETH' +@pytest.mark.parametrize( + 'fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log', + [ + (None, 'ETH', 0, True, None), + (0.004, None, 0, True, None), + (0.00094518, "BNB", 0, True, None), + ( + 0.004, + "LTC", + 0.004, + False, + ( + 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' + ) + ), + (0.008, None, 0, True, None), + ] +) +def test_get_real_amount( + default_conf, trades_for_order, buy_order_fee, fee, mocker, caplog, + fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log +): + + buy_order = deepcopy(buy_order_fee) + buy_order['fee'] = {'cost': fee_cost, 'currency': fee_currency} + trades_for_order[0]['fee']['cost'] = fee_cost + trades_for_order[0]['fee']['currency'] = fee_currency mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = sum(x['amount'] for x in trades_for_order) @@ -3561,19 +3587,58 @@ def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fe ) freqtrade = get_patched_freqtradebot(mocker, default_conf) + if not use_ticker_rate: + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError) + # Amount does not change - assert freqtrade.get_real_amount(trade, buy_order_fee) == amount + assert freqtrade.get_real_amount(trade, buy_order) == amount - fee_reduction_amount + + if expected_log: + assert log_has(expected_log, caplog) -def test_get_real_amount_no_currency_in_fee(default_conf, trades_for_order, buy_order_fee, - fee, mocker): +@pytest.mark.parametrize( + 'stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log', + [ + ( + "BTC", + None, + None, + 0.001, + 0.001, + ( + 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).' + ) + ), + ( + "ETH", + 0.02, + 'BNB', + 0.0005, + 0.001518575, + ( + 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' + ) + ), + ] +) +def test_get_real_amount_multi( + default_conf, trades_for_order2, buy_order_fee, caplog, fee, mocker, markets, + stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log, +): - limit_buy_order = deepcopy(buy_order_fee) - limit_buy_order['fee'] = {'cost': 0.004, 'currency': None} - trades_for_order[0]['fee']['currency'] = None + trades_for_order = deepcopy(trades_for_order2) + if fee_cost: + trades_for_order[0]['fee']['cost'] = fee_cost + if fee_currency: + trades_for_order[0]['fee']['currency'] = fee_currency mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) - amount = sum(x['amount'] for x in trades_for_order) + amount = float(sum(x['amount'] for x in trades_for_order)) + default_conf['stake_currency'] = stake_currency + trade = Trade( pair='LTC/ETH', amount=amount, @@ -3583,124 +3648,29 @@ def test_get_real_amount_no_currency_in_fee(default_conf, trades_for_order, buy_ open_rate=0.245441, open_order_id="123456" ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - # Amount does not change - assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - - -def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, fee, mocker): - trades_for_order[0]['fee']['currency'] = 'BNB' - trades_for_order[0]['fee']['cost'] = 0.00094518 - - mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) - amount = sum(x['amount'] for x in trades_for_order) - trade = Trade( - pair='LTC/ETH', - amount=amount, - exchange='binance', - fee_open=fee.return_value, - fee_close=fee.return_value, - open_rate=0.245441, - open_order_id="123456" - ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - - # Amount does not change - assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - - -def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, caplog, fee, mocker): - mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order2) - amount = float(sum(x['amount'] for x in trades_for_order2)) - trade = Trade( - pair='LTC/ETH', - amount=amount, - exchange='binance', - fee_open=fee.return_value, - fee_close=fee.return_value, - open_rate=0.245441, - open_order_id="123456" - ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - - # Amount is reduced by "fee" - assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) - assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).', - caplog) - - assert trade.fee_open == 0.001 - assert trade.fee_close == 0.001 - assert trade.fee_open_cost is not None - assert trade.fee_open_currency is not None - assert trade.fee_close_cost is None - assert trade.fee_close_currency is None - - -def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee, - mocker, markets): - # Different fee currency on both trades - mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order3) - amount = float(sum(x['amount'] for x in trades_for_order3)) - default_conf['stake_currency'] = 'ETH' - trade = Trade( - pair='LTC/ETH', - amount=amount, - exchange='binance', - fee_open=fee.return_value, - fee_close=fee.return_value, - open_rate=0.245441, - open_order_id="123456" - ) # Fake markets entry to enable fee parsing markets['BNB/ETH'] = markets['ETH/BTC'] freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - return_value={'ask': 0.19, 'last': 0.2}) + mocker.patch( + 'freqtrade.exchange.Exchange.fetch_ticker', + return_value={'ask': 0.19, 'last': 0.2} + ) # Amount is reduced by "fee" - assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.0005) - assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).', - caplog) - # Overall fee is average of both trade's fee - assert trade.fee_open == 0.001518575 + expected_amount = amount - (amount * fee_reduction_amount) + assert freqtrade.get_real_amount(trade, buy_order_fee) == expected_amount + assert log_has(expected_log, caplog) + + assert trade.fee_open == expected_fee + assert trade.fee_close == expected_fee assert trade.fee_open_cost is not None assert trade.fee_open_currency is not None assert trade.fee_close_cost is None assert trade.fee_close_currency is None -def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee, - caplog, mocker): - limit_buy_order = deepcopy(buy_order_fee) - limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'} - - mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', - return_value=[trades_for_order]) - amount = float(sum(x['amount'] for x in trades_for_order)) - trade = Trade( - pair='LTC/ETH', - amount=amount, - exchange='binance', - fee_open=fee.return_value, - fee_close=fee.return_value, - open_rate=0.245441, - open_order_id="123456" - ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - # Ticker rate cannot be found for this to work. - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError) - - # Amount is reduced by "fee" - assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004 - assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).', - caplog) - - def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker): limit_buy_order = deepcopy(buy_order_fee) limit_buy_order['fee'] = {'cost': 0.004} @@ -3768,27 +3738,6 @@ def test_get_real_amount_wrong_amount_rounding(default_conf, trades_for_order, b abs_tol=MATH_CLOSE_PREC,) -def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee, fee, mocker): - # Remove "Currency" from fee dict - trades_for_order[0]['fee'] = {'cost': 0.008} - - mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) - amount = sum(x['amount'] for x in trades_for_order) - trade = Trade( - pair='LTC/ETH', - amount=amount, - exchange='binance', - open_rate=0.245441, - fee_open=fee.return_value, - fee_close=fee.return_value, - - open_order_id="123456" - ) - freqtrade = get_patched_freqtradebot(mocker, default_conf) - # Amount does not change - assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - - def test_get_real_amount_open_trade(default_conf, fee, mocker): amount = 12345 trade = Trade( From 707d0ef795868961fe652046f12a190047809de3 Mon Sep 17 00:00:00 2001 From: matt ferrante Date: Tue, 21 Sep 2021 12:16:10 -0600 Subject: [PATCH 482/519] remove trades_for_order3 --- tests/conftest.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5e08e7097..7354c0b2c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1685,14 +1685,6 @@ def trades_for_order2(): 'fee': {'cost': 0.004, 'currency': 'LTC'}}] -@pytest.fixture(scope="function") -def trades_for_order3(trades_for_order2): - # Different fee currencies for each trade - trades_for_order = deepcopy(trades_for_order2) - trades_for_order[0]['fee'] = {'cost': 0.02, 'currency': 'BNB'} - return trades_for_order - - @pytest.fixture def buy_order_fee(): return { From b0de4d333e86bfe23f065ab365e2ab18bb7ca41d Mon Sep 17 00:00:00 2001 From: Peter Willemsen Date: Tue, 21 Sep 2021 23:20:40 +0200 Subject: [PATCH 483/519] fixed webhook error --- freqtrade/freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1cb8988ff..95f2280ac 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1217,7 +1217,7 @@ class FreqtradeBot(LoggingMixin): 'exchange': trade.exchange.capitalize(), 'pair': trade.pair, 'gain': gain, - 'limit': profit_rate, + 'limit': profit_rate or 0, 'order_type': order_type, 'amount': trade.amount, 'open_rate': trade.open_rate, @@ -1226,7 +1226,7 @@ class FreqtradeBot(LoggingMixin): 'profit_ratio': profit_ratio, 'sell_reason': trade.sell_reason, 'open_date': trade.open_date, - 'close_date': trade.close_date, + 'close_date': trade.close_date or datetime.utcnow(), 'stake_currency': self.config['stake_currency'], 'fiat_currency': self.config.get('fiat_display_currency', None), 'reason': reason, From 553c868d7f2a8d4e7edafaccdf814cc7249e0ac9 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Tue, 21 Sep 2021 16:40:24 -0600 Subject: [PATCH 484/519] combined test_order_book_depth_of_market and test_order_book_depth_of_market_high_delta --- tests/test_freqtradebot.py | 122 +++++++++++++------------------------ 1 file changed, 42 insertions(+), 80 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index d96ef71d6..0a2f73263 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3546,24 +3546,19 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f @pytest.mark.parametrize( - 'fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log', - [ + 'fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log', [ (None, 'ETH', 0, True, None), (0.004, None, 0, True, None), - (0.00094518, "BNB", 0, True, None), - ( - 0.004, - "LTC", - 0.004, - False, - ( - 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' - ) - ), + (0.00094518, "BNB", 0, True, ( + 'Fee for Trade Trade(id=None, pair=LTC/ETH, amount=8.00000000, open_rate=0.24544100,' + ' open_since=closed) [buy]: 0.00094518 BNB - rate: None' + )), + (0.004, "LTC", 0.004, False, ( + 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' + )), (0.008, None, 0, True, None), - ] -) + ]) def test_get_real_amount( default_conf, trades_for_order, buy_order_fee, fee, mocker, caplog, fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log @@ -3590,6 +3585,7 @@ def test_get_real_amount( if not use_ticker_rate: mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError) + caplog.clear() # Amount does not change assert freqtrade.get_real_amount(trade, buy_order) == amount - fee_reduction_amount @@ -3598,32 +3594,16 @@ def test_get_real_amount( @pytest.mark.parametrize( - 'stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log', - [ - ( - "BTC", - None, - None, - 0.001, - 0.001, - ( - 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).' - ) - ), - ( - "ETH", - 0.02, - 'BNB', - 0.0005, - 0.001518575, - ( - 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' - ) - ), - ] -) + 'stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log', [ + ("BTC", None, None, 0.001, 0.001, ( + 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).' + )), + ("ETH", 0.02, 'BNB', 0.0005, 0.001518575, ( + 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' + )), + ]) def test_get_real_amount_multi( default_conf, trades_for_order2, buy_order_fee, caplog, fee, mocker, markets, stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log, @@ -3653,10 +3633,8 @@ def test_get_real_amount_multi( markets['BNB/ETH'] = markets['ETH/BTC'] freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) - mocker.patch( - 'freqtrade.exchange.Exchange.fetch_ticker', - return_value={'ask': 0.19, 'last': 0.2} - ) + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', + return_value={'ask': 0.19, 'last': 0.2}) # Amount is reduced by "fee" expected_amount = amount - (amount * fee_reduction_amount) @@ -3788,10 +3766,14 @@ def test_apply_fee_conditional(default_conf, fee, caplog, mocker, assert walletmock.call_count == 1 +@pytest.mark.parametrize("delta, is_high_delta", [ + (0.1, False), + (100, True), +]) def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_open, limit_buy_order, - fee, mocker, order_book_l2): + fee, mocker, order_book_l2, delta, is_high_delta): default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True - default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 0.1 + default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = delta patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2) @@ -3809,42 +3791,22 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order_open, freqtrade.enter_positions() trade = Trade.query.first() - assert trade is not None - assert trade.stake_amount == 0.001 - assert trade.is_open - assert trade.open_date is not None - assert trade.exchange == 'binance' + if is_high_delta: + assert trade is None + else: + assert trade is not None + assert trade.stake_amount == 0.001 + assert trade.is_open + assert trade.open_date is not None + assert trade.exchange == 'binance' - assert len(Trade.query.all()) == 1 + assert len(Trade.query.all()) == 1 - # Simulate fulfilled LIMIT_BUY order for trade - trade.update(limit_buy_order) + # Simulate fulfilled LIMIT_BUY order for trade + trade.update(limit_buy_order) - assert trade.open_rate == 0.00001099 - assert whitelist == default_conf['exchange']['pair_whitelist'] - - -def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_order, - fee, mocker, order_book_l2): - default_conf['bid_strategy']['check_depth_of_market']['enabled'] = True - # delta is 100 which is impossible to reach. hence check_depth_of_market will return false - default_conf['bid_strategy']['check_depth_of_market']['bids_to_ask_delta'] = 100 - patch_RPCManager(mocker) - patch_exchange(mocker) - mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - create_order=MagicMock(return_value={'id': limit_buy_order['id']}), - get_fee=fee, - ) - # Save state of current whitelist - freqtrade = FreqtradeBot(default_conf) - patch_get_signal(freqtrade) - freqtrade.enter_positions() - - trade = Trade.query.first() - assert trade is None + assert trade.open_rate == 0.00001099 + assert whitelist == default_conf['exchange']['pair_whitelist'] @pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [ From f768bdea503780b6c220afe36691fbbd09752689 Mon Sep 17 00:00:00 2001 From: matt ferrante Date: Wed, 22 Sep 2021 10:32:30 -0600 Subject: [PATCH 485/519] cleanup based on feedback --- tests/test_freqtradebot.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 0a2f73263..eaaadbcb1 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3594,19 +3594,13 @@ def test_get_real_amount( @pytest.mark.parametrize( - 'stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log', [ - ("BTC", None, None, 0.001, 0.001, ( - 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).' - )), - ("ETH", 0.02, 'BNB', 0.0005, 0.001518575, ( - 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' - 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' - )), + 'stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount', [ + (None, None, None, 0.001, 0.001, 7.992), + ("ETH", 0.02, 'BNB', 0.0005, 0.001518575, 7.996), ]) def test_get_real_amount_multi( default_conf, trades_for_order2, buy_order_fee, caplog, fee, mocker, markets, - stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log, + stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount, ): trades_for_order = deepcopy(trades_for_order2) @@ -3617,7 +3611,8 @@ def test_get_real_amount_multi( mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = float(sum(x['amount'] for x in trades_for_order)) - default_conf['stake_currency'] = stake_currency + if stake_currency: + default_conf['stake_currency'] = stake_currency trade = Trade( pair='LTC/ETH', @@ -3639,7 +3634,13 @@ def test_get_real_amount_multi( # Amount is reduced by "fee" expected_amount = amount - (amount * fee_reduction_amount) assert freqtrade.get_real_amount(trade, buy_order_fee) == expected_amount - assert log_has(expected_log, caplog) + assert log_has( + ( + 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + f'open_rate=0.24544100, open_since=closed) (from 8.0 to {expected_log_amount}).' + ), + caplog + ) assert trade.fee_open == expected_fee assert trade.fee_close == expected_fee From 8cfb6ddd518bc4edf910c9f07afdc1ee85d103aa Mon Sep 17 00:00:00 2001 From: matt ferrante Date: Wed, 22 Sep 2021 10:48:13 -0600 Subject: [PATCH 486/519] fix long line --- tests/test_freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index eaaadbcb1..b987d54d8 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3594,7 +3594,7 @@ def test_get_real_amount( @pytest.mark.parametrize( - 'stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount', [ + 'stake_currency,fee_cost,fee_currency,fee_reduction_amount,expected_fee,expected_log_amount', [ (None, None, None, 0.001, 0.001, 7.992), ("ETH", 0.02, 'BNB', 0.0005, 0.001518575, 7.996), ]) From 30cc69c880cf736225f6246331527ab202398588 Mon Sep 17 00:00:00 2001 From: matt ferrante Date: Wed, 22 Sep 2021 11:28:42 -0600 Subject: [PATCH 487/519] set all to eth for multi test --- tests/test_freqtradebot.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b987d54d8..8e036e80a 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3594,13 +3594,13 @@ def test_get_real_amount( @pytest.mark.parametrize( - 'stake_currency,fee_cost,fee_currency,fee_reduction_amount,expected_fee,expected_log_amount', [ - (None, None, None, 0.001, 0.001, 7.992), - ("ETH", 0.02, 'BNB', 0.0005, 0.001518575, 7.996), + 'fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount', [ + (None, None, 0.001, 0.001, 7.992), + (0.02, 'BNB', 0.0005, 0.001518575, 7.996), ]) def test_get_real_amount_multi( default_conf, trades_for_order2, buy_order_fee, caplog, fee, mocker, markets, - stake_currency, fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount, + fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount, ): trades_for_order = deepcopy(trades_for_order2) @@ -3611,8 +3611,7 @@ def test_get_real_amount_multi( mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = float(sum(x['amount'] for x in trades_for_order)) - if stake_currency: - default_conf['stake_currency'] = stake_currency + default_conf['stake_currency'] = "ETH" trade = Trade( pair='LTC/ETH', From 2bf49445b7db8a15681f8fb690d3db6162f0e8c1 Mon Sep 17 00:00:00 2001 From: matt ferrante Date: Wed, 22 Sep 2021 16:11:27 -0600 Subject: [PATCH 488/519] add parameterized names --- tests/test_freqtradebot.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 8e036e80a..9c9271810 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3547,16 +3547,21 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f @pytest.mark.parametrize( 'fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log', [ + # basic, amount does not change (None, 'ETH', 0, True, None), + # no currency in fee (0.004, None, 0, True, None), + # BNB no rate (0.00094518, "BNB", 0, True, ( 'Fee for Trade Trade(id=None, pair=LTC/ETH, amount=8.00000000, open_rate=0.24544100,' ' open_since=closed) [buy]: 0.00094518 BNB - rate: None' )), + # from order (0.004, "LTC", 0.004, False, ( 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' )), + # invalid, no currency in from fee dict (0.008, None, 0, True, None), ]) def test_get_real_amount( @@ -3586,7 +3591,6 @@ def test_get_real_amount( mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError) caplog.clear() - # Amount does not change assert freqtrade.get_real_amount(trade, buy_order) == amount - fee_reduction_amount if expected_log: @@ -3595,7 +3599,9 @@ def test_get_real_amount( @pytest.mark.parametrize( 'fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount', [ + # basic, amount is reduced by fee (None, None, 0.001, 0.001, 7.992), + # different fee currency on both trades, fee is average of both trade's fee (0.02, 'BNB', 0.0005, 0.001518575, 7.996), ]) def test_get_real_amount_multi( From d7903f012f7af5f3c19df527780456bab0ca986f Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 23 Sep 2021 07:25:11 +0200 Subject: [PATCH 489/519] Move PerformanceWarning to advanced section rewrite to use strategy parameters instead of plain range --- docs/strategy-advanced.md | 30 ++++++++++++++++++++++++++++++ docs/strategy-customization.md | 27 --------------------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 4409af6ea..d7d0dd04a 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -695,3 +695,33 @@ The variable 'content', will contain the strategy file in a BASE64 encoded form. ``` Please ensure that 'NameOfStrategy' is identical to the strategy name! + +## Performance warning + +When executing a strategy, one can sometimes be greeted by the following in the logs + +> PerformanceWarning: DataFrame is highly fragmented. + +This is a warning from [`pandas`](https://github.com/pandas-dev/pandas) and as the warning continues to say: +use `pd.concat(axis=1)`. +This can have slight performance implications, which are usually only visible during hyperopt (when optimizing an indicator). + +For example: + +```python +for val in self.buy_ema_short.range: + dataframe[f'ema_short_{val}'] = ta.EMA(dataframe, timeperiod=val) +``` + +should be rewritten to + +```python +frames = [dataframe] +for val in self.buy_ema_short.range: + frames.append({ + f'ema_short_{val}': ta.EMA(dataframe, timeperiod=val) + }) + +# Append columns to existing dataframe +merged_frame = pd.concat(frames, axis=1) +``` diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 194517b2b..6d6eff2aa 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -793,33 +793,6 @@ The following lists some common patterns which should be avoided to prevent frus - don't use `dataframe['volume'].mean()`. This uses the full DataFrame for backtesting, including data from the future. Use `dataframe['volume'].rolling().mean()` instead - don't use `.resample('1h')`. This uses the left border of the interval, so moves data from an hour to the start of the hour. Use `.resample('1h', label='right')` instead. -### Performance warning - -When executing a strategy, one can sometimes be greeted by the following in the logs - -> PerformanceWarning: DataFrame is highly fragmented. - -This is a warning from [`pandas`](https://github.com/pandas-dev/pandas) and as the warning continues to say: - use `pd.concat(axis=1)`. For example - -```python -for i in range(100): - dataframe[i] = ta.indicator(dataframe, param=i) -``` - -should be rewritten to - -```python -frames = [dataframe] -for i in range(100): - frames.append({ - str(i): ta.indicator(dataframe, param=i) - }) - -# Append columns to existing dataframe -merged_frame = pd.concat(frames, axis=1) -``` - ## Further strategy ideas To get additional Ideas for strategies, head over to our [strategy repository](https://github.com/freqtrade/freqtrade-strategies). Feel free to use them as they are - but results will depend on the current market situation, pairs used etc. - therefore please backtest the strategy for your exchange/desired pairs first, evaluate carefully, use at your own risk. From 692e91a26dfa2ec1c746923d481421cc87a15b8c Mon Sep 17 00:00:00 2001 From: Peter Willemsen Date: Thu, 23 Sep 2021 10:28:15 +0200 Subject: [PATCH 490/519] changed close date from datetime.utcnow() to datetime.now(timezone.utc) --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 95f2280ac..9cfd219a8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1226,7 +1226,7 @@ class FreqtradeBot(LoggingMixin): 'profit_ratio': profit_ratio, 'sell_reason': trade.sell_reason, 'open_date': trade.open_date, - 'close_date': trade.close_date or datetime.utcnow(), + 'close_date': trade.close_date or datetime.now(timezone.utc), 'stake_currency': self.config['stake_currency'], 'fiat_currency': self.config.get('fiat_display_currency', None), 'reason': reason, From 422d5601890f9749ab7f69a903d2de3f68ea9257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:05:55 +0000 Subject: [PATCH 491/519] Bump types-requests from 2.25.6 to 2.25.8 Bumps [types-requests](https://github.com/python/typeshed) from 2.25.6 to 2.25.8. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 4859e1cc6..d8d8ce916 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -23,5 +23,5 @@ nbconvert==6.1.0 # mypy types types-cachetools==4.2.0 types-filelock==0.1.5 -types-requests==2.25.6 +types-requests==2.25.8 types-tabulate==0.8.2 From e85dc6326302e83da897b06941ca725f6d5f242c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:06:01 +0000 Subject: [PATCH 492/519] Bump mkdocs-material from 7.2.6 to 7.3.0 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.2.6 to 7.3.0. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/docs/changelog.md) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.2.6...7.3.0) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 9927740c2..9b7c12a43 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.2 -mkdocs-material==7.2.6 +mkdocs-material==7.3.0 mdx_truly_sane_lists==1.2 pymdown-extensions==8.2 From 766ef90b56cf190b8beeba1a249d9b53792fbba2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:06:16 +0000 Subject: [PATCH 493/519] Bump sqlalchemy from 1.4.23 to 1.4.25 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.23 to 1.4.25. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/master/CHANGES) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index aa729dd9f..74ce391ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ ccxt==1.56.30 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.8 aiohttp==3.7.4.post0 -SQLAlchemy==1.4.23 +SQLAlchemy==1.4.25 python-telegram-bot==13.7 arrow==1.1.1 cachetools==4.2.2 From 90d5af9a35211a51756603cfb3ffcb3cf67edc17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:06:20 +0000 Subject: [PATCH 494/519] Bump urllib3 from 1.26.6 to 1.26.7 Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.6 to 1.26.7. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/1.26.7/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.26.6...1.26.7) --- updated-dependencies: - dependency-name: urllib3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index aa729dd9f..2f6a4a104 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ python-telegram-bot==13.7 arrow==1.1.1 cachetools==4.2.2 requests==2.26.0 -urllib3==1.26.6 +urllib3==1.26.7 wrapt==1.12.1 jsonschema==3.2.0 TA-Lib==0.4.21 From 0353f070f9714655e1ccb06448ffa03dbe3f5fb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 05:06:24 +0000 Subject: [PATCH 495/519] Bump progressbar2 from 3.53.2 to 3.53.3 Bumps [progressbar2](https://github.com/WoLpH/python-progressbar) from 3.53.2 to 3.53.3. - [Release notes](https://github.com/WoLpH/python-progressbar/releases) - [Changelog](https://github.com/WoLpH/python-progressbar/blob/develop/CHANGES.rst) - [Commits](https://github.com/WoLpH/python-progressbar/compare/v3.53.2...v3.53.3) --- updated-dependencies: - dependency-name: progressbar2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 7dc55a9fc..9feec80f1 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -8,4 +8,4 @@ scikit-optimize==0.8.1 filelock==3.0.12 joblib==1.0.1 psutil==5.8.0 -progressbar2==3.53.2 +progressbar2==3.53.3 From 954c468191b3c130c62d7bedfde1ff138f427908 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 24 Sep 2021 07:12:38 +0200 Subject: [PATCH 496/519] Add pandas-ta to requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index aa729dd9f..f0da0cdb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ numpy==1.21.2 pandas==1.3.3 +pandas-ta==0.3.14b ccxt==1.56.30 # Pin cryptography for now due to rust build errors with piwheels From 72a1e27fc6cc0454dfb3035f4957820dd6c6423d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Sep 2021 07:31:20 +0000 Subject: [PATCH 497/519] Bump ccxt from 1.56.30 to 1.56.86 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.56.30 to 1.56.86. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.56.30...1.56.86) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3b004dfaa..ede0c74e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.21.2 pandas==1.3.3 pandas-ta==0.3.14b -ccxt==1.56.30 +ccxt==1.56.86 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.8 aiohttp==3.7.4.post0 From b59906b117f300aefd828fa02c8b5c5658388df1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 24 Sep 2021 19:24:33 +0200 Subject: [PATCH 498/519] Update minimum for tradable_balance_ratio to 0.0 --- freqtrade/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 4997108bc..fca319a0f 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -110,7 +110,7 @@ CONF_SCHEMA = { }, 'tradable_balance_ratio': { 'type': 'number', - 'minimum': 0.1, + 'minimum': 0.0, 'maximum': 1, 'default': 0.99 }, From 4c268847d42043d53fc59751ffd1717125d9e0bd Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 24 Sep 2021 19:32:22 +0200 Subject: [PATCH 499/519] Add pandas-ta to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 727c40c7c..cf381bdd3 100644 --- a/setup.py +++ b/setup.py @@ -54,6 +54,7 @@ setup( 'wrapt', 'jsonschema', 'TA-Lib', + 'pandas-ta', 'technical', 'tabulate', 'pycoingecko', From f4f204d849eb65eed74a1ac09e1ad52e5a641ee9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 24 Sep 2021 20:09:24 +0200 Subject: [PATCH 500/519] Update test to use cost dict --- tests/test_freqtradebot.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 9c9271810..b233a6267 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3546,33 +3546,32 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f @pytest.mark.parametrize( - 'fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log', [ + 'fee_par,fee_reduction_amount,use_ticker_rate,expected_log', [ # basic, amount does not change - (None, 'ETH', 0, True, None), + ({'cost': 0.008, 'currency': 'ETH'}, 0, False, None), # no currency in fee - (0.004, None, 0, True, None), + ({'cost': 0.004, 'currency': None}, 0, True, None), # BNB no rate - (0.00094518, "BNB", 0, True, ( + ({'cost': 0.00094518, 'currency': 'BNB'}, 0, True, ( 'Fee for Trade Trade(id=None, pair=LTC/ETH, amount=8.00000000, open_rate=0.24544100,' ' open_since=closed) [buy]: 0.00094518 BNB - rate: None' )), # from order - (0.004, "LTC", 0.004, False, ( + ({'cost': 0.004, 'currency': 'LTC'}, 0.004, False, ( 'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).' )), # invalid, no currency in from fee dict - (0.008, None, 0, True, None), + ({'cost': 0.008, 'currency': None}, 0, True, None), ]) def test_get_real_amount( default_conf, trades_for_order, buy_order_fee, fee, mocker, caplog, - fee_cost, fee_currency, fee_reduction_amount, use_ticker_rate, expected_log + fee_par, fee_reduction_amount, use_ticker_rate, expected_log ): buy_order = deepcopy(buy_order_fee) - buy_order['fee'] = {'cost': fee_cost, 'currency': fee_currency} - trades_for_order[0]['fee']['cost'] = fee_cost - trades_for_order[0]['fee']['currency'] = fee_currency + buy_order['fee'] = fee_par + trades_for_order[0]['fee'] = fee_par mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) amount = sum(x['amount'] for x in trades_for_order) From 6319c104fe1c51e0343c08def9f4511cc537b377 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 26 Sep 2021 15:07:48 +0200 Subject: [PATCH 501/519] Fix unreliable backtest-result when using webserver mode --- freqtrade/data/dataprovider.py | 2 + freqtrade/optimize/backtesting.py | 50 ++++++++++++------------ freqtrade/rpc/api_server/api_backtest.py | 21 +++++----- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index cdee0f078..b197c159f 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -149,6 +149,8 @@ class DataProvider: Clear pair dataframe cache. """ self.__cached_pairs = {} + self.__cached_pairs_backtesting = {} + self.__slice_index = 0 # Exchange functions diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 79c861ee8..f406f89d7 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -85,18 +85,7 @@ class Backtesting: "configuration or as cli argument `--timeframe 5m`") self.timeframe = str(self.config.get('timeframe')) self.timeframe_min = timeframe_to_minutes(self.timeframe) - # Load detail timeframe if specified - self.timeframe_detail = str(self.config.get('timeframe_detail', '')) - if self.timeframe_detail: - self.timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail) - if self.timeframe_min <= self.timeframe_detail_min: - raise OperationalException( - "Detail timeframe must be smaller than strategy timeframe.") - - else: - self.timeframe_detail_min = 0 - self.detail_data: Dict[str, DataFrame] = {} - + self.init_backtest_detail() self.pairlists = PairListManager(self.exchange, self.config) if 'VolumePairList' in self.pairlists.name_list: raise OperationalException("VolumePairList not allowed for backtesting.") @@ -119,14 +108,6 @@ class Backtesting: else: self.fee = self.exchange.get_fee(symbol=self.pairlists.whitelist[0]) - Trade.use_db = False - Trade.reset_trades() - PairLocks.timeframe = self.config['timeframe'] - PairLocks.use_db = False - PairLocks.reset_locks() - - self.wallets = Wallets(self.config, self.exchange, log=False) - self.timerange = TimeRange.parse_timerange( None if self.config.get('timerange') is None else str(self.config.get('timerange'))) @@ -135,9 +116,7 @@ class Backtesting: # Add maximum startup candle count to configuration for informative pairs support self.config['startup_candle_count'] = self.required_startup self.exchange.validate_required_startup_candles(self.required_startup, self.timeframe) - - self.progress = BTProgress() - self.abort = False + self.init_backtest() def __del__(self): self.cleanup() @@ -147,6 +126,28 @@ class Backtesting: PairLocks.use_db = True Trade.use_db = True + def init_backtest_detail(self): + # Load detail timeframe if specified + self.timeframe_detail = str(self.config.get('timeframe_detail', '')) + if self.timeframe_detail: + self.timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail) + if self.timeframe_min <= self.timeframe_detail_min: + raise OperationalException( + "Detail timeframe must be smaller than strategy timeframe.") + + else: + self.timeframe_detail_min = 0 + self.detail_data: Dict[str, DataFrame] = {} + + def init_backtest(self): + + self.prepare_backtest(False) + + self.wallets = Wallets(self.config, self.exchange, log=False) + + self.progress = BTProgress() + self.abort = False + def _set_strategy(self, strategy: IStrategy): """ Load strategy into backtesting @@ -226,7 +227,8 @@ class Backtesting: Trade.reset_trades() self.rejected_trades = 0 self.dataprovider.clear_cache() - self._load_protections(self.strategy) + if enable_protections: + self._load_protections(self.strategy) def check_abort(self): """ diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 4623c187e..32278686c 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -47,33 +47,34 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac not ApiServer._bt or lastconfig.get('timeframe') != strat.timeframe or lastconfig.get('timeframe_detail') != btconfig.get('timeframe_detail') - or lastconfig.get('dry_run_wallet') != btconfig.get('dry_run_wallet', 0) or lastconfig.get('timerange') != btconfig['timerange'] ): from freqtrade.optimize.backtesting import Backtesting ApiServer._bt = Backtesting(btconfig) if ApiServer._bt.timeframe_detail: ApiServer._bt.load_bt_data_detail() - + else: + ApiServer._bt.config = btconfig + ApiServer._bt.init_backtest() # Only reload data if timeframe changed. if ( not ApiServer._bt_data or not ApiServer._bt_timerange - or lastconfig.get('stake_amount') != btconfig.get('stake_amount') - or lastconfig.get('enable_protections') != btconfig.get('enable_protections') - or lastconfig.get('protections') != btconfig.get('protections', []) or lastconfig.get('timeframe') != strat.timeframe ): - lastconfig['timerange'] = btconfig['timerange'] - lastconfig['protections'] = btconfig.get('protections', []) - lastconfig['enable_protections'] = btconfig.get('enable_protections') - lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') - lastconfig['timeframe'] = strat.timeframe ApiServer._bt_data, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() + lastconfig['timerange'] = btconfig['timerange'] + lastconfig['timeframe'] = strat.timeframe + + lastconfig['protections'] = btconfig.get('protections', []) + lastconfig['enable_protections'] = btconfig.get('enable_protections') + lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') + ApiServer._bt.abort = False min_date, max_date = ApiServer._bt.backtest_one_strategy( strat, ApiServer._bt_data, ApiServer._bt_timerange) + ApiServer._bt.results = generate_backtest_stats( ApiServer._bt_data, ApiServer._bt.all_results, min_date=min_date, max_date=max_date) From 08b1f04ed5f9b7ce69827fb44acfc4c154aecc2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 03:01:18 +0000 Subject: [PATCH 502/519] Bump types-requests from 2.25.8 to 2.25.9 Bumps [types-requests](https://github.com/python/typeshed) from 2.25.8 to 2.25.9. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index d8d8ce916..1d61369eb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -23,5 +23,5 @@ nbconvert==6.1.0 # mypy types types-cachetools==4.2.0 types-filelock==0.1.5 -types-requests==2.25.8 +types-requests==2.25.9 types-tabulate==0.8.2 From 905950230329688b81606c9fb78c3e5a631d8cbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 03:01:27 +0000 Subject: [PATCH 503/519] Bump ccxt from 1.56.86 to 1.57.3 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.56.86 to 1.57.3. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.56.86...1.57.3) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d1d10dd1d..feeb4d942 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.21.2 pandas==1.3.3 pandas-ta==0.3.14b -ccxt==1.56.86 +ccxt==1.57.3 # Pin cryptography for now due to rust build errors with piwheels cryptography==3.4.8 aiohttp==3.7.4.post0 From 78096c9eff08cc2aea47d44e7ae20751c0b3420a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 04:32:26 +0000 Subject: [PATCH 504/519] Bump nbconvert from 6.1.0 to 6.2.0 Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/jupyter/nbconvert/releases) - [Commits](https://github.com/jupyter/nbconvert/compare/6.1.0...6.2.0) --- updated-dependencies: - dependency-name: nbconvert dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 1d61369eb..2f03255a0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -18,7 +18,7 @@ isort==5.9.3 time-machine==2.4.0 # Convert jupyter notebooks to markdown documents -nbconvert==6.1.0 +nbconvert==6.2.0 # mypy types types-cachetools==4.2.0 From 5b7a1f864257c62924760be45f2dc9c651c665a0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 27 Sep 2021 07:12:40 +0200 Subject: [PATCH 505/519] Validate config also in webserver mode --- freqtrade/rpc/api_server/api_backtest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 32278686c..7ce9f487f 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -4,6 +4,7 @@ from copy import deepcopy from fastapi import APIRouter, BackgroundTasks, Depends +from freqtrade.configuration.config_validation import validate_config_consistency from freqtrade.enums import BacktestState from freqtrade.exceptions import DependencyException from freqtrade.rpc.api_server.api_schemas import BacktestRequest, BacktestResponse @@ -42,6 +43,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac # Reload strategy lastconfig = ApiServer._bt_last_config strat = StrategyResolver.load_strategy(btconfig) + validate_config_consistency(btconfig) if ( not ApiServer._bt From 3fbf716f85b96a8d53f99523cd50ab703c034256 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 27 Sep 2021 17:51:01 +0200 Subject: [PATCH 506/519] Fix "sticking" timerange in webserver mode --- freqtrade/rpc/api_server/api_backtest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 7ce9f487f..edbc39772 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -63,12 +63,12 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac not ApiServer._bt_data or not ApiServer._bt_timerange or lastconfig.get('timeframe') != strat.timeframe + or lastconfig.get('timerange') != btconfig['timerange'] ): ApiServer._bt_data, ApiServer._bt_timerange = ApiServer._bt.load_bt_data() - lastconfig['timerange'] = btconfig['timerange'] - lastconfig['timeframe'] = strat.timeframe - + lastconfig['timerange'] = btconfig['timerange'] + lastconfig['timeframe'] = strat.timeframe lastconfig['protections'] = btconfig.get('protections', []) lastconfig['enable_protections'] = btconfig.get('enable_protections') lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') From 5726886b0620874be0d1c6c8f8b4737912e42786 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 27 Sep 2021 20:52:19 +0200 Subject: [PATCH 507/519] Reduce backtest-noise from "pandas slice" warning --- freqtrade/optimize/backtesting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index f406f89d7..8328d61d3 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -385,12 +385,12 @@ class Backtesting: detail_data = detail_data.loc[ (detail_data['date'] >= sell_candle_time) & (detail_data['date'] < sell_candle_end) - ] + ].copy() if len(detail_data) == 0: # Fall back to "regular" data if no detail data was found for this candle return self._get_sell_trade_entry_for_candle(trade, sell_row) - detail_data['buy'] = sell_row[BUY_IDX] - detail_data['sell'] = sell_row[SELL_IDX] + detail_data.loc[:, 'buy'] = sell_row[BUY_IDX] + detail_data.loc[:, 'sell'] = sell_row[SELL_IDX] headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high'] for det_row in detail_data[headers].values.tolist(): res = self._get_sell_trade_entry_for_candle(trade, det_row) From e025576d8cac9cb0227734efc18d4c978d01cc6f Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 29 Sep 2021 10:15:05 +0300 Subject: [PATCH 508/519] Introduce markets_static fixture serving an immutable list of markets. Adapt pairlist/markets tests to use this new fixture. This allows freely modifying markets in get_markets() without a need of updating pairlist/markets tests. --- tests/commands/test_commands.py | 9 ++++----- tests/conftest.py | 21 ++++++++++++++++++--- tests/exchange/test_exchange.py | 4 ++-- tests/plugins/test_pairlist.py | 4 ++-- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 135510b38..b236f6a10 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -208,11 +208,10 @@ def test_list_timeframes(mocker, capsys): assert re.search(r"^1d$", captured.out, re.MULTILINE) -def test_list_markets(mocker, markets, capsys): +def test_list_markets(mocker, markets_static, capsys): api_mock = MagicMock() - api_mock.markets = markets - patch_exchange(mocker, api_mock=api_mock, id='bittrex') + patch_exchange(mocker, api_mock=api_mock, id='bittrex', mock_markets=markets_static) # Test with no --config args = [ @@ -237,7 +236,7 @@ def test_list_markets(mocker, markets, capsys): "TKN/BTC, XLTCUSDT, XRP/BTC.\n" in captured.out) - patch_exchange(mocker, api_mock=api_mock, id="binance") + patch_exchange(mocker, api_mock=api_mock, id="binance", mock_markets=markets_static) # Test with --exchange args = [ "list-markets", @@ -250,7 +249,7 @@ def test_list_markets(mocker, markets, capsys): assert re.match("\nExchange Binance has 10 active markets:\n", captured.out) - patch_exchange(mocker, api_mock=api_mock, id="bittrex") + patch_exchange(mocker, api_mock=api_mock, id="bittrex", mock_markets=markets_static) # Test with --all: all markets args = [ "list-markets", "--all", diff --git a/tests/conftest.py b/tests/conftest.py index 7354c0b2c..c908c0cb0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -90,8 +90,10 @@ def patch_exchange(mocker, api_mock=None, id='binance', mock_markets=True) -> No mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title())) mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2)) if mock_markets: + if isinstance(mock_markets, bool): + mock_markets = get_markets() mocker.patch('freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=get_markets())) + PropertyMock(return_value=mock_markets)) if api_mock: mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) @@ -376,6 +378,8 @@ def markets(): def get_markets(): + # See get_markets_static() for immutable markets and do not modify them unless absolutely + # necessary! return { 'ETH/BTC': { 'id': 'ethbtc', @@ -675,11 +679,22 @@ def get_markets(): @pytest.fixture -def shitcoinmarkets(markets): +def markets_static(): + # These markets are used in some tests that would need adaptation should anything change in + # market list. Do not modify this list without a good reason! Do not modify market parameters + # of listed pairs in get_markets() without a good reason either! + static_markets = ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', + 'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC'] + all_markets = get_markets() + return {m: all_markets[m] for m in static_markets} + + +@pytest.fixture +def shitcoinmarkets(markets_static): """ Fixture with shitcoin markets - used to test filters in pairlists """ - shitmarkets = deepcopy(markets) + shitmarkets = deepcopy(markets_static) shitmarkets.update({ 'HOT/BTC': { 'id': 'HOTBTC', diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 97bc33429..79b4a3ff5 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2735,7 +2735,7 @@ def test_get_valid_pair_combination(default_conf, mocker, markets): (['LTC'], ['NONEXISTENT'], False, False, []), ]) -def test_get_markets(default_conf, mocker, markets, +def test_get_markets(default_conf, mocker, markets_static, base_currencies, quote_currencies, pairs_only, active_only, expected_keys): mocker.patch.multiple('freqtrade.exchange.Exchange', @@ -2743,7 +2743,7 @@ def test_get_markets(default_conf, mocker, markets, _load_async_markets=MagicMock(), validate_pairs=MagicMock(), validate_timeframes=MagicMock(), - markets=PropertyMock(return_value=markets)) + markets=PropertyMock(return_value=markets_static)) ex = Exchange(default_conf) pairs = ex.get_markets(base_currencies, quote_currencies, pairs_only, active_only) assert sorted(pairs.keys()) == sorted(expected_keys) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 1ce8d172c..cf918e2a0 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -131,9 +131,9 @@ def test_load_pairlist_noexist(mocker, markets, default_conf): default_conf, {}, 1) -def test_load_pairlist_verify_multi(mocker, markets, default_conf): +def test_load_pairlist_verify_multi(mocker, markets_static, default_conf): freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets_static)) plm = PairListManager(freqtrade.exchange, default_conf) # Call different versions one after the other, should always consider what was passed in # and have no side-effects (therefore the same check multiple times) From 656526c007dfa8fd80036966d601e8b873d1ccd5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 16:50:05 +0200 Subject: [PATCH 509/519] Add trades-to-ohlcv command to simplify adding new timeframes --- freqtrade/commands/__init__.py | 4 +-- freqtrade/commands/arguments.py | 14 +++++++++- freqtrade/commands/data_commands.py | 41 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index a6f14cff7..858c99acd 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -8,8 +8,8 @@ Note: Be careful with file-scoped imports in these subfiles. """ from freqtrade.commands.arguments import Arguments from freqtrade.commands.build_config_commands import start_new_config -from freqtrade.commands.data_commands import (start_convert_data, start_download_data, - start_list_data) +from freqtrade.commands.data_commands import (start_convert_data, start_convert_trades, + start_download_data, start_list_data) from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui, start_new_strategy) from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index d424f3ce7..48dc48cf1 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -58,6 +58,8 @@ ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"] ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"] +ARGS_CONVERT_TRADES = ["pairs", "timeframes", "dataformat_ohlcv", "dataformat_trades"] + ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "timerange", @@ -169,7 +171,8 @@ class Arguments: self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') self._build_args(optionlist=['version'], parser=self.parser) - from freqtrade.commands import (start_backtesting, start_convert_data, start_create_userdir, + from freqtrade.commands import (start_backtesting, start_convert_data, start_convert_trades, + start_create_userdir, start_download_data, start_edge, start_hyperopt, start_hyperopt_list, start_hyperopt_show, start_install_ui, start_list_data, start_list_exchanges, start_list_markets, @@ -236,6 +239,15 @@ class Arguments: convert_trade_data_cmd.set_defaults(func=partial(start_convert_data, ohlcv=False)) self._build_args(optionlist=ARGS_CONVERT_DATA, parser=convert_trade_data_cmd) + # Add trades-to-ohlcv subcommand + convert_trade_data_cmd = subparsers.add_parser( + 'trades-to-ohlcv', + help='Convert trade data to OHLCV data.', + parents=[_common_parser], + ) + convert_trade_data_cmd.set_defaults(func=start_convert_trades) + self._build_args(optionlist=ARGS_CONVERT_TRADES, parser=convert_trade_data_cmd) + # Add list-data subcommand list_data_cmd = subparsers.add_parser( 'list-data', diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index 141e85f14..7ef1ae5c7 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -89,6 +89,47 @@ def start_download_data(args: Dict[str, Any]) -> None: f"on exchange {exchange.name}.") +def start_convert_trades(args: Dict[str, Any]) -> None: + + config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE) + + timerange = TimeRange() + if 'days' in config: + time_since = (datetime.now() - timedelta(days=config['days'])).strftime("%Y%m%d") + timerange = TimeRange.parse_timerange(f'{time_since}-') + + if 'timerange' in config: + timerange = timerange.parse_timerange(config['timerange']) + + # Remove stake-currency to skip checks which are not relevant for datadownload + config['stake_currency'] = '' + + if 'pairs' not in config: + raise OperationalException( + "Downloading data requires a list of pairs. " + "Please check the documentation on how to configure this.") + + # Init exchange + exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False) + # Manual validations of relevant settings + if not config['exchange'].get('skip_pair_validation', False): + exchange.validate_pairs(config['pairs']) + expanded_pairs = expand_pairlist(config['pairs'], list(exchange.markets)) + + logger.info(f"About to Convert pairs: {expanded_pairs}, " + f"intervals: {config['timeframes']} to {config['datadir']}") + + for timeframe in config['timeframes']: + exchange.validate_timeframes(timeframe) + # Convert downloaded trade data to different timeframes + convert_trades_to_ohlcv( + pairs=expanded_pairs, timeframes=config['timeframes'], + datadir=config['datadir'], timerange=timerange, erase=bool(config.get('erase')), + data_format_ohlcv=config['dataformat_ohlcv'], + data_format_trades=config['dataformat_trades'], + ) + + def start_convert_data(args: Dict[str, Any], ohlcv: bool = True) -> None: """ Convert data from one format to another From fc511aac4486dc99568e88edd01635453d0c1a59 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 19:21:54 +0200 Subject: [PATCH 510/519] don't use %default when no default is defined --- freqtrade/commands/cli_options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index e3c7fe464..d350a9426 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -381,12 +381,12 @@ AVAILABLE_CLI_OPTIONS = { ), "dataformat_ohlcv": Arg( '--data-format-ohlcv', - help='Storage format for downloaded candle (OHLCV) data. (default: `%(default)s`).', + help='Storage format for downloaded candle (OHLCV) data. (default: `json`).', choices=constants.AVAILABLE_DATAHANDLERS, ), "dataformat_trades": Arg( '--data-format-trades', - help='Storage format for downloaded trades data. (default: `%(default)s`).', + help='Storage format for downloaded trades data. (default: `jsongz`).', choices=constants.AVAILABLE_DATAHANDLERS, ), "exchange": Arg( From 248c61bb26399d3049f7ac2f116d6cf0e49d8665 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 19:39:29 +0200 Subject: [PATCH 511/519] Add test for trades-to-ohlcv --- freqtrade/commands/arguments.py | 4 ++-- freqtrade/commands/data_commands.py | 6 ------ tests/commands/test_commands.py | 28 ++++++++++++++++++++++------ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 48dc48cf1..2fadf047e 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -58,7 +58,7 @@ ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"] ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"] -ARGS_CONVERT_TRADES = ["pairs", "timeframes", "dataformat_ohlcv", "dataformat_trades"] +ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades"] ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"] @@ -93,7 +93,7 @@ ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperop NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-pairs", "list-strategies", "list-data", "hyperopt-list", "hyperopt-show", - "plot-dataframe", "plot-profit", "show-trades"] + "plot-dataframe", "plot-profit", "show-trades", "trades-to-ohlcv"] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"] diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index 7ef1ae5c7..ee05e6c69 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -94,12 +94,6 @@ def start_convert_trades(args: Dict[str, Any]) -> None: config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE) timerange = TimeRange() - if 'days' in config: - time_since = (datetime.now() - timedelta(days=config['days'])).strftime("%Y%m%d") - timerange = TimeRange.parse_timerange(f'{time_since}-') - - if 'timerange' in config: - timerange = timerange.parse_timerange(config['timerange']) # Remove stake-currency to skip checks which are not relevant for datadownload config['stake_currency'] = '' diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index b236f6a10..8889617ba 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -8,12 +8,12 @@ from zipfile import ZipFile import arrow import pytest -from freqtrade.commands import (start_convert_data, start_create_userdir, start_download_data, - start_hyperopt_list, start_hyperopt_show, start_install_ui, - start_list_data, start_list_exchanges, start_list_markets, - start_list_strategies, start_list_timeframes, start_new_strategy, - start_show_trades, start_test_pairlist, start_trading, - start_webserver) +from freqtrade.commands import (start_convert_data, start_convert_trades, start_create_userdir, + start_download_data, start_hyperopt_list, start_hyperopt_show, + start_install_ui, start_list_data, start_list_exchanges, + start_list_markets, start_list_strategies, start_list_timeframes, + start_new_strategy, start_show_trades, start_test_pairlist, + start_trading, start_webserver) from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, get_ui_download_url, read_ui_version) from freqtrade.configuration import setup_utils_configuration @@ -759,6 +759,22 @@ def test_download_data_trades(mocker, caplog): assert convert_mock.call_count == 1 +def test_start_convert_trades(mocker, caplog): + convert_mock = mocker.patch('freqtrade.commands.data_commands.convert_trades_to_ohlcv', + MagicMock(return_value=[])) + patch_exchange(mocker) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) + ) + args = [ + "trades-to-ohlcv", + "--exchange", "kraken", + "--pairs", "ETH/BTC", "XRP/BTC", + ] + start_convert_trades(get_args(args)) + assert convert_mock.call_count == 1 + + def test_start_list_strategies(mocker, caplog, capsys): args = [ From 178db516bf79dbaa37ca2ef9b779d77ed8850f75 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Sep 2021 19:48:56 +0200 Subject: [PATCH 512/519] Add documentation for trade-to-ohlcv --- docs/data-download.md | 55 +++++++++++++++++++++++++++++++++ freqtrade/commands/arguments.py | 15 +++++---- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/docs/data-download.md b/docs/data-download.md index 0ca86b0d3..5f605c404 100644 --- a/docs/data-download.md +++ b/docs/data-download.md @@ -204,6 +204,61 @@ It'll also remove original jsongz data files (`--erase` parameter). freqtrade convert-trade-data --format-from jsongz --format-to json --datadir ~/.freqtrade/data/kraken --erase ``` +### Sub-command trades to ohlcv + +When you need to use `--dl-trades` (kraken only) to download data, conversion of trades data to ohlcv data is the last step. +This command will allow you to repeat this last step for additional timeframes without re-downloading the data. + +``` +usage: freqtrade trades-to-ohlcv [-h] [-v] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] + [-p PAIRS [PAIRS ...]] + [-t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...]] + [--exchange EXCHANGE] + [--data-format-ohlcv {json,jsongz,hdf5}] + [--data-format-trades {json,jsongz,hdf5}] + +optional arguments: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + -t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...], --timeframes {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...] + Specify which tickers to download. Space-separated + list. Default: `1m 5m`. + --exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no + config is provided. + --data-format-ohlcv {json,jsongz,hdf5} + Storage format for downloaded candle (OHLCV) data. + (default: `json`). + --data-format-trades {json,jsongz,hdf5} + Storage format for downloaded trades data. (default: + `jsongz`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` + +#### Example trade-to-ohlcv conversion + +``` bash +freqtrade trades-to-ohlcv --exchange kraken -t 5m 1h 1d --pairs BTC/EUR ETH/EUR +``` + ### Sub-command list-data You can get a list of downloaded data using the `list-data` sub-command. diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 2fadf047e..9643705a5 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -172,14 +172,13 @@ class Arguments: self._build_args(optionlist=['version'], parser=self.parser) from freqtrade.commands import (start_backtesting, start_convert_data, start_convert_trades, - start_create_userdir, - start_download_data, start_edge, start_hyperopt, - start_hyperopt_list, start_hyperopt_show, start_install_ui, - start_list_data, start_list_exchanges, start_list_markets, - start_list_strategies, start_list_timeframes, - start_new_config, start_new_strategy, start_plot_dataframe, - start_plot_profit, start_show_trades, start_test_pairlist, - start_trading, start_webserver) + start_create_userdir, start_download_data, start_edge, + start_hyperopt, start_hyperopt_list, start_hyperopt_show, + start_install_ui, start_list_data, start_list_exchanges, + start_list_markets, start_list_strategies, + start_list_timeframes, start_new_config, start_new_strategy, + start_plot_dataframe, start_plot_profit, start_show_trades, + start_test_pairlist, start_trading, start_webserver) subparsers = self.parser.add_subparsers(dest='command', # Use custom message when no subhandler is added From bd27993e797c8d83d54ac76b278a9779e9c4eee5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 30 Sep 2021 06:42:42 +0200 Subject: [PATCH 513/519] Add documentation segment about indicator libraries --- docs/strategy-customization.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 110365208..0bfc0a2f6 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -122,6 +122,16 @@ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame Look into the [user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_strategy.py). Then uncomment indicators you need. +#### Indicator libraries + +Out of the box, freqtrade installs the following technical libraries: + +* [ta-lib](http://mrjbq7.github.io/ta-lib/) +* [pandas-ta](https://twopirllc.github.io/pandas-ta/) +* [technical](https://github.com/freqtrade/technical/) + +Additional technical libraries can be installed as necessary, or custom indicators may be written / invented by the strategy author. + ### Strategy startup period Most indicators have an instable startup period, in which they are either not available, or the calculation is incorrect. This can lead to inconsistencies, since Freqtrade does not know how long this instable period should be. From 5f23af580248a86a56d60e050bd5d0a7f5424236 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 30 Sep 2021 07:24:16 +0200 Subject: [PATCH 514/519] Rename update_open_trades to clarify it's only called at startup --- freqtrade/freqtradebot.py | 4 ++-- tests/test_freqtradebot.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3a9b21b7c..bf4742fdc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -139,7 +139,7 @@ class FreqtradeBot(LoggingMixin): # Only update open orders on startup # This will update the database after the initial migration - self.update_open_orders() + self.startup_update_open_orders() def process(self) -> None: """ @@ -237,7 +237,7 @@ class FreqtradeBot(LoggingMixin): open_trades = len(Trade.get_open_trades()) return max(0, self.config['max_open_trades'] - open_trades) - def update_open_orders(self): + def startup_update_open_orders(self): """ Updates open orders based on order list kept in the database. Mainly updates the state of orders - but may also close trades diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 760a9dee7..d312bdb11 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4033,16 +4033,16 @@ def test_check_for_open_trades(mocker, default_conf, fee): @pytest.mark.usefixtures("init_persistence") -def test_update_open_orders(mocker, default_conf, fee, caplog): +def test_startup_update_open_orders(mocker, default_conf, fee, caplog): freqtrade = get_patched_freqtradebot(mocker, default_conf) create_mock_trades(fee) - freqtrade.update_open_orders() + freqtrade.startup_update_open_orders() assert not log_has_re(r"Error updating Order .*", caplog) caplog.clear() freqtrade.config['dry_run'] = False - freqtrade.update_open_orders() + freqtrade.startup_update_open_orders() assert log_has_re(r"Error updating Order .*", caplog) caplog.clear() @@ -4053,7 +4053,7 @@ def test_update_open_orders(mocker, default_conf, fee, caplog): 'status': 'closed', }) mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=matching_buy_order) - freqtrade.update_open_orders() + freqtrade.startup_update_open_orders() # Only stoploss and sell orders are kept open assert len(Order.get_open_orders()) == 2 From 15df5fd9c5c8533b14809147570e2a66d79241fe Mon Sep 17 00:00:00 2001 From: Robert Davey Date: Fri, 1 Oct 2021 13:49:16 +0100 Subject: [PATCH 515/519] Fix pair_candles to point to correct API call pair_candles pointed to available_pairs RPC call instead of pair_candles --- scripts/rest_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rest_client.py b/scripts/rest_client.py index ece0a253e..713b398c3 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -312,7 +312,7 @@ class FtRestClient(): :param limit: Limit result to the last n candles. :return: json object """ - return self._get("available_pairs", params={ + return self._get("pair_candles", params={ "pair": pair, "timeframe": timeframe, "limit": limit, From f69cb39a170a4a223de4f154c902c69903188a36 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Oct 2021 19:26:51 +0200 Subject: [PATCH 516/519] Fix missing comma in kucoin template closes #5646 --- freqtrade/templates/subtemplates/exchange_kucoin.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/templates/subtemplates/exchange_kucoin.j2 b/freqtrade/templates/subtemplates/exchange_kucoin.j2 index f9dfff663..9882c51c7 100644 --- a/freqtrade/templates/subtemplates/exchange_kucoin.j2 +++ b/freqtrade/templates/subtemplates/exchange_kucoin.j2 @@ -4,7 +4,7 @@ "secret": "{{ exchange_secret }}", "password": "{{ exchange_key_password }}", "ccxt_config": { - "enableRateLimit": true + "enableRateLimit": true, "rateLimit": 200 }, "ccxt_async_config": { From 5fdeca812d28891a915507362ffcc9f36ccb4cb0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 14:30:24 +0200 Subject: [PATCH 517/519] Combine most hyperopt-loss tests to one --- tests/optimize/conftest.py | 13 ++-- tests/optimize/test_hyperoptloss.py | 92 +++++------------------------ 2 files changed, 23 insertions(+), 82 deletions(-) diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 5c5171c3a..690934b07 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -39,16 +39,17 @@ def hyperopt(hyperopt_conf, mocker): def hyperopt_results(): return pd.DataFrame( { - 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], - 'profit_ratio': [-0.1, 0.2, 0.3], - 'profit_abs': [-0.2, 0.4, 0.6], - 'trade_duration': [10, 30, 10], - 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI], + 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC', 'ETH/BTC'], + 'profit_ratio': [-0.1, 0.2, -0.1, 0.3], + 'profit_abs': [-0.2, 0.4, -0.2, 0.6], + 'trade_duration': [10, 30, 10, 10], + 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.STOP_LOSS, SellType.ROI], 'close_date': [ datetime(2019, 1, 1, 9, 26, 3, 478039), datetime(2019, 2, 1, 9, 26, 3, 478039), - datetime(2019, 3, 1, 9, 26, 3, 478039) + datetime(2019, 3, 1, 9, 26, 3, 478039), + datetime(2019, 4, 1, 9, 26, 3, 478039), ] } ) diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index 0082bcc34..923e3fc32 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -35,6 +35,7 @@ def test_hyperoptlossresolver_wrongname(default_conf) -> None: def test_loss_calculation_prefer_correct_trade_count(hyperopt_conf, hyperopt_results) -> None: + hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)) @@ -50,6 +51,7 @@ def test_loss_calculation_prefer_shorter_trades(hyperopt_conf, hyperopt_results) resultsb = hyperopt_results.copy() resultsb.loc[1, 'trade_duration'] = 20 + hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) longer = hl.hyperopt_loss_function(hyperopt_results, 100, datetime(2019, 1, 1), datetime(2019, 5, 1)) @@ -64,6 +66,7 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> results_under = hyperopt_results.copy() results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 + hyperopt_conf.update({'hyperopt_loss': "ShortTradeDurHyperOptLoss"}) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)) @@ -75,91 +78,28 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> assert under > correct -def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SharpeHyperOptLoss'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SharpeHyperOptLossDaily'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SortinoHyperOptLoss'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None: - results_over = hyperopt_results.copy() - results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 - results_under = hyperopt_results.copy() - results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - - default_conf.update({'hyperopt_loss': 'SortinoHyperOptLossDaily'}) - hl = HyperOptLossResolver.load_hyperoptloss(default_conf) - correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), - datetime(2019, 1, 1), datetime(2019, 5, 1)) - assert over < correct - assert under > correct - - -def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: +@pytest.mark.parametrize('lossfunction', [ + "OnlyProfitHyperOptLoss", + "SortinoHyperOptLoss", + "SortinoHyperOptLossDaily", + "SharpeHyperOptLoss", + "SharpeHyperOptLossDaily", +]) +def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None: results_over = hyperopt_results.copy() results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2 + results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 results_under = hyperopt_results.copy() results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2 + results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 - default_conf.update({'hyperopt_loss': 'OnlyProfitHyperOptLoss'}) + default_conf.update({'hyperopt_loss': lossfunction}) hl = HyperOptLossResolver.load_hyperoptloss(default_conf) correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), datetime(2019, 1, 1), datetime(2019, 5, 1)) - over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), + over = hl.hyperopt_loss_function(results_over, len(results_over), datetime(2019, 1, 1), datetime(2019, 5, 1)) - under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), + under = hl.hyperopt_loss_function(results_under, len(results_under), datetime(2019, 1, 1), datetime(2019, 5, 1)) assert over < correct assert under > correct From 77388eb423f8867aa63860ace494dd85505dd416 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 15:23:48 +0200 Subject: [PATCH 518/519] Improve generate_test_data to make it easier to use --- tests/optimize/conftest.py | 20 ++++++++++++++------ tests/strategy/test_strategy_helpers.py | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 690934b07..13d90b36f 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -39,17 +39,25 @@ def hyperopt(hyperopt_conf, mocker): def hyperopt_results(): return pd.DataFrame( { - 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC', 'ETH/BTC'], + 'pair': ['ETH/USDT', 'ETH/USDT', 'ETH/USDT', 'ETH/USDT'], 'profit_ratio': [-0.1, 0.2, -0.1, 0.3], 'profit_abs': [-0.2, 0.4, -0.2, 0.6], 'trade_duration': [10, 30, 10, 10], + 'amount': [0.1, 0.1, 0.1, 0.1], 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.STOP_LOSS, SellType.ROI], + 'open_date': + [ + datetime(2019, 1, 1, 9, 15, 0), + datetime(2019, 2, 1, 8, 55, 0), + datetime(2019, 3, 1, 9, 15, 0), + datetime(2019, 4, 1, 9, 15, 0), + ], 'close_date': [ - datetime(2019, 1, 1, 9, 26, 3, 478039), - datetime(2019, 2, 1, 9, 26, 3, 478039), - datetime(2019, 3, 1, 9, 26, 3, 478039), - datetime(2019, 4, 1, 9, 26, 3, 478039), - ] + datetime(2019, 1, 1, 9, 25, 0), + datetime(2019, 2, 1, 9, 25, 0), + datetime(2019, 3, 1, 9, 25, 0), + datetime(2019, 4, 1, 9, 25, 0), + ], } ) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index a01b55050..cb7cf97a1 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -9,13 +9,13 @@ from freqtrade.strategy import (merge_informative_pair, stoploss_from_absolute, timeframe_to_minutes) -def generate_test_data(timeframe: str, size: int): +def generate_test_data(timeframe: str, size: int, start: str = '2020-07-05'): np.random.seed(42) tf_mins = timeframe_to_minutes(timeframe) base = np.random.normal(20, 2, size=size) - date = pd.period_range('2020-07-05', periods=size, freq=f'{tf_mins}min').to_timestamp() + date = pd.date_range(start, periods=size, freq=f'{tf_mins}min', tz='UTC') df = pd.DataFrame({ 'date': date, 'open': base, From 3b5cc5f01584329b3d9b0bd47cc71dbf97e8e3d1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 2 Oct 2021 15:36:51 +0200 Subject: [PATCH 519/519] Improve dates used for hyperopt tests --- tests/optimize/conftest.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 13d90b36f..8c7fa3ac9 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -48,16 +48,16 @@ def hyperopt_results(): 'open_date': [ datetime(2019, 1, 1, 9, 15, 0), - datetime(2019, 2, 1, 8, 55, 0), - datetime(2019, 3, 1, 9, 15, 0), - datetime(2019, 4, 1, 9, 15, 0), + datetime(2019, 1, 2, 8, 55, 0), + datetime(2019, 1, 3, 9, 15, 0), + datetime(2019, 1, 4, 9, 15, 0), ], 'close_date': [ datetime(2019, 1, 1, 9, 25, 0), - datetime(2019, 2, 1, 9, 25, 0), - datetime(2019, 3, 1, 9, 25, 0), - datetime(2019, 4, 1, 9, 25, 0), + datetime(2019, 1, 2, 9, 25, 0), + datetime(2019, 1, 3, 9, 25, 0), + datetime(2019, 1, 4, 9, 25, 0), ], } )

hc~WVh1f;A!$@v=2Nt0O^$ZK}`+q)_Mdy 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[$O3FVlrlE%^vnGCr>0uPVrw}K;gn)Yk>1ZUmx zoh+NGOra#V!C3>cv+G&PONm;01&_23t~cWPfHtRxBEIGvpuSfik4Hp%2>7Vf1MT6T z;_R(OzF5(NITUT^${6)1g@lpq96=_~3TO{KVOOJ2Zyg2g;onmk1S}*PC;{ib0tjl9 zo;UzM=Cq%u9ROkl0eQNdy&5dzK+XA{cD_R@`660etH(!#gFA%No) zm~6nHoi92lX6G{O2jp!^=z5dM?mq#$Ud8!h6B4_5&y#ZQE)7YaZ8C<%`URE3gin%H z{G#3m(3by0<4%j&uHNGZ>nF*+DX|kLM2du0-#0WJA%J3#eP{IPibiap24yqCTMI7nnV9sXmNXeK;?{ zalonifaZpb{^!Z221|ScX|jNOO&hn^FJqSYEz%Mnh_l2$AO-KUwa3RNn}J>ah|Ow` zmn?g{UA{=!DvPV3as-vb2OQBU{e6G?qiqITC&b?QH5<6fpJ|ydM(6hj&1G zM6T_Loezt%%+qKpyKb|yk$Zt)xCU`3k&%00ULoWrvUI~5ni~qw$eA6^v&{-mbVH(>^;pHsGF7-2u~y9ic{PWElSS|IvQc{apDZjA;V`QY39%qm&H6D2v1l?+Ntm~ zvXjToM^>VW{Q_Q-Wyp(MmadV8LCe zF`z;3?v+90=JOm3dA5W+O|bB?8D9K*i`~)RF9RpbY}7XXo5sP5+&&XyY7c0S!Gu5} zWSuAV?kbx!<&lnxsES@8(xmtJrZTJ*Spef$G{04d<`h7SgheStb3D`MO40l-C7LhD zWGR*fZ^l?B(fry>8A>uOla*zwl)mkfOkQQ{?Gj;Ud?sTO5JMs4);qN;GZxjUp#J6K ztKsdFaoKuxr#FXA?sW&pf=I5)IJF)TC0oa`SpO>)>t7H*BR^XZ7frIjD%L*{C)RJW z$LBqw{ur`9vCfB#3omnWc!p-3$gc5|>jXuBU z_ms%`c8W5!ky?8@!}UNp_!h(Uu+YDYfqA4}@Y2flSrVa#rF|wkDc3V>Dr1u%f&?D> zW&lYyHSBxvflCM=*+YK%jMuD~@o?sjXuZ$q3Qt`NN0k(DB!{hWB(cmhr->suW9AtL z4sZgy{ityyBxk`Hbt_szC?h?u*it2)Nf}3Sy2LXMus~1{rph~W%~|K2NiNQkcIGru zBxg)JgMm`M`nx&TF6BcqF8to{aW-!+d+f_uE-|(uogq@r?2sUKK_n$=^StOw;jzC* zdF&TU5jvH}etf7yqR0MT<*~mj=CM!E0O4Dres8}PMnHw%FiuhHnuC?CL1VAj_R%`d z`^*jWd-jPfrkb277g8Vyr^fj*CvBa zEwQ)}Jm5E_ucO`k=!oiA1>Gz=+OLqBi>;}3>@*3Z#3X3$*HmBM?Khp(Z=g_PtiFJe zVecDfE7(jD`JYg>#+m)@zNE=RXvB^&h#jCT!6LLdk~$){oOfYJ5Hsw}E56Zc(tX%eIzcssr_^k>{t9@jZU=(9}B^3|{th z4?%%ohAzVgZg9T&@nmlKDjkxh>|~7Oxh4z2^XFU?=GZ|+Th{?3O$T)rg}bSzGsT>{ zxf=6ia#1JuQcf)?5!0#___JI%@5V6(WXPacgg7CJb8AoXiu zy0iCgbZkLv5u}ecOP}K=&w5e3{1D#iCWTH`6iPn#K3gmhg*3*5y+4OUA9KzqFg}1O z6IJAKc5m0EUwSahCJt2N01~{G>X7x2hB(g*j}Le`$FsI^x)hSX zSuvXKr&U0_#7!sQASF&8QQ~x;6sL*3bw4Zhs7g1%+%d#y;+75OpxNB`1Pk&Dlu*6= zT_sdky&Ds%6%)?+q;8Yg)+NUAl=-9xLv^0aC-scVC-sZ-Y$k>@Oo}8+L0qmmPs+{9 z&*N5-5�sT{6K`#3m)0GMQo3#*#)58CDAq#^sa3D4CK5#w8|c4A`vsq+(F8>*JfP zyai;6RR#v9d{XSA_a>yRRrKw(-}}cn8=-m=A~yqjv&oQ7Y2EV^fW@#mFoblfuW&lm z&E%9p=~O3OmD)sax}@S_3Qf zkCsnWNe^2-aynHq{MYDfirMc-kxSK@mP-FTpXz|BN6sO)JhWl+BdA^ZF zih43ditD?;(v%Z`_(Tw zkLqq%u*6Vwit1m}9{vMW{|t*#3?-(mp_9qmTwP7{2jrp5DLphxg!uzH#mxy*dgeje z`9MagW=2 zALA5V>accc9|zx?U9x;UuY*mxZZT$eH!ke?l!O+WBqs>Hxq!z2rl8Av@8FcHG*hae z#GI?eRwZWbi0ggJMBaSWl2TPMgcko=px|@NPYx^&Riv+%VRmNj zsvLbjBBADUpxvb^&~9>?RM4k=NTA(OrLR9=PmHnqWXc#jv%*wSc8jf1b}mbl-Ma{| zD+y1lSK)MKBwft@g9(2L0d=}0_E89^8x=B|D4_0uEh(Vx2R0c{cYRVoUHbPd@pSDf zj!j4p9hP{y&DLjI;^OJz9&Po7XIbOv;vRNzJl*EFc)AjAlMJP6OKgG}N>`6ix+V^# z)9WS=s-mvUr^-NN-jMkh zKy5;RocltsY|8vE5R90Z4Zl*xphrH$)&CoI853|P2^yOtPu>o3 zO3Xy4%U@9M^BzK4AkkG{9Vq*HW;h8ft_I}k979< zAcd|A0(*$Yi5$>D4@DAuE@{Mg;$y$*QcZroHJudM2X8^*4IyzKVTrrMIATZ=7_S8G zBeYTJ6<=zqJ*2HK9xsVt`goSKDfctXeV-gJCGCmhS@O-bOTjvwZm^RSk;6OLENK_9 zr2VS+8TsT_hZJe}>Ns!?^iiO)RL3Ok!UR{{_QZBV5x_WCqWf2rbd4h;UKg=F?*t@F zo^G6ks7(^Rank2`0ai*+EgSB3cwRuA9!^I;g)?Zw7PrZ!vOBL)PP!`Zwh+0=p?~6I z_eh6+z`-3N?_G%PuLej^zlRXv-Yls+L{7Z<1Q<-1(m=$4gf90y+VJ*4gA!)cBVV4g z#OPqW;N3xEANfzVKu$05Y^8nBv!}$sySWCx1^`e!HW@xsvlg^J=rSq39(mCmPbt~*qOgd-qV#&o;iJg@;ibrThMyweiRp`I&L6hL<~IFs2vA&1jC+_KsYT zvgep9uPucuuie_LcxUqNfbkABtok&>*g?d&_+t>`$nT5C&P;_Cq(4KEe;#)ilx$trG$Tuh7@BBdV+xJ>$s-+K~$$}Kdw`2RNzN^f!*y{{Y5=oTM< z30IQmjqYh9`a5ioF*Qb$@qIX-;#nj;Sh*t*Z6N=fxaLRegW=4rdU(dVJ9$5p`bt(d z-pq73E2f5H9M?Hx2IACk12MIwQ&D12y_rM;2S2j+4N+p3bw4~XoG7jbha1K9&`6-T z9%79p{BJPhVG{Bg15j)xJjr4v1C?#qPQsbZK}1?LdtOq`H@e07W|svnlgG}foNsh1 zT&DB%ie3{g^Poa-eM}}OWUfI7Q50Dk{4&S$XXSr6%iJwXDtADQ;5PM<+P$?_lsfI< zU8D~r-Pq>pC8WZ6iR`S0S#v|O$@dk6+%!CMO+8Hb{S_}t{r?Gl~lsnz@{IF>ZJ}lo< zLxl|TJvS%f29m4B=hy|HzUMR2RU@IU0(MHLd*sDFzXej7cp9Se*_%EAYVU=q_QcHf zVw?)4YX*P#vIOEks;v##d2CpjxR1wX{$4@~{9N@lMy=E(7O4SZubXo!WxjYd=~g+mep&8i80yXoYJVBpp0T`n$RBnTUl|)%O~;GRT8q z_~yJcKx1^6_oXs+bAi#jn;*P5AQ(~Q<`SZ}UE*yr{dW(jxe6UtZ<};&4(4ti%nhjq zlR`ZZnlV!=6CaD#@4GLC2#BOKMRZP-+9^-LYF-a;--XrSQc24&&a*!ck<8>~Q_Z_Z zlHO#K99)xI6~kuF2aKd1H*W*?PZ<)>l~8gs|sPf6aP znY;Bia^3GEC3v6DXhbw4rD-sF0&wp%daMZ|C=dMHc}NgZudQz#3+-8pwgK+)vC#VW zwKYE*E0uCjj*W%ZliF@Q9POcrA06MM!c5g(R0j=4n#iE?dO^huGVt z$K<9s=N5}_siKl0qFI8Hm8_T4pcHC{6j8|^rglgt=h>Ls3*OJ+@^UQYaX2!CCgZtT zUQVpMoLG4YelK^D(L}6@5^^%J#ZT@o4o!~pe+kC{#2)*1^X+l|LJkR-YLz__F#*Le$KB7K zD&l^tw${rmcw$B8QAO#pqCcLoA~N2fE*Dn*7WS`3di*g{S4>g_C`#5{g!fiJEeZ4Pn_D;D5HL9`* z?7?wm{4-FA11g-kRcseSAy23Jdlqi`nI-K#*S9u=eye4 z|5Ph>oLaFH&wF8E^XTc^D`uT)xx))EO;{QDy6Ollg&&Q<2W||$W&wsOQ1c$K^Xf=Peag>SJ4EER( zHiM@BrERB;r5Dq7-zDHT3u$MeN#N~;yd8OOY3cp4A|`WjcCEY-i!&#Faq`+6kF|+> zaK3|bNnTWNs7-tc$`Q{fO88?ie+?^7}o{l8A7 zibOvLNr`}3lc3?58BT~iZ#JFepSL*4n=G^ypDW{=f5wEs#k{8ex&(MX^6$vG^|ssf z+k{*d{!LyzI<@+ZA|TNk89(^ZI9s5gIZ#*oC|OwkfJDPV<3zxyeFC_9$eLDN=rcYD z)cFUD1ED&X;_ z9b&j#;?i?GRN2ZV`wf(4IocYiY?Ti6k5K~VXlva?v%(V^DPRw&l?k?&f_FLfGMG8O z_$wu7M$py_QHJ|%QB?m8TG`ZA1VTll1iF#l$`Ey;lN)aoofOY!(5(R>kBSfC-a-Ld zc_ZJ;#J!4M+|#wPHo95SLIxy>d{^-`PHIgBsIn@ysR|qE6AIuJEQJfkn}r|VprQ|NP~gK$DgE$L z$`VT`$inZ1qJyASBWBD{AZu)2}*I{USXL7+EkwLch)M*Ri5#L}wBTc+I( z=_ET$^2qH7bj>a))s0HHxkXO1Y#y5GH#9{aEkEDTi;{$)l!&?k*yfI;IcNg(LW%Sk&`ui)s zRmA^P@jr$9Pd>d7p})UF+^m*eOR^Wd&c(eV@tc57vrEdvWhY-&iOUFGQs$~qohwvV z>JyXji=O+kq^x&m!gZ z6$9625p+%A4O!$>wJaT5@UcK;lh}T9g5k9;>4=n{PJZ+Cgdl(>_g#r=#rTqc-}vTHi8Cqq%$32|BxsN_9)%S634My1FG_(h#RpRyTi zwNE;4@Lgqg+Ilc6-K!H{f}hI6+*Pca)-)cXLTqs-#UQsmYIlLQE^D zO)Ccm>DPY+@4{|Qm# z96^*?_C5IPx(DT8vARI2g5#ioRkRJL(f|uC`CUKLr|8)4dS=P-T{B4h1ds0q`k^`? zeHgQEg*Le4IXRNg@mJ5uuaXCI13goPz%Mf61s{(mFndbc<#@JpL7m*$4!>nc^Q}X= zfnHJNb)mek`(mTIK#l4VjOr~uIjRe2RMA;~7grSFioWbuqZ*N;iXlBh!?}ota}f>a z0&_SQnZucH9nMHUuX@q12vsI%v-~Y6D8!DSYvo|nd$AV(F_YlHfHPkHMnCZW2-2zv zLF@=$22FQTWI{Il{Iqf;CvMl}Tli|LSW?G%dGM}N)<+z^Xe=sirDZ8L90(T`?TmdT zzeG_GLNp9~T!M4!EAz8acEE+)}x}51Tb~p4+I!t_!c2NlTgph>5P5 z7<9BL;|jXmE|));pZS=ajBCVwhSvw&ZSOcLEWm{bp4zXKAI7d7G+xv}$>ksTNv1AG zAIs(xx(Kg*X^uTXLi4Qvc zd~ms{U68Z1IT`6Q8T!6KTZ=4WqKAm1aW_WRixR~c;K&o>im|y_^Z=k`NJ&suk2_zb ze$FESp?=92{FC9uTSB(XC>9s;-b!)lL&R;!`<0vrCIt-tfF5n|L8XsLrwx`kX~gx0 zOYCUjt+{e8FU{7oY{ZCM@ph45PovG4*otC={o+*kl#aC6C$7c}->U;7Z&%2uc5Aac zMLqd`<5)uo^Q>;g z#k!ffrGVf*hYSb?M@&Kvcx8MI_L<@1tKS7khAYcnM6}heJ@nh5XcQyav1A;L&rPD) zJx|J;JGHeNhvAMz4;-XvNt4=AR4umQXlo*{>ysdnO zqkol*6HVHNLw3_dv`bs_0)7Eq>sdQni5HON=ofaj94#Z;$P@GltV1$yR0w&azRZ&J z#5b>tht^8BGqhHbN+A?rXe~FOwGhteqRc~-ZD@9Q2C%hUq42bvSu&-Rczt#_Pu!ZC z6Li!E>eBOqMgxGgP)SmDsbF|Qjf@Bb#MQ<0*G@?S2rLWs#;X-r79g&s46j zwkBjOd^TNv#;J3!R`^)-l~a9X0%KLLr}2uV^Z}YcF0@;2=%b5}iK<9tqS_tuGzCl) zEHfEZS_}n?IgNSo_*VxB$AIi-oRw-38QpwHOT}{=XIsnvl4)7XkD=O=yi0&=@XK07 zhO>cC<-vrAh6OQjHXt7Q;LQGayNqZ6L7XX~VXa-dus>+$h=#Vy2BU&XD4s!A@eEZm zo?)5koi8h;&>0oLpoj0`2pa8~ZAJ@18^|FXK@B|!Ubh3aB14L9Mtfx z3Tk*4K@CAOr~%;weL(~@{F)34gkIJitZWE*y2P72HC7y#Jif$0!L{mjLTy>+2 za5$UzhHcD_B*r&1b9_UajBnVR9N(}8;Z+paQbmU8SkMaxIwTFH8R(#f7MF1_Xs{*A zsT}?1F__8o%oGQoh;+X`6#erj!1EjRk)7g)?f|K|Ir9NsCF)aZ-dI#(beS3u@y;S5 zBUq!oJ7u~rY%&yl9it+ud#r9&{&hlVAd77M;swZVNq`69$1R}T-_egBR|uhXRZ=Vxhr)U2{eTl06)Bs82& z#)_;;V-qt5!KSVGruZ58x@lq-e4Y2$ihR{SiSZ$-i5Z$P2qxNsY#!Esl`mW}ND)I~ zJz_5}6dO4Z4uh3DLzRQ!8x{t=`?WB_Kue4lgW=hIC7y1vVeJ$ranLv%^mJR}d0fb# z=du&G6e6F@v6MDp!q_F(iU;LTu5MW}2Dj#nbuB#~N!pfNLb~R$c-FY&0=%J>XRs4JX`V! z>Xdl$JW6|xRnMKeqq8@yU{~FoKiHy8foQAFel4QE^oGw{k+(?Ocy*NZvmgV2F%m!{ zj*LPIj*3wXd!alkw~DGdcZ^)HZyPioKnJ z2jc_@;_{}(xDR4@COKrgIbM}ZwB#D11xdPVK4>jq?@m-C5 zZA$GD&t$d6tFFv0S6!9Hi>!+MIKHd_&m=w}0(xTNnJkENG-lozbtmCW* zKgqYtTnL+NCV1jgrh#14D_%uwLNdInDp)x{p;baS%zl8R%f+ns@#VQGQG(c0D+H0i zQ{daF5m6@CRhs!EM3j9g(#+qa;;jgFOeLDv=DgT@V~RZUpRMtKo9bRkX{W=)2qQ2--cM1 zIQO>AY3UNDAf`<7cy4yd8S9esbj6uz=B-X9#UCxre9S3C6d8*vU=pCIX2g1$+8Y%j zN^5+!`TYv4-t-8;91f>Lh-e(KJ5S1nAxX|F)V%5hO5DzrIQ8r)@qd60-#HsP>@(@` zc+U|F9Zqmr;bp7{9yFw>(Ph{<$r@obEL8Z;@Kd3Tu)23h;zt(JG;g$-)A~`Uka2mm zIeUA3F<2fOlUDE5sbG0*aRZm&)g@`LpL37NW>q5i)Hp=VdoqCvdvVmw5zoUn+HpUe zDQK+;$UHTXbZ$B1)L83}a2JL3slr`~(VtvMJt$%8>CcAk1uzcL8BS=^!bSpm~5W zX*!Hdu(QEgjPVC*jI;Zb(Nc@!f?Zsh+4miNSJ4=`(i8f+%2}=RSa1Q&rJjl zzz(eQV@BQ2lrLJsU|M@Z?6@6>ttqtcarcK1FwQ&b2weMJH=P z1=XygU{tyLIHd&j0H4C6rIqHc4a2jJ@Uk^GQkPNF#SlW=uQ(xNVm{E;{$D$1lsHOp zAfm#(+QX00=xpT4_>yRdc(wyxO_VybUUR=En$j=I0IWuzwsyW<4!1R9i5O)v&D1^= zBV%%B%5#fSOGK%?%P|0%YCtO*dLKt8&ZiqXXZUOs9d7HbftnZ{ZUw3;4dh)sklT9} z%7Hu&N`5Nk5|lEAUKZa@q;EA@HG*3RnUx!)25mbH+73+X-&JxGXn5KvZAbaUNgmQZ z!vs)Zlm%`Mt|$RR?M}FLt6S7XzDzwJa~OM^VLY=&6t0cio>Ua^~~Y zPw~oOowI&a91w+e_ii^GmAWYBhIs2wG^Kx#%`eytrxgei&=d(7v@Y{;xe&#mUI8KC z$7rg~O9W_`dpQ!5;$HIhi6x@Vd95cb$@~-j6mA2zG(k%g7bEro@lEfYsJuLm<`Mj{ zkGzY_FJz!cYkyo!4GWky6L1uljEqW`Wk(su7oLG=@f(+u0fj__io@cp;z%515Cm@~ zPLfNl9eT~$q1R=HYUQC}>Co$z4!wpBeUPd{uUk6w`WZX)TC78_#X9s_a)%a^m(yW+ z=z;^`1K!#Z@z25+RY%}h*)2P=T6QFdJ0e=uZMH>x(Yp}cxIJkv>XzN$_fOLcC<9(J zV{bWRhirK-CUrtIA7X!f5O_JwK*cdAK~|HJ-@H#z5QMhzN*U6!h+VW;N_0O!&RL6P z#?C6TfMEA0lZJ#peN4JpyEiEKQx-d6(jZaP{jAL1r-W7(VgoX}D4dHR=lU4r)I>Mk z*QX2^Tl->;TS`#gy4B2gKAIApLmMfRoKFlGS-<$+IGf|D9LQjef|*F< zZ{o)4liZ-cBq_Q#Ww3go-X_%xk7Teo50#5Jd@(36AsbsD+i$YPGN=0MG=E)YUL*_p zygn_|=l$o(%W6*AtKFU<_^!0+b)O4WwgrS3({RKQ42v_TZbn|D=c}1EF^Ipmv%-cWU=Gv4(zLJMk#w7mXZIM{$O9Sn`v; z(knRFc}Q_{W|ljeqRX z{ggv1#b+Nf{{ImFPB+JYD}A#kZv1;m2?a||KQfeS^Y;3@-79ACOwcSGUGlG(h2|v_ zbfW?MPP74jSS_}mwd88kC~jjHrTg;;W*;y3#0y`s)8K_FTZ7(qEqohk80(QEfaJFK zZJhTye4C5%22ALHz=oU+(yR#Z)Id0M^__9!dtV>;siWfPXW+;R@Dgwu*^^@AmJg_Y zG0y>ZvGp|XekS&6t9-TXbj3P9VzGgm#$5F(OESQF7sK)3jp3P5F#&zLQSWIGG$cBM zNKjYacAmKM?f5kVz7hM9rjW*(3Kc~?&@Ki!;N77;@_-m2QLJw4M9HeebRqFJkkd`M zDtn*i9!M-S)+5W@XY$-(oi3lT(`tOr zW#fA*um`=nv<)w$vGKi-%wa!Clg9TSq`}GhfIkh#F zX>4WBhXtRk^WrqP_Wljy5Z|~$^BIpkXJZ^gM|-cs2Q*nWNw)?_2+k?>t z-RKB-_E!J7XTBI-zh@_Q3zw)LF^z&$VMcG#rp(BLcZ?`&S`)^owtmG;CF0CQ=I>61 zpR=sriZ8R4V+@&T#qaJYIdDxIL;IiWV$qV9mYhcm33R!;wtnSi9zm@82=*!=OngGbsCRuy)5$Y0^xXAwoTCabXKzd?e#foy( zxqDt8!~zo3pe&`iG53Ppn0tZDcOA-e-YQW#OGA|>D6LjRtOZT2g;F3avaz&=oKf~` z8t`g&cz+<47>ta(#Fj!g<8U zlYbqTkL|k1Z|6}2^bM4N%{OBp@*{bJv#rgDg!%Vqe(sDsD4#n6;g#aVeA8b22Or=| zW4wVx!S{BroEofb)uXM!@Qh}o6|@DnD$Kxu$clU~mm06Qv zSlLfNLVa>$U}l3z^E@N4A|;|hnfC&^af~QL!m$<#E*Ht7!qw!2lrJ_G2GKP7#h_pF z^?ya$+hz7(<-wBhv{q~Y4MC$h=xKpNftbup_G*&u34*XL7OaGBY!RfTdpZJjd_QQk zh#UCCrJ!EHcSalP((}Y(5!=kXK6C$YjrQ)lU2$WyLCCd6r+96}0XTYzF<_o!4%Ej%(b zB(EnA$xnDlg1}8r9Fc_a06$7HGSeFBGOgw9_K5MZI$ic)9 zo5Y6>d|@4t)ffzE^S33o!e|f!SWm+tSbmJhJ{my5uti%ujS3T2^tTDFMv4Zd%Mr9D z+3AJBnl`5xMcw<3wsF&-#UUWXK18|j3dj%9Ij>IqfW*<|B@|m>;+yoiRmNV7cOdpv zFm&xbrEpzDqN9i4a^W*tMNfL*#-;We{!DE({3}H~)@@=-_ZtU-iiOHF{nBz-UazDx zqhH%_hjLl;$N8lFB?-~_QadlFB1$IuFL+xpsy=V0PYZSWy!#NX>l1v>t~wVk3hP|# zNR>N}{Lx(4CdB@bqZ=Q128_3LA*-*rKTx?t9M52;?~Tb74+L=?zQrn2C^yBa>mW`f z_>0X#2+}Or4go4Y%L$g9dL6ts<<92&EWI^d#?W*8L$hV_$?UH& zC$RbrFht@+NSS|z*v$&*uW^;iKXV!7pW&Sy3K2T7z`r`qlV`*SV`3U5!r$&0!zv|m zsKAY`F?>3>*k5C__!-IRipz9K`6HFThAqxt!x`tVAzNa|mc*Kk)8mLu=AZcuDQqbJ zj7pvJtUR)>xnMQ>X<(f|AydU38a_hZsMkGfIR(v+8B1nLh%jz9vlGS~B%|pKRz?V> zBcA{{NHcTkvWZ!?koQF`yo9*?mIQP6MwyCcASBbtTi_h`BfDUVR5VF|9D7uf2>oLx zec8{p#E*{0LAfjIt+^{3dJwuWY`H;9* z!$;RC^C5|;Hf-k9BJB);i~j#0LWYBR!Y5Nk5L}D69-!;l1EjwGww7hPDV*6ukva3k zk3e)=K{Vj$4ta+B;&Al5E>6U^67aTs;csC2rcEMsjZ_Z5r)x7?D81%7j$&9i2`2b* zKs~%-KD%4mQP*0M)wC`DQgyj*tR-;B`syq)`c~|v?KP--Tcn%B#bm#q&X5R<{&unE zZY4+X`9k=fyPX7Z@lxjW{x~GXPQs6@`5P)=V`v8%L$^Tv^#iiYLseKI3YoVvmS*zr zQdhCoie$Nbe)U|@)>CF%_rUZ?Z7o4t*%RYp_OF=k>@7rVFC$Nki=7msHg^x{6y`U{ zSNh3)-P+!rvc0ELwwGYmNXOh{X>aF!aG~v8k|qAWz9a@;h_p1m@7rR-`$=e=I6ulH zQ3fHN6Sivx$!IRaX z1N9+YjVmx3M)%G~ljk6Cj|=i$97|S}6YmZ)o!w#JJ>p0PeMsj5`xC(NX!;r4fpWcw z#V0oEeg=8y);8{z_zq4s9?G1D3n^bBF)VI!BXNhw&O!Pja6oQmhk%Qr6dRl568xQw zjUM2wI1h|X*#+-?U#V&rz8|v-Duyg3AFzJaZ*=;MgT?`+tNlxYt?=&EA1Fy6gP#;j zE$+=Rt?Kg*8;2Z7t?JTilmL<8FWB5WN-#*yr5F2!7-sAe5`DxzUdTCHd`4H~bE5~N zx}!;I!DJ`|Yr#xty4y+rwfYURr%EsjKlEVrX!!|i@(9w1%;)^_hqU$ArIB4p+wj>m zwnma(s7?-R-9iLn)oW|U#pSShAxV$&7N+etG;ON~d6-_H!5A0zD>X9VgYy=-dWhfo zn}grEUcK`RawK|HUmcZWW1Q+ePp^qOwRHxBs_9SD#>~+^_(jo*YraA1I!*}YH`*}m z;OdQi(7IF^+dv;+{`_^9`RXo-Jne$B{5HY9leyWbkL=38AlG{bw9tU=J-Om@{3aOp zrzBL!VuVA<@i7$HVSKD(3g_p#FL>I-gdxv#v_4ST9%y*k5uI3dN2bjq?XhSOTew5U z5#up7+wP#KkwBOmg&*!f*q65uSGkdIiUt}3VzF0E=NA@#RgttfGmSZU3j`m8SIgiT z!ikgcvouy9GLRr-#fKmas522Kf+^08oG%6>D2f?~PV6ktwADtd$4~bz9!E%1;(m1E z^X0Gw>vf~*97bEuKg__uV?(j^6B`9fd=9)9u4r~<35jJ|9+X@IP6Q-g*}6P!@nl3& z9--e?9nse9uu=ZB7HwS%U0u0dh&AH12?Xr5?sxonn;;T?yiY!B)Yd)5&-QBT9_DA= z;#ns28cQD#e_vd>1Up+^5ek)08kYVal(w{jo&@5a1n5aIJt>WQawR=+)048eCllz& zIMTRj?rC|*?9+q`wPbUewic1E!0I>1ccHSM5~>bZF(V1yT=@xn1AYR>wGCgDegXvK zEaX4R{yOEaG9B!cHFC3@@|5(VnDVqrgu($o1r@0gP0G-?4@X5*Ti|F zX=V_!ec^a;kihH5Kpfx55LJoK#J2t>e>SMCyGwr7tF4=tPECE9!YD-h zj#9hauk~WYPigDE!0mZOTQ^Ns@HBaVpaMAITyuS_g4ekM@s(3nAU+$Em<`X8=L$aS zm#@FauMcVK4mkMrcSQdk)Sflue03-KOzml*_J|(;*1>52wRKO+f}hG;CJR0&3-0EE zPipH{$$}4Q>#7~nTCd_q7CLxW19H?~Kv&)^TFC)#H$ocMqV8mqKTy3*dI+iG+-;Y>Lrm;$MeOc1(p88T1~`L0$q7hLB()llFwVNx zs6jiZL63uZ-JwR;XLQu%1S*@Bf3kYA&Y95#tJg*%Uh6~yTF^%HMuPm=5O*^Lq>PN+p0ZsKMpU?KPmKO0eQ-%r6Z?hS5Rx zn(}PlHpw8h|82cK*lER=Xe~FD+S5oaFQcr>ZXiX^ z?XRk}pY1qYC4#37W&WIvLz&-Dq0Dc@=VyZm z7@_=Zz>^r+{A}c9LZBRsnGe6->@+h(`6t7if-PWV{MX&^w}6XalZwCPd~Db|f<_NT z48Ck-s7bn-r&q9nxi*8+v}zk3$Y6(yWzghT(V;s(LvHN1WiStk&5d-@{tVVUQ{ptx z$+QgCJYy>p{OzUUXJj5l3LX)paKu;lH1B`jqYU~?YUqMPK8~&=+-8c@CnN=!e&ZU7 zLxlmQ-bAqQBJK89L&g<<{2EZ7#R?HjH)yA{r1G%e^8$rRR=$M2s)*FhsL~SR{L$7x6@PffximjVvPbq|rn}{LzgtPb{={;%BdbICtWLUnpv0rm~5sfXC;@T6Zob~&5J=o_Li=m z-IBNv4?!t*CtTh8Jx0kDlr>$cm8MuT@m`I&qd$jr93|Hva+MR@X$zgmo3%9nuW%T4{ zcGf_dj@;i7HzGedHKa_wa3di|(N$8DP#CikVnJDP69_b+R5uR75?qE8WI@6EI71cz zH(_C35qXXTD?7wM4$%?s3x73Q;%WDVSK8qyZS_CH?2ZY9@50g=qZ@m0W8oO5-+;H) zt>E?!=-SNvNQ&dvX13|x&gx3Mvv`b42P~ZHE|=6bLlF!qRx%d%T zas_dA^WmY;x+Ir=%%zy1CFkqj{ndA~gfSl&-8+|76>Qe)GXF@)F~IO{1CO-vCW#=J(Dd13@l z=vwGR!DjCVD<-hnpS&Q8z=xs=On5HMuFr`2Jtsny`vaB7Lf*aFBNY_n&?2Pqpat(} zNUC*m$f+10)w9AAc9p=B|Gl86U#zmUK+Ri@fN>zu@K%}_?3=>rt>TRRQ%QKje-oa( znCj9#vC-{h8MdJp`*U%hOhjqKP zOiFH^zRzsNOwpqzie(Xl*yseJ)ZP_sG!H~?pfc&6DoBhchKPnPvgAda{~?$}C|?Jd zL!=zx(nmp4Od_7mpUW-^En}sN!e7U700%Uau=gKP+~1R9C8PN0SoTZglpwEiCJ-pO zDeVZ}xHmpE$b6&*d2d~ui-OC}E((PomK{X4#E>m9TMc#*hnzoLHU!l$As-ukA(_J0 zk4p~J2^rT`EnqK&dYg$R!7)8k*sllLA;0HO?5pr660~aGN+Wdz_Rr{cQdX?uHz}fY z;@)>NMSb1mx==-~3k&>0{-6MT9r^!SX*uj9p7ZgoHgz{xxgG8>(S{OZui!KhC=2uA z{*b3F_;TT;mkd`Pmy5<=bj8yK1O61G+O+|Bczt~w2_O#mbWsY4j>1wXWq&rU2K4x zCuot(L_woj`~ofnn~6L-L*aBmi<9>f6e0xn343i^X221d6`tmv9iEXJtZXG$!}Jz< zMhag~+pKV=8;Nn;0hyaK6rSJ=I+`U!TBCRc&}oIU!xP;8f;VS}rx9FXSs*;aY2jA` zM%LALj6)`f)fUHxR_)=}NeaWMgcFnk$@@7?2qZ}d);UMVmp|DhpUt2|A_)9wlRgUv zK+~j|U;8wv?(bb~aaj2Bpr|3AwrnXu!RTGs zvlOxDj1d6G3kP{46jN3cDQHRIc9b;#S4;q=`N5uIlFqu#i0>|)V%tB>vF%%WJ|%Ny zGIRO}C~E#3^Pt#59uyeJ?@*G_nT$UJ zvB5P!Y<)$ulx-Wyono%|hK6|lR`{0?kXev!%tdxkj_)fJe+M~+@y~>_sLkB0dpB1v zCFaaU7BbjF(#5tjv!P}nT8#*u$kO}4m24p125={Ov_;6#Plk*O$Cix~Y=^Ox38MNo zuxoBBMUcLoG^F`-iXyPuCa#Xs%+;KTRzJ;k8x66Fb#Fs;m}HxEcQ-uMyM`t>k61zL zEXT?&;&OYJ(wyJTbI$Y4Es9Mye~*=JlgTvH{FCP_`+iKYab@f&veQ}-;#nd z-?Y7Z*T`HTdrzjy1+tJr%!xyEGXqHmB-1*y$;Q?pQhd6fmF9QFw<^~yWi?`s*O!n9 zq|3|%vfYviM6$`8|KnEZkL&Ho|B;p$?`O&jx9D@oUxR=pjT7X19;Z|$FW~t|!wA9C zFKN!W%8SK>jtaNG?0_Ofv(uaBbDDF+4(nY*5io3;5&Em#yM#VWb|496d%#2qp zlR?>gbcOfBoSJ1zr0yfG-2o}k=x|Gr=~tj`{*83hx#EU7F?Z&e8#DVV>z-N+VW_ez zSa~XF^d@;NH%2mT1da2p1bCz^#CnDQN?S%zUks!xWNbGbMqAlobW4EHai!O?c9AWG zc#lBNt~`@F`TZDN**ZZG!ULC=5(Lwvb>!Ejxm?9?1Ay#qSq&|-ZXHjx(-(@P1cEKaDIW5{21?rv5Tw(<)x zr%p^i6xMqMcd=hqfRE$dI$b|+S8koK_Ra(CG%=F=LqI9wCRSH}OSEl@rEL-;|ETx` zFeiuMak1sT`Rx9AM{>J&plY{jH#Jx`zh7kvknI<3|3zHe@#Uk0G_;X*DwLcR7#r!& z#s==9e$Hy>BDST1JWc6zhJWd+y(`fq_AebnZ&9|b$z&+VE9_l>N^XHpmk!o3d=wWe zkJY>!I*F00OQuZgw*bGSyG=rOn`}j)3HyRngfvEav{mWq13-Bm0l+-qUYcS@m z35ESxyKC|(5U^lW;H>b){4`mm|8FPMdHd*z*m)Z}WD?F-sfr!*XGCO$^=OkBPoIuo ziSR93i*C6Urd={6R2~e%9V=puEW(3%bNLM3oNlvu4`>@2(m9@}-X?W)zetw{>yH4w zZ?H(Fn?9k>tLU&|r4_DDXU2olh2qR!n9gS_0n>!P81;5&p$_ks6_==p(_G1am>Hg9 zO$#U-SHTTXieBTmN?+>p^s5Y}+U@U~NlZCv$jsx*CvKI@sU=hRa>8ES7z$SI6Te^O z)V(3&V5o94xs&CS?@Df+Gw9vP{wzVG!HUJ7EpLTqJ8?Ulxf2NbAy2D}PSe9P)(Q#p zq4@k0QpusY%3-Sg0EkKC&r#mN$vGOO+5t*2@$-;OMZu5o0r*5LE&2*@OO$GZR9!v- zvk>3N_Zrl9@|$vJ(nqcj|Hx(eh)t%lRoq67FMtjqmyj@at}G3Ckl7fpeF##^d`OO;>E*(%KaqSiHfo>3V3&Gk6dAAy*9C&p0`RWUk`W=2BXbj{;;-<6QS_b zeF4PA<`L@FoGa*LCj5@MbSZ@4j_@~;rKkh=X~CKna5Lp?Jk&H76`c|?t(+k4W=ut* zl=m?(J0;EFrFwQEIYf>teyw6V-kp{w=W<7|rWvEV!yFLG^0kFUW2!-dfRrOj_GrTB zG+IVy%tbsp>kr3`4n}2i)Yy*DS#m_m6m)?7k3g6WIY@GilY>(3x#xlBtw8Y+B)K%0 z#?e1njH7B7iTUd)eE#vYhel?t8;XY0iwJz*ba>B-6j*Ls(D{pQy^XwOYTj6+!&T;p z&*&#KESCw-4^u4VM1NmfpNSxS@B7*Yhjer_;TIwnck|INB*oWmPGb?RUWq@?rSTw= zBlR}W8B0iTW+{luV}&ing^?v|hoU|s63+Bv&vCSSk87dhy7&DRqs2IF7q>jymBgZl zueUoIOdsywOojA55D*8rGkk-i=SdthemgnK!1Z9xp-iy>Z-XyWiO~$j5&_;9htt1~ zsyuzVu@CN%m^EX&^p^CoTecfv`2-J_8)+6TH2R=^+Cfqfi&uWa2%~*r_hjFz4U)q^$a!M87m1*^5EQa$+^-fZo;#dM^vG#%+r(5{>yqcLHFr+A}VZzrgoDqY4GePE?Z0A0-|Lw8hL|6PhLifS9`2yUugTXSFP zeO-~|EiwrS_H~=K#wRnHEWnn2PhMH%ud8GW#r7SERu4w659iU&>UX^6a~#pVrvxFN z(!J3YpHNFxvCzMh2Vw65bMp`#J0%CMB=uo z8~fSEeup|7kVX)(I=>WyfJh*%c9a;3>5>OnbHXNfUz#r6(;|u%{RqL3!|&kmLD2=9 zsbpC$m3fO<=6~BPW&YfjYDf2b-b>ihn?%uEQo-iPXjxYE*JL@1zZ{U!PWV!sN)%{E z^GM}U6RAOh$Ymo@Q1|mgVkvDgBGHN7OEYb?Z&r_;e$`?e(t`Vl7Hl?J>a@CXJ!}!u0fO*k#1Pr$&rirFV(Mr0TcyWuiLujXY%Vb(Mx)i81CMle z_`MI=jFD^TE(Jn#bGr254y1En1BYLQ@|*#9i+v`YwAN^@xfaO}d}55j8HaadVj*!Z zl7NbK1yJm9!7YVvj{3YEJ|QRi>ZFd_+rHvcYM>R>w@a#zH8~^%IVWY};Rp)-FAB71 znQk1yf~b<{Ba4J=NUKlCgIF15CE>5o0Z{5Ujtkimn*q#$VjrM=p~Pq`@w9*!Dxx^A zU1HUQtL(wb6D8riW{8XV*t%Ro{tR(&TPC4o1w0M2!!xExlp}^L@-)_^=iT`zrzh9F z-CB68v_^$;Bf8NY&UC|l7^z!(XpGtdLb6mR&M%4E*?(g=JvZdp7pytvfWDV-0WyCO zkB1_T7sdtZSR%M4fC?Qnb`lPN=6+ebez*0>QHjPQM7|!Mc73Du*|G4=X{3S`rRm!B zopB|G%N^SF2TdrRWA^H`$SOrlnRf@{6HVwyYDa^1eG>qA8jLRS8sQU>{wf2|V)%lKCP(hHF4d)4sfjKW?}VCJ)#5dANcwWhai79HB1Q`p`0Tdbc@3 zM|2({^U=k(;y1L{akqL;N<+QyJni`B>2Sx9vG;K%6vw>=ZM#VQ6uHx4R{9Zy#rybkN^*&t;apzg_Y`4Gw)s(eSUIg$QAmFV56K;;&~ zDKR4Fya+CmJjpAcf)IDO{w1&3;z3;VB9> zX$^*N6hc^i8e9CJJuC^Q*Gm@8W@8~v0= zgDay1%G@v3cvUo?MO9Spe#lCs9Yy)FU}R7DfOVrj4$fPMU?M>sKeS0A$ZRsT%3?(k zGG#V&l76wCu&?)H&~@ZZ7p)5I#-tzPp=#9xYb#cd`+#g{Z;FQYik?zKd(hA~p~s`} zgg_MCEPJb;ZW6#t0H zAP?~-?InSkALCVD2m`u;qJtvcU5Ad%AWrH#Z(Y~_L^N!eRqpiWpePTar3x?CT2qNDd zsNCms>>_iX*at-U$7$z+ZoX)M&k7zA_wiUucc*CSZkS=yNqMbPdiRRAIiYVo&Y&_O zqm_$l%8!X625EGH3WN6{AD{{f>PhSDN;cx9L|ci4-`r~JSv3H?HlMeOS>Z!h@hbc) zP9G7@xc~7?n-wpjFkt98exoyF=uV$;kkL>AhTupfBnBcr84}|Q|8)v#iNaWsJ-@b{ zidxIBd7~_;ynx{}BS!>RSA_Br#vU|^av=cbmly#T)g|r-siw%u)m`_gAyik^6jy#A zb@|AhjIw0mZjvlT!G)q9k%=~Hc%X^!Wfp{tn_Ygv!-^7&gD6fncFRUSDjQkK#RPPt z*oC5s&_*;`H_{+NqwOi0{M40$sAz#w@QcD-Mybni_IyS>k@z;H+yhxZ8YAkbF*g(3 zu9BiUsxyZuxw`NYs;)FeHR7zLL~c z7Bd<6jq^|?hMa1<>K{Y#wQ+quG4wp9_m&M>iEk^%i#utK&<;2Hjy_W(&RZc!N2XXVou6p`mi_<3gQqP0FFNCR7fM>C3e%D+ zyz3XF`zxYepWXWGz95>gKF{v^*?$~N_AB)h0Hd+)c9X0>Ha%h6qfwZ3iG`fh zcxQ;4!#>{m%T6VY_b}7X6<4R;fBe5XHT?Z4xJz7ZftpJ@^s{i3sEucX8Fn0>$Yg(>CEdn$6HeZM|3IR(A`dh&?T>w>0{-OoC2NYS6WpBb_9V8JuV``fV2 z&yp1D``1fGY<(9wMs|LxMy28pMvd6~bS_Or-%Ce~zK@ips;_Ls>g)Sq3i){$%ipvt z#eChpY{cek)1@Ozf4>`-;yn5G;SoE(^7~VqmnV;pSbar59og|a@|{%s(RW5{KRR+{ ziuwBWl_MtKFG#^}_jn#2vHCV;j_mp2dLl)CdLRFv;-7Y2l7fFS`I*W~XdfBD`oHt1 z6#Uc()_3(KspRe9-0vS4S$WMiH^ursR54=fd)2p6oR{8jjo5ivRg$8A-jWgPU*{t! z_J8A%5!?Td>`kt3SjTTqs`1-1V&iA~Y$|%ZX2j~NZ%xJTw2m0RWBYpw`rq=mk>gMA zN^u_i@U9U%-)#SpO8@!qBQ}4o`jO>-inJ8{8LN$0fBI&OY=3OiNA^6~^y`%J$ywMt z=WQHHW$z?=TZyqVWCUYw0_q6&8)IZi`KIB`4XCBu$ovPbrZ#Smw6{GNJxBYnY0rDU zF@-&^x-n+YJB$ARZ7{L_XK8OPPV@&kr}4Y)hl8gXzt{e4(CpvG@&EYwz>vlNV;KD} zhEm(JFC7|6obQh@9xF?RPBR{tmJEHI`QG@A#Q8eA@89}rqJO|y(#z7s?|Xi|H1Yg9 zi}F4>C(+-br}vg_FpU9dV9t#NKhcF`sQO(|E^dv`F=;Q1&X{db+wX+_{lo&X1nX#ZLJ%&R6_RvVUK>D{edqU}`4) z{nuC{Qh56jXGO}E+{oTZr*ZO=`lJ9Wm1a*ml`y|?{*I~qNflEYV78=4IXf1{c)EP) z@aSgO(%i^DI!`ICBEjFWGiCiBcfYvl-P7`0)06xeKSudixl`>&XUV^jV`OB{{nCMV zYl{AVM17ka|1>rHuDa9v@h+}A70=kkkI%%q;TWH1P9*Vn!{9#}{H$eG&+WW>`wZ>- z80G!_b4ll?xt@#8u%EbMWm>k8SlQS^zV!&N;8Ijz$uBfqa9 z?X5mKGVT4*cT>~HHQyaR`k3^sXcB$2+-K#-hs(eJqA5uhoO7bTiKitmN-_U&{0l#)aOZco=MjrwKyO7>t9yOFrMG) zK9YjpVVH>1HlYfCXDyIzP(Tn>+=fMH!-BXrl+jnE)Dl8vo>fHm19U&1?ibPhT)JN< zJBs`H)ZMtQ72VmlE@D{YMMUNfMJi4@Ir53dnezSew4K+Nx0enEcCocUa>OKQ{lo_+#w%ACcZK`Q^zE zqxVg}z$<67KCis`+~U!`aNe-)H#>fNI(=jHZ=)ZP-z${!o3D&hCiBEiHfF^Msy%nl zN}(_Qxb~5M4ln=kli#0iJp8{O4*ly-K5_Q)|A&q5$w1se_;KQS6ZfoQxo4#%?^zcX z5&9ENsHh2{eREXG>Vyzi`M&YYP$H~(^! zw=N%pMeuFPf-lvwSVGp|DEhLmE#Z4^xf0)1b_>qYDY4Hf<)2N|6FB2=j+X<6cqQ>^ ztVxo5)+3)u-WL3uKnhBHiR$TrIDe=+P9nmKk&|5al?9)&S}ZdU&7gu!TNjnz%Y9P< z-ifoCc*?CQ^aIHXZn6T!J22R|F8w+mOaGeUg~b8q1K-Bemu)b81#hm$N1e9~ju6q4 zD7=dZZ;9|)^#J{Ik!{<*iMP0vy^8fxQi6rcuBtI1Y^aUowk4R^++MY^NyH$Yls5B> zY^@-Ha&B97@c|`_g9a4nE0gw$CY(%R+C91kJ525+vaW!fet2X^H+UsxK*S z*l@0}G`D;jcEy>(eLj1NvlXRbDh|HGalU~*coWe03`w>&p|`sB_u3tt{gMZX%E#( zK7vr0&$G-!M-wk(H`+JXpFEHz6z(tB`Y``FfatO1FDgz{i2>zr2L0kb*ChFaBA5QoN!se7A935 zZnAoBle~?OtW})*_ECA3=PS{ZK4ll>eZqf(z@d_SQdEKUy?vCxja?QW?J%!Bv}Kvk zE9F0&NGzF&iu2$;Bq}3O@@F^{X9@k1OQc(Hy-ZvOGLyvb*YG=ovYF|SN{=N;$r<=K zq~ubG-#4Wwgx->VBpXzcJ-w8QzXg)OZ-&26Nk8l2C)NDDWzfs~LOChF*Q!JD7K^Wz z;v7IkhGyPLSgMk6s#(DL7hRX>--H+ZnJXYJHu#gch*ohB zR$hx+Rf~ruIkt6YZc9>J$SS2)cpD{`oR;jL+P)wl->+T77_cHLbU__Ig0hgU9jry_ zCv;sfKkZM2)a6=nJGM(nsCMuZ`mrL4MIl3jL9ZyGnFOAO^^O=KAk-lEiuh4G0AfI$ zzv#yxbkZar9FG?VJ#By*`gLq8hoNNMc)ihS^wFx|4^|!#uF1a=Mvqe_V7$A#`i-E2~;TYCd`J7%Vt=ggreU{YB0FKs+T~c;B4`u4&fA{aDSOu#;U&E-i&5+2qY=-Q=o{i`zuCUd)kq2qN|a(M>9Q6@oaPDC+b;PVwQ{HK z^(M-9)bpg4K3!Mt6BkqJNiBJrH0$Ozp`+9oXtd)(&!5=JG8tEOsgc*B?`UrGiF%nQ z%{&}@BL*WZm0f(n5npZT1?T(CxHVAKh&a=F&8It-nTNfx1^ayU$-4N0MCj|Y7`4%& z7O8)Jb}#kM{MJhln_s^jH&hBNYn6uAj7>jTVny8w&7T@0I+VO)SKAFSxU+>iGNn9_ zdD^wU61G4)oMlfx6=(4-wCM%>nxMd(Lwp$Q4pOe9zjg_w+EY`ifnyxXkqpI3x+4&b z6*Go5{B|JB;xTTGW9Y&P1P0J!{Dq)FLina@y*T6@V^1Mos2zNjPskS%aQ%?QkmaP_ zK;J{q1r+C0!X(22qgIx%tOFUR?XQ*bLm}g-c9YlGuKm(K3yexKHFYlvSWno zLlLrH?yFgLGukl88*0drOhaqNP8*w=$=k}U-RPRT{^E4GG+9ZPcetjXe!~0kSl9G3 z{U3h9moD#DP63Gv6y;#(v8X6?2Cz7z5-^A)?Rd?&bWAzizc~W8xsWG zX*4dk+&W^1$Y;5AFkO*`=Ag#ohT^NqxH}0hs!}RFo+QOrO8f>;ymYTst)8`fU1V!Z zlF6@#E4MbF3$$^+)j?Nj=gX744f89K%B`2S+bmWNxUgM}o^4<|d%5@>qsn$Bm!w!v zQxFX;VUr`J_3`aw7qs^5!2dh>e`o%G!%qDFldtRZwT1tmWemHNnAu`VEHP}ACXRR@OXVG&{xq~IK{;@ zdJ+t+ZHS)Vp3J?AzB+8me!e5#qK=^DnA#<=#O%i@IrwC&-`c02={jN}YxG~ja>XSp z^3e;^mh#Nj66H;h6k;s}6^7`}a95_~@G(D%7J1d`5NxmY9>4C-%_2>!M zPI>H*I!^^b7)S9d;LJ;P2=UvtixN|dYc_tOq;wdX2MGP_yOvN^pg0502?OjxeC{^H zH`7M=Tez&qs*cFf%_2t=I2pF+upD`}agN#pmmu#}b0Tl2nCKrKYH|VR+8$~SeA~iu zu$z7+mk7b|`hz8(YI}qbTylv^uC%{~-``QQvcI;Ot~?~^qMNSx%J@neLzULQY&Ufm zp+kiZmM>G5B-u?lb3TqDN!{+fRdMzZDW}YpW6sc-6I96UmWD74&yro*t~QhIs%Dq_ zl47ncG-?VBjc@!nge zJ$t>y8>cv-w?b47gX28Nr?MTYBt8QJ=q^fQ$f-Cph$du16be?Hk8m0XT$KFOQuNVb^+}i>QZp>N!_H%QS65(A6wQO7TAf?Aaff+=!ir)o!V;l4RE&)&+`g6!%I@i{ zy82qbo9_U%Y}!b0qJh7ByuE@g+(rao+nRWizFG`v=X zkTg$Wl8U)a9j^{m#9PezA`V66kmQk$47{*zn^bsYN)r^9y}5?#;1P6@~&byeS zq)a4U$0y7tVef0NN%oo=WwrjQIA7WlZ_zvk5%va$Kk*;J@B9zp9se;L4a#K2WMB=0 z$~{(s{dO}Av$Z>4QOJDDt5nUmUt8E^bC47N8ms|H(e*Llo>*j}^MXs!jH zWu4tVN;lRTY*vl6Fk7t|aciG?^s&fHaF zzb>UUm~OwL(!#OBBdAzv~5J7q6W%M71Ir9a$vJKd+%k{)hT|^k%NV#4rEL z`nyo9)nA9-|L6L9y11tLE8ZV#(LT%QvxGitP51D*di}jr@_%Z-kG2>2Kk(n?|1&-R zXaD#7e^z7usqD4SGWsl`&sx(xeEz@n(+ll5e;5DT{9UW(FZ0a*oWEB#)s(-tiCg=; zNS{Lbyl%ROPmTE#J|})r9!=ne4aeIsUBk&N=QpItXENktm`GJA&JUj-aw^xlD0 zaoN@?jet_5L|;8gZhF*}V5yqHqqTGJby#?+@W_1<73VXXgnPkn-Q>dUT9~Iwe%nvI z6D*;T1oLeV+;%o&As5YOrimB5lz6Q!r7~1YamohuAi`CgZ*IhmroV5(Y)rCeG_5K^ zWADgw)!mBo280(L@wSyxo9?C{?=^I>g<^ZVa(#^42tn^n$x5iD;w;#RxvzBzMNFpZ zbiS5|l-M#TU5fLaVg!nJp=Qck-10%UyeD`#L!3{4$Rgf+iQ+3%ym9Grx$DBAP)vsW zMFtVvM)r8Et|;#F-uANd_hbO?4vw)}EUDHX>DOeZG*^l#Nl!`N8i%wu*;@67c=aaD z-!^Hvwik4ZX8b1U@*bMi#IJ`x?0^)o?Y-GSX?&GZ9wO;oiqpYDK{U<=C=h$$Wv=zz zRVT%iZC2+5O9rMGwm=%LN_eMe^1gKcL#;-mQRQGpku5g9`PH|Erio1(ygxxKz+sYS zVTbM`YSB|2tEK&5!s!M_eO*5K}*7Lp| z8!WK^jp7QVjPrZ9{MOPgSiW3E|I^sxxTK3%wDjlEL?E#W#V>bwjc!r}P{B|c*M^V# z!PDuf=O(wMN|R#trA#^)=W8m-^$tq@CI_Ybwp2JXISaq9kGE7!1%`D9VWb9q9Rs7& zu(%Z((hxXnBDI7 z2gUr$?r@!!0`AlH1n8h&qI|z*#*`>q{2fl*l3?lMpR_gZ4u6xak`hCWFsZ9rJ^S5w zf#(Qr=y(+@O&#QlbbroR#raKWy+gOs%xa1&B!Q{(|bNvR*N$5>88sc+n&=3z&LzLQ~A=X_l z8X`4<7dB`q_q8*nbH+M&k&vosB2^AbRiaxSmqaOJzs#>4v<7;zm$`vb`bI;&<{-KM zL;KviPIFzJR?`s6cl2U9w-zbgTJ@-+C^f0uozRt;>YkTGe#fY`-KAUG&gju!r^{#1 zdhbwMZ{PK5>#fbNn9oHUrRt@|a;X#Aaj28lj=$|B+OhES(d1X#A%p7ZoFQ+OI}~?f zr9rJ4FZm62LP8$An7U>1O_KZ-HS6>gFSYCI!`pQMXT34yEHp{BPVH1 z3vb;dOzI_x=7ZZaW4856P2UzbB@V6!iiMCV z?OpO_SNpPg?Kkhe=!z-BdzoXlrKhIvh?{IpmuW<^=deQ+?;FM@ZOxvqH zuQzmua0n>@Vk-k5$~mx@Fv78kNn7JeifoeekL2QLQL|=Q*xy8 zemOiFF7KDEevcL{{GqkFl2AwHncYnzGsZD(WZsZq8kxUYt&Pk#@DN;E9hp7d)seX` zk5|rugsYCsXV&P{Vq~^3X44UCw2^t>s<6&BW0f&7cU`59%CE?z&KByZ7QfR`nEETP&o~8F!@Ng1});Id;J4{SFAgi^%tK_fSg?Qww%8e(_ z&a&AqJeX*x&a;$lk33s6)_rKK`_NeL*7M-K7aOPK{k(^a<{_WzRoehLu@`P;usv1S zz{goV$Qi5pRLkliHLH6*Q7!C_ToxttGt`ob_1`PiVmD3J1Y*#!4|&VRorfg_hIIs^EILf|E_~BPodX{P^! z_iZEj?SMZPGVQQff*5ab&RcCCjC6uH+w+Eh!lo3lKj_ z@(-Yb+TqV<)Rq4-)VzE*IR4*$fYV*cCwzs!J2bdBmLY(Ir<{0&eU z?vM3eac%OEKz~K@H}!Q$u$XsJU?F@D)!5MXc9OX1W4#MLi!EmP8+bB4sSf-=EgY5* zRj#!QMU_(eCV4y2-32Xp&GP+^S?Al^t1F?uE(=?R?7y6<`-n-c+`1eS^QJ%Fj8XN7 z?;g{XoEQ3R))i_C?Yf*>Xespr$OM|c+RiT8*3%r4_isx58||RJ%NTkvH|f0Dl42cA zw?lpgu4ZP`v>#2LF0AfgD=RN*p6MCud%%Myi{w2;VkgX!<$4J<#|j7$KD|t=2sbok zc-S(u?@$j?xSXy^40Vs8{?zsOrW~ykMZ>0*L5Uo(JlKzW_C zA?aND&Th?AZ{V^Ik;AHu7O|XApA@;`ocggSiYXp>fWw`CiHl=!ViotcfHSKJHKCh~ z=viv?frw7GNqO%JJxUR;?S%Twp!C^1%i{Tg8(v>$&d?p1STo*_!ao@51-Q-(7bW1l zH?U~H`BX<<>#u7<7$y+nH1Ypb%BJ#^2T2~{4cK~6;v}W@{tx5Lr4A=5R>{9XYvA1v z5zj-2dB|8pYaMpO7I2UD@DPC_sdOm^@uKycdDcH5iEm5t0rj?1-0mK6`)_VD1JWXH z|154x5w|PF?W~B~8^rDGh}(F?xg0=Pj32i9OVeBy5tcu+bE;c_*EUf;8y*OOk zWPS>@k4)12F{3dD#6IH23@a)h0ZCdwv~1$5T}s{S3XHvj~rX#tfYK+*yvEnuAp z$kGC`w1BroK(-c;tpyZ{fE+C#M++D$0&=y0TrFTQ2j~r=q2&KaY&;afT}ALhQ}9Al zuu}xDFa@tL1)qN(!J^${a#NDerF_JV=VopFoZC-5P2vlZ2x~##u{;#`HU{J7I3|yG zm!NTt4Oa)4{KfE^XgbaDlS|V5JXm=AF0vs8QO@0obUhmSCkCUc-<8B0{^=$@X>QmG zE}9v-X};*nqE9XlzVL$>GGt4X67n=go<2hmExUbgbJ>uZgdeAiaI+t?->&NDN3Q{dP}L!HPjfrw_>Y=?yyhtVb$oZ z_j#PSH&GoY3f>D_HBzF<#-?%8zk+?$v$YD=>#4~Sb;nY~8i=0$g+}^sky5xQye)zY z;H4~?5T|W{kz`?bX&E8Ah5JwSj8)5@kCck@0NS4tO$l8n61p%lq2u1u6H4W=gyMU5 zN0dh~mB))qxI89btColNU2}QVkiWS#=I=90wfxPE$lneB{vYyp?tLwPb4C8nzr>sitJ z{ojk#L{-ZojnCA1tWA-x!hO8`QZer^ps=X-!>u1#NcC^L?ZIT2Ov_ zaei$dzpI+53l@bn^YC^6^Z6Sm**?2S<9DZGcl)!thZXjXiz8dTR$i{|YGZIJ&LG+n z<|U>MizN5$UcE#}63R%jr(5eR;e3}}B|qNeu;e6Y(psH^)cwcw=;fQHmTwbcaj@Ws z_qdqTsugqERr;2eenQLQ#+UA#^RDP${y|w#?{+D_4I9H1inGVUnlzlAo-8#zDZKg# z=N@I5DnGP{Qw2DhJ!qZ2!@F9WZuxG!#hloz@Wh(thpFT*!02#!c1ozWjFBoNw?u5p%vi??n352&Cy^wF7zH z;d$OJ#rfeoJkR^6i8{}l$su{GD8-*NG3I$=7V+)3Xr4EXZinVX)W<*g3;yLuKF_6m zYD!;E;AwR~SuIJ`2NpjEpOFR%BuzrO6P}DZOYm?@)yy(K4Jjq_8vQeKMVwQWR6$nwxZAP5SU_jJS0IeKaG164_aC7QYpdh9BRIw+Q;qH7n_w{UO%E z`K+Z<;F&$^l7sU8bm*5$*!nJ|^oz5MPEKx6pX)Gg{+bKf0H| z)pvQmxLu!jXbg9FQinP0g{`=^_NP*~GZC9f9lUG1u9SaCu@_>gQ689-E^kYh`z2u` zerZ*7X)xNA|9$QJW3|&$E9`|QsC0_(%x9oa;`!1fukCzkn!MefULnQo3B^()lw9Xa z=r;XNBQ;bh#T>-#VUmmeFZQ)j>GrkJp+@vGntraMpX;JSRyitNj&`NeWd1oAfc%Vf z{$J6di5YTfru~!6!OJhiTj+)_wy-4hFwPMu3*Ao*a}#BrN+|Ub6;R=3pCqN8k<0L$ z03N$YhiVbd=s)M`YcI8V6b`kJiLqT zYqz{P^T9*XJhyzs^t6sQ1AAQed9CuGMC@OvlsDr6oy!Uth#AyN+h*_+n6mAMH(2*A zZ>ElBJKr!o)SQNSt_xS>&93CK(qQw#ed+d$k_`JNWqktmqK!y2{ohFq(Vf3JmN??Z zk0!+ZR(|H^m1UQ;XMP4XL9Zx1-VD!JUHCd=!yUJJXRjo0hNhe+T~M$$FS%SQ4fdu^ z;2}nj{gd)LA{nE(BU#Ik0eQa%58CL#ZY3x2k=ZX1H9kGmZ|&nTT~{k#vJbFum%81D z=cszk)9j4odHfue3(x2bBu?LTQvL*o#rrjdUHgU>%k~ChrCObc(crAz;da=Empo$c zSTXS{-+|C_*ZOF->|1~NdX4Uq8vCn<7f`C)a&9hk=L1sylsJp`Zb{xw^NxA?^2x)L zcITV;2^Xp2OnD=6U1Z(sVhJdfNA_LHu(*H7nJWv40T4Qxh^0@MI2K=rS$~$ zD*KF`V*Q84Bkp7sSYetWyeF5Mjc?8hb^kHCUroLv1oAKS9^XP}-TlX;nxY-0JgY*bx!2OtX_#ydEF%pxLlFT!7UCNbC6$6z6Ym2v02y1Doc=3zP1v>gO_0O1_;_ zbMx5gk)3B6*?9%+9@%=HF7q$O`mD+O+{*D@@Spf>Y;ZUSV=a#ELCyIb*rXD0_KxRG zDs(&lVhlDUI6Ii`MEUB{oyI~=lJ`mh+cSh;RmbdSk7$}OD1U)jo$^$oZ;DvyqoQzJ zWeNU?*OBW=w%AVd$`D3~G%OGPj-pH5ES{9}$TQRp`}Tq&K)2vlH#xz zyll~*GI6I?ObT($sm}>o_3HDs#kv)5%^97@WBI_u;K3H!l7zO;=B-!N!CYP3HMed3 znTXBahUzmB&U7M>x0;`fuoqw}rCaXPM&h@j40PaHmK^!t&+-pUs=Al$i~pqH_4!a7 zKg{(HWqAX)Aa*Czbp>3iq2TckNOX$@*F~>!iITn$r3ve-vqyjY;UOu%G;ubbe{h9A z{~&-OpO4w>P0;c#z0OZF=sEXZM~rH&2RLx8=6XO2{cxEV#c3$pD2}C2rb>m% z1cwxe?LlppX7Z%m?9wGEb-!fyd@Z#$=--cKa=5 zo>biO*audG-PZ8Far^Ifxcy@i)q`^Iuw{1gDYw7xF*bKyIhldJarE;L??7;kb_WI@ zlKhX2Mg#LdmQ52X`pR(!1{|YEaf)*gx(M=(SMC3zsd`d{`wx7VoZ#~ zBU6&UE_VV*?rE!_Y^KPMrIBX2<>DSq47QJrc{^09ict`9nzWcZK zXxvp?A|e^z)l}cAYUGs`J}WMdKH1a)93FpKoX0OY!kXc+7pu4aoiA2z{p(-U#<;7m z_j=@)d3{H2ivIJ+Y#iE2Mcc`pTIOx%mhYfMM7!m?NS~0-siQC9P`8djw^p}@lu=A} z;O+9rdqW$|`O|Xc{1@Y^kM>&8N9t>CKdU5{hJLKsU&@1FDH*8SWejhn_QE&0ok=wC zl!l#H&;m>3wOnMpVtVTmREFX7?w@GuU@eF^33%5lTCbUuKch)NPo2I45CJ}1r< zOmm#IXIndWuZJnXEgaq1BLQ1?2%>e~%y>%@vEvh}W!10oscpLc@AN}P@_BL45L;-6 z(^I^ya=arH=e_{SE$D@j-!Z^O{%;o5C%4;ASqt=2LJ#8EWYTB{J$&{FT2I}X>;-%V zEv>iE#G1nQ*A8`1r$(_9%a`V+T1_3`m`vsBilV`aqU}iJcA`*+^4D6g?IXULyD-%n zk3wsv0!@mgUbjaF!}H~_6`_w6X9vGn$9q567bg)dtKMEr{fW<@y+mbEZV^U)IXVRP zI@iAH#>ap5w^12q$d_F5d5=sM^CiW(e1_qYT*osLy+`&yQc1&N&0Jo33<)@(&fF4E zzz5XLZcWkyxltKZP#uC#@@YxFRyZ`SCGvMY?n`Ry3RhrwbnuEee<#`0&nstC*9V(s z2*+CrJKmDXm=3iui7cRCx4&mAnhN-D4P6&0J!OVzp!=_VZ>I87&t6Q_hOQBb8JIep zWv}ssB;BF68op5T z{s!5$Xa>_3*)-Z2&sTf?dx&c9CM)7J2C)I;aPVGXKsVqC`8|A0LvUUL3-6A?PtxGm z<6gH|OT<(&^t?V{DgUSaI}!c_YP&`a?*e?o)F7d7IdS~#usq=f}EHX!& zuK+ulE}|&d+m1BgAuBZC_F&d#1*er=n<=k!@a*rrHz6}+aUxxN<3eLyn}pUNY}NuJY6VyS2EUzaGlaW&Ud4f@?@fq8ulm$nzYe#(hJeCgl{6$ z$?fk=-Hy1ebT5ScWsOAR=}?d6^m0ZbKTJZ3cu9fgX~{xw%SJ!Q(L36`u6wlVHu;OU zd2i7=(e46d-qyQ-!KEwe@)6dX3XIi3KJtV9SJgIV_=dk+aVBz~2}*KqHVSz>&N)Gb z;jm~BeC0FLmiGzFthpK9J6dhllXE1020q#J8Eq=#+X|{Hrk0%4NusTrt(Ce3Y6Caz zN`Yq`u5&E|)weuJ`IpeO&pEI&wv>8zSv>EC>zdC+krA)C-=an^cXT;>Rczuz$Ry}eWi)2af!bMzppKe%8kxsDb-udaEt z;DU(CHx~b4_Y|s2hZCMR-M`uf2(iArR~!ptyM{!32{da!58aNn*s6^Q=<&886yhaC z=^PM^GyjUYZWD2Q`}s6Sthc@)@S&Z}x8E;1*mI=bEpXJZ8FQz3|CjMFk~li)Jn5O| z(L3ZIPfQQ{nu?RR0`$*=b-46Ezvy6NN?+AVp_V?O=otP6Imi%|OY@Hfgv+ZN9x6xz zOB+nA&X*?nVkLQ8qNc+M&{aTnzS~HcfFJDA2Wq%dOPHIUW97 zb{ApOGoF0t|0L#B)7oW_169HL1LHVfT1TTBhBtxstNTW62< zK*t%HXXPR(*Lq|rE>rA5oTLG`mG9mB;443egOS+J4=|9$k&c;6eqN%*UXUWzPnS%i zHgaBgTwh|k-Pd&M;#AzaqWIj3^S$T92yi>bkEy~PEH_Y`_vEvcI#O|toQA=_e;g%q>Ur+Y zIn(0}Uv^&-3MIRQ(-2QE?hdB$fqx(tD?S_bBDi@}(ezvLx&^^L()oQnQ z1FHp}4(hk|X=b_(uYYxGq*ndx=uaEX^?A2ZpIM?l4@`--s5OtL)3{z=yh^>cuen}l zS@1YOc&$zetJTDlT&sUHsI>7kQd=v|!YO*Ce)tDhsyx+HsZUJShRrnb_#SsYK9WqY zX=+NrVHk}Mk@SyKxSDrcP1W3XvRI+b5+{g_Dsxgn;1i>MYo9+F8rKo_mf_!gDh`8l zYPVF*&n0`oPpr$j9hpSBEJIDyW-NB)pC+?^8_wXB2Rh*R+)P?55SxHWZvJ86YAfei zR5hRGGt^$ri<7OT{3&QJAJ8Qh`bN>PI$uelU+h#!C0&z{goN9r*u zm%>TvAhT=|yEe=xfvc?4A34%~hN=li&0JYT=5`=&{T;|$J%^N9@$@j(oZF}9D=v+a zl^PaN)ZHl2lOtDMjy+rRstYea8%MgwR_IS(n#S+p>i%ZTfG>^D62~=nR-CCua>PL% zoTjjpU? zWrykG~;i?bN0l*3cnHPfQY;q=t4|!#U_T#T)i- z{>ko{c;&%f6aXyrKF_j>2t}zY@zzYSB0l(H$3@vxW^O{k*mAWn?QlRT*cVKX(;&kWn46PuC(r7xbsy@6tGM`~yiV>qRnZRzjBr_!r z_Fx|zMI5KDreKivI@ulXZhufJy&U8A$9`4y8~bT7dVObXUsMm3BftkS?@rz3O>?7_ zr??!xo80nV#d-gPc#C|ouw;Ur?5~MjU-rUwyo-cV!_x$LKpY=tOXXY?YAn_P>X zo`ppK6%{!jwtoRqd%ude=V%Nl!`+#zSISFnB+SM;=0{3|O;?GBdRj@ABx(eE@FOvhg&&+A|N+R7#>Q;I?OCR`nUd7Vabb zKnxzNtAja}JO2u%UJroLo{r-=%TL)&uMwv4-3R><_L8(}Vm((lAzmEMA)IUNqwIytE#f(pYU_d9e7xCi!i5*~C4YEGs4JlBv`j$y_^^;w#mZA9_WB(AY>WM%i@eKFc`5n}S5 zYCTEOd{3%VxbvlP-fMX0)Q;fbsIaJ}*_}(B(|Kc)k6??}-HGQ*qZuFW8D{f3cwT3c z5Bs6+4B#;=^o%ys^EKjOEBT0VbS=`zvp+OBo&fF$ei)@q`Lt~`x#Pqfud@^;S)=~0 z*1l;-yR5nZa-R8zJtj(RPR4%8pO=WIC|gEFdH}1QHjVICX-?JoVQ5^YIeiiyg4Bw1 zM55yCL#a{Hl4L0f`>W|kYTT5TQoz&$vHx|7 zH862^s8;Fa=s?fA6zBI8sz^yGc}bJ&D$f1%Yin97-uZch;{4JZub(emIgUHcW51}K zru$QXWgG549Zztz=Rf1vusF+mDwd*Ek$U&VE6(LaRW+AuqmR#=e;?=HN%?=^7tVh! z=O613W4WUrRK4)FXwLLSQ>I6%nQlUvKIs*i&gM*i=hZT;Ak#<38JRxvG-vvYDmBx6 zbInHWRkw{)^Y01+fwaH`>~{6YPbGRVsT`5SXKdZ;6A^fP;B$7kWHt^ zCSI(AQ9XZZqQ!Sl{?t}|eZx>u&7}wXlgYl-cTFbtNN;tc?)L=o%w<)fMI4LN0g9vz zpY%(S-KizM#!_H#VkXS4J;6bo8}gZ-j)X~=g-fxd~MT5gyj+n{Q) z++?BP_#EmHcrzF#95_QKo!_=<{6^3e*K*te+b?E67Vwj_=lAMMfoI})=fs^CC4arm*qoEPWztW0e2jYi z{#?A)sb5mKQ~KK{aQDun8d}UhJzcN8FHLg5Iqwomb{jN;JCS}xdr22=b1TQ`uZHpF z63#ukfQi#rSJ|U9C0EyQIM!m_libF$el`ZnO^?T0{;69>0Mq>87!B<==4!S~+R(x3 zf<|C$s}<7pld7ZSo-yi!0~u5n)~)B@tUTrMvoG#2zSA<~zwj;L-)N8j@oDs#L7&<5 znMRKYK9+u$ zm%v=B`W#XGt6R%m~X!Y!S;aefuGN89S7)RxG&sfQBxTZf)-Skxh|*WRh|mOyq?aXzGNB#n1s z*bZL8wCb-XIeN$6jp&Qj=mm)W-F0g8_c{98PjK{~PZ`n2snH7&{S%5Fe5_bc`mm{5 z(qBOE?CYY0Gqq{AKOB~zVkpTSsZZL-gQJ5&RMbr>AsS`9vr~B5B1LHO6q1P@J=qMcRuHqa)HDa1M(G9_fW$W5ft^3vrd6<&3n` z!uA@O_;|9$vk54{Ybg^A-iX$MXX(ME2;PR^S8vkun5zeuA^1ZC$2Qf22kF7v5G*6u zISA%4S)*R`f4|s5=CcK|V~Y0kZGH(nVs6P;4UDtI#+ z)LCbBf;JZKZ(J{M(sZ1^fwRvE9OvFDhd6e}HkDdZ@Rjo-vZOi(D1NWvI8tI&i#a~; z>OJ>pyx7&t&VW>Fx5w~yi)8!=@LQKFNJCdjfsASNeTcqi(DyO=o=x9D`kqVQr|Ekk zeP5*SrS#3Rd2|tni(K(CwB$!cQRnDsNkdw$af;%TsQ8ZkZMPKlp}WCnhNAd980^30 zK|su7*7L56ub|LrD&_gMQc}wJ6$CgOC;v*FNhc@}FT90rtj|bJ(s@I=zAf3OZ2^zzWE5lJi5pZJ;pC9mE$z1M6vldbDDZh#!==B^_py? z%-QNSQD)8+*I7h6bfJ1p)I*o5*WF?A<%u6@X>0;gB?nIoMwMkw3w|vwW(4<&i`l^n zaWOaexwu#uTq`b?2A2;O6`dD+SG~#&&Kn%BH;Hfmq*~~ST6l1JA6T9*u{keD&bZQ=E5>j<>+R>G-JHU%9AfqSe0 zaK<#YKg{4~9u?qwA2EXu1GvQC)L8}yIH8|KV3GxX~!^fO`5 z*?>Nkz|b=YdNiO%0Qyc7^z{Pz)h!GSD`|!qI#q>k90olS(60#S*#!O9WrDsN(7z8d zpg(_*p^p_YG>os=X6R#&2ug*(2o|0wwDJ})Qr9RVS)YeLuTv;fZf_5+Fu@l4+Qvw053Fw4-(+})(dd9 zZ1c?Mxhi^A82Up%FSiTyTtff;BB4JF^qUynnskH*zcV+%ytnqKaPZlvcup2`7wegC z9oh@Tt-C%`-*v*ab?};1)y9(E#ZQ26m_15Q_}u$B;cg6MrsS4BB$B)6K{dI(k=$HJ zC)y3|e*=>H6q1`akdwRZJ5KJzJ)GQeMG?vEV@hr_ExEz5E32i{Q#=t|%jz2`{WXVE zdfS@tlwPZ*^uhybN%Z-{fGLEbRri z>Dcxl#_y@0sEO0Z@8^NtfK%q&u*t!XMaZ^Czk#vw*!^lMdLk7w5Hn~qQnBwmrNWO? zNOuZ_nMQY);qFYj%epeyK@Ea+G95v(8k81na3`1Y6*LNr0x7n{uN)R16{qdmUmUzL zP(NrBk!sP65JH@k#^BsDio(Yr*51S~#tBxj;d=NWh3))NHFeynTYUD71CjLCkn|2V zQT$oKI^Tv{F$0TVM}&Gd!;so~j*>bTc^jLlexn^Md(Ma9`KM=c`enFp9H z^`i8`coHyg0;Y8)o?GtyV7$f5JgE3x)_*Kwjp7Q#)?j4~%D-xq3O)$nOAvFk4lX;7 z7jbGfevP=Fg~fdzaeJB)a_|pI$iI=0w>&(jT0lh|T*24Ym+5)~?~6my6z6rLOh#$l z;*|jVC7!3PH&WLMPGB*%PVuaaH39t%R*xS0n!s;eA$f_-LGOxfS`32!CZOhv#BH+j8VT zBK#LoetD4p2>Fkc{|MoHHWNkZi73M(qTD2+h@~mp{m4bvh$tS;g}V3ORtr%QBBGq( zgs2Ar+49j8e|3jPd4ijwdKQxHB6XwSj)*8H)^HSY*pBVgrxayrM3h6HaujhQl&#$= zic%O6#kq>3tmi2FHc=00yHRdK+YLTLUG6V*xwdX>V*6I}-$8paNWFMSWpTKW!rCIu zqeRC(eh?k|hr7kL*@xl`?G!vs(6#45|4W8;y1bdiK02str=_ZFi#XQrXO4*x4C4g` zg`|vtD=N5Sw=p-g7yMS+P#RBxCLT18N?Pt&dCAs(2X&>r>V&GaC+^jhb~Z=1KDJj? z+BxX+BKVCP@zQo;T^Tc48 z8~m}K(Xu{3P;)gXFSxy*n3!Zq)320ZP4vaNe<_MKN{>b4wm@Ip&lsiq;cmNm+9>@3 z2U)XMv%&Lnsc=q2jSL^HMiwWNMAZ*gZcY1_Yt}-_muRS|Bh(RSBu1cah*@Mc8q*i2 zi1AjW<9b0BTE%3q{ZgOFUK>?B%ADjlxPM^EXPM`cM^(Or&g5-*#dT_VQs#IM*PoeTQ zfM{)W6qR(jx=vWM5j2T?u_f`ZDqFNg4Z9U##R&Ve4~GR`t2A6a(q2UokfnfpFgi*f zi{S66$NnBpzh$K$YSfqt=w0oj!Tua5+>4p4awMWymggeHQY0 zYksN>oD1fs%D|~*eyR+d%jKua;9Uu*3>kY6P#HL9&5z2EUAa|rEW)vsW%3K<8Ga$s zjV8y?Qj=q7VRgrl)W+m6>TGf(bvGPLS!r+*sh+1S2|sewA6fX3t-6o0*?p9;BDln* zd4J}+RM*feF4bA&cd729X)e{FG~T7Un4WN{PN#=e`O(3<)vKJ~AoVIc*k43j5xm`H zsPU(RROic3=W#AggHvoW4y=3Ft>O%Rs7~+AbHv|=iaFx`A*!O_THT3Sy~AQm?Z5n) z(02j-!!(f$06!yuFD$v*)P9bN?h8ZT4fK`f|3Oy(c`fET}{AJPnk0*X0=p%q`Yl1$sKIE6BShh<`i(Z9&nE=?;gzrIojp39E z5ALX4Ik4d+)_%74&ow>T2`p81M=TG9wV@Yq0`-m7W2vQ!HjfFuiiQUa&KEndjB|Z; zx%OM1Q7#vhd}k+bB{zRDEC2cp)|3;+xf}IPCHD=St@~xe#W&Iea!afpZk=MU&l~NX zC#{KTmltkS8&x`)-s75YO;^Z4SJb4QZ>ZqJPKvDlTD*Lw{7Y|o4Tlow50xYC|Z6JS?vVY<<6ve$$pQY z+epP-A&_!@W`9ZeKIKlQc`-k^uZrz|2Et__lP^cmp$}H?w%}vjKIP5UG?`e89oa*# z5Ah9AzBM$|j1HVoXa^7qX?xgE$7qks5^n8Y6N>GYnZwABj22_1lpkjNnJ7OfeQrYg zlYg9J?65bxhHwQ-HK;%J#*e;liY<;_sgup^=#Cb#mG@6KWsFH+^RCn}$-{xAfTkF} zt*C`=SU~`mCmv_hH@o^I`cMhP@Pm ziz3*u*5%{iAuw&@si!FFH}y%c@lHC|1H?e1dh!tpD1Y$MigUjrjdxN|NKBOQ*_hec z)5>znagFHHLB{X(IaU!U0d1zaH7pVp`u`H|1NXKr5eDtrF-ONK;0FW2kb9G z4?53-LI}UWK^rk<%+*sP}?mgbxElr50T8w-}ecnP9yK*CEjor4iGzxeEzxr39 zOryiy`#eKm3>dcOpZvHBue$5|eUbOXw#F>hBkHmN^p z=rpt%g+fp_=lI5iMAlXV8{LyWkzuv}Ne?aUJ`40^3R|yF4zM!(`c^}o{EXhozBrqoG|{pdu(K=+|@&%`+cr`LE$Rj`1Z*|21Cg>{rKsTApyZ zSWl#JZIO2QW2tb?2iMbj?5%sjX};!&ABm~Ob^Er*S*&>#-YG*a*CBr_7d~H(624k7 zF7ULC*}a6y(paM;ay+_VeV)CJZ+};Jmk@PSx%?;2`wL_EV0YD*&dd4#DarVFLH~pO z0Zzrn@_YGp%X~-$bvkFwD`3~87(M`ysiWP&+WJK54~Oh;P7j|UluCX-5fm9xFdF5v z3TuV$Mtv>bt`XDH=;}!Y=tL_>zW;c+<5>DR`vVDQh0VR%28>(5n$qG6#fJh=!R)hq zz3!@$&6KjQ{>nC?EFEsDld?et860gWnxpYWgx42s%i*E~FV>wa+ik5slsFZjllS(h zqVCW}iGa3Z*6%{^DTi%6%DuUBe!^DK!2L|J>FFOA25_K9Jzy%z{YKFfwL`9y#+Gu8 zLuF*Uh_dpeP;{q+@3zSETHj3XZPtX+kLZK1qD|M}jD5d*0(J8Y1|59UE%)`Rq&gn;7jTo0<8V?Xvb_tiy6L zRVA>|)Xdi2+=0|nb*h#zJ01w(E#ixP?~dCLYd+bO-e)pIWBODy4Sp-&0a0M@YE;^a z#Cod{vVcXh&b4=zxsDEw-c><>Q-;N^VjuseITa_I`6Qq@u|2>jb^}pmEVB|5aq>4O z{#ktZCC5b?zcC+~MPiXjOgTSZ?{IQhXoJR7oRF1DP68Oku;5F>ABFt&JJ#V#_2^MV z!nW=-ao?36u=6aBljxh zjs^{SUldt8egTq-_tlB-Ezc7Tblv!#$M?Q-VNUowr^0)ErLmX4OkztPg`f<&Z}EAv#4lP4;GK^+qqwr7fl;;wF`l=$PwgFmq&x2Q z)b@VvXQ$jbVv|C1?Fdj$)|;HxZ?pmMUR4<)cUCJksf3ME2TeD}RFOj~(iACHo=O^g z*4shrxC12pG!~2!&9PR)Ueq&787_S@OMv}^@Q;CdEV5d;@kg(Dv=$3_4PrDJV*vKb z?uvTD_>sj)EQJJSIIRO0j%h{|$E$NY*50&) zywPZrmC3w@)!Cxy7bm<Wjc(pcYgA5^A`^47>+hm%huEW1OLa>g@x>d$s>5#8u6O@ZC5sO+r`Al42j)#(KC z07RQa=el&C;nOSZ9)Kj3r6+~$WF$7{Nvtm(ya2@tI~w<10Zy&kS)LEp3brGLcj%(5 zU6*JX(Dxo@?adqGRrSw9on_oE=yJwu6+>cTrAWVZ7`Ob~yIAUU;bgjbWMLN@9`*Of zu--H+BuO8h3Kqem;>m7suw7Q{yTg64rzTmMfT@S>id0kipd9DNK6uu`IiIYf3EGAD zUO$5b*!*n?HwL1u|4a07xJ)dLqLdnv47MuKfQT77icwiy!iR>l9-TO4jay9)w{Fmg z87w(*Md@TI1ELy0k&MKV&8!m(UmywSen2%P=)QAYI847W5F?VKaUCn|>MjCYApZop zH6~yw?l^k&lANYx*=^D6C7LO9kt1^`WJ*)Z?HI{KoPO+$5By_a(;B^j*9nVPVNg@g z*3mzw#g=m7&uF7o!3HrMra;m?cPrqOlKakC>agC|(1q8-&iweuO`2#>BjIuaI94ay z0Z#sgu!-!n~wRlMmZyyQ09t5_B znj~dVB8M%txy-t5D}iN>Z)SAVquHtzD}sphM)tSEyDJ^0}=J~&tHSY z6tK~|-1yjtOi)4m!DY$~yi=`uS4j<#YQ#g!g+7qjJ7!r9$HN2zT@HMHYvYA;mKZE) z_;EcC-d1vW3s7sO@L*E>U5&7Tu4jwM`5*HvRGZc1i>Z3XPljbVsjyJ3>}dcg-QH67 zW|m&i?JQGMWDiSSNpA?v)+a_8>q+NRMaz;WS?e1#(>L8$857V#SgwD(+7osE*mi-p zp0az#Ks4#?pm$V^J9rL?byGf^EQP~%>a|IXW?bHk@c}Z#D(6%*57y0h7+(R5D%W;M zOOuKPIOfDnQajoW6~ZRc1WEpuB_3IN<;-F5-jfx&D9@}&x+pkEPxsaE(Do%fv7Qq@ z4cPK`ucGp+1EH$f6^!NeyWhRK4ViS~x|O6~lS-~qgubT6qvm_@?99Z17!Le_LXQ3`gojaw?W~U~-QhW(Z2f5YPOq*om_h_~P-RGI> zLWf??9U)B?3Wil#h}GfoHXO&xFAdH^1+v%U(C-rbTM)}yi)Y=rf8gyaq% zjsL*TTk7_L#=p}J8oHm_B*3O0NsI%cEDX*~MQE9hD ztC&YqdfmB52v96Dex&Fc$;;sUT$?hbNRf)V6PpDbE#mB)vofcvEes1`Z3d7sX0i|s zeUCbJJ&%KQH!JcIp4Z^Ks|KRiCJjgHG-d#{Y~8<*^M^CHHV?j2Ho9^Ij4_RNZp#h>R7Osht^p5m#lXtQ%ZvjTmEYHgfbe;K*nP`SG`SI)t zWDcw?ZPX0|T~j-hpSE#UHDHU{{TX}Rbmmo3-%SKLh5cA-GebMd&~*t26uq7;c9M>b zrHTsnlqp?(OiZhJx+7`^DM=1suCwj!xSbWNZ5(@^G%Tiyb(^M*NzfOQ8+(fCC{Nv71|q|iLC;}mV;q)QfiX-&4?7~WbCANZT5S)8!-Ac4%AH8e5r9&1n{ zi`|R0`4Q!l!a!7T7ooSOY2+2-U1?t!x4saI&~?mey@Zb~=Eir< znr1Bm>geNEX{LtUu92*{)QEZoapb)S?WkziGG(C5D{QDi2^%qCT7}fZTnJ05fqax0 z&O9_~rL~u74B*5ob=6uuB>tu&4R!SDGdNn}FHdnr@nTIGh!Jpbm?It*+sQzrk89?4 zl#FXW0p%S1aD6agDe9NCSk64h8c$B3BVDDaKn!f4>~GOx78_2yl8uoUFJDl?o)RDa z3WTZ`%1YXcCnvRNlYCv5Y#4s1WCuE%;wubv&9fqT@nUbU&*54)@fGpm!j9I|wHs79 zfm7kp!a39G*gy(D81%;Wg7z3gl(CsQg)lqa91U$n>|UxA`vItKMiW!SQU`&94N;*a z*DU(k4ZLwj?>&Yes@bt0^0C$NZwV+rc>%>wMfXn20x^SP+tI$@IXiw+K+z4HtfDlhuRf$Do zK2rSZ;~oQ1dg77(xu2oT1x!0<=$$-kh2*eu*yTEbUMDt%R#4 zfbVI*oM>S$ynFG^Z?2K{YiUVb+4$Ib6GxujS(>_dIQkHZfc4$X>iO<;dGQ@{u3=eW zDmf<0Ar6KH6#z+P(r~in76Z|zmx;ztx$$u4A(5`*_v=r14SqZzJpPQsv1uA~5h6V` zZV;mtKOAZKkoXq`Y}Y`7=^TT@8)Mk&{)3}^N=(jobwF<4%r7rS*2)BN)6`qy&kQK} zNe(Tk7!I2Xo0wE;qAa^a9sT3%XzL8~EK91KRWwwhgc4wHl{nzNJ@Tx?a_jdoY$0i& zMV+MK9(zP;YBoMRPMUg4xN(;kf9+{P!o~gIm>+j9>;16eq9i8~GDJ2|4+9ZKpTo|H zU;i=m;oMii;9R271R%fZk2wio&k39y4tzw+pd-z9=(7Ox_pV|28hTH(=2@pM;oYnr z5tF_fy|+%2Y6bYwH9qO>aLcmhucly$3SYWVmJ>T|$JJ<9_AwPLjuinN{VIf2f~3BROV8(nbLcVwUgAh&f9L&ZQ|zV1)qk z4N6nv@ds_7MRhaDMa|OESnF@YvA@e1|0e5ObCD)i9rfr*!a_NE64*Rt+V&ervcG#X zb-s{eb)*UjQ>{+?RS5n9ign@Rr-~ns6vRiy&jE-=1`u_d<6o2N`3^`4tTUAb&D0%t zN&(K{r(+h2z^MD7#pJ(=MZG}`WWB)!mBtrRv$Jej`V5Ictu`RT?k)#7k*Y6&K9XH2 z!I@zeJ!PG7 zWujz^#f=@6M-T*}N~>kb>92=@cc5iVVp6n*AkAmyNso<=?8 z*m3G2kYFwH8<+6}S#$~0g$2{2n?ZjnnIesk6%yCi2x0))xk&aD)TnzRU%Uegnzzqy z+pMIO{bRrKxbgAiUlnizw?cebJg=_1NJFV4%=YH_nQ}^X5q7?$pJlOFta_XaccO)! z(*oaw_;?%P`mVt0xSzx=Eqe2udni(Rpg}1^I0BWQV6$!)Cxi*Iy41`Jc`$)487Uk^ zXfgEU-A?Yb=+om+o5VPSz%)3WIXReREYo=7%l`dB+$oU}l~2$HqNIdHCkNO9Z{rm0 zpf9Em)vY>rn=g(06<^j`B7^y9(-5IIe9-W;~+KSIP6SO@nUd}$(> zr#o8xp9F=J=n&{1l5n?9Jtu?*a?!iRh+Y$|CR_@+5v#(C8~=tSaNi}cz}cLjN{Kq0 zF(BO}Yv$Fc&Tt!4u7GHaY$hiDV+$-O=A$JSd7i(&F{5FpQzo1w)>wet9 z`C7i%ZU4mSBnNS*)>8AbY!WKVxh+za<6JzCJxxl&Az)&nwcKTOm} zc5r~MCQNroa(7o(h@3yv#`IL>;(k_@Q2_zX1m}@pisgJOHpj#TwBBN$BFZOuaa0a1 z(^t!lCWaiFw3o2IA^dj*R?M9nzcloK2O@-f$hi>L|1tP@t$U6bDu*EO5}fE!HH6ua zmzm?yv$&o&;^0B?{(WFI&efG6KEcufb!oV1lw}ziYKuw zO4GWE-zrP0Hil{>rUXI7*-D%8>F2(1n?N5!A0|2IL5)Q#38rT|ghcL=TS*1EaB2=q z?YEfzf+dOxV(oX1$3(ez9s6*KqXdVai3MV46HF~Q<#M*^yPRqe%sfOuRF1YnobUtV z%Are#6VXHiQ}~+3Ea$8P5|0uc7||3WzG#`uBJYyGuMDVOs{U?!TF$I>~tl**+*2BaU(n(v=qJAjWy$p*HYV#TOzo{cL54M>cLW&*w=}{0IYjn zaHseS0d+rVS{>@2;6Rl^kG1n(@Jdv^b*10>Rv#O~H*efy-erma3X&hP#m?pQyGg5K zQhK+9(1-n7+cD=|&hk27k)+3WaMX8$sNhMbc2L?xF{9&ZH|`j;yjZF8k;#Etf9zvU z$nPAA_t0=nw5^T~ESMit@N|Um@otbH+|udzu;(BA09t6el)bJ|++G)gkWIJ(K-CgD z(3u#w2ugWATc|ycs1)tR%|=)$(|9sri{au=4rV+Ix4$z5{7@>e%Y@=3Jf%XJ2*Q@VP`GeOaPR9h&zd7Zao-A5CxsE!|M%{O^Gahe_aFm5{M1~3{EqU+wTogf$Mj9}q|EZp9=U+_m9_ogtm5@;<9U(A1mUEu}nY}FgJeuus-AbPfc zYb1baT--F;jtSL(F{~<+1P(>k5>h|@WBo!+vcC?~_(Rqp}U#H#A!pt z+nQ{XG>neHiR(B*slO~awOP+?H6W2M$-xw=m@wT|%-!wSu5rHw{-471rh^| zDO8y`f>SOCJx&IiKz*R~@zbJEs|5p`=j;ql!&HJC0TU>>!z3Z@{U#}}B>A5JQaom; zW@N> zTaHcI#Ly+ZTN3C;PpS#FiaUf;NsjZcR>{$3eGeI&{BR%N{9~r0?rK6KM|aP>_D{DT zEP6BWce`Yr$|Y%_NHd|&_8&Ir7j2IJ+H)@q2nJPsA>_youO7o^5zhwd10}qE^Mer$!=ES=nRa!5IQ6XDiihfYbJ*NEg|o2 z763l}%I>)2s*9c}g)5J61Q%vavX_e_m>GqE+%K?yn;H$7!Vc^d)Ny|@9yDO~}Va{owNQdR^DU;vykXX;5P zKNH+DlOX5~RkDE!&W;|!inB}X7aK;5-eMBlk?3zVH}GUw_QiQ#{s#^UQ9{@*yj*8H zHa!ws6C7kIS%lw_2~(h)X{UVBkOR)RvP==^sZm2fas7*Qq?^rg{u|gCOd^H;6>c2K zgeoL#vZ3w@$GlAt(T%x^xjYD+!F5(q5{X?Ws6*n>O$t#ekK&Z;T{jhRm*mmR=$3vz z4)me_(pkH3kSKSFV^RSUF3yqYe+L&i^e-zA>!~ETS{CnH$fE&7ufOXf^6&u9uj1t%(82R837rhh*JGRApi)^J@9Jr6M z!-W33jX*fJ6O=Jg*%%thraX?j{3z)7qnimd7P=ZgtqnCsRS=f6BcSF?*7cZL^FBI; zwFX<<*3dt{F6vej3=yOw-zl}4G=X!)R031nG$nd?f>8+SdMp*@;#{u3qH0Nu;hQf` zfI~E%&@Bu}>YuMO#82xX2>%f_Sx|-q?bdu$=?KE|Dbx5{jGuWQCj)Q29q#+!zo}YJ zM~-YH$y7?PNt+1ldA;75SsQ%Jxsw_7H++A2@KSAf`Sv>^p8Po>`Mf^ zVwB1sTneWy6U1+cl1Dg9-8rs%8Uf{HI6AiYwX!rz;{h`>8W3?yZurHC6RrY&DD4jusBAFyhf$OoGu;?e;K7D9w>}f!v zb=-6)v@2oy%OtvFy0Jg>E;N8=!I4Q<*82v^ zxsZF3q2bBBnEA!@keEH{W+y3tj2t0Jg4Yqf+dl7HF zxFW4NGVpqY^gDQaum9EeBV7Xhg5ApL&qPz@J|D?L(=pegQ1qBylea-&0BqU74tehV9{gb3xAvYTc)2owvmRYa_ z{9$-1CO|saIOXVk<{)G} zSIr-N4-t#XvS7M5befrtY?^#0Rb#O^Z30^d1u&!Ae?h^EL<9se*5L3E7iTd43q?t%Rm*CT=aQ(B(3c55 zTrl2deO&sqQT=Iza>TIUtRRBr!-=t_VX-L!SrPtL>ZQflgR2Uv=7$7#1{^YMW}8LB zeEIvM$6l9}C^}M&Ju)#v3*P&dX5njhNm?3#MCQHq0u_W&H}uQ;VA~|?{=gfeW2gC* z=fb(#ym<2`qAKvH4)ik|(r{xQVKQv8J0InuLNC8CElOLv4GLu{rAG>do>i2A+J7`!)VQ|4@m=%(K)Gj3-q1uc_twv{ zH421pe!WxSB}rekhMW!Z@yN~f;1g5wu{X};)-bQJ!|gB^u+&{KM+!;%*Agk#40_z^ z>U#K>nf)Eo1u2Bk^~=?4n}~4<5&boPU@{C@*gXjy9xp2QLfa7QWCGpVDkV%@bt@kGGaJWU`-D$ar*(anNvdlXtdFNQ!e}IaPwKw2QOR~VJ$3oUlYskj zFe8nCUZuDn7>)dHVrI3FgI+~?Y;-i6q*~wq7cN;KLfyP0G|K{fMJ_(Z$1~Rp7ZbdW zygwssf7Sea`OAMA1qy~el^W(Up*@vr$mJ^I;nrBs0xzX66mbG@o5(M+zk4f`E*G@9 z3+!AqhY+)_V5R4;C6qqO1@5zSOBFn!w}`Cp;{13VJj|!2-76b~MI5dp^JY}=SIyrL z^TnTAyeZ(8ZZH}Ciu`B%`Kr-yg5MylRrp_AbPvDVn3zQ^SVt}+tE7#F7fs9@87XR; zMUTSi1f>nY%z{|yg!g~Ey>pw^=RZ*Z+t*ayXhVco^KiA{ndlMsBvahMQ0dVrhBH7bNoWNsF5d_b&fa z$!wz_#KcT*w;;hKgvRhnz0HT>lzVCgb4oJi-7cxFA?5rORq;X{yDw~rGqV(*x;A;g zvI#*thm~5GRCv9x`Icbudg^H+!qlMxFC7@U7&2U5UjAuOw(w4FtFp@nsx#ylSpmN< zZZ7XCX{H9-Ow0lxLi6ojyfVNeC81w@B^=ODGuJ0eM*`B6o3~1fEKZoA>#&MS^;T?( zMZg2=Z;P^8W6{XhF3TBKcb-+?0V}+CWDUa=?_6$t8PbKw;VfrefmhfwBezxZ93Cnw z4Ja`#b#>_#sP)!FU}WF*);e(%gi+3x`Q)lKwkW%((4JOgn+$^%#FB!{_)T0bDk}9d zJ8rvNf)6+j%Dx*uqi~Q)nN+jQ@|ZNBy;98{Y0XDL#7dqQuz;Lk;-*5W#7zOm&s3g> zG@B2J4l_P3--HZu5Xd!QwQ)h^px;k&n;sQZxu^&NJ{D1&L%-&vRG94JZC~NVVBOjh zGVJ#R>ynP#LW`#@A)EN`Mk;+$3SJQp&$77Wer;FXe8G9$tQj%xFRj1yzDPwZFtZtPQ8>)pXq z{Db0GXYXl_^sFX#V>jWQPaT%dtM>^*zK3jl;CT#t!k_teG^lB8cE0b?!YO)EXY)*P$lJntFOai%rSJoH z#`wM;3S!2Q=Q@Laq7+*?EubME=S$LR(=zF&R;Ju9GFp9L41eqLmGt7p)WG&yt8d0y zYfRi)wACjm%`4;PXjs!+BYU~n@~^<2h&NJuPJYEDe@)Bw^wDJ#0-^aA1AB#G-Oc9$ zm_4-^_p_YE%5}8SMcn|ZJ5-eC;z|f|!J>7jX1_n$M#JfXus+?b@ZtPt>SD4=(hU=W zbEgTRhY;|IbF7=pj!p=Me*|0r_MX08dxE*>)sbG;0{j-sxLk~O^-V19>jV{B!3jjv zou1yt{c^+a;lz-4mm;LLi#~cxy?ohA&`0NOS-NY6M*`y2-d*p4DQGmDqK_IEet&~X zL%!UXr>S~iXT10#`_5a4<>?j9C)+AUfUZ=xvLmO3GC|jxG59 zsemYtxbM9uauTM-&t;?2iUStW=&*#HY)Y-@&T!1tEuMbVb2d)JE0Z=X*-HEpvDwX% zmCswd-KSO`RCE0DNnHKGbNgpf&R4vm2Yb<+)irP8g6;H~QB7tsK!KLL7nK^Gg9R}j%DqobqwBN(_;u`19=-|b3jtk8VORK_pNu!k! zo{z0+Vj+IY07Dcl+87mOL@9n)SMo`S4b7z>UY}jEm+oCyFn>F_QptkpJ^rq--2HMX(<(fbl)!!Bt1+;4{yiizz+A=E zyj(m=D_*Ml{@tdxxw&_d`FBG&kq%$p z5mMA4_q$f~yP-`|su3NmQR>NH(`QTkdzZx@&m&W#6G1A9UDP~Cyh5si1+u)Vspb>5dEk#^3i*tlI`07pg;fBN5T64YUQ~+Q^3X>f0+;8P8S@SnjAPLa zb&puL=3)Lp{uv8j*1zP1C)7voTVdl@_LZhyeWn9|=IR;di&k!>fA?B<9+VjqT*vj{`E)yHlA^>$cNPC9;G^k-mea63_1cM_ zUpG`5qkfmaS+96t%X@u4^Ny{_{kyTX6VY4u5i!%B0{-4>Kreps4Wm)h_BXAaSp0Ce zg8jQ|e1^EDs(t7EI=OH=&Z7^-WoNI`UO9JGOv#Dj`hHf_R8+oUjcRC*tE0wdrrf|M zqa4J2w+2A%apSJ5a#6ZydkOs?rjM%IH75%?FVe3Ejd7+#*^h<7byE)BMRoSSf3wEQ zK2%iXpd;+(3t4W|RUZ#`y!}O0bn4BT8KvRnzzs^nIW9|AJvQXlt0FhON~=J2mx8 z`WnMa_jthZ6oP$-**3R!9YH0QoQ!pr!up!!d4jg z9na8vT5oZghJ<2cq-72P3B|qY|MLpSU89 zV&p5Qt*-t7-aShQ`#xo(?fm=L?sXk;`mlv_U$zHyyqdgt>DWqIuhp#Xbzy0)m>>1> zzOO@Z7g3+c zXapQuiUu^ZVa;L^zwgD}xZuknOL)!Qa8huJb+KAP43@7=nXH%tqAMnH?**6&Itn40 zG;?FW4bFN?DVYc%?7}LFE2IM+nth*vhS!XHDvZKx9)BhL*YA_iew97Y;8LslZFd;- z)4=*2m&a{s;Fog3=|b8QpPFrce79nX?!1>C^pS1$tbRH-va8q7?2U3Oi!z3AbhVkb zYsQfu0)Mdb*72FBh@Onhc1QEvEK`)1?o(44mP2-1BT4rlS~|H3ZvvPLv24iO^S%{5 zh^BLDTmO)17UXtroG_Bm#$+^~y*1O7c>zuHlLLnh_A$Zt`gl0 z8Z8(OyCKG?5LRB&c=6m!X8pvB*4j<1{sZaq<+{N=JgoA>l)&LyJcE3E zBb4Kq-44+;$)R`239-JoQ!#irY=yXS!_x1GxoYeL0AG+nOq_2O&(*uuQ|wku^m|Jd z#VR9U$_(X6?x-VQ-zQet)(T^zr{4TZgrhg}^oN``m}U%1-6AqK6u4cLu86!&wr%>g zsqj4F6j{Qi20!~~FbFUNU+xz%{j(^S5(A0l3F<3RH_ek$O zxt8}ohiX2B5XNa{Kgay#J@fpo%upwjmA6l+v@{T(snUo%tq6qgZ0cv2-SBNph z@vl0>xWqEtjT*=GlY72^`8F4Q?a%S?6OTWugUiuv1ugQi`}N1D7iG@#9eh`@8Yl8Z zMlHpVg%;OQ_a)OgK+Mrw2?neGaMQSbeL(0Lb%mfO{Ia%5Pq zG8?%rUF>sYo(9W*8K~l4y|J#YxYAKhX1&4;>!Y(Y$tCR7Pxksx&J4Wu0Yz7%60?lBms=(>J0zT~H(!LVwY53fg%7kmc|SOC z)MWBY^!dsumK4^_`Til@+u%P!k-X|Y-hb!q8G=mxe^4=i)y7zsgn?r%7jMU8gOJ)4 zwiy@xE}R*XSQ#x~M7uGFa7 zp9~7}p}sr%_b1Q#E*J-6MJa0&&#c$gJx&ukMz)4%SYm3j-htZ65m@@qWs98Pt5LJo zxW%^!pDP6Ut1)Bxm_^$H!mFYYmH(12r2G`fLXlYSro_onTU7b*We`z^gjk)r)3L~-~B%l$c=y~rAEipnHz>NQT zXO{6|^hw`<<~Jk1%bdVzBVE^~ZY2KmJ<`*rfaL^hS-w(Q5yXr5aCqlE>GWf@E zrGMAWzv3Tdi&91#xk9iCS>l}>#P^xASID-uiQ6W(6q$mB#+*^g#onK)#~!60-{YBl zezC;mw`F&|YNDm?`MlZXx12h4nS#5VVQhof^nQs&jI>$@0S-k9Slj-fcET8L8#mE` zSvgoW)9EZ1ZnMsO+W+r(z}w!Y_kmhjcB!vEZibbA`KsVoy7$qX(Sq+!pVrdB{@a(! zh>AM@8W&~{h!M_Vy`@+@hIwZlI7vk>7{tb^*@XZP9sA1r-$`FtpO}$1)zFRQffzIh zyIygYH*G*3etw^bp1WJnWS>E!xtP=WUvr4pT8_)2#n;c-^hfbcbr0%086FqQh*2X>%G3zQt7PadHn5vdZ?M{mP6Bqe24{ufxh*o8HIx0Xs>J(23jq ze=cd9i5yKU{hT=5tNC*9yd|~^Z=QC(wv*xHQ-IyPtah>IQ|$cBe%8GM=!0rnb}Jbf zajsrIemyY?0*LDubtnJ+T;j{+f^k#$a?z+3>u)2!W2U>9^Gi@s{KE4y4*c0r=H$7s zigm+`cp&H9|EgzIgd3D@E{J=HCbdb|>w9*8yXxel{8(iW_A118v|zjpLE>0H_Ok!g zG|#=8@B#+M@7>a!GGvYB3&4Ize;;Yvu0T#?((65~ztu*9iX}Wb+ncToe7_uTE zv)S_Rp_&$EM=sp4r8-;fHp5E&yj?W1;)dw5?c#M9?DQ|Px{@v@+{_x#rmz8Zri|e>`G>2Dk)n_A@V)IMW z;&aEVPLnU$q>wI!y_4OqToNW0-?#K0DbH3F+BkIxu@7+6bx#c>{FtH@)Rx3rWM zqbgF$HHjD?o*(ol{v=CjW6lKO*-C<>6mRW@Lmx? z^*{zzAWV1Og=ErG30HtkOAVGs$z_a4st$0z&*N}zW)`7(n!gO_Jv<+TJQe$_S`Ow| zl~ezGx2%r!bIH~uQlbBG;F#ulD?tDCkg#@(Lie9Lf-5A`1fl}zQAgM%3UIF3)R z``&RNKe2ZGBmZ%N$HDw zkec(gJ6`WMRF$NzYp)muPYTH2{m1r~1pGS{D29!QxA1xFhnENcd8e6~<}{h! z?VL*dAS;$mTomD1O<;oZ)5{8>>|FcS!-aS5o&~Oye~a0Dp>X;mR9Wi#)x^=gwA9gC&1W z0NOk@tZ$KEdjMu0L6;~IA>yGyxxHU!GA?m0&iviQ*EpCx0Bi>cv8etMB>4TdI*3QG zk(RaXBT}%j(3<0mq5&&^HrBhh2vb8{ku%`iTL?4(L_AM8#8x~<=+{+GQN?N(uiV-= zNTCb+)z8Y1p2fPMP<)8_eam?=&iA1aKw={9?_8Xx);sEfD28X(rsJ!V?g7{?BPQdF z9sw5843DrQ&u_e#>20Z@ULhskuC9#r?oDP0(4W!!K&KQt&IP$-9?Pt?Vb0 zuRbIoF2P}$=!2m1`YydySKLhF^&0A--%k(~Pmy|RO9OgyakiZg z0VTza#Y%neJcK6W@~VQ^1q2)2wbb3!_B`y9?*cxz3$l7U+)?ix*Rco4bO^9+B+W!W z??TMj*4qK*+NcpO6d$?R11{Cjc@^A_#Hp1x`}VFP%c+mP#tDtQrLO*93-FC7qm#P* zQ1pWPYep>ELmf0b>D`;ZB5^40QQUMfEqEvCHbE0JPnpO5C+fW8sea?XUwm*5C(bdC zQI2)2gM$*8CnKADl_TOH*^vgB`N$p_IS7#v$~aaCaU7d6QiN>DNLKc^Pru)NKkoDA z`QyCLb-l-RJ+Id_j2r6Do;2K{9WyB8^11@g<$-gZ^NpgP>?Trdw>`BG;G=>Br-FC9lH3-eGbJtpGG zl9O)0rRok5V2ms0oR#a8J=>ZkBs=If#fL(#G>*A(xu>JkV(Q!I<(`{`Z^HqYnnHoK=zbmzBlfcHv=Yd*bdrwh;6 z8-C&rMf7b5gEYS{fkSBW>%6oa3Kk0cB8$s^7jPnkcz6T&lHbA$a!GNJv+O~XI7C7o z$A})CFGpy(UxrAuz||Y$^_eL9Qc=^nsYdG<8OSToWI!(;r3z$S1Ye@M&VZURrB?j* zd?Sx}qMX_*p4{O?-ig35%Xz(9GrqQ0$Nx_8$LTv(E`6LaM6DJ@=Hq!2|Ej%okY}za zS2(Lcm`nH?eyA@_m?=h2&rC#ITBKeey@Rz-RPPp7LQ;d(*FS3c!Ysm4w?=OxetPNY z3I*T3;CEV(WDRHNN=z5%X3|^Ze7hUOK|U+u=eaHT-G8eOA$DDF(k zz~~hU8>Gyqy?7BK%x2(+Ms@$qH5QrG5^Q1?8$aDB3+zP_ZMq_{Xk_XVPL ze*4_In3xFkg>dd%v5+O?%U+43E_BGrRh!rz-#v`x(R0$_C=ml{S zla}-=t@M{QWFYY8H`6a*z-OcKasQYgb4-NHfw7w@DB$(Ujaojr@RWXy()H_K`qE8TZ)RVr~ zD^f#KW0oXqHUv**mg%AEy`Z788jR;6|2w z3vYh18+1`QeBr*xlY0I_E)+C8-<5QW_v244H%+#Zb1{p9#&4Yh#`JEp3_mvOzRV-p zL4&**ZpVuZayL{KAKBY6e#?q5zYb&-7mNr7`a?qbGBuN+p zW!*zaFw(0E+QSx-E$A#mrzxN~po=i4A!FhBcfrLntprScsvyMH6Q2Kc#u*tOYR7{9 zB_k?%7_fBAvLZ~*)3w5$1-*I5-x@)JAmnG?%&t0?T&;GCUsw)$x5Y~u<}|R=EP!~X z4-RRW-b7RrK})!nf;qsA!}0>+Y14Wtf^}9#LTPoM>$pFOCIvTEr~M4hoRTWeYQ8MF zxm$XpnE}!H?dVHE0sO__X5lpC;F_5iFBrTm z4mK&#kMg6mzc5wW*D;Q0|Vmyl{jd5QTJ(E%xBrx-c>kr9B|pfLaGa zl-VZsjDWDe$7F0WiVn6y!HU4F}jH{J=sVcD&Jf*nBUry8|AQXeX*aEP26@Y)ilan;G*RfToU(8^6a@6#^k6;?Yb|rKT9U=)+ZY8P1+~jdVR2ylW zKuoGG*gNh{)_}-412_~C08b1}v!IhVM-HYJSVIZyFbYADG2F?Ol{}ZNfV(BATMk*) zCnF&zWyO%(0Dz2#2f&0thp9+Z)JLafchDt zIq)J6&IWGCfa6CO^#Qz0&OkpWFF>*Lq&-9eN4rCQgSU=|b-4_juk{0qxs9M&mOSW$}3O~A_;SVQ3% zzzc?^AyDwIlK>cE&>|j;sD4{1>30qZ03C|#n9Jm|einL_&U~N+tzzH{Nu7;KB5_DS8|~ z@*X&&iClYU3^>i}Zll1q=$u@3m2gza2f;2DP!#2>@@ndIBMKt7JeL!txBfT}+X$gt z#$rAJmxD(EHa==pYOwMpfVFVpVF>3N%I#2;GX>C2n|`biH$6p1k7Gdwfipfx9djY@ zaAWoOYJi}Z1+b7mEeg!N6Q^N;AJLGH?`x5M3n5B83lJACLTx}8wiEo}(-~jn&+wUV5?bp!QAX>^+)byt2=-*K z5q@+-8^LEBXTX7~QwyI8-hKtqSxSBBnhrc4fl|(4G0c=BL()Nw5%vWnygvm{&d(7B znk6Jb5G6(j>KG=3LanzSI2SJR0Ck=cn<_9#vX_ISpqONB&r~uvy~w9$s>}FR1FI#3 z&(w#T(~(mKwMfh75idqkubxYuKDlMLcQ(AnE-eE}lc_+~5JZ+h-s|Gvz?6hI*oG=% z0{sjVQlOh}h9Jg$vXEP?*tiU=J0qngB@(5@8n8o89{HYx%4WY9L3pZzRV+X`p9Pun zA_?EE1jT;n2o<@qPS}LrJ`YoF!78(y2dS^Xfh?S922@Tq zM8533(-paKlrtl>XIE({DIEJgpElE$nz&R?or%I-B6P3e+g~9(Avyez&$08n&Bxb( z2d>V6rh~$DA9*CXU=b876Ps{y;6M!IKn%QOhNS_0f}*FL8F5%v;H+UnJoNFh6PCG8 z4#GqBqJ@c)y^8q}h6xYyFY8e=`i*cGnl4%3S22Q}9;XP&y#^;AUDoni3e_?FEL>N~ zNHwt7f)!#52y$e5C>=~jKaG>MPc2^gi? zaQ*nT(LfaBgBU1cM&bY$7dXfRcR&I!L_!wljo!1g5ZZGJ$C>hmI}+-+!nb#z|Jqi zI3Dpj8k|+!bX_f~ju8x`M52yF5CJ=b5XN+1us3Xm2RRvLSLxlM2oV4kq6uZ}6A{xm zOKnqjfJMXfC6rYb*HFttCc4n$E+Joq&oCj4C41*IKC`U(J)J%=*PBiNE#TEd5TZX6 z96?1Q^#L~9_4^L2aj0xPuZQW_BbM-NyR>IeKV9&r)_@tJ?*&*6pAgT3G~(9J0qyCi zvuu0ELl6bSOArY%_6dz0(kB#~X&qiJ-&5cvFVJ-A6D_POL01O9_yrO!Nr zVt;sWDx6d})rlW(}D(_U{O!>lP?2uj1ygCuzc7$)E;g*%|28Fl7SV7&Z_4(@|xbLR|#bRcm2b)5QHM8dz{D z3e?&405t@QN+CK1I4s~~AO<2X)crXFd{3Y5WfL47!wc;x_o2jK6nm^YE#Nx~)YhYQ&$BExCZQGbmxrt2gjxuW`@ zZSXkw8g4!UF|MEcNDIgOHUXN_r-!-XTI^V-KNff0!16Ir`r9K>hqM_vj0p4p0L^>? z4bT}a4kn?}AYiQF`jpTs{^3E$UE_-oZ_2tb%&7(Y0&3G;v*D!z=gZ1jW3VDH-Nl(& z3gNTP?4Ey0uu~WOb@D6tGfUD-z|C%tCp^Y(@1q#P{DT|Y9s(z#jn!ZzUiB&}64X5l zI-+!Y0U3T)Tqr9Kc^FFFt&T(+iNQuFBx*7y3&~1ILzv@Jf$X2-U>w|h0HV|0*~EZK z49d#~>B3?QC9{hd}Y z?PoFKSsb<7T1ZeB3x&kXb}`tFZ8c7FS3UN>UG=^yx@!Vrd5CI&EDP^j?)?W@Yp{c3 z7Btxm$s9VPg#0LG2w2Wf)(azE+yqn$rfC3wMoCb9 ziX)XYD8_~fTeca5S|*5Khdwy+z=P}o(RT(4a9#|+mjgJ?4UPP3D&!>(+3yPINKIl_1<8~N3dCd2p1--QCNmaqFeH|gB>Nk`w6J-$_z(RR!IB-;H zL{jZAfUol9twm%yFsWTN)e_RB?w&%WO=LiBVQ~zB{_sxWGzLu*mWkjqlnA=0oc>1{ zIVc4CP2hY%i(?+#U9t$$(bj&v7yC|%!KwXjiFis|4Te)m&O|I!?b>X?otLy z1~G2|cod*60U3;9ARL~578iit+hxu+=_^OTWT8o9FF35Kg`~s=OEIuJmVvwTi9j#( zRnmQ=J_8T=Tt9t63y)pW1J0%AGzKaB85avB@W8mpSaq1^&&8F`7VcoCR)Q6xItc{f zCB5a49xw+EnUy0ij0c?LB-;(vrTTH4VRAcIz0>H#gac-DUdh$sTqBhbt27kjXeK1l@wAg;}+(o7o6DZ}p+q1wc%6P6hij4HBkYa8E2garZqvdH0@{ z*8w`)WVl@Z??vb=g`@&&Xu(E8{jzGHoWTs_p=M$7*QNJ4yg>RzP~jhKgczEJLT8sd z4a-mEARYhW{gAS5eL*l%X4h~j3Y8~VBtu6lfPZlfVZA2~p%0mUU=eh>f9~Tcs$OT{ zt8gR}#S|G2+M(F$dQ`uFo+!McHLG>zU8W9=1Y;pF7O(;ZaRlEQCQKO!`KnEzkY_1N z*i0JAfAvuWl1fC* z*z!@}i|Cv;>?L7@$I{x^nTCI?aXBk$x}{l#S;86cFarPl_iV-rc`w3_1MMv8v?BlU zDdC%`o|hTp@KqYzWwcl)Fem2%u!Qpfw03D?vTN#3XJ#^0n59R7Nv#A! zgn23$3ODR8hKz+!QN!k;AlT)mHJza?4}aE55`>kLu@VeZ5#Bv$T|+>R9p@-s$Bgr3 z)}Cenc~QP&iI9_eePH+RcR%^$>EkS)4LjtOKAtZ~-pU^8!r->L}nzsJpC;u>Ro6R>u|+Ay|Z>^At!+yN$?aAS=UF zRy6?SGC0}6GcKn>Zyjxj{2WT94dNY|X6FpT#5K4fQW&~pKsJG}Is?gSOrR|aVh3I| zgqvO$s=LyK`bAG&NQrIML#}=9SWKK(8BBr9N`i7`)6C>i_pv_~YEbYQb#C!vM~k!| zhtN871^ya=T*=Rz>7_#O2UJ_)yNDf&H|cvDHJ53f6*Pp6sjWoOr$U zhq>5?yKTn|SWFk-xv)dn^M}VGQP9VFI2(w4rE9b8S?c|5fILnb-IbgDRaLnK<;*~F z{|6hNEcgBLCX`PD7{IAx5gdZL(o4q15mp#*ri{SI7%TZ@j23+3#!Bc*G?@ZzC9zV0 z11rM@^c=Sm1d(qc@AYsTkf1w*LCrMaTasP3#;Z@IK>y)(8I(`}DJ7vK-p8z7@+utB> zpAv*2#$j;s)Z$)a2?fTHN?7a6D9BpRqqB=X4t$;mx?qVJmIYkn31NZ!44w9s z6io9cmdpGycpa6T^Sl~>FP?dDl!Z{Cd_Z3I@9%UU0`_t;B zV>p`UE$sV7@=d@BCM!phV1y;O3jV! zyCD2Y+fWkZDm&R-+?S5r^e`I51hEOt5oQy<17Z&9tBq98ffCq08cIJlGb#Q#m-HA= zn;klRk3}!Rn(^b+fvZAkJ6GWWwB)tBTG$g|{>vxt5c9H7=I8Km4g%7r3TxO0)IR?X z7)9mO3#Lg^kz+#t-SNk*dXyJi_LUg95MH+7Z~e|?D|YZbGD!)R+~Q?+>*GhSVRYRZ zK>oCr)MCKcDBzrncu0#ggS&g`19^G-2UqpuP!W3Yzo}S0MoKmIp%Vx_^Z<3V;3bgO z0;-&J`sO-tISWM5LKx+6eCW}4Q%cQm8*rwa;Y@aE=fmfQ;AFmf_A(V(a?QOYR3iKQ zvz*sD(=4DhYINcmcStI8v033?CkXSZfRbz&la;&-tyqugm72{5W~xgGeIMZvek9(v6e*4oJD?qI@8G> za6zw(DJ3!|S`aBrSA|kcx{A2NKz4`cJAoq+QG}Y0KCdRbQC(2VIMy0QQ8Of2ylPk4 zg98!D`k+jxJJ}2(;R;k;V5Cw9o=Tn{@SpAR`kZnClL1YE2hH z4)n-65N2Tu;69E71&Y(B_XPxX-%J(qOb(*f!|ZzUsT1FD)wJm)n+(DfEnMqS0>Do{ z20|b`Eu1oXU~5&wg`Ld;pfu`8;|#oLj6&rKa5pRbDE{Ru9_MObwR;JRGL|8 zc5n+|`*+H>gNiCGUJS4)%6i3R1ba%LX&NM20{m>YjsVY3j%~55F;clhw-4~i-TV0H z*zjA|c86%Kdu77O^VB?G9$Zw#^? zpBDLB)l)k_Y{`P&TU|U%$-4;Jp>d4J z4wLXhs$`J_?fFCP;5u6HlgIED%TLVwHk)maUvO6 z;Cyk_P;WMzjrPDT#sGoQ1TFuQvyjS=mK12&nh;nNF{X&T!|F>RDZsWUByqMEYN+gB zFJ559>fIv$b`2e!Kvw}`wL#>c#9{6cbaIyM=;$>fz{TTV4?(%b(xsBeH(%F&^)omncpaA9t}BS2&Xo!aLgVa_pCjxpAr+Y z|0i->zJGca5Q0ti)Mf0w>WS%CPcQ(*I~2pa(ThF5^gzDSjt7Pb)YL?rt1vn|@9G44Hr~ z>IQCXyZt$>k;|rnBMWwwhQbu(+l(3si`AOWgYf9%E<=dI|NL=&m;dRsn^DjS`GQ*c zkq&Rv4JfYfRIS%xG3>Gisjh%(MaBvX2Vp!oIF&qd)Y;6PXm*bewpebx z>{owkCrt&98EzKp(79{q5`}6KERv*iK<$ldQscMzA8hi|QsV>>a2gVXepjV8O+#`0 zhaP9pc$rQz^|5CBz*TDa7D}bj<9OmwGLSc+IV^1Ecfp?rDSw=#`lxY55`uBHV6O-u z*k2e3h#>NB0yX&r0pI}})f;ux2+~-8Pp^JD&HqvTdh^=Er10K%T2V2(I-c;aX@s78 z)Eo)MVGHa1PC}^k(efHm7fLt}@x$Wi(0KoAR*cVIW>A|kBS$5Vnd?3u)$eBqoSgmt zI>(QiMSg24Xrp`=`uo*KKkZ7c_`msvCXcMOVj2He=U`c|%yb#iz~-g@v%&WaRjjkV z$4GSujtebD0nTmvKl*b?{+~nq|3~PcUYN1)_g}J4mqOm^ler;p&;zf6&wy1ZubYch zr_XLN@YfU!v`@vB|qodcJnYY z1Z!`H**PLVhS<5F6-5pStsHPe*W6dZ|M)|<2!UyQ_RmB(G#X33dVC=^I3w+!(;1LK z5dt<>Fpx|)cW_T@x(w2zN2QPT46FSk+W!$d-U{lha(T%?$lq2T`^7|EZo21$l5QPb zd0cki@!H1OrhB5@S7f-HRu-H0A1O=E+J?;ByY%a27VXyK#Z~2Ihvv{Hwp=P((m^j) zZf2f(J-RRMZPpjT`#^c&o#P_@#>VwW-5jM4d*iHV(=cCe$GA^C$|%7k|82OJ7*@uy zB$~--8BwG4Ep4sN+nP&WZ}Is!^(XBy23&^D8~95;`3i;Re(5IKKBsc`_yGs!z>Q8& zXZHD1!x1-l)#W<@zi5Yc$vH9RnYVOT+ zn@O9QjkynX!;me5^KjzcZv&Z=)j&S3)R%T4p8p9+bfwoZ9x#1+u5>bl^SlsKZ+I;5 za_-Vy8$LDW36aH9E|-%jh)}QF)p13Y_@{M8jT#u5wkIhr+_8;ab7Eb$Zhhg$8?3C{ z_jfUx>2c~TEx2JlulPl!ig@$$?doddq4XQqR0Vv#eQ=bL3d(pnlJ;f$gGcS#G^3RJ z8EhD$p7bf{!7&=Djhbu+srMBDFLloqsY`>h@$R{WHt6f8YIoxr;dVS@pYIKY zJjm4(8O(P`Iiv4s^1@T+`CNqYuf@=1_;pp*uLajtYguLT5*DR?)hsW3epJonBaXSJ z;$j`ZBcLu7Z!-0tZK{>Dx>U4@=T>3zLSfj((uSh1{0+1z)AY7I;;Sd5)%lck=X1zR zvqM|qBkFFWUxo=tKL6f(1-07Qsu@~+)PtA~M+?dwp$>eS%Wf3KT3y5O#EAP!^Ox+S z_mTu<)c(fgDF}IlZA~&dA0I{YMLSKTQVN5t_HjEwpJ%>V*psf@U2XAO{l2R5ob?oW z&5QPtYIW#&a|G`6LmmEuSAT}nGe;4CqonHx=GQv~g`2HE&uj3DzNMevvflgnqm+8_ z{vtLy>#w*>y;y&QN+TEQ?HhRGs(nO_1|HQKA6anB=a~}))3H3*&n>~6W^o2{%{Tuy z=V^PQEOKIeJ_UYiz_$>8TCd&CbsAH0O=IEBEFYwar!bBGbYZd2;dtRspZoFG?)2!B zx0hPepPi;nKb%v4qZE8INByUg`Z~WsmdxJsn$HXzc=dt(^1l``i5(TlVDrNqWO1Dx-LUtpWy-ug`X3!EakP6w|&xat0NHZnM!>GgBj zKc&}7T~1Y?wde|Gqr}ikSNjJgeDPzsBS)g)_HoK9>i5NLoH>?P*yg%yxsW3+1`5#~ z&qg_GbRuFuvvsPkx-~yLu`6`o3!X`?kbUiOMETsQh&GF8qPs3K6Bu=6(>-<$m*BxU zhNP%W+;4%teIOgp{P%mM{-{;2T)9};m7T4e<&!+7HnLNz;-#vHbL|IuBv8%tXhCS4 z?dLw^oQ^>HF~ zsiuFrtWn!#S!NdFZogjrC$^o2mWO@%-0SLQJ>SJ&J=VXXEYhOCzs@TzxnioXP{}Hu zd_!NaB2*l0{#sl~y6XI*v|0)44oB3@iTbg~815H;_XWq!`rdo%R`8?jlZ%6RsbYP$ zuNv+src+WUwxM~n=!rvPe#hwF4Hp0FLaVpqNKCTvwpXP)BxyOsFAZPT6(-)d!mMg0 z(}zdVJ&YqAMzLUW`LxWb%AIjzp9EfR9D{3PT^86`JSw#U{CHxTa)R-fr;?C#LH6Gd ztCQR~zKPt=v>35TjHb72IA2S4Y6lVTOq{k~|E7`+FR%*{gt{f|o(KZ?RC%grBW z-oyv*4*EO4mA%;DoIzVS+WNy)z1`r!=ct>UO?NzgF2~$dmh{$>(krHS;Lw=jjsaab0Wk_HdRyKX-}#dHZ0;_U=o( z`>W>{e52RKY^@%Rz=p$=_0`wse_JGLe7zGC!^!P?9lSSLwm)}^N#s76&dHXH@n-RumA?@qi zcV$}2E3!J-&fmgcMn4|rcb1g*P`)nXZSf;-g2;s#vlCo%q_fGNRh$g-Jx;G3m=jK_ zRbKJqtM)xs#Y|RKJaJ;cH)@(#HGWUq?GcLEz1DL2Nx2FgRvw_$7~k-sVZX86|9$wZ z6mn!X%OX^`iC=tD*+-Y_QOo~+Wi7<{Cz*%SvsRwdv)Q*T14%z!%Do=^Jp9^(Wv%BI zXQY3IcW`!CREclCW?v+a zczmi44!bMtcA$lSsS-T2>Yvay^f2TNr`u!x+KaZcM>u&?*ICWv@Jkm8r9LI9yS%im z`d%5?oN4myzQ6nX;^3ixCeOvp28oN~_^QD20+$`pPsYVAjGwUIw$NCdEzOP%_1Ez4tdHLH|C$}aMGT+W?$+U_AYUL)A7>15t6iT_N9B}<8uoBHTb!ma@TKlMo-c^Je<8f8e+F} zw3$B|z78}meA#sETt;7mlB`AixY0!=_t!G7h|)j%ex=>)xU+xSd9r=FG8OdKr927uAwK5q@39D(3aK8R2I{AMjkEnG ziMkueTwkA^!`vKhir_VMr)#j1X<%wdNwr*5+pS3J->RRQI$3Xb5|$s z9gedh(1UTo{l9_9k)9gj_(ZFvb(K@5L2R|!rD!ua z>9F=3^W{fe@Lf}e<@LWfrJr(>Nq2Nkx3^#$W@nXdlzgfQ=I|r=F7-b7TkxFwR6B{T zY1pih-QO&5DSSi3z^^#dx7#*ec}~^I?6IGCq3@C}cwqKyqkwPQ?3TaZQWxFdg7%Ko zlBEK-SrKhxr0ASM{jUPyjiE^SWrH7aGAa3~-=b1Edu~07lc~v1RcSL2y|Wb-D^ovc zFxO@#WE?Ml z+`b0OaFw&bHofTG8xq!Tg#L6TeGoTn3?6IaaCJ(->lL|y$t|L1zMwJiLc%~@H@#Uc#+sh!_qZF{!f z9F8M5s%I>6viNk)j|~Oen3WB+qcgat5QyBSyiDJS(tyrCdfV~5yfr@fNFVbQ&5ifI zozE7=lrPl0_5rd@rxFu3;$wNYM+P6;_wi(Zk-4n4tIO`$7l;qcYu?LaEFC^6N|`~r&opqlLG>@2a!D@4-Jb68ccfK4)5X{}OsYk-A(k zM+$-m#%n+17M7}vmNvMaI-8|X)+_JxJ?s&872>wqk9SqQ4N^tER^6+=-7#gcJ$3J^ zZ~AQ1C~-Ik*c8o2UQ#Q%SYqjPjn}99A;mCpojCtn|FqO@LQg4RGlDcDA9;@7VD7k| zzWVm>;2MvitxDscOW~Ed9-(bbvv=BRqI&BH)rZK_SBc2w-M_c;Hk39_n!QWhALia< zd6iIl0pEM;(Vfy;NcD)GTeqvPnY`m>+Fbo<{^5&FeYx4Yp6WBPp+WC{J;3C4qUtq| zcxAo`m0oMORMIJ1a&0S?>#|Nx=V8O69p5JIl+P0VOBZstUz>VJ>_@X}?iBw)N>9rE zsdK+~UD)mO_dCU=_y?sY77tE4%?5S1A8Sij$aGv^=;*AO9BNah@p<=0QG9YkNYB6X z$tByygLtFa^HcFIe?}!H2OmZpF#Bx8*LII7H?}Kh?sWNc`$bebT6QS2%l`M#BPZQk z9@MevMk)Wb3@`U)sd|&L_aLa-hA~pV=DA+CLBL>Er!dEy;%$cRKgxftf5vLI9HM9W zL~A?ND=xCz?%9c5Vc?0F9ecCg!CpKgNA%lPm+*hMpgH*Cjubm*@2SddlnwccF{p zvbQy7hU1vycfsYKDTzOd$^=#;V~-vv#qf0rE@FeS6SN;syq5Pq?Jys^K3Oj=xG5#0 ze82Hiq>2D2_}s=Mk?UF1d-31 z47^BO$gO?58L1LSm7&`;=R6P;T;#O9*vKe{zT{& z#~X(u3{DE#h62TB8vs-gE$8VOUJi#)_*5&`#utRT);8AmRYGPF17%iIE+_-Sd8!hV zEkD=JI}f4s@lIPU$Hrl4-rkRPEoAMjYE_ZB+hr%SBXe>= zKd_KIkta$zy>%L1mIqHInKHEUzCC^C2-KBml4~S1=6Iz1PDD-EjEbBzn?sY{#c<8} z_13w|+r%={S|lo}T3dcO_k_oE=|{xy&@~7v)Cc}VzsUzDPP zM)HOPDj%+T*{3f;iPkf-0bDwm0!^~n4;D=J5m`|N z7ce+$OPaV%PqYgmRNSH`{t_X4uZCg1j|zX_DXVzFp6ad{J2qZx*Ouhpe5d<;Y{m9P zJMIcb%w_vWm6_$51zcTv7gO~m|9p48zbQg&co(@~0KrR_()V(Cl7tUJ3dA$CWCF>v z^+?RUAi|RmR~@Ndk>Rv60LE%7lDDYAZejNDqX+kZ=(gRV>0 z&2PC(zara<;QbEay-}w2`_HAS${RHhou4IYM%(uL8Q7~-ak5#z^=maRMhE9+(|eoQ zK0rlR1A2CIpTurr8Q;ZnS(O3pubi{kt=z6Jscv3WOVF3R@xx0+Sx`(ODypjEeBvJ& z?fE#2_Y(#2GGC!hVHT;%l*jgPPh#d zA2=s!hT`WX(TNOUsLe33tVEMfaD;jF% zAzOh-ml=EInE7Y9G<{?EW$tI6x#CBu48QJT&i;^5$W@S`zr7NcxOH&MTIJcl(4^LfL~3x;kls2UqflpbAX zQ0_Hh$$h69@hfxjsUQI%<|iMW;qLv!7tC#=F{K_&jt)~S)`<>!*Ca5mNV@+=dSn=U)?C$7E~(#a)@Y8`JQI&(3ad47Nm7OAxl-|2ig7)l=#4) zMXr|?v;Q+xglpXEc52ANwVaMh!|iX@_s$9xU#5WPlN>YD9nwAI>ZK1t3yhw9?*3XQ zm%^Ov_PO_Cn>T?I|AC3qzv3rsm#$aqGHH18Os|c?t#BEd-l=-Remw-kysE1(dD|gf zz^_4!zwy!QNtnZ&NbgiZtYZz-U{;P~e!D!mz)(aY(8MOOH!xCUN`-WBTa@@pt!?w= z54v3|2(i_jwR8(G5cMIcWYZEm*9b2C@lH)ME&OReB$I(2L9ypS%YH7wxQ7sd|72&Lpu!#Ul;0F%z$663t5G1g`_$?rW?ZQ<&BCyjM%vp07i{Z{iRBB$+ps5o6T>n1S5-A4 zh5QvSHUx?DXGrFmEW}zSvYaCVA6@xF^gbtk2Ll-39#@{*{epMBku$mT$K1bHr-@tN zGa19-Bu}(OW2DsxPY`dC7lM=|ZVT#5{T@liOIOw`R4MjZ#fZ9JKOuChvS3EIo;sqG z*Cm-+D(%qtZ9B^POVGchV$+?F;nXM2VD z%)v$u0o^Wo0(vmi2S2fD< z4L|->AHBi03d!8E>$B7Y@~qr>O!8vLf`oGI_Uk#fwZ6n(R$aPO=NYUyI(isc(= z*Q)NzitOQ0I{U5mzk|8?pDE$LXCXIp0*xQ z{y%Pm%I>cHep-yG#VrU|v>rA%! zs9oGOdvR3`%Z?{yXOil|>zG7INhGtY2l-kR(~Lnn;3lzdf* zH*L%#k}+pY?(E`w@0_(&4U8>1q##GRV)&&A#%YvAaoL0#u@)pu8kE>mM$k)#5FN@0 z;>~9;R$k+ySGRYBG=6{5XySIgmw@@fDNB5V#;h@sH!qor_KvrjA57n>*?ze*`=nQk zlva;0n0eAxnV}jkA@hlMynvEHzMMK`7OQlr=Je~(%4s|8DM5&NS037t)J!H z=eN_CbJhRDRrhdf3AkGvko)BwG%nJaEVJL=pFDgUIO!p&)>&nIXx(#1=VHIy*4-a= zYLU!)D-$I?9PEqSdh@ykDMp`>O2u1Izh>M8U;bt$IwiNTDsy1IT9du~8S!tFLmZdQ zN!HXl7{YXsxSLx`GwJFnItK3ztw}04KUFf49-H}ctA(}RaIp(2sZpRRVsK5ObWbV7 zu~WrfNi_zqLoIo&S|q^atU(YLi7x|2d_6~&b7sUdf1)>7+2`ps8q8=?0Xu5Y#Q$cJ z92dkoyt|$i3|NyeTVJ|o)3fz=Z@(p$85N`~&5S40DKoL6{{7++ggDV+Y zR-z#V6h%@sHLiYH5{l(a9Fzq8+=PAlQc5Eoozm{;@qWUuWsuVP&-TJ>voLJuW`pk3 z-}(Saxfvc$+ncM@#;OpCUzR>&@Jt@BV?@*2D^7q?J^#{yY({-LnZ^kenX--(xw-vV zRQI=soq@e&l}@EfW*hz_oz$34N3>JBVtKHTHLoPy&E%ETsVh&4AO7&F+0Of^W&Rizx$1-%^s*2)sqSR@uep6PewwVl1vbU1*T3n188{siB7%DB-rCYyjtYs&E7cWg4cT&Oi6e2_&f0zGDyR(7>S=^9#)pr zyct?h{W_FYTs^A}!!*2M?fo9rCg*wuFCBNrpv#%W&c{Gxa~Jid&gcd5$llLXN8Oc; z9O|X~#?`O=milNf-GrTh~I`-7)xC9k3ve7C#n8W3Z}4GS7Hh!N=BQDRtA|>P9yHF@ru)zbv+( z8XB{3ht%i&rCUy^(3kq)(u6&A2z+=ZhT4^uD~6cAw_7R)5f}#@aN(~rsf6z=aZL07 zko4X0Y`)+7t=U$Qn6))FLG9X9&{DHXY_+%AHDk0DyQnCNccUq4#U3?Vu~L-UiZ(Vy zV??a@<@5dhl|S;l&g(q)bKU2h`jl3 z;6M<9>1Pj4^)`5sjDu}aG?<$K z0ZQyOccOx>t0W4=WcM?HB318H5!NlNqDjk+^~8fB&zCo`={KFW8nr|WEB{)|>uc;h ztkeNF(I%pnAeL-mv^-hZYL*kn#tk;Cw$qR-H@NhD&p{BfU)J$RQOBWv ziR_>zWQT%kG&Nh^p}E6o42w=xti?S_C!~Tz+MgCeQ*Kijrrzw6b>gmzet(;dmKOas zh{|yC2KtDb1`$r#bKrdk+MYp+UAxJNy)$lmWY^U4X|nJ`cZuui@P4LZ0lM#gJZe*| z2prL1zU31nqGFQ@1=A*=kaBg6oQB|VvZuNPpjWwR*slIKd8x60mCh2^hE36ve6)zm zQBK3~@debWcfeY?QX>BFzXX&rpClQc3H^z>`IR|qnJ6M@G^FVWQP6i#qX`XC1sbl& z^aVwOpx(D>upYGN?;%u%9JFX|GBu2n!AVSp4P5wM9`X9t@pSNq`9jHK_j|Ft7HIk3 znPp^ze&?V?oHT^it2F>LYpsz82Zuzc{}Cm&Hn1-{T)xh?@ERf^u_q|t29!G~9us*3 z>U@?U6m$bx-{8>^yP|+F$7MGamk|h^Ds`)P1o+AO(>5VncSM#;Z%{D87(usgq4IPo z{Cnxd@SiTH2w4P_lqQH^9d(l7x`D_^?41Y{g?2Ge7@G2-!CbcynWa6lG4K1cpV-MV zM)*fS)1k4_xnOnLf5E9Y4fAB2lx3fY7$)Qsqid6FBIR=}ozLRde%*17P)vZbq4mvc z&lBCw`O%YM*RYrIPWeB%z;ZP4Yoia*w|Oe-qS-nlhHu8Ui`S{ygRFT^);8Ff5s{_6 zOsYB}h7dOK&Ywn-i|Gau+MT`i|s&?fOTKT358yR>Jccg=y)e( z4GJ(%zbE(v5-_qVw`BhmzINd}6x7W1KSZ(cQ6qkDsaSXX48CK?jxg!rH1%>bmPS`` z>4I-&^i8;ML$l8lFn-s_ph$GnhoM8ZZnB+Ucbe{b^V&{CkS0{-*(L>WExx{4E-O8SErGKT9iPtkn^27c}_Y+xrP(RG9P%KCc!OK z6Fs?e9bt3XTc4ob2$nZ+nUjhv6pK2nf`E6_1+iabE`B+GJ$QDX9MEmLa$MvYL>XHc zZu~c4kjLwkFPi}ENMbVFl5xWC(<2-%dxBi9LD`O}4e@(UV!s%{bg$zv(I5Mn#3Q*x z#w3{~m{MmzL*^ZP|9kgeVN`}QUMkk}qz@BgpFX2BpYkIj7xAr8zsXsdbuF;vHoua_ zUhy0q#Ax=V%6GktU_OA`4h@K|**eor|BE<}_K{Po-m3VS#cEvx^Rd^RU8&^lfA(8} z&EGS>oX}I6tyM(A-=+nB4G_OHeUxnj!S zsKiY#c7VF8s)cRXcdS1n(p5jbylk@ptu|)yp9X#ZSmG_D9YflnY?M24O-K8!Uy3mj zrdbh^AUJQC0iS4(6WzZD`mFK}Zk%z=teThqY@QC%a8ACeS{lH!dmEYP+aiw1h#s3y z^gO6w+5-qaO{lH z(&8^SwIPjx_o2RD64wk&&_^yg*bWisbSMS3pxEhD=>|CUwKtf$Z!JnRvvhwLU`U|O zMyZw95$5D-H7S$g)-E7LJ7dZF7VWia3y;YgY{}TEP5mWQgPxzwQ6C+>e+xmW-ZPP4 zRIaqxTi+-ERf~*U!)T%Jt9pX1`-FVfJ`%!8<;fOK#M)d{1`#L>t|r5AnW_jJxLh7` zF6O<}YhhzO0|XDyr0(mVh_KB(bBdUy24^kynr9gyeSF1=of#1~7kh)eu0hXkk3${E zhia^0SSw529Zg1YD2SLfhJA?|=0{hBPY=}}NaRZK=e0JIWqLCrsuTN8O3#6*-%Qc( z_iiKH7JIV4h(H;_sIff^PFoA~;76Ce6TayFAl;+pg{Qlcp9KHE(4bj4#2sb?xjP!S z_A$rp54-rT&=})Xd7~_f68eCK$+R ztFLFQo4iATKxa5{OjCnR+T(@db|Fva<53UT(L@4u>Y`S=PPjx1vemkLG%qL>H9r3m`(E(PKbPwzpC0yH*^Dpe-{t+riO_Z`sr z%Gfk&$J~M}_O)v=jw?i$8PTWVRH33LVwlfpp<=!V$xDUrYc@YyhW(Z3x|+>_Ixt={ z>?V*c01&*Rl8DMthZeU`U|pinjr-IHxflh+Gh_?n&&j22R^T%1y_7XW)Bjmm`p#bG zJJsaLD(ME&Ts4R#M`-j_9qT#Cg%5u@z}pwpqzScYq)dIK4n>4f7#1-&waqetJ;>VY znV|pfmDbIsE*WE^+HtQiCw%roSh`W!(U+Jqb|@)*a_HcgCzm9=U$4ZWO3AFT}{Zk`u8zSv4h(d;?^Qn(YG6EQzsa} zTm^}4eyZqqWSHR`I1Qf#4($yuX|Mpu3m|MArnvGy7jsv=C&-JO0-98^P;pPQiHy8X z7PkKPJX5{zgzpO=6~>3Y@TNxS`1MY-Q$m+ouBV#Fp}}5sh#C7{*^07$XL9a0xz0PF zMz(K=f30`#Mph3eDyYt^fPN2vs&LAuHcaB)Q0ufwe~lR#CE>JNZj{-9&6E zI#o>Yf|YC&F(@&paLaYASvxQ0IzmwP0EwEWpk9kF5f6&7tgb}ZT&HFTPQMG*6UWhUF*ojAssz+<+FLao%$&q@@A zYX~R22cz6!$P^T*8<3nhxwO7<7x9$2aii0ZNS3+wR0Mth_W01mhH|bV$*#bl@QK%2 zr2PfYlNDP4mbG`{lLkoIFJ-W3Zape1{bkz-uuV%x@_^Uuq(e>bZ1vNi*{Ez#PR7OK zuOP0gjEVpr;G7q_c#vG`{WI3rwO@s3CY$LM72=M1N%((*%Z28vyvNTmItBcsxOF%z zlV_--;BM?34cxK5jSW`2`7F7EjxeH&S5oyV>Gm7JNxS3IVVxV{QGMaOA4R$%+Mo43 z5jPqeNJj;md96M2yBN{jG7^pbHqQd{_Pb4DO`nS>?tA}!>?mON|C ze1Vs(X{P6a4FV}>d^Xb3P!BjHng%-@TVVL_Id^%|SJ2{*tDCtR3J}R^Q?E5k$=jrx zM#%}I)?Kd&p@6wEASm1=t5#lrmD&i<_FP>uD4|?cYmWizD;D(qK1al3O19IFL%*-) zc(X(pekc?5)jyFl)6ru(s9X;PU_^84S>}TI>QWO>x$!QD(VYuNsrWmFOCab~yUi*y zaxa1-BAs4$N!3zsZzQjEKsc+RelYlKCr#}bnA;_1A!(mrAW`D&)CKd=$!btw+IVSm zXElo+03&CpVb~htXFa%M3j=P(w+f>v7Wr_ingDQ@j3ue+^#{zSU`&+q_vhyCl77Xi zY@9bHSwy1kyba8~)`CO(u9j?~P1;m2;k6k=Gv6k@F`5h2uwyGF$VFrmu-j5VGB#zi z*SJRck0p!dv&J;XbWJ)*|6Ju*LNi>mwsw>##aJHlkg+L5OadRX>OFhXId2vnF7tb) zv9GJUjTfO&_Sz|8`3~5JGY%CiiJo)^OW4M{xx}OkIUHG<8K+>B$&uV!*E+wi&;o0> zr4!9&8tkTq%E{&3lZwXlj;Z1NC((Ru;(C};1a1R-3`favv0e`ab}OaAa(JTDW``Y- z!Z>!X|1isUBOh)f31~rD%4gR1FX=Ic-)Gk$VI)@5Qw_u>%57_8?gWd=Opv z9qSZn2npLl1#^V7>!XU+Vzjlzt2-*v2I+RpR`W*04thSrJDL?ijI~LFW1Rd!ruL8; zOZ(Jly-9V6+8~HxO0-jJn%bM?`?WI3z9XP7$CDfp>-OTi!W&k~po>LS#*zT`;abW&cXWdpYlqm2$6O&<8enM^)13 zU~#ytW>S?AiuIMQgMAXHFm+HW7pVL}7&KO$IJk8Th>)=TqiQh)(qs+A4%xeRKgg0)-jj`6z9YV3QvMYOleZogm)E&K`>U+bE+Z$b+_n%1 zT=15!P-cx#m&pv|?>^a8zQ&k*&LAGn>A737<5y-4mg?%+uG%*LL^(DkxzeS;eroq; zR$|F%&Qb1e=O=xP`=8}V2Za`Aeo9Rt5_I@O_`#ijG74g`d;KghGir!&#p^*01>jsW z18BcCe(+Mc#Jv z4b3_3W!pp$J3H-s+yOkgNgR|kt+fgwZ`CFhP-a!R?kEVK;_DYZk7% z=g!m?>H=AsvG!PP-LZ&l0IR<{K>c5jfc{;7I4{XvTF8^_Am z#3$nXBr0E5_k_hqHF8P~rWYoOt`|?%pwS*V+Do5+IFXQv2)nshBQ>R+s{dbH98i(?#Z+3$OfXyl1x5vvg{R??PIH3?!NiL9sD`DLbagIQ_Q|F3NnHGAq)EPb=?|stw@#HZ=G$ zr^F|HLhmNEcmM41rCe2)g!xNjaozWlmp}00_&jUnNHvDIi(gMAcgaa%6(RI|nI)2_ zuw!~bF9bZbj(}uTzZ{&JjqbJwW|ielgEoMFgV$(I4wQ78e}JO$oU$6`T^)AMCv{h! z?1t13lBCVO28_M8lF5ZzYwoPtrymDL`UHk|g#f2dlp#x2z$~r?WzzfVf2jsuS%x>f zm6Qj&Nyx#rv*E!j@47*^$7CyzI5@Mgoff^9KvXX1P%R{Tf*Q;}i{@%x```KUCsYH$lbBSE5A7?%LSv;{3@J#=AsH@e2V0;;RwRQX3qN;PY_)Vt#d?&prZZ>vj0}GTW9?bF4z>{ z>Zj4gn4+$3xpn^~Qr@WudtI@b!s**EMYDEb&$JlrtZCRL=XcAy%KqtSqgl$Fy%+v} zMJAL76PMxpRI1%&FPp@No$_R7e{o1VlwY5nu0HB!Im~nS3!Ir>u>~0@$RlPFqiIfvK?xLhp@C&Sjk%;D^15eJQB?R)9%Kml1tX*a%<{M$4)*k*bM;$aY9uClNa zTW5PrhfzQZBgZ2ArZj?|l)V1DD3}#-UGFFd$kBapuUj0hq4-nh<3}!sPv%~7ZJjk2 zk0C3EEBs1zvOq~uZHN*be{2>Ge)XRqE)$$c4D)qdu(^RRHT1?87BijONH-;^O(uS@ zvGP+F`-)?48i{^mYf6pRRG3l@bU?w6A$!0NC8Dug+wbn@^lAG)3rvAYXzF1UDfM(8 z-$g^&D6thxPFr7vM>BCwH^pusdYCv3l>@y93RY3)ms!w*@GP*SfgD1(IbURScG#(H z0V0CU&9OMsifL-(%C(H#_*T9t%@DUWEkq?GFoM#+>2Z$ZI&-XXbC& z^IjL%azzmj<8kbp;FZi#EKc=ZT1sFp;L5?J2wz(a7|U91|6`2G)rhF`?_kNnWwm}e z#VF#cdxtN<8Hr%1qcLk54iW+rwjg)tw|H_AjvDIJydp<&&BBrYqSY4%ue{ytaV~j}MhvPzN zcY_;HcXVY-?=5l4i!r7UX;hTvM)WH}Qf<7qG%Rc?-p~nrFtA2?+Q3CESM@Fyr~xp^ z1uet6499X{TkuuZx8)JfP^*zmyb=cm2=v<86b#}o<+8TER`~X4{UfVUdY1?{a4}!!Xct(qWVq$ z0p!SX(xDi(1i&_h&9{NrT~7V#6W>bEA*PN)>Zy1?O=uqkY9A`$?4G6GbQU!NuVKc0 z?v~E>#d2mlUBmoJ`0t?yv#ZZ_(~v>>6h&%<6kB9TsnJwybGLBHPd_2uROd9%%1plS z!#}eB{j06aamL>jS1)a{X=li_9M0?M3?4OymN@VB zUn?`_u3%|F;+5=b+5FJlarTYe zP4}K{ei`dJf-5WN)3m*fx1IdK(H+Z_Tk``dd3hv;F&TV){2}-zK3FLbyWUeAn&$E4 zv4WylSiRfNyGqd5bt>%4FDH({jAy06Uf`dQ0KZl90FzD*J;gk*wlxPfIOq78i5c3W zswOx_G)uIrOd#*mp$S|FB%!DK@qSiplK`8oqIl#SC^nW2X5`QpP(p|2+t9i=hwpW@aW;H>Xm~ zFBqt0#3L7+ELaAq5ok51Ehk!p$XxG14+Ge5_Aq^~nG*{bUuf9&4rn5m4re}Puf0(0 zH`lOQH><0;2R*QuQWg*TTRPFXTK@0_1gwFWRZv_27+w!7v~0L0T40k1O63t%o4Zlr zMVAshrxiHN#_}}q_Kw&YUaXezB>qgM(H|uCi%7NgM&huht^*)i|8%^~=VCzzwNm*R z)uJw#@`~-R-CezhKcgY|Q6_fKu!n5bfwU1gV z9+WeY+8!}Q)e%mZ{*8oG4c*AM0)Y9%x47?0hdtQ>(@#!F@B*;+gR8ih516sh63%VU z;)$<`SGo_xg|K7h6Qvzm~^2dpWs?y=uv9F1lLHs07pT4moO8m-qpr&FH zTX$P4mE>x*rN(xzX=xWhIa=eSB)=~ln?!UR-b%X?E;h)_PJ|uXr)d7+!|Zuv7WA*SO$RSRYMKgrY2&ZN!&=ewqS2;~FLf zG2>dOCkI~$(fw60r)nL{oi=+C)?9GtM0a1LygMLd?>k$G?_Ujn=H_Ankgu;-8)+G| zQPhMwM8E&uCK){H5*>@+8kIBH2^`J$f7hCB@H3{pE(AzEE?>D-#vt9Z9*hR~4n+*| zK{D=n>{KUwIL@Z81~e~DT3E}1O4viYS*>>b+aF~yPGx819HIwD!S#4%g|IfjT&ZLm551-kUX;Zura{jL@*zib=~J<6(d{V-Up z>DwfqhCqbX;1X@_V%8}gOl_%DM^K$lN13w}67?sJU|`nTU@z{39dQOd*Eo)>+h zX(x6mRL`QOv0p51$*V?zi$wi%1FC zU-eepywz)5EB;Kqyz7<^LgaAcKl31p=0G!b^xRMSCdDUP{X&xyj8e=$q*gMm{!;ly z`6T$=-J7Rz{ni{cN)tj!a$1la-UNL|?}j~dIamvSJXUt4J6@%853!edyN~9%u08XjA@}9PLN01>ZxoN%F($;9#BLZr>CZBJ`E`-m z&smDSb838^v$TY`l=gDHH)F}@ljy<}S&~y%oU~H1ro05sQH*R@x9Ba}bKaN9RWwd) zQ4;D;w&bl}Uw$Me^T_~7(X^N>$NxOf*;)8v6Ta@tiFU-s<0Oq`+c0abApNhY!H{PT z+O|qvb;)Q6ll9K37K)U~3zd_49U$)XnRe3=m3VdI!*7q)w@r|q-r9!#zi1((!F7@Y zTiZAvB$hy#wW#T3)!o`k<<&OT?l@}h`sF}2NG?|7g^S~{tw;1MF|i3Ctdi-=vbx}}N~RZ&W<(8=v1YCB z=f_T-K(flxKHkae7yZ0CqPDDG5RY@`Ejqk>a6h-$&OlN<(?DWz*-7w-&2YI~TAqoa zOEx>Ef@=*nJsftA&NRYs{#q1G&r4yj#2O ztIftB-u4k{Y@q_h^~4La&LOIJaW_SwJK6ap+?3n>OERshrQ11G%HqEE_@$7jN?AOJ zl%yAEbabeYH$R3bg#^~a2`4d^?kvg&Zl4CP=vXu?Rm6T-!N=|n21n70SHIK5WZGDg zgj22saC5ua1wP(>0(cJVjM1Jp8$xgq<SbT zbwZ<#kDING8aJf#^1jekyqMm%i8AR^{kk@tE4UO`ea&ZVFL<56i6(?Ki18H|c|?)4 zE!1rGw2gG}MEUB@W@M*mMD0^(M=5W>*p3eQ$p*qf9U<0rYRs>2lsxoYYIi zD2+S3y>U;z>@1`0h{3HPaKyZC)$R~3ypfY9uWZlvEP*GY)aV{9ej_?3rz4|$QnH({ zFBI*}>}$&Q_@2-ThEo$LyMJ|Sh-rSvO{we#hj7wE-1U@u6U2W~m2lJF0=ng=MWDaY z@MF;4O{HkU*ip>L*nlnXIOQ>e|V=YqJFrw(&pj%TP zs=Tx~8>94l7{juE=dDqU{^u71E786WbLBY9yL=z=TxJlE;82E&=P>*U_261nj_6(- zAu^c|$;Fs*bZCOlQ0(Yj7sguO^Qo2hk*-`Kkfcg+i=+xni*Rzu`O?h%f+Ph?E?sfU z3w>9I>7~?}`~};Q`9tDsLch(XZ$PZRpL-@Iwm!VwK(Q~qM>C74bjCyC; zHsNV=9IeO0cxF_$l6+_h3{P3Y8 zMXtsR6RvAtAm|mEV;%<_Phn8Q_1{;*hu3Dm(exmtJpfiGh95{p|2dKfQZY=&2Q6x^>!Q~f;@M$8}dTOFB z>Jf2^`LAagN#NQ3$t1pCb(X@v<3}Ue*?kV;;4+3~JS#3rLthD{{#$a@4<^36Q9bD) ze+2_peGJn9_S$Fs*of-e2$eORlDTl`5U@8MWjs424j19_sz!qb0mvO0IxqX86 zed(8_KIOAbm%pj@bKgGG&#rsG+jC&$qV^CE7eAq4Sq-YIdn_wDKIfTQ6=(Zb^cZ7@ z8CeHs^=#(z1OF86?7DmrH|DG;`0?gAPfGMU8`6&i_Y9xC*-uDjlYD$}>KOZZ{L@59 zz`us^E{}^hx)RS`d(Neet!VPwjCEfHj5pr(>3(Yv@aD_c7JKYlP?-E*p)1TXxEMZb?n&Ek<(s1*r^yS3hb9$Fd z84z;}DWw>;_mSPomLr@3Z(R7PMRuPmhCHwmejYNu_GbC26no_(vMJdfl8YIE;DZgD z9u?~}oe-4^Ww>S7mi%8(|@4G|G?NWLT_(R5;Z3Fjct&@W;ViW$l{eh%f zF#JW52ZL_ypHEQ(E?_p;l2hHgV}RVYHYLVS#COFgT0e^aEFY_YXIW& z68h`{O@f!Ci!haeY5_Th@nSlzmzrIac76#JX2YL~gnZ{>s6nopzp#+Tw|IseCH?WyODe}gUt%qkQ2oQFF8y*!f)#I$m= zGF5jW4%Ph%Uyy2>+E+Mzjz14&pRTjobP|oolwOex7A5gci;Q3^v&b#C-AOf*mtw+s zmE_IL+))4IkTIv}fY(;y3!30fswI#m)F*gK=lSlRS=Q~_Pt2d2bgU$(0e0E;TmM57 z#O=TTTB4kG_+{A0e)QBM%7Lz;wdL3I?Nl7kxI^&x(i!8n>Ope&oPEkJRlFR5T2;E&OMNDG4;r z$c3KC-4+2i2LBR@g(z6pmll^s?T@L0<9DmKtm z0qRLYtu#^MYe8nI1f7f1%+=SCcV8nrWoJn07*()M*x`5mNx3nA-?>N;9b{5Xe38`2 zS=}NU^QWMliVNm%V72}Zzu(!P6BmYD!v#!U)xe4S+dh)E%dV0wGGiSVY)+C_r1C@W z=mJAjVYH>8KIBQE$NulzZ=}M~6Y@5_-_^VhJ3_sN*#w^6%N%mHQ-LpD8% zBeaeqQf|

c>bbDEWPnfp-wK14_i&y!>x1}JQfoMuAZYL(6nz?h8K|BHN+a)V_ zC~%Z>R`Cj{Yc?w4gv%92_^joM%~J1drgN%uH5O6ta^63_yBgIz&D>j7@zbbc*(W?~ z(=(I;UL@;IxJQ;WD10`^TW#vCb5XjIpIMb4 ze_}?YPOu8esia+DwOuOelFml!6R?_>jMc7$`jGW7<6r9HSeHozOY0O?XIiT3 ztl}GdgE8I%nGGoNc2sFA<%+eXeW837M zJ`OL@$|IPCqZp*PXqASq&8te`txYtoPRJyYg$U{X z)>(0Q6)ik9X$?ZKLadgHgA#&-X1b&A=d1XJZE7N0r+SM^2;uN}i7AFvzYq__~3bo_LT?se3jJmxw zga14MvP+r~+Zb#07M^|ZH&o+8da>+edd+CWqG)9q*P**!c0G7m_QDxRx7ySlZPp+d zU=Jj$0y9eqei(>1v8@z$e!{(+As#~Whu|yYUX&sMFJmrUsC&Py19$W|GvRtQ$W@(i`;EB%+ybIN0T|X0#R99dykhnaz=4Lm{1!n6`W<&!yp$m`KJW81#x4AC|rv)aOMbS-~{s4ByL|<(d=8M&GsN zB|~9OzqUzRK1J%N>k_Mdu9IKPDP~PGD)?1&m-x@wtNs8%1RpNz@hj2X^o%CM! z(+w;RUJ(&shUYI~u`uV|EM92M6^vM@3}>_{(U6wvh2Fybi?dsCl&vGb!7CR%zZn9o z-suQ^-aJY2Tj+IH8z({75)%d)0@;#O6*gUz%1~ruJIE9cp9Q(R)C5oH+*Bpd=~6nq ziKRLR-)82uypKh&^7Sm`VfPY?P2R!hUshS)hcJe)5U8@I&qKTLv-7Uh}<&batR`!157xZ6K*3e=mr2 zj?nat?f(QBp_l`P+^hhZiD~2EwLfFSKgnU{Zq|g9qLS*5;3b&Z$MKsLV^;i=xV1op zz*HFXU;o6Z$a}rwP!z0L!qf%BBm`O)_AD6^aS%EHS>hZ2#H&c9F3izZksvmU3fi|n z*9cD`2?Cr_U_a9zhz#^uu@4SK)GXIukR(V%7YSsRAki&?1ZpZJE_BF)Jjr`(b{B^^yvy~JCJ7KPF9NHz zgIQgJZtP<2*08u+{Xtwqavz3NWM<6bO zlz$R0hZQIbvI-bqu#iA|MbszUB^b-7&Gu$YSj1cbCk#-62AFB4!-|YQKcIMqMVHiM z`OSkg#v6UesMI4vHRBkuv)xU9(x|I#^ql$Jg;L@_WH6WQ7x4>YxpIG^oCPr@BlH0G z7@~^Kd^`&*og%Q{?We7``w!g8{ZIE7S0+him6o{DE#eBQ5n(A~s$i%`k)0a5mHQLL zCze!BEEKr9tT8ON$FQ6$6gev66gNquSuO*a_2m9GTRhQ=S;6-!sfz_28N->js1r~E zk3EK~7g}Qod<-;v`WTm#Nf^d^7A6d1uPra~qC&DD522F{6&oX8u*PlZYdmf;XuKYv z^4101qwmA)(W6wd`tZvZf}COL+d(^h$NbUUHvCQrD*eY@JlIw3kwFai99avnjRgVq zsDI{4gM69`?W!E0Ua^(WWuafEl;p$9eKb&5<9zUQSO>EUyv&nk-Grm!WSJ)J;h_b9 zWh=v7pqOgL0}EekS%ebV>yX##UjYjVBAIZUEZ)%EXZzT9p-72e>k{7WVf`C~t_bmg z6LE2vF7mvRwtvRj?hSur`2%dcreFWC;BCzE`67eu;_n$G@U;rC8T*p&j{2G(m@bl%U7w79NLb$;LA>@?Swu+DGNhR(2 z6(0~X{)*-PPz^2Dv_U*}!)Zhq*NkRPx-?aB(pdY!Nn>|lXDN9gyiI-sv*wEb5w?g~ zcwv;>!agyNUX&_oc`#hXp|8WI0Nhw4lbP~JhDtUR2ImWXebaksY)WZQWdjK~WEe=Q zlI(H{FR7_F<<`>VGZg&VZ6c(F7Yo<_v- zJovD`wn4Xkcc-g#fFe;58s<3=z4e{4D zIs=6*i+`sQ48leSEDP52*r}$fS=slz2O%%Qd57-EQ3voAk|2~5k`&KAmo-)4wMB&V`OGOXt_#8U`5c89j? zar)VfoF#>ATA(eolHI!sH^|8bn4$5!SKW@}yjKi`DNrN;3=g3OhlFk}aY(G(!hm_r zlml4~WSpGiGfyGDpoAq@05`Ab$lr2gMJ#_a@}p&igF6wk;^^J_RMycF^fe zIFMdq%!WFx0#*Q1R~8g8#q?H#f}yXc=i^jeQf7}2dp}ep!dIh2)o!lB;i%fbBeld z3#YTgc*d9?Krx1MBlO`z=-TgjPaTBf_nh@tx58J*^HFiQdKMg*kY@uE?(XyD%jT1} z*JDQ3H9I(6_JTfff?y}7Jxa>5vt{F0UpR8S*@r9)k}P|hq=bG}l&9$vcLkuw246%!>}_BBjoDp3obh=VD>>r~NX z*~tC6r)Ro4#(S`J3NB}xI?8RRVCxhjRr zTag6-X|yzqB6$oL%D4(5-voHKoew;#xa=5i8%+GPNiy5rXJvXW=o{#C;zrK@;E!Q; zo`g5VV>hW>j93!Ql~yK{GESAkOO~5CN-S<}TIrC>b`Ga>0bj-2zA1~OFErX(Xjymp z!f8u)`P0*JpAls75=L^_169r9BTWo?ui8AYp;`co#2pY!rChv)6l3@z+QW!_S!qd4no-v*{1n# z9|)1OML~eH_b85sekW;@lBnGw18-8!q)lo_;)=VYk3*Q_J+tyho^O|O4!BDVaVGaH zp+ecFhMaesat^pn4Y9YW{nm*P&9YC;q-DU1eM*kb;n6KIw;I*NcdVmPq_M<#VSf0E zw)$wRQvR#n1pn1NktyQ8Y6ncHqcp}QZtRKC5<~2031=c;92MOx{;NK9nz?zv8{6mw zInEVwRMeQjFY!&|0*Enz7SG2k2`v(BosU;?_T!a?M9(3B4HtYg2~%R5qD0s{%h&f~ zm;PInxZz?`;>H9->58(F5S4H=iL>9)Btw#TlN}ZvZixF*C(_}W|8+XtD0FC4Kgo0m z-wv3O-~X;|sh7uWr)$``5RqS;zn(}?;4?fOoQl>k*Wniu{shA5M1<*lnj~(N8sTg? zOw!XjEu39Ie7qYWDCN__Il2Do76j~+Lrx8+OXL;{*`C&^;miU9wo|&uHPY7M+De_= zr0Ocpj~U_X+(@2PIyF2g*OnKjX!@i<-q%xw}o(1FNEnG{i8@qrGujt4^ z9O_a|Fe=>LI*$Kes?v)NXO29$ljo0L0;UiLm&R`WX_2qOE!M?}PbHK|oSC(%7Aask zG9&(fM?^V1&uFiG4fUrg)t;mg)heFoWVR0>#w}E;12aeXLAddvky*Nm`UffNIK1O9 zx>OsDX?4uCQeP4{)pZ;mzVwXU7cimQxi2LWxd;+3kQttbxC-hGCq4-jL|MkJe3Mmk zoV^A`&h1Z!^ykb#{o$h@@IGfSu=>+J*ANHkzkn>IkB#w3bo&S%$cY%+xI`hY8k^u%xcqbY#TGVJ`J1|~oLtX6P zrx}>h8JxjXL6UB`bi=Z&B1>IBN)gd;i!U$IUv2BQwrjiX)?c@_wzVzVTP~1(#&AzEq_$MyZJTja{6`GA6;5oc-QcK6Vm8;}{rR7TBN z>~B}mK*RKL8U+qG^~jWZI4))cBcvgZ(M}%X<7$XkqkLE|0mQAj^ejtexOo^~HSRyy#pH#l!72*r7!p&x}&$s62ypL<_L*WEjSIQ;?uP~6%b?PjST@DE~>v2c~SVHmo z9KR-h;$U={*DrZT^ZJ*+lj8L?Yg;xYFcmo{!qwd|21d`SJ1b+3ysbMgP%|5j`66zr5^99*a5_}JeSv2HDj`gYL? zoC{skxdsbv*b+^Vdj-!#j(1zf=_EKN?MA={Z0?sy#Hyl4R8tH)2q_H zt5>Fdf#vJ3ZthpUb~W1mJ+dYR;85Z}B>FUFz6oh{KC?avYzPdZcz~G~TC&iCGS+@Y zX50m`h5)({DQFqG@YCGoC&*?Q(Ul}@-LU-{v* z(U-20WCn;~6d;D54QLd@fDL1)p;=X%rbOJ*2E|!xXGjRk3}P^^zo!Mq;zo9laoV7T zJtB&;EZxc9lPc*fMCpLe)!W#KvRKS_UN-8D)-D$CXhK9O6fpi%h6SpGRPr(FwyvnzHqmk=BIaJqhXsh4wPx&^&k%wson;2_T}I|g_K;>d1YW??Ad=t&@6}*> zssK00uyg4kg8Tr)+|#O8Qof3l$yV%&4~2EML1loeIGu(fh-3h}Y!tz<8FLV>J{b1z zH>^{vc=@^@U*Fb=08h)cuP@D^%q5zCQqf$2vQ2gtgU-+g!pEeG$-73A_sU+CT^57| z{$Vlm3l3W~^GWf+flI&hCokeGjk5%6Cx) za8~PNK~%ND{#4bDd=Y?3xxTgnHgFEzd`9^od6u?8qge?&b#VT{c6w&|e$bGkHdiGD z8fxBm6k~r7O&%~FWewWOUX~+Q3X5;7{H8QKxs5%n+hq#&IuuT!sHdb;t)1QXK-mjj z`#3y;UV1l!sN5kb$LiV1_~yBO+f?=jVEn<>PSzopin^EsD~W%bepnzi3&eT#`fp8T zucOV~7b!3uLA;aBwRXkJz+ggS9mOT;LKicb_`!1-ZC54jxg5&DpjwXFrADs9IX6SH z<@lf>Y{RX2&?Fpo*u!=!|Ex7pegUx%TMVG8vy zg?fZSJvN0@Nl-}UuVQ<5MOxHsN3YI<@Hx4*+_6(q6unC*lnqj z_H1~#5kXZOtJ-0Bu>5DIcpNerhG#BUx3oPWo*Ks_CU^Q#MXF9?6_`HtBC&Vv%2%E0 zMS4_>C?gOCFrr<(M)~8Xu(QUPHrQ9%;U$k3h>C2?e04U;(|7^R?QpC<9(bPFIAg*N zIDVfo8$!l}`givQ{WLG%a0*aaJ#(}Hm4swDd(z(crx(=49mHF#6i}=i-wv4df#=gp z->EpjXj>=y*9Szx1#qAA1#eEsm~jZrA23#3<8)}hylHh(&~UPE`wZmV09IB~D+Q)) zk^*n=`kC<_{W@)|!ikOTp#_j|@ZMn;o}(BDQMQ50R(7MVO~$HUa$PW?qgQm{`|3hK z+^U)c*$>JorVoqRFCeton9vYk!kdVM5V!Fj;^@BboZ3UMNDJGsYq#kFV%xza- z+TP*dy#tUgymw&1z`4@iJ}x`Q+eiMr^iX%ykVgr^ZELyc}0 zZ)>x7D?O1mI(414J9~84=wzSM_tiY~XyUdCE*P3UmKvRalqGxmj81O6*ONB7$w`49 zJJ+L&Xp{|nRVW+p_4KBpYg16Mcpz zH{MHROq?L1Boi`miPXgD(peLyycw(DSJS|Lj$`qHU8y!-PvK$Sfnk?DqF%1O@n!sG zU9R5ufGSmm7o1kVBR-wqtMas+fw;TFsf|HzBx3YEoZ1}l#1Rb{bwp~j>7w7KoxRGD zl5^#H^2gml+(xnyi5Qd3EQmG)M5Cy|)XIW>IEAnK>`4(#Ng9nZcED2BBR!|}NLq&R z{y^)Ip467($8`=3Eed&+(BxHulviOU)gn$O%CJS1cH-GPbmH0WDZO@>OV}K+;|2H| zp~7TcqsqEBg=N)F))X9#L4^WTqD!mK(PpyiADHM`q_1NCPtAY8F+QsIGtiQWf2~m zVG&k6F{LM&9gmtj?aedQ=yGKDu3QZee>r@YG<85Z?IdHs1y-THPb67^i^!?B8hM?B*Fs< z)`v?f%=9ry_?BKVWkZ2T>1S^zDcy1*%EEKYk=p^OQy2$CAL~ZmCP7H&o{#uWLUDk1 zBkFrA>N^wl9S@lA=`1lJYofFCU`jtm^-WZE3le;Smoi#$3s@#LTs$wJGV^|!AyX}x zy>M2*tWhd_gDnj-s_h_c{LYm1qEOV%!xQmaB)^+n1XIOs#K4sScePp5B|1F87L1fA zL3`fxy#dqIx!oX2wDG>kz%IX4$=&t~Obz=O0vG3yO76DLSaniZ`fF?h#&w+iLxxDC z-s`X#+@G^70K6R9UZ*R+APg!X{>G=pU_zYobm%9*r!^a^pl;&xVt7Irw-t&-+qmdK zdgzL-2xA{t?1xEc(LI7E`u8ai??SeF43B>cW69W&NqpR|U%UY}00Sa`_ z(_(~9e7~33v%BPSTlXaP5rT_Q@aqmmKN=Ly!@uKMnRO{ntC!-pqa!?Bvt+tv4ab3a zO4(BcNQO{`ZV^_nuAVVh<{p9X}nfV z{;c9VC8iZGoro`=(kJ4bO)>UF%{sWg^i0U^R>9}ppd6)uJ;x{0tdg00E^^WrPS%X$QtS%{#lBFw z6MA4z)I1Y4w{zC^Z;Lp0MKa`3+1u$T66HQ0qcHMBtY8U}g6@Ongd!8n-$#zB-E4Srx{+zaQ}i4RKyQLC&}k z%mw^TJ*|WS7b4*EupHc}qRfbL>I7G=E*NA`=;%N*i|RsnVW1EUr5`&|^(> z;UQ0?W|OPWgC2g+g9kY?LBJ>|D+5xNHz-Cqu^jy#8#)6|MWw5MSQP?tG7ga zXKC%9je#B=?84{C^9HT)&g{|f5ehZpXzm~4thaI&CFHK!&Ss28_S1*)$phR(B-tub z`L!_L74}MzK!+3BsvsA$@SL;}>UpFDGwW04uUz`k$NDAydS*_GTft%!M&`T4!P>%-k%c8mR|(yA zK|8?iJ+9$FWJGSx5qF6w@vG)0D{o-b>^xwC~)9r+Z6Y02j|A{N6mv#-;qd7 zJU3Dw&xs@(sr`SI{9*`?pyMCfm-};xr@)zS0+gaG!NE%7x2NCg3|ax$<7iXt4jSVd z((iRypNBcF%uUs0Kevyb%f$mV^~xR>cP^}n`gc7viiNm6jy`=L4rhEF#GQtqSw|(8 z?WLveQp<$t1j=|cWm*lZLFuQa5@A}?2y?tIVW1i!qD0K*{^*h(KO*!w09O_C4Wnwb z>2r+wxFh}6#-J5Z^m&zryCwbJrWAb+WIxwP&w)N$`p{=2ABwyCbOGe4SLA5{c{mxPIw1Gfuq;*-x*$jmntz)bT06vTkm~~XRDYqU)Kxz9n?Lh-! z{tWngjdkp|3iVef0DGf$kTgv+b#+9nyPUz? zEzIgWm=Vv*kvQ%q>t+mS<$zzH6)w1kS4lkZ?--&y@NXR=c7H}Bul)hP^?dq%e26-_ zFCQXNnmrD6s1F_@oH4r`p+sV^9Mm4sw{+9bu3Ixb@X>2{;C~idg(E}X;?DBG7i@It zNqWC=euKphSrtj>S)S(SKQra&0#>(R{|T50L=mv(yqVB8{Gjg-Xx{e+b{a^wvyI-E zn&b$unE~?%hkuo`N~X+K$&6+{(uc+=u;^kSNQ3|&{7>57zL{U)Hq6R5Ys|4HjT6nt*@e#djp&>BB@OP?XwlBzwCo7Z>|RM`MivL2 z3|C9a<8U=?WJkd9Soh`F91kzUZCHlubpE@jxr=tL1RU$lR^-2nn7e5N+4=7#StGu= zo>d-!RQ@|2!i7qJ$I}I+$1S!L*CzoUCy=bFu$N({T84;ut2o+y#F3T*Ph#wHbc`KV zp|e+KbIXXa%jw$8-@#kY+(-+(fy5= z_F>&BcE>*_+C@p9=?Qw>`o1zGdfm2(uUM94zHa3ZM`_mlG3CWhjcoD->skopwBm+hn_ZIOlXVelx|Ko)#iOu zO>)#4@6_fuQ7n#Pc8ONm#WIJqNSBE9O~^Na%P^5yCs-K&TWx&v2$`{RN2SQDsLtw7 zk84zlPzBvC-Ui^#L=I?&rS(~7$7n|Y8v>5FAyzf8 zT(zS$+ZI;pAoeStbW*E9gC|pJP@7@X^4VX4fB8@D|1a||FT3k*7Czq;U9Ou)H(j*2 zbDg5aoyFG71tne{SVTL_mN`|ULmbkMi3rZ&k^VaoUVHx3aQi_-K> ztgwo(_*Y2p-iXn-2dOZ^X(8y+xX}@_o>2^J;m;bUWxtwieTb|}*Udz+K#yiTVSBoW z4)+4)H^EZEfOrvIu(OcB-}ExzZbfYIYvHp>bwE9i_;);18m=bUYD-iYDeC9mi1m4A z?rD91S7{hvr!76b#IYNolNW&v41YmXf&@im=IcB=(^X>m>6t3fnp2l98^{G{CI@`t z$pXj1WwqsVYs=@?mM^F+UtC+hw01q~G1jl(&o%tHmOt0?=i~hO1b;rspHK1U5BQVc zhxI?<&u97bIsV+hpTFbJI{w_qLQZ42dUNgMHFHZvfTR8`&Bax7K<&3lmZ{!s2G`6@ z!;dise2K2thUdrw^!tnS+bo;YsN=IgkKYg1onJnj%EtIE77tJL8=uXoc4nyf>_2Jg z@Pv{MJC$^3qkS|EQD*rUlyoT8c#;nPuaXYE+D-h4AsN>A+97@8vmNR2+3#nKS3NOR zPg331E&~Cx4zeMeJk5Ow&u-xduD{`11-3syx;b81;RC*vAfNIU7n{7;<5On%X!Sm^ z!N<(EBGo-eR4>+ewm46@!Q+Vf4;$8l%$YaYnW^`3w=Bf-g{|8XQFE`InmWarb82ea zMOme$R`z<$-Z>R}*AXAzhaVpO5%^&-vcZr}RevsJF)4EkXYOJ9h;Zi6a8!WpV&qwP zUBQcy`QPsDZp9*HWtINSN6jjo>QFx{_1}DSmRkFARm9WkE8_Z!(&{Uc`ikuPy1pX2 zzM|f=ygkCBtIXuHnMXe?DXKH3cMnf581?p4V6(YbGwP5EJ@!lnZ5Fdod5LRlEM!Gb zuZ9nC_x9_Huk#%Vr~WE$m;Vy3J{UC*A_a-{fUHFuzNSsqqH>VOT4ej)2dqUB6TkLf zw+eedXcfwYl-sMYN3B8@>fY>r-W8=5qoYXou1K%Htcm6| z*eh#rj$VW79a+=*!E11YN6Ax1;N9$Uz9WV|4}3g6;Y z_)W-*45ne-kziF?mDl6A!^&fHxoba1Kf=ka;bfb3%0WIC?UVD0?I#trTt`BsJ8kza zEAL&fpq!k~TIdY>jzoP);#N6S_p0U0zfk?I%arGw=W6ycAsMBKoplD}C_o%r+zc2u zG%T61a9Y4zsho9&ihF+5ZeHclfWJw-p*Qo8ua*p{pWrw2W_ZBR^lukW`WYLgNUx4F zPI>7d{nPe9&F@o@(~V5GjS$Peu6%T`ehzU#nixUZxl2U?5M!ms6mWB1Ck%=@XRO4D ziQZgyHEeXos$wPf{=QDc-XsQjddZy&%W067DuNS5FdxquM~ zvJbk5%uw*HscOv{7aVIkRJ}To;VmB7+gQ?8i&Oh-lL#~&fi;^L1^xa9rB~Gpzqqn) zfq|ZgP=}tpsG=Q^y9*7o19ndiI(o2rNGH+6{&<-!^Ic}kd{<|BUaPg)t1<`2Rc{Nqw@4{l z#cA8{I(P6Z{S~{nh1}Z_W7WExz*>wd^!ASQ-YP|Ik*$){%UFWGi~2@EkCnRgP%=|t z1tb+#Bn{CB+g8wj#8_9A=DtHB>V@;((N3E1`C&t@Bg5WEvg}yST^cm^r{g+~Wr^>nIAQ&}jI}>h(%fhk`PMLdyxCas5H8m& zEtyHI@IUj#oyLlLoFa2C*X(sAUFfP|CK))3`-1)hVsV@g(Py+~kAo$PZQd9iU|Bd~ zP3&QtV(CFPc1GZpLo!mt*A=VYT6!|(Yk<619JKCoMyq>+)>mdtwDLA*;3-zQwYz83 z>(=lSTWP~-2>2ToK8WRF`wF_?-Lf6`0CsRGr;96~&s~d+Z7L)Mipd(83u_~er~&&qVWdZv2SsfxyKnR zb{&02Jd0MN<1Z-Gv5xKsU@Ar3TAm+k3-e>u9GBgt?8z~6KZ*3&CS?)qhbs51fjPFu zg66SHJ6#=drkj*^IMYqajcQ?(XNdWha>!~b_GF0rUS%OJ)8c-Hg%~DZHVxZ$VG{T0 z@0cR30ifcOEZn-Th;6~Hl89L!HC-j=RR+kM^67@Wckn)~$jcL)Vr<9-5eecl1V>?L zqE{kHC&T88u*S+=R7i|eE@pd>SdWWvg}R!-Qj1ukhPv%q-08KXnQ>=nGw%5;xMk2U zghjtV;#JHCY{n*9$Gb9A3`zset+`c5rQIi6>9Er_*@i{wFo*R;Y8BEd$j}On{@awr zHroM<*OP};MEj%FJJJ`EM;6mpdCzDNcK}SMo0rW!wm-X46P2@?2n<1}^k4)TFj}JU zE+~<;Gwl0UW_lO2D^*ePMCDx&@ik$wdc#^@6s;XsGLgLr#uQCdo&+hKQ4#BmG93m+ z@H&ZDg^y(5n*!_(pm;dsp)-P^9?Kz0tAr7mp*2E!Pn0uMIVTr$b1}CrmeL&kd(<3P z5*F>z0-54sO!3%Abt9{g8kGu(=Qo&pK#Lm{N{1wk*i+iccpf06J1ARA8N;T^MDTSz zpPtlwda~y;Yz{bDi+Be_+Up?wEdRN?kT@~?@N3IiMD{VMHzJ|2^Dpv z38+#q+h$lS5$cdn?`Qe+h(-(J`3zeVY!#T<#PTVo5A$et^k>Z6nQqMFxRXL>|He#5 z#{6X))FpJDH-c}o{c_u7h!>f_!fF1`=tsCRyWAdjkF_lH&xO5c=*k&nrF$-V4;^;TyEto%- zOkFsIv#FR3WTVHy6cAB{?yBb``N)H4WxM`0S}7EDYumff1{{ zTZy=+yN~7Z>&umtIu})GV=cQ#F1mrKRBX$)#fntaCBSAiGHh1sqCpZDlEc8RB)mPA z8b`6B1mq!vIDWoJ3q(5^a_%9jYJ?u$1TLfo04ZjyW5Q-X75U;pjD3p z%RXUyce4n`1{bf`1D2SKik6RPx0)aF>fmpz-^9J z@11ST>)G%hKmPG3<7ZnDxAiX4nSN&4Fpq;O@Sdyhx+)iZ=s7A!yxR_bi2?{vH>wgb z{|@7^Upgd4ag9UJYuEE>e;R0rz676v>_j%;$5dsc{*~OQFB!{CM0`8L);-Rs`HtxK z85)15*|_rHf{znJ^Ep}=%=WsIEpXITPP)h-zmFC=65#C(h(ZN=4k%TcyM8srz}5)t z_<})5TM?;w!@2N>yo5$;t1jf@HR{BFN){WD1PiPT3#?qMyyqjXW{u=We%g;L{^u{k z-uuMl|1x{8;OS2eOrb%R!4w7BM&NRZ09Qi~wv01x^KgHatAbm9lB?LX$8+Bw9c)3a zvZmdei+Nk2+%kzsU%-=Agc!PRV(7Y&@OmZ!S+CJ~9&|7STQS8KS#<@Jn;-LSgh((p z@Pb4?5O*CgUuOk@F?Dmud`ENxzBeP)FGi}HLgp5{RUTt%U9?uZffxu94w_U@14)!- zL%_EwXkJ&6tV1{hO~9DLA^%>(s>u;`f)OxVb6Z2e;b~@n{Z|}){Pq9cA^!T-ZAIA* z`!06aPqR;#t9XVT0QZ6i{x0^{FPC_RTX&}4+8rd}06YT6Gi2V|BmVm363>u%Zx=n6 ziw83N^`ZBP`Wqh_$-1?sKHaFNY8v3PUl%mDQ^92&diFxJD_W~n{K6nbkTy+_6MYHt zz4s$XW_%Usv5!wu>Ifn>4%qYnrrM75TgO!V0@0(3hPNgC-m58k?8|=cfW$A{njOE8 zMvJU2fEM+N7A;H*rkM)~?t9aM5Y&VE zy;nbFe=~Qe7k3y*f*oHeaJ;~QRm-uNlQZbIl-ZL}!h)mkkm*)8SIv$~i4!zM~{cWV__8>LtGLc!`%U>@-&F8u&g3 z`ma$Mdd_F)OU)VQgM&5{6gq%4dgjy1J{GRt8m>R;N)FpF!=Mq?g#qgtu*!1dcSkMS zljHe_YALpxGY34dqyT9_L$#OEP86)2qN&-KhAig|`d?WV3ZsIkziz?ch}n`HHl11! zVb;Mlt!1hr3()UYNfsa^_9WH_NNnrvr-)%skV=0Gm6>XwL|Ul{Mr9PS$yFy1!?Xz6{(>mWMEfJP zW9s4`V|dy;T+XXwuHJ>~k=g}yZgIVfT9r__RtzDL4z@&N(yp-CfTr>;MVqQI*j`kF zd4>PEwxXllbQb3DaA-?t;PBtDzawl+>ZQhIbZ%o(3(%bX9U-JHZ3-Hb_V?e7C@YCG z`w^?n>$e}c9;=P&et4qRBp&ocPwC01znzD@Lps1Iic$?CS0^_H>H6i5K5!amz?zUl zjqq?088xYeMn~h?9I0t2#`w)PBEgn$&D@eQ60dW4L&N$v&*@i|-=CESjdhPIAp!4E z%y$?I&36)Ot9nN;Hy*JHn^?npdBlXSVh9F86Zz>h@}0nlXc9;9SiX!w5J8c04}hIA z1p1s?hskpCRM2aHN!f|m?c`Sa9VKe5JowP#%m&{+gki64`49RV^1rrhis1zk%`w3N z_#|)SRDIx&9PMosgdE*(I#}B6q_b+#N+#GNs@N9dd$;MLoSHA@U=%VMX{`EC#NTLG zKV=#aK`>lRp@{FWri)FHsBd2=H?H3&BFiP2WEsvHg*sN+@Y3i*Ba9eyGFA;>$^IkI zN~pxg=?WfPw7RNfGz|lGENn&?%VU?Y4IwIHVTUUoKpTc7b9{n^!K(?zs@T!} zCpmU`HlO9$d`3+qrjSWRwR_J;A{jnoA9|B|>BB0BeV4a?HVtA&)>)o)lIPU9aBHO` z!PfY#<-yvq-Mj2~{;fKmzkBm3g>aM7=ce)d<^e=Q_hd5sqR%~Otl5H4fD_!ke#p^F1UjMf?uTMNa6t>=wxs@4xBpZRm zdOGOiTv?t-bu;XR(u1f~2-blX*Y2=2-xi=n*T=8#SB4{&$VX;{0MZBW6)^yFcPfM;I>Jg=)HB?7b?)}u-U z2oX)%(`eG!mnQ$p5ff3!E+ysP% zJgROJ_Mrk-2~5N_8zBn#&BpnG=`%!!@OxzDJ7=u@Ra#~~-desj zAe$Xe-&mHatbBD2<&AsqfK;3(RNJ(lL7#~2KzUy&HD9RKfNa*4uT^pWcOUu$ar6tl zzH`Xh-5jX>n`m`&*ldl|cjn?$3e=7(>AGwJ`0nl>K=BM~P|!Ra^z9Ra5b^UrnpfaZ zobiAfC&yMsMN5|dc#ek-@2zR}p%-$Pb@p7>VtC&R-n<4>HC8T!*=_M0r@&YOreW$% za~HGJ-@^G{Q|HlabHvJP37U0^ZT4*s=I)yfef<}E6iN~M3jl9qd)y^vV9!4a&S%xe ze(qiUK99N2^BByT5tuVquiUwej`hZ1?g7{>QJwfHc}CJRhxnP@{LD7i{%u*g^PvN? zjd_it0fZmabOtwq`_#@wc{pnu04lOIKA3KCmfOwD2MJzI(0^oEgeIx94)fSYr!1;w z9cUZ1BR+Jx#jG_L^c@T&8>unFyGv2~N#?e;3bConIe5`Q^R{$?r_;On!qNN7ZowP= zsPuk?{@(n)^p3Or2T44Un-j@6gZ{(IZe;+>ilZR@u|*#fMyD`f)*If&0JQl9Rht~$ z4~VlHU8d(Y;<_k|3xag>3r?n2w7MzcZ!xUf@sng=$$Dz6jk6k&=y$h=Gs+Yq=Z-6A z-IY|v_mDNB2H8((1o!vAymO2;Eb8Y=y^Kg(xf?lz`ythmgdPZ!4CP>Z>9_2sQ$?Fp z(dP42>;90{5-VA(i;61;psR@n58iG~NJ0w_WQuxPE6~ET174fVROMd$8b`k>+xpBI zh%S{pYq30dO14 zW5xUQjrO$eanK}_=tfO1`v#k7O6%yI!yDIKdscIm|IRLN@?L|!ZLC-~|=LQS-u z{Rwii_32UY^qL8s0)>%-qx}0i6>xUBY1*kE->sYqTy953l08Pcf?vVWK`O+5-yqom zICvB07@+iHD=5-I9KRkUeg&f?h~r1}vui>+z`x)Fz`wwcBe*yth{NmbKgr&++5Lu2 zig6Rg#Q>)k3@jUXhx*JtbKqj|oH+e)@P*P3;t-i1@U2?!R52YhIfA3yMpZ0hr{iCo zHQYSxyKo?1ZpxZ9h&3%2&Qag;ZxdA@uP;*nI(f<$-M4 z`w?6aXyas0`E3D4rKosQlIL9@M|tkeh4Yg?*n0upXFTN zmg}w3*CFp0u?N`eBojn^hePIB9O8H}ttn1AAI|A8?5Nou2DW8e*!LE^{c2TfCi@+a zxTyD5BK2hnVg$D0a10<8n3@j2BVyV%n6>NJ&)isbwKyFdXFir4J&CS);==HcZrc-d z=|xZ({7F1Pjg@gx-n?S0Ix64J7^^zu+l$7kRtI~$LkBUVLHyPca*cD1-(&5Z-?*f5 z;naY+YOOOn3)t@Xt&-6UZxu%)77iAdg{$R~%KfE05UK#ZAn=g!d`t#h7L-^1AJ*C~ z3#~j;0Qar@#uGfmsy}5W0ozFbUjtjB@GZ9+tFPm8e=Cy^Vbcl@z1~=LZ6DGRWjva6 z*;FI4zAY3wVyt>qzMWMd>Bfz5V-=7%dqc*w28D>;)IZ7YUN|{}WXLTJNQG_j2{y^{ zh6qt;CnAzvDVH$PkdO-4tW^LYt=P=;+6sELff3E1*B+)94(iHved)EOA9}TLF0|vB z;Oy+wu_;jN`bnze*RcOQy;LQf_l#DoO4oY`u$7p4Bz8AazGc zwPjQ8M*82H*A_<@9?(3^vaGEaTO6pGF@0Ji>`%QpBE-JRUem%4>GPe{U(FiY@(($p z2dmS1uxIE3aZng$ALEmB&{$oYBedz%vhiZRAYt&Z^FPi`Hjs4%W++hW4AkayojwN< zH)wXTl)G#YQ4aeQ3A+z@VHqE5c0lJf+H39J#;%8cnM`*77_trrVb4$2 zAFJZE-I~?^q>9%r7FNHgpLlJ&V0OH=Cxg{5%3}5F(v=IT zZsYC8J}6*b;?u_HT^j%E`Uk5dU>i$RCDZ0TaHNFE7*msWKJDM5(E#Vr4w4+eX|fp@ zpCo~D8_$sH}?$-wSxo>>FWC za#P0(S%}Fy#kh`ps1yzYTA?x8|7m@uRwmME*VOWZBUWqt|My*=0z5~n@OCVhjrp}pFu#sEE*SLOu?q`5ckIGL&mBwU)<(ij%jC6b$rJmiwUi%Cnw<6V#5#5g~PCZwm2C-BU5AP9=>)+mJ#db1O8mIH%Tz zO_}6M^}dvf_TGAxm>b_4g8xO|qrP*r%_Dpg!AaHB$rC~ApPf;d-ie2#k%$T2nH_+g zjrXDRing8bLB}74Yv>f$+r~s5>_Ph<*jK z@V<)s;(d+n?H}*!bT-~sz~83uzKR9!YwY>(zKZ+9`x@In-q-I7ZIMir4Lg`<#Ug6X zWxTJjGE#uUIbh>`!Q1|Aod&E8y&F$Wwo=pZ725W|ZKXTmZeRFawE$>_Ztm5gi}YK|^?{@xu=#(xbGU#>`q$ml{LG>1`N8m|JmKi_L(er1tb z9?;-fqf%tY?~CmOHICxqK1+u4fLxp1^QK>AT0Tx#upG^TWf=>WW4O>4cS&ziK z55G2_)v*Ki-lW_jR|=31V;Zn0=yN5#+UwdWHJQMVtg7x(&9$c)`nEHCG zx;>C=j#M|(Cz#xsIl|yLHdXH|_r?rQ-fonJg!%q5>Ri{%W+I1Tndv7(W zmTq>xe=S_bV^?P;g8LSec!-a6LpevbyWZ{iiB7C@!&qR zHCp;d-=0D%XSkI))XE`}LvZO>%gviUzlF`!T9nlmt2gm8}P9l zRUbj=I__b?eKWa4m_N&Kfrq_@e~;zQa{8Q%Y;g4H#Z3(3zV${yXbvr7w^6W?_lAO4 zF@1ePzK*4@&&t>Gp#QY-*xT?pB$4fDTC0<+>o&7RrYuJQO9tF^+>nG8MRaSxR4xswfr(b#IR0MeaU$%+H0^+TGP!`Wv)I{@G`69j`)EF6N7trfXKMz2ats=*!1d+3B0QhFpagvoh2r2dU@U-yOz<>77)9?F*FyM)8%({L?Su z?=%>3Fxmm1rt6GGJHYUE#`Ey-ZCrHPQOr;e@A94p_eGLTI9!7!E$Xltg4A017CvCr zKS4znZpZeu?w4nhNzRgpU52wHdU8~j#Nr&4C2@3)LYyhf5!UEwKDtA=Xdk@#DE7+X zj5|2KaRbLWE8}6L!?7RFlcYTZgk>@Wa)>U%1#3ekN!<-cOAH*dq`k;Cm}#wgzj9th=51!Dw{@ z+{kJ@k?O{XRoKwAV}#*||3U-%2dxQC@J&y1dqLx8jUn@R(ANp={2VCWXBtxv#;VU+ zF=wRu44u4z9f^>+jRQjADNwsYMd@`!{Yk^>awt>3Qm*`%S<820)_70M_ioUNz_!}HCbFj*ZZpmI_RDDgoi?8uID+4%61^A90-knCF z7>5wdar6Czz7?QH1M+O8Bdt7_f;kCCL=OjYt__+BuoALB*x&KsNVD135X@y=_H{dv zizr#I15{l*!$?0>(L)879*$IRj+$*@f1B~x9_Hbj;9aC$Ory$AuPkhhfkoC4@x3FF zt5Lep!@xiD&cuB0%nljz>LR(l5o?JjmfJhg8kdZi^$}|dJ1eCSLZEoJu2W$uFT-64 z;kI?@@oJD7hf6ZTN~EEyc$r7f?p(*|?VQoW`#=@&!{GH+FtG=_7P9Dh=5j8**HRB4 ztJjQ#7;G6T>>WG+7LYUCmGo2pRcWPZBP$rfwY#}?x2|1R?WVJ!IJB% z5F!`{(nkE7ANo?bdQ%c0XY)FVc)MUD30sAmn7Y*)Be{EGma74od+Kw$*nJDaEZFi* zaF_QsFkQBw17UU`^AttIZ9viG9r2hi7{V#?{F|5kLBB;D#tS3QOvJ~{!Iq=p3Z8Q4 z=xNRbTU*q9=UL$C&^xN#T^FYX_AXAv^)h}`C)sERXUf;kr0%zuWSe)wh-Trh;4#}QB4Sh`bH7N*MhVQ(M*r|Qjt zno}1owK072|BkA<-Eu{OwQK9Kd)7W)zl{gAq8?f$+#>QkS-+1v)HgmkY|Sm{BoVWi z--iMJ>{TLW2wI0>Kj?rB2j|T%Iz;-roc|VZW@yiV$K_XO4SpEv2Uw&5pH)A)@MF_n z`QhM$*2AA=e0*2%_xWHWz>UN<0F|jZSmGW6s?ZmR@DBQ0tS{gY!UfoB@Hd<*f1|2k zxh^=<1#$qah<+>{fFGgd&7t*>N1H>Bb2XtSxR%hTd+mI3@{ z-(dc8NG^8D#U6R!ZMm2jz&f9cmJJb|0Pia+KPCkJ#rOhRnywPICVGs5<(M>ZFC^d< ztWjz`;tT0cC$Hilvp8#d^qn%glTgoqVOpb5?1`3i$K#Y|AoQ#$)_3O99a!A)3|QGU zDuaD&4c#c?M?&C7*OaU4Pto-Xejo%Q^Pq&x(?HBYGP zU3C2^Dc^(Z&#LPrHZ9-4-wW12brByKuCAvI5^mA!14JQ@Ya4pLjVQFDr9~vUwJoxU ziJ$R+Nl3|3#S(i?#W1jRJ)BI+exoflU18ITs%PdHVEB{O8~GhyPqwz!p((HX3Nu z#(@s_R&s72-*;v@(~>lk2)|h%qSFDPVK)>&Ba*FS=9x%!05CO>S-mL?4h-G+_`Fti3c`m-D!cVr_%yL%;P z%inzToGqzwT{xe`{aJ|6v$D{RFvB#phhxB0Zi3T@ZIsLKF|?6*52qvXI#@66?jMQQ z(GL=@JELQ4z=;bIuf~I>u6;d61sSQuc!FJ$Qf9>}F)Qv5!zpq%6{DWRtOy%vmRS+r z=s{lP^rau0;^BC#Kb!a1|Afukp1lt+DyA8|o3v5UUX@3?fw9on=>4gWV)VAxWtth# zt<$H+Z|dKw=+3e#4tBp^eQ8GTvHuC9w^{&VSv)R0>U55InyfaYZ^-h*RLk<*76CBF zrqp)hI}^<92#O{>%ffhA{9s}H9>G{@BOLKhXImKgUZ#ODkbxC#uh8QR&GIkt0o*-W zfOu)5R^DM&4vuOgPAth8#uFqSSh1RV@mh83749j8$J# zCdX%H@{#`v$BhJx8J%dU+jx}O@|C|}nzK4_W!=L2XwtiXq*kNskwdZnjf~e|#&6)o zn8(B%+N{Sh+C5q9iaTs1=Iv~5yjdlbK z%GJ}d9BS7INUuGr=NjF)f#(o3@{Ad3cn`Bdjeh)4IHpR50b>BzFEbHz8$cdgS+^h` z9lB9OnY@t-t{DwOvo;->VYe(QL+C|fgMw{srX@ygUu<}{h=*+z1Bq^pH^DZ{{lufK zDD*S#;4`Il$>#C3uJ5WPMYDXBn&kU}1Mp3W=Oo~1*2H8R+6sjD{~Fd2#J_bc+kt68!#)JbPYaA!-fFl{jE!}R|| z{3{@#fIo9*OE9w|xU(fAH5PSxX9MYA)I)7BYL#_UTwcXAiPv@ly|#J1DtkRI32}Md zPydgaLAFL7f(D^ptdYSiYh;jT?Mv}}QhGBEAOZoLxyVzkC{Iad@W5r_{TFd89Mw~Wg8}r_pZOlLE zcsMDA|9GVozD*at7=>4e*IwyCDxakCNyjZ*JTG82*2>NJU?PRF`nXahx}8duxRWlJ z+s*c_iw0!G4Pj10{$s{t-(;AqjR9lQMxfz7wmMgYkEa|0R>N_MwUVhSWcCJqJ(21+ z!pThmYm&1z3Kdo!BZMV~RbFO*jd_NJ2qJ%Za@ehOsdQ(odUwd0?hKnfA%JASn?8V8 z#z&w(sJUfvQGp}JSo=o@G&N0kSa}^$%e88P<@!Xpx+P+{+Qa^y2L{Ld^+wINc}L&+ z@KNgI@&^YcH`i=Fiv;eCzWRuDhbLNHH_^)5QCc^9z2OP;gsn(%O@r$#W7*-^#)%gC z+dSJCdvpD4WA^^p#y{<%3v`|wprJg`xek$_`l%|rCR{_r0 z74g?CH5oOn)v%iQMcHHr8P*C@TEOh+<|qjuk(zE-%yO-X)OY7Ztit9Wj4kK_?wM8ngg{^U0yfdoqhEzu6nYrI&iTlyQm9#GX&^TqeyG%Z+*~9WF3Nb z`!rbVS_cJJI9X5SmUcq8T*p!$P#YNdBWm_UeO*!WT)?~v@oe99h|@TuA(~sw$5kvD zyF`zLe{_%?sM(vE-KN!gW{>ITa=|5a(o3}RFcyG0Fl@HpUhBFc?0@gUn}IxC9GF64oshg_a;Z4Ua3u2a8{3dn>h>pW7^>_Vb#;9U9NGuBOD zIPxeK=rhCNV$hZx_5=R1f`bR>Rv80Th>Xku?WTGYOnDPD5@gs$`BVD>?Y8s7D$^o# zlQYxz7uD!k-RO;JjcQa0X)$1KjGOwFf}7;+98lQtS}|rkqW4$3flU98(W&^x+%dIG38QeSt%qHk&B4!sRwL<(pqBNo~3gV5&);gB9RxeL$Xk{`L3(4~QjKFjO z9DI&rAv=t<|KXOs#LiB&-mMfFtKAYvQRY-Cjzw+-=6)Z$h|u0Ti@tPS&#H?A?Ouss z?b4F#g8tLXN&r|a4j)Q!p;}1q2R86x7XF*DhvUFv_~aijWwD+2h^nIIMhSE5RH_Or zXF9xZd8^TYra}cduF$Eh)`~_KM)dWfv^W?>kYkcH6v%G%daqG13w8tI{!j7dxl)4Q zLR~R)D>4Al8h{YsYYpSyo~YRo_3cvnh7j>1pU6}NR5`_QPN#)3CLifu!+R<5C0Fzc zmU0zK0*qx`g$GbD{`UbeCwHf50WyG>b@`OLtd{V>7%3mCK0>}zWZ%KPA(D9!nxn$( zR612ZqsoE;(97-7-1)K0807}hph3=i0`c!u!0XVzQCuSS4g(kjgsIvk{`x1&~S7Vf{x!I zrnY5v$hfhdzSPlivhu9*bGXwTu%DPUN>El%aCeXj2K+6?%AKm@63_Fx;DS*elsQM0 z2F$v5_o3K-s6;nE#Bu@erYsH9_JJoqzU01z_XcDc*coq#+1r7PH($UkN+9NNPU$;5 zfk?ZYg{A764%@5e$ib>8mtqb0;yU)afUP>eioUZXvCEcDcB>|-vIV*D+MxP=FM89; z4oD3m3`jWOBV8g|-EdqvGtSspmN<2C8=G) zA_xl~!=*zA*+aS;zAhBc8_w>pGdA_AfJRmFGWp9i&)37*0m7pSs5b`e{R7PkUbF$f z`-9Pk?m^?m&insa5sGGP#$MymgG{JBicqWgG=c2aN<^ReMdF1iSR+Kts)Wa~^ePN;Q#GhkZ0C^s7`e=BqHccA&KQLz5q z(nIYSlV9oXQM1AthrHoTlJ0+I5^5czkf;iB9Mg%XWZ&=Al-!}G#AQ#%7&RTwQJL-= zlg!d5jg@}~8z^i%A1RX}|`WvsdhPu*KLiyyZ&jk9>w(5GOk z`{35W!x^8jZ?muHoe37Y_>E(s%Ox>}o?8;sC$0Cb6kXcL1QPwkBzZ8&ZdF4Q?3X$e zIyO#HjF)vJHxWqa5^D4Oe8P4o9-7jz9b^M3?X<5^Taa)s2LVT+9oG67bW~tba>k(| zt@|{SoH$Z6SC~uXJ(@}Z6}DN(_(CWk zUb1S09L( zJ;2$l(7Y4?CdWo=tH4LS^yN!5AC+(ja`<#XHY*qcM(U8U)~zf<#ZHMd!w8v=dU>$o zpAHQc=Cn++T3ZK;5Fd8x+@XUNhxK#%(p8e4rsDXr>oiY=n9!7s2^IA8=^7X+FY{I# z!AOnfg4cic6KRYT<3*^`T|&ZA{E|vjA!j_Ba8|znFT_NodYc^cA-H@7wX@^`FjJ=t zYc}jwcAknZKzUiNM9l1f5eFIC_eOksqvrmo??}itsO0de!EBDhHmI;+p{kr!*HA8U z%ocm2LPOQPpP4IqRmLc*394X&2y^*bad+;aN)`QKxOW%GeS87<=p1|!{Z3!F>XZ7t(>b9#L zmtAf8`ZKxA$IY`OBf~q4K~MB~i!R=>qrc#aet@%z0;n(BWUK3@#wF(nV$)JMei7BjfT!r zL+84Gn4+@~WOw`#L5NTnJlB5pIna=au*>is>b^&~sVq_$D{oUY|KfA#_@VCcDOxXW z0}HaW72FC{eihE|Hbx#nj(cEsoR$03Mj~EFzt6;nN_P$KIW%!fQF}MDv%_BhCC_V= zE2->YpYSRuq^Ix1tJtvUdi-f!^a)6uZR|$+F*Mo=fh^eSSd=f>McmQ|7}EMct?O&4 z_p(s!9L#>Tb+OrCq8g4Yz$$t@c{t2`MY*WlqQP}(lR#9L&Ek$!3$v2k!mM*$tn1e; z&~I>&@1?ge`5d1=%^oH9c z2V8rPE}$C51rf^enrOaKw=g-$ElfVw!+V=zvG^OVS%C+7Cw%V^=8w2(~b z0(zcoj{m((jz9m(pBoaXMz7B`KL;*QJ%i)JvGXlC8YBKK#=09$Y1Z|^bFJE@!oVfa z2{M-oV3B!t@Yk%g;IA}>ER7$<2)r}`;NbFK=@(91DDgi zZR*ei8#&v%V?}v&E2)QA-_E~tG-+*ZwR)S9Fu@rqv=TGTu1-dzjuiwjI zaGuk~>TfF5%v#pD!uP#pN&do%IP~QNo*0fTXtS{zP-74zw-~FdHR8z^6ebFG|KDQV z{;fuAzyI*EE1(m_l$7qQ_24_qHOO1n{fL8=8YFp+6kV|4eRmOb48$~g-xNHNT>?O| z6@VdZB^L#F>W3GOhG?(rpWi!|WHG9&6iB%BLGwVs_(tQ(x&;%$Dx$Eq0N=O+1z0J| z9U{kw^C=V73&Lu|(T6v(dliuG^eBR6L%4ctZBYO|;VkvF^ZVYGh_kh_5C>FhVv0g&O3U%BJ|I z5a4?MI6BB#KQpZ%z%$4V&_mN&1Q02$0mP+ASsV+k@z26up3&H@^E4(;ZlgstgOzYC z;ba}mDaOio&nZ{rnR=B3Yk@{`myJeJsnJNfzi>{WTeJUQ!DT7`!SG-G2mA9MeB;s& z-$0 z|5*RQ;D_-aEYI{GJn*0OAKdZ3&VMlYSO3BPHvd85pLgVge+Elwes%E$;GbJH|Ge>h z{PXuT|NKNY|GYYze_oWyKmWsj5C8nry!Ypy1r~clNTh&L%QDJ2chVk<{75c{+Pp|DDa# zogHjzGq73(-2AZOp3!C>b_<=G>Vrzv6Z(2z&f?+{Znj$%CaY4vEpT zqzSnvj`1P8iHkDG)g}ivFuDV@vEA62wlBI;9AU9_A-6?~PJosNt4~EsvN2MS>b2Cuwu&)o1 zCn`CC7>~J2xnorV5VxLh(Z;ao3UPBi(aVXt7!a3%yud<9<+BF6Rqe=`#?>!irtL%3 zf8}}VXRb;&;L#0u>;^o4eghr_OvZpXyP2m_^&M0Puw9GrS@*L9K-2p$?@z75UTp#< zHbZ;U6cXQ`3lGr%X+W00>%bq5#&i)sKhBYwtt^=bAtKNkq zJpK;jvDY-`-(X`e-IBuq&+x2f(U=jci2buBd5EX?5^n|2?$Hyiab6Sy7!%zN=XOPN zJEPW^(Ua&##H>e^Dg33N%Dor13LjDHlST`dSG9tDA;x=k#(2*=R^bQWR2yK2 zjae7rO;~zSg7FzW3kXrSrZOLq2LlP8pP& z2j$j-a(|dXxj*ut+*stYmTzS@NI6cRciHYp?fy{}>J{f1sLcohFm zAl)IYn7t9}^Uh!{Ud-#f>C4^w0n-mcxjy`IPa_t15?=kLecFK4C}vh;G_x9I;vyi1 zYW5jOcO<}pBUbA#*g2WYPo)?LeK=+KJ5Q1Lu_t&FAMk_29)WXOA+E=W@pIY{We&#G zf0LR*cuCCM7p{I4e$WZ!2R#;k&|&Zpsh~3DP4Yc3vacI!4sx98pNzGLXa$$B-KkK8 zf0L`YgP*G4*G{M64!)C{oww$aR2c5^T#0HN9bUQFSaE-@1o;B!*!oH`=ugsto48!-}pbZ6u)O>TznJbrF z6|UY&@)`rbC5L_d@KCCzB@7o-I^}cY{}#1qrs4zOj_YN%i2a>Az!7m2ny-UTS6nSU z39a6Ir_$=d<(xra=dD#Buu$$tt6!lmESnpt-V&>BMh3_)M$GqUt0Kww95E%n!%<&- zD0h2izGtw|mxQe_NwO6{sdrrpzka$ros+7&>I2rTPU=9!zb$A?>M5oO!|xvC z6vM!_o4uZO;Fp%x2?A)vHT1ip1kn%EfjM4KvOhw*`_h{tR{7a5(luKV4-!OLuCJq7 z#D`UU9=>si!Kf&}7xmiHYjZr}uX_-@e?=iM`Vh+7iIX0L(*liBSa@xdvl$E5tQp@+RnUI9{V-A4*^w+al>Xb*cSSO3tuu=fdaE z9hP%k=#G@)+vph_HIL0UZaA?dXMvmk9$qpC{+K5adNdurrhq@4ejJ*Gh;_R&oZLhQ z@+I(KE1;#GY=(jZ0!-%gA$x#-Thy4eKkQ#xQf91Mdrmyu!U)bNVL_$bsRR`P-H>(1 z+lW(k$@Naf+Co6_@VgbP~9VZb#(vQPSnZY*g@vGv)Vz1 zMmAv1qwKJD3Gu>6VD`W#=ySj&bn$@^VKaZ{J09D8#!lx`Q|HR|T+{Kc*sQ#2Yg)qr z8QsWDwV1yq5V5hGH>@ojyB;*_k$q|=Vlyh}OIa$p*25c7&5UVcH5fKjSq<`rN|0+< z#b$WqtyGzP;q=y-ujHAId^t{L>X?kCd0@REz! z0eiqF*61to%i!A9Wvu>6KKoD&h|?`&^?ms|ik7}~{pehp0Rr2szkXS1KyPY+=3Iv( zm682q|S^cfbZ!?zHU=Fo;lZqvrLcZ+j%tRx&4I%W^oy3en?8ci!2q zZ4#Yge5m7%OI>|36;<$*Ljkbk)MAT6#TsN5?TJ*kaJa~P4i^E;wpRt720@y!QU9BU z^$)y>DhHn1x}xS@dM$d`?9d$bb%cE#QD1Y^+#mBDX8sM4BYu7d`;8o_%1N05IC8|O z0H=Kvii&RVmK-0XJm^+>2l6^D>iaR>?50F5UU?(%H+cN{uuWThh`YNSuJu1BjD zb=ZizR{*0qYQ&D&)tN-0DRtQTSJtYsO8=M&7*TrFoRnVGA$nEb6kiX(jh?=|tQDAz z5;U^!97n=}#yZj$#AnU>@vwitVJ&C=yWZBkcR1|?Kr{=TL~q1FM}73bUaFI1Kav~T zBlYo|s7#2jJ(48JaKyLmgg`ns=-zlx)Y{|O*uusDt=7)W4VtpHjL*nb<5I5v6&uZ3(fTYEAR&uBsD_^eat3Tgl zJo*M}wK>@if^yf$v0v2&SMsWM*j)}QfL+O&gUEBbiJvX=VFAb*Fp2MAIUI zoE~ZKyOINS8dQSVCQb^ws!BreLBb{`VI9+M;XSEPqjnC2KE5t$SSBL{znkwXr=BAFn5ZV<|YoNABxMPuUvV#n$NI z=|(dQHb4E4j$F?lv0O#2=jSXpWbJV{MSDV`pyEpU?p6frGFBaM2ztV{4Jx})JqM&W zs>=;&my;5923Z*n=p)*ZZ`Xd}s}2sUYM|F<#?fBR?i)Z-_@z8`)7tO`ZC*5w-Ml3? zZ_6SCl2%rggz>6vWyk+TG5o1tbSn-2U5SYIOU7WN#qC*i13Nu=^;(_5v*L+zgi`$j z08GHJ77da>CC-@JSnK+l+D?Ge@kzA{U~4_0HUr)^U+GlNKYP0^{gPgxUO*InS--1D zK}4nF_wf5#mGQn}Z$6Rn!paNG8=ogAb*NXDYkj4YSJil0RTT;y z3*q7YL&BumRiIUBbx8mgz2-M%zUEbFulfGeYhGo)<`QW2A_wqL_hy~!0&Ql!D}h#b z2%rFNywiow(vA3M(Zr{CFzD_4FB9j|{CRV`x1xv>si=9$HUo zXgwK2Q(j!DfO>mqX?sxm(Cj_orjNWQWDITcNya&t{BDAIiPX?0zmt%TCenr$<L2YK18ZFNui}QGo-pM0oIOal-q!3S0JSNy97h2gKSQhlsTfCDs-xMNhF3Z&?l& z_<)&!V}b?^(olCpua;;x^rn<&N@mKEN0HOGnDObR*r)+9WO7b$R$wKmrfac``aL;u#0lUDrJLMo}qEk~A+ zAg*GnZu$O~=|s33J9)FQdXynCRkti%0&(+WhIqYIh}T=5!5esU*Srd;_31`zpN4}$?_Um#jbL* zN@l`Ijy>ecZQnS~O;x*~q!?L2tQC^*R^_`r&bkYg?$*~7*4b`}S%%vYWTDoWZ!cpS zv_&}Etu{LEcq_``veUJ8Q-=p!8E)=*V&P?AUyp073T|ns`b39DW^_e-k3jyEkfu_yrVD8WY@;8akx(=x*_C{n&c z+;vId=ya!zO=PeFw=Q6h&7B&XJ8NudHF~7ROz$JE(Jo-u=tk~;Qfc^_4={w)9c z;~s~d`c~$e1YFL1yNM<>2E!9PcQ!@NoB4JJ@pJ4kGoDl5G6_d#XTBYuk@~hT1$FA% zW1RZ-+d)%1jvfjmw}$-3wW$7?rN=yye0ifSu$$i~bgGQZ8za@*K5&f&tPv~L|9`YT z;~#B(cBQY+?*GT!_rOP0UHQ-C4-63UCK$HVqQ*A1!9k6UwzQ))12Z~!*@8@7;Ibz2~0$=luH~n{V7-&Ywq9;Xljg)Lw2bJL$xV&*RJqUKTZ8y_sjAlb`M)IPuS{XKVo;fA2HYGww~+yEI(p* z8b^u>>4e>nm^czxsWP&+vLivyur(%VcH7*DEp=d*xM<2u7EPZ+vM>)<2fEVKfsWw} zDY~OUHVi!(G_$|C-%UI=b%+x(1FkS^uHgeypyVwW4cOz~)8RK)@Z#<{;8*(dP&EGl zj)pjGm7HYP&TWefz~-=D&VSN1po*&32R-ef=nbvmXyH~l1xfuOb3M(zum-ivY!7Dk z!d6-k&K$UB9pWGMxd9;jpj}JVXsmvZl@uqnmg-4|WHg-&=N5SW?H1?5$|rl(lN%uD zUuhJb8k8(Yy;SHp$-F0w`Z*G1e@<|`wBX+8^HRmr`Psb>T3Mgv^RM&g`!I?Ra@J=m zx>9{f^3UG^-D+1#ZN1D+LMsNb_tURJC z>9r?khN{HSPN8Et2Xv!aqA!+(cY-TkDuU5;?Y?}6OpA*(o6OWDEk%- z#jZYgyLtJVW%aA-V%aJtxJ3zst>O-umt(C0dR~rAZ50!gy5FMU0ZyX*5J$w#8;suO z^d&d=8nRxX1^=DhZinBjw^S@acEPmxm_D38jg25T2ul+~t<((lQN=It6#as*6&6UZ z!a<#NVa5t7M^8z0{RPcm?wo0ZDs6QB4HhEa4P0rOW(8c?Y1XTR*L&my-B|Mp&HVVB z!Bc?#xeTpF>p9^y8a_G2o9@Y%X}&`TJ`$gYrhiHEsb*2ItKq(EL|(j_P!u&@6e9fUKekfLNZ#P&A1wD!bF*=1;#=nMk zSNsV+l=n2=w%(7c#%iA2*=JF4Ndb^C< zHvts?ieC;1DE@fs)&#nBzDb<@cc6NxeRCeQ{6!W*cSp>A^QhlEp3*+luAhhY!H)%A zIM+fF@V;%V;XTO$#TTb#Rx)69uzYT~Gj_MzqYlQt2O%xUd+wMG@*ZmY2Z#7!R>{fu znA#zi@$j!~Y5-PM;DhD{$nWr=^?6#8jlt^nVAG+@CU^d5FqBH6x(9v1Z9z1B&4 z_7<}@IdK+!-)zw^-K%waKkHL=|Lg4Z@%}0?{3YN11_R)1rt2%d{0#wn{n$4w^qxca z-@yJ5pP@3{>fWpOW6ABj|Kr-}d0e1mqtEP$UGxiZ#A(<4F8s>=#YoIR|MRExQObpz zc){-TfLlIs+`ci(;UM-}bG=HCAfPsqffQO7b`~I>SmhAE?iKuZap41u^vKAL%qsEB zl#jOC$CU6ltl>dOW15ysHWB<Wl#>({>0YrK6SCO z$$)p;->?+*sKC>hLSl9-FJ*VJPu*iU%#6>;LT1$cgvXEg+HW>8UxdsZesg2U+}i)O zvuOX}#STCNTM4{pU&Br8`B)0Kv_%Zog;bpnU|Cq9mW5Sn4cFjSPpGwA z)_h=&r{)0lqQKq@<1(~=978Ww=9kcc2wkaY7FRHIVY-o*Q=+@jP20gj0XV*TVCl>n zIOwe1=CG4xU5Xwh7CnIUjLJY5sxaTGa647_F;!t=X{hEFRJdF!40D~RZyeXh%^PI1 zY2_|dlT|Xv*p+$t9(#S8Ng%6hT<*2n#BJd!E)qA?R%sD;v&-scs#P6SOWhDtKUQm@ zAuS(D_bc^@YCPCbRp_2YyHRo2EH7LXG84h-J(6Tu1&^3jg+W?Gi|6th^mE>b_8M#c z-l@E+9(1NfeB=&aMZUswaJ#Xxg0)#2`@a!}Hx#E7`uVcz!<`lpwvsBqX&ZCiJwN3rzOTGWm z@6`DNSaKYepWx$$c>6MD8HN`wyxv{LnqEF;AXFDa{CmBO{)bbtbXdD+uACvP!Ty0$ zno+^aa4r2xZuA~E!pFUX%O|s6W64H5FA#veG91zP#o}M5u)$$+2d}RlHcL>HaH0z^u3Py5zl2W1}5>jTf zzt>n;MB!c zRf{D7i=<#D0xB>%dc+*UKbbA~Ds>)my@UuXv#BNOOWycX9EdXM#|S5uQeq!?NCzY4 zfO(o2XpCkc=ARh&r^i@xNOR1#^wmMfd8w}$4?pizsM!C+3(IMr5k6hA(R*fjKI1$p z97e?^?6q9Ln~&d220W*O)knkC+rr+h#v`02#oS=b+8DBB{C?XZd^{->z01ve>&$3A z+_7oH4|$G30=a!mpvEV_7V^tGg1ZI#ClSXE9%RlVa__QIu}ZFnsj<&`Xsn9 z6DQTaP0Ucq%&=HkU=k7I`!|KE+rrgNp=f?9WbUZXX|HcA$6R*s=Vwq4T0$`NHbRCo z+v)Y?o0;aE{R6C}=IS&cwqp=9Cs~Y?h7Ur-c7f2msT5Eh6#=u$XAYzbTDBzv>FAZP zl~uBxj(BJ2hzIX+EohB?g(a17R&?O~OEpJj2k4Dae9XQF-ei^lG)7MMGZZt2l+cq^ zl8k59CSCVW>;LYPbUrt?n;m|$xBnjcVfL7t{bpzX-Eg2e2M4!fAd1_+QeWeRNdX@5x4l#x2t1 zHzIrBXkCVD71&8*4UmDb&uphBrTyQ1oQ6CM;j$Z}KL2sBLyx%?>@e$bu)rQF=O;GU z2+F$w$eANk`wFyHJ4sVK#>0ENZ*Oxk{N%DN+%~){VPOnF8N7aGH7nI z7fw#Ri3<7X9ai${3|b_gS9lf{`hDh3KL}xNg|PF25^bhgxx>nJ?l4f11)T?oJaj?d zTzrj%kowV}ivGpbw&cXk|B_2QPZ!N}fvqZbh=Cyhm>T8fMLtHK58`{~*5TR8{Doou z+!*;>+53L3N`LN4^0~4Y;v+w|u&~^yYo-N@u7XA#mM&V+D!dB|OO3jJp5 z``yU7wu7pyJ?AnWdXz4RhgB-PFsH*y+@BrswtaV*x%I(AqpeJMiDVN=M4SuH+(ze= zpl4e!dW&291q^p^fb()Rx&Zbo0pEmj%>b#Tlh6@J8=U@}PvxHXau`yD45csCi-GV?(qOZZrFn z6L&wWhMfT!XF+Er$AKQj2w);Ks0mJpeQnEI~$@*iWIcvkBrHbfV!qJ=#m4k4L zE#d0Uh-XWPTJKilQ98hf>lPP4h6Z3n6{0(6 zW91uRry7=0HzZ)E;VyT|IJip-xDY?5E4q(y33>M{`=!s@`8_WN#`8{y{vXX*PYscg zNs|v0L_!tDCo9()pd)(n5}hzUl9Y~}ZWWSb;Q~Im0v2lSJ1_a$%{zXTXI&5 zeGAW~#6G>~Y#F{^^HvRHo-&`g+iz|%PwUO#kJ)N7(2IF}YPZ(m{bQrk)i%3OZ`rgj zH)3B-xbCm8_TVu&K(yVh*bOFA?()6)KOI30ii-W{mdae7z_07B$yb#ZU<+jaC0Z z8?5(TW6clMt}OXqWQ${M=%P_-Z~FQu@uOlNFPa$(c!U1pC>~Il71I@uHi}*~ic^=u z@v9+qgc--=!VRP_)n%Qd*#4$9Uh1nGSW2 zQ5JN4ze;WGV`w1;UDW>mI5ywUJtwhdD;u&Ux6T9lwEh4-MRGa9j5=i+_FujZhba2S zB8=NWe;)nzWwz2G%G+;*`-yDO{CnSCev_5=P;~VR%r0%h*=! zE9N)f^?L>^FFe$Wmj52?VL6eRXF;t}23l(f7Y1*MPW>xmRub-Z#;lVeZ`#&|lf1i)l|KVNwYK@0u^c|n2Ad9i^Utx8a*83w zrf&Cp-!>k(LlmG_{P+&T5s2RG3{|&=qTikrsooF3E)qZ6R`NLk{|ir9MxT!^a$>yv zw?^~(!VQm#o{R?QfnapuhGFrd6MWtwBMM^+<1t*>??aZ*c3K8QP-^t^@v|k!XbvJ5 z{WCA7`Bz5F?yzT9sPXV z`;$&T$r_MtjG{ijadV^nS#Q*r!IBKs<{S2`J^DA9#?6V^CY3Xl7R36XNu9+$;g5JX zfB#b~v}`%QCDa0lFHL5GiYa88j~&Uaf>D#s`Z`sxG7pmwClCe&Zt|$@`;^(S2F#!jo313kdUw!Bm^YY zKs=925y{N`e#Vh!s)A(9+=$>@!N-UuC_j&5K9z=|)16CQY_@KSPvBWJs650F+tG?U z^L!#{GH?5mt-{hU8#c%cVAW_BN#gj#Dmkfyk}ZDkbIO`H_8gyKv6kS7$l5_#FBb56 z(G*nSopR7bh&v7~*feOhFXAK1OKXObmU+<}u)JM|TN>#|U1MHUw|()qv7BJ(OK!sP zeqPB&)LsF!I!-shXXLa`+(n-VS7(|7!Rm5I0&93R!fA^p-LC#u*XBBEmlXybi^qhJ zkM^fOr32H+nvXVI6FYS#nUqK4m|V$;57EPt#)i=inPzKj&l$b3_nt{@?DoZRn0?9Y zQX9|2pqc%V-?<&XgUZeVeMu|;|ksRnRFXOP)+&oIzXl_&pKUOZasn`e~W%a2HU4JUY#<%b7|ntWhR<)B6!t zUdvGR(oO@`@JE*>vDL6anthFhS;gBpT)4>J;P!jE7!+t8dnT8Ks-fI*-_S%y!6tT7 zE@c@E}CAZ4dpoZP1w8DH_p=7#(pQ`CCcLN=@b1;HPE+AE{^qeq8bV ztp>c*@7<0BVB3j5)=tGVfb`NzogwL^h0}Gn_%pZbd}Z`rh&!a3a@r``k_6h(94J}y z>Gml!{axI_?J6|-G+vvm{pSvGPY1cD2c)Mvxu?6hr-;72K1@yXsr;ga=r&S08CCg( zsIrN>?UHVx)9AdU&e)(@GPg?-WYMjoAO+R__L9+X$$$6$>C|NQ^_sI)S-o@gXmJ#rj29O7ka%$(-BjmFwQI`Q%?;&s{h!4Ib= ztgv?aZM5j;ejoa|{|x%&%zQuk{Rx`!p~VdWsBi@GRT@R>x#l?=n1oCe(2$Y+D!T0C zy9(980@Maei}0slV>R+CewfTaP))=~_7YVi)jFVsMtD@v>jgqn~2_8{CuKVTo~lt=`$?YErT7dyCezZ z7;mrfNG9`4i*+{q9iP${7ETIAvygKc5Laxk0M6BI(R@cZI=L9n6u#BW=v2COLvct2 z>s!cH(Hk_dRc$&x41CExH)vF5O%Zcco{xbA!sFn>RT&;jES48ye=h&Hg96QT5Tzub z@uP_6s|@G$qf|eer~b`S^iK+TyS{&2u(~bm*%Vf;=n7aV^9*wpwI>823``|J zF3Mm4mz-&AD+p4Z1<;2Qsrfz+7X9F%vZc&odEJV{3Z+M4xzi)DR3KK-I2DL>**H04 z*aNY6tlk@|4jU)N%5kAskLjyphecj>DZKD9iJWQ%UicFelOshL#GzfnYg+)RNWai4@;Po#H%R;kNJf~K_JSl6u>|WK4LGo zJ^C_xSKpinn|l;|M8xb7_y{0Q13n@Zk|OXC>ole>!)?QDc87Kt_i_A!&+ahpOUu{l zv>e8L=R1sJ+1W#t-z2CS*!oLdg2{nxEGXc`82~D%0LlSxtMIViLjQ3>z}>Oq4srOG zvJC89RpoH$jM5h5cZN$Bun;?3R@A0rn6Lg`Ta6lN#8#tQqkw2!^9y2tj9B9&sd196 z#z|7+BwfI&aZ;+rN$30j?-d9Mj+eri3xtF<#u2kmAS61%$nJZ9qf>T;&1OJIXq;wb zum*AWX)JM{1=Q@cV~JxlGu1=wQktn-hu9Qb<6Nq7{fs5v$89_9urSW0Z9UR9aGk|* z#FfO6)HdcL&J@h*5E(267GWkp=*f=QS~d2UD^}AeG467wh1-I{@dfC{-PDaqgBB_c zn#?JJ6~N4F^h+fNme&EAZc7e)K}?wV7$gNwSRoJ`DTZ;8{Op*Ke&sUzQc_OZ?yDT7 zNDaK5&)cuvgzHB0Wfd;_6t84aUk3cg;2#Bi${xlKT(*mo_~!KB-~3&)(VsGAv?HK& zlQDy&R$5Zo5W=V!D!74BF%B-Rs=3=Iarct3cNew4hbJ?~Jabyhoe0Y*--QUCL`F>^ z0@ZgW_}nsk7lS;^ZIc4x^-|GgbLj6;I28DT27cbaECGNF_rLY>LEwM_is)0I*!zGW zVAQ{Rym`eg!DPp!asP)W_%!l{*>B&jw!sFoI^I{+CYG@kXbu^SW3n{3HznH|`ip1!{NF;7&-}y=ot)N$T;au8fJoJb|5qD`# z&3Ee>7w4fdpM{qTH>PS=!Y2qG`w_W7cTV8w*m2+8%vT(2@DczVj2TVwpw8vYt>)%3 z&dF?mdINrOgVYFMw)k^aOvGLdjPV#Z`G-J)Xr9rKRht@-DZKdgAjsW4|A++N@8Xd6 z`EOvp@1y@-i>+~Jw|QwkqC{R-y@1V(%ErHm56$7J-NOO$N^r9 zth&qh^L#vDue*L1gvS4i{8H+M%ltI>co*J>0>qkEMQ`w_`OH6!|yb9Ukv6 zyJbu1ojzFmih|X2(uP0EcpYz04fe)~_@#98k3P#WZ4zxf6Rr(?U_@E!#Efhf|iaetwnAgJ9iBRcbQ;qkC9Tv5v4#K{BStSWiz0Zb-UQaSVWKnjGCij?XE@HXqBF1xb#syAH?gy?pfk z2CfmIF&{X08!gPyKy-#P%|5%VW8g)7c=vA|;A|r*JkzMVE8CXRB|zH`=XCKKvwLvt zI=1xgnu=5|m+=@nADmOlci}jt&V>wLR^BlXzaIPD$K{ql;>~TOt^pU90nn;Cty5u- zWZ966D?FD#Eq~4u>`X%7q4Txo?8bG`K?0v@S-}9G$!(Xmhg4{Dy?JoN0Qu?Ts zFDGwMAvY0u6O8O9AQgO96tv-vgfg4V-JmH&+RWMkT0v5hv#Pyq~Xj6a4>T&nH_*uo|hQ5yS-P4T~? zTDAd|5muwkECyC7hRz+JSHFh~LV@4oMNm@U1K(Zi{Cra@9P^Eqol@ z^BW=)RI=rT6~1K4^oFd}sYO(B#|bFi)&UE+jU_BzN$HtL*pTD}Oj2XgZ5yzp9|cOs z3$G<$^-=v7vA{UQm~`dm28Q9bCFTB*1xZJR!(7-+g?Zyj4p_Kt=XB{b5v7OMV+Gts zJJx2kQKve`g5uT$Iu}ZHj_-C@pmaD7rSRz7{%r}3CAd|UOcT1SzRe#JarsB`Ts0PP=2=ptG&9!g2ZLn-<32ru?@_E2KvVRaUvk8@TjENav>hh&FK$O1Zf z8!vb95`6Yb9yRfd>QE1r(y>Dg87^!iHi4suhGf7T7%;^V05wVS(PBt*1X5THNyo$( zlS~bkz#$I}8!L#DK@63(o+=c*;Qs{`q};_kg|r{Zc>5%X3x# zfdNa@AFvJ8g^&^dM|n_ni4_(56T`Zs)fcudiT2_^LO<7C8cBO`#1j7Z;kfssD+Lh| z$GxRjDoDgORs=K&HjCBqqJVdYv3C4P3Cx;@Q(@Wph=|W=Y5y;QMtoW2MG+}Z!yu+Y z0}DzTW5+GngCjQB14|lA`Tv^P&L5GyyaLD7+Qx$6{zDu*B@VTB1*6XuJ`H=pgKtVo zsq9ZKRoO@CUS#p}H!o;$`Zq7K2>oiXp=N+2f`&k^6>J#$M;E|FUNcrb>O$1ZVPlQy zQaMHb)}^H1`&@kR$j+K#IQrLrBbU7=KQe`5&BxE30`%zY;A3v6EZ3W>xbeuCG^d>e zl`!#7UgV(9yKnhLZKFj(ZX1Qau}@L84VRHX3K@7Ey#CL6PO~su%sq|2V^ zkBUtkt=vBR@p`&Vd*ogTKBkXxYp@M+$~%0H@j55ExSGmMRwY)!&0HNHil}f^m+OLc z;dL(J{?oNg`qQ-(q&k$UfS(kh9ukx2)j-Kx=y~4k1^CAWe<=Fg%+Twatj2KkJj(MwFGvfDrET>71DNGqr95xvp(-l$ zBK}z+t)@y5h7KY50B%DSQ8_rM)e%*!o1hw}F>n}4G*S!kcU7>qc?{lvf!Bt-*=1Z_ z{w>A9?*#6m8MD{%%?c5P_+JQ`2rtc>{e+Phs30az(BDP$*KOo6?m1l+(dE-}S?cq? zXFQUT%`A3F*C%;)T=C=;2Ajj50DeLA2F?jrCfs}d_4EvES(>4;I}9kl*DFf!R&`6s zUY27Is@qpBrrW{lQ#675b$+d!<3`arqVkOg3n2^|Gp} za30p*(V^Nlmk``1@%j^gMXxhOSU|1pC_ULmV)xxLVf_0O$h~LG!Vrh|bIL5@9$uao z1*5k*F;RlGXEO#Nq0<^0z;PXZ+hz-oK5L@sa|^gKH&;|~h~7HF`8z8+xL(cmffbmS za|=1G$JbVHRS%#CsDjyLe~~dO``#;f;_l`na*~c{%>6%udrio0NZmwsVNMY_8 z8t}^N&wfIs4w=S%!NB7c`amgtU_5^yuY7+g4iF`KnNMIh2^s+;1T4qFC~q|GyEkuRvp z*ADW%{BfOmMeo#kc!fUmw0vf>G~`kK&AK3&Z$!E>CS4hmE@+Xi%pzTpophZ{Ito3~*~Xkr{8=Fp|D0p2$*KkZQyWpl zFS!LPN$PK2>Y$CHaz@ZGA?r$##&dW7TOR|lk{x}ZUS8cf* z70iyI8Ay6q`7YK(0A#$)cDu^#v6sutZwz?{mgV@88)$CljSAJaLSVoC!%6x#@C)_g=VF7hp|W`xpm2)eP{F>7Y{`nFgG49Kz~_<4{?K9EYBe zjaYVD#xIsVn2%R#mzEuzk5{s%7s;OQZ@3yKDVzo3IRYG%EypD@%7>bgE&caDnZd^_ zSjXuUg~N|;j<{RXF{Eh?Ak$W`!C@6Vjr`swzqtyBICGsOqrI)k@7+XyHU+%2r`_I8 zOPiP@*#GHEa=Lo(P3Y_+=JAMm&^*Hs92aRj1D^0FzuZ!nG**@8I~?A##+q=xcs^Mn zve)LT^HWj&uvAAM$rneb1>)%BQa@M7&(jw<9R0;4jC*^HaIZ2Ic>9)Lrz2@nK2HzW ze4g+HhjVxfgVmCi&1?xOOM`h9))02tkK^P8pFs0O*u1LFWa9(2DDj=zjopcly|a>h zE>pGzxIJ;8k>?M^Y>xPiSS9{V2uvrnM!ON>g_IHE8Bxj_vtA2U_wweZj1G$#K6;sW zNZnX_8Lt})#qPi>YTh!=<;oghLASwaxQun-tA=IR*}*6BHB4Sa8xAOc6==vKW_JjA zSh^!-D^OAqkcf~G&yI*^eJI&HGdlT)mYf1F;SJ3!E%yvrkte8omR3M97p?8-lY|>7*HhzeAH)wnP#MenOX)63(OSK zmD2(<%}6;&ac%&sI(BrT=kECRsBWs&FzqE}&$+_|&+nYAvPoQKQd(t`1}z6Edukmn zm|hvXMJYgBqwe0Jq-3<376LZvKQL=PdV#@himwOrB<8jva7i)fF7e@;s=E+j z?A~7D%CKPf5{rUA8M}AO{|0Zuuw{AheGumK^9HK8lYVTB9r=*i6S}kyk3Z4D50^nv zqj2Z~WvoNk6^5O`_-w-)79Y9)3+@CFnLQs@4GdNfg@7sT9JWR#sKH{y%}NOOS)9jl zi9N<;4r(hi6^y4(0gi}m;~EpoNh?7BEr$SdVkP7!DD7-2;|xDsk&RHs-SH93tjf9 zy7m31>N<_HEhyP#Or3bh!jCRzob7UVLUSQQWJ5Kk;It+Gx%24n3lM94jC6Aadb=lP~~Gp!TnA&%;MHLy;&XY)!wnL{$>-5$B z0UsIeIa&FbIHw{d_F?6idIZ3rM*YuJin+RCmRsTi1~>a{X?JqAEU z0RbW(GU|6^$cz317|=H3p_U98t8iZeCcxuH{jZc!d9(4*KPjU!pVEz@KI-~OY#Y7k z`#Zp1wCr(&y%PBTK%je2DzzPHW}{JnnZH)_7F6LtC7$U$RdXe;k_=B_)f zX6GNkXyo?^_IHuog2hrl3?vsWMK#L(gBurw%WzuK5Zr9^v%;>qfr4waWB+v#cKc0O}oXpf`0 z)Up%rh>y41iFawL>n2luQ|S|x2wQtyaoG-|{xM}Geh0bb_OPjV<6;AC3*eQ>aK+ru zE+5iTqq0ww8c_Al*^MzDEr|#>0tTRYNvULs^P(9Or6!*;dP9%CFaA7MWz1z{ zX+D5ruoiEPKd$HxS@uzuhJDEVwZa%TZ_BZiX)CukR68iYfoCg_Wg-|9Z2g}-=FT+zW3|bo>af~ zrTTS1S3W|&{7U!99a}6q>xxb4*vY=mSn7DMq0S`usC&D$%2pka2Ym^{pTWWOJuyr2OI$>YCTGMh38%tj$}stw z))w1rt;O1OP1_~4Z1O(tU(i~YCi6_6+$Vd;RGw1%7zbjHo@`=@&)>sm7_}_uGmPb9 zeTL5v@A0aqYWK0d9JiCYcAP&rN-+ODK7kBbPS)>9!Qs-o_OiAzB5F%2efSF1E%&_ zRv3z=j19%}*j;=Pt2|d|EAcvQC4O34iC@rG;zn&H?o?LdgUU)Qnl#a}SzrmuTI?4X z1gP{lkoNO$YCCc9n<+c7O_?{$9{m4>|M;(O{h#w6r@kq~jEflEw*Pl8(@_@CQ&b{_ zmvIU0S<7?#e{6rqAWL$kV5y2)7!UGGcLEWz_RWf*0%MGrG2lTSWeOUaf)&2ma~0k{ z?C5}leAX$BgOnzl7+@u0Zq;o9SfB#` z2SQyzf7S1kNwc3$k>eRjL!i^Z2hh2fg}up| z?4a2fzueP7OyZ=;qxt8N3Xr;&8GN-vEY=r|HXPB2^B&{greIXK1IKhM6w`d3cbOgh z#2`@s9hq$mQ0OyvnZUOlV1r*l|8M^TKtI6dzXP+STF~Y}u$4a6y>!U;4gNm!Z)wom zw|Gp@+?<@~q`NY;P1TJebi;f`A$l&va%1JS(8Iw{`MSm2N?-J@Bx5W31?#dXY_`Cn z%}vZl6Z_Fb=mz|19w4e@B~O}anhwivg3Gj9=!`w zEp}wc{&Xj9w#K$wu=B4Dr6z_ZO`3m^IsU=#AYO+JXC~)g@_QT2A4mvJ!#c5_HTu2H zX0y+@z0v1wT0V|`(RF8Zy0Zmd0=632o^~gP_+UF^K!Jm%LbT6I!}+ViTD#X+^{ojC z(tiE~LHFy_OKE6=3fn22kV-|%tL`-uL^R+mWB|9nbb{E^O2wXL(9iyYqy=;j6k6%p z-sPXxIDsjf+O#yA+H)E|z-p$pp7~%>lPnrO^8}XTcD_6tHn0EuA~)LtF@povZsutuFgrj!>hV!z}0S%x4B%(IXM z%2GBpLDhRJQoWb$YQPgX$?);4?W)Nb$rQ2Hrr+G8%w&wI){PW`7ci5Fev`GpmVR?P z9ZFBD%`K3hFnODLQwmzA(oFHIt$0-%%u*_6i5dzj7ywJX3AZRt zy}F71+asYrNWdoQO;+29hVGi-wh@UZ8c{)KHADBQD#HRM_{{ls4S7DytYO0?$V7xK zCtbCwvrq{>V^eObup)VGRd*gJL8ipXNXAvsZT>Yf2&$>T!(H#N4KI|Nl1C6~&4_2m z1vH!6<1i1+l3po<-~QK zVa%S?GF{s)_6c!!WqxF9#Gd%f6tN|e*O9OhTLeF5N<6oSok$Tokv4WG{ydHxuyf?` z504`UKU|JH@!@miiIisnKAoqt z(&EBi`D=yWdIV8)+GUDi=tk}nqp;h~pX=RU?R=%(7SoLDUdtI6E z-jFY~^Ou@=X4flA-V%Vr{MMn7rKSrS1vK6J9r1@SZ?9)NcOTo6`4P0TN-HjUNWrgQ zT5+sjm}5p$k))kejZyb$2{3JpU8O<+hkxxlF&)ZHF&%DDCY~^VK}?6^b=AAX0QE}B zawzeBLN9GOlvfWao6f67{g;&qtzK+>yM#t_cY;PsKVLJAXrnBB*Gr%uEa+Ux?)WFr z#JtpYUdUK&$99-E7S-wpa%o`>=vUQA~3l{>)6&Y`(4c( zOTSa|ZEDUM8LE6HO}>@ zu4_i5>l56@Ih|I8RWMBs+?UGK8&q22P_)d+!Hf^8ITA+NTw?Mo6q6%;ZJV$_y)}>Y}eS}h<{D6u0~)_sLa=o%%mvYX6ez_TjBQ`bhVbbWE23y zi~7`D8B`f>UuAWFssl+|2i{BXKw?-2&ZTwWoI{e{Ua$l2UAO~Ds{`*@9XO|}9ian@ zZ5R2WIPu zq8M7u7mUD`3rrXEc$+l@TdX0F58yk)dciz%=kPD8J|aD^{Ute!_g!~S^-kD(#k^2T9hKus)UuQ6dLY! zhn(dlE3qhQBtu8Bwz3qxt@UA-zC8@KyJ{F->HiP*m6f0VAH?U${=sa-8$yv<+j!HT ziZ@*G9>*K5$l`dz|MKa0!%lYn|4nBm#~TLCy53A2We$cLY925LgAIWqb9X~|VK5bS z2+0E~a#(e|CyX_}%2isvpJ<>R!1q9d_F@*)WsT6FEy}`4Bnd_I(g7w)U=jyuOj%i2 zmL;C)Fz^N3{4c}Q1Q=F0;PL|w-p@ITGq2?U#_`JsaE8f#qzZwHbK1>SOogVKRM28& zc0@iBp=mJ(fn@;%pwyIl4=lgM-*8h&W1!&+U_mqsEQppekCrigqM!-_7Fribz(Qjd zGa9I;0~VkUoR@{oq#TI6Q)p{JCFTjg=Qz907iZyo)oRSz+i;nWW>j_SOBoIoE#^i= z$yWBFchL3BXg&wWUiH0Wxhx6hujc^BflO}K;NUGr#dYhc3YxpyIUe$0CfC46>-99b zEAknX5zr#p#i)d)K9x@u!9!Gl7#MXo2Y0JZNVUy!v#M2M@u=8VJiTvVP=acx4y(SZ z4*3?x)1Xj-;|;mWRH0!tefon`*cEu5HwL4146GHsbrj!N#s9Vk%x=C|7eF{2 zUcqh@Fd3|&j*V%8W1APiDL=tiPXzQ;FaP^A7cLObmb|(~hfcISPyUo~DXu>7EEk!k z%l2Aj7s#IqF4fQfz96?s<+w{o?I4?jstOB@yeM`?x|QHR z?0HVV7&|O4`hxr_l3y$!rRBd0H%tmQRJiCrcQ9I7N`HIlKU%gc`su#``Y*w`PkX2! z(V`uNVbFZ!ISB~ABpye$E8vn`wcx0Pg+EJwgVimx=$D0}g4YZ)&I`OiWi_&oY5`U? zGmgdTk|33jR1b0L5q1xORb-ru?j3O*ZcT06q5~8N4yeK`Hetl$lQRSP}KOXb#xGltU_+XsVZ)BmDI`uW5sU1 zA+eiVQW1~otnBXy9VCEr8rNl19+3K8u+@iP z((L?Lt`)&qCb**RZ@oYlSDoq4l?cup`l_)-bwd@&Wu`C{x=i|PjzeYgV$Z5?PIlr#)^--0)6 zq*0NzQC>^D4_OnoS`av&JzhHdH8_YzIu%(vr6Qg&v$8La*~nU)PFA-wRo_9>_lF}X zvd+_F?FU(ZengRVp7s>Oy1*YQk`Mi=Uz4@SnF@{VXL>GRdM;pk&a>#bV9*{_Y|(RI zQ1JPvUjzIxL?d4@lN(U^Hiy?P!W8O8pc3ZnHS&n#W8;W1lkn3PnHUni+{q9@h~<_P z=N0YZtIIEKHi~vjl(-b`<(N}yaVZMR?9H1!Z%_x!s+Qzl`FxMJcFSz2n|VC~bKg9uZtcq3N_&erf{hYEe4y_dYAqw+9T{h+zWXP(81aVEe_QTYz|Edt=ZiZ^@$XqElq z>--+WLnG8B`+3|jrViAdTz(&F4iqE1%1LBbIjQrf817&1FXaKJQ|pyUkO4~6IYU%O z|G$2cJIi}xCvGgU-RKS0?X4O8`)jF|cxEuUIoakNTJ}7?Q!|YtFp-C%Jo|2R5y_#9 z`ky!%Bw-GcTr~GzN_F|+K~CMm0oI-UOKLA%_!%XF>GOxzd3~HC!CXtm;LrGVDCRe= z8UHw4sCzrOtL^|;s|Hu!gQBsrslWdUIfmm?ovcJ5!hG^|8N|~5jr3Ty?SP9$AY!(` zvzm6@+M%pvw?}V&uc<#H6b%;+BpYW&b55I`{%DTMw2;Mr!=A?U{AU$k#JTN@IN{~t z0{c^eq&@I^JHGc3bJwj%_UJ)smppnV`5XsnjB@TV?rjxq*qlyuaED;V_)J8sf2+VK z+Cz=L#P=F_iDRg*evLbKX94>lQuimZM}FS7Cdn#*Ria|HdyoYVW6mX?r1DEf!9oAK z<1r?$F1Z7+-uH!l<_VwK5=W5y_`Ji+lkLVsFpOb5nwR7}Fuu`j8coko3-!yet) z#|{hQwa30dcOc*;TZIQJvpw_-!X@>sgZj1++|C*~THt~{*@r}up%Ds!)m-a z&k#`FvYTzUn)n4ZB`?F~iXD1R+;5a$HF#LK;;PD5a~(kPLv(my*!#V*Wq%bkBSpSs z6M!BH8<6ERE7%Z0#@HgH6r*wvggDsz&I(Dh{=Iog)+XP%AXt0)T0R8KK&>W%_^&FU zOXD^yWtT(`>(j**chKFSaa$M5H$TP?lQw!Q>}B@gn@_W^Xm;e&O3;=xqfllbd_DqE z`(+NPkC+3Tiy3C4Jt)VXBW4W#DI7Q7pfn}O!Ny7&qS%6zIR1AK$M+hmzHkw|Z}uB& zuD{5^8LvB?;_BGzRH5t>E>bRRV=fXu)&Zw-%SlWScfy2IiSkD#C{g|Z{BI5-;jp*g zZ-o1i__5gUjW54B9S{iBbt(_aUhcZ2sf7^zV5JDrfFB?s8kB(plW+FiIW0l+`b}Xs zG0+BxyX*L1zaR1~M-U)q-V|}N^_k~9_3WTK>!jcF69=!%uX%p*fGpACkt1ou)^M;s z+d57j%oWt3GV#liczg>;x7euy+MkG2zr_N#Tkf31F7j&zh^95-8TKQhiij`Nyi6=%hKtI{wC0VCCW!!%tgBE+uI2iU1;Lr7ch!A08X zH`!UO$?Ll5k&(X0bV*J`%e^Nxgw?MHBE>iRGons!+b&En9c+7%W?oX72}8^(yc znGwa$0J%~=Fe7UJk@Q&quDI_n_1D-nsNCw_H`JeV*GRG+X2XMu4H;hTP^{mfL;XJl zmk;K~56>lTcy(5mo>X-bFoMwnY#l)gmS;{2H(;MUd^ zYfx-(S^AEVT2Q#CSTdm=7y)^m7n&3RyH4>9GI z(y6pK06Ph7izVCP_sv`9dRC%y8Ul|%1u~EhJ%1o%w$VMDsh;2?$_Did1}H?_md6Gp zyy#|X`!N-3O{a$59+sIdIoKm&SEn(f$=hVqtyWRhRWXIpP4CS=#u(lERFSW%B2!~l zi5rfgz%fL#RhN5=dYoHH5crXb$liXBarLgH3v2$`tXJ{ah6+io!fZYy zF|!i~Nhl19(+S) zC7BXu7&Q7;_7-W0-E6sqNOgEC{FXvq<%n`We*@_&XERiDlPbdT)rK)^qYAGM!dRuf zFmP+)_jcwA68FUFoh*876xS(q0*hzi732vGB-Z;*lE!*w^9_*Z^h^rt!#Pi${*f$J0@mT`x{j8{U-_2nXxz z4Lih}==$gALdPt(E5v)l{ZCIgHJo@WPSZdX_ZbzN>?+NGp z6}aau;f;vM+RH+k1@vZTaNgR=nzNV<{a}0p89O$rUK5}+%{EeP$Lo0 zSZr6A@1>HU$vu8MdhkjrSiDh1UTfE;fKhb}Wl5HfagtG^{y_)YfA!l-Z>afv-Nwbd z739F}49OOVGuW;0V;osaZ9hH8Nd5ARn$Z`ZKmrKIeLtlHhB^B+4O3q~j4#7+2UdwH zhHyB0-&k>G$tI$TKMSPAaP+HAU$Rlh7C{Dkg3SzmlCiUX03|mqn$tqjnL~c_zXWh8 zXPe(-N1BkQ8KU;nTu=T62y-E%ENr_iI@uMD<`hA+gJ2krX1RlKu_bvX7`<+Th<&YC zQ%RI%Su(Rfn3)JhC%a3w2kK7N6tfI=UpGX4pg+R-VjS-f7=l7<1eaq4z>>zfK2U6t zxnhf&)Z(itd&(H`!s0V&{#f3O#;M2pv^>r_EswEI09G}HIT(P#DH3ISl{85CCKl!> zCp*OH&u0HRhSScU!a=H;0Hr`rp52Zq%7MdAR=ZO z>Z*gF`-j*;PJMuf`34;+66liIa58d1Lx#&ne-#2QME_XUQ?g?b)PshQteS! zu=Y&Ch!%4OT9Rc`v05U|!u1iXA@i1Vl`>R3JMzF!CC0r0s(01oWB7QFMUKB$gIoj$ z#A#X`!EYVow|Zp8fa2vl9r2~;*jL#-=om*K$M{7n^g3o$f%8|Fsz?at&g!vNf(k(X z=J#ll^hosmVb*T>hr(*%{$j5nysFICh{;7n;;%gnY z(0@Ef=~D^4GNAOkDs!5I{!4{^uE||~N}PpK@4?Rpcn;!7NN>MUuD-Tao;3EuFQ)(K93 zi*Z+Q+~ z2I$fkEzM}L^n@e$=RjT9DeA&YFSXQ#MNUx{macRxnh0`o`VJ{Q%5Y&YO zN?q8m)P)sL7nU|W%A3Ub8pE!Op)uTNtZBbk482fCA{Gw1NLCd7;$o#BeClFx(JIJL zxS(s3&$2B}BSUyrH+0Svd zGAZR&SLK+OVcN2ZS5`ShCEUq!<$B2g<~JLvoT7^inH>=v7D(jX7V)%&JUt{7hoV_+ zVb7!da)89k8>uPUI*->n)Z*?KNqJep@Ma=kbJN_G29r_CB(0aowc7_%dBBujtBUoi z$O0x(YdR#MGo|U`++UpxGejra0fi%ZMg-Pgu_0vcvX4Q;(;cuRdxs_2J1oh*MN9VG?BUB~zGP6T0hrdJ20*Vx zum??R?y@9%(A1WW*CCRK>;3Y1?Yf>j-H!?p6+0D#sHBdU_OFllv2vA3s{ zlrwcEr%^|=>a6doI2fPrO932O%;vOf%AGcp!!&rqVTy8xg_5W6F&W^Hnxb+v+h(>_ zm48|hNtTJYD`fUD8N#d<0;!(0lZxo@471OSAdT*a%&<51mnAx zJs+&KPLWKOIiXgeRuQp{toi`-xeJ&$CU2bUEq(5O-T)qv89mf@0RhOKcsO%=kcfm7%gp-cVd*oSW32q zc~EI|>A*UYQn%ZV3aOR4BceiHNU57;U-Y)8P$8*xbodOHoJ1@jZ=_IUs*S+hO2@Py zq;z6c+NB${vXXxt&-7ayRZ(mAA5mo&$g9Zsc1ykc^TC}ytuL<@T>oKkwn=FtkT z@K8#3x%e<64jxcNeyWOO9g-4M_0+>u1w4cV$AVpj7OCQkMl+;gi&1}%Tpcj#t40fQ z;VGm3i=$apb?@V>pb9{#8352e!%RM`B8SEc-s2G>{$WX;ED5k)7p3Nr0a3Djk|QBr zAK<(~v)|Aux}}I#XvW7u=NZqYA(`{4&wQMP>@%WO*~%lc1ql|Phib|vej@8ts=HQ@ zq1AZk$D<^%-`ht0-;LttU)^b}-p0B|#6s0==bYsjbYx*>_lDm$v6gC!s#0gqYq^J$ z=iMqb-uti}YqQZ5sXbs`IA{lnW%0$R{~9o1mo^phhsj zdZT`Z0$Y9l*_57kVPV12K+Ozl_A{wwcLLPTSu;ixdn($Suc&O1w_*q96`YT}EISyi zlrxXYEma}wstT!*I@@5AWV3YsQDO^z`hr$5AClCYm79#BQ($T+YTJ$S>;L#i=4pfm zHQj@mQIFXVdhb|zS4}xh1Gwc4#cvnxq@`pTap%oO)v_bN<~suwvv6ksSvdU0?4(N5 z*s4k~qtF_8IzEnSdXHvp0BUzNT{lW{%Cc8-*_e`l2%pF~%xda@QF)HF;zoJ)NB$^Y zg{M)~5V$6> z0%3oQEyCPp@6i)r@(~US%fklC6QGLKwR84qwe;&Ms~eNUp}3v?t3r;=^=zDYc(pjd zU40OmaB9F-B@p1jE5uia(te|`eJJmep*Ze zu>o!PxPF&-=@i~&XK*PY0?j`Xs}00+Kq5%e#7^(v#fN$IuTzv0dj6L~Ey~5R73^VS z6Sa6Zug*)uH8W`{NgVRxuoxY9noC~7BZ7TwBPQS(XK&{&aRJdJ@}vf5JgTI4UIkM@ z!LliB<7IG~T1u$CLBVM4Vm#Jd(f5xT)aa^ElVHt^L>D-dtqKOsO$1A+9%DV3+yofWz+`P7MFF z%dIw-r>x#UznrMUjJUMf46?F>)&lLN1sjX zBEjWZUd4dU?q11D8j05d^S95i??rRK{Noy@;P{mVqcewKEIKH*#?3a%B2~Gbgqd(O ztBB=_6%O$pg(g{<92p!E`XH(`&C=Kcy--CkvsWyQVr9G%r5lxQ&}Ls`J04Ir_VQu+ zSw3smJu{-|?fO60^u{znJrnWucJ=lAoI%F$xCzyL#%h;_K!Keu$7Q{sF+I{CJ=9EL z0EegYm4a6;(v6ITDI;THuIlAsMwg~uj%;B3@AncdFT~SABW~rx7USX+^+;@|QwAj$ zEUb*F=}@)LGBLVzj~G3F<_D8-;~wuYUEm|fE@)^3|Ulo!wt7xdB};;-^rGEZ6Pf}!MktwBJ5z9?> z?J?H;%qgzhlNgZlA*X`){TrtM1C$0~0Q;uUC+lq3+{VhGM6h9Dp}XX*aP=M}>3L*| zQz4SI1(S`Uf!yejThUuxxE;OA9jxuor1kN15HL`*yd@i>Qzd#Pn)C3WU_8+|uxkn2 zt9^<1IQp?^Nj9;AkM^6q=?n&+J&7;lZBU zqVY-*d=Y%`%@kKDGYB8V`Yqf_TT)MufEM|vW0WjffeCLTO9gksCrv%e^wBS)K22!Fa&#%SwWNSA!y>ck=6E^1r@79?l~%uJZ$LqtO}9atb!%v|>B?r3@6^y{(=BMT={B?4 zn8D%h(H6AXc7ZkvRzD?xzSVS8=nH}izs1`mFS5*rTfl~E^PLhr5@yiHMA*|~<@?$Z zIp1fWPMwdBgooR$qepyJzORfCqepz_=lja|OYm@8wA;RA<+0=8`ck<9&x;>Ow@sww z3LJ)qYuQJU=Whh(fBpPIdH#ms;rgs(Ul}7h|Lf=Jwj!16?|txa!Gt)k$e zAmVu=RNYpxJ#5~1WTKmXz8#6?w<_CIFghbiRrRu&Dy!8WUGh}AnSgQeI@n48VS#!F zcg4w2dQ6g|t$?@vv{xjIsR$D-W*bCRD-hhkM`E7&x>KpUCOJoj_oFs}y7Y~uQ2iBI zX5qcD-zm1s#ax|S#4==o!0!P?5o&(KBWD$<=SmDHvP4Kk;O>dt!LHPTMTewIz3k92 zP3#fvu@V>@G|V5qkF#?DhVDtjMF?$F7hZ~(s4Ep!&myj~aXUyn` zXOwKD6L-$zYLLUx=}sc*r800}!z(d-;Iau?E{!im$1hgoo55w6$c%oLAOA8)P^OK^ z!ns$b$gHT&NoU+ySEfjgf)jM+n6Xls#n_{WO2pef^YMHpL>0q%ViEs@VgMftm|z36 z_Hs*@dPwbtll4f<;DRz<_)1|;{3}@I?|R=+o1v-~=gcX#5yXu=OCS|-5{=K%Zi{_v z1Vpx&7R=31Z8QDXlQPLI5XCoF@L6)-qiF@uuux=wt+@9Jv~b^|39e@`7++@oguy{NX>D!X&c}Y#NX3E?+t4E-47% zTzb@R908FCTQYD;eA8I-1?+A(g?`eObHe3&>Aa*9qcQ};a1kSz zCHR21G2lHEFv5od-q)8;W~{N2jmSvgFI<3Tr@qN4Z+)H}kYCDP;+z=`%L*%e=9@lG z43gm_I^`R)8Y9*FeCB&dS>g^xE1coz4Xt$6-_O#Dr!gG8t~l&zmAHIQ6Fd(WC7ap* zFuxFZXf&?5-mOM7qFi)Gn_Hv#qr>KAp!z=2=dkdbwY2mUALB`~xMdOqjM}4_jAD=d zI_HP$h_iwNMo}ln!y{4!5{4h@C3i4Eg0$bbxyk-)b95%QNMITGjGNo-kGA-u)3F%@ z>0#&a_cldmX5xj~<}B<_d=jY@T9W`IhnbGy?J%3EmSl4%*<`j;vE{o>_z%x8^0G!H zHz1RHvMp5G<_ZFBuf^v*XRO`Nj$<@PW=T6tvPe!Aa^Cpi%lG}G zxea!{f6#z&>Y0X&Q$OwacwZu3bVtmtkl9O&I8HO7`^zi~1LV%zWvqGKp`_m~hf2`U z&NC!jSQ;S-YUS_vR~4}HsL*o`;lp{%hd*(M!+V9NtG3x`Ou4Smm~|3xXl~psYj7?8 zZm_zEoiaAlIcbAt3Ot90lKH+z_^7v-?8Kp+4gqI8eY|`!c8jS z_|7S)@+PymiLv{wQFL7)laG3;sfEk&)+N&i{MAZ`p*H%B~85wkB)ax&uCU6PD=ju7)U1y#qwW@p6H#l2|@bA(!hJUA~Kb>lEs z&LxTos`AHybyN_vWQe9mpF_v48^^AKRUImw!DbjWjQU1hzoaubrl9)YR-Z{ltEV!P zf5#~5ialpBCRTtvp4`rO9mf7z`s5g%`^=d6hHjq%`71T?UNG*!0 zBnI4(Iqht3x`e0DI4n3ocK5-Q*1BZ0q`2mM0FUyiIvl;-=}WdYWc^MtB;>9l;*iT} zOpvO(@EWiQ%_npdJ!FpFC z&yQ1YN-f%YOmAJIwLaGj>Lj{TSt;VyhGSiqm7Kb2pPV`yIhX^Vo%~s0czSlJFg?xj zzm@#I!YMv3B!v8b?0pM-RMoZjOeTRLBsmid8oa1!8*6Z|VgnjFc$)(gJ)<*<79^=5 z&>Ka1OIxZ0DHjn80pnr7wzjpc?X~yoz4i99x3;ydT5rvRym=&qS3r6BN*KiuTMQ`7 zxBqMHedf$$0;u?Xy}#erUkx*J&e>-_*4}&Vwf^hB(CEpLa*WEYL&ei`X>t+uWL!tB z(dK2T*qGGoxGdEk8qZFux6C(fqtUnOdYM(%e;B^5m)Yt%e)zin!*F%IOxAU@RoBb7 ztf6%^34L7N4GRTQK{mN@Y%#=n9+^%9Cc#h#d}}0K{=dBc3DD&~oj{ZSvL=X)S)jvAF)yWRZ=clR;_vKHVXs;!&WOmUI!T+ekIv5GPzhUA9?p%}P^YAP zH%lBWyAj)@)m#SBlHb&YCyzENclpDUvleLIsFM_v{Q=*0ZT*APtqlQhgS1GkC<~hH zfw0jZ^0ph{jIKa+pDR$`mlBB9i?e2!4&2^O8{g$uaH;_W>?>~h;Zy1d;=aU}063bv z#S2$Jt18X!G@|YFyKnmgN2AT5%B@D_X~WzW@SS*YalovP!3kE}6fnC2;bM>B#j!$^ zc*JFhql6J{5ViZEC}bgUC9mMap!`svesGg!E548hDc;}qAiaNLEAc#*z9N2Ed6dl2 zoyaY#6*L-^tzh$rHtH`>;EAqfr{)KI9oojhRB5}bw`_N}L9MK842CCe>8B*Tbt%E{ zv@O&})h#LD%+C#Z-wJs@2$}DOT)jc>u~0apE>L~cCEBv3S3DE&ZVJ>NO$oT_0^wC#0P4JLmCs_Zu*3-^$^c$*l7mOMN${-OZ%I1Io=jL6@LH@Jbg5Pc%#^-}Z*I@?5| zhROy#+7Lo=smewq!}IP5gmdb2v(a1ccXh@Z2oC_>Ts{$Svi!ZGJU6|s)f#N|Ix(E1 z$d2yoSoxQ}%V~Pli-|GXFDBDW%nl(3QOX!x*%_YZmh)uAzr@dx+2=M#iqFgZ2vi;q zR*JLM)`0KWgZI+xXjT;oTC-yp4&CvyL!7VA*Zj!n4ByDpV=&qDcwgJtGZYBK@z%{L zMrF5{7N>ZMG>9oO8QhQ#F)hT*IfYpe^8Qtfv*^yZ0_MA7kUI_Ud!XITHN;dAv%wHk zB;@Kh#8Tnvjhh)xqG_4-C&gzh_O!2pn)UX6DIbTZv7%95=_fg#Kr%mpp#3De%`8OO zdOtl(vi%jfyl?0J!b4N(p{Yq8qH`cHiWpTXh~nNw*U;Q5zh-dgGqj0+l_V4*;kgN( z9fBQM@4JTHcMZoMG~+!InuJM`i#)-m5LeOf58jP~LpWp5sO$)O4~VYY6bPrbhANwb z-ugiJW~bjpFz1PNvF@PgQ+iK6!U0Mr(78o?7#AS0l6q$f<`J>$|2OSg*wj6++ULP) zUjPK`B&d)Jfm)hRm{r>3{A~GNOyDNk<>+T+g)WYzcqyKnn9n6k+tcfXW600_>(47v;3jHKopOF- z7Y6Foog`cs*i<(HXJ=aB!mW1ONE_QmR5-4OUF`|{kE(FGu7b4PF<*NE|6@n2I1d*$ z9RH(di1By&Q2s}&xLFiij_6+nxI5Ru<8H=O>Hkeq|BqP#JCa1vPi{SPI5ez#L2KDSBm>k#iv8hM5$8b&L5)9vh z9sc^)*JL4ZqmM97t@sk{2t+p*MuXndC579A;ne*&bJF(U-G56s;|-kkusMXk=?r?0 z%5-kAd^Hp$pcppCEzquhRh!XNJs8z$z9nsGYi-!4=H*|m5~n}Kewj%&u5yk@GJH+4 z@fw}vCvZqhKL4Z@Y$BC*p4hDCZXRG7igJ*lafZ%abe!KVGf7$@$t`3_!meNTgFSTM z#PWDpdWIwco)o1dirs$Ser>~`ge5^6GS}05BlAxlnSUk` zAgP!DNizXRnh(8lF`V_R_54DjlAg9uNtiLUMM~Wwi_gz2G}2=>G?G%V{?76vuBgv+ zd}8?#Gr#qg<}g>oAZmUG7BOFgc1uH}O`o29hlT9uv#ZmZ9obPKV+CGPu>yZgmw6SW zO4_Ql>F=e>NPtJuC0m+PMiG2l1`iY{q{qVaI7~;G_}Y6DPA@|-9YtrybWlxH0!^#d zagdG|6{G|DKVv|yx-FtnnnuW>4a&hLNT|)aw^pE$PgMZwH*JxJ$@vW!` z`^V1UZ+!px^^NziKj&P1 z>%JPViyz%urlm_AM(S}Pzx*FIOTJ<@^4YkfxY8zidP&LgY`OIb4+{W z1$xc14!d{!>smo~-+xNcqys92H|JzU)?)ZSN0OPL;*GQ81T)L#8E@#D!!Gcym=l{k zq?^c3c8L7-KjFe+UZcQ8!@%7>)!vYg|j&g3NTRzq+xvV%JYZn?2^Icp1 zV5;nRsw|kgS>H!U!hZ4J8|)Dt#~r9!aq(2y{H(}a*7t3dq|z2i~$vfkVPJMH`P=tI3zv7FSAD&u?Un!9iO9Q`FPdWsBL)kgHOZ}-06rperqi? z5$4r{>NK$bZ|XF#<8Sf)---kavpAckJy(_GSe~-v0-^c=RDS=(;%{1|`0;*^VWxfh zkWi;7J6g1^kq*H25};3i=?;B1a*UHkeJo(^@smH<3cke!eQpIK%*TxY`>;4(ct&vS z?UN4HtDmDqYzZluVnHPI`QbFFWA~)-35aYJ(6w9BShc3SQc$h`l*Uo&kLAm3VW6!V z`^pm0w9Kl}w5C3DUzQ_sF^Go-v~}y!m|SV>a&e2cZdn>@=IA4#nSV8n7=jvlU$*5y z^Bq}Pg6@Xi9n;DWI$~NmMt44{^_=a*Mq`_2?+57R&(J#9goD&F_^{0>&mVwZj)0U2 zI6PIJ(mC@PtJ;22waK>4{Q6^-zwQnTo*KIM&VIW*QI7GiTZ2EN~;pHckzX%gr zs?_I{zKC^d&RztS`ZdRSO_g+CKtZVKm|G@{#t zW)k$iYIxr*DQqh-cN*S^&`91lpO6vjJi|)D|KeOg`LcM8S9Y~eiIwMNU7OLO`&zV` zR~^YLgmbE9$5{xG%y$Ph)GwamqzIJoU;=NMp9b0|i*|2jNz~?T8juzzCN0c>d2pl! zeT!KKx6$U_YO@Z)b%}VvE{G{Hb?GE98J21AHezK!8>$DRRi6=Ga^|#Qf?Oe$Ozg;#OASV zs;_Isl#o!Ku1o+{K&ih(AdR*VNcU$EkpwvDYcz3*3TGQpBk_SO$@k0w9?7U2@U{BQ zF5Ns45?huQ?DYF~tsF;`6ouQ_Wm{D1f#Y&Xe(t=+60;rFEsx*Z3LA;pQyq1#ygyLc z7OHFr_*x%W6fo<6g#ilT2Jtx%{*njZ!#V9ZMB)T#5c$DsF(Fpl@Ir(_Ay^59fzcm0 zyi~o4V#n%cowweFrJ^*W!-&?4A6E4;<&me=-oqkq+<5> z>Ef}TK(sE}K#7>jg#=X4NQwfpolseVzUGyCxYNX8*u!)j)ci?)w11v`znA?!^7%A$ zd%D%@Q^efN{N~riZmZE=k!dcwJHf_?zh#-?X(2;@S{(VzW{G zRxHV>QN3v)DGeK(9G14$$tK3dP!wijpDZTh$u5z$jUetv$n?QLX6YOG4-eT%}`px2Bv0YF`*M$jy1tQRyL&?#nHN;dCaIBL{yj9wZQ1G-eI9-FoEtC66#Ukt;qw*oM1R--GVs( zk`8!F05BZU%Ca^whwy`wzn31vMK>;xWq>Z09;5ly81i*La3SgZyI`>A2l~aYD~<#z zkHP%A;g5277YD-AP8*VW2_dt~ZVFGNAlQKS9SGv2fa|E3h$n;I_eonLj6vF#g-EA; zT+GR6Y`@F7ED%nm1bhhI*JQ=FtI9JQsHk^Ei}<$zN@|@LPT#MTAOvd*F^~pANvn@K ztNu4Fxp^3zNr)OT1XcjqPIORRWDD}yl~GhQTbMab+YFs0vYV4eN~ zrPGu8I!{vS)M*dDMP~9QZT+i~QitFwP~BUJN~cX*^O9uF=~p`Zf2FH@Bk6GzIzNC} z{P*vliX|U@T2koruAF>!3Z18h)G$V#Gxs#ebDkt={F}nY=;kR4Yhqsh`5l@=k>^O5 zNsKRtBORY*w2GHuG1W&?m_x@T(@o~L7iO{1yqKwUblYVQ_Q2B0$&AzOET>cy~T{oPwGltTf*6FsixAo_RKZmPv-uQFKP1Jba_;ajm zw~cnN$pIyi%*_V|9W}rqnLpN|(b;|0Ot&AEM#qAiG%(ymRVNT0zhBJ|v5gU>&e*U1 z69T__$mAWO&th3@E-_D)6uw>fb|5^t89HemNGH5|3_v_h&V2Hja055;x-hFMC3IT$jN3=NUzY zL^o^II1}!bXTnBp?SHfGxS8}@aTcs6#LF+GeORndfp89koj#A_VuN+d8h)@|Tf3Y; z9o8ON%*yMcFSt3jg)$7R{jyUv#-2@6dt@KBt(u**v8>6Lc{Lvp&zCVm%89}4>3dNw z)U>3GA4mSS?7lHDl9P){XChUhM)7q=PlMxUKMv3bh~fuk_qNky zcHiE&>9j4UK>0YXL$-&hQi+b1gxaH9H}IQoi-EMVS=2M}5;xO8gW~7K{8;YJ?Q~F= zgCyhlzQUORoE`$yP&QRHLVK28CjK6>jgD9#fR;%J67!HpCgjwkO|_}NQUYrQh++J% zWib&i_^ht-{jHUKn+I*A*C7MMWFG?%CFXc}{&kVBGGWdzU2Q$A=yI-A9Zv;NKEn$w zPt&ynl7RI(vQ%ero`L-+Hk293~}`{su7U~tbi*T6i>ca4jt-# zS)>Bv_oXwBAD&T_ztlFA;>rb9#V=JAzic2bK_QraTG-;C;;i~~&V7Ql&hO($C~d=) zNlAash3@AU7Tpi$iGJ1s*c@d0gwi$1rv7uwrv5{lP2Hwtl0I#A$z$jk!%Ta#O>?}E zCBB-)OI!_&VLf{Nzv8f*DXw*hYy5NEA-MQUiZG}&WF88cz2;u?SPbf2y{VKaUG8i| zCch1$I==nlSe(K-H^HhU1tj$LnS|-CNRcM;*(t2qKk1Nqw;$OAUnGTjb(GKbFQK}T ztniIoRc#$J(%LNVS_i##8IiYn{FCiwW=RuVsQ~E!gjScjZXYdoN>7 zM4b;7;OtancH`VJ$=n*0`Mim$Rva{X%g3LNQg&x=Ge9;Nm}s2~QAkGpAy;(l)4$H7 zB;Q5mRxkTX#M42uKU!~u*U0ZpxM31V0R7_RgEw&54pFw>92CNTJuVr3a~s|158o4w zJ>5)C=tvVK9@ovye2i~=JIW6)nH(^;M#p+ZMReqhe^yG*ixy0ah;|9j6dNuv;yo>z z!*7Z+9Wp|jGndqCUoy?8Z1IOvzkr2&hd4bRfbj;JqWE_o=~qNihWsKhRgPl)uA&2* z>FeS!7;+Htl6U`-(mr8`CP?w@Idn>WRGw0?{|C$yVBa-+{brZl??gt2c@ow3b}9~#d$skirN)SP zNTAzNWvoM8D&wg-)zS4g;s>$lW1U%jlIO<@a(+x#^TSM)lj$K8BMzG6gf}mVG-;HS z6Fv49G{d)RWrp9ZM$7!JhKS2ABuyGlOMIOrTClUQt;E;8@`75T3dgcd>keGE)_D|% zQ9g;WRRu3ABfzw_+MIoaV)JNo-j;ng-OLx5J$$6dPKg7ND)zm%seJMO*at{Dm;deD_ zYo2$|kO$0N6b_ovLLme#C9Y0o5A*}AEbYDJ2X&GSIyLSVAEr4ugVjOQux;r@6h5C# zZjOp(K5CTqL`K_w&4 z?j5n(iD7vUZZYp3%RVRyo9>nYkWe(3jFB3D<#}uT4WjX%w>2IQa0etdo|<}wh7ns_ zOZXO=6UPE#31)T&H8K3)F(dqd$4+esq-YQ zP6V4$Tpg5xqLA4@QKz-U5bn=kOxrR;+p~jWL;kdSy8;6nmQmV>)tOv2cYuG@2c~* zN_((Yipb1TimgPnv~Bzbrrj&go0qRuTO_L z9sVgW>>>FRxe8$?5KB{82VAJQ!Oa1i_M2iih&8F|S%lN(>t?;TUUZ17j?M+`6j&R3 z2m!Wlz_GPZv_ec}%<^o*w^iGCiq`sOd${@U@l$v_#hSDCEhxT`#uJ`Q(d1}ez}v&r zMm>S>ZMgyOv4CrD$h9}%8UXM9v>GSfk9PZ++yBD&R%By-5!wa=tz{Ue4EIrI`8JwOhM%U#DNYwLv#qd-wLAiedmZ z74Z5%pJMR^IKHqWfiiuVY14nMac*-zI4z&eUq-0g6VN7CIoZWfc^Ied#pK+ZC0Yfa z1RO~PB{fl%z5HI_;>9WE;fKi6@TRu@+v%)u5&Ef?LG`4z-Jv`VA;RBtqIe>)oR&@p zo>Hgnz%w%8z>|x6o}f$>tKa8){w$Tg8S0+DjzSyOo68t48_utV}Z- z#hy^}c2%a|SGUqFWafFSdbR$?3?tgm;$L4xQe1t~^+1-nD8JZTl0QvMqN2z%;+r_= zG>IRA<}M*TGtG|P-KVhh4T^EECjwZhH4&$c+RnP5!m zxLaP)NU7t=klV5dj4U>Cgtp|q3(DaLgCa-cn$-mI2fG9 z{1?kWzLoGZ5bd9MN>i_icv*^RUw)5?#~tH>z2gJ15p3TdW7X1&AZK$(b0 zb8#=%!EGyOfC{3F={v0wHgO5>aS33seix03mEf_Ja2UVulF!m*7utj(0t7^w_bNY$ zy0W`t3*a8Ut_K#1?Ku!m?Gk+(^46nY#qR3eOAUfa-;-KIcvO$|KC4FMhiz}9tkm_e z64Q(0Unq{B|0RU0L@RjH+bsTWi-LqAXdXGA<1ae#u3X#riw}!(2PN6;53 zH2w!^SuaXH{+s#b=Q92WxemjO|CVzYe_6ti@!zUO1DA7?jsHQa>$#1;ZjJxQvyZ<% z%=oJp#>W4Ew@LhMq477&f&V$&NREV=**;S97K28 z&$0WFd=-*F>7xNa>BZ${zTAI*ri{R-pi9)j7Y{NV({q6o*wjm@cPp;C0dJrVFT3hq z`b<5}gUuovTY4{GS$UJPF*Ukn@0a%yAZ7$U?w|~7=&iY%neQ(qI+|yUDr^SBe^p@t zsvH-_>)(a=$gABLnq*upt1!Djj3GhDp1^X!fJ0Rp1fzTt;%2lOWR4>={bGnkfuAC0 zDcP7b5wS8s$;Mc{C}5?7K=!PL6tH3fko`digY4O#$chae`3MCk^zFVLE5dfSim>f_ z<@k_%V`kd1-zva-bzMp_K9@OU;a~k0sueO11wbwiaLpKHxWok*P&q%@B88Z*l!$re zb724%87_-QNDKdUBiJf>&>@YIpB=#_Y36fzTU#@BL>!;X2=TSoh(_&D8|oY1k(Iu) z@wv#FNGMrcy%nQNiTMhQpk#vx&pi#hD`h*M^IFiH()x@>7R~tzUNe(Slv7Kx93@QU zqE|;4TT{$DE<#U6qKEJzI4&A>Z=;Q`h0+yK>~&=aT??8!5xnQ!Ee_LI|HB!rA+sgu zZ2<~%a`l4Y@HCz0H4zmO0G%tI22&hidoY|L_~_7i%R*(3v9s~nUiT6BY$*c}xWCVD z7Ah-Z5BLQjrXVM}EeTaKxR$-!g2DJ*l7>v=Z8{L| zvni+Y*r)M@Y^{OHEsQuY#mB~wx!ds8(}olUxl5>|#9B}%@IQ1ag(LGif7c1&Znq=*6Yl$e{a4^=l);+;mR_%u;s zjq6}oq6hpb9?CY(_g+R>c*YU0>jlX2u@vt6J{zPSs}dbfqs=o2T5t6TfR|&))z0B< zkj3#=qJQA7h%7@N<#Hd1d%hq$0>1l;V=NMwcBvQ8q=cLUGB0UHJyy@vleAqaA@=yd z82c}9VFNah{cbU*XuvU40QnNgp1s{d2z?ymuWdMbDjAc+upAOFx3E)^S7S*N%6lBI z#N=YS#scAAVFTgER&(3_^>@J^ap{AfB!9#;zXE^6GUAUwq^z$~yQR}yZScoi8yXY$ zb6C72qXhZvljNTy5JrVw&YTinl`Q1bS-hhkbi|TvkO?cADR=banY@7`{FXNG*E3l` zVNMD0!;Z{2PKhS*wfEZOoDyF+GpB@Nm9^MrZS{_0m3Wrsxl9z&DYcP0dh+^rpVE>i z2(c(8bniNY=q3JjSpvNTxq!ZKebNLWl*PWU zlJHHOEygY}KM3ykaCu4jCir>+#!q%TzkbTU0a`3!K;gMKurdx_t zvaBG`32aG0l>7}_+If^g8K1czJ}(Fj?77Hb^1L7_HZy5nkl|u8&m}L&;|aj;0Y!U) zydb*yLqEx5##W*|*-5k~n{2cvu|_1&p2VG>hRX}$X3-oqEcb~!Dfh{!50@9DoX${X zL-K-aQe{iGU64Wic=ET;U1pX6FSfiwMx;4Q)%(|l{ z_;g0S@C5h7$?7mUYz9be`-x0N848c*T&j0ZhW+j+9kBxR`3<3{+GtPOLgAeKit8lc zJ<4Gfe#$G-9JJ_8;`mP1+4)ZDv`63})@q>j5Z}pk$-yy>spL4X1g8oWm!^T{;!~Ur zWHTX;nHR7}d+14d09(fyFH|;=x8!S{r`PpvVlD1P<$sk5nO<#j`>a| zSx3_e>YChoC~Kz9U~^`s>zmEgCVOIePYHVc1(geAip6-MOGcNR3E~_qPB7z1)H66H znp1(uQj=r?c`256qld;2R^GB7u99H($r)_72Ii%k2@oHa{r*FPkl1tIq>xn6DV@uw zR27QO@Ka|jnS|PnZjgc|2K-Vv;L{~OdE5ZQo+-HpQ=sI^&Iej-JPi*D_eSPojAou;jMM{cVD(yK zWg(t%8-6Clv)3XOp)?*+N(OA{J1&!Tonh5gw^#N7ra;jxniN@CooYJ|{dxNtYCFTK z?Tq-^>Z-aktm^7X>+84GHxgf8f>}|0$5<4ND-!BUscE^sIae~I{5D&CXIMFJt=^^C z!u8FGtFJBREtO`iI;y&zlCx#6%Hq)|Ia?Yzmu!@xhu-xyYa3cWi3A>eK*EW9k7GFg zmQHmbzt_s&u`n@z2M%Ki=g&Ln_{sd>$A;$zuaNxUzfp{a|B-HIV6pLoSEtKk=yDmd zc~F^;znad+Lptl?htl*odvXAwo4MhS#t$xQlE4pMk-!hGVm2*)aLNF}HfVAFyzj?~ zB0RUw-5K;K~h?pavSo?X4nB!KRw}`n{ zlpf!akl(M?VGS+Q!|haf-=y4ymL9*|&avXQt48OV-w%DJRHJr|m9r5SZcNbNUsW1B z6BquFviT|ELTK=!+MLvH>luj)!S^#%f8QGkCgU!=?jvv)sG8(43@D^73!UhII$g1LoPnPGVg?Rxum#o#nu&1a9A z&oRAydg6Reteq9dDb)5SEYiXsa=!bF{cy=YRgfjrbl|==6o9etsMzGljwsZ0MOpYp z(0kk;zQ<{h-u^7@8;S9m>ummvChd{GkskeygTpg_;!tec(%Z8giQ%D!TO1>R3kd`@!`YL2dCB-^sOrY2{DXdvTNqIzn_gx)DK;=&6#d7&y08l|(79rP`dS6>Oo$Tcy^_DtsDvj>sJdomnaH>fwrZa zVARC`jgtd3BgUSj6MfQ`nc|Q}O7%@5$DuV=Gc50pMR7QTzz%vMhtf{5492v3A~Gqi}yO|BOv}9wuLVGUa*m*_3Bg z>i_e9{}18+j_J@Jh5!3w_+!q%|NZ?0HTsQsHTwJIb~XAu@_&DP8~C~Kf7@)}as1!= z*-IFo8~Y>Mz!Ukp*#<6@>EZal-3eOss5A0^p9c$gkJ6-HO_m#5GJuymKNbTxWros; zqJ9ViIA@KDBLLTvGt>`Z0JoHB7$LrV32D=CiGT4DA_JEM;6j6rF@RgN-`=DC@Pl_L zwYr$pYQN*2yyz>{Eqs(rrfD0zq|T=>9Co=6AMoB>?EMCQV#;>oAO7H~xgDlOnK!tb53=81&gFnwn+Hzr|l|9v58cV#;y z<#+eeZ&N3zcYwE4XuX-J(ACQ3tuv!2UR<`>%3oa!57Tok3s^G6}#NU#zNY=&Jg zR+Sxe4zCx>OwdUxJB(hOady4fs_a=c$0bY-z#Y=XRQ9)pmt0@cWFZT;mb5?P#;5=Xfy^jfCeRH1K2 zNhRK=t$7_PpYMdO1y2;V`A#y!s!aS`tb%OmJ)j23CFV@O`Cf_lSg>-pQF$cj+o^4Q zfJDS|$&_4zU5byFe^-3u475T~K{w?4g2%0^} zo{H3|Mzs0XaB53Qc=8`Z;hf*u`6Pv)%Ua3FX1W2a-H|`}@KVzHrAUh#yQlwi@_657@`z7u_kI$hCf%~5s zkNd~r61aat33}qrp}EfZ%u|YQV~{GX*>)%iJ`;hse!%KUgCAUUkF>?>{~aDh^X+@!i`CJ14&TB!%Vj zJ-Km(#nNP^*wSPws`~^q2$26r68P>VOocPG+WGFu7c-RaUbpcU?ogO2-IgZvnh%pE zGlo-9kg6XmJC_mNVnA0nqT5JE|6?}TblOQt|1my$TZ7@$2uv)i$i$H`NN&dY0rM@x zeBbc)8s0&1u%wuPSB+XORjBe{qXpaM9>h22>ESz^F+S(XS}>eLISt2uT7l;GYcm>% zFp!WB;`PA^Zg7eOr@GaZYo%`fl1xJxg*rO8ytL{L-CQS=KT7)iUDR(S(5o_}wuHms zWJRApibzxydw#QZEacdLOhSxE{`}ptaHTG{Q+IZt!++UIhXf()Ai<|_w?<;d(H;VT zj;r^rs&9&VO|aFAb>yd%XOy8%BrKb5-vh@7xn&C zT)pw!W28QR7i%2b^eB43|5Jx-6f%705qBO91_3BiNt4E(JW<=1#ec7HY8p&{afC8) zkvd6vF7pc%%mJz=`j*b|9#*FLW=nVGr|hX|QY}=5_#{))6vU%4&_#$KP=qjm>D(0;x9jtV!R3}*X zWq<)T52p9uc0wtlO($ZCsI^~6{%H`tG|VZ_e_KOf z95znE$D2`DaOosJfs;yVbB=r0!m;B0OQ*EtdYsad>rl4a{+R9dF&bK0sb0$ziu2>4 z%6I%I>`A`oxKM%?!GjgBpFJdU5;vOFy2%3>uc zDlI!{I-3t+?_ic#v4>&io(LdSt{t-Xn z=Ey?opE9g~GX9N7V`;COuFcD!c(`m{RfrhBw3Fhi=<&l$=;>B>v(=l5mYXB>(HpwO zHO(SZI>24@IXZfXma!BC#VPesh>B3Yyp6>4(J5tWVM*b3p~4aA2kFG(y3pAR49Of> zQrITWdBg+4NY6$*wU}1Hqk-^jiiexF>v;-|D-l0->n~wP7`RdV9PyiI-Pn=oluF?e zEuA^W2KDfbKNVH;gEngh%wbozXfx{lzB;YuE2)$Z>Ki{GV_69hizV9Ub}e5}b(fWv zWvnPH?Z=`pb~z(B{9xpE6;ziGJJMY{9g!le@PzybzB9;c^4vGsJvpxqgL~K8gL3yg z6d5gUcq5V~u2mqYI?X4FXCcBkic?w#T$`j~{E?&U;Jz*Iq6WZYHYU!KL95QCxQfGjr_2ZYFd4uT@e>$=|J0a9~+IBUV91ThYLj z$vaZ0uHv@2EmTqMk$)%a;lFa;IJRaas@T|93R5I$&0v*z2SqG- z+Yz~uDqx5TV6d;|N{M&8M}FORU_$QKIkoq^7Wt5roMVxT#R49Io?9_M#W-bm3+a}k z(+JTagzRxq3V9)-yzEy1A%9Zz{Aq3NJx~;dtnVjL6>oZTd7$b>7L09?8_^M(tPhR6 zOhrfwQR+K7TZZSh#ze!x*j+K2kx{Twl7Qe57t}iO)Wmm`9B>{YkmtG^@ zu_C`TU^YDuTdkqpK)fKS%@DrSMqQh+EuzuTEEthR`z;o=$5`PAf5i!{p^mi72J$xq zYwyZ0onD*quqe@Q{*j%eH%I;EUwFUG=m?sZfA3qGgAiU!bP$vjuK}|`tdZjIQ4|c1 z_XNWk1#HI<>t=W|lfZ&IZ4Ir*MtFP?_AgR}T!Th$HSM@9Iv_pk1nKrbu{VA z*ocYa+5#u^J?UUp>CLixSZ;@hkoC^h15NlmosGqPDSZ6vj=e0-MGuJ6cp z@oIMd+J!U9_|bTm#x4kK`Y_~^`5GDNWteal-7~ZyG9Z0cP7yVxNE}SvM)k=-EgZnH z8EIe4V17X$+C&?0wBCqf{G*K^(<>EPb^&Q|q^|hZ=RYjk_ux&mrWVop5&EBpHgga1 z#h6pB{H7Mum!-0!)Md4?ljExIoC2?S2<$L9K-Py;~tBzxJ&UBw@SWZ^77%_ z@@vIc{EO5W>n$B%zQ=sUa1ad3SNu)ISG+*-74tdn65rREuNWphJ6|zcX6GyBL_9nC=r;Y zn%qoQyqk*_dyV2No<_t0*GeMS^FUdwtzRzLSycxlhd(hi%1|~_^9gAYmE&p~{1R^` zSw8cavzTZyZJfm(<}99=A4gf756WWn%~kJ;>NmRV^3`E6WlSIXreED=H9U>wmjyd8R$ztS#Oi5I{h4_5y z;`n?U;)sg7Fw_Y|#iCueOOhjERRW}tTYKgCXzdN6wVzk5MN=g^adKwj$QfFajG6e0 zHrW>vJ~5xnNFFjHFiKL*)JFMB+y*W1p6O zA5o~X=?IO(GU%WVl;#fyQ=~A^&Ao6=6&D^4RJI$@HuAj$yqzWCZ`~FCh7)W8utHml{yreUG|C#&|fo%Hf00?4e$Tw!nWFsKk3-go1={Z zvsL%)TlpVUKk`n&B~=W!93^m^L~Za(7GuibNVP|)e;i<2iDw*TY^OT>8(|KfptLAI zK-Hd?z=N%kS}MwNcEvnhHh28#jo1j2WCAVYaV{bdGW?@^<&*?_+|JdId{1K!hyD`L3b4~0|fWH~XuWPf-( zu{rCmcR?S#z~T=kUYX*4p{Egba_}NLc_G17_D#f%cM-nB0|{qD@C&3}8gAy?%eEx| z{T=#047Z#CJ{h; z;_T$Uf2?kH>SkMS;S{kRwCb*g;h2Q^e?#oUcSvSozj;`wIxw<|3&9X^diyC=ilt8x zSuK;Uyrwb&g=uE-9VFUyS7@l12o< z&_FGM;50`(L2yeN69msBOSsSkR8M5;&!Lm(w>YZc@24TgS#_nk8?B0rsy^w~)-6YF z@~JNpL9(`HQ5tK)%bYROBhiyNaG}6zULPMY2aK9ohIOGSTaj&iA!!}f?;3%l9G_`h_+@g8s&>< zYr3!!`1-Uptz-({)Z(WOcj#VX0hQTces*If5j;<#st5D^WUempeV}ce=|Za>BlCMu z=$)lOvjMP?JCA2NLf!_WvME$~z!1eQcF_2*k;m!9PO$>GZVG2ma?e}B`PrfH#0Gho zt_R>mykZ&67jd?hbjjkUVmbd|pv?3AO$(26^0y@gz2@KaaK=cXiiLddY8xYT1ZWI+ zo2^BOV>i2R4yP8tP+Wb)Md|k7qt5t2A|HEkztF*u9d@bENc*7|m>^&;9z?R{8+O^R zbo#?OY|26f-tX6LXs}<~6TS`0PqEPXw(8mqopB|Gzva?y7^tpKg@=|n(AKKX4EZ`9 zq{yMv_96AC*KTNz)(4{XX1};?CGb+vgj-;zX^vcm6X^WDxu#2?*?-xDQ!aX$@*HaPDoVDDWqnlB2 zs~@&M^c{5#Nx04@`^7Ml)niimm%C*=d=5<}>OjEe;HRyq!*9!?O)Bex9&HI!H@O4R zW+2vtW}2E2sNUpOgNw9Uh+WV&6oMn%j2*m*56hn))Wf$$aRA5i0dp1~y+K|!AN89F zibd#0lqCTgQvsz@6m*J~ee!i;T*PYPK)A+o&W( zrvZ%dSfc{<2VKB3_3rz8reo@od}GO|fVnL?_D3@^#kyJu@0;TBd{G8l^CFMRI%IPW zCTtNxYcw3PpqVp}*{If4JJM{9{Enu2}U~9$uo^!%HgWa&*aG zRCLK7(iz%AIR$H(;f)YI-%XbX{n~UsA?7Iu<+8-+l6&H#OAw6Rd-0neB#SO7I&*Xh zirphmq^)taP6#eJ=&*uIi22LB$~iY_a7iqRMEBOfD4=_Pnn?I{t}!K_J(d}ZDOr(# z{@G2H3+Fh8i78=#ck-B${|WIVuQ_b-B#3?}AD=Q5cCK5I5i#63N0kg00rGbqQSwg- z5hWPkj~-D%PSx>_}rBBTC#KGNL3FoBfd@NQ74Euyt90E z)t74OmSmwa69=TF~IQLh?*IxIZPT4hLG|P&&9vT}!@yCc0+?yv&DGPW-m}2ngRBGGAo~JgLWd zBWJW55Vlh6hD~??8^9Lv9}&lNF?XY)@bu7I zKuc>U5>kcJb*ag*R=^3w9X8^6QD%3nI8I0Ih$j-F;ym)YRa`6|xx+JDak*SvuC2IS zE-qJIw~EV+6_;xjr!?ShJ1>zK=8cYoVdWDJluEl6yfc{={7R%`Xk7K@KU`e3tQ)Bq z-J}FRu9RR=yZc!PEhTsx3!#khhY%V-@1I2oZ?Vo}pNt&t{jhSl$1aCGtnI3}2bM!b zWK8C`2P!KTk>RmLWK55b$gt{jex-2V`Io|dEQSA^Ace7U{eLKh`&bJ7GFQ7eZVeJ* z;PQi+&{q4%HS5NgXa~g(J$oS25r}RsjH>9Pp!al1;r5`=*bS)!8{Yl5gfrfNB?P-i z_?u2p7%^zxZhxIPPC4v8CG@Gc*kPyDtd`p9+M(GP6n<1DW7t7rZTOlbW7t$`<0`NRRBAztFMrU zaqTW{$dYkx)e_wei!3<=wVFRU6x9#oNfE3~Evs|dT6S|)K(y>d3q#rw(M5|gXjV=Y z?`#)GLDGil2UdsCnAaJ=2xoWcW;A6RH3j*LT18V(;E<+lXeC_^xnE1Sh)Nw>ig@BO z(X3K5Y5%>k#1BOG`BZhTL(70mP0irP^iWTA;;o0BgJAs(r0$Z*ELtw zHE-Cu7TM}ra4vN%vg(=}Usvk@oRd2cA66pkic0FyM%GSMWC>BV?eqKgYa8D9AQn*~ zRMUg3b1ot%*oO)$QJT{)WE%CSrBRsmEmluG3fc8#DoYK93yF-ZoB&rMUW$# zlN@X7jyNdMC>miU8ok)TiAL2u&A51)E~XY9kBq85?bg=)*1>jb&Ot8}TD;I~f^JE) zm)>TZ`|DpP!u@s7K*W?D_UF6MagRG#C60^Ju{94}h(7Y09WDO+nGRCZn}9tTuMqDy8zZ%BB7a1|1FToUe`~%RsJ+u2 zsQsGfjyW5>(i?eQ?@Qdm{#Z?Q`-dE>45U8E;T)^xVx3&7U&^Gb) z8NWGDQd{|y-#pPfDOc$AW*tyE6U1ov4jADzQhu6rbC(u+UpKcwcIq6`vx#iZlu%mS z__%CYfnPK|ulLo9tioQV+t*8;(R!~xzgT=j!TzZXfLroyad6r2OI}9D7Vpg@Jo>>s z^zY?^J();iYStU|hg}HkbM3P4AEOCyfxkUV)3iBt6xf|A^T8l2ju8DWFw_zXU^Af_ z@%CDG1hWD2 zRnGJ}uOb1TQ)0GSDTYb%V@bqsw{V9S^M&d1!qixr+~-F*;!_M?;_K2v?Y&>Ajz)E} zO$ryDJ){k<;At6GlXASV%1!dPF(RrZ~L!)NjlFtUsmRcoV9ku*U z-P{iJ&SGjv-{lStrC+STH==RY^s*=9A!x*x?vR0Vm^_kAO{{Xdj}uYB-DYK1bM4M_ zXd$88hK~8olYaA%sLS$CnPbU8rqq=G}jjG>wP#nb%miXRWc?miUhxx*N0Hv%U zJq&=JzV1|q#Z654Up6V`Uswhxr;Je0{I1?W<=aO6>n_9G5*@qo3Ni7_L9B{Xunvu!)v4)uTfTDw<)y8Y%JWzR*CWG7q7ee46mD| zUUxHI*WBk7Z<@+{&sxo~6#7DXWzB2I!M7;C*vFxtT1__?L40p(HSH-V+UyqFQip*p~u9Ba7k3`U4d}wR-t!qGkk;hj|%ykwd!S%nk}mjjTE!$fsxU+>bAjXz1Wk? zMsK~SOi=81-fc$sM$dvrHILB`ToW>edi+iyvgNHXl55#UKap&dcaqCV%l3#{J$%{k z%oEq~LmvH*ZRClAL~+Phzv4H(oevZml?_CuN}1)%CU1k;XQ?$C=-Z`Lvs(LF~ZNbVtA@A;h7&+CS0AuK4@US((3)+tmMII%Gu4M;P`iYK!$FbNWe#%Kf zNFooMr#O#pqU++*tP~o^fN$T5|0wa*KQJ{=Sr_oW7r;9+T0~uRZ=G0q686Vy$l0Z3 zr)IN4+e`c(GK97^PtAEhl+4IgXIK%g_>AoTOT~xhq8G%{swpl@JE=JvU~PbKu_P-_ z%XxY^g+u>+@T0V6f0FztUDFUTf%Txe?u&_dKUB~|V9wb}T?{{+e-P)3)o#)VlOogyym-Lqu0%xE0=WUtf5A&oF+)Z4n>cyYI-US{~ z`!Wx0!|szd;M3@+YCcR&tpB3#gcdv@ZVfqnr&mq~utcvN34y>XaP;v|v%+YMQMkD` zU^s<3_C!8fD>SmAfO%hWOc|To2$eG=-UR?_t&hV2#F61i;I*#j@~m6pOG!U_A?~vla+BxmrR1Kyl)ST-QV>^4k}@XAGRoVypeP3Y%Zsg2 zxM18_boL4rpS?oH*-M#u_EJg{OCgy`Qr7%f^Ms7yx3cYYVIkT4jnxfULlMi{WmNY$ zwVEHCibfF%qh$+~7*WDn_8+jE7H-EBoK#Y{5C1+x>eVu>;3&R5=a(N=`H6Hp`ww)q zq;R_IDue`9p}?w#@uwAk9>q;{v`J)F&8Naj=`Y>fi9e6ymOb*8Wpt5mM8td59{hQd zzAvP|Mf7(P{Vl-7gZT55SeWt&>!D@WtirC49m2=Q`D2mz_zZuXDL%IH$A!NA+QxAs zm6HM74f-q&<^dOb%MFJlB7(9f!}B!l+SO;tqt@S_BdOG}BY-hydbI7l&9) zSpZ)^puZt*C$fiC@|WuysDO8E@l_ee{V?B&QA)cCZ$lEIG9-L0rEyK0-%hbGrCokR ziHmUzA}rJ6U=AIjv@8~N5nFkI7_G9gC)+Z4XvyYRw(WSM7~t7t-}CrS<%Op^wK+oE zvxBE-CSiy@C990^txlYbjp}HMxHx;2Rsx53Fn^gL4pX8(Da>BHYZ(<&L}ip7f|c+f z8B)u(QJF;~dZNTfv6%f=8*OxVi!bG&hmns8pOjJ}iS zUGykH&1j;oz+cgmrB+YQw|cT5t|t!;C{mQW5fonrTvgdFar66dqU0}>I2PiZKP`S9 zqzV<$ZxpRA-nEb`P}V`bon^R8=~fChS&wcNbLJ59JCq*eTgBt2)ZN*$$ZFe4@$&X( z`UofpYJ;Q8Q~D zGp`AIn7*krt&o41Sf^JHrmiT_!^Iy^QubT=qvG_Fam?)WhjS>NM(C>aZ_wL{{MV(o zVP6JWOKCPf42mrwj}Bq4miRg!$kolxo5LAfb?ZJ?y4Ze=Fi7RpZ1iPUA9 z=k7t~pt@vKGj&kfi6#=6*@Hy0BxJCk$S$()X4Qm*wsbC(y%O=K;*w+He#2MgUNJ^z z2=#bg%z>lE3Y^^p-3-f>=aL7+JM?e~K3}dkVbO*%Id+5i7;QAgvSom)>BKdD?G~Y8 zmw3dYj;}%OmJ>dqYik=;oJ4T<1GyAay~-tHsvmVpPzM%uT*2y3LDtacC{y|I9ewwTPceO5oJVHKh#M6?;x`ZBY>Cj8fY}Qx7ou=N zW|x5w4;ZA15w$Z(tjYysM=q&(Ee8D{{B${=WTI|Ln4zsd?sib{Ff{gLMAQP4+fz&5y)~-i%SmJw*m4*mWoReTNa+$3+*`fhkbDQzAZM zwE?0dk0VWBX95BSpyb-8C9mSMbclxofPv-LXH3!@q)B5v1%R308H)*?v20T&k{nC0 zv(OMg{a^si@qr(9G~hgdb|xI>O%(NsWSIDK0zSvS-43G1a8_M2z!Zj_AP~J|-6&^DL4xF# zqa5o|CVL=$lZW{Tohxg=ynB=_elB4e3FyR(hxlG9)^Ja$TisIbK;=>1$uA`pD zghfV4^DUu_mV_7B`1|yJ74_+#(64JVHtD`iTFq}#Sl_yBkbSV?WBG(Z(3o`N9&#z0 zDMW_C2_{8L_kqa=0pWA^BK%_$$cbXPB{kBpgjU^6{^c(reF#Cb83H^jYY;3yDOFzi z8zBVCgIR-;7mpII5JnMwwy!P|_Hog@MB;)4zM@m+ngVc-_+wpIN`;oMlCG3z(Kgwo zIIy*~?6~zzRgUJ#@F}0RTKJz%rhC~$YWds)+9NGwORRSgzVJSPC71_rC#~9m&&9vs z9)7`BEmwB=1o*Ne)+=s^z2a54pS4Gt=@oVSiY4rmr#|b0=Nk!Pm@yonXoVcTihrRk z6QqLZx)`Vv9rml2NHtc#QSP?g)NK}W6xXklNoeHI5W!ZKTMW%fpnmx`PJ$WCa~WT9=uJ&5E1Iis92=7KLAc`v})wK8uRq8M%e?E6re`p3YJ|l~bpR zn*&rdUNeKS@RFA$I6>VU2NmBTzERdYq!$Kw2CjO*VMRk`P(#E*40@LF@Qde0uEvW# z%Mek?JO_?BYDKn!WoN~;A)B!Pz=lp>*aww+6PHW=c8}^H9h_~tN(lr^zGRh|K<@gi zOy z%4|Do%ai z_JwRHS}VSpqI4^C8CbSclQ7!N8oSQ=rN*MDBDtMH|CmHZ&!1=#$?#o`?bMT$^0ivy zb~A@&R9mxH?`TtOouU#Hw^L7LTa5u2>`1wX3hcRNSJi zyGCP7ZYg7OKdsSb*3#=~ii=c(r2@EI1B#{*hs&L5!{w@oL4}gAxJNPYlkDAMn3;Ef zR&$huOHV8P4oW7`;G6xiV75W;Z&8r_b$@@SUa3)MYRFndmJWu-S@5*{x%$EW}~;+N|9uKXR|~8 zW>jvi?oBnK^&xXNva!Q07E3ITyzSAt@c6Y!B8GbGc^qRoXokt-c*Yzw3T1fi0YmjF zV5ki>;eesn9m!JAB+cXK9!Q>oMxiGUC(FVfYuUMHVYgbAECtPVA0Y(|qZH%fh322e z0+reG8BHh``#8-gk8sG`W>hwE0(S6(hBKN2fO9wdz57bcZqeHeh8-otXSyP~71%;< z$s8wdN(_b_rQlyqtNW(MQCp+?h+M}nHRY-&{H6-;d|8inl=xm%0hUuHpuT3K?%hOk zxdU?fgB-dQKL#o{pH(pR@c1<(nZVa(50%X69C$f>sAQ5dGN^7)fYt0l3$U6XnGupq zNp)IIq^H4bDVg;0c**RwOJ<)~Yv{k4nP~j5N!nBR&l?VWPUmm56#ZY%1SM6%y5 zlE?Z$KXG1w?*r!(wLWnEQ0oe2{;nNnE<_O~1|HL!DV%b5!pdTp-N|w=ciD3=I~X>e zJO{HQuEX33lF0b-0mb{D_L)mHAzTwr8 zX?c1ulR>PaJv7(JUFNU@=Kr~Bp}87zExL*ppIH|}cyDLvon=)YN(EKN!}c1=92~^G zp4)kbrK>rAle*IqVk`z@4z&*$Cg_6bD7U zHuV{?K~!C=Is6Q)huG&E6j})^JY*k?zxgB4W%*Y?J|W|g2XzkooX&@NPri?E_0Xov z^Zzk2pG0VuT~@6^b3o#HF+amc=ydt4O4=Z9AE`k2BMDm%efA&i*m`qqjx!bp@T#cw zXVg5xD=wqiB>r3^Yuweh+QHnk<72|^$)vEPpW2{ z1meMqq>O1?q9tKm?cL_u56Ha9Mu-SHo2yMaa=sVUhFIS1P(9uEP<*}l#tB?+(z)}m zjC=+4zC#J85_ZpQ*!aGzp5VS_;V-v@pFhCLu#xk^k9nGk25&t#04f7c7 zL}0%m`qB;mev$(kW^zCSJJAa$F+#y2GB4ak<^?+B6{s_xq_?q=+dM0?w*A5)qB-bb zV}T;1$s?mdfjaY2V8gg$$}OWEiv>1V=UO{n>hA(CRuRegr0`2KZ4!|KHrmJm!_%ZMGp9O{ zzbh3YUy(Z12nWFTr+4x(8sb|K91WLvU=JTRA-xs;(QrlU2l+??_W6q7ICvuiy@c)m zHMo~1QC7Bquj|3dMrBLT+a6@qT~u{fAbgwCb&@8Bs1Hnk72@AwKr{3~cBIC#yCKAe zLO(=L0f=2;CkU*N41pCfiojw(&u0q>%%ec>BLI|J%tzIm^7OfSIpVzE)*ikw1@@h{ zwDtd(B1h=6@i_!9btFLYM-aTvpTC#}c91Z(Z6&@#C0g)MiSPB5<4LwSjKQD3kdg^n z?}X?Yx_1YLb0+19!0?yq=36@W?3Rczu58lg)P*Wv*3Azv$Q~nH>J+0XMwJIY=&cKe zCr=W6NyN^&w;tZj#nA@%IBRqAA^)OVvwT~$FvJdDMw(l~IU|E+M?fgE8xN2|*Ju~9 zCmhUG7QQhz=nEAue_%KGzk#$%O~18)AfI)!f&LwJc+6Vm-_TQ3^5x)|;&s zYZ>vvj)*n|s+-*)7ZXcrv@sBE)P1M44X>PL+w6QZBOh|=fiUGlx=JCjv_cq!xg9co zc=NsgW^RK|<0oo?kk`}_A;12;t%vj9_~h2ZzlZic{`x1@K8t5`j5vG!(xdp-i&qf& zx{Slg1^IfWxM8ihfv+)-u$_5b;fb-BqqT^Sq1c?tfdFVJdhHy;eTWr8=UoUgO`*-L z&rGw^vc0XX8BnzAeUf$^J2`DFds3O~lMKsNnA3Avayctj%h@xj3|(+2=)%8B3idi0 zn@gV4G&(JAlx9~E~q56c>G`PmV4^!S8 z^J`*ZzONu)zB;c_Wq#7;>{IETV4AM=FnwoG=rZ0eW*sNo=`_VUK8u*zkk+R}D9O!a z)-J}0@vbHZ!nnw$okzX}4|xNY{x1UiWRK4W6*QlSsmSg7;a3&k_bI0P zrW^{JQBHn3qoF8E+|mqMi$;)RDjPqw*(k~n;b16{Rps5Bm8QId>I+L1z!1B|f7@&% z<;QC39$dw>6#Y$xeW-EO^-gUKmj3{vLbk&H-YF_gDS;5x!Hng8R~Per`jM0s$|M8A z%EcnRIZ7!{8VDYt6}*GG_Td*`CK3HzK*8$yDbi(lh?;hUn)V7et&5trlbUuI8J<Ly0ofb+iE2+?R zI!gjGtep_a_ZZqS=^H&aorUu zIj647;;xj>4DCyF;S6_x;yYnIdmWTyIGRjSs{*;hJalN`DC}Z7uFRw;?D-Uhy_gOv zs~qh0d{iD-utHGiOW!ZwSI3Zle_x@){@437IbM zOXz>CqF;qBe?;i=4Pq7ij%~d%`V}m)K7zx2JB^%B&yxP^PJVNepP`45I{uS@uk}I7 zgO|~sX!#hG4+)-A#hzUMd@}y%g#@_3>vvF9mZ}C4h zM$d!M+N_TyJaLIHL$_C}(sp#9KtzYV5C$^RSoPb8D7F7fiBd7|su?H=!paX)5fo z=2qws&=kDwYR|QH8j8pL-#-T2zTO`$bMhWc&g8d$lIgH*xpjfcePT_D$ekCdH|mYb zt>UK-3}25HMs~oES8Ug*J%+c#@9MGEOSKw`v!7V^#G=v!Y1)Ov5dhPH*ls7EA9`W? z#_cEg#unVTA2;?4Ff|`}HMVO7CkS@JhR}9~Hvb>y-UU9Y>e?Tl$s{mH!kJL8p%-hc z(K|S(vB4TTSaZURp1}#miW=JR=r7b_wKlZ`lqwIy!+039t*zSHEA8Jt?rm@N{%UX4 zYMXb$JG{eF1ho_6Bg998kj#I7*V_BcnFlD6+~56tG|bF7XP>>-UVHEL{4T)|;e;7{O~jh?=f z2%+06JGq_9!y#rH8|+4zfl?7Aig>BhqM;yQ@u;C3#IB1yA63|AQz>@0nA%RHo8qol z`Q~Qa{044DY<%TwxcN1lKLs+HTSnB)+{~M`I2>4Bp*}b8_z1xSc!+K4CNbRE+i*6Z z$jlxNxNnNw-~`l$HWN5Dl{-v~tMVlNMDgcX8Spn=7+`ai#DueN_UX2HCB52YNYv`c z=~w&xNL8z&YeawIes7pLxmiN!Uj7J$DF6}1->6M*tgX*z;(T-&zrtmYIDF+ez&`&K!DFe;HM}^RM^NarS3IAW&A~Z@_b!BfFU7V@ z&*spRat=MYMtqG%Ts!-@ZnPzOh>(R_)E1Jn@}#Dyrute zD0D8|NRG=H8ENaK;{^_zqf>$!r}SpuJwruNN9^H_(W;*rGRy3V%OzfTVoA$Eb z^iJ}diaBNwiTIqBN$@{ZP2U=(Da2fwyV-k6eHo8=5ClD-ppqGzR!@dX)+t(C0@q6O zdWMR8u|XwwD*x$wi70V#7BrGs@TE8lN|?CQvf%WgWWhJjkOiZ07VH{)7JPG%S+IN1 zS@6wMu;5Go8(6SgvLKvwMrAbO417GNHbGd;FhdFoLa1Xhda4|7mYQ%Dh>D>wa+ zfaH3@k@4?xFrQ(*0yl*DZpgQnII_p^MZ;8B(N`ZVdK+cTiatbHGk~`mb^IDVaydE}P{4mF%UZzlz+Yqb)-P!XT)uqH+yM>Ij8=*Ji^ImO67iE0S*h^!t zL>-T)_5kovX$0Cszr@+Q67^!m2xd^Wp(kti!;}(6zH=m*z$&0U@VHY^q25jf?V&$W z8w4yQIw%3>z6l6ww4O|Wf8(;Brz8N0f`B|-%ux+CI#95~*ZJqRuCK{phqItAr0Sij z)8O|5Yje_2cTEB3Z*T?yvznk~fPx(A9Fmxezg6?imGuiW^oc^hxmGM!r{0So3h`qF zl~mk+_btTJlu@8v*VY}vOD~WQiB2CU(V6f$AkI_%U|aWJu&}L|j01S)mvrIZ(D(ec zITEjza?&EVARvJA6qs!wpdDR{x`;IwV?Us1Q$p7pPk#R~*!3zuBQ_zin|D1S=kB7g zjM*k^!nhd|mCt8gE^Om#eoC=hXV&rKOhGwBc`wN?KSzRciSxh>2TciK3UwG&$L$bh zC3DC;Rwdf-w`ps>p1~fM3hBK@p~4Jl-nb+~B7jlhyz$Z#dJp+>MrE)bg29pU)9++g z&wRt&Q}kh2C?Ebl?P0mL$F6%woMpa7%h+{`lbzgig}~L1Ly4T+^9qWgH<70s-q8GT zWJ=!jNP%NoWTJa|WTFRmTW;@3Zli8eff1QR&K0-#LG&oIAmnOsr#HsUhr z8|5$OataV0sUYzCicCO~;xo6wLF<~Ens9RyZf?S@nt**dpCe@Nj3NIv0GI(W7jSYZ z!o!`JkGji+VRIfl=7OLh|Bj_0)aLW;3;Q;PeNFK2vKwCfdy~`E+ba_%D;(4}{+q$c zi`+j8V`?XOkHLh%BIKPX^X@W-bmftainxkyA=ae(_($bfE3pB_v1)#^P|YcU76psa zsOGq)&yuS7?MgMDo6TA*E8dK;POACU*)o-6QZ^gQmMMGNh1tBy9&}2CozdBhNk9UH z&|7cQF3T#eQ%U`cN7o?QC+ox2>X+_3I=NTx8wn=4JZsZ~$SBz~lGXa(v0DGK_!|Ay z+=OhBxpuYw@dUMgoinlS5sk-`W)Y%w(yOgWDdLAW%|H^4b4&G8!X-~9vu5B+o=GC(}z z;Sifg^|r=4yXWZ85^sv`DPtX-)=6zj_ z_5EHeSp7OmHOA^Q%q(ZmC`ZwH(jb3R3kWc%5*L}ZKzrB^b7;VP5YrSgn~}IsMc}$~ z4d1qq*&+`A0(g@NpvS>*fAay3DConmUHB1mI%!uAg)jk4Co*Oi9oto{s7M>hY;nj( zi^Z+eBV+QzfIbr+y!hZ1AH?z0iqD1O263L}PmfG0o)*a#;!Pu37riXUbJHT@i^JyC zm%ls85sXwhr$wfCq4=z}g_gHy4`pIYTM6W1oDVD4$zyxGmx5o1Vag=L$x{?yE;QYV z6ofxzSP-8102R2N-5C_CnhLCIt6&|%$9GBnv~ss+sRVcmq(<^PG7m);-XhKG4O#=M zIi^}$x!S=MH!CwZeItW*udekDLO68RqgzSzkdB-Wi;roYALl!TY8P zPR)pClel=x9EDdQmHd$Cka!ml7fr$1@edI9DT?PtsZoqr=)3$1a!b#I_6uzFX6&I< zdHR9jBp1#>O8=~W@zY1zs`nj4e~@+)k+zi;e$RJ5R1A7OYuFo#Zq1R>OcTYiv z1Zf9ePqU06Z{`^CUyx{b`6k<}I^{eCw3z_+2?Efad?O1uwEMg9bODOg9^>jZG1?-2 zqYe$fRQ8F2tHl;os?EvfKDyK{)%A6nrCl;d-sq(qd8pA}=6QOh3w{6y0BcB$2t}@U z#sUTTQ|mIH2$~Ob1o`@!2^7v`XKxxaSsVb(egK+z#;&a)bsE7dJ9Aa-bOnUrxmemhgbdJk`QY#KYF_A<#nh=GRaTHGs< z=O>c$J#q2S)VmlAUXFApL4ja~F2)01NWO)MY;Joh6Pl(RWSr&sO*V$-$GIuYv5T6v zuLEeBE*dNv_tHqGiaGc4Ypj#WLxbE!O{qpK#dxA2qZMIt#7q(g&2vBnuAJv}Ta&rQ zVGC0qKO9fYb8-DZ8rQr`clR9_*xdLc$Q*99KEq3q^`d$CAfnYR4jr#Jlw$4!j(8#p zS&RvLe+H>O)|^pdyn!hbuPET^-kytm_vbjofokqWf!A^!x*p097g*`>hM#LZYilP< z6&iA{8761-OG-n2PHD(W(DXj765=IpIspeMb^5SUr+cJ2P2#QhIcY~#wh5Mwp-z*u zY_bH+?#9Pik)NxS>c#IXrMmk4xKgczaIPnHi^R4rGmoXMCq)>lvt>Q0XH-3@-<;#H zFr?v9BwY&Xa_u=%Z(e*3_mYeNP+)GC1*W17snJx(3abv*G(xDbTD&izo)oBLX&RtQ zT+`@t*y~BfpP?8;4D8J=Lpr5( z-%|jKVRv8{P1N&z-+UrQYqI3qDS}s+I^w=_hOr@=)YUNgLx93*M zJk#{KmF`m~qwZ3UnGG_xGG0dY;iNLEN1X{}R3A$yqqs)#5(T^Uu>lS%;#{NfaNj#t`P4VUz7GSDZ#hvi73UzGk78f6 z#bYI_Qy-AP{jP%|-^e3HBbgx8_3e=Ig?Rm#O4g;?lj8`ZuyP~$aRfJ^riixcd?(4< zO)pA$dneHKR+dWt)o;0u>JE6Y1SvYi>tEL%`ZK-$8CInjN=#cpCzHRqriSDM>9a!Gs2 z5Ym@?GofF#vsVa1uV_c$)nLLC^>LC9TjBUwK!Na)gs@55|A#( zM!N3HXh;-+Hp>bWuA~^_G(*~8AJT44zPE;CdwBK+cIkQr&F*NN*YyPnEjCV05Jqz@ z(*UlZOS-?tC0A*tR71%%SFO2%aC=E&$yIjwP+Lljg_OEWY=d(2y|Uoy1}eB3C+dV= zTftQ#YWF>$lD(^=;$58}y4rV&u8vFJ)d`6OSEoupLgn}g$zA=}a;uFfxB69C(+V=2 zkXNNFJcArhVRAeP$t#ZMPiAiiTf4bTGRt%vQOaH=7VSvteaAxHe9cx;RSAT)_*$Ug zbIwm5JPy@lua{|dR_&@heGW3A=5V6jMJmy5e19Xt=*t6_Bwk*5%kziLAnN+XR>8wn;xc>(e{t6Q6bjj?)kWe=)Y&KCs-CjpZLfuat zGNJC;l!Us>AKUWj+EpH#&>l9}^6A#wuWd}or%SlBDG-@v&!&m*S97NU+vi<_NO(@Vah}(>+eOzcsYFl`nc&3nDng0cb5fiiKcgh*`@W+Ju zf7dB<0&XKkV}10ATcJ*gnFx0Vi|PaZ{U{40h8n12Lx=bm+LQHKJ#r&4S&5(_!pe2+ znTFCxb{Pe(JhK5?^s6}HLRIhGrri~N{`dd!AD4)G){EcX{%2ROHuKWoekaEv9`=6F z5Ix@ofaVVnu56e)7%3Q-*p)qStUUa4GP5=q<{`t^X*3*lg?#%A*DDZ8BBjod*@0ZD zTes_GgHSyeTrn+@{TWL4wZ;2X+(_vvvG#b$q?xcUcZwbIRdK9woNo(7_)=xW*;^Q4 zy#;X_j8)vg!7x-uI(Kx4Qr888JwSA#0Cey}i3FcZp%_nm{5ySAlV4~rCq@3jn^1T| zXxxWc<1RA~o00`aD~0(=K66`!(@3`uT4SDAownapD~KqexY$j%(V*Nuj#!$^C>OfN`$G z@GmXv97RsNP7-_GGbBu&Zk~XuO&Yy8iThLgl7_{;~7!lmY#KgWFHhyHML-Ge}XtlMvzFF00y4LA-?o7)+SbK*WKD zF84gz@OHz35@FP%K!Ll=+`xFjJ3{7eil1zOo?hnLMEjs`XPK{A?1s~XhJM>4NDa`9>VULbjNJSY85a&p%o%8i14A@q+JKMb zLZInJK!lp!%mWlWxmir1n9nlb&Jb{jSwq(ueK}D>XPbsjPCcoi>tv)%HEQT-aJX0t zI-)f6SHOf`>eZ4AmSCuxHfG#G%sA0$G2@QpYR|jN6b9F(1ZJfC$W;II?$eIr6>1=g9Zs_9B||haB;_%{&kW6c;n&PUgo% zX5>g-n!sQ@l$V%sywhInIUdUENE6EIw0A2pnEa4ozDFIaIR!IrAZA?fDVTBS_r*6) z&xJPR_C|$JXxWIm65-&^h6~>uTrTX6lgK04DuF~EW=4=m*@p~|#ea!=PjXDTjRzO} zzve+1O)j(dbu$**7yvNgQi{CMeQm^l2b^)C#%wZwg5XnJi)MzZwi>YpioZ$deylze z$=;+#rmVh=_d{u~WMkuv%!kwBW;n)ioiU;>!3@_IH(R6Gz_=CwAKR z!@Yxv;<|6JQC#;A1&Zr_wpb$m1~VQZC7&?>#b&~jEN0TEYQuIC$!-oI)2i9`iVD8b zZNWF&ZE%@PJGTnH(d}@V?o%6jEx62m3c>YJS)h=m2BAbzX6d+Za6W&I;~P2FZdq2f z6=nplWslVET4_hA(;nJR_CT_Yt*==~CY)Eu&w79@H>8{VL_x?+!Zpvt1BBmSc~Bw; z?qh8J`HUR6lFDg#UuWfK)*JkuCVJ1ySl%lqa_Ia5gwFrTq4H?p>^(#d(SwfeRam=| z*gQbSokMi}faMH6An#P9LIwG*84nm{RMQ+WBssC^yNS@X5q^VtiH5UUr>AP7t3T{GVxp^5DN*da8-lkgGb1I zH|qlnv9P-4E_0RKZ~`aC#r&3kUmzAgaP#!gRMs+)b|kLw#zl z!Uxpd7GGP$+`+_Lla82_>VedZsal12SoD7P-El-f6s0MlbE4cybqbdAdO-LtyapFZ zUWRj?<9Uc>#y4AL-W8Jd##`**8Q-edO8isV;^?jdA6BH{L)_e)o*T&mMCM6cYPRf? zt+G+OfpU+SNnQ}jlzK!^-`@9_t?wrj``(w>_W^s~`_cElQ}_Mksrw$Y`hL>t`+#gT zweNN_aIeAzq^zp(|CC+Fas0m3_1dJa<5d1o^#Pms2M$h4xHV1Jar679*aSS;|5Qfd z?DwVl#FP1FzlGjc z+SWwikbP;4-*k$Vf#Ghoh`-4q{ubGA@(>6QqH>2GYYdY~8wSKSBi0&@pDEX?EEQL{J_0S+h|fqF#Hj zbtJ53E!rA{%g0meKh#$IYNRyEJuxz#T2E%X2N7rw>)g_j35oR8h`28ir>8%vg`>)} z=8rD<;>TstTiNqg=MepZcpI}tm1Ho?tN+kE%Ax8o1R~$;hgq)OZd*f0i*NNKPGx1Q zjk+xR+IE65I#vN>jn1;FlVMX@+}PBHlG-7-o$n*VT0HK6AnqUL1nO&h!pg3;2RP!e ztF=Oh4Ewe~fwUrDWJWTJc}{7M*`ZRBZQ5E&O7dy6wM?qzM1HjOHP={5S(=pON=iwt zfX>sTqFpwM|MzpWOP3{0am_7O;nHO#Lq)SCB`aMor9)}V4r#KIKhErsNx`%6uov8) z$L;0W+T(O&N=?RftG&E$L zm77cE`<4))xN$wj-n|zECJ?_34*^Bsi>8&?Yg+QZf zbFz8avMqCNak!v3>_4tW{u4!=k%9zZa;!C0O17AftszZxw_)~$eY?Vu%tMB+KV%*< zB4uLeA54p69}oLJgipil3Hvq!ASj3e=UfFwN0|O|oDbt;o4qptouLH#VF9WlVa7@z zfi-KZ-jklXi6g}KJM;RqRj<-P>bVCfNB_`PZRf8G^6>owZB?U_lU&4qi2dyoo8U|J zqZ|uw!M6n?JPXT&IJ)c+rIzU0WsTZHpP{Jp94Gti|4zOYnd1lqwEa3)o_t&iiJgG= z60aNg{RIRR!xDEdN2-Y5TeVew7QvHWbPl~JQ@-fGPx~Tr-r!vxto)4}Uyb|}l1lZL zk>*N6EJJPX>!P~Zj2Z08b+Qtjt$7bXZ|eGr45PS&wvlt_XOGifTS{V*?V=d;&#(rw zk3?olv85o2z5)8Vx$C_FG-qr7x-)BkeERmCG1>k}(LTiL+Im;Oe|V`LT>18raf$&& z%ese9zdQRD{foGC?*;1Er=ZtP@QvT2s z5?RT0>U+=byC^0WbSp@j zPxjA$hJAe@-}_#p!-V@lL`HNOfLPF$f4r@otzVq#F{V0wZ&$d9jwov**1a$ z*a)=RmmHjBA~u7aj-<_?>3?b8X=CZewB3CnlrM{gtSF}gXRN_( z#M(&Q3HYCsndtv@GF>M6S!hZG)S3bf&%$t0=6SOfB>$o)o&4cRpbu^^%&Ihw@QFSYi9lQ7o!|TQL|CE@)7c|{27IYL*{YA zT=_U~_fR#hrZ`}JWYh)w%)Q~dpjT)eMjXGdju3!2$P zP65ukJ)A=#esv~nmam~)Z$Wfi1k>orc{B{0W!A5sABA<$ZazrkX}HF}R}1e&)#j=& zr3jbxpf0meJziPzr9j=k7S#vpE(T4jX+RMgM$x9|2hB{{Wtl`c3|F6{&z>V_C*C%8 z;Chs7zQqG^D_>kQ-y?y`BR=|$g{xZGWxs~XEXP`ns#Y0L|0oq;jV=JT3%sYi zqT;tCwsOhGH{$--M!xO&_*U{hzLorsZ^fI?N}4tbWgC^7xgk`U^n9(_YK0rdoy8yD zp^}g9kn!=Ylz)6H70Im-Tcv4hZhUxx&E7@s-A8m`KJGhiEN`T`&OxoRALxzR%AfYf zV!@*AVrf+t+@swJ?Ibrs`pB(Fbj>X**Ujzp;dwnWZ5VwbDCM$xBSQWJ`to29ryQPb zr-+&j^y_>=JHn@S@#z?SdPHZ$#@wIbedaMAwG8S z$7=C0N*}3mRk+R*t}Azo|2;;eq(uDNDgGCNS$S0a-y{C-BY@03;zlw02HdRs|EWEE z|1jDW&%55p5&JAkUSHaNZ4N=#xUb8hsH%H1u?4?rR5gk1H!l=f<&l9%g_#sLUrz`E zDDqB<FKRs)80JcF~*e+6gw&f*m2VO}yvk2MuxXi!-@w8ByPhx);G^+*Sn|(kg@; zKl2TanA^NbH&LzKn)No3v_hO99?Uyh(Pkdv zma0`t>(J7VgCG=gTET7FOkj|H=XZ!6yf-MpDat7`uNbdbhgYoVe~^|))@r;3)amDX z=@lI`qq(zw7uJHuMUyiGQ);<);;-jUw1dU!0jmm$gA!KJH=s%bEV%61Ugl3RuxGob zN&2qnCwYSDyN14~0mvA}+?!zyE_+^5@_ByhdHGZ-G1t&F)d=DuvmybeCop@;+9f^P zxuFgo?9YNWk`~%Yx`u91?RBEPs{?VW&Q(-B460rjkW`&ZRK;L}o&2IGzvzmfqH0u9 z6(l`OczOC^fJMlGv9%MLhJ~-HWPw+H`d}`rV<<& zaK>l8)eF2og0yNv5Jb2fs=phMTbQUjd zrDZ8L94Hr+?TmdTpF~qoLe@jd$zPnYU=pgB9}^#ZoNzt6HQHSZX=!G#2b(o)p1+_T zd(P`Gr6o-Y#Mr~7AUgV#)lVO{$mI{=XAUMOD@#1b@cMwe?H)yqQHs>*G9kkTFwYz1k%)km5K7JAjJ8J{~WLJ{|bs!w2F&95z<&uNnT=D?}^ZE+e)v z)~5Rh?)#%?@qTxZ4=zuQ2YQw^BWq_VL*F-Ot58Kuj1YM=-p1(TqD4Ui9C?DSpv?ui zHK<*Vk^~j?_~j}Z=WJ3C>KBf{KUpJjm(VS0M$YEilDq>R(V`XlsuhCzNCW^z>SJtQp$ zizzdBol9m0v&`yMQmmJSTS^G-amj>WNW`S%Kvu@n5T6-7zUF;^WO#D6T|>Opu08O_ zfmjTb+^}#I(dT^8?XD-}kK43WYX|6wtY=F2K&tOrF`)FG%az{Kb9H?glL)Pq>11fF5>-OTU}!BbptX?B=%LC(RBdQ_WD2mgJmJWs zylJwel(>C*q(J;MF)!q*H|jDALS_SiweXUZ>QbS|m|B?;28gQ*=&zHC1Q1vj{Eb&A zuq;4aP3}X|5Tj(3l5S1NSa@u*e2h!yUZL=@=qa~)$^yo!d64K8FX;m`fqYoEys$@? zpb}MyszkLT>}xVC6fCP4RYn{IizSWOiTGFh2*-f@XIzzPJ~`b2C`-k2o8Vh3{JP~? zD~zMslfFxUY>3NRMvk+-aMiw~jE1>!a5f+w1`y2tcc;u~07INEqhXa(hOpo7`4klu~C@0)rm8oik{(XEvKHNNu2ia3nQ!A%Vf5q=v)x zq=wfhso|pSQ#h&NeU;SkK9U+jR#F4f33@_EYWO`l76`rUP^hXQ?CTVF_Jnc;9I`3;*{97)b^Xy*KeHkseBD>c7i1=6c1v89?E)A6JiPIO2i zrIqNQNQ;jNFlg{4%c&gx!U)XdIaY~-&qWWtF%Wy!W^diO}(dii$$iwuiuWBO_p;@IH}u);fj^kR9{63rB_tCx1F(zD^!FLk{PU{} zK5CXZq_6ph84?=KA#+8RWw47GL~v*;z9+s$zhk+Wg!*w%u^zFP7mJM?2#2AnZQ-i^$aV8V{yka*X`p51!BAv+PnqwK*s!*V zlQ?7^2>A}#^Lad|pyzRtv=pY8%#lGbqxw@ItaR zkHodch3Dc9os6rxc|2@x4`|obcYir@oh#sPUwUb9W&M)Ty?pyr?ef6xzC~liZ}*O& zz>!_B)QU?Bvx=aiv!5x}V8j|JnNbX6O@(L}7;ja3s1W<~o}&G5_!WH^Fb^seiZb6D zT)!w`JU8@m<~WUK#KMx~@r+R8xj1e+Z(KOeKAsb0<{pekH#@qg(~vrs1~8=0&?DK_ zn8spEm#8s`*2T4jpQAyED=(t8=h^h!p}RV|Gm5s?&G@q;)?~z5b&hKhp;+ z+S)5(Y@Y=i0F03U8gXP4Q*uk-s5xG@V*LmaYLN`eq#XAW{k$zT-guW!Me>2DV z^eBh9oQ&>2gr(TuA!INvkRU$Z(3tQ*9M2?=d^g9+(&3rlnuTZ5rb#@LziEtTLXITy zFBMn7+WNUB137-AajZ?bQ{tH{*Lc-cIOVFV)_9Rsa~#JvG~k&e7DT{EEIgCB385VG zod9(}ioc0?ChXGUp4hq#nf3cuSp^YFoU$MS7h-|U*WqLc$sVHg)qnfdC^BRmc35DE ztX*Zicd?=et9P$^vJ#esHYMKK5gMahGD(({L6A2Y(%>{Q+x!H zK?(V9sU&Ju6ZuKNez!eBcszND0bo=bZYAs$$n;`D{1zAb=kb zr!eNCWEf|`2o|Cwma|ZFY>RgU6&#_Ecr#0 z#rFmJMdwVD;M znG^Gow(9?Laz%+Flm{YSxJ!HJQKHUTo{X=Hj)-ep5!FPcGwU_)2cj#zq7A@m^k}Q* zI3>C56-xw_$u+a`fjApeI8%XFv|1)w?OqH5WGjMJHgrFRL41aO(7D2AqZn{ocP-4s z7;r04Rf&+dGa+`V1F91xkiCQYDzXt7)Ku)N{HQPpdIju3z9rS1tfH4_jGY;fSKj z9k5fsdG|FhQ0B~+X`JGg13FjzsN5?WJ=DF$3RLQ(nj7M-ztEKaS$4l@J%U!CNWfAg zbkMr&$K*m3M7cWHWJ+Tx1{6>-GV?AqTWl9vZd*yB>ln~S=?3(MZ9s3Fc0jMk z2lRS;K(D6`XaPkz9gv4EBoIE}?E?}2%sZ$Cg22i{av;m)K=ODXqF0BkzKAEf=V2JP zrtC$Bhje@kn6Tz~Jm(z^X5SrU!f=4u2yk-_fO zEQiBCpsm@P#TEstkjUS}56e$*hyI#k=w6q_=7oBPG%q}y#p*mvF5>V7QQ$&0H$t~x z=ZIHM4c2MFy6l2z4(xe7TDZsm&!rdFo^aN9eFKPHY18Yj3RkrmLXBxS>)=IswmHtx|@F2$~U&wL^7)R4z{z%b4Tq7A>#tMI#gTliA+5y zL|MwxZV#CCkxX*D_&NfvPGvv)Mh{0$qrO%r%1T!XkwtpFZ|@^_Z)J0uYZsj6G50AR zgOnD6|7G>c?Mn(F%DDESzzBs+Yn8wXo*_e#6S`OZVNT-PObv`q}Ly zqflHlYD68u8M47vpY&+AkYHz{yy?tW;V5@->F$94l_eoDS4H)hPC`N}BqX#FwaftN zXfj7&^2wrY{=tGoOJK!-Z@*aC;;h%a8ztR;O%mEhCcEf)28vUF`djRzPq4=-`nOrc zYNvsBq@{oSS_=K+m+qw+T4^5p82bN1{5#pA|0a57X9E4Z$p{5cPA@8yYqNI+{D+oI zpAU8^Y6rcwNiU6g*GO)&eoAs}w}rjv*lu4)bW z+qK9oWMQmFjR4ZyK6G&1>&Pu0sv9t70~9vYY>;k6fTtRf?B%y5(D&{h$Wuqe(a*q< zm5?P6G_o@d*ZDYQ=l{FdQy$f>Oe|=cxV@ za_qYEq&jTsC{~%5n=)SWnoIIuC7(Z<<&Zj0$Ye`Ki(U4D2;zycugBV5;snAzV4Xwc zf6ou$)aeYE+w9Kwe0ILK0(;QEU0d^V20P!2$sP7ohIGFFGz0FoKtZ{_ywAbTvFFvv zQ=$rae=j4R&8e-Z%3v>hAw2lxofoIU)%UI$h5W`PTEKkxc?aVdy4t&40iekS3MzvB zZ9y%(t!RC~zkNwQ;~CDyKcXR+v#Ax)J?^?uwLMVh5$!F#CsfsF#IP-oZwf~;o9GZ} zGW?DAzZ~}M51Z=^Ux#+*334FO@W&P6i zOhK&sDE2C$Ongi90;6ZRoq%gwe7tiN$h92rq1bxroI@t%}o}S$z^~W73 z-EI9K)P`rust^TmZdj1_+k(7+AP@OEripP!zU{p2k@$GVS>vJ6jI)pC6TC~j#6$5n zuzLNw1kw|P6)Vb9=k0o<9}7rugYuN-$HNQq#w?0I`tFU6Jg< zu(^j&Q?7^_{uA26|Dbm^p;VFL@?R4Z?Y};fc}3XwN~pHm6>8|t2*sMFN5(vrAitCd z`EV}*p^EJmNYLV-T94m_Z1`$GE8Sqfc3AAgh<(<)UAk7f&3-8wh=dTmTq1_woLpaC20CnH%u z@+4b;Jhuo&Y+=d}N!~~$$x}>{5OC9zDUw7F0Q5AXAF#YN#p!V*sFk+cuQdlEx+KV( z;#!0KT8oV!`;s4Q5)ZoYgq`1Y!C#jCvpgpAN1Hw1QY^Wti{);Fmc6r zn-FTGcu-ud00b!0I%12Z{VVKT&b>bTmN0*mSe1(Z; zG80xAM=^d4xvxT?Yws#Y=pqUoJ%EslfY~ZW(uFWCwb$@tYOCRIY4Wjd5nFoD+#6CN zRJIkDme2Zn6`dKq+M2Jaki}p^Oxj;l5S=e_@^UJnVxphL-9k_e_&WkxxFg`-jcnb3 zkbAb*dGMjQ&clIJ`Lih=&4X=1><@Xm`H`>Bd{-B``jUH%s;%O9hA@3sT(7t^g-CQ=GUO>O@k!*fgXdO@r?cpyJcq5ZNiF@F-dyL-M&RDlTPj6mm4=?e~nTwxaFf zsuoO?M@X7sUnfaI=fk2_k-aw*tB*Bubz%T!aS$N@y~F<2B?oALRBe<8TNtp7)(RQJ z$ng)&mL(^1yv7V;`CAZ(#EFo${tStml`>xAa#erkVyZvGJ39;_bYek%b)6&6h>u3Z zElPyH-8F(uN)%9mA38_y>EPjbjrHPdG_NzE(k0c8RPh>)gm?{iLcE6Ti79&$?>0`4 z!wy-0<_~1Bq53nbbk1|~$iDL2_nTw3EIgYUZpcYw3QhrO4yL+uHMbj6S<>YN}j(bpDFik0% z6hMxhsz`+Xsf)hsVPE1W$K#;dmG$=8l?`1;T^O{wF@$;7G-!2WQku)7=JIUKS$G`k zdLkePn#=1=h81nHm!UbxNE`{%%25m`?y|{Nie^_R4X)z>JFcUXY}StSxQ_86esNq! zM{hE&W0S%by!FghQ4wn#swT-G1=W+jNV>Mkr)@a zDMxMAPO>S?X_B|}Quw;PzuRPgPp0iJ!K{&uxyjbwj=K>;+r2PH{C#6#9KH}`X#ziS z#L4@4Se&>%$~e&mA<*QiLX6#uF^11kj2R?^+IIXjpM?HsBlQwt+ZBl zQVmuJ4CwH9MRcG(WGcD>qhWaW9CUdG68E^Fzy5A z9FL}-!5yg9i&%VOqwZypheO)h9TMNc&CWwv^Kc&3OC*8COJOA5F!?#iUIYoq%i<7l zF_d9rom_&?I@sv}*^2AHIFw)Tt`C)|cHW0^zn~Jx;`#yGSA%9pz}#o6|+S;3<9-NS@{k&C4lvly@*~f1qhw-p^!unTRne5>#en z!UyLqa`^z?^EVgYbFI4PHxx+pn!Y?HX=9%3K1Z*OxwX|Ml&Z;3)5gr%KKMr2iYvZL z<~lA27Bt&1?U3rt-LSe;o14KO5dMO77YFJtj6Ur_u>58rzLUGztdDNb0+H+eeOkCr z_n%mD72gTL{YeQGvHk=zPwvrYt&Xh z%GY*js~_TPhs3pP*fkd2C;lE>v=BR6K?xd_R~i=mAGEfplCBsDR}8vRN>|Dgu3Sb} zymX}^;mR1gGKws2ns-tGD*H6ygIcnAO`A$6R$%#C6uVH>O9fT??3j@RZ?575zKb}4 zW7?W;$v6Q5au)g@RezoEk}L;1VTIf*Cp;;mC?-6u3ZZbq4~5=WlR`T>ONJVZ$Rgo` zF?%W7*E9CA;WRVI**@>cupGzY;Wb%8f$YXKWsMllIm+7VEty2?joRwwOb+B?R322Y zZd~lH83t{%`&(2!DGGh_7C{>0d4hO7vKJ#7{819vx0)JZo`H7@C`SNS9JT)e9RIqI+AEB{8VY_I-k^)` ziT`(qk!@u}=vf_ft=x`Ij-9E*+(B7y=0@x>1h%GjF!jZYKYnQT`oS+aB?XgsNNwXgw%2Fb;{TwW_GV)cJB%qD#Qx|l0lD@ z1Y{(VS`8=|XJ2d7p{>-R#~{3JRn!fb8|w0ms;0%CuNkj%Wpp9xwULU~+SiTn%y3&D z&HL7c!|9rkPG{tN;NoBxM00w^w}m78OhIedJY@J=wTJJ;;pO`$rM}FiNZIW!)U@mj z2?@#EVA;>hB3~84@LLq@c5WZg3Pdws>(i-_`*Pm%Hr;(aI12&CgZlu`u-UQjTmXL)15^YG4Dbu!2-=lf94_&0 zY?OQNzKJ@Z%dL;l+O=7bb(8vlLXu6~I+_4ya;PcNV=Ef?xv2Pmb9VrKTJa=$%Uz}Z zG*ZtisOqv8NYS%8Z2f8ERzQIs@&6X`)i=QQt*!j80m{C=4`{)>UN}$wv3_-w{kA`T zlv%uyYA-&M`fQInWoa(cDJ_b3J5TSA`l?fAGl!g<&3wG;o{8nOpHmHD6s?P>IiJ`5}Mo}gN%E6fVi0jSGuu_zt8srjehMDzWcOc#ZA%aaR z|CZ~qVebf;U6e8Ss$HNa?P|7O$qwd~SyZM~TXSC)2VC3(OMW#Sx^uGR#(ql{%aGXI z$R-`kV#_lXP6MAz%3{kiwlX2#UL?LoXH%x&VZjQA19eaH{^vi!pwDE6F4`a9>`KCI zrc8Z8Qh@6>p`$od7*OhMBn!{iZhb9mUi#nP2I{j|AwuW|@06ES9SHhfrc}wQSFl%= zkhz&+{IWc;Y)8CG+V_&Gq;(Mc{)Dsk3^y*Jgfxmr@8Lw1mN3_kwkN6tBU4rzk@2-@ z>PC0k>qc`OmdX*QtQ+l4TQ_=yt!}hdaa6p;Pu8N3D;tgN6P40nyF)C4ER=TEWmu4O zf5cW@hH;-qoU&%L+m@H2C+4N3X^jj0j5uYS1!ywbG`kb(xRKZi;r6ayvSK zZ_3iVAQGy#baw5K%!PaiDzQ7}itZmXO0M9n$;zxW!Cr~?3d|iPN>p@o-6m^14_9SZ zfF3-Ze%#U(iZM8G9=DD@c)0~wQVY8)@TM~LCMtKGYc~SLUq_5yoM-_8$9uUYy9H<$ z1GVnZIdNQBe*}94{R7(C6$5Pip?%AIT8VaItHFs~f(^bLuH(7L3!l$>T!)kgNrSJ; z7e~2vLLKEYHtP2Gc{I6-Y{o9^$Zslq)AMtF3)pHh5NYzCwDnfkWG5ah>G{&$oEM4p$7NAiFZM(!TUBW zeB_P|^SUEpdq9~HM-@n{_k2qjvH_y_VKo~;JR&4PI@|ljv?6xw1E-wlqlwboIZB#S z)~qI{gdcg41i|CF7Cv6I-v7~(G3@rID994Xp?C!*JfCJaV8(*JnX^}D8%Mi)`LCDuDR#}Eo`;N;n_ZkiFWC&v45Xo#6 zXY60fB4hq9;mM1sF7FW=-8R-?8@jPSm-a|$3kPU!=6<@XFXgU2`(1(kbVPaVcVYj^ zNW95Y7Rl!J+z~y~sBOwjY1XZt2*Gb8p3?IWqhPj+p@@Sh6ywl9FE}Aj5NGLBv89Kr z8j!Xio*y02o%S}VxOwJos~c0rh?*#uMG&#k4MeG3OWJ52h~L0vGCWnV7*7l_4SmRw z9}?mZAtb{1+Q$+i)d-g{3YroUac#~l4pC?sDMJ)q8p#P9uuQ_=e^^O>PmGj;;v*wD zE|E)uyvCJ4VC1H(P#L^&hP#WZe)d?XA9w&z=6n|WG5ZMz`_QdKn#6=u(!AQjb zOv8kHZ1#j@3Ev<-a-vSyyt;ZWM=8`hEHnwu>6yTBJ+KZ1eShItg};!ZRr^i`nJch= z#rEU zhB9-PkTj7f3-{ulu&*ub+bRUot+9GNGJe%3DEn9XiOc>aX%OSxoI!30Hkl*%n(`HouzbaCd&k^XFh$MJ;48+eG7sso zjfi$5{YNmJa* zBDlf|BQnKplUEHh=Zde5LM4dhw!nv0?V&eF3&W{|6OS;3F-?iKpvLN2lO>aSoRdYE@Yd_lRh1>Y(hWCs5p{;s|ho@3wdeu_N&iirN?A5fmFK zf&zs6Ar%>&%J?%-8$5j^)>k%5-L{d!DQ1ahh{SU?A-;rw%tG|TEK~>O{JvuGx1Vzu zUm%=CZR&d6zrJP>31=Sikij34DYl)d4Yhr-8f5517u^e~WDUtSfIHD+EkcieB5a;F zvSO4FJIqbY5H+_zT=UvFg6!>NAuXg+6p7Ui@#zT7TC&6 z9Su)*ub>GoAW_gh%kj2LxZUnWH0O8lob!BhkK)tK&ts)KWHAjj{}egPu^(d`{4$Od z*=Bzc^0QU^(h;cChcxdSx>+k@K@O-`5Fc(g;`3I?@Is;;e97mPBFF$p5iryRxWAk> zS>h^B0R?bW7SaJC#`axs*gUJ%aGbw%8EsYRQ4dN}faJJJi{m;K4vr*;gI_8$=M%?- z+9?=NtR7Z^u(=oRZ`$7dD`YK@T_@7j0+~lC<|H9{S%4%5lI0y*=V0#;89u$wN%y;w zTUF?m@*1(k8%U}I(rHx!*9}VN+3=V33P6jm#1#{0Z8N<9~7>}k$*&<#Z#>3*EX7R%x zh9v|w|3Z9?PE9l|lSA2mWQqUdf|_Mdr0pZ2--c9Zbc7|y@+&Ym|3L=oTzcJ%csO%B zjG1GVb>B*mFkICcsyZ1myHlc;e-zDj5H!yBli-mylIRtAiMEWAo;XNX*xX_TjJ9&X z=thIkab?u9cD^G`;PTm55`@LrDnEa!z-5l%E$6^xu0M|`Q8>H0xajvFl@X-g3||xah$HODjq23)QC(KbGq9KS>D0`$C%4_ z^}xhv(Q8vn(HEtQ7Pa6oQcBU&K&9zfmZHZ}h>`)RWq@?l39n}({fDHl9QxdD_?jq8 zU&vM69FK2Ew8mgjEbKdod@WMAeVdCu44UtwJfILGj~f=sg-{T78NP1Acfv3`xgP2` z9c19@-yB#f4y;2k&{5!MofFZt2wC(5EdW;kB;kaG^fL3hRWocQ$yZiIL2Ub>YHJ!YIiINB zA#HTO$&?4{F95!8vPz~~F`+N2?66X06|Tu-!Gp?$;>@0x$!9A8(?q@+^Ka0?8~huW zT&OZm^QHV@Rd|kf%}_Y5LK>hFz2-4hzBJ(LRTWINTR*glm~z&TRmYc4+-g}TsmX?Qo zsB8?_J|wF|LQdE5UYYy>CtJlwvPFK=20nK%JZ`wbYJDTsG6LT3MMn>2V=mwBy;k$w zcaY-r#ivP!kQ@@!E_qW{J3;NXJ=jpi3n`WYxbKv(ff^}Dk3G6*qUPNaq=S9tRt8O& zjmlS*d!tvKPu1ZrS}0ydwn?7BP4yf^vY!H1f?DMk+&ig2&gIrnZ8NC5)glO0`P#^; zF}*>8fXqae?2#nuG}@>$?(Wrr9h-?!3Ws?2!z>`M3QTq0+jO4 zz79NZ1xk(}&85L|j{e2w996qWJYHAf^N*%IG&*hdKrE73Lg4#WzuZvaK%zh^bfTMvyr6l|4>`wl7WsE{6f?gZa)0wl>FND z8LXn!EA{933??E4Qf~&Iv4(_TmZF$ER@hTq99_6-AQmvAk?bJ$99O&lm=->!`#)SV zT+nHY_{q0LX)Jo=TBobQisAmlL}>4QhB(08k?UMtPvDpdIw@ENp$7%~v&9Cy8L>=d zW-|;+1bAN>$@~spo4y$TXW z811WDxIg2mKRxKrWW(GV8~YjYL*!Z)In34J3T9(1je_5p%C@N>R2bDO>XRP1C5C_}({ z0p8&%dMaFi|BaxlT}`3x5~t$I?MqOqYV3=9vjsWY%c>dz=0{;)0|3mBJr>E{42p+c zZ9+8&ySA7cl)p1`Tqu%BIXG0jXan!9B!-R@{TgZ1Ho0kI-A)v3xCA8L5*zzGeP9*~ zz%$tOQ;J#;_xwWL^Qib>wshUEF04=&esBpqSgl=E>dLoqXKU9@Rv>+q6-XbS8OE1G z$hh4nJ<5tSEL4u^5gF{5cB*<0Z+?tpdV^CgoQ5RF^tfclwDlf%rwO&fo8q0G$(qA? z_jhJ<25}=2*p(#ko`VD7^5LZ}t7?Ofnq1T5qz;>V$w!^eL46wk^lUEGReE~=bo{Q< z_^0hRd8XU_)1g!Nr}>G~`=|5Iz%^|f%xPTHRNE)d=9;#q#tLubiNc3uPj$-vKAiS< zTm6-B(b>xfVsw&6#*{>Vxv)RxYE0VTDc|VkTM25XT9^4nAKAGQKv#3f(H+ws{1N3A z#k4hb1UFT$t++e=zOLBvCRv08`?^D05s(#4=3+~~v!EjS(lXgYv3*BlHT|(`BL%dx z23@ZQT!(f4Nx{e`b$@Kh=hRYFBJ{KJAncy24jM8{Ehw1pYl9t&4o>m4JQ!Kz47hqi zRkX*}eI+*bzb^(`cVd_QlD*l;R9D?7YeNM-7D7zQ;n<)+r3#B0kPFRwMByXF#xrbxyx2nSdWdxC{^IJg#WCCd`hY3{ z8-~nw!js}uqC`8IM{19{NF5SPE<1^a4!t-ameNKu8XN1sDBH2}?V6F3FJFK|T1X$U zqV;A=omMxhYq1O-Q`3Su*%%w^7LS;1U6liIKoFjcI3nAeIZ5?IEc>i}vyAvHGq(lI z^<`$%Y_x}S;F8V(zjvdWF=`Fnu0V)x%#<x?F>~NFN+j3LtQMy*5wiUXPA@QvI!;2@HI@2Oqn22ju^7Y z*I1WXaN8qXo?Q1I(jp_JH!7ST)y+eZY%juxQM$E@Xw+s1%~G8>za?pB&-Iba{IG9# zsP;`4?7f5wkp0s{JQQ)fFfP!Bg+giqsL(!h8{q(G-dDA2ci68Sk!Ur5i+(#|-EqLM_`wOw%%bxIP&n+ymV1(JwV3*r!7(F|m&B=w;| zSL6vWeReZ_x;3~Q@6HXF2Zj2|HbA&JcT==5GC~Beieq>WAH%P7k2cI11?Uu+%Dyaa z-G^U1daM;?_PntcD2dn^gn)v=^x~Wz|7AcOsY&#+HA`C?mS(Z~xHt=qt`PD#o#7O^ zSV1QQMJ*bfReC3a^m#pUK3D3xa9d=05*M{L>$1K0-Owo3@jGlL}Igq?@%FHIcbakJtyIJ;GcQx4Hl)QDHEvgsS z3}qiPxgi_c4)-1h@vU33fm^I{Aarw|!r1Y3Ccx{k&7K`nmILa}0#5*`tl_Hn^xET@ z_f?D0oiwU85>Bz%A#rkDodI9F*uP-gXeY@tlgf#Q+dUkKSh;`~ctoEX?cs77nK* z#YMm+ZW7ItMINRo&!zv|2t}%LD;0luKzxZ&3EdW>YDZZW)wzZNrk)E8R`ij<@b8o^ z(!g#%yV>w0C7ZN{BG(HgtUiN1ey|>vMKbH9iP2Vxb9`+uGD+GIC7xY5@C$E;=ed`1 z(Kd@uIwD#(Xfxgy{~VIU!48^ca2o~9X>df&YCgEXEZLi&WN2i zdh9QIN51T>UaF(PFQWp=JTA6)RW_f&tEk;Q(3QwKiuPs0sGjf<+eQPNoHq~2M1nhh z?vPB7-DFvn#flWP_MP-ny{h+LN^nuJDC+mM0lf`bJE3$<`ZtiCLgz0qtJoGiBvG0Z@u+-dRi z?HKh}F|-dT{}Gcx5!##2oS%6zQYHaIxre|-zX#j&5eL%E-jCL;A=1cnctYDIOkHvesBTY|tzzv&8#%rC* zyH~!;1%2yr22}_hZKin5%r`|7{Y0IR!r*-%0H}hZda^ouQk{5d(N^N=H#a-Fmi57| z&F8HWR`}3WvI>7out!9)LXTxT?06ByhNm&oN)1ia7Pb>+hTnDn;}>icHBkp6uiJfHAB zNngAc@6+AteP!uiS;}k>G|#~+L2`QA<^LE+d>g+nAV|+Ny{n?%&U~9VU)+7G>_eU| z$NW*)oaqr=*cuW|QwKt3hE+dUbwsd^3;pn7>=-k3;+!Rdb!3U<@;S-=Z#n)y-+$6J z{*u$uyIArny)Yy7g}48PY=0#*>N8uPht7>9t-8ZKQUzQoBL#c z8h&|l$mTD9RGRT^%t;vECs==mdN;Pb40$^8i;MAM+RX}6U#~l2$)_`~N3o4Lx-M)z z5gTlNeLD?({cjH$eXGBcZhed!y7@Fuq`t5Fp%X*4{vS%+Zw9mdM+Q>)b1>WQ`F^_f z_R#+n{~w#0BAwd!|Ho4Y2Als6Kc9vl|N4AvsQBZN)-?Oe*IS2fzy8lO{O|wIA>;pL zFQz$fzy9LD{}BKD(%+xTKSTJwhP5Y7!>>239rXSB@c1`C50;(DL82qtcuwKR7UC=T~8Gn)C9+u_1e3$uEbN zeusaUZa@0rknKl@FH19DzrSqA^!vGKO$H_hVcDA{7V{nY6#zV#f9ng?ZMpd?;Bcu%`q#@`rcnTWb1p`_tTu0{_hXj zd0AbSW_U(hHN}NQ-*dt zj>$uNo~-+QTK(h<{GGGc4y5yUlE1CY+!i)N@h|~(ga^$LvZX@H@#Y28Qhs#K{dQLy z_ec8M?vI_Nf7tTpz1Wz>pI6fu_vf9#`2W_QJpMEEHIgPaxpuK9WYDd_k5&-$(L zeVX`>U-u2z;y(s4{$e1#Kl`GAf#muA6!chHHgF1hTvRsjY36(Fca!Jq%$|SeTgmYO zXUH#$lArJT{i5Xa?+n`e{EXyyhpz6MVk!DZ37q_igoi_4kzM-qy%zcNqbpxLnM`@v z{`D`W`WMc;{XgfPs{J42ChzxWK#xCeOa5|Mtg}`MTqe{i);sl>8m* zIMsgn>yDKD@>8_`?%b62&#FD`!IW7$tM;Dxa>{;pR_$$lGL@cZwm zeaSYfXLsJcdz$`ziuV3~Rm%Bkt>=={>?fXho3?Ev-Zu7-??22dxEwFA)fXD9_AjhW z+x}q3$4*JFPyE`SqECEk{`mLTPQ+(Hwtw?$CkBC^U-{Xo^p%z?&rW~)=Jfs{(cfmR zJDHZ=S?f-I+WuCczcojOroX@VQF{Kk@<)TmALG6sOW}`}yY2G$V8!>JKOx0~b5{I! z{^G&iujc%GaP^0Gzc{$^{tYh-ZvU39P2HcRyz?1w7Iem3J9W_`!FAUn48dt3VN!XdP1no(@N-f zgMKfh-%IHCeEPju4ivu^(r^=oV%CW{;Vf~GQSKi!rfLNF14F3-;r9l=fxTI=dSN1+kZa3KbSJz ze&WZsL(({Cq+&6JrJ|^%8>au@jiWfx!s`nDdm?BzGlxq{M7RE zf={vEe?or0@V6&Ej^Ee)2DhBa`n>Flv&%>O$~l9&-)#NksqBq4e~f)Xd9PT`Z=rHd zS;7-{*_@W3sCL~kEseeS)A~pLIk@t}Q-3-YJ%WE44EyU-pF4B?|KsR;!bms>KTSSw z@}5;H_pFT6J?p$;V!*ix^0sZix+;0U8npFzbaYw|fqgwz(=BI7Pv31PQ|LK(`pS9F zCB-bAoqYcvr|_EC>e={n8usKkdTT}-4Go^#{@Dqk|2T9@Eyv7FVwSGLe}6o`m(Po<$G?q8s9Z`3(nCgwZB@%KU=6L zaK_;rF9#0sO5)R4lO*}HM?RHY75tk(ib{Qn>gjmBEpN2lU#R|2cNK7EYIzm zMFpF-I;ybW{_7I(PMpt(r@~rAKai~87gnHnhX$KBq+e%a>0e8{usGnn=ev0NvJI!N z;Ps98sPm4&+eP#w3hyDp+aSDNBY=7d6vqXm^-!MM8f+CuS4E1wc&wr*!JIe?pS)Mt z$Kq`($(6x->oZcKN8Y>+LO2Okj`7I0Er;SQE@hWu^(Q4*xa?}06T;g1NNz`hna%B0 zE1N_N;z?=GoszBDB#?ZtxO93GQ<>mo+oW;jN~Pc0hr)wnDT`^(ou^{yQ`m1)J4&&y zSMV{sl(_*ipVh!(@wfb#?g#x=#M~JiNzBQ+)1KQAe4Nv4f3;jL56u?|np0mSNWu|> z*`xu|68%|JUsBwt(Oh9^Zuunaic`hAefAXRXV)ZHrsLo{9OoMtfHwh6%8+Dh3(7T) zC2Uxc&aVixgqkYOFAv6BGm4u@;~>2hpu!#cM58Vti>ja0?Tsw_+?MI&{nDp=3|t-CEY*n|!Ep1@!F>Qv*k3 z*1Y()qVK_^4zWMEN^z!sO=-btft~0=arWe&mFuWd(a(~`N=my9xaY^=ePk@Gy%N>e zs{tCexBgl*!R;NXJ(stls{ar@7Kd|6e(Q%+V>r~X56<48-+l30w!Vxnzcsd1f_SYD zUB}U_3hVbR6D(k@u&smECq!vabhK5Deke)|>Y6?>5Jl{hsnS;6Umff1M;P>n?G5%ruR72emj z4<*5aQ_Du+gv&~{Fsbrzlhu2NzYY*0z|^fD^`)<^=s8U9=~{cMPzRP%S1 zLof3S<)p%XEBD1)EWUb*a|jU`nt2)h5A~Y+! z6y-}`#UaVtCAmUzHvATKJ)&*4%&$qgL%fK!kD-feAS=?xAYKO*_6X9=Jn$WcD*kxg zC56zf!Edc<1*!SmuKuq13d))LzkQX}PQt}i>-jB6>E*Mm3U$QbW>SN}c~-pyng(Z! z9}Nhn7=?TExpGzw#(Kt#eqWKOMc!{OhJ&Z@LX`d4PoO8x#BxeI6d&}prPRI$f^MP+ zkou4D`x=p(iNuvJ$Q#d2JxK;}u;nrherop6>t{ZyPK+tH@y@oRztukPc+V7HQTCH=KsDAk)Z zr5ZTGp&ZFjtfV^v!B{b4=)i9W!Ym%+)J9Wg0$o6HPA5zBQ@7mzm@i%4EuT!T3fvNv5npLv6+oO;m~Z|i<}_`2G<{KB7Pwk1t!E3J*P zL@~8wf2V9>({$Nd);z)DDy)o3_gh!dWvD*gTSgaaU@WGdoO)eym3#f$wiY~0U+p3(}{T%*4wv=d{$V8(-moG4r)GeB)(cqyp`ahs-?0Ylce}+iQfQ< zm+rN#cT+uI582v=WC|%?u^ormbkSbwGQfbLC0Xo@S&Ui>nf zz%GZItckf$vN&GyeAM)=G^z4B;oRFOo!jRwJg-Rh3~?HI^6uLA+=ZpFbR%w3Drv`x zluOD0x#;O_n`13f%tqyi>}nyq6Ui`XCB^eRtvvkBxqa$j^;HM5B+erYeE8gqB|NjWM0pb=g;>i#g(12-+?8oLe9Zegwq9oQv>Yc^!!lNO zi>%m--r`W2l&4yMr{-WUT1GbvU^06Wy)^l#@&v5wQys8wzb4JYhb&5R1Da=SVdax5 z$(JNCJ<#}}9z6luagQBRXQ?0v<1l^&oWZ^hA>Mn7C^5CTX5%MHN{69&fY8srD+y&e ziZkGxJj5=<=TsrSnKr`T!evEPbwrM?7dfioWZ0s^a^$Vz9CZdRL5@{(B5$FX=pP48yZz zm$srlE<$q~`CIV@f~{pt`FXnkReXu#^7NPiZk#Tnu#B0ZFlWX1VuB`2h# zhbLXsJ&+p3d2wqUN#TKC&^KgyaYME?<))z;cLrZ!zk6P@<2-zB(1DhB(l0)r)4pop zRSgFwN^-pSh8a&?ZSlq_PUx)=mBa8j5AvyOfhvj5zyP|P(in0o&J3an*${<-73Tw- z#v$h=Ked#B=ix6S#deM29LdQ!uXwMZL5@=S)Om~V`D8IO)!)jHWcFvyJcgHwQBDSL zBE_8i6<#sOuOnk)g~s|MOb@9Ut{ZIUvAm}%Ff5AZ!BDNvO;y}s9;2{zu@+&8P#h}8 z<`iyUMFwS`^tL^H?Qq$HE^qW*4aF~B!73$h3&tPk^+qIu&-CxUu2SDz2R1UcX7_f0 zWMLnNYOxB7VlCy>C8kzXb7?592e^_A@Q;rf;D$b@zCk^UT7l2xGeZ|FT*Lj(9>D8^ z&&^ajW177vjf-eNoIDHnimN6ko_ksg^)_-780HFTG=kdK{cyFtkolo)X?815aY)5V zL31fEAn}3Op42#d5#9+dDP?SIrS6;>%1A!Q`dhqLr&zJS@HzMMUVo$82F4_21cpaF z!0m?`PFPB(TFfc+=6O=9yajG~W;VaBrPSNYBd0l%cTBDbIlAA*GgiZP0Hgq|_bM{X;Pnm405~R?1;c;ia3; z=TP&?zyuAi6(JTQx2Mb!Dxa`+hxV$#Q zZ-?`qONpo)<+U5HqIqE$fJD5gq2S> zDh%`c#%q&()kT;fd`ei@T=Y|V_bb{|h@@`M6byWrH zZ;Hd!Oy7F>lP=8k$o_18vtX6CO(xkk$)zY0r#nCvM@oXX3!k-JMm(icPb8O?s~iw* zA~%KW zF4OXN{LKHHzaC|EdN1I;?_RT)2Em|ubJ-QQ)m8!&xv1@M-#YFqwzLO*Jv`!`3))ZsSNoDCQ>zu^8;#F zG@8rj@fMT^*&?<5(l^1vuZSAL%UF5Yc#&l(8{)Y+G-J+%IS)h5?H`>@pB(z+D$c*T zj_?*p?_jKo%eJ@r6Htnj=xZd&El0Z&EH$%uw00g$#KKdhM;?%&S36=~@F>KZ}T$S=p(d6Cf{`=bIqEY2wMv*NxzWLR+hNg*48@xY3 zEWlxsXJLo#18UJz9jm4P?poY=)WWqvX?kUCym$@d<58T8*mH+57x`s}wH_q2mk{j)n{4MrMg&nDIp5rY1z9!yMGaVS#Z8s!X zQiHy(f!s7K?%w;2RQ6-6;%vQ6Z-Ha{UEZfia+i5G@G9$keBJv^ro7pmy0@U6ByW+* zeu$M~cDOt574t8po5M^`F_icDOEQ4yNo5#0sg6*;|BX%Y?hQ5YJ_u= z)#};f#tS@e=Z20~!P3-0u1xpmJfb)ctrM-=Zt~7_UxRxJ?semPB-~;R<~VQhb|AMr zwvSsLpXQc-a*e}_KgSvGNT{`*CUzT3@+p@bq$!wuQE{$Wt2qh1X+}eQ`g1hIz0?rb zbVNgJxJEQYY6Q=&)l%;3WJ>3()$k%ARWn4Y9F(d=w>&Y4QpSFn2fAnt^uB)P21@D6 zg?i0Fa{q_+xp}qbx;&|-A(rpx#dKyBQo6b3K}AvOQn%ZnD>KzSFNyq)QEj_RuZ7*v zqYtFZr_g!_tF3q78nyM-=U2?34_nuGPG_AcPe@o3eJ;h7y`s(m@UBp>$PB{xrm96Kx*4d~% ztMx`b+TUe&S2XI%uH2}rx^knIQol8~Xmx(i?HMoJRhx9`x6_YFF$!9=pXAd0-oGiw zE&fK90^?~=%K^$BU z6bm5*on7)qSLgD%oj2|}?}{nMdzoXZ(o@s7#!a)P%QPYtB&G+lqcTztO#Q|bSP-p< zX?v~b^+t{oCdn!JC^hT*c|fT0H4u&yemC+R;=Pm--h`OtksrwAS0Q@jC$sS?#3YYg zkRw$L%Hi2?#h`5UJ6E*u`&Q{nLLHf-``E|=%!fYHM&>tCaJ#-bGJn-u z9hukn;E}me!evM1Q=jY9Vq~^3X3N_@*GA@{E5kb5td+*d+;gQmGI#!5ADK&6sw4Ap zjLb(@^2i)dU&+TQB~LqeWNy;~BeRv7Z>GF0Q?7u1CzYRmvv~b=_^oL z+@A{+XT4Rr#?D*Ul|7!eYuiFgU%y;k7xu{8p(mr*h$$DQjpAJPnWh$eWWU&+S*d3A z%%_}Hs+QqQv|ZD&CfO&1EWe>PaySZnT6@@<{HZXEvq=F%SKwLX?yhm;!d~DLF+Q0# z9D3x_)T^jrEOaK&U{b}qK!RGibKb3#oDiz#S1li1L_TuJuKX1F*y&-jj(yjP^KreD zeX+@BL#;6Yk)Fkn_Vo(Ul+}@hvb1h_?JT{woQIQ8w7$_#-(h0f0a>g4T_%6MrpF^^ zHEuk4cACw0;lV^hbxvqTl`qd0jdeE~>uxmGv3ef7cVgp|yodLY(LCf+y=t2vC-&lv z47Rfd8~8Y@dpTn@pK4j%r)G8MC#r?rmCK@(eummmvA(}TE!MrCaItz7XV2wu^NV(~ z55+^X3TJ)lx|v7|710o`f&9L7d(Gbt%E2bS-Ba9dgZE+Ppz}4Q2;CZ>%Ts7G5lZ!0 z9!fBk_l=+My3~JH)c>KpCArI$olvCIrfjyZDEX5#AYEzzeKT*l)~M`70V*ysWBd~x z{z9?adSD01KbEit=AzxlNJ@Py2$8Y!R1)1J+hkyxq|}rA6Uks1NS4dMJjp*kTT(JG z79f6<wbft9#+ScvmcY%Y%Q@oC0`av(d@biH9sP+(#r(JV{}%%)(RHe$u>BOa zf47FpaDS}#iYt>325wO#f6L?7Cs@ooDX|5)#WPhpE$;aZ-IPi+7{ z5MhR>a;-fms+2M?$=i+Y+FJ9P<$E8q&bN0~S3-YV8nz61%QC9&gC@0d<1$RlTaH6& ziVynkFipvMq0eSruC~yg%eaM>Q9po8pykV*?4oT=;E??HDfO>+g8D9J=;7R?^Jhzn zHJ5IO{0v;n%&2QWnmkij-NCk2Uer8wQ>^bE51uTNcdipVVHPjbOXwP`fDqw?rD8?+ z3R8whEk*kd^(BSN>8i$1_Ymq&U5{_c(YjGIY+4zX$Pp`ogScm(R~MZlH@cZ?J`)DP zMu^rVSS+<&4t3>E;_VFkIBzxSau($)FV0o?quoUl9D5Po?@d?H_M$h%Rg`zQtMFp1 zw-r@LI+wn4aZA-3xb#Ehux7nQEGIN3MXorfe=Lfkz#|WFxC<|EaSTta;ocT-mNus* z)ZU1mrA8l$=yaQu_wLYx6!FS#sLu>apUty0o*%g3^>yY94bH@x@g5ZZ-cUclb!WIJ z0q4DzMFY+!y7F3oLleR%fe@#O|EE$mm8U#R@(^#p)|V0|Ded-r7;i3hI8m`meh96B zcRxft4Fv*01MT|BxiUEy>@gx1Hj4pNQLkbDJ5G z7IFLE;iBRM_2n=K(PqO(E@U`fJa0?o)(a&1q|l^y+K?d`9BgH4@GcK5xl?@ zyucLf6v4|)!OKm-XWv7xXg8VMl;krhA93TkURyus_R~m{_=+ULTG00_4+UPuVB8$X z=kx9oG_FU&)d41dF}x<4PILU^(sVx$79PKgY=~i$b9W+L=R*I)U{v+Hl6b>E-NYx& z4O_uQGebAc7hPHO$>YHneh@>3Y>6^Lp25g7X9aqbf zz>V+M)d02j&qCNALtTodldpatRB%0Yql>Um)Y-Xt&yd(pQ3f^FUrKd;PL0ufBeqKD z4*Mh@R*l|zkH?AaSE}Pg(Ys-*MoKi<*fehXm$R>WwpPJtHyp*Mrk}M1_EhA*NaQ~^Dv0C}_ky3GfgZ8IHQ$iPrgf56o=)`yR zgi?7drug0+5#>=r{B5w*%HN!b{9XJ0|B$~k?`ip) zBl0&VGJh|>W6s~E_iE)Ymh-ptUCv)WEq|{p`se)ZSN_GOuw028#+$Y(Npf1EW;UZ| zPzUlGl_(PR_aZe>wX#U#Gj$$oOXRC~H*ddG%sUJyEb6^@b?4`~#qpL}@~&(_n_c&O z-zdMSLDEevbs;q3tCvxfvi+o_lKp<;LYv-*S;_H~OQTfJ6Zu0CpG za4OCq+7jj^rVfiF5A0LBL`V|KNV2C}8!YC0mtQ77-sG_4I=nPPbW0+YQdlZyEHs*g zY+JF2YyMzUwdUVgP5)YF);b#Eze!J;NKaZ=dQL7dr{`Lco=ff2IY`5QOpji^ zX=?elP&KIHy!~A+=CoSHoOYSMrKO+Hvbgc3J70ZA^e_LgEU0(8RM>%y;Y!8XcR^hm zPESvknw}J1{e*LmvP_j9TF9va9L*lIPT%DntxdOiC*ERCY*u(;P4h!orx!>E+}#p$ zzT^e^jOKAk&WbBsXUzE?ze1h!-Q3bN=ez0(Zu=`1@SLxCOSQZ&&JUaO4Szdg&Ntxg zNWU6^v|OZiAkW)8&)cpzKYW|#c^|b<=XuX@Nd8Jn@h2^edEWSieETh$=gpwop;sg7 z;~)G54?B|2aw#7#80ZO1Q1_G7ljQt3cs`QYcSOwj^LgXw73yrknajW7?mjFjo85(* zv~p0YfJ^XDAjMC~{C%;P!9mdN%wa@1&J)8?yhhHcuE zT`Rv2v*u6FXA8#CaUA ztcgS2mnkBn=vV^bAy45KZSyDzQNG7X?#YM61K|N{bC#Nlr%^m@(`Y`+i!Qv$lk7Dr z8}->i-oN?Y0+i(BE~fUfVbv*2eDtqHu7z_(IuX4%}#c&#vUd}CL& z;RQG;$u&|SOOaBmC)}=XO2t&FqJmW>n!8JHR^BA`NLF*w2h0;G(Kkvwq^3pPDA7l^ zozdJ~l>A23Zm1+YuGzC%E0$!IqL|v&+@!ne(uW5y;x-5j(2N91WOv0`@>WC|et9F_ zBIvuWG$5f&+KWJ9F+H@L%&?W)^{moP@MghjjrNqvDSUol{CZGSaBx2 zg~n^2)eiRt(Y*w&zKd^)+l_gL#%PBpb(F(i+?IRmEmR7(CSo(GgLiE=lnO5>_F^nG z%0rXV<*IafP!cxcm(@g<1*2VsKh!TgQa?Sl(q8-%mCiam^BEYBc(yFbYdc$(CU3E) zS4uHEL$TBdCD++fx=sJnNDWm>F?(@)l;mRni+xp8x_wo2s44x7rk|_n=j!N?RgOxR zqg|;qnSUAvAU`8r_*ZmjN`_pPY5!znu;H`u7P{e!EiMh+k8=ddLw8fd{DLx1C6s!B z3aEIaPm)qk$>n%X0FPayL$wHJ^q+I|wU_!l3WwTZvRu3~VwcVJb5U`jxWd0;?5~v9 z_K9B3BV&wb9^S=vz%6ggymy~8*DYT%J+0%-z#iA#UaLGT5&IXa<&Ahi=b}OeVg~in zjv4#}rfmE9b=G~$TB)Piw$}|0HK$>&>)a)Iqbs?*EO<@v?sR)bX@>oi@&SQH(MBYi zeQl$L=q}tCOB`{NatU#dm7n=}Y3W7nnV(@T&@0M*Y=vj6&K(HZaL29Q*(J#vp(*D| z=M?PCORkX0g8iuzc!<$s|Dh#=F#83f#;33Pt$jSE z>ssY2JNMfYuz%Nw=cxM4)$EMqx%?cJ3(x2bB~IT}QsHEW#e0Clu6$jKWqTd5QmszJ zXmZ-_a69ayOCPXzt(@|$@0-vv*P3Xy?Av|)TAl8aI{T}K7f@>4a$X*E=RHzkL7c@q zR+6{SykoAud~!df-T4N7!bPe$Q(li;7g_z9SOQArk$t-|DlYsvsn-!st+FLGjIE+! zv~A?DWMYPb#$kW0uH}ocrW$b;0p@2Uq$4{Bb}K*jga5>5V}rxFwPAt< z_n_u{4s22hI9E61O)7M|@O%t5BsdqF?nL<-(w&!uoFwm(0=6d!zovoN&mPead1VMAL>iU{e@D@!ZWK?-dE{B@hJAZc0Wf$@ zumijHuW|=Si=;U0MgOwsPnoz=E2oCI=G5l|t$Ou&J7V36x8~#~@>o7JF}Sz2wj`nL zvw7=fbud>Kcg<~Ee31=oeIaEX$>5Tyz0th+~l z{NX;Suq<&lo_}zKKmQ$`2QZ1)NE&Xtj z7sY8PTQ82KP^L@8%4CNWi0w;lmS*y#-0ac?DRqxz_Z*NqA0`DhJrR4ZBKwT%CDlsp z^Abv?^fG;yW|s1xL@ChpJf6d8BX!<|d64n^Mr=bobrx{PvHv9$p&pDNb5TtoMn*&KSvXG!0~PMLF+6$`{-{b z$uILlq`$F8rWjM=@W_8mEKvi7FFf1c0tE(o5`YtNHOsfqTYg;A#G$u><5k(Mq#ZhzrXw#obX(LH$j zpAOAh&z-u@Ui27;4KyCSy9$p878;KTHsGzkX~rXhSG)8Kq+!G7)?m^WMXCA2qCF(| zEi3M9M{-prb+jwpfi$woD1F`nU2AWhdLjc4s2#Mwuo$re5<4YK&r`Xf0x$Eto0PiW z+e4BIwWr9sT;_SV#^KMqaZQAV(zKe_+y2AzZoBw-x3aiQ<9WBx6*z1R?diAKXcql? zbARx4F}x%8=im8Tdo=DcE)kK8@9U~>RWZ83@^pVDz+s`V=Wuaf{_LuTtSVjiw z78%1^nZ5W8Zf6n=JY``g7PQ6^c|8{yuh`!tSDxGN#_Thrl3JhAuK5d?u$0k++BWTRIP5 z|DTdXlYux&sn3aX1=Ad-?b%jKpGKGh+`!SDD;(ImLlEuqpNqF75j#GidRF}!pZccj z|4u(-B%c)r4Y7r`KRw0UHphFr;@llTxdpv2^1BAu$RB4>eR4bel(oPhC3G*IO(u=D z*TZL@p!GC-j=g|SqNVj0npji#{`#RV>eMKfV)@eCRI8~&9Mh;=T~RbxQM9dz+)fng zQ2u&rw0*>va~Gys<56g>RG_J`)a!QYV0gYfwlegw;_TuV>v-=42jV26rM26OsXy@< zv=^u>DlEdtuRw>uUg!Fk-T3&={x&M(4EcgfKI@UmV!og_m(4OgUz7YU_h9W(mhz3OnAC$(RmxFp12gV7LFKwlo#+-x#_oQo3N4X`uVBeQ&1n zQ_o&Z)P}Cx6EiS%IL%(;$w}COrYf^5kWhas^J$E3|IHvut815olxk~@mXZiRu3z|j z8?!HiI+XHEJfCGkVLhK^hr7G*eDt)oZa&ngSPD3=v-2L8_Rr$U zd!DHWoKybddsjSL_x=Xiws02HuCr;hv!1E-{Pzge-c44-X$)cm$o}A+!hmkV6Y@Lw zn1CsbE>r~l_YQUqO>(sXskP5gYjQ$-{)&CLFc9{j-#7CvR1b%&V$bgn{ZGR4qf6~ zee}-YbyMf}$XR5LI9~#GGF?Pbu(uOwz$_~?;Fe(4Mg^ypU70DbaPaK!tT!PuWlA$}f9iI`ZKZo5>@RC78c&x-G^dv{68T{gQpAgkG*3$wdRsR7 zL5|+h?sVOuRkz7sT;;t%>qI+>ka=7GA_iY`QC&X5dQ*|HI><+U(EpnH#th%^mn+Uh z?lVD2&dWw2Pr^AT$S@ohO@c3NKy7&+!_1nS;hni^v!0eC`7`jzrcbV^jC&W=R!nU; zsZ&K;H(M(Wi_`{g+LZ#&I$Y;k25N74k_s=NYd`A1&e$^Q-R1GT8?I*|7ez+A+Sj?G z_Aji}r+KfN^O*}Qq&yw3c4=qrm=Wc#wX0I!M8#R}8F85-6#gE=)c5vN9ZV~W^v=!vf@Qk|V(V7b)s?b>chuu@GE*(yO#&rL38z98`@-A^KjI9}o_!4N=fF8O9 zYq2%!6VT&rBPhg6iqcsgg){$3xNZ}1eEZonN36H8A@HG{&A0LA9qc($?-n`g*o?W& zy#LF17)czRbe8nYGw2<1kSC`5eJ#bwTLJp#!8%-eU{G|hIi;^=g-}bMP;?A`!yIIY z%4LN|0>b6h3l9|}fu#c`R%gqSe6f-|F;Uau1n4TFI^W@~k5gx=L-*6?Zr*2(CYz=> zzbVqSh0Cqf!#N%PJa!jh(=(ox&80yqCOFS-iP%wbrLJgRQ$qd!XYK&9m~5lq)^56qhOXAWqc)+{zDbe(;r_!@)@G=LZ!*unP#ZZbJgzUWY9tFj!cOkOEA8?viPRd;9fYq^vS;k8d5NQMmcs6a zrb}cqOR$-HQa}0a_(g?G=A3gFvyqMgE6#VH79#*z2F`ucg*#YoqB!p;WGi)y z;v6#rga0jYl*|)nxI5>}j5mDQ14$^9>=I5xJi)jln8pVJo@Dp;wi&E1KCf>#L>$w( zFopHASv7#}5SFV}yZxJ3E%SsrPS}xb;Sff6(M16i!5N}ay z9#5xny*_`LdhJ|yy}oF{;{f5cS`b#NiO0BB|7cogt=?9vR@1m1Z<{G!1R4y&n}J5CcTv{~W=v2o>2DhPaH)Nk$c z#}&qPguP|>H=l^Z;GEhkmGg7KUi2&LvR((Lk}k_o6SWbGU479G@FW ziv?m6Fv%_4FI;UEJd3L3(|nTJ%UN-G+C)*5k=jN5Cc_SxkUzp*S43sC%MQ;#!vsOQiWbbMR8GgyusmgwI(UX`KsB~r<__e4n%av1z-J~ z4JQ4(wLax!I>lwm z{R17DfgzRjT}f6HeN|@$20FCYKX~wlgCC?o&yDWD@M^{B&gU6gQ4XcibY4|sczI+# z!@LY5MsqTO-EwJWN*wIL0XT{{PF+pGAnkRsJKo)SuT*w1#_f;&w&n!;X)$_zZ){&w z50xXpdok}$t@5V1(aKX?4qtn>yi0N3JvrVYpD!+*Y$y9`3fGstxD)Rpq15m+K^_vv zhuKm&uM0I7>j3grcjp~a*#&U##Bp&HnCF(i_oSZn4xWvZaAlkrCe{ZdD1Xa0DDTlI zS6d9q9C3E;_rBHGx|HwXtfD27`H>>ZulQNS4|ou;4YT=N7?>RtXU&r$`~%(dn}cB_ zPdp~7o@0Q0W)>c}9?nOLE@~^DcBZEA>67ZzEQDBFkK4r%H6Wkc-$A8uc=V_B=&yve zmojlkX1ov^VbQ!2w>??c6bA1n@yH`6l>uudQD&xA<6R!zxevh2T0V)!S9|7uno3F3 z1>E*b%&ImJZ1HZwx5nVXx(1k2xeG60>U9qo?ddq4vo`@6ujr)l-3R><_L8(( zGg<@}i{NLDsmkL~Z+tCn6{#|QGF+XmT(>_8Q*7LJ(WKgyTl;xjZ4Akl=X-0f7CfgX)tpN8d9D#{9ixne>eDngTaV(SNnCT| z$jUy!yJEECBE;l9)q0Gg`5srNaA(Wnyv=y$)YjnesIaJ}*_}(B(|Kc)4`Peg*u=AC z(TorG46}J1Jg+m!hy74@2JjdbdQzL|`I_>um3+`Rx)y2V*&mu5PXM9uuWLCu5J~&riftlx?CSJ%BY6Oe6ednp16l z7#bI8PM?H_AhjaBJyCHEpwuX7NwSoL{nhj%HLjqI6tF#g6x*f$e1c8zI>%Gq4MUY^ zJLlsJ$q6$rV*hJ_H85pIs9xE{=)g_4Db61#RFRTW@{^`DRGfS0*XFdgyz}!~#rd^2 zUO!*BdLnn4{WWT*>GK3&*@pX1#}i!c`Oi2uEKc*DiX~`Oq~3k;igOuJ)y(1A7~nJK z-^clPQvP?>aQ^c+|5%53ggg3P)eCQn=1iYAWqOR7=@yjfV_uQzY|ixeUMrO9cGF{~I7W$P= z*(3|a7SGvqifrP=IvCXprzcu`cN9)y<4`9r?QpfW?oS1 z@0Cu9LwN)jE+YJOLWB-SSu(*~fQq%M-h+ClFo!`DxR7n>f!wG%~VR zpTN%VDsC#3yg%`z;_N*^_xasUQ{4O?9I*Xj_Fw@&Nqc^;u@rbRj(1MndS3E3+KA0L zshg($ipR&O*YC;0Yn=uqg*&CcdklAPO{$~C{L|C*+WXQZ2b}XRp=7I|5!{LNE80uC zu*$9cNPl&VHy3d3(K$?>m_dK)Qt;ks6E=QKTK_jjGIO%aldu@j}D7E#P!=XJ>C+? zjw(40X&Xu7ofx)TFJM~r*W(=hz0*eYMQZfl5dHhB)ab1^`uxW@`ncmp^oeTp6Nvr^ zMGrnyq9=XSbS>$p5Ip;;DB(|UYik+7PGN1LD_5*|bC!Cl+W~Y!2aNBpjU7E=mg?p$E4_@Cy{2p3>#!4Gs&A zuC{)@38>D3N(O(O!S_F=gE!NquZ46LNh{8%@rI*>;+&o)(taIcbVb?&&dpK4BfYSF zycl6_Ag+|toRK%Quw6zbKAxuWbO1{5O3K8>*P^xH7xmyS2(Ci#%kA|%=IO!R5d0y6 zV_WLM!}Q=@2$m7-90v24tWhuezh7)2^VtH~u_ft#&9#I7;t!i`)woqj>#eD*LW z+P)sAv6l=&++;m&z_!fdu&ld3Wyk0f?CHP!)N zaZ}s+6FexDyqOK^th+ivdj#)qTqAJObexBQv)c(Ar@O`>j@_}HJ4L`kRXHV-(*r zBT#%E4EEpgARy*3Yj{`2w@~ObmGXR7Eh**v3IZICQ+TPt)SoC2FTXD`*Qb&p;6<6h3gJ*sW3veJHb)!$plqEihl}bJ> zi3)B8eRrVmS@caUA!9au_o44O^qofE3+P*-?Rep#s;r$Hr(&A*v5 z)N3-1GH0pRWFuwHR zAR!W2P1a8La~E5`#lf+2@vxMy5&U_I^VZyW3+$V&532o@i@Fp=y|_t~8lU>>B|IEb@UHt9c;jaP&X~dWhZ+3Tg93cl17`4z0GAk?I!nMd5ZL9wUOUXd_6h9yrHl=8 zW`-I1H5K~FFzBxUeLR7oXA$(iiv+z1(07@juNKhFH!(D89H|zL&Nx*ZH7MbfPgN&-wa&|=-vW)4nao)x(d)svJB|^1oZu1 zGBj+uIcDf$75e@#=q-Rgd-*U$&DcBc7uX-)XU5(E>~;>({_+X@%ku=j6X36! zz=sL&F>3@kTekUT^gIo=51zfW8apy&2s)=Kv3Wr`p53x2~&j@Y$$% zP8M?)>qFl;v=@k5M{ZExb;7oFu-VF5V@dy#10Wn_kJ1x9=Wb4T6^&=+V$0rE`?r?~vSCw{mh_-*a*=?%?EJS{sqvb%%`PzW0Ti+~6ZCYNgau z@+-KOHa1fFTMno6%g@47dZn7ubN8qz^&q7|NI%xtNa?<_l+qfc^!vI0-GG- z@%t$x1%tQtg4=X#dl2LI^iR~p>ErilU^n5EIbB~k_^}AtcK9HSjfd`5Q*jeg@dq1z zHX{`Skcu-%MRul8m}zvk1@4}uyR0jN{nQ{>C#TL(&^a|IEqHAvm-57eNH2{7DYn^r z9Tp!Ir|rcW2d@k?4z?7L3g|`%A@kOgWijWJqVO?@FRteoTF<6f88u^` zDJsrEIcgCxj}4B!cfnX7}#&WG1=YLX8i?uM|qO%V4cQ$hwIAy*(FONa2B%K0soRViOv8_@My z-WP|YDbB0LnT*ngC9MEnU{TKQhtRUNJQW?RABZ3#LK`_iG zTW_+|gM%Z&uiePu*n~NhT41nKM0j8BTUhY3y-!&VHjD^=X`MqoR>=0^G73LBI6QpH zG7d-U8MlhUw?u>wUd7?vu^$fnA4Y_C68wFTzU37DTts-^K0)B;BmEyC{}JKWeuQv7n+f}3KBOoQMnqZtAx9BQQ??qg zq(?;gnpxDn|F#zprFBG<$s&q42*`E`W%;)h9_3dqOZ6-y+YIVP!GjS|#(&OH#9=!& z-=`F1O+=KOPdSP>5z4k=1x0x^BFelK9Aynh;kSu;NZUOw8QQM=Bz3um(B)DjY-0OP z^WQ;xGDxF%NM&)jkiy!c?7Urc>@j=Mv40*bw$0ubXK1HvKu-kp_qQ0@X@Zyrx7n*} zrzNUv>p0f$4;>LB7{&_>3P~9OS5$E84r6X;FFH}*P#Qm+Aeu>_nRJVmdsbeuwd5OJ zX?OlfRobVkHKm=)(XBsJt4ezxn)Od1RcUW2W2IeQ%}V>&j}&(05kqO)i^kRGY3Ue) z<6Dj6$$c6}tl+qRpUQCu>Uz4yaqH*IamzmDm_y>hm4^jKp2%lC<~aMc&Jpv2%F$zw z#<5@!bF|*0a+E^7HP<*+tYMCrJe~-%8}0;)9dN8_-dY6D9%=Djbzs|%%q2Z zqhj`>m>q-Sb<->zcb}Uh2FtwQnGB<4U4-PSvr&L z$X3a6K(-7qGfiZxk6|JYvJL4>R!@-i{!@^(k05)XWHlff3bLw|8riqIL3X!B_5jG% zBIc1;gRC#e)`2Wbkj-7eWLxTqVqBLUPBy-z91!zjne2`djqG72`(w09HXdY~5Obo5 zY(t31HiPV^fowu9-Oe7KcDiRqJn>6Bc_lj`-HUmwu`Afg^WA4`BtE&Fojj5Lo&N8I z+lP52y8v-IhLgI#QuFUziN?O-=WwUe#FB$-|HPXCLSih%3_$a|xs^sxy3o<{8N;RM{J$NUL(krJ)>d#vFBNfbbp;v28ll$)JC z`QmUnR0f{c_ETlZE_i(w@Of)~stlY9=BLWQsbzku44lj5r^?`638)Midk|0=IA_g| z%8*@oHLqHPV=K$#7s@yMLZll_j-e$c$Iyb>jv=Xo$zjyp^EA!-^HG}W8d{X5I*aC}sqUlMX{tl%*)-L~RFI}RoqVc% zZg9MMl@q*My~+-b7SWamZ%s4Q_>cdlI!{EMC#Go{oMMx4U=@BUXWobE^xiy2{C%XD zBkmcYDhjUEK4=(&EymP79q1`QU)NtG1HexT;OUDmH?@CNMfZiF_XYYf#iEZ{dw(VL zRG{BsLQfRvAFR|I9@yMu0`H@OCx(IF1n?IboIM^71G^uvEhg+Q*d65Dv_SXMggHFE z8zG=K3^zmH4CtN$8h#q(7lQ5&=*2f1(02>yCTn!>Qkb9SX%+gOFz7Tu@BiDPd6!-R zv90Q#xD;J?AX{A&ubtX^j~KB> z?Y*j~U0aLLmQvI#qGIn=yGD(os%Gd{J7!u9irTXXQEEoiNCjW|Uv6IV&b?>+?mh3` zcYa*!NxpLZ+W^dCKPpVgqq6DQ_S=8v5kDwMdX8NZ_cZx-^rFG8caJ1~P)IZHEbiwh zSFJTw*2GtYNB80_V#>^RXkyB4{}E#P%YUY-+Agyt=Q2(;{0yT_SOn=ke1>r((kdBv z-a)nS>*oG~Ud)wk9fsOSfqH^5Em>QVrL5xfQW?MGLyDs^Hm!Ht`EDfFnI^0evR+zx z3%v~+L4T6!jE42BhWxMg0!Z3C-@8jrO^M9d9K42s+r5*`KYg;;5bIXO%az_7_RCABF$A6juD=vorg{#CQ9K{*tHE!gZ+QrA5cd zH+$?YM#3hq2aG(^?{<4M6x0dO3Y)AHIoV(^{)Ir=o4ZK4eEM%kP`c=A{*$x(4=UsT}CzRL67;rn<;G&%Iv3Ba{!LqjFB;1fvs?5Px47f8AG3&rnawy^EHYapLt&DJc5 z)5a+^Db+Pc)MAN^+--Uu_QB4-5@R`|@e`5|AbjJ*)N!7Q_XnavK zS`xpqt*jTH{kC2YPLiz1mVSnrdOU)8(iUYkGcYC9=HQkx+o}rhYNOa}d9^F7kCQX* zNf{SB`?~J?Ig)KJY&8a2;ET+UnY0}E%i@!?NW57l`7rV2%Rbkg*J*3kYFfH#tbZ6b zeSI@PBY?G73=5#VB7tJh)^l!Qd|j;zTt4CbD*E~tJY*=PdFaUkC*smRcJ`I2_LIn$ ztCf!kxz``#-3Im9^9?c^qT&q(V)v4QZk+Y1APPt zmukK|SF&S~Zc+ZW7*y1x#YcniZF=Iu`D6%8v44A;`d|^TzDIPunL&O5C5jyD79QjK zprs5U{R_YIM<#mpPe}!Cn@`{mjgCy$B&OkfvER9I09v74-StQ0jYsaU`12|w&|BSR zY(s^P^4L>FOAHIM=ZdSMY%ZjSGN4@3`(>f1&u$tb7u5M<0!-D z;3mTB=6heeT88brh$H%JY~O{xm}TB!6R`SaXB*R2O-dfl_khLozpw_K?icQ4#U>jA z$LfQM=)4?Q=k)}Dtm~z9$3~IWC$@p|5?O_{1d_b(5y*MP9_sU1L%)FG6?HL!>x@+FxNs~iL9&DfZd7T_i zb6K)kE6Twi^rG43qN3Q{4s^~WkV90TCRk+`>u<;Ia}D2n=Az966oFiyQ%wK7DKoG~ zzEsM1{j}`v_ieM6M+YKZ)9)_jA8H5s{*n@=-QRCX9~dv&$w{N<8@SiDUCSPKW6pZr zr4ah{nUC0L|8(+xJM@p#ti3jIg0HuMnc7a|SDUJS=fLiM+vv;fBZE(Q5qS+Cl|>(( z%g6mU>*!SgVD0g#3yof>kG*I8r+Bt6NUY=sRm39>*&Uz9I&uCcMY1=V-uLS{}&YUK4k4VZ_%{ehSXu+ zXB8Lj@xbArc*YA%?dv*6i%+2{xab~lSl#sT8!;Z}yqGzeqA`Mv5?Dk(Kfj&NtHWAu zr~bW5*w1zJ&5Min5ED#0*M?Nh!R;R2<#%T}>pV=AQqn#hpyb@D+k>>ptkLZK^mB<+ z9n~X_2}kuOz{%8(HvU#xtN#`s6V)1&ux+27wAL+Mf$DZ&Q}@*2yL7?<+#2pL4{+hN zR%-9+ru^0`Y2jn&_Fjpu;cpxa=&R@i5d~x4TXShOh4_O6PTYNA?UJTk|a5=!r+_2y66|gO)*P}Y6@D%Agq{Qb%$cYFu zG;fyQ(GYr!6t_!Oxe#ucIrx>iVry2XkVnt3KW>$3Sh97n5!1az_ATq7 z&tsq#_QHPr$nDzTRhb+pms#gL9m!l(zZVF3-dmAAH5iwA;HS;H2cFR^xA{nqqI`RsxtVQejM zZ7pFK#)3XlU`CS&4u?+9yt=CI7bmxK)a6w-6x8nGMT6s@719_<(nxLJtTh1kA+S#` zOC~aR7)F)yWNkkc$alM8BIZ=awI`}%*idE?$>)v@daf4c=)CR@(0DFzmyth#bAAf|AD63S-&oQ=nqVsNdhw z&SbbB5wTEDA>26vFv^h8N!i>*g}<5Q2ZNnba~sK?fiC!6?oB5-dSay^7dAr{a<|`0fbI~8^~%x~M%qt2#^QTop=)Cd zXgXqVx#lwx?*mI=mygOFzfD*Bfqj;2Du9@WhBVj~pf)Minx_a314hZ)eLic+MjIwE z&N;bW5jVUg#YuGiW*I|IoKFwW?-Ji6>cCT=-$DfFVpk-P%J6Y?eVzVz-w~VcwP^Dk z(H?ViZ^L7(H+T-2W*@ggFy4DAJZ%h{7j+;T^5zzU2j6mt zYPk|bvW-n6Hw*FAZ(m|%f{Dn8Zwq55+88>w%mm5>kfSb7v9Q_5>k~5uG*e74g9FX# z7deNlVG>c^@v zpxfAcJp+o*u&T9cIAP~?V}R3hfvBevb08fpy)644@w0(iz1_(f0Cu>4m#Sf|f9bEE z@LC<)WgkS@Iw;O*ifl<|JtK+>t(4VH7M2b?2Vf(q^h>oljjlz^_UAK+?ATm?iX&lf ztFx8evABp}Y-5*>GT^g9_Qb+n^iSRE@4=?MCoIAf#S%!1vkYK4ZG%|9G0|L;OA)-v zSFP`w1L??xZr>%YrFX#8>t2VmWUrB8$z$%7^069fIe3@;uG)h-== zQaij`mdvUtS|8{qjXWY$gvV93vYo#M`cXEhN3-0TBZ-CQgZJm@_(~u&dt~kcW58(k8-A z=&XXXwr^wUX6CM8Uql#DCzH=B$7d<;-?67uA!X=C1^hsaQutaWf;65D_@!H*WRwmq=4FC36C{EEd zlD{8qsC*k+FF34y%Z{@-^4456!c@y{{=j`yN9zl4VxBBAw*L)+JWM<5h8pXpt7V(z zaV?JA1{r3#&^3fd2Xi}+#lnl4P*X)vL%LGDRhO_oF>#SFcFX-YUMKY(zcJ*94Qt+)-0-06a12T`~I&HK1+8<@Puo+$B zbOx6I4WzEKY-kbiJhDBC6gx>CmM!g1MtTAym>~FT@wdQ21b3A849xPiCmS6)!=bBza_!s0ON7P zcBd+0)eG_SO=>unuKovr6+eLjoiZ{k4*_oSWAX4h@a4T6`}Y;g#CsvG&LE^U<)3Yr zH<%L{a-4TpzXO1jjvc+%*Plq9MaP8p`~Czwt_{GRItwBxNW2-E4=LsxDeh^{&+ciO zMmD6GN9IdofTTljk=?NxY-kskuV^W(i{IC1igR99GF{h->O$_Ly(~=vk7LrdH zxi&>6e0RP5iX1n1dhd`7=c(Jf{%~@_-E!OASpfN8^r>L;2O{suNZGQzD~*C-3l~_f zOBI!AQa_V0_5-k-2uqS;x@wRW)!26S=RQ_bxoUatTd{ic(rqlbt55R}FEWB0C*Z7; z*w1%KjrEvkz?}g5th4&g(-&MC#EVk*1d)bBdyiauC>ubRg>aksgG^Z*Zd@%)-FZD2 zU@DjG;$(sj(uqVH3m=IhDP&mCae~9==X&jAb9Y^XS4-Vcj!Er#D>F%Y@P+hHQ;Ee;D107LX3RDHz zcMGQ%3)N@HO*%^}Ir`148%fv9O61MdthGFfF7J7tZSffE5Qissen(84K=7Ec`V=@L z`df+Vs!5hh&0jYI{hXVeR9~tF=l)$Oq|1y{QQs;N?mwPj?8M6$P5O@nc6K7%JTS^B z3ns=K((Q#fIAYf=qiW!bIpt}di$I{`f`4ko4Rdj#dxB#S^q8opSu)Q0XhWUIF0tGi zO6&&_G8|7fx}rCph0D-Ev42+tsr);b7lDqURl}({14&~Hgt6W_k<@4{T`*Q5_XujH z(1G9)K=P5y1^26wG)%#YoQf;6wf_U4Y2)DK(*Cr>GLbK``u?b9ms8+y5``-uq+hss z)*6#hq^u=2yZZt)A+qqtGqbG##gq^+==4*4vD)%A?A#u=kJtdht zbT+*%(JGq=<3i7KqVD4Sh@Zvb5<4_RyqkI0}`+HU5QMA8KTU zDk8t_5@+hUE}F-!5Med4l$p?*gaDzo+?N*^KIkV{W4OP|G0V_w2DvPctC`Z8*^_;8g9d>W4O%F zbTN3AtiBgdvFp~Rk=;v6T=#M&i0tScPNIWfb{CfK?FQrC0*}12v_De8X<}NJX|Q^u z&5^WNZ!#Q2_xgUVr~gDdVO>Mp(2+>!?OatX6m65CQ8cV9^c!s$?n%t2yDrRxdypkV zhTAYOy9qy zxsJRR)L5Ahj(8Rl5~@_@%o6Fw)Ko60A(C|d3!^lr_@+q90P+liL+ssw{|$toAE?8b z6Zpo25Nj--$PD>EzE5D&yO53dGc`q(lb@Uhd7tgxLD&z}Gr`9P_eQmu!Di;ba~U0o zBFm~h<+z?do{y#1SMZ+%?Ib!h26LabynGliK?DoHblrvxaD(qlpHMPKidRwJSp8sK zN$hu_vPieHUMMh-K?{3U~RaIcy+ZBL%igIyACKxDG?P8!vNmyO>}Pe1~2 zg``Z5`>5Va6{sz_ARGXHvYvncBRE}MALX$n2h&H{Pmf|ctg_f+6CYii1b0Nf#*p~p zuUu&PRI$0_jQ8-yyXv2iwuXEm|L{v)0u;5GV2)4=NXtf%n}vs6{a{e zc93p_@JiFgiGrL)0<%K&yExWfS4Dzn;NcPpHNP;g=Jb24o4{p9t)4t;Q(V zx4F^oiKygd&jVL(DX$#A%5X^#WI_ioVMct8Q5#b=9B zw)>}6$H0rFRpvv#;P98?g7Dk$u0d}i_#9s~CY9#^Q^tI)Sxr45O%|nh=ROrI0izFy zVlNmB3WAiCu3#TMyJ##K^p=C$AtYpyw)6LJNx%PG(XsS`Dd+BD))-iLTe;^a7*<~+ zl+P;SX$Dn4@}d;xVOrZL2iI58eD2N-yw66kxWP!pREAiA`D zD)ETDA{1TVI{Pg!#ky@FLS)qa`UU5pVLhhN%AY<~<Wk9MuBK3-2Z#y@}nU7n&Iqk@{W# zg5+;wIj8U+S#-gk70NaN;nCiL z_$dYrk>iMTpk%z8HRrW3DM`y0-j&bCju*~;&WEuudE42_vP^K zncZT68LVJdloo(*40ZFe8WWiWvK`~6?|oIvc?GYdE&eogeJ+J zi&lmJmFARdC^5v5$0xEV-3Rxn`sC;c0(vp(3EGFqz0OwqG{3}&*m-4$S?v6D1ao9- z0#9!M@nBjlMv1__Fwhhs4p$qj0cwyWg|1^-=h(!BA)F{NRQ?DR0hwk~5Cy|=iFjt- zLw-d0v(IEFK+%c>!|(qhLgvx{C8PELa_G)|=$j}LZ7&9qN}8|tr-Vsb;C=ZlCbX?l zrSDxqqzou=4ZcBpehrDF&`k`Ka8%_x0$@Q0`-NGehVgPo!a8}t1mXt9QH(u@&ww3Z z&&&0qFFd7El^dPBJfCF(G|R`j6k5-HKrti$@1i^sAuHflX)#QQDo-GoTBaWr zr=n4yxX#+ag;GTYQ-MQKit&g`g%-q>DE_4Y$F*XqR0o|djTD?~kPis`=0SM_lUiF_ zjdLtprCtNTZq`qU!F?eoSN{AV7n8C8SmR*Q{Q(KU_`V(L+~eO|nig;YpgmU`lX59W z0w(BSpI18~X<^yiywVg-ij;lp!icEqRx%gLD5KU^f+KyY4eQzEle#<460dPJiMBHHp=3^#vSr)>tVGC38>|#~H;RPz69Gs#8 z57kr0<3;0@ZBd)mKps-%kUC7p^uLH>QH2J2_eS={B9w&{22Buqeh~M#`Mgr*L;mf9 zQ!%_bF-O^b%0QhTPE-@gm=b{YST$LH#2W%Fv+#6s?#iv96&M-kts5 z3Sv;5(gJ~UZXmK>qSns0g5<~Bdqb)>F+b))E)WcWqq@Ou6J19D*w-300Pst~kaPEb z1V!Dfs2OY0uL0mz}+iQIMH6d`GzmU zBYr+f1Z)K`T)X9yQWx|FryH*7vy3KfPslI@f9l6Tl&>9M7u%_CeChj;+yQC&d|!k<3Pd>L$CyM>gqq0R_|6LV9js?gD#$^}#xZD#4gI8D6C`Wr_n#j0 zv*`b~diaG&{s7l*52#1+%DVB(1b{TU~r!lY{m#L*s!dtdS56f?Gg=Nqm%* zn4>wlsEcL55gF{;zZ^o4>q5iHSsl6oj2E#+vAhS8$8vDlKrG|CCD~Bg;0F+QT0i9y z#BvdeuP461B#?j%b!@h(@It2vChLn57dd<~vrr;1fweb;`=1b%q;k)@C=rjqixU@v zi(3%)*a8ypP50-2b=JQ*b%X)ixdYiQe zBWxj>VjpP-Mq0)3i=KpZu7e^;c>_cHLIoPlaYzXRKxX6YnP^=k9Lr(`=dZI+_8LCA zB!ExeC^Lptden58o<-{GYGo4Bd;L~j~pN*5wWV!R-Z6C$~C-m;+nA!RoC4tdn#Bo#ure2 zk~esy_zpunM^;vNWN={vskVsO+s3?VE8SrL?F9lw>cP5PLqtyF`F%*YwuGFLntr#BwUmB*byrX;H@h;uM z3GSJYbb_utd;H+~mAKF=;l}NQ4Q|3a4c!_}QRI}6SGUInTW*yeWN4Rvwc09JcYB_& zJ;o7r?xP>|&BWg;H30qn8NR1b`-(?qU|2vHYHn}67JGdtcnxNUSnz$j4o`gbCsYG< zT)n`jn#mQ__o(3d^)^bAc+G3`$fJl(W}wr?5g$=_M~dmxoUWfd3)2p9M93qcv}#t`jPOKb;YzJtub^LX9;+_2<<;k53 zgJD_UKQz*~3^EKxcRz;>ev8-Ph;Hysnw)W&ViluE0rz0>S-a(@?77dDM;$_rW*UA= zqQ^}}ptTM($bpjsWMA|(0c>#au(DEA%qP#S_CCnhNv`3O=&_u!n%xIRKk4TgEGMT| z3m~G;PhK03fxMgFTX2BnAx4+}MlPQ{Gc2ca9P@HbgrCrGKF#}6t>|f^D;+@Qn{0`F z9cLV9#G02UZ>-U&AL%s3R3+VLIhAf$s$F2i5M;VNAKqd@Ia86NQ}5EO+wmli^auH4 z8~OqpMAnjT9`go2%OFUiWwFRB&$N1M{G++kCu#{F`zoP4`vtI5AdAV;nyBg*lUMWw zP@S|-9H7uneZkK6x4NX8EOXv;`wA4ir#QUoZZTn$JA5fEmazKMFYhAoXnZ>-eO7;% zxJt+}c(vEz%E~s6`i}7jyRCyXMX-2s4!MER8YMgel>_aU^2~exY{g%igV@QU>b>SB z??z6eBIf!(^%fJ>x%C680&c_-WJ+{##CwxW`XTT`^SG_S@WS7JeTkQhspo9Po9ql8=rJox`DmC@pwR0 zP+$sGqK#b@+LmRt*FQxhF)z&$+o<8^(&0v7e7KtG{Hf5el&?TxV2S;4p3O$vkEZ z9G;AEpFE*01A^-9*{S@Rbithzpna`@)CNsJNx z@jJypS>CufU>+A_1okO5VF%Shrn!-zB9qh{W`h(%BZBFMk3LnRuWX<8I=&%{Cy6x_-gIpfg&mUNTEC)z#VM1S{_ z1o0I7x%)%1>b)w(i_IMBulhzZRbLua2tFjl@wd9o5}S6pMPD%UHEZNWUfwT$mNC%J z)Rdff1Ebt0|Gd?5r%R;ZX~E0QrtSd{&yPxkfsv-~B#E?9dJf27n2nd(+*@;S=;=Q# zDf1G*$i>o<(Jk-2^aw#S!?BTK&+;ITP6{sI5d%ww-$fVuy{oti-hbmgTQeDg7MwS) zIt5Z*R>>O-OYdDIn?yb(YEU)?V~8=RoVJCz3}xe8hZ8nwjuf*8j#cj^{9-8shaZyS zI6x?Fn;es+bsfn#ORKk`TaiwId?0P6BSpgobx#_PV_O57EwQol zL+(yF>?HrSSYi|Ahk~7QIHwy042(2Z<$N}C4js~04UFhzhWw=;#_*s1g6`+s;k8QI zs6RM-UiD?^&>$$q@Ui8mIZ@oBWP+nuQN}%Pu-q8U&E;~+VUY5Aktejls=1!?l3ivi zBZqWFX8hu;Xz9VX7eqoMB(cA*YYeeyU--n~T<79ziGZ7Fg@fxF>5lqP&|#DsMbt#K zK&RJEiaHc5zAtNiZcAc^*yoP9Ovc59H825we|lAC6O$#G<&tn3_>a~q2L4b3Jm3%TK5 zgL62wWOYo1LUwzm0IM_AsIJVlf|ZYu4HX$*j+V0~IP|!zU{1 zUk%37aG!hcvnx`lxmX@oe*B5w8=J(>(M>{OMunt?V~!~WnQyqRE&u2dUmnbl@1vc= zOXW$f=-1T&GM+DAJ?uLU7cP~agYo0dH_tsT>?3o_P8hBpy>*G$h6_YW1g5Tq9sv`PiqzT9Zys!xCiI+={ij8zp0HppC{?rPKO$X%ajG9Yd-rgX zIZpT}BKW68o?+LddI|6X@2Kx_?~l~Inu~9xb3*XVJ;(6l-vky0uX6hB^XWP4b~ZzR z+8kiP^NJHG=$d_zznboTc4pba>_WC&|JC~5cSFotjp_p3dCU4VnZ$uI{cEAUrAcm^ zlqhwfp?jbIW4~dPj#Nc|H!Avx(BI5tYi(EH3-XpB(SyxGlfXflsk$N!Mc=t-;Y>aT zmP>=dw<|Bp4@V+R1WB_GciL6z{cm#f;={^3MRYcs2Cpn?JgMxzPmFA}$I{$EOkAg7 zzF4N9@=U019>xr=DO6mR+gu3nC!41GTiow8wL(?Xmyx0x%{!Nmiv7-`DPX2P<|)IK zR=T|f=%$k$o2DvzTV9vDzv>u+1Z6oGr*-7sfDRl!b(Q2fbqRu8)|PT4vu2(e@xPgY ziGob>(S>>F7$HHE$mvcS!w<$*Q)Wib?i*R&2b&2EYfCP=+%q;7GcpkqqcFO-H;eiY zYn4RjZF+tqYQ!rhMmOA%Qinh+C)2{BX`{leLN30A8zl8X<7h3ow&=nX6C3m**6;6# z@O!MiNx6&%%1QMQvE-m7#jM{5U78pfd4o7)K&`;AJ267$oO-W0tE+|jaul+1ZrxY1 zc;~!7U1Ik_XD6H8@VnH*Ly1xED(Dx|*`T#B;Rm%0uMX35;88D3c6F@0V|P1w8}%n< z^(PM0_`{n{dBVSOH->yijZ3z+@Sm&{A;57|Tgg{yeUg@?>o>|{F5DHW=yPq(sdH_1 zq=X#R{jPVP9HIJwpnF*_$&3z}l0$h?xhgMu(yk%SiKM9cR@Hd(jC_~Xf7?^k0| zFLUt``=D1jcDxf{8u&rzbwuyu(0Fv+Jl=OpKet9RMv0?x4k-cP}=?aQ}H7k#d`YU`}KvM9WEBi*ZD8izPHHJoo^J5 zw}ej;IFoEuhP|eJy~R9hV%jcR7J5{!?e@WcGKy(if*-wl;$3p1HPjvE#K@Q2y&!_S zk?C;|S3iAn`1ccAJGlMTBwLS4x!tTv>L`s9Revj5^mMH6^vU_gsZKqc`e#DtlC%NuNR@ak=_?voX49nb(3+Gcy`>salFrKfae0 z!;HRK2Wdxm@$TT;b4rSjqJ%>@Rqt5wHdWS!eJ++vRNuB^vOejkLeJor6u~ZW`?WZX z$HL;T^jH6l6c^X@Nwz^X=^p+RCO6v{g>P*dHLAQGv!Dv_;n|nWamo&d3EUdKokhp~ zDWV}o_1A!Or6vQPEN3tGOAqEp#Imow)aXL4{mNoR>}M<>=o+z35B(M6fNkIeT>u5y zV(M6O20AApYjys^_6_*W{n7Y`Zqmh&rXk%!zXawLW~PZ*$n}wK*YiF0(#l|J8FM>t zukhvMusjzaJ`f=zi~o>azUOB5VKef#0lkZ~k)>KPRws|R@>$=cFt@kCz2^wYADyzs z>T2{SH=?T(mqlrRKC+9$#22~pH+3s{!Y5m!3>W3YI>-!%jUqeb?~Ih)q!~rJ%N_D& z?47FQf8(Mtr1Gv}`0-=a^Cf@2cTwV;!BhU%LuG9(N}SfIQwD-0Kex6!5-(||6lB4r zyClI!0`-8`9XL$}j2GdnvJQuki_XH&>a6t5?mOV(jXW~P;?!n$p! zLtAN=t{Ug_LVNo4j6T4csYeGe=l-o^jQ|Ap&OQWaS8gbhoN~M8u~d88UEZS1)$H4G zBnXzt?xBm*cj=LRnpgLGe)FK3b(5*3HuePFr?AU1sAFS%&JJrVTuIfRz;?U$jHfG_eVBHDK& zQ6Gnz!a^b}3@h7v!CO=+TKqi8}Z%8s5A?_6QDIaJx zXYN?`^Zvzu3cu%3u@h*?w5||a>3rg$`*|<^&&ziK6>2>vyo0eDnVmm?$MSut%c??g zEs0Lh{={6@!b}>3m$gWGc|K_`$yb$zNAciNa5EBgyd8f11~gu?jV_NUnU?1?oFy6V zd^jVi;==o+(cx}x{6wp*==zI;FRk|3Y^)I(_SquVKke6v#@#C~fAv3~qF229J*Okt z+vx!1JDUsvsYcPg(`R^DzxNhT-U%}}e35(7cJPFpL>sS#=_asdcv8~zNtcrb4WClr zd$&3I22@klOfK$)P+SM9l=3!U*yh7<=i!t|M6R+|8KO2L$4e?ZUr{*Q&7ZW7V*7_7XgMYW{? zDdkAeJMK>VVEu<`sI3L3ADoQw6`}ek|E;jNk0YA^Ii}kTpPs(kJ*oqAMrXZb-q_tU zbk1%R3D)0SF|eO$s_Uw>X>-`zFnmy({1G(*0jX&p*A-WzV?!Tc`Q_jTyvO?!alHg%}Mb=1tcme8zxd;{;hR~m|U%LIjP zCv^Bq8J5bnXa^e{y)^K+u(d4Bo>!jhfaDH&aVgE%ok*wZogu65M<$^)NM=%3`HMN_ zWODCJVgh*CFElMo@M2SFl7pMS5_rzwJ7#2&KYzyeHL#+N>J2a2zXwBY+!NL`y(RJZ zsD6t?^y&TU6twRZ%k@$&R7=*x2TxVQ5w$Fq5_R_G1(r)#RRPc6N)}0tsW}qpHsa`+ zr8fHR)^6yXd+=EH_4k?QO$_;H$!1?EZ`Os5*P2v5hTtaKJ%|7LD>(wRpJgdheJ{&? z7NffRL-Gd~n`Z93l7agp;L$tzW^dBKzR1r^yl)p{(*oY7%$W-Snv7<+njSLG!#7yx z-;5~D7nRz4?)WqY!k5f~?4ND?t(OZRLvjB8am)L(&hnQ^)Y0~bv3z2u(7#=nTAD+@ z-Ut2RK{m>HvtpJ#)iQh^$Ze$pky8of-{I=3;CO`Q3 zxeK_sqq6xUYncm3E0}k&!ft`#rSV9BW>p)3WGLzTh9|Ea!eWH~zXyqK2FMyMulg(! z{)hC8ru$|VN zFJ3o0_6Fr}HQ&Vh^pEYeS!=1VOmhC{_~5A%so**p;-#a+nNmiQI@R#RkFttrTl^#W zJ-5`^=nC2@q8IdBF$;{zKi3=;*Ck5t1->6YNg{v$A17ulU@>}Ts+PIr?;krFc6&XA zcM6>T;ap*_F1#dOxTbGzw=75)Dr-^gJko9A&J=$r@eRv(Hqw$0cd999wtL;9vXI-5*&g5^?4Bbjl_%Yr%U=;1HlOs^}#YS zopv*=og>RmwUSL(*7@KTqu_BL&w$dAuTEPj^-_l(YABfVr>cWB+qd=79f5yxB&7bn zZCCdqY+}~$Rs0RN*jveIn#eh2 zr+i*l|L(MH{9Ln>p8Ztcmsg7a=IIDZV@OZtADZNTPKlIF3?GmlX?CCv6{e@Bk^?q zr;Rj~cJ|Q9zQO$li;FZlu8ZFa`1qFNs$9;rXM64p zm#i%j^ly_Fc#=$x#gfQ|FJ`EUt;c0c85iQspooga=i_Z0I#@`KlgHCdo3UjzxR(hw>n>S8z zUXPr>pchRB-%poUKlu#3T)+G7YinK&2_0nAiZV3#E6mH3g3+Yufq(0f@Vx7v&;}_? zTw2I;c4L#!%~@sAdKu?}mN}HpUOEtlDCAR?9^1PE^BzATBL4#UtwAwP*HtHl9T|BN zHjcnHv{*JGnqJC%x0KquQL;YM{2nKK&96$}j?BUVIF@W*h++lZi0+p8yrT`asmOmf z4t#W5*iF$cHm`cdm|oeeq4w9L&`nTXryX~(;q^T%rO>zEb#tgsP}&2ZvxPnXOTBcL z(|2F|1kLOwuVHC}ezr#IF!D&HA#M9qjQpuEUU!iOFAQRd+?r{@Y>LRTj!6VK@fL}_ zPW~CYQ0KgJ%iqaJ>z2=v-+uLZm4@I5!m`Y&%E`s@fk8ADcIsvHX3>J@$B*Mao?A-l zNM}nmuBvk8pd8~S<$hZt|9g`BW*q< zqs^K1+gA1#J%QE(r*?c5|252Mrs>(ApW=B1`Z)Q#o5PQyd$#=u6`jR^#@*Bgh8Y%^ z^|jw-Tox+*RtvsZvR?@GS7`^?sdwE{bKcrHJWi_hf?cgt_TOKuJe>MV z)nF0wKhU6t)9A;UY3WcGnFGo1jcr;x<@u5`VFjMMN)qSXlDeE$-asf zv|BMn`$}&)xU!{|s1+Hk%TYE@AYfK_-xYVJcS(Z~8zE-6S^^H>5!x zJXLTFW0;L@Mz?T25Xqj5ev<4Q>p=I#+#AN}Uu;gTn))86N3v?*PcnXxO9KgB3Na?G zsXMSGi5*ELhg1+b+F6o_qbtb2Z}%9~{xKloWwxVH$(6Jt8C*#x|D(76>dOa%{mzg& zqLdDz8tbbHB3uJGq>dc@#{4--p{>-bKVGhq==YCkWrr^%=K4K;JOqSUyy3bWC4G&^mXT=ewPTt6PFxhG9ew$%}Z8>f+@ zbx5#*BqLkz$(gtL{}go6ewKXC`9S`81G$hA7e<%6%Mfhfqx*&Yvn@?w^V^B(u$u}Z zE<`6$MYuc3oKel7e`>wr`g5_#XaP^1d6t=ITh)(5wBVGgDRvqMrw59zIiU^YGp4Qz z@93W#0ox>>=q-OFFIktj+Sa`J5nT}8NmO%=B-hY>uAZM{{Z*-sNbE;6rDuQQ)=cyb zgvVhvlha?RY%*G%%!Z`0=Lz9<^^?)yL z*R$4H$;_TTd!CsydwRoiI=lth^e#OwU*GH#ecR>p==`jo<=V`ZP%d0MPi4kF{_M>3 z>8GPURu=&klN+X`g{N)(r#}V6yKI}rJp8Bp@KZ*5b@&$>t;@Fd1yzi8LzV~J+1Q@B zy~UzZ|nV@yfv#&~+tDj&@n7g}txk z5^yR`{Tte*_cwG{@MDSrP8#y5%JQe;Umhf?o)Y{3inwtDr2nG;_KQ)bTrvu%-VoS# z8Qk37#{M(^rTu5IgioBXh)Gu?9dkZ_it7!7a{I$6H$xN9K~HezbWwZL4+b8gq}&Yv zllnWLwf36xXAylNMx@|k5ki}r3rNxQ_IdwcSu1{KL`rzQOj+?+Cw)XF_8X3=d;!^y zP+x78Fx@?Q{YA$T%LhtRM82WYb(ilQTn1#GN*MOr8qW9bb zUcxNR86;=#7tf9>Zr8JmXEGajlYenNe;RXFenW2I`L2^h8>3Iy*~~M^`-!t`*7ZN@ zA#4}=0zYuKjc0p3&G5i}I~~D=ebfhHf0k4S_PjxH7`^&JvLv&B-rTBd^AFP!{}>jC z_!r$!5ShVqkU$ZJ3jMaZx0V}!$da}U2Eioxdx& z@Dt4x7hOjC|H={JhYji%r&=dlTUPKV2V*?4qD2Z>^$o6wwp|qEL!X(jmmCZ>@OVGi z8EM*3p<~z}(ClfLc;|5u?p_tXm8UsE;yr#>`o7=rg2yivQ6U>oCDI?$TF%IhyICjL zWm~hrVQz77h`@E(j%DYHcgY#3`g+Atag_gE(N`BmrxMHD3|`HO*e=r2iAqKW0pi2V zZKp``^RAzjQc}-eP3B?tGyp`>Y-QsD>z#<=na@Q6i$pj$yhxoTU7!ou(BqYeOVJGt z5x?wY5oLaZ!P%5#RYQ&oqRi;wrsQ;tM?t{KsF-*gTGS#DcEjM!NgWz0enGS~ZPqna zQUzkbMar3)KWUc~l}o$vF7+%ZB5qW@sayWiZoy8x4KJ<&YpX{m=iC!)tFyOEwlVl^ z(1y@gtSF*OxTSXpv)l+SOudnsT$l)BvAnHiJzk&Q^_1v;#lZ1g(-Ncp)4p}HL_5Aq zb3C`HK9`O&^)A19S6-R&2~qH;DA`NaIzAFx2BY4?9u7QyXnEECPwq8sMff!fAh&pu z-t#2;0(g7hn>%v;#JD#f+K@T`xp_w(Vo4>-z`Q7AcI177o5)lVP?&Gv(63RlR%_EK zp@0Rl+JERLg$Ro8*__zm4No_~@(fzXu!pCp{KjjS^MXzo5ZZ6kfoH;=l=HL?4*_-b9t>2t zw|B(89!&@kc+1X_4agCd*`pdg4kn@@A%@^NTb#KyqLM_P{@@CHSRY~BB@CI4Hp^`= z?#+d+T78Fzy;ZuayBRY}i*TvsdVx=+iGBOYFMNmDjd|%f&5)haRC3_2#0|frjf&Cy$dPv&fx-g2E_%f;ca*Nt5(W8D zBR%`SR6-v&h?&~CRp)Qq)^&Zpb1#HdX*i^PqS3m_i53S9YBxw%IdwFq8_l{E(q<^^ zv`!uW)YBA_3msOU^?TZG_Qy+epL*>;?&^V6h}W#2ZV2vHC_|bP^L}LMQO-o}X3uv2 zXyYI%TpjyGYyE{GmCTB)jCl`b;lO?2HCXeimAt@;6Hg$7n&QmafQvUdC>MEz^e3!@ z_1!rz;Zvq4>xkzc)Db|t`p`53m>gU1nR8EzT+<-*?KH@XUL%6t#*=wk!l;@Sv_3oa zHnxnN%4WPa_t_*d9kNqqeGpM!T_6a2l?V4#0Gu!!lS$caj4kKPzr3v;?Z$z5JmrR} zems>DP{$uIG~=x$7I5-WXc5CG2wy2HfpCNsC_wtvHgO=EHV8R0~T^X!%qRF&|EFX}R3C zqX2&6-e_pZa&Ygch#4iU=sx#~$6kf{Kk&t#<7!j1E; z0O_G{oJR1?jlZ%HghRH#_Q2CoJoM27^Rs~t8QI)`dTc=w=U5oQBTT529n401yMO`4 zaHa4;=3ge@9(Dyn)x(xifxuZs8Ab%gRst&JRl{h8{#JlEyFuwHN23G`frUmS<@0Yl zfjYZe@c1a=1=vLMnz5wN;?8%mj7%nAi3_JD{)m|}QAO)|gx4|Sg2YO@0D~>Ht8OVx zj8WH#dkj$unFS4F5`jocvuN__)dH@piFM{c|2IEozuzjX6Vn zRK|oDOS)dv_DVDH3SuCUUI23QhdIzA3jsUPdn!qg*Rr^od|uq`l*v(UBAzblBM~11 zeHc^$9g8N+3mJG@vTR&Qye8}p0fW$}W{_&3gjp1vYa z&7kyNI_6d^hF7p%PKdJx(h$S&$x%S4^c!$_`qm73NMx_l7G(+&MNuR-@V0LVJRkD% zSQ7fw5XS{kRRBSBDGNwv?7b$}gVd^2uRW^4^)g3g6Yz^}ps_{qZys1f$gM4cm`91|emtjuOKC))ceFjrK71dy-AK|v3n z83D+6f-q=lOn#oG+xt!AQN0D~WsEfJpoy%EJPdK!rS#0AQH`1$+J-m{h`j>68!xU` z08q;y{M8fMBV8A+t!qLr(o=o0XX&U8Rxt(b*Ru%_#1|W&xQHMExV)ACThJ&*@TW^m z0Sd{<-Vgbm33Q)sCQHK3Q_0>|E@y*`bpLAPzw2f;q;Nu}-GNq1RuBunPXu!X9tBR8 zAae4(;P=C(091G6RJDYvSLSPkTC`po22OL%<7dFVq!iUcIY${OgfoZ3-<-zR`?|Hd@wXgt~JQH>}# z4eOzLv%-|D#6TC{H)BsAA>C;}WD$W6&{6=lw44f2Q@mhMyLhQ({8_t^xkjy0fY1HmDn2jIaLAYMm}M@+qr)lR&^FZVzY zH2WZmlM;V)3g?tCsm+30g9CoL8wV2^guAETvP)zEN`r(saLNdElL$uolID%+LG@U| zTh6+u$@+Yu3oM`#+Pww1II*$Ma0F|i3^UJDhiMCAZC!Dx*8Qw{zqX2aYA?r#wEc=7LJHI+EfwQ zumW}D;ZYVOWbiv=r&}KKyNL{geV}@W_zPWFtc2Qegx`z$#4pCEBDh$JkY~V2!R=Aq ze;aSm@4n#0$)rqn8WM+Cq9TbI$NFKZ* zhTCEx)^bNF5HrehWZ3T3Mie9nWt?ZEgR5fRK&<_zGU?H!ZNLXd4oXU>0{Hg(Y48^I zkU1)Ws9cYE#-O6%ZVa%$G@yUN3E+ASh4q=hm*{AIL%<-jz=VT#r;361osZZU(Ip2tCe72W!MKg7dD47eZwMevIJ-yh!6f9PnhTusY#83^c?V=b58+!lqJX zgL2oOo0#WAR*lmDgW>`npi2R)$*UEF0IgAL2cemSTjvtc=?32JnOF=RRS=eW8aS$JIMDY<&=}^b^V3+&!@BteDSwuhrJVp{ACwd45 z_8Vj8&mipkp5aNRGLTs68W(K0$@_*u2>+i(MVx|AEnEU>UyMEre&VIL`>A@K0;U-D zj~0yKKc6FRV4)8sxuYEK*4olVH5$Pw^#_U+85{V31>jONi2~e=E`WY9-V=0?SUHN5 znSV&Hw(#EN0rUkJ)fubGOeGrknsZze0_9Rfac9MAPvImJ3Pd4NAmQgJp}pv`VL@c?L8}mQvEpOQ~yP z!iAuof>UxRHfPXP$rIerhN~HH9R3^SRu(KRN2D@QSmAe;&4393B(CcnZ#Y$C2%!G> zk2IveMiV+1vI(K8@M~8At>Q@}&>ANWK7jhrgxHXyXo_Vx*$8JSw4uM9re4J!o~DKv z_nPp3B+;BL^2ZT@)iaznUaPbWQ3T-5(pvbgY6$HeE{1lqCuaf6ggvIHRqUa8D#Uc6 z3fepa79=L&q}?9w1_q3GNZqB0l}FCL}&tGL^}d~8I~f(5hYF>ii28qB;j)) zY$l07KF<(10Ke7?QLmjIXMmS28v%a>Xu&>nLeqBu{|Mrpc*j70N<>tPc|#W@FJLJY0eD>M{?n8 z;i(KbZn4F7fd7mF2%R|%#$)Z-sJE~yUx3l_VPHQ79h4*TNUn!_Ps{v@@DQARS0IdF z>m~HaCx4gqnLNEs3IPFoMkTAm^0}t{UyKQP#;$V!U%7BL;_eX2>Xlfu0)#glEsVT> z1AO2m56aBIz)EZo9hHQ=(+a$k9tJ+f5PsqxXw2GQhtuN_34|c~vCvbXPJQ&`lA6$B zffxI92Czw7TzY!IPS`6&BMxcv!paU#gUzu8g`D>z2!S}AHF368jZ-)~G4~GOAkG~K zO;CVXHk08n<|b@WfzMb*E~GlaV&X}@3o8@OTvOPc0i>WaCQ=~$<8#Zb|BSbPq9O>2 zFg}X6JnSzQ8mzJL1g#o16P*y1Y2bVK9jO9{RPE$31H9-4Xl*>1>?Dl3#UTG;hh+dh1i~r5E(fVwXk_mOSK_eeqT-0P@Ti)N z%?Aq?6)6?@=x=W!b_Nt7$l!tvknNmYtBqm8$%?sm0~)Lz0D}oAf>ni46|gX8Fvr)k zQy*B?H2^<_*7h-fn z0!Z}sL2|oNflgwZV zTE7W+kU8n#n^5q^0L~M2MA+kwqBF+ol(V))6Z#+q-aelsA2H)DB@jA7!AUhp%R@o% zgHklQFk--Z-32=}Ztu!LJ<1$;6KxRLSo zZxQ$9p&@3-s8K=&AqBhTMz1u?Y!FR2_BQagrU8b2F~ay*5W*2-mMeT2NqM!by@u2H zaCE;GhlNF@|A!doeb<9Md_8kExBzi3oFt7gC^`1AlcRpP$y1cSrrPGY&~d9?$Ve8T z@sfq=hwX$=C5+d!y9a;85O%Nz-i9neX#H};{ZqJ`aF`1?^KgVuimkuahWMzik)&~qB1@InI8a32k_7_VvOzg1F#sv9GsBndRgaLt@-Q_Dt6 zA&k!mLD`IOXdp%bj7OI;gCWM=lPnXViAKV;DqqlxjI^Ws4ihzcCFqxJcam-hefVdg zQZ%C^IwREqVZ1K{0dZ^3)exyf@ccM04w*9PS&Ge}B{#5`Hh{-;1ZdO%3DX1vZ%vj< zxI}Z|y>(moeI}f=*pOdlkHED7NI9D#fs2fuk-51M8u91F$$*++}~q{Z?jp{5cWmjsZm^Vi`e;hLK$xO*j*n?io`6 zL7t6UzXm)o0o_s_KhHkDsu4!SiJ?Sln4QKW2(y|7Qin*0&WAJkGTLZP472ch#Rh;S%APbEVMj&YzZAfw z=|xPlRnYIc!DNW63`H?mJXHoNmB1BcMaTf6mNE(L;@OjZx8e7%0v*Loe4x#ls=q1q z2*3c>n+WC37X(XL?F3eqUEn}7Ss3=c*_)`_uo^TV1`!0)>qT*5;`;13w}b*d8e~jb zVi83yAZNNf$RS4gDAVkXp+N>#0jbPB4D33F7o-T`F*L?_l!rr%1xP}C!_l;#IHc|h z*Z-uAm>g%qor0xP$ynIJ&B@J}bWf05d#x;8ME^Xaj-dNH#9l-J3esJL2Co@o*VA>) zzlbQS*`fF!(>(i20%+nJcYj$FG9nH#T23-kX0~DpJr{B22!Q5~7v%FbYM)C+{;7x2 zhoaOYi6*FS4yiSKj33~jyj@Lh zGGd9s^vs7LjTMGKOG*KBAHAAF1igD&U;JqFkya>`-$?xQr{JP(*-i4<|P6?s9A5ZPcYbRPrMV=C>9W>z(A%Vki`F==q zJ}ZqgqFIE%$HwqnN8xX`IJD!NexIg_V&i)N{)Ic>_a4n&bA)j+=nA(teG93GAgDmv zWhu4DpvDSyHj)lEm3(9Q$4A5YKfy?gv~VJ1rS{W3M1s$;8pO%zZi5a1bktP&4G^pUTy}Z zm~c206LpOn_5>RqSZgw=2b5ek0yCahPqG0D^57}>7$fDWuJ+o5#c{}tYXUmWfXo;r z{9jFD;%rbIdLhQ*8E*0&B+4ip38t{=z}NBcx>GosAMOOYwuDJ4xu?N8tUVKz9ecJ3 zIL9^$xW-IspEp>WfL~vOg0FdTrtlyp3Moo^Z5x7s_4f#fC_xpQ*3h&ev};#cXh+P? z$Y?!u=?e&xQFsTppmG+?w1P$cSJLp$EYLuf_3d`cT{;7(5FatqNMnTx6c>7e$=cpU zg(7>GhPWSTnp+4*v(uZw@3?CM0_>YUGZshtJYp1HRLB#eA*#X16bP|Uq~tw8uU!R> ziqQ%HbIL_fot+WK3!l9KjvFT$H<~Gw72+mYsi_7uJb(paanB&x1s4$74-x?Z=3#oL z|4K9L{1EUC8hd+=p27b zaMd_b#4hw0$Go}EjD!Nx-(z28Tt3;F=!&yP)ZrV+3Lp4t!DncxBD zG|4acHBKhcPJV;=>WRG~czWtMwv0*3&9XtjpgiIFYD7Vi(4rLMF?_}3wc5!kKtXeo z3gl*@jU12mng4Vx&@4(FwoD~6AZI58^FuX(z>!xA0x&Y;X~7FG5uJ_aX}Cc!Kx>?! zV;g&lLR;nX49M`P%fum$D6_g4LgPxr>0M3pYl!F4!7Y&Zs3k_FJ2=> z;dSo*Ap^|@i9D!kB8c&I_+|iH9(lbWuvNqO3w@yov4zxILj1y!$x<{qP6u;eDQkwXp9uR26(Umb7JtlDgr$i0@ z-e(T>q!EX&2jGt*qX4O!cVvodHaREYjm#Wdi!sP>5 z*9f4lF;@MJ?*ED!Q&yQ^@QeVlCV~V;^ z=?ka8lbW>umLufxlmg08zw?0cqfS;J^8eN}od4`Iqidn`N@At0fWpKr;rE{n-~v$w z$uqPldXNaxwa#v`M-vQ;whtp5t0$qr8F^6a=YMS7@^f}U;PIw^(b2z6cDt+|TF@qi zYKWXVZ4N+ndLj34bTt=h={en#pEQ5VI3Z)S$^qfG71t@<7RXLx$HL!0{S>dNb)V#q9Asi<8d{%q6}4vz31rv1Z+H|RBMz^b@F1o+Nzt4E_Y{(xO` z?JcjyNf?wrxSw=|Y_Rq!8ylZb{~w*IBIDEDs%Matftxf7wlrs?H%ll$LtcY~Q^>Pp zUw6-s4?^xL$MF+M5*p~~|Nqj4NrBqQBnK@~Kq+~Bc*r1@@!IM3 z?OPmm%M!4QRPq^^ksMA`yKj4fh;-cKOGvu`?mT2I_c~3mxc`*(_jNQ%hRs_RLK$3s=osxGU4M7s zoK$$<^20ozx!`rW`nztcm!x^zmU$ZYA6!O$^`aw|KUs>+WY~PTkn_3Gr7^-klt*<_ z3U^IAS2D{SK!*-jF?yA@S3VvG#RrWt%lD9hn}5qa`L%!UfXl`96X8l+>+5Z;Tdan~>HF92Z%WVnv%v1$ zMu*^s@Q3DdEf$|-v^xAEHC8%%wa`79z8Xp=z5DEIy3;;22MY*V|KO+~)qGna@0_f7 zlhQYp=M|rg{`B@JT>0xP*mqFb6Bj8@&-#kA$44?H(E7owhR95>*G#v>z_+^M8a2n_ zx6+s9{&11d<#9#{=8XdiT599<8`C}Nv~kC;xOms36kgEhE?q2a>^;mtzB{`$|30H* zK;VUnqsv0;^Q5%knw%4-qWw^pc+9&P&zUu4dS_X*6yx+K!%A3#)1(ilyX0GK_P519 zmLJzwlDu8tAP4(nIo{;Ub=|w|;W?^f^hPMx_CXG}o>uor&D-LyURO7H57_JPj&3W@ z%evYxFvYkgcW6}RjsEOgwqMNmYt%k=JW}3F{w4&Sm$dSZki&+QmJHE)5B_zV;5cJrY|Nf5j2_e)~s9?y|e| z>JR+bqyE}GdHax0^!XtL4N^5%LO#Lr$EVCnzL}*@enX;Rp?eP5a_s#<>}s%+u$mD~UPRMPB~-Mt!8V z!7HajZy+O(|L?0sc2b9%_OjB8?d2n7&Ys$dcrzu>Ip*O5l@hy9j))&g+)n}*bPgw*;PFnF?URlu)!ki_hf9($mSL)SfdVc2xUd}OXF28kV z0X&o&%&=)V+;l(8x%67&cr*IqL@%>ACPnJyCkBJR({3qnj{X*3va`%3N>N2xR{&kIQ-|?yt79Ypz z5EkD&XNuy2;JQ{q?`F66dZ^~Bpp-8I4Nk7>T9gmdPdMqGFZi6h*4g*^G5_iMd3%`! zKUjl};Okp)eb$Q8+>-M_+xsu4WlFy4Ul+=8bdB+Wx$84jg-WP~o??*fe82v*MBjvS zrj|>8H0=JB`LuiK(k-bcKl$jMzX|f6`7q)4X-^{;f9G@1`Plr1@n}S0-9y}okXz0^ zb8Hu-)Fpg+CibmFbs1_*b#E~fe{#rcb!S>*zG|qp4!$W)c>FK0RXK`5XuxM0EHU~a zBy;Q%@@1=ML1R99sJeC)o^J)KUsYK6mXYrBj|br{>=Q?JcNRQB@tnxC`dZ)N3L0qa zj0`dJK6wy)tINub<8HzecatQoid&YfWoMsWdhzq{{5F->Z!okUTi5$K|D9R9aHtw# ztUz+uofqkReSr6zteDx^lU}bQiytEhH-jQj+m1*to4L=B!b5Y zphGs2{y?{b;+YDI+vwl#Y+XjpEOO}wCw^msTA~@x-1vQub@pyB@`kGe{d(4rl9<@M zm2MKY-E_!0eMi*SosX}^zVnN{gw~6?@!5?-rT#8;MJ+?y>+Hd{!dt-!y{s}%ovui=Tx8%9SLjqS z7a{pL2(285o`!I-F7gt0c?9sZV^+wU5EP{iX=l*4qm=npK%U$2Di%kz<_6JXwGD?RnwJ!rw)+ zzhcLQjt9LhU$*fk>^vtzt%v6mwzpf(6Z1ZPb&mI`D}OWig__Z8HliaPiyLi@{ox;v z^E0`lbgOWMFDMr;X~{jGwb#3B^^1f=VnsB9jyd8YT zflmf=^iQw9{luH^KBGZdM0SnfjryJ*GkChbtlvcUb2a||Jt-N8{(4Xu_4Qy@Ag(4D$bCF4_(xvYPjj}cGL-Sz*Z>~yC|=E?0N{Vc{u3Ax+n6Ivy&f0%yR zyK=K@%-iVuZ?midzUuk02hY!Z%}NiHG|AKO*ibii_V}cEHCtid44?JR=eWQ}ovZ*Y zFra4jl}L8=X?WP#e;BTa&d)LXc`xuzwMS_G{bZGea+a=#gR@V`@?9rI>uFgxdtq)} zqg0YuYreeF>S&gm&EuC$w?7SaEEIMK$bTrlDKF19SX7zbgX7_?`uYxE_|SCwBv7)W zH3CcF;;q`1{=T=V;gT@&^v8hRL+V$%4i+2Xwz-a5W96<6_lp$ndJWAe23Fx`cHZ3@ ztj!IGY4L2F9eMci^zoxbE0#ZnpMI>U-CE=v?~3{8J9hU)+w-`f^~(D)Zn~|NvSJ-4 z8apK?PK^f&1!p=keD#t~G}>1$mBp5O^3{e#L_;8VxjtV+8V!Opydt@oMo)hR)) zVxAg~m0^)5vd&U97yl~6v25HiZxX7HNbq=K`IMsba#S+s`lqJ*DRsdyLH!GVH2-C8 zK~@P%8G*VD4HxPeSrnB@L{TQO%Ei%^jGv|!hRb|Ph{;ms%d;(;Yx7p4BHmlcbE3dE%Wulf9ZcI^nnSs`+-!%M)0%Xl>}U7 zJKuG(Kov3aU!iq{pQ`GsxmMXWD2q7;d~}a&U12Xzy;M|MSZCvN|f)c21|j(CsgIj^W*zKt0B)8oK9K zyiB$OjGpRw_4HS5E0&+Ldbr@GHGkuE(8=SG>mGyGRh_!0hc4UYSxh8RLv-(j+2v1 z@I487`gvJ*#(Ty(p4y2GcpCY@HsgBHCyVsH);X!+%W=ms-zHqypLb+kNvHP9KlHDW zOf$#(pG@yAbk-hJal?k)R2-21x42&1 z{HmnpH6QLMGfEv#$z9D+8psNGE^pVBWfRNGC2j|vaT1;X++M4*+~&z=G1@<;{rl(b zggZe`>qD6YE}EA8Xu0*~+#uxbQw}?YrgS%3TP;dpPOUtaJ0J(W<({$Y?E1Vl+%B+gJj<|W!Q}@jZMDu& zEe*7dz3tI{Ikv(tsB6CKD3&Md-uG+?`oZTP?`NQ zKBxH^yZ&A4G@tNsY^zE6OE+{`bxkgL=Ri@msy-- z>6T;ztR%kVo0Jh|Xh%_cWAp*%{=Qx8p>$MLuF`>#b^C=BjlH_(+RM4SZVOxlnWv{Z zS_tH_gi{^w@MPu4H<+Zd8lXXE(8BAJMrO`e+p&ffIKFfCC<4pzbY(!ozF9!-M;C4cG~T4Z|#?=+q%WVwLvTR*a;=+8&N88p)RMtHlp;_o!;K` z44kiD?vdSbORv)qV57P(pCMY)pV%h4%wG0erk%>srnTQ!sgr7D zt;6SnAoylRZU+GW6vN!HCQ3NN!9uG1K~SkTcJ*O+Zf73KwX(im@A1HC;P|vJgT%6hLzF(J$k17`J1^D7;-grm_&F)~BDg>Pur6M$AaeQ#Tza-rWfz@uGch#q zp*l-aw$~tsidSIuJG!)E&UdNRFZ%nJ_M=t9&K+$*V()y0CzVL*&c4J&)4ncFIxmI0 zA*?O+wbXM@$Eef%;fYv+p41sB?S>aT`=%m7e&a=!7ppG`Ow=Lxnf`M3p3sQKhtjyyIrvQ;N6HI&b;~pKpL6XTxV5(vxps0N#Yg^HeVd4ZPNDBF^5{FA{a`wo`cMVj=8-Bfg!`AAV zVbA>cnG(vE=?fD;6#?^L7@9n9hMC{QkAB!3|6wAS?Dy6*WTj`Lyed*)@fa%HZiBMe zX3+GzslI3mgq2s2)dCTWpH}uIen|~Bx|Q`ep4)zRF|BSj5a8;E4sF8*!|; zsc-nK(+vd{cHj3iBXU10S(9`^wA0m|EQa*Z=@>Yfk8Lx5)t{P`m}Zat;$ING%d8{y z@HMn7Up{2orX=LP&Q)cdI(?ZT>`A+x1yQH{{)nb1y3}F7?HmqD^4H_uz@Iw!dR9 z|4<1yZ}V=Bph7mCY5Va)F|B(3p6S3T=OuMM49}U4S94!B9*NVmddesYYiGxLzB@Fo zi37`ni3_6mYM?Or+^;cmwq6cAdPT8n$nybjNWV}E{ z>rwRF09&{0y@sX=r)R~M5IpH^-#2IL6vJl=3=injNc44-3c<(t9ce+*YIAe2FFzjS z0lzl0W0<+aTzk)wx2{7-4$a)%{P*I7m;bmW_m5^ad`Ygz{SeRT_ttLEpIT$lZ=GUMcH&)IUSrI9_?P|*oR~IsLT1A$j7oF2A`TyX_GU!!4|;MLceTH( z57!-=h=AMQ7V3WmM~jNZd=Tm3#)mAu4}#Kne8VST*k4;LSsjYp(k(8=HIK3hADU$2|G3=*;Bo7+Zd7Wl;x{QsYZHien^k2VhzmD9$`@lSDtVq=j_q#fhhp zZgl}hC8A$sLC;NfAmXdZP|eW4FxTB6;)E;@rm-YquGOBr#d@lvOH`uc`rv4+r=b_W z=@(f7t6S!>I9Yl5{fa$|)c;1ILfhtLzf4f|Nq-8y>}FVT2>Si~bz#!=X8K>Dig?2= zMx!xRvT{Jg+^{@Zb%V+1BJr}HvbpQW?tt4IgXhw+rofLn$#5Q^OZu?K86*^NrgW_!-FgYhhU5mHDv6I-Y1# z);E?4#M`^y&uQQGR>^GtS61P;^Z~K`>f(n?WO4LQ6B&AoK3Yh35V6?DBB|}4eUilW z>b`8go9riKpQLnr&bwpnYxfa|w+{5AemR=CD&ku@8RPcvknd!(kh(rloEKS1U+E-_ zFw1|&C5mf&2eQj2#k%|bUN`xgd(Faj89J6OBnY>@r%!9>e1}tfC`=N*O7YEiKn6Q$ zV#31-GFfz_ln+h)zxXg3H$l;UBqJAZmVxY52URyHw_n}v z7XH!!D5_v79OvL)Z8rJc%iG3zcpgeZHqiE+`uj&xOdX>h1cN*%Nw?Wx%bf`y^fJtK z$Czw?{S=83A`y3aSu(h)f0S9z`Q!Vf50Cd)F`--dJ3Qf{idTeWQpAg^oix=hJzd^D;1ONFbNo638Af=6+yGx-!|mB?#5 zPI}p&@bg0P<*ObZb(itnl4w%K$1j$8wWre-q1-hdgncXn=6$o_zoWfl*_&b*U31?8Rljc61PIGMY;vcc}X!DTezWsCm z%k+W?=4%d2cHDbw}=SF~h>+)l)uQ({z7r`Pg5tW_As3aQm^7 zhfElaM&!xyJ3z;T0bzd$N}?Mld{i@Y_3sH_b$GEgKOBx{dOL+#`1)SpMNU1lWRz1ptRA2nw;g* z@x0waq(a&{Ka|DCzYG&KSB{|tcfLMC`0jwP;+8vPkuED+wh!Z$dj}^ zN_3dOF^aPaE5>46qQiRd`h<6J&830tZi%Q5wbA!FG}O3jh6x|f6eYP5d?b!!?Zk7( zNO&pJrmcgUitjmdaF~lh*dy+HT(qV7c19@vtcPs0VmxynFO7`FSKPXv(f19gNc1@m zF3^vcK(}8Vkh73~P{Q!E^bNDdryn#c-C|mne$vRwYazc6b6ZP$v^8RJ03#Hjnh;$# z4=&cTMq5T&418hGHnYdchn|hD`=xnbYiRTiA5^GhSfUR<=vJs%+_UgDW^qiybXLs( z?b|E-S`n!|70w%jc{{W)a5(E*+;7>doMWGTlf&XA4`O?Rsbe^X0DZt1S9KX*Ar|hs zh$q8*89P=z2p*S=T$d;T_ZEJd@Yo@YMhv?-F6P<06dj&V--nSONe@2qJ*q6~@9s$L zKPs)9Rp7>SxWSF4uQ_`tGGhY8XnS6SeCWf_!OcUyf0lv!i2)&IliuiSri9Uq^Gcv% zP~^yHGA+uU#8~M;F%1D2jhErBcfxc@=ebuJp1iaCWM#CI^9M!l%reCg^S)SmDB=g> znLFHh$f`W1FV>Jb%w@IW7PCmFnrs1e*f^| zCylzK5A%2Opq7R3eME+3(UU$qN_6NAUYBX`4pa~(2_kYt zdvyr@FItm-LrEJAytacG7E4tjhD!n_F~wi>$?`ddm|wqQ=Em=nVf+lFiWy2ph!8#i zaf6zS8llBsdN!!LBsx@u+gzJ*TU#>JT=lhx|iIHrmO_ts)?^EvylX4vi zuhdvDe_O-8-d4r8Kcnk7eoinDVBmh!MzB@UaW!zRSD3zMr1smyMm6?&!X1;%D=JFrc9wjIj=ARq{{>S_OZopH>AU0Ee80aHr6>}!rFPW{+M@Oj+Gy<& zt9ET_*WMMoifC(9H1^&*s8y7Yy^1!dO=AX;Jo!Gqf0MkfocrA8TxYz`=&yGbhE{4t zZ~k?zy~{xfS1ameIJyh>FX}yBW(LtH7{LYSEY|NJTNqLnP6(qiNlmQ`DUSmJ>@I(+foK$akDX+obZMm8Lv=QnG7KOd^`3eMCTRXE z(xjx0k*8^A=i$v$7M|*MLBKwV)Y>)Vjs%g43u98~aN`yQVVa_F186r(fgHnK;gaHR}mI8|i}wQtty>qX$`fX(<* zugX@hN&B)LYItcIn^|Q_nIFAURhyxUki%nWb1TDWI#vJFJQ;}feKfxvIZ-Srw9ntx zCf_4x*#g?UD7XV>@f6k;pUYWSK(>t1!W`EO&ccsWyw?yz2JQ^dzd>#emxdq0BAnU{ zg1NKcZ|;(6V;`R965iRl?9jox#%(^!KLEAy^?sDR3$mh!Mt*-=TgmA8#>Cs$?8}Rp z9NdE^XU%KyFoFWC!6Bh&Gl6roCc)U%vC8SCIzoqFHdwyW~kNG~wBFeFVt?SytI;9q-fc4V1A+3_)s`4Uj~s~8Pw zCXnG!)aEicYao-Oeho)XU#10`CY2ra@E)nE9P;s0fPVQoHC#U*skk`5;^GoP0z8{9cb=Q^ky%4)+Wu~0ljjs(+oe{ zHc2*VkK}(m{YX@`tT(O>X4a9Ad9-O6Y8R07O1V~&Qx~LDl(1R&mfdw*rPk;kDI8m9 zvmwIg@?Exb?%-K~-4cmS>aH2&W(L#72( zjHA~pOu6b6=zUnPLHL6@Ne&HA$)jlGI!W!PyA;|m3FsGE($OZOQI@;|^4E&t56T_! z$vvA3N}RG_CF;^zLBpqnWm#)Qc+V4?%crmSlIE3#6;m2=D>Oh}M34MGs9l-NN2mc& z&tFH>hO*s(-&gN3aN&m1osnt}s?=`BUbj!$8^iM|Tj)b&omIMaAXnaS3}b&-5@*tu zbM3E_so|%m;#qhJGod__3~2Qk>FCg8zrj25y8dKJ?SycfHVG0CA4SZ79|`oCe3T=R za2)$BmbE9sqr!8`d|XIrb|RTQrd?~ieg|W9B{?DG+f!-GAo~HB%-6^$8&(k>5SUL%>%}8LzkM{kexOo^TZu0#A*8e^`v@& z^9&TQvM0Ll6svD!EYl`-c6)MzliLSqQfl89{>Gmb(8PvhII|7Y{C6`p>k4j+h`Q<; zuqD-3&A@@@_*5Eg5;YMK%4LnB0&FjlNHawhCfR6Mvpl2z-V^ST?c0c;FjWWcTxa>K z$WpgJfd4KHg0^qypjTr7R+FOgaDi;zdzBK!0b_#%zbE{Yo}L+_{$8Au-5c=~|;hGWg}V&65X( z^_dRwUXPDN+o|h7xkF|Fi2~V0#EsKi=X$k%KJzk!2iHWu>LlGTAEI5=5#>1i*bYMs zE;WmK>c0v=n?(h%qij#`vXbUbmDE{rg3JIRVWd9!?9G@iZ<5dcb((~%cBYK^ zOX@x1ACN%lUj8>cg=Wm6BPPRAi4kH4aZ72$0~Jus6<{=%$L4bGKIpzx?^HV_6z@x} ztrAiT+NFkHhxI-U{yOqB`|PEj)0sn3V( z1UW8Ta4plUMeI)yY0ZHS5{+Jrj?Los9r*4_uMd(2s<0F_Fbf0;Tty<6N&c5m`8lAy zbKvcAelxU_i)@fFe{cpBJ|i2g_hvI2Q_^#BX*38{``@<#eHUEUbw|qQyzs-A9-n$X zXmm&90D>J#=SMNhO<8-oM+&#<>a|<5cJy}WYFeN7b8;sVQ{oL5;!PsnTQ1hMV-G6Q zH<#sw5B^kV9y)&SCTw$u66(aS@kNYvlEhnZgU;sj>V1%Gs?CdSa+dgJ8<*Mppo)+v zq`Pv~z@g&VxdVJ^`OtvK*Vnm@FWmlT3XYM|P|eiI;p5+;S%;g2nVu$zVT8J>e>X!) z#Gff9Ab*k6f?J50X&sxOU$;S{a?wZ=_FAJoa=3<$P5M$ss+YFCbAcVjSO4 z)o*S60*026;6Y2`u z{vT7{!iCXd7g?O6CdGMI_Qemq=_iF}c#pKif_Vy}J~}|CJ27|DeaGTNbJ#ABi-Vu5~{s zt9PhhW%(!KOi8pd->t$@TiX8TU70O#fV|>e9K1tbAlWe)+pWxX3T||8QbpLnU^=v! zs->{Jg<^EC*#_fG51;br{kVD;ROZoZx5^9xDjLDTa~5Py$QE;A1n5&AjB4rn-d+Wm+nKD)BlxmCw+76iBy9Kud??X!y>n-^p&i|#pT)fE$ws5YQ^xb ztynRpAXSlPUXp!4@nr@KSCb~ryAM8AFNOBrb9U7Sh8OW(Gr3o(&NfRw%n)L2% z?oucsr|7_s&(Ys7rF}fS_e=wq)(0#QyL+7Y6G*U8?_Zm@Oo_-n`>(iQN}W~W z{PKvLlkh19plo^+O1}@oT2nYHtaWkf?^J}A%bLcbuV=MzW(SMDZfAnwbyqO!8Y*-I zLn{8RZI7wzdu;qZK)L8M8-aX}=fi|->P*adfI=GT#V>xi_(x^@M>-gx2{nkF<}`3= zScUAI6f>=%IOoDLbePs6X|Ypu(2G6k!I_ITA?TZCq6sRt`Qc6izj@VbfZ%|-O6!{i z)UD2)q`PPlJ+qW`kch<{P*u`~^+icD<>*Z~+x(3!eW_p1JEUK*t(|vGlKzvWC}<}; zBPq=d{k{a7IVh3tigfzA z_R|~PV0_B%Zh6|D^pFLb^eyHe%Cur{RUFPOQ{TJ%cgC3&E(a9~s|0A|Td=3o)D4Rf z$91mD!7QtIcnR}&(pTbCJ*V%aPw$qcPt>+^VD%4G{r%62X19j=X}0cl4fA*aWlLHH zhZ_zJb{>R>TRkft)^_FbA+8MyS943mbwA^kZdBhmI*09x-re6lhvizCri5n9iuP>f z@(k`@ql(^boW57v=n76RTIR0*CRI&$}h+%lM>zop53t;9_gLo*M?MK;6Ms+G{% z0WdCyG;9hxH-6LH6~|f`FGQBfX<15XRr5z7d|guwXZPlE-OD8bI}qZU%oRk$D0~ed zD2Z=as@ccC*(JYX|NIo{cA*6al*bR7^euJO^YFH?veZyY%??{btt6DHem-`Xh@e|f zcWYtbUH-hu_0L5V&}KlNl0Ls?b3t~=k#9}0f2nJ0?&hJLK3`Ak@naXJ&?v@hTC1_~ zP9A({%$2@2qsPZ;lP-bPKbNwZP`+zi5CJ$^mD$l4_e>o=>u^i%T$jrdn-I64t{eVr z{CLgbcfstwyS~) zvvNvtIr#?Wwft&5z;R0|V68Ie|8Ti1U_>BeVnj9FK#d7AMt~~GT+z0tp0qA!B|r`Pz5X+{$61+{`FSaIuoO>C{ei$x z@g7#=^TpWN3hwTT+MZcfiU`-RelC4VOoXup?g;ujd4cEocFif^Um9AMb8LNR28i~W zO^WEGLNCj@CKrUpL3B4UugV(zzVV_<>O98c%$ZP{j{SiZ3D{&;rumuua0e^&$S?=8Cvsm%d0YE5>w!i2Wrm;btEEi9IOp6jc{)ksN4wdKeVeT4EW9RjmNM++I z=;%?>TJLDxH8O9vW(jnZCa%QxmrDFqsNiPNEd>-DJ70;r6;a0`_}7F|<45AxS{A`# zNj;&&26es6qbDZ#hlxQqGZSaKR7`ZFaiON??>PjQ0{>I9&8(33$%n8X`&Qk}f)5%E zqLiCH|2&U!uktcUZ94dTQGEa*zf?t&j;xIa`V1Q1GX1&xdr9nNT1;H}Ns~gWk_nk9 zy{+`S@-HK2xo7+RTKoME&_27PZK1m~mPO=Zmum$OcM`F^bMsc}zXybgy6dg3%qg!3 zKz3KB42!*G%);*DPY1Nr*;yi3Q!9hW)oqVa{Fd6e@&w$r@XhF+hGS278TEph6o~~= z*rs9R*13vt51i?lm2RBj$25zp8_~X%n2c7h2YpjkTOm5Wkdo`UobQ@&pbgpW1-l?| zxkb_dR1C|4T3h>Hr?OMc?mr1Q9hE@g-{ZD9!Q?r{aW)y+F-X_2+A%R&cu3zyXE!yp zoVsKSIb!qKk_9AYYy`hw+49ILb2U7a4^G)N6LC}-)$@_Jb~unOXO2=k)FNe1e^bIJ z*fRv#NKfP-oFP0y+u!a-nJ#GZna!dRJ1AQu(ow^)cHvIR9}L9iz7+-Ndz_;jSxzIc zvc>FY)+%QhKRiPx*DYcD?mS{qaf{YOle3~CN z(88os%DAc8*vJ4Ii%SSYJ@%o1Q|MbqIhWK922o}25_3&pQL_STdBHHwD>X2cqO3lc zDr&n#4KflZ!MQ7W z8$klGV{=mM{SgxZQgI`OIDLMAav=!aVWfg<>6^5+;>56Lso^M?XRuX^JlnrCcX;*~ z2iAUBh2=Nb15iXQ{ACV1nlrG+%?~Yxl|O>)q#BuDd8A;uT=j5^`@CY7&|>K#ah7OV9sNz?Y z3^97oQT!t`w(?R1jJKLyI}r~Tlvq|E;NGGrVC~Q5Wt4N}AV|Y6!blNAmz{to$sbN; zVcQxmysmC3*zjktH6#dK!2HaoA(pw{JRZy9IE!^$v|Z{AfnMjMDP@kanvQYUC&7Fh zs{Z{)bHp_pvU#tNRu&@2icY6%T5U_x&K6t3xG!zukoQBAdaoI*+1!)Gw-1vU(F$~9CZK<54NrSPt32mWAe zT`P%Z)1xc>ZIv)uUUE`~p`NZ`%V0lq>Y0(b>!X)hL&Ao*Ykr|{M(zF!u_&yEJ3zVP zE;=MrE!0q?U%FX;-J(JLZSv7$)6PPyQr=8Zj~c9?h?Nk`-0w9Hwa0xoGisMzgJcow znnZnb?;LkO7RQq{XSupYF&1|G+Zkly5Zu>}va5N^n=DF|XsNA-6Du?LRQvr&S=Q9V z)I9h}6JH6IXPhdCVAaj|=cS{bq|3K-UeSz|i@!M@G{Ud0)pU4{XifyHcVUQXSwU=g zq|%l!UE&vh)w&f5rWox=bj58!CAgjtp4f{bZFpt&k4yjM_E$V78HV$nuVP{)KTp2NUOvlvguVwK}K(gW`Q&{Y)vv+Q_GYrkf@{lM;7#(PBtoQf@B|M+G z=V^QE$Z_=9T&C`dSPcwm8scxQZ)9^{xORA+!$LS3`A%IF6qHR!r(E={3jGp%@e#`K z@`>8H8KX85(m$oPpe#e&%LS1!Goj}D7Ur#|AV{4kKcG5EZc*7$S)kkuQT|@WWi=F# zi-y=n^=Wy<*6vAY>M!W5m)C8OWQh$5<#%!?Zufn9mYro!@a$mL-}u5C9&ZP;E|o!3 zW~<uLWLt*+tB)#Jw838ApW=zp#PXL++ofF5{7JfPYjZav#xL!Jj6l~N4)+Y zV>S@|uJZ%t@6(Ij(P@cYX$b>G+$OYZi2mdER<6t-szie~Ut0x|Kj`;S**yY%wV;mT z?R~&>8&bxU163xg5V@K88t(U3RCeGohTO&FbHwZ?XR^eKCaG$+u<3=Hv3AJLLE6I2 zOTOd}#qP10!a7VPGRfWcNH+xw_!Q zQU)#G@#A0MU#8nBA6Ah)KDF>c;FJ%2meLUGG&6kyGM?uzZ9GH&({Fd`zOnJ(FNh3W zyNg+da^v@SVJN6tsI`PtxZqwdvUp1U$*yYl^vgVHYFG%>DDFhF;3F)ew0JP6Xx39W zEI%zVi(W8Xm#p8bqzF4+%oNeWgC;yy#gSWn2USd;W|A1 z%Fc;cIt?Jf!?xerE*6U~cE!D&Qx6G0ttz8jsd=<};X6R{??y#r9LXdlFS7%ZIZ zhW46=rJ*lIq)gWXZE|*PA2BIck)R2ZnmF}1AS&-Z8YQ8M<5usd7pxpivr%OFlWjgg zJnm>vA>7uRFU&$boPdT!w0lT1X^I#4)t;-l$45@4jQU zX|{9wNe$bel3BQWKfdI+?L^c5TsdnZuR@T|8vpAO@ica0B5x(LkbjM$!1?Z0QD5o} zqLwFt;8ZOqEfwwi?f9bJm)7<$OxSW5)H7w68<(g5d4kZga6(Vp=BsVk+xGdlJHIl) zhHC3Pt4Sv3?{2Dl$&}w(etyK)si`dMzq`}od=Jmt8f0aquVaRl-uH!y*gA7CzK568 z{`w>YIzB?JsQNdIiukX48EhZ}c0=9x!JI;1ctLoOYt(R;uuFpbhl-95V5IWf10sSJ zjAcw;p|0OzPcqohK>=llNzy}Dz#F53Fa=$xojblfeNSSyxYP5PHnFq(RvKt?*=Ab< z@0>dPUD!1_G?4u|H%|O8rL6Jv8Kv(&pF|>VI+FL|CQdfM@z%>n<9*x_+z&9w_b_X# z`#e6l5zSrh7RP;ycl1K@LU>mTPG!SRu_Q5gMCy$Z6DHpDKbfJ`WeBnY^Wz&;7JT)l z2UuYL9CK8A;D{=aL04qTs-`N;0MfaL1lrh|m0>It(N*_4`n~iVUfUNC=C;yT`iHMi zQ?bet-~iDY42;kc7e=36Q_ey3Z}vLcf`3Z&WrpD`B6-tBo|u-j#zx3oiBZU0dBqi> z%%KoAp(2f9XKuY%n+V4tI#ov{Y@juN;-}zTyXMy3Rx!Qdc}wfWNIM5vQKq1IOL~UI zEqVXUsh_2dlE^#xW?u%ylm&8t8j&x$n~KP3N&FA)7>zpz9c;fQaU0TbbRTvbvUD69 zcGLP%5ZS~M;At2;%2IT6&px5=-k!jia7bKU&Y84-@`~Ng({jO`+!}y;NBe_-hqI)#acV!7dNkKq|0kn)fKb{QZ-5#&99#3|AOCbuyIH@87_( z#fCS%=fXh@f$F|fJo=s3FxkhyxjXVuZ$}ZCH6>BnXmlYlSCcp>ImHsjTYXdT*xd6@ zM$i~nzj#3s)+yY+@g?>>)*fG4@65t&0puqWyf1y_vR54eD;G1 z&`fRJ{7qBTiZTe9nID>JH4)mQ^JZQbQ7efMlHTUyEnpqLdCOCONYBglLv`a%!EDjd zz%Hj?@ytyyoOi9NSgAn#HC2A?2r9$H2#`L%Cwb@~T48l1 z_u7axP%BmjLS5Lq8lE?FTOE0~@{U%zrz6uM(p2%sB4~jdmC$8tSj2@qdg2O(oenR> zu-0=n!;OOMdl7{M36D)LYWcRRTk+I_gYfVpQ~d zAw^5fe0i0n=mt3 zHQb+IOwKeBF_kuRO}3VT6AS*ijDD6(8LSg+-JEse8eU=`Ei45~ANN@qPlKMvreaN% z`S6)7u%q{{j@{?Jn@Awzt%rp0>(mP2x4q}Lidazt%KdBZM7!Q}Pk811sQA|@gACJ8 zl;;h-sB5kP9xw_jtVP-#JL%>2u?**(*}sPFY<+)%Xw`ph zb)ihnR2O^_d0?&A|HUnr5~5|e^RW9OUk8bl6aP887iYB(UO3LS636wQ^_^X`b|&Iu z_u_o4d;PEYS_e@S^V_<=W>UGtKc9>#z4bioG`!%wHgjL^G#E ziTC=>eZe<49_q9;Fc3?|RM+SF)L_9xky%q?J4A0L=yoC-m&%{)B7+%+_`fjZ)I>8M z82DOq#T3UPh~L%k#8V|kX-FDaTKDFw|Go~e5EkuvmzZX58NoOJ4YK+M+B$o-JoF_wTF9xr3K-Tvc&dqQ0roR3(JrgD)ptVA>rgX3zl{CP{; z!cY`M?7{o2Zxk$U?VzpivV&%2;jm;W6 z9C-ZRtC1EFo0x}$NH542Lxqm+jjc|axO=Ua;0~mimPEuGol2X!G=TfQa!Osl6u|>W zp{C1jBTK%2v`r~OhOR!Uh3G18dDWEtw@PYz&TjGCK18>#3@h19(PTFeqg;AW_2yRb z@ptPlUEfvQy?zbh4w?WYk@A6_3dOosxfO z9drd>_#u1pBrn&jW_x4*=AHob7yB8xwk^@Z7HphX8tgFO$U`2!{wc@WL-lC03v2Po zniE1;rh3y`vI4Z-zlx$y52F-RGratp_ay1xUp@`&_v$y9@N7bAP*^#^T>P z*B^!qjidWMTwU@IK2Wo~pLuF4mt(HC{^6=MH_`gVpH1H@BSEbP6Ks1EH`3 zvz=E?K(&w~R8g0RCfoAf_a%`VU7`HUUM0%lug#{gri*eI12?Se`?R_9(e@thK!fJs zjA5x^e(Ck@Q(=5&!G|~ZC6(+qFZ*DF<&D2NX{Sstex9rzjiDD#ESN9O^&7ORurdzo zrY^pDkLgl2_WpP|`zI6*dP8AN!DW#K!IH!(`PCn8P2BQT`CTQ`<*UBCtv|xfP7|C6 z2b@X*ruH5=WgZ>ffTdO8Mytn11A*3)=Z<74qaH^9;jk|SZOi8dJKG_AQ!qNCK>tmzw$d@-8^d* zNn1_<6dpZg41e)HibH&=P&<bIxJn46hR6ZUn9^CM03rXX)lBt86|`U zNo$;sS&T^#F$_m#y$$t1!Q;Ocu)k+ilT&dVV|xOwjr99lT=+6@4n)~nx>Ydk4emM2 zq?crp`o}yy7qIFV;od8bdhaNsr9A80FkPgC>tIT!^ZVNiTQ<_0)8+DelgVEG<0Vr` zA!F~-#%tm@58^m!PhMMMZYhpm7S#>q4j5fMLs4&QE3$~5-;wCS>}|X*Ft?g`YK1R; zYx1Uw#l#{H3+_Ze9abjm3{Nu=$IUIc>duu91tR_IsGFQ{oVnUw!-{xYFL#_p&^nGO z*gF&$#BXy1GT$}9XMIK8GKo%A%a$V4h!G-0RP9KZ1AhWZe9XwqY5vJ zf!0>}KQgzt4u2tlUN^9f3rnXWGve?n{m(x(*!_k$Yn4Z(w@udi6ytv!y`A9M5*BM$ z&$p?UIitwXAZW=Hol~){;QQ`xzEf8$nyZc72`w<^`K(B=8Hw*{qwE9R(r|bcZ1TXhrN(AeCBp+VPKP@@L!oJ9%Oiz{A>tj8n5q+3(w1+j2~Q zfvLY(cq^rbQFn}(c|YOOfHV3Jh|yoprZAH0=-Zv!`0puA_4Es&7m>W*tOq6=89Zl{ z-^CT}+>mSeZYe(aDRAK}IZC6|Fu1%C39eJWD@UfqYa4FIZ1nF7%we#zh*X zE!{#cSoAQrLVI2M5>3@ZfS|}_txb-BMfr`bz1ee3%aOHJ#=kAK5+QJ*8UA2c0|&fU24zhL)v#`^wf>M*MlYMDszH$RekBDTuQEP zRtd^b7_|%$2p-xRX%ter(m4 z&8aWxR=FVScFrxS{#i5?amlY9HW%I_;jVve`8BBgIOsT4IbKNS--BWszCYDQHA$|G zoXJj<)_UUZh0ZSTcaQi{d|-(uV<-))%LkL4%5J&b;a7St6&H}k@XFI>w-Kh?dFL>> zr;9;6VQ&Svzod7DSK$I37b2WWn{klbaQSo)ySqt+e>u^!+a=}eIUE@Uy=dHbvidq+QOxVFCi?tw1`)=K5Q zVUZJ-&#SYZ#2^MqAU=RySb7SqmF$Y*c%y(ED#N3bu| z(wASYwpH(Hha-Jc7tp&GGkB-qubzu(x5ysRQM?HXd4$iYNnTuu`{)j-3StO3N~N#6 z2xv=PF|Wv3sQUbC!1L$6aDZFGkzKjWyZc%}GeH0i+y6}LX09*!9YaYw4ljoiURyE4 zOsK%eUfn(+cM-3Tr1Aq<-x%)X?A}-liz+HanfztSKs7X5U1AK9T{qB4vvlv3vWN>jqV#I|>!#vPd3js9;)f~UBIhDGzBqR)zgp=zyvF}c_ez{Lm+q1`ELPh9dP$B`T{!9O zjE4p;WkR%%($I1%Nw3^I-FvP@8lHsd{sCP#gyt#jlWLAu+20dg7qf4yoEUU23x44D zBU?q(O~0Nz!5jn=azFi_sM~*Vx27gZ3|A ztx@MI(6bg8@{d%lpAs$0w6Bd&1Q0YH-+Ro>4yx`uwIIkrmkRXFbm~_@m-s@FvtqXo z-_2gfB}M;9lZV^)D~%uJ^PcJ2Y+!_^ZeH5AXMBYA22MAOF3BUd~s;i8}!z z<~;fX<7s!-VL1fwh!@#eX$+7=s?4P8$ zf%W&FqD|$au!KA{oK8G|;Kzcdmr24tYi-E<5j-4LefnEPaoaU?9W%{@UYC*B{sJMQknp)%G z5a6d8j3vR1l99z@muz!O99dA4a}}r6pvVp0D`7qOVGDLUHnMM&3=|GSR~RfsYsh<( zGz7O)FjakLP8_@-JFYwoV%qZIvmM%g#}=~~p-j<`@c2`aPL$(6joOz=#r2_fU9;iE zr_Vg3>SQARg1E%*fH)~Goz5D}Y|8$D*BhGyo@UW?mSm2bX@EM;?F~I5K0||RJ04xs z*ycz+WCw4i#ta`a!G1}oobHgozMXJ?31$GNh(`*N$&b-gPN;HaDuoypv=c5zF*VfER7; zz190677a zMS;bxA-&uRB_|5<9zdvGO{~Roqt}AIdTZQ!ngCgAtH@mC(7;$-*u{1#>tbl~3b7U9 z_OVP8eRtaUlRvZ!sTfIcfR`_#w*R>RW|@oE(wtNzIZTUHD<|&g7{y-AgoO_!2p_V- z@I-K;j#8!ZqI)o-G=ZigZ749!HXS^}&oO;urs=o;Aje53YIB;W_8SMqsLP-J^r;?( zMpjUJkxg3|8|dh%F$=LhL=i=P&87+pT=RifqpRx?w)mZvq43&DR7pCr4=n8AV6exD%J?5xsp0&&*^do4-Yt z9&!R?A5cyP*iIh?k%FytE5LD!g`IuMLwD%-d20wVlcjv5`n8;s(|!P{uBvpwFi;lf zj|p#WV$P{Jrc}Nolzr$hIZU1gQFfo(H5350YEWVNa2L_np=xw^4;KAYb;b}nAXZ?I z@VZth;$PL*Q6JvjvqVM@2GcsLU~v4jiV+U3i5Pxj)V^a>f%cD& z09Lbn<_(nK0#E0M#}9rUD*to&_wo+#riC3mMa3&T(Hz~O6H_|=8W2WI_D}KvnpUU~ zML)RPgUG=gbkU{%=>u*T=q1cGC|&={A^Cm_>gRVOT!V0ItO+2yQUJUQVg{d2+!xIY{&uIk?SHG~f`rm{RmDfTBDpZ}z(I zq{7@5nvkjFo6Rtp5I#%Nh4Bd97E( zZUg66OLn)Ec1TSvqO|edPCuF6URuufWd(X;LS(XZTEAs-5(hr){J5uYyV&0(mCfX9QcPmVzJc>#cTIp0-JTSRn+BB~T#mfK`LRx&hIpKO9rQF6w1| zj7o_h(nViJdDWSN$JgQ8s=QahOyGPWZr4UiaB81pC4mL-`C(M)zA8ZU1*LwPI(NGt zls|axV|4LBrBOXi(2WvSDem$x@$H+FsaMF;WfETDdNQz7{lZa^ zj0!lrL3KvrK6oG?rqo;xrYR7;eQ5%?`9+VwxNw*I1Hkh2MhN%0EzfX4d4qy>IeJ0TzD1hxq-STey)C?3AH;`c@h?;!Zl*4h9^oQX;53R8J+z zz>M@|V5Q25y-;TSmNt0e$Q!Ztf;9bAFAhxB66L%sn*wg4(04HmM_lT=xU8GFlRTvd zOuh=}7~`j5k91n@ZPK`>Pifej#Yiv4MHIP%+w%oXZ#h^@T1wx&mHNIikYDK}po9<; z3EUrPrTx0%cDb_{NScuqv2h-l~sDKvU?(py1Ul>0mggu7SVT2j;IUf zEC0;`pj9gJ2CwYvBDFAav#Aj6E=E;J*%%n<6s)wL2cY3jHrd0$?emIa@-HoSc^(Pk znG5$)GYyz?hx;nGkb&7Q{+I=2$krICNC?Pb6kX#6+};H$uHHoqmPj!i>}Vm=xJ)5A z#IB;%Hvs>Yq5Oztb+9#Er1OXgV9|?GKii7?*n%FRhT#?_UNlienN*|&vtE>eC!hrp zmpUPRp}f06NsI%YK-kAq*i-4K;oVG4#J}$SOF1R}?$27_;v5yDFmYhh$}4botgc+J z`>k|G23Ywa`DCd-cX0D9uxuGOTMJPnFCv|F9DOI?_REG?tqP+Wx)a*Y<8=&?oV!pQJ^|R=`B~Em3_Djo?i>ST%M{gaei}n&9w~x(7im2VQ%8APnFnRI2J)N}L z!~3@n#IS9!KEK4bdTX`c|B!9Vho#N^8_u#yI$8wxNmFd}wNWr|4cB4dBIHb_)AOoh zam%>_Rx$q?koSs2-1Ll zZ-PBqXuuvwoPf;^E%&iZIfguRJ_Ay-;cLVyEcyyc&%0*Co4E1<>_4sA$j=GRkdATw zV*=nvzLox+4RE$bh4^lvvO&D2A9WZbe027e7F>`M{tXGv7-pRr{Z97BCTZJ4asdB{ zx#xbxlp)n=UbE!}E@j{404Xnd5R3A#(zk3d(r3}7dyfFLYov&6XFygGS^5ecpy~zr z;40fo;;U9>Jq38mHU*4dDfMe(gzQjCz_z^}z}y3X`rjY&%6*|mH1DZ8FwrAE z63_8shIwknZ2x=)pj{$IJY808^vs4d#-Ft%Tr3j3tE`UUgmF)%{}DU7u5$X&kZ*Fk z+Nk}A2YDK`1NIYO0j~uD(g&XLnir5F>i1MmoylO`k0MKZA%FujT7;p6>Wm;!l$TXD z8VvcVox*}K?2qPVN)G3Pbo@kK|03B2whcrJ?tRciJWJh|?IMHh%ud6cz3#%QJp~Xg z7BJ`6PhsKJwU2M%Y+B^DolZTeEOfojDqi33%${E5fWwt|9AoRlS_x?21adbK7iTHMLBP2 z0=iPk(}y5{%SX}MHxvM*=Pmt=17ZEZIVXA@zi?sTj|qXth)*(6rASWwCTGA;*{|t> zC5*)RD-)P*Bl9oqY~aym;N;(*;2A#wSin%U^PD;0win6dk2?T=>syHQ996#nMHm(j zc~q}skl-5dW-aD-nCHnu-pfmJ%|^Ed6-k4%;kQySio^NmeqDT7obQZ;JhYMgCC1!7 zD-dva4y+VQ2Z$SpEG4i3+A1ma)ns6pA+Rg&LaDz3&}xAK;aQ>Tz)6D`>El0u=>Zg}C?_vch%Ouj zI5dKv(L>uPp&nWwb!j=1diCw-`U!#o?H6W}CMZV@KAMtZc;z|NO*wYtRq*L0c!E#W zq4qNj;`I>mjp#hoQG>k@s+sJR2>7UKCNl-1f?Z|i868-&k9N7|r?CEmy%P?Un|c1# z4DrPY5)Kz2QQI+`lVi^Tc`v4)&&)@a=kn>*E<-_3axAc3nxaFGI zz!!F^My~gXh_z~FCnwm^{{I-!`bv26!JHFKxc0vmk^hqj3Lt5^e<2Qh!>f|ns>P>& z*1R9|Ed#O>XJl^9N{1lBfc-o;fGMnp9Ez!=lf}l|Bd}YOEjECkW^@QgqOM4={eMn0 zY3n6W)GI~o8(eitCBQbe7&F7tTRS|d%xENBr{s@(PT zxAe26xR<)9|3A4mt6v$+nv0bT14P0!Up_?0YROE}W8h1qiir79)tN^ce3OOn{Sbpt zrYvF#Dr1v-w6{nJq0XQ^YIQrdE2{y zV6r|H2SzGHu!yRp5CD7>t>OTn;k{tyu4nWdE3Pt37#9rlG`!kZ4cpyskeU2;FKYW6 zr@q=9m`{M~!!NVB2hs+aQ`tkn$srrS=PG`L>l1Kh_bo83y6UOo18}j3%1qD`*f{9c zWQ;SwKZz8$X8i*UbTdYP=SsFXkrU=bSMBNlBk3x`+G?6MP~6>JOR?Zia0&z{QYcm& zLUAv}39iN6i(7Fk?hvdv#ogWY%k#azPOg(&yJuu~?wOpqH-SJnd>-s@9Y%}<*?+{2 zmVL=kKOlNm^4t4DFVaca^!OIZK;H|dkI+Ky^e$gB6>hGA;rK#8ei@=an>q|$1yL~Y z(|6y)F)|BN_LY!&kgyKxvM`B%)Am~{TwnTQh>?<5UOtxy zYbn0+fd!5^r^i6XaVt|cyynNMJ^rEkIf<~)gQgRVHypAKL z6H^6a)qqWR5%kqh%w>HLTfGT|I>G zUM^AMytA?t%qbLQL(L>-zM+MoBSK1Gy`(o^;qq-FC9gjq$iAcmlxPu#ZEBawvQ|vk zXCz6Sl&TpXv>)Cn^%0!ZQ>*_5hEdzkedOzjQ4(B%6+i@|!t5Ud_D9j_c_L}^`KM@W z(GaIzXmNee;nmw8Xq9wP^S63t>_`yu9q?$=>ol13#ve1AWe%p}A7@@ZYIXwKzR>d`;a5;?N z@}qlP?Wi&H**0j&o28kY&3tb6NLbG@!$0~;1mI=iRc}QQjjPQJlgLh?k%ucSY8+H~ z^RCMk7M{z|F5xN4vPg%6CqLjC-k?mCN#ZhbAWe<7&^l+}34w=xwFzrbUyG-P4ZdhClREE zAF7x#f6M3wU0;DL^G#e*!?S%^(o0AWl=R6%)ecaY$uHRWr-Jy#WfLrzTg8LZhMWr1 zn3w*NrmO06@)?q+Ym0zPm@kJI_ogS13B)r7L44liyy|s?mie5#Y2ihT00N?nA&{1n z0PoZ_8B|uw$b=wkgiysbcDeWne?tQ028qDBNrPY|YbXs|2$l6>;Uy^5Nwv;jjQ;qQ zFZx~_9gct%c5gOYdHi82AY9dN0yM)Po;JjjmR2%{X_=pU0hPUD1`xDo_k#%2M?kVS zL;_PwE&1WM2~SZ41~X`u1of)!;HB$X9V;sX8@#?+5_6gp8(YZ1Jd@TMR?Ui2`%Z%s(*)T?1nMx zfllkNck;hE=A@fu^xx=-S>=1Dg%7BhGmF$fK?I({lGCx)(`?V(vZ45C;r3(9dwxX$ z;TkZluonRcsFY2aTe~@|`JJG$k%PPhc1mW{ zZPZ8V4|I?Zf(U6QKzugLyi*z0|FeNxI!F3Y&hnnG$W+xW0>R0eq>5{Tk$?Tqw37eE zvIeGF)64mQTi-PD4&Y%ENF(r01?n2-kMVZNX0-_Y?SN@avlu`ymN5Ww^=0Rc^P}Wd z--g*^RPlcke4Y+xc4oX24s+fW00O2qZ%nbr;3m<{`L@+z|w5-SRu#24*NdwGH zeP9}hRzV>+(CC((m}Ay?`Ub4*&vFbEK5#gn?f#BQ0tiUyS>V&Ng+ zPwxQ{a;L+5j?^-LVmd86M9q@<+W?Yn(->?K1TGi%nr=w6Z%FXR*!15E03)zRLG?Vz zY5O_Bmg(;s1w1(+0zTs?phS`MtmxZOM!?h;)BHeE_qY{L`e$JEUi{QaQZt&-^K+6EvKq}8{)40j&qfiWYtlq&C*3Mgw?7o4@&rPFY)oVjir{6(m5 zFMnT8P(xY&$^(uzC9P*&%RoEPbOS%~o;3JQ*4G}R3J6B3Ac1F1;q`3!C^EfZYDm~5 z1tASq`Nfi$U^7^}!PW4m2=U)#<2nPgSN@tz#qOCVi}LR2p3HgzT^fpZ3kc{qR1 zLA2#tAPl~-n8e00o8;S-&M=CDii(hAx4_rugaW{wv9LGDSm5}SvGpdv$YaTnMsO|a zScGIoCjAT$<#4|5MKTWwYbUp5I9EKvbaLqc8q}!sPQ@sH(iW|rsU}JzyZ`)g&gp$Y z;$i|Rv;+^+GC>`4cTSNW`lBz3=s?7yk}dp=D-WP~?@40IR7fbp2sr?bf>KjH{Pa{P z_9g)9Juo86kd@PP%2X6v`1Sj?6c z=LMhMay#n9+B+*TN<;0%|-tw|*!_>R~glsnt;>{CIVVUUt3(wocAbEvu z2C1XE@OYJ7x1;Ps-oqG|I$lRE#%M~k=g|miiLbvgNuJa;1S@OG2c->Eb59H-@Ac7P z?mm!Nw%$UPv_is?7#Q-WRL1*AL|wy#hUqwhFN4)kII`--yp6C zE`L{FSlaeU423ljqDu=A_5(ml)%Xc4e_@4_Om!V^+`{qF1$9R;`vUm!jaRWO07s57 zTX=%tP?w{TpLq1p(O;F)hXn6(IaETCHe0?;QG$~6&|NfzAl4?FP(~XbnqqX7HCu(- z9Tc9_(moc>Sh!RNm~C!l$Urayra>#Y2XRPDUgeeo-O&5|mg4RwywP3yzF=F5lR>v2 zq|h=}%N_#*;`CJkL-3=UKK>ip?j|tMj}jeVCzR?F0!U2~PF3C_g+fi}K}-QHjzyDm z&_>2r-q@bw-fH&NA>&mHGeB1SSP-m3`!bXj#D;M75!Y;fP#wy^Md(V2w60n=mV^9F2SJ+-}$7+}3pnyy@3 zJg|-ed_8qH=F;5;rrsKC8I3v7*Jt;+{!Br7n6QIe%07EvZ&>i%g9|bTNR-MD8v}uZ zleZsE!=13Ykh6)j58>SrU$4dW;=)6gochFrq?2Ty=7hn}+I6+{P z#^=8*5QF*5A=woNUwJNmycNV<*N16o`QU}g8wqQC9hFz562>GR+f+Q|Fb{NeV9D%e z&)CzbBfMlV5w#+wBrDF&yT`=U*3$gE-m29u6W0v#=a~QLeLdEhU<0%hux81 z!FT<#2Z!8wV$Pf9r^Xs@M8d^qNS&_Tfn$6=D>LMxF$LG@2}JJnA%xHf|Lk_499VbQ z$R`E{^WMinFLdKL?{!(0doz>GUqto29hj@<4oB93hZE~f8RpD6#~f%Xg8A*{7h_zAu4qvAb6UYzzX?6G7SYm51fDmKW9(j+aJQ&~5;|0?u~=|6Xzc!x&*l7# z4PM3J7x5f!YULw$sxThhl{{{j?P6$m6it_LBgK$0^)_+pKWVvCtT06?{NP4kYit1T zbpM^~y$e9o9Ka!z3QqtH@+=hMTS@#8YB^bdIq?<5vP`Npe#fwCLO*!u^29szn#s4c zIY=w|z&jKwEjv^h<|#Nuv{q#a=c${1zZ!~)eK}IAk@cyO3qqM~%)AYZz*|uzYY^wF z^xheyjtFNdbKuhrF2u4_b+o2+2{e(i?N~eQ-}TZ$%{VLf;&Bnaw%lFn4&M%lZ+KIt{{4@;JMH%1^e-RvLU;I9p${En2qk&hYvoL*XmQM+CxKEPHp)4 zcux0qxS)$xrncmCca8M)O|JFM2{m-FeIEtzlrX!zAXSlKb#2kU9GyXE<4yJ~zx-=~ zsBfJmB=Xz_Pjhd|>&qhPdU2t~y-knMeSbya_^FQr@|xf{$jW0N7lAr=yh?(v^3T@j zkB3cK|F>UUT^?t{#^adAfHv4(x!1zq69-QZA~8LDO`NTxfQKfrZhCTgm;8=ts81Pn z0Wg@39q#~v@1Xpy{qr!;6LM_|$idC|n%RYU+HG95-)oG0Flc<(GP`CcVC99E5K~CK z?JVP;mc@RGf#L`d9W@p>?Kf7XvSNl#l!TFIjWGf>gT>@tdr+=Dy$kVonPOo=DCL~RxCzH$Z;7o8 z&@y>Y&oUWrB~5n~_IA&jq-F9|yIkEh-JLtFd~=h{O*cDAe2K2_rb3U#IQy4efun9H zkN5y(T`p2}zXG`R8Ju@O3)a(J&EWQYSv5{>$umxNlfr!U1tJ57bGkcMlDct@2G-w* zy4D>mV%YtcWpv4XhbvLeaa0S>*$}T@^#-=be8`@+r!09qYj7s7)wDp!GSf=>98Eby znCB-y;GfY}F?R#9F?Z`%F^imWArBp~s4j%od3$J)gJ!A|3|}ki2&|n14tbrl!&x?x94*>VmUA8S(+Y0UPN19oROT5XP;AqA zKjB@5@smci=x5ynlVYB22a2oJPd_qiX7f;w$!ib3IdLi&CcZJfdg+q%{}Wm9XB?&W z;k7Ge?5@Grw(IQ5%F^>cwZu`J7e1%ys=6TDe8rh!mlmY42-m7TbQtNZ1|un~m=$sES=W#dBD z?T-%X`AWadE!}1o>~1uVJS4~xv1g6q^iy>4qk^=D1MxDKc%xn#3}hI~&TLBZTMGOv5|+nP!Up-B7zQ z7T)PeBUS3oM|%EIC696TcNPqJNy%wUcFk!Fc{9MtdNzw!DVR!YMa^EBC5)4iUSRR= z9qIF0EB8GwZ1q0bWBLZ6K;Ft&Af4sasKBsgnVh&D@#Rva^H|l~bEBKhb(Y1CX=^QP zkQ@mpXjkV2OQrf30Uc$!w)R2KI*_Sg)4;(WIGfcC+#Fk<*k%r9BB|lNHa?GqwE|oI zLcN~e|Z1n2Tv+cE?xNU z=ucpg;HRZDL>0Cy12QPG&(KrIANXBULF6m!!9zA-GR;`Cn-3lIP~l&dpGFP^5O@E5 zN7r1*fOzU7^Nv8emuXCxp|@Ol2ZSQA#9An+5?N#*9lu)Yc?76k73MAl7c%d^jP7H? zA>&5##D?VhhQNr~kjdl$`Jk2-Hk7M&%w0YY6r}KkezE6u&AnWP%$5 zVQ4hv3%cf78svZx(y|iW&2GU7C7Z}S7<3(EtVZuLn1S!EX9Tn*wCHxz7z>;Xd)H*3 zn3-AV3=BY)cr~9F~=>c^y-QkiW1?IW9ocUP%V)5Mi-!V&t=$vgN4fpXiD^}YE?O#?CWpN+y7%Bhs~_UDr-&cc=r zm$RkXwgz%5uZ<@UA0j7{e?F?k;vQD3<`R+QeYPHz8r{s@_!YdMin%llaLLsru57RRoao&D}LSf6fyF5|4jwaj)J%^XGt9m6!p?+_z^Nm3YHjc;W9{|4Bjod zlhyZA!Sd`pOX)~o>CN&u>FGg!IJ;U5hAwW|^l|5RV4S7e?!~U8Zub2i;}}@X!M-Il z`O0F~kSk#US6}Evp<;6Dx?`N3fVbX=jcKV^XH1U(8elbe6a4Kg6dD zT@5t@CI8K*bMV@@y#up|zN=s2jQSShd|E-*l)EsKkP*9_OEt2rONP*LYMi&S*vR;1 z^6S1MR+Vx)ey4C08hS!%o)WMtDUT7c5}RkeFdy=a0z>~N<3h`WDNqF(f6DwM$NMW) zd~BSfA))2fB&X&`(XI0Z*gwg>(iRm%3h zDotYH5Kjsxq4a7;6Hj=qy9N4qvW6Zl@${}@@C^R`0Ab$7;}cI9$rIn{l6?8`J2P{B zW%!9|NF(?V-nNyy1M2#1{KfFN4|G>d!E0#8V%eILm3E-#cogW@?AC2+PyXm!^far; zXDHV4w&!a6rw)F4g@SXp#$y9C58$zqK@leW8cZyQ_Z?z%6Op55Wj2k#v9hquot%SAG>M_g#TElF8bXYD zi8emJF9H~95P=1=cCziJEZL!{Un@VfIKWG%Xvl@hRgXOe;6l0Mw&bD zd|4k?mW9B2izP@drTpUX&Kd4-!gWj#&)_Rj-4inC;R-2;0+~0dem1J{b3~UnF>8lk zfDzp(E0Q|ttU-7wcyXoPIBW`(c0|xc{1b22Bg$ZNAah6EXXq(OLC@g z_`lx_kmni0pl5Mw4{F0F9e;Oz8U|Ua{XVIV4@Zmp+L+F&M!YYod%)ZS#IkoJ`tatR zyCAtfcUn)6Q#j8Q2FPt5Kg9hv2V_o;P5Ph8;hK_8m|dQ4bQWr*CJOe_(WM5_MHT_% z*2+KagXu?Vz7w(!d_I%-x&8cRlfSvBg5G zzxJBE_GtxpenQUWupZgnq7?LRY~+{J=wk_><2;IMclmoK$SGsA}7e=^k=?)0%zcg7sed{)?L8`4M-(!HIO%$#0ve- z0Pt`Kq`tTY^=#e8=A$C!M+58IwloKSZ9&pt+{2}ZDQ8Auoi2i@>v&LrzXuIJd*gb2? zJrL2-oeeHZ5~P4(a}fmSAP0PrjTtg;Arhq2uHQI&1JPX72Cs;h+uEiq(LDt*b+q?A zi0!LDMPhgTE^N!=CD?t*n7oF-OHWh5@(3Fg0l&dE1Sn~3jIzVT?ir7QfFFu*%>%`J z`c)m45b$4E+U9RwUb$~5Q-slBix}WfrUfuQA+E&qoj$G@)4*1caDCR4YxoVLyLe9(E_O8|M2U9S%!7pYw9+ta$nmK9Z>N1v9hLx!; z_$}vJMl6<98+NHJ*dk%Oj~`DT=x%rtl>H$T_|jAco+#tpD_fMy(XeB$_)jjXDGhXo z|C+71fqz7GPP*3M8&|d8q~VuO#bgTpqlA7Q4UnQI{&u9mg6s)!asPnO zlGikOTCoX`9>Dy?@QaWJ-oF{KxbIZZl;KxHbsB@Y73`0)X&vy=e|7EZL1tya9<)>Q z2BPDhAHX8kqZjQMD4`xpjNq|w=bC?-sjo6j=*E0jiMXfmbNo}QZ``ng5b+jJOnk1C zHjBLlD~*->Db+gSWbsd#2Cd7r#wH+mtR%_DA+v!*438M*qlbQ#eyA6EtV=+>k=4GiO-WqPh(h z0=hGI-uLkRY`jDWZY+v>Ech7nt}zFdHxQCG@84}ZI|fSa|2@GC7Wf)aG|?(EU1O2y zTt6LCCBqbR%$@tT&tOM1l%_a_2tPG`T7@63$93mOCEXZUqa_>Zuy_9 zZHH`d-{I$Nhu-(cC)*AUc+l$IkVg`VDfHn;PMSfvwY@YkHEO?`0>*OG# zXm>chfUgHBmd?nN&_Kco`r<_b*+n|kr&$s8t~RVEOYgtuYE3{^*=0)V5p8J+*1n^! zs&6{|BXzY)|4rg1+U=VN_Z7d`2Od8C4_jZSP6^0bC zwoW~-?h6(xpSPXKX&|0upMfrNb>hWwnI=^qx_=sg6dVBphpSgl4c$ZgGEX$-AmEPg})_|_#ZwwqIk462(;A5 zQC|8MW2JMJkW$0Vt)kMLDj-&nHKLMZ3M_5Mp}+q=ms=XxF_SfP=5Kdu@R$IfA&jyT zU41G>&PBsefg828*vxffV>^X9D1MN@{Z9fTwmFLM3DkV`fcrcLJPu+@{q(vnAb^=a zS%}nrafJ!LV7RmHEy-7W!-BH8bbcSHi6Lj^G$o?9;_36r?^=Nm^+F{dkJRg{T)DEO z1-3m`OsLmjp3s+qe>iqe`vN`f_xswt?Qs;dSvRv(^M{;A<@p=GRQQc4!W-tqV(o&C zH&l){qEFPxJG{d>ZB22Evbn*p5}{FiIkMNhGrS>sCRem=`+G@m=Cr&9q0B_-yH)mi z_H_2|Rsv;EkZZ_dSIS2^xuC94>d?3E-SzO-88 z0Bq_Q`tXs6ap}vQ3}h>as}Y(oKc^X&aM@pQ!QF^-DB@*TyGlQ&9f{oGc4!FrL?S9B zk698`?#l7$o7=u0V5xUK+i3lEs{ezxuI)=8^_PkH_vMmu-mZopDEQ7M%1X>SVp;%S zZG=LH?Z<(WfxCs`I`>dr0C^c!I+0R#h)LdYxUK)7pg@~i2u>Ndj2VS-@V6XuhtRUg z>Wk1tb>^pKSq!;Xi4vL>yAEvMbX!ehBvh83N_m?EEaH0&1@o_1?%8A`!#?VEa}uFG zJTq%;p?vt85A-752zhn5VS%XTJdfk<*-lo7D|88tbOdwuobCL<@G-(@*l$Ep z#_@^ZCx;h)V8BhIl+2Cyc6}XUvYzpoGPd($b^j@FqhX-piX$_QkW=>bB~D-1MJad^ z#?^TA$F2ApMae{_zoE~w-%5*gcE^$JI)yXI$93gH0fs|cX{n4Xj5bB5v&WK;Fx2`= zea#R1!1n>#V~>oZUU^yvP-4p(U}~R)ibPszCyHk4el@pjiBH@4FwQP5y}6Aq@M7%C zp@(oan#T>TZtV26@U0YLf^z9VELpFI-3YGOF0dEAuNw1p9;V36wfEyKbZOq#GJGe@ zYp~{{_unq?wWrIYbC%mB_h->X$fI_6$FrmC!1KZMk9r+AG4JLWCFh#%)bL({-SKl~bnGc9z56ly>3EMlC6-3UF}$M% zYR4ssE7R!&dsA%$W+nXHEc$&JPSUXi#Yi{HB)hzUV@ND2Peh z1L{P&I^Dj9XyaP!y=RAO6GAQTtO5VUu8xu`A@+is4=g)xQ+;m7d?1Uq()pPaX`E;@ z*c7-_j%ZgO><95@-MaFl)$$pG*SztRo^4Qz_*2E=h4B5EZ1R2@YTc9Md@O8+yN(h^?PR6DgMq4}R=rGHIu z*`(SPQD)zc>^9@0%*zbr!|kVVk#U?0X1xkwV$Gk05@kwRGwRX7rjKZ}(Bu*-EjP1g zCc`;+U22JtFK-?OcP#mqlwb>D0}It(wb;LE{iO41+o-ma`Zq^n)<+rxugTTCzg#TD zP4d#I3X`T3cwFvnmK3u3F36O+h#KQ0K@O?Hk5tB`{H8$)Mw_PVzi`)X>gn$S?M?5T zh!k##n}-o*%md0@UBlmD$dh4v_+!`vc!!_1uJ=%9=W-sK&^Q17HgEEm3agtF69w%$ z^z(WTC!N?+7zRccXNA7nW{*J$hQ24I!52ZJ?%*Veh|Eu_ulPt#CNzZWJxUCTSke`Q zNUIX99Uly&K5I!G(-|;-b3Hqv*wl17&H~fBoxU&1LhZ&=!^NYX`wtnQMz%ZEWf(dQ-8xD9mm~!- z0Kd2)qa6gDhJGDG!AlZOh3!{Umxz5X?~Re$`;nc%&aiJ>kCC85E*>O@b^7~A#$7JT z@XlNwNk@F7j$9;a0YsLmYm!685Gk*$ZB^4(hrom(M3y+GC>MMuM@6emMtOta3v!C6 zOxeQtKM9Wgs?w2JK{@M;B5wnkvbpPw~D4s|B=x@#|AHhO%Gjm?5T*` zd)?xDDsy-0v6`L#1v~pn%NBAOQeZOHJ5ZQ|o?>4r-=E}6O&orhe0@L*Qvo7m1X8^p z@YYYoIW%%qCL5_)5hC&b=tlIKHB+_B54YdyNg-@VBO26v#bln*q~J)9eq)H_OYQ!t zaA<;XLHOQ`u5dNv1@Gp^dT_+hc&JlcW5rN|q}H#->M9z-gP=(ELAej{^eXCc-2swN zZDD*t}K zd58jvD=7w+o>~sRei8jMkQdx#Z2<0eLtu^@*+lq=uUGQ@6TV)U?N~7JJ*QX zv#|xF&W@5y+y|a7SJ(-w;%v0yDYJ>!rnwj7mq?SbC<_Stp_H6m0JM>L#wfuJ1k51Q zfUt{0-5~_b?hB9R)kO-Rv@1=FOWso0Z`-V*B-_gyO7y4wMF`b zyrfCTK64xsenimixWe6ticXji{!rBZS~708H~Eoyg?B~|NX50wmnVv|@}2e1o>)ZI zXUTu3qKt^FF&nMl{~l@rh_H@EwKIPoJNYYp3W%CRGY<+cVp7jH{NPA3BSiZ-|JSdG z#HdaFqobE!znD%D7uxaA*XV1aQG5!YcS7Yo%vHo%3&L&1XXVzbdk|&0g395wu3V*09Opnkk)G@*> z2IhOMLdZs!?gU*$-M@y}SNN#Kp{3+G_35m8dPq=ADKUYxH1l*AsicEVRoV|V^Kg$! zf*$35XMS#E>X^jHCliVxj6xpWwWV}FEsJ0a4&nOvbva4*>HLc~p2-Td#Z1du&a_R4 z1Y()_Np0GeqQ~Zl?lKP>z3WeiAjgew4hDBMRukOA#xjB-TaZeXC$1{RN8!o?-}QPM zLqt{m-y}|Yyma9BTjYfyytB6BcZ(f=Vj01;b8%J$?e}`tL%&|lIn8^x2PWj{5<zYv(rDoU(v}qN0RWHbj_cG3=E7YqFh5c- zI5;G5Q{rRz@1KeV?WuXOo4&CzFc_imKo+?UvZj4BK@6bsxcnL+2 zd$P?A=_4T>vw*jWKnynF@?RASI9myyFc^zA1AWC5ef#2)DHVTNq?{;-i999+OywEL zD-7Z|${_2fg`KwO<4);I$=cKj#mNcG7z0xFmXmN@?%L|j1e@bU*bFW!X!kSNm3(oE}lJvGueePH8HgI zeK*(7n`=Givg-nK&?#wqf~nGKlMgN@s_5>pgSYuW!~Jxat?-=F7wXS>9B7$SO#QNk z^a6~nND!}1ppDu?Vw|mUEjv!2_V~-<NSUtx6C2mUtuUciKMBRh z2t$9Kq@XfKfSd|GZ8B?1PfQ!?Y%)TLba!aq{O6J`BKCeFXaUk)Twrl!OQ45GZQu{R zb1(XR{l^RJDM6Hrqf5)1l_{9q~Rc3+u3~%|7 z=iOadaPOzHzx7jZV(>%AuZ~}wL@%xP92+!0etiEYA^hj$M-6?kQaetajLKh)D$R{- z%>oKvg5pjmj4-yYgs}I&6hreRyzrkz(ZxJSOK~E-?VdZ6pt!Flkf^X4zV5x;fj24Ek6jl|Iy#2?x| zp)v@Q^Nj`t-qb3y`0I2!W0LNaa@`&ZJt^hfBma4{pzZcu$S4;Xq5qgC+Lckhgt7YE zSB)#BM^~YKD@H&+w;K`=Ljby8A<{Butv~6)- zY4YZ#ZV0_|a%&=_iv4k7M;SZ9QDAvdf>RlEvz&CFzoq`UQS$emFNL*O*&hsyd-m%P z|B2Y!OaIs_=Dr(}OK4VDDt!f;ij`Uaet(}4 zsv0ohm?;62Inh;JNQnWS%VhwQZhFvjAu8RRoBZEl-?e51YQN8rHlwM0whB0LuwCC& zx4Nc}JDW|9QF!TzkOiv@7Fh20e^0C={_gvi8ExXtAaw6xg`$HzR^-RUT7Q9o%1+~U z%qGI8?2A->_l0z*o$hZbeE%O>^T*4{rw_li7SxIFPr5u3*EVw(+Z$^q6JC&$|Cu(W zVTh|hE*FEAC20I5{EmW$?8Gng;wPtRjyiw-qYoE+F@icME?pNsC-2bAuv-+4gydi8 zj}UEL>c8q4Y57K`a9d&85AQTldJqc95II=-M!MJxIq2>(?l#%hCG0gdH_|_FWb<_v z7H17VC)eLd`zoJ`@cEPvk|eEZw|ImJMJYxlL(waa_8R5RaXz#wgBQHgMJT{4o*ZS~%zKBJ4RqQJ0&lY$Er<0gc$ z)8cZ;FKz9z|5@a1gAzq0wikL zL4G~Qw;b>7I+iWMZx^_EUwouD0&L&%_bWOM>EPN4c|ZYP7d6diZ0D$qnZro!vCb!o zVB^ZZF>d4pc+$UN-jm%IH)jO-F@N59uo_omzTfjf90V>yd3JMFfQt7DpI;~*inZur z89x%Vn&QAH&8P~<{d({_WQ%6_xvUk#HglP5ta>Zf{Q1gywsV_i`qAqBpm?aMQ$e`V zB(VI~V{zHy^0e<8iY)~W(-hEpMqnauu~h!iim2GnceikJXAEI%~Ab>s=-b=?V;t!=`pc|90`Mi5}{!iHMPcVpClw26|Vf~jmodR zVt_0x(;aq*C&8mzW;iKIn`R!q+nZ*U8}Z%0yzZo2WnX@C9Ig)i$=@<_Ng*E`Wai|L ziIwAgnY&*I=zqiMA9PM8p`y_Y01$5KzAAT;(u}G*I#mz+|EJpZtIlL%DjH>X?%hY^ z51bP2b&Xv&XSe(Y`6Ibw#fY`Q93sN%9HNT zE*TpI#QvLPS!Vqpahq4gE-{B;FMXBQxXyEH>2y&Ot<%4*^CnQVWp+^UarnP0o=*S$ z!tER`4aQgc@Yq7VD|;*TIh`%Da-3J3i<%XwOo@6prDJ0~ngI*N>kTK0x*HQILhv48 zJx}k&8*Ad4o@AZ9^V(>H4ktbLvR@b$)q+*^$Vrv4?9 zkI)V`%fl+y2R)h&?*Sr-x)M~M?;T0pR*Zh@Id5fjjssIQyAp4oQ^ejZ2D9HZD8%1B zQ(T+;)84!?`Zhh`$AN#naf$?f{7R$0ePz^=DPfQyceBx7v;9!*Hu+|gb)Gp`jCG)C zpE?uzP4wZ)DAKsL+~)6I=dNVy z;*ZxRiH{UF8_+z!;PKa9N*mD&HW%A|@6(5BxlD;fXiZ;(@m4E`)AL>{hz2|Jt$NF> zoAL8Z900e7CdHTX#g#nrUqvuheP5XGDwN?Gg3>^oF*F^)Knpab2mVH-qt!8`5q)`< zp-oeeKuMTUBW$9|6ZUFR}IzTgG_C2_7#K;1dQOOTazl%bkNNb3{Okr!QA{|c#D z?O)y^{iZ?N@@OV-J9Q&paK$X4R*FoP8~5FRPTs@YTmo})|6327-Lp8g>CjBrcwo#r zx89Cel)9Bx^QJdx!I=KXff8kF-zfJ4Em};?XIhE0O>kEg#yWDvA0Xp9flV(SV<*3$ zjn@U;5Qq4SJYoG>YKFJgIUFsG#CUOe`#_$2p$>GFSfny!;~HwJov8_zPilN z4C@@=2BYK$JGByIJ0oWX!oT+4Ll1kSC+y4|AxxmyiH^15fxU^sx1D=~7dYu9C-aeU zQneE&nshdfrom~DfW+0F)7BMPBA3ju(aD7wfxSnl16ht$*~oy^%9)9G zb}4(5+KR!$eWs&4WzNM~_r3Qjh)1GM>2k~EWw|<9EeR+nvGYPlmII?>94MB*;Tp6G zeww85>j--NPH>m>N-C5RJ@LBcvZeW##!g$xFNk06qqMl_I(t4{k$7`nmt8bi^(@#)pm z^S8D&>PpEGUxUWJ$JfN@)lQyQ9N5XN2d^34Pp4^t@1LbRUe-mwB@_Ay%G}I)KPlt1 zU7U-R{^%w;+oe0F%aT};yL@QIEv?eH1h zWMAu0VED*jn@u;|w5F$??vSGi8fwCsr89hB-L-kR)wODY&1WviJ@?_sB1*l(hea#T zsiVEumV52B0%NR+i*sh0?@0v{JmWrUB4lK59?V-ii95fSm9YJf#vSkK1a^d}_tU$& z01F0Nq~uD$$kC>1Z)s@LJQO+bWa^{zZ#(wyfj8L}i4L8kDP~$R+)-C2W>$)fhc%{a zsSaz!f`EZ8mcF;aRAWquwO36Mv#7he&*%iMPA;qqcMb3IBgw3yuTJWw3mWN|t;zZ_?B(k+t_TUO^lz6CbEJ>dYByQXFwhbNDbglDN$%#QXb zsT3Mq@}6O7^bYRjCw)Q5n9_e_WV5QuPqFh8?yX#vIiiMp$$eha5nq9CKVsR$L@UE? zT+Ds8Zmw+f?O!O>wb%bda9dX8R4?^BwwMV!y(~Y~CE31Y>R``JkQ`0jTwSSY7tKvz zTlYMAK4bQ|M4LwF0ndbnD{~m84<1`MH;Q&5^WOf=^d4Dx|7cv@N`%RvOR@O4D1jAr zV5=LDYFVr_H<92+Fh3E_8<1{$q+DWcokygglA)TbL+;%zAPw{Qlh-m z?&Z1h1GEO(1{hq8y&s{(P`216%p3>@4pZ8sAPKB~A{nbuV6iybBQJe(_e0ue`T5Vt z6Xh>LxeFgggOKfEncqwj?_$u@J*R+a0_*l^oj!QV9xp5~u7G8Ao3Bm0@85b~Ggk*xuQhdlc7o}QF zN2T{8pduw~fj_TYe?SKDR*{|h2jcX&Q`6S((F(1??^(el&_k$k_$4JpD>fs%#?w|A2Ua(H_Gr-jQdxSZg_&D~KFT zPT<426z|*%JSa>R8d><}bx~^WrCw2=Q&BRcO$zwJSfT=0I9x1T>)%h zbFZUS)U6+^Wql;y*Xw<=ME{Sas}76e`}$H+(%m54DIu|RBi&u1bV{?*-6G8*A&64a z-O|!2-QBgz#=GC=_x>^S%-(x)W}cb7XFlf~b|-OqaVFvmA8LeJKdZuwVOxO9eF%lV zR2t`1?~XqzLGiT;hpElyxw9>1WmM!&GPEbP7j0<@h2T}9Kp&bEgLTf>)6;}+N-8T$ z9X%?Av!#|IYG^8fawGN6hDVM;Oon43{W_|y6@1$5Z`?Ld6bfA6vq$ONudpE)eyX??)70E#(9L482H4OXqsbat$26rPwuli77SLBQi{Bye_^d)g zrKMflE|{f>vBP{4zI2R(NMTI$(Wza=Eq9d#^pV)dZMhe1(<<~jtX%jrslK_9-4P|W zy6Wz?v;mk_HK>h>=-q5^*6hWqS*QWbgrdIOb@pX-_GXi&HaRP)zqH&{uOU)5vm<8d z!_zalvL{+df|+#G6foplXkOV%UsGy-RzwpBZ9E$O^N&=7b&3rqh8f@(4=A6_QIWF5 z4{2s3;Cj!In$7id#3&<2MWLMX_ULX*Jt@%jCyrq1Lne!0Dop+3#O{Pf-+Gd&Vi*s~ z<~gS-2Ic8ZlUD=<_6|v6(_O;@1+~-&b0T%*_sD6WJkpA zR2c4HI&``Dxhk8Mq|J>o>#!GVH?J_lC|h-wr#(y}0`|g|nT_SXYvf_kx7iBzDMHY* zN{YoE8vXq|^b``VJR^wLg4*7f~SYlp7FLO7RGr*=zm z$-)L_FvJ(qqR5{rBm8)WYN@mA@E|-2An;MC0_gb2zKk6Mxbyz*b5CuWDOtJ77_lIw zGkx-P)Tzv|?H&pOu$ov?n_*W?Fs;+G0U^|8}Vm zFH>JHjF4+LUoSZp(p9u*b+%cO-@EE`?TH98)2oyP5+(h|w_n7FC5NdgJj)U#g~i$F zJ9_K)uLa{{Oz>p%B>z%d^iyB+7x&K-krCxtgf6-A#Nrry;4oBH7{!;W_T=g#Jp0R* zu8*CzOGjKwJliiCAw+za=^@n}{l2axoskx@CR+4?!<#ESUzbCzJxs_?rfbl4hE*cx$zl*vt5s6G8%!e2(tP`pE3 z9Mnsmyalyb0i%n8c$25tD~(=oGe3(LpHq`9$D_uAt_tWx@WBHuGnK0>B^6=M4j)5g z`c~L={xz9Yo~QBtbSo$meN%HzQd7N;2#}E@tjaFZo?iP}q+L8%?O1tP5={D+y>6(5 zyFh!I&1h(Sys`esYSk{Xnu*Ea7OAb=x*aX}S*q{fV_fOR21=$TKfyZMC;7e{x&qvm z3hAPC(rnkyQ|z8XX(pwgFby$y@$ym~gaw-AK8>eu4VizBpr|#X5B7pOtHbUKw}!k> zYc8rimHX1)qX5TlY#Vv#nX#$R?UWylM)P|46k|Ciz>^t?1t z_Bb-UJU2NJame=X1~?cHsh>4^Qu3gVBOkobzu&aY??Ndd$TAdoM?M&#G7D+WFXOF7 zgf#e}CoI>i4H@a|&V+a>GInC`oysUm>~gH)vaHS$`7d5jbZjRwP##DB5!B>wOt*54 zl6oyHvprVv2=t zmtDmQ$nm?9-ODqQ(K5292?fY~2`7hDvIFeM(uWNO{l35EQ#DXbCP_P&K18pKYeUgg zGO>I0ws*@-UJ3d}U57}55e-hutV?t_od=#S`Jk&*5G5~RFHTl3o~KW=^v9EHC-#F5 zQNwpNL&Y7AUu5mMIz+ZR)^Q0Yy5t5?L(Tk^ag@Ja6JMPYSK7(Sm#lKIu?Xd+aR?^h z#M2QLQ#20Fr5k8Cs5M2UhmFKF_k@4%H!5M2QqjTCRns9PQsvuwO%)Emd|ozwL`A^K zv2mcmoM}W%LvqTMzns3JQO#VhYo!r&=u_KnhM_t{F;vY`!>kkG*6Le!k-bA67<^%T zBMHMY?b~sw1L=m!8@FlDVpjY5*zbGk}6cEIYPJ74VLw9t(z z_WGE4zUIvF%HaMmKo+8b`@n{QkeMV4p`FB`{?HdwR?KU?D_3K~X_iL&T>Wu)a~HLy z_ziE4MwGfacf8K1k^@4M6DP|DB3K)z(??M5aF`@J$4>f_w9OtAwy7R)s~2;V$Le>c zbrETA+)YFm+yTt7Cx}0wdv4zzsYghob~?s$Zt<#^*?GiohF4qnPz7u3>2TS`{uay> z|3WeN%{5Dc)c3f&N2{_z68PC8SOoR%T_x+B81n z%{+Sf7{>VxWzDw(I&R}$Zho%$g!l-8m+36u!&2u?p=!^2MZlmgd$@Xj%HI0hV-f(b@DAeE1$@ zH%)A;o7ELTRT**(w&;`Efe9KLPYwb$GxNjrKfk}PjdylVG$aXQFgWql(VDcvsXG41 zq9}MvP&~c`ZdoYdGV@T1(tknn4$~{$)u=2Zi?M)Se|wLq2u~)J)JJ~Eg4b5Oj35)M z!p$H2&C{SY-RKD}t^IV8!bEhxZ0?V7Y5f}CzwcreO`ex9z8Q$C$JK+*1!yc%pCJg9 znyFxhh?i>X-~EV{c{8mNDU*BDKofdVqFoQ+QnJ50j+Duqal(T@kBaqpRz7#{T9y4+jg=Nx?S49B$c>?JS6JUlG=zPTcsEI8@@q!9?EWSjj?u{L?yC zdlo>s0?6tuYGipRvvpww`@8QCLOWEn7iOUyDGK2#3}jeR5FhbuVD^1!4zR@rtF_ms zoJDtUT`b__SsE$2#xIobHiDU87uwM?z{<&2)Y6@{bJGNsdRwCHsNL;Y2rnh?FtlKd}8NoyYQbDy1QQjo^qWWUr>PI%PLL&}@<>-W+#ztr9O)e11UT znd#;RE~vFS<9AmHOJAsJHX8TC65=09xqf_zIHzmnA+i2W+}vXExBr10;?*o2MJ+V) ziKICoiQpk{KZ<`alrNt&mF9mVa-ZT(~9$tGu1h#oKWK3e|i zCpXkk8*zdPoGbURB5ntxe6lDU1z zu8z_b03}5v5((hQKqHm=_LM|AHdk#i#eEsp@x3`R-I>dJahL-+!@g6#9M=;(B}vXU z2$+9r{e*;GFlzFs+V;HB>E3NbO)eI|rQJ(dv0oRkEsXV3SmL=$6~%*P=Gq8QP9qvh zMi9p68Hn1s41|YYcuN2`#q&&86)l5TEzW&eooUwI&)`_GpF+`-Rb)@u)y1W^3EWrQ zJmV%)YcC{??mN9VsMq{BB}3YG0wB6^6|vtE9*Yr}B?u3)ozv5(CkPHxcM zP@a((`OfK)f7!rp#??2iI>va;5A0#J$Xp5)=sWf> z=At%w(`iewU=`H0d#dHLyvD-B!onjhQfY^`!?HH+*{$gd-eShyxjI7O?g7!{_W7oG z36XH?f=Nrlb#ttdUBRyxxr*aqEcxmvJ>HI%!3|pFB4$^_oH@UHlnv7;cT={?HdzX7jJfqOaVd&#%!&SWh6P){EBIlbg4a1%o+I zV7)aW_(R`62k!GV?qgKWmx14|vcI0K|GxJgwK5Cs%wCirzAWD^Eq>_DHMm-Sc~;&e zV_Uv#9&=WXr{b5<)!4aPm)-pMy*2yc?BzcV?#;wa4cv?Psl0?J!wGt<=t^FzZNmx5 z&IIgS_OtT+tvCY{+IZFIz8#j4g~?UhHQc+YK`Ct)%tzcK(1pYi1ETr}R7`dR>O)op zBT_p8od*Oby{uW@FyEwMd)RnM7ki$*9k%terKeHvg)926d9MxK+Ou%?b>j7)Zf?Q| zarXO>OM^vW?cO5Ot}9rb)7RfSh&5UUownYujoi?eiSB1dh1{g5H2$41 zT@23054zQjr{SdyR#oB)+ypN90r{wJT(`58Ew{pV8|W7&H`t64f7(q;-7wo%xPJ0v zS=VUz6cG9LH6Fjpx{hP^b(pfb*tg%dqB+7LbFcD3rHe(SYtAKp@M`>WBdn>G`hm;= z1gW`bWX+4l&42wkoy5|#zr9MIx!NAzq_nk~5*D#a|Lw-Ku~{@@6_UC7`&FJb5_$O6=va&n)|LBFJiV`&FIUZ2 zHK7N&LP+0Z9uKc;6aR?)CU=BXv%J=x7!ONFeoXGp;=b~r%oB^~?~Y@(@6qdQO0?I3 zhIMLH-FwU{&Cmdo3`Fct!2OKP3%uRfk!?PqSBrAp?ekUr~kxT%*g&DHy3?%x68bUdZbIuEqQUMJg`4EJ9&m2lY3S>c}l@n%n z5~(r_Gs7EN$O4(5fIBcz=(`eRg>ElnPWs|0-n+IWnTnZ1EXbJx==`Xm&$4}3t%zMK z4WLcUETv72p`F)j#QwFzXTHFbjxbzlN#~dBPpVN%kh|u^%Nyh5n4hn&4S`&H-Ys@w z@oR<7tYhn>t*(^auEM%C>X(~?t~F#LA6+mEoA!9OoR~IZ7pkvbk(!3JUQGeils?u` zusFclNEMF{4-CH4e10h1FY&ExVN^H%%z<~kt$3{5>cB!&P8ZSlXu&I6VgSgG*Xztx z2XVv`H&!N#EtK@_{M*E7QjFUfP7NS)RtG9qE8Ym}_wBLg1<6<$Xm;N-p_s5RcX_=c z2ge(WJH35;;~;D6Ro23&W<^9;eujc*jslKn=A7XjdE|Ku%Bl(sP0`|o{D(lBVh?q2 zm*c)Rr+akMx~3_-O3r%9cX!|`%X@Q)&+Q%(e8k5kH_mtE0gTIeH;IUkaz%n;SeY(| z*dpfHUtd>6k}3{p%-N6_BdZRd(N9*!tXUfHEZ9gpyAs`8hc=I z#1-{+G_>AZBfWQHK|Ps}kP#7evJ4|rY! zy2m3G*tjos{qFCwq~X9N=mKZspr!rEWaWGwOmwn-(Zfg1tq?@DBEVSe#KvMoUy!W zOZ*!8QAOBs)c*Uf;rG7)&LNB|;uyQtTcNI={WGSO&Aj@}Ggx)J_O|Jk(fA0MJ()wU zHu8PPIGT#edfTmbXRGW6KF6iPa$unK(H==Ep`?F4Cjc=B18i*mzJ<)fjVc16VD<~8 zOAlCk6mp^~4px1Tn)KLmuwK9S7XY^0s(MhElt%2)o_Y95F<*GxZ@BovN2!)K&7kb- zn?c>fcS!br_+kJ{$nRi$-WzD90~|_1(B4x{r~Vgkj0QtIFcs~+3&;pA%acLuHM#=F zH|X9!t!ze9$^0`6f{!|Uf~!$@cSG&fTt6MVkAm52{`0eMl->PPt~g@Q@ORIC-6PFo z3zu}b#3x%p2}Bv(_`oEG+aDe;Nl}nBQU3{Uv)e~7O&k1$2kF4Q^@T_79;jtV9KIrf zF19)?f;c0hh5@OP`fxQm zRH*(O0R-Vie*j9z0j99!?lEkV2*FL)<9`8DXYM^Pu@NE2disY7DqA2Bz3Nj1}DQ{v?})${`xFPeAAIZ?zvqOp5=4RPMChob%s)ZYoHJ ze;hsrrO+ZoB5(FFU}(Ce_p2Qy2+j0U&?iI_{P;-;CY-ngIPWe2cJJP6cOWrHp2Io7 zwd||Io_#E6gHK%>U%7MI5j9D8U@RN zGT#3~Ayp(BAY5U}@W298p9#UtBM>zbg=Fg{4x3B)4~68igy(t|hIo`=wr;koBm~RA3egBpfRAq7YoES;It1g9K%4+A@SsNREs*i{!3k(U5kcz! zaf(cbOQ1vN5HXcN(VRuVUju|V6P(kO1<{yrc(3g;28i)20W!WTk@2OwopPH1s0`;X z0x!^po-FeEV&vi0FR&t zMIM2;kj@A4O2Bp}u2UUS9D^g03%%>%@!6-KuZT=IICd9wr;@C^*mrM z05-S74Pj4S0`Mm6fPS*=0HhF#XXH;6ZEa8=KfnxNMHBJs%`9Lg3Rsf>aZ3IOn~OLB z9mE0G1`_81_DJ@O8*R*fz<7r&;<7sed`Nf%^7?*1Wrb8CcQ@r;1v<6Nq(a9)aHL!5SWDz)nyPfnf_LVwnCmw-3)RDx~3_|K0ZIYrTK` z`0ZiB$~C||mg8bB+sDpN^!@ zfpo$c1Ph|MDczq~9a^;ng}j57(XE>eR6h=|kg;K}1COQ9^{kH9RHX>|RHSsVd$0&V zW~l!nJggHjnO;S>^@-IN+IWja0?(h^`UIMJKkyd#=_F{XLd!(oH-~%J4j=MLMIbTQ zuD5-`k56vhjEpIq5oj}a`Gfm(md>cp{vOArIm4~64%Of6VrIix)(cVRqf{UI-j&&m zgw$-I{}-CoQNUT;#f5lbRDbO2!SZWv=UCYfPW=9KxoKKISi$xW{os|{;)Fj_M~H{5 zHjPI%#d7+gh*}jdoKx!w`*Oxs=>8qtb(^_>KBw8ddW26-^d;`6IBi!hqk4AHL+j7g zecH*a+OF?bI797x{;F@sPkv7(%w*d(H~;>mZhaDh=Z;CXBPS1^fy&8`eHV$e7peNy zyQ%*eCj++9xFqD2iMHM-Zd0qyrAthFyLGrRN^(zk4^r962-=qIt(I4dJ;RRt+|1zr zIXB&xe(nKB^B)y+c=L5QKYjZl>PO=ZrrxxQnWkS6hUU$gKYo=#EE0Mg`Ze9o7xqbsgq53ZYXpwm z)~hNMO%Y+FZ-$3US))A%Fs?93(KCpl#yKeq5o#N|kPObnF&9nZ%Ssic;^pBbBOx=m|_Fz5~E0pD-^3A#4qCbd-zV)`dH%b$-$*Ql%|dBpJH7?Z}-D!x5@kK;BY zxwVrR_o$-03`E}?9riZ(<-Pua=cg|cq1D`REtZKZc=grI!th#p17u4y;E`HvjI>IS<4PO+w_z?>5ZhSEDev&A7Z>>WXwiMr! zIWV=|`bm{SS7h+ToqT)T9jrQY2Dc&oR5g+J~8$%@LCk@0a=Pf$rlQ1ys#PfMR97FsFSg`E5jhKjbCW| z9hmz;=Y`BMd(7u$#jMD-8cUI=^`dri`tS+H2sl_9rY8y!ci&QpPD8cB;ABjOfTdSB{e5Whb{|PK?mG=2TrH@jxvHt1;DwNe_9}<-=A1CuTPo7 zcf4K_%5=$KS7;S?d#-A#Zp0Ts-s0MPC=kVAK~$xv>k1GHcgh${bXNNR$&ieoO9_`u zNTkIjOAR;oqZZFX~JX*tXlzkZRo~!0VHF4PADzN5^JmE z!GU@HwzKi&Hcub-+g1qPoUom@^wHbJ3R;i~mh+hgb<`~0XwY&?R6)9ftQ zg-*kR4EuwvIm5$rR`0CJ-F(Qu3T_DYw(%N%@!<{kVIPz5_i|I?+kEt_`h3N8uI@R? z(%&XnZKDYfL-lgM{|5cp$dt`b$lyWFmi7~KqnOC5TCp2W)ZC@^;Kg0?(cr`2sVcIm?t-I!D8p}XrtfRX0^ZK|@8t#A^PEFk&m)NnP7uK0YZAL~KX;WOvCTFe51U)bj_a&lgYq}q#SK^dr`EP=44(GK_l z`ER9?fSwnH8fhwQfn34a^Yz8q*u%Z^0`Jcm>Eo1SJ=fIG`_dj>MAW(q9QCK@6E>DJ znrslsdZt5Vo=xLkif(6_Rp?ud2v!k(E}o}GL5~L?i%N{VFVh1yWGO~ARbb(Tz^+!l zc`^;tw~Y*BmZ@UrHIXtFh3|6~hQDdbYP6oZWYMv=ki&S>JTW*QH#+rj3Z6k*fW={w z2SzKmB!Oh+U_oI+Z*|zT)gy?NhRZcPYn8a@sMm=sz%EzB17y zp=FHTC3G(U)j7rdyttt*SGuo{dK$$78K$>FnvG7LKfgT`-j#B>f8J~-I0{73e?*bE zlSdVo0kL!;CsXbR4z2g~#Ru}9KR46PI6Kaj;v3CM<79osH3J7rWeLEL_L~*n+nyPm2P>?p|cDS~EWs)>&!lTan&K-00 zHkj&&fdu(znlD+^@2vju{8_ZdU{pIO0A=9MegyPxqPmr!(yzFT_cTSq^Aha_>&pci zj?~NFdZM-RMrQAZe$O(uj6Q$fabFpEL}8+v-oO-*XbEAM?lkA9N$`~oX3)V^b^UsQ z_BJH50&OQ$mS}D^h+&%k>%`>>fp0O{dT8k)3hn#kQP$xKf<2|{kI`5>92g932&UJl z_vSw@(5Ug_mu^CZncAWP{L~*&$|phmUL@eawkC0mwuR2~o2-eeCPZ$XxSS`TU zun7tha>o=Wvb%_8*p4xGPw?{r|?`ox^r10PAe3Tl+s7Dv|XR zM9%j1KkDn6$sxD;s+B1j#t?qX#tUbtWU!sBRdJ)}c>Q31g&PBg)fE(TE==1EDPsr6Y z@W)6M+IuK?0R9_5K%?o~@td0!WA<&7i|kVKxmo zCWAvyT}MFt*oNMMasX>|$l|fI;&iO2@bOgboC^$W3IWZ6_|qo805*T{68EkLWx}CL zqLpi;U4cOOwR^SE_swu9n6Ck&+G6=n8Gzv%=2XYAR5<_;RCE@qy@Gf1ouT5P+xRqY zal&oj`fxpW!NbQ9xPfOnF~vhq`?GmD$;@KuWuwzilDpI=OM~F<xfn?s@A+SINE;Wl% z4FLA)BCiDK|9=5CbhoE^l6H?jA5C4-_)`|y^a8@QO{r|!dd$YY`en1^U5%metk1tXd7@BawWKdvE~ofwkpqZpeJ=Ycr( zzasHgk^bDN3jV3uj~3l%&AI@(R3dG+KZds$oPkuuBdKeGTp)becWW>bjbD!ZUT8IB zFPdtO+Me0l&AB@VaVuEg`i46%-F-j|_7fqN;PIx%HWz?r<0lqNP&~+iRO~85lDKQM)>uY~=iG^f&nU5G_U%lx8+Ig~Fz~fVL^?HCiHW1JUgkn-M7)H`; zIU^eTK3DSs6n7jxmi70`4Ln}nO(&Y4YVs0PS!c>U<_Prr@~CZ*!xDE>VRQS;Zsmwh z;cfk8R#2LR?-JDd8+&aeeA`)URuCGTei?wtH z;vg33@0GSwTczboV+&pS0{bW4{QFdWitbL;*<-4frGkNr{(C|VG)@LBL;PLCj zy003Q1r}n@gWNmc@D$Q|(8asF2{)6xTMKZh_Vw;;Vyamnb3UK-*{yCqp1)osQelm! z!+G;~q}#b*Ke9>V;phC4l4p}hQjB@+R4-cUv~|l%xOX=B;~M;y`<;9oaGKvl+hvvVpiN58esteq#I{=<6JHSU zbM6IJdV5+0NvLTZc^PC%z%QJajjFdiL^Cb!7Y)#3yM%(Ns3NzrkCwt*Sb(0W*IF%6q9-R@RL z?!9hU%#UvoeMsA>1}~w#=~QcAc7`qz=-ab&s^VNPj`Y9Zt)312VL;c8>y)A5JB4@Q zTRDS!p&HE$N}eNFNy9JXnJ8AnUUJVp9?G6mvk#(kmVJ)NuY}G<_0Y5M4@809-JL4c zY49H-D)?9mzl;bI5ZreT;^8(nhycBWjPfupJRPmm4!%C-ip=@9mz7?MP;7i;39V4N zO4Q)$+D%M0+;iyT{|mOfxxX*FurPa^cF^PgLBc6aG}Z}b!w_@r!q@tS$kA^V^*#1~ zSbI^cFlRQSp;{*y@l3S6?`2 z)ZQZ!r=`I6E?XD*^2x-Op5I@6>OIc4V=eBrGE3n&RXiot&oiuM)ShLI&fi-q(AxQ5 z9c{?%2`^5^O-SF2>tNQYGZY$1pU8`P5|<=sNJXrWt1<&Qc>(Zh+_X#}} z!h%J^kFCO^GoS#Y9?;;xQvA)kTa&_rua;joB%FfX7>R=_dZ#T*DtDO3C1Et`_fy-t{*u zeLzyNPStx!eE8;V0ZEf-6nd3Bo*5bCwG!Frup{RcDC+gw+X6q*7_V>l_pGN0$b^;; zZv@bujsixh)egXqzK?&#rLC5_Pt?wc6+2O;;CLsbU^|OCg#IK_rePhb_VU2omD5ruw8hMX!d&Mn$i4vZQw{`w27PHeC8beEx3_xI@H4y?`TY zbzM41Kq?^%jvu##4QV(_y$d9fD#ky0~Wks7j%0%5;s%LnhvjDwV6v`q5$LVvH z9NO6tuIEz)ieh5VK}tRAak1ajROu;@Y?(4R`YXF~XYpl-Ui-t6*l&ui+w2C}z8-|9 zq!~l#bc9SRF)(!gNGjz$1f<*e-={ z{?B_{dL9*seos#^z{RZW0Y1=o1|JB&fyEYzJTl-roR<2OJre-;H-S$UX`ruaieV+@ zIbDcpGUR~#9qy+_n@0wDEJb5Sd_&M{gV2Pm-A$mlY$#&ehY&Og8$#+^<+uRwa3aZH z4IcKrj(&P|TG@(7TQFJ&wo>sff14R+ElFL8|_q4WMh zZ7QT!L*umcpO|`Iuz%%OirQ5C^Cw_+YPU};ugHex!wHB2pk^PShvN4utYPM5ZE8)r z@Ofd0xk6UN+0P^xjEWcQdcLSEG~qV!d?@PkE_XQ5kbdE_+Szp>NOxuN@+M>mnv(&PU) zw(sIw(6nk&m9rIvy9rAx&x}#!R*XX<;GuTZh7WfJD>Ut%ODg>Ly49&)BdiBJITdvt zs0iGm?q}ko%RL`Y0-^Uae9`6iGdMm>v+I!h*R#^iZx&$^^UX-ZvL3!xr`|vtq+Qa1 zMy!OQ)eZ*`Cp3C4XhWu(9uRO(V>8@R#b}T?l_0->NB=C{kU&SpFb2acaBZhBjPO&jtbB+)f=B#t0hS8{pWx znns*~c!NGeg@-6{fw*o3q-uXm;_Z;q-6HnkRF)pNcY1Df#e!X0b%@VzUFr;(=T*hE zjW0naSKBx!!F7ZfwnMXeqc$yCNAg;V?a1AK2*D{)`Acy*^UD2~1jO2u)3@pw-1 z{wuJNYC~P9A!w?dS#`@fJmd}zvsZHiUTy!Ssn|BF{ke`}x!A~9k9(0a z$t7C8+Pra*o6@DeJDq>ftnJ%k91q+#4HUu4;PvN3gB1yeGU!&|z9j5azWloN27+vm zy}d8b06Y=-ofFYW`~uSd5b8=&sG*l#l!h7HG|J)sJ>R|{X(d+f3zI#@k-}waV(15n zPDT0mSqdu$HLvFx0<0nBxGEkoT#u$=-mxE&_;y)llP`#Hg7i~=|7v|M6;Bfy80e$&Aha}Ub-?>{O2{2pq3Zg3dyfuszP-r9504h5MhM^J)@iX^^7die z!xb~7I@dE{uVZ@b@|Aiq%O#aMcaN_Fuw`RE`qGT#Fy!8myswCby}ZT)u&Jc1ce{hC zv$SKFN3jRf?~6sF>=%;y>z%Q8?p4Bw&{Q!A;p8w~Wo(DWh9Ay;Vzu#-z}JJKuWz!Kc5P0} z5%ci@-P03Ogp77AsU-5$GqLW3t%<%jsoLgGke{5SVO;&Uk!8L26Q=fO(^__CLT|o? zQ{g)N!TF@yzTuc&Z=G9jt~LJ$qG#_lzwG|>Q_eKwPT*4L{i{<9mP^%kC*_g66{jJo zXcSAE^RH4n+Rh#m=}CLog1g7SjEucUb|ClDdgfc)FfsG!`1$G{)~)9`yqhhC$s=X5 z)uE4%U-_dlJ0uFF7QWK}3vNr3xmw&4c@WF5Op5Scv%QVIuCq`79pZsv6ZprsUzu#X znYZ?DRWesv1-;`H#L%W~0dKidKJwtpel1>xIsBi&&ezAsC0B#^%tZ0&*Mn4L1H!U= zqN(KO+!P(O`?5>Iue*}%>VwL1!dz8_IwHJt6LvxHi8 zKALocmycd1u~~;Jz^j36W45jEdJfX3P_m$zAD~6VLghz-p{6XG1xxyP4wBFAl8F(R zWCD$bD5Sc)ZrckPuv?lySNRKb0>|WgEa!}fLCrASUi<=T<^8U_#q&2%*~hWc=L`LD zi)Wp5ut)Qt0*<--7l*ka_9oXO=aBltq4jyzYHoF80iC#$$cIBcfZuY-EZJ*Y{ygs+ zyi`k!pPB#0!7+m8j)_A#O2FhH%sLE=0jl9LXbDhbWnYy1J!paE4w}!9M`C;!Ad*5brZPp#I(hSLvEotA_$JKn6maDK>KCJNokYbA&&M6sUlwC z6dX>`YR_{a(|A$z%^@k;X|CqbBmTK(@s?c^x#sA`4)qiVSe?;T#8FKB070Jr*7Wu- zD)@gWOe?fIUHd!?uUR}9ulMd0m!Tg@WRL@={|aqpk$nz%#ePmwU`WPaME}e%oVdQD zb*x>As;c*QFfWyYfinh&Kj94tzdJ*XU(d#aB1E_@H$cQI)8J1c14cOfsU_|1aH;(y z;QA-ICNL)MdS~zLT0p(ZFGc_foaILNcYIsGwHI9ig+iE&q~>qz%Bim%S1=9yZzprf zRoXt%VpbvSI;=8e_HrQ?A|!)OGp0(N;$zChAhko;kl(3BaZ_ykJTkF=y?2KA^rsuK zllj~_pGJ{H%cbsSmy#VX>|M)fk_0=>Ig{$A8-4@kCCR}G34)7P_>I>Sat=wM(?52G zYMk`jiZ<6H?xDt+x&ws-#h0^rq87H3JbvR;fnTYUU8JMT+&)&bAP26fhIsD7%JvGw zVw}%EHWfzrx6f$gm8%n(5Mj!M@rX$3J?Khji6u@KpS@+`NaUlkb%Cvmg+I4<(;~qG z)jNkmvbTt;qCfN!yqfk@LM8jYK@~^+I^`tKc06Gvr{ZZL^+72C)u8?T?aApb?Qz}Z zKyOmft48s{^S_0E^Wt0F#nZbUY-Ud`*p`dVF+d-U`(Tem5z?W58#sq8_g9{0B-aW`x;OVK262up ze$xA@FN<@*I$dR*{pF)$#Fp%LOBl8fG&OBladueo_$_AdB!`HwN0_~Y@a2{o%^k7m z9jnoFb`D-XtqHQk_fW`d7zyKhwI2r4CSSM0!GjVRc#qM4niGrOX|jS?JUf7!6n zAe-~>uGASrLuPzF^x5e#-WR=DT@~2ohM+Xm0oiM1qo_dr2H*c?mVT6nbjh04I8F1dOJZ znevC$&T3{<%w(w!oq zbcqNkDIii((k&nzqd^!7&!!_yODvTM(4-@+rGE&&+m`lU-x!)Z@cg3 zxz2Sx&bhC1&bxQy__Ge~pgXrplr&fp8*Ec@T7%YAxESB-(|YaBE7fy(ZKo34o?FqU z>`J5U%Gu9G8Y(wi)Q-IO3EJWpRT;D}o?)=ULVaV@+q;C{F_!Q0$7Fr9Avkv_A5yM*TyFY zg6Ttc*RH5SCKs7{&ie?@->;V-eTwx@4F7{zwL^>kt9Ww#C9b()hch{%G z63O=`Ij+kG7Rr(ZKIPVX#_(FcFPSO|)1GZ@y6Za7Nm?w%u}DX@;TgE4ZEA8Ka3DLcq zxE|w1lBpkt3i@Z^kz9Ycaho%Yi7jFuPhukf1o>V_=@*GU_SSxU5o|q1=%T1ZzVPwu zp00V@Fy1u@?T~A++A&u7njv*1xROWjs%WETP%RL(tQxO=Cn}zCUvNxJYU0Cd|B8DB zKEwU*_>Ujih2om?0;&25LNA?rbgv*{Y~E`edY+l$s6@V=j1H$5j-ON_gj2RQ7-L|Kf0DV@qdblMA*DnL-^RH8`{}R zE-$D=E#?Nyv$=85ntGBT?8oiyG0=Zg_GFXv%d3a_i&bSm-LA1IY4IlIx%_P2sE}{X z>c0O)1woC*`k%pY{F}CN(S!+EQ$`$K67I$usC~T+Tf$OdHoch!U*mBvF3?_1v~HDUqJL{LtF<+z^@g>Z6g9s2Kubb=ZoTU0 znbIz;*U%+Ms(~xHYsN!iAXq{)DJ9y?hQH0G6@wQ$P}eJ0m;Q+dJ#Mrw?=;cPOC&`7 zDW71u#BIrmgrh{IWdY-_@t#Z&zY}hBwHz%z<^1ltBW#>mJ%FZ~@+C74CABf9n*Jrq z6R|gGd`DmcYjnSQ{V1l7*|?5s>Q}R)+Fb!je6j~Yh6SN_FC4{d*B@S!fTx|rYeUaO z-rTM9v17j4h*2YxxueUxyWQ=@g+6`Swb~*4y#1v9q1+##-BqcEK*vq&e2&co7XwMF z%vsF#cZP7`Zco*}myQ!iE=d<)Jzw--J=B$_cwLYc_6f#gCY?u%6YXOZw5MwVnY%*F7wMZ4s1iyMCGf_Y0P&j zbT&4YrC0Gh2QPQ7GsPFJJ>E(F);f&hNZsApScoFZ9)Is>_Y%o-l&Zw3{W~nsaH55s zf8jnIn2#PTm~9nFVyHwQA5B0}A7c&zr()txM$xzy*5jrw?<= zMyz;xdIQ!TrF=N?o79tpWwu$&h}6gA`ehZB$myOZ-G{42mgl^YnJiPsMikh7jqhg_ z6VFL(@227VsP~&uae`_!I1Uxvcm7GFJSJCJxds1&{VTXj(U68=s|24#{A65mNQZ`? zY+mY^s(O#7B9_f$r%LBKMdvzQKk&X#@VUxUv;e156hzCZUp-p&CTv$ZxpeiFzWRa=`Gji;>{^sZWQ z(I?+&#PyAKHxc*SWpy=WaPQjU3~UwOTzP&wj&!5&gk$er}fo-J%&HoRsIivGp7s)l#`EP`ZL{*FTYY z@}}#wzMqeA4DWRADxr_Hc(tX+PeOTT<9h?D#GzGHk5b`xK9RUH3)aJ+YsaOh+u_+ROnEl-p+zmwOwEUUQ6PKJ&FthB?OE-F`{_kG|) z$DTR)Cf@B2jnRp@@iD|_51>&K-nDs*tD39RQ#>__b%drvz;82vA0+FvS(Q0_g_QpHADs{PPoA5W=gr=GcE%#XL z@PD!}e7K%_kkj1?sV?$dEn&FFP2jI?Q8X;W|#9(IzmTnht-$j zbC==2^EK~~EdKXW1~bKdA>D9po;|WGv*SNSlO!iSw0ii1#r4x*&9|aa`;)EvN@sff zPiF6HMXs%|i(g+b{LQq|U_pv3XT9Cas{cYSgz72kQmTXh~6w>O)T&vi9DXbf$lK1p4^q|TiA z27B|^t&xEFesFuH8n0_+@quA^zF)EUoy#g~+w92B+3!WIV6pD~b zC+FD*FjLFtto7vO_G-NHIgJLx9TSG`pBvD`$>=h{Ko z>bC9|?Y@^y;SYBvzN?7fnp){n>?vXx-}@X`cOAqV=ow>B#V~y|85$Bli`Hd)n_laq zf_S8Q`;hKWN^TG0*CnGoU&PS~xH#@voT_=x1A~Lh=Z!3|{(Ae3pfd={U?J?a>4-0OwCXnI^$k>>JHO{;dZ*l$WBhrmfDx07;5&dL3 z(Ec1cFI;;a^Z9s$P6)e8ylh*(Y)K=BZOgQ+0nWUZQwVeVlF0x!0wccO3Vo}Hd(djW zEFZL#a_ummG9f?QsCzaZ)#3@9DN~F^my7M@?H(0u=e4C(UOSYu(_f4~*jo*{lKyoW zAj1)HwAyKYmZS#&KjdQ9 z?mGqX0{WZu`BpUsKPD?=1QW|DBJqasN;!O%OwTi;T^{bnwOba*3uMc_*<_t~;vKHm z^GaUFcV=G0tqMDh`7>>pZh8qvQEP@)K=JXnUFZKDJTys$waRwegE#!#jwC5u~+IAKVzNKuaE^6uGOKmsvu+LE1y%C!RCdfsII7VY{^k$9o7gJD z+H?K3(y2Tc3C8w=Cuo3&4;Vx!p5=c{TNsFkIZ($(5o_3R)%;S;UJad>8Dv}psh`LT% zxqB^UM+5g?(O5U=EywD&_sr&nD;r!V*>Y@;cXfwhNwZ2P^1cZqV$aO)Tu<}q6v!u? z8$(h@*0EweSlQQ8NgmP#xh@HOoG_@j9)%|z7_T8Eu$^o>3htH*_SgF(LQyz zPZGkrN_`8nX8f4R(HqFwmfDjkHVE7G1ep6auSFfFlIM&u*(P=q+Y|e#x$jZu_PNJ4 z=NWd~d;=%=@|p$@NkW1hCv?nG7%jtj>mA@m&^4GmG&d%%e*u?~6^1*-zmfMI0a6Y} z8?o9r^#$CZ$hLOs%x=6t)h3QM5yPeuhg<*ex}hdx1NR(-$$Rk~Cl<43fW66EDWb3th?2qp{i?jX1GLv^>EpoI}YJE-DR^_a|RoEQ=SzMic>j ze+&2X)phu5hJIW|@fqa7beX(?dKaVj%^lk+l>lN}!4Pf~WRo4E8(T+4sXA##k&hi0 z2Y&Gshk}^0V=`v$kn4JH+{lZiVB?i4&K+!}j>$wCUZ&zG>)gJt)-eIMPr88{d426o zJ>)=pn>RZlVX*raw^gx-%Q&&Rk#EqvmWNp599|P+wK*>!_UV&(ikd&K#WtlcAYLTb z>j-v03%pI-MiPe2EDo0xKiY}^3e)1ya|yBkf*pVMU7nRC{Tw1}1KeGid>2_du7Mf` z(Gc0iQRXFH$)^DVSOS+Dj+Spx#yM0>;a>3E$WNPuxY`!uY^#qUC8*mxdsWE9_S%)a z9&itC%;%+D6!);7VD{MYkFeXF-oC7fmQt}8hc*pJZcLG{|xWG+r zj?;}MSm6No3NsJfLlSnJbszUetqb?z6DE&W>{_oa)ek$16T5)alU$3Hhd5*MWJYj6 zaU{@5d8 z_I1zt=3hJamB;J-jO*?#TH7kE5lKX2Dqh4p`h4P*bT@6GI`lsM#_&qQ-a)pW=paFP zAf_b#)58|g%85roT19Sm31t@>=n_(S@{J6+i?g)DEX(yShC&C)%Z#-2O)1k}1x{al z*SJmq#l?qMv;7*t-I>rpkaVt~qRtr3K2uDveywGCZE7|a>}BZKnKW7a5d5+5a^>#Q zwNM0wjsqw7WB&V|R<$Jx9Y#g&*0->YyT7q~!FK{XYdoNLCYL}yy z)S-7p8`D3y=X=V-Ij9egRybX_E~h}#DvuunC-)Zv+g6`Z=d=v=3ANpQ``m5%kIF8| zvvTQWnN|hyp#=B#4#W84j%aSg?V)kYFnP(r#)Gy(O3{pTB6pi{<5;U2+rN5K0VGlg+1X zb8I^PuiR{+T^W?@X89p(hI6!?{KW^QrVpIifU1B`M6D%FD1NL2VQtmzBU?gY-LCwW+bili{$bz zIyYuCJqOb&xm-EO{47~pOpArXczAl)-u~&-3KF=UNB#Nfd;P25XX~SZ;t{OvFA3)-yyP0mD^S)4URZwbM4vAud%%tf%b>H zR!046Sn;ZdXEk3;hOpxAX@ko{v$3<5*9>!=0_f&qOyw`$m}{d)LYboMuqu%Ritij5oLo4KK(}~VE$BhsRU|%2KReTvatKoS46WYYmvZ{^>CR!WtQ_-+INlJ^eJtB6L7(Db8u{i)Q97%BOs+c2 zy-}kDzM_!QiY||H-sg&Tcql>JbN^m^%~53OfRP?H zk7qZpWwqSk%@w3)7K;cXx!HOva2%^GZ-cessnZ3oc#1#6B0M&5aj!59FOqBct{~I- zy6&^ZSgY7Y980PxZv6EDF76%HihB~rQgMR27y?e;1_pgGr`IeIILT+qxEcw75VZ@= zaG%RDo&Nv$aPU`ZCPfDB(uFwB=0peh@s}9A6Fx%CP;Dxm$ZJSs3l^cH0qm*a5;&u1 z;qAqG{pdQr9*9?s0SX?!_14v^M+S9y8+FN@&h1 z*;a*G<+Nf_rkXz#<*oKT$F!~7P{_qt*=}C{XGPq8j^%bs>#aGd zXzb1j@#XkurMJ6zd(QdTa_mDO;m7w&1>`W$tvo~ySP@&69M+$G!3T~%3P)nmA9#23 zqK`bu6viU%$ky+6*&`;%!Y;j)cb&$R^t-bsbvTt$s%<&8S)YawHScfHAQB(6A+ zbe-(dis#(!MSz^@VP4c~8D#DwN}@_uOVj35oS_xx-&p~1XG~7Teh|*7fY7tzV@}QM zW%u$DfGJhp1LWlp@&}}thK{uFkg=ezH(Q@W`OdZB&Sj8_&dBeO+y}rteog~Kj;&b@ zdLToASXauTg{iGPXGwFIJ(*nm2ymH_)nR& zd5^V?kateH$}0cR=}(}_d%-8b3Ji|v($&46O$g#Oy>!Iibl{-^Be&ko=t33H4&)P0 z2srK)PNzL4^Y*K$C@f()W~=cRWO?u0u|DDX^OLUcNA&qu)RvBQKc4-ZbZg4Q5C5vF z&^hUcJ)#Cm`-PPO#azSRcU^SVE%+OWD*|?=6z`_b(J3r?7vl4bq5lwe)#{~FEu_gzNC%P zbe@w|&y5h3I3bO#<4^#e-hKjBTniflwLNq7afx*I_I0_++pi!MKUifbJu`j zUKw^_*TG3ET+Q;jfvIysU`A@bOMt)W7S-KKy6f*tt6$QRhUI+9!U>>ir?JPe#Exc$ntG& zr=Z<0{Gs^EarTc2WEqt;ggp=Hok?SGKPlx;WdnS&!*?I7j|YFsAAkDgPx}rJCRb}x zO$RA+Gj&2qJ6T=xdg;viKiJ{;PFm`?XsUbqBeoC&HkJY!5@=t7y9^ojhv3pQ4S$t9 zX(2>q(P`wQYw?D6c6*(eT*2ORS8E6Qd|`Q(Fe->OpHoq79^Jp^f{`X|^?&Fyye1J{>jS@I%~1Wx1%A6QDVikroL}U5D;xg3tTU~b z=^&BjrntZcTI<=lV7hS@tZ{D_|KPl~x0{)8iz>Nk(I@TqvQl$@B3yHSE}5GiA{T4c z>|D3_dmKK@n0X3&ZgxH)%UMxQ^tzfnx9uEAlJg*pOFEf$@bP)MvtxDAFcVb1n<648+~pxfl62vz9ny zdf(0L;W?p#zudr@72fxJtFTB{eUeshZDra{k1uC?&y^A3{8{PwJQTK`3QtKS-Is1> zXE4t5PJe56LmJn=Y1~dP*K8u-K$+MjJ05sRDbeyA<6P|7q9prx2l=UYTPG~yn^&rLzyv+1jJlb9@V9JZ%P0UZ zyhlJGWX#WCsip~1TqHyzfI2eD>W`kdnT_uGz}3pPOjlpOMV^`>W}*4F#;}RD)rxHA z<3&iLg9Y+tR<0#~M`Q}`0OQ=o;ce1tRsD@bo9Oq=EcKC8$w^3~%d!Gd_C+Z9{^|Pm zoeM-x>ku{w`)~ka^yODt*Kq>FCO2oTJ;n%$arSTJo!_--yN3mxl?R(qy{JF<)Em}_1eOI|$ z4~RTJIKLqyKrzMK6kVCc)JwA|L$?7kt=8|OdD`*)I+<^yg;tv}SdUeE0ENGHf7m2P}yhqMcL=u~q zsn-I1&z@=$4LxdfZ-r{cc@11L( zU+UySre4p=i&@nDgw$786MO}R2hDka;Xwo;@HY?tfeG&)B3QqZEc#(usu%Ddz;|g6 zBT4F4LYl9_%5vOi^F`iekYyX6;M?|Suv{LP-t z`bF^1F7RX!bTL}MM>diIgwfH5A5YKpK&E>S1;?NacrFp3xyK75$!6XUUo5R;IeBY< zky_20LphQj$T7Z+ps@%6|HKbQhi_&qTc)+!y?A0R{_jX?(L%#*-qJuOm6IS7FL#HH zYT=pFbEeapx$lqg_XtTWD*{|tvVW&N&=}JEjYJU$lx$zu*?#g`&(*j@Z9@x*waxyN z)ht>^G}OK5=+N4mai5&N+Ir&mawQ=sOiw!A$CM-au~nmzwwPV%>@Kxw7cCfLH57$` zoub6IvRJK||DLx8GGnW`JPk@Ghy){=k@q$QN=u>Z34|^hBk?wy2W$&w=&x#8+KBiu zRCtt-2xg7ON519Jg9oY)zwOA7S>~!z0Qt_PY8|eSsL4Bl-4^xM)WK`?sNatzPXZra z<(|ELCS^H3rXs0MNq{G=T_%S7T+d_0ycTDSD#s50OXgR`6>Wok4 zb>Jp@lgH!=eFnB;HJL%aex!Q8S*0CofN7tdvMEG(OXQD*q#+$(bCTZM3up6+`QapY zyyp)nKI*vr;2<_I2yk^5fA013TPf7Ul;c~F5?ba>v87LBjqy}&*-RLk|C`ml+xYom zrSLbSpwg<>dWLQEn_JKwiNbE^-O&*c1UjwW^%Zo1y7jvhQXn2P&_>=iT?MBjUN5I? zLItq;BdIkD;&-8Z*Ja~;ODf@a-)741kA9V^0-k$F?f<$nd%ODEiDZkS z;bG6^6T!%%x)Lv9gc1Y$IGZsH=R0;8fD!=5J24GjroTv%ggl>ojzr(>pm;W+@qvRk zM`gmLTH^jC8v&EGzY4Ff@(!5~EFmvAu_LNxmgBF;_Zz3D!(T)%M(u5twd?53fZob= zg^{C%7T%V?m?SEEp<(!0$yI5cAWC#0nDjN&mG@en$=Rx~@$SOyZ=>A2cE`taa@sD_ zT(;|3Rb>}mWx?Td_VdqkFH^So;LMYaccrwW#2@}@^i%pbo577S=Kp3htm;&{&_kk& z^lGgik2ODUc-D`m;YSptWC%QG`WrLidCM6=!ZShlp*wi~r|+KfgKyZyyNK+q*U-r) z(D8+AR`0i9>9f2abiLTq|8g^5-e@z;Kx{B>RRbioIh^j@ibZ;7X1u|9=eg&s`Il!J z+JV|DK0EK%>vukBa(3)o#ErQc-75%e?u>iF+#O9ue~G&u7RygSk(SkU)-vNs{<*I~ zJ$w5{VXzzNdw?b?@xDlU0~&y9+t`Sy!Ov*l^Nb$ylwlZd}%49t#v>9&1PxdPRjy#YT-*4QU`vT~&}@?;-Jv=-wKZ-LjQUa25cwbmRHm zXTJ_5yB444MTaz{K@&;ad9Sn&i|%sG8Mt$y$*^T+D@4=TT1KBt^a&+mGCwF=jx5;{ zH1L)db_?WcfGEc%E@sJC3gUHG{jgUUcJ8IvbPqs$Y-DyGESdoR*I|l-?w^h;L8# zb2M{|LvU1Y;Owt>fyq?#$)L~l0M%2$xUKKM;%&{Awk>HF@orw+m(|~H=+>ehAFe1~ zYB^RX9FM`5P6~ddY3?dt$VSc04J%7Uu$X$!D}=hB0Og( zj%5E_XZ`8mfrueWqIWzrf;5C+qkMPnLAA~xB zCT>JM)nNx;lEfhs3=`mSjPq!xX_{k5YQ2DMiW?!00LpSIkloT)fLBqg(J(s<%`(v-Vehio%!u z6|`3pkWas%LzN#vdVBm=P5vUAa1)Ujv>y!KIS4&qNy;>BRVu=9&4l+d$BHkyyh zWK+hW^_HeEgI|r(3I{u2SO+Q@eIazWLY&cgbmLTLF{uYCZAgvyo{!pxd;#_Lb>HG{ zXeDqW94I$g8QoXdp$-(|fJ!KB2Pz@4@t(M*Gf)Z3#bwzVZb{&*2I>?IqpWc#@NBgJ-Z*l6aH}qa zcqsS^3=cM6f#2|Rqx~A_w=qL{>M#o_E*ET4NgE1JGBMzRd!+9WR@9 zLE8<=(l1s{-Mqww5G7Q1-yV4Bv@zs?;wqfbbF}k6h#HlEhVfQ&(=5cil5S?VXA3@B z@c9ig)0KQ(JJ5 z+EtT_aWuGXrvM38qYy?k1$taH{dyD!4zk#BK*E!p=}`w0Pd{bC9&)2EY#;J(?O7*+ z+87GFB4n??SujSrYr|f1uvbee=n$j}M(rwuf{X-!3QOb9zwEb31_!Y`a>14(^U;qa zE5+p#S!M28SNN{)knkkq?6#6zFm0#PkkMf~nu>^YjT}ahIVS#@kFtGj^m|21n|t+aGH> zgq$HEOh}sdxH?#w0D*r!bPDAH!&-)v$Al$%pl`fdLSpn*;Bh)ppcf2x5o1Pgj}N8^ zM8uq@Tw~&hi51Sr3AhR@;Nzi`& za*Cs8C~mZ&2OL(0~v+sy-gUdRVAaMsYO*+uXd5St$f7_K+?7=cP9 z>VVX04?_1R>o=`z^?um7%w$@%Jea&?O?eONZ4Iq^agz*|Zqb(T^Al%uDG^OIPGy9f zt8Kzn?MM9c5ZId;r8o91a5}rXagig`MJqvAb2A~7+?2N)0^N(kg6gs9T+Mqkv^MuH?Y ziW5M{9Ec&T>jgpT@d451+{Ig<0tBd2VtDNmL2M-}r`qZ#Xg_Oye*MB^CRnxR%5R>y zFQ6CDD6?~c%nh@~xA~V$e!S>Sn9|&B>ivAQsK#t2qC3M8TNV8jk=ypMZW+RhzPO_J zV@zdV)!CQMDlSp{eFSPZVjM4@49<#=mF*TW{;W8 z=pELWp$~c+vnPO$&@v3?2sjc(W_?9f`-=>tQ)?}Hq5Q|{pFdUOBT`d!j@?Xo?yGAa zqR{l541AvRq=#D*FA6&HfMRZkS- z-j9SgteL_(o^sBae_9;$Gl#kH{XoLmnlEU|a=wm3C)S=KKChZ0@(l^)5EXP=$JGaq zqCh5i0~uHjm~8g_Q>eh|{;d&F0_exFzL_}Udo%~EgP#Lc&7a(43*N)SqqiuBe*%3z z`PiV7jrY1dJjR(Hc90+2q*JUj_GmjCAamvU!1|ZsI)9AM7 zDx<+caU_j=&YwZvAZPTPlawi>vfXk8PU)WnCJ`nMwDL(x1dU1zW+JY*9YeZh8aF8f z-#mkSY#Whe74!2gOa#ph05#xR(w9ymKG$!tY4(Qd34NXJAT*f8&h`lOvF@7L7Mk;= z?&0AB*!g?IWyvA%-e*C2J9@;Zfm!w|AuTT=n5oy^Fd8JZ znnG&0M*w3LE?R*%Nl+&*S^_4kF8p@2l?mpXFl>;UfRCtXO1Qe1cU>`S=_ZB|jCO(d zOa^RV=OvqwF;p_JSUW$HP`%#HI5ugyaxw#Lj5H6S<0DW@kj*I?Byd zAp;cSQ7x^rm&=+4>LPdnW(hG`gR_)@5r=Hi+J7Ty%ff9#LFZ!8;9*kL5XaIr_$K}r zkV*C{7&2lie*YtgigI+qpiTYCu-q1CAHw-aGYq~tEJfhgs@Y$BrwubiySxCRm9G99 zsIiY@rhN>Q2zvCn(8~dpwPA+TEn7m}f?;;7al~Z9pwA#P5f`+d6H8`iqcO*^P;;0X zEMkkOqo&Pt5Q?p|8wEj_o*|q+o1SiMaAhLChmA-Y+3)x8b0O5(ZHv?ESf^+Ez~yxx ziJu~!XG`hu5r;^_V<9=6u&IwRV3=EK%(HgK&!F`U zuNOR}zo3Sps9QJw<7!V!0tBJ9!4fVJZUbP+IS5sKB*cXX5upe=LxTm9vXXFqBz5-m zGk<}w8)Ke*3(YFK2rTP}ABVcN=wMlX_}+6q04iFBK?HY@`5UUCgI6wxdNA9uEljq1=XtpWLcXSYGwx0a} zW-y#FcMIJIYJhvA=^N100|9KxI8mVRp?C zrwDptaHZv6A8Z!h8El%`01Ky1W}_s^JfbZhgbZsv>s0@cjO&X9*7O;AV>=dY0JrQTRJ71`+|m^=Df z@H)i-4dQxtPws^Zx+xoVU>>8t(J5rO)d>Uw8HQ*y$Rq^~ihIl(ku8>h<7O!L9y7;g zng7^pPDUY^)xdiUT_{Aoz$t>8A2^!k;tY{iqmZ_$4KpIig~IWzEGt7TEZpvQCZ0|Z zcOEFC!3%}(I8f6GlE?#{nCM{PT>372GnvC%@G!ylTWCNONV))Z3&7|L=I(3wX09Oa zAbbEC5U|TBj>z5f#F|MYfS?6uHhoYrSZXF95=gr+p z_W$b+G?)%Rslq+5S$`iR$j`fg(}3Q<0^3%n-R5bv8sivb*wr1k0Yr~XOz4$_L+UT+ zt$WES;%WHb_>9USNS1&Lc5Ye_=T^my8%=NwahLojxj}B_hMCcO!EB}=>DNiP*Ku~< zN@&oC@!A6eEX&i)(b(!q#!7{_7p*5KgwH$Q%?HUi36-Yjr?kV6X=Cv2OTpIGx?8B? zlmA@uzJpn`&=*ynfOMN>rw9N*S?#0Hz|wBFl5F?YZ?rN8ATI258D3NWBzM33@=tPA zu!cNPc`x`+5wH1Bh<%}Pi1&@rDZ(ZNe9BMs&(8PU#s?uW%2iHyjW@?=!@1lf+-H}| zu*G1&;o3!_5Mu?Vj|vPSa)|E-0ceoJW7z2Dp}T;~H)y8-w@rOTc6QLBn^<$Dj7KGN zSjQmVlxROnbqZ^+#z_B_U-o7vx)?CkN+g`wOc+tJ=ygT!o&}ir8^FZ1{+JCwxeA`+ zM&}+Q4*mMkpeAm#M5*K$qyc$a4Y~&$MSCQHbr8_-fZxz%U3>&AO*{u@e>bqxE0Z7R z#w^vj!8;6L`2m>tt7kZGlYe?Qrk8BM1Kztn6HB$Qb(IyrXc8QP*!Jz0@SzRqm`)w~ zw8*D7I(2Qwhfyd*RoCvYs!@bCBGy(@@ft}N7QW9EHn%OJbM!ifFzs|%=)MQm45Ex>Y5!@51}WoaZ=pTvFguTke<9WF z+P-=eA{}d-#NwQ`ae5$GE-?h@X|yP4BFO`}7d0gV20t5WT8swYpk^}xr~h)zOrvc7 zn&L!gopvRwfMyudCW5M#mzmWZm4u+`y|7uerdzZa>15&YtIfoWQN+d6E!326t24N6 zZnLus;RF~<%`6&ZRW+TCy8!xES!8a;F(^bxFB!@h0F#fFcf^{dB%dPwI-%_` zUx%S-pcmjav_u?O0C(+xG#jJ<%=;$kPATdrfauQC>UmOuNw^ljUHDo z`3WHm$$z0iO8>!x1l*)>2SVGypRFryq1M~rA8vxU>NalNf%xRDa;hs4dsCKJs?XzLJzppf)Q1M1H@OeD8@fOvTM< z0#QK7T?DAhr@ts&J5x{xEkdJ^%5*??55Vl(6FIDF0ke7A1$DA}%)5-R`KNn*P|W{y zj}!)UPf6GU(ExnmLV%EI1-z4`=?Jv%^FKdB!G+LD4wJoZy1j)m05HG!)!3WH8PR~; z>g903+!{BnxO&Db!LhGgR|Mjj))Bml=Ue)T_?|6urUa_sAH~x|)xnB^<~33g#5e>|q6bw2)WZ4<4VqbteG4!GOJhM+Z7%A7 z6bXMn4**O=`TyLzA?Ek3WMLQx9<8BMZwC6OyO)2?Ki$m>F96-?(ysixfxsM8v0|nZ zq>Rm)1OR^Uwq~=_O&{eJN3uV;bvim(hBQl!`yzf*a1jFVd#)qe&+~2tD@7v(3N3LE zH3C&`o_kd0n?4R*;duZfvl2KtAkmjYQJhrgN{%4tCEuYzb({z<{DJsNbQ4;1%`Ej* z0!TUk?0z5g-WU7ru^$C!Q1M$prz(Kl`vGzf8Ue_?{~x&%{UdiKp$)U@*UxbW32Lyo zviMWPLqL7xXP|&84`c!X)Q=xIuD);{yLICngY0n~9~d!A1BEY-_OK;GkQ>dp+q8XJ z+sZjWcA{6O@Rs~rN&9`JFZ?(%rWpG^zyDlNh_X6%hGg6YWo$KtF6!X*7gyH@Vsi$z z`*5q@#(E5vSeAxG!T*q*95HDNChz|b*)bMC;2>fQ>#QjPn`xq(%3lG7t2l!O{Udfu zz|RB|Al69Kb=09#uK;}6*d_j-muFM>r+e~MDlq>Y5|qCJUttHrLvRdQVs;ZCN3-G$ z_G|;R!4wg5j?=y zS3t&ZsF}`(69Cx#-9ka*E&ydEaU$fp_0r1G^g$FSqX!}hAf}I9!%Y9CTcbgv;()`8+MM@${SOnz z+|^;TDt(4eY|}EG{s~_(#*2$%lAbn5!OhX-)QtRpj|W<$0boA_fZgIBus1_Q|KX!d zP>iwb;RxU$x`6@Abd(z{65gU%J^rs*oCnVg>V{c@~sJ@TIv16e|US&uSya5 zcbY^0^M87j8AO18%9)q&RXuLcqI-LJD#PBK>Uhho=&aBv9;!*nq+f=R~w!vtUmwiM#(E@pbp-~*3^I+^mv>xv%9rQBg!7i7k%kVsBjd} zIpDz~&-o&Rsz)u4B1-N5zwFgI%Lm~*sH8TDmOIIEU)%(3mp|XWgBqCd)kS%3UC`**3GL}M=rAbQ3FvTZpvSe2& zWhq+}Q^`za-&?GW+-##TmKn=jv%J4o-`_utxpQZ__uO;O`<&-I=iEy_%x^U_H>=_` zcV8*w-y5(5 zwR6Q3vuw=1QTRfAT+3;iGSzHr!~fjxd`82G`^ConcMhC0QjK#NKum}AwMA6LCyNSg zd_DCJsfXj$eP*Ve4upKHOD)diczbnq45%hny7pvk(51HA54_OCSA9n829Ne~h~`*c z)4*VR!&zm&nFGgp@4kAnxcmD>?dT;Q;!DNQNV%{t67-E$uJSVx>dV_zJRQi)?V-od zttKBgqCW~ICj;r=eTv)qSb54faBsyg3Eq6IB9rr#|BQmjN%}pm-70ASS=fF&U?oZ+ z`nbtDw0fzl;=@$C^KDF9)83Ci*Clx$z5ERO&LUG|vmjX7{HN`!VcJNUT76*fhI-k7 zmD0DkJ-@Pq8=icBAX=-f6cR!AH~zo_-&(-2(n|g%4Gf-&BX5o;cUqOIEUhF3s0A}O zX`D9Eq~Fq0Gc!41sr6-v$7LqUeeZJRZZ)b|yqi=)?|PRN(Muc^^(m|PU2{Tjjjx?E zoh~9}^&y%rSS5) zyBxNnE8pu&>q1o2$@l!2>;kUg=HMIrHeQ@VoUZMn9BD zmSz(^Ko~*n{-eE6ieHaR&WfTWN%#DFIdhm%QN+ky3?*%_B6{P==(?tnj_m*D#)4n8 ztRybDkh&8m>EYgoW9I5cfiek*vIvYitZmcHJO_db((hp-j<7CVl}gfYT#>y}JdngF zWDeIV>6J?YwWuGBEs zo!fIMr(0L5Te3Fj^tpP?{58xOK6RLvwBloT3rVfN*?n69v9=LMxuxaNb9q=*bU4%4 zz-x0mdXiDiOOp9wP6F55$5|_*pH{xDn4DR)ySH;|+s(;+TS&HWBea*pX13}3&v}21ht5aFv98QG>{_mk=mV4LrxeY zGLL)db!z1_zG^onzWbUpH7vrlUB)k+%HU9U2qR?6<9LziwsLqKD-K6py%8?;oAsnj z0pb5*{q7~|J})@RD#n_EsvkwNdgv`1Ld&D&7!B&0P~9#QGxgO`O7MLS@N zC*K+cRMvl1_vgAM!AVk{VXiW%`_u1b*6HeC z`^`z^(y*mg_FQjv<^A8i>K(S5+f ztK*3@_S`;RlAVv;fsI$0!_T5yR@y`r5Ly3A%qK;asdGP)9`bwa^)W<>9w#&1U*@%} zxECEG<*D=GJ=UL+bQ?d`cJs(YZBkj}Tu8VQsaf~*LY)CSC)xmWckWd4R>acfgrlZ$ zO7}|5wUqthHjKI|*U}In2L9&jROYTeVdj(B|03rKUR@yd8)UeU&RD^CJ3g%9NTrWY-q*R_ z=9SQ{aNfSv?niM;xw2p|@iq8ixsLwUESi%SWb&vv0yd_FhuSQEIZFkl#S4TcvA4&h z%(&fA@_OxdCn#ePRRf2WC5uI^51TY7Z!_cCDLW%@`|jg2IAxzQQLIU$h14jD$@a54 zG7gT#Q<@Ke$vkPG;^gWai(>YF(HOWOc+n$OXo=v%6b8OWs)2aO38_iNRm_SyCok{& z9zw^Z4O}}z@BT|(@a2lLxdXR5>GCL((U8#rLQVdHMSCV@C3g)MP zelut@#t%U7M$gV2EhK-~2RQqK9(brLV%^|MAd}l2-U8y@3?J<~DnkOna^1ktaWwac z{8DA79ld#H4Bxby6cxDcrI)D&EM+JD1UsTQc`S31)15!JWT@aUzkYvJN)JGFJ72&H z*{(mmT@y)NxyB6fpG#WB+yv!#^%}&66kb2{^FzNp;z#|$?XKfPGjgO@m=!u2q9(UY znAQEFPZWK$q(fIA*etv7AgfXbXTXra(r{Hx8#sq8M^&PAfbWh(5Ul?a4d0>wCUlMjN6z5~=b@;l+E5ECA6MxyYGth~{TaxOR_jz?S!Ul=?nu5(qwIW^8QbCx{_UPBF{a z>%vsLbDyTYlFICkFgAk>5k_PtsloNg@V z%>s-ALy?Jmr%4(`_}ny4V`Sgq-Zg7KHkbGY-kil)`3aE6x`<5z2<(T zz@a;wyj*iqUDTh(`?ynIb4Luq*DVo9dacB#LM|lq%5Qqv44*J^Es%K%O#VdDaliDk zTZuV?uR(B~DJik#J|}Nlh+dWoTT#nlsm0%U2Dswx<2Z(MMJ4d3CwNJmzgOTT-8B{d z7~{n4#++i6$w6y>g}ukAIUZfIYfk(srVYe3P_-&)v(Q;3)c-zU=S`agl*OdtK&U)H zP+XudHvHVthAp)WcAkyy*9|BpWt44C1wE?e&*P0}JHUInE1z#aOT#IuA$lJVGkr8^`K~HRcVD5e93HVPXmm&24WWpe%tvk=qQ>els}|1pwV)SqYfasJQr z>w~bkDPVK3l^C1uLgK9$rE&cxLMgQQJyTNk zYuLssfhVOCo8hYk)45d2m$O0s}8k!)UM8ps%dwu9y)Me{NzD#bicJ-8sD$ zMpl;2bEOhT{}vppyc9tNLz{``iMxKwKEoTadhn#s^wTWNh0N>jO6FivtN<{|Hx^{> z=40nwYZ=+P7P-5KuhhV#TO6*)UkglX7l0qOBf!;@1Q^VUamrgwD^e(R17TAhj%yA5 zS~FHRQQqiIH*uf!TYT{6LATwtkQb4XQUt!fX}TM$ASTwsX?24^(@~1|abfm3&O_a& zgzs$`WrXc&^g!8w>U%l3{ZhM0y6$`Ffl|jHA~vc{@7({_q923zPQ@`KFQa-9qM5jG zsUW80+duqh%?&QJb+2{DIAs%Tb)+W<(lwTIqjvfdTjUWwpJTU0r08g0gBUN(&o9Veeo0bC=K;RB~rRAI94=J$^4L zdB34Xt8$xE_1SF_VJT8&gq8-egyKHTG0zm6fOLz}LlR*bnMCdgkNx9Q8_Ni1H^Sp> zW6Mc5PBLQiZH;TVBg%T@7wQw(i)}=ym5xblv%=Jp2sFB^K-{!km&1G~8ojZg$W5@6 zaLa8=++WWC&QZhJbG#1Ia_*T->S+n4-bGR*F6@yOaXi6LOxYaL#fZ3o zuQWmhbB1{=ah2RK-_5dsiePk3 zJPwz*dvFArevoH7A#8OQGX$U9LUe!KyU&l5{v?{IcO$xm_<1X>f$2kxT(YZq29F0y zG_VfUcq8NVc4DW4AwE}si6327UcCwBJlAzlNtWodLc}o&&svsl?9KB_#?=l#d&lE| z;&YdG@rGx6-p853%6G%G+|))Atu+ujzncAA(ztyYA!|cf-ZyDKuB!Tt&TixE9=7cJ zZ_va6=Y^FJ=iQatm8s7}jU;Rv^d>B(<+Lls4Ab!bi4FPL18;2nDla$&*j^ciyD!Dd zxPYx{v;MsCI=b;nFGpVv1#s$>N(ogG6WA(UH!z=y;<2NJ@MhDV4~#qM3a+MJ7EQ2=M4{Q+ATzU_bJ!oX{IDMLfq)b=di@I zx`lTFj!Gc5b-u-0f2I+8_VFN8s`d;aA@VjbC}{)KNTy^98;8vFa*3N?P(iK$ig4Fg zo;#P0H+mwmWT#zel6ZFXmV%y=6rHS9xeB+-L^ee0X!$1&@a&aLPnaZly;AFH0nyvK z4_@RFEBqSC#iJiwJAj_lmCq@*YDkwo3C)AIUQm}GkeNx<)J*t8NN`+5gdy+l=l2OH z#|-&-b^zsOTAObh2su8AZG~_3-2W0-o=F^B3YCTQfzu!t%GW9lFFb$*uWxe@8@II8 zzuaVbJuI>&{e$Eu@61HuNxHjY*f@h}b#K{Gh6Yr-gR=4leW`Wylr_vEcRu1oW63A| z>0Js)+b3tpx%Y+bTPB;w;~D?`5xxgC92zAD&wLmKKv3T|z448N~M(jD*EulArH^Hzc> zneyW`2o&rIv*u3+o$Jf;L_e`{ay`1_M%B{{e(EtU@tb^2yZZ#;jpg)Ft;+S%T1I;% zbHq_3>uhY@8XO-W+mset1c2iTeTpjGpvlpVmI8#-b}jhAXPi}gio7cBZe`#=!_z(3H`(d=?OY;m$7O@u|-2VX9RNQE3V z+9#~rLYxt?ANunQ>Xkp62F6H_U==Z4EbFTC0Lyn57kF*^GFj-}Z4cFlZ6M=BIr?`g z3(%@RP(^l{`9-wvYynLT<=9tj-0_)%;(Hr|~ zhfRa@_-}X(Ud1TU$-5hyA+F0Pb$)bQB5dM3SO3URqyt_JX*x}^{Rw2b`qtGzDCC51 zis>R*2%0kFxczrjdY;_6yF2_oij2R@KWB>~AuSB+Yg&M^wE)m0sMsXPmLQT`=+{N5 zxaULX*Zex0ANuu^SY2QiVgRu?|1~zw>D2O}mR${#l%Bir7JDOX*0s|;C0|lG8#Lo%{$z<_(6S0_Z7I@1}5h(sYfn@na zF3unmcd_0O-!8#@&Lwimu#hAKqloUDLeIy|I#3tWZ$I?%Koq`YQ7#@U-86~q!>V37 znErBKL-?lRZYiloj$*j)+kmOq;b)7LP0EygvJ#3NT4zTR%#_(NI(Iv;rtObmo z{UKIBM$D+Ap+@CZ3qL^jHbm-Gl#d`sj&=-ty$>3UU86OmpTneTkl4ZZs| zw;Z3l)lV7u>0w#i`m*ZZWin3;U+4cKIN@T?A-sdE8AoAKGd@E zAao!78N#&C63O~iDo*G0L=J&0%^)9f?q|ClInmjjExQ&pmK~K4e9FKf<&cRKmwo@m zzxRO)kaEZrj3s%)?Vckx0Us^)iAV%@bTP>){9_vRAeLuDu%I*2!RLk}-xp9WYpCB$#~pNDgI?}Tp~ax#+Lav&<-4Xb#+XWWzB zGP;QgzfxdHJ55)*s*Qo_RTy;xXV+26zT?YGSeNR1Tt}d;;(P*>>eW<$PXCfW(S8%RRy<;=T#b%IJIzG`3H12E3J! z8kKfkU|~)r)IKjq`)gBk@-l?h3|;^~i%c2NR?fJF)sQrA9KLuI8a#t(1+}clOZCOE z1)?P<8{IFC!F83dcSnUc>G3Tk^;yMV3Jp@?@xh7x>`k_^`@VBQ~zW z5l<1SeKE~6`;FylLVJpBwu;*Y{X3x``79)2^bKcwrNYFy#6~eB0|Eu?85mVC9mF4x zJ6FgYhBXIgo`zRX6QV*P?!rnN==&Tf9Cf^W6YqsaR?LfrV zl1{hKDFXJwPtbP?qH$S>#=oaPH2wfHMetv#rPGf2>Yz1$_P}o)Y$1jP4S>FH@`#UY zBYd_#z)=HO)S&ub72yVznQJO?n?0%E>?uI`4j0VE5@yJTkQ4e-FFz4o0@Jy~_w?2E z1^;~9tpNHswFNG>;&_UYWd!-!A=w?*hlWo6CDh)B%}9wbs(gb{B?AicZORW=BJpzs z|M~^;#L?2$&4YGjOfFH+g{0d!cv0SHc}^)Z{#^ z`^WFO;T`R4oVx<@<$Os+X%CkuKSS_WhxOcJDp;+5kWiBqwTejQER(At6Lrg96VW45 z=L6lR<(A0ROgB^#X|f?T?t;&6?c+n21NX!I^je5+{4hZ2=HY&LR)Vs0QPN=A_*-P> zYe0MQiD)2I&m}rT9Ddl5t}*e^%hsZ@b<*r4t<#y24n*U`SLdF(~Z`e z9YB(Yc>eil8cs?TvFc>OxUvEEb-aZlocAj8Wk-VxK;Vas1^$hKYk}oPcFE zj2wK=$>77+el`0`Fp5HbkRgauzCxT5>_DeocK(M`qJ3Z-m+`cf_LduEl-cQMAgf~m zDpFZ%!lU;-YisA0qkUS&$afxy!2`sQS7!7$hzVbJf5n7ekz$aYB+$N{HMdYqX;MLdv(t9ADU=Jw!esGC}E~F#=Z4;Dxg*!=&2RWwzb_}vudYzr)l)CNk zTQJ>0YrFu~Aqrnh5?~=YbVvy3FOzk3zYrJAc9PsIIS*gz!xQfGgSW!ADb5(yiu8Us z!=Fnv>i{8UV+4O2Kkk>(7SOk`!ROa$I*qAuZeL^^uU~_hjA-THxfE#eUNY1{Y~4ex zZz$FE6eu2eTe`Mx+M;$;tkw&I=kg4)Y(Enz9<3?u0DU{Ie0JegL;4&@Xg3pDoUoVl z{^9i18jw}oj$?)<3aAD^O3kb44G~TEMzyK)ru9Sf&ax8hVB%?Zm$ZEM4#YP(B;9j!qugr+$9w zv@vpxcb<)VAckBsrPHCkvmc(1X#FMI0jRK9KrG-!dJe9R10C{N3-RaHx}1eFW0>+h zXtm?u9Bfz!yBhyyd6KU2etis%D9tVgWozt;rx3Kaen(lEkRZK`$_MR!J{k8i7yion zJX~fCgn}Cs@d@4S*&cMk1r3(cU7Xh=xL8P=)Db_v8qaNZdQa`{g11^|1 z0*Z%x&2)S!uDiU&^YQlx4xan6VT~0O7T6u3w_93Zs1{tT;XMq)5TCK zpS;|2D)z0&GPzeAb>5WUtWCOS!k^%k^r#0&Ui=BD5U8AjK;;qM0B>Ewg3$0MgoabS zIuCKEo+rS%MdAI)Vk~4zdK|l4Jpi0DyNUV+#er^K_~0M(&TC4`_*5)*PIz6l-T-3@T-gyygd86_o6`` zBVqup6!vC77E7`S_GTd&dj zm%vbJT78R4ysn;tyD9&^({2=7V^S0G$&pG>SD@1r1FGwCa5Da$AeY!ewE6T~7*gW4 zXTb~gC32z`jZpFu;&59{q^)m!UgJzB4BbvFH?*&_-G^ea(dO*$ehG{qr`nK+A zSjvy#x+MOmXK8n_)_7-X&9 zPhM;3e5R2O$h8QPbqaWL4@IQEXq@fNrk6=hP@+YV5rR?SG8qPvp`=(Y@fCDzlg(B| zkN!n&^OOGoILTj`*?0Tt%v{7KyuYLEUjik6p6mn}k-m}4RfKd_jVU?0DR}YnXOCid z?h{NT_D#SCn=)1CEHR~(X<-xjp8Ex$x-s57cdsCntZ#+#(0wxi{~7d2*GoT;N- zCEENq366UFYl5OseSYd+k7R-Lc%$@9>NX4gz-~Rkcyn6BE+QxY=MktVD(^oSzGWoO zZYOH@$^86p<+e|iNa-WxfT9-(A^U1Z!*2(2-j#vtd-QWAo?TDvCC7U>GB-G%!e7ze zwCcLpZ~2b(Fm+5$_mL~3ZY;H`-tP8@3YM8ky6GCu8NBXx+{uQl@ac#L6AFqvih`bx z)a+@H)z4$AYh_fv{^A*5@J7(u?k{UYK7N$VyN#5#RsX!+mV}EJRQ~RGe&Ohg*QXh$ z1_C5QL_Grvwo`ajebyzs->mh9_pHV6E>Sa{_6O#72~LsisvS>8_1h-zZk@FC01pldnYjG4Gcf;W)JoaWwL&hGa4O2Rn}zjaEcGZgc#&{OPB{7oCq* zAG%E6?Xzl5yRk94fBD{reByJ#?h`wn9N4;j_EVQ{^ZF+=2=G+=Vp%0?nDjj4+xQ3Dw$u37QPOgX+3CO&o7OAUC5V5 zzJ3oyrz53TgPNu<$o>7ihkqqD&shGh8Ozz4FVJdZ(dgTzMB_tStX|v)H%@WtbDE-%%ig&PsTu4%vqUrgTi={uUf@K ztw7hT;2?zS9{Xb01T|Ivt=@(UsBy*4Zm{Aq%Gz_hYt^6W{zAuwctVyJFPG*v&XOL8 z3-BW$>IuwQH`8&JaBQCzsktYbb@jdONZ9q$v~3@)1%lgZY6Uar8h!J#c3c!>)P1wx zHt_uZ1(NUgc}}ZO!^&-=jU*MrtnskZYEk$V^$qviKVVx|c+|gsz+~UapUf;#`2;fby>{|ppeH#$c@PAF`Wnj}DRaoqJ9Cfy z&D?h6iXN69_g5ZRcA5{L6N;OAc=HvL`*z!WI6dU<+zpEz{93nd2wh79S2*gp0G2BvcLnFGGHHst3Nxi#}j1Fs8_8BxnEpryej!1;T~;Wa|ZRoB!1<$!zGIKkU9BscwSS^QcxU;&i| ziV~D7-3*N6`d(UYG6)jCjiE@{3;t03>CjlR+yTx)RJ?glUsyM98>!gClv75Cu4<{r5`&YO5)(%9$&-5oFTR`lac+<5^ z^R8GzcUn1P90*kykviF4l4tr&TZx-WN^?|9&x`>q&G@F?M^1*CzN0YwMA6C#| zPTYK;@0lN5OW4Bxpf(t-8GKw%44sAfb_c7Lu0eX5jZfh4v*dmW(PfGlJa;uqdS4`ewVWz%s~F=X$4(Olmcc9KnLgEXvcD^?y?UWSdf;CQ3Y&jq-7 zsm*Vka4i1JY15!Wi^@GtLzkP`NfW;%cV=6mHBabejzx3xm~v;99AiC_QM!$j6q?3u z?xU|Be+lc*8Aq?w>3`8zH=Lzhao{$?0TkRrpV>*F>D=bOTY%Rt1}CYS@DeOMNyQZf ze3tt%Nr@hUu70E;CoH)Ra(2e=tMSuGZye;qv>=IgO%W(|Rq+;TPB1hyq`JW^>?qd# zxI6bD)0ugQ$+AxJ;#vx%;G8a$l=R>QTJFJPIYvTF7X6xtVqY7_ZW-L0r@0tGFNbZL zN7`Fq;s@I{8^_tFz;nu6n||(LA3AOL(hQ*kmef&&*FdWXmR{r@aFPsBJmnA%OD-4^ z)DuJ(QfNmbx7nNrQ3w=*8_ntn9~~zMuFCyvroKHR)25Fp_v?74Twnq_$1)iBNe2%+&InfaJ`G(=CP8Qj`XNtOSXga&1Wr`2z)<~nq{ zE^@op6FfgTjy)B-tDWgA_aShnm%A9L4hOlUKd&ZotJO1ct(rSYzpkOs*o?KT?~o3< zEEMctl3+^e-Fb(TqzMZzhqkihpgOYl;3oKbzcVi!PP`a^sviT(-`LlQ7r=9Yp0IXw zFBrqWr^?wx=E&)rjuEaa>ylwj7~{iR>Bey6Hi!P<=-LF_vFLaZUtMj=wN#eWhD^@; z;~g<^61}msn+l#90k^o8g;R$_P`Q-R<;kbFZhVCPF2O$tM?I>rW5@YSt$=-86b zu?esmFs7u0EcX2tX>EAy!T6FgM)(+e_RYqsT;I{Iiox;X35wpMI`Jy34}81%#xbtt zqZ`syE^8kyTpvf}x=-M1MOp*3IjN{_|J0YOt}M+vM#OERH}fv2{45F5SJ3(LtA3xt zY{0r!+3mBDPsxpyzK1_QzTx%o9ZT_zGv(bC67>tveWZu^{Rhs=KSseqa;-QHH?>8lhOIxMfmF({;Ak} zlQ8}$hQBkW`b>9);Rn9XinzSJzpDzXTT$KHM(`7{nY#zDzN&GzFQpw$C}?tcYLj=$ zug$yaIZ4DnG@=u)+;<~7wfMsEj_f#zud%e60&BCg?F@fuZ7t#st9;3$o=JHJxQRXU zN%0ayJuNczo@~HwJfClb?a=X{h)!bj@)vfFyP__`KicL@S4F7TQN5wIpKMgOBCr6} z1+&{G`yW+GP4*wGU>{58Ep=P4DY&WFLIR@_{!{CoH;Y4U?^U-f;yaP4G7|Mc(vuy@1lG?}4Wj9puwo>|m^kAZl>`vIq{`L*3= zbls6Ng>q|(NjFNxuhPx{a^K87PF@Bdr9$z8CnzfTyzE~vI{+@X;>bQipSgC{inKFD zte?yNnYII9htfL3+Vl&cNNH@Ea4OV^55iSW~_^)~#u=l(j2q zQEU33jKM7^MkjK>r7pDw{JmI7wb^%8ak(+_gH!36@=dzfl56+k9a~e?=f|2BtFaWG zlMEET&7NMKcrin$_DGc@_;J?Ru-Qe7K<|;-+9d*_aXTn)K>gJ?-O(1l8WuewTHhd~K zJsO7-c{W4dO@X(p#vUx4Fj~5)F#@CtAq zQh;u54sMd<@@z>&;~0q+eMxqV;^O=6eIcTN_n!^C&8e?&{<$PFa&zsUeyFw~yKuH6 zg$0A`LVT_Xa^*tbY7Aj)N#yT0NbQ_5`p6xTQa_6K|F*e;pxSV`4>NPWAG0lzIql1C z9-*i<5bt_zB!#~|_>sH&m)TpV%;UYQF)5eAi*xN6zgQ*y-139!30d2$-*R`0x9@s6 zp1WTR+uTsT=#X&FzA`i866dILh~FcqZV0->PKitGkDYktxsdU3NdyB$T0!vR7w@=y zs@#{SAlgv<>Ek&qcSxJ`>nT1AccLFtx*$6(_rRfou&MS1LmJ9fn5RAy`z4fA-9B_p zQVb2fdv7S>v1-kAtNk01Xvehv6*pe|bgs`m*z66*QhiABVFU(~JU(zojNx>wvyt_m zQ_0*BD8`YVs~6^Pc|aVmfIr!;FZGBfFc_D?hDX(cuN9A~`f1{vszl(_i@;u@0gVNX zZK)nET==(8l<;6};=pRmO>>fMWErW(v4W5l&+KD9O#ehQ+{$T6EhAiNruMrk;RRL> z)^RVqg5@CxI1gZLx{m#n2tClhZB4Qz2T8@)WvEAi;+2HJyZsm&SZuy)3OB4kS;6Hf zsQAMDhpR5XggWyR5g#E5W{A#ns2@U(*2x)}L50qy@uYWxx=2aU2danNZe8GKv33DU z4w|(R&+iGzi7QyNPcT1K$=I=`YS|a`&X03+Pfc=M+4oqdB2N_^I`k#}S31$pmqcQo zqq>|CeC;uGIsg03Qi6eN9e4LkYWbLb5T~BFg=CnS-oH}U zGD&IIrmvEu66a}IJJ0yUzg!Y&Z=n${ej=)=Z6VSAK0rfO69@6Nzn86{jOE!lrC;!W ziY*xp#O&tzn4=bJIiNE&@n4BZtHl_qUZ?xD5mJEVx!&E=8}o2l6|mv9)_fEze%yEo z@{2F@nTp&iO>6=j8om@=_QZ+Nxu83W{ew-x9hG0*IfhkO^f53#WXB=wD&!DaT}WkC zRC=J5b_`CmbxpFhA4*sr-|~Wzt?C9Y*&Fia{hL@I6)Luz<(GjHtX@^@SO#@a!ICiv z6)ZdAnKkM*zbV6l(Huer6l}@8paY>)e^RF*6->jS{dMxX1Yf>!;N_{or-th7igPn= zXA`6=s~rP^|7{M5O2e1LH%Mx0O(i>{pm^Y?;9-DR+y64&iUPj2_!GWaz;3NdWaoRH zi}|^JE6sofJ4Y?rh#eE8dZ2Gl15q$j*dop$oY(>z?9?Q1f)OJ_i-U#1IKPn?^y!r+B9GdJArTUWa4tg4e+^{^HKOP#^Rb*D;R3E zDBD>;Ops2?m4l|U?ux1|-DXChT#X z00Qa$5vxH|;9s-=jwtF9GI;4gtMcx%e_29Nc2Xqig~@7rm_a~)vC@iQ^WbPGVl-=I`)V9g=G z88b#%S4?Fgl@WNWbb^xMG%&Jm@F<*v!8qGaR>d$-=h?~*;*`O(jCUd&d`AGeHh*v)@xfc-de1(}p-BA~kY<3if@R}X~aG!AmftQS;~{Ga(? zZhdU$7TA${YtwClfVeAbFG) z)dBil$H4RWhu=peU6=S^Hq5Vva0~H_{Y+UddDaiet?9JRE`nc9^BM;;H!3wp@v#Zl zTArWvf}TE$|4$8goE((Xx`%>UHD<>1>@VPEe*h`Hnq9ypD_M~=P>ajohEk`K48kni zi3tgn_3Nv!F4s7s59i>VFInHb=$XC@SHiI|BW>j8XeleXw+ZgtawDdmC@ruD3dqMp zGRO^b7j6hgk12<(n>F4k7BngN-)wP{8;i2(X3T8-yY>y=9&evL{7kAj2#JWS^@S6{ zx<+awZ^~T>3R?Ls$$!)LN_Lcy4qH@HKafl2#)>axVH7R^@Llf0O{pK<0fdH$pDq7w zJs~37FE`>Mbg$a?!cIcNV2*03wtxdmv9HHWt9)PdbkaNfZM?>yyDb@ZdRmMn4J;JZ z%_JFoGyQZ?{=KgaQ}OojTkY{A15|FSkBhI;sZ708w@vk4ceI6MUaB$1`JUN?e+d07 zr_iu6|i2Yd7rC%rhCcV4?Kqd2w+^HL-<4G;wPfR2+Wk`jZ*8Fz|Cd<#Ya$^moHdjfk z%yUEqCbqJUL~}$pOuTm4aG#|p{jP-@OSscFtiSE;>`2n(BllA`9EIad1j=ljT<)zMcOa}~Z`F&n%yCy5^VZjkDe>3SkC zxC>(3KTg?m^;i3pTVA!`$hX}g`S;5vZq8EvJskqsu8)FaNzN04t4Uf4U+3zbkMx=( zks|MKS93Nk?ujjLy0eR?&hSCZO|hrbLpBxQmbt)U{+V|sH$Qw?fBWZzTmV_`J+rpP zW7aMbEW#^$`S5R{#j7-ds{D2e;h6Mf@6-@2(hZ!;vl8cV`l%FO#_% z%}akT`f)7zUvi{%q+`!6=SRkt;SY$0J<_k`{9O4}e7B|~3)V&?f0wEyr^6ATPlMrDf}{*)4Nt1uS`$rmGioxJ2Kd7+ z8io`ET?QMLNGEmDLB)<7k2i5B_^8VzJM`$xn^Uoul{2J^>0ozdJB0Mz)-ZN??jX0| z!>&p<^zrRQmp$a6a&N7%!|+4{2@k~(3JyYo$GDgmS#R&6;9&f8vFGsqs{$^_1^EBk zlfqk$a|qdx_>(GL25VKU+*mGI+zNOoht|aj{R?%JCActM8!9B*qCoouzTF@V1t-;* zdhZHhtowooNV`WXF+w~YP9DjI?LPP6mdy_UPGL>o>K-)tIXum{ZR6c$2XTW^CY>FQbz9`|U)FW&=Fq@bYsmxgMHa`O$%gIGQ!ItX32r>$nl_ z2aB``VgukNWM=3}m0+&864muL7)x4$f5TGq{vWWwID86rcknoJ$?Fc1eRLTh)Q?Sw zdFd+;N06?Asl(H?)WN8;q1>^T36av+!EBtQ%}g4(4NN9c`wvtuNLL>hEEX1b=QA~gu6)k#A7JM=rQyy%DVa-n4Cwawho3?@N~>TrjZ3b` zdx7ef2r|KVvafm~3dY}Q%R=~ccHacnWjh1NITE&~l?9$~tVp&pCF8l)(i(-9 zOgtlLj0u`NVVsd0V`x}E%D`zF)E@HAgFSnrrsF^+sw&5%Pt6#i%8sMJ!U``CJ+1@Q zl19!OklT~KTynYs()7u>y98aE_As&u{=97C*S;vv77;^EI?j zKck-bbUO8o5~q~p0D}+CFL3`Md!!yjS-{sw8aSf$0Jc}f-xi{#Hiqhs&Ufp_!Z1& zjXz;eIxEhZp*7ibS|ia88V%pW#X#b(`A|OCZ}Atf$)O_80 z1NL>ye&&+5r{KbO-#+rc6yb?Gyd(24*^7YIM-S}10>_8VQ0Fx~SCUpi2owVwRrcGE zG}@9lgj&dKKap7mG^lAh_r4O~pjwhcKMxR1MZ9{gNxOdgrWsJ8IUmTdgxN&3z!Zn> z0s)l9dn%IG%_bB;1SJcD$|)-Lpt%1j>sI2z@c>!z;NIA>0#9{J8=m3m8B+Z(NB9DZ z37EA@QNvV>!FAt^oLbIl0cRYX4{Il_1(jt-ty#sP$j>SROv>Rl{UZ}tY7*?4bF{4n zB2Y>8>cbyEDTl?ijHin?%GX0%UkfeQ0v=V{h{bWx=2b%x(=f&u!Mi{Vf>u2ShoBET zAvN#8Ax}#i3}9z}?%TvMAkYlc;$2;n*!sYKA~HliY)>Api;@gPeBbk=Si=0kU%eaj zL;XR}@m;Kk%)T3%HroR;VF2|>z?TSMWh@ZNlEj6z1VIlb2)P?rpav!e`o1%O;t6{>sxAOT(ikcH`x@Zh zh>|373nnOcZV!MnP~Xxc0twlg9aEf?z0lqgixE|XY)}!~1c-6B`HWlel$HiQXE+~N zLJZr~aQL8Sk)BxPf}A($hPS8ED^;{lI&R6wvwMp_o8TDG!!L}?WKX5HwSbsyE1lk4 zOrqzBSML@#3`LHO{g#~3dq^*nnZUl(hxR^ku^QmS=L1iee*;9*zrZ@}X_`U(9bhLM z_xM5wP-%V)V0+$v*y6Q#vI7uL{RQg9^C0j50&b$Pea2Q!2?;5TTuaD=rVY7dR@=HV_+-{O*=h#@oUUdl#@A>}^2By*T7l3ywy z@t>THN>Ity!YrT{i&q!<=C&QNG9f08eYo5MR=)2+zW4dT;ocgVIH^839q-MuB}`Ia z*V14BjNdC&o&a%)!ngZEH}{8b{y}jWlua-K{Uv7r*8e};Ne9C@++~=LUED}ovetzg z1(vu$p>W0IxlLZ&Mg{FKA1f<#U^t`zoKS4%UU&yD(Eo70+w8Mf%y<_cd&AnCi&1ta ze_Wdcd^Y(1TdNNhz^RHhx&=F7plO_Vm<@JUGyxNd<`>!kaev2)Jz0n^If}wwJv^x~ z-GK$pXI>Awcnly`1z;{^V?ph=e+y;}?5xHZYFfKFz~n{@dBU#+ki7+9f6^B~{0>*g z9Y631^rEHz(o#Qb4=&2o z%~!uYTKPeidhI7a?~fI#nUBIP(W2UL~0 zWb4!iH4zGLwH!j>wG<2Qr6r{B22t&=@G$0BCPt zwpa)*w#?+rScdh6Gt=H94xm72VM(?$nQV#n@3~RqS{%{!p}J$^$lr9+$uDV`zf-^| z61u#k9orYO6DO8hp~6W0$1%gmCX>9BSC@2Q!Byr@8GkCQdV)8p(fXUTGE_|0y%SDN z$(Zo>aKrtv9d5V<|4{U}Q}kSV4CuK`z7KWUjS?smR8mEmtxJJ2LB$2i%2z-igJL_v;wI!un&i+~*2fEa`v z1RB)&m+%oY|C5NQkzKADiv3AHJr z-q9-OOl$HKO)H3w!=P3W-KT_gIC~LfyU^?=npRNvMPfVVLP8kM+74OSXjX1b>!-pZ z){ddApPD%9BxGf$Sx+^spDzn()(BlYk;!vT zBo81-{DZ)sEpiKJ7$r|vau(la1oB*YXHUuVWUNS@Pq5|OYVtH#D3E6kkf-hv>6;da zu>wx!VdN>e1#X0tNJ5_T4?d;)c%-sM5TK_QLJvX8`vr0%C{#et!v%69XpP(mI=uij zg1QThpq&fo2|rGeI>fk2+L zAe6Npvfk3H3l>5>^tb?aQu4|p~aG0_M`B?Gwpv5;QoqrEsLsut{P<%6z{YdIf3*g5KQn4)HPl{y$e|;d-$(ppD z9;G4ISO|UV#JvVkBhSoK)W~;sxJFu2?W}bv+>g}PUr)&XIh{Obk}L8UYyXhi=(<-? z8$06HOK8VG0==90jefC)jeq!fJRzl#+ySS_gTA{rc}j^IOrG~|(OT)P|3b^9Q;E=% zPQ_CazBZQ%1&J?xoiy0J_go-{%{z9#ur8c(kXRgE&*7eM z`8qsLZ+{OJa^_%GA?dephHr5lzQrT@EzZF)l#1o+X;3W5o}}wu%%misZ{-PhA*t6^ zkkr^$G-*03XaNp)g2G!x`U~*G z(RnDj$jQ}nlK?u&iJVq)u|1%toSg6Cw5&T7-vL1I*YAg>iobpbYpLjk5gYRJP5A!} zg)a&Pp49z|Q|YC9)Nk}rdO>-&ZF59*y|q$Q_~`CS$+buI$yK$-sl?zlq~Pb7EWY)L zoTUD&-uM21U;E^v|4QHcbc4S4DLz1Yj)wO>9cR5I7rwqr*N@xkXYIgV`$Wi5#XoBJ z2jR6($-*md;>rJ$@9cA*)(QWp@Eb{){Pp2Cpw<_j0gw2USE`phg|L@A!I&Yu2+OR2rgq@zV`bn9jVVA0;bljD^a;8&u%8|}C^zu0S<=783I7Tk2z=;8&SwTHUyf`& zXKn|J7%SFyH7p0&tdsvq0*Yx?_G0gqRpekOo|DPf*h4Yp6mXdAqf<_TJ*R-vtbj)A z@>|)n8XX%=Ska&EYZlnoh$_Y|4L^OrecH8DAmP~sks-O#b#sM->lD+LDjgL;LBiu< zedua*`M*LQhZI%EGod;1E?xZl>coAR-BbfTT0t&0RF%-5GQ8;BXpsXy;Lq8<4(C!YZF@gbiYoht1M!`@`np>>;FVWi1?+g>ccg2_r3>*mEZO!=6TI`@=3par?t!qB#9wE2F-@KPQ1!ayh(jRuPzqaW#)(!QCJ-PsH5b$o*lVOsr~FWqU3JiM{LxVyR!%Vw0#?);FB~uv|>_v9sJC=7tp8 zFvS%bMNg_&;SJn-v*yCR=hTk^XBq`g_0#6}hi!3I^@rKOF6p*p5jfO0QqdpQ@vzh` z`!|%|A67#S=@DwAKTIdb`4%?OAC`DX>JO_>6CPLYk5{zyY`@qc2Ih)1yFV=ddXxQO zUOzM!AiEzLpKtpbCNgK>&IPzSPM!1UV(QWwZ07mYT832&~DiDBsGEdj#bPB*ft)` z1vmmJ(TqS-p})m(NFPF=>L-lP1?W0fI~SnwSnXVZ>SMKY0SxeE>8RgzKEUiqGv@M6khw+@_c~e4tCo403J-NIumQh z#O{4ii-l3KOs~zH4=}<3<^$XyGHgD;wVu*^fH5EJ@O*$7_VRpy-J|h*fDCk#rCyso zAE5kUG#}u}7_sB8$ZNRia?gO9u2fHo9*XdULr1vt0sgXAQD*6Apv+&k0%hz*D&_;E z?UT@Oqz8XKzzsR1Ts|Z70XFZG$dRK8kYnX21v$>Y$K-f0!i@OS?8(8d$GZC(v);bO za0{^BJ}>slboYcO`JuL!z6!N9wX0ZLH^AoTEtFIS)z)?+gf6+p^8sx5CVa+%PMt^Yhe5)`g}$AFHRp`N+|ooR7dbaz1Xeb`$w> z0^TBrS;448IY03LaN0-e>$7_;bbY>lMK@TVe|;7mrn)}E-=$_#rd#_hV!xHZ*o4iAK+b|PluSgKEGr9c}sP4 zoC4rDmEw;-_ZcFOKS$uK&X84xKmJ^0h+_PCX*KC#c@U(P!{g6A-`M|u#-EScf#3e$ zvv58tbr8?T4A_!~`q_BlV8!_Jtf6T9c`>B?$xca_9!^QcuH=-s?>9dF>^GP@{#<_W z_m4kEhyO<7&r>6ek3ZkMiF))44K_IbJoOHW`#i|>_;d1klyG{G@$u*9owd)m=X0U) z=cVvoD)M}5|H}CIbKD^4S%|Vw_E*e(iQcc$nZ5t{rJZX0`A#@_NVIrmZ2Y+slYefZ z9DnvCTEh6VQ)hAf`RNNg_4xDKbX&#va}T;j_g4nTpV#e1U%~Y~6S^!M zhj*fnp*iSdsOMhp`16r;d*%3ZF8CU{BVTRVHy;xwjz16HDm`B2?Zh8{ZoX9-f4=St zPnHb^DxNIEl5ou*A7*m=dB!|*$Df_w*!O9}G(Mx!V zGp$^UGpO-iFU$&>BD8BeP?=ncCU=EoN1EIjlF40>EGFBBS;2snpZ*4|mIEpY)skkv zB5n8BA&~7vvuSIL7R^d8grZ4q2W9tyzy3VxcMu*bs?ho=bUHAQmBT#_LMdH)827qRyRis%!sQ<;*&}Kfl{Zq zP3#8xryP4TLT<%@($Q-iyXhr8Ry7hM6=Tu2`qR&4xVaDsZ?G;pZ1N$4S=W*9j+#mf-*Yo_RP(oTiv_s2RXzU;zJ@O4gHX@6^%3j&n)OmWKh%a_&m-4M^*nnPsOLjN74>|@Q(Vt?^-`wU9DxBd;CLPXkS7t5n|S;Px?Lol5)u}9M`o7{)B%Y;cn z_n)y=nICG9dC>-d^lT~?RMutt59+dAB6Qh8kL|ab&`PMqo=TTA6Q4@g!Opz4P_+_l z`pB&|%YLZUwhQ9lz!m8Da1@;4=|SX?Gn8EUa z+zPF~+RB}-nNx%!R+iS7Dxlu(1FZN*8(c~5Y?{hEX~N1s;)In5Hesdy*l!v9g$XO& zKiXTwWMZAGXWrY26ILEPvXdsPtY`&y=iOe42`lYWZ50z%`aBX^>yAB1F>3NcWWBL- zsSil^j)i$Bk*C7)CwOf9lwMc=fYvD;X}9(k=azU8v7|sB{WFqa!$NH#^fe82N!P`Q zM>EEUhLQpp;6yHQ5?LeR-Ub?(Aj}_`+FP7I^4ANvGfoU=?dO)X_+6*PulFV>et(m% zT34S;>lQhAAK$Uk*G^m&tWhs0eIMTnrEhnLl|>?L)Y8(nZWWX^Qj8r}_k`|=KU*rg zCr&-UrR_G&eGAth^i1ee68uU_4?>DMv{LAgsIUJZ+`ZO4Xwj_a?xR0T>rl%__7(wR z9rB5n%8*YoDM9RKs6aj`P<`qWfRJZ6T6iKoq3|3(0fnbnV@}~Ii3-o;5GXu8@##8! zFDna9(dh- z@>mO*bh@52J~;Tn|8{(^u|4#eE}1C0+}f|Seb z+=oWMxsPf_&V8Qs#>WTicGHdzmg}Y+AI#Iu?D4_TU4Pf{!9gKrjt}lSfpC$bo5AtH zK4(zef58UF2YoTnwgwv+AKZXZIU(4{_+Z9B?eq4CcL-ITUI?fvm}GE#uz9krc6_ko z9T?IJ35M#qt|>ffJKRwZ=`HO_Sd$}39v`$#w$+Xgx-zjmOsqE(i@mKDYevOflFS?* zY)`gCw0GYY#|NJ_k;Vr*-&Twd4tQlNjStTL8;=h@>_Rr_nPm3(VAfS=eDG+n_#p3* z1UKF1fpF7RZ$eqNow97{YVP=8$yfHu@xjr51Ihd~mlM z(ki2o@xdvJrSZX5dEvPtva8~$f75>$jd7jL7#}?IkJ0hLALGUGBB+M9dk!-q`Mem{ zS?0x5oYfz)R`Yq$u(QmIAe=P;vexo>k)yMk7n2J~yjTF4>oG6baS`D3b`hm~c(xMGb$67Zt84cu^-o=0&#-m={~ykxiCjSM^Y#i}jJSe%F}1B3yqG`6ocBu@eb=5DQ|cr0qlxOC ziL}^_f+n11N`0NSiYfK*s=RtQyAfoMq1oLuQ|hhS%2VpI;;gQaMcpci)5er_Ail=}U-B?3)`WP03SDS1ku<5%p(DfKW#9=$xTu`s1x zB9r(BKl#mjP{l~@MTQ}2l{?A)i6`18_5&dsZ^IyI zz+f1AS>L&IUDY5q*Isx5A9uRFwm(k)f?TYlSZ~~fmSS(*YdA;v|L`nCnd%w;jG zSFpMeLjhIMrgqOqRx7DcJKBP% zU8ibka5u8+NSGo;{dt+fhQO0hPd2W?RG?q8+H10%JVb4;HBBXL4YC!dfJ5~ApR=QT zw)i1*UsBv&eIVoZV@r(N+H?? zVf*B;t4>6z1PQyTG#2z0j0IDtNjM+<4laZ7EfhH4dR&lp``NUrfR7aI0k0|94@EGt$FV-dQK~+~;Hg=vI#^hc&#Nj97SG7%9{F4+pY!BX zmHfUV`Ai_6_2hGbd_Gpy`t7`Uv!&Y7=dN4y8J>-xCX9EJ<_BeoDMHS7-Dt0Ni7*nX zm?!1iOr9sz6z6!doDSgDRL+ueX{MMZH8i^b;4CqXSW@T2DFS}$oi#j1s+?+$RB<#% z$~|FaH5k*+vJ;r<=E<1q5Yv-wqH9uFMAT7%ipfz!T1a0cU(6tu@He@RTL;5+oU1nB zsnu+GYE&3)sgOe_AhSZCh|IQZrWE02zCc4$qEzj<#sc@C>Gm8AkNam}g@d%C{)D=b zkG0i2;rBu0B8y-{;r$A>sW^q3x6uFLVIN zJDhkJess(k2;ZPg83=byvL+PI2)3#t zO+j_iZMHXbBs=c5tk{bw251x?HztZ7IzqeyBDo+f6)r}bS6}|w`}G@4mPL_ zm9FP)Ri!%&JD}_NpHS&aHm37E6L$#1F}b6q`ZUP_4@TFKD&5CP5K@7JRCR!6#;YcZ zy5x2O*QF9{1A*{_U_)B$vMp8Cb-zsB@YdLZ-~1-VKC zx9GksB;5CnPD;~!ykPLzOSt3D$X!12O)q!( zxF>9^4EKHZt#IGxucW!}@k^}#X(S9kd!_5fHDwQX>YKi6XEwi}Cb{XIXRs-%(jZX1 zM#5vT@C00${;WSDzO9D_xX3*bU(jUN-a@AJhZ!UCAhEq#VfYX*kYR|9lUj$CKp2_~^yA42W)m2-Czxn!A zSu7V5>!K0+RG)}leJ1;xr!ukDSCt@+LSlETf>?Hq*aj+g{u#&L?8#Iuui8rf=E_L1 z0aH90r&bK2ipkFa#Jk$Uoo7>(g19KHcWtB1_cvd>qC#^q*d^V?O9Gmw_$mC&qar0V zM_1wdo7>4D!3lPT{mm8SxM!9o{LOZeQZvNmIW$Ak>nm_y{2<1?r=Kx@b1&StSgVg2 z?>pD}fm%7BCjj4PPzUn}wmIGYQ$PLsj&P+Fu?*qWP(`KmBer3ceJ&&dgqHvT2;TE3VuEV>}MP@;q2LZR_r1Ae;vRCGe|$~)f9z1pOL_bi?w>inG!D6c;$zKp{~W&u$A8L3 zIQ}O~89n~xeP!=Y>H6rg3LSsP)^PlfS0u-OZxqk@lSHlN#+*OG^*HuL6~E|ah5ZuV zett*y@yHz5$HKxsx{|H@L$|Vg4J`yjsmc+wuaRUcdWr=4dqeuc-cEfkrZ_#joKS{s zWAQA88 zeG};AM0)T4BzkkWdJzWo*zq0n4n#dxg6PqbOw^gqH>aX?sOYJrY)G+qU3v=r>C5A& zz|vF@*tQx3#+H=#Y62C=$la?ab*P5nGbHcdqZE*z!46_!B;T0IFJHwy|0d)~y=zw3 z85Vg6#J$#Re1%jE{jIwCczP#xgod_0*)M88yfsh%0S4#m(%%eY&G5$bZ_vRDcP{zQ zVEUg{^gp5Lx`)1Om)-pJ74MSv`M!mO(%uIvJR;;h!k&A+Eu}Z?1Nt<7Q7o@kov2;7 zY8~5fn}mi3Kcy#;N*h~8Dtp<8+7zm`2-Ip4wWPs<>ma$d70A)^DE!@vZp|$-)%MB( z(+d2es*sJ;ok8$RyNmn_6M$M(q(4S^+wRQ(LaV^BOnaxp1IYIB)9ulPbv@F(4>o;7 z(Yyk7IDon|91|+oTh!Gb@z&o+I6zqDPZ~cDgcg4&bf>@&MdD;<7!+8yj&7We>-qn&VA+Jw~yY8j`0i zeI45mqv+!``JZ!`onJMpMno*!fiyzWbzR93OtOQ2+SQegQckj!+H@;Rg{}0ahbtd| z`6_KHMU#7tHeA9;U`{d6`(WQkG&~^kNtko}v84)wrQ>Y6&IZPMUWPg2M&w)SHdeiJ z@E-YHK;%oOaofLh4LHZ@hQ$v-ocI2m{wX7_{n!)HoO1iFnEyW(hlScTym4e=MxbDBwJ@B&i z*YBZcEg&*IyaNf06$8Bw&ZJ>W=w{P1750d$tAAZr|B=R3hW!Yr7FO8VDf$F`{;Os+ ziyV&dtn?2#{?g}pO<-C-{diLT2Ku(|atJ@TkbUz`zah@9W=hC%0AAHh5Js%gG_+G^Py_){sDHE|7+Ln7{;4KoJm7L{UHy5@nYq zQN~LU5flXl5fv2`1(h`kYgj|rcR@j&IP7a!=U<=dzT0Gc-}lb>pWi!AGWXu@>gw+5 z>e{-RiY5A$^YT*(KHa{}5JJ>XU-GAXeEOX}`5ix`3fDn(BDzzI-xilr$s73w1g6Y3 ze#Z;#a3o5n^38nmd74a;X?uzv9di~&i_Cqsy(z1R)bFQ8r*)H!3aa@5jSl5TXSOrZ z;6N(4`ddkZk*BEGBey`)JKUZ{%)GrHjb!Bwg&04~!YG|D$-E_3i@$?%y@TR+<@VQ@u+Ih|ECIFCXha94*qG^h%uiT3b#+HNP)h1cec4Cm=G+xG-)c zi^=o~iuA5qg(W5Gt+!b;`hP^H|6CiC^KfiEp5bvW&tQ=Kq<28}pQor~Z_iG$2?M4i z{TbZQeqx^9D|K|1qbz+ZW=CTIefB$s5mfp%bxZ)9*$l_g>how` z4X)%%nN>UFb_#{n?MIGov`>ANx@OP5c?YzYM)KWY;^N+s73hKX-X>nmtgj_=rR!}q z>7K~uypMF|y}Tv0OID3qUdh0!vkQiHuA8waYqd#s5Rrb1-_Hlm4HxG-US8NxSfijynZ)j}<%u}xorZ^A@$hFN(r7IKRf5LLvG zZwfKZ;UjHq7Q=3xfJdUk1%yO$%yU@Hb!qehrH$X)K-x%G_WK>z9)`5RKj$W^%A7qL zX4!NxdpxWMTWh;O%Qk`?2z=PhLIPVl)cS_Xd7dlJaMDp@d>?=Sb6e|BAxj#ePHHV^ z>m<|9X18+Q`{g`A3X@oed@w}vB9%$la}M4Dv!T9rpvom%`RTloGdF2%;>>ILbg-jn zuHek}99*60oxl8szvL;tCMC&7n)>RWm`}}RK6RZCDcO3Bw~QiuT*e>Qg4g{dUjM4U zUWM0l#p?z7>!0vCTfFw@uYbVnA@o{4e~ah)X%OY}*Yup0cvfbuO?s>|Zyv-e&&vW| zp+XY@x491Y3mtR2_;`<@>W^IJJygATmA_s~5B&NrzU~!Y4;j8L<*zyT`ZIl%&y(rd zH;9UUSElF~6n#ekdbiBiQTRGke0|OE^#}g?3chwlGvav&JwvqeDi^4{GhJAx?KzFE zV%}*1v};9FP)&fVT?X7hfb(DBuU7ghUq7o4)*n}}6EAJetCXZ%miingTWKPI?Nt^5 z%K04w(bUX4V*Xv)-uAc-Y&HX1-Adcmv0F|<(Vw~K5uo$(Wo{yCDl9KMA=-}e7S|G6 zHOluz`CqvFUnu|ZWqqUW){6J%WZU(Y^rI)!`>d9x=4zn&uU!2ys^7XKHfNVIhXt;j zqgfAP{yS;KuIe4wcUkPu5b8+rA@%EoFTz#0UM#^(*qo1MWzecBOfv1xwOq-&?YWIY zwoz9#%Rnm=dmi!*+zh(7F98N@^`Qsm-za^;$7lkU-y7jn$o(Tpwa zA92nVp==N>^1k|!U>i6?l?kCX{H`E5`W|^T~%F5`3~_ zAfH^>Qt(OI){2q@pQN9hFF+#vGc%cDZ0O}{${qA=m31l9L3_^DyOLqO4;K&Kl?d_n zx{+(Wo`_jwhkne%^ME_G^mw8=we}nl@txF;RQ1(sc;337rs=JIk?FAkUa#QS@uyMn zd`aXb{t|;PtN6s;^@DB85l8)A?4qC~V zC0ZLVn9&mFoMTF6qAl7omp0c8CyP)$pQ~r0p~=+H%24U}8&1NC-?5dKsxU~X)rO&3 z0au%ZYTeJPOR&`K835f7uGumQe8=WCPFi3$oFaQUHE;D@L1A?GOG zX~y#QTv)$$SqoSSZm=!+_Ke?ecy=qt*qkQRP+Lw@>6;Yct!at=bWb1Rn%gowLyFUL zyHAAcmJhiMNK?-9VHc-z2!Drre?P$nz?n1H09fZPnm5~->$`A)P7}M?>P5Ryby_9|zE;dQO@6)5NAC>JroqiCvukrYFgY>(k znJWEW`dX8I-~67X-(HIi(r-1tqnB78nYMIAiof?3sNyf<{Hl|1o(7yN17{m>Mu4zO z$%ynjer&F}xl#C0t*`@yyG8KC-SZ%}q7$w*og(4c=pP3xeR-vnzRhHItSL1A2Sa%3<*M4!6i}|aa;`CAU#F^w)dga|hMQ#c zxlMWG+hXIw21NAVOhy@?@=#RHMdjwG92z0z{vvPZ59C;h?zh(pwm`wdtk7a(--Udq zlVG2n$Pp?SaHqc*hPJ1gnMTl}foKU370ifUYQmPg4NZ7J)7x~6%xaOY^y2|-l&(yO zd_ph&*9>Fv{wV$)iZ{bPI$9L3qZj{m2I5z#LM2gQ5Go9?BEtKGk=;M_ z0BO}n32zz>pk9#q0`7$!3k@r|sezhmwxj;e0H`d0T5gr{$qw)B_lcS$9h9c1rCvv= z=~iLU7f}?4)N%f!v5mlQd{Em@QFSe#-e5d6DD&HCNg~oUK&ttw5`Kg)joNN-v zcRpRM@g4i>SwMe2u)9jXl#E!98!p4=nuy4X0rIe&Ech7;?hwWU|01m$?r*6Z^$DH5 z^F=o*ayM#h#3KS*eZ#22_is{Po}zL4;!V^%;8r?PUj{c8<_i%EVEcNJ)}d?cV5spX z!#s)(h573F9d_BFXmC0P&L<$Zz&0d+9D-;rG5#DFS7nTCR~y=hn07IHe88=|8V6~h zwu^He>`I5pmPW;I(eA&a;_OQN#8eWEfJ`b;xywy4IZxbLGwwQDzSmBa_Z3 za3I>n_?1T_jM3&Wjaol3V@W&Mmb25Xfz8OUoeh`~ZX1Q<6`N`C5xM&vz6NZ8^B@Dl+5^8^EP5*}uJGz5ioD#$ zY|_45-^Po`ZfM~|{jwtE_WD9d8xSa@{Z#*7gv5{P2N6QEW$EF8rH9l9!-2c*+56aC zr|kFIU#6|4k^za10IA%Tp(R2#f{|#2Mfm&*n1qE4g_A|XX}LsNh33`05JX<0c}g$A}2(3nGJ$8bRHHNw^SWTh|!9 z7B51B;$>)9Zw{kQg&n*v!qTXh6B`Qat*ER9OQ+ME=Z$CmR)mCAsjtPM2|v&(pwF4Z zz8q~Rcd?!thekbjt69L#TR3zq^WL(dV~dq2b{WTGQ{Vor3>_<^;V9`5BK)SQB|q?x z#$2h#QB-TcH^*k3{a%lxYJM+AQjM7#7}6YD*JfF@;iP)N6uG}hV)KV)QdeW<3c$}Z zAd&HFjsfUA2Lr8F2I%TKh#osB@2TN|<}v2M?t-wZiWs-#ZJ9}a$8UA8Xoy3qNv06- zE7+XhaX+u9(bmMHxZ_HM*uv!8J{Ic2R7pzVE~QUHUj%IdZR)YMf*dj@n6H0v$Ta2qjieWGo=r#@HZhnYtCp+@_eQhr!CNyrZ=A5B7~gS1l+ z@n5Me_FTpG@F&`K`{C!j-A2wdY_}voEU2r=562)s?DFCuer^B@m4NnEN7h~;u^v+(+G|~&i+)% z_@BBTJaIEeuPYh1!A{jDKfh3W%TxW>4-&-Pb;@bVHKaYVm1F$z3 z*m(dO%)lJOKZIfPpRCFRcprL=1(LLle)+n)lr#u-f7Y~FvZ}b-42sP>LZ!a1sg_zq zzlhBx+rCAKN|)@h8Q9fcus^tE%z>FlFva~gqB49pK;FId&! z!|iUxi@B&)6Kc$z`!&KswQC7fZ7f%-fi0q$&|~f!A1%xSgvaMKnd_Hx=GQ`rvqF)% zGe=`YPrNG`QM#ECJ*+aKD&r+1iXCrYL_=z-jOfQX8Y2pSml=`&Ju@S^FFueF?f+h5 zM6vnfxC0G=UiUEOJh*Q-UO$&EOEgA_mxV%by88dbd5%|?1{3ByZ-1;AOtyPFFAmCi zzO8OE>hbfcv;S4Yr44ZTEyT2(M^$G*wqz)H!X_U5dNtJ1Z;J{cp=OHeSRHDO-6J+1 z;u|%!brG9Co*U|jdTqJWj#|TX$lI%{kXj&CA*ZhL#`NaF?d2UuSaGuXDG0gJ>?$8rJcL=*k9!u~$ z-q-UFP+xlB<78pW45{`%P4UgD#wlJj$1ue&ei?L%w^TJv@ye=ViW8mr0|^aAm)6+K zrYr{8v-2z#sm(1HqLttA7F8+v(qf{55mlKAx@75dRjV4$Rb6NP3jjP;0B+M%GTy8g z#rc1#=<92o#0CP?)R9=u0@S{$3@Xx@Kb$2ow_;gyXl*;| z-sZ5l!YVcyQaHXGbd*Ab6u$fnvh}%0RPHq_*c?BV^0bn z%OS*4cOzW{9`WJqT6rG+7EUjQ@Xv9ZS%YZ$25S&`x2>9f@=rMFCn=+aeqwa8+<&id zvUD>#S-R;?7C2SdU_g=^J6C!YavRNG;5K^8HemMLOq%#=97$W-`Zt=<$S{+Vvckc8 z#_uzJDJkRc_q_e(I z8louhJD>t0CZAluEEO8;Ws+gquu%Q?T2YO71KskXX-Pu}pC zd~J5-d|1gQVH!a-EAz+v*_Y*altq{k$6s7IZA7M+|(o%DIA zf<0R#7;g_|dgX(EGL47K|f+I~s5gV^WiBM*s_=>o+D@^)q6Te`BP8D9acoFo6SSQpnR#T#+{t z0KQsBMW_gX3pwy9rq(o#gB{C}9Bg~8iG!`ll^pEUE0Tj9dBwoNax1DFY_Fnmuo*eb z!6xOJIar&@fgJ4mXC@BT4M+|Kk_UlgZ9>wiJO+0SjqtKcHhB^Kei~nC#?vU+tc(Bs*05p z4i5^t@=7Jyl?>&K=t`5%IBJth5oSk$**Ru0wt;qLt+ZKT9SmR=+l*N(KN6$7U%F?Q z&l&>8^82}TW8Ie{CUJ&-o)cq8Am5LEN1H7ILv6Km}_^X0gu)G0F zp?6OOn?)W08OU15&XS(ubi?pU@XGuW)GK4p`INm!K|C{X!w7TXa_O1b0W|+h&y4KC zHFSZGBLsAzPLM9x^)853c9zTpb|tRdj8L&|KvD>d$%Py@=#c|ld>Rdk` zWAgR%H2eW1~Z)f`%ZS93G5j4PbrrfS5CyIXh{IAhPbcmv^s$p1+IN>1TPXJeKa z?6`li8wTb};YE=`k(##efApd(DKFF&gBRu57P=SZ|Ed0xYX3L=Wy13Yz2tEDf7M@J z7+@&(WBLD}zbv{NRn~uU(4fC4Go}7gdOVo^vb;R28RKW0^_TBv>#D}HxB)!Z@$lq8 zRiikXRE;aggX=GS1mL=}b^WE60qBl94D`=Qfk4LssO`7^pug-cujwzJM*j!><@cEX zO@FyjPSaoRjZyU%%Wt~=lIk|-FPF=!`b)@ftiM#P!TQVg+fk&yJPG}!IK!mBJp141 zFR4ABWZ%i8Got&pff1Sp1E!U@)upV*`OE}pF0G076R#znCA}ss@vL{(N^!3CWmY6A zUa)FMR#R?9kv=uF%#qcsD50yg22arK46ay$ihoBlU6#+*Hqxi3%8hiv z43i=3y&3YnM8m=2yhKW^!3IOvm?+f{Rxm}|NMok5A?(Q+218g?zoULRn>;UZRM^4( z{7AEdm2>9%fY^^wLXl~muGsyKY2`G_9~C%@0yCm`!fr+&9-OUUNvai$RG5*P73?>I z3Uf8WCag}JBWQk(@UZO1PX65cQiU-y@C^|*li_d~nmY^ah48oyJR+k^_N|!_%)*zk zpt_k@LgG=sBPCL`M*2}@f27G4-k+<)Or0EUNxUX!VaV%p7HWG;vryS1XJO$Bauz;% z!RY^r(EMMMwOOe3I?uwPspeT&5EU>B{pT30kzJWaqQGt*h*bSuwrGO^R+K1*5>Xt| zXL}|5f0&Dx!i{rLIomK72i^-h7Y~J-=b}NlIu|N3Re{X72-Adwg!6<{de%50OTyI& zsevjTB1{ufWaA0BKG85Cm8QrE`8vlmAs^?+38^?pPDtb+!-NbBS107_iQ0s0p3D={ zKgT>F_Q-$t$~cs?A9A@V(9-sZGc*!KZ#3X`YY(OF*>03E=?~k}}&cA>Fu# zX-xk%1oJ%>Pa8$5-{H3zCPkF$gi@#2{S>ocg2ro`PLq8YH^!*6)EhQNDlPl^2&E-u zdmlp|Dp*Cdo?`1ULF>cs3Y3<5Eyh4;ISr-7S{F*oN}E(#Xx9?Gcw-g^ZX!Zc#V;NB zwP*Zni$iVL_dqlcxRoJlELnRHjff>XWYAU?hHBbMduS_{sJX0hMs1}BiMt|*Yyig} zA~i6~f8ICMWv-7?Z{V>yEJ*y=!_-b1Qr{caf2fRYvgf3-s5kq!1Bo#pPOEf+{Kj-FP0|xDpzq8zV!?bYnn>8lp9x zy5Jk3+;HVSMtaIbtJuC1jF5-&`VTb(odKF>Xes?IpkXbIXG7R_7E-)T-z_u3Zvep4 ztg%3Q35-D7oWF}VDW zXp82Ot*Y0|f1Htd3)Odsgj8*~-~sPpMGG*X3S5vX@R43;{!~<$8)9lNULgBpcNv{p zlz7eW*ly90&6LP0+;y{4@|52SWE%==!!z`xx2@L9e}a*njq0@oveUTWUT+gc8(<*& zgbCRXQQ@4~{WX#+I;Le9N4BP=?8ttM%1bP!p0*OmHX&qN`yET~TGf%Q!qc6&HNQKi z0^+JOe-Q!jjU-%8a=8QEiSK9w8?5}7DBs@D+7_uD~C0hfo$J5NH0^$0Zb& zEJ?~7Us1@pS~T}KzzT4$4d!=db-o9`3rJb<2)!(GHl{JZ3|L(bMmfk~l-qZ#(v?^H zVwCF2o9|?Wp^~BfeN-+Thg5|G4mH^;)#H;=9Tw`bjE5q<_oX_l_d#q7~J3y+RF+4c`LnWp@i?iu1u_l!L0RIa9b#vXDicQQO9i!(f9w`9P()1mHQ zoB-s@S@BOV-UjebD*kbCnySYL6mn-@U>!B;g1jv6esu+`z%`LaX-z0G zm={Omagj9tdor^WTQmAOrPP`gjq_gHo&G((S4F6BC8+AT4nMmw`NNYb@SbNmh;h@0 zzh09TVgB~;l+v)QN;FBkOt0%qDGkZ8)9V#F7<;<1m&W*mv5^*UqZie)@=&l+CRfnV zjRv~MIN42Fq+7Ye88-}hIUZr^b}~oM>OYG5v0VRt)Sp*g1~YSFw^DUMJ#Y)PVtTx; z$r8U$l%eVqC!0dmUz;pK)e|^WeOYgWs?!qvrZ=BI%-~S5L*38HBrq5kmUKKabu=yFQOY{Nfb0Y7=eWAV*-Y!5 zF;yd!SmzoyA;Wl;^n(f>AX&SqL2;Mi9eYj<869sI!i|^4#-0PwgeEUAIvz-Uh+{$| z-3Y6zNGA#j2YBrSUTl?JBkx(AC*v8VDK=f{`Y#d==K{!i!teJx*Ywn|Ul65RR*PV<_wfR^pIv`H*(%hYk_l|nYw4j=o(POSbuSwB8{VC~hNPWuSZ|GhsR=Z;R zXoP+q1Ry+3J29lQ#NwsD;qDEq>N+_q{0(bV8aj*9tSJuC$5&V4u^BCU&>>Rr8@>(Q zvAkc<jRNUVfZijG>~v>hVs#gedL$xh4d z6zYV&OUq2LhRCO-Uamn=aM&UyrgE|z>*SiOI>4?$4%;jxT@9W zoFGp>cIhiCaw2vM{PUBWcA+4+k%+Ux9ZGLrCQr$HT><<9@ zjM$Y|($EVy0fe=1`mYrLDa>=D^jTE(?A`Yfa+2fGh4I9KFXI z$&-F6gsp%u-kg$WIF+aQsO(mne!;8c3bg8yx58j{yf^*F^StLqN9+Y}b+e6Da*OUSiUzjke_Tu@E>B#<(1L?Hg5o0Fi9Phm~ zQG-gAEU;Go5@wp7A1_%|&*YbcYGx<;kwNb=+rbejGP#gmb@Mc4Jsx~wLb)SJ=M65rlPJf#ya8@rE=4vAzE;k_ZXdrW$UhCq zgZ!Ko%{KmumFI16Msle=TU;)Vq?9{*tGqb#6+QvF%lpkRRU^&pY6N=|?g^0y>VP-x zsFhQ(RR@A-%$_q_&0w*c3YNT0PpfM(@jZ?ST7~2_;<+n5`yQ1KZhZVi=){N6y5O8p14a#gPB{iTOnEX)+7H#c%?ZyZ(EJm6(Ke`>tUo`5=LhuXBs?P} zp5L+SlGrYZhdIJ{tT~-U!$zDogzrA`J0@Q+$qT38R1rxb&$;~JNS*GINtAWf?}GG* zT0(>h$Ueyc4fEskVv2eNM$b$-FLcrYZdHj4s5#(#S>4#pm6q2n6${0z`ZhZcuCYl? z$^|9#^gXPELTTghQRkX^G%l;Fhq6#WL+uN(1=o!KIA<+$_^;@kwTwi%>ReeYzeHVJ zlo?b0k0_CIbDuXIeqfAf$q#o;xfsm;VDR-g!@VS9g5FD#p?if*_mU)Q_mYfI?j~E16xzxR*ppF=Su=9(@(AVzK10n~Qr%jvC9oCCin$m*gMe zEzbMBthadEmzuYDm-m@W=`G%Jgs6D0MR?A4a8vgW|86j2WjWKAn$1EC_I&!^;Q)hVQ1|y=(xEt6HdZe8wqnnodiyt zK83+{k4*Htl52cO)s#Xie$FS=E&Hs3^yJ<44(n(e>5`GNHLD6?e&HtSx55c2G8@D;e>w-8bCHTa}$o`}~58HW~5u`Rh95tjlz83f{E1&YtR9 z(sP|5>7KM$UqvdeE@Uz> zBn{APae2RiYix!st}@Y&uOsr`($_pImw)MqRQ7jsTdk=r-)8ykMNr^RhM}uUUm-`4 zs)P}WM?`)@T!)c>)igjPTo2%Bf0bZ_a0_nh{z0!3EMvdh=+o?X@0A3#-yJz){V(lz zrT=L5JI5K_e&@W+Yi0fMD9fsvMi|DLhp@cB#a96?B$(;+AFfWnz9$2*9+Bz3T(Sg>auij zr>YL_hoqM=mCr(zIS6&}PpjdS?c9Mt4!6aqM{S>eO=^q*^3R`EqZ;__pDZ?IRnL17 zRG_51Q!2a}J(wc85+pcNG(sFpV+@e!=WV84HG3t@0jRC z&g$3`?mBJv;{*LLVm4Oy z@BYX6Sk&R)=Hoe_%@=xNKE_cKFa!_@g%I9(D`_fxgK=L$XU=}umS!(#PWsh^smw`V zOhY7d(r5iCY!FHqk4YE?ygkAGf>h+Z)rYh(q48b#hf1Dv4q{A>3-WAVpSLRnaB;?G zxp8ULhWue=!9JkCzwN*3c+6`5Z{zXA>3(HQ47yKb{Gq|-^WT@KLx;JEzGz~aMeERpep-iW$Rhm?@%FzCRs?kESt{py z($KqKPjK(LoltvMp^etN#J-|;pP$q)Xf5GBdq{MQU=JeLvVSk1P^FBC`!p#drVc@A zU>JoC+}2JS0hL#_z#Ev#{?^0@K6u*ArSkpMG7j{+zggws(uU5yW$s%t=;N%I{^vOD zX8#hWUE}|Z)1LFYbbXYG(|)CyA^y7g2~GJHvf-~5HBS4lR71I_6RPrUjMKJ;h&b)N zWi{`DA%-~ZQ_oAyyCMiY3CDODZas=WUnvc!nT`2> z0tU&3EBW?2<*eV){x^eLVKY~}jf%a)poxhYIUww`M-Bc0QSvTICWM)qn9Rj?r7Ksw z`4u(qfGBS{NaZbICKmd{QFsT0k0-ZFsNeD9@iOg1px%~H9ixMcQT~xKxJRMnr=g}6 zE^x7;bmg|UX&^@BQ2rHjJXgUHt7-g~9%28*y~wAEPkhX3Ed44;$Ku0qhOnvi`Y{ck z%B#09{Dgyibg`sdXy9;$9x-%N6m9`zem4huB_7d+b9k(VyeQui<$p9=;lqxI;q+L9 zcV*zQGR-8T+L(~Q(R}T&VKha_6qM{{ZeZbIZ8T5V0}y%y<>SpA7;)G##bSj(|cbhrz)2GaIdPgw8n*}`(s!oYvjZNK@>DD|4Ed#e%GA~nhub5NbTu!F*# zAnXM4rn*eQ#e1=KS0?x^?x4EMw>GAbF#Rv!zE%Qfdu?C1dS5F6emOQR@fuiqH`N?} z)3EvH@g~0s0!Culi(0?rnHX)nb7Lr4y9c3kl@M)2( z`?Agc>%LNQ_!Zp#8gCq#Li|cCaR^t~&I7#Bf6(82AL{1*`ewg=J8$;g-Ey-JQ#bpP zeYDvx18jL8>O=u7y}wx&8EpWTD}aTU0X7f7zB?dfkylw3xf8~+NMix@azD{Ew*MO- zUfw3`|6Kwhb_T@F2Ur&Q;BS^iKHLUbWFuq|cO1(i_X}BML>$OLmq$SOwEjN4E>1!A zg6FF7%~HM;bcQgx4=`T}%J$4|u_DKvYtk8x_<&r}>jE(ZuN&6)QHPw$G2C9Au54ww z1^SSqY7#g6x=c)MFRe5ZRBhh_FJ_>ti&JP+L_yN zoa3A|j%?Di^S<2=$kmg^=8*9sO8~Blhj0%bcg}DS49SJ$-8n|m*8YRfqk!VvvBxd+O1iIk!K(FuDtIVn6eLp?<>iO zQvLth4N}!*kzBNQI0g$g+^w9;P_A&=&&xAV=usNLw%pB(*mR6wRlW;q73acA3vx@; z0&z>!RpTvDI}Nu)^|e}5SNL(E2pgjTcEeAjOq0i99hVya>sEJSukQT6#K8@FUCI{k z;BI>Sz?J~nHFLKRT0Lu+{?b9J*Gggd;Cf zF78F-jauw`fBr(L^sdiYYyGy+M>@lenTpYGN@AkPJ4DC@k7>7d#!ju2uiLmSnQn(%Li zRL3rkWI0fz*;7}$iGd(`c|E_|?j6%Rs9E)Bk(H?tLse+D#1s^X1t9|8udB4Hw)vU- zEZD>WZ^;UlPwhFWxBdRSWxT%Q_A{*`QHpY`w@MFBe_T{jBIC>PH_o zqCjYz{$jp$Jnu(_d~_dR9V?j^rM)^VUwQEACw>)5?U#(!pD*tdYRC)vtBc|lDt5!$ zrTn%~!b-ubANbWO`Kq!0YK30+o+ya9X~Oeq&yEx6IkJkhXq1n`)yq{%canI1{VolX zV1kQvC`FNg^5!lROKo_PSn6^v`oMm;%g&VC6g&{Dxhgf0`4s(Fw}XCkrBO{UEEKHy zsU4coU0DKXzF%}=P-K~ytQxXNqAc<#PJgoF>JBkwuB+0r6fuGRLSL<}N~S+fK2#!Fb^7P{|jDx2){ z*Sp(vjJMA($SKamsL|KBuAY_!>A9&PE>FkUl1Y3ws)%L$V!K>}L&dF{XO9!<{KC^Z zWfKC%~Ie&#R3U&9t^ZsupBzXV%7~)q(&2I2>iCi3LkKd<<*1#D(<5PIa-`I`B-sQe9oNiCaE$M4=OXajY(8;tM*@)8V3PpkRzzKvUW7n;3A?n0ed zetkoA=k*r}Y{HT5NzZci?iRWIZ1`Lx-k;Ls(3%&_z%3#O0s?5Kqgppt1DPvk z%UEj$i-RMaTdJ4gBu!g!U*vXOn7ws`V6A0Xd-tokm_#lo^c%`9P(58}<*7*Lef`P0 zK?m#lR+W)3=mh|sCrp?CJ&Zu7C2mdJgKj2w)@%zXo2i%m2W6AOj5${ha@tCqW7*0< zBdGDHUgHdEoDVf|k;Fo)yd7^epW&o&iKka3L-;AB?<0uhj*!&#R-K^zX<+V8G3nxY z1>B+)YUg+c#fNz}5$H@e{TRBLeza!{qBd)^iP|&u+R>=}NT@3KEEU0y*RrZO^ULEC z3x~K`#)pkWx#EkETa=-H-8ZuZ1`)q$kr~P3X%l%#Y$9z6MZ_;8{_3TCopPbCW*4RJ zojCbW5hQy+W%=dg9@3g`hc+?WbDIP!PJrmdm!%uO7H<-^oZ>e6oy)(N{aoL1ksh$g z+`?vr>GTfopq9@ZqtjGq?Vxk;d7&yRW~T6cac;Pa5yw;rQWzCylm**uV&SZ0AB2T< zJl5?opkF}_63BepT4A;G zML-;mOM1Io4fm62-LVA={a$3Kf@OkFn*`=R+JIh5-8kUe4E`p=y^J5;kd=yqRO%xu zmARDdQTd_FeH)E71UUeE&jvx+y)aQC=Z=B0udNrb$^-!)mmgXOe%QEP*c2yigyp5I zQL&`YIdlAx(O$zQ#=$O6FE8(hgHOWpF#M29`N!oMRh$Ob(cmp?yBS`8+Dg3fjPC0z${=e zJpksURfZoW+YV7{eDCI)>*z;Mco|gVhGU)8q8;&*J^=}(H=qm%Q<>gWW_lhsyy{!C z>dQiXQ7Bm)a*!(2MBc!!z{(VFp!M$S#7wYLz9m#nck=7R1bc-s6KuI&NYWjN30iSz z3g(S!os|yP;^adGY^%l0v~Dmfo~E4xoDoP5e-)rH^{iN&BPgf3v0fcOlFJ#QJXxr0 z@s58)XOWMay-e>g?3g0V!_gAO(Cb6pCAm@hKM9m9yt z2}HjEm+54<>fW9`Mr;=4F@x~ZAwDiSdvpP%a&*wa4-(t$9 z@{SdOBRa|%=r<^lN&?XmZu+rUmWZmjV0GfN`bhAnROghRG#7RF!~4Ryd_7LSjluZr zmpjD-Hca0#_3N>Gs2ypsPTZ=Q7LV>rHDDvD0U2%5bTuFX`(am4TUG-Q4q4KhFLgi2 z+~CuEsRpFc%e+P0uLJ1UQZcH&27brK>-1frC|}zZE??(e;W@wK@qDohXpzy9v)FTb z+>!V5ews&2xFbKIVeemyWxFltUEY`6?v=gLg(S!KYdk-wKX0aIU$wl?_#qb`E8?Sg zerGS*ZCZb~D*W8GNa3drnnsQVS#m0B9%SxOP+--LgIvkedRB#dX3zeL3ys7FT^!xF zRzzuLD1W*|+}1b|j^9mlybLJAbmy-Xe4UvWVhIDe=mc3HST^wI)>_LXTr!$D3C*KU@ z=WdTH-t9?^Q~7#NXHQq|;6tuSXK?B!-OuSvh{rQ?Kk0-?b<`oZXF#!A`GYw77Pn_q zf!i}|Dg8Ht&-}YZ1{mBV;~6%G{#!x+6=*pDy3x}XnJZve0v}kUA5-bSiJYGW|Fov3 z1u~Vuu*IY=t+A^4Ut{wJ^I$D0B0|_1FD@%4S%tddc+eh{!`+vs$-}YvFLCW(P`T4w!!wG=vW#7(h1ipMt2$Sk6PlWN4W5A>f#AfgaWEN@LVf9i!T=T>KXGvz_z;9;pJkMF{i%AsLo$SVVhPT8$)gt9;N|KMl+Pka!AUR@OF> zZo^$Udq}2L9*x9Ok3jrjF;w8a-p3x+cgsD@zzqFg$%^)s6>O!Yh9_6nV#^LDiS4q} zD%mIiOfp-Ss!@G;?$fnKCFjh?gdBCYi}PST(6V!rF?&$o-P43C#ost)`buGV*)zAH7p zJn16uIH`Wei4|5enYLR2ua#s4b!ANO0d*PMvlY1*dI|r(aV2c|#k3M`LV_Y!U7S*HyktBvI-HO zcpi-By881VJl~@~KZj?l{yYHBSK@$a^a_1-wEIO{9Rttt>PYiD)?qaCg)B7eN+(3s70{`LA#*Z=sb{XjRZ(H#|>>lV{4)QVr>4b}Y9-|B?V=^Izo( zGf`o56^t^rEyphojy0BH9vy=j;a3}%#c+o5DWD`W6kFTnlI?@jk(l0{D7I$;$flXh zIOp<*niI}7Uk-?(P&90eiyu*s40LS{28+ooHBs{2WwMnVKa{|v-!@w<5}74HW>cJr zm+nIG!lS?lQG6!p(OtDxMLa@(@X@u=S* z{FLIHJ)>juU*jfLqKWp@ggv`XIE=|#5l|7rPLl$pEZXI@wbF7*tF`h`D<+Od>6HgH zcWUAzGRx0TO-%=QKBq_D1Gt}@R4ni84m2dh8)C96AZdjyrzu}wa;+VG+uGWY!%(!f z4sB)K%N}g*!_U-3rPP_cSu3&uMV`3Vlpp`ApG=fEhaPiC=x@iGd5zudIL}Ds{2oql z&Nr6HbOAMgLb?FIBkxCp*fxUOIfQoZy{`<?*# zv_m@Sgc-?i5Ub3!GON5x?PbN$Ytov_Uoc1xP?#HZQ94{V$_p}LV3$E;sL57>j5$cU z=t-MnBCgMVM9fwgXJN-FQj+D%vmOjP{*TPLDm;XQQd?=1Iv|RO?|K_i&%ph@4>J*9 zSFUDzgB{|}B*>|cttY4cd=ul;+fKrPj<~qTEHu3zKPso*Y2m3~--M@rZ(aIU)XLCi zW42io^NI3JKVrVwAs!ZHW}@qtinRmBaJw+hE3u5Z5`v>fxpUJdVo-8>PW4*qg0PTR zouojw;v5b>P$h%r5kF#~N)2ajb`ZS5`0Pz;Za{1*t5Gz{$g#jV1n(|oo9GKhyc#d% z{b8zD*M|A1C_YSt(qQ^f`hyTFMnRP@efjMNvD1z!S;IF#MS$$ta!QBC4SE`lj+5{3Kb6zX#V#|Rm(gQ!=6=l^V%-RX~UO=+EWx+y&2q9r?kIk}G0J9$%_&+&SNC8}~PV@y6ZD z?}+@~YUG|TeP`mHz41ufs#_UUjQF318p|I*V*qcp?Y>} zqhI%ZugWsJa0HG9q$!52G{-+$1$HlhybNgJ)(_S>%6Ovm-+(`h(n$BsN+d1 zfY-&veeirI?66`|vbkt|6m{4|NbO(_cYkz=#@s}i1}Jk8agE8P_MDs$BQ+k64LYi= zmKdfaN9bmN@@)mC#$Ue{45D>9thpW2m8VD?Kwf^5Fo_p;ie>FN)NQ(@<(sdC3$V4+ zaYVP=N}<=)fM_*VeS_^SJn4pCxFRt!hJ9j?+Ok-R0<~12_IG*Hgtz_L#4MU}|J%Qev(gC$UudKF~RhJjf<^@;XPN;IK1}k z3!x$&>l_C(KyLh2@I8Uf+YEgmpgUvOR*68C*#-2&p7Sbeu{ElFqYByhHfue-id?J` zAqU^8bHB4qPCLC23Oei1T z*f3H8!kd6FiZgoJ4%gOrSp61~hwW@+;$eA>BoDh2N_@)FM)I)pq0GY;G-4iBLX6ql zvZ;ZGiMA%8t@7n{9u~h?3TaxADJZfw+O!%geP!ZdVPAo3i{4_0Fl`TTq!6tw*y-g6 zy~6-M2Ef_nq~P$z*M?;y3d}`;OfC>J%Ffxt{Em0OG%y{7%YA`zb)q$SVfoi$-*0UM z_5%vtjS{xQ)_ll=eUP4B@nU5uF>A`4~CvF^Z3kMqj*N8L*iyHWPdu zAY#3pUm*EiCr*ABk**Z_#6*1jm1zV-y+2W}A=k?wId}7yh5;8P{zi##E^c@`I~UbM(*hxm6l$#=*lfEWy_) z1EW8sG}N9`AynKbP52Xw^)RXQkH_{EC7(Lcs_YdBh1nlw!2FwqyakB^>mn6|VLqlU z6ocGNuED+m;f_NW3i@JSh~tB+NZM2sgu2EpQmM67SrOyBn@f2)kuXa0%mh=~F2e(fxHXGG9f2JyU$*;xQr};VMj_AO* zpBWcOPPlnv#35;Hj7{lp{!Avt6p&&{xLLU#^qEkuy&J1z>X0EdX6phg4(9v=lg*U7 zA@1CmM;b+P#*$periS#(-eHT#uSW$X@H7BFY10}@+9S-?1m2SWKEHYzMb_H1=O^(z zN&nsh&!5ou2|%I)BjH$kZ7h%JR=R zR!A-Q8VWA3nkIki`~W_&Vm>Gbme#9oJ_XH@M4*l~FA<~ooQ7!z_(Nx+gZT4)AC`yibbpiN;2n(QwR(xvU5BHfZK^^9+{NZ>Yi%^%vpinb+kb8_h+Z`If zAM8~He|R9-$6Dm72AhK{%}L$C&632INQVOv%dT2tD``y$**YhHxRpuhsroZpD^ z42GAn#qAlt#kA<(_*60ljcJ&f!c(6b*Occ{lwj#W=6cWqb*|NJ24f3Sa4JU7Hs85g zR8#DmVtm5P5hsT~VVlh=A2o|@R_vOjr_2|;VPi;uu(9|PBf8&&1*OW>^VMx%x<`gF z7+X2f+Vj_qus8&w=@h8Mc#3mxy4_qp1fkV9A2F=YPb{ zd+~hSqCFSmd8htd#QYD>bNG29p1;$d*D?RopY!pYOV1M;Q{P(7Wq$ZwMH4@4H&;VP z!4N&GazYyyx7BZ`al)~6m=jv-7{vAKF}+BZKjIJ9yQ$C5vn3wQ;qq%pYvd*QPW zv_@8$TQ>TG@dy_1cjja9p-jNvUjC4_An^TaSz$onhlQfT;=uRse5~e58vLGS;cWU_ zDHEYDQi_uZ+)G6e<^?wTF5-lz2*wntR+rZmX{o}GBHMc$z z_`8sW)f*%Z=ZGvYIPk+d`C)kAhsFE>-hhDkPw{?5;QLYHeL>*+0kVNIVMCqdhccm` z4+=&EA$HdaA%Il7&~brVs@O*XgOd~aTZ+U{7B8fjZ**6el$-QW(Y=eH^R^m zMsLYT-ZUY>iDMEw{f_oJ$oVlxJEmI@=Toig&h=q=dv7#jB5>uU8R&nv0({p&C533!C*XSJ%Qs-45Zfd6;FxR$yI%h+sGF4%+ zl!;oOsUY^Yc)#Q32Ug44p+)R6>jW94o z^8y~8Ef0HfGMXus4Trrt-Y?T^o8!{Inr-EPP}%eeG`-!t31_2-@%-;>pLpxGv?WxH_P15X|qgr!wW(CIsXF@UeF-FAKJ5v_MBg-8^;d{ZyZG9 zR}YrT>}jcp!)y&gPY?;OrfiHiq%m?U$8bMUx@Smix@Tlux@T-Wu{SNrbd8oD@*yxJ zw?e8S&%i@&qn9Au^vlddZoDT~qua;!sR(%nP-mEl&F< z8>z`{)d{@ay^h%1i6}zVzfmwVDlKBzb7Nkt0pTWNE3rLqb56s&>3tTPUWt+^;bUf6 zEvvrT$;l(0hHWZ0<{uAu87@lp+7Dos5uzj#d-IW)1Kz2(NOH~Xi-VJWsB=RarYj>! zAsR~p@F`@AFrG?)%|bnS;4&n!8jkrIm3WQ$O`>1R1%W*KNR11=tz4pM*j^gNDaPP0 zG}7v2^n}>fU@bFFd+en7<#r%`B-&z0JnBkrn(;mu-iE}j^xOHKWQ6p4?JSiMZigY8 z7RX*23FT1E(a|cP7yw$#ciZU8DD6Fwz37NVJ8}cMx)^jhb$egilw((SV4zh1w1Kco z?w(-Zv1>0b~yG^+S68Uj~f3!_FEGJ2vNmi+*1)&)C(Sxc(s2ZyPD; zhtI-zfm|rVhU8IzFAi_xd1OE;>$vtKWCd>np&hV!J{sVj)QB2b)t@k5BWbf1MrhF6+ z+hqpi-4|u=w_G&ze(rQZMT+hBNUitvLpTwIzk(qV1@*qu47K-u(fjaPY90y=yXnAg zCEK+`y0J&8fezBWBg@P{nb|foOK6d664mPDf>3c+h)7g%$7UR@q`!iWR{eLiB@w&2 zGq?2>+PcH|sJWOI2d`W*U6W4u{O5ckVFBZ7z;Kw)Nl%%E^}Q3Ua{&16Ex%*=HSS@2d)H!v?zWHXAafqBg{ity}YoDNZ9J4$@a1~^CjpSZ-Jkcab% zBJ7s3*iIB1$!CfSWtM`6tMKK%MqowED+4mo{PT`U*Esr)RUWNr$gytJ))snIQq{^c zuqaBdnt|m_7ZvxQN8bxH19OS_@@;1`&lzT5kiyL8dkzvg$t%!f>lMWp6;~;5yr{Ug z-w~TDWirCFPEByV2PcyXL$X+X zClB>Nt=S|u^CjUzt$`Zvef1tmyyh*cc&gZ<{Cvgz4ryjH($4o&ttxdNHFqF;uGRrR zYO?;#ME=|~1Cc+*CCWoK{h4J`NAe#!k=s3z$VIUVstBv|wLg79N3-iY+ER;I-Hj{X zN0pIX$kF?Flt}x&OW+S3Up7z$WfF|{zrSr2D{4P10$zufm-H29ejO^zJ*3`#@UgY* z3Fi0SwwB2uIG*2`wXK)lwhF5UcTHSlJw>&5xap&Bl=0G^hu<~}PL1C-?E}>q<-dHx z_(WQaN3z&RXZ|DT-TmOHSsgXLH7iQv60M#2PW0&s(WmtvsR;^)PtXz+W^IGrZd}~~ zSent=0lB+w&Qz^tIUfbxY&aB+!DY_KjeX0?XX9E28s?sROFir6;312C%+X} z;=E4FVpV1S>Cm7FouYG?dA5PsNp_u@|IG+FOClwd)02d3h;c}o6KSa zd>km8ye%`)@8~{-PnI-7oNsFgNV&7>E?|!O(Ob-%Z0X8%9O7BduYJ|hm9$vZcSU=q zlEs(EBrVHkZj*)lXE|bnQd>_&>nouV>=0)1Y`ihGC;O=To!){V92?g{6R5cEH*B0jKtm=l(4=IkX`;^ad`uz4-G zVY&rjSAY)VdefMj zFz)i=Smm;*2OZxOH!giYNr!aakchmPM`z0feQMU3!v|~vKblqVqd%AWl?(6_=bF(}Z#{5_rDEM&|;>QmS zBYqrx5=1O;DS3ty=rjybU)XcHFiP-$jw2x@B>=&-c?JZBs|dtv2!4`hK(L$o{azJ8 z<=_kUSN2}E|Fy^4U0}K90Au;*bF%2GICY*IGiWGbI^cu>(`7gP{=5fxAG-=Be35?2 z=gWBhU4Ooa=iPX2!q1`Gv8Yba43(_b--hXLztHRcgSxZzXD^;}^yk0uoQ3B`B5DB7 zgY@V1c(5j1{E-De$KvN(EPlK*-l`pA$r}qg=3^|Umhmx` z{(gsZtkq&vOgHlkhAkr~3^_^d)sULK_)KBu6 z?@edkYdkjoy!uQ;O%rp2WI1|pjI~UtL)9_XvNxsL#t6M@0x#fEynru`CTVw=K{h!_ zFO+#2s#_mqRNoOoQnEw=eki{f!cN0$6+ONxW z@@eqET}W59rhCSE6a5*UfhRjCe`k1x9C9oFWOzmvXL!bL$xv>%5oYSU2I=0OUl|9a8uIetecP$Kk*3lJ$sVHXwG2w!K-vf)@9>5$2rFpBSBT_hp0omG9aP(O zC&OG16uUixmU>6oZfM<1x5`kErMgk%3)Lv{5M%qippR}P;UO$qAEx%~In#I%wU2cw z-C~vXQ%>flVAWvB6CUo|p2gI3afjTeLsdKu0gqk4!_9bXNhdAf+WPn`t4rw;cWr%W zhO!}D*^?QOMiN-DFVtVG;`nP=kUN>@W*KnsF+C`Uxzp0y0S|t zblPWah8ZC_>7h~bU`^5&nbIHWELW6Op6TyVu5kf(c1}$)UtyC^rPCnCT<0hgd-f-{ zae#zrH2I=Vqh~^mnMc*P-e%@h0Nzs~oBEW)Uwn6Ez&K6|n)2k1?I_ zJ1%93sK;b;Kz-#*lUduEDTk=AL)~5=(h|sEI+As_1Vz&Jy1tfE!t}NL=j*}Oa+G<% zrjJDR>sZbsZfQdmdqE$nEvBLRH26@xnWaA<>?+7teG@KIe10#e!)tXxJK6_qA7da($bn}DTvWqkInH?)OgL(`QVb^1E zLxo6Z6M0_fN25mS-D_nQpfg5T#a!=Gr#npcq10mPaNQB6>2{2e(_M1?hTorW!$!RJ zV4=m5SWxm=jKw0~oDgr`jS(Af>-=zj9kGSzV`9KdD_#ykUGW^ch3j_L-&n*Orw;$B zfZt35C0<`Bgj*os<)3(2POrOJ)V*Au-dOp~DwH@;Uac0*FM9&5eE~0@z{~uAm)-F4 zn}C;H@p88IGKyce=l%!0Oy&NoFB6I2<%f3o@C48l&u#JC8qa(S{n`XPH`JdW#B&^; z!}$4obSqMSUV`V+a;E7`8!ou&fb=%w>c-l$Z?eO`#_QfF)v1`uxpUZBbn-xO|NjVi z=Q~^QYrBmX$-!wUaR*P53O`|Hs^S z2Sjl_j~@a8Djuk(*ikWJ4R-9Df*yLt3v5WP7GxOf=zP&q0^poG``}t$xZg1Y~+gGN~Jl87J;C3iQXN1bp8KKfJRuC*} zD6Mfy+N=1r3-gpvz`^y0>EzJ4jECC&_%@vtCD)3^)`2V~kTyE?_|ZTd6Dx*O7NgKp z>J;=d1$2Mo-q}Z)l`beXKAVQH?LbWEfq89twmjV$EZv!$JfeV#ff^b2&cQdUo(-o` z>GsHh$VC2#DhY4<@wo>#Dl*Y6NERwQt%jBW$4?4kGY@cHrb%zCSOdSLAM|gZ7p<4A zG;7aO^HWmZi9u|ZS*`q%@5d`}+c?})4>X*(F!K|2Y32acGs}T@V9&>RXnLMZV5coQ zdgCXo#B9lh>JxThqaGfbk&*@w&(z1(C;77K!Fz1U!c?-=`ctyQ)hd0hyazs|3O>b~ zo|1x3`QXk=hu3l~4FeU`5k8#l4!Ncq0yLKQMSVSIx{H?#C3U2$N*;f`OWk z>kzo13$C~h1Lc_lahhTRlF1?m8?IVEl@oS$(V!ekKb^2_TgDfYep+FzX|V-Cbe=}5 zXEBvzYcn24CBl;PPxaM~IcKO4==;eE!EIP*PY-0I5ELDtzCk#lb6wvTZ~et*9TWnh zNY%omfj(555mj-2Sb$zw!QUD}%~ge9USC-uNOaOE3-t~zhVB$X&lLg-C>6!)m{74_WPRDK6`pHWP_bIF4i@q*8rVC_Yy1=Ocs0(1(>oDk} zjV>^hx&XQWPPtCh1>Ci|z<_i`7YLP_W}pkO?u~KM*_$V3_Tk-18_3jvowz)eGG)K< zHlPSZ`f&Fpta-kEtWJ&N%y)d))Uq$WNZY%=RtIG!Nmo=IQrQ9p`nZVeEPEfe&YCPy z)>$pBjFlS6cx#MV8n9GZaOK{M;_-()4vEzLzUum>!(w1hVJRQWfCrobCCgEz%Hu{x zvb;+Z^Wgt{09gPCc4hifZJ(w>-|*-oRsU`=T?7O5*Jx zyq&~u=T&d_!rQATOs3@TBN-=#9|**_w+H^Zoc)?&_iH!&bqf1+rSPkPer=1tM#8WB zdkg$tNB!Oqzn5pNBkfw3!e7gF+i~WAt!SC7e_wU*7yEMP&LJ0u~tZ}auS;SBV^PL8Uv5;r@ zJKE{3hT45pYvQ2ZiZyZW60#=tw;G=J(v&M`-Ag0ShxFp|9|`s7KJ$Wo3ae}Ge&lOr zRIBhGKu=B6!n** zDTYVV(~7{;<_yR2mY&0i?<+*lx`EG{OkFH|7{|xzOh##wvy* zf?255Qqp>)E9haB(gAU;;VzU4q!;Ky^`7>TH6?o5n}YS!`zXJF%GX)CsmL6k^{`J4 zyVb)U@o|KcPMYF9&vxSZYZom8R;9BNXP9K;ON(l|pT843(<8u%rLYAObx!mPqW|0d za8cO`Ed6^h$-=JOe=GijT{2ir`oUiGDb5?%rKfq&4+qh3%&ekeUU=SO!Zdh=2`Ol; z?QDNUsH1$a)xBhIS%&#whk7U*LC6Q2+TB4u*pMq8|3W^P`72F6Sov*2KG+jY?I7oaWy3&8o(~p= z04yKu&j=UY-cCno|5Vc&v#NK~gnapTlk>s4bif=6tceb5^w{!G%?GP)vCRiN6`^@j zQUom}2l-%T=>{y82@jKX%-++58~@o+39LA{xt43y?o=WwG_KzhqjV@VZqyy4^azWA zn5Y`nMNGue&pCiz0f3%KTkp3p=?S_8g0Rpg<$>37ebkke`S90fl}+i!vdR{*qc!5T zv(TM5cBaSVbEvS@Nd@&i=@|*94-diA)dX2Tx~kQ1tcOB%5$u2p_8h@-qY63mo&L?F5P zc2y56t0~8Z!44eCW5YtFTeypSL)A_T6B%feR=IPW&^SMf_>W+}$qN z;w8q`VpmcID_!x%uI$ss6YS`s_5H2rBEIs}M2D}uJ3@J7S+A`Vj?Z~P&nb-0*}&9~ z@_>&rRH~b$VLrI^E10A%n4v-^o;|1c6@p4i((@^pKZ%Wh1pgvfMRZFFoZ zYJM!**O*x4FfY|Xk-F@SFqjhap;5t36^6TEXJ*bcVGue$45Syr&Jg9Fpxrb zD2ym2tw>`j{JszW;*IZeQ5Mx_xN;|rWLSh9$C5Ra8dl5oMrSNZF@bh5o=1g0^OG#= zYt_@O;Yw6ZnDoIU&BqtCo(HKP>$+ouOEj0KjQgDE7dg;kAG?h=jvC|JIa$VErh^!! z%|sEm^cY>V5q?gqW|Eqqy(f!)@+8^^qo_8G2l}X_QB;7Ux=9-6jtc@KBbAQj4_A!W zkCE^OYS{j}PV85Is`4`$sIK_y%}(+_ONRZ#OU~f3cCeV!I;){0C)uKn=|uH_u}&}s z0DWnehSSph!X!1qP$*iuVvVD(J8?C64|AK*=`#skP<7p>W15{m)0ENl5Z~Iby`rCz zij1dG;Q0uax~e^ZPQ-v^?K}{c^-EgT%MK&6e1uHE*H%P&*wJ3Ry3*0bE=W0r?%Bj* zZt5rpdWN)80zF&)m~Kto&g$(pq<5J_J!)jDhJQ`(XO3^p%seruh@%PMl%jt}p%<;c z-yp#w*DO;le65qt?rpMBCts%ZVk&s$&gzQ(z`@Qa z<8rCJi!OB*rdvvN68b6haRidA9aQ{Ced)FB>7+|bwhOq6qp3hw(gox(b;hYpCt%Z& z(A1bX7(f2OR%z#c$|_xhx7$#0k~$6X_A9!525;BJ<73$OlWZ}ku44!asgTDJG7myB zg!?$;1P1x6ukxq_gnaa#9(7&4y$Nq$Vz*P&+q3X?BHSKZ6zh(*Bi&{FDR;2|NFBJF zmJLWrjH_k;`E+>8aQ1YVd>R2jIk+$L4(KNjdC$*R7zoW$d%i~wQm~_{AUA8TD9BN> zNI|Y;HJodwkpZzEe64_R+ zV9p`TYbF*k7#zGi@5Cj!(yZWYUcWqq6o$;^@g&ddcAP3Q60YZKT zoSB~ihq~NS9nRu4ppQ$S0SmWt>bP*1uM<6@{f*oBLCcS4MDrsHXoVfwHf-)8ql(ozq zGo^*9pwv#sgY@vo*2dE4k3RehDJY7HU?wZ+DYNvKMmn$8Tv0h-u#D>*vXGW7g`jk1 zjkw*6ooSB>bS;zA&j%&hN{RxFN1ERfl;giaf&vm-)rG_EM;ajB(_k+izm$#@xm~T#EXGe!_a0L z@!~>L7v}iAPpMf*Dyhy&AkjrgBbVa3fSW4q;B>Z?pLXrM6H{vLiV`v`+zL_91%KiWxSkG6)`) zQQ#O6HM8^VfqH)^Hl;w??30!<({wD!J}G zs?e|lzakB*b1=n2tq}k;tVDB#hP`Jr?6+IAyv$_FWk=*iAtA#&`SUn%Nk1OMz;{xL zFO%IXXHyJEW2y8EWGRAl(=GC4F!g)9`L$hT~#D7zDcI`F1NR_c@v<$jhLEi<-ex9(KZ#o8wY2 zWO*c|TkY74uSt&5 zvCV%l5&vQH(a43(hwGX~j9>XOMvMu?U725`lf6F-H=a9EV=t6;DO!V-ziW;q#Pscw z!%${@_FO+M7=!(I>1SRG%um#t3e)}izWlJXjy@C&h>>pL6pv&z#586mDzT7QnY2(? z!%gS2qEw_2d7BjViBSpkL1BF`kRvwWM4o`_m`VoKT z;3=jyCG^AXNqP}>l*A5L9}gRmpKHfiXmTW&T%S77hwkBbhSSvuj{vMg_PP^@uVOTD zZ|I^+=nZJonj>RK0|CsMC8`@>oyV|tJlMqfLOJ3|0t%t0zEmId|U zc+Yjt~i!wy}(Lxy+Gi|LzQ}gw}etw7lQe| zKW%AoHl0@(c)g!Tf@^KVtAlC3qi9`qI;j48i(;p2*!HAgxtd>SU7QirxItz0n?`ez zOMvV^k3LM1pF)wpNh!WU4pQoMm4u$ny;u9lBdXZqNzwxG&yAkntm0hrMvEVrgx z19IO{0A0;cX&7_gg-Ts8c8)?BpQFc;FsUA`Rt<0z$fI@kbUGb;aN05~Io(lObg2eR z@yc%8R}&w6R~u^R-av6-eQ3Z=^mSccpPlG?LeUFr)^|}4hnKxr`;3V1Lk6eXmoV;Y zfWO~#R=rs4MZ$1U`}_IYX2*}ghwyX5QDg!+PS6(bu(R#kH(=sX7j$R6BM%m7i{T%A z_!rU^l@$4j&Wbzhl##o$+?e9&*#O;HZ*|{*QQp}I$;>R6n`pKQF+(Laa z2t)e?ng7g?Yr->*w#$t4AiYOjCN(#0qBT*$UNo`&%f`ODE; zV>SF}BsIM|Go}G2)K6~lfsaT6v`TG&o{W9daPbebaRM!@$NV906J&qLY0Jhv=>R?p z!C8^M@%Zs)cy7`rq8Hl^Fk|(6_10#73nRDqt&RK^=20YVq+84REp%1zTg%w3v}XMK z5)Oi919D*us!Jxw)a@D<#>l#=3*%z~_P5^w#f7nf?1WV}wqOQISeW|Baq_NOjV;H5 zK50~ygX#4Yle1n)?fW8@8tq{d@eJtfATYpXcp5CSF0ooPJ zr6Bjprv=+GvuyW?zOI8*;J0<`y_@&zIPh-%T1QPII`?v3w)CF>%d5225Jf9x zL>}nIY|!MBHd8l`KsVD)n5FWkf@Hr!EO#Z0M*jkFw$ZE%YjYDb&W78|tThVtpKxdL2holNA@<<;!v)8sz{`Lx7AE}y>H z??^s1*C9#La9As!h8$L<$rjZ}nmo7FR+@~g1=8fp{SM^QSG5W2zC)^f`uvcJ@pe_h zINlCpUc{L70LiD_o05DgRYhqCv+*rH<1&*^3w)A%YHPZ$vQshLN0RBD4CH91$7;7^ z?8^U(2bDMSRZaJGH)+gXUvA>2`^Pi5+3Vg6!R$4@mTdMqyhAa29o$W3uNND&X0LK} zY|UPOo?v{0xt=I&!%AXR4baf{E~kA}ALp1=Lv+nnlUtbnQE66}n%qos+A`r#Ixo#y zYFE0W(yUi%Am$fT@R2{bIX?KAwwlP68mwZinKR4`c~S zw|NEGcyG}V>V-(q?rl8K%X=IGDG$x)Y)iGeKDKE14 zak7UFWPnxMnGBGxhKx0;vA}fHWcR-jy`C^Kfv7&!I#oQtv)<9-?!Y`BX&c*|r8*03^0l*@SgP5W=h>>6uph_& z1-#3yPC;1ScrZwg zN}@+8hmJ9QItO%h@4aLgSU+aOTUV9rOn|b_cS`2*jIC^8e5|HysBC*5V5{@m)pe?> zi=dQGo#}AV-l=Y*l>eDbO8IzGCQA8G!t5Gc^%IoxdQ}yrJTX~T%DZoMsFdff%9ZlH zZ&+`hHL~}oJgy=u<*{EP8(Pei@|iJqN_k2Zu9WiBHrN1_CLAfW4{OY&$*vh%G>d?>lEf zM(_Jm)ePR(*id}myOrekZLvUk-@}{O`&O&s_!nwut#_>ACv3fx zipthA?v=ORgN+Wi-u#NHJ#2I^yibQ@dwbZ>it^U$P!8u|Cfj;>qV2X`XvNR4hhaS) zBUa?uZJO|qC8D^_s-RTl!TLPwQ6hYVCu4-g^{|+w-^1dD-U&h52c2cR^(PeETBBhM zWA5RPiNb`;B;nw(2xQ@*cx5{CcG(V%u=p)}r893QNFF*%jsxlJ%-f;x4MgOq9V8-$ z!9y%TJGtby$d76V#ciiV6%5;_3S+RC4QAN?4P12b!@o8r4%iFJqgf&3PQiZeSg!}x z$3%zgBczhDJ`ly@`ha2(p!gb5{8*5NW&f8vyuJ~YQD)@}%bBHdynJD(WDQkam3zK) zVS#6tFwpU<4e3ZG` zXDy&vwe<;_UKr)g26L%SGQ~&PIkj$8VooiNZUUm4UqIvEd0xRq9KRQH9M?aos4z$z zzl}y5zahp%2{E25AnwrOaSkyY4A9I#e;GqOVko42#jX`-V+K&yKG4prA@9t9jCN+3 z3OFIIdnS0u8}#fts>~%BR?)o^mYKisiE*%5)5^1lu{fbRXzxlILZh|_dpIM%MjqH( zo}|sZOAzrTOSNAVmsn1Et0mZ`LVk@GI<7o7zaH6-Qat*9@jXFEkBsUB-}1xnQ7z5V zZ}fz1@XU)i8l58{Ki64Af&k^Rzi03^li;TkGwj2{j#s}W|Mk50%d@DG#jaf@to z;boqbV-7ITsoTT**ra^L%yYyGB=XSpa@gam_9bblJ$hnv6io6K>o zm}WQdM1DH?RU3#uq#RB5537(-+~h>(;$jbWfOfebG{mIv51g3Hfpu!xx0e%>DRYRJ zSQip8nGcOE&$n=Mq*WiAPjdzWmUD2hK#Ny!kyAI$QlC$=2!^#~QJDP@0-6kxIGI9J z2g2Y(Re-}7zu=+$ZbE2(cB^4dS$wWR~7(7MM2yxUMaYc9x;;OnCB(L*Xea$WGs^WU|xSGOEh?Ll(AN z+v9?0ufcY(G+_%bjpPF9-NNBArXY@Kvw*2a7Mh+@u07)bKpCgs=Y`E5!9*_``C({tHdS^`F4kf6j@t{%R2QJO_>`YWo`LkvB8r71YUu(%fgB&r+KK?9D6-bqSa%(wJp}araW-+No3NODY z&|PnTrtpH$WN@9}AeToU+X0R7*0ZVY(4an)sQweazxw2cTf=nhY$#u2Vn7nR-@@j8 zd49i!&Hb|celfg1T)iK{?`O5S-+3SBpmAXa! z{u(*KGWGi<{%SMiTO8E|et%dlG(~({noJS;*ezuFFdQhQm?Iu^C36Ijw~$hpAOZly zD>20H7^if zYLqcCl0PEWTNI7l#ydo^x|AXsVcJ9s8A;zH-ett}#!J)L+9in$d6xVX5y`D2iAcUf z6%lE}Ph3RGF_ei&{WoVUA|3TpM5Ln7&o$LSL>fgaYJ}L+9$p%f@vR?;N@;VMsFZ!P z!pL9-NAW2c&ByDVUL!KK^;107O`z{lgW$un67Zq7m~=U<1YcgfP7)4cj_Q06Cy8A< zs+SF&@T2^=q-3&9g6s>e6PS?nmJ3O{V9wEg0a8M6e0>yQl0iH|y8_F7#44g? z&zj<*H=E<4cUywCC`+qr{Gic5h>#Gj_M|B|h`5H>2}ltl_A7uGuw!vhCL00jqk#IX zrQ|kMCZRK^Y}C39#aOW&rq+!mVPyf_OB*U3XOhuDKek8%%>3N%IUJ1#M($C&rg2f} zK9qJyM?m}vy}=L!D*XAz*D`pM+J{CC3jUNX#+G7$uw@O!Y=k4*it;afX*tFF%2uW9 z;}xsYpv6p{xl>G)XZGmW${fUt>}rd0t@Kq{?YW_urOj?Hu9)K6Q_+%3^bX~Vli|IR zY_FrFgzjkd-#?#nkdgHoZ>^-fsu`+@9+8M-KySt%Wo~gTVv?fV}5Ho z-eQPuBcjpj_Y3&FzxsU}eh*i_Z{a9Hvm=N5s<>*mU*M}S%B1$hD63cvg?u&Dn8p+p znM=D^%@!+&V^oNf)@4ZwGrdKH?M4<;n5n{dic~^sH_n*owU71cc{Xti7oF^I3kN9k z8l8t)=h~+{YY3eYY1li-gQY;g(9iZIR+=|y9Ca*@3LKJ6L-Cgu(fBB47ojDVut1S+ z^-DRqsxVt4-4>nA$<@3eK$3pj#L3mPAw;g6W;6dRO1FmPvUE%Nfki3;E1F2%D#}OT zDOHAaeOOp!kbiI8yI)wb?)6wBTlYME$e1PWD6FtV?8m$Sr61*aKiYU`#wNC~IPp^^ zIh>g9iz*Y7o1+Wh=&n(*KEsNUazR(C;Y|^m7a6h=hurM0d2sf^8tLYJA+aAH9b3)x zYCrJpc7F+PH;Eonhd)Gemo*Ed9i@glM02!=A|xT2!77@198G|>uU!i<`w#W;CNYs3 zvS&9SBhPM`LL}Ts{XAp`@7V=hxEW6O&RAxy5@%9thfOQU9>!<#WCf*@O?fAWYtQ0L zC`fWm?sz8GR9+=3O)zJyVGG{y3pb5WGoc{&CDn2r27QhShYy9}UQ{cFw}4hyrutivJpWjF@XvkgfSKnndW9Xa*!-g>dQPTK*{mu_+CDUsSZn3 z=mHYVkF2!B$%3qaBbNNGeFd>v4>D{eLZ#X_#2bp~hgb4dO+TY;WSQXwXr7l+6vzio z9G)%Y#G&*u4RI*2j1z~7(I~I1Y_6s0^1|us-hJ65! zh#nwH98aJ%NgNg`b`Te40jUFI91uG0SO%?D_3#f_>?;}s=4VEk0F)9y4$x0_C9weO zU&2qRn8XcFJ%&#mfnC)|KE1_$xXb;}DOREAbm)%GQg*G{iD?cxvCYS}6XSKf6Z7Gx zv}joXI#AHYzV;}KZPf)BFpBXpW6kpuEC-te8Y;T*%-1q-rP`SvvoI0JR}g`|@Zr+9 zu(5ObC?e2Jc8-8X{>jJWAN&_TT-MVRlg@xSOa!{=qgpAvxN#_gmE+LJKMY$v`e*R( z!>Zbv7_MR**K7gTCMSjqm*YycOgAX&fT+wMr*6F;Aqbptuq*Hlal&)78nqk*ZC~q4z z7|3%9o^thM?0wwCfyXH^93pD+yecpk*7fZBl!9Fa_gJv4`{U`i>2)|F| z8eD)e@h;xnY;*HBy!iv(oTA>mw2!Cxh&Rvg;~B2v&0q0mxXsNo`* z?c#R)UR?dY2EXT0zfZ&OS=8@S@cT0g2g&K9ik0>ADq>~trX*I@uo?~-G+g})gNs7# z&c!&Qb_Sf5yqr-C+s8}uSq*;q6t1I`p*mU$xXv_YLPE$NX=BR=m>wVLklC|!Nqo@V znD}NNoi!;Lf@9P=4W_)b$l}%!>URK=#3Gn)!b4k?V#~^3-cv2}*aw}|&0`H~BzK`UE09hG6?(MYcT&LWu*LDQ~>+>Jl+x}3R|6o`6&x`+H7gzc^7q2zJDgkZ| zq&gGmB{p_;Pz#X!%jLF=WuzPqxiQxENUl+ zN9~wc^&)bd#e^G^>LtQLJH-yD4}k7fenh0LvYf45#-wIz+xY)-;agrr!Ee=KS#}Sb z$vyKnF})wWnQPu)lFI)EntA+Kcy<^*`;t~(zu?V;HLM(I*wjNdEIRPD4lyua>%MSY z)=A9~*-&i_@TuNn6j8Sx4mZ$3nyn{%NIDynIwGb74`xWD#~UhiC4tsKFCOOMz35E6 z2*KhCfD7>98UB@_2XFM!9IJXNwEcrV6$xmCHe^NBKobjW&7?XNoSI@hP z_R@OY9ga{;E~O+o&R?+-Fn=+CArdjtM;3krB^B<%nL%Eu1Soy*11btQDMh^{BqqW3KNjj|M?#`2{wG+tA!qh~x{3eFL1 zDU^RpYw)633W22%uV;%^ay=iXECv6uRIQ_Qy!PDe8s73!pmVcNae1B2&CVG=Ho}+` zk9|9uRb26-duO!b-+J@+ilFzJiEZC$%e?Dz(uxm^Q(p7t0{)tXB+ZKVl=y4r>CRVt z_U^)pUzt;0@eijduQ_`Gt@v}_XjgnoFPjzby+mB`J&Z{Uu&ckZz>T$=FgRru^bwUzuVIfSKIFtepO#fy)54;u5eh-|8hX3T#e zWS%Kgl`$_pnUDGE`I<3bFrSb4QogjRejg!>c_mMI%wwi1V}3K1#=OgX?U>)nX*1@F z7mH)w)0ng!yE=erc4xDH%9tO|u8eu3@8vOnG|lmtFV3!x`Gg`k=J4zT`0QcY0-90T zg)v{!C6h7FrRQVblg9iIBFn99Pr9KD1XRWB#KjAM>wh%((fWLFGvAQSX4COuE&Map4FJ>Z#@U&%wts=~sM z_ESy_zexkjht!#RcnRt5_&M^Beldk7Kxn-hm)&thA5mF?J)pkt>qGr^LVxl`on}ne zBd9PLi`+#~=kQ>MueB+gDC+bzCV3*}Gu*`xX*C@7P~O|!n52ha@sOR?n16ea&P$!g z`<)zJ-I(MJzwhfyt;^>qs8NhO-I>BLhPphQ*-hPCbZPwsAd8zus=w-{eWhb=?3E}u z!Em>IMhvM>cADj~`})yn71>K28dX3pUvMl7FCTu%QlSY&d1k}-bQ}vSMYqVuo}-jJ zDaF^=wW}In8-!(RFqtO?+8lA=ft#mHsXeD`=6-#rcZlPUT_10)!lY2cCywkT!rd*)PkIYhUFRI6hkxvmQ$Tyc$$sWn4 zf~8=pT4_op^P!wDc*58I1PjCG%d?qS@KjKmEBT8k|6rO2$#onbbc2B?s--hC990p!(>1uL}aHqP;*{^t_#3`W~iTW zjIz%>dpQB{r1#DktjE&NcGG~y8yyPHyV{8gNtcEKtF1o#LiM+-W zAp4pj3!JG3X`zVd)2~n3md6N8!+} zCb@=rBp&}T2(zAXW#X7dut+UXVE-zOHAGTkjGeJ|2QD^TXKeM!DRgFbOqf5_N{NEm zs`*qdcAn0=bjMlKrC;%@bR=zpBYoIp&0>-c9Q^F>w)p{tT4fY3Khf32K0m-42DDgS z7hO_?J+K_Uqe*j3&!Pmh|9woz^DN{xap|DMge(}(-6l`kvzU-kKg8YZ2WxU9gg1!Cw2Tf=gDCFT06<(m+ONtelLUn zX8Z~{{j>4o`NC0tUS!4jg{7zhAIa^aiX70vL{LVPeop{0(E)~ltn@KH zQA^$fh?e9guY_jMQRD9Bd0+#aU{Zof`k>T*`G}Aq@XBqx%u)bmT8AdVtMs^Iob=ub zt$R}Ime@KTyq3r4$Xl#i0bbaftvm_f~V;V&J=LcsV9Z$R&*^9}{SanYfh3!w3FE#ry1)ESL zDl2fp_wok34cl!}N@Q0}4))*Xr$jl)z11Sv!*C zqyt`2+j65$x>ZWY>TXr7hM6DP%a=(glt8N~DJQL_%dc?@*MSvg>0b{wFjZhfk!ieH zYK8S@USh;et#eVGOq0(=7sUDlIF2BHO>$#h+5XlUUL!3uR61^1U7ad~dgS5sYfU>$ViJ(dJIJH4kIF0fP^sGacwW#XkG~&e!-3QC2oDmi4%@=nQ;8@ z)|r^!ItBbI0uRW;{b=O8yds15C-Bpj(R-bh5_buMsYOL>!Ae1&4wYWZEowpw`PkDl zmhF46-5pk!FJTNdtb`4pq8K1G^P&VP^wziYvb^5qtm74qq_eEWRBBNfTi`rO)2Xtz z<>pKzWhaB<%Vojio<8o7U*>0muZHlLv1jhV3pb$_h9vE-S!&{i`d8!lJXpp~7uw^L z+4;EH`G7e-&(TomkQsC8kfP1v6fMx?d>p^e=ih-4w)V41vD6b<@3_1xAG7Q}_ZkV` z)2>AL*4~y0-)`ztAR~OabI?)^ehGx{NNpf|c?H7vF&zmXp4Rx1qUotFG@YwvXH_|m zpz!k`Tk$1u)bz zWi1!<{vk*-_3e%UmM~w{LUHPZO1-Kd<&Lmen!0OQqZe?cy&YQ!b7Y#@$08GBFpYa{5ot~qa z0QumxK+N0AkbPK%mA23AiTTEF-KdJUFXlMzAH#{c7p$|?4o)nvh8IrvV~X;Q@q#|f zwPnxb0Z4y6kG&?^_Uf5CQo?=%Y&B7*S3sS!B5qd7-7|8$pwG(4hB9QM8L}-5SrI#A z+g}1Q9U`NXpQPc8*EmNFpqc3c$|ULnp_4v3!;_NWNq{@w0OTaeSgR26PD+N~3a5g+Lfk4y{Ft}bO^1+$Yq+cQ=(7|1>=)(fo0FI{Fe z#eSjv+sd8x@A9X}Y6>B%ajHSA2Fo|?VXUSNvYKzkF;)|ZtfmO`>bF zzXBNJ0=V&;XMl z#=jDG3j8aAT6kd#A3Odv^0~;rUThcnSAS~gorkBt+w!mKTZn(5Tr3@BErwBxTzR-r z$l`MSxfQq;49Q7kTp^p}xA>L-4G0*^8Q1tvXIz1zTpqv~mn@q56XTM(Rl*n&)3yV>({QUGuk0a2qxiF0iR^g-xBb8UkM6D?Z^55bdfh z)1rWa#IzL7*EsmphC9_D?t~`OfVKkM4^I{D#NyKE++TuoZzsSF<#5f}u83+Y>kyq8 z+iCL1p6wiI=`ORKla|KAIoomHjh#^44$Nb?RuG36khUy|=Fp1owD z&?Ii!&67vN79x->$cI;I_M?AY75bq9<*EU|J>rdMKFo<+=aC1DGYAv1v`_- z{zrBuk!l-6lgJ2a{Ty3kRTaJeX|#%L_0w2Iwylw^B6A7+Rc<^@G()RMpq*7@mO_06 zox%T^-W!eDbNz_uR;W!xcj|eW=&q$_rRS!V^aPDd@t$u|f#@y|u(6F?e<<5V^!`{C zm#GO+NY1e22TCX>U1Ibd=Kr;QIY#@oz7)TWRh%eDlF7@k6N1-O`dOuYq zqp-ArjQ(MiZlKi!nl&f+|CvcTBv8^y6iW9fsI^SeW>m)dZMDZ zVE^$k_7<=oLR=!nY@Id^<@wrzR6YBlv+Rjtayp$2Wj%wrbTcY3F?@0Zp+Uq$txbhN zx--aiVFF||4w>D=&Tnom17BwbnEKQ0iRZ%h?i681GP~yxPCN_m!f*j6W`LoI45Gzp zk_O`OwRG6gMA}>Mszd2j`CG3KyJSP|Z+l%8|C3(&3RTB{@1h%7 zGdj>}*vavfPpG(yw!*#lv=wqbK%#mRiRyRdiKt?rETF3z^*u(2wMBxOKZ+65c1Te3 zV;$8rBB=R?aDsZp0_3bQ(!tXu865jvh%2`NC&Oh&|B|+D;RU@FIm2% zllSgmO*WYh=Fq2G&GF%Q7GftLJ^f6$J0n7+yF?JfyV^=Ag1kK8Aqi-oAu>N$CrgiA zb|U>agQRVXoyfe}!bIlX3V2@vKa1cK?L_7g7A7*strJA%VbtPRY|-3KWNu^;Mdtl$ zM3H$SH9Ut6^Eugz%q}ZQWJZb2DgmLK9F3 z2-7G@zx!E!xz*JC5;kAKCF!M~E=e~>^1YOkZxffKSM%>dvI*!cGXVv{$D{0H0DMfg zF${HuuEJ8&)g-Dv`;<)e_fk)U_uw4eJL1 zHki=*f$;wh)9W!$7>!0DXMt9ss09oJ^MuDFr{$$@6948-pQJI#B!;h{^$yGbqpN=E zTk@4m-w{lYI%WM0{jCiJxw#gOR9r~19Nv&!F%qq5EP&-827J-SC= zEEyypM}@{8PqKv>%2ySDlkYL==ti%~0smm^X?X(y@OuI9jtsbm0d}e<01gxYEi&M@ zOaUk|MdV>&$v_off9Iy2*Vniol~Iqw($@)YJ9+vCW&x%iW&9{;paVNmlTO5qTHhnG zTM-^2hip9UDAHW%F0i4r9e{Q$Ghsa&;J}3Sp(A0-%CK#ltYZ6FgRKr?dy3csNNHo! zpSp_H=V3HJ>0`f<+3aZgJCK-d1j9%E=~_?Vi4x%bB`awit|wyMft&7!r&YzL0iPPk zjuV8B4F;(8sNR2w_NXntLto18RH`9oyp-i-Oe;hRyRwI|Rb;;>{bcr=$7&eIr_m#! z&`#8Dq;~*)hXMYKfKNDJTLrKf0+y1V1E7VOy+-=UX0HINp*#mHnefFv+ zjf^+}8F98Rh!LMBJ4icZ!1MdtSiJtaOAL5^U(SHr{YDnArCg^m6;TwHuK;hFDa_8| zRZ<11HVHsVg4}&pWAK`fU>^%B2Cv+=X+^yFU8C0&ZZEEeFR34C1MEpEfqy>9Y~@>- z+s~Y@+pB=RySb_Mvur)N3ixPy=JvDFx5zm4te4h)R=l@rpjvs23{;z1*&3)qZUC>r zn%1)Y?8Z$(wxXBTes-~!imVSq*4++S`gK5dce?}o*%XGgSufRoHc7zv;3{GCvctF% zF}~l<_$F?k<_^A*nakl z3i5WeWBb{>e2V=n?F#Xk={GcdW=9K=&m6h-xAwCOx=*v8{qtC&q4z(=N4j#!Ut)rW@F~bR5A?g&TmMA@$@@kE=OauZgw%Rp8NZ_g~AXnKAjo~Yh6*~y!i(V~HW(u$gS5oytA zq(y}aI*cb;(ak2Fi2P{rh80~o5$btKjVEfKTZt!XbA=R^;ukemr{Q5D^@zLlFT@jl z8qJuuK%f}|x8WWetBmBBlKIDW-KAt=zoc3v{+9x z-9gF%x>R?wmcnxGo9wB4;N~FDh*NM0znMg6PGHE<`VGbyn!b z^Tu}Zhvyv8i|ZE@dNIB+rx!)f1HGufEi-x%ZV>2&{*p{DYHg#{RQEourtg16deIr_ z#lyS~=|yyB8+vi_0?~`;PMls;Ij7PKf4xF4N?ss(QRY_-y=W9F(u)q~{xf>T{g-G(blOX#6=SOq zt$4jlrWIG|DeW1pi1>qM_qQ`ZEAkj@XhoA>Kbcne{OXWaIQ{xRB45lupAo&-=_`L~NxMmGgI6QJYR7y{L=yA~Bak zdJ)pWhF&Z>OY|b79j6yAr&W59o=u?_FV7IYsDDaBFY<+m^rH0Xe=A=3v+Px6X3M6V z#ak!<*|EJU?G&?Dy_+S2uOaY?j_p;6r{Ssb|FONQ>d6f3RfSGwV6SpMnX$dh z465H+Yp)v8S~Upu9Y6*lYmlu$XznqftR1$f_NsCW*~?a1dsRsRSr|jM)ee~nk+s_5 zz+QFfIALAVO0`#=Z>3_a)t@j%*kOEi6fpMRLR+Qr4d$;siZr|^((v>gnc1sGwz9ET z{YsrMjBLS4d5xojy{eq6VvPFrh+}(|hYGT=uVZ`FeHX=EHR%Z1t8N_A*sEOYi5wx{ z(Z97<`8|I9|H)oeKoaE1Up6CA%I#R5ymG`&o(!8R%9E$5wLz383(eQ)o~I_s^5mZc z>+>(klP|v~-E&4sBC@MC$VApkPbt8Ntk*SKIr$F*kxj{FBTv3N{K@j<(Zdeq$t{Qf zN90M|hZ*F*LPs>JqE9SqMIV~Hp`BU@XTCn`L<}ClM znVSDr+g6?&km4}^?Xa5vR$I=0O98SHzd6(Vx2$6R+YwowoU)nazg?yk_23}VA|2AA z{_c+R-|=R6MW7Y8HsRiSkqc&$PB2D!@TL;~f6$Ir6q}^d3eSY+ z)cO^)P8)6MweGn{C031-sf3>P?;C__3&N>mD&db*0@qulOeca_F;ZX4hsUI6UMfO# zVdff{F8C4Nce!X5(=XC2h8_UAus*8|UAT*>!BjLD=|TX~g+L?;K@O9^b|5rA96d(P z|2iPk15+2fjB`HzW~n7ClWVY2PMFzB>4Ph!IP|@E3aykAjrmIXW*DHrl(t2ZwvA|maeswoRGoXkFxY#^Bhar%X7bOjDxvvqt3mt>0W-jbZr)Z5JZJ_JN5T$y$d#n&dw zqIgDg|LOa0;3Q%H)ud6p?0El`OSap8%f^Xw(2QDNW&7{`D$V{YHCmp7egt*nv-e;5 zT*Q|@7NbefuaqZYJmI~?CL!h=O+soCOhP$ln@Q-g?^E_))qVE+uh_m%*?&z!ZT8xic+<*Q_pR@mlOc3^;F!%QRFCfWo|E(C4 z!Q79s{JBE2|H_VZF!!Ic|0;RW)I0mq)aP05aO%g9S;g~lB20ZnC!48{-1`srU%kB! z_FtL3|78Dtc7DX&al%%dw-MhdA(zAah-Ha(TQTowQEXmEtrK%;^CK$7YWC!JG4i|| zA@F^<{>}V|lCjSzKY|g<-apBcb%UO@pH0@;(==J#_P}JtT3>4?YtJ6%&&rROw8z;| zenkI0@}~Wi{D{N5|8MdmZqZuHI6vZ5HX(^&@E+&C&5!shm(U<+k5=Z^$2(iiMR z`uEnB{_Xr^`@yuG4($hncm9vq58Qvr$bRreR>35&Y-i^7g9V-faMaFB><4$-Ij|pu z3HF1gwK;j}x8W1*2YZcVKe$(q><2~aDfWZPcZ5k>vwTwtb7Hs>^{84Qbdhv*PqL>dV-4r*-LRt(^jh9F@ z6q2!OnCy)?upal@F>rLZ#%<3Y=j zE?7Ljmg$d7#E0rEQBtp2^Ku)3>q(&H@($zs_=xYt-6 z)XE~<;Vl{`PgD!J4yqe--W+IUPq5ZDQId7eSQc031ZxVctJF1|V6hIT9$>DP{aeT> z@=Z;xt0keP;J0Z-j*&*CxML)X>=?PZ2`JFS_2_CbNzq%R;Fo2N2|E27;+2QJY9mT(DW=LOD=U zBw`mB_UzF-UfKzmmz*_ZwBk+$;s+ z0?9E@q&{iXx^pgmU{0_!-R}wsLKl=|OrC{#q1kJZE=Bj1DXJS0un0u)7i&s{?uw-) zg3$3t0F|4dx*^mtB3doIP{5O#fCTj+v_}dfOF0ga0~}R-=VNB)UnxHmsTi=gVBQlD ztSm^tHgf_-CA@a?FPNW+FTnFNdDf=CnziX`S=WptuS91ecaOf4>8n5Wum{rDG6HRD zy}?;WMJug_37cioScPDFGSXOv>@}YKHUMe71)MO=o5V5;$+A{Am`qSBLrjAQ*3w2|y<71cG;L>}`$ z&;}GCCvB#a)BJ^9buUUgrWDExEMG4n|8uJpyT#|v96FQpe(N33j{;F)jc`=rOA%Vo zq28Z~e{wZ153G@cMem80mL%%ji2n9t`X5PMNUdY>1GUqluw?2!cXgn z68I((C77@V*;fN(U#A}u`?oOAiC1~t1SGNqjN6#YE= zT9T{_twAt0!EY;A(l_KBrNM4{ImZH?U?HBKDVEh}fW`A<}D$OFx;REL=;GRmdtzXR(bE zJA`f(khU)>{AVSBS4Hr*b`t$O0zYk8*%w(Rw;61u7S*uDQJH1Flw0&%g;8RMc$C;w8Ij0|u@HR@^ETSaB{E#s;ckq)(_d zD=aqOOe!iwuE5p8Dk|8XOJ`o;%t@;aW3Eh%9F$rpu!3=ON2L}%E=R%ah+JwxFuP=0 zU!mPk|5<$neJ)mCVb-(%AN3VNS26yrsjsm9nOa|Ama7n_b{!ow4(ls~dI(_K8JL6m z3dOStVDStr!}^1)>l}s6R=hn0B!3l9Q+_)%qalc)>r7&$gaLZ^#MYC zg+EIR^%d&F4o((<$X_n(O^3~r=WM3|jlhw3YAET`00xLa1O zui*Bxv(T3o*q2D`%SvWi{Pg+?MJ@gBqBL=bOA|$w%hE(Uz?;@0Sh}lLSTNodM1&VV z%OXNJLYBh%3YD$Q2Iq5EmO3g~4JSBYsf2E8S^n!bB2s&np_EVMMkU`~j+jujTe@E340cL@u*%>d@=V z$}IGnRrhfYT>xA~;K~bEPPnqd_3=>-T_5;A3$9&oU5Cr%uN=Dae`#eBjXW~{^W~AK z?lSvrwT7ycB2#T;Cy!KJ!sL+<1-vVP>k-^9i>*A8bqSM4@^%;Gk$%*|3tK$1lSeN6 zD9R&EBSd**I5qUfhQBzFM`nkUJc9g(!A~IgTnHbBf<&y}6CG3fEjlKf++niG6(*aU zm38LfFJD-l875gqL5K=)LkUYFZ2ISeu?kzcNMncW+uVRT_8&^ z`M3nr3&fSnAjZJ|um`Xb@cV1nkoet=#F#F0$lgbH{ZwxfW9pbZNQ~K4v5vK1p9)rokKX zqb%0A>8GOk7{r=g?dj2HNUZsV3=neu(;^Z?F5@o6&UaMAnjt#DEp_t;0=sSIW{L|6 z7~0_#u$>I-0l|C}#G2(ytQnXpJUZ?NRS5Y@1{}ozZ@v-$BLu+LGGI#v82nZMtS$gr zWk9Bs8G48qa|(cNG9WASZumy*@EvRr!D(^nV=L5 z;0cdF?MUm2Qj)D;an1__3wI`1tX(Jz7NfmLuxR6_2o~RzPz8&P3q`>qeLe^l#n#Zq zG_R+P8MY9)^-FIcdT(#MrXyp*@*zWepTrp26UNXs+b}d}TD*h<>8-pM@z!b!N!}{B zP?fjN>qY)L0K0N%HClJAz)RyjFBEqmz1`LmHp>FSc4&c$ZL$ViQN(rvu@$$XMS!jH zw2r3uh97{ZfY`q2BixU#bKjMA*wmQM*8T8q!ilqmjCo0g{5jujPu>h8p$z8=QO94JEiw25Gt~%X9gxhIb3(5RU=~dG&+Uib7j! zKa|?W7GqLd4V2o7!jxQ`Pg2|1qFidLgY`3{BdL3#8H!uw&nr^dt?!xO)#Q)4!+XaP@nQ;FiTxTnc%3N2*}|Lymxm0Ycn^Q0W4O z8UYOtM9vpXrX)J0ZTTw_l9pv;0l7@d&4yY zuB~ughsz}$cLeVt}o##^)_Ss(r3lYZE7!siWFLn{5I9wVa(is zxptu@p7GB3Xu1|%RFALOrX8Jzd!>aUT$xGZtuUi>OYH@D_m5K zmm6P5G3?LvP!0P7sx$rXJB?vKcVVqzzk~-j>>rp7%;m|dzl)c9cV38>+c-xirhlxm ziokV z*yDY_BcgpQKPTGLX36n#V^1me_)*^yvyJ^$V~=0#CKB0Av;H<-E^+6l#mjwK{QkyP z0s*YJ3hBf#Z-?>w{b$(`z~7pQG29KQ^$Bl^-~S>;6D!=?EYrR21a;c`bL00ti;{Qt za5~YrX2WC}H=OXEWi)O#nL<2E&juRT|GW*2`+27GC&ceZBeYHYez%!2S^FpP`%-?J z`2F8!(dv=%@zv9KhF$!A^%F|`ewA6wem6_AjM}=2%P4Zj=fv-CZ6SmS2y<^AzdvY( zUHtyNungvYl%>otP5gep#t!EGbK>`N6{e{_@rtItb(F)Y-@BBizQRnH`oX{2O#SNk zjHcdZ*2f{BZTx;;y!nsg_y2jkYMJK39*kc}B|E(x#;aPU+wH;5W>EU?7E=yK9!W11c&jeslN-rDUw!}(8Q~@s^Fl0 zN;M%~^+#_m9)zv@#CTQDd=#%*)j;v81M?{Hs*{{-<5d@zcNDMMPg3Gly_~p6uqh5i zf^I7_i&uSkLWoyg!d!q+WfD5Bq!o2~5v{0k60-3H$i_#Xau~1bn#U$yHC!Sp?3#;H z;go4=yz1^lO1$chZ;1Tnk~HM^veiu>zp2yyYx%Apn+UXG>k8ak@s9a*;xs#2QPCvQ z>?72ACgaz`25G3ou3(u;+$E@QKbv1~=s_;rymyE$EEy=%g>=F@htUQ1A7~bTjRU%n zc*2G*JfHe0{QA&Tdw#uT>ZkDQ>)tl}x>6jil4`KW7Re#O?t8SF%pdLX*fA;=EDyEF+!`^l5gn(z*1lQ8XjnuNhq zU=nH{wV8yWv7fU4LSpUrU&Yu@*?*mK+U&orlWG5T%E9+vfk}4z&n;Qme=d`0|NSyq zv;V$$EAGF*NuRU-CN>oIpD_3K`!8sc-TvEAKZCg+WqI3Iv;S()wV2m7z)#D6F+w?S8|zY~vf{%0v0+=8bs z9kV)EUpi<${4q^~KeJ-2RxA9UAIbhNZO{I%HoTC^eqpObB#N7D&Pp2waBVxC77wQ7y6BAm2&9>3QUMfb4;*aiWsJCO+@ z(QlR$8WImb3)Xvqtc`c!({#V_{L>=8LHyHlzew~u>HV+cxAlGz?ArzWHpef9eLM9` z?>*Wt5bk*g`vu@XSUvc!H7u4kB`JJ6B)*&rmM7a;TOO!|)UWo~uO&p}o`m)|#c0rb zR)DM_c~}Qwgf_0h9Cu7o120)!2y$W?LL`1M@h=h7llH79%~?-OtS3SEFP!zVC+lT@ z#lV?dk~Bc?o3x@^_L5ihChB8HwxaiZODj5M0<7q-58ABgm6#+=nJB?}e-_9EAC{B$ z03Vq355dT0eJ9%`FQpIiR1y+`)?IfeX1z@b8R!~gd1e>3+Vt>*rtrQClspZkwyasSaYlQe|So}9VRc@O>6wJ?GWr0Lt; z3t_KMk~8<=Ex}w{eJs=5h}jMzTkuZBQTx;H0;DI0jFBC>U9+gp+EUJ>=Xu;08r&Ge z9Zsk~#)XWRVc#*Q>7g3}?B+45({!2)c#Z+SxGn%r6#!?+fNL0_^ECmmivT!Z1{}`- zYn~PWs|tWiWx#d}aLqXZFoyuRS_Z7Z0Pma<0Pl`g0XNHlt_<+9_%aCs;BFc426Gip zET&b22Z0PwsfKV?_S`JYSBy!13HN_SUon=3+f6m3+$^e8nGqWnXdg zXKrL)%&Pc`ySS>p;)$b0U-8M2c*xIkX8)K$q$zX^YO8^$t)5QezT#xG^`2ddw%$cV zzC)!>K|r*JK7l`@?u6+58=QqdO_JeHtIhDI(^B};V;21BHy-|ML-((26;^%Ahg}@_ zmVbSUl9cDQQ6wpS4?{(-jdjPLiei#Cc6}?1O==Jk1ZBsB!wwo18s8AuU%I<~>Jjn@ zmpu328Ty1{0}B0Xd%UAhT5k4S{*8<)5on)}E#-ntvW^-4Bb5bl)OFSfbOmaml5G&>jpSp!05u=Q84kt%!mxA7b-o*g)_541<0RPKStx0@ApXDlVcG5@po1$iA2qTINV}llqpO) z#3U)$3FnzG7086C047ZNGhxct68FMtO`QffF6Z&o9fWHgTr=UC3fDNehQswWT)pAy z3RgS0JPn?@>Tq?1OM+`VTsPs$n%7hJC0y;{nt+#&r*0@*{ov{WS0}hy!PNw=25{Aa z%aQ!Aax7s!g1nDn!+X&K~qhvu^??2kFIXNX!NgRYqVvLz5NuU@!%|Uoz(I_Sc z&vFn}=p5xNgcWMV;0HrxF&LLiSNm*6$MSIGGFV{vEP2%cDJVBs7H&bg9m!KALecgQ zeCrwJNc=zMt~(&d?)^7tkuox}va>=+wg^Sp`z=bOqGUv5L{=WzduQ)Gv$8|T&R#E` zWRGO_JLfw0x$kqYr=GrkUVr3$DQIj=lNqtWMC>80 zW?0WuGenRY1=HJ%eA6MkWtt2nT%2rMB75N$BU^BYV57aIg5`;gHmsnmoUf!gHql0r za}OWVzmQzT##Q|Fw8Hn2@`%(X{QKQRZsEMuf`yYl7%ZIP1qBOdo|Z+^YJ-r4vk0p% z15Vo*>r^zMTBD&OMJE%8QHUcT_0y5|SxCph9KNx-%Px{L_$9aSXYi-w2ePyoBqGa3&)Mm1N3<8PvVTY-2%efV?wT9gDPN&_ z?+doMz2=en%p}gMkA(jsAVVNCQ&Oy{5WdPrzPFwu6GQ$HixD@Ma=te`L>5C1c3^`> zd4caN*?e#A!lM2}uPaeXsS2{zzbW7QtPn8Ba>5{IJ5dH{3Qx&L7^M0*SSlMs5rh1@ zQ^FucL-fw~YJ(N?y;p+&l6)`hE5&^8q$5lU*DyqPzW3`XM)-QLj(l%KUU|N^wIh@7 z-S&~g(8IBM=6j2Og?=2(znI_$jY%`1`QABCO(pr>i`Gi{-iTm6-)r}j3*8^-mhcfz~DtGz>8Pbig=L_B*Tk_l~}xRg3)iR z8N4{WUxXJ&%2T}P0J86`e-B>NZUmv*`PTq17PO;y(GQ;T(HdsgU^L9`&H)H7_H32l zMQWhlcyS<55ieo`{}Q}#9-xL7T@Er3a3xSTUYv_(gx3V>z>5X_<#?g_Wg_6kn715W zJYK0MUcCMUcwzGq@S@EJ9xwbJO7X(HzY<=w4&?FT-2)CUX7odN@p+{#yhz;0;KkS= ziWhHI!iw595?0jMAdDBcFX^E?cET5{p5I2vB@99A4n_!F>P26+-{RKIHyBe{uiA+bmuz>itLYVpBN=FZ!>53Q}df2X3jUmbXH(oex3v1Rb0 z#0w5DDzDHJFJfK+UhI7dc=0BM#|z_IQoN|`r-T=&K0IE`yvgB3wO$A>8n4iW7XxA$ zyeQe5;)U}HSW#^T!;0G88}2H{c=2PEB3?{Pk>JHWAHa)=FF3q#?J3}e^Aa8}YWtA; z`+AG;qU#M7F9Li1QM?#giouJS%b}7`884iB%J5=qaTYJu!suBnUR2pE!i%c56fcg0 z?A+gk7Zcw@aa5aqfET8&6fbVVQ|1%A2n~hV{py48qHL@LFaGh?8!zm=74agk_g{h+ z8+xhX#hEP(1h{zX#*4ap7~ulmI`HD7fgCR?7hv$>%@YnU+AY@;FTAyY7ZaZYUL1YK z>r1$fcWE8EgzI!otRR7(LjUyIT`CCV_MHnj0>f z%i5PqL&za!t+u=N9lC7_yRU})=2WIt|G5uxv7S;b`xhhi2lVz0JD<%)YtL!R*#f#s;Zvq5tzN{%G&YE&#Gs zyNf{f#03^)uj7FQT{Rb=J%TIl&~#IK?3>W<$^&h+nu*$bBZc$5XeIP_#zkB`g$>K~ zgU)hKd(sM;SZYayXko;br5JzOV8pB?D~y=ci4;b}7Geq`4#0@Ec!ZIxFrrr{zAz%O zAX^x53P!cVqY@3og%Ka~v4s(rK(IX)Tn*5jsQvITjV*?#X-#qaCCGNdxII#@&i3L< zk)@sOao6XC23!ZLy$|8A%A557++XTa;Ld<&cOk&-;}46fdslq_`vM7Yx9&(QBC^ut zzBnAbR%mjcqa!JQQ0nol?Lf*O*e3TX_Z@q-WA8g2DZB6ZgNx>;yXGuoGcMq292D+5 zCf$xl+Pl&Fj^W5YZh}u5DcF)D{QTcZ6MU|_LSlwZyUsxUT=|0dzT?qo7cSh*lngxW zU@Y#gkPcve5y`dN&&VC(e>A)P~WBAk!wo!?n* zjFyOJ3KD#iDMq73kzxRkpcfyL1`E)SQDYg2pA?hR{jQ0qfqwS{q20ZAXE0Aq5=RrF+zABM`LmOhDsy-7(=_1CtNpUHrt+=!vP-VUp?mqN!?(VIgQXW6{F81&emr zQqup(5B5agP8ewGW1#&u2Y_}pwP?963Q>0m)Yha>d#61>ZB#M`wN2Y1#0&=icN$*_ zkh2rpjUU<&p28wF)A%-oB|Za_GxMNtBE;JV1 zcORae`|j)Iqo}-L36zmx5S(VnS;2hg!0&DFrn#Wa#f$F&aR)5!Ed%jH5Wl3omzTNk zJ|0F?!y_uw&Vl@W_lw)G_uUt;W;+Ka!occy;0FWwefM2+L+3y^HKBdg7&2OOq`J|N} zHPUH&z8C4>{o5@i^>4K(+M5E}I}x-W3=ta3L+x?>8zO()HASd@^TcS6JMewx?DPQ` zX!~G5mo(mU#G*=P<%+;Rj6znugaZsb2?xMZln}?i9(b99RqxVM7A;`-Kqsd}n(x}&Fp;-!vpWF@SJ8vqz1$Y4tYFRsbt&i3F_VOAbCTC1cCD2>Bv<6~& zd4(gUjc&M%EM>6)>7~1PBF|38JP|v&d$7H9kHP)Ng2>D_rUt3y@hPKrv?p5=z9(DT ziF`fV08glAYu-x4Vj;&^7K?Nv^=uy4Ve&vApt*rN_-g~8o{e)TG$A4$3x)e-1P;sA z{aBN8QPf_u7%I-J74OG#b0dyKw_J=Pu>(fru~xhvtDqb2NJLq%j>KUYWo4~!Kh`D_ z){!^^g4WjbeylCkME7I8Hl|+06A;XA{oC)yvTX`vvN{&X#+fya3gLO-NT!$wuR9uI1E!16ytu5rPgHn9?Pv4JK+g;;Jws>hkWV% zSh6w?OVD`ZkW^!TtTAjctg>Sey|A3(jxq1M z71|ELQ`GLq`gloFo7)Orz~*Bj2Yb7k3HM{Ii{t^Vq`UI{SW%I}{aC3$F(aCbuxwQv zi)DM7W&3`t|Iz-AGofI%YZ2a|udNjA?}%o^{?_o~M?w6Jm7@La(Tv#NUM7tFeI7=< zwNkXdZJV+7_uL$;{e2S#zOz!Xzi%1H?e9l08@Zhx85f;nIAVoNZf0Y zV1K)vQ?S1~5@lXQnLAPTYp+zs|uzhPnc*n9qGt5PIFc&=@r)Yl%B}nY=;Ikh_uhtT_V@QG4Dhz8uWEnKVdcu# z*KL1qYAnZSi^nE{{rxkZ!|1^aRqgLm^}$kpc~WeD+ny4v{b5`1z6IjTzw09;@F|FE%d) zocyCx0Ay|zryx@tp0baCOp{hH{kIz+koh!H0y35B=v^I ze-cv}k++WSdNK3qjPSosy6VO5$=&z*@0s%}M-OxGv0#Co^ zw@B;7Jnxz+)Qf4H_tlTk4z1S!xK$CJuI5hdUrcf`I9Lm>=-KY&_XuW!(Qid;^Sg64Hk^ik; zESvQU)?cWz`er_qnOZ5{G2D~0-n znjcuR#~K7@TQT(us~kj@&ycqiiK>9?JgfiD`h~s@;8FX}AocG>C_cHsvlkG2TG<>{ z(;#PD|2|Aw|L&-d5#;TtVg$K5svAM}j#;i>h^h7$)i2DfrMrG1c{WpKP{vWUeqrW( zM(%r!%W5F7o5(8-Oo}Z$5mmKrwz=X}2VPI<68BQ>C38F{M-x#ZT|_;o0e}gL3?IzaSESSx5YI zMjt-ru+#x!4ihViSZv#Jmc`;LWFdZf!85b?DL#MFxeM=4@aIp$5%PWjPvSoDBpzW; zLh}(ooeIe0i1%AqlKJ5ydsqr5V{g3!I&L*tBB{+H*h?f(fzx5J~qakhVpp(5LV z{S(R(*1&lkvFs&~y$=3FvUUO%(IgS6Vj$HCOKpJF+QAyLE+P=*54iiCBBp=ti9jQ` z`mjic!4ffl6z@~m*?-L zT~m>gOhb_EN=U}DF04|Ms)%F^gAmE&g&2nx0z(<5Xj1Hy-3c*cV>^j|Y!l)iOQi7+-^xno^zoX=+_p+EM?sX$5SAO@4}JjHj> z#?H*|x_R)L#P)>sKA`(v$~w`QaF%D-4YCRY zP)ZjM)_i?QYQ|i|Ek=2bR#S+|4ISl171k&C=ZfHz0K9 z&fOBAH=N%Oj2q^*k}DwO&8V}xeW+4K&9=Zag?U}Pg}ttP_X&xEUYv3 zO3_m7zIf#MgKni3%gzMtGQ41zdbq|D7OYpj&4CpIRA8<-gA6`6-b96iEpO_xXARooW?CzQ0$^Fr3 z`(sv)hA?yRdhA{lh+R~`keJ3xIMq%85KCB0{OARo#z72LX$4SR^gGr!D;RO%TFngZ zMdg2@x-$ti<|5Oqbr^+DJ?5!m$BYz; zrzOd@_E&(H*D1?iVRJCcxsl?e95jDEjPl=$i$04l-wF2o+>%C|(um#uIWg-Xiu{sw zY%-^smwCOY3fyd1KwM8TeHc~e{y_mgLSd=^A1CutXvYb)8#n!Nw>8X28y(||k82K~ zI!(}XwAIz-izK~aQUCL%kn*y>D_irTp9(7M?kBiI;@2QPje`TY*uL^s%ed^HmcI*z zA~Dmps)}_fUPDB4W43IUau1?P3!3tXmwuww#79C;WXX5OjT{lvD`rmMk3CbX9&EN( zeoLW~J#QVr?{&(mq?sN3A2<+aVvU!`ZY7F2%b z#!Y_D(|=9Y^08p!ro`U+=FOQa2L#ykU)$#Kee=U+?dLxpYxq1@rA$LNVfmA<3_Eh~ zZ^F#A@4@`+51UlD51SmCXFpl7DG}g((tEJZw}(w{HkWEy183CFHg}XHZ=yho=xdZ@ z{DsL(L;q0|O{tch?Ea$%e%Zt31zP9lB8{hvPWRvr{HJjEgVBciI*R|zB8vYOR0-$+ljw)t0#P=-DuRUtuP@?LG`DpNQFgW3 za?rC4038ALtY$ntNR>!)O1VQ#tXCrTWa}K9IcF$GX3E&pput-90v7;ZpK-x?Y2V zK<&4ZR4VzN*WA1vVA+NH48oz)+VcpK7(spMMkZy~uAcFSO$gVJRkUQG2Fp^DlzV*0 z9rR(d=obP!O^WT518@z++vjed8<=1u(Cw=|Ie?=LzOz8Gv}qFOKJj6ex&Z3K!(^vo zAj2$-j&BK!ygf3@s79EtP7C~m>q)83a>}s{qD%djEM+gt z`KFoYPom_$4}O*Z9+j?-Kd>pbzB#pp66=3$Gj$&OyLhS)jT;BBit%`=K{uU@iHRY( zWp2~Qh@*^l?ZIN7Xf(KK`uUPQaKVtwdaj8thg6i!ZhVpKW@k7yM#wI8TIrN@Oesh+ zjr(kHjNyd{+)HJ2PC3ia?j`f-Fz(Dt>CzIaKh?lbk0}#OZ0Z-^&fKy3Me&MdwRUDp z*XFcLO`d9IfCf18E zh9f-1wjGq(*Q3kSSXqO{zg8bOoPUxp{Wj^`^&cj-$|09;UnQ{6jLMd>+8Bs&9S|9v zF}5!_*LE4F_@$boH`7{-PST9W%A7?SAaodOKXZ0KRFFTI##^@isU}cPMPGB5z`AI9 zmnOx>_#j+4PZd*NQ*<&$^5pWxezl66V~p5~$T1*)h365j$E;uEA7#EJeG=eLu_&Xs z=~lxj$^=s#+!+?*H&i~`TAvgKg)t|CIaoDHLa`>Vi=Y$L!wt&1)3n6Nhiv{;osFCn zhT@F%18+u|R1j~5?El!Z7;d~vwLQ3Y(TWm1awfiCi40x(R$M;6)5Rc-P(ocMQ_kpr z|D?#6u7Z+FJfv?O86wbB9K`bLwjB!pFXEQ=GNMq#`A^?lmZax1rJlWwzwsm*M6s|` zF6J(inW~xpRK1-NJ}#a@>wgsxints3mfKfaw}zP-SpJU55|fxg8F1A?$anFvgCsS@ z<;C_qqxyZc_XvG;v^PfbqKb5p`o+7Huc(X4S;>vD62IQYBxVB|TDzexv0szVk8s zn<(US%w=+Q>^I+8!|mWwzaQVcfYRfs<6;K`bXvM`@0akE8M4q1Xx;?baJ7DZFS{w_ z{9`Y8R||liFKr+tc9v#4RP5nCAC9AB@3ViI>_`XgGxozovnEv`g^DwdU|4g0e;_XK zAdu-FQc)jKaxC$d8)B+VV84Tuy308{OZqfAqGHubbqL<&+Hilil9C7`dAQt^xMpE$ zs+S!PLutL@U@H2R8=joERJ)oA+kNr;&I5S@e!yyiM01l&r z-9cW6p&tH@vDE7%%^>~RBv8{K@Hbxk_L`4tiHnna(dQ*;s86*8s3P|1u+ruJyAC`t zRI*jfY}hjC#inA?H!F@xDU`fP28u%kW%7vNilXMNcop|H6L~91YlSu12x!DnUnbcH zn-6-K8OK*%GN|x47S0Jg$*B-L?tOgh^%`fWiNgsaHXX z0I&U%r~OlO#iYfH&2#rhME<94_-e{@SlI6f>T7$-85NMt<);0I$?*^O2Vl;xNPmj) zuvF@cP0VsYqhGn*BZ4=T+7I)?BLaT__+sJn0r;fp4&p%}_)MGa&I8{>?}Sj;iXsZ> zAF_|U1w19=-HOq9lCu&I@a3JsSXd>d)uJfHAvj9y4$^`m6oe!F+k5XIQpX%;3raz% z-LRg$JCB0tzB@=%QvPQQb(3Av%%>Q!$OABO!>3;_X)?2AjK3pqt(J7&JbMD5j6yRR zaL~SxiTjMd>U8?4_7D2D>&W>IOXVYUE*DB5AIvFv1GgYQmYu)!nOQei1%i%AFtYs2 zCfBwsT45LrlkHkwQw*N9B>jzO@na^P&N*~0z~7l$CZn_JNj@*T8dP>HG6#P(NU{IP zqJNAi$|aXIhA7Z_&Ae~tJE~&xaKC;L7YZ9G2=1QG8loo*ROyCkoI;W$B?jZ}eTV5e zVK8-uu~--U)w%k5?ROBGN97!Vy^liL(ccVGbIl<*z@Cg+YX}DZ%Wl+OOUT9yP3TMJ zR%Mp%uT~Y(;8kxBW~(ZX>{S)IW>#-t;p9qISmHRhdnj%?+RX~lzJi_M67@dJF-6#v za^1tJO!_WhTru|EAdG&Edn76JbcWafvwehVQ;+VvVxaD%l3lbyYfAt2& zr6H;6Q(CoQsaAGPvB1W;oZ??(J_`T^yH8Xd(P{Y>82T)OP7Jq>`J;p?KlJ6rnZLAT z;nvrUTk!o#ers)kMa4IXLpHkPFg6M!e_5nJ`U& z?KjPtut1iSpt3$A^cWL5+fB-V`W)!&kVY4~Qu+S<#;v&8kC-4x%yIbh_jkwPL7O&K zz%U^@ydP?ces8;M|EiIES?h8Y2ymU*Qt^XtAjUxLZ&(;Ph1%1sXCseaD57%fC;15r z=5$T33};@?@kJa)zT`P_&C zOxUdnG6_9s%TZF$8&g%m#Jkn<(-HlZ7W>GhE-xaE-Et|3;c$c^n$T71I&!H~)Z~}v zkR#6M8xn}D()2O0QNX;@vO+U9llSp+Ik5teSqR?jqXW0}(ZyCWsr=E`6U_Zuv9t$w zJfJVJsyUx^CELt%PeZvQJI7S`#6&1p#ScA46=9#4AeS~zya92GXWH5ZA)6$5iN@c! z{gED?(0w6~BDM*iV3j{^>)hQ6?4#CuNbz&Z(BAiILtl(2HEe@;oE=<=D&TGnweXXd z6k&z71aP9*J=}I>SaxMM+>M?8kxnfUto~~Uq^AvA=BkJD9y8M1t>;3zzjbn;*{5c} zqHCxm98SO+H1qd@WM=!G6{f7 z2_BP=J}-r<{7qQ>pmIbQ`71a#q)))Xk5X=#pM8 zGx5v&v6aVBnZ9S+z$Dvp31y&K)U)>AADyV25+7e;1W5t@#`cIPze_v!*q@uD!b|0& za7wpbI42yP@#>kw)VHiV4civ~%lP2~k(n78zo#Dq2M#F(TcR8LM=9VkF(r7^K;5>=_jpNxW(}yx04NMh=8%=YqegxW> z)ld@$3$T~^s^nMeGT@5=i#HxnPMk#bYpd*%h%-~V(y>UE42JCYIPvMyJZQ=gbuDl8ZpaVJF&?{CD zhs>hY60>U3J!7l-vqVd!;}3Z6-O+xKbiH~B1#fpghsLz;Jol;lGDoLsNQ|sJ-n9BL z>@l-wd$QTmxhvVI^XFc&E(JbackBK6lYD-{W;fo!Tt`4l@3n`D=EE9`K8lKG5geek zD|rsd+>wm4g2L#TJ(+yD*C7t*dF6Q0A`UyfY6 zv9EUgG}eqRNIB=g=qDbyQh`x}dW3@B7^)Ix-o5OUua}m z)e>IHPs>2K<+-TQH~*=AeRQCTcDBSxmPv!WeY4%m8h@jEAa2x_wT^@%kUdy-{Aw-0 ziaf=D!u1(6KR9IoVgMI1FN3bZxrxD;MF#K?{3dfesYrjoJ~`#idA_ zGWx6UFPwl#{@QOakK|PB)r8J`SmAt_XuDKDwZL0wR-gVQH9G?(Qb;^5jsyN81ET7+zkH#K_eHob zN~jIz5^*`&MI%R&w#<3p1(%p6{j#ap{ep&2TvCRm#52?ouG72z4%N8|CN`-UQnB*L zhlD4O*d!a!Xz-t7(yy)Go04Du;ZgbwOgvOsubfTqP#x}I9}yK(!zw1#LD_LoZcO#;cU2HHlGrA5$t>&sE9qY()rcp<@?Nk1WV?((gBP%f z_ACUSFhHw}d39UMV?p|#Y4>MLzCz5`lAwf|j$V>~g?>oN4$alnY`^|rl3|YD!iGpi z+TZ=mkeEn`{q@}OkCfLy$F7;pcCsdoSN_OC#+YAg>AcvUn(5py*)rW^ih5HKIf~JY zY9S+vvGrNnuhG=1Uw$SH>dw?G<^s)cOLe(WN>I!M^FD)Nt(mfQbJ z8ft1+mXGkJwD|1ES^nNMB9!V|G3}AF$o>3xW?qLLsZwU^D^bcPCP>BGxzy25TA0RH z3uq6E%8Kv*vqkKGnp^Q!y6W;xHOZs@D}e$Wj9iLqZPsh^(uG%&`uMs`4xH+`M>|bP zRGLkn-wY86QGAVmz5PsvwIM|yb|_qv^@WIPTaH63rvKJKe0Kh5hWLP9>sZVp#Sx~; z=vIjuVN8`-=Tbg39`=_;6Lw1qK+VszMk@6PL-wpro;E5!=+c0s9e;uTP3NlDQe?gj zznIs)0vdgmPsIuwU0%&hh}8_0=C`)KpJ!?JOuw+)BZhVtsdBJEGFGnmi`|oJ7z)Hy z{0#&SrSbeWkI&{NZls&AQ!>V;sBru0O}PAq2g;3<1{m$IRe4O%&Ch9shlA{RZ4MYm zp69Tdpl|zEQ8v7@!{*vsUP0^$Zce~9H#x%&nX+9bCPFS|c|;WQXBjv84SIAR<3vTj ztui*Ib9U-JmgBSKre|hsa58U=X>RmPYrcV`gRVg9UY`jDQ`r2AksDS8yYSCs_i5Md zR>>gzT+3!L=E83Q(z}5YkG+Je?gjACxGwSXyI-j1yjKH>(PO#y4u!% zFD@Tl4;{Vg?V3v8P&oTtcS+#inz}e?iv!!h)^dyvd;6 z{7nfhJ;OMCS3I|+cOI4wzOWc4y{1++-9|>Hz**jCmOyLlyk+>e_~wK`z+`HOO-=TX z>58pY;-M~kPeQ#qEANrtS}n~v-O<&ZM=jmF8$7F9aN@bqQo3;z^LI4)=S8=~=tpS$ zQ;#qWDU`Kp9tvFI&zSy~up}ix#uB%8+JkFRI04#9?!(%PdjEnuQkcr3PQgMSOV&&v zPZt-FQhZ|YgnUdc)D!EPb0HiRtxROSuQ7g4$#{rK+M z!oN{bV?W8~S+zx3NLEq`f{S&%^z*K0Fqp;D)JBPWqRTX#J(w7y%-t0`quWG8~O;DYyfVN+jV}g!hZ@2JAn4W4C+5h;?vUT+YAwmi~LSq{Ct*Sw7#T z*nx4PzL0D#Str02sI_~&jcv#&$;ZOUZh7*Y@0+Q=8h(`)z8^Yvyf}*(hYuvGGHoj4 zNAS7-w391QPDk9}rz5s5w+&BbsjH@UK3f&sNt1g;I>xyO+>hlCIr;)~9T1RS4q|3t zz00t7l#1Zb=;!EaV#*4IaP1yFm7M#fVcTQbv-bt}Y^k=#YwNEJ6uF8WjwLPh^8?3g zQ+tMFvDRDIJXmm1WJlT`&#yPbh?;XAeuiQ!u5&}c#?+38S;^SKs6nAl+?BACo5Y(< zpoSDZdBSdGm*`V{n^DmtC- zBOrgVR~}Dl^qyortN8ozOc zm|<%#Flg9ygnd;^n|<5+Eo=5I@02Rtv%-bKS)vyEXv^7uW6s3g0zW$pRC0p-650x; zLqy)p2K#AqjyXfl(C$tp&+dZTyzD>bNZLPSFpv z6}t8U@@C&AMpFJhAgZm(T5i}zV$?aF5#tXB-wfS+h4=MfDQzcwl&0{LXWMHoOumCF zoneJ<;yf`gt+l?u2S+$R9^I9HnJ2LNqp|(qYy5;nm>q-Kov2a?(hVFT_^kGB$Zo^v zbYb6R#}w{$0A4c0?4MPBq&@1Ib6G|A85HetJQTCU+IPU74*;7Eem&||UXS1-7M{>K zw^#om5oUfYfhlZGtl$0zL>T^apS|z|L@@cvAn5J;6l>-MC}9M7JZQ=4qkhFMf&tI3 z1+U8hdOWWiANMofq4ZOizy(TAu!`=P`|JYuhsh6#)WxIe_u2Uk-#M&D0AIpktn;cJ zkHZFLDDT8Ya7)FtAZN~fc9K}{<37b!%$cLtgO;V92?83N0NDB| z$5NL*Ef&txt1~;ocC3s^t&j{)FdBj}CX^1Kv5j}pSx`{rJj_xTW=ABq&)yH@AR%oO z>yC!*7!W;S!AGx0fL8#uZU6)7X)6}~`e`fHV|pXZ+d|SRr+H)y43mMfvgxHzlm5;L z_0ll+1lU#!S{Yum+!nX6-WIpx-xf=~hOr9i!5)_ZOoBgT=MlOotleDYl2U=nmn~8$J)qK0qVs?yx@FK0 z0M6Evbwi~H0I-fHbV(4=b3o&ZJ+qNET!A2i?~>jr#A zcUrzB7yx#-Fvdf72GDz8cH8J`-8BANg8jgOxJBr|cv3`oAmeX;8ck543(ZQZ9SWGAdX-2Wq|3dnwvQ=i#r}RDYPY2jEXAWw zZRP&j8Tx(s>2(OJt(uM^4)2du?j-65VJk6sMLcz7z9#hhAM`%pLb6nMPJ?kD5K1g( z6@7-&E#;+OfU04gK%G5ULoq_soC5b5b_Sk+Z*o)+kW~kz57BmzBLEi2MepB>Q5xc- zGPC{qh?9!jC{Di&K&T($&AZP~;qc9;B3ob|CYra zS2x>c@J9ZAk74BO%OOMLZPgxw!(HslWcIsQ;vm~{z7KtM8BAS{cd=QJUoO4}4* z^7w-t$Zu}m*K%uGCUr)wJJ>RtsBBWfUd4)PahF9+K2s&WoA6*;MzT;z8wo}VSD!X$JwGYiuODM0$!GF>O>_jWuJokyKGRDb(3dc*|jJ3 z+*96ARN*QfLh!!I1tl*ydk(%jTX|x|jT1to=K9|~ivD@A7G{h2{y<*Y{KKlVA1VX_ z5`wIZ--gWfwfOT9EIdNwhCkfOp)YJ4;pX`brhfh4zfm;&;M{6yiwarG8V+V&OvaZt z4#=0z_?i3qGG?}WygQ6Ul(J`JYu12DS53{-`CG$B_bzOyz#EaAk3WSd)U!uRy=t85 zG+1Z4Ioskt*rYnMNsYzV+!oB){Xe970k7{0-GAFkzP~sm31p=Flby_l8AfpgE$J6mLbB(R-LWgPzhBwT{ zVxBn_YL44v@{B>b*#oJd^UDD}+kMS(UqbJs4o?5Be)_Q`X7xyA{Ns0*4!Hrafoogh zvdSW_Gr=<^v}XawBtp<~{$#`_OrbG@e9S0dzSl?YUwZ^fo`(2JTk*S_=IFRfLkZzq zp&zJlH=0U8A5_9vCkaveVV1=)&qPjq&RmemhwrS4B_$@8oNFFW?~|hDIgs~!5^%OI z1;LxrlS>ZVyvgOmiqNV1U<`dKl;VlcJ`1kM;WXZf50tM{0o~^qJ1EYm50fvD&S;bN5<>TC z0u9!0a}rny#Sy>$fzb5@eH@oV6C|SkDD&EBQWd_+euF=U;GyQr#EY}3Uf+z%O1$L7 z`$D+>E%jli>D4E&;or3Ph<#>_goh?KHokX$S6ynieI&JLVXr*6^ARIy9iEa&-+5ED`8?6wu5rOA<_d>gFDl`kyGapA%#pb_ua>hj`|Ka%tyv=hLJA~Ja+YHwV;+(nh=n_ za=L>^ydLzp+IP*G)`3E`Y53wXK~XjPZTU4$T)EZVhkRZvF0?d^}0j zJzfS0@|zig%quwyqbSl=znt~>H==0|lnGV<$lkmyNr&QJowKG3ITC9tCo{T`(aEK2 z52Trd+*nH_+<}@hP)4MWHJW!GVB0xS)_!ZzE-AfA0N zrRmf42KjU676P{JoqFU&V|M7gc$Dq7XhXSPIUIgtO)7eQv)SH#PGQsmi#X>R z!o$th^8<4H(OfvSE!CAyPdT8EON_@}8*cmFB6AnZqTo~DvJxY`IT>E_Np%IZ2Yu;?hAa7V4~at z)v0=py0*ETCbzvxvv8MR!}&sK*2-I$Y?8*9b#l9xe;bPC$p=U082!vpSP%lu@D!0Q zF}L;Z^ccDZ5woXX&f4`{PTv42FQ)^jQ7`Wf2(^A+Ogrmq=iV<5cV15WZi)_IQ=N=S zR(3iVq0GKcN*Xg)dpx*F>!Zw`49hI4W9z!IyMq~l#A^^R`req=4At2;#xmnuHgmr$ ztKEBdW|!yjN$Tq&_&*fGKwyE#c`p7kc;o$)q`v@vU6>^n*ZNAz5?H|I*cycYR0dk; z?oz^u^=16AH8s-Xn%mQ_=qzS@GwirNK=&TbX^-VK-`yV~jF-l~TamN(n93x!C*_T` z;eH0aDdy-JxoRuMCQcRMb z%njpkL42;c9xINAM2M^>kD%#3f70q>1{MQ<*;as$zjB(Ni)CD%D=AKMr$G|;owqc9FjHHrjy-R`$=JwV<*DckiLbb~q&VmF)$qU~ z%w8GEomqbq;AhSUXdoc(0bmz6C-zt2tme&kQTQ7!ul+)r;)H)9$#2a;n2 z1XC1fD0&nS4N$MTDooznzd0xHEzkgHI@M4_guOc|_#l)zWNyqeWR5DF6dN}@hA1JL zzr00nrw2~Dm(T-oI{c=8oJg3>=gra1$#xD4&Y zlG1;29%KZ;!RBcw4eMwBUcSRQ)F;jUVl@%7MeDmg8-_L?`|8qm9YT<|;mwIko!y6J zUpbwO|3vx2C&u`9_SRkKvneonoG+5U&<~Bb4!)y&wT3(OLLTQ?naEfELlK*qTL(OL z%lSZwjHrMjv*4Qj$yNO}hMZ-kNxpK~<@A_fMsCMf3FSIo2TQuElbbUboj8UCTCNbf zfsjJ#sgC^BZ9Op6GxWGXCbPAXUG+Cuy6a*Va-PsnhWj za$4BSiP1P!I8OtP?oOGmCu!uXu>wDq1+_~%eFZRt4Hr^<-SjwTJ^8JB8a8`z%&;oulTAqg1uN!5AAHg-4E>2$ zTAwJ8zYJWbX$D9p^Nz&(4HWv}H&~dn#z9)#n#d~3@7(pQQ6vUg?X25s`uKpYXOc{~ zSl{Au3njuoVwbLDXZ1(nEdR20IV|WwNOSs^Rmt-)usK{?B;NXTP7kf!HMN;*j2O@o zv$^i+cNaHrX+Q4dRuP>Ji!Rt^*bBrUq$OKzFN5%3H8=?bDjsHSe@94L|E##VWFq-I zalHmP>=wlzE1M!`NguS$(4OhXEX2#rh+;|EFWZmT{$!n*^J_`!W%@osx>#FM&!ZJs) zK8lA&7*~-!p1%>&_AnJY?`M=`o3kD5X`cB`J)LMZl-`;i3vwcawgm?50}lmt2}>9; z(Fp0kG|8#QPe)yk=qEokj$}O{hf`iv4gC7ZJd#s^>G`2Wb|2>I;LD1q9#Ve^HEDCkvssC!AQbo^VZNYok=piOk;#o4vGc|J zEWK8q-?(}=T4~i1W?3>nefbN&KdE{5Yv~AmOL^BO5uYj?f0NIKN>b@`$a^U`8sF;h zWxJ5`R|VL0Z~7kW`X>D<0iHG_F|cAo@|DOnKBt3luYzZ!a&XUR^k(rU&-~YwFhA(Sy5UCH?RN(RC`8nc$aV0@ zp$TYhKvJsYL&=1!QLQnVPOM4rsc+{iI(I2WXE;#!t-4l}JKz5<= zuNhU8oDBiF-bDnj2@uOBuch&i*PX{PPXnrL)by@xBP>$gLOn=Wi9Lb-piE`O=-- zh1KdF2H=k|4PVOX3(jtuF3KAn-|bdx;S;u3Yz3LWYAk$B#uxh#^3P6+Xf}lt*#Aj_ z@7XP_i?D^_3sZuQkz;bKc1874I`KPu9K8pSeR@C??VM{8=|?Fo&??)&ou>P? z%gYWQ5`BWvFpV!YH*fa03ZfES^vY;7>sJRYoyj%BOU7w%v2*ehF$vTZ@V3LfGFccr z4@wi*8Mefq=OSOiFpc`im{o#_*n+zR?gzTR+>e&NMq*U^r%niAT&W%JrU_8jI97hS z`F)cH9lR9`9#XDMR+^g-MNg)8SGLCV|25qZKpRo?helqwRi>wi_<-E{N1|=XFTIh$ zcE#90ni<;$PVd=p`E7Z^AR4!ZI8uKCxuoBVgIo8r5~5$$t2${EVs)As7(8f8ZZ!!< zK4nnV5%A z^2;5DBv`MHvH1N`=^MpXpkNm4GcY0v7K@K)C6S`s-YsGy3)W^V@kj3T;SR&qPUFGs zq1$qA_(SLS8OXo3izq+6u^?mXc8X4c4=*%%$Md)j3<-`XadHQ5uD_Gtr3U#ZAqPfi zcBnrl!QnBR=H#!D1I@Mi$nV#<3Zbxb!k^{N1f#dpguf}lnsQ^`CjWe)u#_Q)p=|xg zQ(GltAk?WnbhP8EDI5x$dPfF!5yF_;H&uUo!Q&Q-2Lr}*-NA>uJ%!?Cb*XX(5!TIH zUZ4Ij1Fhw%CT)f*UI09j-`x~v5vALzS#d7KDm*J?i$^V%SB{05ZE5Q52xumcx1lHs z=smZmY5QUttthBBA~rN5@mINpfb;1j0(|mZ{V{H~_i$ae!M(?YTA>hRB>dou8_bwN;=)nn5yrzAIe=wmsADNpmA&?WJ(!8rEUp<(!Iki6dP?@RX_?WK4 z|DQs;fs)E zjve?&-jSW~)$cNoHn=A>-wA&kar9|SwP_{$V{fo zLPrTfA~~?*-{0JE^Kq|uHV1@SIGGX!SLUB(KNi>qGbAsuzj%%O>l$P@Je$Y~GR5Bu z;cs_SSx3gpOpS%^aaigm|7>;J7l*n93ul;Xqn`_L_+6d@`f|0O+;S4>Lp99QRduM% z)LZlo)uSO_ckttHGO_(VE__Y8@GSqW`o=9^vbp_yhGoR3n55!%yTUZS$}fR?)=xmL z+Y)p4>xBiD{XWrKrg4wx9o-a_kIDRRP>@dyZ1$VVU z9$li|HlGC0BATI5S$-a2Qy$2gX~*iphWuSTZ>qZp_y0}aLsQ#TEe2r7>9u9{PgKzVnRO zkvJMp0$InK?|$?FioZ$Zg~he4UK_qA>5!&N_)PMiY~>@6_OJB0T=9it`x9#rDItlA^~d|P zZ94`*JHzG+f>J7MgZ_Q6)1pE?OFpF^HjS@|>_pY??dv>J^$@A&ZO#r@c8k*HFOvcl zgnZO3sjCL6gd>iVWVqfH>dE39+ZY}yy;Of6FByO5n}yn(0Hq_GzgP!Z*;Wa7eB%KN zD2|SmV^v+aWdq$GJi-xj{nZa-rxY4|1k#gZ zeb`Mc<-CQv#n6SLKeKX}D&>5|5@9W$&=d(7My^Kc8KrV{`C0$qBjKf2(A+Y{{C>JF zD+~?|;pCcpouTuUn>RL-kglu`G>7w&<1o!116=Pc=0|{&-0DC#d#gt0Ow4+y$nwK3 zSD4|lM*d)VDB1LInd51)bfQ^}SB%rASc~4r!>=v*bQ5=Dc`r}YUX@LHG9o*T(q!`_ z$wL`QCofNa+V+7kAJmYYP=QHM{{7L;vTBy~Pu+(>q)ZQnV;_IXJC}+n&_@JG=-Duk z8O_VwF|_qW1>Pzn82Wq&$a8rTq&NVIrKAbfi6kA)xVz3Nj5@%LYzwEXrC{rfI*+n9}IPah=?p0UKij_#L@u*UB+2<68D8OK6T{s{dkdS)AM z?N*--b$bVyx@%SQpbV{BH7~TjyfHdlp6!)^i?2wUBh5;epT>yt-r|n3gbj zDVW=HX1~8r8YWp@uux9DQ=m|S0q(s)cSPt9A@AkX-bp;ss<0Y3rDR?cP{Ms*>QUve z9xSURHO;7+2IMvk%71D4RPr3Tn&>iJ%nEhWQr{ha^$m7USUtj0>!r%|4<(N)0foNG zv7QzJ6PaZChK7`7LfsevFY`5Kr9&;Z`FECcfiZG!RU+VL_^ie%zEC%xR{}$<CYycfKt!%hxfN4>qA^wlsJJ3dU* zI0Lq}{SaTF!SwElx+u%4U618&{y*8gg&$4CnOG_WtG=|m79Z$D69&O+y~ z!h_X#lDtpmH~pY{aQfq{WS~AfsH*wooay+9{ydJzk8yVdc>}RSe{2jR{ z^B{h&5JEmW_WP`4qVgoA>CuDOQGwAvi8S!HB~Eb)Bg>%yc$c8KC}0fsVJ+fE@O_gk zbn=|jP9fx8bt~sNX1KkwpqBJV8U0Sq5+=5SrFzDlBI)J7VD**B$8WVs+AIC_nx>Z# z7{zUD6Nz%9m(@>Nl~>R2@~hzrT5T;%30vV|DR~IYj30cz-}tSOYhLPA#XObU6|Lrs zB36G^4@2~wp zj^NHh>SC-N|0&9c-*Yq3x@-Zk6_(Iti+O9`f7~#?gqoMnvoF$~etbUCRCtIzCD{PC8!GE7Z&PU+k~CVQk(9rMTVx zyf}f*nC>l0#L@f^J*}DmE%=Li|CD`FdccQT)Y_|DYb{2b=nCH_rRR*`*)yM}4ZYSq zfdA%J&9tucL+kwqFrEVG`R4*Wt+yCXu(%Iv-QuK1g;lqfV^wp*h|KV zN7r46XzR6$oqiUk%2SZp&lgD7QD7<>Ol9Kn*j{%41gWD9EyQIb;ziV~$-hJfE_~fz zHWj+jtax;-ikY&u#864Scszb!776ohq#dkByn2L(P1jq7hJ(#M(l^wY>P*xpKm$4P zx`>Xk^DoPZ?UuboU!ETCf06Ez394#vdrj=?Cw}wJd6~M-x8dY&$@I$DtsPYtY*wOU zvOP! zIAEJ6U*Qnn2>Y2EcGxDs6uHdkrTxW*;|XOn6b5>|175~*G)QQAA>w#oV*2sa(ZCHF})*Req#ne8$imYjeRp(74RheL!-k<@OVai2;Lvza8i8VT-1zx)^e zA@HflH@o0EO{8SPQxg++!Q=q4U`RO`ic)n3@GlYd+GQPrWy+Ls}2hr>uQN9cX-|3F03> zw7y47W1t&LjHFJGh~wg4XkZOqX;w5m)iIqZUJs=6Md*jx&PLtL>I~PwAWrgZ#qU`f|wE4f^U&e8x=}4^qfs_fuJVA9neG|VWC(ZRZ z$^0KrUmg$D_x~S4l0ExYBwHH$E|o1x$QI&SA{tADY{MiXYm0pkl{7JyELmpk#MlyJ z-;EfAX_#TmeDC}7dpv%B4d>jMd+&MO*YbRx=RJp12i4F*vpWos5XVDLDY+gmQu8ns0)6yLAv=$fL=;L32g zby_$yf**slRz%|pvxB2jS}rVMoxp(hZe26VZ`QC2Ic`Guc)vMG8SQu$d`&oL7hh}D z@?cmaXBXdYrHl5*pQAL*8Fe9l9^a3OoYEWst$_k_4@A(4>Dz=jyP@ncLPdxt%DFuW z%e?olpZe9pqXSvo5_+5SjpczoRP{1t)4gjKY9gRyHU$iNUs}TM3&x#8AH1wCRUz81 zpoPs&#;*%##~s@1rTqFx-5fy1(Kp{cO@2yKYc5oZC8%d%Pc=W6zdaSK)Z%j94ZE`K z8koBEdQ#0dKi0WH6_f8=rfTDcjbL_ZB>TDGkx#6pc(GT=+{D+`f8mfk~Sb1dn({*<|Yhi2Ch&K9ApqQyS+Tn0R@z%Qx+m;Z~u=nwg+^{#0s2cYA4%z!t1J4_-Sk1w_`#%JmKThIZ3qrW%diyH#O*^Ms|MM ziI>c-kDkENr!AyEQZj`W57jz$}ocCKTTg*CFzu_ zd@WAXF1?1Rh_T{%{Ad7Buq6bnVQ>DITQsf&^i0^T*~gQUXJ1c$r5wSy@U#g#9Cjs1 zuvo4RtbSfiJd5)C>`5A5iJ4%e49 zB=NP~iX=UH!cA4|8FNaRx|!NRn@|zcd#d!HsqUocP_26uQ7AJw%3S-(680?UF{V#N z&_iD?E@S&R3(xB}%MuU`7oN?VGovT?R>gENvB01~XcXI1hN!_O=l2jNOql?Fysrj5 z-G~>*VY!QB7^vr75Wa!otaCFKScv+#jEjC!XgdYWSrzO;bs_!?%fyV6@u%Y~=%J&& zU8tT{2yCyD=DS(>R&`tqIAL~SN{hqMq?_{Yq_&kMFb z#5l)@)cirZlv5h^H>NfnQfM{0Q}Z|Us5%8HH0>|F@JF>&WQ-Xlgk6|c@ioT6zOkQt z9=msTZ(&ZPNbH>D8=9Te-a;gpTdqZWI5vTwj~yrt%FEfUbayCOGWZ$`ZklJh1E(k1 zvtQlabA}Ty;AgFg2G1D{W|l)Rv)JCEglMGD?6`a3$9tCqCTygr@~^)?{whz!Lssq6RFY^% z1lUo}t1h^F(h6GMXm24Abe4tsld-)8`}95PHuJhYINE}|KS&S=mA%U^nsF~E0`I~PO!;G0ZfXUU88MZocAq_{a8UvqqtceUZuApENLwI3+VWgC--pZOPbsl z#m7$=gN^-w8wFNS`;qo9ujC&N0HQVrpr1tX*7hM&ycBH?Q7v-l)ErD-fjqJwc~@wH zrhvpC!^)IymiFTpvHWN4e;8GtTx{}PA_aTu+eV2xh><=|Jy$}rPhEX7bne-4Zu>pi zbqpj)^O28!G=Sjt^qB#@sL*O6T4tuz@GC}CK5xNd5o@w`eX7ULJ*u_U+|tV+ojj0! zA`_FRMYLYTmK#EQ{pitmy;P0WPNgt{+da9Z=p45*QP)ljH{NB?L?@DiZ==5v5OeA* zX!dyfn(CR3J$&aci74~U?zH9hqkc-0EoY7kz>L0oel^_XXDrZsxJ(|ezP1aUsga0c z1G@I{zkh-DdDM8~7R453EV%npKOa!DUd2BC#DZ4Gbq7R~G*pbD)MU&_+=rAmGbIr*9+?2TbF#=QXAd#VpoPYrzP*?wxQ3lwm>(bo z_v^(0x7$y3(sF_IsK_^D;;qxW&|H=^d+Gl`4YKud$AKy$iKGS>XF=E34CDg?Gv&L8 zPX*d&6r_9zsbRLch-Hwf?M8+r(JCm99kr7vM=#a}9i+ezWq3)F|NgxUAi!AZ*;T0w za`5_T<1hD3MJbLIsrdjw-<721$JUEXJFz8?*I(Kl93#3%4WgqHn04sZzvY8kw6f*b zfh82isMh=R>4N|ShgHEddL|{*q$+#Lc0W;(25SJx1m#2UYohHkgv1i zlIplekDeB^A$bK-_#emg=mmZha&k*yJ#k}zAen?ief>0t6R(@$m(sur0FYk7-UQ>> z9#k_*)XAm`Sv(hd598pJ1L!fn)2R#Eg=*cnI!wMpB0}v8f!^QrQISq}x{$9P)lwYG zy)(&mxdUK8)8&w1*x6)VcK{)FjlvQ({;-^6#fdJ(Am#m7&^wFW!!aah&nVG{w@JaX zk#Rsk$LolG%JigN2a*~BmJ;+He;@TDnHp9pM`wGkEt2c{`Lxi}3$CQ`bA6QRDL4ih zq0WFVOg$Y{P~_IP2YnjaTIN-yry|#R;Jf~s6x@&%3jlmW`M~gbyCrPJt8sF$om+4e zoBX#JpqY1w554o9>_iT3_2VN4pBu0SMZL2jF<+>B@4 zSi}mBuTdNcr#q2^tfv&AU-lWm%2~Pv!wRqp+sU9`SFL2vAG>7SuchI~xqh0+OH#|0 z33Qaj*~5)<7_APT=Gr)@+D(*!rPy;rhyJrlXg;^}xi3up6F<|{+WNx}Y!|Tv#vAy2 zUV3z)^F{9~2CC2B`@kGd`H0N$t3Q#9m~6`uUDTDwVeRKYFmp{FByYrBe)c>P>_tkk zz9x+~wT?hKKzqTP&d<`k#u5ts&b;6?@0D=XX zo2Ko{o^3{vi@FG9G&{zE6nq7O;_F&TC)b%qbs)Dx_X;jV-O2$7IBAOG=H*VLiEEde zXH;Z1h;1%o??>q|d%StrkCcZDkcmRcdU7OW>)MF~_f=)e5g(HX4M`4u3F(TrIAo3r zHY=m#GHks{iv{e{Uv1~u2<}09hq$8(ay0^@h{K2uWMSAixz5-nII8X^A_f?%0NrPs zK&hZiE5rH7bzNuTHY@KV_MyW!l4p&fwowWKJ$)&)U^#I3?QK>adsN-M#?6W;yEvc# z7f6}@@kS1PP{o9jt-Xaoit@AM2Y!=yGE|yyZ%8nc`RU!`YxHPGZch#VMDlp7m3oFu z9=IGXmppXNWDy%GZ^_qci9s^+E1$3>)bJ^L9$tAGSa$6!Mab~`!~>aKsJ^k}tQNlq z!RMvTip{rCG>7jZ^Yg0cndwfl9R@iz{ipz7<1-`G$+x^uX@{i(Z`Szs@a!Rl=aDv+ zIIO^pht7egJvnM=b`)AWBn2*Z;U9sQ8Ox zs6mc^u=VdiN`>wDTtMB=0mRoNC`UWoyug{|+v()sM^`(Ly$~fz6O-{3TO79f2l#00 z&<`MWT(@fOfTnzz53IOuJu3u+4WlymplyQxdD@FfFuYl$yo$Qx#7vK#hnbMocwGY2 zmaskD2{cFcJ-mqp3)-%xFCQ?!VnZU<{m@3cDA})LTXEW?;Nq})60!9sNUTAgdAJewsk?P zDDV*ugUN}23?E9vAG|*dTlf)z%g3(6aQVakY*z7Ex@~zLfl|Zh9C^jE2U7c#yo8$d zh3tzs)g9U0U>zl4HG2$}Eq!Vwah0s0Ts!u;vC-Nh8hNAZTh9X}K*!pP##y`0^Kcm5 z?o}62QwTSBfb6myMi+~)^7Jw)_vTKHmP&_F%~}4mNz1oU?J(GBzo=!hhSD$aBy<24 zgDZr4NJLT1S;Z2?c+VHYC2z^ujT}&fZ#+VqFarTvdoc5>;C|B{a#7Sl%~3+Yoqsrl zXT`z^B`-~c?)cw}lZvox{HqWl9@ORC{iO>WgPji`x>MP^YH?W>^yEfe46$Q7g#~CX z`bZLDROnfEtK`4>=t?AY@M$hGsdzh9w`qb^|4w=ZzY8lU-&mSp%{zQs!9SFBeUzy9 zr}J4}Woh3Q(cR($K3g@lGHYqKgT-0Ayg2ZxeA1t$5u=;fz__N3rk|G4m4W9nHne9g zb1DnY)s2h~Sq>9Bpn=I$%fhSb6KQ2J$72)~o7Ig}vGkqVXbFp$m<=+LF0brvq|B%~ zgMx1B{ahvBByZWLD~QcPo@lwc!N;&Tu$$vvSeYlQ=oRGSnmp(&>+wGDoXy@^_!(Wc z3tqO562mrc=S*?cN|%20@~C!55Tm!Kcs&K~V{JjW=e2MmT?*>VVyq|_P#g%3L zsgNoBn!C^W!0G+qSj6*|hc>1Px^wb>qI}2N$8PBqmPy^Fw~#J|WGrud=2Eo)f#;uQ z7tP{d0l1B?hN+>2b8c|?2-GxN)Q~wVSBdgQP=8R zCA!DlMoPd#!}s!{v&+OeMs*9$_%Q^5OAWF2ts_)E_J zlNn3=51C8sA~9UVQz9-FH-BsKjp(T)T`Z{&ddsQy4-tL0|x-)DE> zz9qSBG+9nHVp2O2V_$IXq#WjV+P+B)R`Q28_LcmjW=N zbY;fpGq7z#tx9?NR14;L zEf6k%1CNosx_KEetA}l=y_T ze{OG>WEmQsM3*NumNTJ@uSuRw$s65h9lGS{3RSzS_#ib}@`jLx1O zbZ7fkHkUKW!6L0?+2Q$jQ9|S$2Z!9dAr3Wd>8^Yc)?BPuLSyBQm>72bp2?fhMeRD_ zqgf5ND+1)V$NM9K`3IxVHYG`QrsWNP{Bj#ttMk=6PaW;ZAin3s{_&WnTgvZANSH?< zWi5XA@Lm5Q9{Dld>1~2ch&Vd(k-J?|b}I)j&s?^0pp;khL$8YJDRW=D7uj=gRxe?L zZo@Zt$?P?0uhxfNfb!cZPv6W_25c{zq?+PJq&#%pPsRGpjp`&z|8;$NP3Q78PU?rm zmK6T|?+?PxwVGCG;T&B4U0ZlLdK@dR!c_gKSGI+wVZ+0=c+KvbiD=xyBxgLg|C5ta zXi@Y0#_~Ft`563GRZ8EGH9*C1^ybg`pzoH#qEEsZeb>2yf9ds0b}MWOCI1~~L83fYAUu&j z&P0fnYkGfih&|1ga9u{X=ap#hiYU8SK7D@E@+k>TT*qTpTZ!=P>qxdgb!S`oinDwv zl%ih#hA?d$K5of6SroVeH(ySwXlaq@OR8N-ibk5klnS%#PlvHQhmBIy5+J(n<)jWF z$MlE2{AA$va?;Vv2C0L6I`fwxkUw+f4aD)r%r6Y7q%AxQ_O9vOaq^9kng=_*{1Mpp z{f+elP9UfW)6FlrbV~j$Nn($ACmATPQ?>8N+v?O`FS_mmZooiB9%OHGOJ(tbPs0LBr26h zeJ=>WO4RfKxnBM?A*-9sS&4f_8Y&L3>L_7e;5_SS7k@lMa1TGTHSbE&G9j?^(fbRPWpO$NF|l1hQ-6@{5x^czJq!Rk572)V3 z@S3d2q~$L9t0+=#PdC48w4&U2Y)fib6psiXutHk)GPiOO1@rgH43-`59xA-2e@%g-gWZJK;B>n2)1Tc{o6CumZzxKL+ zJkl2i@_!;NU~a8HZ`z8rUKtrU-x}rn-7<2Pq_dnfVlE;oe%h4;Q}pQJH!pfeDp7z~ zFq2JGykJ&~f(Z~a!oKg@M|nysNf8#Nu)T0eUO<3xnXd?wz1KZa_MWu7AFhq6imfji z4C>}*!1k{sagQ7%3Cg1cyfdYeAYR{aD%VH$n;j~9`5y;m|GAgy&9Y~7;Byt_&A^=P z!KA14NAlmQ36l&;=AJn>@IA8R5zJ~Y;U?_eVZt-m-cj{4*wc;bOp^QX3QY(QxJDV? zGkT!I4Zx-e=w!$>{gY3mWpClL%aY>q-TcN1vN`J>V9Q%pDa1jxAaKRU+$UK~zL%e6 zB7OHjvYWL0Ftz*&>P+!+QscE;lDo@`AyUbJK{v^rCh6*XAN~}k)R1=lDM!$yKXcnU zrm|5)x|bWtr!Jxdu4Aa1kW`4Hm6u)DRt566ueN#~_k}Kg&xEO@sAZDGjJ;y>J_BZA z0JBv!MLH$A*IgXH%RY1>xM!3M;Ro)oXhf4ra*$V0Pw>*I5O9b0B_o%UrmFeA{`{jR(E4&zp0gF^5!}U3Yo)FWr6xx-x$izu@&NZ2zwMrMPV8gC z1@z;dN?KS}7hggPMWhdzQ;mnuOI3Hzj@$S?<#BwV@!zrX2@1v)A3T!EMh@a~hK`DE zd*aFS*T?d_X2Vg+oeKk-@T;Hg#8+cJY@|9|MP7R?z%*Vben0J~$n=4x6+8Q1F&62w zsAzC*wA&^lyyV+=>blW2D_7CVfbcv zOj-!>K|s}4MrRtKemOkm_<7LD@Ju4O-`Ug{Q9qy-D|{~a19-i^YUW-0q^+mIW5^X7 z>-xcCr%zpU&Hy{;J1X!O%V|>vjm*FnkD}_Af^XDyw%D3z=klYXE=)sw_6}G2)V8mp z{1ug%4x9h0Ff@YwbUY%2qRg)#b;X@{X?^DgBU&b>ZCovi-VYH9hj< zg>>*T9Nr;$ghz<_5c&P_dM@xnl=F~`!l0QT&?V@slNNwg`#{>nmn}iduJ(hi3;~YM z-~Fa-Nhp@hIJu_w+uG4o7?paPS#4=)*^&RUe$zf;ZHQttbrc=v})dB_X%n^iKO_?ZR5|lJKa#WWn=5z|EMo zAA{J`YlJ^%iwNR$!h3uD1JtjHi}VM0p1Lq%U{VN4jluq1Ns zmO?gB+G}39_PcdxXO{kdX8HlPYiBAW3@~>*z2vKM>huv*>8FF6*T-oeMY77GSHze> z|Jj-xD*8=pVGwO}7Q*4rW9$#<^{r^NG2f}uBJZ_FlfYBm>%ww+6i1oKDv_V^F<{|mhSO9Uu zXZ!Cih_~bLxeq(A_g%!8_TTxU*v&7)*pfg(fK9=;n!Ne;$`bU+=|SwLk>}sdUs(3#Jf~V;-=eqfagPd<17xbr_)veii?&9}HY8 zi7-*y0?<{G2eAigpe=g8@7B#(YXNV9KjGm`J|xFawV*9L+VY-+n4VaKL~ChTywFND ze@N6%;|!Dw`V2njDo!CvK#XjyO4k)c^*5@ty8L*~SDGp2*R_IGcA@pani6bfe=7{;ZQPAezN&vqeJ!;65doLJW@rC;6 zF&W6Wu>& zke;a*dn+2{Mi=DQQdo+=q1kfkCbmv?L~C8|dJs|R?MU#1+g#9g#Qo^XZFc#wx^_3u zCO(~SuPOodjWBdw1f{5d>@G|A{_m!B~RXyWtp_<*+S$sl;e7n}GJ~V^2ET$p%!h;|_0hLuT#mlvY=}KICua&|mrwcPL zdJQ`zggK@wBP=izKQXzkDhB$|WwOlezCQ9w78VWMm}|3)DnQN@f}wBANt(&RAOSjH zM{i+bovN2mT8()s`25NWv-82A)}Na09rwceHeF|s7=c(+IGTn|Gqaoejo<7XOFA~v zHK(*0JXsS}x3E9^-Yw+&ZqUaMtUF~fW&Yx!g?z0Hy1}pc_r3=>=av;86HyD=8`J=t z&!T2(Xo~jIy9T-ll%v1?l8>{?JohTRdL5DTG`396WuASNZHh4dQL|(0cJX1tntojcI0 z*(c^0$9|0eNfPE4j1d*bS$lb~8@6qd!zSS+4!t$#s-lt}1;h{im(g;yUwAARD1h0tc;qb3{<9&xu8Pf&Ow|hYg6|;@(sH!^YA59pQ^o z=f7l9`FIK8I5KrO(vJO()deCRFru!J?7OhK<36ZNaJ3Cs-E7JN1}omFMW0FRUU}lC z4)D0M0FeFt+zXHn9Q4kWCA^S}KkGBKJmj1Q3lPse{O_g;xEC&{T~s=%N3nzfz5ni5O_xj4wY`AK4M@40*+;{v|08RL4Wyjnh!`Y zTb)(x{rn!Rx9qy|@z-olEZptJ?J)L0|JHz!V#GZwI~q^kCz2&ta7|Ujfw?Dr%FVt0 zmY1DyUcsK*IBnK=e}N6G(%2Y#9lNSCFnb4ltJymSn{csccFH0>4sj8OpVK%Zj3bKw zmq?}&boR8sq(tDgwIJ24A6_9N80R?P&P4qp0<1MsjHK;@Xrw+0vpN|b z3@4~9ErV(OYqSWRIc!%m-?P7x_9JIE_zVPcX8-W>)=^O&3m_U6CioVv=n3B59g^dd z3*A_uk>yg^X6pK7pOayfDjK2po-TyYkd z4o@$sDu)LqU>T((3|06aXLjHFnM%J59lyn=^NV?IDRsyOCw>Jfy*d8L8=u&LH*|2vhE&rcd{i9eJnc zSIOZG-(dQwfPErT;X`}DEDk?*X%U(OYs0RdNktYc?(T4*Cg#j1R0^)cBKHkDuhzYFS*wua^l>ffnDt^GWEpKkT3l@3L+;u3Xv@QRw z#64Vgr6I=QuflGyloD&@WgK3je=ES<&eZFB`Rih+6pKh%zsPO(UWWORXy(7M?7Gq{2hHxN%&0H=fX7!e z(h)tosi?I>C-(V(5iGdnr-4;L7Y-_TV_(^d70{pMByCp4Aj<;4TBzU*%>4evW$5D@ zxkw;$V-TC)1qQ5RIuSZ#(2J8wUsR=wF{vpnOaByEOdT-D!U{r9WZtaOBWV9wtSqmewBGK9KgZcmgpWV8dj zc{?fr>Bb5=NUFjQrMrX+1qFt=M9cVd&t5~yo;mWn5}3J0;B zFT1o|SONaCO$kHVq-n^jb^IDb8`huI2v+d!4GvuXJ)96sj0P=|fD)lwndywOR|iCK zJgEAWwV(e3Bjd0R?0ySHxsShweB>>*gm)58L>?A*OQ-`OLutFjaOPYjqgk|QH~fYcKHEpbFIpkzd{ z?w1-1aI(IMLu`ZDEi(s!v5)xyLK42xSI9DV&?LNGv~rk&I$(i976165Zu|~85g}b=8wc6$-JE|~+rrAEgV9lhyoK!|c zYgJ#qhik+bV+1E$7V)iGuJy%@*+^`X^|5n;gGJ8Ny7vQ0d21V1zO*Cr+;S7Ig+L=M;vDGi_rNoT|h5{0SGKHf>rn% z62|>A=V8I5Lk&WmuTJUQ2_q|OZa(@@~`&vsaMpTJQzfx^wFz?NNcKlR#2R*E$EmsNu zb8}Qp_}>ax2>(2EB|hme_<;Y+nOezs!7=;#A4krb8#vyOrK9+Dsh|_Sa+8zJTc3{N zLEz!cUw#L>@y_wjwEn$?qj)a3oP~w)#?LWZRp}WMx&41pLZ1*^<$YsDNZEoLm-5OC z7A#AeRle_jAG_NVz9fC#;?Ti6RA>=gaN`NnuBY=2)057Zzo`?VZP_2hFaH96@U>Dw z>zAW=kWTrKT?W!AR_=E9z3K(?!Xm7Q93Y$W(!Ef<81tE{cJ=SQ+R8~xXOb=LiS|#p z6WCpGy7$}mqUKFpHir)Tfdr8w>t@zmqv%0{dm*1@slJ7@lPO;OKQ_hb36Y~L0WU7L zcJaPP^UV{38}!4ncHiLHUHJLQ&9C4m;oo1swy^@P<)e7OT&56}{}q1HselG80Zk26 znQo`rsaveykS^BAHiq!XB(F?s3B0ii z@4}t<`huW+)lsW@G*L#CxDqCGa>LGo^hvLK?D)gq_cOXDB(c!VU4hU|?3)!IJ`tBw zOFqAD{>d+P;?4osg*ruLQPFc!l?F++Su-QTb(sZfWS{0 zE^QS~U`gKip0En9{ggbV$fDF!Et)}-^7>5f(yjZ1G2^cF5dY*=P@UtfBS+K80S`CZ zM~9VD{u;lhW#sU-dBN*E*5C=y;T9We)-&JR|BF`A5++5@FVS>3ntX?tCKt|<7fSWt z(FWxQX?HJ^0hOyXog4YIl=X`O^*1ik`;)BCM_2WtI)O=!EdM0*49?8Q?qVTLtb8{4GfgUv=oDXw_K*-ukQt2 z`(9<+M@!AZ!ShCSo!LO_F+>A})^H7jnI0Im0Uv5iZQwHc%`@`B$DfC|srf~K>j^jj z5l;+T+2XMe(r5*2b1I&qL$u4c2L>ELcEn&=t^ZL1;c;twj?C2QcnE z1^KQme;jH`)iCH6$x?c)HLeQ|>izdLwetjGQ?ctrOg1q+CsHgWNhjp`96Md4MeIuN zqnUpF?}rDKCRX7l2?TU=m`tHd#akNhC4?RHzky#L4_dqCojinmTS(@48FuAa?RyE(HP+KldNPaFbLcFZ7%T42q4c z)Pb3lbR1mx7cAvPeRt6Q_*YauQfZ`Ka~K<~{tTsFUrcIZ6H4#*LtSILcpOm40BDc` zVdgJ8K zs6s)emK@sn{VuErX+a3ra-RvaXO^M79(Kk(^;LWumZb~xN@Z*}_`GNRt8Bram`wLt zT`_vV@AcF=q(=fZ@d3lcRT~NU=(RJ68Hs7f&KQ_dg?~JQweA@`X{kLg67f?6eeOnT zhVBQ3h&Ti0!rJp-C_+WeROkZ+sO&^?I5${C?SpgiO$LTA1+@8n@s{*K>|du>#P-`@ z0iOF|#_9$$pb@}3!3?28RkCyK5r3=XA{&_TgIH$PC>7w%@0Bx?vtWSBRU~WsC@$ee zTy~iD4=>+Ag|mT7)$-c43pCe`Rp_hsSR`@N3bxIza1A^(SLs#Lr3be?TfSZ*doucl~fy3&ejT z5?$EBYHrk3;V*f}QYNr)bp4iVxMzy?)zvq;me3q}3dyD`L71kv_v@EazX z{oq|Kvk=!85&+_}Wz!FaNtjxrVFDBTNC$S5Rp8hu;1`i&;W6pxN5!*~PP(={roUiD zCA+xZ{lWN=x?~^=PLf~5!Mtye>tFI3uI+*;nC0jdT5O8yfSB61vq zd1StfEHrHrfV!JVog;EUpPl|T%XiRFLw2m>Sb{pc_%rN-aNdiF2lX{HOyw=$K~R&; z-X-2+OGH-w4{QX!{6yqH{DN(t@eBB~im;bTMCLNyi5@w)Jd6#jq8Ycm^tl60GZv35 z-t{L0pPqsTQqS)a!<^}-Bufj>pSq+0u(>K354DSlx06+U@82l*ykmOwOw zD8S}^UCTD&0&dpNU|fHT#fuBs1)DoG zQ$(2lj~)r`?Jk_Q{4Zyx*}J0-N--|OU`7rw`;Q+mDIzmJAe{t(_kR+_NZy+fe z#*v3qnmYqvclXWy0~*jn|5&7f{5@C&tHMDw*|8xH97U~MqmAgUnm0+%pflU1qF!u|7h0arpCNNnCP9r+MA#^gou0&DryE1}*|K&z79^&+V#0a!hr>{{)Yzx7+Lc;2nicOO#H z_)5K~ggCnMA1(5YSt;U~>0@=iJkjT+o8sKHP8Qv;!P_~u7rl}mdev1sEi||Ani0XEJDpA`lI8v72ra?|0e{41rZq zQtXL_HSebQ)$<1ncgF2#mLtx?NCb81rW5jyLNx4AFsQ>3h2XjPou_V(UYx((!`?-zE76qiNlI8Fp$SlqSa2H}{= z4*G68SObDJ-_fQOZN$EuT8vZ%rC)BM)Mr%!_ z-8$)1xraL)A&K*S`U!jvPuH$SfU&+sI$KoAYe~75d(yX69t=X+DXr4~(IHoIv#0to zL&0s`g%2D+FM)oFpa*oUQbR7kKZ-B=lv+Hg)SmHMf6E-ai`bKjY~%|6iQ!3SGdHR} z&fwmo`H9`&M-fj?I;T2LunGUU!_NVh=}qyoO=mnncX-5l2uQy@ik}B9RR+qp8n9^U z(w_xqTGbhoHm3?uT$hK`a*0J|wonB7Oh5p&Yy@&4iqde_|E4)Ai4w3m+w5h0h__PW zAoijQSijn-0t9-B34Rrk@&lakDc%++M1G3+S(LStRkRFs_Ex;wKyLaVy&F6&v zkczBW+`Y_+n%JhB3+&MZ{k9PFDIL%u!sWon-v$SLU#1Mu_I`I@1q;!3w5n~;4#FPB@CkUV=r^#eYowQL+SWRtE=qEKJ5Y)$R*i<`%sP6ag2w_?t}gFDa$EogTLD z9Q{aja{}#U2(lov|B!Ia)pe3~TswBMXc6I=^47Y%f%VDxJ3-NUNCDnMP${=#EYO^h zRm2cxa%Dl*W9zy`$K+lM<#JzS6|5$?dT{QHhu&GsFCz)yr@=E^*~>D(w{xhK0494jnK057v=uE zbgDcxdG*wzkbi0t+3t(WOak@0&@OA3Yj#=X&gduW2c?b@M|{`e4fW6B(OluBDb|Wk z9;&{jI_g%!I_9ajSf8dAKjZl11>Na}1nP*B{+sKKqFhq# z+Y=Ka{tAdUZO&ea~DK#w!v& z|21{@79G>ywsY(>H+QW_jmv)D!>aXiv=mA{?>H`dmOoQ7>%UPEr}jKWg#0$ydq{&N zw!nI}^yWZw>BV8aXC1PKeK!&|jFteaT=-gaQ3=!1fUL>fRLbW7-o;8*$`murM@#Ko z9HSwnd|=eV$K*_s{7%<>U6ZQNK}L^Jtx@NgWvs4yg#X8fIQ9!kGWS@ZnVaIa>= zFq1x4w*|*jv04LFEgn$7J?15FeL>VSLp_bNwUL!` zXOvNCbV2OnCKCrke}%Tpr&&;=gc6TB9s)OPRmoOpym0v_y#Jf_|3pgB5~-xZ$#18W z@@`=q0ZyJ)9?#5c>EGb*a}+wg=q9hw#R;7bz8%C?e7h&K>(rT!cXDef6BZ%W?x5y(4*ziy8_2N%M~wj|!k9B@a%g@cf&88bw$`hQ>D;!&dff0U{~$ znsaQG@SyELtj-+sFM7CZS210&?h(5J;t`XRqlWG~pATp2gr_u3PR4T{@C7eRK5(`9 zy1`odYoAlZKyz7O+3R!{C;2DYS0E?a1hhN-aN6s3K9h5wMtf&x&Pv3!)#kn~zQf93 zJ8fj%;;NPq4>O>Q94v`h6~t;@U@4?sdzZD^$$k3qY*P6XH^EFP)*5$l%SKCvpZ?Ek z_&62LjT*8JBz$Lo-@_j5?dE^m>N_X8D3xzWOYqAtc~_k`U($B0#AN-gu&yxV-B6-} zFg)kafYBu3R`-q0oNJf7Z=Jrmq;z#lQ|#}P-No~Zzj>KC+m$AEGQaNs@r-u){D)Xt z@o7^s{AbTFEeyu>7&H2`SS8Lv4^+(kPUzzI3nM$e#XjogXUx9^v&zj1U=Cw=4tqNM zu8SX;@`bc~>N_b(6z~mB@8Zw04T3$TpH}}Z2%Mu-zVGJeFRoBfK}}{Vyn%SZY@Y^y zEcQ&BeFLEio}Gy{>Anto=lm)OV#BGeCbef|$^7T%-55)l)ujiZnksli1@+|*s9;+S zwoHY%%z#R$tgX5({)jCa6y`R$rQgf{)Cp8|{rn3D#d6OnpmgdF6DX$I&RAwG9Z$!M zxyYh4|7l1eyoQq@ncqrPjSu?wj5_X^g*N|ihb<{y7Xz|$&+Hk^Dw)HUHovK${`|V5 zgqmCs5fIM2k_y4?@X17>^etzj8q$~T^CYsTby)YhPq`o1*Jz@=jdBz&dIiuja?N|a zQVL#?O0FnHJiRFbJfz8I6EAt;J-wb!enZu!l9nx1LYdbe8pF52-v z47Rt@cnP%|{idAy)P@HT@xR&4UtJm-elc=t&*=Mwy={aDaGJFy4f(y7-`r$xue;$I zC-568mk6PFY>?bt(~43cM;!Z( zFK-=XrI`A@YmW&!-&b*Gk_Zt_o0NE$WkDBqgXj6lK+mQ6>|KX zeWZTED2YG!{S9ug5SyG7RwkBRr=R@vWMC8&hU{0ZMzyhNULvl>2pwtv=EtH%I;;tH zA+L_IUOqGl@gqW*2~F+8L9-m1irHxKkz$>4_|)|Wv}b8wsGfQ!c;NZb91*e**acnS ze$yh@gE`)=8WzN&SJ&t+46Az02gFAXHBzM@f0_3i{_7-eausT~mW66_JGQ8R-NV)I zCeX0k1l4oXUV`|hg+h{}RP%xuqu+us0pE@6!`6QKY;8DqhOH(gFhXy}vypB$@+#+e zgN(^@h?Y!BC25C*V>!5;9`FQ?iJ2t0lJff%#yOHh@&i~|!MBwa8 z(SiC{c1ow1_U`7@Y~-O5(7-lYgj_oWr!K&E5}c#JE8%#YNXq>cAMc)V)feT@e+i(} z?FQ$5sBJ~YAPbInVa{tDFh6@IP)P;#K@EK?KRh|mazW=HV}QWZzO(diI{BwyuOf%O z%xLcJ+g-45Bk&$*NunJ0o|9`i z_%d`U8Z61nEAx=H2>IRdmL_Fj!Oj|ouY2rBYC=y#_Uc?SKv9C18gUgLnVT1gklnA_ zI%kGO=1KralTjl2D+s!wd{T1G<{;>X2_9FcTER{l%{#G&cN1X>4s4->hh^EwS03dj zL#VcVHTjR2>WY`yX&aYzi7;Dm*-UE;cD3Ml)FXGCg34_~GR8%l1y*neGy6R8KQ&dv zHiOq^{=?!Fh@ z?g)=^Ylef5<2X_$KU7CU_=8@86W_HrUrrUmqNOYybr!y@?tj%}Ymaz9c#|(( zgWh9R50!RUb);6fylp0J4?f~N@I3+|Sl{{998YaUz>Y@a0L!)rxTg3EJRk6FhxM)I z_YOf`9(bxQZv7F~u_W5~_5b!E@td3R^6i6J*<{S7$tR)*PkLrDM%TdBcCxcY#bK~N ziT3UFpX*!Vy*EFI|H(EWAwekPamvt_NB?NsK7- zWlY}(XW+IEEpwDZo1*_MFC1HKF&$%rhqB1m-=VvGT5B@Dr{Gjx3@`Rp zcJ1T}%0d9kX!``PgZ{(~v-{rmdik|BV{g{(IVbjjw(}Z=r~oGHcW1u3U03Q}jm}Ej}KoXn2%4S`)Bu z?S5F%aBc#hD&B!w!qa8qE0_PtokE*}D>TZ%rf^$2RsNKJX6E)7qrqQkPCo9XD6{2W zgBsbM?EMB8oi8H%vCm=41G_4OS~SL%EJwU%a8WCQI$2O)DgF!rqgU0ksimPiX1dHu zzVWN5RWUe*#z=$218+b(EoechU~S&q@CA4 z<*kgI@F=zV?caj4?mzxdZ%;4T5_j^krQ}4}ljzg`ZP9-8{kGV|YqGHit zxGmhYCfJb`@~RG_soj0j_OdSc`9y3ago*az`X@59dsM|AGmWnxhWUDdGFxxGLYd{} zv6|BTpivPAmtnZX-33k+?44QWJk1+kgI5tS%_M_O?Z5M;25|9?fsq3L@H20*byWa@03^#mDe_d{KEQ%_3Wt1OSY- zQuY1EpXN!YvdJ!+xe<{SMTakCS?-(@KrFIbAW>@=0g39?MO@q2+qdLZ%qn|Oy9yE3 za3aQ~kB{|#R}fo>e|=Vk-33q&lh=vYg?QXxCs-s*FM$!Sek)pPI z^{izRP)vS2@K^(bDDBLv&!;TMN@^QnCvb|K+hyKhCms*6WklJ-U8CO_rDWBmWiJZ# z-F!a)$d>NPzmEkEZ}jXCjKI0&HP?l+PR%m^!KpztS^ps{VowwHZ4-QE-BV@Cu_nL+ zj)-7rsVb{&_}_mxWY7QMkh9_D7zEDRb)gGLg+P*f5=+Iw&-i-^OXa0P={>YOVU9-M zcn0cYC(O;U!sdq9+D~^Vxqa*rKnzRZFgqvoy(a@kf>myHU&S)`{zkz8#P3yb=Zt9U ztyus3FXZYXlCqt&P5u-m`TN9byK8?da~cZ*;=oM5ongW(1^)UOWLSJ{=@azHj;m`a z`XuEql|`%)-Ad}bmna6`H&^@uw|S(0W5<+$6h>LJKR?dFvP3+FVj&~6INqAlp27G7 zCR7S0WYLA>ov72bdZC?S-qQNvK*F; zEVt~Cz@S*k9|!`w7^t?T$r#E)#+*oWxx|b&>>Q?m%cGJYUBrYQ> zyHA8uu!$6Y0Ya*7py>yU(gTd(V!`LZI-WZ<7tg*7lG=y*|0718r{~^*NMe#tL`Z?? zi-=j*ga;>Efoa_e>s7sAZW%1!Ps78SA0)%NQ*b=8ZVn zpz`ygefqCvW$*V*44l=zh~ zHYZxhsSC*nC^y)Q0pNyfFuT_?_%RTY1BG2N2JW-Ek1u7gqVJF>e*QfN0BMuPrXal+ zU&O%bCqyBKi?5PlclaH-n!-I0Q~?hF)Pp zm*7Qf1@S`;`FCgz$N$W5`hdXTY25bdVZAX3iUDx=%Q6181O_1;c=eE>l*H*3=~=Iq z3~YBmGm+<*CS=0yHQ@!eU!O2M6%iEY$7&GuvN`x5X@C6T%nOVq5N8xXP~W zTvL<6=!z;*@w`TSy(ui@X0>)qZz?poERY=$j`$Y9yqan^0PikQdq(0)4zKyeP4T#E zyTi$@?xww%`P}se)LthdNRMX=+A@j;$0wv?NxuH4Am+o)$B$qglwTDX)!I!WELBd= z(HtbYi@ChpQ23{rbe@!3*T#;lSa0<^-zYxp4G^`j<4MP2i0vp0IMB;Hp>rUsX?;7N z=M33(A2osSyh$o>ih%8w(vU1^#wPTaN$t1FTLJf=$Eur9o<8e3jXUH20x`Hbz!%Hz z)RZ_{7X%A;qL;q`VSAF)E$C{})Vezl z)(d$GvgjJz{siiZk^~>#g`9Zp1jm#O7O8xr@N74|{2W%+9%x_S5?1dT?=@^J?E81S zqUFn>G<$n+y{0UfpIM6n1roV>mo}(u0e)hnEPnnB0c{?pKnXHh)>HW3rSf>`&d^_> zb>qJxpabLDlR+krlr6GLUrko?*%n`nj4K+>@X_dbj~LI`-IYTuZWVOfNxwCjuxcg4 zq+0#U|0A)0JC>S+8U9V3c}jw zb>vx1^&<QBPyg9oqldaj7dtXcMg9H9gd;j_`vXC2? zLpdc6Rf>sK*z^V5O_;fnkd+o-sd)br3H>L%7f!N%E`4^(#lF))_~s?}7>Ld|pHL#8 zkJ4qagg|sgG$QcMAR-F(;~$7B{|7e+Tb|*z{5z(8TTZXpTk4A5BzD^oVhddR6$Mxv zMI(dVjbB1eg|WeJ(7G)WIQU|~qI~{h;FdO42vg_a&F5R3T$q^ae*qdacM{kJIkiFp z#Aht1z-98WRAOL(_KXwMx=#+Qw&?I#J|upwI7J5RPWZcR2wAt^Vn@*B8-ap^ooi7+mabk0ahHp*r98FnL}HOaEr-E^1s>vmwei8LO7{5;Xj zhH=7vpHR;{L#J=cmHwx=KUdXlH}(2jh)t{NH`et9hG91bB8n;o4vcK=vzu-dRE5Lb zH#Z?n?Ws9%Bc>m#j(<60AkL|zmf>pZ1z3^L0fJ1R1&>l>uskG!WgjU4wLLi0S&Y3p~?qhwGM3mQ`KV-=3m+_EHl?miON0={6Ez!XU3<3nX8f4{D!aD(2qVHVYCdKXkEFzVtxtW}?Jzqq z1few&d3N!1`F(dKOVE+z`-8>dJkY%j>G&*EiLoqK++usSm6O~@3P+$^W~C|Y7g7w+ z(i1xSKH2@jViB0%8hp<25eO<)1fO@LpIwhtIW{308BLEw2T)AvTvc6%#)vII2VcwR zV!e8A{h`YsG;}GTnX2<}YVYyF7WCV=E5Z1B8Se$@*9l!h&zYB5Ef}8nFB}QR7s|q# zhM3gZ3p|D}Q`ci^EKlxcFlr_9sTj3*U#!3-3zMJDgz+&(&0wKiTbAV1w{HRJRZ0D< z_Nh?sUCC)x+CdK`wIepUkfBskRb&XGmg>a4TEoR#icxcaW^T)&z{_E~0kyB3J{U-6p(AEWu_t<~_X{30Bp1mE-`LFvu zZp6Qc`^e*;$6m0M4zB?>zAG>X{Th*2E<0{aDfr6o3($h_v@KRw{kS%4zf#h|iyy7n z??0ZlNPutdN?@f|z`e&R{yw=V;x^^p)lws>8@rDMnvuhKeIVCd-KC!%Whp)Ch#T-z z5vC%22M3mS-TwQwh5}!g3V8dymW}53IXnCDw6LcD9=X8vtH;LDl4WD;0NpYX^w{h- zSgVKTV$@13@Xo~_{Mc-^s@OVKY}6-V1RXdaguZQK{~f=&eLQ?*H?VbFnDH8UN&fMr zT*LOU@o@*!hY!*ZxkD4(@ArsEw1#thY0SEBF;n;O5qIaH7Ea7nOY`<|>=EzKGS|YJ z0=O@{0tEGKuduzd#2g6>FL$|M%8{32m#92=V14nv-13_|IKoAB*JV*J58fi7-)!MK zi@bcKllF0%i65vKzJa{NS1Lyby7_9$yDb{I-^n6L{roQ$%dPIA-c|NZyjeh{5g=<6+D~>cw`qDJ;$AG za^ES#X)p^ez+NR22%OoIW*xlt)?|wgN-5c19Zu?W`bf$xl-kaf+wQn%{2HOM+E1iY^>&nktc7XpvWd>a)5DBF7Gv)ikbCMvMB!sk^$ z@@FIiEdRSmbL9{awZ1Pe(V7fU4f;iaN8)ObpN5K^##qN@v_qhF2+X#M2e9QiLf}G+ zsm>V#V*sS0e(@h<>!b94knP0}Ht6-a^$czip-);&vx#zn`aQZa!@mmZ+--ER+dZ{BuMD9 zC{4JVJylfN>5wi^B&fncnE%GN9S|0vti+6b7ZzA_}=X++80)$)Io?NICg zC|k77#GjegEfDx85(n)0BKpd-O?23}g{lp`cXA3)wg-@ePvy{6_r{+To^9u!W5Zxi zkP_&?q!tFj8Ld7`z(csP`6Hq_a5k#RQtE(hb$`L13QQzVq|3e)u9l%pXI|Zbh*`5RS1ilK}X33xEoN(dvD3B#ADuhldbWNb3JZI+@ zk|(Cy?EZJjWGDze<=JfsP0cn-(Fh%UOCY3dtb1riZD``*z(JfKP-Vyoa9~_yb{MEx zW&C%XGtERqP|F8+WIoL0zlufOmej5^x3B}-gwSnnvlycgGVBr zpt>#)|GQI4gxnr?&>aP1N`ME!5n8QG1ym;p5Vr{RRkT`Y_oVtfGVzsPK1$gFAt$uz z-`tY~!tTUHm5DPc1D#QN8L%5C4$QJ>izU`9oIa^7nK~7WNUHcX=VUJ5_jeZ4{jBpp z=`!t8Sl5<)n?YEajvke6?U!&-0J-IQ2Kp)WdCQ&@fM`<7q77_8!$j_(BnSiK+b6nU zogzO|ymJzd{4(i~GSaC}Wx?~31^@o6igJLooAL5JV;-G)*~k*=Hay$k+KsaTW}+HK zTjjnrLb?pk99Ma&w2FZhVG}UrAf_Nq-GTtqiCZnBjopJEUU!#aY`PW>cjCz>=gty66Z4~;ia-fBfcbv&zh?Yrx;KK=NjjDr2&*@O z0CcXV<+A%_T^(*k9ZFR%|2 z`t}FU;Io3QvVXCbpGX&u*thm+mGZE|{xIm>j7bhQTuHy|_XfZ>uid(ek@aY?%tA=W zIT&OCz(`4l#IOH zE^d1xSK)3TDIY5%f0ffeco}+I$105t{B+`dvI(zor6@J6{&RpPoq@P^JgH8#XxB zp$B@_pYOO8xGV!)q@7Nmf~dpe_+)ys#|wuoMmcFCu7Xe&^dDfzAD^ zRIP)bTecP2{ZffYx6t;a`A-l)LJRv9xcK3M7Ms-aus>M`uy+;EyUVTP5p2Rnl=u{W zV(W)FP(D)Nzg!B;xM!YD1^5^(Kl;u=<4?Ds+%nYNGmtmE#;F(O@5YWT`;iy+cfNGP z6EdS0#X1F?q#38@4@IvaD;ri!5n)nBH5rb0%`x5pPi|CeB=Qp*Z@?C*Mb z&%ElX^RbKN{RWwAUU$A+xn#m_FqAWpd0KJ^QvbhKl=gxdD zzoE2Jus_)bu`YdY3Y@;*3J3~2^Zmu@+o7Z8ehvI*i)!nN&fwA=2lDK|PbV4h4aaXl zZSC-?v*SsgW3|i!#fpK^vNrhN+Z3Ecvc7>8y{x=bGgqEfcA8ZdDTLZC*Jl5JwkQd> z4)r$F!2h9+IsocFN+B)(cLom~X=nj{1-yufL;=9B(ISC0(@VoH{s1~Zr5%LwKHI!J z4&LzJqe0OW{cdEidSkO}SoU4~VJX?z_t2LCl*nGMg>#Y6k>9rJ#+vQGl5|CFHvKP(iP45#tCu{PDlv2Xb9RMj)cPbbD@QBw8J z@RDAg(j8A^A)28bkF$|b7QYw&6jG{?Jz;fqRTksX5>)f`VQ{oxf&ey!)v zVn+rk6$`CJilLyak}rKXf)iUSM-!fSMfd4D$#?P`WmZq-Ih*@sR=@}(=u)St2zRXs zNN%+BnBuayUv<9U2nVB+AKFsb@CX-kYb|lT_{sX8){gH1xmPJ0f&!xoS3Q5OIp#{R zL><3g@}2BRZ~gC)X{Y>?jLu(F7~d!=FU+c3ukq3IQuCYPFb_9s&Hgzz;gyH=2z-1Y z{Tkw>;l++MrU+sNe}{9tkDK{>(4LlEvpS@U;XjYgmW@`bfJZJFCoht=XO%rQGwU@v zTSS;`+iAQovD!Gts&VVD#aN``u3slae=%<*e34G!-_JF}eM7NBO_HR9Xn%s&Ry0k& zGxc-ORcW)x_{poGz?qVJ;V@$xTV{P4)uvyKe6N204X?Tvs&6a$^yQj-wIeQ$0`&pL z0?)2zU+>e}5HCgTziuaf!Isym4nRi5^e($!yu($KI6Fgh@pKP9a8@g@$v+w!eAqVe z?p&v_9r~n|LX-mi&HK;1F69`uJhBLEmEBU>XU{>>AjsJlx-Z~tsE3Q1hmM-ntGZ?% zakn>L5fN1h`7(OuF^}=p;jGC|YJ+Y?hV;Nwc{w?!3Eo9mdZtgu?{2N*I!o!oTgLB& zXO32m@hns{G+k|7ve@LK!)#66)3w?SMWt3Dw7Ns&36H*?hFuOsho+-A=3$?0VZ_s zdZqqCsy_W#vRwqf$R?}0GP{Jh4J|)H{&9gmM-Z6Z{RFX8 zUNmH&-vP3KJTg)XOxXy*P34TI?8m~y;JX{s9`uT`lBRFK&o#*CbXq&MDMN;Z@1tsT zFKkMP#r){G+IsU56@5^J{pj0CQ+HXqW|~&hX(|LkHPJ#;Y7Yh&jy zoJBUmQMDu*hFaId|)NT-Z4OEe>Npz z1F6;*VR_XwY>n6@4U}!PNTO;hv6Yq39!)>|ZY?{D7SkQOnwKB*sZgO}G6?&&Gn5{H zAlPP{_#ivWJ6_jH7CC_rBh>y2I-ykL&KX@$U4TS=dz2hRA4opi-YNJ5nLEY<1M(rHeP| z>{@7l#OS_rdUF*~L3pWDIJB>m@F`}%!tjnMx_);ZvXJ9J7xayniDLa8H zGo0#oSCW{_B>Mm*V%D^#3W*~`QD#it`M@#&|UP>w|Zf7%beuaj|nx^8AnH2YSi=Xw%1 zOySv2T{1vaGN7yZ6*>5qoQgwbq+s)bB%dLF2=IOq-G!@Wgy6n?LvA&4`3&SaDoW?D zxv)|vt@w%7ii}DyV4DiX^e1+Oprdy79%8^l^>^4QoOC$%iQD}!wxPH_;~J>H@~^e4 zc#G4Hke9gFgf(n_;%%I40Xx+T{hy)H;sJ8E_%!dFaX;-Q$=;90O|a)weDVc7DC*@t z^Lm}~-YO`#%AdECp*pXOsGOgwYD_!OS#mz)RaJ9a3H>ZT7X>7kk+9S4D+MhZ`Dq(# zr24xLAc+L7HxZAFR-T5wh!IJ@pKTdP$Rc(~^#tae@p*MyGv&G-FWf=K71p)WjL;Nr zruQhRxX}k)t&6`U$-Gh)5)kSZEf}Sx`E($_j~j>`Xf)JKV3m~x`MD}s>SR3EeR}D3 zKx!KRsXW=(31fYzkzK%?-l)KALB8oZ*`UW21&d!2T*TKA@(ms<_vq+S{44#D{#-+t zL<*jli~pt^H)w6%Xoqqc_8#ZzJ&sco(Swdf`dUa00sYsprjM_qi7!Y_-hpwD1hNs$ z3V6fsur7j9VG4d7+HC`5BU;I>0eF|(u5_7)r&mFY1yEIvMu(9V$CsQe=DhT&g{Gs{ zXKDu))zqY%2}L2#y}L5=7=0ls1y7MOA~Xz46!AR@UjB@`eg|*!hTI_*U6u;rLh2Jm ztWBV}AwTutXai3jy-Rn85kkD5$w3BrCCqE@=Xmx?0Gb(-F;&aw7&n&|&{vrA#&5E> zOPc}2_abwcH{(S_6HYTt(5P~eV1gr`)E{D2J(WYZ&uTJrq-9I0#Y|N=_?+irvbu;Y^uUfIa{oO^QLVU6Vp;kTBEiY)b;Z$#JBxdGn z$~KM74pR=4=g@&P<2;xc`Mg5tU6jzjesXIw?PnLJQNzZ4?bpNe?d&BB6M zwSCU`O}tl2+W`a%S6s%Gz#r1P;oo#4C-|4}fB-*tbsPr8TYY!TANF~zO4816GKTSU zuu$mW{rjv!Uz=@Fr|F-+T%<_n*e7LAi``2OoDA4{4G$_htF$>%42{Z-yIoYjh5Ds* zQcq93GQy!rX|%4BhgJW2x@j&V#CWnXey5QWOvmd>dSo zR150!K6P1X_7ebI`QM-H(c+vdbBV~7)HCos6t8BpJtfbkZ&B0fpHAx;98G%=7;>jd zQG&z;kv6F%v?jY8FpHkySKA=0vT7QO5o|o6Gl^7xa9foqTH|?-24eM(gEW&n@Sz&X zBjOUBkkMC?CvjGktZM1TX_j?K!~u3!SrV%pY>ZhNgsyz#VFD6G0u-cvI)uKjJBfAP zMn1&A^*Zh8-^(&ut4MwDG{x<#6n6e-=f5D2yZnd6&u@mRmx!-iVY<(BrzJ|fgLWaz z>DE?}W|124aIaBOP~trN&ue@+{xxXM543$Z_XaWn2ENn9~HsVdy?*FsqFTR;gMkmk8T@N-#Pea#W;SktF+or^lsL z`^lQbx$!opB?&7dP{@0yH&6OhNd4rOgTn_U#rpLK>ZV`n%~ zu$eAj%n42ee}Ta_F*ts8{(Yqr84?HGbhht@-9_~a5c$LM$MlmrYWdP_?2~XAU5S`& zIP$5r^ie3`@EEn7h??(8M5&uyU(NMrBR#IaqcPFf=c)3BiHyXP&`XLh-|o3xbW}$Y zD4Ih_$zH5ody2Q4{TL~kAv~9gvkV+_yt%|4UwejtDr06k*#q%>f`6G4XR}UiZl)_( zCg+2P@|4bEdH>V6X&&5KjWeczTPH(e9^@#*4jkOBU8uC@Dk1&>kxGaAbVpL=9( z(n`xu{D1_HL4NbWXTR~MgBgjh>6`Mw3k{UtTZ%w)# zQwLLa&1&bGk+D4=o^Q~47N;X)b&JzAf`v@Ag59-#7odAW^`H*HV|=QJ+cgZ?q(ybp zSPZ(S#+BOw{;}tF4b_k~!jl7B1eNeOJz+Wp3~@7+6(#oldKSw zm&nd)D(^u~{YuN06ckR+{@G#I=O}a*3jlXU6s+*K@g9u6j~FlM)S2er^?eX68muxU zw5T8BAqG7s23-X3w9;SXwu|~6jqJ<{?TCNv*njSGZtA1Nchtg>6^QGfD~^+DW~t? zzMTP-v0j?uVv3#8Ko(L@(-~RCm3*hR=o=YL6Mn_g z)+HMsVaanN(b6v;Ud!XQS&LH5#HgUd!~I``ah046SY>+lLD z&f^4}WiO(wLNfXOivM_WG_dV&73E|=s%4IWf$*IJ$xb_x_K&X{H(|+3+#uX?9j@$s z?U5|4@5b-xUmw2;CPA^4MNQ|1ah`lX)3Jhy?{cU+9sI5oFVF2reU<)zqeAG7vJe8% zJ4s6=qLPT>#1*r*%_x;yk!{;^ZWSx%RTj&ne9sh@~{t*6;7~(n| zmcr{+FlTWm-u8}Nua2I$e#Kmc$lspnFPc5P;0H<#1+;`DOY7&~DmdFr7J2@O6ZrmW zC(#L1WsviVS7m4TgT9P;S<|xcDS5F*K)l8mZ#grGLy)ER-ivr#!jCqvAVr~HyH6qr z2!&_9JAta_SI}D4plWHMzLSWz@v1JMgv2r8W$KgTcTqkh2W(75qRa3=LjteEMb%93 z{O!isexv|AW@i>#fCKEe<_rq351gFyZne{ zGWU$@ZzAg~OUk?PLN~EYr!==|g{5*5V7lOOQc#J58+MRYpPryHG<`f&R*6#|2}gp2DKVi}B>ph-49 z*m*I%X)N#T#P&O>-?A##V7Kb;*j0kb6whi>!w=9VY<}T`=js8yukQ6Jd<&@dJOXne^(1>tz#R4Qz!3A@G4x8=EU>IgM3iS-Dw` zindwYQqaSirKw!Pwb!Ag*f!5E*DT&gn-~F~)=D7Rz&E#}zS4{1XM0dH2H_D5>>NHw zxiY!j^q?$Qfo!WBE6bo0^bRD2YI)32ChMJ7fDw4lSb}waQl^W&5!hD>J!tx?WhHu> zebPqOd!6)XUHziAK*k#fp%6QaKZIg^QgnNrR5iSdVihl%qQM2KoN%6BC-psDaFG8= zmVGIaYRTd;lp&3flW*x-!FmQoNy(*w{ezibMC>Xo^t`5f5%JbT&_*%f4`e6UOR!Ro zuZiv`?hU5jy)sP_leOk`XLcET~JzfBK1dYu&M7QzD~D-X_XW*380YT5_& zcLkXQ6<-P&UKUyj(Dt=VJ`sN>ET9dD@ zlN|5U!pP>Z_D!-jM;}V^_mv*0?(8eo|NO-0D#3Gjodk7#GaU)4(Ihpil8L_Vj%4_* zYkomRYUc9i{b1d^`Aj6ESm!U<4XYoj7w?ryl!u{nyfcvpkWTsp#+$U8>!h2)>gh;E z&}UUPIrH*(Wy@>BsgK8X!9bts220$cSS=}o9JKU<95M`D6w8S_n-I8XpE9B#JiwoF z3VIX7lUinB#e6-HL7zU62dZqv@Mn=QG=`tTq%&`9&|EeqkV@6_cdFEqDk+sib! zW(Xa+9u~OrmP)d-W?v3z%*CUaOM4wBE*QlWqqdeQ)h7e`F2PNYmR5RNAKS{c~cwZY6x5@HIgL2zPJgbrSZsEQc6#kaGu~T}-XE zF)kTL82v}h(@u-b>Q=YrP<0N&54lxOGdQR|n8ojhnAxBAu>ZoUTo<=^FJD`oFsW41 za*prC9$lt;sd2SifaB&%@7+FYcMWnsSuej{tMwlU=w$j_DZplM@l)V>E$pNSrfPq^ zHWzFL22Ju$LocOKJu`wyODKbjQh_S+9kK@&-}YjWH~c~FstuF0PcS^!Yp?k-iUq2A z7-zIKDg=CFigscLAM1n7#loeAyNvb9{PzSWY27Djx8-(!2*{#+BdP?p^h9gqp4bGr zgAMm$waLt`*B1H~P%8Uf8SPhR9OJZu0%PYr!%%%x53ZgC_#pF#KybjIuZeEsTg#aW zK6YiMQ`edWC>P(Q%(#cZ5#0fH%*mF!%~5C|A3v=iuzXXY!&FF)PWy=_T)c}}$JY5q zMnRnQoLvNu&5jPlbR6)dZN&ffcyBgfl}G<&u*$nZy_Z#ru!j9vDVDzJMn72W*D5;e zas$S_+X$gLy|AH4mTXOM=Z?7TYlfjPc$8h*u*s^-wQThVIrBRbH|_dYZ07h6So zO9{~G@7l%p9D9L9-^nXn9(&P*ej4qbRyyqH^i)(P?V5Kuh<=okDRCuAOB9qSOqD6n z@PWIPi55wWm$lXSqSH2lEh31819CsQ#-v=BN>6YxTS!UoT}^+?gow)B5-}_(#Wf~a z{rP5a$oE7}uMcC`FK<~0Y=kdBPdPbU(2|<<;^8)fF?1e^icv!rps`Q2xW(LaWbmqc zx9uv~Y9|9}J#3py$Gkj+`Udzm10^a0rlPiyIpaCGS1qhgryAuob@zNrtdyR9j=cL* z<4y8%w?Np>g$;^WH(dNUZE3*g+7cVUb=RB$eI}4jw0I|sGq$SOQ1FM?dj6vzq2TGD zo=1_{5?9I3aF?c3b%!D6F`8x>lIW>$7Xj#yPS&Ugd5W*D-LG3`Z6cu_trx+0+!9q# z4Es}s8@*6w*|eaDS$HAn-Mp%NLqm-gXHzAypC?=?vW9Fm?Z&l*!a(Plp6G{% z1_e`4-miRnbSvo}pjK zj*X|p`)rwq+T0BlmWI=vxtdGyZ0$GRlX&l609&Wbd+Bzdi;>GZmi2G8&DB-p39FDX z-)gyx_-lHMb;#MYDcVqBH{Wh4EZK0qfXixU9|z|@t!8N&@&z*(ZMUcu7LWKIb9SUq z*l)Ga=A}t3V+2F^=vK3iEd!FomMx@ArneY0p(9aPTI8Y{vvSqxH)eez>>E3OYjt*X zxanITo?i#jWTB=KYw!CYzC0seiCx=ox;Y2Or+@C^<`7?^k3v+Z9?bAxgGGb4(MKjl z*q_KnE(<51TFd(~q5WIGl2Xe&EwrNpcypKRgY&EMjp$ zE>iw_4O(vd^5?bkISI-y>G11&gzvGReD<-9*g!}}j>cpV^mocfw=Fx<{W$vV1^z1J zA`2v=2;qxxcnbKT%PKuxU=v#{u)`Wv+~eE+p!SwAotoXOpdnwxam`lCJe1=cs?H#4 zejf6p%gVbe>$u&k%WcH?N^TTPtDDSrt43*0QQLASRjRWC6RzAy)H{nly58TnRfNW8 z7w^CdDY`j)F?EjFKj1N(<=6YvIC*(GPz&pCd$0yVn-~MA=k7Zb*tF$or5*#80NJeNM7c_#W5&UqV+ znt^3HTszJjW_V~WY*xXUf2R`L9VGd!U~!ByW)+ z|I9Cry(nSCp4gGaJ}viPR5Z8LSakC?SaFK`#@8Vu#b-a!^snoejmB6P#0b??W{ctX zcF1$IMH(Yl^W{Q8@wMvfGx2LONz3eT|EQ;`tjg>P-*0@Enmc|$w+Bn!99~lWNyu;z zKD^5xIW2cM@el2BmJ7U#aCm*tgy-ByUAOun^J7DPQiOTXS0NH40}g`m4~Fy-=|w*s zT;S5RgiuQ5>smxmzMlG;Ib|ab-_{bHW1r+WT3O@`-g0Px{1afU^Y2O2a85W^ualGy zmwo+xc$0tfczWT!1M;ecX{nA$&()5j{AFL>gMh3*DA zV_t}vkA+{W|5~$;TB>NDb6pUfch|V;-F0odZD7^yYcA`}%eTE!#aKPOM|5_dAxj?5 zpZwzVXmGLhriOO1VfXlLe6EM@wYd3o?d8LO{gsAS*Hi8$WZDyq@))az+55M;9^Bwd zLsDB*@2J{SG|qh5ZW;?)x){~v!*%zCW(;3xK!?vW6Mc?(o)4ba{Q1|vaw-iu_De+~ zO}|$i8;!rBw(oXAWar^i@7dG{)PRFjHEzA@-XG2$rV_$wqyiihQ%;zky@$WLN_ah1 z<}_ZvfAJ{{bAr*k@LB8&jy&^Qz@IFfx4UnAjDEgZE#!!xD=Iw3_N$coz%_VLa}N6D z@D~2AS>C+#x!7gH&C3oe`P}%AuGF&`kd5NQOhTtYyn585`d1|LK~aYvySJ!kNt0$( zd1gszXA`Jr?Viupw-s4cC^~UHt%s(*qkwcvr@2ATb~boxhB9LPpNE8tu@ws5LxYHC z3lSy)qM{7c)*C8y!^b;un8Q^T)KK zMO-h(N~HUcLm=TZ$Gg3Ek;Xn^&Ydx^<(X(O!s_sqN|3>~G+Hcn-lI-ri6IMb!NnGs9uJ`mDG-7RmgKb3e1S_S>ktd8l((x^GvXbVny2wp59rW)j}WZnBxw?8HRK+OF@ z+o73**XJbBdG1SuduF3mm+^zCal@xHUtOiSjP~8id=@1$JfA2oxyo&FJVM;&7*A zc^EBC`|N1!^(PC%PY2yD8I84r8jx_kGwr64$0^UHr>F@;E&@Wf;8nF@CGga+bts<5 zs^8k+zuU}?;3PWX^T>8Z$wsQodGYlCNB35oYR6B#W&%IX_Ms=OVNW78|Ii5iYACLB zmlRlawNGe?hgJuN%Uf(27N zQd{LvvB_!k{)7$a@NOR&f%kRLEMNhQjhp2F%g&qbxh@HT)hM+|v;h3sLH_o$*14Zw zr59ZZ1GoJN$v{%7wY`yjGb>a}r$bD95rF~(hTc-T<$^WV}@b*{H_H%xF8aPeR zpCEFGe2jxLrw1RW&e2uyDUiRgOr$gsW|Qvr0=jj1n|zvf`H5|Q<_XN#DUQsJyw{tD zj_Yaf9H;kOzy4m$=1j3-SDiVg&x!HQpD_Pe0-qZCjm_4#eVDgup&~ys30fBf-N3&@ zK2E4hbz*5tL_WT28euMUy2?O4wzT+QKVn;=;VFAOA@Tw1^TzWo@J}^Di_QT(jeOib zAKc(dDnjg-Y*pdG~`O(Hl9>+O%)WUTb7eri&(G4c; z5v@1o@}gxC6(}^q!2GPo4dGQzS5L9TuPA6!pldE>CBq%DUFFRk0ZpS@eh;4)cv)u+ zpI?6l+G3dwZdg9I+qqe^3yijhA~}T*nR<9`7OR@Z{~g!6gk-6~Rm}p=J3M&_ocZsL z4}2N?KMDOX_$TWPH`8?=2ySS4V*l@M{BCLjXpJ1h>i7jVALDX~$;b&tcUfU>c&fDJ z>T_~};m1l7$8V26UjHff(mwu(YIs@X37;2>yS>1;d_man*cS7rMoTf#?^dq;e9~Kc zld6J3k2*J|O(74Wi1{3;X})foq>U*xplp&tdtx?=F}gabzov{o(9O%rC5V{Iok?*r*f}_k9Qt?V8bg9Oa>|2B0xtNguw+}mb%Wq_|+nZ@yKODTRCAsoqQz!f_ zC{?2iB~0uy(af*Ij#yHyDkrXCU}R&QOdL;FTAe+mA0-!Rgf9?9?h80AmBFO0i4Q9fyB zzW=sG%_*T05_E-T8ea$fl#S&?vg{RKs0^_)`q!qUf$KwN7W!W_Bc3%{-khlDH zx#Z0F9U8MAVQ|OHhdo_SK=iG2?_I_XaeP1e6>c=P z^i8RVbZ7ZA;?0lG4O7h87MMwyzU1Z?5f!;odFCr<7xw7yVlsjo> zJuWyq{_czOAr8hq(3tHAdN}F!f%V8c>_%i#EM1mQ_GvpEx$oPkM5}Is*{I9qX}ouV zi3(pYch#XjVow)esUyRxnlc&rtc@E!lbeOr4#3V|5^BHh?3Vu)8?>URTX`Y}sxdci zaSHZ3Ps8xe~C&liE4^%($o)DnwwYF4FkvflQ*AmICS$IzhQ^nV^jEpx=4XN5bwKm0P5)-oopJQGU$Uq>Mwk3dep z5&m7ZYWxUl_5aM5xEC_+OKsc-ZOay|K7Ma4BF@qgPesH>QLB%~RzI`Dm*27DU%gvK zBrp7O$B5(@@5X`=-VLKbAMQOAROgPzG7QiJK*7!}BZc)eaob#x!vrAZ+hs(`1J^?H zUVS;1;-EQ0dym$gTTh;Wl?2=3EAJ4HbPr^35I4*1X9(|Vu9v(V?)HS_|6Tp}t&)>W zFkH_TTm6$`w{;VxaM}Le$&;~KJ$iyD_mpj0DcL%~nEQ*8$1a1CzqXdl z3Bi-f{pWb(Mn}e6%xB1O?V67Aja19mdRCh9Jv@%eS1QVPuzUF$OR0RlYoL6mxR-CK zDBmq-eM|YA{(UGd(0kXospPr%yRTz+zpqVhzc>oUZQ{6>=->ZfBI>}MMAT=Gm!^J9bhXQY zq=);7q)%N6B;CCRNxJtag`8tgCvujYDahHeRE+PuXdnjgq;nHUoa@4&j{Q>BQU?EN zZd<0(?3c{R+F&+@+s zzuFHizgokcO1fwBr2h>QO|uf6cbAgr<~-H;w#{oo`RS zG|Ju^<=f8Aw_IrMJ!e~cqlBk@Y&?(9oUudqyDi^g`=X9tO}?nXN1VQ>>rcbGB#yHE zZj%MF&w6yf+i?oAzpN7Hzoj7aAhIb(*?za(-y>xA_UL}Mw|i`4PYGlfr6BuaE+8|H z5)(;Lnn?PO@Vi|purBNoBtpO2V-|vL!1|mn|ii?_D9*Gj?Q*o~z52gXzOi_NQ|#0dvF0Qoy|Oy|e=6<&Jki zlRj_&378*jA^}rj5I)k?hov@J!zJbWsNg+G3e1PtAsV7ck4&&NGpchZDM znr&X1ebj%_69Yqt^c6lc<~YcI&H^e*WZQe=sUSCDK|Wi_%EPi`2>#EH3;1gkc=U6r z@RbPvXbQZo?+#5UV*cvWWK&!@o*FH@cOs~a$m8UL8R{UqnG-~IleS$)iSomc@1-S_ z-5AVn{z_L}7n?)0d+0P5U1yi)%D7UVf6jF+&+odaJW)}e&ZAt)v$vSavt|X9=dl&8 z<>{lX${9zwl?U|!31X9t#IYYgf`;1XU?`6Fo}RJ!*$kug1c3g@_6#cF7}7V#-U+XJ zargQs8{voF+i@1oQ+Yey_jfG7^u%n&W84fgRM2G@Bxt@Y1E8?K_WzCj5il zgcInHWb)I(NNW1gFz(*i2I+PkDVuP+Y{DaRW$W9mI4W0IasP-(={;j8`+?b_C=+qg z^ZBI&KYd35HpUx(wlzxApgZv{+InSP^qHBM@QwaIM-|ife8x5Q`3tEvcOOa1$xzH* z{xEgVw%e(D{ykeX`zg;~LRq0`OA%pb#(4b%N`SJJ88CsPW*W9HYMHEnYKNKR+KosW3;fM>f@hRXTEQ1N$AGrZG3;cKzfq!t;W2^k?qZhKm6lA>@)9R%=^JVU?S3j^M-uu9ZJ*|h~C;5HbwZ!bjxoJI>aUh8lDiIB4$ zqr5{qam;=7P-`KiY{8V}yxflcpR|MeEgTQVu!=A?G=Sp|iwQTl#n!?NZh@UKO|YdS z!MBVL|F81<&Kc?W{q8ji9|xzU<@d5%lys+0bK`ej=`}i*%I`hhqilXZ>z(|Lw)vkIg9vxZv>gakNln)A=~_6L)zx&OWV9Kle{t6CD&&FXuIIm9^}NMZ%Jj;xsdjyGgBD#)v1zEPl_mvdT|SsZU_u;D`D4EX3b%A z!Zhm>%{fr2ziccA+MGDu+M$^Z>H*KI-MG@@*l@ zC7E4NwsWy;HywZ|WfQ%)(Y1*lJ{c1U*Wtz(fL0=C^#PjBF3&*{WaPGyrQz#%YtCV}Ti$z_Ti6|Fj(!#pz z{a3ourgN&zz8hlzx(q?j%+zW*htBQG_`@|=vUiX+LqplWJBew7j1oeq{W3L*{$~o& zX3BjQZ4TPRw7K#Ljc*c9Fl{QXWZL+yRA_VaNs@0qxYwf1^S2S-HucWHd{gD$4cX;(09reC4ZrfrJko9$gT-#kKmQ`M#O&7>R$-+c14 z$~Q%x?Q5~9^Y>Hv=J!{-(x&<(m2UvF9YIIywfu@UaA!Q1RQcwC$qL_Gg-~y1XcYa$ zB%;k{_gJ)9zL{xr@?#p`9Q_#6W0O;Ka`uZrXmM_w#myFG8RKAH!RQTpz zgt~Q!X&95(0XftV|qbI3+1E7x}X!l61mJiZ0^$cT;$~U-0 z$>N)CgjzjPqv$0k5N+-Tsi`^krZ>kCUE>2kI<`)beG*Goj$?I*vROzdpfm znQX#gnq$v{=C9iOh3>CHf9@-vli#HCGBN)(T3F>1VX$!QEOKnl+jj5~G-{Y$hx1=S z%d4ySOAZvJD&$hR&VF-#OGJLA$j_(b_eidu-_Mspeoe0V-El1C_m27P`91h|%CD)N z^858DJHN9;esj0|BAfDS!Tbhm-SRtGpeG~@RZzIFVbd3?I@ zdr49mm*0DgWBmRzYyAFMvyyJrF>d2`+>{4H1q$uiZH?b? zl{l>?1SAXY7n@^ooP8<&?igEt46Tp-V{L{QbpzTFH#&#gw@tuhbck2X=2~O;61;!n z7{a}Gvpt4C{(gH5e?cvc;UB)iiL?Nbh5_l`8IDmpPJz{5C?~dZhqAwStifAoL)kRu zUp$?-;P*8+uEt%#{fA#fV}5bmWhF|wGmc#kU>&bO@*|IC8jQQS$9Klt`hXNuUPt=K~_R_uz+$hLAj2y@F4 zvE|EUY%%@^q5*vsJ8p8G`X zdF?W`9DeP&PQ<=r)%aETg(ux+)V9f}#qeuy1KuMYlToAK*WOBqDzGxigI{>2OvXu8 zX^w?od#6Kt4GkFvNR*C@iDCKeDEPJa5Qxf8$f(os7p_+DbnL|Gi~t=XJ(T?=&d7wyqM+2ie8a%ftrX(nX$Io?C`{E>iT+|}abEKt?|L__9ELm&T=kuWw+*}q5Y zEAO*r%}xMhp64e}x1D!dVwj4pOHG6*KHp284ozJFZ&`w*S=LP}#=QGhR zdH#6pRM2C*r5$eq-_Ku)=EW~?x%?QMJBBZ&*qDY3A5zZ$Jpwn3a8H=AD$6K=zoqaO zhrjjkcQ^b!34gobuMhsdgugMXvy2nrFAx68;4cV&N%*^FwZ5m^-{HyfpYAc&9^oAt zi6-bd8xHUBZOk9x-QdZZ)AOQtXtEFTSJI!Ch3ktuJZ5pn3*NT}{b%g(F6Vz=@SySfwwNy-k0<+I-P1O1PEW_qtG*gJ?&cjJ z1#j+gStbR9Ib+wj8+)|~eLLi}_vxX|&i&BV2I6eYK8|WQ_V^=MH%@WEi`E4m(SFBy0RBt*Am-vV%NP%! z7)y%{_Xqwc0X&ZN)9`(icW8u+^`U`wpbzRZG-9L5y7em*U=810u`SM#XeDijU=*XhuNH$b~ zNZL5|-tVD!Dvot50<~XS+kfHNUkr8PQo0LYL@0uo^Xe6O8HUjVy|iNo#2f$rV?Wk#|;msUw=5QN_yExp<;inva!{OmqiSR@YPvx+T!(|-C zIlPX;%^W_-;maJp$Ke2n#yYMShf_E_mBVv5JfFiPhgWiVBZv2K_%w$vaoES<$?crq z)gnBU!$}l_}(?ejIikNLg`O%7*sSj1r!hfN%==I}ZW z@8Iyq96rb4>m2rT_%(-PuHpPRJe9)|4$tQ>!r_%1ZsPD!4qxVQH;12c_%(-PSk9On z&fu_s!&(lHV0!PzVgKb^eh#1D@IDT&<8U>HwHyXGoX6oY9AFg{pU7itd0gUN6- z0x-?dNTU%CC1v`;B7do;%3JQoJ!+MJFq8mU7+q=9B!e}Lp=5b$GS-?51mnS$P%;$v zMH-@pQR*>_XlpXE3^2EZB1z0=aj>~HlrZC=SUi+~I5QYATVqf}vpL)nPGb5576~#e zUR(@?Tu@Ygj<2%HQvq#VSt85hDJ-uvg7HS9%2VU_l@)s`DvB4>_{yq^D+-GPRbU-g zl$V!5e(=4p96tKR@6r+<&?G@{rJltBqdZ<8iihfp!gWcb(pQH0w*>3z6XC{4uo>&x z6s(V~TpWt8@*_!P{uSkg6|-`4p&jEMFD!zDhyAAiUO2-mk^eM`$Luw6x)hKW-Qh0iQ<7llTARo@=}YBJ=H+>IihWSMS%)mVX=X{;loOsWznSRX>K-sk#I5$HC-L5H*+AV zuPm^rs-~>Gs>b8@!<(>!Mi^`^GJZV%RBk^;f2~SlM@F}rsY*n%;8VT2#E8{`xQ#!f&Lqu zCcj$8P2vC$@cD1!rr>3O6=QGeIa% zGpXlL`Bp@mTU&sr)FdD{%w-|0m$@>WY%-Hg^rCTk&QwAMRcwmZ&oGxXg(08ip;bUV zN|2M73O_>73>cFT1WTAJn?e!5FNlbEHKJkOOk{u(Er^Lk#w3kQDM@D>X!B5Ab1;!G zK@?&>b{D3?2OSM>A{wf5G=}d`mR0b|jAU2Pj4r!4RF@<^6MO=hQ;DH;(Un*kV;`=~Edl|&L8 zJ~bwCE>M$AyU@Hq2oC&K1Er3}YnHFXmkXzg!WKpAT2V)n0YD6v2Eb+_XmILk(5bjR z36m6{P<0R*5$J1(EFNtHbb!5%+GRyJ$PCaBou;t?6haxwI;qMCrG!c$sVWdSszT~SK#Hmc z>_)P`zyd0-EJR&WRa8{G80>`7a@MB;Cn(O!O5JWH)1va+iYlpZDj^=N0;ot5Y7sGW zE|@+$H+RO2>0q8+Xr5&jgo5#K0tDj)7&BuAMK0iOY<R?!X!l4-YqJ%ZX=p>By^paqt-!H0;rfKYkEXeS#;;;OA$M9Br2Ui2b~xH? zXhc;&TtT#+>}OJj;yao;#u6|q#P0yw;k?m_W5;rP*}bQhhd(oDo^1;BhYy$jYpd}Z0E3(!z~Uaxj03y){*!DV&U$L1B-w?Ld^?yeSzAqam-y)V-UYk|CXzGesS$$#q&u^jA%_PvUc+&Q-w$vw&<7nBf}B}ai0p;?!LW2TmK}$vxj0Ug z_0xH&tQDEcxAaqx=%1?VI~j#T();DUL<(AxmTZYV=;qY}Pa zsKk(q^ArQ6$q636FYqpb@wb6(F3N5DN`wsey78wE1|yB3xPkNT#AgM5s3kMHLmpGR z3F*V~6M2vLR>1pOVj!eQ`q8`X6ak&=FE*AG`o-WFxA!4nk$7+6|?p zf@eu+NqJQw^m^0IJJrX6dds(1lnZ1tw4Ca%tSrV{u${0ka6M6k8)G!QmEQ7-DpF<{81FQuu|710 zHjJ^N{uRiBUJsZaWm1;1FcW36yI!TgJOFK8P+UdSBZ12F1ko(`90azs2Mv`!?U97S zeO9?iPp3)G9xL&nJdXnIfR!`7>*;|ptCm^!#9SyWb7Xazf;W}T`%oJP%`}Fgq{x$fedt1S^UQH2dZMUXAsa{61*wf zRk|9La=<6a6^4<|_M_f@kx(PF*@}>m-=2kwDttD*38z22ERHT8q?^-Q?DrKA^;AEx z%BKk)A4tBmxze^{$_k4;{sK?Id6i4hEYkelY(B{JU?(hM>nmXOCrx2eKMjBy^iKF>2oM2>? z8L21YVvLW;@#0d1jtzLhoninRynzuw%?T+su_6Ku(z2 z^wawS_Qx_9!-8(oB%_N!n#8ja9|r43(Ht>YuVjm=-S>~)>^v(nkJ1zyU_%8G@+Fx>1d_UEQ^n*Yj|3fye=S`i%UGfTa~uFVDY2q7k1v)Py;J`XZaBE zsaNN3Y&uw+tc^>AOTZwrfn$C;ssXuJVl~!Yy871xDKZ*Ee{po zML{Zs0M5wNh!K#cZ+$#Y;_2F^V8UD(he3f^58M$+SjY_B&#Q=GIi9gN$LUVD>XewL zmOG`5fnJ`X#U3y}3t)`0Q0dnxZW9%coRWcdtn`?k`d|#rXBQEx`hA@?F4qscQkn1E zMa6R}e2b-EuR*#&V~H`jZT}&kvKxUOm5yTlhp;*8!wn6gxL8!=sj|!|r~d_tr`0QE zZi)HC8gQY&BdJ>nVgN>M%D4<=N(m1uEk7xbsjaaz`^Pok7(SY#!Js({@>fI~8UQvL ziHCruiI8pA7eTE`prpk*Un+WohV53ricl~+k8r`qMbyfn>ue$F!G?EE}93`OOPn!tboPElb1*s)w2_sFFJ?5ANR zW{CM9>35ZUq%18oo6Yi7BTLJ)&}96H~p_>60?*P&2hg9h?}qEuBBmlZ)v7F%#qS0Zy` zXxpE&m3Gri0D8~|r}~{$zYD|BS__q5(3qt8OhrIYIc@V_8P`%-rMgr6nqJbFl0H1H z(R>6tKe?Lg#^jv(Z~{c{Dr$%l6A!HDgwh zH!hEL;hJs;hjNabN0HHIZo<|@@qp9siyNK;V4lEUW&eC~x(by`PpEX_*Yjylm5SKXYB-JiL^K{>{ti+eI9rGeu)CH3h zXH8CAXjn^Rk;g14Wa#UG^m=GA9%NZ_C|IB1Z4j#efM_83UJj)7d4*Us5f<|bVvf+3 zs8Vjq5pH66I9AgTjwjebN>Zmr&cuT}OV|rp8-n3xxym&}W-j_gtm$=04^75WKd-Rt zF2mW8?EM4L6)5z0N>kdub*kB2;fRvXa)me@UY7ruYwS7zQq$@01=0bg zIr#+Nsp5*}>q3!eYh%;!EJF0_B5pY-m3qw&Iwy4vrynx5*KXoNYiQ~th8$S6gcGuL|GmYIX`Rq_gP2??=el-fq?DagUDl%P#tnmQvIa2Im%?4W%0s{=&)FF?X zLd`UUavD%H0CPA)7gUrl3TWO?!w9v+XyyiM#q=c`-u52|^O*Ym4Ae#t+VUaYh8d9k z>2}PB$F@5S$=@xwu2HNDGHknqhXUf=p8L?|gNmVoX=a0fB-UJz+P=JJI^Hlj;DrbWR-QwYTq>sbXa z4j@vfUZw7<;<`Hc>yp-BJ!I4xM~td7f&}O+VN;>nMqjh^5Hg=9>VjZ1CuqkA?_t4x zYL)gpwWmg#s8GQS@SIK8dyLGwP!8(2N%7xMAM7B`mvS)D*K7FlJc=VA zF6k$iXb5+=T`y@5h|#uY3;@xAm@GfNpoaX>m9!SL+Fp;iL|N~rIgk`TFi9sahvs8q z{-b$NIiQkslVh+r@l?IT4`8o_v29O{f1SfH!#RQOoNORjkYK-3vy?vie1OV>+PXl+ zcia%HOGe|?JfY>IM1P1BinjNJ*9kf|gBbeyImHRV<`>vIm$ZiCz#_DJ5R5Tt^rvA@ zqbwARE9JJv>?^pyw*NeZFI#afg$qm46I{`#t&4CPgVO)!oU%tb5LPA@LXmnhay{%J zRp}AjtmPwxi+PKaq1yn zx0F&fTHK_pQ&9X>+FT^*L0*S%w?pdODDsjrWvt6R!&+yOZW*ICkBPIEiY}eD| z4)b5wo}b1_&r&ojlz%qq3XNYFcm zkG8DR81cl2`*7WP|D$3(X7$d)DD-le$D#RSaUMoHhtnPw=VE;NkO)U}_$v-y|FH;{ z{zQb+IIQRIH*+|R)7{D6pX2x{4%cmAyc|yB_~#gI442~zh94v2`TZq^`vb?n#PNQH z+syd7#vLx|_fJvZtdB&P`Gp8$Ln7=RA=334M^HF^K*ZPbdpCzUBSpHd&&Bt^Uq!f^ z>Fj+~ytn;9gj*S|_CNtQu)hei{)^j#^WDwu(QSzLK8DL1A;Knp$9By`{G5KLgyXP@ z!=9%_dz#OTw%~BTMi!T^pVI}t;qyv3zLWW&dzB5hKi0dC>Gd1tn;`+WWf#NUD$)5aO=I?JYJs2+bnt1QKUZl_efezR6w)np3cU&JXXW)H?yP4r`(cz3=Gn}j^!}W1F zj2lGyu5~)x&Ucx9zh}D2dUDuvBcB_yL5CaUa<1q6dbxi2zht;;nV#1===rAjp8qQm zj%T=D&OgTW>%Ccr>*^KXa~ZCi;i})`ccx=jrz1b6cMszaFx*Zqci)vv&#QE}cBc0L z*C&VJ`uN@CeA*rPf1?eDa&Qat zOD(t8Aj9vJe0B%7-wz%1vXtU*^fFkk)H>u#HPd@H z*Qb`-VLj*Dx!yg$^_)*Xm#dTW8_)f!oBL1JdPjd?x-@b9)-%5}v0O5*PVMhhAC~9+ zvYedXdXCS(!=0X;OwWGqr`@vOa(l+Eckf5lEML4#pC+mQxSTQW2fdE|u!ZTiiRs?X z`E@Zqlj+g!po^Ewvz7IkA^D8)1b*PIH*2|n4stnbIX{#8v%&qm*3sWH8Shr6=K#ZP zWj+dQa+lZLoKL6J*99$IHgyVZJXF1re_oD#wXD`zs#_hM8 z^KVZhmrRzkTcw6Ikt)E+2Kx?cFt!v*Qbli*~a+tnNPaf zb+}xXBb%6hgPdPC=hxYh%Ab@Ux1Y)FF(~B=_mf`MC%F#0F2Ho#$>rQF?H(?FH`_f~ zj&Vnf+q+5H1G0Wh=PWtyaLBWMu3tdvJS)8oi5 zhwDF{>*M9}d8K~8%3a?YEJwOH+`{#%WjwviC%GGSIZ@61I>7xekKwbp-tDY6d+%__ zGj6}#OqX1yOMvOpo<`1hGv1vH-zD2m+Oe#M@;5o)SWfgy`-kh7CH3YNsriu{{Dj+$ z!z><$H*uK%6Oq32Cpug{r*GrX*E>3T*vElHXfTn)# z2QvJW%l9XSzn9_e)8Tsm!g_(zc^Pi}pSipb>T+H_y(b=`z4!5Z-hg@zYTx_;M>@*Ml`=lX8q_pMBafd^CT zi~NR~!oGyHgd-^A}Gr|;aXw`VS=%VawANV;(TCYL|P z;YcoTx2f~hPJYiBp~jz>zuQdZKg@SL=jY}80{ouC?a;l2>-msQx7b9zUFBm=)bq*Z z_e_>2cifK8L-X?;>FlEFcrQKt(FGr^UN;~AAO7gy=8ts5&!Oj&`SLwodVE{((XRO- z|2TZc3Fdo73g3^%}dYVTpVd-V1h zJSkN!Aik}PCzIy^s-+*2@fnsmF3{~ZccC9qra{Ar; z-Y@a+I6g~(!(!y~_g)U$C7<)Vd8fNRZ9j$ONT$w5O+2qw>)!9CmSh?ONvJwK~4t zC+l*lpWmxB{buA3p6sqqnoddOYb@Fz=i9{PsOI-P4zt)VQhT}H4n6!mhr{(u7q6T* zyxg5GnmCr=l+!Kv7hSwdkd%Q=5lpP{%3rBKTV}8<`-bNUWSXw^!#pe zIb%=SaHy+0xjbF6UvPe14ByV_d!N+HX>fjh9Bz{H(Gs7doCm?*IQTon5H`Yb@Rz~A z&oabWd)Q8$+%Cp9++P1?d|&Z9e{bjS?Hr%=HMb|f*K#^ZpG=-tua)!b44=>My)0ME zyYzbYa=IQF%JCh;<=*Aq9&J2tzMj)<;jl^0r$3X*H%No6oX+5MJEgvs`8|`GAJWa_ z^jREk8j^MbuQzzcUB376_whXMpJS~%5MeIQ+jl*!+y8wt^nNxt!_EGmmY+FOPbc9w zIpDip;g8Ahnc)tfpBffo>n|vRb-|0xV zm^K38YWr|qFd2;}4E@Q;)yEI=+2X*{!|z$6L@35f@67LYq?5k`4*HZvakEQni?bx( zjXr0m&`17m%5}F>2bo{S@%y*sXN=VO-I<>}N2MZE7j49KkoFch?fEwPiKj1xexX(yW>qsZz*E`}jxx&xO-{MFo$BS8x zcyqSGZpZijT(|O_oj>SESH2>` zW4HpISUNzPyX*%1Zv2+Y-vR!9fTR9xj(V+kt-q1KDGeR_xnCT|?<6a~Kf(|?%5I0k zRdvm7;^W=xub92-hNoZQaKT6&eUHpv_(@)!FF$-YdZd?!;u%QszCoEU*6Er&`$oH>8!V5wci3`m``mQ`TE`ZPpUix{RD~=7yG*F zs7%iHV1Bpk>I~M?qj=pT>w9xF>-)!2^*zG3@%P=V=M7%}xSsKJJ>hPT#r9)+iqlnd z*vsoJySv$6^SEwrc5{9PhyBw2XE>wVJ-S)*9bKUu^ zKTZ3$JKCv>-!~l~Ldgf+r@6!Tq$!`Xoh2R-{yK%Py3_QlJO^F8bKJ{UosNDEJG6hc zJHHxf;M4Vwimk_8z4zRmRy{2Hz`;lBUHL6Df0HAfgzt64_osm$OvA4+2Oq9a!>^l8 zcZaWa^rN^33G--u)bM$B|CzgDsgterNK&iP{6$ zF5qzBVb+rmIrK8aHE}qe(+_fJq=C!f?=cRya@fXr@>$A>&?=iAP3eN4yvOCA1A zPM5>=$>sd=_&v7Dz5TKnu1mHX!*z0gJuYxq&aJW@vi%u8lj8$izV1tPxPF;l(uc#{ z9QL@hAC7^yFde(OyqR3i0ggA;>gD`y{*cTRe~5hA&vMsaooDI#=)3t(CZ+gKa%Y@6 zBiGC+uS=Sklh6@21QSx{^_ zU(5$oEDqwuzv5Uryj-L_Vip9O>H5aS@~oipWh)GM@kAjW7*vnLvcjfdBob;C2X+{R zbz%#A%K1$zqh?_oZ(I{kVSV##?cH%OMO9Ng6fz5%gLTWDaC0b@Bc12<2smFP5v~uJ zi()g2c>g)ViC8;7^&lvMn~ujVn3c(RczMXQ^TSvXsow`?=U0xmR%;m|9LHMux$SQi z@OS`z0!nRGHNi`?HEG?Cfq3YvFQUh%JExZLdFod{)8P)#B~9TZqHL}Y(>d;lkK*ho zi7)lA1b95SNjVw;HYx}%TSf3zl!&q6)dOxs39tpDbdUkUi6{}{JP*Gh)SR4-hw#|- z#wf3haUKj>5Dx{H2UiAHDe2XybO*i^Hb-freQ7Y>x-7c7RiQI~(!ZoTf1xl6RSD8< zw@3!FawT29M|i|1j7}_pSR0Syo(F-a6;GE4Ci%bzf~P2tjFRxK=S~#`1Co@o)pQaCE2-n*i4zINw;&j+gfI9 zlujhnCbi(`i++C|7;5=Z-8*L$G4L63OEbm1W#}uZyU&dFDhe*}owdEwKrEUXw z>(d-X;W-ax@I|JVg+M1N_4GwB#!%9`9I)VvfDFJBg_tV{PDDBRL_1$%VYqHNc0EWh ze>&3X=NfS$bGo?jl-p7omLgA?=wy3r77p(HrIqz0J+F|Pc zU_IVyQ4mcgqb&kXL~%^I17q<7XVV{QY@`FV6*&a(@JW2p`;~Suf`CW!AcZdpUY;yd z=(sn*MbBta&T~~tgUPxkBp@EjBh!m0ic!yD6!?>-*H)XH-i~oSRD}3Ho=#r!rxbaq z9nve|6?w@~mb}ysLM~6`+j0lrYUkVrz&5E%RR;(sKS?<4BAE(cqC``0r4($CUPe(& zx^sCd!l?UamhcVsqH~tg?cfOuPa=fh=P1!>&(E#IOHYGzceG#X0LD`pqUpe};2OK1 zYX=ThHbtXJg}?~MQ4~|?xKDs?^KxNO0T)k71}$_PL|z#SN2npIqA{7CqbMfbIfqb) zW;{1YX-|%F=0jlCv4u-A@CR&$=P0-PXogSk&rqFRg9xzBNo$j1!uUMZXoOVuh zJe)|{wBIE`iFIBKRk;=cpQ~k7z#HV^bcW5%MBbo*5i3Ky!@j) zQ5SA*4$@g$g)LD-9;6unMUsd{PD+588;^&Y#TAT&(NIG}xGszbgyzJtrNvh|%TLW0 zXbkKEnZPiv4!{I0AdqMNrAyZ&(plp!c{pvLxiw)Hh2!FGm|(L#J8952d48?DfGs6` zDH!D~;{Fm!Un=f-4uEXKYkXk5E|v-?1PR2V;_mNCx@FxG=#jUgEE%`6JbpO_9W5j5=kzA+S1sxBHy#-q@rcxrp8#}r3LV*28?q;;O} zu<|iW!focP*)wOKM)2@vw}W4tlS}R3w=d>fga?m_Lv+p7M92(Bh&h7tc<^9zV>AxM zl=pI2e8D(~FBE)!`%vED;X}GY<3Lz~p}gPo+wK`E#lvI``))+NU0MF44@)@<-%iJf z7IOmbC3spUjyGUX#kT;^+1Nod5ewCc(Se1jG$>AL)=xj<`(2PBn}&x7@c8(+dV$Yz zPgZHDJ{*jQzR&OW?-C!>ClXp^mS8JbcuE_>DfbqZhU;U|Fo;DVMW{ULfqw8kpj<>j2AFv)c(UDAHYR8pE7s)H4hIF;IZnByo6{m#O6^CK;?3Qa?x8Rv2Wb6 znHjgwI-gtQVZS>M!lObVcB>}eDQnK#W)1Dq6Y)L0!ja!0x%2*$R5(TPSa79=Ja|zm zV*YK&gOyo7dZ;gla2;!DYct>w|BaM;UcBMvWiYz5SGvK7um}gpLEtH|XiU>z-eggo z8sR+Xb2#|y(`*@^5&`Y!izpHBey)0cu=5;IxBL(sZJ20_I14&K`G{D<&ZiO;5QP3v zu)b1`3+3e?Zg??~bZmG9c*^~IFtw&3ZP}IhHyKGreXvHELzrc^MxUAZwt4XbQla42j zHN&8qtSCB})U=K!web{19C+wx-?Vtd5X{EYnyd(cZAbTS71#%#ay*$OO0IRWvvkIpH8(W*9kEG&_hY7KI{^zXe^}$tuI2oE=-Y^|Ub4ZGc^1`X83WFw>R}4o? zQ4xqEC5M-NR)vAum2_~jSrP@F4vKTOs}c>OJdXRq!Tu-jsWe{T-Lhj4`@RTM45gPB z4yE=lfHdfwfBLRn>T4+XHQ+2!$hOax)HU0`l?#)Wz@RN0Y)PDCPOgye_(LA7o$mgJ zC8FsBUm|e=%4M?fl;9OIis~$DZ2>usv|ff&h}iRHK>}e|%t*mATjHqGA6N%xEv>V>QV_!Rp^u-ucM!C^f=4Ib#h|T90 z7f&-;(((x-hJ5Ik8~%M^Ou+|@trVa6!z#Wj{uY%#zH9lBI!cR5ddaBa(@V^xI5SMi zj}b#Y8XxV8Zf=;}=%ylMx>-@fr(0@n>b-tizk{(~aa$6Gc_9)u&_JxZCy5NM2= znU(VDetA)h;rQi6QfK{(T<{eQAD?u{qrU}9XXx+r*}CKye`ce}nN&iWF(crW2J@-j<3T?Ngmt)K;vv;xCa zJk^x0B0D#$!xT{rLN`|13n z$M#L{?AprTpSAY+>HDjD=gV^$jz_e|z02`^^S$EWy7@DPPWaE4tsh(G-4z{Hdg|Ln zpUl7HtOMWwQS6WN<+(fZd^>smoxn4|;h@})l`n!!4zoBM&*3o~njG?e>h=(dqaLB7H3LCymu!!euW78+VkbUE2isnT+ZjY96gM;hw<|JiXOSo z@aXW8!mUSce?_*>ka#YaAxF2X;pct#)nYgn2{6(yVxB{c*hzcn{;fNR#Fw9Mq4_CYh0&Ti4Kt=OSm(e)buK6;+d}3rW4M*W=E|xK1t3 zN9i`{ag?r0iwjV?EqWZK>(=6gk9n&eN9lSX?*Ak1UBKh2%JuPgGLy;7WM(qCUz+C9 zrll>Vgc6|80HG8pP-?6}3RVaZAWDT$0z@cSxoE^vN(Bj8IT2B#R*W1j@z|pggXSn= z#Gp}oBtqmwM2r}YIiCLH|9*R~omp$mr0wAE|NMBK_bKl;dwt(pYp?aLZ|z(9ze?Ly z_isDN*pM;e^17REzPar((a`?W^WCyrJ1?(0`!v^|VY|rnr`uZGi==%=WEwoiKD`0m_m#=bB#)ZQ40#aQbpv0X~yAYdhXK(Yp%J zbdDb@)Ss|w`gJiTxsJ#lR4O5nuye5{yQ zgvz!fP}H&np@tPWx19M2(Jkex0;n2@BHW$l*q)ltF}>EUqj8+nS&Qq-3$P!n5t<4P z)`qeWny3I40c(h|GkE-$s_>;5KOUM4V8>WCZXFKdrhyz>*B3%tPZ;m(j$mU~E>?}s z!%>};SkN{fg)LRAAKPgA694T{jFDfw0$3$Bc~iU=goNv|_ zc6~+DH0}##A-FQD-WTv8um&#QnAK66orUa{Y}9bZuO|AXJz{GMID8W^;Mp3&4(}wM zJJXF}bEAxWWrTh}|6o}ohKv)9`|^UwYs*1i1OHu_+u;lO5xPAvl{E+zo2wwRpT$J; zSyG<>vODSDwnu`9OfY7;-<)>k;41Qah;HZj03xH=?1PYF`w{WzS#JGoZj?im;GppTT8GS)P%&V$?UY-`6^z{iLs>(p4aY(D2WAACgt6mcHK#&bt%t3s&aoGIhH zNKDIEt)fN(A17KJKdc9T2Y<)3j1BAeU1iwC`Svl+w{4tnH*&spaK2s4`L>bs?NrX8 z-PXp z6MLoX>L6B6Mo>8rL#Qhs8*k@2br-98u6It3AhF^}uAp(Hk@d9~2616$1n=Vf&Tb1M zu@g9z7(G{M9Df7tGXIU=WX=l$`B zP34Y<2Xi`t%?$Su=7Y?)o$=Pzi01o%mxz}2s-F!majat=F2ci;1-Ngl5Z%K?xMiRi z*YuU((wokZw7RWIB2fg*fh%#X{5185n@#yNdKtnJCc>h2Kp%`ht4BPi;W zwSPY5e+4*#s6WrGgUxlnT1okLjCg<4Uaj=iODuJtJR^#HyMw~x(GR_(}m73Lrx}MWe*u92h@N~S!WB;3& zJWuuI@F@;|<|zi*;KtH<`SLL4HC%*wzKw{EM@B*&Ij!FLAD8pbysc}d8kbDg;N4@j zSU0=?s|OZhVP738+L^;mUx?=K06*p~wMG1&7s7cxi*RiBVvb49x1mkWZ@+woK2?01 z>*bx?f%C|>CL3!SI5*n-2zEf_Pld_+sgwJL7UuWQ$=@epZX>?lTY&fWWudvpkG0(a z9MhGJrR~)7={Sl~`vu^BqUBwlzA#a?eTklF+>Kw0oIx)yrZ%_KVB`h) zdp6vYh_6qV;M2o7*fkKsO?_c}uqT2`x^uC%D~iVU7>=2kkLuAX6tyu|ZCH#Oe@lHP z_Ivf9PuM&*O$p% zeK5+(c1YvCXf~n?m|2em2D5k`Y8QmCU?K~pZMlfGM4WuB_a)ASadu;~$_pmMFY^Na zj+EbFa9?p4#SP0)>|22NlrsVc<$ODR+_sc`yae?x1D_#ARe6)M-{iFb`?${+;PW#% z*fJTymB1m*z8 zFb7z|9H6YToNbq!YaV{Ya-+mvDJ%LRxjxr_vEBSvF8QjC#&NPm)nfXh0Yq1Z#)Hk- z^-eB2l*_d^f~mg&E13hW+Njpz=`4reK&;)U;*I*9@g=rLiOm~b|28+uIs1iNXU#HV z@Hm}2b82^FTzazhgeXqn`gJt(j|H7MnAgU2u7&%T25RTunP&bk=fOsm6Ss@5%bLi1 zn8$&~A>}xjd{qFe8f0yap!sd!wzs`Kf{Y&-ry@;O#d*ryH_m*&&G(m4=m()E(7C1^Q!HB@^QCBq z*xp5~Z*j|;+|>@Ftejt+YkB4fZ8LxABRXmf+ zn6P}vxYM{##=)D+4hN}sGM>5qhuTUoUh6~60w0!Kz%uLOSkGMHbmj{6%oUDct}u@| zLSZN8W=jz+TjIkOV#fta8<|IZi)BB*h~xhPZ@rGQ?UrBBUXA-g0fZh8bU6Di$x&Mp z=%?R#OMQ6a0@ePWEaY`@p0xAa+P_x^xnB9*>w**cxQO=WiBT2H=0;g(<&v$#Z--lH;_7mYhWpI%%;S)991QO6Rc_~A<>r*V%67lESJ^Hfib6Mm3tb-uUlYNao)bCmPhxJn zn)!Gm^YM@3!zb|E5_>OHISh{hj{%PXk1>Cbr2LK)AJTVbT~uGWO&N5)*N0tR)?JOe>ux!mdGGW+$5Bfe zYBu=LO6(9{ca-3cVLv(t0{CEGHrjfEcwcu8PU{MB?1!;pqL}-k67IL9U#jrqO+LIx zyzbRQ#*FP#uIXyr-M_w<`_~O}-ECaozYfd(^@1oan3<0alT|oltQtplEyBX~#T@f3 zNL@s7T5jwa{S17 zS=64f*9EYyr4ZSpaWq}#Lnra*`&HX$1AjB@Ya>|8HDK9v6pJQeC>mmZ$2@PEzc0Vs zhhaIUB!CjG2iaUN#CBhs596vHCeLzWm~mn7@=8C?VI8tmeSF$MwRYX|eC@jBQPX9{{fnz_;CvuPF3z95Zh8LI)QszV*mkj%Kk2a~f6};n zUAkB_f3S8z1Pi)?nBSg*veA4bI=L=3%)=DpoNy!aLh)M`MA-z}(^lxLz8VCl0^_sSs@fJIaf=Th zB?hD(Hx=Qgu^e1A5XQwl?5FN1PVS1~xb`>}k5-{-sG9w{*tt%=;7*PQqM?=Ljt$}1 znQYWfGJhHlA>0?i41Ip~Rv-3X;;lngEE%&J_r-%82i0UzSJ9gA&0+*sJqmSVRNI5%oW|QlM`giN6F=bsXXVU=I@f@a*y^mmOtY@j&J3| zSd9-S{RC z%HKHVyr0MVTJdDeYP_6zd(3yN%=uP-b5_0RYm;|9ee@(WGw$=mUN3g%Du>rS&i%Ra zXLF;BvxYGLWI4mWHj1@9#aP*0f~8%hsA?}m;lxVz&xyE&zMdwcZLU9)XF2|KUp~23 zaXjb-DO;UI1p$icOJA-umQjCXhC zroC7B(z8CiMy$HRt&h!h|GsDt(Z}VAk2@A-^Chkwi5GonAqK8cb{O1O;70-HQSJ!G;|U3zz`SrJ^TMMh@~~tmftt>Il(o?Z^OI0R32J^x z9VXUYnaoXvKQ~67S9;nPaCpJ5IH#|4W7^y(gQDc#pgpK^*F3f>;a-n#D{%57*zx80 zg^bH2l4TC-co^K72Mu9FHU>Ltg4<>|HnU(!ghI| z_j;>QR-PfyxV!Ija>Zs>$Ede$RjBzpwT`$`{CvI=pQqk_R(sy+dTQ#2)bmzn^L#YP z^Hw|`OWDU&LzT=GTwT3^bLAt%)CWC12ejkiwze}Xt2czxJMA_ADd6TRA zV0u}ZYZ`a%gK@9K47nq@pI_*#g}bPKejr9YTwg7g_blK(m+ctch(Xq&rz8tCThv~3 ztO`dBSEH`04n^&YI4JX_oy5 zF}g+d;XuUUFD=c&q^g^7jEo^YrZ$E~j?Kc`$EJ=Uu^ruzh0Pyw%bVOCLq=KARgL@N z*@!>l++%MJdRj3=oo%B|9+!pFiNO!4nA6k&=9*&r2{EE#nq02=MBJQoNY*pUkCRuY zv%}zyhCP!r#q}!rEJbG#noi)jA~s#4_5-8b!wnTXwjUDh*QmO-XLB4EI=1XpEOU(; z!{$ag*+QC6-iYTTXUJ)GmaQs(%GTmwHkh@-pD;5u>^ z=h}m)-@y72fez(M=XVt0j+s;Nfyq;G-dGb(8$Jz-`c6kl&sy%)&t!gf2G7x19AlOr zSx*Mmu%ADT``*R*-;#x9`5N5o^&{@yUFtdoZ~U+3{5=W1@6W=Diy1@t zF}aN6)yyZX$7Wa=kC(dS8xT^4Q-IGhV%C64ciM z$5wGw782LF@$}=$7jYeKSc5{}Npelm=;I~cHL{7**#0JrT$hE`tsK+JkI6;1Pvf|; zj{9a2zByBff0-=8r^kx%iQy8ikEOV>uM8LUl(YU7tUt$KJID3)Stua-m4BW4f_?;< zBRbDZ$UVIl$xHocx+e?Q5{a$en6hm!xpp7A=3Z(d@lNhTKm1S@`iQ+-y>sT?hcsB`I15Uyy!F3?X5~TrX^32XF8M5n3%`FF3h}v-#wc}E1$C=cQlc^oY zQ9JBsHc#4{g|)=M_1-vepU*UWi+*U_xlg*4du`b-4CSDeXb{JA#;~+4%06O0*vHFxPqD$^ZZ20W_s-pG_@i8lI(g2X;CfCpbB|PeT@kJu z$ik(4ezf!i(A1rcle&W3FXmwJM1r|n0o%;IY(oW>e*1Qnr^<|-$``yHldq2CEZTT%xc_Yg<$#YPCoWT8e9rwl6+(#$4FP3Mg zCq}aH2GMb&$}i+Tp6r|H2eu#4X^lJ2IXpux`dkQ~n}|C1v?J}=&i&u17pPl5=QzL7 zJGTj*^E}7)BGIS(nLH?eu(Bc0}OeudR*YZQTPhtCTt|dk0_zebk_df;5o>KcCCw>_czt6&Z ziEUjhQyM^NTd8x+NNkT0JH%ccK{flUoc&eE{)(yhljQf{t2}>QZakYCKAOtVG|Bdj zaj(iXDadnxK6O34JD9vKFFM;+>Ri(?>gMcit@_G!F^&5+1+l3!h)6?@tVylq)Py`= zt-Mz>F6)!5|C_wLrE`h%3}^ovS*ZRq=gWuHIcYh<;m;G@A9iEd+$eKh1+L>>r;YoZ zP2A_ai~E)}-0Pgky-q#%I`g^LDdb)!G+M*+w1nsB2)50NE9(aLN}rv|Lfzk}iz<%E z18#Y%tS^y`gs%wB6CB=im(vNhqYxwi!~EdyS!lVL~LcM9N;Cwm%aS zs=Ue7US2ON`(%x~*SF@&GkETM0bIM~K1Z+5kC%ycx3J8fGVD3jdm6G|=&YE1ZtU-Q z^q=r!@Rt19`yAa_eiY~WvE>$TO)Pira~QEiM>RgXE{|*LGPSpczFUb&DI@Q%@O->a zTa%AtpD&$OYjS6yb6yTqFh`un{BN7KCfmLwC)2rh@281nw-fL5-p@&k{a8zkZcFyb zqDnt%7SOL(Cw1?~h$T6y#+~P=8iFzx-Rqqb+0ON>R{DOOIIt~$_MI}}#~k5D=vLQ{ z&5bhpzJG}yZ!bw5Z(=)oDf1sy-sEcEua}iEt#MybHupefh>hg7&OWO$!JK)h1cUVB zU*^Yv__{EG3y1x9&j9m`zHA)Zor9y=!>F1rQqQGT;rsP|{DfF?8;>stprD6+(nT$$ zk0+1vWBgVxXW*XN_Ahy{&b4=9#_4nUF@EMf#r)wY!UHk9K%YNu@T2`UuU_suF-APrOXtq>%5SoyOgFw3r!>CkCDSxuca_ zTjg3X*SoX)xPTaya_7gXGhu371PxueSlS-N{AupD+T=NfYUjAE>p1?mduz=+=en_J zhs7iAaf}JZl;dH?l^n_YJ>@r;T-#gpp6ADJiFA95Pi*qzQ$+Lahqt%5eS;rgCI&ug z?k&W2D>ZQRqldq@Fnnoy3!}brjx;{|tk>*5QJzaGML*jxMRZ9!ZYjYnV_CR?`gIxg zYZLWrEw$+sYS?kqr^ORRT$f6?S1fa$^{Tntk7dN`w|nbC7!O>?{NfTn_T28RRdQX> ziX-`x&b51~P4sssq2Egtp2IO*bcb8k<2vNpWlezflIv=BV~XW&xRz_bif3}!6Uucp>$s)}n#NAT z3B#+gbf6JMeJ3N_a|#OSyp9On#befmux@4*j-NacOS@0y+S%m%wy2Xi$z5(No%`fD zBkB|L|7P#Gt?f7a@fy)7$6r%~Yi6=>@njI|#&U4Vuv{0b;&`v-IjWg`W(#(?O;Kzb z2xEO8>)exzh22pUcEvak;<)M-YRavC+<%wXw^muXC#Ul??`luo!TtT6{yE>(HhzBx z?`k*R<;PjXs*kz#vAN-28(%f|bKKmYI=;mA5n@1afr z*sdokKd#m)gS%^$QC9R_=W|@2?ESPKCq3vt==DkS7?@nYZ}5;Gmpqi(KVrL=7*lmO zxw>zlmz8V%I@e+(KI6x!pGl1&wrh#TZnwP2)w{%cS!b0a;+kIyOl;YlsWukr>&5bfw6gl_9FPY53hT#C#3}mCRFNg&_@~*zDhY@!z zqhF((+zaduVf`LIKJYnep4dd2$6WsVhy7^ygd5M~S&l!$m*l0L@;+P^dV4u8KCk68 z8uuL^$MLM|a@KVr@_6gM|+%pM3pBPqkvbj;_ z{RMbG`{Eq-#cKA$BDS-VeNiw`>FoWS=hW%bC?okT%YbFTGGH073|J=p@mY>tx4GXr zsFr&p?mJf#@*ej*^nZ!DFVTIsms_~cRv7l(71&KJx}AD+1NG)w>dgnJHLcW|)2THl zQKME;Zx#+!VxIOLmPfzr$2W+*cdPS9jS_z6*O^P+?e>jvjN}PgT%U}Ct=S#58-m!- z7IXKH4Y}z5SB^QN@sljGFocEE0aT0zQ9P96e49Xi+w=tYu%C2&nq0lVV*8QUI@i9V z)BKbly9oO`I)+{Qj?SIxJ33b!;yXIGJk35OMkJp09UZZ~`fcttRK1MjpV_sB{XbzWV($(we{-)j7`{YPb)M#ZVm#0JM$Gj-vH3epu05aCF+!dGnSb{A zESog{N9hFf1j$&V!MPGQ{_$WYO_&R z`bFcu@@($2s!%YJXw93we#`m!D#tU?xRYhBE5LQ?+RnS!)-&b(NVfH;>2lO^{V#C# zFY}T6rXR&bzm$>ZLfIDy^?u|l^!2wtaNXbOX{Z%L>ZNh_e)cZR^L3}Zzp=}Er)y{@ zni=mUVpRDsx!mirj&Z(~a1!&f)i{5w5oZmYg5MO{x53cHzOcCEo>fA!-UqU>HC zzaoeg!zZJ*{dA6ldmVd-I<@>>H=fCwhB&_C!O^bh(1eHn4IcQ0$+ z_G2S)uy-%z_kSgZR6R_t@*>@jhIlauDa7l)aGi z9-Dm}0(015aCa}dP`fr-?aaB69R7bOfWUp8uWIUrW&dg!zRFzvKIZD(%+;@B{@up> z`)u{D#Y*Ph3z@sm82<=f^)`9r*WXVgKxd-tCe0sL%U0Cl_7n6i(P zF{N|oTQ$ttTY}EFu6nuN{FLbRzQ@Ji+;?*a-Zu((tlJ-9 zwSL*h!r-&dbhJY0zu&peHhx3^*DncR!mB&&rVR#n=SivdeqMp|t-$R|IZlb?_p{8+ zCAfJw3!MXgwD$$LzGpKB3vzwW;rcGuYzn!DEn*H<;@r>Ne{2A+5C$HIAfJNwyz%-z>xB3a>@1C$MoYo z+eSZN8N-*1L+y9-HyGTP@FUUATzdi3ea@QXv)gMDSTmV}#<38N9S&pJKm@hjF^=On z%jB^Q%-yKzcN1+7czc^g4!`5106N7c@9g#EQa7TG?c2no54iQPxlvBm8O8=Ck!@A}O`b^gZ}~NR%DSs_E!Rq%62Q}h zmuu-3+?huLX_GbbM7TSentTMh`@tt z+;ytw%k}b-SLocio}Ggcmis#~A>Z^lCV^vE-$k64HA5kkcgDD1jWX}#de{(g=BjbL z^p8G|heGbR$XEs}1C}w5&r0J1CfA7OQ zMBHavMwz1x%}32S0bEWL^{~vRN|?U}aS!)QH*v4Eg?p{@xYt_Cz1E4`Yb~3uK=s5t z6t%f~t>X6vP)&69cxx&5hPF>x6EyD5^Aefo$@j4*s5x!LXr`|Rh&@uyxyO}-_fT_A zrsf<=y*Y|nQ#D%1HMhvQ=Xlrq1GtxnKE&hYUafl1uknHaPP>Tx;MHrQz_}-5_?I!N zargQB3b=3Hc;5*t!#MqyUCca2V%%1S+h(%R#aw?2^WV+Pe>XD!UB~?Q6z0DxncpsE zemjr(t#f_9vkE`GEP!7TgHpaSfXdMt_IowHK%c8GWzO`FTNjhdSh8Y^ZfjgUi#u+7 z*Q}us^_K^*g_w9q?F%?RLZiiw?P+5Aq5RqBc7^{B5&Dd`#%DYHh_(P~KI6u;xlxAP z@asLREAMxVCZE+c>f~J0wrw!@>^>!gVL+^rzTgB+(YnX2 zug#4z&N#R03WGb(ianyf5&L*nz4PyG{_E{mb_YAu^N`9GN91wZoc~WC@`CsO5od4X zT;F*!%lUhjJ|e$3gyPOHikVjybIcX5%pZU6vjM3Y@T~x@CFGmk^56IP|IcY$eLuB1 z-}zo_X!d)tUiqSb3t%Pj33dEN{{Q1+cCv|#&$K3ku0KUlM zU#lv)B7(>cw z)fsT^FB^7Q+cfT;kC5}8ocCQz*OueH-{pNDSp8dGo`oFWqSei{ged~R*_z?fj_RmiPSUM8Gwtunu zN4HD=Xx#SEPkZ=h)G*73VV7%(8lS5^{ro-r0=2K#@}=9wm&WJx#osG_&Na*SrP=K= z9kl-kvBN53*k!My@wwu6jPe*_%=V?*#h1oyAI&cY@D-x+^QI5OF1doneO0-rYNw{&e|3v`XiB?r!=f2VqO52K1+ER$p29|%5^G}{>;NGu{ z^P+5J(YUh*?v!iG9Gkyk?zEqK5amnf-Zz|Qzsb>z0X`7qbCapq9Ol1Eg|tZ`pO1QqmKK@F*(hE&jB#mbWL zjO}_|(rHpUn|v?0C5!~ylvo)ZclQbGCu&vwB=h<|aGg?h*14@Ij=r^sX6HDX?dB8o z@mc_%{3F+&FDM@xPy5~Se}*6GIK6-7=tFt-G)isE?nr(=z|qL6D5^R+PTL|Jt6`2+ z=FTfCrpm@k1|7d_yF9x(+kY}||2u%65H*h~4bZqxa@WqFTd#EI&svL*UCkDRg_KVH+ zGAZLp>e-fy;+6>O$y{e+{;G?qG9% zHaab{IV6TwC9)A1TSk%f$XKd3y`n z27}w>n@h9tA)@lj$?~QR2KUViV4gP*?7FG%H@eUI#HsC3ly20X_3^%0Y?sr=n=^hw zVsG`n1Bt3^{2Q^)tD|L0KesfVZa$VpoNqUjwd8ZnNuccUqN#%M#9_^e@`W7lzGdJ} zYCL&RUd>);_mCb#8p8Se{Rr2%wx} zE?b_TYsA^jfqL)nI*fcb2j>mupk4VhxqBU3w>i(UZguWz z)w}C*9@pi#T+1Gpb$RxCVa;Cd5=YZ;4&FFP%fP2fxguBlb`<2AB zs*}x)vT~VB^Iu-g^_Kh5#0$y0Z_7Xgk412H#996q84<<9s9#X zhl*$NEazT@QC@UTA(r%r`ho$^_y0`%5~Fi6bI)+x?9vA&%N$NoGK=y&6pT zmUr%bCVBZX$!)7p=_`R*EA{s*q+asfEsmoi`YPi2;e0u~ade3Byz7_{N>+w2+MgWH zrVR%FXXj)8st}GpF@&|>OvW>9F!&+o_v#RSLENwTGi@-qqnFG-WUuG0M@1NK* zzNyY_dx&dK0ndA(V}F~NP`+%gmo2|?J~mCp&^#8$y5T%D4J5FNIaPg60gmi0#Nw_Z zENCz0{-*&|zUA(7+44=@4p|-Db3N#-NiXYjJC2dFLU@JP_PEktj)llXl=D00*#Ag$ zKknAa=6c!kWAm`6D+je~$O}f zh?Xak`KR#Ph}I`k^G~sVjOcvAjc;?KtlT@${2wy^wAPbdH-zv7V(JNVJrUd2hyyBy z&Gm9=*ON5;P=8AZXA+yAO!kBD4Mh8ssr?}K_Y&KlbYt4wC@Xsr&3~r-(E8C3t|w-m zH2Xnpw-AA++!!|3%cbpyLyhk=V|Du{LwJC=^QqLaD)v3ZE*0PAdf9g}R(p4a@Dzck zljlhIcZlfIspm-SKOkzJcH`UJC@X8C=KrAQDDFr6naGrrkCbq2nuwr4uCcU5owa)8 z)105g9_3f(?%q6)d-FK=<}v01QB>^a-rUWDJ7&KHK%X(^*}W*5J{!V$#FX-<^Ev&= z_qf<+`CH|xMQ*cue)D;L6DWB6F!e3X`S0xu;Ys4ofzz0k@tu*USFXdAFQ{ zmw9HIy}A{n>G2RQBWk{t?|w6w<0HU+5!(ld<=^t^96@L_mwUUYW50!H`j%TqlV>^4 zfEaAOWk6JG7dCv3iAsZjbcb{c3?SVgAsy1)-A4go=mzx}9-SEi<`3=_*@r1ac2@3NBxE7_wTA^VxO9iw+k`Y0cj86Y z+xX9L@b7IoJ2TERDQb^hh0EB7_*r}xSCwQD*P|{N7bBqQ>di&oI_EA$GM*PGbv^!Y z{SpU4(lb;&M!*U2up)KhCyxK-J#X}tVl`g7Pcj@Q3c4SQZ*_*Z`H1(J>Gs`| z{BfrO!7$@~<9l#k}1 z`f)@G0}oH;{xpps5Fhtq>$U{R-|0l{VBTZ`bKYvDbw+nC|f`6UPU)$)bO;5uV}|K=?F*mC|EZ+xxHPHwVg#5V%; zY+^?6dy#lztGQPKC&cCoJ_O}!<^V@;YyYNbv5#ltLef>zjPY=AEhfhqGCjt!)w$mH zrNw-gX0<`7o%jf4eWwssKlbI@&&~ZNv;OEk1*;vwinF}w z6XMaKtkI*|>u?KES8?umI>KsaSK*tPPeXgfiZ3=r*s@(=G8{R4A2o9SfO_uF;=PNH z1kdc`N=6o<`&NQ`^;1_i4r*w-&QI6Ly9vMV>O7Xk=FmcHP!-S5=E`*__vF=_?iHd=? zf>z))XP{BG#z;@u@1Yg^%RdC$bhMG}+$E6XUs-+id!l5r$rsyqdBGhdG0+ zuNF1&SkAFM6YjIeBwCkBlvm|fcvgl#=2ld@j`j<~&^7X1!i6m@WN>CLDK<5%8)^UT3ttm#YoC=!>*GTa^T%{uB&oaLC+lWH9nP5A^^AY%3v*(JUAF@+(9j>3RWwbbkSonMNiyNy(GEcdalzaZ>^~#5bmhRu zMaJhEb792?kegOTV|I(4UWqjSLvHx$XzMs%tccv9NUgt1jhiJi-cVd~PO8|FIUx~_ z1=YC8Z#I9+NCYB6gRllCI$st{bpb;JJ*$b2UDL;eaK|m)x(`7`CuXMT8?#1^3f=p6O;QAmK_#7*|ft4Hzd~gYCUBj!GUKr;5O<@-gGZY_`&>`Iw=etgi|Ji1Y!ed`$G( zq7>#mTf{aL_8vH%RdpOl(n26f$At?$&9Nd4=^NKkWhfx|en*auNl~vuqsqq=(A5m+ zGQH9D4abdsHr#Pj6u@oFq8#Qv>rcGUrFZV?@!#oZO_Z-S8E*sx*v{(A@TM?wvqRK$ z|5ZUeu|J<#jR3|`g26Y%ea}jrjuL@q||%&i(xFisS|VRJheubC}n7 zC4VUTcmCxf+5bELfIL-F`o+xu3aGS;nD;m49YC}HRcT2{KmRPY?)qJ|jXPQ;$a-nT z?b+&owOOQ&$C7yo0pwhnD6xVGVerm0!;_+ad2wP4RJYr2zC*wP?m-ZqR!+D_a9&Lf z2cK8=6JJdQ^qq(D2&ZqHRRYR{iIcD*H_Bd9y!cn0VangS#lJgaQR{O2#4OTvLp>HU zKJBIdGS0pDE%AF#KuqE}X~J*d$h}MxIsY4wF8GyaIA=pTMdKE4;}z5)_dE{ZJDndr zE2{lKDkTD`)Gv%ptgf!t)dlY&je>w;UB)8Ie&^hI803F)uBOmJO@k%ZyzwxX?cNg}G`<&*@<#v3S`b)am#fwEZ_)zJk%`Lt*?r+CL)rDVMx!#rfJ9pvUwwhmC< zI6!&p0OgIlmz5J=RT~1W+F4oT2E|=bTFKvV@jueMrVy#3!7|InB?+la*~nK0bh*>F zvi&bXY+mM{GdK9&Z`e7>tb*&S2wh4gRKYvn7@h=|q3;>AR%M;|m?g2aR_(?6hKe?1 zP&93E1J#PYA>&g>^J3RWpM{^Q2mq5R029RT@*)(@*a-YF;SF>ZNu9ZhP+B*X!dpaDoi9Bb>uL6ln$$Vtk> zY$$0USNv9krT-SA0HRe780QN0{=27WzR6z{09!u*8w!A}zo;~m*ev`DplTFa5Uva~ zX#u=&TjqbMb<1)YSy2J~i1>}2;e|1!Bqa29&8BeK`aOdn0Fgt}B$glmk%I$_u39u> zb}i7X>hg0S5TSQdPD}o68_kbeAILe-UcZpIX7FYIh}z5l;T-ho_=hriG&(a8 z-dHrfA9Uj@p4A0fwIATQe{d7}6!4Y(BJGC2n6|L#iv2(@Yk0&~hPr1UeiJajJn@@= zU9vcF{O&!2|L#5eV>lio|CzysHHlrS+$^og8%$iGSMH@SPk`n^-Pvh?#PkFb69puu zX98XQ;(p?{;oV??I!pkTy_P|f z?cF>Y0(#fcHg%4`abr5Zo`cgu^6I!nR)5eAG~LvmT3e@hproS9@>@w_$G)SCleg%YDVh2^S&OVBiu#v?WO!O zuT#6jr3X^dy$D6^d&j#bZ5m5{Kn>pc)2iP>XFO%POa1WUdG@>{-S2VKV91YC24qse z%z57nE^-Wuen$@VNjER+RA-L(Zf_V%&8CKEBe1x#(s~_{bP*wa(AB|C{%zSbzH9SH z?tAUcx`BaiZX7SsQMkrAFjWp{-CMB8jI#%ib(JT&*GMb57~IT_C09-}r+Eb%MHUs; z)|XG9lP)et8jOizHC>w2FKTWCY_|RPX(D`(#D=+XRN%$Um1iVjR5kwMzWLxQYv|8< z6MJAKLjfXmoip~@Oz8kblHksQX1(OMS}>{bCArXrt~Je11CQLP(!M*Bc35aXQLo_I z4dm(l>Dh8h!+)F!;S2=zKj;@xQEx9RxYj<&)3dsk;4fLuGwlm1G%}xwH{^mG^qlKw z9PR!%9c($}^t=vVr^1+ncf!Go*M6v*>DckYCm-jPO=^NWSi8!U`UM5~>rA_Z(kq$b zHFu{3p=M*Iqy0t7ivH9Z$X>W2%4ux%8N*vU>$}`kxKbBms3oj~bhj@z9gD64>Mj%b zTY%DM;n{o>3-eW_NdxdUhd=%azvj;Ek;iPR9^)^b>~3-ObGtrnF1MxiQc{~izT9Z$ zB0nJ-6?DRz2$=P6-M<_N7V~EYDI=Ra!wx_p&4RI&uKcDjVkOf>0(W-#gNByo$N<^1 zAA#asb-r|yTb&Y#3via^;9}A_BMjOI(+(4eoW`%X7~pO>eMEFcR~AjFMTPSA3~_6X zx#Fig@zJ-EI2u1|+h*EDoE3CwN3Hmq4Lh&61E<+{4ZAbxKv=Z7a;oHMLPs#4WmQas zQIlV!d^bYr$&qY91?K}u-HJP>ZI0ur))BoWZbp57SP$&I!ZqZdcb8**(dU<0Q()Wn zqlsbF#5gNTA|J!5r}RRS<`sTMLO1N)#nr>ag>Ss8%WTl{F3yDSr`!IdzS8?m&6|+; zu!kFLA1~{}Sjg!ZgEHs}E`sDXR>gA>nQ@@4nD$__gQ^-%rNywqg?ZJUxmdzQuB^ir zuBGt$L=E>ARpF-y!v&Q^k^BB#T4EcY@7pVSu%d%hBQ8htOqnN<>rUr(Jix<^*A^LY zhmbMlCgh7);{wg3HN_%P{|8S9+-SQ}52GolpUCBt7lRPGHgU0b-_KgWGdz zl~Fo2ge3-ieX-o^E>0l9N_^^I!l0^nkb*G!d3hxT>pRD+EORk>JWpW4y3GQQy}hT4 z8eh>6>m*m2As72Vi}(JsVEknxj+co|l_Eb@cS{H+juy3MtVXZa=vVohsx^Op;&x@D-|h_U=;Nl*q;Xe&c39v(k6Kaow> z+7}ImMzHTY{KncOaWES;2>}rUX6*(j|M@3f`~FE{o%#f;%K=l4cl$j>`aN{hFJ3a9 z69|V1?_Q2Ft$A5Gh3_+p-S1~wtz@%f zWhT2Sr*MF~%E|jLn!`Gx1EI!abuV4>wYEzJP$Me$RtxN;AG&1~Frl^+1eEZ84(DUy zo?%u^CThseF8l*GB!y!`wTCXV5DtPdCZ6DNfj!h!b1?VGW+lN#4OkqP?Dkd{4RemqKqp?qR)Q|0m)-iEo9y zK^9u|C11_zgr!cJwDQmRQc*`lbNjZ9wnTFVUypm`>BN+bd1IDYtp@fAR_lS4-evEj zlua~ESt@At(R*@?VC+;V|0)7P;_S4l~MW5q0 zAsiNcH4C))2YIrC&f#PJ#y>;_)w0zTJ?|5Z@+j}-RE%4-<*^U*D9;GSbwnE$?o9Qp z>|&?c$E{te`XyJw#70{%r>y;W{OPWR!8MY~@iq|PZ+J@f3tzsL;N66M0dsCZ@Uypj z2Vl?1%b7E^XPyE2fc?@zG30jdUG zAH3BDJasgd-(o$CnTentf0{Y!OLzZ=i1X*k7oT!tDyY3b$Lw$|KiAwcB)T};Klv`} z=91QC-OJ89>+C7HEAfO=L)vOr?J#O>SMcmAWck5ME#P<#{PR)h+`>_9qsW`tP5Ki; zv3WV=#pShGnsM)TBHyJIYEfW`dvT)&(9;$qThQCH7deEle`LDX(Eqtd<+cJ0%z!) z`PbTcn8QB}8N^&p$=7`nKHCjCQYAehq|rW4?Ftm8k;BU8`z{4ZqKqPt);86Qv|5|xZ6qL8;4h0K8EmlFHiM>Se?-Vn*50Mg8)4~5 z%C#uR<1np;TZW@T8#Qbx0tI8ch%fBtR>DrqT*BE{?dPD9e{BWVL}P~nrRK_;Z67s< zOXPIoN^iAii3(H4U7$%TD0}O{6@pYcRr3ME|C_q z^EdeoV(bZpA0Qw=>RgW)&rv2?KC0nN+17&Ji-4fV_mpvWTKLtOQ?EGMBeGRXk0H}- zG0!_E$l`7`n#$I$J!XYy6PA37xus`VZ!U4xbq2jvv7!gQF6lyFggNCwrR3r{ZjMI~ zM{J~Gfx~2eUUG~1hI>9SCgGo#|NQjVRJM3DQt|WSIV0O=&RmhY(LK*B>ED!GczMce zkv?j*&PF~~QUOkbct=?!(=d5;cd{@_bEYju5)`uTU*Rx0tk6Ggn4Gp-J z8S!$HkAqyfJx*QaJEosLD{QNsDW{)fJ}KRwE(bQWG1T7VHH|MC2AAK`Ii`_umUKBM z+tDJp;w|sibb0Isa&^%usee2bL4?}W_B%>3zKsKGD0ug0BCF|VVyNR# z&=1oq?liU1`wjU9VHd=}o&iu@yw_vP7^+>e>ErNs_jlgGwn(8__9$jIHj(oib&XYFzhi_$tuw3yd(1Rm=&hC)(7N> zxtA<=Uw$xH=&Hq84)+S$DNrZfdo09nnmJo!6`na=ZIw3^|3nkLlU+SxE*+nHI zQ1xMst=ZyQJ_gJe3a2d4tH+z`)t7!OFo@NSdlwwseG)lcOj&|TjG5zt{dv;>Z{%i) zFG33!$a>BL-DuQz6GcnfSM10TDB4RgK8Qs@``1c~$v_c1#C_|4NQ$EMou<*SbmTc0 z0i5HYMpIHwkaL?-8XBlsp#^W?cVQNP3P&h~IkPmrY^_n>o$CL^06aVN#4N5u>TNh- zw)9DM7IJl_b0b9bhFx=@Rhw3B3N8{iWc;kyq!;jlDeY{kJt3IP$}Y^S^Fdc8j$T=j z`~F;lyivx@q%aW10Qk_dCV|ynV}urdj93x8yy7m_>a7O=AVds8Qm@jIp#9V1@MWxlCQI%(sNyXn=#GvhwBPQU-Gc8}huPr_H^S&-#%-4D*$G8v%Pu&Q9pp z@jZXi*Q}nPVqR3{hLa&esRbpvv>S`X%NSH+Gmz?tGrv^UrJj*D6k_!1@1UAjLyOSz5*HKbp*=`bM^mtI;G zy8$!Ju4{0%&K^+T9vEKIlE^(Z+VE%u_ktXT+qed(7q>3bs zfVju1u9_sSV7mHux0qXXCY>+xV_2f|P4A!xunPq;%R4Vxojtw1>q^Ep$J`E{u6x#? zVP&Co$a%w_szpv%xOvx^B`%VISZPF3f91p;Jj7&LbVF1=S8M$vFJox#gSor96b?Ta z-u#nTg13cCRTG3E0_E?^Zqm41CzB*=m7AYXi;n1B3C>d zD)n6bH_;E}R{r)aUbhasX8RxKg2MYj%c~T$W|=N&zk-UxjexG9GnPWX5wo1ozWqu@X-d<^#=gexjCF2%+zA+WI6g;&8-Rbo80V0k75sLAoI^5?PQ! z-I{Si$)PS_u_Kk&0ZRbUsI2b)E;HibDgd}Qdl4VLY<#7Ni%wE)R0+6Wv-trSqjbbg zd!_& zt9X*sfcQ8P$=JyGJKFm$-pkv7CMN%g4KfgR-A5#V{wIs#_^$D5Jt#ZrfV^O7%w) znds0^Zpls1g?neTpj8?F3HrT*Wy7qWXFq0{+jFv2;lNSWjJNt*{}XaZ+mv|rxD>-| zqDG>{5-$;rm6~_u%dp^#W?g_CwavHQ^#-sZjPuk`&Dl7@Ba(@$C!8_G_e9Ho4_2Nn zO7SMrKnw5&y@9#)FQ0Yl1JnjH>L{p(5{*;@4ra)NOYsYPlz`Wb+33WppFJumFAL`YM`+~ zstC~(xkNk4ZSfyEpRVwWAke9f66Z;=CoHI3e^nct8Js4i$sis zL0O~RHUWv`r_>!jS7ph!VR%KLzO^7T0oVSYo1T}24&pEufalq!in3?%Ypx?4B>89X z;cq9cHxR!Df$0c>1?A~m6+)GVI%Re?g-t1w7Npym!hqhzD^;Wlmr5ro#k1@p-P6{y zgD0o4#M+vgfRIUF0@@foT~tnE6&{~GF>5)S9E5vCDHRw$demwIs^VE`VGhzaLB%#A zz)A*?CkFM`5fp}%37kDZyEdkqQnsc>rqycYJ0Dtv5(Q(X6afrPv|Jq3U8ARYSsO)J z=l)mYY#?wQ1&fwR$vcA<+0UEAm=&S$VpjabZ zJwYli(mwzQkBKBSH2}FHwjeEye<{@U@72Bp?rLWRT=~RooU1d(aQs+4Shtp@9IvAP zd-YOU|1{uo-`TF@2tOF=sNbnJCsMp$FLcJUBLO~#*M}g!0D&I#_bp1r82<)mqeD8; z9?buV#ws{5TNw@nFEbnV-&j-E((s~p*h`;#VPp@h_GYz5{7A(bSE1%L~oq7$}GA`i?|7pI418r@v%Yz<^!)irB^H!R|33r_TAz5b#NLn@xj+qtObUxQB zv|Qz>uVl;2Phs_ce`Qs?hs*8H57?mSMZN%8;kij=&)wRjvs?U@4RS8QF%?2PgcsKG zKh^&qV*8L~z=F&+3ZO8VTHO@%YD8%EtW_u{JZ8`w4A{pvB{Tzwf66Y5mCm2zQDD!Ulf}SAZ0JZtYWN&RX`R z*bkP|2w^$)Kku?P$i>5l;4g||g|$8wDSv6;d3m9yX}U$2IwgVX_h2ude~#IgnHqtuWzfAFLhVIce#96ppL7K>}Ey>r9Lcw%i5=Q zGLLV)lQ&?lDgYhHP_;7d8Kl~WewQ`)t#=|rSWDG_*rigfVTU3Ncnz7zKImyfMBjo+ zVPGBr1aIp412qZgvq})&pus>|X^JI)*_7ItMTz8fL!>5>b(4kJO*oKs(XJ#b`P3kF z{pTE45SZ`=crw(Wn@G51H zmh#R!PxN4kg3BQ>@wMIl=QzWEt3JXzYOI1`6G8WxoE4-Wf`NDo3N4N$$u5RwgQ80yaR4d9x16%I9R2V_ZyX?62pTs z6!?w_y3R#<$W}fxjjd6R(m>f%B2YlWrSCCuI7?vFg5?u~$Qul43_b@=cq+W9^sO+q z*klpE6*z@SKYUE=FBnMaKfA%njOE#{2ZGM|chEV3pm+Wb`i{~Q!;-7QzunYIRYLQ) z-%kkl%>U)*UqNGalOgBYp96KIV*x{S8t7O$6w!?1-`r|wgm%65N2co|`XB?|0~7fC zH(+0}1A6IxTcMwbhhYHBsOnH(0Zd>EROGE&4?N$ETbT(r0DQ zl?ZF4D!v7zbF17~Lsbgjb27Iw0x4)39EE3=0@z`{{qT|515e{XN>zg?5Qmw{P4%Lo zO~jJvAPoamaei=cHfYCSb_2?cy}z>AF;PtN=tlY@iOE5!c+CymM<7%`m{zs0l~(&6 zy!mLw2w}||vDqe?1_93LiTDj+fqng&+L0BIu=Xti%_p?QNvZ2ElBPw4cE$I(4%1KY z60Z3I3%5Cr5`Objm;_J_qM5(>sd`?`=f(au=JA{i3O~)hwyFI$>wvzS{QqQzeGCZG z{YO~KM{A$CB$ZZud3I2cv;h{Oa+$Hx!s7&sh`Nd8kBG`Z9ZAFA2}ppYgh>v1YZsN? zPixK_giqR++)q1g8z9-N+xYf(0)7L~rsevZN$N^@2M_1 zWUa(EC7E6Y#Jn~2#^mx(ViQ>_f$uDeIf)UPg+@0#!Z~=*T=sXO{$I-Z;d3Mxq{aOn zl?~9-uHAlouggX(6sb@sVf`taAq9d)QH6uQB@Q)V-7aS^E$ zyi@{7ZLJbUCZ*cD{PjcOZ4`*TQzcN=$cL-(>&pcTXZ!VnpRmI4VkPHTH9uOgiALdZ zb3%dAvV}_FVsq~p$qmetV|8mPJD?lpGcG(T+=jhbsZfgnsiFK*?ahkw?H$`?3g6!0 zw1|8%{t%faB;dl0IKskCn)8>s`bs2K3L|>=5z3yqf6Nswg@05sVN6;ta8|QFB{4KX z!^TQI_n%;yJAqEnU)DY2)so3m1MV{eA4In!?GELBd}rMxE59bmtGPC^slfX8aLHLz zrigYxHYiTSwRn$d#i)U5U-?axJBKFs+1y}huON=5`7+F^O-%sY?L3e+XH*IH4;V zl*P<&!D4ut`BfH6I-V)s-1uz`cXqH2G<&rYn7y{{agRInT6lltz50{Jwf7+V0mZi) zc=E@|kkA+D3_@;Hfvxw4ZE+HS2ze!E@OG=e#X;%@o4u)A)#p4}6D7vB{{`Zx857#Q zyJA%Ev|2Dhx&Rau3kZH5>ps!r0lMjVC)cArL|}b2(5csqjp#!>3DWFy5i?d0_$giT zy0*E}zUIv+AUPxoR530Cq?Bhz$+un5v?xg-%eY4?GZlM#9tZ(@)^_k}u#GFQ5B?cn zfGb3+pIYt7(1Gu&q~$2>j_-Syl@o7o)9xg#s8}Ob z*jYdCfrGz=<{^H%V^rW(?0eH)SBOIsQ7cu__h2%usi|bI(O@z*O&BZf{T=x&)k9Rh z!S!-leVlcvD|5k&6vU@ooq@p7T9V^a7>P}+sTSmgum0D}^}8CKg1!kaXX9ug{+tNM zI62xK>8fLgw7{`&w!B}h{zMBR;~uO%Q|__NwTE;pab3wpDztY~-wu(0D(|?lX(fZ= zuxa%@RqQnP95hpyY>E^hN*L{qlF_?7h_}r z&IO?zk$JR-boXr>I{8zx$T*w^XBbapwM+m}cN1fXL^r7Wg#Pl6ZWF?=drP-*)`<$G z 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 diff --git a/build_helpers/TA_Lib-0.4.21-cp37-cp37m-win_amd64.whl b/build_helpers/TA_Lib-0.4.21-cp37-cp37m-win_amd64.whl new file mode 100644 index 0000000000000000000000000000000000000000..bccfd090f7b4b4ac71af170cb56ad16216cb475c GIT binary patch literal 489824 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