We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Minimal ROI value is not really optional, Bot Crash if minimal_roi have no 0 value
## The problem:
### The "minimal_roi" configuration object is suposed to be optionnal as explained in the doc:

> Most of the strategy files already include the optimal minimal_roi value. This parameter is optional. If you use it in the configuration file, it will take over the minimal_roi value from the strategy file.

Exemple:

>     minimal_roi = {
>         "40": 0.0,
>         "30": 0.01,
>         "20": 0.02,
>         "0": 0.04
>     }

**However, If I remove this parameter in the strategy and config I get this error:**

> 2019-03-06 21:56:56,768 - freqtrade - ERROR - Fatal exception!
> Traceback (most recent call last):
>   File "./freqtrade/main.py", line 45, in main
>     freqtrade = FreqtradeBot(config)
>   File "/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/freqtradebot.py", line 54, in __init__
>     self.strategy: IStrategy = StrategyResolver(self.config).strategy
>   File "/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/resolvers/strategy_resolver.py", line 77, in __init__
>     {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
> AttributeError: 'ShattaStrategy' object has no attribute 'minimal_roi'

 **Also, If I remove the 0 value key like that:**

>     minimal_roi = {
>         "40": 0.0,
>         "30": 0.01,
>         "20": 0.02,
>     }

**The bot start but crash when trying to check ROI with this error:**
>   File "/freqtrade/freqtrade/freqtradebot.py", line 699, in check_sell
>     should_sell = self.strategy.should_sell(trade, sell_rate, datetime.utcnow(), buy, sell)
>   File "/framework-dev/private/freqtrade/freqtrade/strategy/interface.py", line 273, in should_sell
>     if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
>   File "/framework-dev/private/freqtrade/freqtrade/strategy/**interface.py", line 349**, in min_roi_reached
>     **roi_entry = max(list(filter(lambda x: trade_dur >= x, self.minimal_roi.keys())))**

### Standalone Python test to reproduce:

> minimal_roi_keys = [0, 20, 30, 40, 50] ## Remove the 0 to trigger error
> trade_dur = 0.3486263354619344
> roi_entry = max(list(filter(lambda x: trade_dur >= x, minimal_roi_keys)))
> print(str(roi_entry))


### Solutions

1. The quick easy solution to temporaly deal with that issue is to make the minimal_roi parameter mandatory and make the "min_roi_reached" work with missing 0. Users who don't want to use ROI can simply put an unrealistic hight value.

2. The other is to make this parameter truly optionnal.


Minimal ROI value is not really optional, Bot Crash if minimal_roi have no 0 value
## The problem:
### The "minimal_roi" configuration object is suposed to be optionnal as explained in the doc:

> Most of the strategy files already include the optimal minimal_roi value. This parameter is optional. If you use it in the configuration file, it will take over the minimal_roi value from the strategy file.

Exemple:

>     minimal_roi = {
>         "40": 0.0,
>         "30": 0.01,
>         "20": 0.02,
>         "0": 0.04
>     }

**However, If I remove this parameter in the strategy and config I get this error:**

> 2019-03-06 21:56:56,768 - freqtrade - ERROR - Fatal exception!
> Traceback (most recent call last):
>   File "./freqtrade/main.py", line 45, in main
>     freqtrade = FreqtradeBot(config)
>   File "/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/freqtradebot.py", line 54, in __init__
>     self.strategy: IStrategy = StrategyResolver(self.config).strategy
>   File "/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/resolvers/strategy_resolver.py", line 77, in __init__
>     {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
> AttributeError: 'ShattaStrategy' object has no attribute 'minimal_roi'

 **Also, If I remove the 0 value key like that:**

>     minimal_roi = {
>         "40": 0.0,
>         "30": 0.01,
>         "20": 0.02,
>     }

**The bot start but crash when trying to check ROI with this error:**
>   File "/freqtrade/freqtrade/freqtradebot.py", line 699, in check_sell
>     should_sell = self.strategy.should_sell(trade, sell_rate, datetime.utcnow(), buy, sell)
>   File "/framework-dev/private/freqtrade/freqtrade/strategy/interface.py", line 273, in should_sell
>     if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
>   File "/framework-dev/private/freqtrade/freqtrade/strategy/**interface.py", line 349**, in min_roi_reached
>     **roi_entry = max(list(filter(lambda x: trade_dur >= x, self.minimal_roi.keys())))**

### Standalone Python test to reproduce:

> minimal_roi_keys = [0, 20, 30, 40, 50] ## Remove the 0 to trigger error
> trade_dur = 0.3486263354619344
> roi_entry = max(list(filter(lambda x: trade_dur >= x, minimal_roi_keys)))
> print(str(roi_entry))


### Solutions

1. The quick easy solution to temporaly deal with that issue is to make the minimal_roi parameter mandatory and make the "min_roi_reached" work with missing 0. Users who don't want to use ROI can simply put an unrealistic hight value.

2. The other is to make this parameter truly optionnal.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `freqtrade/resolvers/strategy_resolver.py`
Content:
```
1 # pragma pylint: disable=attribute-defined-outside-init
2 
3 """
4 This module load custom strategies
5 """
6 import logging
7 import tempfile
8 from base64 import urlsafe_b64decode
9 from collections import OrderedDict
10 from inspect import getfullargspec
11 from pathlib import Path
12 from typing import Dict, Optional
13 
14 from freqtrade import constants
15 from freqtrade.resolvers import IResolver
16 from freqtrade.strategy import import_strategy
17 from freqtrade.strategy.interface import IStrategy
18 
19 logger = logging.getLogger(__name__)
20 
21 
22 class StrategyResolver(IResolver):
23     """
24     This class contains all the logic to load custom strategy class
25     """
26 
27     __slots__ = ['strategy']
28 
29     def __init__(self, config: Optional[Dict] = None) -> None:
30         """
31         Load the custom class from config parameter
32         :param config: configuration dictionary or None
33         """
34         config = config or {}
35 
36         # Verify the strategy is in the configuration, otherwise fallback to the default strategy
37         strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY
38         self.strategy: IStrategy = self._load_strategy(strategy_name,
39                                                        config=config,
40                                                        extra_dir=config.get('strategy_path'))
41 
42         # make sure experimental dict is available
43         if 'experimental' not in config:
44             config['experimental'] = {}
45 
46         # Set attributes
47         # Check if we need to override configuration
48         # (Attribute name,                              default, experimental)
49         attributes = [("minimal_roi",                   None,    False),
50                       ("ticker_interval",               None,    False),
51                       ("stoploss",                      None,    False),
52                       ("trailing_stop",                 None,    False),
53                       ("trailing_stop_positive",        None,    False),
54                       ("trailing_stop_positive_offset", 0.0,     False),
55                       ("process_only_new_candles",      None,    False),
56                       ("order_types",                   None,    False),
57                       ("order_time_in_force",           None,    False),
58                       ("use_sell_signal",               False,   True),
59                       ("sell_profit_only",              False,   True),
60                       ("ignore_roi_if_buy_signal",      False,   True),
61                       ]
62         for attribute, default, experimental in attributes:
63             if experimental:
64                 self._override_attribute_helper(config['experimental'], attribute, default)
65             else:
66                 self._override_attribute_helper(config, attribute, default)
67 
68         # Loop this list again to have output combined
69         for attribute, _, exp in attributes:
70             if exp and attribute in config['experimental']:
71                 logger.info("Strategy using %s: %s", attribute, config['experimental'][attribute])
72             elif attribute in config:
73                 logger.info("Strategy using %s: %s", attribute, config[attribute])
74 
75         # Sort and apply type conversions
76         self.strategy.minimal_roi = OrderedDict(sorted(
77             {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
78             key=lambda t: t[0]))
79         self.strategy.stoploss = float(self.strategy.stoploss)
80 
81         self._strategy_sanity_validations()
82 
83     def _override_attribute_helper(self, config, attribute: str, default):
84         """
85         Override attributes in the strategy.
86         Prevalence:
87         - Configuration
88         - Strategy
89         - default (if not None)
90         """
91         if attribute in config:
92             setattr(self.strategy, attribute, config[attribute])
93             logger.info("Override strategy '%s' with value in config file: %s.",
94                         attribute, config[attribute])
95         elif hasattr(self.strategy, attribute):
96             config[attribute] = getattr(self.strategy, attribute)
97         # Explicitly check for None here as other "falsy" values are possible
98         elif default is not None:
99             setattr(self.strategy, attribute, default)
100             config[attribute] = default
101 
102     def _strategy_sanity_validations(self):
103         if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES):
104             raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
105                               f"Order-types mapping is incomplete.")
106 
107         if not all(k in self.strategy.order_time_in_force for k in constants.REQUIRED_ORDERTIF):
108             raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
109                               f"Order-time-in-force mapping is incomplete.")
110 
111     def _load_strategy(
112             self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy:
113         """
114         Search and loads the specified strategy.
115         :param strategy_name: name of the module to import
116         :param config: configuration for the strategy
117         :param extra_dir: additional directory to search for the given strategy
118         :return: Strategy instance or None
119         """
120         current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
121 
122         abs_paths = [
123             Path.cwd().joinpath('user_data/strategies'),
124             current_path,
125         ]
126 
127         if extra_dir:
128             # Add extra strategy directory on top of search paths
129             abs_paths.insert(0, Path(extra_dir).resolve())
130 
131         if ":" in strategy_name:
132             logger.info("loading base64 endocded strategy")
133             strat = strategy_name.split(":")
134 
135             if len(strat) == 2:
136                 temp = Path(tempfile.mkdtemp("freq", "strategy"))
137                 name = strat[0] + ".py"
138 
139                 temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8'))
140                 temp.joinpath("__init__.py").touch()
141 
142                 strategy_name = strat[0]
143 
144                 # register temp path with the bot
145                 abs_paths.insert(0, temp.resolve())
146 
147         for _path in abs_paths:
148             try:
149                 strategy = self._search_object(directory=_path, object_type=IStrategy,
150                                                object_name=strategy_name, kwargs={'config': config})
151                 if strategy:
152                     logger.info("Using resolved strategy %s from '%s'", strategy_name, _path)
153                     strategy._populate_fun_len = len(
154                         getfullargspec(strategy.populate_indicators).args)
155                     strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)
156                     strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)
157 
158                     return import_strategy(strategy, config=config)
159             except FileNotFoundError:
160                 logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd()))
161 
162         raise ImportError(
163             "Impossible to load Strategy '{}'. This class does not exist"
164             " or contains Python code errors".format(strategy_name)
165         )
166
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py
--- a/freqtrade/resolvers/strategy_resolver.py
+++ b/freqtrade/resolvers/strategy_resolver.py
@@ -46,18 +46,18 @@
         # Set attributes
         # Check if we need to override configuration
         # (Attribute name,                              default, experimental)
-        attributes = [("minimal_roi",                   None,    False),
-                      ("ticker_interval",               None,    False),
-                      ("stoploss",                      None,    False),
-                      ("trailing_stop",                 None,    False),
-                      ("trailing_stop_positive",        None,    False),
-                      ("trailing_stop_positive_offset", 0.0,     False),
-                      ("process_only_new_candles",      None,    False),
-                      ("order_types",                   None,    False),
-                      ("order_time_in_force",           None,    False),
-                      ("use_sell_signal",               False,   True),
-                      ("sell_profit_only",              False,   True),
-                      ("ignore_roi_if_buy_signal",      False,   True),
+        attributes = [("minimal_roi",                   {"0": 10.0}, False),
+                      ("ticker_interval",               None,        False),
+                      ("stoploss",                      None,        False),
+                      ("trailing_stop",                 None,        False),
+                      ("trailing_stop_positive",        None,        False),
+                      ("trailing_stop_positive_offset", 0.0,         False),
+                      ("process_only_new_candles",      None,        False),
+                      ("order_types",                   None,        False),
+                      ("order_time_in_force",           None,        False),
+                      ("use_sell_signal",               False,       True),
+                      ("sell_profit_only",              False,       True),
+                      ("ignore_roi_if_buy_signal",      False,       True),
                       ]
         for attribute, default, experimental in attributes:
             if experimental:

{"golden_diff": "diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py\n--- a/freqtrade/resolvers/strategy_resolver.py\n+++ b/freqtrade/resolvers/strategy_resolver.py\n@@ -46,18 +46,18 @@\n         # Set attributes\n         # Check if we need to override configuration\n         # (Attribute name,                              default, experimental)\n-        attributes = [(\"minimal_roi\",                   None,    False),\n-                      (\"ticker_interval\",               None,    False),\n-                      (\"stoploss\",                      None,    False),\n-                      (\"trailing_stop\",                 None,    False),\n-                      (\"trailing_stop_positive\",        None,    False),\n-                      (\"trailing_stop_positive_offset\", 0.0,     False),\n-                      (\"process_only_new_candles\",      None,    False),\n-                      (\"order_types\",                   None,    False),\n-                      (\"order_time_in_force\",           None,    False),\n-                      (\"use_sell_signal\",               False,   True),\n-                      (\"sell_profit_only\",              False,   True),\n-                      (\"ignore_roi_if_buy_signal\",      False,   True),\n+        attributes = [(\"minimal_roi\",                   {\"0\": 10.0}, False),\n+                      (\"ticker_interval\",               None,        False),\n+                      (\"stoploss\",                      None,        False),\n+                      (\"trailing_stop\",                 None,        False),\n+                      (\"trailing_stop_positive\",        None,        False),\n+                      (\"trailing_stop_positive_offset\", 0.0,         False),\n+                      (\"process_only_new_candles\",      None,        False),\n+                      (\"order_types\",                   None,        False),\n+                      (\"order_time_in_force\",           None,        False),\n+                      (\"use_sell_signal\",               False,       True),\n+                      (\"sell_profit_only\",              False,       True),\n+                      (\"ignore_roi_if_buy_signal\",      False,       True),\n                       ]\n         for attribute, default, experimental in attributes:\n             if experimental:\n", "issue": "Minimal ROI value is not really optional, Bot Crash if minimal_roi have no 0 value\n## The problem:\r\n### The \"minimal_roi\" configuration object is suposed to be optionnal as explained in the doc:\r\n\r\n> Most of the strategy files already include the optimal minimal_roi value. This parameter is optional. If you use it in the configuration file, it will take over the minimal_roi value from the strategy file.\r\n\r\nExemple:\r\n\r\n>     minimal_roi = {\r\n>         \"40\": 0.0,\r\n>         \"30\": 0.01,\r\n>         \"20\": 0.02,\r\n>         \"0\": 0.04\r\n>     }\r\n\r\n**However, If I remove this parameter in the strategy and config I get this error:**\r\n\r\n> 2019-03-06 21:56:56,768 - freqtrade - ERROR - Fatal exception!\r\n> Traceback (most recent call last):\r\n>   File \"./freqtrade/main.py\", line 45, in main\r\n>     freqtrade = FreqtradeBot(config)\r\n>   File \"/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/freqtradebot.py\", line 54, in __init__\r\n>     self.strategy: IStrategy = StrategyResolver(self.config).strategy\r\n>   File \"/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/resolvers/strategy_resolver.py\", line 77, in __init__\r\n>     {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),\r\n> AttributeError: 'ShattaStrategy' object has no attribute 'minimal_roi'\r\n\r\n **Also, If I remove the 0 value key like that:**\r\n\r\n>     minimal_roi = {\r\n>         \"40\": 0.0,\r\n>         \"30\": 0.01,\r\n>         \"20\": 0.02,\r\n>     }\r\n\r\n**The bot start but crash when trying to check ROI with this error:**\r\n>   File \"/freqtrade/freqtrade/freqtradebot.py\", line 699, in check_sell\r\n>     should_sell = self.strategy.should_sell(trade, sell_rate, datetime.utcnow(), buy, sell)\r\n>   File \"/framework-dev/private/freqtrade/freqtrade/strategy/interface.py\", line 273, in should_sell\r\n>     if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):\r\n>   File \"/framework-dev/private/freqtrade/freqtrade/strategy/**interface.py\", line 349**, in min_roi_reached\r\n>     **roi_entry = max(list(filter(lambda x: trade_dur >= x, self.minimal_roi.keys())))**\r\n\r\n### Standalone Python test to reproduce:\r\n\r\n> minimal_roi_keys = [0, 20, 30, 40, 50] ## Remove the 0 to trigger error\r\n> trade_dur = 0.3486263354619344\r\n> roi_entry = max(list(filter(lambda x: trade_dur >= x, minimal_roi_keys)))\r\n> print(str(roi_entry))\r\n\r\n\r\n### Solutions\r\n\r\n1. The quick easy solution to temporaly deal with that issue is to make the minimal_roi parameter mandatory and make the \"min_roi_reached\" work with missing 0. Users who don't want to use ROI can simply put an unrealistic hight value.\r\n\r\n2. The other is to make this parameter truly optionnal.\r\n\r\n\nMinimal ROI value is not really optional, Bot Crash if minimal_roi have no 0 value\n## The problem:\r\n### The \"minimal_roi\" configuration object is suposed to be optionnal as explained in the doc:\r\n\r\n> Most of the strategy files already include the optimal minimal_roi value. This parameter is optional. If you use it in the configuration file, it will take over the minimal_roi value from the strategy file.\r\n\r\nExemple:\r\n\r\n>     minimal_roi = {\r\n>         \"40\": 0.0,\r\n>         \"30\": 0.01,\r\n>         \"20\": 0.02,\r\n>         \"0\": 0.04\r\n>     }\r\n\r\n**However, If I remove this parameter in the strategy and config I get this error:**\r\n\r\n> 2019-03-06 21:56:56,768 - freqtrade - ERROR - Fatal exception!\r\n> Traceback (most recent call last):\r\n>   File \"./freqtrade/main.py\", line 45, in main\r\n>     freqtrade = FreqtradeBot(config)\r\n>   File \"/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/freqtradebot.py\", line 54, in __init__\r\n>     self.strategy: IStrategy = StrategyResolver(self.config).strategy\r\n>   File \"/home/axel/dev/shatta/framework-dev/private/freqtrade/freqtrade/resolvers/strategy_resolver.py\", line 77, in __init__\r\n>     {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),\r\n> AttributeError: 'ShattaStrategy' object has no attribute 'minimal_roi'\r\n\r\n **Also, If I remove the 0 value key like that:**\r\n\r\n>     minimal_roi = {\r\n>         \"40\": 0.0,\r\n>         \"30\": 0.01,\r\n>         \"20\": 0.02,\r\n>     }\r\n\r\n**The bot start but crash when trying to check ROI with this error:**\r\n>   File \"/freqtrade/freqtrade/freqtradebot.py\", line 699, in check_sell\r\n>     should_sell = self.strategy.should_sell(trade, sell_rate, datetime.utcnow(), buy, sell)\r\n>   File \"/framework-dev/private/freqtrade/freqtrade/strategy/interface.py\", line 273, in should_sell\r\n>     if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):\r\n>   File \"/framework-dev/private/freqtrade/freqtrade/strategy/**interface.py\", line 349**, in min_roi_reached\r\n>     **roi_entry = max(list(filter(lambda x: trade_dur >= x, self.minimal_roi.keys())))**\r\n\r\n### Standalone Python test to reproduce:\r\n\r\n> minimal_roi_keys = [0, 20, 30, 40, 50] ## Remove the 0 to trigger error\r\n> trade_dur = 0.3486263354619344\r\n> roi_entry = max(list(filter(lambda x: trade_dur >= x, minimal_roi_keys)))\r\n> print(str(roi_entry))\r\n\r\n\r\n### Solutions\r\n\r\n1. The quick easy solution to temporaly deal with that issue is to make the minimal_roi parameter mandatory and make the \"min_roi_reached\" work with missing 0. Users who don't want to use ROI can simply put an unrealistic hight value.\r\n\r\n2. The other is to make this parameter truly optionnal.\r\n\r\n\n", "before_files": [{"content": "# pragma pylint: disable=attribute-defined-outside-init\n\n\"\"\"\nThis module load custom strategies\n\"\"\"\nimport logging\nimport tempfile\nfrom base64 import urlsafe_b64decode\nfrom collections import OrderedDict\nfrom inspect import getfullargspec\nfrom pathlib import Path\nfrom typing import Dict, Optional\n\nfrom freqtrade import constants\nfrom freqtrade.resolvers import IResolver\nfrom freqtrade.strategy import import_strategy\nfrom freqtrade.strategy.interface import IStrategy\n\nlogger = logging.getLogger(__name__)\n\n\nclass StrategyResolver(IResolver):\n    \"\"\"\n    This class contains all the logic to load custom strategy class\n    \"\"\"\n\n    __slots__ = ['strategy']\n\n    def __init__(self, config: Optional[Dict] = None) -> None:\n        \"\"\"\n        Load the custom class from config parameter\n        :param config: configuration dictionary or None\n        \"\"\"\n        config = config or {}\n\n        # Verify the strategy is in the configuration, otherwise fallback to the default strategy\n        strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY\n        self.strategy: IStrategy = self._load_strategy(strategy_name,\n                                                       config=config,\n                                                       extra_dir=config.get('strategy_path'))\n\n        # make sure experimental dict is available\n        if 'experimental' not in config:\n            config['experimental'] = {}\n\n        # Set attributes\n        # Check if we need to override configuration\n        # (Attribute name,                              default, experimental)\n        attributes = [(\"minimal_roi\",                   None,    False),\n                      (\"ticker_interval\",               None,    False),\n                      (\"stoploss\",                      None,    False),\n                      (\"trailing_stop\",                 None,    False),\n                      (\"trailing_stop_positive\",        None,    False),\n                      (\"trailing_stop_positive_offset\", 0.0,     False),\n                      (\"process_only_new_candles\",      None,    False),\n                      (\"order_types\",                   None,    False),\n                      (\"order_time_in_force\",           None,    False),\n                      (\"use_sell_signal\",               False,   True),\n                      (\"sell_profit_only\",              False,   True),\n                      (\"ignore_roi_if_buy_signal\",      False,   True),\n                      ]\n        for attribute, default, experimental in attributes:\n            if experimental:\n                self._override_attribute_helper(config['experimental'], attribute, default)\n            else:\n                self._override_attribute_helper(config, attribute, default)\n\n        # Loop this list again to have output combined\n        for attribute, _, exp in attributes:\n            if exp and attribute in config['experimental']:\n                logger.info(\"Strategy using %s: %s\", attribute, config['experimental'][attribute])\n            elif attribute in config:\n                logger.info(\"Strategy using %s: %s\", attribute, config[attribute])\n\n        # Sort and apply type conversions\n        self.strategy.minimal_roi = OrderedDict(sorted(\n            {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),\n            key=lambda t: t[0]))\n        self.strategy.stoploss = float(self.strategy.stoploss)\n\n        self._strategy_sanity_validations()\n\n    def _override_attribute_helper(self, config, attribute: str, default):\n        \"\"\"\n        Override attributes in the strategy.\n        Prevalence:\n        - Configuration\n        - Strategy\n        - default (if not None)\n        \"\"\"\n        if attribute in config:\n            setattr(self.strategy, attribute, config[attribute])\n            logger.info(\"Override strategy '%s' with value in config file: %s.\",\n                        attribute, config[attribute])\n        elif hasattr(self.strategy, attribute):\n            config[attribute] = getattr(self.strategy, attribute)\n        # Explicitly check for None here as other \"falsy\" values are possible\n        elif default is not None:\n            setattr(self.strategy, attribute, default)\n            config[attribute] = default\n\n    def _strategy_sanity_validations(self):\n        if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES):\n            raise ImportError(f\"Impossible to load Strategy '{self.strategy.__class__.__name__}'. \"\n                              f\"Order-types mapping is incomplete.\")\n\n        if not all(k in self.strategy.order_time_in_force for k in constants.REQUIRED_ORDERTIF):\n            raise ImportError(f\"Impossible to load Strategy '{self.strategy.__class__.__name__}'. \"\n                              f\"Order-time-in-force mapping is incomplete.\")\n\n    def _load_strategy(\n            self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy:\n        \"\"\"\n        Search and loads the specified strategy.\n        :param strategy_name: name of the module to import\n        :param config: configuration for the strategy\n        :param extra_dir: additional directory to search for the given strategy\n        :return: Strategy instance or None\n        \"\"\"\n        current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()\n\n        abs_paths = [\n            Path.cwd().joinpath('user_data/strategies'),\n            current_path,\n        ]\n\n        if extra_dir:\n            # Add extra strategy directory on top of search paths\n            abs_paths.insert(0, Path(extra_dir).resolve())\n\n        if \":\" in strategy_name:\n            logger.info(\"loading base64 endocded strategy\")\n            strat = strategy_name.split(\":\")\n\n            if len(strat) == 2:\n                temp = Path(tempfile.mkdtemp(\"freq\", \"strategy\"))\n                name = strat[0] + \".py\"\n\n                temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8'))\n                temp.joinpath(\"__init__.py\").touch()\n\n                strategy_name = strat[0]\n\n                # register temp path with the bot\n                abs_paths.insert(0, temp.resolve())\n\n        for _path in abs_paths:\n            try:\n                strategy = self._search_object(directory=_path, object_type=IStrategy,\n                                               object_name=strategy_name, kwargs={'config': config})\n                if strategy:\n                    logger.info(\"Using resolved strategy %s from '%s'\", strategy_name, _path)\n                    strategy._populate_fun_len = len(\n                        getfullargspec(strategy.populate_indicators).args)\n                    strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)\n                    strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)\n\n                    return import_strategy(strategy, config=config)\n            except FileNotFoundError:\n                logger.warning('Path \"%s\" does not exist', _path.relative_to(Path.cwd()))\n\n        raise ImportError(\n            \"Impossible to load Strategy '{}'. This class does not exist\"\n            \" or contains Python code errors\".format(strategy_name)\n        )\n", "path": "freqtrade/resolvers/strategy_resolver.py"}], "after_files": [{"content": "# pragma pylint: disable=attribute-defined-outside-init\n\n\"\"\"\nThis module load custom strategies\n\"\"\"\nimport logging\nimport tempfile\nfrom base64 import urlsafe_b64decode\nfrom collections import OrderedDict\nfrom inspect import getfullargspec\nfrom pathlib import Path\nfrom typing import Dict, Optional\n\nfrom freqtrade import constants\nfrom freqtrade.resolvers import IResolver\nfrom freqtrade.strategy import import_strategy\nfrom freqtrade.strategy.interface import IStrategy\n\nlogger = logging.getLogger(__name__)\n\n\nclass StrategyResolver(IResolver):\n    \"\"\"\n    This class contains all the logic to load custom strategy class\n    \"\"\"\n\n    __slots__ = ['strategy']\n\n    def __init__(self, config: Optional[Dict] = None) -> None:\n        \"\"\"\n        Load the custom class from config parameter\n        :param config: configuration dictionary or None\n        \"\"\"\n        config = config or {}\n\n        # Verify the strategy is in the configuration, otherwise fallback to the default strategy\n        strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY\n        self.strategy: IStrategy = self._load_strategy(strategy_name,\n                                                       config=config,\n                                                       extra_dir=config.get('strategy_path'))\n\n        # make sure experimental dict is available\n        if 'experimental' not in config:\n            config['experimental'] = {}\n\n        # Set attributes\n        # Check if we need to override configuration\n        # (Attribute name,                              default, experimental)\n        attributes = [(\"minimal_roi\",                   {\"0\": 10.0}, False),\n                      (\"ticker_interval\",               None,        False),\n                      (\"stoploss\",                      None,        False),\n                      (\"trailing_stop\",                 None,        False),\n                      (\"trailing_stop_positive\",        None,        False),\n                      (\"trailing_stop_positive_offset\", 0.0,         False),\n                      (\"process_only_new_candles\",      None,        False),\n                      (\"order_types\",                   None,        False),\n                      (\"order_time_in_force\",           None,        False),\n                      (\"use_sell_signal\",               False,       True),\n                      (\"sell_profit_only\",              False,       True),\n                      (\"ignore_roi_if_buy_signal\",      False,       True),\n                      ]\n        for attribute, default, experimental in attributes:\n            if experimental:\n                self._override_attribute_helper(config['experimental'], attribute, default)\n            else:\n                self._override_attribute_helper(config, attribute, default)\n\n        # Loop this list again to have output combined\n        for attribute, _, exp in attributes:\n            if exp and attribute in config['experimental']:\n                logger.info(\"Strategy using %s: %s\", attribute, config['experimental'][attribute])\n            elif attribute in config:\n                logger.info(\"Strategy using %s: %s\", attribute, config[attribute])\n\n        # Sort and apply type conversions\n        self.strategy.minimal_roi = OrderedDict(sorted(\n            {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),\n            key=lambda t: t[0]))\n        self.strategy.stoploss = float(self.strategy.stoploss)\n\n        self._strategy_sanity_validations()\n\n    def _override_attribute_helper(self, config, attribute: str, default):\n        \"\"\"\n        Override attributes in the strategy.\n        Prevalence:\n        - Configuration\n        - Strategy\n        - default (if not None)\n        \"\"\"\n        if attribute in config:\n            setattr(self.strategy, attribute, config[attribute])\n            logger.info(\"Override strategy '%s' with value in config file: %s.\",\n                        attribute, config[attribute])\n        elif hasattr(self.strategy, attribute):\n            config[attribute] = getattr(self.strategy, attribute)\n        # Explicitly check for None here as other \"falsy\" values are possible\n        elif default is not None:\n            setattr(self.strategy, attribute, default)\n            config[attribute] = default\n\n    def _strategy_sanity_validations(self):\n        if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES):\n            raise ImportError(f\"Impossible to load Strategy '{self.strategy.__class__.__name__}'. \"\n                              f\"Order-types mapping is incomplete.\")\n\n        if not all(k in self.strategy.order_time_in_force for k in constants.REQUIRED_ORDERTIF):\n            raise ImportError(f\"Impossible to load Strategy '{self.strategy.__class__.__name__}'. \"\n                              f\"Order-time-in-force mapping is incomplete.\")\n\n    def _load_strategy(\n            self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy:\n        \"\"\"\n        Search and loads the specified strategy.\n        :param strategy_name: name of the module to import\n        :param config: configuration for the strategy\n        :param extra_dir: additional directory to search for the given strategy\n        :return: Strategy instance or None\n        \"\"\"\n        current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()\n\n        abs_paths = [\n            Path.cwd().joinpath('user_data/strategies'),\n            current_path,\n        ]\n\n        if extra_dir:\n            # Add extra strategy directory on top of search paths\n            abs_paths.insert(0, Path(extra_dir).resolve())\n\n        if \":\" in strategy_name:\n            logger.info(\"loading base64 endocded strategy\")\n            strat = strategy_name.split(\":\")\n\n            if len(strat) == 2:\n                temp = Path(tempfile.mkdtemp(\"freq\", \"strategy\"))\n                name = strat[0] + \".py\"\n\n                temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8'))\n                temp.joinpath(\"__init__.py\").touch()\n\n                strategy_name = strat[0]\n\n                # register temp path with the bot\n                abs_paths.insert(0, temp.resolve())\n\n        for _path in abs_paths:\n            try:\n                strategy = self._search_object(directory=_path, object_type=IStrategy,\n                                               object_name=strategy_name, kwargs={'config': config})\n                if strategy:\n                    logger.info(\"Using resolved strategy %s from '%s'\", strategy_name, _path)\n                    strategy._populate_fun_len = len(\n                        getfullargspec(strategy.populate_indicators).args)\n                    strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)\n                    strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)\n\n                    return import_strategy(strategy, config=config)\n            except FileNotFoundError:\n                logger.warning('Path \"%s\" does not exist', _path.relative_to(Path.cwd()))\n\n        raise ImportError(\n            \"Impossible to load Strategy '{}'. This class does not exist\"\n            \" or contains Python code errors\".format(strategy_name)\n        )\n", "path": "freqtrade/resolvers/strategy_resolver.py"}]}