Merge branch 'develop' into freqtrade-development

This commit is contained in:
theluxaz 2021-10-19 00:00:15 +03:00
commit 5ecdd1d112
29 changed files with 273 additions and 135 deletions

View File

@ -11,8 +11,13 @@ if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then
&& curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' -o config.guess \ && curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' -o config.guess \
&& curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' -o config.sub \ && curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' -o config.sub \
&& ./configure --prefix=${INSTALL_LOC}/ \ && ./configure --prefix=${INSTALL_LOC}/ \
&& make -j$(nproc) \ && make
&& which sudo && sudo make install || make install if [ $? -ne 0 ]; then
echo "Failed building ta-lib."
cd .. && rm -rf ./ta-lib/
exit 1
fi
which sudo && sudo make install || make install
if [ -x "$(command -v apt-get)" ]; then if [ -x "$(command -v apt-get)" ]; then
echo "Updating library path using ldconfig" echo "Updating library path using ldconfig"
sudo ldconfig sudo ldconfig

View File

@ -22,6 +22,7 @@ usage: freqtrade download-data [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[-d PATH] [--userdir PATH] [-d PATH] [--userdir PATH]
[-p PAIRS [PAIRS ...]] [--pairs-file FILE] [-p PAIRS [PAIRS ...]] [--pairs-file FILE]
[--days INT] [--new-pairs-days INT] [--days INT] [--new-pairs-days INT]
[--include-inactive-pairs]
[--timerange TIMERANGE] [--dl-trades] [--timerange TIMERANGE] [--dl-trades]
[--exchange EXCHANGE] [--exchange EXCHANGE]
[-t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...]] [-t {1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} [{1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,2w,1M,1y} ...]]
@ -38,6 +39,8 @@ optional arguments:
--days INT Download data for given number of days. --days INT Download data for given number of days.
--new-pairs-days INT Download data of new pairs for given number of days. --new-pairs-days INT Download data of new pairs for given number of days.
Default: `None`. Default: `None`.
--include-inactive-pairs
Also download data from inactive pairs.
--timerange TIMERANGE --timerange TIMERANGE
Specify what timerange of data to use. Specify what timerange of data to use.
--dl-trades Download trades instead of OHLCV data. The bot will --dl-trades Download trades instead of OHLCV data. The bot will
@ -52,10 +55,10 @@ optional arguments:
exchange/pairs/timeframes. exchange/pairs/timeframes.
--data-format-ohlcv {json,jsongz,hdf5} --data-format-ohlcv {json,jsongz,hdf5}
Storage format for downloaded candle (OHLCV) data. Storage format for downloaded candle (OHLCV) data.
(default: `None`). (default: `json`).
--data-format-trades {json,jsongz,hdf5} --data-format-trades {json,jsongz,hdf5}
Storage format for downloaded trades data. (default: Storage format for downloaded trades data. (default:
`None`). `jsongz`).
Common arguments: Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages). -v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
@ -80,6 +83,82 @@ Common arguments:
For that reason, `download-data` does not care about the "startup-period" defined in a strategy. It's up to the user to download additional days if the backtest should start at a specific point in time (while respecting startup period). For that reason, `download-data` does not care about the "startup-period" defined in a strategy. It's up to the user to download additional days if the backtest should start at a specific point in time (while respecting startup period).
### Pairs file
In alternative to the whitelist from `config.json`, a `pairs.json` file can be used.
If you are using Binance for example:
- create a directory `user_data/data/binance` and copy or create the `pairs.json` file in that directory.
- update the `pairs.json` file to contain the currency pairs you are interested in.
```bash
mkdir -p user_data/data/binance
touch user_data/data/binance/pairs.json
```
The format of the `pairs.json` file is a simple json list.
Mixing different stake-currencies is allowed for this file, since it's only used for downloading.
``` json
[
"ETH/BTC",
"ETH/USDT",
"BTC/USDT",
"XRP/ETH"
]
```
!!! Tip "Downloading all data for one quote currency"
Often, you'll want to download data for all pairs of a specific quote-currency. In such cases, you can use the following shorthand:
`freqtrade download-data --exchange binance --pairs .*/USDT <...>`. The provided "pairs" string will be expanded to contain all active pairs on the exchange.
To also download data for inactive (delisted) pairs, add `--include-inactive-pairs` to the command.
??? Note "Permission denied errors"
If your configuration directory `user_data` was made by docker, you may get the following error:
```
cp: cannot create regular file 'user_data/data/binance/pairs.json': Permission denied
```
You can fix the permissions of your user-data directory as follows:
```
sudo chown -R $UID:$GID user_data
```
### Start download
Then run:
```bash
freqtrade download-data --exchange binance
```
This will download historical candle (OHLCV) data for all the currency pairs you defined in `pairs.json`.
Alternatively, specify the pairs directly
```bash
freqtrade download-data --exchange binance --pairs ETH/USDT XRP/USDT BTC/USDT
```
or as regex (to download all active USDT pairs)
```bash
freqtrade download-data --exchange binance --pairs .*/USDT
```
### Other Notes
- To use a different directory than the exchange specific default, use `--datadir user_data/data/some_directory`.
- To change the exchange used to download the historical data from, please use a different configuration file (you'll probably need to adjust rate limits etc.)
- To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`.
- To download historical candle (OHLCV) data for only 10 days, use `--days 10` (defaults to 30 days).
- To download historical candle (OHLCV) data from a fixed starting point, use `--timerange 20200101-` - which will download all data from January 1st, 2020. Eventually set end dates are ignored.
- Use `--timeframes` to specify what timeframe download the historical candle (OHLCV) data for. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute data.
- To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options.
### Data format ### Data format
Freqtrade currently supports 3 data-formats for both OHLCV and trades data: Freqtrade currently supports 3 data-formats for both OHLCV and trades data:
@ -312,64 +391,6 @@ ETH/BTC 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d
ETH/USDT 5m, 15m, 30m, 1h, 2h, 4h ETH/USDT 5m, 15m, 30m, 1h, 2h, 4h
``` ```
### Pairs file
In alternative to the whitelist from `config.json`, a `pairs.json` file can be used.
If you are using Binance for example:
- create a directory `user_data/data/binance` and copy or create the `pairs.json` file in that directory.
- update the `pairs.json` file to contain the currency pairs you are interested in.
```bash
mkdir -p user_data/data/binance
cp tests/testdata/pairs.json user_data/data/binance
```
If your configuration directory `user_data` was made by docker, you may get the following error:
```
cp: cannot create regular file 'user_data/data/binance/pairs.json': Permission denied
```
You can fix the permissions of your user-data directory as follows:
```
sudo chown -R $UID:$GID user_data
```
The format of the `pairs.json` file is a simple json list.
Mixing different stake-currencies is allowed for this file, since it's only used for downloading.
``` json
[
"ETH/BTC",
"ETH/USDT",
"BTC/USDT",
"XRP/ETH"
]
```
### Start download
Then run:
```bash
freqtrade download-data --exchange binance
```
This will download historical candle (OHLCV) data for all the currency pairs you defined in `pairs.json`.
### Other Notes
- To use a different directory than the exchange specific default, use `--datadir user_data/data/some_directory`.
- To change the exchange used to download the historical data from, please use a different configuration file (you'll probably need to adjust rate limits etc.)
- To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`.
- To download historical candle (OHLCV) data for only 10 days, use `--days 10` (defaults to 30 days).
- To download historical candle (OHLCV) data from a fixed starting point, use `--timerange 20200101-` - which will download all data from January 1st, 2020. Eventually set end dates are ignored.
- Use `--timeframes` to specify what timeframe download the historical candle (OHLCV) data for. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute data.
- To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options.
### Trades (tick) data ### Trades (tick) data
By default, `download-data` sub-command downloads Candles (OHLCV) data. Some exchanges also provide historic trade-data via their API. By default, `download-data` sub-command downloads Candles (OHLCV) data. Some exchanges also provide historic trade-data via their API.

View File

@ -8,7 +8,7 @@ All contributions, bug reports, bug fixes, documentation improvements, enhanceme
Documentation is available at [https://freqtrade.io](https://www.freqtrade.io/) and needs to be provided with every new feature PR. Documentation is available at [https://freqtrade.io](https://www.freqtrade.io/) and needs to be provided with every new feature PR.
Special fields for the documentation (like Note boxes, ...) can be found [here](https://squidfunk.github.io/mkdocs-material/extensions/admonition/). Special fields for the documentation (like Note boxes, ...) can be found [here](https://squidfunk.github.io/mkdocs-material/reference/admonitions/).
To test the documentation locally use the following commands. To test the documentation locally use the following commands.

View File

@ -51,6 +51,7 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--print-all] [--no-color] [--print-json] [-j JOBS] [--print-all] [--no-color] [--print-json] [-j JOBS]
[--random-state INT] [--min-trades INT] [--random-state INT] [--min-trades INT]
[--hyperopt-loss NAME] [--disable-param-export] [--hyperopt-loss NAME] [--disable-param-export]
[--ignore-missing-spaces]
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
@ -118,6 +119,9 @@ optional arguments:
MaxDrawDownHyperOptLoss MaxDrawDownHyperOptLoss
--disable-param-export --disable-param-export
Disable automatic hyperopt parameter export. Disable automatic hyperopt parameter export.
--ignore-missing-spaces, --ignore-unparameterized-spaces
Suppress errors for any requested Hyperopt spaces that
do not contain any parameters.
Common arguments: Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages). -v, --verbose Verbose mode (-vv for more, -vvv to get all messages).

View File

@ -194,17 +194,22 @@ Trade count is used as a tie breaker.
You can use the `minutes` parameter to only consider performance of the past X minutes (rolling window). You can use the `minutes` parameter to only consider performance of the past X minutes (rolling window).
Not defining this parameter (or setting it to 0) will use all-time performance. Not defining this parameter (or setting it to 0) will use all-time performance.
The optional `min_profit` parameter defines the minimum profit a pair must have to be considered.
Pairs below this level will be filtered out.
Using this parameter without `minutes` is highly discouraged, as it can lead to an empty pairlist without without a way to recover.
```json ```json
"pairlists": [ "pairlists": [
// ... // ...
{ {
"method": "PerformanceFilter", "method": "PerformanceFilter",
"minutes": 1440 // rolling 24h "minutes": 1440, // rolling 24h
"min_profit": 0.01
} }
], ],
``` ```
!!! Note !!! Warning "Backtesting"
`PerformanceFilter` does not support backtesting mode. `PerformanceFilter` does not support backtesting mode.
#### PrecisionFilter #### PrecisionFilter

View File

@ -1,4 +1,4 @@
mkdocs==1.2.2 mkdocs==1.2.3
mkdocs-material==7.3.2 mkdocs-material==7.3.4
mdx_truly_sane_lists==1.2 mdx_truly_sane_lists==1.2
pymdown-extensions==9.0 pymdown-extensions==9.0

View File

@ -16,7 +16,6 @@ dependencies:
- cachetools - cachetools
- requests - requests
- urllib3 - urllib3
- wrapt
- jsonschema - jsonschema
- TA-Lib - TA-Lib
- tabulate - tabulate
@ -64,7 +63,6 @@ dependencies:
- py_find_1st - py_find_1st
- tables - tables
- pytest-random-order - pytest-random-order
- flake8-type-annotations
- ccxt - ccxt
- flake8-tidy-imports - flake8-tidy-imports
- -e . - -e .

View File

@ -31,7 +31,8 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",
"epochs", "spaces", "print_all", "epochs", "spaces", "print_all",
"print_colorized", "print_json", "hyperopt_jobs", "print_colorized", "print_json", "hyperopt_jobs",
"hyperopt_random_state", "hyperopt_min_trades", "hyperopt_random_state", "hyperopt_min_trades",
"hyperopt_loss", "disableparamexport"] "hyperopt_loss", "disableparamexport",
"hyperopt_ignore_missing_space"]
ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"]
@ -62,9 +63,9 @@ ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "d
ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"] ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"]
ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "timerange", ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "include_inactive",
"download_trades", "exchange", "timeframes", "erase", "dataformat_ohlcv", "timerange", "download_trades", "exchange", "timeframes",
"dataformat_trades"] "erase", "dataformat_ohlcv", "dataformat_trades"]
ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit",
"db_url", "trade_source", "export", "exportfilename", "db_url", "trade_source", "export", "exportfilename",

