merge develop

This commit is contained in:
Wagner Costa Santos 2022-09-10 10:35:16 -03:00
commit a1f88cca80
23 changed files with 253 additions and 176 deletions

View File

@ -1,4 +1,4 @@
FROM python:3.10.6-slim-bullseye as base FROM python:3.10.7-slim-bullseye as base
# Setup env # Setup env
ENV LANG C.UTF-8 ENV LANG C.UTF-8

View File

@ -107,7 +107,7 @@ Strategy arguments:
## Test your strategy with Backtesting ## Test your strategy with Backtesting
Now you have good Buy and Sell strategies and some historic data, you want to test it against Now you have good Entry and exit strategies and some historic data, you want to test it against
real data. This is what we call [backtesting](https://en.wikipedia.org/wiki/Backtesting). real data. This is what we call [backtesting](https://en.wikipedia.org/wiki/Backtesting).
Backtesting will use the crypto-currencies (pairs) from your config file and load historical candle (OHLCV) data from `user_data/data/<exchange>` by default. Backtesting will use the crypto-currencies (pairs) from your config file and load historical candle (OHLCV) data from `user_data/data/<exchange>` by default.
@ -215,7 +215,7 @@ Sometimes your account has certain fee rebates (fee reductions starting with a c
To account for this in backtesting, you can use the `--fee` command line option to supply this value to backtesting. To account for this in backtesting, you can use the `--fee` command line option to supply this value to backtesting.
This fee must be a ratio, and will be applied twice (once for trade entry, and once for trade exit). This fee must be a ratio, and will be applied twice (once for trade entry, and once for trade exit).
For example, if the buying and selling commission fee is 0.1% (i.e., 0.001 written as ratio), then you would run backtesting as the following: For example, if the commission fee per order is 0.1% (i.e., 0.001 written as ratio), then you would run backtesting as the following:
```bash ```bash
freqtrade backtesting --fee 0.001 freqtrade backtesting --fee 0.001
@ -252,41 +252,41 @@ The most important in the backtesting is to understand the result.
A backtesting result will look like that: A backtesting result will look like that:
``` ```
========================================================= BACKTESTING REPORT ========================================================== ========================================================= BACKTESTING REPORT =========================================================
| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% | | Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% |
|:---------|-------:|---------------:|---------------:|-----------------:|---------------:|:-------------|-------------------------:| |:---------|--------:|---------------:|---------------:|-----------------:|---------------:|:-------------|-------------------------:|
| ADA/BTC | 35 | -0.11 | -3.88 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 | | ADA/BTC | 35 | -0.11 | -3.88 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 |
| ARK/BTC | 11 | -0.41 | -4.52 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 | | ARK/BTC | 11 | -0.41 | -4.52 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 |
| BTS/BTC | 32 | 0.31 | 9.78 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 | | BTS/BTC | 32 | 0.31 | 9.78 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 |
| DASH/BTC | 13 | -0.08 | -1.07 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 | | DASH/BTC | 13 | -0.08 | -1.07 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 |
| ENG/BTC | 18 | 1.36 | 24.54 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 | | ENG/BTC | 18 | 1.36 | 24.54 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 |
| EOS/BTC | 36 | 0.08 | 3.06 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 | | EOS/BTC | 36 | 0.08 | 3.06 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 |
| ETC/BTC | 26 | 0.37 | 9.51 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 | | ETC/BTC | 26 | 0.37 | 9.51 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 |
| ETH/BTC | 33 | 0.30 | 9.96 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 | | ETH/BTC | 33 | 0.30 | 9.96 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 |
| IOTA/BTC | 32 | 0.03 | 1.09 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 | | IOTA/BTC | 32 | 0.03 | 1.09 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 |
| LSK/BTC | 15 | 1.75 | 26.26 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 | | LSK/BTC | 15 | 1.75 | 26.26 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 |
| LTC/BTC | 32 | -0.04 | -1.38 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 | | LTC/BTC | 32 | -0.04 | -1.38 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 |
| NANO/BTC | 17 | 1.26 | 21.39 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 | | NANO/BTC | 17 | 1.26 | 21.39 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 |
| NEO/BTC | 23 | 0.82 | 18.97 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 | | NEO/BTC | 23 | 0.82 | 18.97 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 |
| REQ/BTC | 9 | 1.17 | 10.54 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 | | REQ/BTC | 9 | 1.17 | 10.54 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 |
| XLM/BTC | 16 | 1.22 | 19.54 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 | | XLM/BTC | 16 | 1.22 | 19.54 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 |
| XMR/BTC | 23 | -0.18 | -4.13 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 | | XMR/BTC | 23 | -0.18 | -4.13 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 |
| XRP/BTC | 35 | 0.66 | 22.96 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 | | XRP/BTC | 35 | 0.66 | 22.96 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 |
| ZEC/BTC | 22 | -0.46 | -10.18 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 | | ZEC/BTC | 22 | -0.46 | -10.18 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 |
| TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 | | TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 |
========================================================= EXIT REASON STATS ========================================================== ========================================================= EXIT REASON STATS ==========================================================
| Exit Reason | Sells | Wins | Draws | Losses | | Exit Reason | Exits | Wins | Draws | Losses |
|:-------------------|--------:|------:|-------:|--------:| |:-------------------|--------:|------:|-------:|--------:|
| trailing_stop_loss | 205 | 150 | 0 | 55 | | trailing_stop_loss | 205 | 150 | 0 | 55 |
| stop_loss | 166 | 0 | 0 | 166 | | stop_loss | 166 | 0 | 0 | 166 |
| exit_signal | 56 | 36 | 0 | 20 | | exit_signal | 56 | 36 | 0 | 20 |
| force_exit | 2 | 0 | 0 | 2 | | force_exit | 2 | 0 | 0 | 2 |
====================================================== LEFT OPEN TRADES REPORT ====================================================== ====================================================== LEFT OPEN TRADES REPORT ======================================================
| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% | | Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% |
|:---------|-------:|---------------:|---------------:|-----------------:|---------------:|:---------------|--------------------:| |:---------|---------:|---------------:|---------------:|-----------------:|---------------:|:---------------|--------------------:|
| ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 | | ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 |
| LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 | | LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 |
| TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 | | TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 |
================== SUMMARY METRICS ================== ================== SUMMARY METRICS ==================
| Metric | Value | | Metric | Value |
|-----------------------------+---------------------| |-----------------------------+---------------------|
@ -356,7 +356,7 @@ The column `Avg Profit %` shows the average profit for all trades made while the
The column `Tot Profit %` shows instead the total profit % in relation to the starting balance. The column `Tot Profit %` shows instead the total profit % in relation to the starting balance.
In the above results, we have a starting balance of 0.01 BTC and an absolute profit of 0.00762792 BTC - so the `Tot Profit %` will be `(0.00762792 / 0.01) * 100 ~= 76.2%`. In the above results, we have a starting balance of 0.01 BTC and an absolute profit of 0.00762792 BTC - so the `Tot Profit %` will be `(0.00762792 / 0.01) * 100 ~= 76.2%`.
Your strategy performance is influenced by your buy strategy, your exit strategy, and also by the `minimal_roi` and `stop_loss` you have set. Your strategy performance is influenced by your entry strategy, your exit strategy, and also by the `minimal_roi` and `stop_loss` you have set.
For example, if your `minimal_roi` is only `"0": 0.01` you cannot expect the bot to make more profit than 1% (because it will exit every time a trade reaches 1%). For example, if your `minimal_roi` is only `"0": 0.01` you cannot expect the bot to make more profit than 1% (because it will exit every time a trade reaches 1%).
@ -515,7 +515,7 @@ You can then load the trades to perform further analysis as shown in the [data a
Since backtesting lacks some detailed information about what happens within a candle, it needs to take a few assumptions: Since backtesting lacks some detailed information about what happens within a candle, it needs to take a few assumptions:
- Exchange [trading limits](#trading-limits-in-backtesting) are respected - Exchange [trading limits](#trading-limits-in-backtesting) are respected
- Buys happen at open-price - Entries happen at open-price
- All orders are filled at the requested price (no slippage, no unfilled orders) - All orders are filled at the requested price (no slippage, no unfilled orders)
- Exit-signal exits happen at open-price of the consecutive candle - Exit-signal exits happen at open-price of the consecutive candle
- Exit-signal is favored over Stoploss, because exit-signals are assumed to trigger on candle's open - Exit-signal is favored over Stoploss, because exit-signals are assumed to trigger on candle's open
@ -612,11 +612,11 @@ There will be an additional table comparing win/losses of the different strategi
Detailed output for all strategies one after the other will be available, so make sure to scroll up to see the details per strategy. Detailed output for all strategies one after the other will be available, so make sure to scroll up to see the details per strategy.
``` ```
=========================================================== STRATEGY SUMMARY ========================================================================= =========================================================== STRATEGY SUMMARY ===========================================================================
| Strategy | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % | | Strategy | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % |
|:------------|-------:|---------------:|---------------:|-----------------:|---------------:|:---------------|------:|-------:|-------:|-----------:| |:------------|---------:|---------------:|---------------:|-----------------:|---------------:|:---------------|------:|-------:|-------:|-----------:|
| Strategy1 | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 | | Strategy1 | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 |
| Strategy2 | 1487 | -0.13 | -197.58 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 | | Strategy2 | 1487 | -0.13 | -197.58 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 |
``` ```
## Next step ## Next step

View File

@ -4,7 +4,7 @@
Freqtrade supports spot trading only. Freqtrade supports spot trading only.
### Can I open short positions? ### Can my bot open short positions?
Freqtrade can open short positions in futures markets. Freqtrade can open short positions in futures markets.
This requires the strategy to be made for this - and `"trading_mode": "futures"` in the configuration. This requires the strategy to be made for this - and `"trading_mode": "futures"` in the configuration.
@ -12,9 +12,9 @@ Please make sure to read the [relevant documentation page](leverage.md) first.
In spot markets, you can in some cases use leveraged spot tokens, which reflect an inverted pair (eg. BTCUP/USD, BTCDOWN/USD, ETHBULL/USD, ETHBEAR/USD,...) which can be traded with Freqtrade. In spot markets, you can in some cases use leveraged spot tokens, which reflect an inverted pair (eg. BTCUP/USD, BTCDOWN/USD, ETHBULL/USD, ETHBEAR/USD,...) which can be traded with Freqtrade.
### Can I trade options or futures? ### Can my bot trade options or futures?
Futures trading is supported for selected exchanges. Futures trading is supported for selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges-experimental) for an uptodate list of supported exchanges.
## Beginner Tips & Tricks ## Beginner Tips & Tricks
@ -22,6 +22,13 @@ Futures trading is supported for selected exchanges.
## Freqtrade common issues ## Freqtrade common issues
### Can freqtrade open multiple positions on the same pair in parallel?
No. Freqtrade will only open one position per pair at a time.
You can however use the [`adjust_trade_position()` callback](strategy-callbacks.md#adjust-trade-position) to adjust an open position.
Backtesting provides an option for this in `--eps` - however this is only there to highlight "hidden" signals, and will not work in live.
### The bot does not start ### The bot does not start
Running the bot with `freqtrade trade --config config.json` shows the output `freqtrade: command not found`. Running the bot with `freqtrade trade --config config.json` shows the output `freqtrade: command not found`.
@ -30,7 +37,7 @@ This could be caused by the following reasons:
* The virtual environment is not active. * The virtual environment is not active.
* Run `source .env/bin/activate` to activate the virtual environment. * Run `source .env/bin/activate` to activate the virtual environment.
* The installation did not work correctly. * The installation did not complete successfully.
* Please check the [Installation documentation](installation.md). * Please check the [Installation documentation](installation.md).
### I have waited 5 minutes, why hasn't the bot made any trades yet? ### I have waited 5 minutes, why hasn't the bot made any trades yet?

View File

@ -90,7 +90,8 @@ Example configuration showing the different settings:
"trailing_stop_loss": "on", "trailing_stop_loss": "on",
"stop_loss": "on", "stop_loss": "on",
"stoploss_on_exchange": "on", "stoploss_on_exchange": "on",
"custom_exit": "silent" "custom_exit": "silent",
"partial_exit": "on"
}, },
"entry_cancel": "silent", "entry_cancel": "silent",
"exit_cancel": "on", "exit_cancel": "on",

View File

@ -4,7 +4,7 @@ from typing import Any, Dict
from sqlalchemy import func from sqlalchemy import func
from freqtrade.configuration.config_setup import setup_utils_configuration from freqtrade.configuration.config_setup import setup_utils_configuration
from freqtrade.enums.runmode import RunMode from freqtrade.enums import RunMode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -228,9 +228,9 @@ def _download_pair_history(pair: str, *,
) )
logger.debug("Current Start: %s", logger.debug("Current Start: %s",
f"{data.iloc[0]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') f"{data.iloc[0]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None')
logger.debug("Current End: %s", logger.debug("Current End: %s",
f"{data.iloc[-1]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') f"{data.iloc[-1]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None')
# Default since_ms to 30 days if nothing is given # Default since_ms to 30 days if nothing is given
new_data = exchange.get_historic_ohlcv(pair=pair, new_data = exchange.get_historic_ohlcv(pair=pair,
@ -254,9 +254,9 @@ def _download_pair_history(pair: str, *,
fill_missing=False, drop_incomplete=False) fill_missing=False, drop_incomplete=False)
logger.debug("New Start: %s", logger.debug("New Start: %s",
f"{data.iloc[0]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') f"{data.iloc[0]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None')
logger.debug("New End: %s", logger.debug("New End: %s",
f"{data.iloc[-1]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') f"{data.iloc[-1]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None')
data_handler.ohlcv_store(pair, timeframe, data=data, candle_type=candle_type) data_handler.ohlcv_store(pair, timeframe, data=data, candle_type=candle_type)
return True return True

View File

@ -2509,8 +2509,13 @@ class Exchange:
cache=False, cache=False,
drop_incomplete=False, drop_incomplete=False,
) )
funding_rates = candle_histories[funding_comb] try:
mark_rates = candle_histories[mark_comb] # we can't assume we always get histories - for example during exchange downtimes
funding_rates = candle_histories[funding_comb]
mark_rates = candle_histories[mark_comb]
except KeyError:
raise ExchangeError("Could not find funding rates.") from None
funding_mark_rates = self.combine_funding_and_mark( funding_mark_rates = self.combine_funding_and_mark(
funding_rates=funding_rates, mark_rates=mark_rates) funding_rates=funding_rates, mark_rates=mark_rates)
@ -2590,6 +2595,8 @@ class Exchange:
:param is_short: trade direction :param is_short: trade direction
:param amount: Trade amount :param amount: Trade amount
:param open_date: Open date of the trade :param open_date: Open date of the trade
:return: funding fee since open_date
:raies: ExchangeError if something goes wrong.
""" """
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
if self._config['dry_run']: if self._config['dry_run']:

View File

@ -4,8 +4,7 @@ from typing import Dict, List, Optional, Tuple
import ccxt import ccxt
from freqtrade.constants import BuySell from freqtrade.constants import BuySell
from freqtrade.enums import MarginMode, TradingMode from freqtrade.enums import CandleType, MarginMode, TradingMode
from freqtrade.enums.candletype import CandleType
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
from freqtrade.exchange import Exchange, date_minus_candles from freqtrade.exchange import Exchange, date_minus_candles
from freqtrade.exchange.common import retrier from freqtrade.exchange.common import retrier

View File

@ -14,6 +14,7 @@ from numpy.typing import NDArray
from pandas import DataFrame from pandas import DataFrame
from freqtrade.configuration import TimeRange from freqtrade.configuration import TimeRange
from freqtrade.constants import DATETIME_PRINT_FORMAT
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_seconds from freqtrade.exchange import timeframe_to_seconds
@ -232,10 +233,10 @@ class IFreqaiModel(ABC):
trained_timestamp = tr_train trained_timestamp = tr_train
tr_train_startts_str = datetime.fromtimestamp( tr_train_startts_str = datetime.fromtimestamp(
tr_train.startts, tr_train.startts,
tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S") tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT)
tr_train_stopts_str = datetime.fromtimestamp( tr_train_stopts_str = datetime.fromtimestamp(
tr_train.stopts, tr_train.stopts,
tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S") tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT)
logger.info( logger.info(
f"Training {metadata['pair']}, {self.pair_it}/{self.total_pairs} pairs" f"Training {metadata['pair']}, {self.pair_it}/{self.total_pairs} pairs"
f" from {tr_train_startts_str} to {tr_train_stopts_str}, {train_it}/{total_trains} " f" from {tr_train_startts_str} to {tr_train_stopts_str}, {train_it}/{total_trains} "
@ -427,6 +428,11 @@ class IFreqaiModel(ABC):
ft_params = self.freqai_info["feature_parameters"] ft_params = self.freqai_info["feature_parameters"]
if ft_params.get('inlier_metric_window', 0):
dk.compute_inlier_metric(set_='train')
if self.freqai_info["data_split_parameters"]["test_size"] > 0:
dk.compute_inlier_metric(set_='test')
if ft_params.get( if ft_params.get(
"principal_component_analysis", False "principal_component_analysis", False
): ):
@ -446,11 +452,6 @@ class IFreqaiModel(ABC):
dk.use_DBSCAN_to_remove_outliers(predict=False, eps=eps) dk.use_DBSCAN_to_remove_outliers(predict=False, eps=eps)
self.dd.old_DBSCAN_eps[dk.pair] = dk.data['DBSCAN_eps'] self.dd.old_DBSCAN_eps[dk.pair] = dk.data['DBSCAN_eps']
if ft_params.get('inlier_metric_window', 0):
dk.compute_inlier_metric(set_='train')
if self.freqai_info["data_split_parameters"]["test_size"] > 0:
dk.compute_inlier_metric(set_='test')
if self.freqai_info["feature_parameters"].get('noise_standard_deviation', 0): if self.freqai_info["feature_parameters"].get('noise_standard_deviation', 0):
dk.add_noise_to_training_features() dk.add_noise_to_training_features()
@ -467,7 +468,7 @@ class IFreqaiModel(ABC):
if ft_params.get( if ft_params.get(
"principal_component_analysis", False "principal_component_analysis", False
): ):
dk.pca_transform(dataframe) dk.pca_transform(self.dk.data_dictionary['prediction_features'])
if ft_params.get("use_SVM_to_remove_outliers", False): if ft_params.get("use_SVM_to_remove_outliers", False):
dk.use_SVM_to_remove_outliers(predict=True) dk.use_SVM_to_remove_outliers(predict=True)

View File

@ -281,16 +281,17 @@ class FreqtradeBot(LoggingMixin):
def update_funding_fees(self): def update_funding_fees(self):
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
trades = Trade.get_open_trades() trades = Trade.get_open_trades()
for trade in trades: try:
funding_fees = self.exchange.get_funding_fees( for trade in trades:
pair=trade.pair, funding_fees = self.exchange.get_funding_fees(
amount=trade.amount, pair=trade.pair,
is_short=trade.is_short, amount=trade.amount,
open_date=trade.date_last_filled_utc is_short=trade.is_short,
) open_date=trade.date_last_filled_utc
trade.funding_fees = funding_fees )
else: trade.funding_fees = funding_fees
return 0.0 except ExchangeError:
logger.warning("Could not update funding fees for open trades.")
def startup_backpopulate_precision(self): def startup_backpopulate_precision(self):
@ -671,14 +672,12 @@ class FreqtradeBot(LoggingMixin):
if not stake_amount: if not stake_amount:
return False return False
if pos_adjust: msg = (f"Position adjust: about to create a new order for {pair} with stake: "
logger.info(f"Position adjust: about to create a new order for {pair} with stake: " f"{stake_amount} for {trade}" if pos_adjust
f"{stake_amount} for {trade}") else
else: f"{name} signal found: about create a new trade for {pair} with stake_amount: "
logger.info( f"{stake_amount} ...")
f"{name} signal found: about create a new trade for {pair} with stake_amount: " logger.info(msg)
f"{stake_amount} ...")
amount = (stake_amount / enter_limit_requested) * leverage amount = (stake_amount / enter_limit_requested) * leverage
order_type = ordertype or self.strategy.order_types['entry'] order_type = ordertype or self.strategy.order_types['entry']
@ -741,8 +740,13 @@ class FreqtradeBot(LoggingMixin):
# This is a new trade # This is a new trade
if trade is None: if trade is None:
funding_fees = self.exchange.get_funding_fees( funding_fees = 0.0
pair=pair, amount=amount, is_short=is_short, open_date=open_date) try:
funding_fees = self.exchange.get_funding_fees(
pair=pair, amount=amount, is_short=is_short, open_date=open_date)
except ExchangeError:
logger.warning("Could not find funding fee.")
trade = Trade( trade = Trade(
pair=pair, pair=pair,
base_currency=base_currency, base_currency=base_currency,
@ -1493,12 +1497,16 @@ class FreqtradeBot(LoggingMixin):
:param exit_check: CheckTuple with signal and reason :param exit_check: CheckTuple with signal and reason
:return: True if it succeeds False :return: True if it succeeds False
""" """
trade.funding_fees = self.exchange.get_funding_fees( try:
pair=trade.pair, trade.funding_fees = self.exchange.get_funding_fees(
amount=trade.amount, pair=trade.pair,
is_short=trade.is_short, amount=trade.amount,
open_date=trade.date_last_filled_utc, is_short=trade.is_short,
) open_date=trade.date_last_filled_utc,
)
except ExchangeError:
logger.warning("Could not update funding fee.")
exit_type = 'exit' exit_type = 'exit'
exit_reason = exit_tag or exit_check.exit_reason exit_reason = exit_tag or exit_check.exit_reason
if exit_check.exit_type in ( if exit_check.exit_type in (

View File

@ -75,7 +75,8 @@ def _get_line_floatfmt(stake_currency: str) -> List[str]:
'.2f', 'd', 's', 's'] '.2f', 'd', 's', 's']
def _get_line_header(first_column: str, stake_currency: str, direction: str = 'Buys') -> List[str]: def _get_line_header(first_column: str, stake_currency: str,
direction: str = 'Entries') -> List[str]:
""" """
Generate header lines (goes in line with _generate_result_line()) Generate header lines (goes in line with _generate_result_line())
""" """
@ -642,7 +643,7 @@ def text_table_tags(tag_type: str, tag_results: List[Dict[str, Any]], stake_curr
if (tag_type == "enter_tag"): if (tag_type == "enter_tag"):
headers = _get_line_header("TAG", stake_currency) headers = _get_line_header("TAG", stake_currency)
else: else:
headers = _get_line_header("TAG", stake_currency, 'Sells') headers = _get_line_header("TAG", stake_currency, 'Exits')
floatfmt = _get_line_floatfmt(stake_currency) floatfmt = _get_line_floatfmt(stake_currency)
output = [ output = [
[ [

View File

@ -1,7 +1,7 @@
import logging import logging
from typing import Any, Dict from typing import Any, Dict
from freqtrade.enums.rpcmessagetype import RPCMessageType from freqtrade.enums import RPCMessageType
from freqtrade.rpc import RPC from freqtrade.rpc import RPC
from freqtrade.rpc.webhook import Webhook from freqtrade.rpc.webhook import Webhook

View File

@ -12,9 +12,8 @@ from pandas import DataFrame
from freqtrade.constants import ListPairsWithTimeframes from freqtrade.constants import ListPairsWithTimeframes
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, SignalTagType, from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection,
SignalType, TradingMode) SignalTagType, SignalType, TradingMode)
from freqtrade.enums.runmode import RunMode
from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exceptions import OperationalException, StrategyError
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds
from freqtrade.persistence import Order, PairLocks, Trade from freqtrade.persistence import Order, PairLocks, Trade

View File

@ -7,7 +7,7 @@ from abc import ABC, abstractmethod
from contextlib import suppress from contextlib import suppress
from typing import Any, Optional, Sequence, Union from typing import Any, Optional, Sequence, Union
from freqtrade.enums.hyperoptstate import HyperoptState from freqtrade.enums import HyperoptState
from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer

View File

@ -1,3 +1,5 @@
from typing import Optional
import pandas as pd import pandas as pd
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
@ -6,7 +8,8 @@ from freqtrade.exchange import timeframe_to_minutes
def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame,
timeframe: str, timeframe_inf: str, ffill: bool = True, timeframe: str, timeframe_inf: str, ffill: bool = True,
append_timeframe: bool = True, append_timeframe: bool = True,
date_column: str = 'date') -> pd.DataFrame: date_column: str = 'date',
suffix: Optional[str] = None) -> pd.DataFrame:
""" """
Correctly merge informative samples to the original dataframe, avoiding lookahead bias. Correctly merge informative samples to the original dataframe, avoiding lookahead bias.
@ -28,6 +31,8 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame,
:param ffill: Forwardfill missing values - optional but usually required :param ffill: Forwardfill missing values - optional but usually required
:param append_timeframe: Rename columns by appending timeframe. :param append_timeframe: Rename columns by appending timeframe.
:param date_column: A custom date column name. :param date_column: A custom date column name.
:param suffix: A string suffix to add at the end of the informative columns. If specified,
append_timeframe must be false.
:return: Merged dataframe :return: Merged dataframe
:raise: ValueError if the secondary timeframe is shorter than the dataframe timeframe :raise: ValueError if the secondary timeframe is shorter than the dataframe timeframe
""" """
@ -50,10 +55,16 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame,
# Rename columns to be unique # Rename columns to be unique
date_merge = 'date_merge' date_merge = 'date_merge'
if append_timeframe: if suffix and append_timeframe:
raise ValueError("You can not specify `append_timeframe` as True and a `suffix`.")
elif append_timeframe:
date_merge = f'date_merge_{timeframe_inf}' date_merge = f'date_merge_{timeframe_inf}'
informative.columns = [f"{col}_{timeframe_inf}" for col in informative.columns] informative.columns = [f"{col}_{timeframe_inf}" for col in informative.columns]
elif suffix:
date_merge = f'date_merge_{suffix}'
informative.columns = [f"{col}_{suffix}" for col in informative.columns]
# Combine the 2 dataframes # Combine the 2 dataframes
# all indicators on the informative sample MUST be calculated before this point # all indicators on the informative sample MUST be calculated before this point
if ffill: if ffill:

View File

@ -13,7 +13,7 @@ from pandas import DataFrame
from pandas.testing import assert_frame_equal from pandas.testing import assert_frame_equal
from freqtrade.configuration import TimeRange from freqtrade.configuration import TimeRange
from freqtrade.constants import AVAILABLE_DATAHANDLERS from freqtrade.constants import AVAILABLE_DATAHANDLERS, DATETIME_PRINT_FORMAT
from freqtrade.data.converter import ohlcv_to_dataframe from freqtrade.data.converter import ohlcv_to_dataframe
from freqtrade.data.history.hdf5datahandler import HDF5DataHandler from freqtrade.data.history.hdf5datahandler import HDF5DataHandler
from freqtrade.data.history.history_utils import (_download_pair_history, _download_trades_history, from freqtrade.data.history.history_utils import (_download_pair_history, _download_trades_history,
@ -386,7 +386,7 @@ def test_load_partial_missing(testdatadir, caplog) -> None:
assert td != len(data['UNITTEST/BTC']) assert td != len(data['UNITTEST/BTC'])
start_real = data['UNITTEST/BTC'].iloc[0, 0] start_real = data['UNITTEST/BTC'].iloc[0, 0]
assert log_has(f'UNITTEST/BTC, spot, 5m, ' assert log_has(f'UNITTEST/BTC, spot, 5m, '
f'data starts at {start_real.strftime("%Y-%m-%d %H:%M:%S")}', f'data starts at {start_real.strftime(DATETIME_PRINT_FORMAT)}',
caplog) caplog)
# Make sure we start fresh - test missing data at end # Make sure we start fresh - test missing data at end
caplog.clear() caplog.clear()
@ -401,7 +401,7 @@ def test_load_partial_missing(testdatadir, caplog) -> None:
# Shift endtime with +5 - as last candle is dropped (partial candle) # Shift endtime with +5 - as last candle is dropped (partial candle)
end_real = arrow.get(data['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5) end_real = arrow.get(data['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5)
assert log_has(f'UNITTEST/BTC, spot, 5m, ' assert log_has(f'UNITTEST/BTC, spot, 5m, '
f'data ends at {end_real.strftime("%Y-%m-%d %H:%M:%S")}', f'data ends at {end_real.strftime(DATETIME_PRINT_FORMAT)}',
caplog) caplog)

View File

@ -11,8 +11,9 @@ import pytest
from pandas import DataFrame from pandas import DataFrame
from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.enums import CandleType, MarginMode, TradingMode
from freqtrade.exceptions import (DDosProtection, DependencyException, InvalidOrderException, from freqtrade.exceptions import (DDosProtection, DependencyException, ExchangeError,
OperationalException, PricingError, TemporaryError) InvalidOrderException, OperationalException, PricingError,
TemporaryError)
from freqtrade.exchange import (Binance, Bittrex, Exchange, Kraken, amount_to_precision, from freqtrade.exchange import (Binance, Bittrex, Exchange, Kraken, amount_to_precision,
date_minus_candles, market_is_active, price_to_precision, date_minus_candles, market_is_active, price_to_precision,
timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date,
@ -4179,17 +4180,24 @@ def test__fetch_and_calculate_funding_fees(
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True}) type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True})
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock( mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock(
return_value=['1h', '4h', '8h'])) return_value=['1h', '4h', '8h']))
funding_fees = exchange._fetch_and_calculate_funding_fees( funding_fees = ex._fetch_and_calculate_funding_fees(
pair='ADA/USDT', amount=amount, is_short=True, open_date=d1, close_date=d2) pair='ADA/USDT', amount=amount, is_short=True, open_date=d1, close_date=d2)
assert pytest.approx(funding_fees) == expected_fees assert pytest.approx(funding_fees) == expected_fees
# Fees for Longs are inverted # Fees for Longs are inverted
funding_fees = exchange._fetch_and_calculate_funding_fees( funding_fees = ex._fetch_and_calculate_funding_fees(
pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2) pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2)
assert pytest.approx(funding_fees) == -expected_fees assert pytest.approx(funding_fees) == -expected_fees
# Return empty "refresh_latest"
mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", return_value={})
ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange)
with pytest.raises(ExchangeError, match="Could not find funding rates."):
ex._fetch_and_calculate_funding_fees(
pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2)
@pytest.mark.parametrize('exchange,expected_fees', [ @pytest.mark.parametrize('exchange,expected_fees', [
('binance', -0.0009140999999999999), ('binance', -0.0009140999999999999),

View File

@ -4,8 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
import pytest import pytest
from freqtrade.enums import MarginMode, TradingMode from freqtrade.enums import CandleType, MarginMode, TradingMode
from freqtrade.enums.candletype import CandleType
from freqtrade.exchange.exchange import timeframe_to_minutes from freqtrade.exchange.exchange import timeframe_to_minutes
from tests.conftest import get_mock_coro, get_patched_exchange, log_has from tests.conftest import get_mock_coro, get_patched_exchange, log_has
from tests.exchange.test_exchange import ccxt_exceptionhandlers from tests.exchange.test_exchange import ccxt_exceptionhandlers

View File

@ -40,14 +40,14 @@ def test_text_table_bt_results():
) )
result_str = ( result_str = (
'| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % |' '| Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | '
' Avg Duration | Win Draw Loss Win% |\n' 'Tot Profit % | Avg Duration | Win Draw Loss Win% |\n'
'|---------+--------+----------------+----------------+------------------+----------------+' '|---------+-----------+----------------+----------------+------------------+'
'----------------+-------------------------|\n' '----------------+----------------+-------------------------|\n'
'| ETH/BTC | 3 | 8.33 | 25.00 | 0.50000000 | 12.50 |' '| ETH/BTC | 3 | 8.33 | 25.00 | 0.50000000 | '
' 0:20:00 | 2 0 1 66.7 |\n' '12.50 | 0:20:00 | 2 0 1 66.7 |\n'
'| TOTAL | 3 | 8.33 | 25.00 | 0.50000000 | 12.50 |' '| TOTAL | 3 | 8.33 | 25.00 | 0.50000000 | '
' 0:20:00 | 2 0 1 66.7 |' '12.50 | 0:20:00 | 2 0 1 66.7 |'
) )
pair_results = generate_pair_metrics(['ETH/BTC'], stake_currency='BTC', pair_results = generate_pair_metrics(['ETH/BTC'], stake_currency='BTC',
@ -402,13 +402,13 @@ def test_text_table_strategy(testdatadir):
bt_res_data_comparison = bt_res_data.pop('strategy_comparison') bt_res_data_comparison = bt_res_data.pop('strategy_comparison')
result_str = ( result_str = (
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |' '| Strategy | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC |'
' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n' ' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n'
'|----------------+--------+----------------+----------------+------------------+' '|----------------+-----------+----------------+----------------+------------------+'
'----------------+----------------+-------------------------+-----------------------|\n' '----------------+----------------+-------------------------+-----------------------|\n'
'| StrategyTestV2 | 179 | 0.08 | 14.39 | 0.02608550 |' '| StrategyTestV2 | 179 | 0.08 | 14.39 | 0.02608550 |'
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n' ' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n'
'| TestStrategy | 179 | 0.08 | 14.39 | 0.02608550 |' '| TestStrategy | 179 | 0.08 | 14.39 | 0.02608550 |'
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |' ' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |'
) )

View File

@ -11,8 +11,7 @@ from pandas import DataFrame
from freqtrade.configuration import TimeRange from freqtrade.configuration import TimeRange
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.data.history import load_data from freqtrade.data.history import load_data
from freqtrade.enums import ExitCheckTuple, ExitType, SignalDirection from freqtrade.enums import ExitCheckTuple, ExitType, HyperoptState, SignalDirection
from freqtrade.enums.hyperoptstate import HyperoptState
from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exceptions import OperationalException, StrategyError
from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer
from freqtrade.optimize.space import SKDecimal from freqtrade.optimize.space import SKDecimal

View File

@ -117,6 +117,29 @@ def test_merge_informative_pair_lower():
merge_informative_pair(data, informative, '1h', '15m', ffill=True) merge_informative_pair(data, informative, '1h', '15m', ffill=True)
def test_merge_informative_pair_suffix():
data = generate_test_data('15m', 20)
informative = generate_test_data('1h', 20)
result = merge_informative_pair(data, informative, '15m', '1h',
append_timeframe=False, suffix="suf")
assert 'date' in result.columns
assert result['date'].equals(data['date'])
assert 'date_suf' in result.columns
assert 'open_suf' in result.columns
assert 'open_1h' not in result.columns
def test_merge_informative_pair_suffix_append_timeframe():
data = generate_test_data('15m', 20)
informative = generate_test_data('1h', 20)
with pytest.raises(ValueError, match=r"You can not specify `append_timeframe` .*"):
merge_informative_pair(data, informative, '15m', '1h', suffix="suf")
def test_stoploss_from_open(): def test_stoploss_from_open():
open_price_ranges = [ open_price_ranges = [
[0.01, 1.00, 30], [0.01, 1.00, 30],

View File

@ -506,7 +506,7 @@ def test_create_trades_multiple_trades(
def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
limit_buy_order_usdt_open) -> None: limit_buy_order_usdt_open, caplog) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
default_conf_usdt['max_open_trades'] = 4 default_conf_usdt['max_open_trades'] = 4
@ -515,6 +515,7 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
fetch_ticker=ticker_usdt, fetch_ticker=ticker_usdt,
create_order=MagicMock(return_value=limit_buy_order_usdt_open), create_order=MagicMock(return_value=limit_buy_order_usdt_open),
get_fee=fee, get_fee=fee,
get_funding_fees=MagicMock(side_effect=ExchangeError()),
) )
freqtrade = FreqtradeBot(default_conf_usdt) freqtrade = FreqtradeBot(default_conf_usdt)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
@ -522,6 +523,7 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
# Create 2 existing trades # Create 2 existing trades
freqtrade.execute_entry('ETH/USDT', default_conf_usdt['stake_amount']) freqtrade.execute_entry('ETH/USDT', default_conf_usdt['stake_amount'])
freqtrade.execute_entry('NEO/BTC', default_conf_usdt['stake_amount']) freqtrade.execute_entry('NEO/BTC', default_conf_usdt['stake_amount'])
assert log_has("Could not find funding fee.", caplog)
assert len(Trade.get_open_trades()) == 2 assert len(Trade.get_open_trades()) == 2
# Change order_id for new orders # Change order_id for new orders
@ -3666,7 +3668,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
(True, 29.70297029, 2.2, 2.3, -8.63762376, -0.1443212, 'loss'), (True, 29.70297029, 2.2, 2.3, -8.63762376, -0.1443212, 'loss'),
]) ])
def test_execute_trade_exit_market_order( def test_execute_trade_exit_market_order(
default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount, default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount, caplog,
limit, profit_amount, profit_ratio, profit_or_loss, ticker_usdt_sell_up, mocker limit, profit_amount, profit_ratio, profit_or_loss, ticker_usdt_sell_up, mocker
) -> None: ) -> None:
""" """
@ -3694,6 +3696,7 @@ def test_execute_trade_exit_market_order(
fetch_ticker=ticker_usdt, fetch_ticker=ticker_usdt,
get_fee=fee, get_fee=fee,
_is_dry_limit_order_filled=MagicMock(return_value=True), _is_dry_limit_order_filled=MagicMock(return_value=True),
get_funding_fees=MagicMock(side_effect=ExchangeError()),
) )
patch_whitelist(mocker, default_conf_usdt) patch_whitelist(mocker, default_conf_usdt)
freqtrade = FreqtradeBot(default_conf_usdt) freqtrade = FreqtradeBot(default_conf_usdt)
@ -3719,6 +3722,7 @@ def test_execute_trade_exit_market_order(
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'], limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
exit_check=ExitCheckTuple(exit_type=ExitType.ROI) exit_check=ExitCheckTuple(exit_type=ExitType.ROI)
) )
assert log_has("Could not update funding fee.", caplog)
assert not trade.is_open assert not trade.is_open
assert pytest.approx(trade.close_profit) == profit_ratio assert pytest.approx(trade.close_profit) == profit_ratio
@ -5430,6 +5434,16 @@ def test_update_funding_fees(
)) ))
def test_update_funding_fees_error(mocker, default_conf, caplog):
mocker.patch('freqtrade.exchange.Exchange.get_funding_fees', side_effect=ExchangeError())
default_conf['trading_mode'] = 'futures'
default_conf['margin_mode'] = 'isolated'
freqtrade = get_patched_freqtradebot(mocker, default_conf)
freqtrade.update_funding_fees()
log_has("Could not update funding fees for open trades.", caplog)
def test_position_adjust(mocker, default_conf_usdt, fee) -> None: def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)

View File

@ -9,7 +9,7 @@ import arrow
import pytest import pytest
from sqlalchemy import create_engine, text from sqlalchemy import create_engine, text
from freqtrade import constants from freqtrade.constants import DATETIME_PRINT_FORMAT, DEFAULT_DB_PROD_URL
from freqtrade.enums import TradingMode from freqtrade.enums import TradingMode
from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.persistence import LocalTrade, Order, Trade, init_db from freqtrade.persistence import LocalTrade, Order, Trade, init_db
@ -52,7 +52,7 @@ def test_init_invalid_db_url():
def test_init_prod_db(default_conf, mocker): def test_init_prod_db(default_conf, mocker):
default_conf.update({'dry_run': False}) default_conf.update({'dry_run': False})
default_conf.update({'db_url': constants.DEFAULT_DB_PROD_URL}) default_conf.update({'db_url': DEFAULT_DB_PROD_URL})
create_engine_mock = mocker.patch('freqtrade.persistence.models.create_engine', MagicMock()) create_engine_mock = mocker.patch('freqtrade.persistence.models.create_engine', MagicMock())
@ -1739,7 +1739,7 @@ def test_to_json(fee):
'base_currency': 'ADA', 'base_currency': 'ADA',
'quote_currency': 'USDT', 'quote_currency': 'USDT',
'is_open': None, 'is_open': None,
'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), 'open_date': trade.open_date.strftime(DATETIME_PRINT_FORMAT),
'open_timestamp': int(trade.open_date.timestamp() * 1000), 'open_timestamp': int(trade.open_date.timestamp() * 1000),
'open_order_id': 'dry_run_buy_12345', 'open_order_id': 'dry_run_buy_12345',
'close_date': None, 'close_date': None,
@ -1817,9 +1817,9 @@ def test_to_json(fee):
'pair': 'XRP/BTC', 'pair': 'XRP/BTC',
'base_currency': 'XRP', 'base_currency': 'XRP',
'quote_currency': 'BTC', 'quote_currency': 'BTC',
'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), 'open_date': trade.open_date.strftime(DATETIME_PRINT_FORMAT),
'open_timestamp': int(trade.open_date.timestamp() * 1000), 'open_timestamp': int(trade.open_date.timestamp() * 1000),
'close_date': trade.close_date.strftime("%Y-%m-%d %H:%M:%S"), 'close_date': trade.close_date.strftime(DATETIME_PRINT_FORMAT),
'close_timestamp': int(trade.close_date.timestamp() * 1000), 'close_timestamp': int(trade.close_date.timestamp() * 1000),
'open_rate': 0.123, 'open_rate': 0.123,
'close_rate': 0.125, 'close_rate': 0.125,