Repository: freqtrade/freqtrade

Issue:
binanceus - errors with get-orderbooks
## Step 1: Have you search for this issue before posting it?

yes

## Step 2: Describe your environment

  * Operating system: ____ MacOSX
  * Python Version: _____ 3.7
  * CCXT version: _____1.18.1225
  * Branch:  Develop
  * Last Commit ID: 4228137dff46d0e41d613a2c85ffac3060401976

  
## Step 3: Describe the problem:
*Explain the problem you have encountered*

### Steps to reproduce:

  1. Enter values for binance us exchange
  2. Run bot with any strategy n configs
  3. It fails
  
### Observed Results:

  * What happened?
Bot ran with custom strategy on binanceus exchange, and bought coins but while selling order and trying to get order_books i am seeing following error:

2019-10-13 20:49:48,640 - freqtrade.exchange.exchange - WARNING - get_order_book() returned exception: "Could not get order book due to InvalidOrder. Message: binanceus {"code":-1100,"msg":"Illegal characters found in parameter 'limit'; legal range is '5, 10, 20, 50, 100, 500, 1000, 5000'."}"
  * What did you expect to happen?
i debugged and saw values are not in limit_range
getting 'LIMIT' value as '9' or '1'

Similar thing happens while buying coins as well.

### Relevant code exceptions or logs:

  ```
2019-10-13 20:49:48,210 - freqtrade.exchange.exchange - WARNING - get_order_book() returned exception: "Could not get order book due to InvalidOrder. Message: binanceus {"code":-1100,"msg":"Illegal characters found in parameter 'limit'; legal range is '5, 10, 20, 50, 100, 500, 1000, 5000'."}"
2019-10-13 20:49:48,210 - freqtrade.exchange.exchange - WARNING - retrying get_order_book() still for 3 times
2019-10-13 20:49:48,640 - freqtrade.exchange.exchange - WARNING - get_order_book() returned exception: "Could not get order book due to InvalidOrder. Message: binanceus {"code":-1100,"msg":"Illegal characters found in parameter 'limit'; legal range is '5, 10, 20, 50, 100, 500, 1000, 5000'."}"
2019-10-13 20:49:48,641 - freqtrade.exchange.exchange - WARNING - retrying get_order_book() still for 2 times
2019-10-13 20:49:49,139 - freqtrade.exchange.exchange - WARNING - get_order_book() returned exception: "Could not get order book due to InvalidOrder. Message: binanceus {"code":-1100,"msg":"Illegal characters found in parameter 'limit'; legal range is '5, 10, 20, 50, 100, 500, 1000, 5000'."}"
2019-10-13 20:49:49,139 - freqtrade.exchange.exchange - WARNING - retrying get_order_book() still for 1 times
2019-10-13 20:49:49,715 - freqtrade.exchange.exchange - WARNING - get_order_book() returned exception: "Could not get order book due to InvalidOrder. Message: binanceus {"code":-1100,"msg":"Illegal characters found in parameter 'limit'; legal range is '5, 10, 20, 50, 100, 500, 1000, 5000'."}"
2019-10-13 20:49:49,715 - freqtrade.exchange.exchange - WARNING - retrying get_order_book() still for 0 times
2019-10-13 20:49:50,150 - freqtrade.exchange.exchange - WARNING - get_order_book() returned exception: "Could not get order book due to InvalidOrder. Message: binanceus {"code":-1100,"msg":"Illegal characters found in parameter 'limit'; legal range is '5, 10, 20, 50, 100, 500, 1000, 5000'."}"
2019-10-13 20:49:50,150 - freqtrade.exchange.exchange - WARNING - Giving up retrying: get_order_book()
2019-10-13 20:49:50,150 - freqtrade.worker - WARNING - Error: Could not get order book due to InvalidOrder. Message: binanceus {"code":-1100,"msg":"Illegal characters found in parameter 'limit'; legal range is '5, 10, 20, 50, 100, 500, 1000, 5000'."}, retrying in 30 seconds...
  ```



Discussion:
The problem seems to be the use of binanceus instead of binance, since it's not using the binance exhange. 
Currently, binanceus (and probably binanceje?) will work fine except for orderbook and stoploss-on-exchange functions, which will not work at all.


it's the problem we pointed out in the binanceus pr https://github.com/freqtrade/freqtrade/pull/2274#pullrequestreview-290474192 ...

@hroff-1902 what do you think, is it better to subclass binance to binanceus (and whatever else exists) - or create an exchange-class mapping (`{'binanceus: 'binance', ...}`) to avoid having tons of empty subclasses just to have the naming correct?

personally, i would use the mapping approach ... 
> @hroff-1902 what do you think, is it better to subclass binance to binanceus (and whatever else exists) - or create an exchange-class mapping (`{'binanceus: 'binance', ...}`) to avoid having tons of empty subclasses just to have the naming correct?
> 
> personally, i would use the mapping approach ...

