Skip to content

Commit

Permalink
Merge pull request #9876 from freqtrade/new_release
Browse files Browse the repository at this point in the history
New release 2024.2
  • Loading branch information
xmatthias committed Feb 29, 2024
2 parents d3b6256 + 0a8719c commit 1fc69ef
Show file tree
Hide file tree
Showing 75 changed files with 5,462 additions and 5,873 deletions.
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ updates:
directory: "/"
schedule:
interval: weekly
time: "03:00"
timezone: "Etc/UTC"
open-pull-requests-limit: 15
target-branch: develop
groups:
types:
patterns:
- "types-*"
pytest:
patterns:
- "pytest*"

- package-ecosystem: "github-actions"
directory: "/"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: pre-commit/action@v3.0.0
- uses: pre-commit/action@v3.0.1

docs-check:
runs-on: ubuntu-22.04
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/pre-commit-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ jobs:
- name: Run pre-commit
run: pre-commit run --all-files

- uses: peter-evans/create-pull-request@v5
- uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: .pre-commit-config.yaml
labels: |
Tech maintenance
Dependencies
branch: update/pre-commit-hooks
title: Update pre-commit hooks
commit-message: "chore: update pre-commit hooks"
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ repos:
additional_dependencies:
- types-cachetools==5.3.0.7
- types-filelock==3.2.7
- types-requests==2.31.0.20240125
- types-requests==2.31.0.20240218
- types-tabulate==0.9.0.20240106
- types-python-dateutil==2.8.19.20240106
- SQLAlchemy==2.0.25
- SQLAlchemy==2.0.27
# stages: [push]

- repo: https://github.com/pycqa/isort
Expand All @@ -31,7 +31,7 @@ repos:

- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.1.14'
rev: 'v0.2.2'
hooks:
- id: ruff

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.11.7-slim-bookworm as base
FROM python:3.11.8-slim-bookworm as base

# Setup env
ENV LANG C.UTF-8
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile.armhf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.11.7-slim-bookworm as base
FROM python:3.11.8-slim-bookworm as base

# Setup env
ENV LANG C.UTF-8
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ You can specify a different configuration file used by the bot with the `-c/--co
If you used the [Quick start](docker_quickstart.md#docker-quick-start) method for installing
the bot, the installation script should have already created the default configuration file (`config.json`) for you.

If the default configuration file is not created we recommend to use `freqtrade new-config --config config.json` to generate a basic configuration file.
If the default configuration file is not created we recommend to use `freqtrade new-config --config user_data/config.json` to generate a basic configuration file.

The Freqtrade configuration file is to be written in JSON format.

Expand Down
26 changes: 23 additions & 3 deletions docs/includes/pairlists.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ In your configuration, you can use Static Pairlist (defined by the [`StaticPairL