View File

@ -355,6 +355,11 @@ AVAILABLE_CLI_OPTIONS = {
type=check_int_positive, type=check_int_positive,
metavar='INT', metavar='INT',
), ),
"include_inactive": Arg(
'--include-inactive-pairs',
help='Also download data from inactive pairs.',
action='store_true',
),
"new_pairs_days": Arg( "new_pairs_days": Arg(
'--new-pairs-days', '--new-pairs-days',
help='Download data of new pairs for given number of days. Default: `%(default)s`.', help='Download data of new pairs for given number of days. Default: `%(default)s`.',
@ -558,4 +563,10 @@ AVAILABLE_CLI_OPTIONS = {
help='Do not print epoch details header.', help='Do not print epoch details header.',
action='store_true', action='store_true',
), ),
"hyperopt_ignore_missing_space": Arg(
"--ignore-missing-spaces", "--ignore-unparameterized-spaces",
help=("Suppress errors for any requested Hyperopt spaces "
"that do not contain any parameters."),
action="store_true",
),
} }

View File

@ -11,6 +11,7 @@ from freqtrade.data.history import (convert_trades_to_ohlcv, refresh_backtest_oh
from freqtrade.enums import RunMode from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
from freqtrade.exchange.exchange import market_is_active
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.resolvers import ExchangeResolver from freqtrade.resolvers import ExchangeResolver
@ -47,11 +48,13 @@ def start_download_data(args: Dict[str, Any]) -> None:
# Init exchange # Init exchange
exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False) exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False)
markets = [p for p, m in exchange.markets.items() if market_is_active(m)
or config.get('include_inactive')]
expanded_pairs = expand_pairlist(config['pairs'], markets)
# Manual validations of relevant settings # Manual validations of relevant settings
if not config['exchange'].get('skip_pair_validation', False): if not config['exchange'].get('skip_pair_validation', False):
exchange.validate_pairs(config['pairs']) exchange.validate_pairs(expanded_pairs)
expanded_pairs = expand_pairlist(config['pairs'], list(exchange.markets))
logger.info(f"About to download pairs: {expanded_pairs}, " logger.info(f"About to download pairs: {expanded_pairs}, "
f"intervals: {config['timeframes']} to {config['datadir']}") f"intervals: {config['timeframes']} to {config['datadir']}")

