diff --git a/docs/configuration.md b/docs/configuration.md
index 67e8578dd..eb7f02d5c 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -108,7 +108,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `forcebuy_enable` | Enables the RPC Commands to force a buy. More information below.
**Datatype:** Boolean
| `strategy` | **Required** Defines Strategy class to use. Recommended to be set via `--strategy NAME`.
**Datatype:** ClassName
| `strategy_path` | Adds an additional strategy lookup path (must be a directory).
**Datatype:** String
-| `internals.process_throttle_secs` | Set the process throttle. Value in second.
*Defaults to `5` seconds.*
**Datatype:** Positive Intege
+| `internals.process_throttle_secs` | Set the process throttle. Value in second.
*Defaults to `5` seconds.*
**Datatype:** Positive Integer
| `internals.heartbeat_interval` | Print heartbeat message every N seconds. Set to 0 to disable heartbeat messages.
*Defaults to `60` seconds.*
**Datatype:** Positive Integer or 0
| `internals.sd_notify` | Enables use of the sd_notify protocol to tell systemd service manager about changes in the bot state and issue keep-alive pings. See [here](installation.md#7-optional-configure-freqtrade-as-a-systemd-service) for more details.
**Datatype:** Boolean
| `logfile` | Specifies logfile name. Uses a rolling strategy for log file rotation for 10 files with the 1MB limit per file.
**Datatype:** String
diff --git a/docs/installation.md b/docs/installation.md
index 88e2ef6eb..f017bef96 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -248,14 +248,14 @@ 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 precompiled 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.17‑cp36‑cp36m‑win32.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 precompiled 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.18‑cp38‑cp38‑win_amd64.whl` (make sure to use the version matching your python version)
```cmd
>cd \path\freqtrade-develop
>python -m venv .env
>.env\Scripts\activate.bat
REM optionally install ta-lib from wheel
-REM >pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl
+REM >pip install TA_Lib‑0.4.18‑cp38‑cp38‑win_amd64.whl
>pip install -r requirements.txt
>pip install -e .
>freqtrade
diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md
index c4fc55811..dd451128c 100644
--- a/docs/strategy-customization.md
+++ b/docs/strategy-customization.md
@@ -324,67 +324,14 @@ class Awesomestrategy(IStrategy):
!!! Note
If the data is pair-specific, make sure to use pair as one of the keys in the dictionary.
-### Additional data (DataProvider)
+***
-The strategy provides access to the `DataProvider`. This allows you to get additional data to use in your strategy.
-
-All methods return `None` in case of failure (do not raise an exception).
-
-Please always check the mode of operation to select the correct method to get data (samples see below).
-
-#### Possible options for DataProvider
-
-- `available_pairs` - Property with tuples listing cached pairs with their intervals (pair, interval).
-- `ohlcv(pair, timeframe)` - Currently cached candle (OHLCV) data for the pair, returns DataFrame or empty DataFrame.
-- `historic_ohlcv(pair, timeframe)` - Returns historical data stored on disk.
-- `get_pair_dataframe(pair, 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).
-- `orderbook(pair, maximum)` - Returns latest orderbook data for the pair, a dict with bids/asks with a total of `maximum` entries.
-- `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.
-- `runmode` - Property containing the current runmode.
-
-#### Example: fetch live / historical candle (OHLCV) data for the first informative pair
-
-``` python
-if self.dp:
- inf_pair, inf_timeframe = self.informative_pairs()[0]
- informative = self.dp.get_pair_dataframe(pair=inf_pair,
- timeframe=inf_timeframe)
-```
-
-!!! Warning "Warning about backtesting"
- Be carefull when using dataprovider in backtesting. `historic_ohlcv()` (and `get_pair_dataframe()`
- for the backtesting runmode) provides the full time-range in one go,
- so please be aware of it and make sure to not "look into the future" to avoid surprises when running in dry/live mode).
-
-!!! Warning "Warning in hyperopt"
- This option cannot currently be used during hyperopt.
-
-#### Orderbook
-
-``` python
-if self.dp:
- if self.dp.runmode.value in ('live', 'dry_run'):
- ob = self.dp.orderbook(metadata['pair'], 1)
- dataframe['best_bid'] = ob['bids'][0][0]
- dataframe['best_ask'] = ob['asks'][0][0]
-```
-
-!!! Warning
- The order book is not part of the historic data which means backtesting and hyperopt will not work if this
- method is used.
-
-#### Available Pairs
-
-``` python
-if self.dp:
- for pair, timeframe in self.dp.available_pairs:
- print(f"available {pair}, {timeframe}")
-```
+### Additional data (informative_pairs)
#### Get data for non-tradeable pairs
Data for additional, informative pairs (reference pairs) can be beneficial for some strategies.
-Ohlcv data for these pairs will be downloaded as part of the regular whitelist refresh process and is available via `DataProvider` just as other pairs (see above).
+Ohlcv data for these pairs will be downloaded as part of the regular whitelist refresh process and is available via `DataProvider` just as other pairs (see below).
These parts will **not** be traded unless they are also specified in the pair whitelist, or have been selected by Dynamic Whitelisting.
The pairs need to be specified as tuples in the format `("pair", "interval")`, with pair as the first and time interval as the second argument.
@@ -404,6 +351,107 @@ def informative_pairs(self):
It is however better to use resampling to longer time-intervals when possible
to avoid hammering the exchange with too many requests and risk being blocked.
+***
+
+### Additional data (DataProvider)
+
+The strategy provides access to the `DataProvider`. This allows you to get additional data to use in your strategy.
+
+All methods return `None` in case of failure (do not raise an exception).
+
+Please always check the mode of operation to select the correct method to get data (samples see below).
+
+#### Possible options for DataProvider
+
+- [`available_pairs`](#available_pairs) - Property with tuples listing cached pairs with their intervals (pair, interval).
+- [`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.
+- `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.
+- `runmode` - Property containing the current runmode.
+
+#### Example Usages:
+
+#### *available_pairs*
+
+``` python
+if self.dp:
+ for pair, timeframe in self.dp.available_pairs:
+ print(f"available {pair}, {timeframe}")
+```
+
+#### *current_whitelist()*
+Imagine you've developed a strategy that trades the `5m` timeframe using signals generated from a `1d` timeframe on the top 10 volume pairs by volume.
+
+The strategy might look something like this:
+
+*Scan through the top 10 pairs by volume using the `VolumePairList` every 5 minutes and use a 14 day ATR to buy and sell.*
+
+Due to the limited available data, it's very difficult to resample our `5m` candles into daily candles for use in a 14 day ATR. Most exchanges limit us to just 500 candles which effectively gives us around 1.74 daily candles. We need 14 days at least!
+
+Since we can't resample our data we will have to use an informative pair; and since our whitelist will be dynamic we don't know which pair(s) to use.
+
+This is where calling `self.dp.current_whitelist()` comes in handy.
+
+```python
+class SampleStrategy(IStrategy):
+ # strategy init stuff...
+
+ ticker_interval = '5m'
+
+ # more strategy init stuff..
+
+ def informative_pairs(self):
+
+ # get access to all pairs available in whitelist.
+ pairs = self.dp.current_whitelist()
+ # Assign tf to each pair so they can be downloaded and cached for strategy.
+ informative_pairs = [(pair, '1d') for pair in pairs]
+ return informative_pairs
+
+ def populate_indicators(self, dataframe, metadata):
+ # Get the informative pair
+ informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d')
+ # Get the 14 day ATR.
+ atr = ta.ATR(informative, timeperiod=14)
+ # Do other stuff
+```
+
+#### *get_pair_dataframe(pair, timeframe)*
+
+``` python
+# fetch live / historical candle (OHLCV) data for the first informative pair
+if self.dp:
+ inf_pair, inf_timeframe = self.informative_pairs()[0]
+ informative = self.dp.get_pair_dataframe(pair=inf_pair,
+ timeframe=inf_timeframe)
+```
+
+!!! Warning "Warning about backtesting"
+ Be carefull when using dataprovider in backtesting. `historic_ohlcv()` (and `get_pair_dataframe()`
+ for the backtesting runmode) provides the full time-range in one go,
+ so please be aware of it and make sure to not "look into the future" to avoid surprises when running in dry/live mode).
+
+!!! Warning "Warning in hyperopt"
+ This option cannot currently be used during hyperopt.
+
+#### *orderbook(pair, maximum)*
+
+``` python
+if self.dp:
+ if self.dp.runmode.value in ('live', 'dry_run'):
+ ob = self.dp.orderbook(metadata['pair'], 1)
+ dataframe['best_bid'] = ob['bids'][0][0]
+ dataframe['best_ask'] = ob['asks'][0][0]
+```
+
+!!! Warning
+ The order book is not part of the historic data which means backtesting and hyperopt will not work if this
+ method is used.
+
+***
### Additional data (Wallets)
The strategy provides access to the `Wallets` object. This contains the current balances on the exchange.
@@ -426,6 +474,7 @@ if self.wallets:
- `get_used(asset)` - currently tied up balance (open orders)
- `get_total(asset)` - total available balance - sum of the 2 above
+***
### Additional data (Trades)
A history of Trades can be retrieved in the strategy by querying the database.
diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py
index 1df710152..984652e24 100644
--- a/freqtrade/data/dataprovider.py
+++ b/freqtrade/data/dataprovider.py
@@ -10,6 +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.exchange import Exchange
from freqtrade.state import RunMode
@@ -18,9 +19,10 @@ logger = logging.getLogger(__name__)
class DataProvider:
- def __init__(self, config: dict, exchange: Exchange) -> None:
+ def __init__(self, config: dict, exchange: Exchange, pairlists=None) -> None:
self._config = config
self._exchange = exchange
+ self._pairlists = pairlists
def refresh(self,
pairlist: List[Tuple[str, str]],
@@ -116,3 +118,17 @@ class DataProvider:
can be "live", "dry-run", "backtest", "edgecli", "hyperopt" or "other".
"""
return RunMode(self._config.get('runmode', RunMode.OTHER))
+
+ def current_whitelist(self) -> List[str]:
+ """
+ fetch latest available whitelist.
+
+ Useful when you have a large whitelist and need to call each pair as an informative pair.
+ As available pairs does not show whitelist until after informative pairs have been cached.
+ :return: list of pairs in whitelist
+ """
+
+ if self._pairlists:
+ return self._pairlists.whitelist
+ else:
+ raise OperationalException("Dataprovider was not initialized with a pairlist provider.")
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
index 4f4b3e3bb..eda73a8c2 100644
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -71,15 +71,15 @@ class FreqtradeBot:
self.wallets = Wallets(self.config, self.exchange)
- self.dataprovider = DataProvider(self.config, self.exchange)
+ self.pairlists = PairListManager(self.exchange, self.config)
+
+ 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
- self.pairlists = PairListManager(self.exchange, self.config)
-
# Initializing Edge only if enabled
self.edge = Edge(self.config, self.exchange, self.strategy) if \
self.config.get('edge', {}).get('enabled', False) else None
@@ -946,32 +946,31 @@ class FreqtradeBot:
:return: Reason for cancel
"""
# if trade is not partially completed, just cancel the trade
- if order['remaining'] == order['amount'] or order.get('filled') == 0.0:
- if not self.exchange.check_order_canceled_empty(order):
- reason = "cancelled due to timeout"
- try:
- # if trade is not partially completed, just delete the trade
- self.exchange.cancel_order(trade.open_order_id, trade.pair)
- except InvalidOrderException:
- logger.exception(f"Could not cancel sell order {trade.open_order_id}")
- return 'error cancelling order'
- logger.info('Sell order %s for %s.', reason, trade)
- else:
- reason = "cancelled on exchange"
- logger.info('Sell order %s for %s.', reason, trade)
+ if not (
+ order['remaining'] == order['amount'] or order.get('filled') == 0.0
+ ):
+ # TODO: figure out how to handle partially complete sell orders
+ return 'partially filled - keeping order open'
+ if self.exchange.check_order_canceled_empty(order):
+ reason = "cancelled on exchange"
+ else:
+ reason = "cancelled due to timeout"
+ try:
+ # if trade is not partially completed, just delete the trade
+ self.exchange.cancel_order(trade.open_order_id, trade.pair)
+ except InvalidOrderException:
+ logger.exception(f"Could not cancel sell order {trade.open_order_id}")
+ return 'error cancelling order'
+ logger.info('Sell order %s for %s.', reason, trade)
+ trade.close_rate = None
+ trade.close_rate_requested = None
+ trade.close_profit = None
+ trade.close_profit_abs = None
+ trade.close_date = None
+ trade.is_open = True
+ trade.open_order_id = None
- trade.close_rate = None
- trade.close_rate_requested = None
- trade.close_profit = None
- trade.close_profit_abs = None
- trade.close_date = None
- trade.is_open = True
- trade.open_order_id = None
-
- return reason
-
- # TODO: figure out how to handle partially complete sell orders
- return 'partially filled - keeping order open'
+ return reason
def _safe_sell_amount(self, pair: str, amount: float) -> float:
"""
diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py
index 2b3dda188..3e42abb95 100644
--- a/tests/data/test_dataprovider.py
+++ b/tests/data/test_dataprovider.py
@@ -1,8 +1,11 @@
from unittest.mock import MagicMock
from pandas import DataFrame
+import pytest
from freqtrade.data.dataprovider import DataProvider
+from freqtrade.pairlist.pairlistmanager import PairListManager
+from freqtrade.exceptions import OperationalException
from freqtrade.state import RunMode
from tests.conftest import get_patched_exchange
@@ -64,8 +67,8 @@ def test_get_pair_dataframe(mocker, default_conf, ohlcv_history):
assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty
# Test with and without parameter
- assert dp.get_pair_dataframe("UNITTEST/BTC",
- ticker_interval).equals(dp.get_pair_dataframe("UNITTEST/BTC"))
+ assert dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval)\
+ .equals(dp.get_pair_dataframe("UNITTEST/BTC"))
default_conf["runmode"] = RunMode.LIVE
dp = DataProvider(default_conf, exchange)
@@ -90,10 +93,7 @@ def test_available_pairs(mocker, default_conf, ohlcv_history):
dp = DataProvider(default_conf, exchange)
assert len(dp.available_pairs) == 2
- assert dp.available_pairs == [
- ("XRP/BTC", ticker_interval),
- ("UNITTEST/BTC", ticker_interval),
- ]
+ assert dp.available_pairs == [("XRP/BTC", ticker_interval), ("UNITTEST/BTC", ticker_interval), ]
def test_refresh(mocker, default_conf, ohlcv_history):
@@ -152,3 +152,27 @@ def test_market(mocker, default_conf, markets):
res = dp.market('UNITTEST/BTC')
assert res is None
+
+
+def test_current_whitelist(mocker, default_conf, tickers):
+ # patch default conf to volumepairlist
+ default_conf['pairlists'][0] = {'method': 'VolumePairList', "number_assets": 5}
+
+ mocker.patch.multiple('freqtrade.exchange.Exchange',
+ exchange_has=MagicMock(return_value=True),
+ get_tickers=tickers)
+ exchange = get_patched_exchange(mocker, default_conf)
+
+ pairlist = PairListManager(exchange, default_conf)
+ dp = DataProvider(default_conf, exchange, pairlist)
+
+ # Simulate volumepairs from exchange.
+ pairlist.refresh_pairlist()
+
+ assert dp.current_whitelist() == pairlist._whitelist
+ # The identity of the 2 lists should be identical
+ assert dp.current_whitelist() is pairlist._whitelist
+
+ with pytest.raises(OperationalException):
+ dp = DataProvider(default_conf, exchange)
+ dp.current_whitelist()