From 63b55658ac07ab3b6e44ee7bda9093031f2133ba Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 1 May 2020 05:11:30 +0300 Subject: [PATCH 1/7] Add ticker to dataprovider --- freqtrade/data/dataprovider.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 1df710152..b197ed1a5 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -97,8 +97,7 @@ class DataProvider: """ Return last ticker data """ - # TODO: Implement me - pass + return self._exchange.fetch_ticker(pair) def orderbook(self, pair: str, maximum: int) -> Dict[str, List]: """ From aae096c6ae30cbff0af2bf0db53f493b363e68cc Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 14 May 2020 13:36:48 +0300 Subject: [PATCH 2/7] Handle fetching ticker for non-existing pair safe way --- freqtrade/data/dataprovider.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 2efceae62..7ada4f642 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Tuple from pandas import DataFrame from freqtrade.data.history import load_pair_history -from freqtrade.exceptions import OperationalException +from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exchange import Exchange from freqtrade.state import RunMode @@ -97,9 +97,14 @@ class DataProvider: def ticker(self, pair: str): """ - Return last ticker data + Return last ticker data from exchange + :param pair: Pair to get the data for + :return: Ticker dict from exchange or empty dict if ticker is not available for the pair """ - return self._exchange.fetch_ticker(pair) + try: + return self._exchange.fetch_ticker(pair) + except DependencyException: + return {} def orderbook(self, pair: str, maximum: int) -> Dict[str, List]: """ From 78b81bac48e2fbb5acee8f2f2ca897620b10a929 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 14 May 2020 16:43:16 +0300 Subject: [PATCH 3/7] Add test for dp.ticker() --- tests/data/test_dataprovider.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 3e42abb95..32ddfe99e 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -154,6 +154,23 @@ def test_market(mocker, default_conf, markets): assert res is None +def test_ticker(mocker, default_conf, tickers, markets): + api_mock = MagicMock() + api_mock.markets = markets + api_mock.fetch_ticker = MagicMock(side_effect=lambda x: tickers().get(x)) + exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock) + + dp = DataProvider(default_conf, exchange) + res = dp.ticker('ETH/BTC') + + assert type(res) is dict + assert 'symbol' in res + assert res['symbol'] == 'ETH/BTC' + + res = dp.ticker('UNITTEST/BTC') + assert res == {} + + def test_current_whitelist(mocker, default_conf, tickers): # patch default conf to volumepairlist default_conf['pairlists'][0] = {'method': 'VolumePairList', "number_assets": 5} From 323b491962e468a75a99d48a21ee79b4ae6dac3e Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Fri, 15 May 2020 16:35:48 +0300 Subject: [PATCH 4/7] Add description and example for dp.ticker() to the docs --- docs/strategy-customization.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index dd451128c..42474c2dc 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -367,9 +367,10 @@ Please always check the mode of operation to select the correct method to get da - [`current_whitelist()`](#current_whitelist) - Returns a current list of whitelisted pairs. Useful for accessing dynamic whitelists (ie. VolumePairlist) - [`get_pair_dataframe(pair, timeframe)`](#get_pair_dataframepair-timeframe) - This is a universal method, which returns either historical data (for backtesting) or cached live data (for the Dry-Run and Live-Run modes). - `historic_ohlcv(pair, timeframe)` - Returns historical data stored on disk. -- `market(pair)` - Returns market data for the pair: fees, limits, precisions, activity flag, etc. See [ccxt documentation](https://github.com/ccxt/ccxt/wiki/Manual#markets) for more details on Market data structure. +- `market(pair)` - Returns market data for the pair: fees, limits, precisions, activity flag, etc. See [ccxt documentation](https://github.com/ccxt/ccxt/wiki/Manual#markets) for more details on the Market data structure. - `ohlcv(pair, timeframe)` - Currently cached candle (OHLCV) data for the pair, returns DataFrame or empty DataFrame. - [`orderbook(pair, maximum)`](#orderbookpair-maximum) - Returns latest orderbook data for the pair, a dict with bids/asks with a total of `maximum` entries. +- `ticker(pair)` - Returns current ticker data for the pair. See [ccxt documentation](https://github.com/ccxt/ccxt/wiki/Manual#price-tickers) for more details on the Ticker data structure. - `runmode` - Property containing the current runmode. #### Example Usages: @@ -451,6 +452,17 @@ if self.dp: The order book is not part of the historic data which means backtesting and hyperopt will not work if this method is used. +#### *ticker(pair)* + +``` python +if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + ticker = self.dp.ticker(metadata['pair']) + dataframe['last_price'] = ticker['last'] + dataframe['volume24h'] = ticker['quoteVolume'] + dataframe['vwap'] = ticker['vwap'] +``` + *** ### Additional data (Wallets) From 06b12c0a7095e0b4ee611694cbff26bdb88fed1e Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Fri, 15 May 2020 18:46:28 +0300 Subject: [PATCH 5/7] Add warning to docs for ticker data --- docs/strategy-customization.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 42474c2dc..c50266183 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -370,7 +370,7 @@ Please always check the mode of operation to select the correct method to get da - `market(pair)` - Returns market data for the pair: fees, limits, precisions, activity flag, etc. See [ccxt documentation](https://github.com/ccxt/ccxt/wiki/Manual#markets) for more details on the Market data structure. - `ohlcv(pair, timeframe)` - Currently cached candle (OHLCV) data for the pair, returns DataFrame or empty DataFrame. - [`orderbook(pair, maximum)`](#orderbookpair-maximum) - Returns latest orderbook data for the pair, a dict with bids/asks with a total of `maximum` entries. -- `ticker(pair)` - Returns current ticker data for the pair. See [ccxt documentation](https://github.com/ccxt/ccxt/wiki/Manual#price-tickers) for more details on the Ticker data structure. +- [`ticker(pair)`](#tickerpair) - Returns current ticker data for the pair. See [ccxt documentation](https://github.com/ccxt/ccxt/wiki/Manual#price-tickers) for more details on the Ticker data structure. - `runmode` - Property containing the current runmode. #### Example Usages: @@ -463,6 +463,12 @@ if self.dp: dataframe['vwap'] = ticker['vwap'] ``` +!!! Warning + Although the ticker data structure is a part of the ccxt Unified Interface, the values returned by this method can + vary for different exchanges. For instance, many exchanges do not return `vwap` values, the FTX exchange + does not always fills in the `last` field (so it can be None), etc. So you need to carefully verify the ticker + data returned from the exchange you are working with. + *** ### Additional data (Wallets) From d84cb3be77b9aeda62331594ef1ce2500e5f7ee3 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Fri, 15 May 2020 19:19:52 +0300 Subject: [PATCH 6/7] Improve test --- tests/data/test_dataprovider.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 32ddfe99e..c2d6e82f1 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -5,7 +5,7 @@ import pytest from freqtrade.data.dataprovider import DataProvider from freqtrade.pairlist.pairlistmanager import PairListManager -from freqtrade.exceptions import OperationalException +from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.state import RunMode from tests.conftest import get_patched_exchange @@ -154,19 +154,20 @@ def test_market(mocker, default_conf, markets): assert res is None -def test_ticker(mocker, default_conf, tickers, markets): - api_mock = MagicMock() - api_mock.markets = markets - api_mock.fetch_ticker = MagicMock(side_effect=lambda x: tickers().get(x)) - exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock) - +def test_ticker(mocker, default_conf, tickers): + ticker_mock = MagicMock(return_value=tickers()['ETH/BTC']) + mocker.patch("freqtrade.exchange.Exchange.fetch_ticker", ticker_mock) + exchange = get_patched_exchange(mocker, default_conf) dp = DataProvider(default_conf, exchange) res = dp.ticker('ETH/BTC') - assert type(res) is dict assert 'symbol' in res assert res['symbol'] == 'ETH/BTC' + ticker_mock = MagicMock(side_effect=DependencyException('Pair not found')) + mocker.patch("freqtrade.exchange.Exchange.fetch_ticker", ticker_mock) + exchange = get_patched_exchange(mocker, default_conf) + dp = DataProvider(default_conf, exchange) res = dp.ticker('UNITTEST/BTC') assert res == {} From ac076ee0af6b0ee95b902534af2a4ac51c855da8 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Fri, 15 May 2020 21:51:33 +0300 Subject: [PATCH 7/7] Update docs/strategy-customization.md Co-authored-by: Matthias --- docs/strategy-customization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index c50266183..7197b0fba 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -467,7 +467,7 @@ if self.dp: Although the ticker data structure is a part of the ccxt Unified Interface, the values returned by this method can vary for different exchanges. For instance, many exchanges do not return `vwap` values, the FTX exchange does not always fills in the `last` field (so it can be None), etc. So you need to carefully verify the ticker - data returned from the exchange you are working with. + data returned from the exchange and add appropriate error handling / defaults. *** ### Additional data (Wallets)