Additionally, [`AgeFilter`](#agefilter), [`PrecisionFilter`](#precisionfilter), [`PriceFilter`](#pricefilter), [`ShuffleFilter`](#shufflefilter), [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) act as Pairlist Filters, removing certain pairs and/or moving their positions in the pairlist.

If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You should always configure either `StaticPairList` or `VolumePairList` as the starting Pairlist Handler.
If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You can define either `StaticPairList`, `VolumePairList`, `ProducerPairList`, `RemotePairList` or `MarketCapPairList` as the starting Pairlist Handler.

Inactive markets are always removed from the resulting pairlist. Explicitly blacklisted pairs (those in the `pair_blacklist` configuration setting) are also always removed from the resulting pairlist.

Expand All @@ -24,6 +24,7 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged
* [`VolumePairList`](#volume-pair-list)
* [`ProducerPairList`](#producerpairlist)
* [`RemotePairList`](#remotepairlist)
* [`MarketCapPairList`](#marketcappairlist)
* [`AgeFilter`](#agefilter)
* [`FullTradesFilter`](#fulltradesfilter)
* [`OffsetFilter`](#offsetfilter)
Expand Down Expand Up @@ -67,7 +68,7 @@ When used in the leading position of the chain of Pairlist Handlers, the `pair_w

The `refresh_period` setting allows to define the period (in seconds), at which the pairlist will be refreshed. Defaults to 1800s (30 minutes).
The pairlist cache (`refresh_period`) on `VolumePairList` is only applicable to generating pairlists.
Filtering instances (not the first position in the list) will not apply any cache and will always use up-to-date data.
Filtering instances (not the first position in the list) will not apply any cache (beyond caching candles for the duration of the candle in advanced mode) and will always use up-to-date data.

`VolumePairList` is per default based on the ticker data from exchange, as reported by the ccxt library:

Expand Down Expand Up @@ -200,7 +201,7 @@ The RemotePairList is defined in the pairlists section of the configuration sett

The optional `mode` option specifies if the pairlist should be used as a `blacklist` or as a `whitelist`. The default value is "whitelist".

The optional `processing_mode` option in the RemotePairList configuration determines how the retrieved pairlist is processed. It can have two values: "filter" or "append".
The optional `processing_mode` option in the RemotePairList configuration determines how the retrieved pairlist is processed. It can have two values: "filter" or "append". The default value is "filter".

In "filter" mode, the retrieved pairlist is used as a filter. Only the pairs present in both the original pairlist and the retrieved pairlist are included in the final pairlist. Other pairs are filtered out.

Expand Down Expand Up @@ -264,6 +265,25 @@ The optional `bearer_token` will be included in the requests Authorization Heade
!!! Note
In case of a server error the last received pairlist will be kept if `keep_pairlist_on_failure` is set to true, when set to false a empty pairlist is returned.

#### MarketCapPairList

`MarketCapPairList` employs sorting/filtering of pairs by their marketcap rank based of CoinGecko. It will only recognize coins up to the coin placed at rank 250. The returned pairlist will be sorted based of their marketcap ranks.

```json
"pairlists": [
{
"method": "MarketCapPairList",
"number_assets": 20,
"max_rank": 50,
"refresh_period": 86400
}
]
```

`number_assets` defines the maximum number of pairs returned by the pairlist. `max_rank` will determine the maximum rank used in creating/filtering the pairlist. It's expected that some coins within the top `max_rank` marketcap will not be included in the resulting pairlist since not all pairs will have active trading pairs in your preferred market/stake/exchange combination.

`refresh_period` setting defines the period (in seconds) at which the marketcap rank data will be refreshed. Defaults to 86,400s (1 day). The pairlist cache (`refresh_period`) is applicable on both generating pairlists (first position in the list) and filtering instances (not the first position in the list).

#### AgeFilter

Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`) or more than `max_days_listed` days (defaults `None` mean infinity).
Expand Down
2 changes: 1 addition & 1 deletion docs/requirements-docs.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
markdown==3.5.2
mkdocs==1.5.3
mkdocs-material==9.5.6
mkdocs-material==9.5.11
mdx_truly_sane_lists==1.3
pymdown-extensions==10.7
jinja2==3.1.3
2 changes: 1 addition & 1 deletion docs/sql_cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Freqtrade does not depend or install any additional database driver. Please refe
The following systems have been tested and are known to work with freqtrade:

* sqlite (default)
* PostgreSQL)
* PostgreSQL
* MariaDB

!!! Warning
Expand Down
16 changes: 12 additions & 4 deletions docs/strategy-callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,7 @@ This callback is **not** called when there is an open order (either buy or sell)
`adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible.

Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position (negative values will decrease your position), no matter if it's a long or short trade.
Adjustment orders can be assigned with a tag by returning a 2 element Tuple, with the first element being the adjustment amount, and the 2nd element the tag (e.g. `return 250, 'increase_favorable_conditions'`).

Modifications to leverage are not possible, and the stake-amount returned is assumed to be before applying leverage.

Expand All @@ -790,7 +791,7 @@ Returning a value more than the above (so remaining stake_amount would become ne
If you wish to buy additional orders with DCA, then make sure to leave enough funds in the wallet for that.
Using 'unlimited' stake amount with DCA orders requires you to also implement the `custom_stake_amount()` callback to avoid allocating all funds to the initial order.

!!! Warning
!!! Warning "Stoploss calculation"
Stoploss is still calculated from the initial opening price, not averaged price.
Regular stoploss rules still apply (cannot move down).

Expand All @@ -800,6 +801,11 @@ Returning a value more than the above (so remaining stake_amount would become ne
During backtesting this callback is called for each candle in `timeframe` or `timeframe_detail`, so run-time performance will be affected.
This can also cause deviating results between live and backtesting, since backtesting can adjust the trade only once per candle, whereas live could adjust the trade multiple times per candle.

!!! Warning "Performance with many position adjustments"
Position adjustments can be a good approach to increase a strategy's output - but it can also have drawbacks if using this feature extensively.
Each of the orders will be attached to the trade object for the duration of the trade - hence increasing memory usage.
Trades with long duration and 10s or even 100ds of position adjustments are therefore not recommended, and should be closed at regular intervals to not affect performance.

``` python
from freqtrade.persistence import Trade

Expand Down Expand Up @@ -833,7 +839,8 @@ class DigDeeperStrategy(IStrategy):
min_stake: Optional[float], max_stake: float,
current_entry_rate: float, current_exit_rate: float,
current_entry_profit: float, current_exit_profit: float,
**kwargs) -> Optional[float]:
**kwargs
) -> Union[Optional[float], Tuple[Optional[float], Optional[str]]]:
"""
Custom trade adjustment logic, returning the stake amount that a trade should be
increased or decreased.
Expand All @@ -859,11 +866,12 @@ class DigDeeperStrategy(IStrategy):
:return float: Stake amount to adjust your trade,
Positive values to increase position, Negative values to decrease position.
Return None for no action.
Optionally, return a tuple with a 2nd element with an order reason
"""

if current_profit > 0.05 and trade.nr_of_successful_exits == 0:
# Take half of the profit at +5%
return -(trade.stake_amount / 2)
return -(trade.stake_amount / 2), 'half_profit_5%'

if current_profit > -0.05:
return None
Expand Down Expand Up @@ -891,7 +899,7 @@ class DigDeeperStrategy(IStrategy):
stake_amount = filled_entries[0].stake_amount
# This then calculates current safety order size
stake_amount = stake_amount * (1 + (count_of_entries * 0.25))
return stake_amount
return stake_amount, '1/3rd_increase'
except Exception as exception:
return None

Expand Down
8 changes: 2 additions & 6 deletions docs/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ optional arguments:
### Create config examples

```
$ freqtrade new-config --config config_binance.json
$ freqtrade new-config --config user_data/config_binance.json
? Do you want to enable Dry-run (simulated trades)? Yes
? Please insert your stake currency: BTC
Expand Down Expand Up @@ -990,11 +990,7 @@ options:
-h, --help show this help message and exit
--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
Provide a space-separated list of strategies to
backtest. Please note that timeframe needs to be set
either in config or via command line. When using this
together with `--export trades`, the strategy-name is
injected into the filename (so `backtest-data.json`
becomes `backtest-data-SampleStrategy.json`
be converted.
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
""" Freqtrade bot """
__version__ = '2024.1'
__version__ = '2024.2'

if 'dev' in __version__:
from pathlib import Path
Expand Down
7 changes: 4 additions & 3 deletions freqtrade/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@
'MaxDrawDownHyperOptLoss', 'MaxDrawDownRelativeHyperOptLoss',
'ProfitDrawDownHyperOptLoss']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'ProducerPairList', 'RemotePairList',
'AgeFilter', "FullTradesFilter", 'OffsetFilter', 'PerformanceFilter',
'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter',
'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter']
'MarketCapPairList', 'AgeFilter', "FullTradesFilter", 'OffsetFilter',
'PerformanceFilter', 'PrecisionFilter', 'PriceFilter',
'RangeStabilityFilter', 'ShuffleFilter', 'SpreadFilter',
'VolatilityFilter']
AVAILABLE_PROTECTIONS = ['CooldownPeriod',
'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard']
AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5', 'feather', 'parquet']
Expand Down
18 changes: 15 additions & 3 deletions freqtrade/data/converter/trade_converter_kraken.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
trades_df_remove_duplicates)
from freqtrade.data.history.idatahandler import get_datahandler
from freqtrade.exceptions import OperationalException
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.resolvers import ExchangeResolver


Expand Down Expand Up @@ -38,12 +39,22 @@ def import_kraken_trades_from_csv(config: Config, convert_to: str):
}
logger.info(f"Found csv files for {', '.join(data_symbols)}.")

if pairs_raw := config.get('pairs'):
pairs = expand_pairlist(pairs_raw, [m[0] for m in markets])
markets = {m for m in markets if m[0] in pairs}
if not markets:
logger.info(f"No data found for pairs {', '.join(pairs_raw)}.")
return
logger.info(f"Converting pairs: {', '.join(m[0] for m in markets)}.")

for pair, name in markets:
logger.debug(f"Converting pair {pair}, files */{name}.csv")
dfs = []
# Load and combine all csv files for this pair
for f in tradesdir.rglob(f"{name}.csv"):
df = pd.read_csv(f, names=KRAKEN_CSV_TRADE_COLUMNS)
dfs.append(df)
if not df.empty:
dfs.append(df)

# Load existing trades data
if not dfs:
Expand All @@ -52,17 +63,18 @@ def import_kraken_trades_from_csv(config: Config, convert_to: str):
continue

trades = pd.concat(dfs, ignore_index=True)
del dfs

trades.loc[:, 'timestamp'] = trades['timestamp'] * 1e3
trades.loc[:, 'cost'] = trades['price'] * trades['amount']
for col in DEFAULT_TRADES_COLUMNS:
if col not in trades.columns:
trades[col] = ''

trades.loc[:, col] = ''
trades = trades[DEFAULT_TRADES_COLUMNS]
trades = trades_convert_types(trades)

trades_df = trades_df_remove_duplicates(trades)
del trades
logger.info(f"{pair}: {len(trades_df)} trades, from "
f"{trades_df['date'].min():{DATETIME_PRINT_FORMAT}} to "
f"{trades_df['date'].max():{DATETIME_PRINT_FORMAT}}")
Expand Down
9 changes: 7 additions & 2 deletions freqtrade/data/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,10 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date'
starting_balance=starting_balance
)

idxmin = max_drawdown_df['drawdown_relative'].idxmax() if relative \
else max_drawdown_df['drawdown'].idxmin()
idxmin = (
max_drawdown_df['drawdown_relative'].idxmax()
if relative else max_drawdown_df['drawdown'].idxmin()
)
if idxmin == 0:
raise ValueError("No losing trade, therefore no drawdown.")
high_date = profit_results.loc[max_drawdown_df.iloc[:idxmin]['high_value'].idxmax(), date_col]
Expand Down Expand Up @@ -191,6 +193,9 @@ def calculate_cagr(days_passed: int, starting_balance: float, final_balance: flo
:param final_balance: Final balance to calculate CAGR against
:return: CAGR
"""
if final_balance < 0:
# With leveraged trades, final_balance can become negative.
return 0
return (final_balance / starting_balance) ** (1 / (days_passed / 365)) - 1


Expand Down

0 comments on commit 1fc69ef

Please sign in to comment.