merge develop
This commit is contained in:
commit
a1f88cca80
@ -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
|
||||||
|
@ -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
|
||||||
|
27
docs/faq.md
27
docs/faq.md
@ -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?
|
||||||
@ -67,7 +74,7 @@ This is not a bot-problem, but will also happen while manual trading.
|
|||||||
While freqtrade can handle this (it'll sell 99 COIN), fees are often below the minimum tradable lot-size (you can only trade full COIN, not 0.9 COIN).
|
While freqtrade can handle this (it'll sell 99 COIN), fees are often below the minimum tradable lot-size (you can only trade full COIN, not 0.9 COIN).
|
||||||
Leaving the dust (0.9 COIN) on the exchange makes usually sense, as the next time freqtrade buys COIN, it'll eat into the remaining small balance, this time selling everything it bought, and therefore slowly declining the dust balance (although it most likely will never reach exactly 0).
|
Leaving the dust (0.9 COIN) on the exchange makes usually sense, as the next time freqtrade buys COIN, it'll eat into the remaining small balance, this time selling everything it bought, and therefore slowly declining the dust balance (although it most likely will never reach exactly 0).
|
||||||
|
|
||||||
Where possible (e.g. on binance), the use of the exchange's dedicated fee currency will fix this.
|
Where possible (e.g. on binance), the use of the exchange's dedicated fee currency will fix this.
|
||||||
On binance, it's sufficient to have BNB in your account, and have "Pay fees in BNB" enabled in your profile. Your BNB balance will slowly decline (as it's used to pay fees) - but you'll no longer encounter dust (Freqtrade will include the fees in the profit calculations).
|
On binance, it's sufficient to have BNB in your account, and have "Pay fees in BNB" enabled in your profile. Your BNB balance will slowly decline (as it's used to pay fees) - but you'll no longer encounter dust (Freqtrade will include the fees in the profit calculations).
|
||||||
Other exchanges don't offer such possibilities, where it's simply something you'll have to accept or move to a different exchange.
|
Other exchanges don't offer such possibilities, where it's simply something you'll have to accept or move to a different exchange.
|
||||||
|
|
||||||
@ -109,7 +116,7 @@ This warning can point to one of the below problems:
|
|||||||
|
|
||||||
### I'm getting the "RESTRICTED_MARKET" message in the log
|
### I'm getting the "RESTRICTED_MARKET" message in the log
|
||||||
|
|
||||||
Currently known to happen for US Bittrex users.
|
Currently known to happen for US Bittrex users.
|
||||||
|
|
||||||
Read [the Bittrex section about restricted markets](exchanges.md#restricted-markets) for more information.
|
Read [the Bittrex section about restricted markets](exchanges.md#restricted-markets) for more information.
|
||||||
|
|
||||||
@ -177,8 +184,8 @@ The GPU improvements would only apply to pandas-native calculations - or ones wr
|
|||||||
For hyperopt, freqtrade is using scikit-optimize, which is built on top of scikit-learn.
|
For hyperopt, freqtrade is using scikit-optimize, which is built on top of scikit-learn.
|
||||||
Their statement about GPU support is [pretty clear](https://scikit-learn.org/stable/faq.html#will-you-add-gpu-support).
|
Their statement about GPU support is [pretty clear](https://scikit-learn.org/stable/faq.html#will-you-add-gpu-support).
|
||||||
|
|
||||||
GPU's also are only good at crunching numbers (floating point operations).
|
GPU's also are only good at crunching numbers (floating point operations).
|
||||||
For hyperopt, we need both number-crunching (find next parameters) and running python code (running backtesting).
|
For hyperopt, we need both number-crunching (find next parameters) and running python code (running backtesting).
|
||||||
As such, GPU's are not too well suited for most parts of hyperopt.
|
As such, GPU's are not too well suited for most parts of hyperopt.
|
||||||
|
|
||||||
The benefit of using GPU would therefore be pretty slim - and will not justify the complexity introduced by trying to add GPU support.
|
The benefit of using GPU would therefore be pretty slim - and will not justify the complexity introduced by trying to add GPU support.
|
||||||
@ -219,9 +226,9 @@ already 8\*10^9\*10 evaluations. A roughly total of 80 billion evaluations.
|
|||||||
Did you run 100 000 evaluations? Congrats, you've done roughly 1 / 100 000 th
|
Did you run 100 000 evaluations? Congrats, you've done roughly 1 / 100 000 th
|
||||||
of the search space, assuming that the bot never tests the same parameters more than once.
|
of the search space, assuming that the bot never tests the same parameters more than once.
|
||||||
|
|
||||||
* The time it takes to run 1000 hyperopt epochs depends on things like: The available cpu, hard-disk, ram, timeframe, timerange, indicator settings, indicator count, amount of coins that hyperopt test strategies on and the resulting trade count - which can be 650 trades in a year or 100000 trades depending if the strategy aims for big profits by trading rarely or for many low profit trades.
|
* The time it takes to run 1000 hyperopt epochs depends on things like: The available cpu, hard-disk, ram, timeframe, timerange, indicator settings, indicator count, amount of coins that hyperopt test strategies on and the resulting trade count - which can be 650 trades in a year or 100000 trades depending if the strategy aims for big profits by trading rarely or for many low profit trades.
|
||||||
|
|
||||||
Example: 4% profit 650 times vs 0,3% profit a trade 10000 times in a year. If we assume you set the --timerange to 365 days.
|
Example: 4% profit 650 times vs 0,3% profit a trade 10000 times in a year. If we assume you set the --timerange to 365 days.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
`freqtrade --config config.json --strategy SampleStrategy --hyperopt SampleHyperopt -e 1000 --timerange 20190601-20200601`
|
`freqtrade --config config.json --strategy SampleStrategy --hyperopt SampleHyperopt -e 1000 --timerange 20190601-20200601`
|
||||||
|
@ -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",
|
||||||
@ -138,7 +139,7 @@ You can create your own keyboard in `config.json`:
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"token": "your_telegram_token",
|
"token": "your_telegram_token",
|
||||||
"chat_id": "your_telegram_chat_id",
|
"chat_id": "your_telegram_chat_id",
|
||||||
"keyboard": [
|
"keyboard": [
|
||||||
["/daily", "/stats", "/balance", "/profit"],
|
["/daily", "/stats", "/balance", "/profit"],
|
||||||
["/status table", "/performance"],
|
["/status table", "/performance"],
|
||||||
["/reload_config", "/count", "/logs"]
|
["/reload_config", "/count", "/logs"]
|
||||||
@ -225,16 +226,16 @@ Once all positions are sold, run `/stop` to completely stop the bot.
|
|||||||
For each open trade, the bot will send you the following message.
|
For each open trade, the bot will send you the following message.
|
||||||
Enter Tag is configurable via Strategy.
|
Enter Tag is configurable via Strategy.
|
||||||
|
|
||||||
> **Trade ID:** `123` `(since 1 days ago)`
|
> **Trade ID:** `123` `(since 1 days ago)`
|
||||||
> **Current Pair:** CVC/BTC
|
> **Current Pair:** CVC/BTC
|
||||||
> **Direction:** Long
|
> **Direction:** Long
|
||||||
> **Leverage:** 1.0
|
> **Leverage:** 1.0
|
||||||
> **Amount:** `26.64180098`
|
> **Amount:** `26.64180098`
|
||||||
> **Enter Tag:** Awesome Long Signal
|
> **Enter Tag:** Awesome Long Signal
|
||||||
> **Open Rate:** `0.00007489`
|
> **Open Rate:** `0.00007489`
|
||||||
> **Current Rate:** `0.00007489`
|
> **Current Rate:** `0.00007489`
|
||||||
> **Current Profit:** `12.95%`
|
> **Current Profit:** `12.95%`
|
||||||
> **Stoploss:** `0.00007389 (-0.02%)`
|
> **Stoploss:** `0.00007389 (-0.02%)`
|
||||||
|
|
||||||
### /status table
|
### /status table
|
||||||
|
|
||||||
@ -261,26 +262,26 @@ current max
|
|||||||
|
|
||||||
Return a summary of your profit/loss and performance.
|
Return a summary of your profit/loss and performance.
|
||||||
|
|
||||||
> **ROI:** Close trades
|
> **ROI:** Close trades
|
||||||
> ∙ `0.00485701 BTC (2.2%) (15.2 Σ%)`
|
> ∙ `0.00485701 BTC (2.2%) (15.2 Σ%)`
|
||||||
> ∙ `62.968 USD`
|
> ∙ `62.968 USD`
|
||||||
> **ROI:** All trades
|
> **ROI:** All trades
|
||||||
> ∙ `0.00255280 BTC (1.5%) (6.43 Σ%)`
|
> ∙ `0.00255280 BTC (1.5%) (6.43 Σ%)`
|
||||||
> ∙ `33.095 EUR`
|
> ∙ `33.095 EUR`
|
||||||
>
|
>
|
||||||
> **Total Trade Count:** `138`
|
> **Total Trade Count:** `138`
|
||||||
> **First Trade opened:** `3 days ago`
|
> **First Trade opened:** `3 days ago`
|
||||||
> **Latest Trade opened:** `2 minutes ago`
|
> **Latest Trade opened:** `2 minutes ago`
|
||||||
> **Avg. Duration:** `2:33:45`
|
> **Avg. Duration:** `2:33:45`
|
||||||
> **Best Performing:** `PAY/BTC: 50.23%`
|
> **Best Performing:** `PAY/BTC: 50.23%`
|
||||||
> **Trading volume:** `0.5 BTC`
|
> **Trading volume:** `0.5 BTC`
|
||||||
> **Profit factor:** `1.04`
|
> **Profit factor:** `1.04`
|
||||||
> **Max Drawdown:** `9.23% (0.01255 BTC)`
|
> **Max Drawdown:** `9.23% (0.01255 BTC)`
|
||||||
|
|
||||||
The relative profit of `1.2%` is the average profit per trade.
|
The relative profit of `1.2%` is the average profit per trade.
|
||||||
The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`.
|
The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`.
|
||||||
Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits.
|
Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits.
|
||||||
Profit Factor is calculated as gross profits / gross losses - and should serve as an overall metric for the strategy.
|
Profit Factor is calculated as gross profits / gross losses - and should serve as an overall metric for the strategy.
|
||||||
Max drawdown corresponds to the backtesting metric `Absolute Drawdown (Account)` - calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`.
|
Max drawdown corresponds to the backtesting metric `Absolute Drawdown (Account)` - calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`.
|
||||||
|
|
||||||
### /forceexit <trade_id>
|
### /forceexit <trade_id>
|
||||||
@ -309,27 +310,27 @@ Note that for this to work, `force_entry_enable` needs to be set to true.
|
|||||||
### /performance
|
### /performance
|
||||||
|
|
||||||
Return the performance of each crypto-currency the bot has sold.
|
Return the performance of each crypto-currency the bot has sold.
|
||||||
> Performance:
|
> Performance:
|
||||||
> 1. `RCN/BTC 0.003 BTC (57.77%) (1)`
|
> 1. `RCN/BTC 0.003 BTC (57.77%) (1)`
|
||||||
> 2. `PAY/BTC 0.0012 BTC (56.91%) (1)`
|
> 2. `PAY/BTC 0.0012 BTC (56.91%) (1)`
|
||||||
> 3. `VIB/BTC 0.0011 BTC (47.07%) (1)`
|
> 3. `VIB/BTC 0.0011 BTC (47.07%) (1)`
|
||||||
> 4. `SALT/BTC 0.0010 BTC (30.24%) (1)`
|
> 4. `SALT/BTC 0.0010 BTC (30.24%) (1)`
|
||||||
> 5. `STORJ/BTC 0.0009 BTC (27.24%) (1)`
|
> 5. `STORJ/BTC 0.0009 BTC (27.24%) (1)`
|
||||||
> ...
|
> ...
|
||||||
|
|
||||||
### /balance
|
### /balance
|
||||||
|
|
||||||
Return the balance of all crypto-currency your have on the exchange.
|
Return the balance of all crypto-currency your have on the exchange.
|
||||||
|
|
||||||
> **Currency:** BTC
|
> **Currency:** BTC
|
||||||
> **Available:** 3.05890234
|
> **Available:** 3.05890234
|
||||||
> **Balance:** 3.05890234
|
> **Balance:** 3.05890234
|
||||||
> **Pending:** 0.0
|
> **Pending:** 0.0
|
||||||
|
|
||||||
> **Currency:** CVC
|
> **Currency:** CVC
|
||||||
> **Available:** 86.64180098
|
> **Available:** 86.64180098
|
||||||
> **Balance:** 86.64180098
|
> **Balance:** 86.64180098
|
||||||
> **Pending:** 0.0
|
> **Pending:** 0.0
|
||||||
|
|
||||||
### /daily <n>
|
### /daily <n>
|
||||||
|
|
||||||
@ -376,7 +377,7 @@ Month (count) Profit BTC Profit USD Profit %
|
|||||||
|
|
||||||
Shows the current whitelist
|
Shows the current whitelist
|
||||||
|
|
||||||
> Using whitelist `StaticPairList` with 22 pairs
|
> Using whitelist `StaticPairList` with 22 pairs
|
||||||
> `IOTA/BTC, NEO/BTC, TRX/BTC, VET/BTC, ADA/BTC, ETC/BTC, NCASH/BTC, DASH/BTC, XRP/BTC, XVG/BTC, EOS/BTC, LTC/BTC, OMG/BTC, BTG/BTC, LSK/BTC, ZEC/BTC, HOT/BTC, IOTX/BTC, XMR/BTC, AST/BTC, XLM/BTC, NANO/BTC`
|
> `IOTA/BTC, NEO/BTC, TRX/BTC, VET/BTC, ADA/BTC, ETC/BTC, NCASH/BTC, DASH/BTC, XRP/BTC, XVG/BTC, EOS/BTC, LTC/BTC, OMG/BTC, BTG/BTC, LSK/BTC, ZEC/BTC, HOT/BTC, IOTX/BTC, XMR/BTC, AST/BTC, XLM/BTC, NANO/BTC`
|
||||||
|
|
||||||
### /blacklist [pair]
|
### /blacklist [pair]
|
||||||
@ -386,7 +387,7 @@ If Pair is set, then this pair will be added to the pairlist.
|
|||||||
Also supports multiple pairs, separated by a space.
|
Also supports multiple pairs, separated by a space.
|
||||||
Use `/reload_config` to reset the blacklist.
|
Use `/reload_config` to reset the blacklist.
|
||||||
|
|
||||||
> Using blacklist `StaticPairList` with 2 pairs
|
> Using blacklist `StaticPairList` with 2 pairs
|
||||||
>`DODGE/BTC`, `HOT/BTC`.
|
>`DODGE/BTC`, `HOT/BTC`.
|
||||||
|
|
||||||
### /edge
|
### /edge
|
||||||
|
@ -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__)
|
||||||
|
@ -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
|
||||||
|
@ -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']:
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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 (
|
||||||
|
@ -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 = [
|
||||||
[
|
[
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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
|
||||||
|
@ -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% |'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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],
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user