From acbfe91f131ffa5854436bff275d92d2fce2eb21 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Thu, 31 May 2018 23:44:17 -0700 Subject: [PATCH 1/4] Allow EUR / USD as stake_currency It will enable to trade with FIAT on exhanges like GDAX or Kraken. --- freqtrade/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 7731ea610..82794774b 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -31,7 +31,7 @@ CONF_SCHEMA = { 'properties': { 'max_open_trades': {'type': 'integer', 'minimum': 0}, 'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())}, - 'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']}, + 'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT', 'EUR', 'USD']}, 'stake_amount': {'type': 'number', 'minimum': 0.0005}, 'fiat_display_currency': {'type': 'string', 'enum': ['AUD', 'BRL', 'CAD', 'CHF', 'CLP', 'CNY', 'CZK', 'DKK', From c9e49ed7b40a541eb9d2e385d586c73eb6f22b3c Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Thu, 31 May 2018 23:45:35 -0700 Subject: [PATCH 2/4] Sort ticker_history CCXT does not sort the ticker history from exchanges. Bittrex and Binance are sorted ASC (oldest first, newest last) when GDAX is sorted DESC (newest first, oldest last). Because of that the get_ticker_history() fall in a very long loop when the tickers are sorted DESC. Means it downloads more than needed. This commit enable exhanges like GDAX and unify the ticker_history list across all exchanges. --- freqtrade/exchange/__init__.py | 5 ++ freqtrade/tests/exchange/test_exchange.py | 72 +++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 109e3f7b2..7be323329 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -294,6 +294,11 @@ def get_ticker_history(pair: str, tick_interval: str, since_ms: Optional[int] = while not since_ms or since_ms < till_time_ms: data_part = _API.fetch_ohlcv(pair, timeframe=tick_interval, since=since_ms) + # Because some exchange sort Tickers ASC and other DESC. + # Ex: Bittrex returns a list of tickers ASC (oldest first, newest last) + # when GDAX returns a list of tickers DESC (newest first, oldest last) + data_part = sorted(data_part, key=lambda x: x[0]) + if not data_part: break diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index a506a8416..56812c75e 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -393,6 +393,78 @@ def test_get_ticker_history(default_conf, mocker): get_ticker_history('EFGH/BTC', default_conf['ticker_interval']) +def test_get_ticker_history_sort(default_conf, mocker): + api_mock = MagicMock() + + # GDAX use-case (real data from GDAX) + # This ticker history is ordered DESC (newest first, oldest last) + tick = [ + [1527833100000, 0.07666, 0.07671, 0.07666, 0.07668, 16.65244264], + [1527832800000, 0.07662, 0.07666, 0.07662, 0.07666, 1.30051526], + [1527832500000, 0.07656, 0.07661, 0.07656, 0.07661, 12.034778840000001], + [1527832200000, 0.07658, 0.07658, 0.07655, 0.07656, 0.59780186], + [1527831900000, 0.07658, 0.07658, 0.07658, 0.07658, 1.76278136], + [1527831600000, 0.07658, 0.07658, 0.07658, 0.07658, 2.22646521], + [1527831300000, 0.07655, 0.07657, 0.07655, 0.07657, 1.1753], + [1527831000000, 0.07654, 0.07654, 0.07651, 0.07651, 0.8073060299999999], + [1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687], + [1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867] + ] + type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) + api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick)) + mocker.patch('freqtrade.exchange._API', api_mock) + + # Test the ticker history sort + ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval']) + assert ticks[0][0] == 1527830400000 + assert ticks[0][1] == 0.07649 + assert ticks[0][2] == 0.07651 + assert ticks[0][3] == 0.07649 + assert ticks[0][4] == 0.07651 + assert ticks[0][5] == 2.5734867 + + assert ticks[9][0] == 1527833100000 + assert ticks[9][1] == 0.07666 + assert ticks[9][2] == 0.07671 + assert ticks[9][3] == 0.07666 + assert ticks[9][4] == 0.07668 + assert ticks[9][5] == 16.65244264 + + # Bittrex use-case (real data from Bittrex) + # This ticker history is ordered ASC (oldest first, newest last) + tick = [ + [1527827700000, 0.07659999, 0.0766, 0.07627, 0.07657998, 1.85216924], + [1527828000000, 0.07657995, 0.07657995, 0.0763, 0.0763, 26.04051037], + [1527828300000, 0.0763, 0.07659998, 0.0763, 0.0764, 10.36434124], + [1527828600000, 0.0764, 0.0766, 0.0764, 0.0766, 5.71044773], + [1527828900000, 0.0764, 0.07666998, 0.0764, 0.07666998, 47.48888565], + [1527829200000, 0.0765, 0.07672999, 0.0765, 0.07672999, 3.37640326], + [1527829500000, 0.0766, 0.07675, 0.0765, 0.07675, 8.36203831], + [1527829800000, 0.07675, 0.07677999, 0.07620002, 0.076695, 119.22963884], + [1527830100000, 0.076695, 0.07671, 0.07624171, 0.07671, 1.80689244], + [1527830400000, 0.07671, 0.07674399, 0.07629216, 0.07655213, 2.31452783] + ] + type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) + api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick)) + mocker.patch('freqtrade.exchange._API', api_mock) + + # Test the ticker history sort + ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval']) + assert ticks[0][0] == 1527827700000 + assert ticks[0][1] == 0.07659999 + assert ticks[0][2] == 0.0766 + assert ticks[0][3] == 0.07627 + assert ticks[0][4] == 0.07657998 + assert ticks[0][5] == 1.85216924 + + assert ticks[9][0] == 1527830400000 + assert ticks[9][1] == 0.07671 + assert ticks[9][2] == 0.07674399 + assert ticks[9][3] == 0.07629216 + assert ticks[9][4] == 0.07655213 + assert ticks[9][5] == 2.31452783 + + def test_cancel_order_dry_run(default_conf, mocker): default_conf['dry_run'] = True mocker.patch.dict('freqtrade.exchange._CONF', default_conf) From 638d98735f237b94ecca0e7c999284c6aee85d3e Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Fri, 1 Jun 2018 20:58:07 -0700 Subject: [PATCH 3/4] Allow fiat_convert to use same symbol for Crypto and FIAT --- freqtrade/fiat_convert.py | 15 +++++++++++---- freqtrade/tests/test_fiat_convert.py | 7 +++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/freqtrade/fiat_convert.py b/freqtrade/fiat_convert.py index 17882f51a..7e61a3f2f 100644 --- a/freqtrade/fiat_convert.py +++ b/freqtrade/fiat_convert.py @@ -95,8 +95,11 @@ class CryptoToFiatConverter(object): coinlistings = self._coinmarketcap.listings() self._cryptomap = dict(map(lambda coin: (coin["symbol"], str(coin["id"])), coinlistings["data"])) - except (ValueError, RequestException) as e: - logger.error("Could not load FIAT Cryptocurrency map for the following problem: %s", e) + except (ValueError, RequestException) as exception: + logger.error( + "Could not load FIAT Cryptocurrency map for the following problem: %s", + exception + ) def convert_amount(self, crypto_amount: float, crypto_symbol: str, fiat_symbol: str) -> float: """ @@ -188,6 +191,10 @@ class CryptoToFiatConverter(object): if not self._is_supported_fiat(fiat=fiat_symbol): raise ValueError('The fiat {} is not supported.'.format(fiat_symbol)) + # No need to convert if both crypto and fiat are the same + if crypto_symbol == fiat_symbol: + return 1.0 + if crypto_symbol not in self._cryptomap: # return 0 for unsupported stake currencies (fiat-convert should not break the bot) logger.warning("unsupported crypto-symbol %s - returning 0.0", crypto_symbol) @@ -199,6 +206,6 @@ class CryptoToFiatConverter(object): convert=fiat_symbol )['data']['quotes'][fiat_symbol.upper()]['price'] ) - except BaseException as ex: - logger.error("Error in _find_price: %s", ex) + except BaseException as exception: + logger.error("Error in _find_price: %s", exception) return 0.0 diff --git a/freqtrade/tests/test_fiat_convert.py b/freqtrade/tests/test_fiat_convert.py index b37ca0f5c..2da427d27 100644 --- a/freqtrade/tests/test_fiat_convert.py +++ b/freqtrade/tests/test_fiat_convert.py @@ -126,6 +126,13 @@ def test_fiat_convert_get_price(mocker): assert fiat_convert._pairs[0]._expiration is not expiration +def test_fiat_convert_same_currencies(mocker): + patch_coinmarketcap(mocker) + fiat_convert = CryptoToFiatConverter() + + assert fiat_convert.get_price(crypto_symbol='USD', fiat_symbol='USD') == 1.0 + + def test_loadcryptomap(mocker): patch_coinmarketcap(mocker) From e8a59f4c20f135cb3b977324c574f694d27a1ac0 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Sat, 2 Jun 2018 13:26:24 -0700 Subject: [PATCH 4/4] Add a test to check the behavior when converting two FIAT --- freqtrade/tests/test_fiat_convert.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/freqtrade/tests/test_fiat_convert.py b/freqtrade/tests/test_fiat_convert.py index 2da427d27..faf7462c0 100644 --- a/freqtrade/tests/test_fiat_convert.py +++ b/freqtrade/tests/test_fiat_convert.py @@ -133,6 +133,13 @@ def test_fiat_convert_same_currencies(mocker): assert fiat_convert.get_price(crypto_symbol='USD', fiat_symbol='USD') == 1.0 +def test_fiat_convert_two_FIAT(mocker): + patch_coinmarketcap(mocker) + fiat_convert = CryptoToFiatConverter() + + assert fiat_convert.get_price(crypto_symbol='USD', fiat_symbol='EUR') == 0.0 + + def test_loadcryptomap(mocker): patch_coinmarketcap(mocker)