Nice point. Right in the meantime, I'm working on #2370 to make it working (showing pairs) well for all exchanges that are not marked bad. Currently, there are only 5 of them left, one of them is Bibox, for which the solution is to set `has['fetchCurrencies'] = False` for `ccxt.bibox` (see https://github.com/ccxt/ccxt/issues/3879#issuecomment-480520870). The most obvious way to do it is to set this value in the `__init__` somehow or in a callback function, which is called in the right place from the Exchanges (BaseExchange) `__init__`. This will be (almost) one-line function and the class will be almost empty class. There can be other almost empty or empty classes... Mapping implements approach, which is suitable in one case (this one), but it will be needed to create subclasses in other situations, for some other exchanges.

And there will not be 'tons' of them, just a few...

My conclusion and my opinion: let's create subclasses. This is more straight and generic, and we'll be able to refactor it and reduce in the future if this will be needed.

Let's not merge this with issues from #2370 ... - but i think the answer for #2370 (assuming i understood this correctly) should be `_ft_has`.

What i don't like about this is that it would require us to subclass an exchange-specific subclass (`class Binanceus(Binance)` ) ... building such trees can be dangerous:
assume binanceus uses "something" that `Exchange` has - but `Binance` does overwrite ... 

Now i don't expect this to happen in this case, but it could be for other cases as you mention above.
Referring to #2370 was just an illustration that we'll be required to create (almost) empty subclasses due to other reasons, where mapping does not help and a simple one-line patch is needed...

> What i don't like about this is that it would require us to subclass an exchange-specific subclass (`class Binanceus(Binance)` ) ... building such trees can be dangerous:
> assume binanceus uses "something" that `Exchange` has - but `Binance` does overwrite ...

This can also be resolved well with proper hierarchy of classes/mixins if needed, I guess...

But let's make it with mapping and will have a look at the result, it will not be hard to make it other way in the future if this will be needed...

> This can also be resolved well with proper hierarchy of classes/mixins if needed, I guess...

Please open an issue to discuss your plans regarding this (and don't spend too much time working on it for now - since Mixins create a problem for stability i will not outline here).


Resolving patch:
diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py
--- a/freqtrade/exchange/__init__.py
+++ b/freqtrade/exchange/__init__.py
@@ -1,4 +1,4 @@
-from freqtrade.exchange.exchange import Exchange  # noqa: F401
+from freqtrade.exchange.exchange import Exchange, MAP_EXCHANGE_CHILDCLASS  # noqa: F401
 from freqtrade.exchange.exchange import (get_exchange_bad_reason,  # noqa: F401
                                          is_exchange_bad,
                                          is_exchange_known_ccxt,
diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py
--- a/freqtrade/exchange/exchange.py
+++ b/freqtrade/exchange/exchange.py
@@ -102,6 +102,11 @@
         ], "Does not provide timeframes. ccxt fetchOHLCV: emulated"),
     }
 
+MAP_EXCHANGE_CHILDCLASS = {
+    'binanceus': 'binance',
+    'binanceje': 'binance',
+}
+
 
 def retrier_async(f):
     async def wrapper(*args, **kwargs):
diff --git a/freqtrade/resolvers/exchange_resolver.py b/freqtrade/resolvers/exchange_resolver.py
--- a/freqtrade/resolvers/exchange_resolver.py
+++ b/freqtrade/resolvers/exchange_resolver.py
@@ -3,7 +3,7 @@
 """
 import logging
 
-from freqtrade.exchange import Exchange
+from freqtrade.exchange import Exchange, MAP_EXCHANGE_CHILDCLASS
 import freqtrade.exchange as exchanges
 from freqtrade.resolvers import IResolver
 
@@ -22,6 +22,8 @@ def __init__(self, exchange_name: str, config: dict) -> None:
         Load the custom class from config parameter
         :param config: configuration dictionary
         """
+        # Map exchange name to avoid duplicate classes for identical exchanges
+        exchange_name = MAP_EXCHANGE_CHILDCLASS.get(exchange_name, exchange_name)
         exchange_name = exchange_name.title()
         try:
             self.exchange = self._load_exchange(exchange_name, kwargs={'config': config})


Test patch:
diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py
--- a/tests/exchange/test_exchange.py
+++ b/tests/exchange/test_exchange.py
@@ -142,6 +142,12 @@ def test_exchange_resolver(default_conf, mocker, caplog):
     assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
                           caplog)
 
+    # Test mapping
+    exchange = ExchangeResolver('binanceus', default_conf).exchange
+    assert isinstance(exchange, Exchange)
+    assert isinstance(exchange, Binance)
+    assert not isinstance(exchange, Kraken)
+
 
 def test_validate_order_time_in_force(default_conf, mocker, caplog):
     caplog.set_level(logging.INFO)