View File

@ -369,6 +369,9 @@ class Configuration:
self._args_to_config(config, argname='hyperopt_show_no_header', self._args_to_config(config, argname='hyperopt_show_no_header',
logstring='Parameter --no-header detected: {}') logstring='Parameter --no-header detected: {}')
self._args_to_config(config, argname="hyperopt_ignore_missing_space",
logstring="Paramter --ignore-missing-space detected: {}")
def _process_plot_options(self, config: Dict[str, Any]) -> None: def _process_plot_options(self, config: Dict[str, Any]) -> None:
self._args_to_config(config, argname='pairs', self._args_to_config(config, argname='pairs',
@ -404,6 +407,9 @@ class Configuration:
self._args_to_config(config, argname='days', self._args_to_config(config, argname='days',
logstring='Detected --days: {}') logstring='Detected --days: {}')
self._args_to_config(config, argname='include_inactive',
logstring='Detected --include-inactive-pairs: {}')
self._args_to_config(config, argname='download_trades', self._args_to_config(config, argname='download_trades',
logstring='Detected --dl-trades: {}') logstring='Detected --dl-trades: {}')

View File

@ -258,6 +258,7 @@ class Hyperopt:
if HyperoptTools.has_space(self.config, 'trailing'): if HyperoptTools.has_space(self.config, 'trailing'):
logger.debug("Hyperopt has 'trailing' space") logger.debug("Hyperopt has 'trailing' space")
self.trailing_space = self.custom_hyperopt.trailing_space() self.trailing_space = self.custom_hyperopt.trailing_space()
self.dimensions = (self.buy_space + self.sell_space + self.protection_space self.dimensions = (self.buy_space + self.sell_space + self.protection_space
+ self.roi_space + self.stoploss_space + self.trailing_space) + self.roi_space + self.stoploss_space + self.trailing_space)

View File

@ -3,6 +3,7 @@ HyperOptAuto class.
This module implements a convenience auto-hyperopt class, which can be used together with strategies This module implements a convenience auto-hyperopt class, which can be used together with strategies
that implement IHyperStrategy interface. that implement IHyperStrategy interface.
""" """
import logging
from contextlib import suppress from contextlib import suppress
from typing import Callable, Dict, List from typing import Callable, Dict, List
@ -15,12 +16,19 @@ with suppress(ImportError):
from freqtrade.optimize.hyperopt_interface import EstimatorType, IHyperOpt from freqtrade.optimize.hyperopt_interface import EstimatorType, IHyperOpt
def _format_exception_message(space: str) -> str: logger = logging.getLogger(__name__)
raise OperationalException(
f"The '{space}' space is included into the hyperoptimization "
f"but no parameter for this space was not found in your Strategy. " def _format_exception_message(space: str, ignore_missing_space: bool) -> None:
f"Please make sure to have parameters for this space enabled for optimization " msg = (f"The '{space}' space is included into the hyperoptimization "
f"or remove the '{space}' space from hyperoptimization.") f"but no parameter for this space was not found in your Strategy. "
)
if ignore_missing_space:
logger.warning(msg + "This space will be ignored.")
else:
raise OperationalException(
msg + f"Please make sure to have parameters for this space enabled for optimization "
f"or remove the '{space}' space from hyperoptimization.")
class HyperOptAuto(IHyperOpt): class HyperOptAuto(IHyperOpt):
@ -48,13 +56,16 @@ class HyperOptAuto(IHyperOpt):
if attr.optimize: if attr.optimize:
yield attr.get_space(attr_name) yield attr.get_space(attr_name)
def _get_indicator_space(self, category): def _get_indicator_space(self, category) -> List:
# TODO: is this necessary, or can we call "generate_space" directly? # TODO: is this necessary, or can we call "generate_space" directly?
indicator_space = list(self._generate_indicator_space(category)) indicator_space = list(self._generate_indicator_space(category))
if len(indicator_space) > 0: if len(indicator_space) > 0:
return indicator_space return indicator_space
else: else:
_format_exception_message(category) _format_exception_message(
category,
self.config.get("hyperopt_ignore_missing_space", False))
return []
def buy_indicator_space(self) -> List['Dimension']: def buy_indicator_space(self) -> List['Dimension']:
return self._get_indicator_space('buy') return self._get_indicator_space('buy')

View File

@ -21,6 +21,7 @@ class PerformanceFilter(IPairList):
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
self._minutes = pairlistconfig.get('minutes', 0) self._minutes = pairlistconfig.get('minutes', 0)
self._min_profit = pairlistconfig.get('min_profit', None)
@property @property
def needstickers(self) -> bool: def needstickers(self) -> bool:
@ -68,6 +69,14 @@ class PerformanceFilter(IPairList):
sorted_df = list_df.merge(performance, on='pair', how='left')\ sorted_df = list_df.merge(performance, on='pair', how='left')\
.fillna(0).sort_values(by=['count', 'pair'], ascending=True)\ .fillna(0).sort_values(by=['count', 'pair'], ascending=True)\
.sort_values(by=['profit'], ascending=False) .sort_values(by=['profit'], ascending=False)
if self._min_profit is not None:
removed = sorted_df[sorted_df['profit'] < self._min_profit]
for _, row in removed.iterrows():
self.log_once(
f"Removing pair {row['pair']} since {row['profit']} is "
f"below {self._min_profit}", logger.info)
sorted_df = sorted_df[sorted_df['profit'] >= self._min_profit]
pairlist = sorted_df['pair'].tolist() pairlist = sorted_df['pair'].tolist()
return pairlist return pairlist

View File

@ -1,5 +1,6 @@
from typing import Any, Dict, Optional from typing import Any, Dict, Iterator, Optional
from freqtrade.persistence import Trade
from freqtrade.rpc.rpc import RPC, RPCException from freqtrade.rpc.rpc import RPC, RPCException
from .webserver import ApiServer from .webserver import ApiServer
@ -11,10 +12,12 @@ def get_rpc_optional() -> Optional[RPC]:
return None return None
def get_rpc() -> Optional[RPC]: def get_rpc() -> Optional[Iterator[RPC]]:
_rpc = get_rpc_optional() _rpc = get_rpc_optional()
if _rpc: if _rpc:
return _rpc Trade.query.session.rollback()
yield _rpc
Trade.query.session.rollback()
else: else:
raise RPCException('Bot is not in the correct state') raise RPCException('Bot is not in the correct state')

View File

@ -25,6 +25,7 @@ from freqtrade.constants import DUST_PER_COIN
from freqtrade.enums import RPCMessageType from freqtrade.enums import RPCMessageType
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.misc import chunks, plural, round_coin_value from freqtrade.misc import chunks, plural, round_coin_value
from freqtrade.persistence import Trade
from freqtrade.rpc import RPC, RPCException, RPCHandler from freqtrade.rpc import RPC, RPCException, RPCHandler
@ -59,7 +60,8 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]:
update.message.chat_id update.message.chat_id
) )
return wrapper return wrapper
# Rollback session to avoid getting data stored in a transaction.
Trade.query.session.rollback()
logger.debug( logger.debug(
'Executing handler: %s for chat_id: %s', 'Executing handler: %s for chat_id: %s',
command_handler.__name__, command_handler.__name__,

View File

@ -339,11 +339,13 @@ def vwap(bars):
(input can be pandas series or numpy array) (input can be pandas series or numpy array)
bars are usually mid [ (h+l)/2 ] or typical [ (h+l+c)/3 ] bars are usually mid [ (h+l)/2 ] or typical [ (h+l+c)/3 ]
""" """
typical = ((bars['high'] + bars['low'] + bars['close']) / 3).values raise ValueError("using `qtpylib.vwap` facilitates lookahead bias. Please use "
volume = bars['volume'].values "`qtpylib.rolling_vwap` instead, which calculates vwap in a rolling manner.")
# typical = ((bars['high'] + bars['low'] + bars['close']) / 3).values
# volume = bars['volume'].values
return pd.Series(index=bars.index, # return pd.Series(index=bars.index,
data=np.cumsum(volume * typical) / np.cumsum(volume)) # data=np.cumsum(volume * typical) / np.cumsum(volume))
# --------------------------------------------- # ---------------------------------------------

View File

@ -54,8 +54,8 @@ theme:
primary: 'blue grey' primary: 'blue grey'
accent: 'tear' accent: 'tear'
toggle: toggle:
icon: material/toggle-switch-off-outline icon: material/toggle-switch
name: Switch to dark mode name: Switch to light mode
extra_css: extra_css:
- 'stylesheets/ft.extra.css' - 'stylesheets/ft.extra.css'
extra_javascript: extra_javascript:

View File

@ -4,11 +4,11 @@
-r requirements-hyperopt.txt -r requirements-hyperopt.txt
coveralls==3.2.0 coveralls==3.2.0
flake8==4.0.0 flake8==4.0.1
flake8-tidy-imports==4.5.0 flake8-tidy-imports==4.5.0
mypy==0.910 mypy==0.910
pytest==6.2.5 pytest==6.2.5
pytest-asyncio==0.15.1 pytest-asyncio==0.16.0
pytest-cov==3.0.0 pytest-cov==3.0.0
pytest-mock==3.6.1 pytest-mock==3.6.1
pytest-random-order==1.0.4 pytest-random-order==1.0.4
@ -20,7 +20,7 @@ time-machine==2.4.0
nbconvert==6.2.0 nbconvert==6.2.0
# mypy types # mypy types
types-cachetools==4.2.2 types-cachetools==4.2.4
types-filelock==3.2.0 types-filelock==3.2.1
types-requests==2.25.9 types-requests==2.25.11
types-tabulate==0.8.2 types-tabulate==0.8.3

View File

@ -5,7 +5,7 @@
scipy==1.7.1 scipy==1.7.1
scikit-learn==1.0 scikit-learn==1.0
scikit-optimize==0.9.0 scikit-optimize==0.9.0
filelock==3.3.0 filelock==3.3.1
joblib==1.1.0 joblib==1.1.0
psutil==5.8.0 psutil==5.8.0
progressbar2==3.53.3 progressbar2==3.55.0

View File

@ -1,8 +1,8 @@
numpy==1.21.2 numpy==1.21.2
pandas==1.3.3 pandas==1.3.4
pandas-ta==0.3.14b pandas-ta==0.3.14b
ccxt==1.57.94 ccxt==1.58.47
# Pin cryptography for now due to rust build errors with piwheels # Pin cryptography for now due to rust build errors with piwheels
cryptography==35.0.0 cryptography==35.0.0
aiohttp==3.7.4.post0 aiohttp==3.7.4.post0
@ -12,7 +12,6 @@ arrow==1.2.0
cachetools==4.2.2 cachetools==4.2.2
requests==2.26.0 requests==2.26.0
urllib3==1.26.7 urllib3==1.26.7
wrapt==1.13.1
jsonschema==4.1.0 jsonschema==4.1.0
TA-Lib==0.4.21 TA-Lib==0.4.21
technical==1.3.0 technical==1.3.0
@ -26,15 +25,15 @@ blosc==1.10.6
py_find_1st==1.1.5 py_find_1st==1.1.5
# Load ticker files 30% faster # Load ticker files 30% faster
python-rapidjson==1.4 python-rapidjson==1.5
# Notify systemd # Notify systemd
sdnotify==0.3.2 sdnotify==0.3.2
# API Server # API Server
fastapi==0.68.1 fastapi==0.70.0
uvicorn==0.15.0 uvicorn==0.15.0
pyjwt==2.2.0 pyjwt==2.3.0
aiofiles==0.7.0 aiofiles==0.7.0
psutil==5.8.0 psutil==5.8.0

View File

@ -16,7 +16,6 @@ hyperopt = [
develop = [ develop = [
'coveralls', 'coveralls',
'flake8', 'flake8',
'flake8-type-annotations',
'flake8-tidy-imports', 'flake8-tidy-imports',
'mypy', 'mypy',
'pytest', 'pytest',
@ -51,7 +50,6 @@ setup(
'cachetools', 'cachetools',
'requests', 'requests',
'urllib3', 'urllib3',
'wrapt',
'jsonschema', 'jsonschema',
'TA-Lib', 'TA-Lib',
'pandas-ta', 'pandas-ta',

View File

@ -95,7 +95,15 @@ function install_talib() {
return return
fi fi
cd build_helpers && ./install_ta-lib.sh && cd .. cd build_helpers && ./install_ta-lib.sh
if [ $? -ne 0 ]; then
echo "Quitting. Please fix the above error before continuing."
cd ..
exit 1
fi;
cd ..
} }
function install_mac_newer_python_dependencies() { function install_mac_newer_python_dependencies() {

View File

@ -754,6 +754,46 @@ def test_download_data_no_pairs(mocker, caplog):
start_download_data(pargs) start_download_data(pargs)
def test_download_data_all_pairs(mocker, markets):
mocker.patch.object(Path, "exists", MagicMock(return_value=False))
dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_ohlcv_data',
MagicMock(return_value=["ETH/BTC", "XRP/BTC"]))
patch_exchange(mocker)
mocker.patch(
'freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)
)
args = [
"download-data",
"--exchange",
"binance",
"--pairs",
".*/USDT"
]
pargs = get_args(args)
pargs['config'] = None
start_download_data(pargs)
expected = set(['ETH/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
assert dl_mock.call_count == 1
dl_mock.reset_mock()
args = [
"download-data",
"--exchange",
"binance",
"--pairs",
".*/USDT",
"--include-inactive-pairs",
]
pargs = get_args(args)
pargs['config'] = None
start_download_data(pargs)
expected = set(['ETH/USDT', 'LTC/USDT', 'XRP/USDT', 'NEO/USDT', 'TKN/USDT'])
assert set(dl_mock.call_args_list[0][1]['pairs']) == expected
def test_download_data_trades(mocker, caplog): def test_download_data_trades(mocker, caplog):
dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_trades_data', dl_mock = mocker.patch('freqtrade.commands.data_commands.refresh_backtest_trades_data',
MagicMock(return_value=[])) MagicMock(return_value=[]))

View File

@ -226,7 +226,7 @@ def create_mock_trades(fee, use_db: bool = True):
add_trade(trade) add_trade(trade)
if use_db: if use_db:
Trade.query.session.flush() Trade.commit()
def create_mock_trades_usdt(fee, use_db: bool = True): def create_mock_trades_usdt(fee, use_db: bool = True):
@ -259,7 +259,7 @@ def create_mock_trades_usdt(fee, use_db: bool = True):
add_trade(trade) add_trade(trade)
if use_db: if use_db:
Trade.query.session.flush() Trade.commit()
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)

View File

@ -702,7 +702,7 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
assert hasattr(hyperopt, "position_stacking") assert hasattr(hyperopt, "position_stacking")
def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None: def test_simplified_interface_all_failed(mocker, hyperopt_conf, caplog) -> None:
mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
@ -724,7 +724,13 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None:
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"): with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"):
hyperopt.start() hyperopt.init_spaces()
hyperopt.config['hyperopt_ignore_missing_space'] = True
caplog.clear()
hyperopt.init_spaces()
assert log_has_re(r"The 'protection' space is included into *", caplog)
assert hyperopt.protection_space == []
def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None: def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:

View File

@ -665,11 +665,11 @@ def test_PerformanceFilter_error(mocker, whitelist_conf, caplog) -> None:
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee) -> None: def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee, caplog) -> None:
whitelist_conf['exchange']['pair_whitelist'].append('XRP/BTC') whitelist_conf['exchange']['pair_whitelist'].append('XRP/BTC')
whitelist_conf['pairlists'] = [ whitelist_conf['pairlists'] = [
{"method": "StaticPairList"}, {"method": "StaticPairList"},
{"method": "PerformanceFilter", "minutes": 60} {"method": "PerformanceFilter", "minutes": 60, "min_profit": 0.01}
] ]
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
exchange = get_patched_exchange(mocker, whitelist_conf) exchange = get_patched_exchange(mocker, whitelist_conf)
@ -681,7 +681,8 @@ def test_PerformanceFilter_lookback(mocker, whitelist_conf, fee) -> None:
with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: with time_machine.travel("2021-09-01 05:00:00 +00:00") as t:
create_mock_trades(fee) create_mock_trades(fee)
pm.refresh_pairlist() pm.refresh_pairlist()
assert pm.whitelist == ['XRP/BTC', 'ETH/BTC', 'TKN/BTC'] assert pm.whitelist == ['XRP/BTC']
assert log_has_re(r'Removing pair .* since .* is below .*', caplog)
# Move to "outside" of lookback window, so original sorting is restored. # Move to "outside" of lookback window, so original sorting is restored.
t.move_to("2021-09-01 07:00:00 +00:00") t.move_to("2021-09-01 07:00:00 +00:00")

View File

@ -95,7 +95,7 @@ def test_api_not_found(botclient):
assert rc.json() == {"detail": "Not Found"} assert rc.json() == {"detail": "Not Found"}
def test_api_ui_fallback(botclient): def test_api_ui_fallback(botclient, mocker):
ftbot, client = botclient ftbot, client = botclient
rc = client_get(client, "/favicon.ico") rc = client_get(client, "/favicon.ico")
@ -109,9 +109,16 @@ def test_api_ui_fallback(botclient):
rc = client_get(client, "/something") rc = client_get(client, "/something")
assert rc.status_code == 200 assert rc.status_code == 200
# Test directory traversal # Test directory traversal without mock
rc = client_get(client, '%2F%2F%2Fetc/passwd') rc = client_get(client, '%2F%2F%2Fetc/passwd')
assert rc.status_code == 200 assert rc.status_code == 200
# Allow both fallback or real UI
assert '`freqtrade install-ui`' in rc.text or '<!DOCTYPE html>' in rc.text
mocker.patch.object(Path, 'is_file', MagicMock(side_effect=[True, False]))
rc = client_get(client, '%2F%2F%2Fetc/passwd')
assert rc.status_code == 200
assert '`freqtrade install-ui`' in rc.text assert '`freqtrade install-ui`' in rc.text
@ -563,7 +570,6 @@ def test_api_trades(botclient, mocker, fee, markets):
assert rc.json()['total_trades'] == 0 assert rc.json()['total_trades'] == 0
create_mock_trades(fee) create_mock_trades(fee)
Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/trades") rc = client_get(client, f"{BASE_URI}/trades")
assert_response(rc) assert_response(rc)
@ -590,7 +596,6 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets):
assert rc.json()['detail'] == 'Trade not found.' assert rc.json()['detail'] == 'Trade not found.'
create_mock_trades(fee) create_mock_trades(fee)
Trade.query.session.flush()
rc = client_get(client, f"{BASE_URI}/trade/3") rc = client_get(client, f"{BASE_URI}/trade/3")
assert_response(rc) assert_response(rc)
@ -613,10 +618,11 @@ def test_api_delete_trade(botclient, mocker, fee, markets):
assert_response(rc, 502) assert_response(rc, 502)
create_mock_trades(fee) create_mock_trades(fee)
Trade.query.session.flush()
ftbot.strategy.order_types['stoploss_on_exchange'] = True ftbot.strategy.order_types['stoploss_on_exchange'] = True
trades = Trade.query.all() trades = Trade.query.all()
trades[1].stoploss_order_id = '1234' trades[1].stoploss_order_id = '1234'
Trade.commit()
assert len(trades) > 2 assert len(trades) > 2
rc = client_delete(client, f"{BASE_URI}/trades/1") rc = client_delete(client, f"{BASE_URI}/trades/1")
@ -686,7 +692,6 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
assert rc.json() == {"error": "Error querying /api/v1/edge: Edge is not enabled."} assert rc.json() == {"error": "Error querying /api/v1/edge: Edge is not enabled."}
@pytest.mark.usefixtures("init_persistence")
def test_api_profit(botclient, mocker, ticker, fee, markets): def test_api_profit(botclient, mocker, ticker, fee, markets):
ftbot, client = botclient ftbot, client = botclient
patch_get_signal(ftbot) patch_get_signal(ftbot)
@ -737,7 +742,6 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
} }
@pytest.mark.usefixtures("init_persistence")
def test_api_stats(botclient, mocker, ticker, fee, markets,): def test_api_stats(botclient, mocker, ticker, fee, markets,):
ftbot, client = botclient ftbot, client = botclient
patch_get_signal(ftbot) patch_get_signal(ftbot)
@ -803,7 +807,7 @@ def test_api_performance(botclient, fee):
trade.close_profit_abs = trade.calc_profit() trade.close_profit_abs = trade.calc_profit()
Trade.query.session.add(trade) Trade.query.session.add(trade)
Trade.query.session.flush() Trade.commit()
rc = client_get(client, f"{BASE_URI}/performance") rc = client_get(client, f"{BASE_URI}/performance")
assert_response(rc) assert_response(rc)

View File

@ -3378,9 +3378,9 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
fetch_ticker=MagicMock(return_value={ fetch_ticker=MagicMock(return_value={
'bid': 0.00000172, 'bid': 2.0,
'ask': 0.00000173, 'ask': 2.0,
'last': 0.00000172 'last': 2.0
}), }),
create_order=MagicMock(side_effect=[ create_order=MagicMock(side_effect=[
limit_buy_order_usdt_open, limit_buy_order_usdt_open,
@ -3408,7 +3408,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_buy_order_usd
# Test if buy-signal is absent # Test if buy-signal is absent
patch_get_signal(freqtrade, value=(False, True, None)) patch_get_signal(freqtrade, value=(False, True, None))
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.SELL_SIGNAL.value assert trade.sell_reason == SellType.ROI.value
def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog, def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog,